В node.JS как я могу получить путь к модулю, который я загрузил через require, который * не * мой (то есть в каком-то node_module)


94

Мне нужен модуль, который был установлен через npm. Я хочу получить доступ к файлу .js, подчиненному этому модулю (чтобы я мог создать в нем подкласс метода конструктора). Я не могу (ну, не хочу) изменять код модуля, поэтому мне негде извлечь его __dirname.

Мне известен следующий вопрос, но он касается получения пути к модулю, который контролируется кодом (следовательно, __dirname является решением): В Node.js как я могу указать путь к модулю `this`?

~~~

Еще лучше было бы получить информацию о загруженном модуле


где вы можете загрузить модуль без ошибок с помощью require ('modulename')?
Futur

можешь лучше это объяснить? какой-то код?
Габриэль Лламас

Ответы:


130

Если я правильно понял ваш вопрос, вам следует использовать require.resolve () :

Используйте внутренний механизм require () для поиска местоположения модуля, но вместо загрузки модуля просто верните разрешенное имя файла.

Пример: var pathToModule = require.resolve('module');


13
Этот ответ не работает надежно со всеми модулями узлов. Смотрите мой ответ.
Джейсон

62

require.resolve () - частичный ответ. Принятый ответ может работать для многих узловых модулей, но не для всех.

require.resolve("moduleName")не дает вам каталог, в котором установлен модуль; он дает вам расположение файла, определенного в mainатрибуте модуля package.json.

Это могло быть moduleName/index.jsили могло быть moduleName/lib/moduleName.js. В последнем случае path.dirname(require.resolve("moduleName"))вернет каталог, который вы не хотите или не ожидаете:node_modules/moduleName/lib

Правильный способ получить полный путь к конкретному модулю - разрешить имя файла:

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

Если вам просто нужен каталог для модуля (возможно, вы собираетесь делать много path.join()вызовов), тогда разрешите package.json- который всегда должен быть в корне проекта - и перейдите к path.dirname():

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

1
очень умный ответ, обнаружив package.jsonфайл. Разве вы не должны использовать path.join('moduleName', 'package.json')для совместимости с Windows?
Жуан Пиментел Феррейра

2
@ JoãoPimentelFerreira не require.resolveзависит от платформы, requireпоэтому его и не нужно использоватьpath.join
Gopikrishna S

1
Не забудьте добавить const path = require('path');перед использованием path.dirname.
GOTO 0

Желаю, чтобы этот ответ был полностью верным! Я могу успешно разрешить что-то вроде того, require.resolve('@scope/module')что дает мне что-то вроде /path/to/@scope/module/dist/index.js, однако, если я пытаюсь запустить, require.resolve('@scope/module/package.json')это вызывает MODULE_NOT_FOUNDошибку. Я нахожусь в Node 14.4.0, и модуль, который я пытаюсь разрешить, имеет "type": "module"в своем package.json exportsполе, которое не включает package.json. Не уверен, имеет ли это какое-то отношение к этому ...
trusktr

Я обнаружил проблему: когда модуль type: module, по-видимому, package.jsonдолжен быть явно показан в exportsполе. Я думал, что новая функция ESM Node не блокирует requireразрешение путей, как обычно, но очевидно, что это так.
trusktr

3

FYI, require.resolveвозвращает идентификатор модуля в соответствии с CommonJS. В node.js это имя файла. В 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-файл и решил свою проблему.

Ссылка: Использование require.resolveдля получения разрешенного пути к файлу (узел)


2

Надеюсь, я правильно понимаю, что вам нужно: получить файл точки входа какого-либо модуля. Допустим, вы хотите получить точку входа в 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


2

Согласно решению @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

et voilà, получается, что он был в родительской папке моего проекта, то есть вне корневой папки моего проекта:

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

так что вы не можете избежать rm -rf ../../node_modules/myModule/и свежего npm install.

Я могу утверждать, что никто не давал указаний npmсканировать мой компьютер в поисках модулей в другом месте, кроме корневой папки моего проекта, в которой он должен был запускаться, или в пути поиска модулей по умолчанию.



1

Ответ Джейсона был лучшим ответом, пока не появились Node.js ESM и exportsполе.

Теперь, когда Node поддерживает пакеты с exportsполем, которое по умолчанию предотвращает package.jsonвозможность разрешения таких файлов , если автор пакета явно не решит их раскрыть, трюк в ответе Джейсона не сработает для пакетов, которые явно не раскрываютpackage.json .

Есть пакет, resolve-package-pathкоторый решает эту задачу.

Вот как им пользоваться:

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

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

который выведет что-то вроде

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

независимо от того, что exportsсодержит поле пакета .


Я подозреваю, что как только автор сознательно экспортирует часть содержимого модуля, вы окажетесь на еще более шаткой почве, потому что теперь автор официально определил свой публичный интерфейс. Я думаю, что это приведет к более агрессивному рефакторингу вещей, которые не экспортируются явно, не так ли?
Джейсон

@Jason Это верно для исходных файлов, но файлы package.json никуда не делись. Я не вижу причин, по которым их следует скрывать от импорта.
trusktr
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.