in packages/ice/src/service/Runner.ts [145:241]
async requestModule(id: string, _callstack: string[] = [], namespace = '') {
const mod = this.moduleCache.get(id);
const callstack = [..._callstack, id];
let { code: transformed, externalize } = await this.options.load({
path: id,
namespace,
});
const request = async (dep: string) => {
const { id: requestId, namespace } = await this.resolveUrl(dep, id);
return this.dependencyRequest(requestId, callstack, namespace);
};
if (externalize) {
const exports = await this.interopedImport(externalize);
mod.exports = exports;
return exports;
}
const { href } = pathToFileURL(id);
const meta = {
url: href,
...this.importMeta,
};
const exports = Object.create(null);
// this proxy is triggered only on exports.{name} and module.exports access
const cjsExports = new Proxy(exports, {
set: (_, p, value) => {
// treat "module.exports =" the same as "exports.default =" to not have nested "default.default",
// so "exports.default" becomes the actual module
if (p === 'default' && this.shouldInterop(id, { default: value })) {
exportAll(cjsExports, value);
exports.default = value;
return true;
}
if (!Reflect.has(exports, 'default')) exports.default = {};
// returns undefined, when accessing named exports, if default is not an object
// but is still present inside hasOwnKeys, this is Node behaviour for CJS
if (isPrimitive(exports.default)) {
defineExport(exports, p, () => undefined);
return true;
}
exports.default[p] = value;
if (p !== 'default') defineExport(exports, p, () => value);
return true;
},
});
Object.assign(mod, { code: transformed, exports });
const __filename = fileURLToPath(href);
const moduleProxy = {
set exports(value) {
exportAll(cjsExports, value);
exports.default = value;
},
get exports() {
return cjsExports;
},
};
const context = {
// esm transformed by ice, https://github.com/ice-lab/swc-plugins/pull/9
__ice_import__: request,
__ice_dynamic_import__: request,
__ice_exports__: exports,
__ice_exports_all__: (obj: any) => exportAll(exports, obj),
__ice_import_meta__: meta,
// cjs compact
require: createRequire(href),
exports: cjsExports,
module: moduleProxy,
__filename,
__dirname: path.dirname(__filename),
// valid define global expressions
...this.contextDefineVars,
};
if (transformed[0] === '#') transformed = transformed.replace(/^#!.*/, s => ' '.repeat(s.length));
// add 'use strict' since ESM enables it by default
const codeDefinition = `'use strict';async (${Object.keys(context).join(',')})=>{{\n`;
const code = `${codeDefinition}${transformed}\n}}`;
const fn = vm.runInThisContext(code, {
filename: __filename,
lineOffset: -1,
columnOffset: 0,
});
await fn(...Object.values(context));
return exports;
}