NodeJS requiere un módulo / paquete global

160

Estoy tratando de instalar globalmente y luego usar forevery forever-monitorasí:

npm install -g forever forever-monitor

Veo el resultado habitual y también las operaciones que copian los archivos a la ruta global, pero luego, si lo intento require("forever");, aparece un error que dice que no se encontró el módulo.

Estoy usando la última versión tanto de nodo como de npm y ya sé sobre el cambio que hizo npm en la instalación global versus local, pero realmente no quiero instalarlo localmente en cada proyecto y estoy trabajando en una plataforma que no No lo soporto, linkasí que npm linkdespués de una instalación global no es posible para mí.

Mi pregunta es: ¿por qué no puedo requerir un paquete instalado globalmente? ¿Es eso una característica o un error? ¿O estoy haciendo algo mal?

PD: Solo para que quede claro como el cristal: no quiero instalarlo localmente.

alexandernst
fuente

Respuestas:

216

En Node.js, require no busca en la carpeta donde están instalados los módulos globales.

Puede solucionar esto configurando la variable de entorno NODE_PATH. En Linux esto será:

export NODE_PATH=/usr/lib/node_modules

Nota: Esto depende de dónde estén realmente instalados sus módulos globales.

Ver: Cargando desde las carpetas globales .

Daniel Uzunu
fuente
24
En mi máquina Ubuntu 13.10, la ruta global para los módulos es diferente de la que muestra aquí. Tuve que usar export NODE_PATH=/usr/local/lib/node_modulesen su lugar.
Drew Noakes
11
Si tiene Windows 7/8 y no ha anulado ninguno de los valores predeterminados de instalación de Node, es probable que la configuración de la NODE_PATHvariable de entorno C:\Users\{USERNAME}\AppData\Roaming\npm\node_modulesfuncione.
Wes Johnson
55
@WesJohnson solo %AppData%\npm\node_modulesfuncionará en Windows 10.
theblang
66
Si configuro, NODE_PATH¿puedo usar módulos globales y locales simultáneamente?
Paulo Oliveira
66
Alternativamente, en lugar de una ruta estática, es decir, si está utilizando NVM:NODE_PATH=$(npm root -g)
holmberd
98

Después de instalar el paquete globalmente, debe vincular el proyecto local con el paquete global

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

Ver aquí

usuario568109
fuente
2
Estoy ejecutando en una plataforma que no admite enlaces (como dice mi pregunta) blog.nodejs.org/2011/04/06/npm-1-0-link
alexandernst
1
¿Qué plataforma estás usando?
user568109
1
Realmente no quiero meterme con enlaces (ni enlaces simbólicos). Solo quiero instalar paquetes globalmente y los necesito. Sé que NPM fue rediseñado para evitar esto, pero ¿qué tan difícil podría ser lograr algo como esto?
alexandernst
13
¿Qué pasa si no tengo un proyecto? Decir ~/some-stand-alone-random-nodejs-test.js. No quiero convertir mi carpeta de inicio en un directorio de proyecto. No quiero crear nuevas carpetas para cada pequeño experimento.
AnnanFay
1
Funcionó perfecto en Windows 8.1. Desde la línea de comando de nodo cd hasta la carpeta local node_modules de mis proyectos y luego ejecutada npm link <module>Luego verá un acceso directo (enlace) creado en la carpeta node_module de sus proyectos que hace referencia al módulo de nodo global.
dynamiclynk
26

Disculpas por la nigromancia, pero puedo especificar rutas codificadas a los módulos instalados globalmente:

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

Esto no es perfecto, pero teniendo en cuenta que Unity3d intenta "compilar" todos los javascript incluidos en el directorio del proyecto, realmente no puedo instalar ningún paquete.

Thomas Ingham
fuente
44
Unity3D no es compatible con JavaScript. Admite una sintaxis similar a JS para su intérprete / compilador Boo (Boo es un lenguaje similar a Python para .NET) que se comercializa engañosamente como "JavaScript" . El nombre más preciso para el idioma que admite Unity es UnityScript . Debido a que ni siquiera está cerca del mismo idioma, casi ninguno de los JS escritos para la web o para Node.js funcionará en Unity. Mucha más información sobre las diferencias en la wiki oficial de Unity: wiki.unity3d.com/index.php/UnityScript_versus_JavaScript
Slipp D. Thompson
19

Sé que esta es una vieja pregunta, pero me encontré con esto cuando intentaba hacer una verificación de versión usando semverun preinstallscript package.json. Como sabía que no podía depender de ningún módulo local instalado, utilicé esto para solicitarlo semverdesde la node_modulescarpeta global (ya que npmdepende de él, sé que está allí):

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

Me gusta este enfoque porque no requiere la instalación de ningún módulo especial para poder usarlo.

No elegí una NODE_PATHsolución como han sugerido otros, ya que quería que esto funcione en la máquina de cualquier persona, sin tener que requerir una configuración / configuración adicional antes de ejecutar npm installmi proyecto.

De la forma en que esto está codificado, solo está garantizado encontrar módulos de nivel superior (instalados usando npm install -g ...) o módulos requeridos por npm(enumerados dependenciesaquí: https://github.com/npm/npm/blob/master/package.json ). Si está utilizando una versión más nueva de NPM, puede encontrar dependencias de otros paquetes instalados globalmente, ya que ahora hay una estructura más plana para las node_modulescarpetas.

Espero que sea útil para alguien.

Joe Skeen
fuente
19

Según la documentación , Node.js buscará en las siguientes ubicaciones de forma predeterminada:

  1. Ruta especificada en la NODE_PATHvariable de entorno .

    Nota: NODE_PATHla variable de entorno se establece en una lista delimitada por dos puntos de rutas absolutas.

  2. node_modulesCarpeta actual (local)

  3. $HOME/.node_modules (global)

    Nota: $HOMEes el directorio de inicio del usuario.

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

    Nota: $PREFIXNode.js está configurado node_prefix.

    Para verificar el valor actual de node_prefix, ejecute:

    node -p process.config.variables.node_prefix

    Nota: El prefijo corresponde a --prefixparam durante la compilación y es relativo a process.execPath. No confundir con el valor del npm config get prefixcomando. fuente

Si no se puede encontrar el módulo dado, eso significa que no está presente en una de las ubicaciones anteriores.

La ubicación de la carpeta raíz global donde se instalan los módulos se puede imprimir mediante: npm root -g(de forma predeterminada, la ruta se calcula en tiempo de ejecución a menos que se anule en el npmrcarchivo ).

Solución

Puede probar las siguientes soluciones alternativas:

  • Especifique la ubicación de su módulo global en NODE_PATHla variable de entorno. P.ej

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

    Para probar e imprimir el valor de NODE_PATH, ejecute:

    echo 'console.log(process.env.NODE_PATH); require("forever")' | NODE_PATH="$(npm root -g):$NODE_PATH" node 
  • Para una solución más permanente, vincule su $HOME/.node_modulescarpeta de usuario global para apuntar a la carpeta raíz, ejecutando este comando:

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

    Luego vuelva a probarlo mediante: echo 'require("forever")' | nodecomando.

  • Cambie temporalmente la carpeta actual a donde la extensión se ha instalado globalmente, antes de invocar el script. P.ej

    npm install -g forever
    cd "$(npm root -g)"
    echo 'require("forever")' | node
    cd -
  • Configure el destino de instalación global en el npmarchivo userconfig (consulte :)npm help 5 npmrc o por userconfigparam ( --prefix).

    Para mostrar la configuración actual, ejecute: npm config list.

    Para editar la configuración actual, ejecute: npm config edit.

  • Especifique la ruta completa de la ubicación de los módulos de nodo al llamar require(). P.ej

    require("/path/to/sub/module")
  • Instale el paquete en una ubicación personalizada, p. Ej.

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

    Sin embargo, la instalación continuará ~/.node_modules/lib/node_modules/, por lo que la ubicación aún debe agregarse.

    Consulte: paquete de instalación local de npm en una ubicación personalizada

  • Cree un enlace simbólico en la carpeta actual desde la ubicación del paquete global. P.ej

    npm link forever
kenorb
fuente
Parece 4. Carpeta actual node_modules. (local) tiene prioridad sobre 3. $ PREFIX / lib / node (global)
Király István el
¡Las carpetas locales node_modules siempre tienen prioridad sobre las carpetas globales!
Király István
14

Puede usar el paquete requiregpara resolver este problema:

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

Hará el truco.

Además, hay otro módulo, global-npmaunque específico para usar solo el global npm, puede mirar el código corto y ver cómo funciona la técnica.

JP Richardson
fuente
interesante, pero el método NODE_PATH es probablemente más canónico
Alexander Mills
Lo NODE_PATHbueno de esto es que no necesitas cambiar ningún código. (mi caso de uso está calificando muchos proyectos de estudiantes, donde no quiero ejecutar npm installpara cada uno, y tampoco quiero que proporcionen un node_modulesdirectorio).
amenthes
No, no funcionará porque no se puede exigir requiregen primer lugar, este es el punto.
thisismydesign
6

Para las utilidades de CLI que dependen de módulos grandes, como puppeteer, me gusta generar ay npm root -gusarlo para requerir el 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
fuente
3

Puede poner esta línea en su .profilearchivo:

export NODE_PATH = "$ (npm config get prefix) / lib / node_modules"

Esto hará nodeuso de la ruta global.

Luis Paulo
fuente
1
No. Esta es la forma genérica de obtener lo global node_modules. Esta es una vieja respuesta, pero recuerdo que la obtuve de alguna parte de la documentación. De todos modos, en mi computadora (en 2020) el node_modulesdirectorio global npm es usr/lib/node_modules. De todos modos, confío npm config get prefixporque npm lo usa globalmente cada vez que se instala un paquete global, por lo que debería ser correcto.
Luis Paulo
1
De cualquier manera (no dije esto en mi respuesta inicial porque no tenía mucha experiencia en Node.JS), el uso de paquetes instalados globalmente en un programa es un caso de uso marginal y rara vez se debe hacer porque en un proyecto creará problemas cada vez que el proyecto se compromete con VCS y se clona en otro entorno debido a que esa dependencia específica no está en el package.jsonarchivo o en yarn.lock/ package-lock.json.
Luis Paulo
1
Oh! Entiendo ahora. Creo que estás confundiendo NODE_PATH con PATH. RUTA es donde un shell buscará ejecutables. NODE_PATH es donde el nodo buscará paquetes. Comenzará buscando en el directorio actual una node_modulescarpeta, luego es padre, luego es padre, ... hasta que encuentre una node_modulescarpeta que contenga ese módulo. Sin embargo, si instala un paquete globalmente, no estará dentro de ninguna node_modulescarpeta sobre el directorio actual del script, por lo que usará NODE_PATH como alternativa donde el nodo buscará paquetes.
Luis Paulo
1
ahahahah @Luis Paulo tienes toda la razón !! ¡Lo siento! Intentaré eliminar algunos de mis comentarios para evitar confusiones, buen trabajo y gracias
Ryan Taylor
@ Ryan Taylor No debes eliminar los comentarios y las preguntas una vez que se resuelvan porque alguien más podría tener las mismas. ¡Ahora parece que tuve un monólogo en los comentarios! ahahahah
Luis Paulo