feat(static-module-record): expose named reexports separately so they can be wired up correctly in upper layers
MODIFY
packages/static-module-record/src/babelPlugin.js
packages/static-module-record/src/babelPlugin.js
@@ -46,6 +46,7 @@ function makeModulePlugins(options) {
imports,
importDecls,
importSources,
+ reexportMap,
liveExportMap,
importMeta,
} = options;
@@ -536,10 +537,24 @@ function makeModulePlugins(options) {
const { local, exported } = spec;
const importFrom =
spec.type === 'ExportNamespaceSpecifier' ? '*' : local.name;
-
+ let myUpdaterSources;
// If local.name is reexported we omit it.
const importTo = exported.name;
- let myUpdaterSources = updaterSources[importFrom];
+
+ if (source) {
+ // If source is defined, it's a reexport from a different module.
+ // In this case we want to collect the local and reexported name
+ // If there's no `as newName`, the ExportNamedDeclaration will have the `exported` field anyway,
+ // representing the same identifier as `local`
+
+ if (!reexportMap[source.value]) {
+ reexportMap[source.value] = [];
+ }
+ reexportMap[source.value].push([local.name, exported.name]);
+ // Don't populate importSources here, so the live binding won't get
+ // generated by the transform
+ } else {
+ myUpdaterSources = updaterSources[importFrom];
if (myImportSources) {
myUpdaterSources = myImportSources[importFrom];
if (!myUpdaterSources) {
@@ -549,6 +564,7 @@ function makeModulePlugins(options) {
updaterSources[importTo] = myUpdaterSources;
myImports.push(importFrom);
}
+ }
if (myUpdaterSources) {
// If there are updaters, we must have a local
@@ -557,12 +573,11 @@ function makeModulePlugins(options) {
? h.HIDDEN_ONCE
: h.HIDDEN_LIVE;
myUpdaterSources.push(`${ident}[${JSON.stringify(importFrom)}]`);
- }
- if (source || myUpdaterSources) {
- // Not declared, so make it a live export without proxy.
liveExportMap[importTo] = [importFrom, false];
- } else {
+ }
+
+ if (!(source || myUpdaterSources)) {
let tle = topLevelExported[importFrom];
if (!tle) {
tle = [];

MODIFY
packages/static-module-record/src/static-module-record.js
packages/static-module-record/src/static-module-record.js
import { makeModuleAnalyzer } from './transform-analyze.js';
-const { freeze, keys } = Object;
+const { freeze, keys, values } = Object;
// If all ESM implementations were correct, it would be sufficient to
// `import babel` instead of `import * as babel`.
@@ -49,17 +49,23 @@ export function StaticModuleRecord(source, url) {
imports,
functorSource,
liveExportMap,
+ reexportMap,
fixedExportMap,
exportAlls,
needsImportMeta,
} = analyzeModule({ string: source, url });
this.imports = freeze([...keys(imports)].sort());
this.exports = freeze(
- [...keys(liveExportMap), ...keys(fixedExportMap)].sort(),
+ [
+ ...keys(liveExportMap),
+ ...keys(fixedExportMap),
+ ...values(reexportMap).flatMap(([_, exportName]) => exportName),
+ ].sort(),
);
this.reexports = freeze([...exportAlls].sort());
this.__syncModuleProgram__ = functorSource;
this.__liveExportMap__ = liveExportMap;
+ this.__reexportMap__ = reexportMap;
this.__fixedExportMap__ = fixedExportMap;
this.__needsImportMeta__ = needsImportMeta;
freeze(this);

MODIFY
packages/static-module-record/src/transform-analyze.js
packages/static-module-record/src/transform-analyze.js
@@ -21,6 +21,7 @@ const makeCreateStaticRecord = transformSource =>
// exportNames of variables that are assigned to, or reexported and
// therefore assumed live. A reexported variable might not have any
// localName.
+ reexportMap: Object.create(null),
liveExportMap: Object.create(null),
hoistedDecls: [],
importSources: Object.create(null),
@@ -99,6 +100,7 @@ const makeCreateStaticRecord = transformSource =>
imports: freeze(sourceOptions.imports),
liveExportMap: freeze(sourceOptions.liveExportMap),
fixedExportMap: freeze(sourceOptions.fixedExportMap),
+ reexportMap: freeze(sourceOptions.reexportMap),
needsImportMeta: sourceOptions.importMeta.present,
functorSource,
});

MODIFY
packages/static-module-record/test/test-static-module-record.js
packages/static-module-record/test/test-static-module-record.js
@@ -154,6 +154,16 @@ function initialize(t, source, options = {}) {
}
}
}
+ for (const [module, items] of Object.entries(record.__reexportMap__)) {
+ const moduleImports = imports.get(module);
+ if (moduleImports === undefined) {
+ t.fail(`link error for named reexports from module ${module}`);
+ } else {
+ for (const [localName, exportedName] of items) {
+ namespace[exportedName] = moduleImports.get(localName);
+ }
+ }
+ }
}
functor({
@@ -627,7 +637,6 @@ test('export name as', t => {
]),
},
);
- t.log(namespace);
t.is(namespace.stonefruit, 'stonefruit');
t.is(namespace.citrus, 'citrus');
t.is(namespace.peaches, undefined);
@@ -711,16 +720,24 @@ test('export name as default from', t => {
__syncModuleProgram__,
__fixedExportMap__,
__liveExportMap__,
+ __reexportMap__,
} = new StaticModuleRecord(`
export { meaning as default } from './meaning.js';
`);
// t.log(__syncModuleProgram__);
t.deepEqual(__fixedExportMap__, {});
- t.deepEqual(__liveExportMap__, {
- default: ['meaning', false],
+ t.deepEqual(__liveExportMap__, {});
+ t.deepEqual(__reexportMap__, {
+ './meaning.js': [
+ [
+ 'meaning',
+ 'default',
+ ],
+ ]
});
});
+
// Regression test for #823
test('static module records can name Map in scope', t => {
t.notThrows(() => initialize(t, `const { Map } = globalThis;`));

feat(static-module-record): expose named reexports separately so they can be wired up correctly in upper layers