如何获取我通过require加载的模块的路径,该路径 * 不是 * 我的(即在某个node_module中)

new9mtju  于 2022-12-03  发布在  Node.js
关注(0)|答案(8)|浏览(223)

我需要一个通过npm安装的模块。我想访问该模块的一个.js文件(这样我就可以在其中子类化一个Constructor方法)。我不能(嗯,不想)修改模块的代码,所以没有地方提取它的__dirname。
我知道下面的问题,但它是关于获得一个代码控制的模块的路径(因此,__dirname是解决方案):In Node.js how can I tell the path of this module?

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

wrrgggsh1#

如果我正确理解了您的问题,您应该使用require.resolve():
使用内部的require()机制来查找模块的位置,但不是加载模块,而是返回解析后的文件名。
示例:var pathToModule = require.resolve('module');

bz4sfanl

bz4sfanl2#

require.resolve()是一个部分的答案。被接受的答案可能对许多节点模块都有效,但不会对所有节点模块都有效。
require.resolve("moduleName")没有给予模块的安装目录;它给出了在模块的package.json中的main属性中定义的文件的位置。
这可能是 * moduleName/index.js *,也可能是 * moduleName/lib/moduleName.js。在后一种情况下,path.dirname(require.resolve("moduleName"))将返回您可能不需要或不期望的目录: 一米六纳一x *
获取特定模块的完整路径的正确方法是解析文件名:

let readmePath = require.resolve("moduleName/README.md");

如果你只需要模块的目录(也许你会做很多path.join()调用),那么解析package.json-它必须总是在项目的根目录下-并传递给path.dirname()

let packagePath = path.dirname(require.resolve("moduleName/package.json"));
ctrmrzij

ctrmrzij3#

仅供参考,require.resolve根据CommonJS返回模块标识符。在node.js中,这是文件名。在webpack中,这是一个数字。
webpack情况下,下面是我找出模块路径的解决方案:

const pathToModule = require.resolve('module/to/require');
console.log('pathToModule is', pathToModule); // a number, eg. 8
console.log('__webpack_modules__[pathToModule] is', __webpack_modules__[pathToModule]);

然后从__webpack_modules__[pathToModule]我得到了这样的信息:

(function(module, exports, __webpack_require__) {

    eval("module.exports = (__webpack_require__(6))(85);\n\n//////////////////\n// 
    WEBPACK FOOTER\n// delegated ./node_modules/echarts/lib/echarts.js from dll-reference vendor_da75d351571a5de37e2e\n// module id = 8\n// module chunks = 0\n\n//# sourceURL=webpack:///delegated_./node_modules/echarts/lib/echarts.js_from_dll-reference_vendor_da75d351571a5de37e2e?");

    /***/
})

原来我需要以前的dll构建文件中的旧脚本(为了更快的构建速度),所以我更新的模块文件没有像我预期的那样工作。最后我重建了dll文件,解决了我的问题。
参考:Using require.resolve to get resolved file path (node)

ccrfmcuu

ccrfmcuu4#

Jason的答案是最好的答案,直到Node.js ESM和exports字段出来。
既然Node支持带有exports字段的包,默认情况下,该字段将 * 阻止像package.json这样的文件被解析 *,除非包作者明确决定公开它们,那么对于没有明确公开package.json的包,Jason答案中的技巧将失败。
有一个名为resolve-package-path的软件包可以做到这一点。
使用方法如下:

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

console.log(resolvePkg('@some/package'))

它将输出类似于

/path/to/@some/package/package.json

而不管包的exports字段包含什么。

pepwfjgg

pepwfjgg5#

我希望我能正确理解您的需求:来获取某个模块的入口点文件。假设你想获取jugglingdb模块的入口点:

node
> require('module')._resolveFilename('jugglingdb')
'/usr/local/lib/node_modules/jugglingdb/index.js'

正如您所看到,这不是获取模块信息的“官方”方式,因此该函数的行为可能会因版本而异。https://github.com/joyent/node/blob/master/lib/module.js#L280

jckbn6z7

jckbn6z76#

根据@anatoliy解决方案,在MacOS X上,我找到了查找路径

require('module')._resolveLookupPaths('myModule')

这样我就得到了解析后的查找路径

[ 'myModule',
  [ '/Users/admin/.node_modules',
    '/Users/admin/.node_libraries',
    '/usr/local/lib/node' ] ]

require('module')._resolveFilename('myModule')

无论如何都不会解析我正在寻找的模块,事实上疯狂的是_load不会解析模块:

> require('module')._load('myModule')
Error: Cannot find module 'myModule'
    at Function.Module._resolveFilename (module.js:440:15)
    at Function.Module._load (module.js:388:25)
    at repl:1:19
    at sigintHandlersWrap (vm.js:32:31)
    at sigintHandlersWrap (vm.js:96:12)
    at ContextifyScript.Script.runInContext (vm.js:31:12)
    at REPLServer.defaultEval (repl.js:308:29)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:489:10)

require将:

> require('myModule')

但我没有这个模块

myProject/node_modules/
myProject/node_modules/@scope/
/usr/local/lib/node_modules/
/usr/local/lib/node_modules/@scope
/usr/local/lib/node_modules/npm/node_modules/
/usr/local/lib/node_modules/npm/node_modules/@scope
$HOME/.npm/
$HOME/.npm/@scope/

那么这个模块在哪里?
首先,我必须做一个$ sudo /usr/libexec/locate.updatedb,然后在一些咖啡后,我做了locate myModule或更好的locate myModule/someFile.js
瞧,它出来,它是在我的项目的父文件夹,即.我的项目根文件夹之外:

$pwd
/Users/admin/Projects/Node/myProject
$ ls ../../node_modules/myModule/

因此您无法避免rm -rf ../../node_modules/myModule/和一个新npm install
我可以争辩说,没有人指示npm扫描我的计算机,在我的项目根文件夹以外的地方搜索模块,在那里它应该运行或在默认的模块搜索路径。

kyvafyod

kyvafyod7#

这也许就是你要找的,检查:
require.main.filename

snvhrwxg

snvhrwxg8#

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

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

const moduleDir = getModuleDir('some-npm-module');
const theRealPath = fs.realpathSync(moduleDir);

紧急支援模块

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)); 
};

普通JS

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)); 
};

相关问题