From e13a07e3bdeb12aa6a8640950e71e4175bb964e2 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 13 Apr 2016 17:31:53 -0700 Subject: [PATCH] Emit 'exports.foo' assignments for bindings that are exported in specifiers. --- src/compiler/transformers/module/module.ts | 104 ++++++++++++++------- src/compiler/transformers/ts.ts | 21 +++-- 2 files changed, 82 insertions(+), 43 deletions(-) diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 9d7dc0b2a82..1fa1d0473db 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -559,34 +559,64 @@ namespace ts { } function visitVariableStatement(node: VariableStatement): VisitResult { - if (hasModifier(node, ModifierFlags.Export)) { - // If the variable is for a declaration that has a local name, - // do not elide the declaration. - const original = getOriginalNode(node); - if (original.kind === SyntaxKind.EnumDeclaration - || original.kind === SyntaxKind.ModuleDeclaration) { - return setOriginalNode( - createVariableStatement( - /*modifiers*/ undefined, - node.declarationList - ), - node - ); + // If the variable is for a generated declaration, + // we should maintain it and just strip off the 'export' modifier if necesary. + const original = getOriginalNode(node); + if (original.kind === SyntaxKind.EnumDeclaration || original.kind === SyntaxKind.ModuleDeclaration) { + if (!hasModifier(node, ModifierFlags.Export)) { + return node; } - const variables = getInitializedVariables(node.declarationList); - if (variables.length === 0) { - // elide statement if there are no initialized variables - return undefined; - } - - return createStatement( - inlineExpressions( - map(variables, transformInitializedVariable) - ) + return setOriginalNode( + createVariableStatement( + /*modifiers*/ undefined, + node.declarationList + ), + node ); } - return node; + + const resultStatements: Statement[] = []; + + // If we're exporting these variables, then these just become assignments to 'exports.blah'. + // We only want to emit assignments for variables with initializers. + if (hasModifier(node, ModifierFlags.Export)) { + const variables = getInitializedVariables(node.declarationList); + if (variables.length > 0) { + let inlineAssignments = createStatement( + inlineExpressions( + map(variables, transformInitializedVariable) + ) + ); + resultStatements.push(inlineAssignments); + } + } + else { + resultStatements.push(node); + } + + // While we might not have been exported here, each variable might have been exported + // later on in an export specifier (e.g. `export {foo as blah, bar}`). + for (const decl of node.declarationList.declarations) { + addExportMemberAssignmentsForBindingName(resultStatements, decl.name); + } + + return resultStatements; + } + + /** + * Creates appropriate assignments for each binding identifier that is exported in an export specifier, + * and inserts it into 'resultStatements'. + */ + function addExportMemberAssignmentsForBindingName(resultStatements: Statement[], name: BindingName): void { + if (isBindingPattern(name)) { + for (const element of name.elements) { + addExportMemberAssignmentsForBindingName(resultStatements, element.name) + } + } + else { + addExportMemberAssignments(resultStatements, name); + } } function transformInitializedVariable(node: VariableDeclaration): Expression { @@ -665,28 +695,34 @@ namespace ts { function visitExpressionStatement(node: ExpressionStatement): VisitResult { const original = getOriginalNode(node); - if (original.kind === SyntaxKind.EnumDeclaration - && hasModifier(original, ModifierFlags.Export)) { - return visitExpressionStatementForEnumDeclaration(node, original); + const origKind = original.kind; + if (origKind === SyntaxKind.EnumDeclaration || origKind === SyntaxKind.ModuleDeclaration) { + return visitExpressionStatementForEnumOrNamespaceDeclaration(node, original); } return node; } - function visitExpressionStatementForEnumDeclaration(node: ExpressionStatement, original: EnumDeclaration): VisitResult { - if (isFirstDeclarationOfKind(original, SyntaxKind.EnumDeclaration)) { - const statements: Statement[] = [node]; - addVarForExportedEnumDeclaration(statements, original); - return statements; + function visitExpressionStatementForEnumOrNamespaceDeclaration(node: ExpressionStatement, original: EnumDeclaration | ModuleDeclaration): VisitResult { + const statements: Statement[] = [node]; + + // Preserve old behavior for enums in which a variable statement is emitted after the body itself. + if (hasModifier(original, ModifierFlags.Export) && + original.kind === SyntaxKind.EnumDeclaration && + isFirstDeclarationOfKind(original, SyntaxKind.EnumDeclaration)) { + addVarForExportedEnumOrNamespaceDeclaration(statements, original); + } + else { + addExportMemberAssignments(statements, original.name); } - return node; + return statements; } /** * Adds a trailing VariableStatement for an enum or module declaration. */ - function addVarForExportedEnumDeclaration(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) { + function addVarForExportedEnumOrNamespaceDeclaration(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) { statements.push( createVariableStatement( /*modifiers*/ undefined, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index b436c254ff0..115f1d681c9 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2229,14 +2229,17 @@ namespace ts { function addVarForEnumDeclaration(statements: Statement[], node: EnumDeclaration) { // Emit a variable statement for the enum. statements.push( - createVariableStatement( - isES6ExportedDeclaration(node) - ? visitNodes(node.modifiers, visitor, isModifier) - : undefined, - [createVariableDeclaration( - getDeclarationName(node) - )], - /*location*/ node + setOriginalNode( + createVariableStatement( + isES6ExportedDeclaration(node) + ? visitNodes(node.modifiers, visitor, isModifier) + : undefined, + [createVariableDeclaration( + getDeclarationName(node) + )], + /*location*/ node + ), + /*original*/ node ) ); } @@ -2398,7 +2401,7 @@ namespace ts { function isES6ExportedDeclaration(node: Node) { return isExternalModuleExport(node) - && moduleKind >= ModuleKind.ES6; + && moduleKind === ModuleKind.ES6; } function shouldEmitVarForModuleDeclaration(node: ModuleDeclaration) {