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 <mirawlings@ebay.com>
This commit is contained in:
Wesley Wigham
2020-02-24 15:36:14 -08:00
committed by GitHub
parent 7a3b6b4f4f
commit 65e7acce58
106 changed files with 1482 additions and 428 deletions

View File

@@ -33450,6 +33450,10 @@ namespace ts {
grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers);
}
if (node.moduleSpecifier && node.exportClause && isNamedExports(node.exportClause) && length(node.exportClause.elements) && languageVersion === ScriptTarget.ES3) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.CreateBinding);
}
if (!node.moduleSpecifier || checkExternalImportOrExportDeclaration(node)) {
if (node.exportClause) {
// export { x, y }
@@ -35661,6 +35665,8 @@ namespace ts {
case ExternalEmitHelpers.MakeTemplateObject: return "__makeTemplateObject";
case ExternalEmitHelpers.ClassPrivateFieldGet: return "__classPrivateFieldGet";
case ExternalEmitHelpers.ClassPrivateFieldSet: return "__classPrivateFieldSet";
case ExternalEmitHelpers.CreateBinding: return "__createBinding";
case ExternalEmitHelpers.SetModuleDefault: return "__setModuleDefault";
default: return Debug.fail("Unrecognized helper");
}
}

View File

@@ -412,6 +412,11 @@ namespace ts {
Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization.");
Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
Debug.assert(!helper.scoped, "Cannot request a scoped emit helper.");
if (helper.dependencies) {
for (const h of helper.dependencies) {
requestEmitHelper(h);
}
}
emitHelpers = append(emitHelpers, helper);
}

View File

@@ -103,11 +103,6 @@ namespace ts {
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
const updated = updateSourceFileNode(node, setTextRange(createNodeArray(statements), node.statements));
if (currentModuleInfo.hasExportStarsToExportValues && !compilerOptions.importHelpers) {
// If we have any `export * from ...` declarations
// we need to inform the emitter to add the __export helper.
addEmitHelper(updated, exportStarHelper);
}
addEmitHelpers(updated, context.readEmitHelpers());
return updated;
}
@@ -435,11 +430,6 @@ namespace ts {
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
const body = createBlock(statements, /*multiLine*/ true);
if (currentModuleInfo.hasExportStarsToExportValues && !compilerOptions.importHelpers) {
// If we have any `export * from ...` declarations
// we need to inform the emitter to add the __export helper.
addEmitHelper(body, exportStarHelper);
}
if (needUMDDynamicImportHelper) {
addEmitHelper(body, dynamicImportUMDHelper);
}
@@ -1001,20 +991,34 @@ namespace ts {
);
}
for (const specifier of node.exportClause.elements) {
const exportedValue = createPropertyAccess(
generatedName,
specifier.propertyName || specifier.name
);
statements.push(
setOriginalNode(
setTextRange(
createExpressionStatement(
createExportExpression(getExportName(specifier), exportedValue)
),
specifier),
specifier
)
);
if (languageVersion === ScriptTarget.ES3) {
statements.push(
setOriginalNode(
setTextRange(
createExpressionStatement(
createCreateBindingHelper(context, generatedName, createLiteral(specifier.propertyName || specifier.name), specifier.propertyName ? createLiteral(specifier.name) : undefined)
),
specifier),
specifier
)
);
}
else {
const exportedValue = createPropertyAccess(
generatedName,
specifier.propertyName || specifier.name
);
statements.push(
setOriginalNode(
setTextRange(
createExpressionStatement(
createExportExpression(getExportName(specifier), exportedValue, /* location */ undefined, /* liveBinding */ true)
),
specifier),
specifier
)
);
}
}
return singleOrMany(statements);
@@ -1343,7 +1347,7 @@ namespace ts {
case SyntaxKind.NamedImports:
for (const importBinding of namedBindings.elements) {
statements = appendExportsOfDeclaration(statements, importBinding);
statements = appendExportsOfDeclaration(statements, importBinding, /* liveBinding */ true);
}
break;
@@ -1453,12 +1457,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;
@@ -1476,8 +1480,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;
}
@@ -1518,8 +1522,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);
@@ -1535,9 +1539,31 @@ 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", createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
/*parameters*/ [],
/*type*/ undefined,
createBlock([createReturn(value)])
))
])
]
) : createAssignment(
createPropertyAccess(
createIdentifier("exports"),
getSynthesizedClone(name)
@@ -1813,21 +1839,55 @@ namespace ts {
}
}
// emit output for the __export helper function
const exportStarHelper: EmitHelper = {
name: "typescript:export-star",
scoped: true,
export const createBindingHelper: UnscopedEmitHelper = {
name: "typescript:commonjscreatebinding",
importName: "__createBinding",
scoped: false,
priority: 1,
text: `
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));`
};
function createCreateBindingHelper(context: TransformationContext, module: Expression, inputName: Expression, outputName: Expression | undefined) {
context.requestEmitHelper(createBindingHelper);
return createCall(getUnscopedHelperName("__createBinding"), /*typeArguments*/ undefined, [createIdentifier("exports"), module, inputName, ...(outputName ? [outputName] : [])]);
}
export const setModuleDefaultHelper: UnscopedEmitHelper = {
name: "typescript:commonjscreatevalue",
importName: "__setModuleDefault",
scoped: false,
priority: 1,
text: `
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});`
};
// emit output for the __export helper function
const exportStarHelper: UnscopedEmitHelper = {
name: "typescript:export-star",
importName: "__exportStar",
scoped: false,
dependencies: [createBindingHelper],
priority: 2,
text: `
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (!exports.hasOwnProperty(p)) __createBinding(exports, m, p);
}`
};
function createExportStarHelper(context: TransformationContext, module: Expression) {
const compilerOptions = context.getCompilerOptions();
return compilerOptions.importHelpers
? createCall(getUnscopedHelperName("__exportStar"), /*typeArguments*/ undefined, [module, createIdentifier("exports")])
: createCall(createIdentifier("__export"), /*typeArguments*/ undefined, [module]);
context.requestEmitHelper(exportStarHelper);
return createCall(getUnscopedHelperName("__exportStar"), /*typeArguments*/ undefined, [module, createIdentifier("exports")]);
}
// emit helper for dynamic import
@@ -1843,12 +1903,14 @@ namespace ts {
name: "typescript:commonjsimportstar",
importName: "__importStar",
scoped: false,
dependencies: [createBindingHelper, setModuleDefaultHelper],
priority: 2,
text: `
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};`
};

View File

@@ -5794,6 +5794,7 @@ namespace ts {
readonly scoped: boolean; // Indicates whether the helper MUST be emitted in the current scope.
readonly text: string | ((node: EmitHelperUniqueNameCallback) => string); // ES3-compatible raw script text, or a function yielding such a string
readonly priority?: number; // Helpers with a higher priority are emitted earlier than other helpers on the node.
readonly dependencies?: EmitHelper[]
}
export interface UnscopedEmitHelper extends EmitHelper {
@@ -5834,8 +5835,10 @@ namespace ts {
MakeTemplateObject = 1 << 17, // __makeTemplateObject (used for constructing template string array objects)
ClassPrivateFieldGet = 1 << 18, // __classPrivateFieldGet (used by the class private field transformation)
ClassPrivateFieldSet = 1 << 19, // __classPrivateFieldSet (used by the class private field transformation)
CreateBinding = 1 << 20, // __createBinding (use by the module transform for exports and namespace imports)
SetModuleDefault = 1 << 21, // __setModuleDefault (use by the module transform for default exports)
FirstEmitHelper = Extends,
LastEmitHelper = ClassPrivateFieldSet,
LastEmitHelper = SetModuleDefault,
// Helpers included by ES2015 for..of
ForOfIncludes = Values,