除了process.cwd()之外,是否有其他方法来获取当前项目根目录的路径名。Node是否实现了ruby的属性Rails.root之类的东西。我要找的是稳定可靠的东西。


当前回答

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; 

通过这种方式,您将拥有与此文件相关的所有项目目录;)

其他回答

在app.js中创建一个函数

/*函数获取应用程序根文件夹*/

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

序言

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

Git +子进程

如果你使用Git作为你的版本控制系统,确定项目根的问题可以简化为(我认为项目的正确根-毕竟,你会希望你的VCS有尽可能充分的可见范围):

检索存储库根路径

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

我发现spawnSync()最适合这项工作。至于实际要运行的命令,检索根目录的绝对路径所需要的就是git工作树(带有——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 required modules

从上面的对比表来看,以下方法是最普遍的:

require.main.filename作为获取根文件的简单方法。Main ===模块满足 最近提出的Node_modules树遍历使用了另一个假设:

如果模块的目录中有node_modules dir,那么它很可能是根目录

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

回退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并不少见,也不是不安全的)。如上所述,可以通过回退来调节。

这对我很有用

process.env.PWD

老问题,我知道,但是没有问题提到使用progress.argv。argv数组包含完整的路径名和文件名(带或不带.js扩展名),用作节点执行的参数。因为它也可以包含标志,所以必须对其进行过滤。

这不是一个你可以直接使用的例子(因为使用我自己的框架),但我认为它给了你一些想法如何做到这一点。我还使用缓存方法来避免调用这个函数对系统造成太大的压力,特别是在没有指定扩展名(并且需要文件存在检查)的情况下,例如:

node myfile

or

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