从正在运行的node.js应用程序确定项目根目录

Determine project root from a running node.js application

是否有比process.cwd()更好的方法来确定正在运行的node.js进程的根目录? 类似于Rails.root的东西,但是对于Node.js. 我正在寻找尽可能可预测和可靠的东西。


有几种方法可以解决这个问题,每种方法各有利弊:

require.main.filename

来自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.

因此,如果您想要应用程序的基本目录,您可以执行以下操作:

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

优点缺点

这在大多数情况下都会很好用,但如果您使用像pm2这样的启动器运行应用程序或运行mocha测试,则此方法将失败。

global.X

Node有一个名为global的全局命名空间对象 - 您附加到此对象的任何内容都将在您的应用程序中随处可用。因此,在index.js(或app.js或您的主应用程序文件的任何名称)中,您只需定义一个全局变量:

1
2
3
4
5
6
// index.js
var path = require('path');
global.appRoot = path.resolve(__dirname);

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

优点缺点

一致地工作,但你必须依赖一个全局变量,这意味着你不能轻易地重用组件/等。

process.cwd()

这将返回当前工作目录。根本不可靠,因为它完全取决于启动进程的目录:

1
2
3
4
5
6
7
8
$ 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的节点模块。用法很简单:

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

app-root-path模块使用几种不同的技术来确定应用程序的根路径,同时考虑全局安装的模块(例如,如果您的应用程序在/var/www/中运行但模块安装在~/.nvm/v0.x.x/lib/node/中)。它不会100%的工作时间,但它可以在大多数常见的情况下工作。

优点缺点

大多数情况下无需配置即可工作还提供了一些不错的额外便利方法(参见项目页面)。最大的问题是,如果:

  • 你正在使用像pm2这样的启动器
  • 并且,模块未安装在应用程序的node_modules目录中(例如,如果您全局安装)

您可以通过设置APP_ROOT_PATH环境变量或在模块上调用.setPath()来解决这个问题,但在这种情况下,最好使用global方法。

NODE_PATH环境变量

如果您正在寻找确定当前应用程序根路径的方法,则上述解决方案之一可能最适合您。另一方面,如果您正在尝试可靠地解决加载应用模块的问题,我强烈建议您查看NODE_PATH环境变量。

Node的模块系统在各种位置查找模块。其中一个位置是process.env.NODE_PATH点。如果设置此环境变量,则可以使用标准模块加载器require模块而无需任何其他更改。

例如,如果将NODE_PATH设置为/var/www/lib,则以下方法可以正常工作:

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

一个很好的方法是使用npm

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

现在你可以用npm start启动你的应用程序,你就是金色的。我将它与我的enforce-node-path模块结合使用,这可以防止在没有NODE_PATH设置的情况下意外加载应用程序。有关强制执行环境变量的更多控制,请参阅checkenv。

一个问题:NODE_PATH必须在节点应用程序之外设置。您无法执行类似process.env.NODE_PATH = path.resolve(__dirname)的操作,因为模块加载程序会缓存在应用程序运行之前将搜索的目录列表。

[16/6/16添加]试图解决这个问题的另一个非常有前途的模块是波浪状的。


__dirname不是全球性的;它是当前模块的本地模块,因此每个文件都有自己的本地不同值。

如果您想要正在运行的进程的根目录,则可能需要使用process.cwd()

如果您想要可预测性和可靠性,那么您可能需要将应用程序的要求设置为某个环境变量。你的应用程序寻找MY_APP_HOME(或者其他什么),如果它在那里,并且应用程序存在于该目录中,那么一切都很好。如果未定义或目录不包含您的应用程序,则应退出并显示错误提示用户创建变量。它可以设置为安装过程的一部分。

您可以使用类似process.env.MY_ENV_VARIABLE的内容读取节点中的环境变量。


1-在项目根目录中创建一个文件,调用它settings.js

2-在此文件中添加此代码

1
2
3
4
5
module.exports = {
    POST_MAX_SIZE : 40 , //MB
    UPLOAD_MAX_FILE_SIZE: 40, //MB
    PROJECT_DIR : __dirname
};

3-在node_modules里面创建一个新的模块名称"settings"并在模块index.js里面写下这段代码:

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

4-以及您希望项目目录使用的任何时间

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

通过这种方式,您将拥有与此文件相关的所有项目目录;)


获取全局根的最简单方法(假设你使用NPM运行你的node.js app'npm start'等)

1
var appRoot = process.env.PWD;

如果你想交叉验证上面的内容

假设您要使用node.js应用程序的设置交叉检查process.env.PWD。如果你想要一些运行时测试来检查process.env.PWD的有效性,你可以用这个代码交叉检查它(我写的似乎运行良好)。您可以使用package.json文件中的npm_package_name来交叉检查appRoot中最后一个文件夹的名称,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    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.');
    }

你也可以使用这个NPM模块:require('app-root-path'),它可以很好地用于此目的


我发现这对我来说一直都有效,即使从子文件夹调用应用程序,因为它可以与一些测试框架一样,如Mocha:

1
process.mainModule.paths[0].split('node_modules')[0].slice(0, -1);

为什么会这样:

在运行时节点创建所有已加载文件的完整路径的注册表。模块首先加载,因此位于此注册表的顶部。通过选择注册表的第一个元素并在'node_modules'目录之前返回路径,我们可以确定应用程序的根目录。

它只是一行代码,但为了简单起见(我的缘故),我把它装入一个NPM模块:

https://www.npmjs.com/package/node-root.pddivine

请享用!


所有这些"根目录"主要需要解决一些虚拟路径到一个真正的桩路径,所以你应该看看path.resolve

1
2
var path= require('path');
var filePath = path.resolve('our/virtual/path.ext");

也许您可以尝试从__filename向上遍历,直到找到package.json,并确定它是您当前文件所属的主目录。


实际上,我发现也许最简单的解决方案也是最强大的:
您只需将以下文件放在项目的根目录:root-path.js,其中包含以下代码:

1
2
3
import * as path from 'path'
const projectRootPath = path.resolve(__dirname)
export const rootPath = projectRootPath

我在使用express时发现有用的技术是在设置任何其他路由之前将以下内容添加到app.js

1
2
3
4
5
6
7
// set rootPath
app.use(function(req, res, next) {
  req.rootPath = __dirname;
  next();
});

app.use('/myroute', myRoute);

无需使用全局变量,并且您将根目录的路径作为请求对象的属性。

如果您的app.js位于项目的根目录中,默认情况下,这是有效的。


我知道这个已经太晚了。
但我们可以通过两种方法获取根URL

第一种方法

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

第二种方法

1
2
var path = require('path');
path.dirname(process.mainModule.filename);

参考链接: - https://gist.github.com/geekiam/e2e3e0325abd9023d3a3


让它变得性感????

1
2
3
4
const users = require('../../../database/users'); // ?? what you have
// OR
const users = require('$db/users'); // ?? no matter how deep you are
const products = require('/database/products'); // ?? alias or pathing from root directory


解决丑陋道路问题的三个简单步骤。

  • 安装包:npm install sexy-require --save
  • 在主应用程序文件的顶部包含一次require('sexy-require')

    1
    2
    3
    4
    require('sexy-require');
    const routers = require('/routers');
    const api = require('$api');
    ...
  • 可选步骤。路径配置可以在项目根目录的.paths文件中定义。

    1
    2
    3
    $db = /server/database
    $api-v1 = /server/api/legacy
    $api-v2 = /server/api/v2

  • 在主文件顶部添加:

    1
    mainDir = __dirname;

    然后在您需要的任何文件中使用它:

    1
    console.log('mainDir ' + mainDir);
    • mainDir是全局定义的,如果只在当前文件中需要它 - 请改用__dirname
    • 主文件通常位于项目的根文件夹中,并命名为main.jsindex.jsgulpfile.js

    将其添加到主应用程序文件的开头(例如app.js):

    1
    global.__basedir = __dirname;

    这将设置一个全局变量,该变量将始终等于应用程序的基础目录。像任何其他变量一样使用它:

    1
    const yourModule = require(__basedir + '/path/to/module.js');

    简单...


    就像在root中将此行添加到模块一样简单,通常是app.js

    1
    global.__basedir = __dirname;

    然后_basedir将可以访问您的所有模块。


    我用这个。

    对于名为mymodule的模块

    var BASE_DIR = __dirname.replace(/^(.*\/mymodule)(.*)$/, '$1')


    1
    2
    3
    process.mainModule.paths
      .filter(p => !p.includes('node_modules'))
      .shift()

    获取主模块中的所有路径并过滤掉那些带有"node_modules"的路径,
    然后获得剩余路径列表的第一个。意外行为不会抛出错误,只是undefined

    对我来说效果很好,即使在调用ie $ mocha时也是如此。


    您只需在express app变量中添加根目录路径,然后从应用程序中获取此路径。为此,在index.js或app.js文件中添加app.set('rootDirectory', __dirname);。并使用req.app.get('rootDirectory')获取代码中的根目录路径。


    老问题,我知道,然而毫无疑问提到使用progress.argv。 argv数组包含一个完整的路径名和文件名(带或不带.js扩展名),用作节点执行的参数。因为这也可以包含标志,所以必须对其进行过滤。

    这不是一个你可以直接使用的例子(因为使用我自己的框架),但我认为它可以让你知道如何做到这一点。我还使用缓存方法来避免调用此函数对系统造成太大压力,尤其是在未指定扩展时(并且需要检查文件),例如:

    1
    node myfile

    要么

    1
    node myfile.js

    这就是我缓存它的原因,另请参阅下面的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    function getRootFilePath()
    {
            if( !isDefined( oData.SU_ROOT_FILE_PATH ) )
            {
                var sExt = false;

                each( process.argv, function( i, v )
                {
                     // Skip invalid and provided command line options
                    if( !!v && isValidString( v ) && v[0] !== '-' )
                    {
                        sExt = getFileExt( v );

                        if( ( sExt === 'js' ) || ( sExt === '' && fileExists( v+'.js' )) )
                        {

                            var a = uniformPath( v ).split("/");

                             // Chop off last string, filename
                            a[a.length-1]='';

                             // Cache it so we don't have to do it again.
                            oData.SU_ROOT_FILE_PATH=a.join("/");

                             // Found, skip loop
                            return true;
                        }
                    }
                }, true ); // <-- true is: each in reverse order
            }

            return oData.SU_ROOT_FILE_PATH || '';
        }
    };

    在app.js中创建一个函数

    /*Function to get the app root folder*/

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var appRootFolder = function(dir,level){
        var arr = dir.split('\');
        arr.splice(arr.length - level,level);
        var rootFolder = arr.join('\');
        return rootFolder;
    }

    // view engine setup
    app.set('views', path.join(appRootFolder(__dirname,1),'views'));


    找到电子应用程序的根路径可能会变得棘手。因为主进程和渲染器的根路径在不同的条件下是不同的,例如生产,开发和打包条件。

    我编写了一个npm包电子根路径来捕获电子应用程序的根路径。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $ npm install electron-root-path

    or

    $ yarn add electron-root-path


    // Import ES6 way
    import { rootPath } from 'electron-root-path';

    // Import ES2015 way
    const rootPath = require('electron-root-path').rootPath;

    // e.g:
    // read a file in the root
    const location = path.join(rootPath, 'package.json');
    const pkgInfo = fs.readFileSync(location, { encoding: 'utf8' });

    试试path._makeLong('some_filename_on_root.js');

    例:

    1
    2
    cons path = require('path');
    console.log(path._makeLong('some_filename_on_root.js');

    这将从您的节点应用程序的根返回完整路径(package.json的相同位置)


    只需使用:

    1
     path.resolve("./") ... output is your project root directory


    __dirname将为您提供根目录,只要您位于根目录中的文件中即可。

    1
    2
    3
    4
    5
    6
    7
    // ProjectDirectory.js (this file is in the project's root directory because you are putting it there).
    module.exports = {

        root() {
            return __dirname;
        }
    }

    在其他一些文件中:

    1
    2
    const ProjectDirectory = require('path/to/ProjectDirectory');
    console.log(`Project root directory is ${ProjectDirectory.root}`);