Skip to content

Commit c2519bb

Browse files
authored
When there is no change in file text for program, no need to update program (#51626)
* When fsEvent for change is repeated * When trying to check if program is uptodate, read the files from disk to determine the version instead of delaying so that new program is not created if file contents have not changed
1 parent af36a85 commit c2519bb

19 files changed

+264
-911
lines changed

src/compiler/tsbuildPublic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, ho
436436
// State of the solution
437437
const baseCompilerOptions = getCompilerOptionsOfBuildOptions(options);
438438
const compilerHost = createCompilerHostFromProgramHost(host, () => state.projectCompilerOptions) as CompilerHost & ReadBuildProgramHost;
439-
setGetSourceFileAsHashVersioned(compilerHost, host);
439+
setGetSourceFileAsHashVersioned(compilerHost);
440440
compilerHost.getParsedCommandLine = fileName => parseConfigFile(state, fileName as ResolvedConfigFileName, toResolvedConfigFilePath(state, fileName as ResolvedConfigFileName));
441441
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
442442
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);

src/compiler/watch.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -739,9 +739,9 @@ export function createWatchFactory<Y = undefined>(host: WatchFactoryHost & { tra
739739
export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCompilerOptions: () => CompilerOptions, directoryStructureHost: DirectoryStructureHost = host): CompilerHost {
740740
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
741741
const hostGetNewLine = memoize(() => host.getNewLine());
742-
return {
742+
const compilerHost: CompilerHost = {
743743
getSourceFile: createGetSourceFile(
744-
(fileName, encoding) => host.readFile(fileName, encoding),
744+
(fileName, encoding) => !encoding ? compilerHost.readFile(fileName) : host.readFile(fileName, encoding),
745745
getCompilerOptions,
746746
/*setParentNodes*/ undefined
747747
),
@@ -768,6 +768,7 @@ export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCom
768768
disableUseFileVersionAsSignature: host.disableUseFileVersionAsSignature,
769769
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
770770
};
771+
return compilerHost;
771772
}
772773

773774
/** @internal */
@@ -810,12 +811,12 @@ export function getSourceFileVersionAsHashFromText(host: Pick<CompilerHost, "cre
810811
}
811812

812813
/** @internal */
813-
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) {
814+
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost) {
814815
const originalGetSourceFile = compilerHost.getSourceFile;
815816
compilerHost.getSourceFile = (...args) => {
816817
const result = originalGetSourceFile.call(compilerHost, ...args);
817818
if (result) {
818-
result.version = getSourceFileVersionAsHashFromText(host, result.text);
819+
result.version = getSourceFileVersionAsHashFromText(compilerHost, result.text);
819820
}
820821
return result;
821822
};

src/compiler/watchPublic.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
getNewLineCharacter,
4949
getNormalizedAbsolutePath,
5050
getParsedCommandLineOfConfigFile,
51+
getSourceFileVersionAsHashFromText,
5152
getTsBuildInfoEmitOutputFilePath,
5253
HasInvalidatedResolutions,
5354
isArray,
@@ -121,7 +122,7 @@ export function createIncrementalCompilerHost(options: CompilerOptions, system =
121122
host.createHash = maybeBind(system, system.createHash);
122123
host.disableUseFileVersionAsSignature = system.disableUseFileVersionAsSignature;
123124
host.storeFilesChangingSignatureDuringEmit = system.storeFilesChangingSignatureDuringEmit;
124-
setGetSourceFileAsHashVersioned(host, system);
125+
setGetSourceFileAsHashVersioned(host);
125126
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
126127
return host;
127128
}
@@ -429,7 +430,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
429430
}
430431

431432
const compilerHost = createCompilerHostFromProgramHost(host, () => compilerOptions, directoryStructureHost) as CompilerHost & ResolutionCacheHost;
432-
setGetSourceFileAsHashVersioned(compilerHost, host);
433+
setGetSourceFileAsHashVersioned(compilerHost);
433434
// Members for CompilerHost
434435
const getNewSourceFile = compilerHost.getSourceFile;
435436
compilerHost.getSourceFile = (fileName, ...args) => getVersionedSourceFileByPath(fileName, toPath(fileName), ...args);
@@ -551,9 +552,9 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
551552
const hasInvalidatedResolutions = resolutionCache.createHasInvalidatedResolutions(customHasInvalidatedResolutions);
552553
const {
553554
originalReadFile, originalFileExists, originalDirectoryExists,
554-
originalCreateDirectory, originalWriteFile,
555+
originalCreateDirectory, originalWriteFile, readFileWithCache
555556
} = changeCompilerHostLikeToUseCache(compilerHost, toPath);
556-
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
557+
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, path => getSourceVersion(path, readFileWithCache), fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
557558
if (hasChangedConfigFileParsingErrors) {
558559
if (reportFileChangeDetectedOnCreateProgram) {
559560
reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation);
@@ -708,9 +709,13 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
708709
}
709710
}
710711

711-
function getSourceVersion(path: Path): string | undefined {
712+
function getSourceVersion(path: Path, readFileWithCache: (file: string) => string | undefined): string | undefined {
712713
const hostSourceFile = sourceFilesCache.get(path);
713-
return !hostSourceFile || !hostSourceFile.version ? undefined : hostSourceFile.version;
714+
if (!hostSourceFile) return undefined;
715+
if (hostSourceFile.version) return hostSourceFile.version;
716+
// Read file and get new version
717+
const text = readFileWithCache(path);
718+
return text ? getSourceFileVersionAsHashFromText(compilerHost, text) : undefined;
714719
}
715720

716721
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions, hasSourceFileByPath: boolean) {

src/testRunner/unittests/tscWatch/watchEnvironment.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,4 +702,26 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
702702
]
703703
});
704704
});
705+
706+
verifyTscWatch({
707+
scenario,
708+
subScenario: "fsEvent for change is repeated",
709+
commandLineArgs: ["-w", "main.ts", "--extendedDiagnostics"],
710+
sys: () => createWatchedSystem({
711+
"/user/username/projects/project/main.ts": `let a: string = "Hello"`,
712+
[libFile.path]: libFile.content,
713+
}, { currentDirectory: "/user/username/projects/project" }),
714+
changes: [
715+
{
716+
caption: "change main.ts",
717+
change: sys => sys.replaceFileText("/user/username/projects/project/main.ts", "Hello", "Hello World"),
718+
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
719+
},
720+
{
721+
caption: "receive another change event without modifying the file",
722+
change: sys => sys.invokeFsWatches("/user/username/projects/project/main.ts", "change", /*modifiedTime*/ undefined, /*useTildeSuffix*/ undefined),
723+
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
724+
}
725+
]
726+
});
705727
});

src/testRunner/unittests/virtualFileSystemWithWatch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
686686
}
687687
}
688688

689-
private invokeFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime: Date | undefined, useTildeSuffix: boolean | undefined) {
689+
invokeFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime: Date | undefined, useTildeSuffix: boolean | undefined) {
690690
this.invokeFsWatchesCallbacks(fullPath, eventName, modifiedTime, fullPath, useTildeSuffix);
691691
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, modifiedTime, fullPath, useTildeSuffix);
692692
this.invokeRecursiveFsWatches(fullPath, eventName, modifiedTime, /*entryFullPath*/ undefined, useTildeSuffix);

tests/baselines/reference/tscWatch/emitAndErrorUpdates/assumeChangesOnlyAffectDirectDependencies/with-noEmitOnError-with-incremental.js

Lines changed: 6 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -183,30 +183,6 @@ Input::
183183
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
184184

185185
Output::
186-
>> Screen clear
187-
[12:00:43 AM] File change detected. Starting incremental compilation...
188-
189-
src/main.ts:4:1 - error TS1005: ',' expected.
190-
191-
4 ;
192-
  ~
193-
194-
[12:00:44 AM] Found 1 error. Watching for file changes.
195-
196-
197-
198-
Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
199-
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"incremental":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
200-
Program structureReused: Completely
201-
Program files::
202-
/a/lib/lib.d.ts
203-
/user/username/projects/noEmitOnError/shared/types/db.ts
204-
/user/username/projects/noEmitOnError/src/main.ts
205-
/user/username/projects/noEmitOnError/src/other.ts
206-
207-
Semantic diagnostics in builder refreshed for::
208-
209-
No shapes updated in the builder::
210186

211187
PolledWatches::
212188
/user/username/projects/noemitonerror/node_modules/@types:
@@ -243,9 +219,9 @@ const a = {
243219

244220
Output::
245221
>> Screen clear
246-
[[90m12:00:48 AM[0m] File change detected. Starting incremental compilation...
222+
[[90m12:00:46 AM[0m] File change detected. Starting incremental compilation...
247223

248-
[[90m12:01:06 AM[0m] Found 0 errors. Watching for file changes.
224+
[[90m12:01:04 AM[0m] Found 0 errors. Watching for file changes.
249225

250226

251227

@@ -382,14 +358,14 @@ const a: string = 10;
382358

383359
Output::
384360
>> Screen clear
385-
[[90m12:01:13 AM[0m] File change detected. Starting incremental compilation...
361+
[[90m12:01:11 AM[0m] File change detected. Starting incremental compilation...
386362

387363
src/main.ts:2:7 - error TS2322: Type 'number' is not assignable to type 'string'.
388364

389365
2 const a: string = 10;
390366
   ~
391367

392-
[[90m12:01:17 AM[0m] Found 1 error. Watching for file changes.
368+
[[90m12:01:15 AM[0m] Found 1 error. Watching for file changes.
393369

394370

395371

@@ -521,30 +497,6 @@ Input::
521497
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
522498

523499
Output::
524-
>> Screen clear
525-
[12:01:25 AM] File change detected. Starting incremental compilation...
526-
527-
src/main.ts:2:7 - error TS2322: Type 'number' is not assignable to type 'string'.
528-
529-
2 const a: string = 10;
530-
   ~
531-
532-
[12:01:26 AM] Found 1 error. Watching for file changes.
533-
534-
535-
536-
Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
537-
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"incremental":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
538-
Program structureReused: Completely
539-
Program files::
540-
/a/lib/lib.d.ts
541-
/user/username/projects/noEmitOnError/shared/types/db.ts
542-
/user/username/projects/noEmitOnError/src/main.ts
543-
/user/username/projects/noEmitOnError/src/other.ts
544-
545-
Semantic diagnostics in builder refreshed for::
546-
547-
No shapes updated in the builder::
548500

549501
PolledWatches::
550502
/user/username/projects/noemitonerror/node_modules/@types:
@@ -579,9 +531,9 @@ const a: string = "hello";
579531

580532
Output::
581533
>> Screen clear
582-
[[90m12:01:30 AM[0m] File change detected. Starting incremental compilation...
534+
[[90m12:01:26 AM[0m] File change detected. Starting incremental compilation...
583535

584-
[[90m12:01:37 AM[0m] Found 0 errors. Watching for file changes.
536+
[[90m12:01:33 AM[0m] Found 0 errors. Watching for file changes.
585537

586538

587539

@@ -701,25 +653,6 @@ Input::
701653
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
702654

703655
Output::
704-
>> Screen clear
705-
[12:01:44 AM] File change detected. Starting incremental compilation...
706-
707-
[12:01:45 AM] Found 0 errors. Watching for file changes.
708-
709-
710-
711-
Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
712-
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"incremental":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
713-
Program structureReused: Completely
714-
Program files::
715-
/a/lib/lib.d.ts
716-
/user/username/projects/noEmitOnError/shared/types/db.ts
717-
/user/username/projects/noEmitOnError/src/main.ts
718-
/user/username/projects/noEmitOnError/src/other.ts
719-
720-
Semantic diagnostics in builder refreshed for::
721-
722-
No shapes updated in the builder::
723656

724657
PolledWatches::
725658
/user/username/projects/noemitonerror/node_modules/@types:

tests/baselines/reference/tscWatch/emitAndErrorUpdates/assumeChangesOnlyAffectDirectDependencies/with-noEmitOnError.js

Lines changed: 6 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -103,30 +103,6 @@ Input::
103103
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
104104

105105
Output::
106-
>> Screen clear
107-
[12:00:36 AM] File change detected. Starting incremental compilation...
108-
109-
src/main.ts:4:1 - error TS1005: ',' expected.
110-
111-
4 ;
112-
  ~
113-
114-
[12:00:37 AM] Found 1 error. Watching for file changes.
115-
116-
117-
118-
Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
119-
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
120-
Program structureReused: Completely
121-
Program files::
122-
/a/lib/lib.d.ts
123-
/user/username/projects/noEmitOnError/shared/types/db.ts
124-
/user/username/projects/noEmitOnError/src/main.ts
125-
/user/username/projects/noEmitOnError/src/other.ts
126-
127-
Semantic diagnostics in builder refreshed for::
128-
129-
No shapes updated in the builder::
130106

131107
PolledWatches::
132108
/user/username/projects/noemitonerror/node_modules/@types:
@@ -163,9 +139,9 @@ const a = {
163139

164140
Output::
165141
>> Screen clear
166-
[[90m12:00:41 AM[0m] File change detected. Starting incremental compilation...
142+
[[90m12:00:39 AM[0m] File change detected. Starting incremental compilation...
167143

168-
[[90m12:00:58 AM[0m] Found 0 errors. Watching for file changes.
144+
[[90m12:00:56 AM[0m] Found 0 errors. Watching for file changes.
169145

170146

171147

@@ -236,14 +212,14 @@ const a: string = 10;
236212

237213
Output::
238214
>> Screen clear
239-
[[90m12:01:02 AM[0m] File change detected. Starting incremental compilation...
215+
[[90m12:01:00 AM[0m] File change detected. Starting incremental compilation...
240216

241217
src/main.ts:2:7 - error TS2322: Type 'number' is not assignable to type 'string'.
242218

243219
2 const a: string = 10;
244220
   ~
245221

246-
[[90m12:01:03 AM[0m] Found 1 error. Watching for file changes.
222+
[[90m12:01:01 AM[0m] Found 1 error. Watching for file changes.
247223

248224

249225

@@ -291,30 +267,6 @@ Input::
291267
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
292268

293269
Output::
294-
>> Screen clear
295-
[12:01:08 AM] File change detected. Starting incremental compilation...
296-
297-
src/main.ts:2:7 - error TS2322: Type 'number' is not assignable to type 'string'.
298-
299-
2 const a: string = 10;
300-
   ~
301-
302-
[12:01:09 AM] Found 1 error. Watching for file changes.
303-
304-
305-
306-
Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
307-
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
308-
Program structureReused: Completely
309-
Program files::
310-
/a/lib/lib.d.ts
311-
/user/username/projects/noEmitOnError/shared/types/db.ts
312-
/user/username/projects/noEmitOnError/src/main.ts
313-
/user/username/projects/noEmitOnError/src/other.ts
314-
315-
Semantic diagnostics in builder refreshed for::
316-
317-
No shapes updated in the builder::
318270

319271
PolledWatches::
320272
/user/username/projects/noemitonerror/node_modules/@types:
@@ -349,9 +301,9 @@ const a: string = "hello";
349301

350302
Output::
351303
>> Screen clear
352-
[[90m12:01:13 AM[0m] File change detected. Starting incremental compilation...
304+
[[90m12:01:09 AM[0m] File change detected. Starting incremental compilation...
353305

354-
[[90m12:01:17 AM[0m] Found 0 errors. Watching for file changes.
306+
[[90m12:01:13 AM[0m] Found 0 errors. Watching for file changes.
355307

356308

357309

@@ -405,25 +357,6 @@ Input::
405357
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
406358

407359
Output::
408-
>> Screen clear
409-
[12:01:21 AM] File change detected. Starting incremental compilation...
410-
411-
[12:01:22 AM] Found 0 errors. Watching for file changes.
412-
413-
414-
415-
Program root files: ["/user/username/projects/noEmitOnError/shared/types/db.ts","/user/username/projects/noEmitOnError/src/main.ts","/user/username/projects/noEmitOnError/src/other.ts"]
416-
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
417-
Program structureReused: Completely
418-
Program files::
419-
/a/lib/lib.d.ts
420-
/user/username/projects/noEmitOnError/shared/types/db.ts
421-
/user/username/projects/noEmitOnError/src/main.ts
422-
/user/username/projects/noEmitOnError/src/other.ts
423-
424-
Semantic diagnostics in builder refreshed for::
425-
426-
No shapes updated in the builder::
427360

428361
PolledWatches::
429362
/user/username/projects/noemitonerror/node_modules/@types:

0 commit comments

Comments
 (0)