Em node.JS, como posso obter o caminho de um módulo que carreguei por meio de require que * não * é meu (ou seja, em algum node_module)

93

Eu preciso de um módulo que foi instalado via npm. Desejo acessar um arquivo .js subordinado a esse módulo (para que possa criar uma subclasse de um método Construtor nele). Eu não posso (bem, não quero) modificar o código do módulo, então não tenho um lugar para extrair seu __dirname.

Estou ciente da seguinte questão, mas é sobre como obter o caminho de um módulo sobre o qual se tem controle de código (portanto, __dirname é a solução): No Node.js, como posso saber o caminho do módulo `this`?

~~~

Ainda melhor seria obter as informações do módulo carregado do módulo

Zhami
fonte
onde você consegue carregar o módulo sem qualquer erro com require ('modulename')?
Futur
você pode explicar melhor? algum código?
Gabriel Llamas,

Respostas:

127

Se entendi corretamente sua pergunta, você deve usar require.resolve () :

Use o mecanismo interno require () para pesquisar a localização de um módulo, mas em vez de carregar o módulo, apenas retorne o nome do arquivo resolvido.

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

Linus Thiel
fonte
13
Esta resposta não funciona de forma confiável com todos os módulos de nó. Veja minha resposta.
Jason
57

require.resolve () é uma resposta parcial. A resposta aceita pode funcionar para muitos módulos de nó, mas não funcionará para todos eles.

require.resolve("moduleName")não fornece o diretório onde o módulo está instalado; fornece a localização do arquivo definido no mainatributo do módulo package.json.

Isso pode ser moduleName/index.jsou poderia ser moduleName/lib/moduleName.js. No último caso, path.dirname(require.resolve("moduleName"))retornará um diretório que você não deseja ou espera:node_modules/moduleName/lib

A maneira correta de obter o caminho completo para um módulo específico é resolvendo o nome do arquivo:

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

Se você quer apenas o diretório do módulo (talvez você vá fazer muitas path.join()chamadas), resolva o package.json- que deve estar sempre na raiz do projeto - e passe para path.dirname():

let packagePath = path.dirname(require.resolve("moduleName/package.json"));
Jason
fonte
1
resposta muito inteligente, detectando o package.jsonarquivo. Você não deveria usar path.join('moduleName', 'package.json')para ser compatível com o Windows?
João Pimentel Ferreira
2
@ JoãoPimentelFerreira require.resolveé agnóstico de plataforma, assim como requirenão é necessário usarpath.join
Gopikrishna S
1
Não se esqueça de adicionar const path = require('path');antes de usar path.dirname.
GOTO 0
Eu gostaria que essa resposta fosse totalmente verdadeira! Posso resolver com sucesso algo parecido, o require.resolve('@scope/module')que me dá algo parecido /path/to/@scope/module/dist/index.js, porém se eu tentar executá- require.resolve('@scope/module/package.json')lo gera um MODULE_NOT_FOUNDerro. Estou no Nó 14.4.0 e o módulo que estou tentando resolver tem "type": "module"em seu package.json com um exportscampo que não inclui package.json. Não tenho certeza se isso tem alguma coisa a ver com isso ...
trusktr
Encontrei o problema: quando um módulo tem type: module, aparentemente package.jsontem que ser explicitamente exposto no exportscampo. Achei que o novo recurso ESM do Node não bloqueou a requireresolução de caminhos como de costume, mas parece que sim.
trusktr
3

FYI, require.resolveretorna o identificador do módulo de acordo com CommonJS. Em node.js, este é o nome do arquivo. No webpack, é um número.

Em situação de webpack , aqui está minha solução para descobrir o caminho do 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]);

Então __webpack_modules__[pathToModule], obtive informações 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?");

    /***/
})

Acontece que eu precisava de scripts antigos do arquivo de compilação dll anterior (para velocidade de compilação mais rápida), de modo que meu arquivo de módulo atualizado não funcionasse como eu esperava. Finalmente eu reconstruí meu arquivo DLL e resolvi meu problema.

Ref: Usando require.resolvepara obter o caminho do arquivo resolvido (nó)

Alexee
fonte
2

Espero ter entendido corretamente suas necessidades: obter o arquivo de ponto de entrada de algum módulo. Digamos que você deseja obter o ponto de entrada do jugglingdbmódulo:

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

Como você pode ver, esta não é a maneira "oficial" de obter esse tipo de informação sobre o módulo, portanto, o comportamento dessa função pode mudar de versão para versão. Eu o encontrei na fonte do nó: https://github.com/joyent/node/blob/master/lib/module.js#L280

Anatoliy
fonte
2

De acordo com a solução @anatoliy, no MacOS XI encontrei os caminhos de pesquisa fazendo

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

então eu obtenho os caminhos de pesquisa resolvidos

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

Considerando que a

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

não vai resolver o módulo que estava procurando de jeito nenhum, na verdade o mais louco é que _loadnão vai resolver o 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)

enquanto a requirevontade:

> require('myModule')

mas eu não tenho este módulo em

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/

então onde está este módulo ???

Primeiro eu tive que fazer um $ sudo /usr/libexec/locate.updatedb Então depois de um café eu fiz locate myModuleou melhorlocate myModule/someFile.js

et voilà, descobri que estava em uma pasta pai do meu projeto, ou seja, fora da pasta raiz do meu projeto:

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

então você não pode evitar rm -rf ../../node_modules/myModule/e um novo npm install.

Posso argumentar que ninguém foi instruído npma varrer meu computador em busca de módulos em outro lugar além da pasta raiz do projeto onde deveria ser executado ou no caminho de pesquisa de módulos padrão.

Loretoparisi
fonte
1

Talvez seja isso que você está procurando, verifique:

require.main.filename

IvanM
fonte
1

A resposta de Jason foi a melhor resposta, até que o Node.js ESM e o exportscampo surgiram.

Agora que o Node oferece suporte a pacotes com um exportscampo que, por padrão, impedirá que arquivos como package.jsonsejam resolvidos , a menos que o autor do pacote decida explicitamente expô-los, o truque na resposta de Jason falhará para pacotes que não os expõem explicitamente package.json.

Existe um pacote chamado resolve-package-pathque resolve o problema.

Veja como usá-lo:

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

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

que irá produzir algo como

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

independentemente do que o exportscampo do pacote contém.

Trusktr
fonte
Suspeito que, uma vez que um autor está exportando conscientemente parte do conteúdo do módulo, você fica em terreno ainda mais instável, porque agora o autor definiu formalmente sua interface pública. Acho que isso tenderia a resultar em uma refatoração mais agressiva de coisas que não são explicitamente exportadas, não é?
Jason
@Jason Isso é verdade para os arquivos de origem, mas os arquivos package.json não estão indo embora. Não vejo nenhuma razão para que eles devam ser ocultados da importação.
trusktr