mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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
This commit is contained in:
@@ -754,7 +754,7 @@ namespace ts {
|
||||
|
||||
function convertToReusableCompilerOptions(options: CompilerOptions, relativeToBuildInfo: (path: string) => string) {
|
||||
const result: CompilerOptions = {};
|
||||
const optionsNameMap = getOptionNameMap().optionNameMap;
|
||||
const { optionsNameMap } = getOptionsNameMap();
|
||||
|
||||
for (const name in options) {
|
||||
if (hasProperty(options, name)) {
|
||||
@@ -1194,4 +1194,4 @@ namespace ts {
|
||||
return Debug.assertDefined(state.program);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+305
-152
@@ -73,6 +73,49 @@ namespace ts {
|
||||
/* @internal */
|
||||
export const libMap = createMapFromEntries(libEntries);
|
||||
|
||||
// Watch related options
|
||||
/* @internal */
|
||||
export const optionsForWatch: CommandLineOption[] = [
|
||||
{
|
||||
name: "watchFile",
|
||||
type: createMapFromTemplate({
|
||||
fixedpollinginterval: WatchFileKind.FixedPollingInterval,
|
||||
prioritypollinginterval: WatchFileKind.PriorityPollingInterval,
|
||||
dynamicprioritypolling: WatchFileKind.DynamicPriorityPolling,
|
||||
usefsevents: WatchFileKind.UseFsEvents,
|
||||
usefseventsonparentdirectory: WatchFileKind.UseFsEventsOnParentDirectory,
|
||||
}),
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Specify_strategy_for_watching_file_Colon_FixedPollingInterval_default_PriorityPollingInterval_DynamicPriorityPolling_UseFsEvents_UseFsEventsOnParentDirectory,
|
||||
},
|
||||
{
|
||||
name: "watchDirectory",
|
||||
type: createMapFromTemplate({
|
||||
usefsevents: WatchDirectoryKind.UseFsEvents,
|
||||
fixedpollinginterval: WatchDirectoryKind.FixedPollingInterval,
|
||||
dynamicprioritypolling: WatchDirectoryKind.DynamicPriorityPolling,
|
||||
}),
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Specify_strategy_for_watching_directory_on_platforms_that_don_t_support_recursive_watching_natively_Colon_UseFsEvents_default_FixedPollingInterval_DynamicPriorityPolling,
|
||||
},
|
||||
{
|
||||
name: "fallbackPolling",
|
||||
type: createMapFromTemplate({
|
||||
fixedinterval: PollingWatchKind.FixedInterval,
|
||||
priorityinterval: PollingWatchKind.PriorityInterval,
|
||||
dynamicpriority: PollingWatchKind.DynamicPriority,
|
||||
}),
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Specify_strategy_for_creating_a_polling_watch_when_it_fails_to_create_using_file_system_events_Colon_FixedInterval_default_PriorityInterval_DynamicPriority,
|
||||
},
|
||||
{
|
||||
name: "synchronousWatchDirectory",
|
||||
type: "boolean",
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively,
|
||||
},
|
||||
];
|
||||
|
||||
/* @internal */
|
||||
export const commonOptionsWithBuild: CommandLineOption[] = [
|
||||
{
|
||||
@@ -921,7 +964,7 @@ namespace ts {
|
||||
type: "object"
|
||||
},
|
||||
description: Diagnostics.List_of_language_service_plugins
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
/* @internal */
|
||||
@@ -1008,11 +1051,32 @@ namespace ts {
|
||||
];
|
||||
|
||||
/* @internal */
|
||||
export interface OptionNameMap {
|
||||
optionNameMap: Map<CommandLineOption>;
|
||||
export interface OptionsNameMap {
|
||||
optionsNameMap: Map<CommandLineOption>;
|
||||
shortOptionNames: Map<string>;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function createOptionNameMap(optionDeclarations: readonly CommandLineOption[]): OptionsNameMap {
|
||||
const optionsNameMap = createMap<CommandLineOption>();
|
||||
const shortOptionNames = createMap<string>();
|
||||
forEach(optionDeclarations, option => {
|
||||
optionsNameMap.set(option.name.toLowerCase(), option);
|
||||
if (option.shortName) {
|
||||
shortOptionNames.set(option.shortName, option.name);
|
||||
}
|
||||
});
|
||||
|
||||
return { optionsNameMap, shortOptionNames };
|
||||
}
|
||||
|
||||
let optionsNameMapCache: OptionsNameMap;
|
||||
|
||||
/* @internal */
|
||||
export function getOptionsNameMap(): OptionsNameMap {
|
||||
return optionsNameMapCache || (optionsNameMapCache = createOptionNameMap(optionDeclarations));
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const defaultInitCompilerOptions: CompilerOptions = {
|
||||
module: ModuleKind.CommonJS,
|
||||
@@ -1022,8 +1086,6 @@ namespace ts {
|
||||
forceConsistentCasingInFileNames: true
|
||||
};
|
||||
|
||||
let optionNameMapCache: OptionNameMap;
|
||||
|
||||
/* @internal */
|
||||
export function convertEnableAutoDiscoveryToEnable(typeAcquisition: TypeAcquisition): TypeAcquisition {
|
||||
// Convert deprecated typingOptions.enableAutoDiscovery to typeAcquisition.enable
|
||||
@@ -1037,25 +1099,6 @@ namespace ts {
|
||||
return typeAcquisition;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getOptionNameMap(): OptionNameMap {
|
||||
return optionNameMapCache || (optionNameMapCache = createOptionNameMap(optionDeclarations));
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function createOptionNameMap(optionDeclarations: readonly CommandLineOption[]): OptionNameMap {
|
||||
const optionNameMap = createMap<CommandLineOption>();
|
||||
const shortOptionNames = createMap<string>();
|
||||
forEach(optionDeclarations, option => {
|
||||
optionNameMap.set(option.name.toLowerCase(), option);
|
||||
if (option.shortName) {
|
||||
shortOptionNames.set(option.shortName, option.name);
|
||||
}
|
||||
});
|
||||
|
||||
return { optionNameMap, shortOptionNames };
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic {
|
||||
return createDiagnosticForInvalidCustomType(opt, createCompilerDiagnostic);
|
||||
@@ -1092,27 +1135,43 @@ namespace ts {
|
||||
}
|
||||
|
||||
interface OptionsBase {
|
||||
[option: string]: CompilerOptionsValue | undefined;
|
||||
[option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined;
|
||||
}
|
||||
|
||||
interface ParseCommandLineWorkerDiagnostics {
|
||||
unknownOptionDiagnostic: DiagnosticMessage,
|
||||
unknownDidYouMeanDiagnostic: DiagnosticMessage,
|
||||
optionTypeMismatchDiagnostic: DiagnosticMessage
|
||||
interface ParseCommandLineWorkerDiagnostics extends DidYouMeanOptionsDiagnostics {
|
||||
getOptionsNameMap: () => OptionsNameMap;
|
||||
optionTypeMismatchDiagnostic: DiagnosticMessage;
|
||||
}
|
||||
|
||||
function getOptionName(option: CommandLineOption) {
|
||||
return option.name;
|
||||
}
|
||||
|
||||
function createUnknownOptionError(
|
||||
unknownOption: string,
|
||||
diagnostics: DidYouMeanOptionsDiagnostics,
|
||||
createDiagnostics: (message: DiagnosticMessage, arg0: string, arg1?: string) => Diagnostic,
|
||||
unknownOptionErrorText?: string
|
||||
) {
|
||||
const possibleOption = getSpellingSuggestion(unknownOption, diagnostics.optionDeclarations, getOptionName);
|
||||
return possibleOption ?
|
||||
createDiagnostics(diagnostics.unknownDidYouMeanDiagnostic, unknownOptionErrorText || unknownOption, possibleOption.name) :
|
||||
createDiagnostics(diagnostics.unknownOptionDiagnostic, unknownOptionErrorText || unknownOption);
|
||||
}
|
||||
|
||||
function parseCommandLineWorker(
|
||||
getOptionNameMap: () => OptionNameMap,
|
||||
diagnostics: ParseCommandLineWorkerDiagnostics,
|
||||
commandLine: readonly string[],
|
||||
readFile?: (path: string) => string | undefined) {
|
||||
const options = {} as OptionsBase;
|
||||
let watchOptions: WatchOptions | undefined;
|
||||
const fileNames: string[] = [];
|
||||
const errors: Diagnostic[] = [];
|
||||
|
||||
parseStrings(commandLine);
|
||||
return {
|
||||
options,
|
||||
watchOptions,
|
||||
fileNames,
|
||||
errors
|
||||
};
|
||||
@@ -1126,57 +1185,18 @@ namespace ts {
|
||||
parseResponseFile(s.slice(1));
|
||||
}
|
||||
else if (s.charCodeAt(0) === CharacterCodes.minus) {
|
||||
const opt = getOptionDeclarationFromName(getOptionNameMap, s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
|
||||
const inputOptionName = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1);
|
||||
const opt = getOptionDeclarationFromName(diagnostics.getOptionsNameMap, inputOptionName, /*allowShort*/ true);
|
||||
if (opt) {
|
||||
if (opt.isTSConfigOnly) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
|
||||
}
|
||||
else {
|
||||
// Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
|
||||
if (!args[i] && opt.type !== "boolean") {
|
||||
errors.push(createCompilerDiagnostic(diagnostics.optionTypeMismatchDiagnostic, opt.name));
|
||||
}
|
||||
|
||||
switch (opt.type) {
|
||||
case "number":
|
||||
options[opt.name] = parseInt(args[i]);
|
||||
i++;
|
||||
break;
|
||||
case "boolean":
|
||||
// boolean flag has optional value true, false, others
|
||||
const optValue = args[i];
|
||||
options[opt.name] = optValue !== "false";
|
||||
// consume next argument as boolean flag value
|
||||
if (optValue === "false" || optValue === "true") {
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
options[opt.name] = args[i] || "";
|
||||
i++;
|
||||
break;
|
||||
case "list":
|
||||
const result = parseListTypeOption(opt, args[i], errors);
|
||||
options[opt.name] = result || [];
|
||||
if (result) {
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
// If not a primitive, the possible types are specified in what is effectively a map of options.
|
||||
default:
|
||||
options[opt.name] = parseCustomTypeOption(<CommandLineOptionOfCustomType>opt, args[i], errors);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i = parseOptionValue(args, i, diagnostics, opt, options, errors);
|
||||
}
|
||||
else {
|
||||
const possibleOption = getSpellingSuggestion(s, optionDeclarations, opt => `--${opt.name}`);
|
||||
if (possibleOption) {
|
||||
errors.push(createCompilerDiagnostic(diagnostics.unknownDidYouMeanDiagnostic, s, possibleOption.name));
|
||||
const watchOpt = getOptionDeclarationFromName(watchOptionsDidYouMeanDiagnostics.getOptionsNameMap, inputOptionName, /*allowShort*/ true);
|
||||
if (watchOpt) {
|
||||
i = parseOptionValue(args, i, watchOptionsDidYouMeanDiagnostics, watchOpt, watchOptions || (watchOptions = {}), errors);
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(diagnostics.unknownOptionDiagnostic, s));
|
||||
errors.push(createUnknownOptionError(inputOptionName, diagnostics, createCompilerDiagnostic, s));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1220,23 +1240,77 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
const compilerOptionsDefaultDiagnostics = {
|
||||
function parseOptionValue(
|
||||
args: readonly string[],
|
||||
i: number,
|
||||
diagnostics: ParseCommandLineWorkerDiagnostics,
|
||||
opt: CommandLineOption,
|
||||
options: OptionsBase,
|
||||
errors: Diagnostic[]
|
||||
) {
|
||||
if (opt.isTSConfigOnly) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
|
||||
}
|
||||
else {
|
||||
// Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
|
||||
if (!args[i] && opt.type !== "boolean") {
|
||||
errors.push(createCompilerDiagnostic(diagnostics.optionTypeMismatchDiagnostic, opt.name, getCompilerOptionValueTypeString(opt)));
|
||||
}
|
||||
|
||||
switch (opt.type) {
|
||||
case "number":
|
||||
options[opt.name] = parseInt(args[i]);
|
||||
i++;
|
||||
break;
|
||||
case "boolean":
|
||||
// boolean flag has optional value true, false, others
|
||||
const optValue = args[i];
|
||||
options[opt.name] = optValue !== "false";
|
||||
// consume next argument as boolean flag value
|
||||
if (optValue === "false" || optValue === "true") {
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
options[opt.name] = args[i] || "";
|
||||
i++;
|
||||
break;
|
||||
case "list":
|
||||
const result = parseListTypeOption(opt, args[i], errors);
|
||||
options[opt.name] = result || [];
|
||||
if (result) {
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
// If not a primitive, the possible types are specified in what is effectively a map of options.
|
||||
default:
|
||||
options[opt.name] = parseCustomTypeOption(<CommandLineOptionOfCustomType>opt, args[i], errors);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
const compilerOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = {
|
||||
getOptionsNameMap,
|
||||
optionDeclarations,
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_compiler_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1,
|
||||
optionTypeMismatchDiagnostic: Diagnostics.Compiler_option_0_expects_an_argument
|
||||
};
|
||||
export function parseCommandLine(commandLine: readonly string[], readFile?: (path: string) => string | undefined): ParsedCommandLine {
|
||||
return parseCommandLineWorker(getOptionNameMap, compilerOptionsDefaultDiagnostics, commandLine, readFile);
|
||||
return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine, readFile);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getOptionFromName(optionName: string, allowShort?: boolean): CommandLineOption | undefined {
|
||||
return getOptionDeclarationFromName(getOptionNameMap, optionName, allowShort);
|
||||
return getOptionDeclarationFromName(getOptionsNameMap, optionName, allowShort);
|
||||
}
|
||||
|
||||
function getOptionDeclarationFromName(getOptionNameMap: () => OptionNameMap, optionName: string, allowShort = false): CommandLineOption | undefined {
|
||||
function getOptionDeclarationFromName(getOptionNameMap: () => OptionsNameMap, optionName: string, allowShort = false): CommandLineOption | undefined {
|
||||
optionName = optionName.toLowerCase();
|
||||
const { optionNameMap, shortOptionNames } = getOptionNameMap();
|
||||
const { optionsNameMap, shortOptionNames } = getOptionNameMap();
|
||||
// Try to translate short option names to their full equivalents.
|
||||
if (allowShort) {
|
||||
const short = shortOptionNames.get(optionName);
|
||||
@@ -1244,25 +1318,36 @@ namespace ts {
|
||||
optionName = short;
|
||||
}
|
||||
}
|
||||
return optionNameMap.get(optionName);
|
||||
return optionsNameMap.get(optionName);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export interface ParsedBuildCommand {
|
||||
buildOptions: BuildOptions;
|
||||
watchOptions: WatchOptions | undefined;
|
||||
projects: string[];
|
||||
errors: Diagnostic[];
|
||||
}
|
||||
|
||||
let buildOptionsNameMapCache: OptionsNameMap;
|
||||
function getBuildOptionsNameMap(): OptionsNameMap {
|
||||
return buildOptionsNameMapCache || (buildOptionsNameMapCache = createOptionNameMap(buildOpts));
|
||||
}
|
||||
|
||||
const buildOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = {
|
||||
getOptionsNameMap: getBuildOptionsNameMap,
|
||||
optionDeclarations: buildOpts,
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_build_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_build_option_0_Did_you_mean_1,
|
||||
optionTypeMismatchDiagnostic: Diagnostics.Build_option_0_requires_a_value_of_type_1
|
||||
};
|
||||
|
||||
/*@internal*/
|
||||
export function parseBuildCommand(args: readonly string[]): ParsedBuildCommand {
|
||||
let buildOptionNameMap: OptionNameMap | undefined;
|
||||
const returnBuildOptionNameMap = () => (buildOptionNameMap || (buildOptionNameMap = createOptionNameMap(buildOpts)));
|
||||
const { options, fileNames: projects, errors } = parseCommandLineWorker(returnBuildOptionNameMap, {
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_build_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_build_option_0_Did_you_mean_1,
|
||||
optionTypeMismatchDiagnostic: Diagnostics.Build_option_0_requires_a_value_of_type_1
|
||||
}, args);
|
||||
const { options, watchOptions, fileNames: projects, errors } = parseCommandLineWorker(
|
||||
buildOptionsDidYouMeanDiagnostics,
|
||||
args
|
||||
);
|
||||
const buildOptions = options as BuildOptions;
|
||||
|
||||
if (projects.length === 0) {
|
||||
@@ -1284,7 +1369,7 @@ namespace ts {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry"));
|
||||
}
|
||||
|
||||
return { buildOptions, projects, errors };
|
||||
return { buildOptions, watchOptions, projects, errors };
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -1318,7 +1403,8 @@ namespace ts {
|
||||
configFileName: string,
|
||||
optionsToExtend: CompilerOptions,
|
||||
host: ParseConfigFileHost,
|
||||
extendedConfigCache?: Map<ExtendedConfigCacheEntry>
|
||||
extendedConfigCache?: Map<ExtendedConfigCacheEntry>,
|
||||
watchOptionsToExtend?: WatchOptions
|
||||
): ParsedCommandLine | undefined {
|
||||
let configFileText: string | undefined;
|
||||
try {
|
||||
@@ -1348,7 +1434,8 @@ namespace ts {
|
||||
getNormalizedAbsolutePath(configFileName, cwd),
|
||||
/*resolutionStack*/ undefined,
|
||||
/*extraFileExtension*/ undefined,
|
||||
extendedConfigCache
|
||||
extendedConfigCache,
|
||||
watchOptionsToExtend
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1395,7 +1482,38 @@ namespace ts {
|
||||
}
|
||||
|
||||
function commandLineOptionsToMap(options: readonly CommandLineOption[]) {
|
||||
return arrayToMap(options, option => option.name);
|
||||
return arrayToMap(options, getOptionName);
|
||||
}
|
||||
|
||||
const typeAcquisitionDidYouMeanDiagnostics: DidYouMeanOptionsDiagnostics = {
|
||||
optionDeclarations: typeAcquisitionDeclarations,
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_type_acquisition_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_type_acquisition_option_0_Did_you_mean_1,
|
||||
};
|
||||
|
||||
let watchOptionsNameMapCache: OptionsNameMap;
|
||||
function getWatchOptionsNameMap(): OptionsNameMap {
|
||||
return watchOptionsNameMapCache || (watchOptionsNameMapCache = createOptionNameMap(optionsForWatch));
|
||||
}
|
||||
const watchOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = {
|
||||
getOptionsNameMap: getWatchOptionsNameMap,
|
||||
optionDeclarations: optionsForWatch,
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_watch_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_watch_option_0_Did_you_mean_1,
|
||||
optionTypeMismatchDiagnostic: Diagnostics.Watch_option_0_requires_a_value_of_type_1
|
||||
};
|
||||
|
||||
let commandLineCompilerOptionsMapCache: Map<CommandLineOption>;
|
||||
function getCommandLineCompilerOptionsMap() {
|
||||
return commandLineCompilerOptionsMapCache || (commandLineCompilerOptionsMapCache = commandLineOptionsToMap(optionDeclarations));
|
||||
}
|
||||
let commandLineWatchOptionsMapCache: Map<CommandLineOption>;
|
||||
function getCommandLineWatchOptionsMap() {
|
||||
return commandLineWatchOptionsMapCache || (commandLineWatchOptionsMapCache = commandLineOptionsToMap(optionsForWatch));
|
||||
}
|
||||
let commandLineTypeAcquisitionMapCache: Map<CommandLineOption>;
|
||||
function getCommandLineTypeAcquisitionMap() {
|
||||
return commandLineTypeAcquisitionMapCache || (commandLineTypeAcquisitionMapCache = commandLineOptionsToMap(typeAcquisitionDeclarations));
|
||||
}
|
||||
|
||||
let _tsconfigRootOptions: TsConfigOnlyOption;
|
||||
@@ -1408,29 +1526,26 @@ namespace ts {
|
||||
{
|
||||
name: "compilerOptions",
|
||||
type: "object",
|
||||
elementOptions: commandLineOptionsToMap(optionDeclarations),
|
||||
extraKeyDiagnostics: {
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_compiler_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1
|
||||
},
|
||||
elementOptions: getCommandLineCompilerOptionsMap(),
|
||||
extraKeyDiagnostics: compilerOptionsDidYouMeanDiagnostics,
|
||||
},
|
||||
{
|
||||
name: "watchOptions",
|
||||
type: "object",
|
||||
elementOptions: getCommandLineWatchOptionsMap(),
|
||||
extraKeyDiagnostics: watchOptionsDidYouMeanDiagnostics,
|
||||
},
|
||||
{
|
||||
name: "typingOptions",
|
||||
type: "object",
|
||||
elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations),
|
||||
extraKeyDiagnostics: {
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_type_acquisition_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_type_acquisition_option_0_Did_you_mean_1
|
||||
},
|
||||
elementOptions: getCommandLineTypeAcquisitionMap(),
|
||||
extraKeyDiagnostics: typeAcquisitionDidYouMeanDiagnostics,
|
||||
},
|
||||
{
|
||||
name: "typeAcquisition",
|
||||
type: "object",
|
||||
elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations),
|
||||
extraKeyDiagnostics: {
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_type_acquisition_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_type_acquisition_option_0_Did_you_mean_1
|
||||
}
|
||||
elementOptions: getCommandLineTypeAcquisitionMap(),
|
||||
extraKeyDiagnostics: typeAcquisitionDidYouMeanDiagnostics
|
||||
},
|
||||
{
|
||||
name: "extends",
|
||||
@@ -1536,7 +1651,7 @@ namespace ts {
|
||||
function convertObjectLiteralExpressionToJson(
|
||||
node: ObjectLiteralExpression,
|
||||
knownOptions: Map<CommandLineOption> | undefined,
|
||||
extraKeyDiagnostics: DidYouMeanOptionalDiagnostics | undefined,
|
||||
extraKeyDiagnostics: DidYouMeanOptionsDiagnostics | undefined,
|
||||
parentOption: string | undefined
|
||||
): any {
|
||||
const result: any = returnValue ? {} : undefined;
|
||||
@@ -1558,13 +1673,11 @@ namespace ts {
|
||||
const option = keyText && knownOptions ? knownOptions.get(keyText) : undefined;
|
||||
if (keyText && extraKeyDiagnostics && !option) {
|
||||
if (knownOptions) {
|
||||
const possibleOption = getSpellingSuggestion(keyText, arrayFrom(knownOptions.keys()), identity);
|
||||
if (possibleOption) {
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, extraKeyDiagnostics.unknownDidYouMeanDiagnostic, keyText, possibleOption));
|
||||
}
|
||||
else {
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, extraKeyDiagnostics.unknownOptionDiagnostic, keyText));
|
||||
}
|
||||
errors.push(createUnknownOptionError(
|
||||
keyText,
|
||||
extraKeyDiagnostics,
|
||||
(message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, element.name, message, arg0, arg1)
|
||||
));
|
||||
}
|
||||
else {
|
||||
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, extraKeyDiagnostics.unknownOptionDiagnostic, keyText));
|
||||
@@ -1765,9 +1878,10 @@ namespace ts {
|
||||
f => getRelativePathFromFile(getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), getNormalizedAbsolutePath(f, host.getCurrentDirectory()), getCanonicalFileName)
|
||||
);
|
||||
const optionMap = serializeCompilerOptions(configParseResult.options, { configFilePath: getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), useCaseSensitiveFileNames: host.useCaseSensitiveFileNames });
|
||||
const watchOptionMap = configParseResult.watchOptions && serializeWatchOptions(configParseResult.watchOptions);
|
||||
const config = {
|
||||
compilerOptions: {
|
||||
...arrayFrom(optionMap.entries()).reduce((prev, cur) => ({ ...prev, [cur[0]]: cur[1] }), {}),
|
||||
...optionMapToObject(optionMap),
|
||||
showConfig: undefined,
|
||||
configFile: undefined,
|
||||
configFilePath: undefined,
|
||||
@@ -1779,6 +1893,7 @@ namespace ts {
|
||||
build: undefined,
|
||||
version: undefined,
|
||||
},
|
||||
watchOptions: watchOptionMap && optionMapToObject(watchOptionMap),
|
||||
references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath ? r.originalPath : "", originalPath: undefined })),
|
||||
files: length(files) ? files : undefined,
|
||||
...(configParseResult.configFileSpecs ? {
|
||||
@@ -1790,6 +1905,12 @@ namespace ts {
|
||||
return config;
|
||||
}
|
||||
|
||||
function optionMapToObject(optionMap: Map<CompilerOptionsValue>): object {
|
||||
return {
|
||||
...arrayFrom(optionMap.entries()).reduce((prev, cur) => ({ ...prev, [cur[0]]: cur[1] }), {}),
|
||||
};
|
||||
}
|
||||
|
||||
function filterSameAsDefaultInclude(specs: readonly string[] | undefined) {
|
||||
if (!length(specs)) return undefined;
|
||||
if (length(specs) !== 1) return specs;
|
||||
@@ -1836,9 +1957,23 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function serializeCompilerOptions(options: CompilerOptions, pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean }): Map<CompilerOptionsValue> {
|
||||
function serializeCompilerOptions(
|
||||
options: CompilerOptions,
|
||||
pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean }
|
||||
): Map<CompilerOptionsValue> {
|
||||
return serializeOptionBaseObject(options, getOptionsNameMap(), pathOptions);
|
||||
}
|
||||
|
||||
function serializeWatchOptions(options: WatchOptions) {
|
||||
return serializeOptionBaseObject(options, getWatchOptionsNameMap());
|
||||
}
|
||||
|
||||
function serializeOptionBaseObject(
|
||||
options: OptionsBase,
|
||||
{ optionsNameMap }: OptionsNameMap,
|
||||
pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean }
|
||||
): Map<CompilerOptionsValue> {
|
||||
const result = createMap<CompilerOptionsValue>();
|
||||
const optionsNameMap = getOptionNameMap().optionNameMap;
|
||||
const getCanonicalFileName = pathOptions && createGetCanonicalFileName(pathOptions.useCaseSensitiveFileNames);
|
||||
|
||||
for (const name in options) {
|
||||
@@ -1987,7 +2122,7 @@ namespace ts {
|
||||
/* @internal */
|
||||
export function convertToOptionsWithAbsolutePaths(options: CompilerOptions, toAbsolutePath: (path: string) => string) {
|
||||
const result: CompilerOptions = {};
|
||||
const optionsNameMap = getOptionNameMap().optionNameMap;
|
||||
const optionsNameMap = getOptionsNameMap().optionsNameMap;
|
||||
|
||||
for (const name in options) {
|
||||
if (hasProperty(options, name)) {
|
||||
@@ -2026,8 +2161,8 @@ namespace ts {
|
||||
* @param basePath A root directory to resolve relative path entries in the config
|
||||
* file to. e.g. outDir
|
||||
*/
|
||||
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map<ExtendedConfigCacheEntry>): ParsedCommandLine {
|
||||
return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache);
|
||||
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map<ExtendedConfigCacheEntry>, existingWatchOptions?: WatchOptions): ParsedCommandLine {
|
||||
return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2037,8 +2172,8 @@ namespace ts {
|
||||
* @param basePath A root directory to resolve relative path entries in the config
|
||||
* file to. e.g. outDir
|
||||
*/
|
||||
export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map<ExtendedConfigCacheEntry>): ParsedCommandLine {
|
||||
return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache);
|
||||
export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map<ExtendedConfigCacheEntry>, existingWatchOptions?: WatchOptions): ParsedCommandLine {
|
||||
return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
@@ -2073,6 +2208,7 @@ namespace ts {
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
existingOptions: CompilerOptions = {},
|
||||
existingWatchOptions: WatchOptions | undefined,
|
||||
configFileName?: string,
|
||||
resolutionStack: Path[] = [],
|
||||
extraFileExtensions: readonly FileExtensionInfo[] = [],
|
||||
@@ -2084,12 +2220,17 @@ namespace ts {
|
||||
const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors, extendedConfigCache);
|
||||
const { raw } = parsedConfig;
|
||||
const options = extend(existingOptions, parsedConfig.options || {});
|
||||
const watchOptions = existingWatchOptions && parsedConfig.watchOptions ?
|
||||
extend(existingWatchOptions, parsedConfig.watchOptions) :
|
||||
parsedConfig.watchOptions || existingWatchOptions;
|
||||
|
||||
options.configFilePath = configFileName && normalizeSlashes(configFileName);
|
||||
setConfigFileInOptions(options, sourceFile);
|
||||
let projectReferences: ProjectReference[] | undefined;
|
||||
const { fileNames, wildcardDirectories, spec } = getFileNames();
|
||||
return {
|
||||
options,
|
||||
watchOptions,
|
||||
fileNames,
|
||||
projectReferences,
|
||||
typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(),
|
||||
@@ -2232,6 +2373,7 @@ namespace ts {
|
||||
export interface ParsedTsconfig {
|
||||
raw: any;
|
||||
options?: CompilerOptions;
|
||||
watchOptions?: WatchOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
/**
|
||||
* Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet
|
||||
@@ -2289,6 +2431,9 @@ namespace ts {
|
||||
raw.compileOnSave = baseRaw.compileOnSave;
|
||||
}
|
||||
ownConfig.options = assign({}, extendedConfig.options, ownConfig.options);
|
||||
ownConfig.watchOptions = ownConfig.watchOptions && extendedConfig.watchOptions ?
|
||||
assign({}, extendedConfig.watchOptions, ownConfig.watchOptions) :
|
||||
ownConfig.watchOptions || extendedConfig.watchOptions;
|
||||
// TODO extend type typeAcquisition
|
||||
}
|
||||
}
|
||||
@@ -2311,6 +2456,7 @@ namespace ts {
|
||||
// typingOptions has been deprecated and is only supported for backward compatibility purposes.
|
||||
// It should be removed in future releases - use typeAcquisition instead.
|
||||
const typeAcquisition = convertTypeAcquisitionFromJsonWorker(json.typeAcquisition || json.typingOptions, basePath, errors, configFileName);
|
||||
const watchOptions = convertWatchOptionsFromJsonWorker(json.watchOptions, basePath, errors);
|
||||
json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors);
|
||||
let extendedConfigPath: string | undefined;
|
||||
|
||||
@@ -2323,7 +2469,7 @@ namespace ts {
|
||||
extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, errors, createCompilerDiagnostic);
|
||||
}
|
||||
}
|
||||
return { raw: json, options, typeAcquisition, extendedConfigPath };
|
||||
return { raw: json, options, watchOptions, typeAcquisition, extendedConfigPath };
|
||||
}
|
||||
|
||||
function parseOwnConfigOfJsonSourceFile(
|
||||
@@ -2335,16 +2481,28 @@ namespace ts {
|
||||
): ParsedTsconfig {
|
||||
const options = getDefaultCompilerOptions(configFileName);
|
||||
let typeAcquisition: TypeAcquisition | undefined, typingOptionstypeAcquisition: TypeAcquisition | undefined;
|
||||
let watchOptions: WatchOptions | undefined;
|
||||
let extendedConfigPath: string | undefined;
|
||||
|
||||
const optionsIterator: JsonConversionNotifier = {
|
||||
onSetValidOptionKeyValueInParent(parentOption: string, option: CommandLineOption, value: CompilerOptionsValue) {
|
||||
Debug.assert(parentOption === "compilerOptions" || parentOption === "typeAcquisition" || parentOption === "typingOptions");
|
||||
const currentOption = parentOption === "compilerOptions" ?
|
||||
options :
|
||||
parentOption === "typeAcquisition" ?
|
||||
(typeAcquisition || (typeAcquisition = getDefaultTypeAcquisition(configFileName))) :
|
||||
(typingOptionstypeAcquisition || (typingOptionstypeAcquisition = getDefaultTypeAcquisition(configFileName)));
|
||||
let currentOption;
|
||||
switch (parentOption) {
|
||||
case "compilerOptions":
|
||||
currentOption = options;
|
||||
break;
|
||||
case "watchOptions":
|
||||
currentOption = (watchOptions || (watchOptions = {}));
|
||||
break;
|
||||
case "typeAcquisition":
|
||||
currentOption = (typeAcquisition || (typeAcquisition = getDefaultTypeAcquisition(configFileName)));
|
||||
break;
|
||||
case "typingOptions":
|
||||
currentOption = (typingOptionstypeAcquisition || (typingOptionstypeAcquisition = getDefaultTypeAcquisition(configFileName)));
|
||||
break;
|
||||
default:
|
||||
Debug.fail("Unknown option");
|
||||
}
|
||||
|
||||
currentOption[option.name] = normalizeOptionValue(option, basePath, value);
|
||||
},
|
||||
@@ -2386,7 +2544,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
return { raw: json, options, typeAcquisition, extendedConfigPath };
|
||||
return { raw: json, options, watchOptions, typeAcquisition, extendedConfigPath };
|
||||
}
|
||||
|
||||
function getExtendsConfigPath(
|
||||
@@ -2508,7 +2666,7 @@ namespace ts {
|
||||
basePath: string, errors: Push<Diagnostic>, configFileName?: string): CompilerOptions {
|
||||
|
||||
const options = getDefaultCompilerOptions(configFileName);
|
||||
convertOptionsFromJson(optionDeclarations, jsonOptions, basePath, options, compilerOptionsDefaultDiagnostics, errors);
|
||||
convertOptionsFromJson(getCommandLineCompilerOptionsMap(), jsonOptions, basePath, options, compilerOptionsDidYouMeanDiagnostics, errors);
|
||||
if (configFileName) {
|
||||
options.configFilePath = normalizeSlashes(configFileName);
|
||||
}
|
||||
@@ -2525,40 +2683,35 @@ namespace ts {
|
||||
const options = getDefaultTypeAcquisition(configFileName);
|
||||
const typeAcquisition = convertEnableAutoDiscoveryToEnable(jsonOptions);
|
||||
|
||||
const diagnostics = {
|
||||
unknownOptionDiagnostic: Diagnostics.Unknown_type_acquisition_option_0,
|
||||
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_type_acquisition_option_0_Did_you_mean_1 ,
|
||||
};
|
||||
convertOptionsFromJson(typeAcquisitionDeclarations, typeAcquisition, basePath, options, diagnostics, errors);
|
||||
|
||||
convertOptionsFromJson(getCommandLineTypeAcquisitionMap(), typeAcquisition, basePath, options, typeAcquisitionDidYouMeanDiagnostics, errors);
|
||||
return options;
|
||||
}
|
||||
|
||||
function convertWatchOptionsFromJsonWorker(jsonOptions: any, basePath: string, errors: Push<Diagnostic>): WatchOptions | undefined {
|
||||
return convertOptionsFromJson(getCommandLineWatchOptionsMap(), jsonOptions, basePath, /*defaultOptions*/ undefined, watchOptionsDidYouMeanDiagnostics, errors);
|
||||
}
|
||||
|
||||
function convertOptionsFromJson(optionDeclarations: readonly CommandLineOption[], jsonOptions: any, basePath: string,
|
||||
defaultOptions: CompilerOptions | TypeAcquisition, diagnostics: DidYouMeanOptionalDiagnostics, errors: Push<Diagnostic>) {
|
||||
function convertOptionsFromJson(optionsNameMap: Map<CommandLineOption>, jsonOptions: any, basePath: string,
|
||||
defaultOptions: undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>): WatchOptions | undefined;
|
||||
function convertOptionsFromJson(optionsNameMap: Map<CommandLineOption>, jsonOptions: any, basePath: string,
|
||||
defaultOptions: CompilerOptions | TypeAcquisition, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>): CompilerOptions | TypeAcquisition;
|
||||
function convertOptionsFromJson(optionsNameMap: Map<CommandLineOption>, jsonOptions: any, basePath: string,
|
||||
defaultOptions: CompilerOptions | TypeAcquisition | WatchOptions | undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>) {
|
||||
|
||||
if (!jsonOptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
const optionNameMap = commandLineOptionsToMap(optionDeclarations);
|
||||
|
||||
for (const id in jsonOptions) {
|
||||
const opt = optionNameMap.get(id);
|
||||
const opt = optionsNameMap.get(id);
|
||||
if (opt) {
|
||||
defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors);
|
||||
(defaultOptions || (defaultOptions = {}))[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors);
|
||||
}
|
||||
else {
|
||||
const possibleOption = getSpellingSuggestion(id, <CommandLineOption[]>optionDeclarations, opt => opt.name);
|
||||
if (possibleOption) {
|
||||
errors.push(createCompilerDiagnostic(diagnostics.unknownDidYouMeanDiagnostic, id, possibleOption.name));
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(diagnostics.unknownOptionDiagnostic, id));
|
||||
}
|
||||
errors.push(createUnknownOptionError(id, diagnostics, createCompilerDiagnostic));
|
||||
}
|
||||
}
|
||||
return defaultOptions;
|
||||
}
|
||||
|
||||
function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push<Diagnostic>): CompilerOptionsValue {
|
||||
|
||||
@@ -3313,6 +3313,18 @@
|
||||
"category": "Error",
|
||||
"code": 5077
|
||||
},
|
||||
"Unknown watch option '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 5078
|
||||
},
|
||||
"Unknown watch option '{0}'. Did you mean '{1}'?": {
|
||||
"category": "Error",
|
||||
"code": 5079
|
||||
},
|
||||
"Watch option '{0}' requires a value of type {1}.": {
|
||||
"category": "Error",
|
||||
"code": 5080
|
||||
},
|
||||
|
||||
"Generates a sourcemap for each corresponding '.d.ts' file.": {
|
||||
"category": "Message",
|
||||
@@ -4160,6 +4172,22 @@
|
||||
"category": "Message",
|
||||
"code": 6224
|
||||
},
|
||||
"Specify strategy for watching file: 'FixedPollingInterval' (default), 'PriorityPollingInterval', 'DynamicPriorityPolling', 'UseFsEvents', 'UseFsEventsOnParentDirectory'.": {
|
||||
"category": "Message",
|
||||
"code": 6225
|
||||
},
|
||||
"Specify strategy for watching directory on platforms that don't support recursive watching natively: 'UseFsEvents' (default), 'FixedPollingInterval', 'DynamicPriorityPolling'.": {
|
||||
"category": "Message",
|
||||
"code": 6226
|
||||
},
|
||||
"Specify strategy for creating a polling watch when it fails to create using file system events: 'FixedInterval' (default), 'PriorityInterval', 'DynamicPriority'.": {
|
||||
"category": "Message",
|
||||
"code": 6227
|
||||
},
|
||||
"Synchronously call callbacks and update the state of directory watchers on platforms that don't support recursive watching natively.": {
|
||||
"category": "Message",
|
||||
"code": 6228
|
||||
},
|
||||
|
||||
"Projects to reference": {
|
||||
"category": "Message",
|
||||
|
||||
+479
-210
@@ -50,9 +50,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export type HostWatchFile = (fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval | undefined) => FileWatcher;
|
||||
export type HostWatchFile = (fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined) => FileWatcher;
|
||||
/* @internal */
|
||||
export type HostWatchDirectory = (fileName: string, callback: DirectoryWatcherCallback, recursive?: boolean) => FileWatcher;
|
||||
export type HostWatchDirectory = (fileName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined) => FileWatcher;
|
||||
|
||||
/* @internal */
|
||||
export const missingFileModifiedTime = new Date(0); // Any subsequent modification will occur after this time
|
||||
@@ -127,7 +127,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createDynamicPriorityPollingWatchFile(host: { getModifiedTime: System["getModifiedTime"]; setTimeout: System["setTimeout"]; }): HostWatchFile {
|
||||
export function createDynamicPriorityPollingWatchFile(host: {
|
||||
getModifiedTime: NonNullable<System["getModifiedTime"]>;
|
||||
setTimeout: NonNullable<System["setTimeout"]>;
|
||||
}): HostWatchFile {
|
||||
interface WatchedFile extends ts.WatchedFile {
|
||||
isClosed?: boolean;
|
||||
unchangedPolls: number;
|
||||
@@ -296,11 +299,65 @@ namespace ts {
|
||||
}
|
||||
|
||||
function scheduleNextPoll(pollingInterval: PollingInterval) {
|
||||
pollingIntervalQueue(pollingInterval).pollScheduled = host.setTimeout!(pollingInterval === PollingInterval.Low ? pollLowPollingIntervalQueue : pollPollingIntervalQueue, pollingInterval, pollingIntervalQueue(pollingInterval));
|
||||
pollingIntervalQueue(pollingInterval).pollScheduled = host.setTimeout(pollingInterval === PollingInterval.Low ? pollLowPollingIntervalQueue : pollPollingIntervalQueue, pollingInterval, pollingIntervalQueue(pollingInterval));
|
||||
}
|
||||
|
||||
function getModifiedTime(fileName: string) {
|
||||
return host.getModifiedTime!(fileName) || missingFileModifiedTime;
|
||||
return host.getModifiedTime(fileName) || missingFileModifiedTime;
|
||||
}
|
||||
}
|
||||
|
||||
function createUseFsEventsOnParentDirectoryWatchFile(fsWatch: FsWatch, useCaseSensitiveFileNames: boolean): HostWatchFile {
|
||||
// One file can have multiple watchers
|
||||
const fileWatcherCallbacks = createMultiMap<FileWatcherCallback>();
|
||||
const dirWatchers = createMap<DirectoryWatcher>();
|
||||
const toCanonicalName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
return nonPollingWatchFile;
|
||||
|
||||
function nonPollingWatchFile(fileName: string, callback: FileWatcherCallback, _pollingInterval: PollingInterval, fallbackOptions: WatchOptions | undefined): FileWatcher {
|
||||
const filePath = toCanonicalName(fileName);
|
||||
fileWatcherCallbacks.add(filePath, callback);
|
||||
const dirPath = getDirectoryPath(filePath) || ".";
|
||||
const watcher = dirWatchers.get(dirPath) ||
|
||||
createDirectoryWatcher(getDirectoryPath(fileName) || ".", dirPath, fallbackOptions);
|
||||
watcher.referenceCount++;
|
||||
return {
|
||||
close: () => {
|
||||
if (watcher.referenceCount === 1) {
|
||||
watcher.close();
|
||||
dirWatchers.delete(dirPath);
|
||||
}
|
||||
else {
|
||||
watcher.referenceCount--;
|
||||
}
|
||||
fileWatcherCallbacks.remove(filePath, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createDirectoryWatcher(dirName: string, dirPath: string, fallbackOptions: WatchOptions | undefined) {
|
||||
const watcher = fsWatch(
|
||||
dirName,
|
||||
FileSystemEntryKind.Directory,
|
||||
(_eventName: string, relativeFileName) => {
|
||||
// When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
|
||||
if (!isString(relativeFileName)) { return; }
|
||||
const fileName = getNormalizedAbsolutePath(relativeFileName, dirName);
|
||||
// Some applications save a working file via rename operations
|
||||
const callbacks = fileName && fileWatcherCallbacks.get(toCanonicalName(fileName));
|
||||
if (callbacks) {
|
||||
for (const fileCallback of callbacks) {
|
||||
fileCallback(fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
}
|
||||
},
|
||||
/*recursive*/ false,
|
||||
PollingInterval.Medium,
|
||||
fallbackOptions
|
||||
) as DirectoryWatcher;
|
||||
watcher.referenceCount = 0;
|
||||
dirWatchers.set(dirPath, watcher);
|
||||
return watcher;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +374,7 @@ namespace ts {
|
||||
const callbacksCache = createMultiMap<FileWatcherCallback>();
|
||||
const toCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
|
||||
return (fileName, callback, pollingInterval) => {
|
||||
return (fileName, callback, pollingInterval, options) => {
|
||||
const path = toCanonicalFileName(fileName);
|
||||
const existing = cache.get(path);
|
||||
if (existing) {
|
||||
@@ -331,7 +388,8 @@ namespace ts {
|
||||
callbacksCache.get(path),
|
||||
cb => cb(fileName, eventKind)
|
||||
),
|
||||
pollingInterval
|
||||
pollingInterval,
|
||||
options
|
||||
),
|
||||
refCount: 1
|
||||
});
|
||||
@@ -394,6 +452,8 @@ namespace ts {
|
||||
getAccessibleSortedChildDirectories(path: string): readonly string[];
|
||||
directoryExists(dir: string): boolean;
|
||||
realpath(s: string): string;
|
||||
setTimeout: NonNullable<System["setTimeout"]>;
|
||||
clearTimeout: NonNullable<System["clearTimeout"]>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -402,7 +462,7 @@ namespace ts {
|
||||
* (eg on OS that dont support recursive watch using fs.watch use fs.watchFile)
|
||||
*/
|
||||
/*@internal*/
|
||||
export function createRecursiveDirectoryWatcher(host: RecursiveDirectoryWatcherHost): (directoryName: string, callback: DirectoryWatcherCallback) => FileWatcher {
|
||||
export function createDirectoryWatcherSupportingRecursive(host: RecursiveDirectoryWatcherHost): HostWatchDirectory {
|
||||
interface ChildDirectoryWatcher extends FileWatcher {
|
||||
dirName: string;
|
||||
}
|
||||
@@ -414,16 +474,21 @@ namespace ts {
|
||||
}
|
||||
|
||||
const cache = createMap<HostDirectoryWatcher>();
|
||||
const callbackCache = createMultiMap<DirectoryWatcherCallback>();
|
||||
const callbackCache = createMultiMap<{ dirName: string; callback: DirectoryWatcherCallback; }>();
|
||||
const cacheToUpdateChildWatches = createMap<{ dirName: string; options: WatchOptions | undefined; }>();
|
||||
let timerToUpdateChildWatches: any;
|
||||
|
||||
const filePathComparer = getStringComparer(!host.useCaseSensitiveFileNames);
|
||||
const toCanonicalFilePath = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
|
||||
|
||||
return createDirectoryWatcher;
|
||||
return (dirName, callback, recursive, options) => recursive ?
|
||||
createDirectoryWatcher(dirName, options, callback) :
|
||||
host.watchDirectory(dirName, callback, recursive, options);
|
||||
|
||||
/**
|
||||
* Create the directory watcher for the dirPath.
|
||||
*/
|
||||
function createDirectoryWatcher(dirName: string, callback?: DirectoryWatcherCallback): ChildDirectoryWatcher {
|
||||
function createDirectoryWatcher(dirName: string, options: WatchOptions | undefined, callback?: DirectoryWatcherCallback): ChildDirectoryWatcher {
|
||||
const dirPath = toCanonicalFilePath(dirName) as Path;
|
||||
let directoryWatcher = cache.get(dirPath);
|
||||
if (directoryWatcher) {
|
||||
@@ -434,32 +499,34 @@ namespace ts {
|
||||
watcher: host.watchDirectory(dirName, fileName => {
|
||||
if (isIgnoredPath(fileName)) return;
|
||||
|
||||
// Call the actual callback
|
||||
callbackCache.forEach((callbacks, rootDirName) => {
|
||||
if (rootDirName === dirPath || (startsWith(dirPath, rootDirName) && dirPath[rootDirName.length] === directorySeparator)) {
|
||||
callbacks.forEach(callback => callback(fileName));
|
||||
}
|
||||
});
|
||||
if (options?.synchronousWatchDirectory) {
|
||||
// Call the actual callback
|
||||
invokeCallbacks(dirPath, fileName);
|
||||
|
||||
// Iterate through existing children and update the watches if needed
|
||||
updateChildWatches(dirName, dirPath);
|
||||
}),
|
||||
// Iterate through existing children and update the watches if needed
|
||||
updateChildWatches(dirName, dirPath, options);
|
||||
}
|
||||
else {
|
||||
nonSyncUpdateChildWatches(dirName, dirPath, fileName, options);
|
||||
}
|
||||
}, /*recursive*/ false, options),
|
||||
refCount: 1,
|
||||
childWatches: emptyArray
|
||||
};
|
||||
cache.set(dirPath, directoryWatcher);
|
||||
updateChildWatches(dirName, dirPath);
|
||||
updateChildWatches(dirName, dirPath, options);
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callbackCache.add(dirPath, callback);
|
||||
const callbackToAdd = callback && { dirName, callback };
|
||||
if (callbackToAdd) {
|
||||
callbackCache.add(dirPath, callbackToAdd);
|
||||
}
|
||||
|
||||
return {
|
||||
dirName,
|
||||
close: () => {
|
||||
const directoryWatcher = Debug.assertDefined(cache.get(dirPath));
|
||||
if (callback) callbackCache.remove(dirPath, callback);
|
||||
if (callbackToAdd) callbackCache.remove(dirPath, callbackToAdd);
|
||||
directoryWatcher.refCount--;
|
||||
|
||||
if (directoryWatcher.refCount) return;
|
||||
@@ -471,18 +538,103 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
function updateChildWatches(dirName: string, dirPath: Path) {
|
||||
function invokeCallbacks(dirPath: Path, fileNameOrInvokeMap: string | Map<true>) {
|
||||
let fileName: string | undefined;
|
||||
let invokeMap: Map<true> | undefined;
|
||||
if (isString(fileNameOrInvokeMap)) {
|
||||
fileName = fileNameOrInvokeMap;
|
||||
}
|
||||
else {
|
||||
invokeMap = fileNameOrInvokeMap;
|
||||
}
|
||||
// Call the actual callback
|
||||
callbackCache.forEach((callbacks, rootDirName) => {
|
||||
if (invokeMap && invokeMap.has(rootDirName)) return;
|
||||
if (rootDirName === dirPath || (startsWith(dirPath, rootDirName) && dirPath[rootDirName.length] === directorySeparator)) {
|
||||
if (invokeMap) {
|
||||
invokeMap.set(rootDirName, true);
|
||||
}
|
||||
else {
|
||||
callbacks.forEach(({ callback }) => callback(fileName!));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function nonSyncUpdateChildWatches(dirName: string, dirPath: Path, fileName: string, options: WatchOptions | undefined) {
|
||||
// Iterate through existing children and update the watches if needed
|
||||
const parentWatcher = cache.get(dirPath);
|
||||
if (parentWatcher && host.directoryExists(dirName)) {
|
||||
// Schedule the update and postpone invoke for callbacks
|
||||
scheduleUpdateChildWatches(dirName, dirPath, options);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the actual callbacks and remove child watches
|
||||
invokeCallbacks(dirPath, fileName);
|
||||
removeChildWatches(parentWatcher);
|
||||
}
|
||||
|
||||
function scheduleUpdateChildWatches(dirName: string, dirPath: Path, options: WatchOptions | undefined) {
|
||||
if (!cacheToUpdateChildWatches.has(dirPath)) {
|
||||
cacheToUpdateChildWatches.set(dirPath, { dirName, options });
|
||||
}
|
||||
if (timerToUpdateChildWatches) {
|
||||
host.clearTimeout(timerToUpdateChildWatches);
|
||||
timerToUpdateChildWatches = undefined;
|
||||
}
|
||||
timerToUpdateChildWatches = host.setTimeout(onTimerToUpdateChildWatches, 1000);
|
||||
}
|
||||
|
||||
function onTimerToUpdateChildWatches() {
|
||||
timerToUpdateChildWatches = undefined;
|
||||
sysLog(`sysLog:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size}`);
|
||||
const start = timestamp();
|
||||
const invokeMap = createMap<true>();
|
||||
|
||||
while (!timerToUpdateChildWatches && cacheToUpdateChildWatches.size) {
|
||||
const { value: [dirPath, { dirName, options }], done } = cacheToUpdateChildWatches.entries().next();
|
||||
Debug.assert(!done);
|
||||
cacheToUpdateChildWatches.delete(dirPath);
|
||||
// Because the child refresh is fresh, we would need to invalidate whole root directory being watched
|
||||
// to ensure that all the changes are reflected at this time
|
||||
invokeCallbacks(dirPath as Path, invokeMap);
|
||||
updateChildWatches(dirName, dirPath as Path, options);
|
||||
}
|
||||
|
||||
sysLog(`sysLog:: invokingWatchers:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`);
|
||||
callbackCache.forEach((callbacks, rootDirName) => {
|
||||
if (invokeMap.has(rootDirName)) {
|
||||
callbacks.forEach(({ callback, dirName }) => callback(dirName));
|
||||
}
|
||||
});
|
||||
|
||||
const elapsed = timestamp() - start;
|
||||
sysLog(`sysLog:: Elapsed ${elapsed}ms:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size} ${timerToUpdateChildWatches}`);
|
||||
}
|
||||
|
||||
function removeChildWatches(parentWatcher: HostDirectoryWatcher | undefined) {
|
||||
if (!parentWatcher) return;
|
||||
const existingChildWatches = parentWatcher.childWatches;
|
||||
parentWatcher.childWatches = emptyArray;
|
||||
for (const childWatcher of existingChildWatches) {
|
||||
childWatcher.close();
|
||||
removeChildWatches(cache.get(toCanonicalFilePath(childWatcher.dirName)));
|
||||
}
|
||||
}
|
||||
|
||||
function updateChildWatches(dirName: string, dirPath: Path, options: WatchOptions | undefined) {
|
||||
// Iterate through existing children and update the watches if needed
|
||||
const parentWatcher = cache.get(dirPath);
|
||||
if (parentWatcher) {
|
||||
parentWatcher.childWatches = watchChildDirectories(dirName, parentWatcher.childWatches);
|
||||
parentWatcher.childWatches = watchChildDirectories(dirName, parentWatcher.childWatches, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch the directories in the parentDir
|
||||
*/
|
||||
function watchChildDirectories(parentDir: string, existingChildWatches: ChildWatches): ChildWatches {
|
||||
function watchChildDirectories(parentDir: string, existingChildWatches: ChildWatches, options: WatchOptions | undefined): ChildWatches {
|
||||
let newChildWatches: ChildDirectoryWatcher[] | undefined;
|
||||
enumerateInsertsAndDeletes<string, ChildDirectoryWatcher>(
|
||||
host.directoryExists(parentDir) ? mapDefined(host.getAccessibleSortedChildDirectories(parentDir), child => {
|
||||
@@ -504,7 +656,7 @@ namespace ts {
|
||||
* Create new childDirectoryWatcher and add it to the new ChildDirectoryWatcher list
|
||||
*/
|
||||
function createAndAddChildDirectoryWatcher(childName: string) {
|
||||
const result = createDirectoryWatcher(childName);
|
||||
const result = createDirectoryWatcher(childName, options);
|
||||
addChildDirectoryWatcher(result);
|
||||
}
|
||||
|
||||
@@ -527,6 +679,252 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export type FsWatchCallback = (eventName: "rename" | "change", relativeFileName: string | undefined) => void;
|
||||
/*@internal*/
|
||||
export type FsWatch = (fileOrDirectory: string, entryKind: FileSystemEntryKind, callback: FsWatchCallback, recursive: boolean, fallbackPollingInterval: PollingInterval, fallbackOptions: WatchOptions | undefined) => FileWatcher;
|
||||
|
||||
/*@internal*/
|
||||
export const enum FileSystemEntryKind {
|
||||
File,
|
||||
Directory,
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function createFileWatcherCallback(callback: FsWatchCallback): FileWatcherCallback {
|
||||
return (_fileName, eventKind) => callback(eventKind === FileWatcherEventKind.Changed ? "change" : "rename", "");
|
||||
}
|
||||
|
||||
function createFsWatchCallbackForFileWatcherCallback(
|
||||
fileName: string,
|
||||
callback: FileWatcherCallback,
|
||||
fileExists: System["fileExists"]
|
||||
): FsWatchCallback {
|
||||
return eventName => {
|
||||
if (eventName === "rename") {
|
||||
callback(fileName, fileExists(fileName) ? FileWatcherEventKind.Created : FileWatcherEventKind.Deleted);
|
||||
}
|
||||
else {
|
||||
// Change
|
||||
callback(fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createFsWatchCallbackForDirectoryWatcherCallback(directoryName: string, callback: DirectoryWatcherCallback): FsWatchCallback {
|
||||
return (eventName, relativeFileName) => {
|
||||
// In watchDirectory we only care about adding and removing files (when event name is
|
||||
// "rename"); changes made within files are handled by corresponding fileWatchers (when
|
||||
// event name is "change")
|
||||
if (eventName === "rename") {
|
||||
// When deleting a file, the passed baseFileName is null
|
||||
callback(!relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export interface CreateSystemWatchFunctions {
|
||||
// Polling watch file
|
||||
pollingWatchFile: HostWatchFile;
|
||||
// For dynamic polling watch file
|
||||
getModifiedTime: NonNullable<System["getModifiedTime"]>;
|
||||
setTimeout: NonNullable<System["setTimeout"]>;
|
||||
clearTimeout: NonNullable<System["clearTimeout"]>;
|
||||
// For fs events :
|
||||
fsWatch: FsWatch;
|
||||
fileExists: System["fileExists"];
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
fsSupportsRecursiveFsWatch: boolean;
|
||||
directoryExists: System["directoryExists"];
|
||||
getAccessibleSortedChildDirectories(path: string): readonly string[];
|
||||
realpath(s: string): string;
|
||||
// For backward compatibility environment variables
|
||||
tscWatchFile: string | undefined;
|
||||
useNonPollingWatchers?: boolean;
|
||||
tscWatchDirectory: string | undefined;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function createSystemWatchFunctions({
|
||||
pollingWatchFile,
|
||||
getModifiedTime,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
fsWatch,
|
||||
fileExists,
|
||||
useCaseSensitiveFileNames,
|
||||
fsSupportsRecursiveFsWatch,
|
||||
directoryExists,
|
||||
getAccessibleSortedChildDirectories,
|
||||
realpath,
|
||||
tscWatchFile,
|
||||
useNonPollingWatchers,
|
||||
tscWatchDirectory,
|
||||
}: CreateSystemWatchFunctions): { watchFile: HostWatchFile; watchDirectory: HostWatchDirectory; } {
|
||||
let dynamicPollingWatchFile: HostWatchFile | undefined;
|
||||
let nonPollingWatchFile: HostWatchFile | undefined;
|
||||
let hostRecursiveDirectoryWatcher: HostWatchDirectory | undefined;
|
||||
return {
|
||||
watchFile,
|
||||
watchDirectory
|
||||
};
|
||||
|
||||
function watchFile(fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined): FileWatcher {
|
||||
options = updateOptionsForWatchFile(options, useNonPollingWatchers);
|
||||
const watchFileKind = Debug.assertDefined(options.watchFile);
|
||||
switch (watchFileKind) {
|
||||
case WatchFileKind.FixedPollingInterval:
|
||||
return pollingWatchFile(fileName, callback, PollingInterval.Low, /*options*/ undefined);
|
||||
case WatchFileKind.PriorityPollingInterval:
|
||||
return pollingWatchFile(fileName, callback, pollingInterval, /*options*/ undefined);
|
||||
case WatchFileKind.DynamicPriorityPolling:
|
||||
return ensureDynamicPollingWatchFile()(fileName, callback, pollingInterval, /*options*/ undefined);
|
||||
case WatchFileKind.UseFsEvents:
|
||||
return fsWatch(
|
||||
fileName,
|
||||
FileSystemEntryKind.File,
|
||||
createFsWatchCallbackForFileWatcherCallback(fileName, callback, fileExists),
|
||||
/*recursive*/ false,
|
||||
pollingInterval,
|
||||
getFallbackOptions(options)
|
||||
);
|
||||
case WatchFileKind.UseFsEventsOnParentDirectory:
|
||||
if (!nonPollingWatchFile) {
|
||||
nonPollingWatchFile = createUseFsEventsOnParentDirectoryWatchFile(fsWatch, useCaseSensitiveFileNames);
|
||||
}
|
||||
return nonPollingWatchFile(fileName, callback, pollingInterval, getFallbackOptions(options));
|
||||
default:
|
||||
Debug.assertNever(watchFileKind);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureDynamicPollingWatchFile() {
|
||||
return dynamicPollingWatchFile ||
|
||||
(dynamicPollingWatchFile = createDynamicPriorityPollingWatchFile({ getModifiedTime, setTimeout }));
|
||||
}
|
||||
|
||||
function updateOptionsForWatchFile(options: WatchOptions | undefined, useNonPollingWatchers?: boolean): WatchOptions {
|
||||
if (options && options.watchFile !== undefined) return options;
|
||||
switch (tscWatchFile) {
|
||||
case "PriorityPollingInterval":
|
||||
// Use polling interval based on priority when create watch using host.watchFile
|
||||
return { watchFile: WatchFileKind.PriorityPollingInterval };
|
||||
case "DynamicPriorityPolling":
|
||||
// Use polling interval but change the interval depending on file changes and their default polling interval
|
||||
return { watchFile: WatchFileKind.DynamicPriorityPolling };
|
||||
case "UseFsEvents":
|
||||
// Use notifications from FS to watch with falling back to fs.watchFile
|
||||
return generateWatchFileOptions(WatchFileKind.UseFsEvents, PollingWatchKind.PriorityInterval, options);
|
||||
case "UseFsEventsWithFallbackDynamicPolling":
|
||||
// Use notifications from FS to watch with falling back to dynamic watch file
|
||||
return generateWatchFileOptions(WatchFileKind.UseFsEvents, PollingWatchKind.DynamicPriority, options);
|
||||
case "UseFsEventsOnParentDirectory":
|
||||
useNonPollingWatchers = true;
|
||||
// fall through
|
||||
default:
|
||||
return useNonPollingWatchers ?
|
||||
// Use notifications from FS to watch with falling back to fs.watchFile
|
||||
generateWatchFileOptions(WatchFileKind.UseFsEventsOnParentDirectory, PollingWatchKind.PriorityInterval, options) :
|
||||
// Default to do not use fixed polling interval
|
||||
{ watchFile: WatchFileKind.FixedPollingInterval };
|
||||
}
|
||||
}
|
||||
|
||||
function generateWatchFileOptions(
|
||||
watchFile: WatchFileKind,
|
||||
fallbackPolling: PollingWatchKind,
|
||||
options: WatchOptions | undefined
|
||||
): WatchOptions {
|
||||
const defaultFallbackPolling = options?.fallbackPolling;
|
||||
return {
|
||||
watchFile,
|
||||
fallbackPolling: defaultFallbackPolling === undefined ?
|
||||
fallbackPolling :
|
||||
defaultFallbackPolling
|
||||
};
|
||||
}
|
||||
|
||||
function watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher {
|
||||
if (fsSupportsRecursiveFsWatch) {
|
||||
return fsWatch(
|
||||
directoryName,
|
||||
FileSystemEntryKind.Directory,
|
||||
createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback),
|
||||
recursive,
|
||||
PollingInterval.Medium,
|
||||
getFallbackOptions(options)
|
||||
);
|
||||
}
|
||||
|
||||
if (!hostRecursiveDirectoryWatcher) {
|
||||
hostRecursiveDirectoryWatcher = createDirectoryWatcherSupportingRecursive({
|
||||
useCaseSensitiveFileNames,
|
||||
directoryExists,
|
||||
getAccessibleSortedChildDirectories,
|
||||
watchDirectory: nonRecursiveWatchDirectory,
|
||||
realpath,
|
||||
setTimeout,
|
||||
clearTimeout
|
||||
});
|
||||
}
|
||||
return hostRecursiveDirectoryWatcher(directoryName, callback, recursive, options);
|
||||
}
|
||||
|
||||
function nonRecursiveWatchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher {
|
||||
Debug.assert(!recursive);
|
||||
options = updateOptionsForWatchDirectory(options);
|
||||
const watchDirectoryKind = Debug.assertDefined(options.watchDirectory);
|
||||
switch (watchDirectoryKind) {
|
||||
case WatchDirectoryKind.FixedPollingInterval:
|
||||
return pollingWatchFile(
|
||||
directoryName,
|
||||
() => callback(directoryName),
|
||||
PollingInterval.Medium,
|
||||
/*options*/ undefined
|
||||
);
|
||||
case WatchDirectoryKind.DynamicPriorityPolling:
|
||||
return ensureDynamicPollingWatchFile()(
|
||||
directoryName,
|
||||
() => callback(directoryName),
|
||||
PollingInterval.Medium,
|
||||
/*options*/ undefined
|
||||
);
|
||||
case WatchDirectoryKind.UseFsEvents:
|
||||
return fsWatch(
|
||||
directoryName,
|
||||
FileSystemEntryKind.Directory,
|
||||
createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback),
|
||||
recursive,
|
||||
PollingInterval.Medium,
|
||||
getFallbackOptions(options)
|
||||
);
|
||||
default:
|
||||
Debug.assertNever(watchDirectoryKind);
|
||||
}
|
||||
}
|
||||
|
||||
function updateOptionsForWatchDirectory(options: WatchOptions | undefined): WatchOptions {
|
||||
if (options && options.watchDirectory !== undefined) return options;
|
||||
switch (tscWatchDirectory) {
|
||||
case "RecursiveDirectoryUsingFsWatchFile":
|
||||
// Use polling interval based on priority when create watch using host.watchFile
|
||||
return { watchDirectory: WatchDirectoryKind.FixedPollingInterval };
|
||||
case "RecursiveDirectoryUsingDynamicPriorityPolling":
|
||||
// Use polling interval but change the interval depending on file changes and their default polling interval
|
||||
return { watchDirectory: WatchDirectoryKind.DynamicPriorityPolling };
|
||||
default:
|
||||
const defaultFallbackPolling = options?.fallbackPolling;
|
||||
return {
|
||||
watchDirectory: WatchDirectoryKind.UseFsEvents,
|
||||
fallbackPolling: defaultFallbackPolling !== undefined ?
|
||||
defaultFallbackPolling :
|
||||
undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* patch writefile to create folder before writing the file
|
||||
*/
|
||||
@@ -641,8 +1039,8 @@ namespace ts {
|
||||
* @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that
|
||||
* use native OS file watching
|
||||
*/
|
||||
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
|
||||
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
|
||||
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
|
||||
resolvePath(path: string): string;
|
||||
fileExists(path: string): boolean;
|
||||
directoryExists(path: string): boolean;
|
||||
@@ -769,17 +1167,25 @@ namespace ts {
|
||||
|
||||
const platform: string = _os.platform();
|
||||
const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
|
||||
|
||||
const enum FileSystemEntryKind {
|
||||
File,
|
||||
Directory
|
||||
}
|
||||
|
||||
const useNonPollingWatchers = process.env.TSC_NONPOLLING_WATCHER;
|
||||
const tscWatchFile = process.env.TSC_WATCHFILE;
|
||||
const tscWatchDirectory = process.env.TSC_WATCHDIRECTORY;
|
||||
const fsWatchFile = createSingleFileWatcherPerName(fsWatchFileWorker, useCaseSensitiveFileNames);
|
||||
let dynamicPollingWatchFile: HostWatchFile | undefined;
|
||||
const fsSupportsRecursiveFsWatch = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin");
|
||||
const { watchFile, watchDirectory } = createSystemWatchFunctions({
|
||||
pollingWatchFile: createSingleFileWatcherPerName(fsWatchFileWorker, useCaseSensitiveFileNames),
|
||||
getModifiedTime,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
fsWatch,
|
||||
useCaseSensitiveFileNames,
|
||||
fileExists,
|
||||
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
|
||||
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
|
||||
fsSupportsRecursiveFsWatch,
|
||||
directoryExists,
|
||||
getAccessibleSortedChildDirectories: path => getAccessibleFileSystemEntries(path).directories,
|
||||
realpath,
|
||||
tscWatchFile: process.env.TSC_WATCHFILE,
|
||||
useNonPollingWatchers: process.env.TSC_NONPOLLING_WATCHER,
|
||||
tscWatchDirectory: process.env.TSC_WATCHDIRECTORY,
|
||||
});
|
||||
const nodeSystem: System = {
|
||||
args: process.argv.slice(2),
|
||||
newLine: _os.EOL,
|
||||
@@ -792,8 +1198,8 @@ namespace ts {
|
||||
},
|
||||
readFile,
|
||||
writeFile,
|
||||
watchFile: getWatchFile(),
|
||||
watchDirectory: getWatchDirectory(),
|
||||
watchFile,
|
||||
watchDirectory,
|
||||
resolvePath: path => _path.resolve(path),
|
||||
fileExists,
|
||||
directoryExists,
|
||||
@@ -994,112 +1400,8 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function getWatchFile(): HostWatchFile {
|
||||
switch (tscWatchFile) {
|
||||
case "PriorityPollingInterval":
|
||||
// Use polling interval based on priority when create watch using host.watchFile
|
||||
return fsWatchFile;
|
||||
case "DynamicPriorityPolling":
|
||||
// Use polling interval but change the interval depending on file changes and their default polling interval
|
||||
return createDynamicPriorityPollingWatchFile({ getModifiedTime, setTimeout });
|
||||
case "UseFsEvents":
|
||||
// Use notifications from FS to watch with falling back to fs.watchFile
|
||||
return watchFileUsingFsWatch;
|
||||
case "UseFsEventsWithFallbackDynamicPolling":
|
||||
// Use notifications from FS to watch with falling back to dynamic watch file
|
||||
dynamicPollingWatchFile = createDynamicPriorityPollingWatchFile({ getModifiedTime, setTimeout });
|
||||
return createWatchFileUsingDynamicWatchFile(dynamicPollingWatchFile);
|
||||
case "UseFsEventsOnParentDirectory":
|
||||
// Use notifications from FS to watch with falling back to fs.watchFile
|
||||
return createNonPollingWatchFile();
|
||||
}
|
||||
return useNonPollingWatchers ?
|
||||
createNonPollingWatchFile() :
|
||||
// Default to do not use polling interval as it is before this experiment branch
|
||||
(fileName, callback) => fsWatchFile(fileName, callback, /*pollingInterval*/ undefined);
|
||||
}
|
||||
|
||||
function getWatchDirectory(): HostWatchDirectory {
|
||||
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
|
||||
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
|
||||
const fsSupportsRecursive = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin");
|
||||
if (fsSupportsRecursive) {
|
||||
return watchDirectoryUsingFsWatch;
|
||||
}
|
||||
|
||||
// defer watchDirectoryRecursively as it depends on `ts.createMap()` which may not be usable yet.
|
||||
const watchDirectory = tscWatchDirectory === "RecursiveDirectoryUsingFsWatchFile" ?
|
||||
createWatchDirectoryUsing(fsWatchFile) :
|
||||
tscWatchDirectory === "RecursiveDirectoryUsingDynamicPriorityPolling" ?
|
||||
createWatchDirectoryUsing(dynamicPollingWatchFile || createDynamicPriorityPollingWatchFile({ getModifiedTime, setTimeout })) :
|
||||
watchDirectoryUsingFsWatch;
|
||||
const watchDirectoryRecursively = createRecursiveDirectoryWatcher({
|
||||
useCaseSensitiveFileNames,
|
||||
directoryExists,
|
||||
getAccessibleSortedChildDirectories: path => getAccessibleFileSystemEntries(path).directories,
|
||||
watchDirectory,
|
||||
realpath
|
||||
});
|
||||
|
||||
return (directoryName, callback, recursive) => {
|
||||
if (recursive) {
|
||||
return watchDirectoryRecursively(directoryName, callback);
|
||||
}
|
||||
return watchDirectory(directoryName, callback);
|
||||
};
|
||||
}
|
||||
|
||||
function createNonPollingWatchFile() {
|
||||
// One file can have multiple watchers
|
||||
const fileWatcherCallbacks = createMultiMap<FileWatcherCallback>();
|
||||
const dirWatchers = createMap<DirectoryWatcher>();
|
||||
const toCanonicalName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
return nonPollingWatchFile;
|
||||
|
||||
function nonPollingWatchFile(fileName: string, callback: FileWatcherCallback): FileWatcher {
|
||||
const filePath = toCanonicalName(fileName);
|
||||
fileWatcherCallbacks.add(filePath, callback);
|
||||
const dirPath = getDirectoryPath(filePath) || ".";
|
||||
const watcher = dirWatchers.get(dirPath) || createDirectoryWatcher(getDirectoryPath(fileName) || ".", dirPath);
|
||||
watcher.referenceCount++;
|
||||
return {
|
||||
close: () => {
|
||||
if (watcher.referenceCount === 1) {
|
||||
watcher.close();
|
||||
dirWatchers.delete(dirPath);
|
||||
}
|
||||
else {
|
||||
watcher.referenceCount--;
|
||||
}
|
||||
fileWatcherCallbacks.remove(filePath, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createDirectoryWatcher(dirName: string, dirPath: string) {
|
||||
const watcher = fsWatchDirectory(
|
||||
dirName,
|
||||
(_eventName: string, relativeFileName) => {
|
||||
// When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
|
||||
if (!isString(relativeFileName)) { return; }
|
||||
const fileName = getNormalizedAbsolutePath(relativeFileName, dirName);
|
||||
// Some applications save a working file via rename operations
|
||||
const callbacks = fileName && fileWatcherCallbacks.get(toCanonicalName(fileName));
|
||||
if (callbacks) {
|
||||
for (const fileCallback of callbacks) {
|
||||
fileCallback(fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
) as DirectoryWatcher;
|
||||
watcher.referenceCount = 0;
|
||||
dirWatchers.set(dirPath, watcher);
|
||||
return watcher;
|
||||
}
|
||||
}
|
||||
|
||||
function fsWatchFileWorker(fileName: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher {
|
||||
_fs.watchFile(fileName, { persistent: true, interval: pollingInterval || 250 }, fileChanged);
|
||||
function fsWatchFileWorker(fileName: string, callback: FileWatcherCallback, pollingInterval: number): FileWatcher {
|
||||
_fs.watchFile(fileName, { persistent: true, interval: pollingInterval }, fileChanged);
|
||||
let eventKind: FileWatcherEventKind;
|
||||
return {
|
||||
close: () => _fs.unwatchFile(fileName, fileChanged)
|
||||
@@ -1131,37 +1433,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
type FsWatchCallback = (eventName: "rename" | "change", relativeFileName: string | undefined) => void;
|
||||
|
||||
function createFileWatcherCallback(callback: FsWatchCallback): FileWatcherCallback {
|
||||
return (_fileName, eventKind) => callback(eventKind === FileWatcherEventKind.Changed ? "change" : "rename", "");
|
||||
}
|
||||
|
||||
function createFsWatchCallbackForFileWatcherCallback(fileName: string, callback: FileWatcherCallback): FsWatchCallback {
|
||||
return eventName => {
|
||||
if (eventName === "rename") {
|
||||
callback(fileName, fileExists(fileName) ? FileWatcherEventKind.Created : FileWatcherEventKind.Deleted);
|
||||
}
|
||||
else {
|
||||
// Change
|
||||
callback(fileName, FileWatcherEventKind.Changed);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createFsWatchCallbackForDirectoryWatcherCallback(directoryName: string, callback: DirectoryWatcherCallback): FsWatchCallback {
|
||||
return (eventName, relativeFileName) => {
|
||||
// In watchDirectory we only care about adding and removing files (when event name is
|
||||
// "rename"); changes made within files are handled by corresponding fileWatchers (when
|
||||
// event name is "change")
|
||||
if (eventName === "rename") {
|
||||
// When deleting a file, the passed baseFileName is null
|
||||
callback(!relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function fsWatch(fileOrDirectory: string, entryKind: FileSystemEntryKind.File | FileSystemEntryKind.Directory, callback: FsWatchCallback, recursive: boolean, fallbackPollingWatchFile: HostWatchFile, pollingInterval?: number): FileWatcher {
|
||||
function fsWatch(
|
||||
fileOrDirectory: string,
|
||||
entryKind: FileSystemEntryKind,
|
||||
callback: FsWatchCallback,
|
||||
recursive: boolean,
|
||||
fallbackPollingInterval: PollingInterval,
|
||||
fallbackOptions: WatchOptions | undefined
|
||||
): FileWatcher {
|
||||
let options: any;
|
||||
let lastDirectoryPartWithDirectorySeparator: string | undefined;
|
||||
let lastDirectoryPart: string | undefined;
|
||||
@@ -1205,7 +1484,7 @@ namespace ts {
|
||||
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
|
||||
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
|
||||
if (options === undefined) {
|
||||
if (isNode4OrLater && (process.platform === "win32" || process.platform === "darwin")) {
|
||||
if (fsSupportsRecursiveFsWatch) {
|
||||
options = { persistent: true, recursive: !!recursive };
|
||||
}
|
||||
else {
|
||||
@@ -1250,7 +1529,12 @@ namespace ts {
|
||||
*/
|
||||
function watchPresentFileSystemEntryWithFsWatchFile(): FileWatcher {
|
||||
sysLog(`sysLog:: ${fileOrDirectory}:: Changing to fsWatchFile`);
|
||||
return fallbackPollingWatchFile(fileOrDirectory, createFileWatcherCallback(callback), pollingInterval);
|
||||
return watchFile(
|
||||
fileOrDirectory,
|
||||
createFileWatcherCallback(callback),
|
||||
fallbackPollingInterval,
|
||||
fallbackOptions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1258,37 +1542,22 @@ namespace ts {
|
||||
* and switch to existing file or directory when the missing filesystem entry is created
|
||||
*/
|
||||
function watchMissingFileSystemEntry(): FileWatcher {
|
||||
return fallbackPollingWatchFile(fileOrDirectory, (_fileName, eventKind) => {
|
||||
if (eventKind === FileWatcherEventKind.Created && fileSystemEntryExists(fileOrDirectory, entryKind)) {
|
||||
// Call the callback for current file or directory
|
||||
// For now it could be callback for the inner directory creation,
|
||||
// but just return current directory, better than current no-op
|
||||
invokeCallbackAndUpdateWatcher(watchPresentFileSystemEntry);
|
||||
}
|
||||
}, pollingInterval);
|
||||
return watchFile(
|
||||
fileOrDirectory,
|
||||
(_fileName, eventKind) => {
|
||||
if (eventKind === FileWatcherEventKind.Created && fileSystemEntryExists(fileOrDirectory, entryKind)) {
|
||||
// Call the callback for current file or directory
|
||||
// For now it could be callback for the inner directory creation,
|
||||
// but just return current directory, better than current no-op
|
||||
invokeCallbackAndUpdateWatcher(watchPresentFileSystemEntry);
|
||||
}
|
||||
},
|
||||
fallbackPollingInterval,
|
||||
fallbackOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function watchFileUsingFsWatch(fileName: string, callback: FileWatcherCallback, pollingInterval?: number) {
|
||||
return fsWatch(fileName, FileSystemEntryKind.File, createFsWatchCallbackForFileWatcherCallback(fileName, callback), /*recursive*/ false, fsWatchFile, pollingInterval);
|
||||
}
|
||||
|
||||
function createWatchFileUsingDynamicWatchFile(watchFile: HostWatchFile): HostWatchFile {
|
||||
return (fileName, callback, pollingInterval) => fsWatch(fileName, FileSystemEntryKind.File, createFsWatchCallbackForFileWatcherCallback(fileName, callback), /*recursive*/ false, watchFile, pollingInterval);
|
||||
}
|
||||
|
||||
function fsWatchDirectory(directoryName: string, callback: FsWatchCallback, recursive?: boolean): FileWatcher {
|
||||
return fsWatch(directoryName, FileSystemEntryKind.Directory, callback, !!recursive, fsWatchFile);
|
||||
}
|
||||
|
||||
function watchDirectoryUsingFsWatch(directoryName: string, callback: DirectoryWatcherCallback, recursive?: boolean) {
|
||||
return fsWatchDirectory(directoryName, createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback), recursive);
|
||||
}
|
||||
|
||||
function createWatchDirectoryUsing(fsWatchFile: HostWatchFile): HostWatchDirectory {
|
||||
return (directoryName, callback) => fsWatchFile(directoryName, () => callback(directoryName), PollingInterval.Medium);
|
||||
}
|
||||
|
||||
function readFileWorker(fileName: string, _encoding?: string): string | undefined {
|
||||
if (!fileExists(fileName)) {
|
||||
return undefined;
|
||||
|
||||
@@ -205,8 +205,8 @@ namespace ts {
|
||||
return createSolutionBuilderWorker(/*watch*/ false, host, rootNames, defaultOptions);
|
||||
}
|
||||
|
||||
export function createSolutionBuilderWithWatch<T extends BuilderProgram>(host: SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions): SolutionBuilder<T> {
|
||||
return createSolutionBuilderWorker(/*watch*/ true, host, rootNames, defaultOptions);
|
||||
export function createSolutionBuilderWithWatch<T extends BuilderProgram>(host: SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions, baseWatchOptions?: WatchOptions): SolutionBuilder<T> {
|
||||
return createSolutionBuilderWorker(/*watch*/ true, host, rootNames, defaultOptions, baseWatchOptions);
|
||||
}
|
||||
|
||||
type ConfigFileCacheEntry = ParsedCommandLine | Diagnostic;
|
||||
@@ -232,6 +232,7 @@ namespace ts {
|
||||
readonly options: BuildOptions;
|
||||
readonly baseCompilerOptions: CompilerOptions;
|
||||
readonly rootNames: readonly string[];
|
||||
readonly baseWatchOptions: WatchOptions | undefined;
|
||||
|
||||
readonly resolvedConfigFilePaths: Map<ResolvedConfigFilePath>;
|
||||
readonly configFileCache: ConfigFileMap<ConfigFileCacheEntry>;
|
||||
@@ -272,7 +273,7 @@ namespace ts {
|
||||
writeLog: (s: string) => void;
|
||||
}
|
||||
|
||||
function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions): SolutionBuilderState<T> {
|
||||
function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions, baseWatchOptions: WatchOptions | undefined): SolutionBuilderState<T> {
|
||||
const host = hostOrHostWithWatch as SolutionBuilderHost<T>;
|
||||
const hostWithWatch = hostOrHostWithWatch as SolutionBuilderWithWatchHost<T>;
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
@@ -306,6 +307,7 @@ namespace ts {
|
||||
options,
|
||||
baseCompilerOptions,
|
||||
rootNames,
|
||||
baseWatchOptions,
|
||||
|
||||
resolvedConfigFilePaths: createMap(),
|
||||
configFileCache: createConfigFileMap(),
|
||||
@@ -374,7 +376,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
let diagnostic: Diagnostic | undefined;
|
||||
const { parseConfigFileHost, baseCompilerOptions, extendedConfigCache, host } = state;
|
||||
const { parseConfigFileHost, baseCompilerOptions, baseWatchOptions, extendedConfigCache, host } = state;
|
||||
let parsed: ParsedCommandLine | undefined;
|
||||
if (host.getParsedCommandLine) {
|
||||
parsed = host.getParsedCommandLine(configFileName);
|
||||
@@ -382,7 +384,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = d => diagnostic = d;
|
||||
parsed = getParsedCommandLineOfConfigFile(configFileName, baseCompilerOptions, parseConfigFileHost, extendedConfigCache);
|
||||
parsed = getParsedCommandLineOfConfigFile(configFileName, baseCompilerOptions, parseConfigFileHost, extendedConfigCache, baseWatchOptions);
|
||||
parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = noop;
|
||||
}
|
||||
configFileCache.set(configFilePath, parsed || diagnostic!);
|
||||
@@ -1147,7 +1149,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (reloadLevel === ConfigFileProgramReloadLevel.Full) {
|
||||
watchConfigFile(state, project, projectPath);
|
||||
watchConfigFile(state, project, projectPath, config);
|
||||
watchWildCardDirectories(state, project, projectPath, config);
|
||||
watchInputFiles(state, project, projectPath, config);
|
||||
}
|
||||
@@ -1751,7 +1753,7 @@ namespace ts {
|
||||
reportErrorSummary(state, buildOrder);
|
||||
}
|
||||
|
||||
function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath) {
|
||||
function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) {
|
||||
if (!state.watch || state.allWatchedConfigFiles.has(resolvedPath)) return;
|
||||
state.allWatchedConfigFiles.set(resolvedPath, state.watchFile(
|
||||
state.hostWithWatch,
|
||||
@@ -1760,6 +1762,7 @@ namespace ts {
|
||||
invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Full);
|
||||
},
|
||||
PollingInterval.High,
|
||||
parsed?.watchOptions,
|
||||
WatchType.ConfigFile,
|
||||
resolved
|
||||
));
|
||||
@@ -1820,6 +1823,7 @@ namespace ts {
|
||||
invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Partial);
|
||||
},
|
||||
flags,
|
||||
parsed?.watchOptions,
|
||||
WatchType.WildcardDirectory,
|
||||
resolved
|
||||
)
|
||||
@@ -1837,6 +1841,7 @@ namespace ts {
|
||||
input,
|
||||
() => invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.None),
|
||||
PollingInterval.Low,
|
||||
parsed?.watchOptions,
|
||||
path as Path,
|
||||
WatchType.SourceFile,
|
||||
resolved
|
||||
@@ -1851,10 +1856,9 @@ namespace ts {
|
||||
state.watchAllProjectsPending = false;
|
||||
for (const resolved of getBuildOrderFromAnyBuildOrder(buildOrder)) {
|
||||
const resolvedPath = toResolvedConfigFilePath(state, resolved);
|
||||
// Watch this file
|
||||
watchConfigFile(state, resolved, resolvedPath);
|
||||
|
||||
const cfg = parseConfigFile(state, resolved, resolvedPath);
|
||||
// Watch this file
|
||||
watchConfigFile(state, resolved, resolvedPath, cfg);
|
||||
if (cfg) {
|
||||
// Update watchers for wildcard directories
|
||||
watchWildCardDirectories(state, resolved, resolvedPath, cfg);
|
||||
@@ -1870,9 +1874,9 @@ namespace ts {
|
||||
* can dynamically add/remove other projects based on changes on the rootNames' references
|
||||
*/
|
||||
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: false, host: SolutionBuilderHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions): SolutionBuilder<T>;
|
||||
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: true, host: SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions): SolutionBuilder<T>;
|
||||
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions): SolutionBuilder<T> {
|
||||
const state = createSolutionBuilderState(watch, hostOrHostWithWatch, rootNames, options);
|
||||
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: true, host: SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], defaultOptions: BuildOptions, baseWatchOptions?: WatchOptions): SolutionBuilder<T>;
|
||||
function createSolutionBuilderWorker<T extends BuilderProgram>(watch: boolean, hostOrHostWithWatch: SolutionBuilderHost<T> | SolutionBuilderWithWatchHost<T>, rootNames: readonly string[], options: BuildOptions, baseWatchOptions?: WatchOptions): SolutionBuilder<T> {
|
||||
const state = createSolutionBuilderState(watch, hostOrHostWithWatch, rootNames, options, baseWatchOptions);
|
||||
return {
|
||||
build: (project, cancellationToken) => build(state, project, cancellationToken),
|
||||
clean: project => clean(state, project),
|
||||
|
||||
+33
-2
@@ -4929,6 +4929,26 @@ namespace ts {
|
||||
circular?: boolean;
|
||||
}
|
||||
|
||||
export enum WatchFileKind {
|
||||
FixedPollingInterval,
|
||||
PriorityPollingInterval,
|
||||
DynamicPriorityPolling,
|
||||
UseFsEvents,
|
||||
UseFsEventsOnParentDirectory,
|
||||
}
|
||||
|
||||
export enum WatchDirectoryKind {
|
||||
UseFsEvents,
|
||||
FixedPollingInterval,
|
||||
DynamicPriorityPolling,
|
||||
}
|
||||
|
||||
export enum PollingWatchKind {
|
||||
FixedInterval,
|
||||
PriorityInterval,
|
||||
DynamicPriority,
|
||||
}
|
||||
|
||||
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | ProjectReference[] | null | undefined;
|
||||
|
||||
export interface CompilerOptions {
|
||||
@@ -5043,6 +5063,15 @@ namespace ts {
|
||||
[option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined;
|
||||
}
|
||||
|
||||
export interface WatchOptions {
|
||||
watchFile?: WatchFileKind;
|
||||
watchDirectory?: WatchDirectoryKind;
|
||||
fallbackPolling?: PollingWatchKind;
|
||||
synchronousWatchDirectory?: boolean;
|
||||
|
||||
[option: string]: CompilerOptionsValue | undefined;
|
||||
}
|
||||
|
||||
export interface TypeAcquisition {
|
||||
/**
|
||||
* @deprecated typingOptions.enableAutoDiscovery
|
||||
@@ -5130,6 +5159,7 @@ namespace ts {
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
fileNames: string[];
|
||||
projectReferences?: readonly ProjectReference[];
|
||||
watchOptions?: WatchOptions;
|
||||
raw?: any;
|
||||
errors: Diagnostic[];
|
||||
wildcardDirectories?: MapLike<WatchDirectoryFlags>;
|
||||
@@ -5210,7 +5240,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface DidYouMeanOptionalDiagnostics {
|
||||
export interface DidYouMeanOptionsDiagnostics {
|
||||
optionDeclarations: CommandLineOption[];
|
||||
unknownOptionDiagnostic: DiagnosticMessage,
|
||||
unknownDidYouMeanDiagnostic: DiagnosticMessage,
|
||||
}
|
||||
@@ -5219,7 +5250,7 @@ namespace ts {
|
||||
export interface TsConfigOnlyOption extends CommandLineOptionBase {
|
||||
type: "object";
|
||||
elementOptions?: Map<CommandLineOption>;
|
||||
extraKeyDiagnostics?: DidYouMeanOptionalDiagnostics;
|
||||
extraKeyDiagnostics?: DidYouMeanOptionsDiagnostics;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
@@ -89,10 +89,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** Parses config file using System interface */
|
||||
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) {
|
||||
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, watchOptionsToExtend: WatchOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter) {
|
||||
const host: ParseConfigFileHost = <any>system;
|
||||
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic);
|
||||
const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host);
|
||||
const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host, /*extendedConfigCache*/ undefined, watchOptionsToExtend);
|
||||
host.onUnRecoverableConfigFileDiagnostic = undefined!; // TODO: GH#18217
|
||||
return result;
|
||||
}
|
||||
@@ -419,22 +419,24 @@ namespace ts {
|
||||
/**
|
||||
* Creates the watch compiler host from system for config file in watch mode
|
||||
*/
|
||||
export function createWatchCompilerHostOfConfigFile<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile<T> {
|
||||
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> {
|
||||
const diagnosticReporter = reportDiagnostic || createDiagnosticReporter(system);
|
||||
const host = createWatchCompilerHost(system, createProgram, diagnosticReporter, reportWatchStatus) as WatchCompilerHostOfConfigFile<T>;
|
||||
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, diagnosticReporter, diagnostic);
|
||||
host.configFileName = configFileName;
|
||||
host.optionsToExtend = optionsToExtend;
|
||||
host.watchOptionsToExtend = watchOptionsToExtend;
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the watch compiler host from system for compiling root files and options in watch mode
|
||||
*/
|
||||
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> {
|
||||
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> {
|
||||
const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions<T>;
|
||||
host.rootFiles = rootFiles;
|
||||
host.options = options;
|
||||
host.watchOptions = watchOptions;
|
||||
host.projectReferences = projectReferences;
|
||||
return host;
|
||||
}
|
||||
|
||||
+22
-17
@@ -52,9 +52,9 @@ namespace ts {
|
||||
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions, errorCount?: number): void;
|
||||
|
||||
/** Used to watch changes in source files, missing files needed to update the program or config file */
|
||||
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
|
||||
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: CompilerOptions): FileWatcher;
|
||||
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
|
||||
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: CompilerOptions): FileWatcher;
|
||||
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
|
||||
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
|
||||
/** If provided, will be used to reset existing delayed compilation */
|
||||
@@ -133,6 +133,8 @@ namespace ts {
|
||||
/** Compiler options */
|
||||
options: CompilerOptions;
|
||||
|
||||
watchOptions?: WatchOptions;
|
||||
|
||||
/** Project References */
|
||||
projectReferences?: readonly ProjectReference[];
|
||||
}
|
||||
@@ -147,6 +149,8 @@ namespace ts {
|
||||
/** Options to extend */
|
||||
optionsToExtend?: CompilerOptions;
|
||||
|
||||
watchOptionsToExtend?: WatchOptions;
|
||||
|
||||
/**
|
||||
* Used to generate source file names from the config file and its include, exclude, files rules
|
||||
* and also to cache the directory stucture
|
||||
@@ -159,7 +163,6 @@ namespace ts {
|
||||
*/
|
||||
/*@internal*/
|
||||
export interface WatchCompilerHostOfConfigFile<T extends BuilderProgram> extends WatchCompilerHost<T> {
|
||||
optionsToExtend?: CompilerOptions;
|
||||
configFileParsingResult?: ParsedCommandLine;
|
||||
}
|
||||
|
||||
@@ -190,14 +193,14 @@ namespace ts {
|
||||
/**
|
||||
* Create the watch compiler host for either configFile or fileNames and its options
|
||||
*/
|
||||
export function createWatchCompilerHost<T extends BuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile<T>;
|
||||
export function createWatchCompilerHost<T extends BuilderProgram>(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: readonly ProjectReference[]): WatchCompilerHostOfFilesAndCompilerOptions<T>;
|
||||
export function createWatchCompilerHost<T extends BuilderProgram>(rootFilesOrConfigFileName: string | string[], options: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: readonly ProjectReference[]): WatchCompilerHostOfFilesAndCompilerOptions<T> | WatchCompilerHostOfConfigFile<T> {
|
||||
export function createWatchCompilerHost<T extends BuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, watchOptionsToExtend?: WatchOptions): WatchCompilerHostOfConfigFile<T>;
|
||||
export function createWatchCompilerHost<T extends BuilderProgram>(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferences?: readonly ProjectReference[], watchOptions?: WatchOptions): WatchCompilerHostOfFilesAndCompilerOptions<T>;
|
||||
export function createWatchCompilerHost<T extends BuilderProgram>(rootFilesOrConfigFileName: string | string[], options: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter, projectReferencesOrWatchOptionsToExtend?: readonly ProjectReference[] | WatchOptions, watchOptions?: WatchOptions): WatchCompilerHostOfFilesAndCompilerOptions<T> | WatchCompilerHostOfConfigFile<T> {
|
||||
if (isArray(rootFilesOrConfigFileName)) {
|
||||
return createWatchCompilerHostOfFilesAndCompilerOptions(rootFilesOrConfigFileName, options!, system, createProgram, reportDiagnostic, reportWatchStatus, projectReferences); // TODO: GH#18217
|
||||
return createWatchCompilerHostOfFilesAndCompilerOptions(rootFilesOrConfigFileName, options!, watchOptions, system, createProgram, reportDiagnostic, reportWatchStatus, projectReferencesOrWatchOptionsToExtend as readonly ProjectReference[]); // TODO: GH#18217
|
||||
}
|
||||
else {
|
||||
return createWatchCompilerHostOfConfigFile(rootFilesOrConfigFileName, options, system, createProgram, reportDiagnostic, reportWatchStatus);
|
||||
return createWatchCompilerHostOfConfigFile(rootFilesOrConfigFileName, options, projectReferencesOrWatchOptionsToExtend as WatchOptions, system, createProgram, reportDiagnostic, reportWatchStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,8 +239,8 @@ namespace ts {
|
||||
|
||||
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, createProgram } = host;
|
||||
let { rootFiles: rootFileNames, options: compilerOptions, projectReferences } = host;
|
||||
const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, watchOptionsToExtend, createProgram } = host;
|
||||
let { rootFiles: rootFileNames, options: compilerOptions, watchOptions, projectReferences } = host;
|
||||
let configFileSpecs: ConfigFileSpecs;
|
||||
let configFileParsingDiagnostics: Diagnostic[] | undefined;
|
||||
let canConfigFileJsonReportNoInputFiles = false;
|
||||
@@ -270,7 +273,7 @@ namespace ts {
|
||||
writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`);
|
||||
let configFileWatcher: FileWatcher | undefined;
|
||||
if (configFileName) {
|
||||
configFileWatcher = watchFile(host, configFileName, scheduleProgramReload, PollingInterval.High, WatchType.ConfigFile);
|
||||
configFileWatcher = watchFile(host, configFileName, scheduleProgramReload, PollingInterval.High, watchOptions, WatchType.ConfigFile);
|
||||
}
|
||||
|
||||
const compilerHost = createCompilerHostFromProgramHost(host, () => compilerOptions, directoryStructureHost) as CompilerHost & ResolutionCacheHost;
|
||||
@@ -285,8 +288,8 @@ namespace ts {
|
||||
// Members for ResolutionCacheHost
|
||||
compilerHost.toPath = toPath;
|
||||
compilerHost.getCompilationSettings = () => compilerOptions;
|
||||
compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, WatchType.FailedLookupLocations);
|
||||
compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, WatchType.TypeRoots);
|
||||
compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, watchOptions, WatchType.FailedLookupLocations);
|
||||
compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, watchOptions, WatchType.TypeRoots);
|
||||
compilerHost.getCachedDirectoryStructureHost = () => cachedDirectoryStructureHost;
|
||||
compilerHost.onInvalidatedResolution = scheduleProgramUpdate;
|
||||
compilerHost.onChangedAutomaticTypeDirectiveNames = () => {
|
||||
@@ -469,7 +472,7 @@ namespace ts {
|
||||
(hostSourceFile as FilePresentOnHost).sourceFile = sourceFile;
|
||||
hostSourceFile.version = sourceFile.version;
|
||||
if (!hostSourceFile.fileWatcher) {
|
||||
hostSourceFile.fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, WatchType.SourceFile);
|
||||
hostSourceFile.fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, path, WatchType.SourceFile);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -482,7 +485,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
if (sourceFile) {
|
||||
const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, path, WatchType.SourceFile);
|
||||
const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, path, WatchType.SourceFile);
|
||||
sourceFilesCache.set(path, { sourceFile, version: sourceFile.version, fileWatcher });
|
||||
}
|
||||
else {
|
||||
@@ -611,12 +614,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseConfigFile() {
|
||||
setConfigFileParsingResult(getParsedCommandLineOfConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost)!); // TODO: GH#18217
|
||||
setConfigFileParsingResult(getParsedCommandLineOfConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost, /*extendedConfigCache*/ undefined, watchOptionsToExtend)!); // TODO: GH#18217
|
||||
}
|
||||
|
||||
function setConfigFileParsingResult(configFileParseResult: ParsedCommandLine) {
|
||||
rootFileNames = configFileParseResult.fileNames;
|
||||
compilerOptions = configFileParseResult.options;
|
||||
watchOptions = configFileParseResult.watchOptions;
|
||||
configFileSpecs = configFileParseResult.configFileSpecs!; // TODO: GH#18217
|
||||
projectReferences = configFileParseResult.projectReferences;
|
||||
configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult).slice();
|
||||
@@ -645,7 +649,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function watchMissingFilePath(missingFilePath: Path) {
|
||||
return watchFilePath(host, missingFilePath, onMissingFileChange, PollingInterval.Medium, missingFilePath, WatchType.MissingFile);
|
||||
return watchFilePath(host, missingFilePath, onMissingFileChange, PollingInterval.Medium, watchOptions, missingFilePath, WatchType.MissingFile);
|
||||
}
|
||||
|
||||
function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) {
|
||||
@@ -709,6 +713,7 @@ namespace ts {
|
||||
}
|
||||
},
|
||||
flags,
|
||||
watchOptions,
|
||||
WatchType.WildcardDirectory
|
||||
);
|
||||
}
|
||||
|
||||
@@ -344,15 +344,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface WatchFileHost {
|
||||
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
|
||||
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
|
||||
}
|
||||
export interface WatchDirectoryHost {
|
||||
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
|
||||
}
|
||||
export type WatchFile<X, Y> = (host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
|
||||
export type WatchFile<X, Y> = (host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
|
||||
export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void;
|
||||
export type WatchFilePath<X, Y> = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, path: Path, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
|
||||
export type WatchDirectory<X, Y> = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
|
||||
export type WatchFilePath<X, Y> = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, path: Path, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
|
||||
export type WatchDirectory<X, Y> = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
|
||||
|
||||
export interface WatchFactory<X, Y> {
|
||||
watchFile: WatchFile<X, Y>;
|
||||
@@ -364,9 +364,13 @@ namespace ts {
|
||||
return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFile, watchDirectory);
|
||||
}
|
||||
|
||||
function getWatchFactoryWith<X, Y = undefined>(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined,
|
||||
watchFile: (host: WatchFileHost, file: string, callback: FileWatcherCallback, watchPriority: PollingInterval) => FileWatcher,
|
||||
watchDirectory: (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags) => FileWatcher): WatchFactory<X, Y> {
|
||||
function getWatchFactoryWith<X, Y = undefined>(
|
||||
watchLogLevel: WatchLogLevel,
|
||||
log: (s: string) => void,
|
||||
getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined,
|
||||
watchFile: (host: WatchFileHost, file: string, callback: FileWatcherCallback, watchPriority: PollingInterval, options: WatchOptions | undefined) => FileWatcher,
|
||||
watchDirectory: (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined) => FileWatcher
|
||||
): WatchFactory<X, Y> {
|
||||
const createFileWatcher: CreateFileWatcher<WatchFileHost, PollingInterval, FileWatcherEventKind, never, X, Y> = getCreateFileWatcher(watchLogLevel, watchFile);
|
||||
const createFilePathWatcher: CreateFileWatcher<WatchFileHost, PollingInterval, FileWatcherEventKind, Path, X, Y> = watchLogLevel === WatchLogLevel.None ? watchFilePath : createFileWatcher;
|
||||
const createDirectoryWatcher: CreateFileWatcher<WatchDirectoryHost, WatchDirectoryFlags, undefined, never, X, Y> = getCreateFileWatcher(watchLogLevel, watchDirectory);
|
||||
@@ -374,32 +378,32 @@ namespace ts {
|
||||
setSysLog(s => log(s));
|
||||
}
|
||||
return {
|
||||
watchFile: (host, file, callback, pollingInterval, detailInfo1, detailInfo2) =>
|
||||
createFileWatcher(host, file, callback, pollingInterval, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),
|
||||
watchFilePath: (host, file, callback, pollingInterval, path, detailInfo1, detailInfo2) =>
|
||||
createFilePathWatcher(host, file, callback, pollingInterval, path, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),
|
||||
watchDirectory: (host, directory, callback, flags, detailInfo1, detailInfo2) =>
|
||||
createDirectoryWatcher(host, directory, callback, flags, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchDirectory, log, "DirectoryWatcher", getDetailWatchInfo)
|
||||
watchFile: (host, file, callback, pollingInterval, options, detailInfo1, detailInfo2) =>
|
||||
createFileWatcher(host, file, callback, pollingInterval, options, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),
|
||||
watchFilePath: (host, file, callback, pollingInterval, options, path, detailInfo1, detailInfo2) =>
|
||||
createFilePathWatcher(host, file, callback, pollingInterval, options, path, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),
|
||||
watchDirectory: (host, directory, callback, flags, options, detailInfo1, detailInfo2) =>
|
||||
createDirectoryWatcher(host, directory, callback, flags, options, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchDirectory, log, "DirectoryWatcher", getDetailWatchInfo)
|
||||
};
|
||||
|
||||
function watchFilePath(host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, path: Path): FileWatcher {
|
||||
return watchFile(host, file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval);
|
||||
}
|
||||
}
|
||||
|
||||
function watchFile(host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval): FileWatcher {
|
||||
return host.watchFile(file, callback, pollingInterval);
|
||||
function watchFile(host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined): FileWatcher {
|
||||
return host.watchFile(file, callback, pollingInterval, options);
|
||||
}
|
||||
|
||||
function watchDirectory(host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher {
|
||||
return host.watchDirectory(directory, callback, (flags & WatchDirectoryFlags.Recursive) !== 0);
|
||||
function watchFilePath(host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, path: Path): FileWatcher {
|
||||
return watchFile(host, file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval, options);
|
||||
}
|
||||
|
||||
function watchDirectory(host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined): FileWatcher {
|
||||
return host.watchDirectory(directory, callback, (flags & WatchDirectoryFlags.Recursive) !== 0, options);
|
||||
}
|
||||
|
||||
type WatchCallback<T, U> = (fileName: string, cbOptional?: T, passThrough?: U) => void;
|
||||
type AddWatch<H, T, U, V> = (host: H, file: string, cb: WatchCallback<U, V>, flags: T, passThrough?: V, detailInfo1?: undefined, detailInfo2?: undefined) => FileWatcher;
|
||||
type AddWatch<H, T, U, V> = (host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough?: V, detailInfo1?: undefined, detailInfo2?: undefined) => FileWatcher;
|
||||
export type GetDetailWatchInfo<X, Y> = (detailInfo1: X, detailInfo2: Y | undefined) => string;
|
||||
|
||||
type CreateFileWatcher<H, T, U, V, X, Y> = (host: H, file: string, cb: WatchCallback<U, V>, flags: T, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) => FileWatcher;
|
||||
type CreateFileWatcher<H, T, U, V, X, Y> = (host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) => FileWatcher;
|
||||
function getCreateFileWatcher<H, T, U, V, X, Y>(watchLogLevel: WatchLogLevel, addWatch: AddWatch<H, T, U, V>): CreateFileWatcher<H, T, U, V, X, Y> {
|
||||
switch (watchLogLevel) {
|
||||
case WatchLogLevel.None:
|
||||
@@ -411,27 +415,27 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function createFileWatcherWithLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
|
||||
log(`${watchCaption}:: Added:: ${getWatchInfo(file, flags, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
|
||||
const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo);
|
||||
function createFileWatcherWithLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
|
||||
log(`${watchCaption}:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
|
||||
const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, options, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo);
|
||||
return {
|
||||
close: () => {
|
||||
log(`${watchCaption}:: Close:: ${getWatchInfo(file, flags, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
|
||||
log(`${watchCaption}:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
|
||||
watcher.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createDirectoryWatcherWithLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
|
||||
const watchInfo = `${watchCaption}:: Added:: ${getWatchInfo(file, flags, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
|
||||
function createDirectoryWatcherWithLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
|
||||
const watchInfo = `${watchCaption}:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
|
||||
log(watchInfo);
|
||||
const start = timestamp();
|
||||
const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo);
|
||||
const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, options, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo);
|
||||
const elapsed = timestamp() - start;
|
||||
log(`Elapsed:: ${elapsed}ms ${watchInfo}`);
|
||||
return {
|
||||
close: () => {
|
||||
const watchInfo = `${watchCaption}:: Close:: ${getWatchInfo(file, flags, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
|
||||
const watchInfo = `${watchCaption}:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
|
||||
log(watchInfo);
|
||||
const start = timestamp();
|
||||
watcher.close();
|
||||
@@ -441,19 +445,28 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
function createFileWatcherWithTriggerLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
|
||||
function createFileWatcherWithTriggerLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
|
||||
return addWatch(host, file, (fileName, cbOptional) => {
|
||||
const triggerredInfo = `${watchCaption}:: Triggered with ${fileName} ${cbOptional !== undefined ? cbOptional : ""}:: ${getWatchInfo(file, flags, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
|
||||
const triggerredInfo = `${watchCaption}:: Triggered with ${fileName} ${cbOptional !== undefined ? cbOptional : ""}:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
|
||||
log(triggerredInfo);
|
||||
const start = timestamp();
|
||||
cb(fileName, cbOptional, passThrough);
|
||||
const elapsed = timestamp() - start;
|
||||
log(`Elapsed:: ${elapsed}ms ${triggerredInfo}`);
|
||||
}, flags);
|
||||
}, flags, options);
|
||||
}
|
||||
|
||||
function getWatchInfo<T, X, Y>(file: string, flags: T, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) {
|
||||
return `WatchInfo: ${file} ${flags} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`;
|
||||
export function getFallbackOptions(options: WatchOptions | undefined): WatchOptions {
|
||||
const fallbackPolling = options?.fallbackPolling;
|
||||
return {
|
||||
watchFile: fallbackPolling !== undefined ?
|
||||
fallbackPolling as unknown as WatchFileKind :
|
||||
WatchFileKind.PriorityPollingInterval
|
||||
};
|
||||
}
|
||||
|
||||
function getWatchInfo<T, X, Y>(file: string, flags: T, options: WatchOptions | undefined, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) {
|
||||
return `WatchInfo: ${file} ${flags} ${JSON.stringify(options)} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`;
|
||||
}
|
||||
|
||||
export function closeFileWatcherOf<T extends { watcher: FileWatcher; }>(objWithWatcher: T) {
|
||||
|
||||
Reference in New Issue
Block a user