Skip to content

Commit

Permalink
Do not expose synthetic namespace export in entries and namespaces (#…
Browse files Browse the repository at this point in the history
…4364)

* Do not warn for hidden namespace conflicts

* Merge conflict detection into export resolution

* Do not expose the synthetic namespace export in namespaces
  • Loading branch information
lukastaegert committed Jan 25, 2022
1 parent f30e6f0 commit 79ec55d
Show file tree
Hide file tree
Showing 15 changed files with 85 additions and 13 deletions.
21 changes: 17 additions & 4 deletions src/Module.ts
Expand Up @@ -351,7 +351,11 @@ export default class Module {
if (name !== 'default') allExportNames.add(name);
}
}

// We do not count the synthetic namespace as a regular export to hide it
// from entry signatures and namespace objects
if (typeof this.info.syntheticNamedExports === 'string') {
allExportNames.delete(this.info.syntheticNamedExports);
}
return allExportNames;
}

Expand Down Expand Up @@ -412,7 +416,6 @@ export default class Module {
}
const exportNamesByVariable = new Map<Variable, string[]>();
for (const exportName of this.getAllExportNames()) {
if (exportName === this.info.syntheticNamedExports) continue;
let [tracedVariable] = this.getVariableForExportName(exportName);
if (tracedVariable instanceof ExportDefaultVariable) {
tracedVariable = tracedVariable.getOriginalVariable();
Expand Down Expand Up @@ -477,7 +480,8 @@ export default class Module {
[this.syntheticNamespace] = this.getVariableForExportName(
typeof this.info.syntheticNamedExports === 'string'
? this.info.syntheticNamedExports
: 'default'
: 'default',
{ onlyExplicit: true }
);
}
if (!this.syntheticNamespace) {
Expand All @@ -493,10 +497,12 @@ export default class Module {
{
importerForSideEffects,
isExportAllSearch,
onlyExplicit,
searchedNamesAndModules
}: {
importerForSideEffects?: Module;
isExportAllSearch?: boolean;
onlyExplicit?: boolean;
searchedNamesAndModules?: Map<string, Set<Module | ExternalModule>>;
} = EMPTY_OBJECT
): [variable: Variable | null, indirectExternal?: boolean] {
Expand All @@ -521,7 +527,6 @@ export default class Module {
false,
searchedNamesAndModules
);

if (!variable) {
return this.error(
errMissingExport(reexportDeclaration.localName, this.id, reexportDeclaration.module.id),
Expand Down Expand Up @@ -552,6 +557,10 @@ export default class Module {
return [variable];
}

if (onlyExplicit) {
return [null];
}

if (name !== 'default') {
const foundNamespaceReexport =
name in this.namespaceReexportsByName
Expand Down Expand Up @@ -1021,6 +1030,10 @@ export default class Module {
const foundInternalDeclarations = new Map<Variable, Module>();
const foundExternalDeclarations = new Set<ExternalVariable>();
for (const module of this.exportAllModules) {
// Synthetic namespaces should not hide "regular" exports of the same name
if (module.info.syntheticNamedExports === name) {
continue;
}
const [variable, indirectExternal] = getVariableForExportNameRecursive(
module,
name,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/error.ts
Expand Up @@ -407,7 +407,7 @@ export function errSyntheticNamedExportsNeedNamespaceExport(
syntheticNamedExportsOption
)}' needs ${
typeof syntheticNamedExportsOption === 'string' && syntheticNamedExportsOption !== 'default'
? `an export named "${syntheticNamedExportsOption}"`
? `an explicit export named "${syntheticNamedExportsOption}"`
: 'a default export'
} that does not reexport an unresolved named export of the same module.`
};
Expand Down
6 changes: 4 additions & 2 deletions test/form/samples/merge-namespaces-non-live/_expected.js
Expand Up @@ -13,10 +13,12 @@ function _mergeNamespaces(n, m) {
return Object.freeze(n);
}

const __synthetic = { foo: 'foo' };
const __synthetic$1 = { module: 'synthetic' };

const __synthetic = { module: 'reexport' };

var ns = /*#__PURE__*/Object.freeze(/*#__PURE__*/_mergeNamespaces({
__proto__: null
}, [__synthetic, external1, external2]));
}, [__synthetic, __synthetic$1, external1, external2]));

console.log(ns);
1 change: 1 addition & 0 deletions test/form/samples/merge-namespaces-non-live/reexport.js
@@ -1,3 +1,4 @@
export * from 'external1';
export * from './synthetic';
export * from 'external2';
export const __synthetic = { module: 'reexport' };
2 changes: 1 addition & 1 deletion test/form/samples/merge-namespaces-non-live/synthetic.js
@@ -1 +1 @@
export const __synthetic = { foo: 'foo' };
export const __synthetic = { module: 'synthetic' };
6 changes: 4 additions & 2 deletions test/form/samples/merge-namespaces/_expected.js
Expand Up @@ -16,10 +16,12 @@ function _mergeNamespaces(n, m) {
return Object.freeze(n);
}

const __synthetic = { foo: 'foo' };
const __synthetic$1 = { module: 'synthetic' };

const __synthetic = { module: 'reexport' };

var ns = /*#__PURE__*/Object.freeze(/*#__PURE__*/_mergeNamespaces({
__proto__: null
}, [__synthetic, external1, external2]));
}, [__synthetic, __synthetic$1, external1, external2]));

console.log(ns);
1 change: 1 addition & 0 deletions test/form/samples/merge-namespaces/reexport.js
@@ -1,3 +1,4 @@
export * from 'external1';
export * from './synthetic';
export * from 'external2';
export const __synthetic = { module: 'reexport' };
2 changes: 1 addition & 1 deletion test/form/samples/merge-namespaces/synthetic.js
@@ -1 +1 @@
export const __synthetic = { foo: 'foo' };
export const __synthetic = { module: 'synthetic' };
39 changes: 39 additions & 0 deletions test/function/samples/synthetic-named-export-entry/_config.js
@@ -0,0 +1,39 @@
const assert = require('assert');
const path = require('path');
const ID_MAIN = path.join(__dirname, 'main.js');
const ID_OVERRIDE = path.join(__dirname, 'override.js');
const ID_NOOVERRIDE = path.join(__dirname, 'noOverride.js');
const ID_HIDDENNAMESPACE = path.join(__dirname, 'hiddenNamespace.js');

module.exports = {
description: 'does not expose synthetic named exports on entry points',
options: {
plugins: [
{
transform(code, id) {
switch (id) {
case ID_MAIN:
return { syntheticNamedExports: 'synthMain' };
case ID_OVERRIDE:
return { syntheticNamedExports: 'synthOverride' };
case ID_NOOVERRIDE:
return { syntheticNamedExports: 'synthNoOverride' };
case ID_HIDDENNAMESPACE:
return { syntheticNamedExports: 'synthHiddenNamespace' };
}
}
}
]
},
exports(exports) {
assert.deepStrictEqual(exports, {
explicitReexport: { override: true },
hiddenNamespace: 'hiddenNamespace',
main: 'main',
noOverride: 'noOverride',
override: 'override',
synthHiddenNamespace: 'hidden in override',
synthOverride: 'overridden'
});
}
};
@@ -0,0 +1,2 @@
export const hiddenNamespace = 'hiddenNamespace';
export const synthHiddenNamespace = { hiddenNamespace: true };
7 changes: 7 additions & 0 deletions test/function/samples/synthetic-named-export-entry/main.js
@@ -0,0 +1,7 @@
export const main = 'main';
export const synthMain = { main: true };
export * from './noOverride.js';
export * from './override.js';
export * from './hiddenNamespace.js';
export const synthOverride = 'overridden';
export { synthOverride as explicitReexport } from './override.js';
@@ -0,0 +1,2 @@
export const noOverride = 'noOverride';
export const synthNoOverride = { noOverride: true };
@@ -0,0 +1,3 @@
export const override = 'override';
export const synthOverride = { override: true };
export const synthHiddenNamespace = 'hidden in override';
Expand Up @@ -15,7 +15,7 @@ module.exports = {
error: {
code: 'SYNTHETIC_NAMED_EXPORTS_NEED_NAMESPACE_EXPORT',
id: path.join(__dirname, 'main.js'),
message: `Module "main.js" that is marked with 'syntheticNamedExports: "__synthetic"' needs an export named "__synthetic" that does not reexport an unresolved named export of the same module.`,
message: `Module "main.js" that is marked with 'syntheticNamedExports: "__synthetic"' needs an explicit export named "__synthetic" that does not reexport an unresolved named export of the same module.`,
watchFiles: [path.join(__dirname, 'main.js'), path.join(__dirname, 'dep.js')]
}
};
Expand Up @@ -17,7 +17,7 @@ module.exports = {
error: {
code: 'SYNTHETIC_NAMED_EXPORTS_NEED_NAMESPACE_EXPORT',
id: DEP_ID,
message: `Module "dep.js" that is marked with 'syntheticNamedExports: "__synthetic"' needs an export named "__synthetic" that does not reexport an unresolved named export of the same module.`,
message: `Module "dep.js" that is marked with 'syntheticNamedExports: "__synthetic"' needs an explicit export named "__synthetic" that does not reexport an unresolved named export of the same module.`,
watchFiles: [path.join(__dirname, 'main.js'), DEP_ID]
}
};

0 comments on commit 79ec55d

Please sign in to comment.