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

时间:2012-04-22 06:39:20

标签: node.js

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

30 个答案:

答案 0 :(得分:522)

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

require.main.filename

来自http://nodejs.org/api/modules.html

  

直接从节点运行文件时,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 测试,则此方法将失败。< / p>

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

优点&amp;缺点

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

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 的节点模块。用法很简单:

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

app-root-path 模块使用多种不同的技术来确定应用的根路径,同时考虑全局安装的模块(例如,如果您的应用在{{1}中运行但该模块安装在/var/www/)中。它不会100%有效,但它可以在大多数常见情况下工作。

优点&amp;缺点

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

  • 您正在使用启动器,例如pm2
  • AND ,该模块未安装在应用的~/.nvm/v0.x.x/lib/node/目录中(例如,如果您全局安装)

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

NODE_PATH环境变量

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

节点Modules system查找各个位置的模块。 One of these locations is wherever process.env.NODE_PATH points。如果设置此环境变量,则可以使用标准模块加载器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" } 启动应用,而且您已经很高兴。我将其与我的enforce-node-path模块结合使用,可防止在未设置npm start的情况下意外加载应用。有关强制执行环境变量的更多控制,请参阅checkenv

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

[已添加4/6/16] 尝试解决此问题的另一个非常有前途的模块是wavy

答案 1 :(得分:49)

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

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

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

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

答案 2 :(得分:44)

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

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

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

3-在node_modules里面创建一个新的模块名称&#34; settings&#34;并在模块index.js中编写此代码:

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

4-并且只要您希望项目目录使用

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

以这种方式,你将拥有与该文件相关的所有项目目录;)

答案 3 :(得分:17)

获取全局根目录的最简单方法(假设您使用NPM运行node.js app'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'),它可以很好地用于此目的

答案 4 :(得分:10)

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

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

为什么会这样:

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

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

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

享受!

答案 5 :(得分:9)

简单:

require('path').resolve('./')

答案 6 :(得分:7)

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

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

答案 7 :(得分:6)

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

答案 8 :(得分:4)

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

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

答案 9 :(得分:3)

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

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

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

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

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

答案 10 :(得分:2)

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

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

祝你好运...

答案 11 :(得分:1)

在主文件顶部添加:

mainDir = __dirname;

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

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

答案 12 :(得分:1)

让它变得性感。

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


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

  1. 安装包:npm install sexy-require --save
  2. 在主应用程序文件的顶部添加一次require('sexy-require')

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

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

答案 13 :(得分:1)

序言

这是一个非常老的问题,但与2012年一样,它在2020年似乎仍然触动神经。 我检查了所有其他答案,但找不到一种技术(请注意,这是有局限性的,但其他所有方法也不适用于所有情况)。

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建议的树走recently使用了另一个假设:

如果模块目录中包含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));
    };

Fallback 2.主模块

第二种实现很简单

/**
 * @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] :
            [];
    };

实施

我建议使用树助行器作为后备,因为它用途更广:

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 installed on production servers并不少见,unsafe也不罕见)。可以通过如上所述的后备情况来调解。

注释

  1. 进一步扩展方法1的一些想法:
  • 将配置作为功能参数引入
  • export使其成为模块的功能
  • 检查是否已安装和/或初始化GIT

参考

  1. git worktree reference
  2. spawnSync reference
  3. require.main reference
  4. path.dirname() reference

答案 14 :(得分:1)

这将逐步下移目录树,直到它包含一个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,因为这意味着它包含在嵌套软件包安装中。

答案 15 :(得分:1)

我知道这已经为时已晚。 但是我们可以通过两种方法获取根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

答案 16 :(得分:1)

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

global.__basedir = __dirname;

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

答案 17 :(得分:0)

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

我已经编写了一个npm软件包electron-root-path来捕获电子应用程序的根路径。

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

答案 18 :(得分:0)

对我有用

process.env.PWD

答案 19 :(得分:0)

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

  element(by.id('username')).sendKeys(username);
  console.log('Click next');
  element(by.buttonText('Next')).click();
  console.log('Entering password');
  element(by.id('password')).sendKeys(password);
  console.log('Click sign in');
  element(by.buttonText('Sign in')).click();

答案 20 :(得分:0)

这可以做到:

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

答案 21 :(得分:0)

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

global.__basedir = __dirname;

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

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

简单...

答案 22 :(得分:0)

我知道还有一个老问题,但是没有提到要使用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 || '';
    }
}; 

答案 23 :(得分:0)

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

答案 24 :(得分:0)

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

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

即使在调用ie $ mocha时也适合我。

答案 25 :(得分:0)

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

答案 26 :(得分:-1)

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

答案 27 :(得分:-1)

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

示例:

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

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

答案 28 :(得分:-1)

path.dirname(process.mainModule.filename);

答案 29 :(得分:-2)

只需使用:

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