在node.JS中,如何通过require*not*mine(即在某个node_module中)获取已加载的模块的路径

Zha*_*ami 81 node.js

我需要一个通过npm安装的模块.我想访问从属于该模块的.js文件(因此我可以在其中继承一个Constructor方法).我不能(好吧,不想)修改模块的代码,所以没有地方提取它的__dirname.

我知道以下问题,但它是关于获取一个代码控制的模块的路径(因此,__ dirname是解决方案): 在Node.js中,我如何告诉`this`模块的路径?

~~~

更好的方法是获取模块的加载模块信息

Lin*_*iel 115

如果我正确理解您的问题,您应该使用require.resolve():

使用内部的require()机制来查找模块的位置,而不是加载模块,只需返回已解析的文件名.

例: var pathToModule = require.resolve('module');

  • 对于所有节点模块,此答案无法可靠地运行.看我的回答. (8认同)
  • esm中`ReferenceError: require is not Define`,如何在esm中解决? (2认同)

Jas*_*son 35

require.resolve()是正确的答案,但前提是你使用正确的参数.最佳答案适用于许多节点模块,但不适用于所有节点模块.

require.resolve("moduleName")没有提供安装模块的目录.它为您提供package.json中'main'属性中定义的文件的位置.那可能是moduleName/index.js,也可能是moduleName/lib/moduleName.js.在后一种情况下,main将返回错误的目录:node_modules/moduleName/lib

找到moduleName/README.md的正确方法是调用:

let readmePath = require.resolve("moduleName/README.md");
Run Code Online (Sandbox Code Playgroud)

如果您只是想要模块的目录(也许您要进行大量package.json调用),那么package.json必须始终位于项目的根目录中:

let packagePath = path.dirname(require.resolve("moduleName/package.json"));
Run Code Online (Sandbox Code Playgroud)

  • 我发现了问题:当模块具有“type: module”时,显然“package.json”必须在“exports”字段中显式公开。我认为 Node 的新 ESM 功能并没有像往常一样阻止“require”解析路径,但显然确实如此。 (5认同)
  • 我希望这个答案是完全正确的!我可以成功解析类似 `require.resolve('@scope/module')` 的内容,它给了我类似 `/path/to/@scope/module/dist/index.js` 的内容,但是如果我尝试运行 `require .resolve('@scope/module/package.json')` 它会抛出 `MODULE_NOT_FOUND` 错误。我在 Node 14.4.0 中,我尝试解析的模块在其 package.json 中有 `"type": "module"` ,其中包含一个不包含 `package.json` 的 `exports` 字段。不确定这是否与此有关...... (4认同)
  • 不要忘记在使用 `path.dirname` 之前添加 `const path = require('path');`。 (3认同)
  • 非常聪明的答案,通过检测“package.json”文件。难道你不应该使用 `path.join('moduleName', 'package.json')` 来兼容 Windows 吗? (2认同)
  • @JoãoPimentelFerreira`require.resolve`与平台无关,就像`require`所以不需要使用`path.join` (2认同)

tru*_*ktr 5

Jason 的答案是最好的答案,直到 Node.js ESM 和该exports领域出现。

既然 Node 支持带有一个exports字段的包,默认情况下该字段将阻止类似文件package.json被解析,除非包作者明确决定公开它们,Jason 的答案中的技巧对于没有显式公开的包将失败package.json

有一个名为的包resolve-package-path可以解决这个问题。

使用方法如下:

const resolvePkg = require('resolve-package-path')

console.log(resolvePkg('@some/package'))
Run Code Online (Sandbox Code Playgroud)

这会输出类似的东西

/path/to/@some/package/package.json
Run Code Online (Sandbox Code Playgroud)

无论包的exports字段包含什么。


Rya*_*ale 5

这是一个以与平台无关的方式返回模块目录的解决方案。这不使用任何第 3 方库,并成功找到"type": "module"通过 .. 安装的 ESM 模块和模块npm link

注意:如果特定模块是到另一个位置的符号链接(例如npm link),您将需要使用fs.realpath来获取目标目录的位置:

const moduleDir = getModuleDir('some-npm-module');
const theRealPath = fs.realpathSync(moduleDir);
Run Code Online (Sandbox Code Playgroud)

环境管理署

import fs from 'fs';
import path from 'path';
import { createRequire } from 'module';

/**
 * Get's the file path to a module folder.
 * @param {string} moduleEntry 
 * @param {string} fromFile 
 */
const getModuleDir = (moduleEntry) => {
    const packageName = moduleEntry.includes('/') 
        ? moduleEntry.startsWith('@') 
            ? moduleEntry.split('/').slice(0, 2).join('/') 
            : moduleEntry.split('/')[0]
        : moduleEntry;
    const require = createRequire(import.meta.url);
    const lookupPaths = require.resolve.paths(moduleEntry).map((p) => path.join(p, packageName));
    return lookupPaths.find((p) => fs.existsSync(p)); 
};
Run Code Online (Sandbox Code Playgroud)

CommonJS

const fs = require('fs');
const path = require('path');
const { createRequire } = require('module');

/**
 * Get's the file path to a module's folder.
 * @param {string} moduleEntry 
 * @param {string} fromFile 
 */
const getModuleDir = (moduleEntry, relativeToFile = __filename) => {
    const packageName = moduleEntry.includes('/') 
        ? moduleEntry.startsWith('@') 
            ? moduleEntry.split('/').slice(0, 2).join('/') 
            : moduleEntry.split('/')[0]
        : moduleEntry;
    const require = createRequire(relativeToFile);
    const lookupPaths = require.resolve.paths(moduleEntry).map((p) => path.join(p, packageName));
    return lookupPaths.find((p) => fs.existsSync(p)); 
}; 
Run Code Online (Sandbox Code Playgroud)

  • @brillout - 你是完全正确的。感谢您指出我的疏忽。我已经更新了我的答案以反映这一点。我仍然发现这很有用,因为它是一个 4 行函数,可以满足 90% 的用例。 (3认同)