Determinar la raíz del proyecto desde una aplicación node.js en ejecución

¿Existe una forma mejor que process.cwd() para determinar el directorio raíz de un proceso node.js en ejecución? Algo así como el equivalente a Rails.root, pero para Node.js. Busco algo que sea lo más predecible y fiable posible.

Solución

Hay varias maneras de enfocar esto, cada una con sus propios pros y contras:

require.main.filename

Desde :

Cuando un archivo se ejecuta directamente desde Node, require.main se establece en su módulo. Esto significa que puedes determinar si un archivo ha sido ejecutado directamente probando require.main === module.

Dado que module proporciona una propiedad filename (normalmente equivalente a __filename), el punto de entrada de la aplicación actual puede obtenerse comprobando require.main.filename.

Así que si quieres el directorio base de tu aplicación, puedes hacerlo:

var path = require('path');
var appDir = path.dirname(require.main.filename);

Pros & Contras

Esto funcionará muy bien la mayor parte del tiempo, pero si estás ejecutando tu aplicación con un lanzador como pm2 o ejecutando pruebas mocha, este método fallará.

global.X

Node tiene un objeto de espacio de nombres global llamado global - cualquier cosa que adjuntes a este objeto estará disponible en toda tu aplicación. Así que, en tu index.js (o app.js o como sea que se llame el archivo principal de tu aplicación), puedes definir una variable global:

// index.js
var path = require('path');
global.appRoot = path.resolve(__dirname);

// lib/moduleA/component1.js
require(appRoot + '/lib/moduleB/component2.js');

Pros & Contras

Funciona de forma consistente pero tienes que depender de una variable global, lo que significa que no puedes reutilizar fácilmente los componentes/etc.

process.cwd()

Esto devuelve el directorio de trabajo actual. No es fiable en absoluto, ya que depende totalmente del directorio desde el que se inició el proceso:

$ cd /home/demo/
$ mkdir subdir
$ echo "console.log(process.cwd());" > subdir/demo.js
$ node subdir/demo.js
/home/demo
$ cd subdir
$ node demo.js
/home/demo/subdir

app-root-path

Para solucionar este problema, he creado un módulo de nodo llamado app-root-path. Su uso es sencillo:

var appRoot = require('app-root-path');
var myModule = require(appRoot + '/lib/my-module.js');

El módulo app-root-path utiliza varias técnicas diferentes para determinar la ruta raíz de la aplicación, teniendo en cuenta los módulos instalados globalmente (por ejemplo, si tu aplicación se ejecuta en /var/www/ pero el módulo está instalado en ~/.nvm/v0.x.x/lib/node/). No funcionará el 100% de las veces, pero sí en la mayoría de los casos.

Pros & Contras

Funciona sin configuración en la mayoría de las circunstancias. También proporciona algunos buenos métodos adicionales de conveniencia (véase la página del proyecto). La mayor desventaja es que no funcionará si:

  • Usted'está utilizando un lanzador, como pm2
  • Y**, el módulo no está instalado dentro del directorio node_modules de tu aplicación (por ejemplo, si lo instalaste globalmente)

Puedes evitarlo estableciendo una variable de entorno APP_ROOT_PATH, o llamando a .setPath() en el módulo, pero en ese caso, probablemente sea mejor usar el método global.

Variable de entorno NODE_PATH

Si está buscando una forma de determinar la ruta raíz de la aplicación actual, es probable que una de las soluciones anteriores le funcione mejor. Si, por otro lado, estás tratando de resolver el problema de la carga de los módulos de la aplicación de forma fiable, te recomiendo encarecidamente que mires la variable de entorno NODE_PATH.

El [sistema de módulos] de Node (https://nodejs.org/api/modules.html) busca módulos en una variedad de ubicaciones. Uno de estos lugares es donde apunta process.env.NODE_PATH. Si establece esta variable de entorno, entonces puede requerir módulos con el cargador de módulos estándar sin ningún otro cambio.

Por ejemplo, si estableces NODE_PATH a /var/www/lib, lo siguiente funcionará bien:

require('module2/component.js');
// ^ looks for /var/www/lib/module2/component.js

Una buena manera de hacer esto es usando npm:

"scripts": {
    "start": "NODE_PATH=. node app.js"
}

Ahora puedes iniciar tu aplicación con npm start y ya estás listo. Combino esto con mi módulo enforce-node-path, que previene la carga accidental de la aplicación sin el NODE_PATH establecido. Para un mayor control sobre la aplicación de las variables de entorno, vea checkenv.

Un inconveniente: NODE_PATH debe establecerse fuera de la aplicación node. No puedes hacer algo como process.env.NODE_PATH = path.resolve(__dirname) porque el cargador de módulos almacena en caché la lista de directorios que buscará antes de que tu aplicación se ejecute.

[añadido el 4/6/16] Otro módulo realmente prometedor que intenta resolver este problema es wavy.

Comentarios (22)

__dirname no es global; es local para el módulo actual, por lo que cada archivo tiene su propio valor local y diferente.

Si quiere el directorio raíz del proceso en ejecución, probablemente quiera usar process.cwd().

Si quieres predictibilidad y fiabilidad, entonces probablemente necesites hacer un requisito de tu aplicación que una determinada variable de entorno esté establecida. Tu aplicación busca MY_APP_HOME (o lo que sea) y si está ahí, y la aplicación existe en ese directorio entonces todo está bien. Si no está definida o el directorio no contiene su aplicación, entonces debería salir con un error pidiendo al usuario que cree la variable. Se puede establecer como parte de un proceso de instalación.

Puedes leer las variables de entorno en node con algo como process.env.MY_ENV_VARIABLE.

Comentarios (2)

1- crea un archivo en la raíz del proyecto llámalo settings.js

2- dentro de este archivo añade este código

module.exports = {
    POST_MAX_SIZE : 40 , //MB
    UPLOAD_MAX_FILE_SIZE: 40, //MB
    PROJECT_DIR : __dirname
};

3- dentro de node_modules crea un nuevo módulo llámalo "settings" y dentro del módulo index.js escribe este código:

module.exports = require("../../settings");

4- y cada vez que quieras el directorio de tu proyecto solo usa

var settings = require("settings");
settings.PROJECT_DIR; 

de esta forma tendrás todos los directorios del proyecto relativos a este archivo ;)

Comentarios (9)