-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Use getters to define live export bindings #33587
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -998,8 +998,8 @@ namespace ts { | |
setOriginalNode( | ||
setTextRange( | ||
createExpressionStatement( | ||
createExportExpression(getExportName(specifier), exportedValue) | ||
), | ||
createExportExpression(getExportName(specifier), exportedValue, /* location */ undefined, /* liveBinding */ true) | ||
), | ||
specifier), | ||
specifier | ||
) | ||
|
@@ -1310,7 +1310,7 @@ namespace ts { | |
|
||
case SyntaxKind.NamedImports: | ||
for (const importBinding of namedBindings.elements) { | ||
statements = appendExportsOfDeclaration(statements, importBinding); | ||
statements = appendExportsOfDeclaration(statements, importBinding, /* liveBinding */ true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above, an unconditional live binding will result in code that is invalid in an ES3 target. |
||
} | ||
|
||
break; | ||
|
@@ -1420,12 +1420,12 @@ namespace ts { | |
* appended. | ||
* @param decl The declaration to export. | ||
*/ | ||
function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration): Statement[] | undefined { | ||
function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration, liveBinding?: boolean): Statement[] | undefined { | ||
const name = getDeclarationName(decl); | ||
const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(idText(name)); | ||
if (exportSpecifiers) { | ||
for (const exportSpecifier of exportSpecifiers) { | ||
statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name); | ||
statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name, /* allowComments */ undefined, liveBinding); | ||
} | ||
} | ||
return statements; | ||
|
@@ -1443,8 +1443,8 @@ namespace ts { | |
* @param location The location to use for source maps and comments for the export. | ||
* @param allowComments Whether to allow comments on the export. | ||
*/ | ||
function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier, expression: Expression, location?: TextRange, allowComments?: boolean): Statement[] | undefined { | ||
statements = append(statements, createExportStatement(exportName, expression, location, allowComments)); | ||
function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier, expression: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean): Statement[] | undefined { | ||
statements = append(statements, createExportStatement(exportName, expression, location, allowComments, liveBinding)); | ||
return statements; | ||
} | ||
|
||
|
@@ -1485,8 +1485,8 @@ namespace ts { | |
* @param location The location to use for source maps and comments for the export. | ||
* @param allowComments An optional value indicating whether to emit comments for the statement. | ||
*/ | ||
function createExportStatement(name: Identifier, value: Expression, location?: TextRange, allowComments?: boolean) { | ||
const statement = setTextRange(createExpressionStatement(createExportExpression(name, value)), location); | ||
function createExportStatement(name: Identifier, value: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean) { | ||
const statement = setTextRange(createExpressionStatement(createExportExpression(name, value, /* location */ undefined, liveBinding)), location); | ||
startOnNewLine(statement); | ||
if (!allowComments) { | ||
setEmitFlags(statement, EmitFlags.NoComments); | ||
|
@@ -1502,9 +1502,30 @@ namespace ts { | |
* @param value The exported value. | ||
* @param location The location to use for source maps and comments for the export. | ||
*/ | ||
function createExportExpression(name: Identifier, value: Expression, location?: TextRange) { | ||
function createExportExpression(name: Identifier, value: Expression, location?: TextRange, liveBinding?: boolean) { | ||
return setTextRange( | ||
createAssignment( | ||
liveBinding && languageVersion !== ScriptTarget.ES3 ? createCall( | ||
createPropertyAccess( | ||
createIdentifier("Object"), | ||
"defineProperty" | ||
), | ||
/*typeArguments*/ undefined, | ||
[ | ||
createIdentifier("exports"), | ||
createLiteral(name), | ||
createObjectLiteral([ | ||
createPropertyAssignment("enumerable", createLiteral(/*value*/ true)), | ||
createPropertyAssignment("get", createArrowFunction( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to use |
||
/*modifiers*/ undefined, | ||
/*typeParameters*/ undefined, | ||
/*parameters*/ [], | ||
/*type*/ undefined, | ||
/*equalsGreaterThanToken*/ undefined, | ||
value | ||
)) | ||
]) | ||
] | ||
) : createAssignment( | ||
createPropertyAccess( | ||
createIdentifier("exports"), | ||
getSynthesizedClone(name) | ||
|
@@ -1786,7 +1807,18 @@ namespace ts { | |
scoped: true, | ||
text: ` | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
for (var p in m) b(p); | ||
function b(p) { | ||
if (!exports.hasOwnProperty(p)) | ||
Object.create | ||
? Object.defineProperty(exports, p, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a fan of how this gets checked on every iteration of the loop. In other places, we just decide up front what the helper is going to be and use that. |
||
enumerable: true, | ||
get: function() { | ||
return m[p]; | ||
} | ||
}) | ||
: (exports[p] = m[p]); | ||
} | ||
}` | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,7 +51,15 @@ exports.ADMIN = pkg2_1.MetadataAccessor.create('1'); | |
//// [index.js] | ||
"use strict"; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
for (var p in m) b(p); | ||
function b(p) { | ||
if (!exports.hasOwnProperty(p)) Object.defineProperty(exports, p, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test doesn't look like it was updated after the changes to the helper. |
||
enumerable: true, | ||
get: function () { | ||
return m[p]; | ||
} | ||
}); | ||
} | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__export(require("./keys")); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,7 +51,15 @@ exports.ADMIN = pkg2_1.MetadataAccessor.create('1'); | |
//// [index.js] | ||
"use strict"; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
for (var p in m) b(p); | ||
function b(p) { | ||
if (!exports.hasOwnProperty(p)) Object.defineProperty(exports, p, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test result is also out of date |
||
enumerable: true, | ||
get: function () { | ||
return m[p]; | ||
} | ||
}); | ||
} | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__export(require("./keys")); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,13 +37,13 @@ exports.x = 10; | |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var server_1 = require("./server"); | ||
exports.c = server_1.c; | ||
Object.defineProperty(exports, "c", { enumerable: true, get: () => server_1.c }); | ||
var server_2 = require("./server"); | ||
exports.c2 = server_2.c; | ||
Object.defineProperty(exports, "c2", { enumerable: true, get: () => server_2.c }); | ||
var server_3 = require("./server"); | ||
exports.instantiatedModule = server_3.m; | ||
Object.defineProperty(exports, "instantiatedModule", { enumerable: true, get: () => server_3.m }); | ||
var server_4 = require("./server"); | ||
exports.x = server_4.x; | ||
Object.defineProperty(exports, "x", { enumerable: true, get: () => server_4.x }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you do the hoisting it might be possible to write these with a single There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned above, I think the hoisting should be a separate PR. This is something that could be considered if the bookkeeping doesn't complicate things too much. I will note that Babel does not do this, but that doesn't mean TypeScript couldn't. |
||
|
||
|
||
//// [server.d.ts] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will unconditionally making this a live binding result in a
defineProperty
call for--target ES3
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that we want to support using
defineProperty
, and only fall back to property assignment when it’s not supported.