Skip to content

Commit 79ec55d

Browse files
authored
Do not expose synthetic namespace export in entries and namespaces (#4364)
* Do not warn for hidden namespace conflicts * Merge conflict detection into export resolution * Do not expose the synthetic namespace export in namespaces
1 parent f30e6f0 commit 79ec55d

15 files changed

Lines changed: 85 additions & 13 deletions

File tree

src/Module.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,11 @@ export default class Module {
351351
if (name !== 'default') allExportNames.add(name);
352352
}
353353
}
354-
354+
// We do not count the synthetic namespace as a regular export to hide it
355+
// from entry signatures and namespace objects
356+
if (typeof this.info.syntheticNamedExports === 'string') {
357+
allExportNames.delete(this.info.syntheticNamedExports);
358+
}
355359
return allExportNames;
356360
}
357361

@@ -412,7 +416,6 @@ export default class Module {
412416
}
413417
const exportNamesByVariable = new Map<Variable, string[]>();
414418
for (const exportName of this.getAllExportNames()) {
415-
if (exportName === this.info.syntheticNamedExports) continue;
416419
let [tracedVariable] = this.getVariableForExportName(exportName);
417420
if (tracedVariable instanceof ExportDefaultVariable) {
418421
tracedVariable = tracedVariable.getOriginalVariable();
@@ -477,7 +480,8 @@ export default class Module {
477480
[this.syntheticNamespace] = this.getVariableForExportName(
478481
typeof this.info.syntheticNamedExports === 'string'
479482
? this.info.syntheticNamedExports
480-
: 'default'
483+
: 'default',
484+
{ onlyExplicit: true }
481485
);
482486
}
483487
if (!this.syntheticNamespace) {
@@ -493,10 +497,12 @@ export default class Module {
493497
{
494498
importerForSideEffects,
495499
isExportAllSearch,
500+
onlyExplicit,
496501
searchedNamesAndModules
497502
}: {
498503
importerForSideEffects?: Module;
499504
isExportAllSearch?: boolean;
505+
onlyExplicit?: boolean;
500506
searchedNamesAndModules?: Map<string, Set<Module | ExternalModule>>;
501507
} = EMPTY_OBJECT
502508
): [variable: Variable | null, indirectExternal?: boolean] {
@@ -521,7 +527,6 @@ export default class Module {
521527
false,
522528
searchedNamesAndModules
523529
);
524-
525530
if (!variable) {
526531
return this.error(
527532
errMissingExport(reexportDeclaration.localName, this.id, reexportDeclaration.module.id),
@@ -552,6 +557,10 @@ export default class Module {
552557
return [variable];
553558
}
554559

560+
if (onlyExplicit) {
561+
return [null];
562+
}
563+
555564
if (name !== 'default') {
556565
const foundNamespaceReexport =
557566
name in this.namespaceReexportsByName
@@ -1021,6 +1030,10 @@ export default class Module {
10211030
const foundInternalDeclarations = new Map<Variable, Module>();
10221031
const foundExternalDeclarations = new Set<ExternalVariable>();
10231032
for (const module of this.exportAllModules) {
1033+
// Synthetic namespaces should not hide "regular" exports of the same name
1034+
if (module.info.syntheticNamedExports === name) {
1035+
continue;
1036+
}
10241037
const [variable, indirectExternal] = getVariableForExportNameRecursive(
10251038
module,
10261039
name,

src/utils/error.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ export function errSyntheticNamedExportsNeedNamespaceExport(
407407
syntheticNamedExportsOption
408408
)}' needs ${
409409
typeof syntheticNamedExportsOption === 'string' && syntheticNamedExportsOption !== 'default'
410-
? `an export named "${syntheticNamedExportsOption}"`
410+
? `an explicit export named "${syntheticNamedExportsOption}"`
411411
: 'a default export'
412412
} that does not reexport an unresolved named export of the same module.`
413413
};

test/form/samples/merge-namespaces-non-live/_expected.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ function _mergeNamespaces(n, m) {
1313
return Object.freeze(n);
1414
}
1515

16-
const __synthetic = { foo: 'foo' };
16+
const __synthetic$1 = { module: 'synthetic' };
17+
18+
const __synthetic = { module: 'reexport' };
1719

1820
var ns = /*#__PURE__*/Object.freeze(/*#__PURE__*/_mergeNamespaces({
1921
__proto__: null
20-
}, [__synthetic, external1, external2]));
22+
}, [__synthetic, __synthetic$1, external1, external2]));
2123

2224
console.log(ns);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from 'external1';
22
export * from './synthetic';
33
export * from 'external2';
4+
export const __synthetic = { module: 'reexport' };
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const __synthetic = { foo: 'foo' };
1+
export const __synthetic = { module: 'synthetic' };

test/form/samples/merge-namespaces/_expected.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ function _mergeNamespaces(n, m) {
1616
return Object.freeze(n);
1717
}
1818

19-
const __synthetic = { foo: 'foo' };
19+
const __synthetic$1 = { module: 'synthetic' };
20+
21+
const __synthetic = { module: 'reexport' };
2022

2123
var ns = /*#__PURE__*/Object.freeze(/*#__PURE__*/_mergeNamespaces({
2224
__proto__: null
23-
}, [__synthetic, external1, external2]));
25+
}, [__synthetic, __synthetic$1, external1, external2]));
2426

2527
console.log(ns);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from 'external1';
22
export * from './synthetic';
33
export * from 'external2';
4+
export const __synthetic = { module: 'reexport' };
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const __synthetic = { foo: 'foo' };
1+
export const __synthetic = { module: 'synthetic' };
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const assert = require('assert');
2+
const path = require('path');
3+
const ID_MAIN = path.join(__dirname, 'main.js');
4+
const ID_OVERRIDE = path.join(__dirname, 'override.js');
5+
const ID_NOOVERRIDE = path.join(__dirname, 'noOverride.js');
6+
const ID_HIDDENNAMESPACE = path.join(__dirname, 'hiddenNamespace.js');
7+
8+
module.exports = {
9+
description: 'does not expose synthetic named exports on entry points',
10+
options: {
11+
plugins: [
12+
{
13+
transform(code, id) {
14+
switch (id) {
15+
case ID_MAIN:
16+
return { syntheticNamedExports: 'synthMain' };
17+
case ID_OVERRIDE:
18+
return { syntheticNamedExports: 'synthOverride' };
19+
case ID_NOOVERRIDE:
20+
return { syntheticNamedExports: 'synthNoOverride' };
21+
case ID_HIDDENNAMESPACE:
22+
return { syntheticNamedExports: 'synthHiddenNamespace' };
23+
}
24+
}
25+
}
26+
]
27+
},
28+
exports(exports) {
29+
assert.deepStrictEqual(exports, {
30+
explicitReexport: { override: true },
31+
hiddenNamespace: 'hiddenNamespace',
32+
main: 'main',
33+
noOverride: 'noOverride',
34+
override: 'override',
35+
synthHiddenNamespace: 'hidden in override',
36+
synthOverride: 'overridden'
37+
});
38+
}
39+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const hiddenNamespace = 'hiddenNamespace';
2+
export const synthHiddenNamespace = { hiddenNamespace: true };

0 commit comments

Comments
 (0)