Search code examples
node.jspackagerelative-path

Project root based relative paths for NodeJS modules


Dealing with module inclusion in my NodeJS projects I always struggle a bit in the way of specifying paths to other module files of the project to be required.

I obviously use relative paths. But this implies doing things like:

const helper = require("../../lib/util/helpers.js

This has some drawbacks:

  1. I always need to take in account where the file from which I requiring another module is placed.

  2. As the project grows I occasionally need to move some files to another place due to project restructuring so I need to review not only paths from files requiring it, but also the paths of all modules required in the file I am moving.

I always thought it would be much clearer if I could specify paths relative to the project root instead of the file itself. To be clear, that is where my package.json file (or node_modules directory) is placed.

I know I can resolve the path of any external module using require.resolve() and I though I could do something like path.dirname(path.dirname(path.dirname(require.resolve('express')))) but it seems too dirty to me.

Unfortunately, as far as I know, there isn't any require.modules_path or require.projectRoot property so I need to rely on some trick like that I mentioned above or traversing the whole tree up to the first parent directory containing a package.json file.

My question is: Is there any better solution which I am missing?


Solution

  • I approached an, even not perfect, acceptable solution by using a runtime Symbol to store project's root path as a property of the process object.

    Using a symbol to name that property we avoid any possibility of colliding with another process object properties, even with future ones.

    What I did was simply adding this two lines to my app.js (which in Express projects is placed in project root directory and required from the main app entry point bin/www where I also ensured it is the first required dependency):

    const $root = Symbol.for("projectRoot");                                         │     return new Promise(function (resolve, reject) {
    process[$root] = __dirname;
    

    After that, the only thing I need to do in all other modules is to repeat the first row at the very beginning:

    const $root = Symbol.for("projectRoot");
    

    ...and use it in all require statements. For example:

    const helper = require(process[$root]+"/lib/util/helpers.js
    

    Maybe it is far from perfect, but it works for me...