除了process.cwd()之外,是否有其他方法来获取当前项目根目录的路径名。Node是否实现了ruby的属性Rails.root之类的东西。我要找的是稳定可靠的东西。


当前回答

path.dirname(process.mainModule.filename);

其他回答

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

这不是一个你可以直接使用的例子(因为使用我自己的框架),但我认为它给了你一些想法如何做到这一点。我还使用缓存方法来避免调用这个函数对系统造成太大的压力,特别是在没有指定扩展名(并且需要文件存在检查)的情况下,例如:

node myfile

or

node myfile.js

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


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

解决这个问题有很多种方法,每种方法都有其优缺点:

require.main.filename

从http://nodejs.org/api/modules.html:

当一个文件直接从Node运行时,require。Main被设置为它的模块。这意味着您可以通过测试require来确定文件是否已经直接运行。Main ===模块 因为module提供了一个filename属性(通常等价于__filename),当前应用程序的入口点可以通过检查require.main.filename来获得。

如果你想要应用的基目录,你可以这样做:

const { dirname } = require('path');
const appDir = dirname(require.main.filename);

优点与缺点

这种方法在大多数情况下都很有效,但如果你使用pm2之类的启动器或运行mocha测试,这种方法就会失败。这在使用Node.js ES模块时也不起作用。Main不可用。

module.paths

Node将所有模块搜索路径发布到module.paths。我们可以遍历这些,选择第一个解出来的。

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

优点与缺点

这有时可以工作,但在包中使用时不可靠,因为它可能返回包安装所在的目录,而不是应用程序安装所在的目录。

使用全局变量

Node有一个全局命名空间对象,叫做global -你附加到这个对象上的任何东西都将在你的应用程序中的任何地方可用 ),你可以只定义一个全局变量:

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

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

优点与缺点

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

process.cwd ()

这将返回当前工作目录。完全不可靠,因为它完全依赖于进程从哪个目录启动:

$ 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

为了解决这个问题,我创建了一个名为app-root-path的节点模块。用法很简单:

const appRoot = require('app-root-path');
const myModule = require(`${ appRoot }/lib/my-module.js`);

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

优点与缺点

在大多数情况下不需要配置即可工作。还提供了一些很好的额外方便的方法(参见项目页面)。最大的骗局是,如果出现以下情况,它就不会起作用:

你用的是启动器,比如pm2 并且,该模块没有安装在你的应用程序的node_modules目录中(例如,如果你全局安装它)

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

NODE_PATH环境变量

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

Node的Modules系统在不同的位置寻找模块。其中一个位置是process.env. exe。NODE_PATH点。如果您设置了这个环境变量,那么您就可以使用标准模块加载器要求模块,而不需要进行任何其他更改。

例如,如果你将NODE_PATH设置为/var/www/lib,下面的代码就可以正常工作:

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

一个很好的方法是使用npm:

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

现在你可以用npm start启动你的应用程序,你就成功了。我将其与我的强制节点路径模块结合起来,这可以防止在没有设置NODE_PATH的情况下意外加载应用程序。更多关于环境变量的控制,请参见checkenv。

有一个问题:NODE_PATH必须在节点应用程序之外设置。你不能做process.env之类的事情。NODE_PATH = path.resolve(__dirname),因为模块加载器在应用程序运行之前缓存了它要搜索的目录列表。

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

在npm的现代版本中,你可以在exports中添加一个条目,用作速记。注意,如果你想同时引用根目录本身和根目录下的文件,你需要分别使用./和./*:

package.json:

{
  "imports": {
    "#root": "./",
    "#root/*": "./*",
    ...
  },
  ...
}

/ index.js:

import {namedExport} from '#root/file.js'

/ file.js:

export const namedExport = {
  hi: "world",
};

然后:

$ node --experimental-specifier-resolution=node index.js

你可以用constants.js文件进一步扩展它,在那里你可以使用上面答案中的一个方法,或者输入一个绝对路径,如果你需要路径本身的话

如果你想从一个正在运行的node.js应用程序中确定项目根,你也可以简单地这样做。

process.mainModule.path

只使用:

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