UNPKG

@boost/core

Version:

Robust pipeline for creating dev tools that separate logic into routines and tasks.

146 lines (145 loc) 5.79 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); const common_1 = require("@boost/common"); const debug_1 = require("@boost/debug"); const formatModuleName_1 = __importDefault(require("./helpers/formatModuleName")); class ModuleLoader { constructor(tool, typeName, contract = null, scopes = []) { this.contract = null; this.contract = contract; this.debug = debug_1.createDebugger(`${typeName}-loader`); this.scopes = scopes; this.tool = tool; this.typeName = typeName; } /** * Import a class definition from a Node module and instantiate the class * with the provided options object. */ importModule(name, args = []) { const { typeName } = this; const { appName, scoped } = this.tool.options; // Determine modules to attempt to load const modulesToAttempt = []; let isFilePath = false; let importedModule = null; let moduleName; // File path if (name.match(/^\.|\/|\\|[A-Z]:/u)) { this.debug('Locating %s from path %s', typeName, chalk_1.default.cyan(name)); modulesToAttempt.push(path_1.default.normalize(name)); isFilePath = true; // Module name } else { this.debug('Locating %s module %s', typeName, chalk_1.default.yellow(name)); if (scoped) { modulesToAttempt.push(formatModuleName_1.default(appName, typeName, name, true)); } modulesToAttempt.push(formatModuleName_1.default(appName, typeName, name)); // Additional scopes to load this.scopes.forEach(otherScope => { modulesToAttempt.push(formatModuleName_1.default(otherScope, typeName, name, true), formatModuleName_1.default(otherScope, typeName, name)); }); this.debug('Resolving in order: %s', modulesToAttempt.join(', ')); } modulesToAttempt.some(modName => { try { importedModule = common_1.requireModule(modName); moduleName = modName; return true; } catch (error) { this.debug('Failed to import module: %s', error.message); return false; } }); if (!importedModule || !moduleName) { throw new Error(this.tool.msg('errors:moduleImportFailed', { modules: modulesToAttempt.join(', '), typeName, })); } if (!this.contract) { return importedModule; } // An instance was returned instead of the class definition if (common_1.instanceOf(importedModule, this.contract)) { throw new TypeError(this.tool.msg('errors:moduleClassInstanceExported', { appName, moduleName, typeName, })); } else if (typeof importedModule !== 'function') { throw new TypeError(this.tool.msg('errors:moduleClassDefRequired', { moduleName, typeName })); } const ModuleClass = importedModule; const module = new ModuleClass(...args); if (!common_1.instanceOf(module, this.contract)) { throw new TypeError(this.tool.msg('errors:moduleExportInvalid', { moduleName, typeName, })); } if (isFilePath) { this.debug('Found with file path %s', chalk_1.default.cyan(moduleName)); } else { this.debug('Found with module %s', chalk_1.default.yellow(moduleName)); module.name = name; module.moduleName = moduleName; } return module; } /** * If loading from an object, extract the module name and use the remaining object * as options for the class instance. */ importModuleFromOptions(baseOptions, args = []) { const { typeName } = this; const options = Object.assign({}, baseOptions); const module = options[typeName]; delete options[typeName]; if (!module || typeof module !== 'string') { throw new TypeError(this.tool.msg('errors:moduleOptionMissingKey', { typeName })); } const nextArgs = [...args]; if (nextArgs.length === 0) { nextArgs.push(options); } else if (common_1.isObject(nextArgs[0])) { nextArgs[0] = Object.assign({}, nextArgs[0], options); } return this.importModule(module, nextArgs); } /** * Load and or instantiate a module for the `typeName` configuration property. * If a class instance, use directly. If a string, attempt to load and * instantiate from a module. If an object, extract the name and run the previous. */ loadModule(module, args = []) { if (this.contract && common_1.instanceOf(module, this.contract)) { return module; } else if (typeof module === 'string') { return this.importModule(module, args); } else if (common_1.isObject(module)) { return this.importModuleFromOptions(module, args); } throw new TypeError(this.tool.msg('errors:moduleTypeInvalid', { typeName: this.typeName })); } /** * Load multiple modules. */ loadModules(modules = [], args = []) { return modules.map(module => this.loadModule(module, args)); } } exports.default = ModuleLoader;