Node.js: 获取已安装 npm 包的(绝对)根路径
Node.js: Get (absolute) root path of installed npm package
任务
我正在寻找一种通用方法来获取 Node.js 中已安装 npm 包的(绝对)根路径。
问题
我知道 require.resolve
,但这会给我入口点(主模块的路径)而不是包的根路径。
以bootstrap-sass
为例。假设它本地安装在项目文件夹 C:\dev\my-project
中。那么我要找的就是C:\dev\my-project\node_modules\bootstrap-sass
。 require.resolve('bootstrap-sass')
将 return C:\dev\my-project\node_modules\bootstrap-sass\assets\javascripts\bootstrap.js
.
我想到了几种获取包根路径的方法:
解决方案 #1
var packageRoot = path.resolve('node_modules/bootstrap-sass');
console.log(packageRoot);
这适用于本地安装在 node_modules
文件夹中的包。但是,如果我在子文件夹中,我需要解析 ../node_modules/bootstrap-sass
,并且嵌套文件夹越多,它就会变得越复杂。此外,这不适用于全局安装的模块。
解决方案 #2
var packageRoot = require.resolve('bootstrap-sass')
.match(/^.*[\/\]node_modules[\/\][^\/\]*/)[0];
console.log(packageRoot);
这适用于安装在 node_modules
文件夹中的本地和全局模块。正则表达式将匹配所有内容,直到最后一个 node_modules
路径元素加上以下路径元素。但是,如果包的入口点设置为另一个包(例如 package.json
中的 "main": "./node_modules/sub-package"
),这将失败。
解决方案 #3
var escapeStringRegexp = require('escape-string-regexp');
/**
* Get the root path of a npm package installed in node_modules.
* @param {string} packageName The name of the package.
* @returns {string} Root path of the package without trailing slash.
* @throws Will throw an error if the package root path cannot be resolved
*/
function packageRootPath(packageName) {
var mainModulePath = require.resolve(packageName);
var escapedPackageName = escapeStringRegexp(packageName);
var regexpStr = '^.*[\/\\]node_modules[\/\\]' + escapedPackageName +
'(?=[\/\\])';
var rootPath = mainModulePath.match(regexpStr);
if (rootPath) {
return rootPath[0];
} else {
var msg = 'Could not resolve package root path for package `' +
packageName + '`.'
throw new Error(msg);
}
}
var packageRoot = packageRootPath('bootstrap-sass');
console.log(packageRoot);
此功能适用于安装在 node_modules
文件夹中的所有软件包。
但是...
我想知道这个相当简单的任务是否不能用更简单、更简单的方法来解决。对我来说,它看起来应该已经内置到 Node.js 中了。有什么建议吗?
试试这个:
require.resolve('bootstrap-sass/package.json')
哪个returns:
path_to_my_project/node_modules/bootstrap-sass/package.json
您现在可以摆脱 'package.json' 路径后缀,例如:
var path = require('path') // npm install path
var bootstrapPath = path.dirname(require.resolve('bootstrap-sass/package.json'))
由于每个包都必须包含 package.json
文件,因此这应该始终有效(参见 What is a package?)。
您不需要查找所需包的 package.json 文件,甚至不需要明确引用它的位置。
此外,你不应该这样做,因为没有确定的方法知道 npm 包最终会在 npm 树中的什么位置(这就是为什么你求助于 require.resolve
寻求帮助)。
相反,您可以使用 npm ls
with the --parseable
标志查询 npm API(或 CLI),这将:
Show parseable output instead of tree view.
例如:
$ npm ls my-dep -p
... 将输出如下内容:
/Users/me/my-project/node_modules/my-dep
你应该知道这个命令也会输出一些不相关的错误到 stderr(例如关于无关的安装)——要解决这个问题,激活 --silent
标志(见 loglevel
在文档):
$ npm ls my-dep -ps
此命令可以使用 child process 集成到您的代码中,在这种情况下,它比 运行 没有 --silent
标志的命令 更受欢迎 允许捕获任何错误。
如果捕获到错误,您可以决定它是否致命(例如,应忽略上述关于无关包的错误)。
因此通过子进程使用 CLI 可以如下所示:
const exec = require('child_process').exec;
const packageName = 'my-dep';
exec(`npm ls ${packageName} --parseable`, (err, stdout, stderr) => {
if (!err) {
console.log(stdout.trim()); // -> /Users/me/my-project/node_modules/my-dep
}
});
然后可以在异步流中使用它(连同一些闭包魔法……),例如:
const exec = require('child_process').exec;
const async = require('async');
async.waterfall([
npmPackagePathResolver('my-dep'),
(packagePath, callback) => {
console.log(packagePath) // -> /Users/me/my-project/node_modules/my-dep
callback();
}
], (err, results) => console.log('all done!'));
function npmPackagePathResolver(packageName) {
return (callback) => {
exec(`npm ls ${packageName} --parseable`, (err, stdout, stderr) => {
callback(err, stdout.trim());
});
};
}
任务
我正在寻找一种通用方法来获取 Node.js 中已安装 npm 包的(绝对)根路径。
问题
我知道 require.resolve
,但这会给我入口点(主模块的路径)而不是包的根路径。
以bootstrap-sass
为例。假设它本地安装在项目文件夹 C:\dev\my-project
中。那么我要找的就是C:\dev\my-project\node_modules\bootstrap-sass
。 require.resolve('bootstrap-sass')
将 return C:\dev\my-project\node_modules\bootstrap-sass\assets\javascripts\bootstrap.js
.
我想到了几种获取包根路径的方法:
解决方案 #1
var packageRoot = path.resolve('node_modules/bootstrap-sass');
console.log(packageRoot);
这适用于本地安装在 node_modules
文件夹中的包。但是,如果我在子文件夹中,我需要解析 ../node_modules/bootstrap-sass
,并且嵌套文件夹越多,它就会变得越复杂。此外,这不适用于全局安装的模块。
解决方案 #2
var packageRoot = require.resolve('bootstrap-sass')
.match(/^.*[\/\]node_modules[\/\][^\/\]*/)[0];
console.log(packageRoot);
这适用于安装在 node_modules
文件夹中的本地和全局模块。正则表达式将匹配所有内容,直到最后一个 node_modules
路径元素加上以下路径元素。但是,如果包的入口点设置为另一个包(例如 package.json
中的 "main": "./node_modules/sub-package"
),这将失败。
解决方案 #3
var escapeStringRegexp = require('escape-string-regexp');
/**
* Get the root path of a npm package installed in node_modules.
* @param {string} packageName The name of the package.
* @returns {string} Root path of the package without trailing slash.
* @throws Will throw an error if the package root path cannot be resolved
*/
function packageRootPath(packageName) {
var mainModulePath = require.resolve(packageName);
var escapedPackageName = escapeStringRegexp(packageName);
var regexpStr = '^.*[\/\\]node_modules[\/\\]' + escapedPackageName +
'(?=[\/\\])';
var rootPath = mainModulePath.match(regexpStr);
if (rootPath) {
return rootPath[0];
} else {
var msg = 'Could not resolve package root path for package `' +
packageName + '`.'
throw new Error(msg);
}
}
var packageRoot = packageRootPath('bootstrap-sass');
console.log(packageRoot);
此功能适用于安装在 node_modules
文件夹中的所有软件包。
但是...
我想知道这个相当简单的任务是否不能用更简单、更简单的方法来解决。对我来说,它看起来应该已经内置到 Node.js 中了。有什么建议吗?
试试这个:
require.resolve('bootstrap-sass/package.json')
哪个returns:
path_to_my_project/node_modules/bootstrap-sass/package.json
您现在可以摆脱 'package.json' 路径后缀,例如:
var path = require('path') // npm install path
var bootstrapPath = path.dirname(require.resolve('bootstrap-sass/package.json'))
由于每个包都必须包含 package.json
文件,因此这应该始终有效(参见 What is a package?)。
您不需要查找所需包的 package.json 文件,甚至不需要明确引用它的位置。
此外,你不应该这样做,因为没有确定的方法知道 npm 包最终会在 npm 树中的什么位置(这就是为什么你求助于 require.resolve
寻求帮助)。
相反,您可以使用 npm ls
with the --parseable
标志查询 npm API(或 CLI),这将:
Show parseable output instead of tree view.
例如:
$ npm ls my-dep -p
... 将输出如下内容:
/Users/me/my-project/node_modules/my-dep
你应该知道这个命令也会输出一些不相关的错误到 stderr(例如关于无关的安装)——要解决这个问题,激活 --silent
标志(见 loglevel
在文档):
$ npm ls my-dep -ps
此命令可以使用 child process 集成到您的代码中,在这种情况下,它比 运行 没有 --silent
标志的命令 更受欢迎 允许捕获任何错误。
如果捕获到错误,您可以决定它是否致命(例如,应忽略上述关于无关包的错误)。
因此通过子进程使用 CLI 可以如下所示:
const exec = require('child_process').exec;
const packageName = 'my-dep';
exec(`npm ls ${packageName} --parseable`, (err, stdout, stderr) => {
if (!err) {
console.log(stdout.trim()); // -> /Users/me/my-project/node_modules/my-dep
}
});
然后可以在异步流中使用它(连同一些闭包魔法……),例如:
const exec = require('child_process').exec;
const async = require('async');
async.waterfall([
npmPackagePathResolver('my-dep'),
(packagePath, callback) => {
console.log(packagePath) // -> /Users/me/my-project/node_modules/my-dep
callback();
}
], (err, results) => console.log('all done!'));
function npmPackagePathResolver(packageName) {
return (callback) => {
exec(`npm ls ${packageName} --parseable`, (err, stdout, stderr) => {
callback(err, stdout.trim());
});
};
}