-
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 2 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 ? 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,15 @@ 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.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 is not down-level compatible with ES3. You should instead feature-test for |
||
enumerable: true, | ||
get: function () { | ||
return m[p]; | ||
} | ||
}); | ||
} | ||
}` | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -20,11 +20,19 @@ x($); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"use strict"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
exports.__esModule = 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. If you're going with 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. It looks like there's some code that already handles using TypeScript/src/compiler/transformers/module/module.ts Lines 1451 to 1478 in 27941d0
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var jquery_1 = require("jquery"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
exports.x = jquery_1.x; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Object.defineProperty(exports, "x", { enumerable: true, get: () => jquery_1.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. In cases like this it actually should be possible to hoist the This might make circular imports more compatible. 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. Digging into this, it looks like you are correct - this should be hoisted. But so should every exported value, whether it's defined as a getter or not. I'm thinking all these cases should be handled as a separate PR. As it stands, if the property is accessed but the module from which it was re-exporting the value has not been loaded, we won't get the value in either case. The only thing that's affected is enumerability, which is already an issue without this PR. This PR is a step in the right direction and solves #12522. The hoisting is a separate issue IMO. 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. I believe this test and every other test that doesn't have a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//// [reExportAll.js] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"use strict"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
function __export(m) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mlrawlings marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
enumerable: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get: function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return m[p]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
exports.__esModule = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__export(require("jquery")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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")); | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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.