Totty.js
Totty.js

Reputation: 15841

How can I make Node.js 'require' absolute? (instead of relative)

I would like to 'require' my files always by the root of my project and not relative to the current module.

For example, if you look at Express.js' app.js line 6, you will see

express = require('../../')

That's really bad, IMO. Imagine I would like to put all my examples closer to the root only by one level. That would be impossible, because I would have to update more than 30 examples and many times within each example. To this:

express = require('../')

My solution would be to have a special case for root based: if a string starts with an $ then it's relative to the root folder of the project.

What can I do?

Update 2

Now I'm using RequireJS which allows you to write in one way and works both on client and on server. RequireJS also allows you to create custom paths.

Update 3

Now I moved to Webpack and Gulp.js and I use enhanced-require to handle modules on the server side. See here for the rationale: http://hackhat.com/p/110/module-loader-webpack-vs-requirejs-vs-browserify/

Upvotes: 278

Views: 228412

Answers (30)

howard_9
howard_9

Reputation: 603

Using the "Imports" property

As of March 2023, a good way to eliminate the NodeJS relative paths is to use the imports property in package.json. For more information, please refer to this post:

In the codes below, #root is the project root.

(Please kindly upvote this answer and this post if they help you. Thanks!)

For CommonJS-style JavaScripts:

// package.json
{
  "imports": {
    "#root/*.js": "./*.js"
  }
}

// main.js:
const Source = require('#root/path/to/Source.js');

// Source.js:
module.exports = class Source {
  // ...
}

For ECMAScript-style JavaScripts:

// package.json:
{
  "type" : "module",
  "imports": {
    "#root/*.js": "./*.js"
  }
}

// main.js
import { Source } from '#root/path/to/Source.js';

// Source.js:
export class Source {
  // ...
}

Advantages:

  • No need to "import" or "require" any additional packages (No Babel.js, No Webpack, No RequireJS). After installing NodeJS, this method works out of the box.

  • IDE linkages work as expected (Ctrl-click a class name to jump directly to the source file. Also, moving the source file (by drag and drop) will automatically update the file path references. Tested on WebStorm 2022.3.2 and VS Code 1.76.2.)

  • Works with both .mjs (ECMAScript module system) and .cjs (CommonJS) file types. Please see this reference Post on .cjs and .mjs.

  • No need to modify with the reserved node_modules directory

  • No need to set up any linux file links at the OS level

Upvotes: 2

vanwinter
vanwinter

Reputation: 153

Since none of the proposed solutions was quite as concise as I wanted, I wanted to share mine here.

I was looking for a solution that doesn't not 'require' anything additional, no modules, no package.json modifications, no environment variables, no external dependencies, minimal CPU cycles, etc.

...so I came up with this:

global.dir = process.cwd();

I do this once when the app loads. Obviously, you can modify the path by adding a subdirectory and/or adding '/..' which will send you up or down the directory tree as needed.

All my includes, in all files, are in this format:

const myFn = require(global.dir + '/js/myDir/myFn');

Upvotes: 0

Jack
Jack

Reputation: 667

My method of achieving this is to create "local link modules".

Take for example the folder structure of

db ¬
    models ¬
        index.js
    migrations
    seed
    config.json

routes ¬
    index.js
    user ¬
        index.js

If from ./routes/user/index.js I want to access /db/models/index.js, I'd be writing:

require('../../db/models/index.js')

To make the /db/models/index.js accessible from everywhere, I create a folder inside the db folder named _module_, this contains a package.json and a main.js file.

# package.json
{
    "name": "db", <-- change this to what you want your require name to be
    "version": "1.0.0",
    "description": "",
    "author": "",
    "repository": {},
    "main": "main.js"
}
// main.js
module.exports = require('../../db/models/index');

The path in main.js must be relative as if the file is in node_modules, like

node_modules ¬
    db ¬
        main.js

You can then run npm install ./db/_module_ and this will copy the files in ./db/_module_ to ./node_modules/db creating an entry under dependencies in the app's package.json like

"db": "file:db/_module_"

You can now use this package from anywhere by

const db = require('db');

This automatically installs with the rest of your modules when you run npm install, works cross-platform (no symbolic links), and doesn't require any third-party packages.

Upvotes: 0

Ole
Ole

Reputation: 47142

I just came across this article which mentions app-module-path. It allows you to configure a base like this:

require('app-module-path').addPath(baseDir);

Upvotes: 4

Troopers
Troopers

Reputation: 5452

Another answer:

Imagine this folders structure:

  • node_modules - lodash

  • src - subdir - foo.js - bar.js - main.js

  • tests

    - 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 two 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"

Upvotes: 7

fdorantesm
fdorantesm

Reputation: 3388

Try using asapp:

npm install --save asapp

var { controller, helper, middleware, route, schema, model, APP, ROOT } = require('asapp')

controller('home') instead require('../../controllers/home)

Upvotes: 0

user716468
user716468

Reputation: 1623

I just want to follow up on the great answer from Paolo Moretti and Browserify. If you are using a transpiler (e.g., 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 the src directory to the dist directory.

Symbolic link

Using a symbolic link, we can get rid some levels of nesting:

app
  node_modules
    ... // Normal npm dependencies for app
  src
    node_modules
      app // Symbolic links to '..'
    ... // Source code
  dist
    node_modules
      app // Symbolic links to '..'
    ... // Transpiled code

A caveat with babel --copy-files: The --copy-files flag of babel does not deal with symbolic links well. It may keep navigating into the .. symlink and recursively seeing endless files. A workaround is to use the following directory structure:

app
  node_modules
    app // Symbolic link to '../src'
    ... // Normal npm dependencies for app
  src
    ... // Source code
  dist
    node_modules
      app // Symbolic links to '..'
    ... // Transpiled code

In this way, code under src will still have app resolved to src, whereas Babel would not see symlinks anymore.

Upvotes: 2

danday74
danday74

Reputation: 57205

Whilst these answers work, they do not address the problem with npm test.

If, for example, I create a global variable in server.js, it will not be set for my test suite execution.

To set a global appRoot variable that will avoid the ../../../ problem and will be available in both npm start and npm test, see:

Mocha tests with extra options or parameters

Note that this is the new official Mocha solution.

Upvotes: 0

Yan Mayatskiy
Yan Mayatskiy

Reputation: 353

Some of the answers are saying that the best way is to add the code to the node_modules folder folder as a package. I agree and it's probably the best way to lose the ../../../ in require, but none of them actually give a way to do so.

From version 2.0.0, you can install a package from local files, which means you can create a folder in your root with all the packages you want,

-modules
 --foo
 --bar
-app.js
-package.json

so in package.json you can add the modules (or foo and bar) as a package without publishing or using an external server like this:

{
  "name": "baz",
  "dependencies": {
    "bar": "file: ./modules/bar",
    "foo": "file: ./modules/foo"
  }
}

After that you do npm install, and you can access the code with var foo = require("foo"), just like you do with all the other packages.

More information can be found on package.json.

And here is how to create a package: Creating Node.js modules

Upvotes: 10

Trojan
Trojan

Reputation: 1456

In simple lines, you can call your own folder as module:

For that we need: global and app-module-path module

Here "App-module-path" is the module. It enables you to add additional directories to the Node.js module search path. And "global" is anything that you attach to this object will be available everywhere in your app.

Now take a look at this snippet:

global.appBasePath = __dirname;

require('app-module-path').addPath(appBasePath);

__dirname is the current running directory of Node.js. You can give your own path here to search the path for module.

Upvotes: 1

justonpoints
justonpoints

Reputation: 116

What I like to do is leverage how Node.js loads from the node_modules directory for this.

If one tries to load the module "thing", one would do something like

require('thing');

Node.js will then look for the 'thing' directory in the 'node_modules' directory.

Since the node_modules folder is normally at the root of the project, we can leverage this consistency. (If node_modules is not at the root, then you have other self-induced headaches to deal with.)

If we go into the directory and then back out of it, we can get a consistent path to the root of the Node.js project.

require('thing/../../');

Then if we want to access the /happy directory, we would do this:

require('thing/../../happy');

Though it is quite a bit hacky, however I feel if the functionality of how node_modules load changes, there will be bigger problems to deal with. This behavior should remain consistent.

To make things clear, I do this, because the name of module does not matter.

require('root/../../happy');

I used it recently for Angular 2. I want to load a service from the root.

import {MyService} from 'root/../../app/services/http/my.service';

Upvotes: 2

Mateusz Russak
Mateusz Russak

Reputation: 106

Some time ago, I created a module for loading modules relative to pre-defined paths.

https://github.com/raaymax/irequire

You can use it instead of 'require'.

irequire.prefix('controllers',join.path(__dirname,'app/master'));
var adminUsersCtrl = irequire("controllers:admin/users");
var net = irequire('net');

Upvotes: 0

cronvel
cronvel

Reputation: 4085

Use:

var myModule = require.main.require('./path/to/module');

It requires the file as if it were required from the main JavaScript file, so it works pretty well as long as your main JavaScript file is at the root of your project... and that's something I appreciate.

Upvotes: 178

Anthony Nichols
Anthony Nichols

Reputation: 43

I was having trouble with this same issue, so I wrote a package called include.

Include handles figuring out your project's root folder by way of locating your package.json file, then passes the path argument you give it to the native require() without all of the relative path mess. I imagine this not as a replacement for require(), but a tool for requiring handling non-packaged / non-third-party files or libraries. Something like

var async = require('async'),
    foo   = include('lib/path/to/foo')

Upvotes: 1

Blair Anderson
Blair Anderson

Reputation: 20171

I like to make a new node_modules folder for shared code. Then let Node.js and 'require' do what they do best.

For example:

- node_modules // => these are loaded from your *package.json* file
- app
  - node_modules // => add node-style modules
    - helper.js
  - models
    - user
    - car
- package.json
- .gitignore

For example, if you're in car/index.js you can require('helper') and Node.js will find it!

How node_modules Work

Node.js has a clever algorithm for resolving modules that is unique among rival platforms.

If you require('./foo.js') from /beep/boop/bar.js, Node.js will look for ./foo.js in /beep/boop/foo.js. Paths that start with a ./ or ../ are always local to the file that calls require().

If, however, you 'require' a non-relative name such as require('xyz') from /beep/boop/foo.js, Node.js searches these paths in order, stopping at the first match and raising an error if nothing is found:

/beep/boop/node_modules/xyz
/beep/node_modules/xyz
/node_modules/xyz

For each xyz directory that exists, Node.js will first look for a xyz/package.json to see if a "main" field exists. The "main" field defines which file should take charge if you require() the directory path.

For example, if /beep/node_modules/xyz is the first match and /beep/node_modules/xyz/package.json has:

{
  "name": "xyz",
  "version": "1.2.3",
  "main": "lib/abc.js"
}

then the exports from /beep/node_modules/xyz/lib/abc.js will be returned by require('xyz').

If there is no package.json or no "main" field, index.js is assumed:

/beep/node_modules/xyz/index.js

Upvotes: 95

guy mograbi
guy mograbi

Reputation: 28708

We are about to try a new way to tackle this problem.

Taking examples from other known projects like Spring Framework and Guice, we will define a "context" object which will contain all the "require" statement.

This object will then be passed to all other modules for use.

For example,

var context = {}

context.module1 = require("./module1")( { "context" : context } )
context.module2 = require("./module2")( { "context" : context } )

This requires us to write each module as a function that receives opts, which looks to us as a best practice anyway...

module.exports = function(context){ ... }

And then you will refer to the context instead of requiring stuff.

var module1Ref = context.moduel1;

If you want to, you can easily write a loop to do the 'require' statements

var context = {};
var beans = {"module1" : "./module1","module2" : "./module2" };
for ( var i in beans ){
    if ( beans.hasOwnProperty(i)){
         context[i] = require(beans[i])(context);
    }
};

This should make life easier when you want to mock (tests) and also solves your problem along the way while making your code reusable as a package.

You can also reuse the context initialization code by separating the beans declaration from it.

For example, your main.js file could look like so

var beans = { ... }; // like before
var context = require("context")(beans); // This example assumes context is a node_module since it is reused..

This method also applies to external libraries, and there isn't any need to hard code their names every time we require them. However, it will require a special treatment as their exports are not functions that expect context...

Later on, we can also define beans as functions—which will allow us to require different modules according to the environment—but that it out of this question's scope.

Upvotes: 1

Nadav Leshem
Nadav Leshem

Reputation: 193

I created a node module called rekuire.

It allows you to 'require' without the use of relative paths.

It is super easy to use.

Upvotes: 1

Ditmar Wendt
Ditmar Wendt

Reputation: 668

I don't think you need to solve this in the manner you described. Just use sed if you want to change the same string in a large amount of files. In your example,

find . -name "*.js" -exec sed -i 's/\.\.\/\.\.\//\.\.\//g' {} +

would have ../../ changed to ../

Alternatively, you can require a configuration file that stores a variable containing the path to the library. If you store the following as config.js in the example directory

var config = {};
config.path = '../../';

and in your example file

myConfiguration = require('./config');
express = require(config.path);

You'll be able to control the configuration for every example from one file.

It's really just personal preference.

Upvotes: -1

Panu Logic
Panu Logic

Reputation: 2271

Many good answers here already. That just shows this is a common problem without clear-cut best solution. Best would be native support in Node.js of course. Here's what I use currently:

const r  = p => require (process.cwd() + p);
let see  = r ('/Subs/SubA/someFile.js' );
let see2 = r ('/Subs/SubB/someFile2.js');
...

I like this solution because the requires-section becomes shorter, no need to type 'require' many times. The main benefit of absolute paths is you can copy them from file to file without having to adjust them like you would with relative paths. Therefore copying also the extra one-liner arrow-function 'r()' is not too much extra work either. And no need to import extra npm-dependencies just to accomplish this very simple task.

Upvotes: -2

Dan D.
Dan D.

Reputation: 74675

Manual Symlinks (and Windows Junctions)

Couldn't the examples directory contain a node_modules with a symbolic link to the root of the project project -> ../../ thus allowing the examples to use require('project'), although this doesn't remove the mapping, it does allow the source to use require('project') rather than require('../../').

I have tested this, and it does work with v0.6.18.

Listing of project directory:

$ 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 -> ../../

The contents of index.js assigns a value to a property of the exports object and invokes console.log with a message that states it was required. The contents of test.js is require('project').

Automated Symlinks

The problem with manually creating symlinks is that every time you npm ci, you lose the symlink. If you make the symlink process a dependency, viola, no problems.

The module basetag is a postinstall script that creates a symlink (or Windows junction) named $ every time npm install or npm ci is run:

npm install --save basetag
node_modules/$ -> ..

With that, you don't need any special modification to your code or require system. $ becomes the root from which you can require.

var foo = require('$/lib/foo.js');

If you don't like the use of $ and would prefer # or something else (except @, which is a special character for npm), you could fork it and make the change.

Note: Although Windows symlinks (to files) require admin permissions, Windows junctions (to directories) do not need Windows admin permissions. This is a safe, reliable, cross-platform solution.

Upvotes: 6

janniks
janniks

Reputation: 3180

I had the same problem many times. This can be solved by using the basetag npm package. It doesn't have to be required itself, only installed as it creates a symlink inside node_modules to your base path.

const localFile = require('$/local/file')
// instead of
const localFile = require('../../local/file')

Using the $/... prefix will always reference files relative to your apps root directory.

Source: How I created basetag to solve this problem

Upvotes: 2

Talha Imam
Talha Imam

Reputation: 1116

I was looking for the exact same simplicity to require files from any level and I found module-alias.

Just install:

npm i --save module-alias

Open your package.json file, here you can add aliases for your paths, for e.g.

"_moduleAliases": {
 "@root"      : ".", // Application's root
 "@deep"      : "src/some/very/deep/directory/or/file",
 "@my_module" : "lib/some-file.js",
 "something"  : "src/foo", // Or without @. Actually, it could be any string
}

And use your aliases by simply:

require('module-alias/register')
const deep = require('@deep')
const module = require('something')

Upvotes: 3

cyberwombat
cyberwombat

Reputation: 40133

If you are using yarn instead of npm you can use workspaces.

Let's say I have a folder services I wish to require more easily:

.
├── app.js
├── node_modules
├── test
├── services
│   ├── foo
│   └── bar
└── package.json

To create a Yarn workspace, create a package.json file inside the services folder:

{
  "name": "myservices",
  "version": "1.0.0"
}

In your main package.json add:

"private": true,
"workspaces": ["myservices"]

Run yarn install from the root of the project.

Then, anywhere in your code, you can do:

const { myFunc } = require('myservices/foo')

instead of something like:

const { myFunc } = require('../../../../../../services/foo')

Upvotes: 23

fdorantesm
fdorantesm

Reputation: 3388

If you're using ES5 syntax you may use asapp. For ES6 you may use babel-plugin-module-resolver using a config file like this:

.babelrc

{
  "plugins": [
    ["module-resolver", {
      "root": ["./"],
      "alias": {
        "app": "./app",
        "config": "./app/config",
        "schema": "./app/db/schemas",
        "model": "./app/db/models",
        "controller": "./app/http/controllers",
        "middleware": "./app/http/middleware",
        "route": "./app/http/routes",
        "locale": "./app/locales",
        "log": "./app/logs",
        "library": "./app/utilities/libraries",
        "helper": "./app/utilities/helpers",
        "view": "./app/views"
      }
    }]
  ]
}

Upvotes: 1

gafi
gafi

Reputation: 12794

I wrote this small package that lets you require packages by their relative path from project root, without introducing any global variables or overriding node defaults

https://github.com/Gaafar/pkg-require

It works like this

// 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()

Upvotes: 2

Castor
Castor

Reputation: 91

You could use a module I made, Undot. It is nothing advanced, just a helper so you can avoid those dot hell with simplicity.

Example:

var undot = require('undot');
var User = undot('models/user');
var config = undot('config');
var test = undot('test/api/user/auth');

Upvotes: 8

Jon Stout
Jon Stout

Reputation: 51

If anyone's looking for yet another way to get around this problem, here's my own contribution to the effort:

https://www.npmjs.com/package/use-import

The basic idea: you create a JSON file in the root of the project that maps your filepaths to shorthand names (or get use-automapper to do it for you). You can then request your files/modules using those names. Like so:

var use = require('use-import');
var MyClass = use('MyClass');

So there's that.

Upvotes: 2

Andrew Faulkner
Andrew Faulkner

Reputation: 3940

If your app's entry point js file (i.e. the one you actually run "node" on) is in your project root directory, you can do this really easily with the rootpath npm module. Simply install it via

npm install --save rootpath

...then at the very top of the entry point js file, add:

require('rootpath')();

From that point forward all require calls are now relative to project root - e.g. require('../../../config/debugging/log'); becomes require('config/debugging/log'); (where the config folder is in the project root).

Upvotes: 1

senornestor
senornestor

Reputation: 4165

I have tried many of these solutions. I ended up adding this to the top of my main file (e.g. index.js):

process.env.NODE_PATH = __dirname;
require('module').Module._initPaths();

This adds the project root to the NODE_PATH when the script is loaded. The allows me to require any file in my project by referencing its relative path from the project root such as var User = require('models/user'). This solution should work as long as you are running a main script in the project root before running anything else in your project.

Upvotes: 10

Walter Roman
Walter Roman

Reputation: 4779

I use process.cwd() in my projects. For example:

var Foo = require(process.cwd() + '/common/foo.js');

It might be worth noting that this will result in requireing an absolute path, though I have yet to run into issues with this.

Upvotes: 15

Related Questions