Skip to content

Commit 236012e

Browse files
authored
Add watchOptions to tsconfig and allow supplying them on command line as well (#35615)
* Create different watch options in compiler options * Thread through the new watch options * Actually use the options passed through for watch strategy * Support delay on updating child directory watches * Make watchOptions separate from compilerOptions * Support passing watch options from command line * Handle displaying of watchOptions
1 parent 4212484 commit 236012e

40 files changed

+2416
-719
lines changed

src/compiler/builder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ namespace ts {
754754

755755
function convertToReusableCompilerOptions(options: CompilerOptions, relativeToBuildInfo: (path: string) => string) {
756756
const result: CompilerOptions = {};
757-
const optionsNameMap = getOptionNameMap().optionNameMap;
757+
const { optionsNameMap } = getOptionsNameMap();
758758

759759
for (const name in options) {
760760
if (hasProperty(options, name)) {
@@ -1194,4 +1194,4 @@ namespace ts {
11941194
return Debug.assertDefined(state.program);
11951195
}
11961196
}
1197-
}
1197+
}

src/compiler/commandLineParser.ts

Lines changed: 305 additions & 152 deletions
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3313,6 +3313,18 @@
33133313
"category": "Error",
33143314
"code": 5077
33153315
},
3316+
"Unknown watch option '{0}'.": {
3317+
"category": "Error",
3318+
"code": 5078
3319+
},
3320+
"Unknown watch option '{0}'. Did you mean '{1}'?": {
3321+
"category": "Error",
3322+
"code": 5079
3323+
},
3324+
"Watch option '{0}' requires a value of type {1}.": {
3325+
"category": "Error",
3326+
"code": 5080
3327+
},
33163328

33173329
"Generates a sourcemap for each corresponding '.d.ts' file.": {
33183330
"category": "Message",
@@ -4160,6 +4172,22 @@
41604172
"category": "Message",
41614173
"code": 6224
41624174
},
4175+
"Specify strategy for watching file: 'FixedPollingInterval' (default), 'PriorityPollingInterval', 'DynamicPriorityPolling', 'UseFsEvents', 'UseFsEventsOnParentDirectory'.": {
4176+
"category": "Message",
4177+
"code": 6225
4178+
},
4179+
"Specify strategy for watching directory on platforms that don't support recursive watching natively: 'UseFsEvents' (default), 'FixedPollingInterval', 'DynamicPriorityPolling'.": {
4180+
"category": "Message",
4181+
"code": 6226
4182+
},
4183+
"Specify strategy for creating a polling watch when it fails to create using file system events: 'FixedInterval' (default), 'PriorityInterval', 'DynamicPriority'.": {
4184+
"category": "Message",
4185+
"code": 6227
4186+
},
4187+
"Synchronously call callbacks and update the state of directory watchers on platforms that don't support recursive watching natively.": {
4188+
"category": "Message",
4189+
"code": 6228
4190+
},
41634191

41644192
"Projects to reference": {
41654193
"category": "Message",

src/compiler/sys.ts

Lines changed: 479 additions & 210 deletions
Large diffs are not rendered by default.

src/compiler/tsbuildPublic.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ namespace ts {
205205
return createSolutionBuilderWorker(/*watch*/ false, host, rootNames, defaultOptions);
206206
}
207207

208-
export function createSolutionBuilderWithWatch<T extends BuilderProgram>(host: SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions): SolutionBuilder<T> {
209-
return createSolutionBuilderWorker(/*watch*/ true, host, rootNames, defaultOptions);
208+
export function createSolutionBuilderWithWatch<T extends BuilderProgram>(host: SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions, baseWatchOptions?: WatchOptions): SolutionBuilder<T> {
209+
return createSolutionBuilderWorker(/*watch*/ true, host, rootNames, defaultOptions, baseWatchOptions);
210210
}
211211

212212
type ConfigFileCacheEntry = ParsedCommandLine | Diagnostic;
@@ -232,6 +232,7 @@ namespace ts {
232232
readonly options: BuildOptions;
233233
readonly baseCompilerOptions: CompilerOptions;
234234
readonly rootNames: readonly string[];
235+
readonly baseWatchOptions: WatchOptions | undefined;
235236

236237
readonly resolvedConfigFilePaths: Map<ResolvedConfigFilePath>;
237238
readonly configFileCache: ConfigFileMap<ConfigFileCacheEntry>;
@@ -272,7 +273,7 @@ namespace ts {
272273
writeLog: (s: string) => void;
273274
}
274275

275-
function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions): SolutionBuilderState<T> {
276+
function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions, baseWatchOptions: WatchOptions | undefined): SolutionBuilderState<T> {
276277
const host = hostOrHostWithWatch as SolutionBuilderHost<T>;
277278
const hostWithWatch = hostOrHostWithWatch as SolutionBuilderWithWatchHost<T>;
278279
const currentDirectory = host.getCurrentDirectory();
@@ -306,6 +307,7 @@ namespace ts {
306307
options,
307308
baseCompilerOptions,
308309
rootNames,
310+
baseWatchOptions,
309311

310312
resolvedConfigFilePaths: createMap(),
311313
configFileCache: createConfigFileMap(),
@@ -374,15 +376,15 @@ namespace ts {
374376
}
375377

376378
let diagnostic: Diagnostic | undefined;
377-
const { parseConfigFileHost, baseCompilerOptions, extendedConfigCache, host } = state;
379+
const { parseConfigFileHost, baseCompilerOptions, baseWatchOptions, extendedConfigCache, host } = state;
378380
let parsed: ParsedCommandLine | undefined;
379381
if (host.getParsedCommandLine) {
380382
parsed = host.getParsedCommandLine(configFileName);
381383
if (!parsed) diagnostic = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName);
382384
}
383385
else {
384386
parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = d => diagnostic = d;
385-
parsed = getParsedCommandLineOfConfigFile(configFileName, baseCompilerOptions, parseConfigFileHost, extendedConfigCache);
387+
parsed = getParsedCommandLineOfConfigFile(configFileName, baseCompilerOptions, parseConfigFileHost, extendedConfigCache, baseWatchOptions);
386388
parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = noop;
387389
}
388390
configFileCache.set(configFilePath, parsed || diagnostic!);
@@ -1147,7 +1149,7 @@ namespace ts {
11471149
}
11481150

11491151
if (reloadLevel === ConfigFileProgramReloadLevel.Full) {
1150-
watchConfigFile(state, project, projectPath);
1152+
watchConfigFile(state, project, projectPath, config);
11511153
watchWildCardDirectories(state, project, projectPath, config);
11521154
watchInputFiles(state, project, projectPath, config);
11531155
}
@@ -1751,7 +1753,7 @@ namespace ts {
17511753
reportErrorSummary(state, buildOrder);
17521754
}
17531755

1754-
function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath) {
1756+
function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) {
17551757
if (!state.watch || state.allWatchedConfigFiles.has(resolvedPath)) return;
17561758
state.allWatchedConfigFiles.set(resolvedPath, state.watchFile(
17571759
state.hostWithWatch,
@@ -1760,6 +1762,7 @@ namespace ts {
17601762
invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Full);
17611763
},
17621764
PollingInterval.High,
1765+
parsed?.watchOptions,
17631766
WatchType.ConfigFile,
17641767
resolved
17651768
));
@@ -1820,6 +1823,7 @@ namespace ts {
18201823
invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Partial);
18211824
},
18221825
flags,
1826+
parsed?.watchOptions,
18231827
WatchType.WildcardDirectory,
18241828
resolved
18251829
)
@@ -1837,6 +1841,7 @@ namespace ts {
18371841
input,
18381842
() => invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.None),
18391843
PollingInterval.Low,
1844+
parsed?.watchOptions,
18401845
path as Path,
18411846
WatchType.SourceFile,
18421847
resolved
@@ -1851,10 +1856,9 @@ namespace ts {
18511856
state.watchAllProjectsPending = false;
18521857
for (const resolved of getBuildOrderFromAnyBuildOrder(buildOrder)) {
18531858
const resolvedPath = toResolvedConfigFilePath(state, resolved);
1854-
// Watch this file
1855-
watchConfigFile(state, resolved, resolvedPath);
1856-
18571859
const cfg = parseConfigFile(state, resolved, resolvedPath);
1860+
// Watch this file
1861+
watchConfigFile(state, resolved, resolvedPath, cfg);
18581862
if (cfg) {
18591863
// Update watchers for wildcard directories
18601864
watchWildCardDirectories(state, resolved, resolvedPath, cfg);
@@ -1870,9 +1874,9 @@ namespace ts {
18701874
* can dynamically add/remove other projects based on changes on the rootNames' references
18711875
*/
18721876
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: false, host: SolutionBuilderHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions): SolutionBuilder<T>;
1873-
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: true, host: SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions): SolutionBuilder<T>;
1874-
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions): SolutionBuilder<T> {
1875-
const state = createSolutionBuilderState(watch, hostOrHostWithWatch, rootNames, options);
1877+
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: true, host: SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions, baseWatchOptions?: WatchOptions): SolutionBuilder<T>;
1878+
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions, baseWatchOptions?: WatchOptions): SolutionBuilder<T> {
1879+
const state = createSolutionBuilderState(watch, hostOrHostWithWatch, rootNames, options, baseWatchOptions);
18761880
return {
18771881
build: (project, cancellationToken) => build(state, project, cancellationToken),
18781882
clean: project => clean(state, project),

src/compiler/types.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4929,6 +4929,26 @@ namespace ts {
49294929
circular?: boolean;
49304930
}
49314931

4932+
export enum WatchFileKind {
4933+
FixedPollingInterval,
4934+
PriorityPollingInterval,
4935+
DynamicPriorityPolling,
4936+
UseFsEvents,
4937+
UseFsEventsOnParentDirectory,
4938+
}
4939+
4940+
export enum WatchDirectoryKind {
4941+
UseFsEvents,
4942+
FixedPollingInterval,
4943+
DynamicPriorityPolling,
4944+
}
4945+
4946+
export enum PollingWatchKind {
4947+
FixedInterval,
4948+
PriorityInterval,
4949+
DynamicPriority,
4950+
}
4951+
49324952
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | ProjectReference[] | null | undefined;
49334953

49344954
export interface CompilerOptions {
@@ -5043,6 +5063,15 @@ namespace ts {
50435063
[option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined;
50445064
}
50455065

5066+
export interface WatchOptions {
5067+
watchFile?: WatchFileKind;
5068+
watchDirectory?: WatchDirectoryKind;
5069+
fallbackPolling?: PollingWatchKind;
5070+
synchronousWatchDirectory?: boolean;
5071+
5072+
[option: string]: CompilerOptionsValue | undefined;
5073+
}
5074+
50465075
export interface TypeAcquisition {
50475076
/**
50485077
* @deprecated typingOptions.enableAutoDiscovery
@@ -5130,6 +5159,7 @@ namespace ts {
51305159
typeAcquisition?: TypeAcquisition;
51315160
fileNames: string[];
51325161
projectReferences?: readonly ProjectReference[];
5162+
watchOptions?: WatchOptions;
51335163
raw?: any;
51345164
errors: Diagnostic[];
51355165
wildcardDirectories?: MapLike<WatchDirectoryFlags>;
@@ -5210,7 +5240,8 @@ namespace ts {
52105240
}
52115241

52125242
/* @internal */
5213-
export interface DidYouMeanOptionalDiagnostics {
5243+
export interface DidYouMeanOptionsDiagnostics {
5244+
optionDeclarations: CommandLineOption[];
52145245
unknownOptionDiagnostic: DiagnosticMessage,
52155246
unknownDidYouMeanDiagnostic: DiagnosticMessage,
52165247
}
@@ -5219,7 +5250,7 @@ namespace ts {
52195250
export interface TsConfigOnlyOption extends CommandLineOptionBase {
52205251
type: "object";
52215252
elementOptions?: Map<CommandLineOption>;
5222-
extraKeyDiagnostics?: DidYouMeanOptionalDiagnostics;
5253+
extraKeyDiagnostics?: DidYouMeanOptionsDiagnostics;
52235254
}
52245255

52255256
/* @internal */

src/compiler/watch.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ namespace ts {
8989
}
9090

9191
/** Parses config file using System interface */
92-
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) {
92+
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, watchOptionsToExtend: WatchOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter) {
9393
const host: ParseConfigFileHost = <any>system;
9494
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic);
95-
const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host);
95+
const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host, /*extendedConfigCache*/ undefined, watchOptionsToExtend);
9696
host.onUnRecoverableConfigFileDiagnostic = undefined!; // TODO: GH#18217
9797
return result;
9898
}
@@ -419,22 +419,24 @@ namespace ts {
419419
/**
420420
* Creates the watch compiler host from system for config file in watch mode
421421
*/
422-
export function createWatchCompilerHostOfConfigFile<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile<T> {
422+
export function createWatchCompilerHostOfConfigFile<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, watchOptionsToExtend: WatchOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile<T> {
423423
const diagnosticReporter = reportDiagnostic || createDiagnosticReporter(system);
424424
const host = createWatchCompilerHost(system, createProgram, diagnosticReporter, reportWatchStatus) as WatchCompilerHostOfConfigFile<T>;
425425
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, diagnosticReporter, diagnostic);
426426
host.configFileName = configFileName;
427427
host.optionsToExtend = optionsToExtend;
428+
host.watchOptionsToExtend = watchOptionsToExtend;
428429
return host;
429430
}
430431

431432
/**
432433
* Creates the watch compiler host from system for compiling root files and options in watch mode
433434
*/
434-
export function createWatchCompilerHostOfFilesAndCompilerOptions<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: readonly ProjectReference[]): WatchCompilerHostOfFilesAndCompilerOptions<T> {
435+
export function createWatchCompilerHostOfFilesAndCompilerOptions<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(rootFiles: string[], options: CompilerOptions, watchOptions: WatchOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: readonly ProjectReference[]): WatchCompilerHostOfFilesAndCompilerOptions<T> {
435436
const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions<T>;
436437
host.rootFiles = rootFiles;
437438
host.options = options;
439+
host.watchOptions = watchOptions;
438440
host.projectReferences = projectReferences;
439441
return host;
440442
}

0 commit comments

Comments
 (0)