En node.JS, ¿cómo puedo obtener la ruta de un módulo que he cargado a través de require que es * no * mío (es decir, en algún node_module)

94

Necesito un módulo que se instaló a través de npm. Quiero acceder a un archivo .js subordinado a ese módulo (para que pueda subclasificar un método Constructor en él). No puedo (bueno, no quiero) modificar el código del módulo, así que no tengo un lugar para extraer su __dirname.

Soy consciente de la siguiente pregunta, pero se trata de obtener la ruta de un módulo sobre el que uno tiene control de código (por lo tanto, __dirname es la solución): En Node.js, ¿cómo puedo saber la ruta de este módulo?

~~~

Aún mejor sería obtener la información del módulo cargado del módulo

Zhami
fuente
¿Dónde puede cargar el módulo sin ningún error con require ('nombre de módulo')?
Futur
¿puedes explicarlo mejor? algún código?
Gabriel Llamas

Respuestas:

130

Si entiendo correctamente su pregunta, debe usar require.resolve () :

Utilice la maquinaria interna require () para buscar la ubicación de un módulo, pero en lugar de cargar el módulo, simplemente devuelva el nombre de archivo resuelto.

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

Linus Thiel
fuente
13
Esta respuesta no funciona de manera confiable con todos los módulos de nodo. Mira mi respuesta.
Jason
61

require.resolve () es una respuesta parcial. La respuesta aceptada puede funcionar para muchos módulos de nodo, pero no funcionará para todos.

require.resolve("moduleName")no le da el directorio donde está instalado el módulo; le da la ubicación del archivo definido en el mainatributo en el módulo package.json.

Eso podría ser moduleName/index.jso podría ser moduleName/lib/moduleName.js. En el último caso, path.dirname(require.resolve("moduleName"))devolverá un directorio que quizás no desee o no espere:node_modules/moduleName/lib

La forma correcta de obtener la ruta completa a un módulo específico es resolviendo el nombre del archivo:

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

Si solo desea el directorio para el módulo (tal vez vaya a realizar muchas path.join()llamadas), resuelva el package.json, que siempre debe estar en la raíz del proyecto, y pase a path.dirname():

let packagePath = path.dirname(require.resolve("moduleName/package.json"));
Jason
fuente
1
respuesta muy inteligente, al detectar el package.jsonarchivo. ¿No deberías usarlo path.join('moduleName', 'package.json')por ser compatible con Windows?
João Pimentel Ferreira
2
@ JoãoPimentelFerreira require.resolvees independiente de la plataforma, al igual requireque no es necesario usarlopath.join
Gopikrishna S
1
No olvide agregar const path = require('path');antes de usar path.dirname.
GOTO 0
¡Ojalá esta respuesta fuera completamente cierta! Puedo resolver con éxito algo como lo require.resolve('@scope/module')que me da algo como /path/to/@scope/module/dist/index.js, sin embargo, si intento ejecutarlo require.resolve('@scope/module/package.json'), arroja un MODULE_NOT_FOUNDerror. Estoy en el nodo 14.4.0, y el módulo que estoy tratando de resolver tiene "type": "module"en su package.json un exportscampo que no incluye package.json. No estoy seguro si eso tiene algo que ver con eso ...
trusktr
Encontré el problema: cuando un módulo tiene type: module, aparentemente package.jsontiene que ser expuesto explícitamente en el exportscampo. Pensé que la nueva función ESM de Node no bloqueaba la requireresolución de rutas como de costumbre, pero aparentemente lo hace.
trusktr
3

FYI, require.resolvedevuelve el identificador del módulo de acuerdo con CommonJS. En node.js, este es el nombre del archivo. En webpack, este es un número.

En la situación del paquete web , aquí está mi solución para averiguar la ruta del módulo:

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

Luego __webpack_modules__[pathToModule]obtuve información como esta:

(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?");

    /***/
})

Resultó que necesitaba scripts antiguos del archivo de compilación dll anterior (para una velocidad de compilación más rápida), por lo que mi archivo de módulo actualizado no funcionó como esperaba. Finalmente reconstruí mi archivo dll y resolví mi problema.

Ref: Uso require.resolvepara obtener la ruta del archivo resuelta (nodo)

Alexee
fuente
2

Espero entender correctamente sus necesidades: obtener el archivo de punto de entrada de algún módulo. Digamos que desea obtener el punto de entrada del jugglingdbmódulo:

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

Como puede ver, esta no es una forma "oficial" de obtener este tipo de información sobre el módulo, por lo que el comportamiento de esta función puede cambiar de una versión a otra. Lo encontré en la fuente del nodo: https://github.com/joyent/node/blob/master/lib/module.js#L280

Anatoliy
fuente
2

De acuerdo con la solución @anatoliy, en MacOS X, encontré las rutas de búsqueda haciendo

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

entonces obtengo las rutas de búsqueda resueltas

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

mientras que el

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

no resolverá el módulo que estaba buscando de todos modos, de hecho lo loco es que _loadno resolverá el módulo:

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

mientras que la requirevoluntad:

> require('myModule')

pero no tengo este módulo en

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/

Entonces, ¿dónde está este módulo?

Primero tuve que hacer un $ sudo /usr/libexec/locate.updatedb Luego después de un café lo hice locate myModuleo mejorlocate myModule/someFile.js

et voilà, resulta que estaba en una carpeta principal de mi proyecto, es decir, fuera de la carpeta raíz de mi proyecto:

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

por lo que no se puede evitar rm -rf ../../node_modules/myModule/y un fresco npm install.

Puedo argumentar que nadie npmrecibió instrucciones de escanear mi computadora en busca de módulos en otro lugar que no sea la carpeta raíz del proyecto donde se suponía que debía ejecutarse o en la ruta de búsqueda de módulos predeterminada.

Loretoparisi
fuente
1

Esto es quizás lo que estás buscando, comprueba:

require.main.filename

IvanM
fuente
1

La respuesta de Jason fue la mejor respuesta, hasta que Node.js ESM y el exportscampo salieron.

Ahora que Node admite paquetes con un exportscampo que de forma predeterminada evitará que los archivos como se package.jsonpuedan resolver a menos que el autor del paquete decida explícitamente exponerlos, el truco en la respuesta de Jason fallará para los paquetes que no exponen explícitamente package.json.

Hay un paquete llamado resolve-package-pathque hace el truco.

He aquí cómo usarlo:

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

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

que dará como resultado algo como

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

independientemente de lo que exportscontenga el campo del paquete .

trusktr
fuente
Sospecho que una vez que un autor exporta conscientemente parte del contenido del módulo, se encuentra en un terreno aún más inestable, porque ahora el autor ha definido formalmente su interfaz pública. Creo que eso tendería a resultar en una refactorización más agresiva de las cosas que no se exportan explícitamente, ¿no es así?
Jason
@Jason Eso es cierto para los archivos fuente, pero los archivos package.json no van a desaparecer. No veo ninguna razón por la que deberían ocultarse de la importación.
trusktr