Skip to content
This repository has been archived by the owner on Apr 16, 2020. It is now read-only.

Commit

Permalink
esm: refactor dynamic modules
Browse files Browse the repository at this point in the history
  • Loading branch information
devsnek committed Oct 9, 2018
1 parent 47aac7c commit 285493f
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 59 deletions.
24 changes: 11 additions & 13 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -617,23 +617,21 @@ Module.prototype.load = function(filename) {
if (experimentalModules) {
if (asyncESM === undefined) lazyLoadESM();
const ESMLoader = asyncESM.ESMLoader;
const url = pathToFileURL(filename);
const urlString = `${url}`;
const exports = this.exports;
if (ESMLoader.moduleMap.has(urlString) !== true) {
const url = `${pathToFileURL(filename)}`;
const module = ESMLoader.moduleMap.get(url);
if (module !== undefined) {
module.reflect.exports.default.set(this.exports);
} else {
const exports = this.exports;
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);
}
}
};
Expand Down
89 changes: 46 additions & 43 deletions lib/internal/modules/esm/create_dynamic_module.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,62 @@
'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);

// This variable is used as the local binding for a default export from a
// dynamic module. It's definitely a hack but it works quite
// nicely for our purposes.
const defaultName = '\u5343\u4e07\u4e0d\u8981\u7528\u8fd9\u4e2a\u53d8\u91cf' +
'\u5426\u5219\u4f60\u4f1a\u88ab\u5f00\u9664';

const createDynamicModule = (exports, url = '', evaluate) => {
debug(
`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 source = `
${ArrayJoin(ArrayMap(names, (name) => {
if (name === 'default') {
return `let ${defaultName};
export { ${defaultName} as default };
import.meta.exports.default = {
get: () => ${defaultName},
set: ($${defaultName}) => ${defaultName} = $${defaultName},
};
`;
} else {
return `export let ${name};
import.meta.exports.${name} = {
get: () => ${name},
set: ($${name}) => ${name} = $${name},
};`;
}
}));`;
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();
}), '\n')}
import.meta.done();
`;

const m = new ModuleWrap(source, `${url}`);
m.link(() => 0);
m.instantiate();

const reflect = {
namespace: m.namespace,
exports: {},
};

callbackMap.set(m, {
initializeImportMeta: (meta, wrap) => {
meta.exports = reflect.exports;
meta.done = () => evaluate(reflect);
},
});

return {
module,
module: m,
reflect,
};
};
Expand Down
7 changes: 4 additions & 3 deletions lib/internal/modules/esm/translators.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,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}`);
Expand Down

0 comments on commit 285493f

Please sign in to comment.