process.cwd()
更好的方法来确定正在运行的node.js进程的根目录吗?类似Rails.root
,但适用于Node.js。我正在寻找尽可能可预测和可靠的东西。#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.js2-在此文件内添加此代码
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.jsglobal.__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.js
,index.js
,gulpfile.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);
评论
您是否有可能不接受已接受的,错误的答案?尝试process.env.PWD ...请参阅下面的答案。