GeezerVison is showing github.com — esm: refactor dynamic modules · nodejs/node@1743568
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js +13-12Lines changed: 13 additions & 12 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -623,23 +623,24 @@ Module.prototype.load = function(filename) { | 623 | 623 | if (experimentalModules) { | 624 | 624 | if (asyncESM === undefined) lazyLoadESM(); | 625 | 625 | const ESMLoader = asyncESM.ESMLoader; | 626 | | - const url = pathToFileURL(filename); | 627 | | - const urlString = `${url}`; | | 626 | + const url = `${pathToFileURL(filename)}`; | | 627 | + const module = ESMLoader.moduleMap.get(url); | | 628 | + // create module entry at load time to snapshot exports correctly | 628 | 629 | const exports = this.exports; | 629 | | - if (ESMLoader.moduleMap.has(urlString) !== true) { | | 630 | + if (module !== undefined) { // called from cjs translator | | 631 | + module.reflect.onReady((reflect) => { | | 632 | + reflect.exports.default.set(exports); | | 633 | + }); | | 634 | + } else { // preemptively cache | 630 | 635 | ESMLoader.moduleMap.set( | 631 | | - urlString, | | 636 | + url, | 632 | 637 | new ModuleJob(ESMLoader, url, async () => { | 633 | | - const ctx = createDynamicModule( | 634 | | - ['default'], url); | 635 | | - ctx.reflect.exports.default.set(exports); | 636 | | - return ctx; | | 638 | + return createDynamicModule( | | 639 | + ['default'], url, (reflect) => { | | 640 | + reflect.exports.default.set(exports); | | 641 | + }); | 637 | 642 | }) | 638 | 643 | ); | 639 | | - } else { | 640 | | - const job = ESMLoader.moduleMap.get(urlString); | 641 | | - if (job.reflect) | 642 | | - job.reflect.exports.default.set(exports); | 643 | 644 | } | 644 | 645 | } | 645 | 646 | }; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/create_dynamic_module.js +41-44Lines changed: 41 additions & 44 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | 'use strict'; | 2 | 2 |
| 3 | | -const { ModuleWrap } = internalBinding('module_wrap'); | | 3 | +const { ModuleWrap, callbackMap } = internalBinding('module_wrap'); | 4 | 4 | const debug = require('util').debuglog('esm'); | 5 | 5 | const ArrayJoin = Function.call.bind(Array.prototype.join); | 6 | 6 | const ArrayMap = Function.call.bind(Array.prototype.map); | @@ -10,50 +10,47 @@ const createDynamicModule = (exports, url = '', evaluate) => { | 10 | 10 | `creating ESM facade for ${url} with exports: ${ArrayJoin(exports, ', ')}` | 11 | 11 | ); | 12 | 12 | const names = ArrayMap(exports, (name) => `${name}`); | 13 | | - // Create two modules: One whose exports are get- and set-able ('reflective'), | 14 | | - // and one which re-exports all of these but additionally may | 15 | | - // run an executor function once everything is set up. | 16 | | - const src = ` | 17 | | - export let executor; | 18 | | - ${ArrayJoin(ArrayMap(names, (name) => `export let $${name};`), '\n')} | 19 | | - /* This function is implicitly returned as the module's completion value */ | 20 | | - (() => ({ | 21 | | - setExecutor: fn => executor = fn, | 22 | | - reflect: { | 23 | | - exports: { ${ | 24 | | - ArrayJoin(ArrayMap(names, (name) => ` | 25 | | - ${name}: { | 26 | | - get: () => $${name}, | 27 | | - set: v => $${name} = v | 28 | | - }`), ', \n')} | 29 | | - } | 30 | | - } | 31 | | - }));`; | 32 | | - const reflectiveModule = new ModuleWrap(src, `cjs-facade:${url}`); | 33 | | - reflectiveModule.instantiate(); | 34 | | - const { setExecutor, reflect } = reflectiveModule.evaluate(-1, false)(); | 35 | | - // public exposed ESM | 36 | | - const reexports = ` | 37 | | - import { | 38 | | - executor, | 39 | | - ${ArrayMap(names, (name) => `$${name}`)} | 40 | | - } from ""; | 41 | | - export { | 42 | | - ${ArrayJoin(ArrayMap(names, (name) => `$${name} as ${name}`), ', ')} | 43 | | - } | 44 | | - if (typeof executor === "function") { | 45 | | - // add await to this later if top level await comes along | 46 | | - executor() | 47 | | - }`; | 48 | | - if (typeof evaluate === 'function') { | 49 | | - setExecutor(() => evaluate(reflect)); | 50 | | - } | 51 | | - const module = new ModuleWrap(reexports, `${url}`); | 52 | | - module.link(async () => reflectiveModule); | 53 | | - module.instantiate(); | 54 | | - reflect.namespace = module.namespace(); | | 13 | + | | 14 | + const source = ` | | 15 | +${ArrayJoin(ArrayMap(names, (name) => | | 16 | + `let $${name}; | | 17 | +export { $${name} as ${name} }; | | 18 | +import.meta.exports.${name} = { | | 19 | + get: () => $${name}, | | 20 | + set: (v) => $${name} = v, | | 21 | +};`), '\n') | | 22 | +} | | 23 | +
| | 24 | +import.meta.done(); | | 25 | +`; | | 26 | + | | 27 | + const m = new ModuleWrap(source, `${url}`); | | 28 | + m.link(() => 0); | | 29 | + m.instantiate(); | | 30 | + | | 31 | + const readyfns = new Set(); | | 32 | + const reflect = { | | 33 | + namespace: m.namespace(), | | 34 | + exports: {}, | | 35 | + onReady: (cb) => { readyfns.add(cb); }, | | 36 | + }; | | 37 | + | | 38 | + callbackMap.set(m, { | | 39 | + initializeImportMeta: (meta, wrap) => { | | 40 | + meta.exports = reflect.exports; | | 41 | + meta.done = () => { | | 42 | + evaluate(reflect); | | 43 | + reflect.onReady = (cb) => cb(reflect); | | 44 | + for (const fn of readyfns) { | | 45 | + readyfns.delete(fn); | | 46 | + fn(reflect); | | 47 | + } | | 48 | + }; | | 49 | + }, | | 50 | + }); | | 51 | + | 55 | 52 | return { | 56 | | - module, | | 53 | + module: m, | 57 | 54 | reflect, | 58 | 55 | }; | 59 | 56 | }; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/translators.js +4-3Lines changed: 4 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -60,9 +60,10 @@ translators.set('cjs', async (url, isMain) => { | 60 | 60 | const module = CJSModule._cache[ | 61 | 61 | isWindows ? StringReplace(pathname, winSepRegEx, '\\') : pathname]; | 62 | 62 | if (module && module.loaded) { | 63 | | - const ctx = createDynamicModule(['default'], url); | 64 | | - ctx.reflect.exports.default.set(module.exports); | 65 | | - return ctx; | | 63 | + const exports = module.exports; | | 64 | + return createDynamicModule(['default'], url, (reflect) => { | | 65 | + reflect.exports.default.set(exports); | | 66 | + }); | 66 | 67 | } | 67 | 68 | return createDynamicModule(['default'], url, () => { | 68 | 69 | debug(`Loading CJSModule ${url}`); | |
|