Search code examples
javascriptnode.jsmodulecommonjsworking-directory

Process.chdir() has no effect on require


Given the structure directory structure:

.
├── alpha.js
└── foo
    └── beta.js

And file contents

alpha.js

module.exports = "alpha"

foo/beta.js

var cwd = process.cwd()
process.chdir('../')
var alpha = require('./alpha.js')
console.log(alpha)
process.chdir(cwd)

From within foo/beta.js. I'd like to be able to trick require into thinking that the current working directory is the project root. The example above does not work when the following is run.

node ./foo/beta.js

However if I switch over the code to within foo/beta.js to the following. Reading the file from the system and passing it to the npm module _eval.

updated foo/beta.js

var path = require('path')
var cwd = process.cwd()
process.chdir(path.join(__dirname, '..'))
var fs = require('fs')
var _eval = require('eval')
var alpha = _eval(fs.readFileSync('./alpha.js'))
console.log(alpha)
process.chdir(cwd)

This does work, which proves it should be possible with require as well. No matter where you run if from it will always require the file. node ./foo/beta.js or cd foo && node ./beta.js

Is there any way that I can prepend or set the directory that require uses from within the file?


Solution

  • From the node.js doc for require():

    If the module identifier passed to require() is not a native module, and does not begin with '/', '../', or './', then node starts at the parent directory of the current module, and adds /node_modules, and attempts to load the module from that location.

    If it is not found there, then it moves to the parent directory, and so on, until the root of the file system is reached.

    From this, you can see that the current directory is not used in loading modules. The main takeaway here should be that modules paths are relative to the location of the current module. This allows modules to load sub-modules in a self-contained fashion without having to know anything about where the parent is placed in the directory structure.

    Here's a work-around function that loads a module file descriptor from the current directory if the filename passed to it does not start with a path separator or a ..

    var path = require('path');
    
    function requireCWD(fname) {
        var fullname = fname;
        if (fname && fname.length && 
          !path.isAbsolute(fname) &&
          fname.charAt(0) !== '.') {
            fullname = path.join(process.cwd(), fname);
        }
        return require(fullname);
    }
    

    Then, any filename you give to requireCWD that is not relative and does not start with a "." will be loaded relative to the current working directory. If you want to allow even "." and ".." to be relative to the current working directory, then you can remove that test for '.' from the function.