/** * @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} The module's object. */ import(specifier) { const location = this.resolve(specifier); return import(pathToFileURL(location).href); } }