From bf4ca30bc30b273f9bae31a2992a8c2481d8a850 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 4 Oct 2017 17:29:06 -0700 Subject: [PATCH 1/4] Let builder find out from imports/typereference directives if file references have changed. This is needed to ensure that the ambient module addition takes effect Fixes #15632 --- src/compiler/builder.ts | 7 +- src/compiler/program.ts | 3 +- src/compiler/types.ts | 2 - src/harness/unittests/tscWatchMode.ts | 142 ++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 8 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 6bb94ea2de7..03eb5b7e8ae 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -77,8 +77,8 @@ namespace ts { */ onUpdateSourceFile(program: Program, sourceFile: SourceFile): void; /** - * Called when source file has not changed but has some of the resolutions invalidated - * If returned true, builder will mark the file as changed (noting that something associated with file has changed) + * Called when source file has not changed + * If returned true, builder will mark the file as changed (noting that something associated with file has changed eg. module resolution) */ onUpdateSourceFileWithSameVersion(program: Program, sourceFile: SourceFile): boolean; /** @@ -161,8 +161,7 @@ namespace ts { existingInfo.version = sourceFile.version; emitHandler.onUpdateSourceFile(program, sourceFile); } - else if (program.hasInvalidatedResolution(sourceFile.path) && - emitHandler.onUpdateSourceFileWithSameVersion(program, sourceFile)) { + else if (emitHandler.onUpdateSourceFileWithSameVersion(program, sourceFile)) { registerChangedFile(sourceFile.path, sourceFile.fileName); } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 24d7707f50d..757e7827f27 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -663,8 +663,7 @@ namespace ts { dropDiagnosticsProducingTypeChecker, getSourceFileFromReference, sourceFileToPackageName, - redirectTargetsSet, - hasInvalidatedResolution + redirectTargetsSet }; verifyCompilerOptions(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 578c2d23c4f..a6fccabd4dc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2525,8 +2525,6 @@ namespace ts { /* @internal */ sourceFileToPackageName: Map; /** Set of all source files that some other source file redirects to. */ /* @internal */ redirectTargetsSet: Map; - /** Returns true when file in the program had invalidated resolution at the time of program creation. */ - /* @internal */ hasInvalidatedResolution: HasInvalidatedResolution; } /* @internal */ diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 5267bbcf047..31d79df21c1 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -1655,5 +1655,147 @@ namespace ts.tscWatch { assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); checkOutputDoesNotContain(host, [barNotFound]); }); + + it("works when module resolution changes to ambient module", () => { + const root = { + path: "/a/b/foo.ts", + content: `import * as fs from "fs";` + }; + + const packageJson = { + path: "/a/b/node_modules/@types/node/package.json", + content: ` +{ + "main": "" +} +` + }; + + const nodeType = { + path: "/a/b/node_modules/@types/node/index.d.ts", + content: ` +declare module "fs" { + export interface Stats { + isFile(): boolean; + isDirectory(): boolean; + isBlockDevice(): boolean; + isCharacterDevice(): boolean; + isSymbolicLink(): boolean; + isFIFO(): boolean; + isSocket(): boolean; + dev: number; + ino: number; + mode: number; + nlink: number; + uid: number; + gid: number; + rdev: number; + size: number; + blksize: number; + blocks: number; + atimeMs: number; + mtimeMs: number; + ctimeMs: number; + birthtimeMs: number; + atime: Date; + mtime: Date; + ctime: Date; + birthtime: Date; + } +}` + }; + + const files = [root, libFile]; + const filesWithNodeType = files.concat(packageJson, nodeType); + const host = createWatchedSystem(files, { currentDirectory: "/a/b" }); + + createWatchModeWithoutConfigFile([root.path], host, { }); + + const fsNotFound = `foo.ts(1,21): error TS2307: Cannot find module 'fs'.\n`; + checkOutputContains(host, [fsNotFound]); + host.clearOutput(); + + host.reloadFS(filesWithNodeType); + host.runQueuedTimeoutCallbacks(); + checkOutputDoesNotContain(host, [fsNotFound]); + }); + + it("works when included file with ambient module changes", () => { + const root = { + path: "/a/b/foo.ts", + content: ` +import * as fs from "fs"; +import * as u from "url"; +` + }; + + const file = { + path: "/a/b/bar.d.ts", + content: ` +declare module "url" { + export interface Url { + href?: string; + protocol?: string; + auth?: string; + hostname?: string; + port?: string; + host?: string; + pathname?: string; + search?: string; + query?: string | any; + slashes?: boolean; + hash?: string; + path?: string; + } +} +` + }; + + const fileContentWithFS = ` +declare module "fs" { + export interface Stats { + isFile(): boolean; + isDirectory(): boolean; + isBlockDevice(): boolean; + isCharacterDevice(): boolean; + isSymbolicLink(): boolean; + isFIFO(): boolean; + isSocket(): boolean; + dev: number; + ino: number; + mode: number; + nlink: number; + uid: number; + gid: number; + rdev: number; + size: number; + blksize: number; + blocks: number; + atimeMs: number; + mtimeMs: number; + ctimeMs: number; + birthtimeMs: number; + atime: Date; + mtime: Date; + ctime: Date; + birthtime: Date; + } +} +`; + + const files = [root, file, libFile]; + const host = createWatchedSystem(files, { currentDirectory: "/a/b" }); + + createWatchModeWithoutConfigFile([root.path, file.path], host, {}); + + const fsNotFound = `foo.ts(2,21): error TS2307: Cannot find module 'fs'.\n`; + checkOutputContains(host, [fsNotFound]); + host.clearOutput(); + + file.content += fileContentWithFS; + host.reloadFS(files); + host.runQueuedTimeoutCallbacks(); + checkOutputDoesNotContain(host, [fsNotFound]); + }); }); } From 9767d77143b89ea62b6a48bb8163ba68ab27213f Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 10 Oct 2017 18:41:45 -0700 Subject: [PATCH 2/4] Update comment on emit handler functions --- src/compiler/builder.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 03eb5b7e8ae..093c6ec4d03 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -73,12 +73,17 @@ namespace ts { */ onRemoveSourceFile(path: Path): void; /** - * Called when sourceFile is changed + * For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called. + * If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called; + * otherwise "onUpdateSourceFileWithSameVersion" will be called. + * This should return whether the source file should be marked as changed (meaning that something associated with file has changed, e.g. module resolution) */ onUpdateSourceFile(program: Program, sourceFile: SourceFile): void; /** - * Called when source file has not changed - * If returned true, builder will mark the file as changed (noting that something associated with file has changed eg. module resolution) + * For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called. + * If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called; + * otherwise "onUpdateSourceFileWithSameVersion" will be called. + * This should return whether the source file should be marked as changed (meaning that something associated with file has changed, e.g. module resolution) */ onUpdateSourceFileWithSameVersion(program: Program, sourceFile: SourceFile): boolean; /** From 993890f06c422ab7fc016e07604f2ef0e00311c5 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 10 Oct 2017 20:19:46 -0700 Subject: [PATCH 3/4] Verify errors more correctly in tsc-watch mode --- src/harness/unittests/tscWatchMode.ts | 353 ++++++++++++---------- src/harness/virtualFileSystemWithWatch.ts | 8 +- 2 files changed, 193 insertions(+), 168 deletions(-) diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 31d79df21c1..b25a7b1eb53 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -80,6 +80,92 @@ namespace ts.tscWatch { checkOutputDoesNotContain(host, expectedNonAffectedFiles); } + function checkOutputErrors(host: WatchedSystem, errors?: ReadonlyArray, isInitial?: true, skipWaiting?: true) { + const outputs = host.getOutput(); + const expectedOutputCount = (isInitial ? 0 : 1) + (errors ? errors.length : 0) + (skipWaiting ? 0 : 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++; + } + forEach(errors, error => { + assertDiagnosticAt(host, index, error); + index++; + }); + if (!skipWaiting) { + assertWatchDiagnosticAt(host, index, Diagnostics.Compilation_complete_Watching_for_file_changes); + } + host.clearOutput(); + } + + function assertDiagnosticAt(host: WatchedSystem, outputAt: number, diagnostic: Diagnostic) { + const output = host.getOutput()[outputAt]; + assert.equal(output, formatDiagnostic(diagnostic, host), "outputs[" + outputAt + "] is " + output); + } + + function assertWatchDiagnosticAt(host: WatchedSystem, outputAt: number, diagnosticMessage: DiagnosticMessage) { + const output = host.getOutput()[outputAt]; + assert.isTrue(endsWith(output, getWatchDiagnosticWithoutDate(host, diagnosticMessage)), "outputs[" + outputAt + "] is " + output); + } + + function getWatchDiagnosticWithoutDate(host: WatchedSystem, diagnosticMessage: DiagnosticMessage) { + return ` - ${flattenDiagnosticMessageText(getLocaleSpecificMessage(diagnosticMessage), host.newLine)}${host.newLine + host.newLine + host.newLine}`; + } + + function getDiagnosticOfFileFrom(file: SourceFile, text: string, start: number, length: number, message: DiagnosticMessage): Diagnostic { + return { + file, + start, + length, + + messageText: text, + category: message.category, + code: message.code, + }; + } + + function getDiagnosticWithoutFile(message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic { + let text = getLocaleSpecificMessage(message); + + if (arguments.length > 1) { + text = formatStringFromArgs(text, arguments, 1); + } + + return getDiagnosticOfFileFrom(/*file*/ undefined, text, /*start*/ undefined, /*length*/ undefined, message); + } + + function getDiagnosticOfFile(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic { + let text = getLocaleSpecificMessage(message); + + if (arguments.length > 4) { + text = formatStringFromArgs(text, arguments, 4); + } + + return getDiagnosticOfFileFrom(file, text, start, length, message); + } + + function getUnknownCompilerOption(program: Program, configFile: FileOrFolder, option: string) { + const quotedOption = `"${option}"`; + return getDiagnosticOfFile(program.getCompilerOptions().configFile, configFile.content.indexOf(quotedOption), quotedOption.length, Diagnostics.Unknown_compiler_option_0, option); + } + + function getDiagnosticOfFileFromProgram(program: Program, filePath: string, start: number, length: number, message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic { + let text = getLocaleSpecificMessage(message); + + if (arguments.length > 5) { + text = formatStringFromArgs(text, arguments, 5); + } + + return getDiagnosticOfFileFrom(program.getSourceFileByPath(toPath(filePath, program.getCurrentDirectory(), s => s.toLowerCase())), + text, start, length, message); + } + + function getDiagnosticModuleNotFoundOfFile(program: Program, file: FileOrFolder, moduleName: string) { + const quotedModuleName = `"${moduleName}"`; + return getDiagnosticOfFileFromProgram(program, file.path, file.content.indexOf(quotedModuleName), quotedModuleName.length, Diagnostics.Cannot_find_module_0, moduleName); + } + describe("tsc-watch program updates", () => { const commonFile1: FileOrFolder = { path: "/a/b/commonFile1.ts", @@ -233,9 +319,10 @@ namespace ts.tscWatch { }); it("handles the missing files - that were added to program because they were added with /// { + const commonFile2Name = "commonFile2.ts"; const file1: FileOrFolder = { path: "/a/b/commonFile1.ts", - content: `/// + content: `/// let x = y` }; const host = createWatchedSystem([file1, libFile]); @@ -243,18 +330,16 @@ namespace ts.tscWatch { checkProgramRootFiles(watch(), [file1.path]); checkProgramActualFiles(watch(), [file1.path, libFile.path]); - const errors = [ - `a/b/commonFile1.ts(1,22): error TS6053: File '${commonFile2.path}' not found.${host.newLine}`, - `a/b/commonFile1.ts(2,29): error TS2304: Cannot find name 'y'.${host.newLine}` - ]; - checkOutputContains(host, errors); - host.clearOutput(); + 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); host.reloadFS([file1, commonFile2, libFile]); host.runQueuedTimeoutCallbacks(); checkProgramRootFiles(watch(), [file1.path]); checkProgramActualFiles(watch(), [file1.path, libFile.path, commonFile2.path]); - checkOutputDoesNotContain(host, errors); + checkOutputErrors(host); }); it("should reflect change in config file", () => { @@ -578,17 +663,19 @@ namespace ts.tscWatch { path: "/a/b/tsconfig.json", content: JSON.stringify({ compilerOptions: {} }) }; - const host = createWatchedSystem([file1, file2, config]); + const host = createWatchedSystem([file1, file2, libFile, config]); const watch = createWatchModeWithConfigFile(config.path, host); - checkProgramActualFiles(watch(), [file1.path, file2.path]); + checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]); + checkOutputErrors(host, emptyArray, /*isInitial*/ true); - host.clearOutput(); - host.reloadFS([file1, file2]); + host.reloadFS([file1, file2, libFile]); host.checkTimeoutQueueLengthAndRun(1); assert.equal(host.exitCode, ExitStatus.DiagnosticsPresent_OutputsSkipped); - checkOutputContains(host, [`error TS6053: File '${config.path}' not found.${host.newLine}`]); + checkOutputErrors(host, [ + getDiagnosticWithoutFile(Diagnostics.File_0_not_found, config.path) + ], /*isInitial*/ undefined, /*skipWaiting*/ true); }); it("Proper errors: document is not contained in project", () => { @@ -687,25 +774,25 @@ namespace ts.tscWatch { }; const file1 = { path: "/a/b/file1.ts", - content: "import * as T from './moduleFile'; T.bar();" + content: 'import * as T from "./moduleFile"; T.bar();' }; const host = createWatchedSystem([moduleFile, file1, libFile]); - createWatchModeWithoutConfigFile([file1.path], host); - const error = "a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.\n"; - checkOutputDoesNotContain(host, [error]); + const watch = createWatchModeWithoutConfigFile([file1.path], host); + checkOutputErrors(host, emptyArray, /*isInitial*/ true); const moduleFileOldPath = moduleFile.path; const moduleFileNewPath = "/a/b/moduleFile1.ts"; moduleFile.path = moduleFileNewPath; host.reloadFS([moduleFile, file1, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputContains(host, [error]); + checkOutputErrors(host, [ + getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile") + ]); - host.clearOutput(); moduleFile.path = moduleFileOldPath; host.reloadFS([moduleFile, file1, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputDoesNotContain(host, [error]); + checkOutputErrors(host); }); it("rename a module file and rename back should restore the states for configured projects", () => { @@ -715,31 +802,29 @@ namespace ts.tscWatch { }; const file1 = { path: "/a/b/file1.ts", - content: "import * as T from './moduleFile'; T.bar();" + content: 'import * as T from "./moduleFile"; T.bar();' }; const configFile = { path: "/a/b/tsconfig.json", content: `{}` }; const host = createWatchedSystem([moduleFile, file1, configFile, libFile]); - createWatchModeWithConfigFile(configFile.path, host); - - const error = "a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.\n"; - checkOutputDoesNotContain(host, [error]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkOutputErrors(host, emptyArray, /*isInitial*/ true); const moduleFileOldPath = moduleFile.path; const moduleFileNewPath = "/a/b/moduleFile1.ts"; moduleFile.path = moduleFileNewPath; - host.clearOutput(); host.reloadFS([moduleFile, file1, configFile, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputContains(host, [error]); + checkOutputErrors(host, [ + getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile") + ]); - host.clearOutput(); moduleFile.path = moduleFileOldPath; host.reloadFS([moduleFile, file1, configFile, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputDoesNotContain(host, [error]); + checkOutputErrors(host); }); it("types should load from config file path if config exists", () => { @@ -771,18 +856,18 @@ namespace ts.tscWatch { }; const file1 = { path: "/a/b/file1.ts", - content: "import * as T from './moduleFile'; T.bar();" + content: 'import * as T from "./moduleFile"; T.bar();' }; const host = createWatchedSystem([file1, libFile]); - createWatchModeWithoutConfigFile([file1.path], host); + const watch = createWatchModeWithoutConfigFile([file1.path], host); - const error = `a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.${host.newLine}`; - checkOutputContains(host, [error]); - host.clearOutput(); + checkOutputErrors(host, [ + getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile") + ], /*isInitial*/ true); host.reloadFS([file1, moduleFile, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputDoesNotContain(host, [error]); + checkOutputErrors(host); }); it("Configure file diagnostics events are generated when the config file has errors", () => { @@ -801,14 +886,14 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file, configFile, libFile]); - createWatchModeWithConfigFile(configFile.path, host); - checkOutputContains(host, [ - `a/b/tsconfig.json(3,29): error TS5023: Unknown compiler option \'foo\'.${host.newLine}`, - `a/b/tsconfig.json(4,29): error TS5023: Unknown compiler option \'allowJS\'.${host.newLine}` - ]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkOutputErrors(host, [ + getUnknownCompilerOption(watch(), configFile, "foo"), + getUnknownCompilerOption(watch(), configFile, "allowJS") + ], /*isInitial*/ true); }); - it("Configure file diagnostics events are generated when the config file doesn't have errors", () => { + it("If config file doesnt have errors, they are not reported", () => { const file = { path: "/a/b/app.ts", content: "let x = 10" @@ -822,13 +907,10 @@ namespace ts.tscWatch { const host = createWatchedSystem([file, configFile, libFile]); createWatchModeWithConfigFile(configFile.path, host); - checkOutputDoesNotContain(host, [ - `a/b/tsconfig.json(3,29): error TS5023: Unknown compiler option \'foo\'.${host.newLine}`, - `a/b/tsconfig.json(4,29): error TS5023: Unknown compiler option \'allowJS\'.${host.newLine}` - ]); + checkOutputErrors(host, emptyArray, /*isInitial*/ true); }); - it("Configure file diagnostics events are generated when the config file changes", () => { + it("Reports errors when the config file changes", () => { const file = { path: "/a/b/app.ts", content: "let x = 10" @@ -841,9 +923,8 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file, configFile, libFile]); - createWatchModeWithConfigFile(configFile.path, host); - const error = `a/b/tsconfig.json(3,25): error TS5023: Unknown compiler option 'haha'.${host.newLine}`; - checkOutputDoesNotContain(host, [error]); + const watch = createWatchModeWithConfigFile(configFile.path, host); + checkOutputErrors(host, emptyArray, /*isInitial*/ true); configFile.content = `{ "compilerOptions": { @@ -852,15 +933,16 @@ namespace ts.tscWatch { }`; host.reloadFS([file, configFile, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputContains(host, [error]); + checkOutputErrors(host, [ + getUnknownCompilerOption(watch(), configFile, "haha") + ]); - host.clearOutput(); configFile.content = `{ "compilerOptions": {} }`; host.reloadFS([file, configFile, libFile]); host.runQueuedTimeoutCallbacks(); - checkOutputDoesNotContain(host, [error]); + checkOutputErrors(host); }); it("non-existing directories listed in config file input array should be tolerated without crashing the server", () => { @@ -935,29 +1017,28 @@ namespace ts.tscWatch { }`; const configFileContentWithComment = configFileContentBeforeComment + configFileContentComment + configFileContentAfterComment; const configFileContentWithoutCommentLine = configFileContentBeforeComment + configFileContentAfterComment; - - const line = 5; - const errors = (line: number) => [ - `a/b/tsconfig.json(${line},25): error TS5053: Option \'allowJs\' cannot be specified with option \'declaration\'.\n`, - `a/b/tsconfig.json(${line + 1},25): error TS5053: Option \'allowJs\' cannot be specified with option \'declaration\'.\n` - ]; - const configFile = { path: "/a/b/tsconfig.json", content: configFileContentWithComment }; - const host = createWatchedSystem([file, libFile, configFile]); - createWatchModeWithConfigFile(configFile.path, host); - checkOutputContains(host, errors(line)); - checkOutputDoesNotContain(host, errors(line - 2)); - host.clearOutput(); + const files = [file, libFile, configFile]; + const host = createWatchedSystem(files); + const watch = createWatchModeWithConfigFile(configFile.path, host); + const errors = () => [ + getDiagnosticOfFile(watch().getCompilerOptions().configFile, configFile.content.indexOf('"allowJs"'), '"allowJs"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration"), + 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); configFile.content = configFileContentWithoutCommentLine; - host.reloadFS([file, configFile]); + host.reloadFS(files); host.runQueuedTimeoutCallbacks(); - checkOutputContains(host, errors(line - 2)); - checkOutputDoesNotContain(host, errors(line)); + const nowErrors = errors(); + checkOutputErrors(host, nowErrors); + assert.equal(nowErrors[0].start, intialErrors[0].start - configFileContentComment.length); + assert.equal(nowErrors[1].start, intialErrors[1].start - configFileContentComment.length); }); }); @@ -1485,23 +1566,20 @@ namespace ts.tscWatch { path: "/a/d/f0.ts", content: `import {x} from "f1"` }; - const imported = { path: "/a/f1.ts", content: `foo()` }; - const f1IsNotModule = `a/d/f0.ts(1,17): error TS2306: File '${imported.path}' is not a module.\n`; - const cannotFindFoo = `a/f1.ts(1,1): error TS2304: Cannot find name 'foo'.\n`; - const cannotAssignValue = "a/d/f0.ts(2,21): error TS2322: Type '1' is not assignable to type 'string'.\n"; - const files = [root, imported, libFile]; const host = createWatchedSystem(files); - createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + + const f1IsNotModule = getDiagnosticOfFileFromProgram(watch(), root.path, root.content.indexOf('"f1"'), '"f1"'.length, Diagnostics.File_0_is_not_a_module, imported.path); + const cannotFindFoo = getDiagnosticOfFileFromProgram(watch(), imported.path, imported.content.indexOf("foo"), "foo".length, Diagnostics.Cannot_find_name_0, "foo"); // ensure that imported file was found - checkOutputContains(host, [f1IsNotModule, cannotFindFoo]); - host.clearOutput(); + checkOutputErrors(host, [f1IsNotModule, cannotFindFoo], /*isInitial*/ true); const originalFileExists = host.fileExists; { @@ -1517,8 +1595,11 @@ namespace ts.tscWatch { host.runQueuedTimeoutCallbacks(); // ensure file has correct number of errors after edit - checkOutputContains(host, [f1IsNotModule, cannotAssignValue]); - host.clearOutput(); + checkOutputErrors(host, [ + 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 + ]); } { let fileExistsIsCalled = false; @@ -1534,13 +1615,13 @@ namespace ts.tscWatch { root.content = `import {x} from "f2"`; host.reloadFS(files); - // trigger synchronization to make sure that LSHost will try to find 'f2' module on disk - host.runQueuedTimeoutCallbacks(); + // trigger synchronization to make sure that LSHost will try to find 'f2' module on disk + host.runQueuedTimeoutCallbacks(); - // ensure file has correct number of errors after edit - const cannotFindModuleF2 = `a/d/f0.ts(1,17): error TS2307: Cannot find module 'f2'.\n`; - checkOutputContains(host, [cannotFindModuleF2]); - host.clearOutput(); + // ensure file has correct number of errors after edit + checkOutputErrors(host, [ + getDiagnosticModuleNotFoundOfFile(watch(), root, "f2") + ]); assert.isTrue(fileExistsIsCalled); } @@ -1561,7 +1642,7 @@ namespace ts.tscWatch { host.reloadFS(files); host.runQueuedTimeoutCallbacks(); - checkOutputContains(host, [f1IsNotModule, cannotFindFoo]); + checkOutputErrors(host, [f1IsNotModule, cannotFindFoo]); assert.isTrue(fileExistsCalled); } }); @@ -1593,12 +1674,12 @@ namespace ts.tscWatch { return originalFileExists.call(host, fileName); }; - createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); - const barNotFound = `a/foo.ts(1,17): error TS2307: Cannot find module 'bar'.\n`; assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); - checkOutputContains(host, [barNotFound]); - host.clearOutput(); + checkOutputErrors(host, [ + getDiagnosticModuleNotFoundOfFile(watch(), root, "bar") + ], /*isInitial*/ true); fileExistsCalledForBar = false; root.content = `import {y} from "bar"`; @@ -1606,7 +1687,7 @@ namespace ts.tscWatch { host.runQueuedTimeoutCallbacks(); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); - checkOutputDoesNotContain(host, [barNotFound]); + checkOutputErrors(host); }); it("should compile correctly when resolved module goes missing and then comes back (module is not part of the root)", () => { @@ -1617,7 +1698,7 @@ namespace ts.tscWatch { const imported = { path: `/a/bar.d.ts`, - content: `export const y = 1;` + content: `export const y = 1;export const x = 10;` }; const files = [root, libFile]; @@ -1635,25 +1716,24 @@ namespace ts.tscWatch { return originalFileExists.call(host, fileName); }; - createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); - const barNotFound = `a/foo.ts(1,17): error TS2307: Cannot find module 'bar'.\n`; assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); - checkOutputDoesNotContain(host, [barNotFound]); - host.clearOutput(); + checkOutputErrors(host, emptyArray, /*isInitial*/ true); fileExistsCalledForBar = false; host.reloadFS(files); host.runQueuedTimeoutCallbacks(); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); - checkOutputContains(host, [barNotFound]); - host.clearOutput(); + checkOutputErrors(host, [ + getDiagnosticModuleNotFoundOfFile(watch(), root, "bar") + ]); fileExistsCalledForBar = false; host.reloadFS(filesWithImported); host.checkTimeoutQueueLengthAndRun(1); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called."); - checkOutputDoesNotContain(host, [barNotFound]); + checkOutputErrors(host); }); it("works when module resolution changes to ambient module", () => { @@ -1677,30 +1757,6 @@ namespace ts.tscWatch { declare module "fs" { export interface Stats { isFile(): boolean; - isDirectory(): boolean; - isBlockDevice(): boolean; - isCharacterDevice(): boolean; - isSymbolicLink(): boolean; - isFIFO(): boolean; - isSocket(): boolean; - dev: number; - ino: number; - mode: number; - nlink: number; - uid: number; - gid: number; - rdev: number; - size: number; - blksize: number; - blocks: number; - atimeMs: number; - mtimeMs: number; - ctimeMs: number; - birthtimeMs: number; - atime: Date; - mtime: Date; - ctime: Date; - birthtime: Date; } }` }; @@ -1709,15 +1765,15 @@ declare module "fs" { const filesWithNodeType = files.concat(packageJson, nodeType); const host = createWatchedSystem(files, { currentDirectory: "/a/b" }); - createWatchModeWithoutConfigFile([root.path], host, { }); + const watch = createWatchModeWithoutConfigFile([root.path], host, { }); - const fsNotFound = `foo.ts(1,21): error TS2307: Cannot find module 'fs'.\n`; - checkOutputContains(host, [fsNotFound]); - host.clearOutput(); + checkOutputErrors(host, [ + getDiagnosticModuleNotFoundOfFile(watch(), root, "fs") + ], /*isInitial*/ true); host.reloadFS(filesWithNodeType); host.runQueuedTimeoutCallbacks(); - checkOutputDoesNotContain(host, [fsNotFound]); + checkOutputErrors(host); }); it("works when included file with ambient module changes", () => { @@ -1735,17 +1791,6 @@ import * as u from "url"; declare module "url" { export interface Url { href?: string; - protocol?: string; - auth?: string; - hostname?: string; - port?: string; - host?: string; - pathname?: string; - search?: string; - query?: string | any; - slashes?: boolean; - hash?: string; - path?: string; } } ` @@ -1755,30 +1800,6 @@ declare module "url" { declare module "fs" { export interface Stats { isFile(): boolean; - isDirectory(): boolean; - isBlockDevice(): boolean; - isCharacterDevice(): boolean; - isSymbolicLink(): boolean; - isFIFO(): boolean; - isSocket(): boolean; - dev: number; - ino: number; - mode: number; - nlink: number; - uid: number; - gid: number; - rdev: number; - size: number; - blksize: number; - blocks: number; - atimeMs: number; - mtimeMs: number; - ctimeMs: number; - birthtimeMs: number; - atime: Date; - mtime: Date; - ctime: Date; - birthtime: Date; } } `; @@ -1786,16 +1807,16 @@ declare module "fs" { const files = [root, file, libFile]; const host = createWatchedSystem(files, { currentDirectory: "/a/b" }); - createWatchModeWithoutConfigFile([root.path, file.path], host, {}); + const watch = createWatchModeWithoutConfigFile([root.path, file.path], host, {}); - const fsNotFound = `foo.ts(2,21): error TS2307: Cannot find module 'fs'.\n`; - checkOutputContains(host, [fsNotFound]); - host.clearOutput(); + checkOutputErrors(host, [ + getDiagnosticModuleNotFoundOfFile(watch(), root, "fs") + ], /*isInitial*/ true); file.content += fileContentWithFS; host.reloadFS(files); host.runQueuedTimeoutCallbacks(); - checkOutputDoesNotContain(host, [fsNotFound]); + checkOutputErrors(host); }); }); } diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index c16f57235e4..cef70910678 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -212,13 +212,13 @@ namespace ts.TestFSWithWatch { directoryName: string; } - export class TestServerHost implements server.ServerHost { + export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost { args: string[] = []; private readonly output: string[] = []; private fs: Map = createMap(); - private getCanonicalFileName: (s: string) => string; + getCanonicalFileName: (s: string) => string; private toPath: (f: string) => Path; private timeoutCallbacks = new Callbacks(); private immediateCallbacks = new Callbacks(); @@ -234,6 +234,10 @@ namespace ts.TestFSWithWatch { this.reloadFS(fileOrFolderList); } + getNewLine() { + return this.newLine; + } + toNormalizedAbsolutePath(s: string) { return getNormalizedAbsolutePath(s, this.currentDirectory); } From 142a88a4aeab6d24a179cc0782d6914eb0904f30 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 11 Oct 2017 10:51:46 -0700 Subject: [PATCH 4/4] Update the comment on emit handler method --- src/compiler/builder.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 093c6ec4d03..192f1e43027 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -76,14 +76,13 @@ namespace ts { * For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called. * If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called; * otherwise "onUpdateSourceFileWithSameVersion" will be called. - * This should return whether the source file should be marked as changed (meaning that something associated with file has changed, e.g. module resolution) */ onUpdateSourceFile(program: Program, sourceFile: SourceFile): void; /** * For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called. * If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called; * otherwise "onUpdateSourceFileWithSameVersion" will be called. - * This should return whether the source file should be marked as changed (meaning that something associated with file has changed, e.g. module resolution) + * This function should return whether the source file should be marked as changed (meaning that something associated with file has changed, e.g. module resolution) */ onUpdateSourceFileWithSameVersion(program: Program, sourceFile: SourceFile): boolean; /**