Skip to content

Commit e7a02f4

Browse files
authoredNov 30, 2022
Port of #51626 and #51689 to release-4.9 (#51627)
* 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 * Test for empty string change * Fix empty string for file versioning
1 parent 1727912 commit e7a02f4

20 files changed

+359
-932
lines changed
 

‎src/compiler/tsbuildPublic.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ namespace ts {
292292
// State of the solution
293293
const baseCompilerOptions = getCompilerOptionsOfBuildOptions(options);
294294
const compilerHost = createCompilerHostFromProgramHost(host, () => state.projectCompilerOptions) as CompilerHost & ReadBuildProgramHost;
295-
setGetSourceFileAsHashVersioned(compilerHost, host);
295+
setGetSourceFileAsHashVersioned(compilerHost);
296296
compilerHost.getParsedCommandLine = fileName => parseConfigFile(state, fileName as ResolvedConfigFileName, toResolvedConfigFilePath(state, fileName as ResolvedConfigFileName));
297297
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
298298
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
@@ -1655,7 +1655,7 @@ namespace ts {
16551655
if (!buildInfoVersionMap) buildInfoVersionMap = getBuildInfoFileVersionMap(buildInfoProgram, buildInfoPath!, host);
16561656
version = buildInfoVersionMap.get(toPath(state, inputFile));
16571657
const text = version ? state.readFileWithCache(inputFile) : undefined;
1658-
currentVersion = text && (host.createHash || generateDjb2Hash)(text);
1658+
currentVersion = text !== undefined ? (host.createHash || generateDjb2Hash)(text) : undefined;
16591659
if (version && version === currentVersion) pseudoInputUpToDate = true;
16601660
}
16611661

‎src/compiler/watch.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -595,12 +595,13 @@ namespace ts {
595595
export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCompilerOptions: () => CompilerOptions, directoryStructureHost: DirectoryStructureHost = host): CompilerHost {
596596
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
597597
const hostGetNewLine = memoize(() => host.getNewLine());
598-
return {
598+
const compilerHost: CompilerHost = {
599599
getSourceFile: (fileName, languageVersionOrOptions, onError) => {
600600
let text: string | undefined;
601601
try {
602602
performance.mark("beforeIORead");
603-
text = host.readFile(fileName, getCompilerOptions().charset);
603+
const encoding = getCompilerOptions().charset;
604+
text = !encoding ? compilerHost.readFile(fileName) : host.readFile(fileName, encoding);
604605
performance.mark("afterIORead");
605606
performance.measure("I/O Read", "beforeIORead", "afterIORead");
606607
}
@@ -632,6 +633,7 @@ namespace ts {
632633
disableUseFileVersionAsSignature: host.disableUseFileVersionAsSignature,
633634
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
634635
};
636+
return compilerHost;
635637

636638
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
637639
try {
@@ -659,9 +661,9 @@ namespace ts {
659661
}
660662
}
661663

662-
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) {
664+
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost) {
663665
const originalGetSourceFile = compilerHost.getSourceFile;
664-
const computeHash = maybeBind(host, host.createHash) || generateDjb2Hash;
666+
const computeHash = maybeBind(compilerHost, compilerHost.createHash) || generateDjb2Hash;
665667
compilerHost.getSourceFile = (...args) => {
666668
const result = originalGetSourceFile.call(compilerHost, ...args);
667669
if (result) {

‎src/compiler/watchPublic.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace ts {
2828
host.createHash = maybeBind(system, system.createHash);
2929
host.disableUseFileVersionAsSignature = system.disableUseFileVersionAsSignature;
3030
host.storeFilesChangingSignatureDuringEmit = system.storeFilesChangingSignatureDuringEmit;
31-
setGetSourceFileAsHashVersioned(host, system);
31+
setGetSourceFileAsHashVersioned(host);
3232
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
3333
return host;
3434
}
@@ -330,7 +330,7 @@ namespace ts {
330330
}
331331

332332
const compilerHost = createCompilerHostFromProgramHost(host, () => compilerOptions, directoryStructureHost) as CompilerHost & ResolutionCacheHost;
333-
setGetSourceFileAsHashVersioned(compilerHost, host);
333+
setGetSourceFileAsHashVersioned(compilerHost);
334334
// Members for CompilerHost
335335
const getNewSourceFile = compilerHost.getSourceFile;
336336
compilerHost.getSourceFile = (fileName, ...args) => getVersionedSourceFileByPath(fileName, toPath(fileName), ...args);
@@ -452,9 +452,9 @@ namespace ts {
452452
const hasInvalidatedResolutions = resolutionCache.createHasInvalidatedResolutions(customHasInvalidatedResolutions);
453453
const {
454454
originalReadFile, originalFileExists, originalDirectoryExists,
455-
originalCreateDirectory, originalWriteFile,
455+
originalCreateDirectory, originalWriteFile, readFileWithCache,
456456
} = changeCompilerHostLikeToUseCache(compilerHost, toPath);
457-
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
457+
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, path => getSourceVersion(path, readFileWithCache), fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
458458
if (hasChangedConfigFileParsingErrors) {
459459
if (reportFileChangeDetectedOnCreateProgram) {
460460
reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation);
@@ -609,9 +609,13 @@ namespace ts {
609609
}
610610
}
611611

612-
function getSourceVersion(path: Path): string | undefined {
612+
function getSourceVersion(path: Path, readFileWithCache: (fileName: string) => string | undefined): string | undefined {
613613
const hostSourceFile = sourceFilesCache.get(path);
614-
return !hostSourceFile || !hostSourceFile.version ? undefined : hostSourceFile.version;
614+
if (!hostSourceFile) return undefined;
615+
if (hostSourceFile.version) return hostSourceFile.version;
616+
// Read file and get new version
617+
const text = readFileWithCache(path);
618+
return text !== undefined ? (compilerHost.createHash || generateDjb2Hash)(text) : undefined;
615619
}
616620

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

‎src/harness/virtualFileSystemWithWatch.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ interface Array<T> { length: number; [n: number]: T; }`
730730
}
731731
}
732732

733-
private invokeFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime: Date | undefined, useTildeSuffix: boolean | undefined) {
733+
invokeFsWatches(fullPath: string, eventName: "rename" | "change", modifiedTime: Date | undefined, useTildeSuffix: boolean | undefined) {
734734
this.invokeFsWatchesCallbacks(fullPath, eventName, modifiedTime, fullPath, useTildeSuffix);
735735
this.invokeFsWatchesCallbacks(getDirectoryPath(fullPath), eventName, modifiedTime, fullPath, useTildeSuffix);
736736
this.invokeRecursiveFsWatches(fullPath, eventName, modifiedTime, /*entryFullPath*/ undefined, useTildeSuffix);

‎src/testRunner/unittests/tscWatch/watchEnvironment.ts

+32
Original file line numberDiff line numberDiff line change
@@ -687,5 +687,37 @@ namespace ts.tscWatch {
687687
]
688688
});
689689
});
690+
691+
verifyTscWatch({
692+
scenario,
693+
subScenario: "fsEvent for change is repeated",
694+
commandLineArgs: ["-w", "main.ts", "--extendedDiagnostics"],
695+
sys: () => createWatchedSystem({
696+
"/user/username/projects/project/main.ts": `let a: string = "Hello"`,
697+
[libFile.path]: libFile.content,
698+
}, { currentDirectory: "/user/username/projects/project" }),
699+
changes: [
700+
{
701+
caption: "change main.ts",
702+
change: sys => replaceFileText(sys, "/user/username/projects/project/main.ts", "Hello", "Hello World"),
703+
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
704+
},
705+
{
706+
caption: "receive another change event without modifying the file",
707+
change: sys => sys.invokeFsWatches("/user/username/projects/project/main.ts", "change", /*modifiedTime*/ undefined, /*useTildeSuffix*/ undefined),
708+
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
709+
},
710+
{
711+
caption: "change main.ts to empty text",
712+
change: sys => sys.writeFile("/user/username/projects/project/main.ts", ""),
713+
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
714+
},
715+
{
716+
caption: "receive another change event without modifying the file",
717+
change: sys => sys.invokeFsWatches("/user/username/projects/project/main.ts", "change", /*modifiedTime*/ undefined, /*useTildeSuffix*/ undefined),
718+
timeouts: sys => sys.runQueuedTimeoutCallbacks(),
719+
}
720+
]
721+
});
690722
});
691723
}

‎tests/baselines/reference/tsbuildWatch/noEmit/does-not-go-in-loop-when-watching-when-no-files-are-emitted-with-incremental.js

+6-20
Original file line numberDiff line numberDiff line change
@@ -132,25 +132,11 @@ Output::
132132
>> Screen clear
133133
[12:00:36 AM] File change detected. Starting incremental compilation...
134134

135-
[[90m12:00:37 AM[0m] Project 'tsconfig.json' is out of date because output 'tsconfig.tsbuildinfo' is older than input 'a.js'
135+
[[90m12:00:37 AM[0m] Project 'tsconfig.json' is up to date but needs to update timestamps of output files that are older than input files
136136

137-
[[90m12:00:38 AM[0m] Building project '/user/username/projects/myproject/tsconfig.json'...
137+
[[90m12:00:38 AM[0m] Found 0 errors. Watching for file changes.
138138

139-
[12:00:39 AM] Found 0 errors. Watching for file changes.
140-
141-
142-
143-
Program root files: ["/user/username/projects/myproject/a.js","/user/username/projects/myproject/b.ts"]
144-
Program options: {"allowJs":true,"noEmit":true,"watch":true,"incremental":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
145-
Program structureReused: Not
146-
Program files::
147-
/a/lib/lib.d.ts
148-
/user/username/projects/myproject/a.js
149-
/user/username/projects/myproject/b.ts
150-
151-
Semantic diagnostics in builder refreshed for::
152139

153-
No shapes updated in the builder::
154140

155141
PolledWatches::
156142

@@ -178,13 +164,13 @@ const x = 10;
178164

179165
Output::
180166
>> Screen clear
181-
[[90m12:00:43 AM[0m] File change detected. Starting incremental compilation...
167+
[[90m12:00:42 AM[0m] File change detected. Starting incremental compilation...
182168

183-
[[90m12:00:44 AM[0m] Project 'tsconfig.json' is out of date because output 'tsconfig.tsbuildinfo' is older than input 'a.js'
169+
[[90m12:00:43 AM[0m] Project 'tsconfig.json' is out of date because output 'tsconfig.tsbuildinfo' is older than input 'a.js'
184170

185-
[[90m12:00:45 AM[0m] Building project '/user/username/projects/myproject/tsconfig.json'...
171+
[[90m12:00:44 AM[0m] Building project '/user/username/projects/myproject/tsconfig.json'...
186172

187-
[[90m12:00:53 AM[0m] Found 0 errors. Watching for file changes.
173+
[[90m12:00:52 AM[0m] Found 0 errors. Watching for file changes.
188174

189175

190176

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

+6-73
Original file line numberDiff line numberDiff line change
@@ -179,30 +179,6 @@ Input::
179179
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
180180

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

207183
PolledWatches::
208184
/user/username/projects/noemitonerror/node_modules/@types:
@@ -239,9 +215,9 @@ const a = {
239215

240216
Output::
241217
>> Screen clear
242-
[[90m12:00:48 AM[0m] File change detected. Starting incremental compilation...
218+
[[90m12:00:46 AM[0m] File change detected. Starting incremental compilation...
243219

244-
[[90m12:01:06 AM[0m] Found 0 errors. Watching for file changes.
220+
[[90m12:01:04 AM[0m] Found 0 errors. Watching for file changes.
245221

246222

247223

@@ -370,14 +346,14 @@ const a: string = 10;
370346

371347
Output::
372348
>> Screen clear
373-
[[90m12:01:13 AM[0m] File change detected. Starting incremental compilation...
349+
[[90m12:01:11 AM[0m] File change detected. Starting incremental compilation...
374350

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

377353
2 const a: string = 10;
378354
   ~
379355

380-
[[90m12:01:17 AM[0m] Found 1 error. Watching for file changes.
356+
[[90m12:01:15 AM[0m] Found 1 error. Watching for file changes.
381357

382358

383359

@@ -501,30 +477,6 @@ Input::
501477
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
502478

503479
Output::
504-
>> Screen clear
505-
[12:01:25 AM] File change detected. Starting incremental compilation...
506-
507-
src/main.ts:2:7 - error TS2322: Type 'number' is not assignable to type 'string'.
508-
509-
2 const a: string = 10;
510-
   ~
511-
512-
[12:01:26 AM] Found 1 error. Watching for file changes.
513-
514-
515-
516-
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"]
517-
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"incremental":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
518-
Program structureReused: Completely
519-
Program files::
520-
/a/lib/lib.d.ts
521-
/user/username/projects/noEmitOnError/shared/types/db.ts
522-
/user/username/projects/noEmitOnError/src/main.ts
523-
/user/username/projects/noEmitOnError/src/other.ts
524-
525-
Semantic diagnostics in builder refreshed for::
526-
527-
No shapes updated in the builder::
528480

529481
PolledWatches::
530482
/user/username/projects/noemitonerror/node_modules/@types:
@@ -559,9 +511,9 @@ const a: string = "hello";
559511

560512
Output::
561513
>> Screen clear
562-
[[90m12:01:30 AM[0m] File change detected. Starting incremental compilation...
514+
[[90m12:01:26 AM[0m] File change detected. Starting incremental compilation...
563515

564-
[[90m12:01:37 AM[0m] Found 0 errors. Watching for file changes.
516+
[[90m12:01:33 AM[0m] Found 0 errors. Watching for file changes.
565517

566518

567519

@@ -673,25 +625,6 @@ Input::
673625
//// [/user/username/projects/noEmitOnError/src/main.ts] file written with same contents
674626

675627
Output::
676-
>> Screen clear
677-
[12:01:44 AM] File change detected. Starting incremental compilation...
678-
679-
[12:01:45 AM] Found 0 errors. Watching for file changes.
680-
681-
682-
683-
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"]
684-
Program options: {"outDir":"/user/username/projects/noEmitOnError/dev-build","noEmitOnError":true,"watch":true,"assumeChangesOnlyAffectDirectDependencies":true,"incremental":true,"configFilePath":"/user/username/projects/noEmitOnError/tsconfig.json"}
685-
Program structureReused: Completely
686-
Program files::
687-
/a/lib/lib.d.ts
688-
/user/username/projects/noEmitOnError/shared/types/db.ts
689-
/user/username/projects/noEmitOnError/src/main.ts
690-
/user/username/projects/noEmitOnError/src/other.ts
691-
692-
Semantic diagnostics in builder refreshed for::
693-
694-
No shapes updated in the builder::
695628

696629
PolledWatches::
697630
/user/username/projects/noemitonerror/node_modules/@types:

0 commit comments

Comments
 (0)
Please sign in to comment.