diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f225b4cacf8..610551cdbb8 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2613,11 +2613,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true); } + function isNameOfExportedDeclarationInNonES6Module(node: Node): boolean { + if (modulekind === ModuleKind.System || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) { + return false; + } + + return !exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, (node).text); + } + function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { - const exportChanged = (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) && + const isPlusPlusOrMinusMinus = (node.operator === SyntaxKind.PlusPlusToken + || node.operator === SyntaxKind.MinusMinusToken); + const externalExportChanged = isPlusPlusOrMinusMinus && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); - if (exportChanged) { + if (externalExportChanged) { // emit // ++x // as @@ -2626,6 +2636,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge emitNodeWithoutSourceMap(node.operand); write(`", `); } + const internalExportChanged = isPlusPlusOrMinusMinus && + isNameOfExportedDeclarationInNonES6Module(node.operand); + + if (internalExportChanged) { + emitAliasEqual( node.operand); + } write(tokenToString(node.operator)); // In some cases, we need to emit a space between the operator and the operand. One obvious case @@ -2651,14 +2667,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } emit(node.operand); - if (exportChanged) { + if (externalExportChanged) { write(")"); } } function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { - const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); - if (exportChanged) { + const externalExportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); + const internalExportChanged = isNameOfExportedDeclarationInNonES6Module(node.operand); + + if (externalExportChanged) { // export function returns the value that was passes as the second argument // however for postfix unary expressions result value should be the value before modification. // emit 'x++' as '(export('x', ++x) - 1)' and 'x--' as '(export('x', --x) + 1)' @@ -2676,6 +2694,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(") + 1)"); } } + else if (internalExportChanged) { + emitAliasEqual( node.operand); + emit(node.operand); + if (node.operator === SyntaxKind.PlusPlusToken) { + write(" += 1"); + } + else { + write(" -= 1"); + } + } else { emit(node.operand); write(tokenToString(node.operator)); @@ -2777,24 +2805,50 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } } + function emitAliasEqual(name: Identifier): boolean { + for (const specifier of exportSpecifiers[name.text]) { + emitStart(specifier.name); + emitContainingModuleName(specifier); + if (languageVersion === ScriptTarget.ES3 && name.text === "default") { + write('["default"]'); + } + else { + write("."); + emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + } + emitEnd(specifier.name); + write(" = "); + } + return true; + } + function emitBinaryExpression(node: BinaryExpression) { if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement); } else { - const exportChanged = - node.operatorToken.kind >= SyntaxKind.FirstAssignment && - node.operatorToken.kind <= SyntaxKind.LastAssignment && + const isAssignment = isAssignmentOperator(node.operatorToken.kind); + + const externalExportChanged = isAssignment && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.left); - if (exportChanged) { + if (externalExportChanged) { // emit assignment 'x y' as 'exports("x", x y)' write(`${exportFunctionForFile}("`); emitNodeWithoutSourceMap(node.left); write(`", `); } + const internalExportChanged = isAssignment && + isNameOfExportedDeclarationInNonES6Module(node.left); + + if (internalExportChanged) { + // export { foo } + // emit foo = 2 as exports.foo = foo = 2 + emitAliasEqual(node.left); + } + if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { // Downleveled emit exponentiation operator using Math.pow emitExponentiationOperator(node); @@ -2815,7 +2869,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator); } - if (exportChanged) { + if (externalExportChanged) { write(")"); } } diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js new file mode 100644 index 00000000000..94bc5d9281f --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -0,0 +1,36 @@ +//// [server.ts] + +var foo = 2; +foo = 3; + +var baz = 3; +baz = 4; + +var buzz = 10; +buzz += 3; + +var bizz = 8; +bizz++; // compiles to exports.bizz = bizz += 1 +bizz--; // similarly +++bizz; // compiles to exports.bizz = ++bizz + +export { foo, baz, baz as quux, buzz, bizz }; + + +//// [server.js] +"use strict"; +var foo = 2; +exports.foo = foo; +exports.foo = foo = 3; +var baz = 3; +exports.baz = baz; +exports.quux = baz; +exports.baz = exports.quux = baz = 4; +var buzz = 10; +exports.buzz = buzz; +exports.buzz = buzz += 3; +var bizz = 8; +exports.bizz = bizz; +exports.bizz = bizz += 1; // compiles to exports.bizz = bizz += 1 +exports.bizz = bizz -= 1; // similarly +exports.bizz = ++bizz; // compiles to exports.bizz = ++bizz diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols new file mode 100644 index 00000000000..6f8dbb01ec5 --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols @@ -0,0 +1,40 @@ +=== tests/cases/compiler/server.ts === + +var foo = 2; +>foo : Symbol(foo, Decl(server.ts, 1, 3)) + +foo = 3; +>foo : Symbol(foo, Decl(server.ts, 1, 3)) + +var baz = 3; +>baz : Symbol(baz, Decl(server.ts, 4, 3)) + +baz = 4; +>baz : Symbol(baz, Decl(server.ts, 4, 3)) + +var buzz = 10; +>buzz : Symbol(buzz, Decl(server.ts, 7, 3)) + +buzz += 3; +>buzz : Symbol(buzz, Decl(server.ts, 7, 3)) + +var bizz = 8; +>bizz : Symbol(bizz, Decl(server.ts, 10, 3)) + +bizz++; // compiles to exports.bizz = bizz += 1 +>bizz : Symbol(bizz, Decl(server.ts, 10, 3)) + +bizz--; // similarly +>bizz : Symbol(bizz, Decl(server.ts, 10, 3)) + +++bizz; // compiles to exports.bizz = ++bizz +>bizz : Symbol(bizz, Decl(server.ts, 10, 3)) + +export { foo, baz, baz as quux, buzz, bizz }; +>foo : Symbol(foo, Decl(server.ts, 15, 8)) +>baz : Symbol(baz, Decl(server.ts, 15, 13)) +>baz : Symbol(quux, Decl(server.ts, 15, 18)) +>quux : Symbol(quux, Decl(server.ts, 15, 18)) +>buzz : Symbol(buzz, Decl(server.ts, 15, 31)) +>bizz : Symbol(bizz, Decl(server.ts, 15, 37)) + diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types new file mode 100644 index 00000000000..3b6752d89db --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types @@ -0,0 +1,53 @@ +=== tests/cases/compiler/server.ts === + +var foo = 2; +>foo : number +>2 : number + +foo = 3; +>foo = 3 : number +>foo : number +>3 : number + +var baz = 3; +>baz : number +>3 : number + +baz = 4; +>baz = 4 : number +>baz : number +>4 : number + +var buzz = 10; +>buzz : number +>10 : number + +buzz += 3; +>buzz += 3 : number +>buzz : number +>3 : number + +var bizz = 8; +>bizz : number +>8 : number + +bizz++; // compiles to exports.bizz = bizz += 1 +>bizz++ : number +>bizz : number + +bizz--; // similarly +>bizz-- : number +>bizz : number + +++bizz; // compiles to exports.bizz = ++bizz +>++bizz : number +>bizz : number + +export { foo, baz, baz as quux, buzz, bizz }; +>foo : number +>baz : number +>baz : number +>quux : number +>buzz : number +>bizz : number + diff --git a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts new file mode 100644 index 00000000000..2e86ba7aeea --- /dev/null +++ b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts @@ -0,0 +1,19 @@ +// @target: es5 +// @module: commonjs + +// @filename: server.ts +var foo = 2; +foo = 3; + +var baz = 3; +baz = 4; + +var buzz = 10; +buzz += 3; + +var bizz = 8; +bizz++; // compiles to exports.bizz = bizz += 1 +bizz--; // similarly +++bizz; // compiles to exports.bizz = ++bizz + +export { foo, baz, baz as quux, buzz, bizz };