UNPKG

@itrocks/compose

Version:

Class compositions via configuration file, enabling mixins addition and module exports replacement

102 lines 4.44 kB
import { inherits } from '@itrocks/class-type'; import { Uses } from '@itrocks/uses'; import { normalize } from 'path'; const EXCLUDE = 2; const ORIGINAL = 1; const REPLACEMENT = 0; const cache = {}; const replacements = {}; function configPath(baseDir, config) { return normalize(require.resolve((config[0] === '/') ? (baseDir + '/' + config) : config)); } export function compose(baseDir, config) { // initReplacements for (let [module, configReplacements] of Object.entries(config)) { let moduleExport; [module, moduleExport] = module.split(':'); module = configPath(baseDir, module); moduleExport ??= 'default'; replacements[module] ??= {}; replacements[module][moduleExport] = []; for (let replacement of Array.isArray(configReplacements) ? configReplacements : [configReplacements]) { let replacementExport; [replacement, replacementExport] = replacement.split(':'); replacement = configPath(baseDir, replacement); replacementExport ??= 'default'; replacements[module][moduleExport].push({ script: replacement, export: replacementExport }); } } const Module = require('module'); const superRequire = Module.prototype.require; Module.prototype.require = function (file) { // resolve and normalize if (file.startsWith('.')) { file = this.path + (this.path.endsWith('/') ? file : ('/' + file)); } file = normalize(require.resolve(file)); // from cache if (cache[file]) { const which = cache[file][EXCLUDE].includes(this.filename) ? ORIGINAL : REPLACEMENT; return cache[file][which]; } // no replacement let exportEntries = replacements[file]; if (!exportEntries) { const original = superRequire.call(this, ...arguments); cache[file] = [original, original, []]; return original; } // require original const module = { __esModule: true }; const original = superRequire.call(this, ...arguments); const replacementFiles = new Array(); cache[file] = [module, original, replacementFiles]; Object.assign(module, original); // compose for (let [moduleExport, replacementEntries] of Object.entries(exportEntries)) { if (!original[moduleExport]) { if ((moduleExport === 'default') && !original.default) { moduleExport = Object.keys(original)[0]; } else { throw 'Not found original ' + file + ':' + moduleExport; } } const replacementTypes = replacementEntries.map(entry => { if (!replacementFiles.includes(entry.script)) { replacementFiles.push(entry.script); } const replacementModule = this.require(entry.script); if (!replacementModule[entry.export]) { if ((entry.export === 'default') && !replacementModule.default) { entry.export = Object.keys(replacementModule)[0]; } else { throw 'Not found replacement ' + entry.script + ':' + entry.export; } } return replacementModule[entry.export]; }); const originalType = original[moduleExport]; let replacementType; for (let replacementTypeIndex = 0; replacementTypeIndex < replacementTypes.length; replacementTypeIndex++) { if (inherits(replacementTypes[replacementTypeIndex], originalType)) { replacementType = replacementTypes.splice(replacementTypeIndex, 1)[0]; break; } } const replacementExport = replacementType ? (replacementTypes.length ? Uses(...replacementTypes)(replacementType) : replacementType) : Uses(...replacementTypes)(originalType); for (const [name, type] of Object.entries(original)) { if (type === originalType) { module[name] = replacementExport; } } } return module; }; } //# sourceMappingURL=compose.js.map