diff --git a/src/transformers/CJSImportTransformer.ts b/src/transformers/CJSImportTransformer.ts index 98dce73c..4b0c115f 100644 --- a/src/transformers/CJSImportTransformer.ts +++ b/src/transformers/CJSImportTransformer.ts @@ -314,13 +314,81 @@ export default class CJSImportTransformer extends Transformer { } /** - * Transforms normal declaration exports, including handling destructuring. + * Transform a declaration like `export var`, `export let`, or `export const`. + */ + private processExportVar(): void { + if (this.isSimpleExportVar()) { + this.processSimpleExportVar(); + } else { + this.processComplexExportVar(); + } + } + + /** + * Determine if the export is of the form: + * export var/let/const [varName] = [expr]; + * In other words, determine if function name inference might apply. + */ + private isSimpleExportVar(): boolean { + let tokenIndex = this.tokens.currentIndex(); + // export + tokenIndex++; + // var/let/const + tokenIndex++; + if (!this.tokens.matchesAtIndex(tokenIndex, [tt.name])) { + return false; + } + tokenIndex++; + while (tokenIndex < this.tokens.tokens.length && this.tokens.tokens[tokenIndex].isType) { + tokenIndex++; + } + if (!this.tokens.matchesAtIndex(tokenIndex, [tt.eq])) { + return false; + } + return true; + } + + /** + * Transform an `export var` declaration initializing a single variable. + * + * For example, this: + * export const f = () => {}; + * becomes this: + * const f = () => {}; exports.f = f; + * + * The variable is unused (e.g. exports.f has the true value of the export). + * We need to produce an assignment of this form so that the function will + * have an inferred name of "f", which wouldn't happen in the more general + * case below. + */ + private processSimpleExportVar(): void { + // export + this.tokens.removeInitialToken(); + // var/let/const + this.tokens.copyToken(); + const varName = this.tokens.identifierName(); + // x: number -> x + while (!this.tokens.matches1(tt.eq)) { + this.rootTransformer.processToken(); + } + const endIndex = this.tokens.currentToken().rhsEndIndex; + if (endIndex == null) { + throw new Error("Expected = token with an end index."); + } + while (this.tokens.currentIndex() < endIndex) { + this.rootTransformer.processToken(); + } + this.tokens.appendCode(`; exports.${varName} = ${varName}`); + } + + /** + * Transform normal declaration exports, including handling destructuring. * For example, this: * export const {x: [a = 2, b], c} = d; * becomes this: * ({x: [exports.a = 2, exports.b], c: exports.c} = d;) */ - private processExportVar(): void { + private processComplexExportVar(): void { this.tokens.removeInitialToken(); this.tokens.removeToken(); const needsParens = this.tokens.matches1(tt.braceL); diff --git a/test/imports-test.ts b/test/imports-test.ts index 0643068d..a023b324 100644 --- a/test/imports-test.ts +++ b/test/imports-test.ts @@ -78,9 +78,9 @@ describe("transform imports", () => { export const z = 3; `, `"use strict";${ESMODULE_PREFIX} - exports.x = 1; - exports.y = 2; - exports.z = 3; + var x = 1; exports.x = x; + let y = 2; exports.y = y; + const z = 3; exports.z = z; `, ); }); @@ -623,7 +623,7 @@ module.exports = exports.default; export default 4; `, `"use strict";${ESMODULE_PREFIX} - exports.x = 1; + const x = 1; exports.x = x; exports. default = 4; `, {transforms: ["imports"], enableLegacyBabel5ModuleInterop: true}, @@ -981,7 +981,7 @@ module.exports = exports.default; console.log(a); `, `"use strict";${ESMODULE_PREFIX} - exports.a = 1; + let a = 1; exports.a = a; ({a: exports.a} = 2); exports.a = 3; console.log(exports.a); @@ -1029,4 +1029,18 @@ module.exports = exports.default; {transforms: ["imports", "typescript"]}, ); }); + + it("allows function name inference for direct named exports", () => { + assertResult( + ` + export let f = () => {}; + export const g = () => {}; + `, + `"use strict";${ESMODULE_PREFIX} + let f = () => {}; exports.f = f; + const g = () => {}; exports.g = g; + `, + {transforms: ["imports", "typescript"]}, + ); + }); }); diff --git a/test/sucrase-test.ts b/test/sucrase-test.ts index bdf65b1e..25062e6f 100644 --- a/test/sucrase-test.ts +++ b/test/sucrase-test.ts @@ -49,7 +49,7 @@ describe("sucrase", () => { }; `, `"use strict";${ESMODULE_PREFIX} - exports.keywords = { + const keywords = { break: new KeywordTokenType("break"), case: new KeywordTokenType("case", { beforeExpr }), catch: new KeywordTokenType("catch"), @@ -87,7 +87,7 @@ describe("sucrase", () => { typeof: new KeywordTokenType("typeof", { beforeExpr, prefix, startsExpr }), void: new KeywordTokenType("void", { beforeExpr, prefix, startsExpr }), delete: new KeywordTokenType("delete", { beforeExpr, prefix, startsExpr }), - }; + }; exports.keywords = keywords; `, ); }); diff --git a/test/types-test.ts b/test/types-test.ts index dfac2cb7..22624ee5 100644 --- a/test/types-test.ts +++ b/test/types-test.ts @@ -208,7 +208,7 @@ describe("type transforms", () => { `, `"use strict";${ESMODULE_PREFIX} - exports.x = 1; + const x = 1; exports.x = x; `, ); });