Skip to content

Commit 6e0c916

Browse files
committed
Use modified time passed through the file watching in tsbuild
1 parent 89d2d4c commit 6e0c916

File tree

34 files changed

+97
-221
lines changed

34 files changed

+97
-221
lines changed

src/compiler/tsbuildPublic.ts

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ namespace ts {
6464
return existingValue || newValue!;
6565
}
6666

67-
function getOrCreateValueMapFromConfigFileMap<T>(configFileMap: ESMap<ResolvedConfigFilePath, ESMap<string, T>>, resolved: ResolvedConfigFilePath): ESMap<string, T> {
68-
return getOrCreateValueFromConfigFileMap<ESMap<string, T>>(configFileMap, resolved, () => new Map());
67+
function getOrCreateValueMapFromConfigFileMap<K extends string, V>(configFileMap: ESMap<ResolvedConfigFilePath, ESMap<K, V>>, resolved: ResolvedConfigFilePath): ESMap<K, V> {
68+
return getOrCreateValueFromConfigFileMap(configFileMap, resolved, () => new Map());
6969
}
7070

7171
function newer(date1: Date | undefined, date2: Date): Date | undefined {
@@ -212,6 +212,12 @@ namespace ts {
212212
originalGetSourceFile: CompilerHost["getSourceFile"];
213213
}
214214

215+
interface FileWatcherWithModifiedTime {
216+
callbacks: FileWatcherCallback[];
217+
watcher: FileWatcher;
218+
modifiedTime: Date | undefined;
219+
}
220+
215221
interface SolutionBuilderState<T extends BuilderProgram = BuilderProgram> extends WatchFactory<WatchType, ResolvedConfigFileName> {
216222
readonly host: SolutionBuilderHost<T>;
217223
readonly hostWithWatch: SolutionBuilderWithWatchHost<T>;
@@ -259,6 +265,8 @@ namespace ts {
259265
readonly allWatchedConfigFiles: ESMap<ResolvedConfigFilePath, FileWatcher>;
260266
readonly allWatchedExtendedConfigFiles: ESMap<Path, SharedExtendedConfigFileWatcher<ResolvedConfigFilePath>>;
261267
readonly allWatchedPackageJsonFiles: ESMap<ResolvedConfigFilePath, ESMap<Path, FileWatcher>>;
268+
readonly filesWatched: ESMap<Path, FileWatcherWithModifiedTime | Date>;
269+
262270
readonly lastCachedPackageJsonLookups: ESMap<ResolvedConfigFilePath, readonly (readonly [Path, object | boolean])[] | undefined>;
263271

264272
timerToBuildInvalidatedProject: any;
@@ -341,6 +349,8 @@ namespace ts {
341349
allWatchedConfigFiles: new Map(),
342350
allWatchedExtendedConfigFiles: new Map(),
343351
allWatchedPackageJsonFiles: new Map(),
352+
filesWatched: new Map(),
353+
344354
lastCachedPackageJsonLookups: new Map(),
345355

346356
timerToBuildInvalidatedProject: undefined,
@@ -969,7 +979,7 @@ namespace ts {
969979
if (resultFlags === BuildResultFlags.DeclarationOutputUnchanged && isDeclarationFileName(name)) {
970980
// Check for unchanged .d.ts files
971981
if (state.readFileWithCache(name) === text) {
972-
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, getModifiedTime(host, name));
982+
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, ts.getModifiedTime(host, name));
973983
}
974984
else {
975985
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
@@ -1323,9 +1333,66 @@ namespace ts {
13231333
return { buildResult, step: BuildStep.QueueReferencingProjects };
13241334
}
13251335

1336+
function isFileWatcherWithModifiedTime(value: FileWatcherWithModifiedTime | Date): value is FileWatcherWithModifiedTime {
1337+
return !!(value as FileWatcherWithModifiedTime).watcher;
1338+
}
1339+
1340+
function getModifiedTime(state: SolutionBuilderState, fileName: string): Date {
1341+
const path = toPath(state, fileName);
1342+
const existing = state.filesWatched.get(path);
1343+
if (state.watch && !!existing) {
1344+
if (!isFileWatcherWithModifiedTime(existing)) return existing;
1345+
if (existing.modifiedTime) return existing.modifiedTime;
1346+
}
1347+
const result = ts.getModifiedTime(state.host, fileName);
1348+
if (state.watch) {
1349+
if (existing) (existing as FileWatcherWithModifiedTime).modifiedTime = result;
1350+
else state.filesWatched.set(path, result);
1351+
}
1352+
return result;
1353+
}
1354+
1355+
function watchFile(state: SolutionBuilderState, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, watchType: WatchType, project?: ResolvedConfigFileName): FileWatcher {
1356+
const path = toPath(state, file);
1357+
const existing = state.filesWatched.get(path);
1358+
if (existing && isFileWatcherWithModifiedTime(existing)) {
1359+
existing.callbacks.push(callback);
1360+
}
1361+
else {
1362+
const watcher = state.watchFile(
1363+
file,
1364+
(fileName, eventKind, modifiedTime) => {
1365+
const existing = Debug.checkDefined(state.filesWatched.get(path));
1366+
Debug.assert(isFileWatcherWithModifiedTime(existing));
1367+
existing.modifiedTime = modifiedTime;
1368+
existing.callbacks.forEach(cb => cb(fileName, eventKind, modifiedTime));
1369+
},
1370+
pollingInterval,
1371+
options,
1372+
watchType,
1373+
project
1374+
);
1375+
state.filesWatched.set(path, { callbacks: [callback], watcher, modifiedTime: existing });
1376+
}
1377+
1378+
return {
1379+
close: () => {
1380+
const existing = Debug.checkDefined(state.filesWatched.get(path));
1381+
Debug.assert(isFileWatcherWithModifiedTime(existing));
1382+
if (existing.callbacks.length === 1) {
1383+
state.filesWatched.delete(path);
1384+
closeFileWatcherOf(existing);
1385+
}
1386+
else {
1387+
unorderedRemoveItem(existing.callbacks, callback);
1388+
}
1389+
}
1390+
};
1391+
}
1392+
13261393
function checkConfigFileUpToDateStatus(state: SolutionBuilderState, configFile: string, oldestOutputFileTime: Date, oldestOutputFileName: string): Status.OutOfDateWithSelf | undefined {
13271394
// Check tsconfig time
1328-
const tsconfigTime = getModifiedTime(state.host, configFile);
1395+
const tsconfigTime = getModifiedTime(state, configFile);
13291396
if (oldestOutputFileTime < tsconfigTime) {
13301397
return {
13311398
type: UpToDateStatusType.OutOfDateWithSelf,
@@ -1387,7 +1454,7 @@ namespace ts {
13871454
const { host } = state;
13881455
// Get timestamps of input files
13891456
for (const inputFile of project.fileNames) {
1390-
const inputTime = getModifiedTime(host, inputFile);
1457+
const inputTime = getModifiedTime(state, inputFile);
13911458
if (inputTime === missingFileModifiedTime) {
13921459
return {
13931460
type: UpToDateStatusType.Unbuildable,
@@ -1413,7 +1480,7 @@ namespace ts {
14131480
if (!force) {
14141481
for (const output of outputs) {
14151482
// Output is missing; can stop checking
1416-
const outputTime = getModifiedTime(host, output);
1483+
const outputTime = ts.getModifiedTime(host, output);
14171484
if (outputTime === missingFileModifiedTime) {
14181485
return {
14191486
type: UpToDateStatusType.OutputMissing,
@@ -1556,7 +1623,7 @@ namespace ts {
15561623
}
15571624

15581625
if (!anyDtsChange && isDeclarationFileName(file)) {
1559-
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, getModifiedTime(host, file));
1626+
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, ts.getModifiedTime(host, file));
15601627
}
15611628

15621629
host.setModifiedTime(file, now);
@@ -1778,11 +1845,10 @@ namespace ts {
17781845

17791846
function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) {
17801847
if (!state.watch || state.allWatchedConfigFiles.has(resolvedPath)) return;
1781-
state.allWatchedConfigFiles.set(resolvedPath, state.watchFile(
1848+
state.allWatchedConfigFiles.set(resolvedPath, watchFile(
1849+
state,
17821850
resolved,
1783-
() => {
1784-
invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Full);
1785-
},
1851+
() => invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Full),
17861852
PollingInterval.High,
17871853
parsed?.watchOptions,
17881854
WatchType.ConfigFile,
@@ -1795,11 +1861,11 @@ namespace ts {
17951861
resolvedPath,
17961862
parsed?.options,
17971863
state.allWatchedExtendedConfigFiles,
1798-
(extendedConfigFileName, extendedConfigFilePath) => state.watchFile(
1864+
(extendedConfigFileName, extendedConfigFilePath) => watchFile(
1865+
state,
17991866
extendedConfigFileName,
18001867
() => state.allWatchedExtendedConfigFiles.get(extendedConfigFilePath)?.projects.forEach(projectConfigFilePath =>
1801-
invalidateProjectAndScheduleBuilds(state, projectConfigFilePath, ConfigFileProgramReloadLevel.Full)
1802-
),
1868+
invalidateProjectAndScheduleBuilds(state, projectConfigFilePath, ConfigFileProgramReloadLevel.Full)),
18031869
PollingInterval.High,
18041870
parsed?.watchOptions,
18051871
WatchType.ExtendedConfigFile,
@@ -1845,7 +1911,8 @@ namespace ts {
18451911
getOrCreateValueMapFromConfigFileMap(state.allWatchedInputFiles, resolvedPath),
18461912
arrayToMap(parsed.fileNames, fileName => toPath(state, fileName)),
18471913
{
1848-
createNewValue: (_path, input) => state.watchFile(
1914+
createNewValue: (_path, input) => watchFile(
1915+
state,
18491916
input,
18501917
() => invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.None),
18511918
PollingInterval.Low,
@@ -1864,7 +1931,8 @@ namespace ts {
18641931
getOrCreateValueMapFromConfigFileMap(state.allWatchedPackageJsonFiles, resolvedPath),
18651932
new Map(state.lastCachedPackageJsonLookups.get(resolvedPath)),
18661933
{
1867-
createNewValue: (path, _input) => state.watchFile(
1934+
createNewValue: (path, _input) => watchFile(
1935+
state,
18681936
path,
18691937
() => invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Full),
18701938
PollingInterval.High,

src/testRunner/unittests/tsbuildWatch/watchEnvironment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace ts.tscWatch {
2828
const host = createSolutionBuilderWithWatchHostForBaseline(sys, cb);
2929
const solutionBuilder = createSolutionBuilderWithWatch(host, ["tsconfig.json"], { watch: true, verbose: true });
3030
solutionBuilder.build();
31+
// TODO: (shkamat): Need different test where we check single watch per file
3132
runWatchBaseline({
3233
scenario: "watchEnvironment",
3334
subScenario: `same file in multiple projects${singleWatchPerFile ? " with single watcher per file" : ""}`,

tests/baselines/reference/tsbuildWatch/configFileErrors/reports-syntax-errors-in-config-file.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,6 @@ directoryExists:: {
172172
}
173173

174174
getModifiedTimes:: {
175-
"/user/username/projects/myproject/a.ts": 1,
176-
"/user/username/projects/myproject/b.ts": 1,
177175
"/user/username/projects/myproject/a.js": 1
178176
}
179177

@@ -245,8 +243,6 @@ directoryExists:: {
245243
}
246244

247245
getModifiedTimes:: {
248-
"/user/username/projects/myproject/a.ts": 1,
249-
"/user/username/projects/myproject/b.ts": 1,
250246
"/user/username/projects/myproject/a.js": 1
251247
}
252248

@@ -316,8 +312,6 @@ directoryExists:: {
316312
}
317313

318314
getModifiedTimes:: {
319-
"/user/username/projects/myproject/a.ts": 1,
320-
"/user/username/projects/myproject/b.ts": 1,
321315
"/user/username/projects/myproject/a.js": 1
322316
}
323317

@@ -456,8 +450,6 @@ directoryExists:: {
456450
}
457451

458452
getModifiedTimes:: {
459-
"/user/username/projects/myproject/a.ts": 1,
460-
"/user/username/projects/myproject/b.ts": 1,
461453
"/user/username/projects/myproject/a.js": 1
462454
}
463455

tests/baselines/reference/tsbuildWatch/demo/updates-with-bad-reference.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,6 @@ directoryExists:: {
752752
}
753753

754754
getModifiedTimes:: {
755-
"/user/username/projects/demo/core/utilities.ts": 1,
756755
"/user/username/projects/demo/lib/core/utilities.js": 1
757756
}
758757

tests/baselines/reference/tsbuildWatch/moduleResolution/build-mode-watches-for-changes-to-package-json-main-fields.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,6 @@ directoryExists:: {
465465
}
466466

467467
getModifiedTimes:: {
468-
"/user/username/projects/myproject/packages/pkg1/index.ts": 1,
469468
"/user/username/projects/myproject/packages/pkg1/build/index.js": 1
470469
}
471470

@@ -620,7 +619,6 @@ directoryExists:: {
620619
}
621620

622621
getModifiedTimes:: {
623-
"/user/username/projects/myproject/packages/pkg1/index.ts": 1,
624622
"/user/username/projects/myproject/packages/pkg1/build/index.js": 1
625623
}
626624

tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,12 @@ WatchedFiles::
159159
{"fileName":"/user/username/projects/myproject/packages/pkg2/index.ts","pollingInterval":250}
160160
/user/username/projects/myproject/packages/pkg2/package.json:
161161
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
162-
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
163162
/a/lib/package.json:
164163
{"fileName":"/a/lib/package.json","pollingInterval":250}
165-
{"fileName":"/a/lib/package.json","pollingInterval":250}
166164
/a/package.json:
167165
{"fileName":"/a/package.json","pollingInterval":250}
168-
{"fileName":"/a/package.json","pollingInterval":250}
169166
/package.json:
170167
{"fileName":"/package.json","pollingInterval":250}
171-
{"fileName":"/package.json","pollingInterval":250}
172168
/user/username/projects/myproject/packages/pkg1/tsconfig.json:
173169
{"fileName":"/user/username/projects/myproject/packages/pkg1/tsconfig.json","pollingInterval":250}
174170
/user/username/projects/myproject/packages/pkg1/index.ts:
@@ -418,16 +414,12 @@ WatchedFiles::
418414
{"fileName":"/user/username/projects/myproject/packages/pkg2/index.ts","pollingInterval":250}
419415
/user/username/projects/myproject/packages/pkg2/package.json:
420416
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
421-
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
422417
/a/lib/package.json:
423418
{"fileName":"/a/lib/package.json","pollingInterval":250}
424-
{"fileName":"/a/lib/package.json","pollingInterval":250}
425419
/a/package.json:
426420
{"fileName":"/a/package.json","pollingInterval":250}
427-
{"fileName":"/a/package.json","pollingInterval":250}
428421
/package.json:
429422
{"fileName":"/package.json","pollingInterval":250}
430-
{"fileName":"/package.json","pollingInterval":250}
431423
/user/username/projects/myproject/packages/pkg1/tsconfig.json:
432424
{"fileName":"/user/username/projects/myproject/packages/pkg1/tsconfig.json","pollingInterval":250}
433425
/user/username/projects/myproject/packages/pkg1/index.ts:
@@ -493,7 +485,6 @@ directoryExists:: {
493485
}
494486

495487
getModifiedTimes:: {
496-
"/user/username/projects/myproject/packages/pkg1/index.ts": 1,
497488
"/user/username/projects/myproject/packages/pkg1/build/index.js": 1
498489
}
499490

@@ -586,16 +577,12 @@ WatchedFiles::
586577
{"fileName":"/user/username/projects/myproject/packages/pkg2/index.ts","pollingInterval":250}
587578
/user/username/projects/myproject/packages/pkg2/package.json:
588579
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
589-
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
590580
/a/lib/package.json:
591581
{"fileName":"/a/lib/package.json","pollingInterval":250}
592-
{"fileName":"/a/lib/package.json","pollingInterval":250}
593582
/a/package.json:
594583
{"fileName":"/a/package.json","pollingInterval":250}
595-
{"fileName":"/a/package.json","pollingInterval":250}
596584
/package.json:
597585
{"fileName":"/package.json","pollingInterval":250}
598-
{"fileName":"/package.json","pollingInterval":250}
599586
/user/username/projects/myproject/packages/pkg1/tsconfig.json:
600587
{"fileName":"/user/username/projects/myproject/packages/pkg1/tsconfig.json","pollingInterval":250}
601588
/user/username/projects/myproject/packages/pkg1/index.ts:
@@ -656,7 +643,6 @@ directoryExists:: {
656643
}
657644

658645
getModifiedTimes:: {
659-
"/user/username/projects/myproject/packages/pkg1/index.ts": 1,
660646
"/user/username/projects/myproject/packages/pkg1/build/index.js": 1
661647
}
662648

@@ -760,16 +746,12 @@ WatchedFiles::
760746
{"fileName":"/user/username/projects/myproject/packages/pkg2/index.ts","pollingInterval":250}
761747
/user/username/projects/myproject/packages/pkg2/package.json:
762748
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
763-
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
764749
/a/lib/package.json:
765750
{"fileName":"/a/lib/package.json","pollingInterval":250}
766-
{"fileName":"/a/lib/package.json","pollingInterval":250}
767751
/a/package.json:
768752
{"fileName":"/a/package.json","pollingInterval":250}
769-
{"fileName":"/a/package.json","pollingInterval":250}
770753
/package.json:
771754
{"fileName":"/package.json","pollingInterval":250}
772-
{"fileName":"/package.json","pollingInterval":250}
773755
/user/username/projects/myproject/packages/pkg1/tsconfig.json:
774756
{"fileName":"/user/username/projects/myproject/packages/pkg1/tsconfig.json","pollingInterval":250}
775757
/user/username/projects/myproject/packages/pkg1/index.ts:
@@ -835,7 +817,6 @@ directoryExists:: {
835817
}
836818

837819
getModifiedTimes:: {
838-
"/user/username/projects/myproject/packages/pkg1/index.ts": 1,
839820
"/user/username/projects/myproject/packages/pkg1/build/index.js": 1
840821
}
841822

@@ -905,16 +886,12 @@ WatchedFiles::
905886
{"fileName":"/user/username/projects/myproject/packages/pkg2/const.cts","pollingInterval":250}
906887
/user/username/projects/myproject/packages/pkg2/package.json:
907888
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
908-
{"fileName":"/user/username/projects/myproject/packages/pkg2/package.json","pollingInterval":250}
909889
/a/lib/package.json:
910890
{"fileName":"/a/lib/package.json","pollingInterval":250}
911-
{"fileName":"/a/lib/package.json","pollingInterval":250}
912891
/a/package.json:
913892
{"fileName":"/a/package.json","pollingInterval":250}
914-
{"fileName":"/a/package.json","pollingInterval":250}
915893
/package.json:
916894
{"fileName":"/package.json","pollingInterval":250}
917-
{"fileName":"/package.json","pollingInterval":250}
918895
/user/username/projects/myproject/packages/pkg1/tsconfig.json:
919896
{"fileName":"/user/username/projects/myproject/packages/pkg1/tsconfig.json","pollingInterval":250}
920897
/user/username/projects/myproject/packages/pkg1/index.ts:
@@ -1032,7 +1009,6 @@ directoryExists:: {
10321009
}
10331010

10341011
getModifiedTimes:: {
1035-
"/user/username/projects/myproject/packages/pkg2/const.cts": 1,
10361012
"/user/username/projects/myproject/packages/pkg2/index.cts": 1,
10371013
"/user/username/projects/myproject/packages/pkg2/build/const.cjs": 1
10381014
}

0 commit comments

Comments
 (0)