Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js require a file relative to the root of the package

In Node.js, is there any way to require file from the same package without using relative paths? For example, here's a snippet of code from ESLint.

const rule = require("../../../lib/rules/accessor-pairs"),
    { RuleTester } = require("../../../lib/rule-tester");

The fact that we have to walk all the way up the tree ../../../ to get to the root is not only annoying. It's also brittle, because I can't move the code without updating all of dependency references.

Yet somehow Node.js developers seem to have lived with it the past 10 years. I can't find anything in the docs or Stack Overflow that solves this problem other than a third-party dependency called require-self. Nor have I been able to find a definitive statement that using relative paths is the only non-hacky way for a file to require another file in the same module.

If there's a way to specify a path relative to the package root in ECMAScript Modules (ESM) but not CommonJS (CJS), or vice versa, I would like to know that as well.

To be clear, I don't think there is a solution to the problem. If there is great. Otherwise, I'm looking for confirmation with an authoritative reference.

like image 565
Patrick McElhaney Avatar asked Oct 15 '22 22:10

Patrick McElhaney


2 Answers

Not necessarily the same package - if you are writing libraries this won't be useful, but if you are writing the "final application" - the thing that actually gets run:

One option:

If the NODE_PATH environment variable is set to a colon-delimited list of absolute paths, then Node.js will search those paths for modules if they are not found elsewhere.

So you can do any of:

1.

export NODE_PATH=.
node app.js
NODE_PATH=. node app.js
// app.js (or whatever your entry point is) before *any* require() calls
process.env.NODE_PATH = __dirname;
require('module').Module._initPaths();

Or, another way:

The Module object representing the entry script loaded when the Node.js process launched.

https://nodejs.org/api/modules.html#modules_require_main

So you can just do:

const rule = require.main.require("./lib/rules/accessor-pairs")

anytime you want it to be relative to the root (assuming that is how you have your project structured).

like image 101
dave Avatar answered Oct 19 '22 08:10

dave


You can use the package name itself as a "symlink" to the package root.


Example - foo package imports bar script relative to the foo package root.

package.json

{
    "dependencies": {
        "foo": "file:./foo"
    }
}

index.js

const foo = require('foo');
console.log(foo.bar);  // prints "hello"

foo/index.js

const bar = require('foo/bar');  // import relative to the package root

module.exports = {
    bar: bar
}

foo/bar.js

module.exports = 'hello';
like image 2
Petr Hejda Avatar answered Oct 19 '22 08:10

Petr Hejda