Skip to content

Commit d514dab

Browse files
authored
Fix the incorrect copy over for watchOptions and fix order of watches for referenced projects (#59871)
1 parent 87d0e77 commit d514dab

9 files changed

+275
-27
lines changed

src/compiler/commandLineParser.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3267,6 +3267,7 @@ function isSuccessfulParsedTsconfig(value: ParsedTsconfig) {
32673267
interface ExtendsResult {
32683268
options: CompilerOptions;
32693269
watchOptions?: WatchOptions;
3270+
watchOptionsCopied?: boolean;
32703271
include?: string[];
32713272
exclude?: string[];
32723273
files?: string[];
@@ -3325,7 +3326,7 @@ function parseConfig(
33253326

33263327
ownConfig.options = assign(result.options, ownConfig.options);
33273328
ownConfig.watchOptions = ownConfig.watchOptions && result.watchOptions ?
3328-
assign(result.watchOptions, ownConfig.watchOptions) :
3329+
assignWatchOptions(result, ownConfig.watchOptions) :
33293330
ownConfig.watchOptions || result.watchOptions;
33303331
}
33313332
return ownConfig;
@@ -3355,11 +3356,17 @@ function parseConfig(
33553356
}
33563357
assign(result.options, extendedConfig.options);
33573358
result.watchOptions = result.watchOptions && extendedConfig.watchOptions ?
3358-
assign({}, result.watchOptions, extendedConfig.watchOptions) :
3359+
assignWatchOptions(result, extendedConfig.watchOptions) :
33593360
result.watchOptions || extendedConfig.watchOptions;
33603361
// TODO extend type typeAcquisition
33613362
}
33623363
}
3364+
3365+
function assignWatchOptions(result: ExtendsResult, watchOptions: WatchOptions) {
3366+
if (result.watchOptionsCopied) return assign(result.watchOptions!, watchOptions);
3367+
result.watchOptionsCopied = true;
3368+
return assign({}, result.watchOptions, watchOptions);
3369+
}
33633370
}
33643371

33653372
function parseOwnConfigOfJson(

src/compiler/watchPublic.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,11 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
435435
let updateLevel: ProgramUpdateLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc
436436
let missingFilesMap: Map<Path, FileWatcher>; // Map of file watchers for the missing files
437437
let watchedWildcardDirectories: Map<string, WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
438+
/**
439+
* undefined - own watches are stale,
440+
* path - for referenced project which need to be watched
441+
*/
442+
let staleWatches: Map<Path | undefined, string | undefined> | undefined = new Map([[undefined, undefined]]);
438443
let timerToUpdateProgram: any; // timer callback to recompile the program
439444
let timerToInvalidateFailedLookupResolutions: any; // timer callback to invalidate resolutions for changes in failed lookup locations
440445
let parsedConfigs: Map<Path, ParsedConfig> | undefined; // Parsed commandline and watching cached for referenced projects
@@ -550,12 +555,6 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
550555
builderProgram = readBuilderProgram(compilerOptions, compilerHost) as any as T;
551556
synchronizeProgram();
552557

553-
// Update the wild card directory watch
554-
watchConfigFileWildCardDirectories();
555-
556-
// Update extended config file watch
557-
if (configFileName) updateExtendedConfigFilesWatches(toPath(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile);
558-
559558
return configFileName ?
560559
{ getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, close, getResolutionCache } :
561560
{ getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, updateRootFileNames, close, getResolutionCache };
@@ -663,6 +662,20 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
663662
compilerHost.createDirectory = originalCreateDirectory;
664663
compilerHost.writeFile = originalWriteFile!;
665664

665+
staleWatches?.forEach((configFile, configPath) => {
666+
if (!configPath) {
667+
// Update the wild card directory watch
668+
watchConfigFileWildCardDirectories();
669+
670+
// Update extended config file watch
671+
if (configFileName) updateExtendedConfigFilesWatches(toPath(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile);
672+
}
673+
else {
674+
const config = parsedConfigs?.get(configPath);
675+
if (config) watchReferencedProject(configFile!, configPath, config);
676+
}
677+
});
678+
staleWatches = undefined;
666679
return builderProgram;
667680
}
668681

@@ -930,13 +943,8 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
930943
}
931944
parseConfigFile();
932945
hasChangedCompilerOptions = true;
946+
(staleWatches ??= new Map()).set(undefined, undefined);
933947
synchronizeProgram();
934-
935-
// Update the wild card directory watch
936-
watchConfigFileWildCardDirectories();
937-
938-
// Update extended config file watch
939-
updateExtendedConfigFilesWatches(toPath(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile);
940948
}
941949

942950
function parseConfigFile() {
@@ -996,7 +1004,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler
9961004
else {
9971005
(parsedConfigs ||= new Map()).set(configPath, config = { parsedCommandLine });
9981006
}
999-
watchReferencedProject(configFileName, configPath, config);
1007+
(staleWatches ??= new Map()).set(configPath, configFileName);
10001008
return parsedCommandLine;
10011009
}
10021010

src/testRunner/unittests/tscWatch/projectsWithReferences.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,41 @@ X;`,
405405
],
406406
baselineDependencies: true,
407407
});
408+
409+
verifyTscWatch({
410+
scenario: "projectsWithReferences",
411+
subScenario: "watch options differing between projects",
412+
sys: () =>
413+
solutionBuildWithBaseline(
414+
TestServerHost.createWatchedSystem({
415+
"/user/username/workspace/project/tsconfig.base.json": jsonToReadableText({
416+
watchOptions: {
417+
excludeDirectories: ["**/node_modules"],
418+
},
419+
}),
420+
"/user/username/workspace/project/tsconfig.A.json": jsonToReadableText({
421+
extends: "./tsconfig.base.json",
422+
compilerOptions: { composite: true },
423+
include: ["src/a/**/*.ts"],
424+
watchOptions: {
425+
excludeDirectories: ["**/excludes_by_A"],
426+
},
427+
}),
428+
"/user/username/workspace/project/src/a/a.ts": "export const a = 10;",
429+
"/user/username/workspace/project/tsconfig.B.json": jsonToReadableText({
430+
extends: "./tsconfig.base.json",
431+
include: ["src/b/**/*.ts"],
432+
references: [
433+
{ path: "./tsconfig.A.json" },
434+
],
435+
}),
436+
"/user/username/workspace/project/src/b/b.ts": "export const b = 10;",
437+
}, {
438+
currentDirectory: "/user/username/workspace/project",
439+
useCaseSensitiveFileNames: false,
440+
}),
441+
["tsconfig.A.json"],
442+
),
443+
commandLineArgs: ["-w", "-p", "tsconfig.B.json", "--traceResolution", "--extendedDiagnostics"],
444+
});
408445
});

tests/baselines/reference/tscWatch/programUpdates/when-new-file-is-added-to-the-referenced-project.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ CreatingProgramWith::
6161
options: {"module":0,"composite":true,"watch":true,"project":"/user/username/projects/myproject/projects/project2/tsconfig.json","extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/projects/project2/tsconfig.json"}
6262
projectReferences: [{"path":"/user/username/projects/myproject/projects/project1","originalPath":"../project1"}]
6363
Loading config file: /user/username/projects/myproject/projects/project1/tsconfig.json
64-
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project1/tsconfig.json 2000 undefined Config file of referened project
65-
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project1 1 undefined Wild card directory of referenced project
66-
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project1 1 undefined Wild card directory of referenced project
6764
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project1/class1.d.ts 250 undefined Source file
6865
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project2/class2.ts 250 undefined Source file
6966
FileWatcher:: Added:: WatchInfo: /home/src/tslibs/TS/Lib/lib.d.ts 250 undefined Source file
@@ -79,6 +76,9 @@ Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/node
7976

8077
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project2 1 undefined Wild card directory
8178
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project2 1 undefined Wild card directory
79+
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project1/tsconfig.json 2000 undefined Config file of referened project
80+
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project1 1 undefined Wild card directory of referenced project
81+
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/projects/project1 1 undefined Wild card directory of referenced project
8282

8383

8484
//// [/user/username/projects/myproject/projects/project2/class2.js]
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
currentDirectory:: /user/username/workspace/project useCaseSensitiveFileNames:: false
2+
Input::
3+
//// [/user/username/workspace/project/tsconfig.base.json]
4+
{
5+
"watchOptions": {
6+
"excludeDirectories": [
7+
"**/node_modules"
8+
]
9+
}
10+
}
11+
12+
//// [/user/username/workspace/project/tsconfig.A.json]
13+
{
14+
"extends": "./tsconfig.base.json",
15+
"compilerOptions": {
16+
"composite": true
17+
},
18+
"include": [
19+
"src/a/**/*.ts"
20+
],
21+
"watchOptions": {
22+
"excludeDirectories": [
23+
"**/excludes_by_A"
24+
]
25+
}
26+
}
27+
28+
//// [/user/username/workspace/project/src/a/a.ts]
29+
export const a = 10;
30+
31+
//// [/user/username/workspace/project/tsconfig.B.json]
32+
{
33+
"extends": "./tsconfig.base.json",
34+
"include": [
35+
"src/b/**/*.ts"
36+
],
37+
"references": [
38+
{
39+
"path": "./tsconfig.A.json"
40+
}
41+
]
42+
}
43+
44+
//// [/user/username/workspace/project/src/b/b.ts]
45+
export const b = 10;
46+
47+
//// [/home/src/tslibs/TS/Lib/lib.d.ts]
48+
/// <reference no-default-lib="true"/>
49+
interface Boolean {}
50+
interface Function {}
51+
interface CallableFunction {}
52+
interface NewableFunction {}
53+
interface IArguments {}
54+
interface Number { toExponential: any; }
55+
interface Object {}
56+
interface RegExp {}
57+
interface String { charAt: any; }
58+
interface Array<T> { length: number; [n: number]: T; }
59+
interface ReadonlyArray<T> {}
60+
declare const console: { log(msg: any): void; };
61+
62+
//// [/user/username/workspace/project/src/a/a.js]
63+
"use strict";
64+
Object.defineProperty(exports, "__esModule", { value: true });
65+
exports.a = void 0;
66+
exports.a = 10;
67+
68+
69+
//// [/user/username/workspace/project/src/a/a.d.ts]
70+
export declare const a = 10;
71+
72+
73+
//// [/user/username/workspace/project/tsconfig.A.tsbuildinfo]
74+
{"fileNames":["../../../../home/src/tslibs/ts/lib/lib.d.ts","./src/a/a.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"-14660415448-export const a = 10;","signature":"-3497920574-export declare const a = 10;\n"}],"root":[2],"options":{"composite":true},"latestChangedDtsFile":"./src/a/a.d.ts","version":"FakeTSVersion"}
75+
76+
//// [/user/username/workspace/project/tsconfig.A.tsbuildinfo.readable.baseline.txt]
77+
{
78+
"fileNames": [
79+
"../../../../home/src/tslibs/ts/lib/lib.d.ts",
80+
"./src/a/a.ts"
81+
],
82+
"fileInfos": {
83+
"../../../../home/src/tslibs/ts/lib/lib.d.ts": {
84+
"original": {
85+
"version": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
86+
"affectsGlobalScope": true
87+
},
88+
"version": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
89+
"signature": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
90+
"affectsGlobalScope": true
91+
},
92+
"./src/a/a.ts": {
93+
"original": {
94+
"version": "-14660415448-export const a = 10;",
95+
"signature": "-3497920574-export declare const a = 10;\n"
96+
},
97+
"version": "-14660415448-export const a = 10;",
98+
"signature": "-3497920574-export declare const a = 10;\n"
99+
}
100+
},
101+
"root": [
102+
[
103+
2,
104+
"./src/a/a.ts"
105+
]
106+
],
107+
"options": {
108+
"composite": true
109+
},
110+
"latestChangedDtsFile": "./src/a/a.d.ts",
111+
"version": "FakeTSVersion",
112+
"size": 780
113+
}
114+
115+
116+
/home/src/tslibs/TS/Lib/tsc.js -w -p tsconfig.B.json --traceResolution --extendedDiagnostics
117+
Output::
118+
[HH:MM:SS AM] Starting compilation in watch mode...
119+
120+
Current directory: /user/username/workspace/project CaseSensitiveFileNames: false
121+
FileWatcher:: Added:: WatchInfo: /user/username/workspace/project/tsconfig.B.json 2000 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Config file
122+
Synchronizing program
123+
CreatingProgramWith::
124+
roots: ["/user/username/workspace/project/src/b/b.ts"]
125+
options: {"watch":true,"project":"/user/username/workspace/project/tsconfig.B.json","traceResolution":true,"extendedDiagnostics":true,"configFilePath":"/user/username/workspace/project/tsconfig.B.json"}
126+
projectReferences: [{"path":"/user/username/workspace/project/tsconfig.A.json","originalPath":"./tsconfig.A.json"}]
127+
Loading config file: /user/username/workspace/project/tsconfig.A.json
128+
FileWatcher:: Added:: WatchInfo: /user/username/workspace/project/src/b/b.ts 250 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Source file
129+
FileWatcher:: Added:: WatchInfo: /home/src/tslibs/TS/Lib/lib.d.ts 250 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Source file
130+
ExcludeWatcher:: Added:: WatchInfo: /user/username/workspace/project/node_modules/@types 1 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Type roots
131+
DirectoryWatcher:: Added:: WatchInfo: /user/username/workspace/node_modules/@types 1 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Type roots
132+
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/workspace/node_modules/@types 1 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Type roots
133+
[HH:MM:SS AM] Found 0 errors. Watching for file changes.
134+
135+
DirectoryWatcher:: Added:: WatchInfo: /user/username/workspace/project/src/b 1 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Wild card directory
136+
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/workspace/project/src/b 1 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Wild card directory
137+
FileWatcher:: Added:: WatchInfo: /user/username/workspace/project/tsconfig.base.json 2000 {"excludeDirectories":["/user/username/workspace/project/**/node_modules"]} Extended config file
138+
FileWatcher:: Added:: WatchInfo: /user/username/workspace/project/tsconfig.A.json 2000 {"excludeDirectories":["/user/username/workspace/project/**/excludes_by_A"]} Config file of referened project
139+
DirectoryWatcher:: Added:: WatchInfo: /user/username/workspace/project/src/a 1 {"excludeDirectories":["/user/username/workspace/project/**/excludes_by_A"]} Wild card directory of referenced project
140+
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/workspace/project/src/a 1 {"excludeDirectories":["/user/username/workspace/project/**/excludes_by_A"]} Wild card directory of referenced project
141+
142+
143+
//// [/user/username/workspace/project/src/b/b.js]
144+
"use strict";
145+
Object.defineProperty(exports, "__esModule", { value: true });
146+
exports.b = void 0;
147+
exports.b = 10;
148+
149+
150+
151+
PolledWatches::
152+
/user/username/workspace/node_modules/@types: *new*
153+
{"pollingInterval":500}
154+
155+
FsWatches::
156+
/home/src/tslibs/TS/Lib/lib.d.ts: *new*
157+
{}
158+
/user/username/workspace/project/src/b/b.ts: *new*
159+
{}
160+
/user/username/workspace/project/tsconfig.A.json: *new*
161+
{}
162+
/user/username/workspace/project/tsconfig.B.json: *new*
163+
{}
164+
/user/username/workspace/project/tsconfig.base.json: *new*
165+
{}
166+
167+
FsWatchesRecursive::
168+
/user/username/workspace/project/src/a: *new*
169+
{}
170+
/user/username/workspace/project/src/b: *new*
171+
{}
172+
173+
Program root files: [
174+
"/user/username/workspace/project/src/b/b.ts"
175+
]
176+
Program options: {
177+
"watch": true,
178+
"project": "/user/username/workspace/project/tsconfig.B.json",
179+
"traceResolution": true,
180+
"extendedDiagnostics": true,
181+
"configFilePath": "/user/username/workspace/project/tsconfig.B.json"
182+
}
183+
Program structureReused: Not
184+
Program files::
185+
/home/src/tslibs/TS/Lib/lib.d.ts
186+
/user/username/workspace/project/src/b/b.ts
187+
188+
Semantic diagnostics in builder refreshed for::
189+
/home/src/tslibs/TS/Lib/lib.d.ts
190+
/user/username/workspace/project/src/b/b.ts
191+
192+
Shape signatures in builder refreshed for::
193+
/home/src/tslibs/ts/lib/lib.d.ts (used version)
194+
/user/username/workspace/project/src/b/b.ts (used version)
195+
196+
exitCode:: ExitStatus.undefined

0 commit comments

Comments
 (0)