Skip to content

Commit 881f449

Browse files
authored
Hoist function exports to top of module body in CJS/AMD/UMD (#57669)
1 parent 27fe503 commit 881f449

File tree

1,235 files changed

+2862
-3327
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,235 files changed

+2862
-3327
lines changed

src/compiler/transformers/module/module.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -264,21 +264,26 @@ export function transformModule(context: TransformationContext): (x: SourceFile
264264
if (shouldEmitUnderscoreUnderscoreESModule()) {
265265
append(statements, createUnderscoreUnderscoreESModule());
266266
}
267-
if (length(currentModuleInfo.exportedNames)) {
267+
if (some(currentModuleInfo.exportedNames)) {
268268
const chunkSize = 50;
269-
for (let i = 0; i < currentModuleInfo.exportedNames!.length; i += chunkSize) {
269+
for (let i = 0; i < currentModuleInfo.exportedNames.length; i += chunkSize) {
270270
append(
271271
statements,
272272
factory.createExpressionStatement(
273273
reduceLeft(
274-
currentModuleInfo.exportedNames!.slice(i, i + chunkSize),
274+
currentModuleInfo.exportedNames.slice(i, i + chunkSize),
275275
(prev, nextId) => factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier("exports"), factory.createIdentifier(idText(nextId))), prev),
276276
factory.createVoidZero() as Expression,
277277
),
278278
),
279279
);
280280
}
281281
}
282+
if (some(currentModuleInfo.exportedFunctions)) {
283+
for (const f of currentModuleInfo.exportedFunctions) {
284+
appendExportsOfHoistedDeclaration(statements, f);
285+
}
286+
}
282287

283288
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, topLevelVisitor, isStatement));
284289
addRange(statements, visitNodes(node.statements, topLevelVisitor, isStatement, statementOffset));
@@ -601,9 +606,14 @@ export function transformModule(context: TransformationContext): (x: SourceFile
601606
if (shouldEmitUnderscoreUnderscoreESModule()) {
602607
append(statements, createUnderscoreUnderscoreESModule());
603608
}
604-
if (length(currentModuleInfo.exportedNames)) {
609+
if (some(currentModuleInfo.exportedNames)) {
605610
append(statements, factory.createExpressionStatement(reduceLeft(currentModuleInfo.exportedNames, (prev, nextId) => factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier("exports"), factory.createIdentifier(idText(nextId))), prev), factory.createVoidZero() as Expression)));
606611
}
612+
if (some(currentModuleInfo.exportedFunctions)) {
613+
for (const f of currentModuleInfo.exportedFunctions) {
614+
appendExportsOfHoistedDeclaration(statements, f);
615+
}
616+
}
607617

608618
// Visit each statement of the module body.
609619
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, topLevelVisitor, isStatement));
@@ -1704,7 +1714,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
17041714
statements = append(statements, visitEachChild(node, visitor, context));
17051715
}
17061716

1707-
statements = appendExportsOfHoistedDeclaration(statements, node);
1717+
// NOTE: CommonJS/AMD/UMD exports are hoisted to the top of the module body and do not need to be added here.
17081718
return singleOrMany(statements);
17091719
}
17101720

src/compiler/transformers/module/system.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
433433
// this set is used to filter names brought by star expors.
434434

435435
// local names set should only be added if we have anything exported
436-
if (!moduleInfo.exportedNames && moduleInfo.exportSpecifiers.size === 0) {
436+
if (!some(moduleInfo.exportedNames) && !some(moduleInfo.exportedFunctions) && moduleInfo.exportSpecifiers.size === 0) {
437437
// no exported declarations (export var ...) or export specifiers (export {x})
438438
// check if we have any non star export declarations.
439439
let hasExportDeclarationWithExportClause = false;
@@ -469,6 +469,23 @@ export function transformSystemModule(context: TransformationContext): (x: Sourc
469469
}
470470
}
471471

472+
if (moduleInfo.exportedFunctions) {
473+
for (const f of moduleInfo.exportedFunctions) {
474+
if (hasSyntacticModifier(f, ModifierFlags.Default)) {
475+
continue;
476+
}
477+
Debug.assert(!!f.name);
478+
479+
// write name of exported declaration, i.e 'export var x...'
480+
exportedNames.push(
481+
factory.createPropertyAssignment(
482+
factory.createStringLiteralFromNode(f.name),
483+
factory.createTrue(),
484+
),
485+
);
486+
}
487+
}
488+
472489
const exportedNamesStorageRef = factory.createUniqueName("exportedNames");
473490
statements.push(
474491
factory.createVariableStatement(

src/compiler/transformers/utilities.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ export interface ExternalModuleInfo {
103103
externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers
104104
exportSpecifiers: IdentifierNameMap<ExportSpecifier[]>; // file-local export specifiers by name (no reexports)
105105
exportedBindings: Identifier[][]; // exported names of local declarations
106-
exportedNames: Identifier[] | undefined; // all exported names in the module, both local and reexported
106+
exportedNames: Identifier[] | undefined; // all exported names in the module, both local and reexported, excluding the names of locally exported function declarations
107+
exportedFunctions: FunctionDeclaration[] | undefined; // all of the top-level exported function declarations
107108
exportEquals: ExportAssignment | undefined; // an export= declaration if one was present
108109
hasExportStarsToExportValues: boolean; // whether this module contains export*
109110
}
@@ -171,6 +172,7 @@ export function collectExternalModuleInfo(context: TransformationContext, source
171172
const exportedBindings: Identifier[][] = [];
172173
const uniqueExports = new Map<string, boolean>();
173174
let exportedNames: Identifier[] | undefined;
175+
let exportedFunctions: FunctionDeclaration[] | undefined;
174176
let hasExportDefault = false;
175177
let exportEquals: ExportAssignment | undefined;
176178
let hasExportStarsToExportValues = false;
@@ -250,6 +252,7 @@ export function collectExternalModuleInfo(context: TransformationContext, source
250252

251253
case SyntaxKind.FunctionDeclaration:
252254
if (hasSyntacticModifier(node, ModifierFlags.Export)) {
255+
exportedFunctions = append(exportedFunctions, node as FunctionDeclaration);
253256
if (hasSyntacticModifier(node, ModifierFlags.Default)) {
254257
// export default function() { }
255258
if (!hasExportDefault) {
@@ -263,7 +266,6 @@ export function collectExternalModuleInfo(context: TransformationContext, source
263266
if (!uniqueExports.get(idText(name))) {
264267
multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name);
265268
uniqueExports.set(idText(name), true);
266-
exportedNames = append(exportedNames, name);
267269
}
268270
}
269271
}
@@ -297,7 +299,7 @@ export function collectExternalModuleInfo(context: TransformationContext, source
297299
externalImports.unshift(externalHelpersImportDeclaration);
298300
}
299301

300-
return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, externalHelpersImportDeclaration };
302+
return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, exportedFunctions, externalHelpersImportDeclaration };
301303

302304
function addExportedNamesForExportDeclaration(node: ExportDeclaration) {
303305
for (const specifier of cast(node.exportClause, isNamedExports).elements) {

tests/baselines/reference/APISample_compile.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ compile(process.argv.slice(2), {
5454
* Please log a "breaking change" issue for any API breaking change affecting this issue
5555
*/
5656
Object.defineProperty(exports, "__esModule", { value: true });
57-
exports.compile = void 0;
57+
exports.compile = compile;
5858
var ts = require("typescript");
5959
function compile(fileNames, options) {
6060
var program = ts.createProgram(fileNames, options);
@@ -73,7 +73,6 @@ function compile(fileNames, options) {
7373
console.log("Process exiting with code '".concat(exitCode, "'."));
7474
process.exit(exitCode);
7575
}
76-
exports.compile = compile;
7776
compile(process.argv.slice(2), {
7877
noEmitOnError: true, noImplicitAny: true,
7978
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS

tests/baselines/reference/APISample_linter.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ fileNames.forEach(fileName => {
8080
* Please log a "breaking change" issue for any API breaking change affecting this issue
8181
*/
8282
Object.defineProperty(exports, "__esModule", { value: true });
83-
exports.delint = void 0;
83+
exports.delint = delint;
8484
var ts = require("typescript");
8585
function delint(sourceFile) {
8686
delintNode(sourceFile);
@@ -119,7 +119,6 @@ function delint(sourceFile) {
119119
console.log("".concat(sourceFile.fileName, " (").concat(line + 1, ",").concat(character + 1, "): ").concat(message));
120120
}
121121
}
122-
exports.delint = delint;
123122
var fileNames = process.argv.slice(2);
124123
fileNames.forEach(function (fileName) {
125124
// Parse a file

tests/baselines/reference/APISample_parseConfig.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function createProgram(rootFiles: string[], compilerOptionsJson: string):
5252
* Please log a "breaking change" issue for any API breaking change affecting this issue
5353
*/
5454
Object.defineProperty(exports, "__esModule", { value: true });
55-
exports.createProgram = void 0;
55+
exports.createProgram = createProgram;
5656
var ts = require("typescript");
5757
function printError(error) {
5858
if (!error) {
@@ -77,4 +77,3 @@ function createProgram(rootFiles, compilerOptionsJson) {
7777
}
7878
return ts.createProgram(rootFiles, settings.options);
7979
}
80-
exports.createProgram = createProgram;

tests/baselines/reference/aliasUsedAsNameValue.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ exports.id = void 0;
2525
//// [aliasUsedAsNameValue_1.js]
2626
"use strict";
2727
Object.defineProperty(exports, "__esModule", { value: true });
28-
exports.b = void 0;
29-
function b(a) { return null; }
3028
exports.b = b;
29+
function b(a) { return null; }
3130
//// [aliasUsedAsNameValue_2.js]
3231
"use strict";
3332
Object.defineProperty(exports, "__esModule", { value: true });

tests/baselines/reference/amdDeclarationEmitNoExtraDeclare.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ var __extends = (this && this.__extends) || (function () {
4040
define("Configurable", ["require", "exports"], function (require, exports) {
4141
"use strict";
4242
Object.defineProperty(exports, "__esModule", { value: true });
43-
exports.Configurable = void 0;
43+
exports.Configurable = Configurable;
4444
function Configurable(base) {
4545
return /** @class */ (function (_super) {
4646
__extends(class_1, _super);
@@ -54,7 +54,6 @@ define("Configurable", ["require", "exports"], function (require, exports) {
5454
return class_1;
5555
}(base));
5656
}
57-
exports.Configurable = Configurable;
5857
});
5958
define("Class", ["require", "exports", "Configurable"], function (require, exports, Configurable_1) {
6059
"use strict";

tests/baselines/reference/anonClassDeclarationEmitIsAnon.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ var __extends = (this && this.__extends) || (function () {
5252
};
5353
})();
5454
Object.defineProperty(exports, "__esModule", { value: true });
55-
exports.Timestamped = exports.wrapClass = void 0;
55+
exports.wrapClass = wrapClass;
56+
exports.Timestamped = Timestamped;
5657
function wrapClass(param) {
5758
return /** @class */ (function () {
5859
function Wrapped() {
@@ -63,7 +64,6 @@ function wrapClass(param) {
6364
return Wrapped;
6465
}());
6566
}
66-
exports.wrapClass = wrapClass;
6767
function Timestamped(Base) {
6868
return /** @class */ (function (_super) {
6969
__extends(class_1, _super);
@@ -75,7 +75,6 @@ function Timestamped(Base) {
7575
return class_1;
7676
}(Base));
7777
}
78-
exports.Timestamped = Timestamped;
7978
//// [index.js]
8079
"use strict";
8180
var __extends = (this && this.__extends) || (function () {

tests/baselines/reference/anonymousClassDeclarationDoesntPrintWithReadonly.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ var __extends = (this && this.__extends) || (function () {
2727
};
2828
})();
2929
Object.defineProperty(exports, "__esModule", { value: true });
30-
exports.y = exports.X = void 0;
30+
exports.X = void 0;
31+
exports.y = y;
3132
var X = /** @class */ (function () {
3233
function X(a) {
3334
this.a = a;
@@ -44,7 +45,6 @@ function y() {
4445
return class_1;
4546
}(X));
4647
}
47-
exports.y = y;
4848

4949

5050
//// [anonymousClassDeclarationDoesntPrintWithReadonly.d.ts]

tests/baselines/reference/anonymousDefaultExportsAmd.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ define(["require", "exports"], function (require, exports) {
1818
define(["require", "exports"], function (require, exports) {
1919
"use strict";
2020
Object.defineProperty(exports, "__esModule", { value: true });
21-
function default_1() { }
2221
exports.default = default_1;
22+
function default_1() { }
2323
});

tests/baselines/reference/anonymousDefaultExportsCommonjs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ exports.default = default_1;
1515
//// [b.js]
1616
"use strict";
1717
Object.defineProperty(exports, "__esModule", { value: true });
18-
function default_1() { }
1918
exports.default = default_1;
19+
function default_1() { }

tests/baselines/reference/anonymousDefaultExportsUmd.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ export default function() {}
3434
})(function (require, exports) {
3535
"use strict";
3636
Object.defineProperty(exports, "__esModule", { value: true });
37-
function default_1() { }
3837
exports.default = default_1;
38+
function default_1() { }
3939
});

tests/baselines/reference/arrayDestructuringInSwitch1.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function evaluate(expression: Expression): boolean {
2626
//// [arrayDestructuringInSwitch1.js]
2727
"use strict";
2828
Object.defineProperty(exports, "__esModule", { value: true });
29-
exports.evaluate = void 0;
29+
exports.evaluate = evaluate;
3030
function evaluate(expression) {
3131
if (Array.isArray(expression)) {
3232
var operator = expression[0], operands = expression.slice(1);
@@ -46,4 +46,3 @@ function evaluate(expression) {
4646
return expression === 'true';
4747
}
4848
}
49-
exports.evaluate = evaluate;

tests/baselines/reference/asOperator4.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ import { foo } from './foo';
1414
//// [foo.js]
1515
"use strict";
1616
Object.defineProperty(exports, "__esModule", { value: true });
17-
exports.foo = void 0;
18-
function foo() { }
1917
exports.foo = foo;
18+
function foo() { }
2019
//// [bar.js]
2120
"use strict";
2221
Object.defineProperty(exports, "__esModule", { value: true });

tests/baselines/reference/assertionFunctionWildcardImport2.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ function isNonNullable(obj) {
2929
throw new Error("Must not be a nullable value");
3030
}
3131
}
32-
exports.isNonNullable = isNonNullable;
3332
//// [test.js]
3433
"use strict";
3534
Object.defineProperty(exports, "__esModule", { value: true });

tests/baselines/reference/baseConstraintOfDecorator.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ var __extends = (this && this.__extends) || (function () {
3939
};
4040
})();
4141
Object.defineProperty(exports, "__esModule", { value: true });
42-
exports.classExtender2 = exports.classExtender = void 0;
42+
exports.classExtender = classExtender;
43+
exports.classExtender2 = classExtender2;
4344
function classExtender(superClass, _instanceModifier) {
4445
return /** @class */ (function (_super) {
4546
__extends(decoratorFunc, _super);
@@ -55,7 +56,6 @@ function classExtender(superClass, _instanceModifier) {
5556
return decoratorFunc;
5657
}(superClass));
5758
}
58-
exports.classExtender = classExtender;
5959
var MyClass = /** @class */ (function () {
6060
function MyClass() {
6161
}
@@ -76,4 +76,3 @@ function classExtender2(superClass, _instanceModifier) {
7676
return decoratorFunc;
7777
}(superClass));
7878
}
79-
exports.classExtender2 = classExtender2;

tests/baselines/reference/checkJsxUnionSFXContextualTypeInferredCorrectly.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4747
return (mod && mod.__esModule) ? mod : { "default": mod };
4848
};
4949
Object.defineProperty(exports, "__esModule", { value: true });
50-
exports.HereIsTheError = exports.ComponentWithUnion = void 0;
50+
exports.ComponentWithUnion = ComponentWithUnion;
51+
exports.HereIsTheError = HereIsTheError;
5152
var react_1 = __importDefault(require("react"));
5253
function ComponentWithUnion(props) {
5354
return react_1.default.createElement("h1", null);
5455
}
55-
exports.ComponentWithUnion = ComponentWithUnion;
5656
// Usage with React tsx
5757
function HereIsTheError() {
5858
return (react_1.default.createElement(ComponentWithUnion, { multi: false, value: 's', onChange: function (val) { return console.log(val); } }));
5959
}
60-
exports.HereIsTheError = HereIsTheError;
6160
// Usage with pure TypeScript
6261
ComponentWithUnion({
6362
multi: false,

tests/baselines/reference/circularReferenceInImport.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@ export function foo() {
1717
//// [app.js]
1818
"use strict";
1919
Object.defineProperty(exports, "__esModule", { value: true });
20-
exports.foo = void 0;
20+
exports.foo = foo;
2121
function foo() {
2222
return new Object();
2323
}
24-
exports.foo = foo;
2524

2625

2726
//// [app.d.ts]

tests/baselines/reference/circularResolvedSignature.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@ export function Component() {
2121
//// [circularResolvedSignature.js]
2222
"use strict";
2323
Object.defineProperty(exports, "__esModule", { value: true });
24-
exports.Component = void 0;
24+
exports.Component = Component;
2525
function Component() {
2626
var _a = useState(function () { return ({
2727
value: "string", // this should be a number
2828
foo: function (arg) { return setState(arg); },
2929
bar: function (arg) { return setState(arg); },
3030
}); }), state = _a[0], setState = _a[1];
3131
}
32-
exports.Component = Component;

0 commit comments

Comments
 (0)