Determine project root from a running node.js application
Is there a different way, other than process.cwd()
, to get the pathname of the current project's root-directory. Does Node implement something like ruby's property, Rails.root
,. I'm looking for something that is constant, and reliable.
Solution 1:
There are many ways to approach this, each with their own pros and cons:
require.main.filename
From http://nodejs.org/api/modules.html:
When a file is run directly from Node,
require.main
is set to itsmodule
. That means that you can determine whether a file has been run directly by testingrequire.main === module
Because
module
provides afilename
property (normally equivalent to__filename
), the entry point of the current application can be obtained by checkingrequire.main.filename
.
So if you want the base directory for your app, you can do:
const { dirname } = require('path');
const appDir = dirname(require.main.filename);
Pros & Cons
This will work great most of the time, but if you're running your app with a launcher like pm2 or running mocha tests, this method will fail.
module.paths
Node publishes all the module search paths to module.paths
. We can traverse these and pick the first one that resolves.
async function getAppPath() {
const { dirname } = require('path');
const { constants, promises: { access } } = require('fs');
for (let path of module.paths) {
try {
await access(path, constants.F_OK);
return dirname(path);
} catch (e) {
// Just move on to next path
}
}
}
Pros & Cons
This will sometimes work, but is not reliable when used in a package because it may return the directory that the package is installed in rather than the directory that the application is installed in.
Using a global variable
Node has a global namespace object called global
— anything that you attach to this object will be available everywhere in your app. So, in your index.js
(or app.js
or whatever your main app
file is named), you can just define a global variable:
// index.js
var path = require('path');
global.appRoot = path.resolve(__dirname);
// lib/moduleA/component1.js
require(appRoot + '/lib/moduleB/component2.js');
Pros & Cons
Works consistently, but you have to rely on a global variable, which means that you can't easily reuse components/etc.
process.cwd()
This returns the current working directory. Not reliable at all, as it's entirely dependent on what directory the process was launched from:
$ 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
To address this issue, I've created a node module called app-root-path. Usage is simple:
const appRoot = require('app-root-path');
const myModule = require(`${ appRoot }/lib/my-module.js`);
The app-root-path module uses several techniques to determine the root path of the app, taking into account globally installed modules (for example, if your app is running in /var/www/
but the module is installed in ~/.nvm/v0.x.x/lib/node/
). It won't work 100% of the time, but it's going to work in most common scenarios.
Pros & Cons
Works without configuration in most circumstances. Also provides some nice additional convenience methods (see project page). The biggest con is that it won't work if:
- You're using a launcher, like pm2
-
AND, the module isn't installed inside your app's
node_modules
directory (for example, if you installed it globally)
You can get around this by either setting a APP_ROOT_PATH
environmental variable, or by calling .setPath()
on the module, but in that case, you're probably better off using the global
method.
NODE_PATH environmental variable
If you're looking for a way to determine the root path of the current app, one of the above solutions is likely to work best for you. If, on the other hand, you're trying to solve the problem of loading app modules reliably, I highly recommend looking into the NODE_PATH
environmental variable.
Node's Modules system looks for modules in a variety of locations. One of these locations is wherever process.env.NODE_PATH
points. If you set this environmental variable, then you can require
modules with the standard module loader without any other changes.
For example, if you set NODE_PATH
to /var/www/lib
, the the following would work just fine:
require('module2/component.js');
// ^ looks for /var/www/lib/module2/component.js
A great way to do this is using npm
:
{
"scripts": {
"start": "NODE_PATH=. node app.js"
}
}
Now you can start your app with npm start
and you're golden. I combine this with my enforce-node-path module, which prevents accidentally loading the app without NODE_PATH
set. For even more control over enforcing environmental variables, see checkenv.
One gotcha: NODE_PATH
must be set outside of the node app. You cannot do something like process.env.NODE_PATH = path.resolve(__dirname)
because the module loader caches the list of directories it will search before your app runs.
[added 4/6/16] Another really promising module that attempts to solve this problem is wavy.
Solution 2:
__dirname
isn't a global; it's local to the current module so each file has its own local, different value.
If you want the root directory of the running process, you probably do want to use process.cwd()
.
If you want predictability and reliability, then you probably need to make it a requirement of your application that a certain environment variable is set. Your app looks for MY_APP_HOME
(Or whatever) and if it's there, and the application exists in that directory then all is well. If it is undefined or the directory doesn't contain your application then it should exit with an error prompting the user to create the variable. It could be set as a part of an install process.
You can read environment variables in node with something like process.env.MY_ENV_VARIABLE
.
Solution 3:
1- create a file in the project root call it settings.js
2- inside this file add this code
module.exports = {
POST_MAX_SIZE : 40 , //MB
UPLOAD_MAX_FILE_SIZE: 40, //MB
PROJECT_DIR : __dirname
};
3- inside node_modules create a new module name it "settings" and inside the module index.js write this code:
module.exports = require("../../settings");
4- and any time you want your project directory just use
var settings = require("settings");
settings.PROJECT_DIR;
in this way you will have all project directories relative to this file ;)
Solution 4:
Simple:
require('path').resolve('./')
Solution 5:
the easiest way to get the global root (assuming you use NPM to run your node.js app 'npm start', etc)
var appRoot = process.env.PWD;
If you want to cross-verify the above
Say you want to cross-check process.env.PWD
with the settings of you node.js application. if you want some runtime tests to check the validity of process.env.PWD
, you can cross-check it with this code (that I wrote which seems to work well). You can cross-check the name of the last folder in appRoot with the npm_package_name in your package.json file, for example:
var path = require('path');
var globalRoot = __dirname; //(you may have to do some substring processing if the first script you run is not in the project root, since __dirname refers to the directory that the file is in for which __dirname is called in.)
//compare the last directory in the globalRoot path to the name of the project in your package.json file
var folders = globalRoot.split(path.sep);
var packageName = folders[folders.length-1];
var pwd = process.env.PWD;
var npmPackageName = process.env.npm_package_name;
if(packageName !== npmPackageName){
throw new Error('Failed check for runtime string equality between globalRoot-bottommost directory and npm_package_name.');
}
if(globalRoot !== pwd){
throw new Error('Failed check for runtime string equality between globalRoot and process.env.PWD.');
}
you can also use this NPM module: require('app-root-path')
which works very well for this purpose