skip to Main Content

Question:

How to import JavaScript files in NodeJS using file paths from project root? (instead of relative file paths)

In other words, my team is trying to transition from Syntax Style A to Syntax Style B (shown below):

Syntax Style A

const source = require('../../../../../../source.js');

Syntax Style B

const source = require('src/source.js'); // "src" is a direct subfolder of the project root

I have reviewed a lot of posts on this topic, and none of them can satisfy my project’s requirements…


Project Requirements:

  • Requirement #1: No external packages (such as Babel.js or Webpack), since disk space is limited. Also, we cannot install anything with "npm install".
  • Requirement #2: A JavaScript file should be able to import another JavaScript file using "project root" file paths, not relative file paths. (This will avoid long relative paths such as "../../../../../../../script.js", which can become unmanageable.)
  • Requirement #3: Right-clicking the imported class anywhere in the source code should bring up a context menu, with an option to "Go to Declaration" (which takes us to the imported file directly). Also, moving a JavaScript file to a different folder in the IDE should automatically update all references. We can use any IDE.
  • Requirement #4: To have a consistent way of importing scripts, we will be using NodeJS’s "require" keyword to import (instead of JavaScript’s "import" keyword).

Setup:

Below is my setup. Package.json was generated by WebStorm, and I haven’t changed it.

Screenshot_of_Project_Structure

Project_Root
├─-─ src
│    └── a1
│       └── a2
│           └── a3
│               └── Worker.js
├──- subFolder1
│    └── SubWorker.js
├─ main.js
└─ package.json

main.js

const Worker = require('./src/a1/a2/a3/Worker.js');
Worker.doWork();

Worker.js

// If I replace the following with Project Root path, such as:
//    const SubWorker = require('src/subFolder1/SubWorker.js');
// The script will fail with "Error: Cannot find module './src/subFolder1/SubWorker.js'."

const SubWorker = require('../../../subFolder1/SubWorker.js');

module.exports =
class Worker { 

        static doWork() {
                console.log("doing work");
                SubWorker.doMoreWork();
        }
}

SubWorker.js

module.exports =
class SubWorker {

        static doMoreWork() {
                console.log("doing more work");
        }

};

Attempt #1: path.resolve

I tried the "path.resolve" solution, as suggested by this post:


Attempt #2: app-module-path

  • Importing using relative paths in Node.js with ES Modules
    • Using app-module-path solves all three requirements, but the program will not run (which is an implied requirement): ⛔Error: Cannot find module ‘src/subFolder1/SubWorker.js’
    • Error was reproducible in both WebStorm 2022.3.2 and VS Code 1.76.2

Attempt #3: dynamic import()

Worker.js

const rootPath = 'src/subFolder1';
const SubWorker = require(`${rootPath}/SubWorker.js`);


Attempt #4: subpath imports


Expected Outcome:

Since NodeJs is one of the most important backend languages, I was expecting excellent IDE support for imports… Referencing a source file from Project Root should be a simple operation that is handled by the IDE (I shouldn’t need to use Babel.js or Webpack). Am I doing something wrong here?

I don’t know what I am doing wrong, since I have already followed standard practices:

  • I used latest versions of the most popular NodeJS IDEs (WebStorm 2022.3.2 and VS Code 1.76.2)
  • I used WebStorm’s "Create a new NodeJS project" wizard to create the project
  • I used the latest NodeJS (v18.15.0). (on Windows 10, v21H2)

Thanks!


Related Posts:

The Stack Overflow posts below are relevant, but do not address the specific issue in this post. (Please kindly search by the title on Stack Overflow, since the spam filter limited the number of links in this post, thanks!)

  • Title: NodeJS – Relative File Paths
    • Failed the same way as my Attempt #1 above
  • Title: Project root based relative paths for NodeJS modules
    • Violated Requirement #4 (the desired solution should use "require" instead of "import")
  • Title: is it possible to get rid of the relative paths on imports?
    • The top answer uses Babel.js, which violates Requirement #1
  • Title: How to import a JavaScript file to another JavaScript file
    • Does not address the question of relative path vs "project root" path
  • Title: How to use absolute/dynamic paths in NodeJs
    • Violated Requirement #4 (the desired solution should use "require" instead of "import")
  • Title: how to use absolute paths instead of relative paths in React or Next.js?
    • We are not using React or Next.js here.
  • Title: JavaScript Import file by absolute path
    • Violated Requirement #4 (the desired solution should use "require" instead of "import")
  • Title: javascript file with absolute file path
    • The script here was launched from the client-side web browser. This post deals with nodeJS scripts that are launched from the server-side backend.

4

Answers


  1. You can try by using require.resolve method that returns a String with the resolved path. You can then use this path to import the file using require.

    As per your code structure, this should work for you in Worker.js file:

    const resolvedPath = require.resolve('./SubWorker.js', { paths:['subFolder1'] }) 
    const SubWorker = require(resolvedPath);
    

    A point to note over here is that this approach has processing overhead.

    Update:

    The code works fine in WebStorm with the exact same folder structure as yours:

    enter image description here

    Also, you can see it working on StackBlitz

    Login or Signup to reply.
  2. Well, without violating your requirements, you can set the NODE_PATH environment variable to the root directory of your project.

    In your absolute path of your project, locate the main.js file and add this for testing purpose to see your project root path:

    const path = require('path');
    const projectRoot = path.resolve(__dirname);
    console.log('Project Root:', projectRoot);
    

    Now set the NODE_PATH environment variable to the project root before running your application. You can do this in your terminal:

    export NODE_PATH=/absolute/path/to/your/project/root
    

    Replace /absolute/path/to/your/project/root with the path you got from the first step.

    After that, you need to update the require in main.js:

    const Worker = require('src/a1/a2/a3/Worker.js');
    Worker.doWork();
    

    And your Worker.js

    const SubWorker = require('subFolder1/SubWorker.js');
    
    module.exports =
    class Worker { 
       static doWork() {
           console.log("doing work");
           SubWorker.doMoreWork();
       }
    }
    

    However, note that the NODE_PATH environment variable is deprecated since Node.js v16.9.0 and might be removed in future versions of Node.js. It is still supported in Node.js v16.x, but using it is not recommended for new projects. For future-proof solutions, you might want to consider using either ECMAScript modules with import maps or bundlers like webpack or rollup.
    You can read more about in the docs.

    Login or Signup to reply.
  3. look at these 2 npm packages, one of them should help you:

    1.app-root-path (https://www.npmjs.com/package/app-root-path)

    1. module-alias (https://www.npmjs.com/package/module-alias)
    Login or Signup to reply.
  4. 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. (In the codes below, #root is the project root.)


    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 all references to the file. 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 the reserved node_modules directory

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

    Updates in April 2023:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search