Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map paths for Node modules, for unit testing

Client side I stub out paths to modules with SystemJS, like this

var systemJsConfig = {
    baseURL: "./",
    defaultJSExtensions: true,
    map: {
        'root-components': 'applicationRoot/rootComponents'
    }
};

and so require('root-components/foo'); would map to applicationRoot/rootComponents/foo.

The problem is, if I run a module with require('root-components/foo'); in Mocha, Node has no clue what that path means. Is there a sane way to accomplish this path mapping in Node?

Is Proxyquire capable of this? I read through their docs but found nothing to indicate it was.

This is just for unit testing, so I'm happy with any solution using any sort of their party utility.

like image 591
Adam Rackis Avatar asked Jun 02 '16 15:06

Adam Rackis


3 Answers

If your only requirement is so simple you can make a utility function that overrides require.

The new require applies a mapping to the path argument, looks for a module in the new path, and you can optionally fallback for modules that do not exist in the mapped path. The code should look like this

const oldRequire = require;
require = (path) => {
    try {
        const mapped = oldRequire(path.replace('root-components/foo', 'applicationRoot/rootComponents/foo'));
        if (!mapped) {
            throw new Error('module not found in mapped directory');
        }
        console.log('module resolved with mapping', path);
        return mapped;
    } catch (e) {
        console.log('using old require without mapped path', path);
        return oldRequire(path);
    }
};
like image 87
gafi Avatar answered Oct 21 '22 01:10

gafi


Option 1 - Modify NODE_PATH(not recommended):

Modify NODE_PATH to include the module path in the shell prior to launching node.js.

exports NODE_PATH=./path/to/module:$NODE_PATH

This is not a great option because it requires a pre-launch step and -- since the NODE_PATH contains many paths -- it's not always clear where the module is being loaded from and there's the possibility of name collisions.

Option 2 - Move the module into an external repo

Lets say you move the components into a separate 'rootcomponents' repo available on your GitHub profile.

Then you can install it directly via:

npm install --save github:arackaf/rootcomponents

Then you should be able to map the project source to a System.js alias.

var systemJsConfig = {
    baseURL: "./",
    defaultJSExtensions: true,
    map: {
        'root-components': 'github:arackaf/rootcomponents'
    }
};

From there it should work as you expected:

require('root-components/foo');

Option 3 - Load the module via relative path:

The config.map option is only for mapping external dependencies to aliases.

One simple alternative is to provide a relative path. Relatives paths are based on the baseURL.

For instance, If you're attempting to load:

src/rootComponents/foo.js

The require would be:

require('./src/rootComponents/foo')

Note: This all assumes that the require() statements are following System.js patterns/rules.

One other possible option is to provide a System.paths[] option that creates an alias to a local path. I can't verify how/whether this'll work (ie I have never tried it) but the specifics can be found here

like image 28
Evan Plaice Avatar answered Oct 21 '22 01:10

Evan Plaice


You could do module aliasing a lot of different ways as described in this gist, but the simpler way is just to set the starting root path with NODE_PATH, and go from there:

Here's what it looks like with the NODE_PATH environment variable:

NODE_PATH=./src node src/server/index.js

Then in all your files, no matter where they are in the hierarchy, will resolve from the ./src directory. This eliminates the need for aliases like you describe, but I realize that in your case, may require moving/renaming many files.

Example: require('root-components/foo'); => ./src/root-components/foo.js

like image 40
Vance Lucas Avatar answered Oct 21 '22 00:10

Vance Lucas