From 81ee9687e76c3624ac683d3150f18a5258964683 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Sun, 22 May 2016 21:51:21 +0700 Subject: [PATCH 01/13] Add failing test for #8738 --- .../es6ExportClauseWithAssignmentInEs5.js | 20 ++++++++++++++ ...es6ExportClauseWithAssignmentInEs5.symbols | 20 ++++++++++++++ .../es6ExportClauseWithAssignmentInEs5.types | 26 +++++++++++++++++++ .../es6ExportClauseWithAssignmentInEs5.ts | 11 ++++++++ 4 files changed, 77 insertions(+) create mode 100644 tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js create mode 100644 tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols create mode 100644 tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types create mode 100644 tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js new file mode 100644 index 00000000000..4eeabebd0a9 --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -0,0 +1,20 @@ +//// [server.ts] + +var foo = 2; +foo = 3; + +var baz = 3; +baz = 4; + +export { foo, baz, baz as quux }; + + +//// [server.js] +"use strict"; +var foo = 2; +exports.foo = foo; +exports.foo = foo = 3; +var baz = 3; +exports.baz = baz; +exports.quux = baz; +exports.quux = exports.baz = baz = 4; diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols new file mode 100644 index 00000000000..64a41c93cc6 --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols @@ -0,0 +1,20 @@ +=== 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)) + +export { foo, baz, baz as quux }; +>foo : Symbol(foo, Decl(server.ts, 7, 8)) +>baz : Symbol(baz, Decl(server.ts, 7, 13)) +>baz : Symbol(quux, Decl(server.ts, 7, 18)) +>quux : Symbol(quux, Decl(server.ts, 7, 18)) + diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types new file mode 100644 index 00000000000..28ce1a1562e --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types @@ -0,0 +1,26 @@ +=== 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 + +export { foo, baz, baz as quux }; +>foo : number +>baz : number +>baz : number +>quux : number + diff --git a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts new file mode 100644 index 00000000000..b79e34e3de1 --- /dev/null +++ b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts @@ -0,0 +1,11 @@ +// @target: es5 +// @module: commonjs + +// @filename: server.ts +var foo = 2; +foo = 3; + +var baz = 3; +baz = 4; + +export { foo, baz, baz as quux }; From 63291d1de0a22de6160cb650d6d25d74304deab1 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Sun, 22 May 2016 22:23:41 +0700 Subject: [PATCH 02/13] Sort baseline reference identifier by name --- tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js index 4eeabebd0a9..636b4ff0909 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -17,4 +17,4 @@ exports.foo = foo = 3; var baz = 3; exports.baz = baz; exports.quux = baz; -exports.quux = exports.baz = baz = 4; +exports.baz = exports.quux = baz = 4; From 9a4b6ab6262c2fd3c0c9d0765861bb7c8a828dde Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Sun, 22 May 2016 22:24:31 +0700 Subject: [PATCH 03/13] Detects assignment to internal module export clause, fixes #8738 --- src/compiler/emitter.ts | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f27557a9136..2ff6ca97bfc 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2613,6 +2613,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true); } + function isNameOfExportedSourceLevelDeclarationInClauseModule(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) && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); @@ -2783,18 +2791,34 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement); } else { - const exportChanged = + const externalExportChanged = node.operatorToken.kind >= SyntaxKind.FirstAssignment && node.operatorToken.kind <= SyntaxKind.LastAssignment && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.left); - if (exportChanged) { + if (externalExportChanged) { // emit assignment 'x y' as 'exports("x", x y)' write(`${exportFunctionForFile}("`); emitNodeWithoutSourceMap(node.left); write(`", `); } + const internalExportClauseMemberChanged = + node.operatorToken.kind >= SyntaxKind.FirstAssignment && + node.operatorToken.kind <= SyntaxKind.LastAssignment && + isNameOfExportedSourceLevelDeclarationInClauseModule(node.left); + + if (internalExportClauseMemberChanged) { + for (const specifier of exportSpecifiers[(node.left).text]) { + emitStart(specifier.name); + emitContainingModuleName(specifier); + write("."); + emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + emitEnd(specifier.name); + write(" = "); + } + } + if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { // Downleveled emit exponentiation operator using Math.pow emitExponentiationOperator(node); @@ -2815,7 +2839,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator); } - if (exportChanged) { + if (externalExportChanged) { write(")"); } } From 269ebda7fc11d07c8305628e31d69dbe5c0aa2e0 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 04:21:43 +0700 Subject: [PATCH 04/13] Factor out assignment op check --- src/compiler/emitter.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 2ff6ca97bfc..06ed0b18d34 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2791,9 +2791,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement); } else { - const externalExportChanged = - node.operatorToken.kind >= SyntaxKind.FirstAssignment && - node.operatorToken.kind <= SyntaxKind.LastAssignment && + const isAssignment = isAssignmentOperator(node.operatorToken.kind); + + const externalExportChanged = isAssignment && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.left); if (externalExportChanged) { @@ -2803,9 +2803,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(`", `); } - const internalExportClauseMemberChanged = - node.operatorToken.kind >= SyntaxKind.FirstAssignment && - node.operatorToken.kind <= SyntaxKind.LastAssignment && + const internalExportClauseMemberChanged = isAssignment && isNameOfExportedSourceLevelDeclarationInClauseModule(node.left); if (internalExportClauseMemberChanged) { From ec60ac94ab5ebb619a6fc59ba96ad933459ea0b5 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 04:24:38 +0700 Subject: [PATCH 05/13] Add test for composite assignment --- .../es6ExportClauseWithAssignmentInEs5.js | 8 +++++++- .../es6ExportClauseWithAssignmentInEs5.symbols | 17 ++++++++++++----- .../es6ExportClauseWithAssignmentInEs5.types | 12 +++++++++++- .../es6ExportClauseWithAssignmentInEs5.ts | 5 ++++- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js index 636b4ff0909..9387f508999 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -6,7 +6,10 @@ foo = 3; var baz = 3; baz = 4; -export { foo, baz, baz as quux }; +var buzz = 10; +buzz += 3; + +export { foo, baz, baz as quux, buzz }; //// [server.js] @@ -18,3 +21,6 @@ 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; diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols index 64a41c93cc6..489636a5d7b 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols @@ -12,9 +12,16 @@ var baz = 3; baz = 4; >baz : Symbol(baz, Decl(server.ts, 4, 3)) -export { foo, baz, baz as quux }; ->foo : Symbol(foo, Decl(server.ts, 7, 8)) ->baz : Symbol(baz, Decl(server.ts, 7, 13)) ->baz : Symbol(quux, Decl(server.ts, 7, 18)) ->quux : Symbol(quux, Decl(server.ts, 7, 18)) +var buzz = 10; +>buzz : Symbol(buzz, Decl(server.ts, 7, 3)) + +buzz += 3; +>buzz : Symbol(buzz, Decl(server.ts, 7, 3)) + +export { foo, baz, baz as quux, buzz }; +>foo : Symbol(foo, Decl(server.ts, 10, 8)) +>baz : Symbol(baz, Decl(server.ts, 10, 13)) +>baz : Symbol(quux, Decl(server.ts, 10, 18)) +>quux : Symbol(quux, Decl(server.ts, 10, 18)) +>buzz : Symbol(buzz, Decl(server.ts, 10, 31)) diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types index 28ce1a1562e..98e8b5528c0 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types @@ -18,9 +18,19 @@ baz = 4; >baz : number >4 : number -export { foo, baz, baz as quux }; +var buzz = 10; +>buzz : number +>10 : number + +buzz += 3; +>buzz += 3 : number +>buzz : number +>3 : number + +export { foo, baz, baz as quux, buzz }; >foo : number >baz : number >baz : number >quux : number +>buzz : number diff --git a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts index b79e34e3de1..e61660f886e 100644 --- a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts +++ b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts @@ -8,4 +8,7 @@ foo = 3; var baz = 3; baz = 4; -export { foo, baz, baz as quux }; +var buzz = 10; +buzz += 3; + +export { foo, baz, baz as quux, buzz }; From ebd4ce6e7adf5ab0265d32eb8d37c7c25ed5d388 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 04:51:46 +0700 Subject: [PATCH 06/13] Factor out the behaviour and handles x++ and ++x --- src/compiler/emitter.ts | 57 ++++++++++++++----- .../es6ExportClauseWithAssignmentInEs5.js | 12 +++- ...es6ExportClauseWithAssignmentInEs5.symbols | 25 ++++++-- .../es6ExportClauseWithAssignmentInEs5.types | 19 ++++++- .../es6ExportClauseWithAssignmentInEs5.ts | 7 ++- 5 files changed, 96 insertions(+), 24 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 06ed0b18d34..010fc2f2266 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2622,10 +2622,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } 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 @@ -2634,6 +2636,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge emitNodeWithoutSourceMap(node.operand); write(`", `); } + const internalExportChanged = isPlusPlusOrMinusMinus && + isNameOfExportedSourceLevelDeclarationInClauseModule(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 @@ -2659,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 = isNameOfExportedSourceLevelDeclarationInClauseModule(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)' @@ -2684,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)); @@ -2785,6 +2805,18 @@ 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); + 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)) { @@ -2803,18 +2835,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(`", `); } - const internalExportClauseMemberChanged = isAssignment && + const internalExportChanged = isAssignment && isNameOfExportedSourceLevelDeclarationInClauseModule(node.left); - if (internalExportClauseMemberChanged) { - for (const specifier of exportSpecifiers[(node.left).text]) { - emitStart(specifier.name); - emitContainingModuleName(specifier); - write("."); - emitNodeWithCommentsAndWithoutSourcemap(specifier.name); - emitEnd(specifier.name); - write(" = "); - } + 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) { diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js index 9387f508999..94bc5d9281f 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -9,7 +9,12 @@ baz = 4; var buzz = 10; buzz += 3; -export { foo, baz, baz as quux, buzz }; +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] @@ -24,3 +29,8 @@ 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 index 489636a5d7b..6f8dbb01ec5 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols @@ -18,10 +18,23 @@ var buzz = 10; buzz += 3; >buzz : Symbol(buzz, Decl(server.ts, 7, 3)) -export { foo, baz, baz as quux, buzz }; ->foo : Symbol(foo, Decl(server.ts, 10, 8)) ->baz : Symbol(baz, Decl(server.ts, 10, 13)) ->baz : Symbol(quux, Decl(server.ts, 10, 18)) ->quux : Symbol(quux, Decl(server.ts, 10, 18)) ->buzz : Symbol(buzz, Decl(server.ts, 10, 31)) +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 index 98e8b5528c0..3b6752d89db 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types @@ -27,10 +27,27 @@ buzz += 3; >buzz : number >3 : number -export { foo, baz, baz as quux, buzz }; +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 index e61660f886e..2e86ba7aeea 100644 --- a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts +++ b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts @@ -11,4 +11,9 @@ baz = 4; var buzz = 10; buzz += 3; -export { foo, baz, baz as quux, buzz }; +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 }; From f1ac06f30d3a6f44f521a8fcec8e1d9241bcaab7 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 05:42:11 +0700 Subject: [PATCH 07/13] Handles ES3 default as identifier name --- src/compiler/emitter.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 010fc2f2266..ddf10c912ec 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2809,6 +2809,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge for (const specifier of exportSpecifiers[name.text]) { emitStart(specifier.name); emitContainingModuleName(specifier); + if (languageVersion === ScriptTarget.ES3 && name.text === "default") { + write('["default"]'); + } write("."); emitNodeWithCommentsAndWithoutSourcemap(specifier.name); emitEnd(specifier.name); From eca94375e38bf00bf6e3b3973db5676a84e116df Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 05:52:15 +0700 Subject: [PATCH 08/13] Fix missing else statement --- src/compiler/emitter.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ddf10c912ec..e6638102c40 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2812,8 +2812,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (languageVersion === ScriptTarget.ES3 && name.text === "default") { write('["default"]'); } - write("."); - emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + else { + write("."); + emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + } emitEnd(specifier.name); write(" = "); } From 47eac4f4af32c8b0d2ba74e836df0505915d3b81 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Wed, 25 May 2016 01:42:17 +0700 Subject: [PATCH 09/13] isNameOfExportedDeclarationInNonES6Module --- src/compiler/emitter.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e6638102c40..f7eda2d0e7e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2613,7 +2613,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true); } - function isNameOfExportedSourceLevelDeclarationInClauseModule(node: Node): boolean { + function isNameOfExportedDeclarationInNonES6Module(node: Node): boolean { if (modulekind === ModuleKind.System || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) { return false; } @@ -2637,7 +2637,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(`", `); } const internalExportChanged = isPlusPlusOrMinusMinus && - isNameOfExportedSourceLevelDeclarationInClauseModule(node.operand); + isNameOfExportedDeclarationInNonES6Module(node.operand); if (internalExportChanged) { emitAliasEqual( node.operand); @@ -2674,7 +2674,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { const externalExportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); - const internalExportChanged = isNameOfExportedSourceLevelDeclarationInClauseModule(node.operand); + const internalExportChanged = isNameOfExportedDeclarationInNonES6Module(node.operand); if (externalExportChanged) { // export function returns the value that was passes as the second argument @@ -2841,7 +2841,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } const internalExportChanged = isAssignment && - isNameOfExportedSourceLevelDeclarationInClauseModule(node.left); + isNameOfExportedDeclarationInNonES6Module(node.left); if (internalExportChanged) { // export { foo } @@ -5464,7 +5464,7 @@ const _super = (function (geti, seti) { // // NOTE: we reuse the same rewriting logic for cases when targeting ES6 and module kind is System. - // Because of hoisting top level class declaration need to be emitted as class expressions. + // Because of hoisting top level class declaration need to be emitted as class expressions. // Double bind case is only required if node is decorated. if (isDecorated && resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) { decoratedClassAlias = unescapeIdentifier(makeUniqueName(node.name ? node.name.text : "default")); From 3052913b92e145b985e9c9a83b01e74df6dd9547 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 7 Jun 2016 02:14:56 -0700 Subject: [PATCH 10/13] add tests for tsserver project system --- src/compiler/sys.ts | 2 +- .../cases/unittests/tsserverProjectSystem.ts | 197 ++++++++++++++++-- 2 files changed, 179 insertions(+), 20 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 0c500d5500e..bcf71b4016a 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -2,7 +2,7 @@ namespace ts { export type FileWatcherCallback = (fileName: string, removed?: boolean) => void; - export type DirectoryWatcherCallback = (directoryName: string) => void; + export type DirectoryWatcherCallback = (fileName: string) => void; export interface WatchedFile { fileName: string; callback: FileWatcherCallback; diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts index c69821ced5f..e5b140237e5 100644 --- a/tests/cases/unittests/tsserverProjectSystem.ts +++ b/tests/cases/unittests/tsserverProjectSystem.ts @@ -107,6 +107,30 @@ namespace ts { } } + function checkConfiguredProjectNumber(projectService: server.ProjectService, expected: number) { + assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`); + } + + function checkInferredProjectNumber(projectService: server.ProjectService, expected: number) { + assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`); + } + + function checkWatchedFiles(host: TestServerHost, expectedFiles: string[]) { + checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles); + } + + function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[]) { + checkMapKeys("watchedDirectories", host.watchedDirectories, expectedDirectories); + } + + function checkConfiguredProjectActualFiles(project: server.Project, expectedFiles: string[]) { + checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), expectedFiles); + } + + function checkConfiguredProjectRootFiles(project: server.Project, expectedFiles: string[]) { + checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), expectedFiles); + } + class TestServerHost implements server.ServerHost { args: string[] = []; newLine: "\n"; @@ -188,6 +212,26 @@ namespace ts { }; } + triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void { + const path = this.toPath(directoryName); + const callbacks = lookUp(this.watchedDirectories, path); + if (callbacks) { + for (const callback of callbacks) { + callback.cb(fileName); + } + } + } + + triggerFileWatcherCallback(fileName: string): void { + const path = this.toPath(fileName); + const callbacks = lookUp(this.watchedFiles, path); + if (callbacks) { + for (const callback of callbacks) { + callback(path, /*removed*/ true); + } + } + } + watchFile(fileName: string, callback: FileWatcherCallback) { const path = this.toPath(fileName); const callbacks = lookUp(this.watchedFiles, path) || (this.watchedFiles[path] = []); @@ -204,7 +248,7 @@ namespace ts { } // TOOD: record and invoke callbacks to simulate timer events - readonly setTimeout = (callback: (...args: any[]) => void, ms: number, ...args: any[]): any => void 0; + readonly setTimeout = setTimeout; readonly clearTimeout = (timeoutId: any): void => void 0; readonly readFile = (s: string) => (this.fs.get(this.toPath(s))).content; readonly resolvePath = (s: string) => s; @@ -216,7 +260,20 @@ namespace ts { readonly exit = () => notImplemented(); } - describe("tsserver project system:", () => { + describe("tsserver-project-system", () => { + const commonFile1: FileOrFolder = { + path: "/a/b/commonFile1.ts", + content: "let x = 1" + }; + const commonFile2: FileOrFolder = { + path: "/a/b/commonFile2.ts", + content: "let y = 1" + }; + const libFile: FileOrFolder = { + path: "/a/lib/lib.d.ts", + content: libFileContent + }; + it("create inferred project", () => { const appFile: FileOrFolder = { path: "/a/b/c/app.ts", @@ -225,10 +282,7 @@ namespace ts { console.log(f) ` }; - const libFile: FileOrFolder = { - path: "/a/lib/lib.d.ts", - content: libFileContent - }; + const moduleFile: FileOrFolder = { path: "/a/b/c/module.d.ts", content: `export let x: number` @@ -238,13 +292,13 @@ namespace ts { const { configFileName } = projectService.openClientFile(appFile.path); assert(!configFileName, `should not find config, got: '${configFileName}`); - assert.equal(projectService.inferredProjects.length, 1, "expected one inferred project"); - assert.equal(projectService.configuredProjects.length, 0, "expected no configured project"); + checkConfiguredProjectNumber(projectService, 0); + checkInferredProjectNumber(projectService, 1); const project = projectService.inferredProjects[0]; checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]); - checkMapKeys("watchedDirectories", host.watchedDirectories, ["/a/b/c", "/a/b", "/a"]); + checkWatchedDirectories(host, ["/a/b/c", "/a/b", "/a"]); }); it("create configured project without file list", () => { @@ -258,10 +312,6 @@ namespace ts { ] }` }; - const libFile: FileOrFolder = { - path: "/a/lib/lib.d.ts", - content: libFileContent - }; const file1: FileOrFolder = { path: "/a/b/c/f1.ts", content: "let x = 1" @@ -274,21 +324,130 @@ namespace ts { path: "/a/b/e/f3.ts", content: "let z = 1" }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [ configFile, libFile, file1, file2, file3 ]); const projectService = new server.ProjectService(host, nullLogger); const { configFileName, configFileErrors } = projectService.openClientFile(file1.path); assert(configFileName, "should find config file"); assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); - assert.equal(projectService.inferredProjects.length, 0, "expected no inferred project"); - assert.equal(projectService.configuredProjects.length, 1, "expected one configured project"); + checkInferredProjectNumber(projectService, 0); + checkConfiguredProjectNumber(projectService, 1); const project = projectService.configuredProjects[0]; - checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), [file1.path, libFile.path, file2.path]); - checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), [file1.path, file2.path]); + checkConfiguredProjectActualFiles(project, [file1.path, libFile.path, file2.path]); + checkConfiguredProjectRootFiles(project, [file1.path, file2.path]); + // watching all files except one that was open + checkWatchedFiles(host, [configFile.path, file2.path, libFile.path]); + checkWatchedDirectories(host, [getDirectoryPath(configFile.path)]); + }); - checkMapKeys("watchedFiles", host.watchedFiles, [configFile.path, file2.path, libFile.path]); // watching all files except one that was open - checkMapKeys("watchedDirectories", host.watchedDirectories, [getDirectoryPath(configFile.path)]); + it("add and then remove a config file in a folder with loose files", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "files": ["commonFile1.ts"] + }` + }; + const filesWithoutConfig = [ libFile, commonFile1, commonFile2 ]; + const filesWithConfig = [ libFile, commonFile1, commonFile2, configFile ]; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", filesWithoutConfig); + const projectService = new server.ProjectService(host, nullLogger); + projectService.openClientFile(commonFile1.path); + projectService.openClientFile(commonFile2.path); + + checkInferredProjectNumber(projectService, 2); + checkWatchedDirectories(host, ["/a/b", "/a"]); + + // Add a tsconfig file + host.reloadFS(filesWithConfig); + host.triggerDirectoryWatcherCallback("/a/b", configFile.path); + + checkInferredProjectNumber(projectService, 1); + checkConfiguredProjectNumber(projectService, 1); + // watching all files except one that was open + checkWatchedFiles(host, [libFile.path, configFile.path]); + + // remove the tsconfig file + host.reloadFS(filesWithoutConfig); + host.triggerFileWatcherCallback(configFile.path); + checkInferredProjectNumber(projectService, 2); + checkConfiguredProjectNumber(projectService, 0); + checkWatchedDirectories(host, ["/a/b", "/a"]); + }); + + it("add new files to a configured project without file list", (done: () => void) => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{}` + }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, libFile, configFile]); + const projectService = new server.ProjectService(host, nullLogger); + projectService.openClientFile(commonFile1.path); + checkWatchedDirectories(host, ["/a/b"]); + checkConfiguredProjectNumber(projectService, 1); + + const project = projectService.configuredProjects[0]; + checkConfiguredProjectRootFiles(project, [commonFile1.path]); + + // add a new ts file + host.reloadFS([commonFile1, commonFile2, libFile, configFile]); + host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path); + // project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer. + setTimeout(() => { + checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); + done(); + }, 1000); + }); + + it("should ignore non-existing files specified in the config file", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {}, + "files": [ + "commonFile1.ts", + "commonFile3.ts" + ] + }` + }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, configFile]); + const projectService = new server.ProjectService(host, nullLogger); + projectService.openClientFile(commonFile1.path); + projectService.openClientFile(commonFile2.path); + + checkConfiguredProjectNumber(projectService, 1); + const project = projectService.configuredProjects[0]; + checkConfiguredProjectRootFiles(project, [commonFile1.path]); + checkInferredProjectNumber(projectService, 1); + }); + + it("handle recreated files correctly", (done: () => void) => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{}` + }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, configFile]); + const projectService = new server.ProjectService(host, nullLogger); + projectService.openClientFile(commonFile1.path); + + checkConfiguredProjectNumber(projectService, 1); + const project = projectService.configuredProjects[0]; + checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); + + // delete commonFile1 + projectService.closeClientFile(commonFile1.path); + host.reloadFS([configFile]); + host.triggerDirectoryWatcherCallback("/a/b", commonFile1.path); + host.setTimeout(() => { + // re-add commonFile1 + host.reloadFS([commonFile1, configFile]); + projectService.openClientFile(commonFile1.path); + host.setTimeout(() => { + checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); + done(); + }, 500); + }, 500); }); }); } \ No newline at end of file From d941177547741af17ae692b8065f5a03ab3382c5 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 7 Jun 2016 15:08:46 -0700 Subject: [PATCH 11/13] check if moduleResolution when verifying that program can be reused --- src/compiler/program.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bab554927e7..5b64d066da1 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1143,6 +1143,7 @@ namespace ts { // if any of these properties has changed - structure cannot be reused const oldOptions = oldProgram.getCompilerOptions(); if ((oldOptions.module !== options.module) || + (oldOptions.moduleResolution !== options.moduleResolution) || (oldOptions.noResolve !== options.noResolve) || (oldOptions.target !== options.target) || (oldOptions.noLib !== options.noLib) || From 37949a3d65fefacff6527c3551e60819176d1e10 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Tue, 7 Jun 2016 15:52:34 -0700 Subject: [PATCH 12/13] more tests for module resolution change and exclude --- src/compiler/program.ts | 1 + src/server/editorServices.ts | 2 +- src/services/services.ts | 1 + .../cases/unittests/tsserverProjectSystem.ts | 194 ++++++++++++++---- 4 files changed, 159 insertions(+), 39 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bab554927e7..5b64d066da1 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1143,6 +1143,7 @@ namespace ts { // if any of these properties has changed - structure cannot be reused const oldOptions = oldProgram.getCompilerOptions(); if ((oldOptions.module !== options.module) || + (oldOptions.moduleResolution !== options.moduleResolution) || (oldOptions.noResolve !== options.noResolve) || (oldOptions.target !== options.target) || (oldOptions.noLib !== options.noLib) || diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 8eae43b503d..628522c7aa6 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -268,7 +268,7 @@ namespace ts.server { } removeRoot(info: ScriptInfo) { - if (!this.filenameToScript.contains(info.path)) { + if (this.filenameToScript.contains(info.path)) { this.filenameToScript.remove(info.path); this.roots = copyListRemovingItem(info, this.roots); this.resolvedModuleNames.remove(info.path); diff --git a/src/services/services.ts b/src/services/services.ts index 90740b257c6..f39eb60a819 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2919,6 +2919,7 @@ namespace ts { const changesInCompilationSettingsAffectSyntax = oldSettings && (oldSettings.target !== newSettings.target || oldSettings.module !== newSettings.module || + oldSettings.moduleResolution !== newSettings.moduleResolution || oldSettings.noResolve !== newSettings.noResolve || oldSettings.jsx !== newSettings.jsx || oldSettings.allowJs !== newSettings.allowJs); diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts index e5b140237e5..103e5bc7d9b 100644 --- a/tests/cases/unittests/tsserverProjectSystem.ts +++ b/tests/cases/unittests/tsserverProjectSystem.ts @@ -107,11 +107,11 @@ namespace ts { } } - function checkConfiguredProjectNumber(projectService: server.ProjectService, expected: number) { + function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) { assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`); } - function checkInferredProjectNumber(projectService: server.ProjectService, expected: number) { + function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) { assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`); } @@ -131,6 +131,8 @@ namespace ts { checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), expectedFiles); } + type TimeOutCallback = () => any; + class TestServerHost implements server.ServerHost { args: string[] = []; newLine: "\n"; @@ -138,6 +140,7 @@ namespace ts { private fs: ts.FileMap; private getCanonicalFileName: (s: string) => string; private toPath: (f: string) => Path; + private callbackQueue: TimeOutCallback[] = []; readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {}; readonly watchedFiles: Map = {}; @@ -222,12 +225,12 @@ namespace ts { } } - triggerFileWatcherCallback(fileName: string): void { + triggerFileWatcherCallback(fileName: string, removed?: boolean): void { const path = this.toPath(fileName); const callbacks = lookUp(this.watchedFiles, path); if (callbacks) { for (const callback of callbacks) { - callback(path, /*removed*/ true); + callback(path, removed); } } } @@ -248,8 +251,27 @@ namespace ts { } // TOOD: record and invoke callbacks to simulate timer events - readonly setTimeout = setTimeout; - readonly clearTimeout = (timeoutId: any): void => void 0; + readonly setTimeout = (callback: TimeOutCallback, time: number) => { + this.callbackQueue.push(callback); + return this.callbackQueue.length - 1; + }; + readonly clearTimeout = (timeoutId: any): void => { + if (typeof timeoutId === "number") { + this.callbackQueue.splice(timeoutId, 1); + } + }; + + checkTimeoutQueueLength(expected: number) { + assert.equal(this.callbackQueue.length, expected, `expected ${expected} timeout callbacks queued but found ${this.callbackQueue.length}.`); + } + + runQueuedTimeoutCallbacks() { + for (const callback of this.callbackQueue) { + callback(); + } + this.callbackQueue = []; + } + readonly readFile = (s: string) => (this.fs.get(this.toPath(s))).content; readonly resolvePath = (s: string) => s; readonly getExecutingFilePath = () => this.executingFilePath; @@ -292,8 +314,8 @@ namespace ts { const { configFileName } = projectService.openClientFile(appFile.path); assert(!configFileName, `should not find config, got: '${configFileName}`); - checkConfiguredProjectNumber(projectService, 0); - checkInferredProjectNumber(projectService, 1); + checkNumberOfConfiguredProjects(projectService, 0); + checkNumberOfInferredProjects(projectService, 1); const project = projectService.inferredProjects[0]; @@ -331,8 +353,8 @@ namespace ts { assert(configFileName, "should find config file"); assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); - checkInferredProjectNumber(projectService, 0); - checkConfiguredProjectNumber(projectService, 1); + checkNumberOfInferredProjects(projectService, 0); + checkNumberOfConfiguredProjects(projectService, 1); const project = projectService.configuredProjects[0]; checkConfiguredProjectActualFiles(project, [file1.path, libFile.path, file2.path]); @@ -356,27 +378,28 @@ namespace ts { projectService.openClientFile(commonFile1.path); projectService.openClientFile(commonFile2.path); - checkInferredProjectNumber(projectService, 2); + checkNumberOfInferredProjects(projectService, 2); checkWatchedDirectories(host, ["/a/b", "/a"]); // Add a tsconfig file host.reloadFS(filesWithConfig); host.triggerDirectoryWatcherCallback("/a/b", configFile.path); - checkInferredProjectNumber(projectService, 1); - checkConfiguredProjectNumber(projectService, 1); + checkNumberOfInferredProjects(projectService, 1); + checkNumberOfConfiguredProjects(projectService, 1); // watching all files except one that was open checkWatchedFiles(host, [libFile.path, configFile.path]); // remove the tsconfig file host.reloadFS(filesWithoutConfig); host.triggerFileWatcherCallback(configFile.path); - checkInferredProjectNumber(projectService, 2); - checkConfiguredProjectNumber(projectService, 0); + + checkNumberOfInferredProjects(projectService, 2); + checkNumberOfConfiguredProjects(projectService, 0); checkWatchedDirectories(host, ["/a/b", "/a"]); }); - it("add new files to a configured project without file list", (done: () => void) => { + it("add new files to a configured project without file list", () => { const configFile: FileOrFolder = { path: "/a/b/tsconfig.json", content: `{}` @@ -385,7 +408,7 @@ namespace ts { const projectService = new server.ProjectService(host, nullLogger); projectService.openClientFile(commonFile1.path); checkWatchedDirectories(host, ["/a/b"]); - checkConfiguredProjectNumber(projectService, 1); + checkNumberOfConfiguredProjects(projectService, 1); const project = projectService.configuredProjects[0]; checkConfiguredProjectRootFiles(project, [commonFile1.path]); @@ -393,11 +416,9 @@ namespace ts { // add a new ts file host.reloadFS([commonFile1, commonFile2, libFile, configFile]); host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path); + host.runQueuedTimeoutCallbacks(); // project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer. - setTimeout(() => { - checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); - done(); - }, 1000); + checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); }); it("should ignore non-existing files specified in the config file", () => { @@ -416,13 +437,13 @@ namespace ts { projectService.openClientFile(commonFile1.path); projectService.openClientFile(commonFile2.path); - checkConfiguredProjectNumber(projectService, 1); + checkNumberOfConfiguredProjects(projectService, 1); const project = projectService.configuredProjects[0]; checkConfiguredProjectRootFiles(project, [commonFile1.path]); - checkInferredProjectNumber(projectService, 1); + checkNumberOfInferredProjects(projectService, 1); }); - it("handle recreated files correctly", (done: () => void) => { + it("handle recreated files correctly", () => { const configFile: FileOrFolder = { path: "/a/b/tsconfig.json", content: `{}` @@ -431,23 +452,120 @@ namespace ts { const projectService = new server.ProjectService(host, nullLogger); projectService.openClientFile(commonFile1.path); - checkConfiguredProjectNumber(projectService, 1); + checkNumberOfConfiguredProjects(projectService, 1); const project = projectService.configuredProjects[0]; checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); - // delete commonFile1 - projectService.closeClientFile(commonFile1.path); - host.reloadFS([configFile]); - host.triggerDirectoryWatcherCallback("/a/b", commonFile1.path); - host.setTimeout(() => { - // re-add commonFile1 - host.reloadFS([commonFile1, configFile]); - projectService.openClientFile(commonFile1.path); - host.setTimeout(() => { - checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); - done(); - }, 500); - }, 500); + // delete commonFile2 + host.reloadFS([commonFile1, configFile]); + host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path); + host.runQueuedTimeoutCallbacks(); + checkConfiguredProjectRootFiles(project, [commonFile1.path]); + + // re-add commonFile2 + host.reloadFS([commonFile1, commonFile2, configFile]); + host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path); + host.runQueuedTimeoutCallbacks(); + checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); }); + + it("should create new inferred projects for files excluded from a configured project", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {}, + "files": ["${commonFile1.path}", "${commonFile2.path}"] + }` + }; + const files = [commonFile1, commonFile2, configFile]; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", files); + const projectService = new server.ProjectService(host, nullLogger); + projectService.openClientFile(commonFile1.path); + + const project = projectService.configuredProjects[0]; + checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); + configFile.content = `{ + "compilerOptions": {}, + "files": ["${commonFile1.path}"] + }`; + host.reloadFS(files); + host.triggerFileWatcherCallback(configFile.path); + + checkNumberOfConfiguredProjects(projectService, 1); + checkConfiguredProjectRootFiles(project, [commonFile1.path]); + + projectService.openClientFile(commonFile2.path); + checkNumberOfInferredProjects(projectService, 1); + }); + + it("files explicitly excluded in config file", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {}, + "exclude": ["/a/c"] + }` + }; + const excludedFile1: FileOrFolder = { + path: "/a/c/excluedFile1.ts", + content: `let t = 1;` + }; + + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, excludedFile1, configFile]); + const projectService = new server.ProjectService(host, nullLogger); + + projectService.openClientFile(commonFile1.path); + checkNumberOfConfiguredProjects(projectService, 1); + const project = projectService.configuredProjects[0]; + checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]); + projectService.openClientFile(excludedFile1.path); + checkNumberOfInferredProjects(projectService, 1); + }); + + it("should properly handle module resolution changes in config file", () => { + const file1: FileOrFolder = { + path: "/a/b/file1.ts", + content: `import { T } from "module1";` + } + const nodeModuleFile: FileOrFolder = { + path: "/a/b/node_modules/module1.ts", + content: `export interface T {}` + } + const classicModuleFile: FileOrFolder = { + path: "/a/module1.ts", + content: `export interface T {}` + } + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": { + "moduleResolution": "node" + }, + "files": ["${file1.path}"] + }` + }; + const files = [file1, nodeModuleFile, classicModuleFile, configFile]; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", files); + const projectService = new server.ProjectService(host, nullLogger); + projectService.openClientFile(file1.path); + projectService.openClientFile(nodeModuleFile.path); + projectService.openClientFile(classicModuleFile.path); + + checkNumberOfConfiguredProjects(projectService, 1); + const project = projectService.configuredProjects[0]; + checkConfiguredProjectActualFiles(project, [file1.path, nodeModuleFile.path]); + checkNumberOfInferredProjects(projectService, 1); + + configFile.content = `{ + "compilerOptions": { + "moduleResolution": "classic" + }, + "files": ["${file1.path}"] + }`; + host.reloadFS(files); + host.triggerFileWatcherCallback(configFile.path); + checkConfiguredProjectActualFiles(project, [file1.path, classicModuleFile.path]); + checkNumberOfInferredProjects(projectService, 1); + }) }); } \ No newline at end of file From 78792df4db2641a6c1b840581c3e064e7c5da39f Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Tue, 7 Jun 2016 16:11:42 -0700 Subject: [PATCH 13/13] Fix linting issues --- tests/cases/unittests/tsserverProjectSystem.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts index 103e5bc7d9b..cf382d09463 100644 --- a/tests/cases/unittests/tsserverProjectSystem.ts +++ b/tests/cases/unittests/tsserverProjectSystem.ts @@ -526,15 +526,15 @@ namespace ts { const file1: FileOrFolder = { path: "/a/b/file1.ts", content: `import { T } from "module1";` - } + }; const nodeModuleFile: FileOrFolder = { path: "/a/b/node_modules/module1.ts", content: `export interface T {}` - } + }; const classicModuleFile: FileOrFolder = { path: "/a/module1.ts", content: `export interface T {}` - } + }; const configFile: FileOrFolder = { path: "/a/b/tsconfig.json", content: `{ @@ -566,6 +566,6 @@ namespace ts { host.triggerFileWatcherCallback(configFile.path); checkConfiguredProjectActualFiles(project, [file1.path, classicModuleFile.path]); checkNumberOfInferredProjects(projectService, 1); - }) + }); }); } \ No newline at end of file