如何使node.js中的require始终相对于项目的根文件夹?
我想要我的文件总是由我的项目的根,而不是相对于当前模块。
例如,如果你看看https://github.com/visionmedia/express/blob/2820f2227de0229c5d7f28009aa432f9f3a7b5f9/examples/downloads/app.js第6行,你会看到
express = require('../../')
海事组织真的很糟糕。 想象一下,我想把我所有的例子只接近一层。 这是不可能的,因为在每个例子中,我将不得不更新30多个例子并多次更新。 对此:
express = require('../')
我的解决scheme将有一个特殊情况下的根:如果一个string以$开头,那么它是相对于项目的根文件夹。
任何帮助表示赞赏,谢谢
更新2
现在我正在使用require.js,它允许您以一种方式写入,并在客户端和服务器上同时工作。 Require.js还允许您创build自定义path。
更新3
现在我转到了webpack + gulp,并使用enhanced-require来处理服务器端的模块。 看到这里的理由: http : //hackhat.com/p/110/module-loader-webpack-vs-requirejs-vs-browserify/
Browserify手册中有一个非常有趣的部分:
避免../../../../../../ ..
并非所有的应用程序都正确地属于公共npm,在许多情况下,build立私有的npm或git仓库的开销仍然相当大。 这里有一些避免
../../../../../../../
相对path问题的方法。node_modules
人们有时会反对将特定于应用程序的模块放入node_modules中,因为如果不检查npm中的第三方模块,如何检查内部模块并不明显。
答案很简单! 如果您有忽略
node_modules
的.gitignore
文件:node_modules
你可以添加一个exception
!
对于您的每个内部应用程序模块:node_modules/* !node_modules/foo !node_modules/bar
请注意,如果父项已被忽略,则不能取消pipe理子目录。 因此,不必忽略
node_modules
,而必须使用node_modules/*
技巧忽略node_modules
每个目录,然后添加exception。现在在你的应用程序中的任何地方,你将能够
require('foo')
或require('bar')
而没有一个非常大和脆弱的相对path。如果你有很多模块,并且希望使它们与npm安装的第三方模块更加分离,那么你可以把它们全部放在
node_modules
目录下,例如node_modules/app
:node_modules/app/foo node_modules/app/bar
现在,您可以在应用程序的任何位置
require('app/foo')
或require('app/bar')
。在
.gitignore
,只需为node_modules/app
添加一个例外:node_modules/* !node_modules/app
如果您的应用程序在package.json中configuration了转换,则需要在
node_modules/foo
或node_modules/app/foo
组件目录中使用自己的转换字段创build一个单独的package.json,因为转换不适用于模块边界。 这将使您的模块更适合您的应用程序中的configuration更改,并且更容易独立重用应用程序之外的包。符号链接
另一个便利的技巧是,如果您正在使用符号链接而不需要支持Windows的应用程序,则可以将
lib/
或app/
文件夹链接到node_modules
。 从项目的根,做:ln -s ../lib node_modules/app
现在在你的项目的任何地方,你都可以通过
require('app/foo.js')
获得lib/foo.js
。自定义path
您可能会看到一些地方讨论使用
$NODE_PATH
环境variables或opts.paths
为节点添加目录,并使用$NODE_PATH
查找模块。与大多数其他平台不同,与使用
node_modules
目录有效地使用node_modules
节点types的$NODE_PATH
path目录不同,这是因为您的应用程序与运行时环境configuration更紧密地耦合,所以有更多的运动部件,并且只有在您的环境正确设置时,您的应用程序才能工作。
节点和browserify都支持,但不鼓励使用
$NODE_PATH
。
那么呢:
var myModule = require.main.require('./path/to/module');
它需要的文件,如果它是从主js文件需要,所以它工作得很好,只要你的主要js文件是在你的项目的根…这是我欣赏。
我喜欢为共享代码创build一个新的node_modules
文件夹,然后让节点和要求做到最好。
例如:
- node_modules // => these are loaded from your package.json - app - node_modules // => add node-style modules - helper.js - models - user - car - package.json - .gitignore
例如,如果你在car/index.js
你可以require('helper')
,节点会find它!
node_modules如何工作
节点有一个聪明的algorithm来解决在竞争对手平台中独一无二的模块。
如果从/beep/boop/bar.js
require('./foo.js')
,节点将在./foo.js
中/beep/boop/foo.js
。 以./
或../
开头的path始终是调用require()
的文件的本地path。
但是,如果您需要/beep/boop/foo.js
的非相对名称(例如require('xyz')
,节点/beep/boop/foo.js
顺序search这些path,如果没有find任何内容,则停止第一次匹配并引发错误:
/beep/boop/node_modules/xyz /beep/node_modules/xyz /node_modules/xyz
对于存在的每个xyz
目录,节点将首先查找xyz/package.json
以查看是否存在"main"
字段。 如果你require()
目录path, "main"
字段定义了哪个文件应该负责。
例如,如果/beep/node_modules/xyz
是第一个匹配,并且/beep/node_modules/xyz/package.json
具有:
{ "name": "xyz", "version": "1.2.3", "main": "lib/abc.js" }
那么来自/beep/node_modules/xyz/lib/abc.js
的输出将由require('xyz')
。
如果没有package.json
或没有"main"
字段,则假定index.js
:
/beep/node_modules/xyz/index.js
大的图景
看起来“非常糟糕”,但要花时间。 事实上,这真的很好。 明确的require()
提供了一个完全的透明度和易于理解,就像在项目生命周期中呼吸新鲜空气。
你可以这样想:你正在阅读一个例子,把你的脚趾浸入Node.js,你已经认定它是“非常糟糕的IMO”。 您是Node.js社区的二stream猜测领导者,那些比任何人都logging了更多小时编写和维护Node.js应用程序的人。 作者犯这样一个新手错误的机会是什么? (我同意,从Ruby和Python的背景来看,起初似乎是一场灾难。)
Node.js周围有很多炒作和反炒作。 但是当尘埃落定的时候,我们会承认明确的模块和“本地优先”软件包是采用的主要驱动力。
常见的情况
当然,从当前目录,然后父,然后祖父母,祖父母,等等node_modules
被search。 所以你已经安装的包已经这样工作了。 通常你可以在你的项目的任何地方require("express")
,并且工作正常。
如果你发现自己从你的项目的根目录加载公共文件(也许是因为它们是常用的实用function),那么这是一个大的线索,现在是时候做一个包。 软件包非常简单:将文件移动到node_modules/
并将package.json
放在那里。 瞧! 该名称空间中的所有内容都可以从整个项目中访问。 包是将代码放入全局名称空间的正确方法。
其他解决方法
我个人不会使用这些技术,但他们确实回答你的问题,当然你比我更了解你自己的情况。
您可以将$NODE_PATH
设置$NODE_PATH
您的项目根目录。 当你require()
时,该目录将被search。
接下来,您可能会妥协,并需要从您的所有示例中的一个共同的本地文件。 该公用文件只是重新导出祖父目录中的真实文件。
examples / downloads / app.js (以及其他很多喜欢它的人)
var express = require('./express')
实例/下载/ express.js
module.exports = require('../../')
现在,当您重新定位这些文件时,最糟糕的情况是修复一个垫片模块。
看看node-rfr 。
这很简单:
var rfr = require('rfr'); var myModule = rfr('projectSubDir/myModule');
恕我直言,最简单的方法是将自己的函数定义为GLOBAL
对象的一部分。 在项目的根目录下创buildprojRequire.js
,其中包含以下内容:
var projectDir = __dirname; module.exports = GLOBAL.projRequire = function(module) { return require(projectDir + module); }
在require
任何特定于项目的模块之前,在您的主文件中:
// init projRequire require('./projRequire');
之后,以下为我工作:
// main file projRequire('/lib/lol'); // index.js at projectDir/lib/lol/index.js console.log('Ok');
@Totty,我已经想出了另一个解决scheme,这可以用于你在评论中描述的情况。 描述将是tl;dr
,所以我最好展示一个与我的testing项目结构的图片。
假设您的项目根目录是当前的工作目录,这应该工作:
// require built-in path module path = require('path'); // require file relative to current working directory config = require( path.resolve('.','config.js') );
这是我6个月以上的实际做法。 我在项目中使用了一个名为node_modules的文件夹作为我的根文件夹,通过这种方式,它总是会从我呼叫绝对要求的任何地方查找该文件夹:
- node_modules
- 我的项目
- index.js我可以要求(“myProject / someFolder / hey.js”)而不是require(“./ someFolder / hey.js”)
- 其中包含hey.js的someFolder
- 我的项目
当你嵌套到文件夹中时,这是更有用的,如果以绝对方式设置,那么更改文件位置的工作就less得多。 我只使用2在我的整个应用程序的相对要求。
我在我的项目中使用process.cwd()
。 例如:
var Foo = require(process.cwd() + '/common/foo.js');
可能值得注意的是,这将导致require
一个绝对的path,虽然我还没有遇到这个问题。
你可以在你的app.js中定义这样的东西:
requireFromRoot = (function(root) { return function(resource) { return require(root+"/"+resource); } })(__dirname);
然后在任何时候,你想要从根目录中得到一些东西,不pipe你在哪里,只要使用requireFromRoot而不是香草需求。 到目前为止,我工作得很好。
这里有一个很好的讨论这个问题。
我遇到了同样的架构问题:想要给我的应用程序更多的组织和内部命名空间的方式,没有:
- 将应用程序模块与外部依赖混合在一起,或者使用私有的npm repos来处理特定于应用程序的代码
- 使用相对的要求,这使得重构和理解更难
- 使用符号链接或更改节点path,这会遮蔽源位置,并且不能很好地与源代码pipe理一起播放
最后,我决定使用文件命名约定而不是目录来组织我的代码。 一个结构看起来像这样:
- NPM-shrinkwrap.json
- 的package.json
- node_modules
- …
- SRC
- app.js
- app.config.js
- app.models.bar.js
- app.models.foo.js
- app.web.js
- app.web.routes.js
- …
然后在代码中:
var app_config = require('./app.config'); var app_models_foo = require('./app.models.foo');
要不就
var config = require('./app.config'); var foo = require('./app.models.foo');
像往常一样,可以从node_modules获得外部依赖关系:
var express = require('express');
通过这种方式,所有的应用程序代码都被分层次地组织成模块,并且相对于应用程序根可以使用所有其他的代码。
当然,主要缺点是在文件浏览器中,不能展开/折叠树,就好像它实际上被组织到目录中一样。 但是,我喜欢所有代码都来自哪里,而且没有使用任何“魔法”。
Imho最简单的方法是通过创buildnode_modules/app
(或者你所说的任何东西)应用程序启动时的符号链接指向../app
。 然后你可以调用require("app/my/module")
。 符号链接可在所有主要平台上使用。
但是,你仍然应该把你的东西分成更小的,可维护的,通过npm安装的模块。 你也可以通过git-url安装你的私有模块,所以没有理由拥有一个单一的应用程序目录。
在您自己的项目中,您可以修改在根目录中使用的任何.js文件,并将其path添加到process.env
variables的属性中。 例如:
// in index.js process.env.root = __dirname;
之后,您可以随处访问该属性:
// in app.js express = require(process.env.root);
我已经尝试了许多这些解决scheme。 我结束了这个添加到我的主要文件的顶部(例如index.js):
process.env.NODE_PATH = __dirname; require('module').Module._initPaths();
当脚本加载时,这将项目根添加到NODE_PATH。 允许我通过引用项目根目录的相对path(例如var User = require('models/user')
来请求我的项目中的任何文件。 只要您在项目中运行其他任何项目之前运行主脚本,此解决scheme应该工作。
你可以使用我制作的模块, Undot 。 它没有什么先进的,只是一个帮手,所以你可以避免简单的地狱。
例:
var undot = require('undot'); var User = undot('models/user'); var config = undot('config'); var test = undot('test/api/user/auth');
如果有人正在寻找另一种解决这个问题的方法,这是我自己对这个努力的贡献:
基本思路是:在项目的根目录中创build一个JSON文件,将文件path映射为简写名称(或者使用use-automapper为您执行)。 然后您可以使用这些名称来请求您的文件/模块。 像这样:
var use = require('use-import'); var MyClass = use('MyClass');
所以就是这样
我喜欢做的是充分利用节点从node_module目录加载这个。
如果一个人试图加载模块“东西”,一个会做类似的事情
require('thing');
Node然后会在'node_module'目录中寻找'thing'目录。
由于node_module通常位于项目的根目录,所以我们可以利用这种一致性。 (如果node_module不在根目录下,那么你还有其他的自我引入麻烦。
如果我们进入目录,然后退出,我们可以得到一个一致的path到节点项目的根。
require('thing/../../');
那么如果我们想访问/ happy目录,我们会这样做。
require('thing/../../happy');
虽然它有点不好意思,但是我觉得如果node_modules如何加载更改的function,将会遇到更大的问题需要处理。 这种行为应该保持一致。
为了说清楚,我这样做,因为模块的名称并不重要。
require('root/../../happy');
我最近用了angular2。 我想从根加载服务。
import {MyService} from 'root/../../app/services/http/my.service';
我写了这个小软件包,可以让你从项目根目录的相对path中获取软件包,而不需要引入任何全局variables或覆盖节点默认值
https://github.com/Gaafar/pkg-require
它是这样工作的
// create an instance that will find the nearest parent dir containing package.json from your __dirname const pkgRequire = require('pkg-require')(__dirname); // require a file relative to the your package.json directory const foo = pkgRequire('foo/foo') // get the absolute path for a file const absolutePathToFoo = pkgRequire.resolve('foo/foo') // get the absolute path to your root directory const packageRootPath = pkgRequire.root()
不能在examples
目录中包含一个带有符号链接的node_modules
,该链接指向项目project -> ../../
的根目录project -> ../../
从而允许示例使用require('project')
,尽pipe这并不会删除映射,它确实允许源使用require('project')
而不是require('../../')
。
我已经testing了这个,它确实与v0.6.18一起工作。
project
目录清单:
$ ls -lR project project: drwxr-xr-x 3 user user 4096 2012-06-02 03:51 examples -rw-r--r-- 1 user user 49 2012-06-02 03:51 index.js project/examples: drwxr-xr-x 2 user user 4096 2012-06-02 03:50 node_modules -rw-r--r-- 1 user user 20 2012-06-02 03:51 test.js project/examples/node_modules: lrwxrwxrwx 1 user user 6 2012-06-02 03:50 project -> ../../
index.js
的内容为exports
对象的属性赋值,并调用console.log
并显示一条消息,声明它是必需的。 test.js
的内容是require('project')
。
我遇到了同样的问题,所以我写了一个名为include的包。
通过查找你的package.json文件, 包含句柄找出你的项目的根文件夹,然后把你给它的pathparameter passing给本地的require(),而没有所有的相对path混乱。 我想这不是require()的替代品,而是一个需要处理非打包/非第三方文件或库的工具。 就像是
var async = require('async'), foo = include('lib/path/to/foo')
我希望这可以是有用的。
如果您的应用程序的入口点js文件(即您实际运行“node”的文件)位于您的项目根目录中,则可以使用rootpath npm模块轻松完成此操作。 只需通过安装它
npm install --save rootpath
然后在入口点js文件的顶部添加:
require('rootpath')();
从这一点开始,所有需要调用的都是相对于项目根目录的,例如require('../../../config/debugging/log');
变成require('config/debugging/log');
(其中config文件夹在项目根目录中)。
简单的说,你可以调用你自己的文件夹作为模块:
为此我们需要:global和app-module-path模块
在这里“App-module-path”是模块,它使您能够将额外的目录添加到Node.js模块searchpath而“全局”是,任何附加到这个对象的任何东西都可以在您的应用程序的任何地方使用。
现在看看这个片段:
global.appBasePath = __dirname; require('app-module-path').addPath(appBasePath);
__dirname是node的当前运行目录。你可以在这里给自己的pathsearch模块的path。
一些答案是说,最好的方法是将代码添加到node_module作为一个包,我同意,它可能是最好的方式失去../../../
在要求,但没有一个真正给一个方法来这样做。
从版本2.0.0
你可以从本地文件安装一个包,这意味着你可以在你的根目录下创build你想要的所有包,
-modules --foo --bar -app.js -package.json
所以在package.json中,你可以添加modules
(或者foo
和bar
)作为一个包,而不需要像这样发布或者使用外部服务器:
{ "name": "baz", "dependencies": { "bar": "file: ./modules/bar", "foo": "file: ./modules/foo" } }
之后,你做了npm install
,你可以用var foo = require("foo")
来访问代码,就像你对所有其他的包所做的一样。
更多信息可以在这里find:
https://docs.npmjs.com/files/package.json#local-paths
在这里如何创build一个包:
https://docs.npmjs.com/getting-started/creating-node-modules
我们即将尝试新的方法来解决这个问题。
以Spring和Guice等已知项目为例,我们将定义一个“context”对象,它将包含所有的“require”语句。
这个对象将被传递给所有其他模块使用。
例如
var context = {} context.module1 = require("./module1")( { "context" : context } ) context.module2 = require("./module2")( { "context" : context } )
这就要求我们把每个模块写成一个接收opts的函数,无论如何,这对我们来说是最佳实践。
module.exports = function(context){ ... }
然后你会参考上下文,而不是要求的东西。
var module1Ref = context.moduel1;
如果你愿意,你可以轻松地写一个循环来执行require语句
var context = {}; var beans = {"module1" : "./module1","module2" : "./module2" }; for ( var i in beans ){ if ( beans.hasOwnProperty(i)){ context[i] = require(beans[i])(context); } };
当你想要模拟(testing)时,这应该使生活更容易,同时也可以解决你的问题,同时使你的代码作为一个包可重用。
您也可以通过从其中分离bean声明来重用上下文初始化代码。 例如,你的main.js
文件可能看起来像这样
var beans = { ... }; // like before var context = require("context")(beans); // this example assumes context is a node_module since it is reused..
这种方法也适用于外部库,每次我们需要它们时不需要硬编码它们的名字 – 但是它将需要特殊的处理,因为它们的输出不是期望上下文的函数。
稍后我们还可以将bean定义为函数 – 这将允许我们根据环境require
不同的模块 – 但是它不在这个线程的范围之内。
前段时间我创build了模块来加载相对于预定义path的模块。
您可以使用它而不是要求。
irequire.prefix('controllers',join.path(__dirname,'app/master')); var adminUsersCtrl = irequire("controllers:admin/users"); var net = irequire('net');
也许这对某个人来说是有用的..
虽然这些答案的工作, 他们不解决npmtesting的问题
例如,如果我在server.js中创build一个全局variables,它将不会被设置为执行我的testing套件。
要设置一个全局的appRootvariables来避免../../../问题,并且在npm start和npmtesting中都可用,请参阅:
摩卡testing与额外的选项或参数
Note that this is the new official mocha solution .
Just want to follow up on the great answer from Paolo Moretti and Browserify. If you are using a transpiler (eg, babel, typescript) and you have separate folders for source and transpiled code like src/
and dist/
, you could use a variation of the solutions as
node_modules
With the following directory structure:
app node_modules ... // normal npm dependencies for app src node_modules app ... // source code dist node_modules app ... // transpiled code
you can then let babel etc to transpile src
directory to dist
directory.
symlink
Using symlink we can get rid some levels of nesting:
app node_modules ... // normal npm dependencies for app src node_modules app // symlinks to '..' ... // source code dist node_modules app // symlinks to '..' ... // transpiled code
A caveat with babel –copy-files The --copy-files
flag of babel
does not deal with symlinks well. It may keep navigating into the ..
symlink and recusively seeing endless files. A workaround is to use the following directory structure:
app node_modules app // symlink to '../src' ... // normal npm dependencies for app src ... // source code dist node_modules app // symlinks to '..' ... // transpiled code
In this way, code under src
will still have app
resolved to src
, whereas babel would not see symlinks anymore.
Try using asapp:
npm install --save asapp
https://www.npmjs.com/package/asapp
var { controller, helper, middleware, route, schema, model, APP, ROOT } = require('asapp')
controller('home')
instead require('../../controllers/home)
Another answer :
Imagine this folders structure :
- node_modules
- lodash
- SRC
- subdir
- foo.js
- bar.js
- main.js
- subdir
-
testing
- test.js
Then in test.js , you need to require files like this :
const foo = require("../src/subdir/foo"); const bar = require("../src/subdir/bar"); const main = require("../src/main"); const _ = require("lodash");
and in main.js :
const foo = require("./subdir/foo"); const bar = require("./subdir/bar"); const _ = require("lodash");
Now you can use babel and the babel-plugin-module-resolver with this . babelrc file to configure 2 root folders:
{ "plugins": [ ["module-resolver", { "root": ["./src", "./src/subdir"] }] ] }
Now you can require files in the same manner in tests and in src :
const foo = require("foo"); const bar = require("bar"); const main = require("main"); const _ = require("lodash");
and if you want use the es6 module syntax:
{ "plugins": [ ["module-resolver", { "root": ["./src", "./src/subdir"] }], "transform-es2015-modules-commonjs" ] }
then you import files in tests and src like this :
import foo from "foo" import bar from "bar" import _ from "lodash"