除了 之外,还有其他方法process.cwd()
可以获取当前项目的根目录的路径名。Node 是否实现了类似 ruby 的属性Rails.root
, 。我正在寻找一些不变的、可靠的东西。
32 回答
有很多方法可以解决这个问题,每种方法都有自己的优缺点:
require.main.filename
从http://nodejs.org/api/modules.html:
当一个文件直接从 Node 运行时,
require.main
设置为它的module
. 就是说可以通过测试直接判断一个文件是否已经运行过require.main === module
因为
module
提供了一个filename
属性(通常等价于__filename
),所以可以通过检查来获取当前应用程序的入口点require.main.filename
。
因此,如果您想要应用程序的基本目录,您可以执行以下操作:
const { dirname } = require('path');
const appDir = dirname(require.main.filename);
优点缺点
这在大多数情况下都非常有效,但如果您使用pm2之类的启动器运行您的应用程序或运行mocha测试,则此方法将失败。
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
(或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的节点模块。用法很简单:
const appRoot = require('app-root-path');
const 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
,则以下内容可以正常工作:
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。
__dirname
不是全局的;它是当前模块的本地文件,因此每个文件都有自己的本地不同值。
如果你想要运行进程的根目录,你可能确实想要使用process.cwd()
.
如果您想要可预测性和可靠性,那么您可能需要将设置某个环境变量作为您的应用程序的要求。您的应用程序查找MY_APP_HOME
(或其他),如果它在那里,并且应用程序存在于该目录中,那么一切都很好。如果它未定义或目录不包含您的应用程序,那么它应该退出并提示用户创建变量的错误。它可以设置为安装过程的一部分。
您可以使用类似process.env.MY_ENV_VARIABLE
.
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;
这样,您将拥有与该文件相关的所有项目目录;)
简单的:
require('path').resolve('./')
获取全局根的最简单方法(假设您使用 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')
它非常适合这个目的
我发现这对我来说始终如一,即使从子文件夹调用应用程序也是如此,因为它可以与某些测试框架一起使用,例如 Mocha:
process.mainModule.paths[0].split('node_modules')[0].slice(0, -1);
为什么有效:
在运行时节点创建所有加载文件的完整路径的注册表。模块首先加载,因此位于此注册表的顶部。通过选择注册表的第一个元素并返回“node_modules”目录之前的路径,我们能够确定应用程序的根目录。
这只是一行代码,但为了简单起见(我的缘故),我将它黑盒化到一个 NPM 模块中:
https://www.npmjs.com/package/node-root.pddivine
享受!
就像将这一行添加到根目录中的模块一样简单,通常它是app.js
or app.ts
。
global.__basedir = __dirname;
然后你的所有模块都可以访问 _basedir。
注意:对于打字稿实现,请按照上述步骤操作,然后您将能够使用根目录路径global.__basedir
所有这些“根目录”大多需要将一些虚拟路径解析为真正的堆路径,所以你应该看看path.resolve
吗?
var path= require('path');
var filePath = path.resolve('our/virtual/path.ext');
也许您可以尝试从向上遍历__filename
直到找到 a package.json
,然后确定这是您当前文件所属的主目录。
实际上,我发现最强大的解决方案可能也是微不足道的:您只需将以下文件放在项目的根目录中:root-path.js,其中包含以下代码:
import * as path from 'path'
const projectRootPath = path.resolve(__dirname)
export const rootPath = projectRootPath
我发现使用 express 时有用的一种技术是在设置任何其他路由之前将以下内容添加到 app.js
// set rootPath
app.use(function(req, res, next) {
req.rootPath = __dirname;
next();
});
app.use('/myroute', myRoute);
无需使用全局变量,您将根目录的路径作为请求对象的属性。
如果您的 app.js 位于项目的根目录中,则此方法有效,默认情况下,它位于项目的根目录中。
前言
这是一个非常古老的问题,但它似乎在 2020 年和 2012 年一样触动了神经。我检查了所有其他答案,但找不到提到的以下技术(它有其自身的局限性,但其他的是也不适用于所有情况):
Git + 子进程
如果您使用 Git 作为您的版本控制系统,那么确定项目根目录的问题可以简化为(我会考虑项目的正确根目录 - 毕竟,您希望您的 VCS 具有尽可能完整的可见性范围) :
检索存储库根路径
由于您必须运行 CLI 命令来执行此操作,因此我们需要生成一个子进程。此外,由于项目根极不可能在运行时更改,我们可以child_process
在启动时使用模块的同步版本。
我发现spawnSync()
最适合这份工作。至于要运行的实际命令,git worktree
(带有--porcelain
便于解析的选项)是检索根目录的绝对路径所需的全部内容。
在答案末尾的示例中,我选择返回一个路径数组,因为可能有多个工作树(尽管它们可能有公共路径)只是为了确定。请注意,当我们使用 CLI 命令时,shell
应将选项设置为true
(安全不应成为问题,因为没有不受信任的输入)。
方法比较和后备
了解 VCS 可能无法访问的情况是可能的,在分析文档和其他答案后,我包含了一些后备方案。提议的解决方案归结为(不包括第三方模块和包):
解决方案 | 优势 | 主要问题 |
---|---|---|
__filename |
指向模块文件 | 相对于模块 |
__dirname |
指向模块目录 | 如同__filename |
node_modules 树走 |
几乎保证根 | 嵌套复杂的树行走 |
path.resolve(".") |
如果 CWD 是 root,则为 root | 如同process.cwd() |
process.argv\[1\] |
如同__filename |
如同__filename |
process.env.INIT_CWD |
指向npm run 目录 |
需要npm && CLI 启动 |
process.env.PWD |
指向当前目录 | 相对于(是)启动目录 |
process.cwd() |
如同env.PWD |
process.chdir(path) 在运行时 |
require.main.filename |
根如果=== module |
require 在d 模块上失败 |
从上面的比较表中,以下方法是最普遍的:
require.main.filename
require.main === module
如果满足,作为获取根的简单方法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并不少见,也不是不安全的)。可以通过上述回退进行调解。
上有一处INIT_CWD
房产process.env
。这是我目前在我的项目中使用的。
const {INIT_CWD} = process.env; // process.env.INIT_CWD
const paths = require(`${INIT_CWD}/config/paths`);
祝你好运...
将其添加到您的主应用程序文件(例如 app.js)的开头:
global.__basedir = __dirname;
这将设置一个全局变量,该变量将始终等同于您的应用程序的基本目录。像任何其他变量一样使用它:
const yourModule = require(__basedir + '/path/to/module.js');
简单的...
我知道这个已经太晚了。但是我们可以通过两种方法获取根 URL
第一种方法
var path = require('path');
path.dirname(require.main.filename);
第二种方法
var path = require('path');
path.dirname(process.mainModule.filename);
如果您想从正在运行的 node.js 应用程序中确定项目根目录,您也可以这样做。
process.mainModule.path
它对我有用
process.env.PWD
在主文件顶部添加:
mainDir = __dirname;
然后在您需要的任何文件中使用它:
console.log('mainDir ' + mainDir);
mainDir
是全局定义的,如果您仅在当前文件中需要它 - 请__dirname
改用。- 主文件通常位于项目的根文件夹中,并命名为
main.js
,index.js
,gulpfile.js
.
这将逐步降低目录树,直到它包含一个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
返回的路径中没有,因为这意味着它包含在嵌套包安装中。
在 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'));
我用这个。
对于我的模块名为mymodule
var BASE_DIR = __dirname.replace(/^(.*\/mymodule)(.*)$/, '$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
解决丑陋路径问题的三个简单步骤。
- 安装包:
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
您可以简单地在 express app 变量中添加根目录路径,然后从应用程序中获取此路径。为此app.set('rootDirectory', __dirname);
,请在您的 index.js 或 app.js 文件中添加。并req.app.get('rootDirectory')
用于获取代码中的根目录路径。
老问题,我知道,但是没有问题提到要使用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 || '';
}
};
找到电子应用程序的根路径可能会很棘手。因为在生产、开发、打包等不同条件下,主进程和渲染器的根路径是不同的。
我编写了一个 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' });
这将做:
path.join(...process.argv[1].split(/\/|\\/).slice(0, -1))
path.dirname(process.mainModule.filename);
在现代版本的 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
,您可以在其中使用上述答案中的一种方法,或者输入绝对路径,如果您需要路径本身
尝试path._makeLong('some_filename_on_root.js');
例子:
cons path = require('path');
console.log(path._makeLong('some_filename_on_root.js');
这将从您的节点应用程序的根目录返回完整路径(与 package.json 的位置相同)
只需使用:
path.resolve("./") ... output is your project root directory
__dirname 将为您提供根目录,只要您位于根目录中的文件中。
// ProjectDirectory.js (this file is in the project's root directory because you are putting it there).
module.exports = {
root() {
return __dirname;
}
}
在其他一些文件中:
const ProjectDirectory = require('path/to/ProjectDirectory');
console.log(`Project root directory is ${ProjectDirectory.root}`);