Skip to content

Commit 65e7acc

Browse files
Use getters to define live export bindings refresh (#35967)
* use getters to define live export bindings * fix scoping in export* helper * Object.defineProperty cannot be used in ES3 target * Accept changed baselines * Use function expression, not arrow function * Update importStarHelper to match export helper in binding-making * Fix whitespace * Adjust whitespace in edited helpers * Use new helper for setting bindings, use unscoped __exportStar helper for exports so helpers get reused more * Accept updated baselines * Use __createBinding for individual reexports when target is es3 * Remove unneeded type assertion * Singeline the helpers * Add check for createBinding helper, accept updated baselines with shortened helper Co-authored-by: Michael Rawlings <[email protected]>
1 parent 7a3b6b4 commit 65e7acc

File tree

106 files changed

+1480
-426
lines changed

Some content is hidden

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

106 files changed

+1480
-426
lines changed

src/compiler/checker.ts

+6
Original file line numberDiff line numberDiff line change
@@ -33450,6 +33450,10 @@ namespace ts {
3345033450
grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers);
3345133451
}
3345233452

33453+
if (node.moduleSpecifier && node.exportClause && isNamedExports(node.exportClause) && length(node.exportClause.elements) && languageVersion === ScriptTarget.ES3) {
33454+
checkExternalEmitHelpers(node, ExternalEmitHelpers.CreateBinding);
33455+
}
33456+
3345333457
if (!node.moduleSpecifier || checkExternalImportOrExportDeclaration(node)) {
3345433458
if (node.exportClause) {
3345533459
// export { x, y }
@@ -35661,6 +35665,8 @@ namespace ts {
3566135665
case ExternalEmitHelpers.MakeTemplateObject: return "__makeTemplateObject";
3566235666
case ExternalEmitHelpers.ClassPrivateFieldGet: return "__classPrivateFieldGet";
3566335667
case ExternalEmitHelpers.ClassPrivateFieldSet: return "__classPrivateFieldSet";
35668+
case ExternalEmitHelpers.CreateBinding: return "__createBinding";
35669+
case ExternalEmitHelpers.SetModuleDefault: return "__setModuleDefault";
3566435670
default: return Debug.fail("Unrecognized helper");
3566535671
}
3566635672
}

src/compiler/transformer.ts

+5
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,11 @@ namespace ts {
412412
Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization.");
413413
Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
414414
Debug.assert(!helper.scoped, "Cannot request a scoped emit helper.");
415+
if (helper.dependencies) {
416+
for (const h of helper.dependencies) {
417+
requestEmitHelper(h);
418+
}
419+
}
415420
emitHelpers = append(emitHelpers, helper);
416421
}
417422

src/compiler/transformers/module/module.ts

+105-43
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,6 @@ namespace ts {
103103
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
104104

105105
const updated = updateSourceFileNode(node, setTextRange(createNodeArray(statements), node.statements));
106-
if (currentModuleInfo.hasExportStarsToExportValues && !compilerOptions.importHelpers) {
107-
// If we have any `export * from ...` declarations
108-
// we need to inform the emitter to add the __export helper.
109-
addEmitHelper(updated, exportStarHelper);
110-
}
111106
addEmitHelpers(updated, context.readEmitHelpers());
112107
return updated;
113108
}
@@ -435,11 +430,6 @@ namespace ts {
435430
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
436431

437432
const body = createBlock(statements, /*multiLine*/ true);
438-
if (currentModuleInfo.hasExportStarsToExportValues && !compilerOptions.importHelpers) {
439-
// If we have any `export * from ...` declarations
440-
// we need to inform the emitter to add the __export helper.
441-
addEmitHelper(body, exportStarHelper);
442-
}
443433
if (needUMDDynamicImportHelper) {
444434
addEmitHelper(body, dynamicImportUMDHelper);
445435
}
@@ -1001,20 +991,34 @@ namespace ts {
1001991
);
1002992
}
1003993
for (const specifier of node.exportClause.elements) {
1004-
const exportedValue = createPropertyAccess(
1005-
generatedName,
1006-
specifier.propertyName || specifier.name
1007-
);
1008-
statements.push(
1009-
setOriginalNode(
1010-
setTextRange(
1011-
createExpressionStatement(
1012-
createExportExpression(getExportName(specifier), exportedValue)
1013-
),
1014-
specifier),
1015-
specifier
1016-
)
1017-
);
994+
if (languageVersion === ScriptTarget.ES3) {
995+
statements.push(
996+
setOriginalNode(
997+
setTextRange(
998+
createExpressionStatement(
999+
createCreateBindingHelper(context, generatedName, createLiteral(specifier.propertyName || specifier.name), specifier.propertyName ? createLiteral(specifier.name) : undefined)
1000+
),
1001+
specifier),
1002+
specifier
1003+
)
1004+
);
1005+
}
1006+
else {
1007+
const exportedValue = createPropertyAccess(
1008+
generatedName,
1009+
specifier.propertyName || specifier.name
1010+
);
1011+
statements.push(
1012+
setOriginalNode(
1013+
setTextRange(
1014+
createExpressionStatement(
1015+
createExportExpression(getExportName(specifier), exportedValue, /* location */ undefined, /* liveBinding */ true)
1016+
),
1017+
specifier),
1018+
specifier
1019+
)
1020+
);
1021+
}
10181022
}
10191023

10201024
return singleOrMany(statements);
@@ -1343,7 +1347,7 @@ namespace ts {
13431347

13441348
case SyntaxKind.NamedImports:
13451349
for (const importBinding of namedBindings.elements) {
1346-
statements = appendExportsOfDeclaration(statements, importBinding);
1350+
statements = appendExportsOfDeclaration(statements, importBinding, /* liveBinding */ true);
13471351
}
13481352

13491353
break;
@@ -1453,12 +1457,12 @@ namespace ts {
14531457
* appended.
14541458
* @param decl The declaration to export.
14551459
*/
1456-
function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration): Statement[] | undefined {
1460+
function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration, liveBinding?: boolean): Statement[] | undefined {
14571461
const name = getDeclarationName(decl);
14581462
const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(idText(name));
14591463
if (exportSpecifiers) {
14601464
for (const exportSpecifier of exportSpecifiers) {
1461-
statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name);
1465+
statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name, /* allowComments */ undefined, liveBinding);
14621466
}
14631467
}
14641468
return statements;
@@ -1476,8 +1480,8 @@ namespace ts {
14761480
* @param location The location to use for source maps and comments for the export.
14771481
* @param allowComments Whether to allow comments on the export.
14781482
*/
1479-
function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier, expression: Expression, location?: TextRange, allowComments?: boolean): Statement[] | undefined {
1480-
statements = append(statements, createExportStatement(exportName, expression, location, allowComments));
1483+
function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier, expression: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean): Statement[] | undefined {
1484+
statements = append(statements, createExportStatement(exportName, expression, location, allowComments, liveBinding));
14811485
return statements;
14821486
}
14831487

@@ -1518,8 +1522,8 @@ namespace ts {
15181522
* @param location The location to use for source maps and comments for the export.
15191523
* @param allowComments An optional value indicating whether to emit comments for the statement.
15201524
*/
1521-
function createExportStatement(name: Identifier, value: Expression, location?: TextRange, allowComments?: boolean) {
1522-
const statement = setTextRange(createExpressionStatement(createExportExpression(name, value)), location);
1525+
function createExportStatement(name: Identifier, value: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean) {
1526+
const statement = setTextRange(createExpressionStatement(createExportExpression(name, value, /* location */ undefined, liveBinding)), location);
15231527
startOnNewLine(statement);
15241528
if (!allowComments) {
15251529
setEmitFlags(statement, EmitFlags.NoComments);
@@ -1535,9 +1539,31 @@ namespace ts {
15351539
* @param value The exported value.
15361540
* @param location The location to use for source maps and comments for the export.
15371541
*/
1538-
function createExportExpression(name: Identifier, value: Expression, location?: TextRange) {
1542+
function createExportExpression(name: Identifier, value: Expression, location?: TextRange, liveBinding?: boolean) {
15391543
return setTextRange(
1540-
createAssignment(
1544+
liveBinding && languageVersion !== ScriptTarget.ES3 ? createCall(
1545+
createPropertyAccess(
1546+
createIdentifier("Object"),
1547+
"defineProperty"
1548+
),
1549+
/*typeArguments*/ undefined,
1550+
[
1551+
createIdentifier("exports"),
1552+
createLiteral(name),
1553+
createObjectLiteral([
1554+
createPropertyAssignment("enumerable", createLiteral(/*value*/ true)),
1555+
createPropertyAssignment("get", createFunctionExpression(
1556+
/*modifiers*/ undefined,
1557+
/*asteriskToken*/ undefined,
1558+
/*name*/ undefined,
1559+
/*typeParameters*/ undefined,
1560+
/*parameters*/ [],
1561+
/*type*/ undefined,
1562+
createBlock([createReturn(value)])
1563+
))
1564+
])
1565+
]
1566+
) : createAssignment(
15411567
createPropertyAccess(
15421568
createIdentifier("exports"),
15431569
getSynthesizedClone(name)
@@ -1813,21 +1839,55 @@ namespace ts {
18131839
}
18141840
}
18151841

1842+
export const createBindingHelper: UnscopedEmitHelper = {
1843+
name: "typescript:commonjscreatebinding",
1844+
importName: "__createBinding",
1845+
scoped: false,
1846+
priority: 1,
1847+
text: `
1848+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
1849+
if (k2 === undefined) k2 = k;
1850+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
1851+
}) : (function(o, m, k, k2) {
1852+
if (k2 === undefined) k2 = k;
1853+
o[k2] = m[k];
1854+
}));`
1855+
};
1856+
1857+
function createCreateBindingHelper(context: TransformationContext, module: Expression, inputName: Expression, outputName: Expression | undefined) {
1858+
context.requestEmitHelper(createBindingHelper);
1859+
return createCall(getUnscopedHelperName("__createBinding"), /*typeArguments*/ undefined, [createIdentifier("exports"), module, inputName, ...(outputName ? [outputName] : [])]);
1860+
}
1861+
1862+
export const setModuleDefaultHelper: UnscopedEmitHelper = {
1863+
name: "typescript:commonjscreatevalue",
1864+
importName: "__setModuleDefault",
1865+
scoped: false,
1866+
priority: 1,
1867+
text: `
1868+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
1869+
Object.defineProperty(o, "default", { enumerable: true, value: v });
1870+
}) : function(o, v) {
1871+
o["default"] = v;
1872+
});`
1873+
};
1874+
18161875
// emit output for the __export helper function
1817-
const exportStarHelper: EmitHelper = {
1876+
const exportStarHelper: UnscopedEmitHelper = {
18181877
name: "typescript:export-star",
1819-
scoped: true,
1878+
importName: "__exportStar",
1879+
scoped: false,
1880+
dependencies: [createBindingHelper],
1881+
priority: 2,
18201882
text: `
1821-
function __export(m) {
1822-
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
1883+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
1884+
for (var p in m) if (!exports.hasOwnProperty(p)) __createBinding(exports, m, p);
18231885
}`
18241886
};
18251887

18261888
function createExportStarHelper(context: TransformationContext, module: Expression) {
1827-
const compilerOptions = context.getCompilerOptions();
1828-
return compilerOptions.importHelpers
1829-
? createCall(getUnscopedHelperName("__exportStar"), /*typeArguments*/ undefined, [module, createIdentifier("exports")])
1830-
: createCall(createIdentifier("__export"), /*typeArguments*/ undefined, [module]);
1889+
context.requestEmitHelper(exportStarHelper);
1890+
return createCall(getUnscopedHelperName("__exportStar"), /*typeArguments*/ undefined, [module, createIdentifier("exports")]);
18311891
}
18321892

18331893
// emit helper for dynamic import
@@ -1843,12 +1903,14 @@ namespace ts {
18431903
name: "typescript:commonjsimportstar",
18441904
importName: "__importStar",
18451905
scoped: false,
1906+
dependencies: [createBindingHelper, setModuleDefaultHelper],
1907+
priority: 2,
18461908
text: `
18471909
var __importStar = (this && this.__importStar) || function (mod) {
18481910
if (mod && mod.__esModule) return mod;
18491911
var result = {};
1850-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
1851-
result["default"] = mod;
1912+
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
1913+
__setModuleDefault(result, mod);
18521914
return result;
18531915
};`
18541916
};

src/compiler/types.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -5794,6 +5794,7 @@ namespace ts {
57945794
readonly scoped: boolean; // Indicates whether the helper MUST be emitted in the current scope.
57955795
readonly text: string | ((node: EmitHelperUniqueNameCallback) => string); // ES3-compatible raw script text, or a function yielding such a string
57965796
readonly priority?: number; // Helpers with a higher priority are emitted earlier than other helpers on the node.
5797+
readonly dependencies?: EmitHelper[]
57975798
}
57985799

57995800
export interface UnscopedEmitHelper extends EmitHelper {
@@ -5834,8 +5835,10 @@ namespace ts {
58345835
MakeTemplateObject = 1 << 17, // __makeTemplateObject (used for constructing template string array objects)
58355836
ClassPrivateFieldGet = 1 << 18, // __classPrivateFieldGet (used by the class private field transformation)
58365837
ClassPrivateFieldSet = 1 << 19, // __classPrivateFieldSet (used by the class private field transformation)
5838+
CreateBinding = 1 << 20, // __createBinding (use by the module transform for exports and namespace imports)
5839+
SetModuleDefault = 1 << 21, // __setModuleDefault (use by the module transform for default exports)
58375840
FirstEmitHelper = Extends,
5838-
LastEmitHelper = ClassPrivateFieldSet,
5841+
LastEmitHelper = SetModuleDefault,
58395842

58405843
// Helpers included by ES2015 for..of
58415844
ForOfIncludes = Values,

tests/baselines/reference/ambientShorthand_reExport.js

+18-4
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,30 @@ x($);
1818

1919
//// [reExportX.js]
2020
"use strict";
21+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22+
if (k2 === undefined) k2 = k;
23+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
24+
}) : (function(o, m, k, k2) {
25+
if (k2 === undefined) k2 = k;
26+
o[k2] = m[k];
27+
}));
2128
exports.__esModule = true;
2229
var jquery_1 = require("jquery");
23-
exports.x = jquery_1.x;
30+
__createBinding(exports, jquery_1, "x");
2431
//// [reExportAll.js]
2532
"use strict";
26-
function __export(m) {
27-
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
33+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
34+
if (k2 === undefined) k2 = k;
35+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
36+
}) : (function(o, m, k, k2) {
37+
if (k2 === undefined) k2 = k;
38+
o[k2] = m[k];
39+
}));
40+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
41+
for (var p in m) if (!exports.hasOwnProperty(p)) __createBinding(exports, m, p);
2842
}
2943
exports.__esModule = true;
30-
__export(require("jquery"));
44+
__exportStar(require("jquery"), exports);
3145
//// [reExportUser.js]
3246
"use strict";
3347
exports.__esModule = true;

tests/baselines/reference/api/tsserverlibrary.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2972,6 +2972,7 @@ declare namespace ts {
29722972
readonly scoped: boolean;
29732973
readonly text: string | ((node: EmitHelperUniqueNameCallback) => string);
29742974
readonly priority?: number;
2975+
readonly dependencies?: EmitHelper[];
29752976
}
29762977
export interface UnscopedEmitHelper extends EmitHelper {
29772978
readonly scoped: false;

tests/baselines/reference/api/typescript.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2972,6 +2972,7 @@ declare namespace ts {
29722972
readonly scoped: boolean;
29732973
readonly text: string | ((node: EmitHelperUniqueNameCallback) => string);
29742974
readonly priority?: number;
2975+
readonly dependencies?: EmitHelper[];
29752976
}
29762977
export interface UnscopedEmitHelper extends EmitHelper {
29772978
readonly scoped: false;

tests/baselines/reference/commentsOnRequireStatement.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3232
// blah
3333
// blah
3434
var _0_1 = require("./0");
35-
exports.subject = _0_1.subject;
35+
Object.defineProperty(exports, "subject", { enumerable: true, get: function () { return _0_1.subject; } });
3636
/* blah1 */
3737
var _1_1 = require("./1");
38-
exports.subject1 = _1_1.subject1;
38+
Object.defineProperty(exports, "subject1", { enumerable: true, get: function () { return _1_1.subject1; } });

tests/baselines/reference/constEnumPreserveEmitReexport.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ var ConstEnum_1 = require("./ConstEnum");
2727
exports["default"] = ConstEnum_1.MyConstEnum;
2828
//// [ReExport.js]
2929
"use strict";
30+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
31+
if (k2 === undefined) k2 = k;
32+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
33+
}) : (function(o, m, k, k2) {
34+
if (k2 === undefined) k2 = k;
35+
o[k2] = m[k];
36+
}));
3037
exports.__esModule = true;
3138
var ConstEnum_1 = require("./ConstEnum");
32-
exports["default"] = ConstEnum_1.MyConstEnum;
39+
__createBinding(exports, ConstEnum_1, "MyConstEnum", "default");

tests/baselines/reference/declarationEmitExportDeclaration.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ exports.bar = bar;
2323
"use strict";
2424
Object.defineProperty(exports, "__esModule", { value: true });
2525
var utils_1 = require("./utils");
26-
exports.bar = utils_1.bar;
26+
Object.defineProperty(exports, "bar", { enumerable: true, get: function () { return utils_1.bar; } });
2727
utils_1.foo();
2828
var obj;
2929

tests/baselines/reference/declarationEmitReexportedSymlinkReference.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,18 @@ var pkg2_1 = require("@raymondfeng/pkg2");
5050
exports.ADMIN = pkg2_1.MetadataAccessor.create('1');
5151
//// [index.js]
5252
"use strict";
53-
function __export(m) {
54-
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
53+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
54+
if (k2 === undefined) k2 = k;
55+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
56+
}) : (function(o, m, k, k2) {
57+
if (k2 === undefined) k2 = k;
58+
o[k2] = m[k];
59+
}));
60+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
61+
for (var p in m) if (!exports.hasOwnProperty(p)) __createBinding(exports, m, p);
5562
}
5663
Object.defineProperty(exports, "__esModule", { value: true });
57-
__export(require("./keys"));
64+
__exportStar(require("./keys"), exports);
5865

5966

6067
//// [keys.d.ts]

0 commit comments

Comments
 (0)