Skip to content
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 refresh #35967

Merged
merged 18 commits into from
Feb 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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";
weswigham marked this conversation as resolved.
Show resolved Hide resolved
default: return Debug.fail("Unrecognized helper");
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
148 changes: 105 additions & 43 deletions src/compiler/transformers/module/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -1813,21 +1839,55 @@ namespace ts {
}
}

export const createBindingHelper: UnscopedEmitHelper = {
name: "typescript:commonjscreatebinding",
importName: "__createBinding",
scoped: false,
priority: 1,
text: `
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
weswigham marked this conversation as resolved.
Show resolved Hide resolved
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) {
weswigham marked this conversation as resolved.
Show resolved Hide resolved
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});`
};

// emit output for the __export helper function
const exportStarHelper: EmitHelper = {
const exportStarHelper: UnscopedEmitHelper = {
name: "typescript:export-star",
scoped: true,
importName: "__exportStar",
scoped: false,
dependencies: [createBindingHelper],
priority: 2,
text: `
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
var __exportStar = (this && this.__exportStar) || function(m, exports) {
rbuckton marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -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;
};`
};
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
22 changes: 18 additions & 4 deletions tests/baselines/reference/ambientShorthand_reExport.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,30 @@ x($);

//// [reExportX.js]
"use strict";
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];
}));
exports.__esModule = true;
var jquery_1 = require("jquery");
exports.x = jquery_1.x;
__createBinding(exports, jquery_1, "x");
//// [reExportAll.js]
"use strict";
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];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (!exports.hasOwnProperty(p)) __createBinding(exports, m, p);
}
exports.__esModule = true;
__export(require("jquery"));
__exportStar(require("jquery"), exports);
//// [reExportUser.js]
"use strict";
exports.__esModule = true;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2972,6 +2972,7 @@ declare namespace ts {
readonly scoped: boolean;
readonly text: string | ((node: EmitHelperUniqueNameCallback) => string);
readonly priority?: number;
readonly dependencies?: EmitHelper[];
}
export interface UnscopedEmitHelper extends EmitHelper {
readonly scoped: false;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2972,6 +2972,7 @@ declare namespace ts {
readonly scoped: boolean;
readonly text: string | ((node: EmitHelperUniqueNameCallback) => string);
readonly priority?: number;
readonly dependencies?: EmitHelper[];
}
export interface UnscopedEmitHelper extends EmitHelper {
readonly scoped: false;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/commentsOnRequireStatement.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
// blah
// blah
var _0_1 = require("./0");
exports.subject = _0_1.subject;
Object.defineProperty(exports, "subject", { enumerable: true, get: function () { return _0_1.subject; } });
rbuckton marked this conversation as resolved.
Show resolved Hide resolved
/* blah1 */
var _1_1 = require("./1");
exports.subject1 = _1_1.subject1;
Object.defineProperty(exports, "subject1", { enumerable: true, get: function () { return _1_1.subject1; } });
9 changes: 8 additions & 1 deletion tests/baselines/reference/constEnumPreserveEmitReexport.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ var ConstEnum_1 = require("./ConstEnum");
exports["default"] = ConstEnum_1.MyConstEnum;
//// [ReExport.js]
"use strict";
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];
}));
exports.__esModule = true;
var ConstEnum_1 = require("./ConstEnum");
exports["default"] = ConstEnum_1.MyConstEnum;
__createBinding(exports, ConstEnum_1, "MyConstEnum", "default");
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ exports.bar = bar;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = require("./utils");
exports.bar = utils_1.bar;
Object.defineProperty(exports, "bar", { enumerable: true, get: function () { return utils_1.bar; } });
utils_1.foo();
var obj;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,18 @@ var pkg2_1 = require("@raymondfeng/pkg2");
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];
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];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (!exports.hasOwnProperty(p)) __createBinding(exports, m, p);
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./keys"));
__exportStar(require("./keys"), exports);


//// [keys.d.ts]
Expand Down
Loading