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

Fix some additional enableLegacyBabel5ModuleInterop cases #807

Merged
merged 1 commit into from
Jul 12, 2023
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
24 changes: 17 additions & 7 deletions src/transformers/CJSImportTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import getDeclarationInfo, {
EMPTY_DECLARATION_INFO,
} from "../util/getDeclarationInfo";
import getImportExportSpecifierInfo from "../util/getImportExportSpecifierInfo";
import isExportFrom from "../util/isExportFrom";
import {removeMaybeImportAttributes} from "../util/removeMaybeImportAttributes";
import shouldElideDefaultExport from "../util/shouldElideDefaultExport";
import type ReactHotLoaderTransformer from "./ReactHotLoaderTransformer";
Expand Down Expand Up @@ -280,12 +281,13 @@ export default class CJSImportTransformer extends Transformer {
this.tokens.matches2(tt._export, tt._enum) ||
this.tokens.matches3(tt._export, tt._const, tt._enum)
) {
this.hadNamedExport = true;
// Let the TypeScript transform handle it.
return false;
}
if (this.tokens.matches2(tt._export, tt._default)) {
this.hadDefaultExport = true;
if (this.tokens.matches3(tt._export, tt._default, tt._enum)) {
this.hadDefaultExport = true;
// Flow export default enums need some special handling, so handle them
// in that tranform rather than this one.
return false;
Expand Down Expand Up @@ -488,6 +490,7 @@ export default class CJSImportTransformer extends Transformer {
}

private processExportDefault(): void {
let exportedRuntimeValue = true;
if (
this.tokens.matches4(tt._export, tt._default, tt._function, tt.name) ||
// export default async function
Expand Down Expand Up @@ -523,6 +526,7 @@ export default class CJSImportTransformer extends Transformer {
// If the exported value is just an identifier and should be elided by TypeScript
// rules, then remove it entirely. It will always have the form `export default e`,
// where `e` is an identifier.
exportedRuntimeValue = false;
this.tokens.removeInitialToken();
this.tokens.removeToken();
this.tokens.removeToken();
Expand All @@ -540,6 +544,9 @@ export default class CJSImportTransformer extends Transformer {
this.tokens.copyToken();
this.tokens.appendCode(" =");
}
if (exportedRuntimeValue) {
this.hadDefaultExport = true;
}
}

private copyDecorators(): void {
Expand Down Expand Up @@ -790,6 +797,8 @@ export default class CJSImportTransformer extends Transformer {
this.tokens.removeInitialToken();
this.tokens.removeToken();

const isReExport = isExportFrom(this.tokens);

const exportStatements: Array<string> = [];
while (true) {
if (this.tokens.matches1(tt.braceR)) {
Expand All @@ -803,18 +812,19 @@ export default class CJSImportTransformer extends Transformer {
this.tokens.removeToken();
}

if (!specifierInfo.isType) {
const shouldRemoveExport =
specifierInfo.isType ||
(!isReExport && this.shouldElideExportedIdentifier(specifierInfo.leftName));
if (!shouldRemoveExport) {
const exportedName = specifierInfo.rightName;
if (exportedName === "default") {
this.hadDefaultExport = true;
} else {
this.hadNamedExport = true;
}
if (!this.shouldElideExportedIdentifier(specifierInfo.leftName)) {
const localName = specifierInfo.leftName;
const newLocalName = this.importProcessor.getIdentifierReplacement(localName);
exportStatements.push(`exports.${exportedName} = ${newLocalName || localName};`);
}
const localName = specifierInfo.leftName;
const newLocalName = this.importProcessor.getIdentifierReplacement(localName);
exportStatements.push(`exports.${exportedName} = ${newLocalName || localName};`);
}

if (this.tokens.matches1(tt.braceR)) {
Expand Down
18 changes: 2 additions & 16 deletions src/transformers/ESMImportTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import getDeclarationInfo, {
} from "../util/getDeclarationInfo";
import getImportExportSpecifierInfo from "../util/getImportExportSpecifierInfo";
import {getNonTypeIdentifiers} from "../util/getNonTypeIdentifiers";
import isExportFrom from "../util/isExportFrom";
import {removeMaybeImportAttributes} from "../util/removeMaybeImportAttributes";
import shouldElideDefaultExport from "../util/shouldElideDefaultExport";
import type ReactHotLoaderTransformer from "./ReactHotLoaderTransformer";
Expand Down Expand Up @@ -330,7 +331,7 @@ export default class ESMImportTransformer extends Transformer {
this.tokens.copyExpectedToken(tt._export);
this.tokens.copyExpectedToken(tt.braceL);

const isReExport = this.isReExport();
const isReExport = isExportFrom(this.tokens);
let foundNonTypeExport = false;
while (!this.tokens.matches1(tt.braceR)) {
const specifierInfo = getImportExportSpecifierInfo(this.tokens);
Expand Down Expand Up @@ -369,21 +370,6 @@ export default class ESMImportTransformer extends Transformer {
return true;
}

/**
* Starting at `export {`, look ahead and return `true` if this is an
* `export {...} from` statement and `false` if this is a plain multi-export.
*/
private isReExport(): boolean {
let closeBraceIndex = this.tokens.currentIndex();
while (!this.tokens.matches1AtIndex(closeBraceIndex, tt.braceR)) {
closeBraceIndex++;
}
return (
this.tokens.matchesContextualAtIndex(closeBraceIndex + 1, ContextualKeyword._from) &&
this.tokens.matches1AtIndex(closeBraceIndex + 2, tt.string)
);
}

/**
* ESM elides all imports with the rule that we only elide if we see that it's
* a type and never see it as a value. This is in contrast to CJS, which
Expand Down
18 changes: 18 additions & 0 deletions src/util/isExportFrom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {ContextualKeyword} from "../parser/tokenizer/keywords";
import {TokenType as tt} from "../parser/tokenizer/types";
import type TokenProcessor from "../TokenProcessor";

/**
* Starting at `export {`, look ahead and return `true` if this is an
* `export {...} from` statement and `false` if this is a plain multi-export.
*/
export default function isExportFrom(tokens: TokenProcessor): boolean {
let closeBraceIndex = tokens.currentIndex();
while (!tokens.matches1AtIndex(closeBraceIndex, tt.braceR)) {
closeBraceIndex++;
}
return (
tokens.matchesContextualAtIndex(closeBraceIndex + 1, ContextualKeyword._from) &&
tokens.matches1AtIndex(closeBraceIndex + 2, tt.string)
);
}
44 changes: 44 additions & 0 deletions test/imports-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,9 @@ module.exports = exports.default;
`,
{transforms: ["imports"], enableLegacyBabel5ModuleInterop: true},
);
});

it("properly treats `as default` as a default export when adding module exports suffix", () => {
assertResult(
`
export { x as default } from './foo'
Expand All @@ -788,6 +790,34 @@ module.exports = exports.default;
);
});

it("properly ignores regular default-exported types when deciding to add module exports suffix", () => {
assertResult(
`
type T = number;
export default T;
`,
`"use strict";${ESMODULE_PREFIX}

;
`,
{transforms: ["imports", "typescript"], enableLegacyBabel5ModuleInterop: true},
);
});

it("properly ignores `{T as default}` when deciding to add module exports suffix", () => {
assertResult(
`
type T = number;
export { T as default };
`,
`"use strict";${ESMODULE_PREFIX}


`,
{transforms: ["imports", "typescript"], enableLegacyBabel5ModuleInterop: true},
);
});

it("does not add module exports suffix when there is a named export", () => {
assertResult(
`
Expand All @@ -802,6 +832,20 @@ module.exports = exports.default;
);
});

it("properly treats exported TS enums as a named export when adding module exports suffix", () => {
assertResult(
`
export enum E {}
export default 4;
`,
`"use strict";${ESMODULE_PREFIX}
var E; (function (E) {})(E || (exports.E = E = {}));
exports. default = 4;
`,
{transforms: ["imports", "typescript"], enableLegacyBabel5ModuleInterop: true},
);
});

it("does not modify object keys matching import names", () => {
assertResult(
`
Expand Down