Skip to content

Commit 1743568

Browse files
MylesBorinsBridgeAR
authored andcommitted
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: #24560 Refs: nodejs/ecmascript-modules#9 Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Gus Caplan <me@gus.host>
1 parent 8ffe04f commit 1743568

File tree

3 files changed

+58
-59
lines changed

3 files changed

+58
-59
lines changed

lib/internal/modules/cjs/loader.js

+13-12
Original file line numberDiff line numberDiff line change
@@ -623,23 +623,24 @@ Module.prototype.load = function(filename) {
623623
if (experimentalModules) {
624624
if (asyncESM === undefined) lazyLoadESM();
625625
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
628629
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
630635
ESMLoader.moduleMap.set(
631-
urlString,
636+
url,
632637
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+
});
637642
})
638643
);
639-
} else {
640-
const job = ESMLoader.moduleMap.get(urlString);
641-
if (job.reflect)
642-
job.reflect.exports.default.set(exports);
643644
}
644645
}
645646
};

lib/internal/modules/esm/create_dynamic_module.js

+41-44
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { ModuleWrap } = internalBinding('module_wrap');
3+
const { ModuleWrap, callbackMap } = internalBinding('module_wrap');
44
const debug = require('util').debuglog('esm');
55
const ArrayJoin = Function.call.bind(Array.prototype.join);
66
const ArrayMap = Function.call.bind(Array.prototype.map);
@@ -10,50 +10,47 @@ const createDynamicModule = (exports, url = '', evaluate) => {
1010
`creating ESM facade for ${url} with exports: ${ArrayJoin(exports, ', ')}`
1111
);
1212
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+
5552
return {
56-
module,
53+
module: m,
5754
reflect,
5855
};
5956
};

lib/internal/modules/esm/translators.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ translators.set('cjs', async (url, isMain) => {
6060
const module = CJSModule._cache[
6161
isWindows ? StringReplace(pathname, winSepRegEx, '\\') : pathname];
6262
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+
});
6667
}
6768
return createDynamicModule(['default'], url, () => {
6869
debug(`Loading CJSModule ${url}`);

0 commit comments

Comments
 (0)