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

评论

您是否有可能不接受已接受的,错误的答案?

尝试process.env.PWD ...请参阅下面的答案。

#1 楼

有几种方法可以解决此问题,每种方法各有利弊:
require.main.filename
来自http://nodejs.org/api/modules.html:

直接从Node运行文件时,require.main设置为其module。这意味着您可以通过测试require.main === module来确定文件是否已经直接运行
因为module提供了filename属性(通常等效于__filename),所以可以通过检查require.main.filename来获取当前应用程序的入口点。 >
因此,如果您想要应用程序的基本目录,则可以执行以下操作:
 var path = require('path');
var appDir = path.dirname(require.main.filename);
 
这在大多数情况下都有效,但是如果您使用pm2之类的启动器运行应用程序或运行mocha测试,则此方法将失败。
global.X
Node具有全局名为global的命名空间对象-您附加到此对象的任何内容都将在您的应用程序中随处可见。因此,在您的index.js(或app.js或您的主应用程序文件中的任何名称)中,您只需定义一个全局变量即可:
 // 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的节点模块。用法很简单:
 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's Modules系统在各个位置查找模块。这些位置之一是process.env.NODE_PATH指向的任何位置。如果设置了此环境变量,则可以使用标准模块加载器来对require模块进行任何其他更改。
例如,如果将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来启动您的应用程序,您就很成功了。我将其与我的force-node-path模块结合使用,这可以防止在未设置NODE_PATH的情况下意外加载应用程序。要对执行环境变量进行更多控制,请参见checkenv。
一个陷阱:必须在节点应用程序外部设置NODE_PATH。您无法执行process.env.NODE_PATH = path.resolve(__dirname)之类的操作,因为模块加载程序会在应用程序运行之前缓存它将搜索的目录列表。
[添加4/6/16]尝试解决此问题的另一个非常有前途的模块是波浪形的。 />

评论


@Kevin在这种情况下,mocha是应用程序的入口。这只是为什么很难找到“项目根目录”的一个例子-它在很大程度上取决于情况和“项目根目录”的含义。

– inxilpro
2014年4月28日在19:37

@凯文我完全理解。我的意思是,“项目根”的概念比计算机更容易让人理解。如果您想要一个简单的方法,则需要对其进行配置。使用require.main.filename在大多数情况下都会起作用,但并非总是如此。

– inxilpro
2014年4月29日在18:08



切线相关:这是组织Node项目的一种非常聪明的方法,这样您就不必担心这个问题了:allanhortle.com/2015/02/04/…

– inxilpro
2015年2月5日在2:11

我不知道pm2是否发生变化或Node.js是否发生变化,但require.main.filename似乎可以与pm2一起工作。不知道摩卡咖啡。

–贾斯汀·沃肯汀(Justin Warkentin)
16年5月6日在14:01

path.parse(process.mainModule.filename).dir

–科里·罗宾逊(Cory Robinson)
16 Sep 14 '18:41

#2 楼

__dirname不是全局的;它在当前模块中是本地的,因此每个文件都有其自己的本地值。

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

如果要获得可预测性和可靠性,则可能需要使您的应用程序具有设置特定环境变量的要求。您的应用程序会查找MY_APP_HOME(或其他内容),如果存在,并且该应用程序存在于该目录中,则一切正常。如果未定义或目录不包含您的应用程序,则它应退出并出现错误提示用户创建变量。可以将其设置为安装过程的一部分。

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

评论


如果谨慎使用,这可能效果很好。但是在执行bin / server.js与cd bin && server.js时,会给出不同的结果。 (假设这些js文件被标记为可执行)

–Myrne Stol
2013年6月7日在16:21



即使运行mocha测试,使用process.cwd()对我来说也很吸引人。谢谢!

–迪奥戈·艾希特(Diogo Eichert)
18年3月23日14:00

#3 楼

1-在项目根目录中创建一个文件,将其命名为settings.js

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

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


3-内node_modules创建一个名为“ settings”的新模块,并在模块index.js内编写以下代码:

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


4-并且任何时候只要您想使用项目目录

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


这样,您将拥有与此文件相关的所有项目目录;)

评论


-1:要加载设置文件,您需要一个路径,然后获取该文件的参考路径?什么都没解决...

– goliatone
13年8月27日在18:33

感谢您抽出宝贵的时间来查看和编辑。它仍然感觉很脆弱,但这可能只是因为没有更好的方法来实现这一目标

– goliatone
13年11月23日在4:48



用户需要牢记的一点是,通常将node_modules排除在版本控制之外。因此,如果您与团队合作或需要克隆存储库,则必须提出另一种解决方案以使该设置文件保持同步。

–Travesty3
16年7月7日在15:05

@ Travesty3设置模块实际上是一个空模块,正在导出项目根目录中的文件内容:P

–著名的Alnamrouti
17年5月5日,9:58

@goliatone使用他的解决方案,您可以从任何地方获取文件而无需知道路径,您所需要知道的就是“设置”。没有它,您将必须明确知道要退出多少个文件夹,直到到达项目目录。这是可行的,因为节点会自动搜索node_modules并始终知道其位置。

–user7917402
17年5月26日在1:43

#4 楼

获取全局根的最简单方法(假设您使用NPM运行node.js应用程序“ npm start”等)

var appRoot = process.env.PWD;


验证以上

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

    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'),非常适合此目的

评论


在(大多数)Unix系统上,这非常有用。如果您希望npm模块/应用程序在Windows上运行,则PWD是未定义的,并且会失败。

– Jeremy Wiebe
17 Mar 2 '17 at 16:10

process.cwd()

–穆罕默德·乌默尔
18-10-5在19:51

@MuhammadUmer为什么process.cwd()总是与项目根目录相同?

–亚历山大·米尔斯(Alexander Mills)
18-10-5在19:54

如果您在根文件中调用它,它将是

–穆罕默德·乌默尔
18-10-5在20:30

#5 楼

我发现即使是从子文件夹中调用应用程序时,这对我来说也是一致的,就像在某些测试框架中一样,例如Mocha:

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


为什么工作:

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

这只是一行代码,但是为了简单起见(我清酒),我将其黑盒装到NPM模块中:

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

享受吧!

评论


从以下版本开始取消process.mainModule:v14.0.0-使用require.main.paths [0] .split('node_modules')[0] .slice(0,-1);代替。

– RobC
20-5-6在11:12



#6 楼

所有这些“根目录”都需要将一些虚拟路径解析为真实的堆路径,因此也许您应该看看path.resolve吗?

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


#7 楼

只需将此行添加到root中的模块即可,通常是app.js

global.__basedir = __dirname;


然后_basedir将可用于所有模块。

#8 楼

简单:
 require('path').resolve('./')
 


#9 楼

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

#10 楼

实际上,我也找到了最鲁棒的解决方案:
您只需将以下文件放在项目的根目录中:root-path.js,其代码如下:

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


#11 楼

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

// set rootPath
app.use(function(req, res, next) {
  req.rootPath = __dirname;
  next();
});

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


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

如果app.js位于项目的根目录(默认情况下为它)中,则此方法有效。

#12 楼

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

global.__basedir = __dirname;


这将设置始终与应用程序的基本目录等效的全局变量。像其他任何变量一样使用它:

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


简单...

#13 楼

我知道这已经为时已晚。
但是我们可以通过两种方法获取根URL

第一种方法

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


第二种方法

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


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

#14 楼

INIT_CWD上有一个process.env属性。这就是我目前在我的项目中使用的东西。

const {INIT_CWD} = process.env; // process.env.INIT_CWD 
const paths = require(`${INIT_CWD}/config/paths`);


祝你好运...

评论


对于作为操作项目的软件包的附件而言,它的工作方式类似于安装后的步骤。但是,我尚未在另一层依赖项中对其进行测试,在该层中,一个项目使用的依赖项使用了我的包。

–JamesDev
19年11月7日在7:12

@ JamesDev,INIT_CWD解析到执行npm-script的目录。

– Aakash
19年11月8日,3:30

#15 楼

如果您想从正在运行的node.js应用程序中确定项目根目录,也可以直接使用。

process.mainModule.path


评论


[tsserver 6385]已弃用“ mainModule”

– jpoppe
20 Dec 25'9:08



#16 楼

在主文件顶部添加:

mainDir = __dirname;


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

console.log('mainDir ' + mainDir);




mainDir是全局定义的,如果仅在当前文件中需要它,请改用__dirname
主文件通常位于项目的根文件夹中,并命名为main.jsindex.jsgulpfile.js


#17 楼

让它变得性感💃🏻。


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')

require('sexy-require');
const routers = require('/routers');
const api = require('$api');
...



可选步骤。可以在项目的根目录下的.paths文件中定义路径配置。

$db = /server/database
$api-v1 = /server/api/legacy
$api-v2 = /server/api/v2




评论


似乎很体面,可惜它有这么可笑的名字。

– JHH
19-10-10在12:41

@JHH好...我必须找到一个更好的名字

–苏丹
19年11月14日在10:06

#18 楼

这将逐步降低目录树,直到它包含一个node_modules目录,该目录通常指示您的项目根目录:

 const fs = require('fs')
const path = require('path')

function getProjectRoot(currentDir = __dirname.split(path.sep)) {
  if (!currentDir.length) {
    throw Error('Could not find project root.')
  }
  const nodeModulesPath = currentDir.concat(['node_modules']).join(path.sep)
  if (fs.existsSync(nodeModulesPath) && !currentDir.includes('node_modules')) {
    return currentDir.join(path.sep)
  }
  return this.getProjectRoot(currentDir.slice(0, -1))
}
 


还确保返回的路径中没有node_modules,因为这意味着它包含在嵌套软件包install中。

#19 楼


从v 14.0.0版本开始,不推荐使用process.mainModule。参考答案时,请使用require.main,其余的仍然有效。


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


获取主模块中的所有路径,并过滤掉带有“ node_modules”的路径,
然后获得剩余路径列表中的第一个。意外的行为不会引发错误,只需undefined即可。

即使我打电话给$ mocha,也能很好地工作。

#20 楼

序言
这是一个很老的问题,但它似乎在2020年和2012年一样令人震惊。
我检查了所有其他答案,却找不到技术(请注意,这有其限制,但所有其他限制也不适用于每种情况。)
GIT +子进程
如果将GIT用作版本控制系统,则可以减少确定项目根目录的问题到(我认为这是项目的正确根目录-毕竟,您希望VCS具有尽可能完整的可见性范围):

检索存储库根路径

由于您必须运行CLI命令来执行此操作,因此我们需要生成一个子进程。此外,由于项目根目录极不可能在运行时更改,因此我们可以在启动时使用child_process模块API的同步版本。
我发现spawnSync()最适合此工作。至于要运行的实际命令,git worktree(带有--porcelain选项以便于解析)是我们检索绝对根路径的全部。
在示例中,我选择返回路径数组,因为可能存在可以肯定的是,一个以上的工作树(尽管它们可能具有相同的路径)。请注意,由于我们使用CLI命令,因此应将shell选项设置为true(由于不存在不受信任的输入,因此安全性不成问题)。
方法比较和回退
了解VCS的情况可能无法访问,在分析了文档和其他答案之后,我提供了一些后备功能。总结起来,提议的解决方案归结为(不包括第三方模块和特定于软件包的):

| Solution                 | Advantage               | Main Problem                     |
| ------------------------ | ----------------------- | -------------------------------- |
| `__filename`             | points to module file   | relative to module               |
| `__dirname`              | points to module dir    | same as `__filename`             |
| `node_modules` tree walk | nearly guaranteed root  | complex tree walking if nested   |
| `path.resolve(".")`      | root if CWD is root     | same as `process.cwd()`          |
| `process.argv[1]`        | same as `__filename`    | same as `__filename`             |
| `process.env.INIT_CWD`   | points to `npm run` dir | requires `npm` && CLI launch     |
| `process.env.PWD`        | points to current dir   | relative to (is the) launch dir  |
| `process.cwd()`          | same as `env.PWD`       | `process.chdir(path)` at runtime |
| `require.main.filename`  | root if `=== module`    | fails on `require`d modules      |


从上面的比较表中,最通用的是两种方法:


require.main.filename是满足require.main === module的一种轻松生根的方法

node_modules最近提议的树游使用了另一个假设:


如果模块的目录中包含node_modules目录,则很可能是根目录

对于主应用程序,它将获得应用程序的根目录,并且对于模块-它的项目根目录。
回退1.树遍历
我的实现使用一种更为宽松的方法,即一旦找到目标目录就停止运行,因为给定模块的根目录是其项目根目录。可以链接呼叫或扩展呼叫以使其可配置搜索深度:
 /**
 * @summary gets root by walking up node_modules
 * @param {import("fs")} fs
 * @param {import("path")} pt
 */
const getRootFromNodeModules = (fs, pt) =>

    /**
     * @param {string} [startPath]
     * @returns {string[]}
     */
    (startPath = __dirname) => {

        //avoid loop if reached root path
        if (startPath === pt.parse(startPath).root) {
            return [startPath];
        }

        const isRoot = fs.existsSync(pt.join(startPath, "node_modules"));

        if (isRoot) {
            return [startPath];
        }

        return getRootFromNodeModules(fs, pt)(pt.dirname(startPath));
    };
 

Fallback2。主模块
第二种实现很简单
 /**
 * @summary gets app entry point if run directly
 * @param {import("path")} pt
 */
const getAppEntryPoint = (pt) =>

    /**
     * @returns {string[]}
     */
    () => {

        const { main } = require;

        const { filename } = main;

        return main === module ?
            [pt.parse(filename).dir] :
            [];
    };
 

实现
我建议使用树walker作为后备,因为它更加通用:
 const { spawnSync } = require("child_process");
const pt = require('path');
const fs = require("fs");

/**
 * @summary returns worktree root path(s)
 * @param {function : string[] } [fallback]
 * @returns {string[]}
 */
const getProjectRoot = (fallback) => {

    const { error, stdout } = spawnSync(
        `git worktree list --porcelain`,
        {
            encoding: "utf8",
            shell: true
        }
    );

    if (!stdout) {
        console.warn(`Could not use GIT to find root:\n\n${error}`);
        return fallback ? fallback() : [];
    }

    return stdout
        .split("\n")
        .map(line => {
            const [key, value] = line.split(/\s+/) || [];
            return key === "worktree" ? value : "";
        })
        .filter(Boolean);
};

 
最明显的是安装并初始化了GIT,这可能是不希望的/令人难以置信的(旁注:不过,在生产服务器上安装GIT并不罕见,也不是不安全的)。可以通过如上所述的回退来调解。
说明

进一步扩展方法1的一些想法:


将配置作为函数引入参数

export使其成为模块的功能
检查是否已安装和/或初始化了GIT

参考文献


git worktree参考文献


spawnSync参考文献


require.main参考文献


path.dirname()参考文献



#21 楼

在app.js中创建函数

/*Function to get the app root folder*/

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


#22 楼

我用这个。

对于我的名为mymodule的模块

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

#23 楼

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

#24 楼

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

这不是可以直接使用的示例(因为使用了我自己的框架),但是我认为它为您提供了一些方法。我还使用了一种缓存方法来避免调用此函数给系统带来过多压力,尤其是在未指定扩展名(并且需要检查文件是否存在)的情况下,例如:

node myfile




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


#25 楼

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

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

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


#26 楼

这样可以:

path.join(...process.argv[1].split(/\/|\/).slice(0, -1))


#27 楼

它对我有用

process.env.PWD


#28 楼

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

示例:

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


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

#29 楼

只需使用:

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


评论


这很棒! path.resolve(“。”)也可以

–诺埃尔·申克(Noel Schenk)
19年8月10日在19:25



这样只会提供当前目录,而该目录可能不是根目录。

– orad
19年8月15日在8:11

#30 楼

path.dirname(process.mainModule.filename);