ChatGPT解决这个技术问题 Extra ChatGPT

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

除了 process.cwd() 之外,还有其他方法可以获取当前项目的根目录的路径名。 Node 是否实现了诸如 ruby 的属性 Rails.root 之类的东西。我正在寻找一些不变的、可靠的东西。

您是否有可能不接受已接受的错误答案?
尝试process.env.PWD ...请参阅下面的答案。
试试 path.dirname(require.main.filename)

i
inxilpro

有很多方法可以解决这个问题,每种方法都有自己的优缺点:

需要.main.filename

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

当一个文件直接从 Node 运行时,require.main 被设置为它的模块。也就是说可以通过测试直接判断一个文件是否已经运行过 require.main === module 因为module提供了一个filename属性(一般相当于__filename),所以可以通过检查require.main来获取当前应用的入口点。文件名。

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

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

优点缺点

这在大多数情况下都非常有效,但如果您使用 pm2 之类的启动器运行应用程序或运行 mocha 测试,则此方法将失败。这在使用 require.main 不可用的 Node.js ES 模块时也不起作用。

模块路径

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(或 app.js 或任何您的主应用程序文件的名称)中,您可以定义一个全局变量:

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

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

优点缺点

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

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

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

app-root-path 模块使用多种技术来确定应用程序的根路径,同时考虑到全局安装的模块(例如,如果您的应用程序在 /var/www/ 中运行但模块安装在 { 3})。它不会在 100% 的时间里工作,但它会在最常见的情况下工作。

优点缺点

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

您正在使用启动器,例如 pm2

并且,该模块未安装在您应用的 node_modules 目录中(例如,如果您全局安装了它)

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

NODE_PATH 环境变量

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

Node 的 Modules system 在多个位置寻找模块。 One of these locations is wherever process.env.NODE_PATH points。如果您设置此环境变量,那么您可以使用标准模块加载器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 启动您的应用程序,您就可以了。我将此与我的 enforce-node-path 模块结合使用,以防止在未设置 NODE_PATH 的情况下意外加载应用程序。有关强制执行环境变量的更多控制,请参阅 checkenv

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

[2016 年 4 月 6 日添加] 另一个非常有希望尝试解决此问题的模块是 wavy


@Kevin 在这种情况下, mocha 是您的应用程序的入口点。这只是为什么找到“项目根”如此困难的一个例子——它在很大程度上取决于情况以及“项目根”的含义。
@Kevin 我完全理解。我的意思是“项目根”的概念对于人类 来说比计算机 更容易理解。如果你想要一个万无一失的方法,你需要配置它。使用 require.main.filename 将在大部分时间起作用,但不是在所有时间。
切线相关:这是组织 Node 项目的一种非常聪明的方法,因此您不必太担心这个问题:allanhortle.com/2015/02/04/…
我不知道 pm2 是否有变化或 Node.js 是否有变化,但 require.main.filename 似乎适用于 pm2。不知道摩卡。
path.parse(process.mainModule.filename).dir
P
Philipp Kyeck

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

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

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

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


如果谨慎使用,这可能会很好。但是在执行 bin/server.jscd bin && server.js 时会给出不同的结果。 (假设这些 js 文件被标记为可执行)
即使在运行 mocha 测试时,使用 process.cwd() 对我来说也是一种魅力。谢谢!
F
Fareed Alnamrouti

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:要加载设置文件,您需要一个路径,然后获取该文件的引用路径?什么都解决不了...
赞成花时间审查和编辑。它仍然感觉很脆弱,但这可能只是因为没有更好的方法来实现这一点
对于这种方法,用户需要记住的一点是 node_modules 通常被排除在版本控制之外。因此,如果您与团队合作或需要克隆您的存储库,您将不得不提出另一种解决方案来保持该设置文件同步。
@Travesty3 设置模块实际上是一个空模块,它正在导出项目根目录中文件的内容:P
@goliatone 使用他的解决方案,您可以在不知道其路径的情况下从任何地方获取文件,您所需要知道的只是“设置”。如果没有它,您必须明确知道要退出多少个文件夹,直到您到达项目目录。这是有效的,因为 node 会自动搜索 node_modules 并且总是知道它在哪里。
A
Alexander Mills

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

var appRoot = process.env.PWD;

如果您想交叉验证上述内容

假设您要交叉检查 process.env.PWD 与您的 node.js 应用程序的设置。如果您想要一些运行时测试来检查 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 就未定义,这将失败。
process.cwd()
@MuhammadUmer 为什么 process.cwd() 总是与项目根目录相同?
如果你在根文件中调用它,那就是
V
Vansuita Jr.

简单的:

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

从根文件夹外部运行节点应用程序时,这不起作用!
T
Tyler2P

就像将这一行添加到根目录中的模块一样简单,通常是 app.jsapp.ts

global.__basedir = __dirname;

然后你的所有模块都可以访问 _basedir。

注意: 对于 typescript 实现,请按照上述步骤操作,然后您将能够使用 global.__basedir 使用根目录路径


简单、容易、有效
有什么办法可以打字吗?否则我需要将 ts-ignore 放在引用 __basedir 的任何地方。有点像向 window 范围添加额外字段...
a
andromeda

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

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

为什么有效:

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

这只是一行代码,但为了简单起见(我的缘故),我将它黑盒化到一个 NPM 模块中:

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

享受!

编辑:

自 v14.0.0 起,process.mainModuledeprecated

请改用 require.main

require.main.paths[0].split('node_modules')[0].slice(0, -1);


process.mainModule deprectaed since: v14.0.0 - 改用 require.main.paths[0].split('node_modules')[0].slice(0, -1);
如果你有多个 node_modules 文件夹会发生什么,就像 Electron 应用程序一样
I
Ian

所有这些“根目录”大多需要将一些虚拟路径解析为真正的堆路径,所以你应该看看path.resolve吗?

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

k
kvz

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


I
Ilyas karim

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

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

O
Oleg Valter is with Ukraine

前言

这是一个非常古老的问题,但它似乎在 2020 年和 2012 年一样触动了神经。我检查了所有其他答案,但找不到提到的以下技术(它有其自身的局限性,但其他技术是也不适用于所有情况):

Git + 子进程

如果您使用 Git 作为您的 version control system,则可以将确定项目根目录的问题简化为(我会考虑项目的正确根目录 - 毕竟,您希望您的 VCS 具有尽可能完整的可见性范围) :

检索存储库根路径

由于您必须运行 CLI 命令来执行此操作,因此我们需要生成一个子进程。此外,由于项目根极不可能在运行时更改,我们可以在启动时使用 child_process 模块的同步版本。

我发现 spawnSync() 最适合这项工作。至于要运行的实际命令,只需 git worktree(带有 --porcelain 选项以便于解析)即可检索根目录的绝对路径。

在答案末尾的示例中,我选择返回一个路径数组,因为可能有 multiple worktrees (尽管它们可能有共同的路径)只是为了确定。请注意,当我们使用 CLI 命令时,应将 shell 选项设置为 true(安全不应成为问题,因为没有不受信任的输入)。

方法比较和后备

了解 VCS 可能无法访问的情况是可能的,在分析文档和其他答案后,我包含了一些后备方案。提议的解决方案归结为(不包括第三方模块和包):

解决方案优势 主要问题 __filename 指向相对于模块的模块文件 __dirname 指向与 __filename 相同的模块目录 node_modules tree walk 几乎可以保证 root complex tree walk if nested path.resolve(".") root if CWD is root 与 process.cwd( ) process.argv\[1\] 与 __filename 相同 与 __filename 相同 process.env.INIT_CWD 指向 npm run dir 需要 npm && CLI 启动 process.env.PWD 指向相对于(是)启动目录的当前目录 process.cwd () 与运行时的 env.PWD process.chdir(path) 相同 require.main.filename root if === 模块在所需模块上失败

从上面的比较表中,以下方法是最普遍的:

如果满足 require.main === 模块,则 require.main.filename 作为获取根目录的简单方法

最近提出的 node_modules tree walk 使用了另一个假设:

如果模块的目录里面有node_modules dir,很有可能是root

对于主应用程序,它将获取应用程序根目录,而对于一个模块——它的项目根目录。

后备 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] :
            [];
    };

执行

我建议使用 tree 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 installed on production servers 并不少见,unsafe 也不罕见)。可以通过上述回退进行调解。


B
Ben Davies

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

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

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

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

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


A
Aakash

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

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

祝你好运...


就像一个包的魅力一样,它作为一个安装后步骤操作它被调用的项目。但是,我还没有在另一层依赖项中对其进行测试,其中一个项目使用了一个使用我的包的依赖项。
@JamesDev,INIT_CWD 解析为执行 npm-scriptdirectory
P
Pankaj Shinde

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

global.__basedir = __dirname;

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

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

简单的...


V
VIKAS KOHLI

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


V
Vincent Lab

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

process.mainModule.path

[tsserver 6385] 'mainModule' is deprecated
M
Manoj Rana

它对我有用

process.env.PWD

O
Oleg Valter is with Ukraine

process.mainModule 自 v 14.0.0 起已弃用。参考答案时,请使用require.main,其余的仍然成立。

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

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

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


D
Dariusz Sikorski

在主文件顶部添加:

mainDir = __dirname;

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

console.log('mainDir ' + mainDir);

mainDir 是全局定义的,如果您只在当前文件中需要它 - 请改用 __dirname。

main 文件通常在项目的根文件夹中,命名为 main.js、index.js、gulpfile.js。


J
Julien

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


K
Karthik M

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

v
vbranden

我用这个。

对于我名为 mymodule 的模块

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


s
sultan

让它变得性感💃🏻。

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') 。要求('性感要求'); const routers = require('/routers');常量 api = 需要('$api'); ... 可选步骤。路径配置可以在项目根目录的 .paths 文件中定义。 $db = /server/database $api-v1 = /server/api/legacy $api-v2 = /server/api/v2


看起来不错,可惜它有这样一个荒谬的名字。
@JHH 好吧...我必须找到一个更好的名字
P
Pulkit Aggarwal

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


C
Codebeat

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

G
Ganesh Rathinavel

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

我编写了一个 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' });

R
Rafi Henig

这将做:

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

C
Chris Mack
path.dirname(process.mainModule.filename);

process.mainModule 被弃用,取而代之的是 require.main。 (如果你使用的是 ES6 模块,这些都不起作用......)
N
Nick Bull

在现代版本的 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 文件进一步扩展它,您可以在其中使用上述答案中的一种方法,或者输入绝对路径,如果您需要路径本身


P
Pawan Kumar

您还可以使用 git rev-parse --show-toplevel 假设您正在使用 git 存储库