Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
508 views
in Technique[技术] by (71.8m points)

working directory - 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.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Understanding the root directory as the one containing the package.json file, and that's basically the directory containing the nearest node_modules folder, there is exactly one way "to get the pathname of the current project's root-directory":

async function getPkgJsonDir () {
    const { dirname } = require ( 'path' );
    const { constants, promises: { access } } = require ( 'fs' );

    for ( let path of module.paths ) {
        try {
            let prospectivePkgJsonDir = dirname ( path );
            await access ( path, constants.F_OK );
            return prospectivePkgJsonDir;
        } catch ( e ) {}
    }
    
}

( async () => {
    const pkgJsonDir = await getPkgJsonDir ();
} )();

This works because module.paths contains the node_modules search paths up to the root directory /. So, starting from the 0 index of the module.paths search list, the first existing directory we encounter is the desired target.

Legacy answer follows below. Rather than provide a new answer, it made sense to improve the existing and accepted answer since it already had been considerably upvoted.

There are several 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 its module. That means that you can determine whether a file has been run directly by testing require.main === module

Because module provides a filename property (normally equivalent to __filename), the entry point of the current application can be obtained by checking require.main.filename.

So if you want the base directory for your app, you can do:

var path = require('path');
var appDir = path.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.

global.X

Node has a 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:

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

The app-root-path module uses several different 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...