diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 92e651061ed..6dd3226e550 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -249,7 +249,7 @@ namespace ts { export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, host, oldProgram, configFileParsingDiagnostics }: BuilderCreationParameters) { // Return same program if underlying program doesnt change let oldState = oldProgram && oldProgram.getState(); - if (oldState && newProgram === oldState.program && configFileParsingDiagnostics !== newProgram.getConfigFileParsingDiagnostics()) { + if (oldState && newProgram === oldState.program && configFileParsingDiagnostics === newProgram.getConfigFileParsingDiagnostics()) { newProgram = undefined; oldState = undefined; return oldProgram; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 3c73eba86ef..e45a95f7989 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -154,9 +154,12 @@ namespace ts { function updateWatchCompilationHost(watchCompilerHost: WatchCompilerHost) { const compileUsingBuilder = watchCompilerHost.createProgram; - watchCompilerHost.createProgram = (rootNames, options, host, oldProgram) => { - enableStatistics(options); - return compileUsingBuilder(rootNames, options, host, oldProgram); + watchCompilerHost.createProgram = (rootNames, options, host, oldProgram, configFileParsingDiagnostics) => { + Debug.assert(rootNames !== undefined || (options === undefined && !!oldProgram)); + if (options !== undefined) { + enableStatistics(options); + } + return compileUsingBuilder(rootNames, options, host, oldProgram, configFileParsingDiagnostics); }; const emitFilesUsingBuilder = watchCompilerHost.afterProgramCreate; watchCompilerHost.afterProgramCreate = builderProgram => { diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index f0bb151b814..9ec362a12c0 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -625,9 +625,19 @@ namespace ts { builderProgram = createProgram(/*rootNames*/ undefined, /*options*/ undefined, compilerHost, builderProgram, configFileParsingDiagnostics); hasChangedConfigFileParsingErrors = false; } - return builderProgram; + } + else { + createNewProgram(program, hasInvalidatedResolution); } + if (host.afterProgramCreate) { + host.afterProgramCreate(builderProgram); + } + + return builderProgram; + } + + function createNewProgram(program: Program, hasInvalidatedResolution: HasInvalidatedResolution) { // Compile the program if (watchLogLevel !== WatchLogLevel.None) { writeLog("CreatingProgramWith::"); @@ -663,12 +673,6 @@ namespace ts { } missingFilePathsRequestedForRelease = undefined; } - - if (host.afterProgramCreate) { - host.afterProgramCreate(builderProgram); - } - - return builderProgram; } function updateRootFileNames(files: string[]) { diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 44565c0ddbe..ca681a8f8ce 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -418,6 +418,28 @@ namespace ts.tscWatch { checkProgramRootFiles(watch(), [commonFile1.path]); }); + it("works correctly when config file is changed but its content havent", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: `{ + "compilerOptions": {}, + "files": ["${commonFile1.path}", "${commonFile2.path}"] + }` + }; + const files = [libFile, commonFile1, commonFile2, configFile]; + const host = createWatchedSystem(files); + const watch = createWatchOfConfigFile(configFile.path, host); + + checkProgramActualFiles(watch(), [libFile.path, commonFile1.path, commonFile2.path]); + checkOutputErrorsInitial(host, emptyArray); + + host.modifyFile(configFile.path, configFile.content); + host.checkTimeoutQueueLengthAndRun(1); // reload the configured project + + checkProgramActualFiles(watch(), [libFile.path, commonFile1.path, commonFile2.path]); + checkOutputErrorsIncremental(host, emptyArray); + }); + it("files explicitly excluded in config file", () => { const configFile: FileOrFolder = { path: "/a/b/tsconfig.json", diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 3756f435d43..323a51c25ae 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -393,21 +393,7 @@ interface Array {}` if (isString(fileOrDirectory.content)) { // Update file if (currentEntry.content !== fileOrDirectory.content) { - if (options && options.invokeFileDeleteCreateAsPartInsteadOfChange) { - this.removeFileOrFolder(currentEntry, returnFalse); - this.ensureFileOrFolder(fileOrDirectory); - } - else { - currentEntry.content = fileOrDirectory.content; - currentEntry.modifiedTime = this.now(); - this.fs.get(getDirectoryPath(currentEntry.path)).modifiedTime = this.now(); - if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) { - this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath); - } - else { - this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed); - } - } + this.modifyFile(fileOrDirectory.path, fileOrDirectory.content, options); } } else { @@ -446,6 +432,30 @@ interface Array {}` } } + modifyFile(filePath: string, content: string, options?: Partial) { + const path = this.toFullPath(filePath); + const currentEntry = this.fs.get(path); + if (!currentEntry || !isFile(currentEntry)) { + throw new Error(`file not present: ${filePath}`); + } + + if (options && options.invokeFileDeleteCreateAsPartInsteadOfChange) { + this.removeFileOrFolder(currentEntry, returnFalse); + this.ensureFileOrFolder({ path: filePath, content }); + } + else { + currentEntry.content = content; + currentEntry.modifiedTime = this.now(); + this.fs.get(getDirectoryPath(currentEntry.path)).modifiedTime = this.now(); + if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) { + this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath); + } + else { + this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed); + } + } + } + renameFolder(folderName: string, newFolderName: string) { const fullPath = getNormalizedAbsolutePath(folderName, this.currentDirectory); const path = this.toPath(fullPath);