Dynamically load module in plugin from local project node_modules folder


#1

I am building a plugin for Atom.

In this plugin I want to provide the user a way to configure how the plugin works by using a config file which can be checked into source control. In this file I want the user to define a Javascript file or a module which the plugin should load and then run. Something like this

{
    "sessions": [{
        "context": "module-foo"
    }]
}

or

{
    "sessions": [{
        "context": "./local/file.js"
    }]
}

In my plugin I have

// ...
function loadContext(contextModuleName) {
   const context = require(contextModule);

   return context;
}
// ...

The problem is the module exists in the node_modules folder for the project which defines the config file. BUT the plugin runs in atom which is a different folder and thus has a different set of paths which “require” uses to check for modules.

I was hoping someone might have a idea on how to solve this or if there is a special Atom way of dealing with this.

At the moment the only thing I can think of is either adding the project folder to the list of Module paths which “require” uses to check.

or

I can build the full path myself, which at the moment I feel is a hack.

Thoughts?


#2

So I came up with this. Bear in mind that I consider this a bit of a hack and would prefer something more…standard.

import Path from 'path';

function loadContext(projectPath, contextModuleName) {
   // Adds the project path to the list of paths to check for modules.
   module.paths.push(projectPath);
   // add the same path plus node_modules
   module.paths.push(Path.join(projectPath, 'node_modules'));

   const context = require(contextModule);

   // remove both paths
   module.paths.pop();
   module.paths.pop();

   return context;
}

This works well but feels,… dirty. I hope someone out there knows of something or is a little more creative than me.

If I stick with this then the only improvement I can think of is to make sure that when I remove the paths that I target them directly with _.pull() rather than just a pop(). There is no guarantee that they are still the last ones in the list after I call require.


#3

HUZZAH!

I have found my answer lest someone has a better one.

import Resolve from 'resolve' // https://www.npmjs.com/package/resolve

function loadContext(projectPath, contextModuleName) {
   const contextResolution = Resolve.sync(contextModuleName, { basedir: projectPath });

   const context = require(contextResolution);

   return context;
}