diff --git a/src/Module.ts b/src/Module.ts index 92eee40b2c8..443d16e9c43 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -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; } @@ -412,7 +416,6 @@ export default class Module { } const exportNamesByVariable = new Map(); for (const exportName of this.getAllExportNames()) { - if (exportName === this.info.syntheticNamedExports) continue; let [tracedVariable] = this.getVariableForExportName(exportName); if (tracedVariable instanceof ExportDefaultVariable) { tracedVariable = tracedVariable.getOriginalVariable(); @@ -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) { @@ -493,10 +497,12 @@ export default class Module { { importerForSideEffects, isExportAllSearch, + onlyExplicit, searchedNamesAndModules }: { importerForSideEffects?: Module; isExportAllSearch?: boolean; + onlyExplicit?: boolean; searchedNamesAndModules?: Map>; } = EMPTY_OBJECT ): [variable: Variable | null, indirectExternal?: boolean] { @@ -521,7 +527,6 @@ export default class Module { false, searchedNamesAndModules ); - if (!variable) { return this.error( errMissingExport(reexportDeclaration.localName, this.id, reexportDeclaration.module.id), @@ -552,6 +557,10 @@ export default class Module { return [variable]; } + if (onlyExplicit) { + return [null]; + } + if (name !== 'default') { const foundNamespaceReexport = name in this.namespaceReexportsByName @@ -1021,6 +1030,10 @@ export default class Module { const foundInternalDeclarations = new Map(); const foundExternalDeclarations = new Set(); 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, diff --git a/src/utils/error.ts b/src/utils/error.ts index 4fa302372c3..7f583b91fa1 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -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.` }; diff --git a/test/form/samples/merge-namespaces-non-live/_expected.js b/test/form/samples/merge-namespaces-non-live/_expected.js index 4b2dd6e372a..0ccac72f180 100644 --- a/test/form/samples/merge-namespaces-non-live/_expected.js +++ b/test/form/samples/merge-namespaces-non-live/_expected.js @@ -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); diff --git a/test/form/samples/merge-namespaces-non-live/reexport.js b/test/form/samples/merge-namespaces-non-live/reexport.js index 976fee83e08..9204c816376 100644 --- a/test/form/samples/merge-namespaces-non-live/reexport.js +++ b/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' }; diff --git a/test/form/samples/merge-namespaces-non-live/synthetic.js b/test/form/samples/merge-namespaces-non-live/synthetic.js index 7592639d278..b180a18b691 100644 --- a/test/form/samples/merge-namespaces-non-live/synthetic.js +++ b/test/form/samples/merge-namespaces-non-live/synthetic.js @@ -1 +1 @@ -export const __synthetic = { foo: 'foo' }; +export const __synthetic = { module: 'synthetic' }; diff --git a/test/form/samples/merge-namespaces/_expected.js b/test/form/samples/merge-namespaces/_expected.js index 3675738aa02..718fc549bf6 100644 --- a/test/form/samples/merge-namespaces/_expected.js +++ b/test/form/samples/merge-namespaces/_expected.js @@ -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); diff --git a/test/form/samples/merge-namespaces/reexport.js b/test/form/samples/merge-namespaces/reexport.js index 976fee83e08..9204c816376 100644 --- a/test/form/samples/merge-namespaces/reexport.js +++ b/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' }; diff --git a/test/form/samples/merge-namespaces/synthetic.js b/test/form/samples/merge-namespaces/synthetic.js index 7592639d278..b180a18b691 100644 --- a/test/form/samples/merge-namespaces/synthetic.js +++ b/test/form/samples/merge-namespaces/synthetic.js @@ -1 +1 @@ -export const __synthetic = { foo: 'foo' }; +export const __synthetic = { module: 'synthetic' }; diff --git a/test/function/samples/synthetic-named-export-entry/_config.js b/test/function/samples/synthetic-named-export-entry/_config.js new file mode 100644 index 00000000000..9722c598584 --- /dev/null +++ b/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' + }); + } +}; diff --git a/test/function/samples/synthetic-named-export-entry/hiddenNamespace.js b/test/function/samples/synthetic-named-export-entry/hiddenNamespace.js new file mode 100644 index 00000000000..97e81d68bdc --- /dev/null +++ b/test/function/samples/synthetic-named-export-entry/hiddenNamespace.js @@ -0,0 +1,2 @@ +export const hiddenNamespace = 'hiddenNamespace'; +export const synthHiddenNamespace = { hiddenNamespace: true }; diff --git a/test/function/samples/synthetic-named-export-entry/main.js b/test/function/samples/synthetic-named-export-entry/main.js new file mode 100644 index 00000000000..cc7963f49ae --- /dev/null +++ b/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'; diff --git a/test/function/samples/synthetic-named-export-entry/noOverride.js b/test/function/samples/synthetic-named-export-entry/noOverride.js new file mode 100644 index 00000000000..205e87552a5 --- /dev/null +++ b/test/function/samples/synthetic-named-export-entry/noOverride.js @@ -0,0 +1,2 @@ +export const noOverride = 'noOverride'; +export const synthNoOverride = { noOverride: true }; diff --git a/test/function/samples/synthetic-named-export-entry/override.js b/test/function/samples/synthetic-named-export-entry/override.js new file mode 100644 index 00000000000..8f40d7ee6ca --- /dev/null +++ b/test/function/samples/synthetic-named-export-entry/override.js @@ -0,0 +1,3 @@ +export const override = 'override'; +export const synthOverride = { override: true }; +export const synthHiddenNamespace = 'hidden in override'; diff --git a/test/function/samples/synthetic-named-exports/circular-synthetic-exports/_config.js b/test/function/samples/synthetic-named-exports/circular-synthetic-exports/_config.js index 35a95977898..89a605bae77 100644 --- a/test/function/samples/synthetic-named-exports/circular-synthetic-exports/_config.js +++ b/test/function/samples/synthetic-named-exports/circular-synthetic-exports/_config.js @@ -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')] } }; diff --git a/test/function/samples/synthetic-named-exports/synthetic-exports-need-fallback-export/_config.js b/test/function/samples/synthetic-named-exports/synthetic-exports-need-fallback-export/_config.js index a11192c4697..7e85c3165ac 100644 --- a/test/function/samples/synthetic-named-exports/synthetic-exports-need-fallback-export/_config.js +++ b/test/function/samples/synthetic-named-exports/synthetic-exports-need-fallback-export/_config.js @@ -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] } };