Answers:
有几种方法可以解决此问题,每种方法各有利弊:
从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-您附加到该对象的任何对象都将在您的应用程序中的任何位置可用。因此,在您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');
可以始终如一地工作,但是您必须依赖全局变量,这意味着您无法轻松地重用组件/等。
这将返回当前工作目录。完全不可靠,因为它完全取决于进程从哪个目录启动:
$ 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');
的应用程序根路径模块使用多种不同的技术来确定该应用程序的根路径,考虑到全球范围内安装的模块(例如,如果应用程序在运行/var/www/,但模块安装在~/.nvm/v0.x.x/lib/node/)。它不会100%地起作用,但是可以在大多数常见情况下起作用。
在大多数情况下无需配置即可工作。还提供了一些不错的附加便利方法(请参阅项目页面)。最大的缺点是,如果出现以下情况,它将不起作用:
node_modules目录中(例如,如果您是全局安装的)您可以通过设置APP_ROOT_PATH环境变量或调用.setPath()模块来解决此问题,但在这种情况下,最好使用global方法。
如果您正在寻找一种确定当前应用程序根路径的方法,则上述解决方案之一可能最适合您。另一方面,如果您要解决可靠加载应用程序模块的问题,则我强烈建议您调查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,您会很高兴。我将其与我的force-node-path结合模块,这可以防止意外加载未NODE_PATH设置的应用程序。有关对执行环境变量的更多控制,请参见checkenv。
一个陷阱: NODE_PATH 必须在节点应用程序外部设置。你不能做这样的事情process.env.NODE_PATH = path.resolve(__dirname)因为模块加载程序会在应用运行之前缓存要搜索的目录列表。
[16年4月6日增补]试图解决此问题的另一个非常有前途的模块是波浪形的。
require.main.filename将工作大部分的时间,但不是所有的时间。
require.main.filename似乎可以与pm2一起工作。不知道摩卡咖啡。
path.parse(process.mainModule.filename).dir
__dirname不是全球性的 它是当前模块的本地文件,因此每个文件都有其自己的本地不同值。
如果要运行的进程的根目录,可能要使用 process.cwd()。
如果需要可预测性和可靠性,则可能需要使您的应用程序要求设置某个环境变量。您的应用程序会查找MY_APP_HOME(或其他)内容,如果存在,并且该应用程序存在于该目录中,则一切正常。如果未定义或目录不包含您的应用程序,则它应退出并出现错误提示用户创建变量。可以将其设置为安装过程的一部分。
您可以使用诸如此类读取节点中的环境变量process.env.MY_ENV_VARIABLE。
bin/server.jsvs 时会给出不同的结果cd bin && server.js。(假设这些js文件被标记为可执行文件)
process.cwd()即使在运行摩卡测试时,使用对我来说也是一种魅力。谢谢!
1-在项目根目录中创建一个文件,将其称为settings.js
2-在此文件内添加此代码
module.exports = {
POST_MAX_SIZE : 40 , //MB
UPLOAD_MAX_FILE_SIZE: 40, //MB
PROJECT_DIR : __dirname
};
3-在node_modules内部创建一个名为“设置”的新模块,并在模块index.js内部编写以下代码:
module.exports = require("../../settings");
4-以及您希望项目目录仅使用的任何时间
var settings = require("settings");
settings.PROJECT_DIR;
这样,您将拥有与此文件相关的所有项目目录;)
node_modules通常不包含在版本控制中。因此,如果您与团队合作或需要克隆存储库,则必须提出另一种解决方案以使该设置文件保持同步。
获得全局根的最简单方法(假设您使用NPM运行node.js应用程序“ npm start”等)
var appRoot = process.env.PWD;
如果您想对以上内容进行交叉验证
假设您要交叉检查process.env.PWDnode.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')该模块非常适合此目的
PWD是未定义的,因此会失败。
process.cwd()
process.cwd()总是与项目根目录相同?
我发现即使是从子文件夹中调用应用程序时,这对我来说也是一致的,就像某些测试框架(例如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);改为使用。
所有这些“根目录”大部分都需要将一些虚拟路径解析为真实的堆路径,所以您应该看看path.resolve吗?
var path= require('path');
var filePath = path.resolve('our/virtual/path.ext');
实际上,我发现最鲁棒的解决方案也许也是最简单的:您只需将以下文件放在项目的根目录中:root-path.js,其中包含以下代码:
import * as path from 'path'
const projectRootPath = path.resolve(__dirname)
export const rootPath = projectRootPath
将此添加到主应用程序文件(例如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);
参考链接:-https: //gist.github.com/geekiam/e2e3e0325abd9023d3a3
INIT_CWD属性process.env。这就是我目前在我的项目中使用的东西。const {INIT_CWD} = process.env; // process.env.INIT_CWD
const paths = require(`${INIT_CWD}/config/paths`);
祝好运...
INIT_CWD解析为directory从其中npm-script被exectued。
在主文件的顶部添加:
mainDir = __dirname;
然后在需要的任何文件中使用它:
console.log('mainDir ' + mainDir);
mainDir是全局定义的,如果仅在当前文件中需要,则使用- __dirname代替。main.js,index.js,gulpfile.js。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 --saverequire('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这将逐步降低目录树,直到它包含一个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'));
您只需将根目录路径添加到express应用程序变量中,然后从应用程序获取此路径。为此,添加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软件包电子根路径来捕获电子应用程序的根路径。
$ 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' });
它对我有用
process.env.PWD
前言
这是一个非常老的问题,但它似乎在2020年和2012年一样令人不安。我检查了所有其他答案,但没有找到技术(请注意,这是有局限性的,但其他所有条件都没有)同样适用于所有情况)。
GIT +子进程
如果将GIT用作版本控制系统,则可以将确定项目根目录的问题减少到(我认为这是项目的正确根目录-毕竟,您希望VCS具有尽可能完整的可见性范围) :
检索式存储库根路径
由于您必须运行CLI命令来执行此操作,因此我们需要生成一个子进程。此外,由于项目根目录极不可能在运行时更改,因此我们可以child_process在启动时使用模块API 的同步版本。
我发现spawnSync()最适合这份工作。至于要运行的实际命令,git worktree(带有--porcelain易于解析选项),我们就需要获取绝对根路径。
在示例中,我选择返回一个路径数组,因为可能有一个以上的工作树(尽管它们可能具有共同的路径)只是为了确定。请注意,由于我们使用CLI命令,因此shell应将选项设置为true(安全性不成问题,因为没有不可信的输入)。
方法比较和后备
了解到VCS不可访问的情况,在分析了文档和其他答案之后,我提供了一些后备方法。综上所述,建议的解决方案归结为(不包括第三方模块和特定于软件包的):
| 解决方案 优势| 主要问题 | ------------------------ | ----------------------- | -------------------------------- | | `__filename` | 指向模块文件| 相对于模块| | `__dirname` | 指向模块目录| 与`__filename`相同| | `node_modules`树步| 几乎保证根| 如果嵌套则走复杂的树| | `path.resolve(“。”)`| root,如果CWD是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` |,则为root 在'require`d模块上失败|
从上面的比较表中,最通用的是两种方法:
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));
};
备用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 并不少见,也不安全)。如上所述,可以通过后备情况来进行调解。
笔记
export 使其成为模块的功能参考文献
path.dirname(process.mainModule.filename);