O NodeJS requer um módulo / pacote global

158

Estou tentando instalar globalmente e depois usar forevere forever-monitorassim:

npm install -g forever forever-monitor

Eu vejo a saída usual e também as operações que copiam os arquivos para o caminho global, mas se eu tentar require("forever");, recebo um erro dizendo que o módulo não foi encontrado.

Estou usando a versão mais recente do nó e do npm e já sei sobre as alterações que o npm fez na instalação global x local, mas realmente não quero instalar o local em todos os projetos e estou trabalhando em uma plataforma que não suporte linkpara que, npm linkapós uma instalação global, não seja possível para mim.

Minha pergunta é: por que não posso exigir um pacote instalado globalmente? Isso é um recurso ou um bug? Ou estou fazendo algo errado?

PS: Apenas para esclarecer: não quero instalar localmente.

alexandernst
fonte
então é ~/.config/yarn/globalpara fios
localhostdotdev

Respostas:

215

No Node.js, o require não procura na pasta em que os módulos globais estão instalados.

Você pode corrigir isso configurando a variável de ambiente NODE_PATH. No Linux, será:

export NODE_PATH=/usr/lib/node_modules

Nota: Isso depende de onde seus módulos globais estão realmente instalados.

Consulte: Carregando nas pastas globais .

Daniel Uzunu
fonte
24
Na minha máquina Ubuntu 13.10, o caminho global para os módulos é diferente do que você mostra aqui. Eu tive que usar em seu export NODE_PATH=/usr/local/lib/node_moduleslugar.
de Drew Noakes
11
Se você está no Windows 7/8 e não substituiu nenhum dos padrões de instalação do Node, é provável que a configuração da NODE_PATHvariável de ambiente C:\Users\{USERNAME}\AppData\Roaming\npm\node_modulesfuncione.
Wes Johnson
5
@WesJohnson Apenas %AppData%\npm\node_modulesfuncionará no Windows 10.
theblang
6
Se eu configurar, NODE_PATHposso usar módulos globais e locais simultaneamente?
Paulo Oliveira
6
Como alternativa, em vez de um caminho estático, ou seja, se você estiver usando NVM:NODE_PATH=$(npm root -g)
holmberd
96

Depois de instalar o pacote globalmente, você deve vincular o projeto local ao pacote global

npm install express -g
cd ~/mynodeproject/
npm link express  

Veja aqui

user568109
fonte
2
Estou correndo em uma plataforma que não suporta ligação (como a minha pergunta estados) blog.nodejs.org/2011/04/06/npm-1-0-link
alexandernst
1
qual plataforma você está usando?
user568109
1
Eu realmente não quero mexer com link (nem links simbólicos). Eu só quero instalar pacotes globalmente e requerê-los. Eu sei que o NPM foi redesenhado para evitar isso, mas quão difícil poderia ser conseguir algo assim?
precisa
13
E se eu não tiver um projeto? Diga ~/some-stand-alone-random-nodejs-test.js. Não quero transformar minha pasta pessoal em um diretório de projeto. Não quero criar novas pastas para cada pequena experiência.
AnnanFay
1
Funcionou perfeitamente no Windows 8.1. Do cd da linha de comando do nó para a pasta node_modules local dos meus projetos e executada npm link <module>Em seguida, você verá um atalho (link) criado na pasta node_module dos seus projetos fazendo referência ao módulo global do nó.
dynamiclynk
26

Desculpas pela necromancia, mas sou capaz de especificar caminhos codificados para módulos instalados globalmente:

var pg = require("/usr/local/lib/node_modules/pg");

Isso não é perfeito, mas considerando que o Unity3d tenta "compilar" todo o javascript incluído no diretório do projeto, eu realmente não consigo instalar nenhum pacote.

Thomas Ingham
fonte
4
O Unity3D não suporta JavaScript. Ele suporta uma sintaxe semelhante a JS para seu interpretador / compilador Boo (Boo é uma linguagem semelhante a Python para .NET) que é enganosamente comercializado como "JavaScript" . O nome mais preciso para o idioma que o Unity suporta é UnityScript . Como não é nem perto da mesma linguagem, quase nenhum JS escrito para a Web ou para o Node.js funcionará no Unity. Muito mais informações sobre as diferenças em Unidade wiki oficial: wiki.unity3d.com/index.php/UnityScript_versus_JavaScript
Slipp D. Thompson
19

Eu sei que essa é uma pergunta antiga, mas me deparei com isso ao tentar fazer uma verificação de versão usando semverum preinstallscript no package.json. Como sabia que não podia depender de nenhum módulo local instalado, usei isso para exigir semverda node_modulespasta global (pois npmdepende disso, sei que está lá):

function requireGlobal(packageName) {
  var childProcess = require('child_process');
  var path = require('path');
  var fs = require('fs');

  var globalNodeModules = childProcess.execSync('npm root -g').toString().trim();
  var packageDir = path.join(globalNodeModules, packageName);
  if (!fs.existsSync(packageDir))
    packageDir = path.join(globalNodeModules, 'npm/node_modules', packageName); //find package required by old npm

  if (!fs.existsSync(packageDir))
    throw new Error('Cannot find global module \'' + packageName + '\'');

  var packageMeta = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json')).toString());
  var main = path.join(packageDir, packageMeta.main);

  return require(main);
}

Eu gosto dessa abordagem porque isso não requer a instalação de nenhum módulo especial para ser usado.

Não escolhi uma NODE_PATHsolução como outras pessoas sugeriram, pois queria que isso funcionasse na máquina de qualquer pessoa, sem precisar exigir configuração adicional antes de executarnpm install meu projeto.

Da maneira como isso é codificado, é garantido encontrar apenas os módulos de nível superior (instalados usando npm install -g ...) ou os módulos exigidos por npm(listados dependenciesaqui: https://github.com/npm/npm/blob/master/package.json ). Se você estiver usando uma versão mais recente do NPM, ele poderá encontrar dependências de outros pacotes instalados globalmente, pois há uma estrutura mais plana paranode_modules pastas.

Espero que isso seja útil para alguém.

Joe Skeen
fonte
19

De acordo com a documentação , o Node.js procurará nos seguintes locais por padrão:

  1. Caminho especificado na NODE_PATHvariável de ambiente .

    Nota: NODE_PATHa variável de ambiente está configurada para uma lista delimitada por dois pontos de caminhos absolutos.

  2. node_modulesPasta atual . (local)

  3. $HOME/.node_modules (global)

    Nota: $HOMEé o diretório inicial do usuário.

  4. $HOME/.node_libraries (global)
  5. $PREFIX/lib/node (global)

    Nota: o $PREFIXNode.js está configurado node_prefix.

    Para verificar o valor atual de node_prefix, execute:

    node -p process.config.variables.node_prefix

    Nota: O prefixo corresponde ao --prefixparâmetro durante a construção e é relativo a process.execPath. Não confundir com valor do npm config get prefixcomando. fonte

Se o módulo fornecido não puder ser encontrado, isso significa que ele não está presente em um dos locais acima.

O local da pasta raiz global onde os módulos estão instalados pode ser impresso por: npm root -g(por padrão, o caminho é calculado em tempo de execução, a menos que seja substituído no npmrcarquivo ).

Solução

Você pode tentar as seguintes soluções alternativas:

  • Especifique a localização do módulo global na NODE_PATHvariável de ambiente. Por exemplo

    echo 'require("forever")' | NODE_PATH="$(npm root -g):$NODE_PATH" node

    Para testar e imprimir o valor de NODE_PATH, execute:

    echo 'console.log(process.env.NODE_PATH); require("forever")' | NODE_PATH="$(npm root -g):$NODE_PATH" node 
  • Para uma solução mais permanente, vincule sua $HOME/.node_modulespasta de usuário global para apontar para a pasta raiz, executando este comando:

    ln -vs "$(npm root -g)" "$HOME"/.node_modules

    Em seguida, teste-o novamente através do echo 'require("forever")' | nodecomando :

  • Altere temporariamente a pasta atual para onde a extensão foi instalada globalmente, antes de chamar o script. Por exemplo

    npm install -g forever
    cd "$(npm root -g)"
    echo 'require("forever")' | node
    cd -
  • Configure o destino da instalação global no npmarquivo userconfig (consulte :)npm help 5 npmrc ou por userconfigparam (--prefix ).

    Para exibir a configuração atual, execute: npm config list .

    Para editar a configuração atual, execute: npm config edit.

  • Especifique o caminho completo da localização dos módulos do ao chamar require(). Por exemplo

    require("/path/to/sub/module")
  • Instale o pacote no local personalizado, por exemplo

    npm install forever -g --prefix "$HOME"/.node_modules

    No entanto, a instalação será interrompida ~/.node_modules/lib/node_modules/, portanto o local ainda precisará ser adicionado.

    Consulte: pacote de instalação local npm no local personalizado

  • Crie um link simbólico na pasta atual a partir do local do pacote global. Por exemplo

    npm link forever
kenorb
fonte
Parece com a pasta 4. node_modules atual. (local) tem prioridade sobre 3. $ PREFIX / lib / node (global)
Király István
As pastas locais node_modules sempre têm prioridade sobre as pastas globais!
Király István
14

Você pode usar o pacote requiregpara resolver este problema:

var forever = require('requireg')('forever')

fará o truque.

Além disso, há outro módulo, global-npmembora seja específico apenas para usar o global npm, você pode ver o código curto e ver como a técnica funciona.

JP Richardson
fonte
interessante, mas o método NODE_PATH é provavelmente mais canônica
Alexander Mills
a beleza de NODE_PATHé também que você não precisa alterar nenhum código. (meu caso de uso está classificando muitos projetos de alunos, nos quais não desejo executar npm installpara cada um e também não quero que eles forneçam node_modulesdiretório).
Amenthes
Não, não funcionará porque você não pode exigir requiregem primeiro lugar, esse é o ponto.
Thisismydesign
6

Para utilitários CLI que dependem de grandes módulos, puppeteergosto de gerar um npm root -ge usá-lo para exigir o módulo global.

try {
  const root = require('child_process').execSync('npm root -g').toString().trim()
  var puppeteer = require(root + '/puppeteer')
} catch (err) {
  console.error(`Install puppeteer globally first with: npm install -g puppeteer`)
  process.exit(1)
}
Christophe Marois
fonte
3

Você pode colocar esta linha no seu .profilearquivo:

export NODE_PATH = "$ (prefixo de obtenção da configuração npm) / lib / node_modules"

Isso fará nodeuso do caminho global.

Luis Paulo
fonte
1
Não. Essa é a maneira genérica de obter o global node_modules. Essa é uma resposta antiga, mas lembro que a recebi de algum lugar da documentação. De qualquer forma, no meu computador (em 2020) o node_modulesdiretório npm global é usr/lib/node_modules. De qualquer forma, confio npm config get prefixporque é usado globalmente pelo npm sempre que um pacote global é instalado, portanto deve estar certo.
Luis Paulo
1
De qualquer maneira (eu não disse isso na minha resposta inicial porque não tinha muita experiência no Node.JS), o uso de pacotes instalados globalmente em um programa é um caso de uso de borda e raramente deve ser feito porque em um projeto ele criará problemas sempre que o projeto é comprometido com o VCS e clonado em outro ambiente devido a essa dependência específica não estar no package.jsonarquivo ou em yarn.lock/ package-lock.json.
Luis Paulo
1
Oh! Eu entendo agora. Eu acredito que você está confundindo o NODE_PATH com PATH. PATH é onde um shell procurará executáveis. NODE_PATH é onde o nó procurará por pacotes. Ele começará examinando o diretório atual de uma node_modulespasta, então é pai, então é pai, ... até encontrar uma node_modulespasta que contenha esse módulo. No entanto, se você instalar um pacote globalmente, ele não estará em nenhuma node_modulespasta acima do diretório atual do script e, portanto, você usará NODE_PATH como um fallback em que o nó procurará pacotes.
Luis Paulo
1
ahahahah @Luis Paulo você está totalmente certo !! Eu sinto Muito! Vou tentar excluir alguns dos meus comentários para evitar confusão, bom trabalho e obrigado
Ryan Taylor
@ Ryan Taylor Você não deve excluir comentários e perguntas depois que eles forem resolvidos, porque alguém pode ter os mesmos. Agora parece que eu tive um monólogo nos comentários! ahahahah
Luis Paulo