module-importer.cjs 2.51 KB
/**
 * @fileoverview Universal module importer
 */

//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------

const { createRequire } = require("module");
const { pathToFileURL }  = require("url");

//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------

const SLASHES = new Set(["/", "\\"]);

/**
 * Normalizes directories to have a trailing slash.
 * Resolve is pretty finicky -- if the directory name doesn't have
 * a trailing slash then it tries to look in the parent directory.
 * i.e., if the directory is "/usr/nzakas/foo" it will start the 
 * search in /usr/nzakas. However, if the directory is "/user/nzakas/foo/",
 * then it will start the search in /user/nzakas/foo.
 * @param {string} directory The directory to check. 
 * @returns {string} The normalized directory.
 */
function normalizeDirectory(directory) {
    if (!SLASHES.has(directory[directory.length-1])) {
        return directory + "/";
    }

    return directory;
}

//-----------------------------------------------------------------------------
// Exports
//-----------------------------------------------------------------------------

/**
 * Class for importing both CommonJS and ESM modules in Node.js.
 */
exports.ModuleImporter = class ModuleImporter {

    /**
     * Creates a new instance.
     * @param {string} [cwd] The current working directory to resolve from. 
     */
    constructor(cwd = process.cwd()) {

        /**
         * The base directory from which paths should be resolved.
         * @type {string}
         */
        this.cwd = normalizeDirectory(cwd);
    }

    /**
     * Resolves a module based on its name or location.
     * @param {string} specifier Either an npm package name or
     *      relative file path.
     * @returns {string|undefined} The location of the import.
     * @throws {Error} If specifier cannot be located.
     */
    resolve(specifier) {
        const require = createRequire(this.cwd);
        return require.resolve(specifier);
    }

    /**
     * Imports a module based on its name or location.
     * @param {string} specifier Either an npm package name or
     *      relative file path.
     * @returns {Promise<object>} The module's object.
     */
    import(specifier) {
        const location = this.resolve(specifier);
        return import(pathToFileURL(location).href);
    }

}