Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not expose synthetic namespace export in entries and namespaces #4364

Merged
merged 3 commits into from Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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]
}
};