Skip to content

Commit cfbaaaa

Browse files
authored
Ensure reexported namespaces do not prevent creation of default export helpers (#5466)
* Add test * Use better variable name * Ensure reexported namespaces do not prevent creation of default export helpers
1 parent 2fdfe1c commit cfbaaaa

File tree

7 files changed

+129
-16
lines changed

7 files changed

+129
-16
lines changed

src/Chunk.ts

+25-16
Original file line numberDiff line numberDiff line change
@@ -908,9 +908,14 @@ export default class Chunk {
908908
deconflictedDefault.add(chunk);
909909
}
910910
} else if (
911-
variable.name === '*' &&
912-
namespaceInteropHelpersByInteropType[interop(module.id)]
911+
variable.isNamespace &&
912+
namespaceInteropHelpersByInteropType[interop(module.id)] &&
913+
(this.imports.has(variable) ||
914+
!this.exportNamesByVariable.get(variable)?.every(name => name.startsWith('*')))
913915
) {
916+
// We only need to deconflict it if the namespace is actually
917+
// created as a variable, i.e. because it is used internally or
918+
// because it is reexported as an object
914919
deconflictedNamespace.add(chunk);
915920
}
916921
}
@@ -1139,30 +1144,34 @@ export default class Chunk {
11391144
const reexportSpecifiers = this.getReexportSpecifiers();
11401145
const renderedDependencies = new Map<Chunk | ExternalChunk, ChunkDependency>();
11411146
const fileName = this.getFileName();
1142-
for (const dep of this.dependencies) {
1143-
const imports = importSpecifiers.get(dep) || null;
1144-
const reexports = reexportSpecifiers.get(dep) || null;
1145-
const namedExportsMode = dep instanceof ExternalChunk || dep.exportMode !== 'default';
1146-
const importPath = dep.getImportPath(fileName);
1147-
1148-
renderedDependencies.set(dep, {
1149-
attributes: dep instanceof ExternalChunk ? dep.getImportAttributes(this.snippets) : null,
1150-
defaultVariableName: dep.defaultVariableName,
1147+
for (const dependency of this.dependencies) {
1148+
const imports = importSpecifiers.get(dependency) || null;
1149+
const reexports = reexportSpecifiers.get(dependency) || null;
1150+
const namedExportsMode =
1151+
dependency instanceof ExternalChunk || dependency.exportMode !== 'default';
1152+
const importPath = dependency.getImportPath(fileName);
1153+
1154+
renderedDependencies.set(dependency, {
1155+
attributes:
1156+
dependency instanceof ExternalChunk
1157+
? dependency.getImportAttributes(this.snippets)
1158+
: null,
1159+
defaultVariableName: dependency.defaultVariableName,
11511160
globalName:
1152-
dep instanceof ExternalChunk &&
1161+
dependency instanceof ExternalChunk &&
11531162
(this.outputOptions.format === 'umd' || this.outputOptions.format === 'iife') &&
11541163
getGlobalName(
1155-
dep,
1164+
dependency,
11561165
this.outputOptions.globals,
11571166
(imports || reexports) !== null,
11581167
this.inputOptions.onLog
11591168
),
11601169
importPath,
11611170
imports,
1162-
isChunk: dep instanceof Chunk,
1163-
name: dep.variableName,
1171+
isChunk: dependency instanceof Chunk,
1172+
name: dependency.variableName,
11641173
namedExportsMode,
1165-
namespaceVariableName: dep.namespaceVariableName,
1174+
namespaceVariableName: dependency.namespaceVariableName,
11661175
reexports
11671176
});
11681177
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const assert = require('node:assert');
2+
3+
module.exports = defineTest({
4+
description: 'reexports both a namespace and the default export when using compat interop',
5+
options: {
6+
external: true,
7+
output: { exports: 'named', interop: 'compat' }
8+
},
9+
context: {
10+
require: id => {
11+
if (id === 'external') {
12+
return {
13+
__esModule: true,
14+
default: 'default',
15+
foo: 'foo'
16+
};
17+
}
18+
throw new Error(`Cannot find module ${id}`);
19+
}
20+
},
21+
exports(exports) {
22+
assert.deepStrictEqual(exports, {
23+
default: 'default',
24+
foo: 'foo'
25+
});
26+
}
27+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "external";
2+
export { default } from "external";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const assert = require('node:assert');
2+
3+
module.exports = defineTest({
4+
description:
5+
'reexports both a namespace, the namespace as a name and the default export when using compat interop',
6+
options: {
7+
external: true,
8+
output: { exports: 'named', interop: 'compat' }
9+
},
10+
context: {
11+
require: id => {
12+
if (id === 'external') {
13+
return {
14+
__esModule: true,
15+
default: 'default',
16+
foo: 'foo'
17+
};
18+
}
19+
throw new Error(`Cannot find module ${id}`);
20+
}
21+
},
22+
exports(exports) {
23+
assert.deepStrictEqual(exports, {
24+
default: 'default',
25+
foo: 'foo',
26+
external: {
27+
__esModule: true,
28+
default: 'default',
29+
foo: 'foo'
30+
}
31+
});
32+
}
33+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from "external";
2+
export * as external from 'external';
3+
export { default } from "external";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const assert = require('node:assert');
2+
3+
module.exports = defineTest({
4+
description: 'reexports both a namespace and the default export when using compat interop',
5+
options: {
6+
external: true,
7+
output: { exports: 'named', interop: 'compat' }
8+
},
9+
context: {
10+
require: id => {
11+
if (id === 'external') {
12+
return {
13+
__esModule: true,
14+
default: 'default',
15+
foo: 'foo'
16+
};
17+
}
18+
throw new Error(`Cannot find module ${id}`);
19+
}
20+
},
21+
exports(exports) {
22+
assert.deepStrictEqual(exports, {
23+
default: 'default',
24+
foo: 'foo',
25+
wrappedExternal: {
26+
external: {
27+
__esModule: true,
28+
default: 'default',
29+
foo: 'foo'
30+
}
31+
}
32+
});
33+
}
34+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from 'external';
2+
import * as external from "external";
3+
4+
export const wrappedExternal = { external };
5+
export { default } from 'external';

0 commit comments

Comments
 (0)