从正在运行的 node.js 应用程序确定项目根 [英] Determine project root from a running node.js application

查看:25
本文介绍了从正在运行的 node.js 应用程序确定项目根的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

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

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 { 常量,promises: { access } } = require('fs');for (let path of module.paths) {尝试 {等待访问(路径,常量.F_OK);返回目录名(路径);}赶上(e){//继续下一个路径}}}

优点 &缺点

这有时会起作用,但在包中使用时不可靠,因为它可能返回安装包的目录而不是安装应用程序的目录.

使用全局变量

Node 有一个名为 global 的全局命名空间对象——你附加到这个对象的任何东西都可以在你的应用程序中的任何地方使用.因此,在您的 index.js(或 app.js 或任何您的主应用程序中文件被命名),你可以定义一个全局变量:

//index.jsvar path = require('path');global.appRoot = path.resolve(__dirname);//lib/moduleA/component1.js要求(appRoot + '/lib/moduleB/component2.js');

优点 &缺点

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

process.cwd()

这将返回当前工作目录.根本不可靠,因为它完全取决于进程从哪个目录启动:

$ cd/home/demo/$ mkdir 子目录$ echo "console.log(process.cwd());";>子目录/演示.js$ node subdir/demo.js/家/演示$ cd 子目录$ 节点演示.js/家/演示/子目录

app-root-path

为了解决这个问题,我创建了一个名为 app 的节点模块-根路径.用法很简单:

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

app-root-path模块使用多种技术来确定应用程序的根路径,同时考虑全局安装的模块(例如,如果您的应用程序在 /var/www/ 中运行,但模块安装在 ~/.nvm/v0.xx/lib/node/).它不会在 100% 的情况下工作,但它会在大多数常见情况下工作.

优点 &缺点

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

  • 您正在使用启动器,例如 pm2
  • AND,模块未安装在您应用的 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');//^ 查找/var/www/lib/module2/component.js

一个很好的方法是使用 npm:

{脚本":{开始":NODE_PATH=.节点 app.js"}}

现在您可以使用 npm start 启动您的应用程序,您就大功告成了.我将此与我的 enforce-node-path 模块结合使用,以防止意外加载应用程序没有 NODE_PATH 设置.如需更多地控制强制执行环境变量,请参阅 checkenv.

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

[2016 年 4 月 6 日添加] 另一个试图解决这个问题的真正有前途的模块是 波浪形.

Is there a different way, other than process.cwd(), to get the pathname of the current project's root-directory. Does Node implement something like ruby's property, Rails.root,. I'm looking for something that is constant, and reliable.

解决方案

There are many ways to approach this, each with their own pros and cons:

require.main.filename

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

When a file is run directly from Node, require.main is set to its module. That means that you can determine whether a file has been run directly by testing require.main === module

Because module provides a filename property (normally equivalent to __filename), the entry point of the current application can be obtained by checking require.main.filename.

So if you want the base directory for your app, you can do:

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

Pros & Cons

This will work great most of the time, but if you're running your app with a launcher like pm2 or running mocha tests, this method will fail.

module.paths

Node publishes all the module search paths to module.paths. We can traverse these and pick the first one that resolves.

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
    }
  }
}

Pros & Cons

This will sometimes work, but is not reliable when used in a package because it may return the directory that the package is installed in rather than the directory that the application is installed in.

Using a global variable

Node has a global namespace object called global — anything that you attach to this object will be available everywhere in your app. So, in your index.js (or app.js or whatever your main app file is named), you can just define a global variable:

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

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

Pros & Cons

Works consistently, but you have to rely on a global variable, which means that you can't easily reuse components/etc.

process.cwd()

This returns the current working directory. Not reliable at all, as it's entirely dependent on what directory the process was launched from:

$ 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

To address this issue, I've created a node module called app-root-path. Usage is simple:

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

The app-root-path module uses several techniques to determine the root path of the app, taking into account globally installed modules (for example, if your app is running in /var/www/ but the module is installed in ~/.nvm/v0.x.x/lib/node/). It won't work 100% of the time, but it's going to work in most common scenarios.

Pros & Cons

Works without configuration in most circumstances. Also provides some nice additional convenience methods (see project page). The biggest con is that it won't work if:

  • You're using a launcher, like pm2
  • AND, the module isn't installed inside your app's node_modules directory (for example, if you installed it globally)

You can get around this by either setting a APP_ROOT_PATH environmental variable, or by calling .setPath() on the module, but in that case, you're probably better off using the global method.

NODE_PATH environmental variable

If you're looking for a way to determine the root path of the current app, one of the above solutions is likely to work best for you. If, on the other hand, you're trying to solve the problem of loading app modules reliably, I highly recommend looking into the NODE_PATH environmental variable.

Node's Modules system looks for modules in a variety of locations. One of these locations is wherever process.env.NODE_PATH points. If you set this environmental variable, then you can require modules with the standard module loader without any other changes.

For example, if you set NODE_PATH to /var/www/lib, the the following would work just fine:

require('module2/component.js');
// ^ looks for /var/www/lib/module2/component.js

A great way to do this is using npm:

{
  "scripts": {
    "start": "NODE_PATH=. node app.js"
  }
}

Now you can start your app with npm start and you're golden. I combine this with my enforce-node-path module, which prevents accidentally loading the app without NODE_PATH set. For even more control over enforcing environmental variables, see checkenv.

One gotcha: NODE_PATH must be set outside of the node app. You cannot do something like process.env.NODE_PATH = path.resolve(__dirname) because the module loader caches the list of directories it will search before your app runs.

[added 4/6/16] Another really promising module that attempts to solve this problem is wavy.

这篇关于从正在运行的 node.js 应用程序确定项目根的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆