esm: refactor dynamic modules
This is a change from the ecmascript-modules fork. There is no change to behavior and we would like to upstream to reduce the delta between our repos. Refs: https://github.com/nodejs/ecmascript-modules#9 PR-URL: https://github.com/nodejs/node/pull/24560 Refs: https://github.com/nodejs/ecmascript-modules/pull/9 Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Gus Caplan <me@gus.host>
This commit is contained in:
parent
e03bcb1261
commit
2931c50a42
@ -623,23 +623,24 @@ Module.prototype.load = function(filename) {
|
||||
if (experimentalModules) {
|
||||
if (asyncESM === undefined) lazyLoadESM();
|
||||
const ESMLoader = asyncESM.ESMLoader;
|
||||
const url = pathToFileURL(filename);
|
||||
const urlString = `${url}`;
|
||||
const url = `${pathToFileURL(filename)}`;
|
||||
const module = ESMLoader.moduleMap.get(url);
|
||||
// create module entry at load time to snapshot exports correctly
|
||||
const exports = this.exports;
|
||||
if (ESMLoader.moduleMap.has(urlString) !== true) {
|
||||
if (module !== undefined) { // called from cjs translator
|
||||
module.reflect.onReady((reflect) => {
|
||||
reflect.exports.default.set(exports);
|
||||
});
|
||||
} else { // preemptively cache
|
||||
ESMLoader.moduleMap.set(
|
||||
urlString,
|
||||
url,
|
||||
new ModuleJob(ESMLoader, url, async () => {
|
||||
const ctx = createDynamicModule(
|
||||
['default'], url);
|
||||
ctx.reflect.exports.default.set(exports);
|
||||
return ctx;
|
||||
return createDynamicModule(
|
||||
['default'], url, (reflect) => {
|
||||
reflect.exports.default.set(exports);
|
||||
});
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const job = ESMLoader.moduleMap.get(urlString);
|
||||
if (job.reflect)
|
||||
job.reflect.exports.default.set(exports);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { ModuleWrap } = internalBinding('module_wrap');
|
||||
const { ModuleWrap, callbackMap } = internalBinding('module_wrap');
|
||||
const debug = require('util').debuglog('esm');
|
||||
const ArrayJoin = Function.call.bind(Array.prototype.join);
|
||||
const ArrayMap = Function.call.bind(Array.prototype.map);
|
||||
@ -10,50 +10,47 @@ const createDynamicModule = (exports, url = '', evaluate) => {
|
||||
`creating ESM facade for ${url} with exports: ${ArrayJoin(exports, ', ')}`
|
||||
);
|
||||
const names = ArrayMap(exports, (name) => `${name}`);
|
||||
// Create two modules: One whose exports are get- and set-able ('reflective'),
|
||||
// and one which re-exports all of these but additionally may
|
||||
// run an executor function once everything is set up.
|
||||
const src = `
|
||||
export let executor;
|
||||
${ArrayJoin(ArrayMap(names, (name) => `export let $${name};`), '\n')}
|
||||
/* This function is implicitly returned as the module's completion value */
|
||||
(() => ({
|
||||
setExecutor: fn => executor = fn,
|
||||
reflect: {
|
||||
exports: { ${
|
||||
ArrayJoin(ArrayMap(names, (name) => `
|
||||
${name}: {
|
||||
get: () => $${name},
|
||||
set: v => $${name} = v
|
||||
}`), ', \n')}
|
||||
}
|
||||
}
|
||||
}));`;
|
||||
const reflectiveModule = new ModuleWrap(src, `cjs-facade:${url}`);
|
||||
reflectiveModule.instantiate();
|
||||
const { setExecutor, reflect } = reflectiveModule.evaluate(-1, false)();
|
||||
// public exposed ESM
|
||||
const reexports = `
|
||||
import {
|
||||
executor,
|
||||
${ArrayMap(names, (name) => `$${name}`)}
|
||||
} from "";
|
||||
export {
|
||||
${ArrayJoin(ArrayMap(names, (name) => `$${name} as ${name}`), ', ')}
|
||||
}
|
||||
if (typeof executor === "function") {
|
||||
// add await to this later if top level await comes along
|
||||
executor()
|
||||
}`;
|
||||
if (typeof evaluate === 'function') {
|
||||
setExecutor(() => evaluate(reflect));
|
||||
}
|
||||
const module = new ModuleWrap(reexports, `${url}`);
|
||||
module.link(async () => reflectiveModule);
|
||||
module.instantiate();
|
||||
reflect.namespace = module.namespace();
|
||||
|
||||
const source = `
|
||||
${ArrayJoin(ArrayMap(names, (name) =>
|
||||
`let $${name};
|
||||
export { $${name} as ${name} };
|
||||
import.meta.exports.${name} = {
|
||||
get: () => $${name},
|
||||
set: (v) => $${name} = v,
|
||||
};`), '\n')
|
||||
}
|
||||
|
||||
import.meta.done();
|
||||
`;
|
||||
|
||||
const m = new ModuleWrap(source, `${url}`);
|
||||
m.link(() => 0);
|
||||
m.instantiate();
|
||||
|
||||
const readyfns = new Set();
|
||||
const reflect = {
|
||||
namespace: m.namespace(),
|
||||
exports: {},
|
||||
onReady: (cb) => { readyfns.add(cb); },
|
||||
};
|
||||
|
||||
callbackMap.set(m, {
|
||||
initializeImportMeta: (meta, wrap) => {
|
||||
meta.exports = reflect.exports;
|
||||
meta.done = () => {
|
||||
evaluate(reflect);
|
||||
reflect.onReady = (cb) => cb(reflect);
|
||||
for (const fn of readyfns) {
|
||||
readyfns.delete(fn);
|
||||
fn(reflect);
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
module,
|
||||
module: m,
|
||||
reflect,
|
||||
};
|
||||
};
|
||||
|
@ -60,9 +60,10 @@ translators.set('cjs', async (url, isMain) => {
|
||||
const module = CJSModule._cache[
|
||||
isWindows ? StringReplace(pathname, winSepRegEx, '\\') : pathname];
|
||||
if (module && module.loaded) {
|
||||
const ctx = createDynamicModule(['default'], url);
|
||||
ctx.reflect.exports.default.set(module.exports);
|
||||
return ctx;
|
||||
const exports = module.exports;
|
||||
return createDynamicModule(['default'], url, (reflect) => {
|
||||
reflect.exports.default.set(exports);
|
||||
});
|
||||
}
|
||||
return createDynamicModule(['default'], url, () => {
|
||||
debug(`Loading CJSModule ${url}`);
|
||||
|
Loading…
x
Reference in New Issue
Block a user