Skip to content

Commit

Permalink
fix: correctly add aliased re-exported bindings to lexical environmen…
Browse files Browse the repository at this point in the history
…t at all times. Fixes #109
  • Loading branch information
wessberg committed Apr 13, 2022
1 parent 6eec842 commit 16d24fa
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {cloneLexicalEnvironment} from "../../../util/clone-lexical-environment";
import {ensureNoDeclareModifierTransformer} from "../../ensure-no-declare-modifier-transformer/ensure-no-declare-modifier-transformer";
import {statementMerger} from "../../statement-merger/statement-merger";
import {inlineNamespaceModuleBlockTransformer} from "../../inline-namespace-module-block-transformer/inline-namespace-module-block-transformer";
import {addBindingToLexicalEnvironment} from "../../../util/add-binding-to-lexical-environment";
import {getOriginalSourceFile} from "../../../util/get-original-source-file";

export interface GenerateExportDeclarationsOptions extends Omit<ModuleMergerVisitorOptions<TS.ExportDeclaration>, "node"> {}

Expand Down Expand Up @@ -75,7 +77,7 @@ function generateExportDeclarations(options: GenerateExportDeclarationsOptions,
}

export function visitExportDeclaration(options: ModuleMergerVisitorOptions<TS.ExportDeclaration>): VisitResult<TS.ExportDeclaration> {
const {node, factory, typescript} = options;
const {node, factory, typescript, sourceFile} = options;
const moduleSpecifier = node.moduleSpecifier == null || !typescript.isStringLiteralLike(node.moduleSpecifier) ? undefined : node.moduleSpecifier.text;
const updatedModuleSpecifier =
moduleSpecifier == null
Expand All @@ -87,7 +89,6 @@ export function visitExportDeclaration(options: ModuleMergerVisitorOptions<TS.Ex
});

const matchingSourceFile = moduleSpecifier == null ? undefined : options.getMatchingSourceFile(moduleSpecifier, options.sourceFile);

const payload = {
moduleSpecifier,
matchingSourceFile,
Expand All @@ -96,8 +97,23 @@ export function visitExportDeclaration(options: ModuleMergerVisitorOptions<TS.Ex

const contResult = options.childContinuation(node, payload);

// Inside a file that will be merged into one of the entry sourcefiles for a chunk, when a binding is exported locally by another name, it should be
// added to the lexical environment such that we'll know later on that this binding is known by is aliased name going forward
if (updatedModuleSpecifier == null && contResult.exportClause != null && typescript.isNamedExports(contResult.exportClause)) {
if (contResult.exportClause != null && typescript.isNamedExports(contResult.exportClause)) {
const originalSourceFile = getOriginalSourceFile(node, sourceFile, typescript);

for (const exportSpecifier of contResult.exportClause.elements) {
if (exportSpecifier.propertyName != null && exportSpecifier.propertyName.text !== exportSpecifier.name.text) {
addBindingToLexicalEnvironment(options.lexicalEnvironment, originalSourceFile.fileName, exportSpecifier.propertyName.text, exportSpecifier.name.text);
}
}
}
}

// If no SourceFile was resolved
if (matchingSourceFile == null) {

// If the module specifier didn't change, preserve the export as it is.
if (moduleSpecifier === updatedModuleSpecifier || updatedModuleSpecifier == null) {
return contResult;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function generateUniqueBinding(lexicalEnvironment: LexicalEnvironment, ca
counter = parseInt(value.slice(candidate.length + DECONFLICT_SUFFIX.length));
}

return `${candidate}${DECONFLICT_SUFFIX}${counter + 1}`;
return isNaN(counter) ? candidate : `${candidate}${DECONFLICT_SUFFIX}${counter + 1}`;
}

if (lexicalEnvironment.parent == null) {
Expand Down
187 changes: 187 additions & 0 deletions test/export-default.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,190 @@ test.serial("Handles default exports inside ExportSpecifiers. #4", withTypeScrip
`)
);
});

test.serial("Handles default exports inside ExportSpecifiers. #5", withTypeScript, async (t, {typescript}) => {
const bundle = await generateRollupBundle(
[
{
entry: true,
fileName: "index.ts",
text: `\
import Foo from "./foo";
export { Foo };
`
},
{
entry: false,
fileName: "foo.ts",
text: `\
export { default } from "./bar";
`
},
{
entry: false,
fileName: "bar.ts",
text: `\
export default 2;
`
}
],
{
typescript,
debug: false
}
);
const {
declarations: [file]
} = bundle;

t.deepEqual(
formatCode(file.code),
formatCode(`\
declare const _default: 2;
declare const Foo: typeof _default;
export { Foo };
`)
);
});

test.serial("Handles default exports inside ExportSpecifiers. #6", withTypeScript, async (t, {typescript}) => {
const bundle = await generateRollupBundle(
[
{
entry: true,
fileName: "index.ts",
text: `\
import Foo from "./foo";
export { Foo };
`
},
{
entry: false,
fileName: "foo.ts",
text: `\
export { A as default } from "./bar";
`
},
{
entry: false,
fileName: "bar.ts",
text: `\
const foo = 2;
export {foo as A};
`
}
],
{
typescript,
debug: false
}
);
const {
declarations: [file]
} = bundle;

t.deepEqual(
formatCode(file.code),
formatCode(`\
declare const foo = 2;
declare const Foo: typeof foo;
export { Foo };
`)
);
});

test.serial("Handles default exports inside ExportSpecifiers. #7", withTypeScript, async (t, {typescript}) => {
const bundle = await generateRollupBundle(
[
{
entry: true,
fileName: "index.ts",
text: `\
import {A} from "./foo";
export { A };
`
},
{
entry: false,
fileName: "foo.ts",
text: `\
export { A } from "./bar";
`
},
{
entry: false,
fileName: "bar.ts",
text: `\
const foo = 2;
export {foo as A};
`
}
],
{
typescript,
debug: false
}
);
const {
declarations: [file]
} = bundle;

t.deepEqual(
formatCode(file.code),
formatCode(`\
declare const foo = 2;
export { foo as A };
`)
);
});

test.serial("Handles default exports inside ExportSpecifiers. #8", withTypeScript, async (t, {typescript}) => {
const bundle = await generateRollupBundle(
[
{
entry: true,
fileName: "index.ts",
text: `\
import {A} from "./foo";
const B = A;
export { A as ARenamed, B };
`
},
{
entry: false,
fileName: "foo.ts",
text: `\
export { A } from "./bar";
`
},
{
entry: false,
fileName: "bar.ts",
text: `\
const foo = 2;
export {foo as A};
`
}
],
{
typescript,
debug: false
}
);
const {
declarations: [file]
} = bundle;

t.deepEqual(
formatCode(file.code),
formatCode(`\
declare const foo = 2;
declare const B = 2;
export { foo as ARenamed, B }
`)
);
});

0 comments on commit 16d24fa

Please sign in to comment.