任务
我正在寻找一种通用的方法,在Node.js中获取已安装npm包的(绝对)根路径。
问题
我知道require.resolve
,但它会给我入口点(主模块的路径),而不是包的根路径。
以 bootstrap-sass
为例。假设它被本地安装在项目文件夹 C:\dev\my-project
中。那么我要找的是 C:\dev\my-project\node_modules\bootstrap-sass
。 而require.resolve('bootstrap-sass')
将返回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
文件夹中的所有软件包。
但是...
我想知道,这个相当简单的任务是否可以用更简单、不那么hacky的方式解决。在我看来,这似乎是Node.js应该内置的功能。有任何建议吗?