diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index faf0acb163..fab7583d90 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2828,6 +2828,10 @@ "category": "Message", "code": 6030 }, + "Starting compilation in watch mode...": { + "category": "Message", + "code": 6031 + }, "File change detected. Starting incremental compilation...": { "category": "Message", "code": 6032 diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index a9e5fab202..e8fae71597 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -302,6 +302,10 @@ namespace ts { // There is no extra check needed since we can just rely on the program to decide emit const builder = createBuilder({ getCanonicalFileName, computeHash }); + if (watchingHost.system.clearScreen) { + watchingHost.system.clearScreen(); + } + reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Starting_compilation_in_watch_mode)); synchronizeProgram(); // Update the wild card directory watch diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 8087d038b7..8c89c7564f 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -80,15 +80,38 @@ namespace ts.tscWatch { checkOutputDoesNotContain(host, expectedNonAffectedFiles); } - function checkOutputErrors(host: WatchedSystem, errors: ReadonlyArray, isInitial?: true, skipWaiting?: true) { + enum ExpectedOutputErrorsPosition { + BeforeCompilationStarts, + AfterCompilationStarting, + AfterFileChangeDetected + } + + function checkOutputErrors( + host: WatchedSystem, + errors: ReadonlyArray, + errorsPosition: ExpectedOutputErrorsPosition, + skipWaiting?: true + ) { const outputs = host.getOutput(); - const expectedOutputCount = (isInitial ? 0 : 1) + errors.length + (skipWaiting ? 0 : 1); + const expectedOutputCount = errors.length + (skipWaiting ? 0 : 1) + 1; assert.equal(outputs.length, expectedOutputCount, "Outputs = " + outputs.toString()); - let index = 0; - if (!isInitial) { - assertWatchDiagnosticAt(host, index, Diagnostics.File_change_detected_Starting_incremental_compilation); - index++; + let index: number; + + switch (errorsPosition) { + case ExpectedOutputErrorsPosition.AfterCompilationStarting: + assertWatchDiagnosticAt(host, 0, Diagnostics.Starting_compilation_in_watch_mode); + index = 1; + break; + case ExpectedOutputErrorsPosition.AfterFileChangeDetected: + assertWatchDiagnosticAt(host, 0, Diagnostics.File_change_detected_Starting_incremental_compilation); + index = 1; + break; + case ExpectedOutputErrorsPosition.BeforeCompilationStarts: + assertWatchDiagnosticAt(host, errors.length, Diagnostics.Starting_compilation_in_watch_mode); + index = 0; + break; } + forEach(errors, error => { assertDiagnosticAt(host, index, error); index++; @@ -333,13 +356,13 @@ namespace ts.tscWatch { checkOutputErrors(host, [ getDiagnosticOfFileFromProgram(watch(), file1.path, file1.content.indexOf(commonFile2Name), commonFile2Name.length, Diagnostics.File_0_not_found, commonFile2.path), getDiagnosticOfFileFromProgram(watch(), file1.path, file1.content.indexOf("y"), 1, Diagnostics.Cannot_find_name_0, "y") - ], /*isInitial*/ true); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); host.reloadFS([file1, commonFile2, libFile]); host.runQueuedTimeoutCallbacks(); checkProgramRootFiles(watch(), [file1.path]); checkProgramActualFiles(watch(), [file1.path, libFile.path, commonFile2.path]); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("should reflect change in config file", () => { @@ -667,7 +690,7 @@ namespace ts.tscWatch { const watch = createWatchModeWithConfigFile(config.path, host); checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]); - checkOutputErrors(host, emptyArray, /*isInitial*/ true); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); host.reloadFS([file1, file2, libFile]); host.checkTimeoutQueueLengthAndRun(1); @@ -675,7 +698,7 @@ namespace ts.tscWatch { assert.equal(host.exitCode, ExitStatus.DiagnosticsPresent_OutputsSkipped); checkOutputErrors(host, [ getDiagnosticWithoutFile(Diagnostics.File_0_not_found, config.path) - ], /*isInitial*/ undefined, /*skipWaiting*/ true); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected, /*skipWaiting*/ true); }); it("Proper errors: document is not contained in project", () => { @@ -778,7 +801,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([moduleFile, file1, libFile]); const watch = createWatchModeWithoutConfigFile([file1.path], host); - checkOutputErrors(host, emptyArray, /*isInitial*/ true); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); const moduleFileOldPath = moduleFile.path; const moduleFileNewPath = "/a/b/moduleFile1.ts"; @@ -787,12 +810,12 @@ namespace ts.tscWatch { host.runQueuedTimeoutCallbacks(); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile") - ]); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); moduleFile.path = moduleFileOldPath; host.reloadFS([moduleFile, file1, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("rename a module file and rename back should restore the states for configured projects", () => { @@ -810,7 +833,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([moduleFile, file1, configFile, libFile]); const watch = createWatchModeWithConfigFile(configFile.path, host); - checkOutputErrors(host, emptyArray, /*isInitial*/ true); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); const moduleFileOldPath = moduleFile.path; const moduleFileNewPath = "/a/b/moduleFile1.ts"; @@ -819,12 +842,12 @@ namespace ts.tscWatch { host.runQueuedTimeoutCallbacks(); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile") - ]); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); moduleFile.path = moduleFileOldPath; host.reloadFS([moduleFile, file1, configFile, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("types should load from config file path if config exists", () => { @@ -863,11 +886,11 @@ namespace ts.tscWatch { checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile") - ], /*isInitial*/ true); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); host.reloadFS([file1, moduleFile, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("Configure file diagnostics events are generated when the config file has errors", () => { @@ -890,7 +913,7 @@ namespace ts.tscWatch { checkOutputErrors(host, [ getUnknownCompilerOption(watch(), configFile, "foo"), getUnknownCompilerOption(watch(), configFile, "allowJS") - ], /*isInitial*/ true); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.BeforeCompilationStarts); }); it("If config file doesnt have errors, they are not reported", () => { @@ -907,7 +930,7 @@ namespace ts.tscWatch { const host = createWatchedSystem([file, configFile, libFile]); createWatchModeWithConfigFile(configFile.path, host); - checkOutputErrors(host, emptyArray, /*isInitial*/ true); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); }); it("Reports errors when the config file changes", () => { @@ -924,7 +947,7 @@ namespace ts.tscWatch { const host = createWatchedSystem([file, configFile, libFile]); const watch = createWatchModeWithConfigFile(configFile.path, host); - checkOutputErrors(host, emptyArray, /*isInitial*/ true); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); configFile.content = `{ "compilerOptions": { @@ -935,14 +958,14 @@ namespace ts.tscWatch { host.runQueuedTimeoutCallbacks(); checkOutputErrors(host, [ getUnknownCompilerOption(watch(), configFile, "haha") - ]); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); configFile.content = `{ "compilerOptions": {} }`; host.reloadFS([file, configFile, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("non-existing directories listed in config file input array should be tolerated without crashing the server", () => { @@ -1030,13 +1053,13 @@ namespace ts.tscWatch { getDiagnosticOfFile(watch().getCompilerOptions().configFile, configFile.content.indexOf('"declaration"'), '"declaration"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration") ]; const intialErrors = errors(); - checkOutputErrors(host, intialErrors, /*isInitial*/ true); + checkOutputErrors(host, intialErrors, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); configFile.content = configFileContentWithoutCommentLine; host.reloadFS(files); host.runQueuedTimeoutCallbacks(); const nowErrors = errors(); - checkOutputErrors(host, nowErrors); + checkOutputErrors(host, nowErrors, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); assert.equal(nowErrors[0].start, intialErrors[0].start - configFileContentComment.length); assert.equal(nowErrors[1].start, intialErrors[1].start - configFileContentComment.length); }); @@ -1667,7 +1690,7 @@ namespace ts.tscWatch { const cannotFindFoo = getDiagnosticOfFileFromProgram(watch(), imported.path, imported.content.indexOf("foo"), "foo".length, Diagnostics.Cannot_find_name_0, "foo"); // ensure that imported file was found - checkOutputErrors(host, [f1IsNotModule, cannotFindFoo], /*isInitial*/ true); + checkOutputErrors(host, [f1IsNotModule, cannotFindFoo], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); const originalFileExists = host.fileExists; { @@ -1687,7 +1710,7 @@ namespace ts.tscWatch { f1IsNotModule, getDiagnosticOfFileFromProgram(watch(), root.path, newContent.indexOf("var x") + "var ".length, "x".length, Diagnostics.Type_0_is_not_assignable_to_type_1, 1, "string"), cannotFindFoo - ]); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); } { let fileExistsIsCalled = false; @@ -1709,7 +1732,7 @@ namespace ts.tscWatch { // ensure file has correct number of errors after edit checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "f2") - ]); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); assert.isTrue(fileExistsIsCalled); } @@ -1730,7 +1753,7 @@ namespace ts.tscWatch { host.reloadFS(files); host.runQueuedTimeoutCallbacks(); - checkOutputErrors(host, [f1IsNotModule, cannotFindFoo]); + checkOutputErrors(host, [f1IsNotModule, cannotFindFoo], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); assert.isTrue(fileExistsCalled); } }); @@ -1767,7 +1790,7 @@ namespace ts.tscWatch { assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "bar") - ], /*isInitial*/ true); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); fileExistsCalledForBar = false; root.content = `import {y} from "bar"`; @@ -1775,7 +1798,7 @@ namespace ts.tscWatch { host.runQueuedTimeoutCallbacks(); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("should compile correctly when resolved module goes missing and then comes back (module is not part of the root)", () => { @@ -1807,7 +1830,7 @@ namespace ts.tscWatch { const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); - checkOutputErrors(host, emptyArray, /*isInitial*/ true); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); fileExistsCalledForBar = false; host.reloadFS(files); @@ -1815,13 +1838,13 @@ namespace ts.tscWatch { assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "bar") - ]); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); fileExistsCalledForBar = false; host.reloadFS(filesWithImported); host.checkTimeoutQueueLengthAndRun(1); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("works when module resolution changes to ambient module", () => { @@ -1857,11 +1880,11 @@ declare module "fs" { checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "fs") - ], /*isInitial*/ true); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); host.reloadFS(filesWithNodeType); host.runQueuedTimeoutCallbacks(); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("works when included file with ambient module changes", () => { @@ -1899,12 +1922,12 @@ declare module "fs" { checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "fs") - ], /*isInitial*/ true); + ], /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); file.content += fileContentWithFS; host.reloadFS(files); host.runQueuedTimeoutCallbacks(); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); }); it("works when reusing program with files from external library", () => { @@ -1939,7 +1962,7 @@ declare module "fs" { const host = createWatchedSystem(programFiles.concat(configFile), { currentDirectory: "/a/b/projects/myProject/" }); const watch = createWatchModeWithConfigFile(configFile.path, host); checkProgramActualFiles(watch(), programFiles.map(f => f.path)); - checkOutputErrors(host, emptyArray, /*isInitial*/ true); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); const expectedFiles: ExpectedFile[] = [ createExpectedEmittedFile(file1), createExpectedEmittedFile(file2), @@ -1958,7 +1981,7 @@ declare module "fs" { host.reloadFS(programFiles.concat(configFile)); host.runQueuedTimeoutCallbacks(); checkProgramActualFiles(watch(), programFiles.map(f => f.path)); - checkOutputErrors(host, emptyArray); + checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterFileChangeDetected); verifyExpectedFiles(expectedFiles); @@ -2029,7 +2052,7 @@ declare module "fs" { }); describe("tsc-watch console clearing", () => { - it("doesn't clear the console when it starts", () => { + it("clears the console when it starts", () => { const file = { path: "f.ts", content: "" @@ -2039,7 +2062,7 @@ declare module "fs" { createWatchModeWithoutConfigFile([file.path], host); host.runQueuedTimeoutCallbacks(); - host.checkScreenClears(0); + host.checkScreenClears(1); }); it("clears the console on recompile", () => { @@ -2057,7 +2080,7 @@ declare module "fs" { host.reloadFS([modifiedFile]); host.runQueuedTimeoutCallbacks(); - host.checkScreenClears(1); + host.checkScreenClears(2); }); }); }