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:
Sheetal Nandi
2019-12-11 13:26:44 -08:00
committed by GitHub
parent 4212484ae1
commit 236012e47b
40 changed files with 2417 additions and 720 deletions

View File

@@ -83,6 +83,7 @@
"unittests/config/projectReferences.ts",
"unittests/config/showConfig.ts",
"unittests/config/tsconfigParsing.ts",
"unittests/config/tsconfigParsingWatchOptions.ts",
"unittests/evaluation/asyncArrow.ts",
"unittests/evaluation/asyncGenerator.ts",
"unittests/evaluation/awaiter.ts",

View File

@@ -6,6 +6,7 @@ namespace ts {
const parsedCompilerOptions = JSON.stringify(parsed.options);
const expectedCompilerOptions = JSON.stringify(expectedParsedCommandLine.options);
assert.equal(parsedCompilerOptions, expectedCompilerOptions);
assert.deepEqual(parsed.watchOptions, expectedParsedCommandLine.watchOptions);
const parsedErrors = parsed.errors;
const expectedErrors = expectedParsedCommandLine.errors;
@@ -45,7 +46,7 @@ namespace ts {
assertParseResult(["--declarations", "--allowTS"], {
errors: [
{
messageText:"Unknown compiler option '--declarations'. Did you mean 'declaration'?",
messageText: "Unknown compiler option '--declarations'. Did you mean 'declaration'?",
category: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1.category,
code: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1.code,
file: undefined,
@@ -412,6 +413,75 @@ namespace ts {
options: { tsBuildInfoFile: "build.tsbuildinfo" }
});
});
describe("Watch options", () => {
it("parse --watchFile", () => {
assertParseResult(["--watchFile", "UseFsEvents", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: {},
watchOptions: { watchFile: WatchFileKind.UseFsEvents }
});
});
it("parse --watchDirectory", () => {
assertParseResult(["--watchDirectory", "FixedPollingInterval", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: {},
watchOptions: { watchDirectory: WatchDirectoryKind.FixedPollingInterval }
});
});
it("parse --fallbackPolling", () => {
assertParseResult(["--fallbackPolling", "PriorityInterval", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: {},
watchOptions: { fallbackPolling: PollingWatchKind.PriorityInterval }
});
});
it("parse --synchronousWatchDirectory", () => {
assertParseResult(["--synchronousWatchDirectory", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: {},
watchOptions: { synchronousWatchDirectory: true }
});
});
it("errors on missing argument to --fallbackPolling", () => {
assertParseResult(["0.ts", "--fallbackPolling"],
{
errors: [
{
messageText: "Watch option 'fallbackPolling' requires a value of type string.",
category: Diagnostics.Watch_option_0_requires_a_value_of_type_1.category,
code: Diagnostics.Watch_option_0_requires_a_value_of_type_1.code,
file: undefined,
start: undefined,
length: undefined
},
{
messageText: "Argument for '--fallbackPolling' option must be: 'fixedinterval', 'priorityinterval', 'dynamicpriority'.",
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
file: undefined,
start: undefined,
length: undefined
}
],
fileNames: ["0.ts"],
options: {},
watchOptions: { fallbackPolling: undefined }
});
});
});
});
describe("unittests:: config:: commandLineParsing:: parseBuildOptions", () => {
@@ -420,6 +490,7 @@ namespace ts {
const parsedBuildOptions = JSON.stringify(parsed.buildOptions);
const expectedBuildOptions = JSON.stringify(expectedParsedBuildCommand.buildOptions);
assert.equal(parsedBuildOptions, expectedBuildOptions);
assert.deepEqual(parsed.watchOptions, expectedParsedBuildCommand.watchOptions);
const parsedErrors = parsed.errors;
const expectedErrors = expectedParsedBuildCommand.errors;
@@ -442,7 +513,8 @@ namespace ts {
{
errors: [],
projects: ["."],
buildOptions: {}
buildOptions: {},
watchOptions: undefined
});
});
@@ -452,7 +524,8 @@ namespace ts {
{
errors: [],
projects: ["tests"],
buildOptions: { verbose: true, force: true }
buildOptions: { verbose: true, force: true },
watchOptions: undefined
});
});
@@ -469,7 +542,8 @@ namespace ts {
length: undefined,
}],
projects: ["."],
buildOptions: { verbose: true }
buildOptions: { verbose: true },
watchOptions: undefined
});
});
@@ -478,7 +552,7 @@ namespace ts {
assertParseResult(["--listFilesOnly"],
{
errors: [{
messageText:"Unknown build option '--listFilesOnly'.",
messageText: "Unknown build option '--listFilesOnly'.",
category: Diagnostics.Unknown_build_option_0.category,
code: Diagnostics.Unknown_build_option_0.code,
file: undefined,
@@ -486,7 +560,8 @@ namespace ts {
length: undefined,
}],
projects: ["."],
buildOptions: {}
buildOptions: {},
watchOptions: undefined,
});
});
@@ -496,7 +571,8 @@ namespace ts {
{
errors: [],
projects: ["src", "tests"],
buildOptions: { force: true, verbose: true }
buildOptions: { force: true, verbose: true },
watchOptions: undefined,
});
});
@@ -506,7 +582,8 @@ namespace ts {
{
errors: [],
projects: ["src", "tests"],
buildOptions: { force: true, verbose: true }
buildOptions: { force: true, verbose: true },
watchOptions: undefined,
});
});
@@ -516,7 +593,8 @@ namespace ts {
{
errors: [],
projects: ["src", "tests"],
buildOptions: { force: true, verbose: true }
buildOptions: { force: true, verbose: true },
watchOptions: undefined,
});
});
@@ -526,7 +604,8 @@ namespace ts {
{
errors: [],
projects: ["tests"],
buildOptions: { incremental: true }
buildOptions: { incremental: true },
watchOptions: undefined,
});
});
@@ -536,7 +615,8 @@ namespace ts {
{
errors: [],
projects: ["src"],
buildOptions: { locale: "en-us" }
buildOptions: { locale: "en-us" },
watchOptions: undefined,
});
});
@@ -553,7 +633,8 @@ namespace ts {
length: undefined
}],
projects: ["build.tsbuildinfo", "tests"],
buildOptions: { }
buildOptions: {},
watchOptions: undefined,
});
});
@@ -572,7 +653,8 @@ namespace ts {
length: undefined,
}],
projects: ["."],
buildOptions: { [flag1]: true, [flag2]: true }
buildOptions: { [flag1]: true, [flag2]: true },
watchOptions: undefined,
});
});
}
@@ -582,7 +664,74 @@ namespace ts {
verifyInvalidCombination("clean", "watch");
verifyInvalidCombination("watch", "dry");
});
describe("Watch options", () => {
it("parse --watchFile", () => {
assertParseResult(["--watchFile", "UseFsEvents", "--verbose"],
{
errors: [],
projects: ["."],
buildOptions: { verbose: true },
watchOptions: { watchFile: WatchFileKind.UseFsEvents }
});
});
it("parse --watchDirectory", () => {
assertParseResult(["--watchDirectory", "FixedPollingInterval", "--verbose"],
{
errors: [],
projects: ["."],
buildOptions: { verbose: true },
watchOptions: { watchDirectory: WatchDirectoryKind.FixedPollingInterval }
});
});
it("parse --fallbackPolling", () => {
assertParseResult(["--fallbackPolling", "PriorityInterval", "--verbose"],
{
errors: [],
projects: ["."],
buildOptions: { verbose: true },
watchOptions: { fallbackPolling: PollingWatchKind.PriorityInterval }
});
});
it("parse --synchronousWatchDirectory", () => {
assertParseResult(["--synchronousWatchDirectory", "--verbose"],
{
errors: [],
projects: ["."],
buildOptions: { verbose: true },
watchOptions: { synchronousWatchDirectory: true }
});
});
it("errors on missing argument", () => {
assertParseResult(["--verbose", "--fallbackPolling"],
{
errors: [
{
messageText: "Watch option 'fallbackPolling' requires a value of type string.",
category: Diagnostics.Watch_option_0_requires_a_value_of_type_1.category,
code: Diagnostics.Watch_option_0_requires_a_value_of_type_1.code,
file: undefined,
start: undefined,
length: undefined
},
{
messageText: "Argument for '--fallbackPolling' option must be: 'fixedinterval', 'priorityinterval', 'dynamicpriority'.",
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
file: undefined,
start: undefined,
length: undefined
}
],
projects: ["."],
buildOptions: { verbose: true },
watchOptions: { fallbackPolling: undefined }
});
});
});
});
}

View File

@@ -102,16 +102,33 @@ namespace ts {
]
});
showTSConfigCorrectly("Show TSConfig with watch options", ["-p", "tsconfig.json"], {
watchOptions: {
watchFile: "DynamicPriorityPolling"
},
include: [
"./src/**/*"
]
});
// Bulk validation of all option declarations
for (const option of optionDeclarations) {
if (option.name === "project") continue;
let configObject: object | undefined;
baselineOption(option, /*isCompilerOptions*/ true);
}
for (const option of optionsForWatch) {
baselineOption(option, /*isCompilerOptions*/ false);
}
function baselineOption(option: CommandLineOption, isCompilerOptions: boolean) {
if (option.name === "project") return;
let args: string[];
let optionValue: object | undefined;
switch (option.type) {
case "boolean": {
if (option.isTSConfigOnly) {
args = ["-p", "tsconfig.json"];
configObject = { compilerOptions: { [option.name]: true } };
optionValue = { [option.name]: true };
}
else {
args = [`--${option.name}`];
@@ -121,7 +138,7 @@ namespace ts {
case "list": {
if (option.isTSConfigOnly) {
args = ["-p", "tsconfig.json"];
configObject = { compilerOptions: { [option.name]: [] } };
optionValue = { [option.name]: [] };
}
else {
args = [`--${option.name}`];
@@ -131,7 +148,7 @@ namespace ts {
case "string": {
if (option.isTSConfigOnly) {
args = ["-p", "tsconfig.json"];
configObject = { compilerOptions: { [option.name]: "someString" } };
optionValue = { [option.name]: "someString" };
}
else {
args = [`--${option.name}`, "someString"];
@@ -141,7 +158,7 @@ namespace ts {
case "number": {
if (option.isTSConfigOnly) {
args = ["-p", "tsconfig.json"];
configObject = { compilerOptions: { [option.name]: 0 } };
optionValue = { [option.name]: 0 };
}
else {
args = [`--${option.name}`, "0"];
@@ -150,7 +167,7 @@ namespace ts {
}
case "object": {
args = ["-p", "tsconfig.json"];
configObject = { compilerOptions: { [option.name]: {} } };
optionValue = { [option.name]: {} };
break;
}
default: {
@@ -159,7 +176,7 @@ namespace ts {
const val = iterResult.value;
if (option.isTSConfigOnly) {
args = ["-p", "tsconfig.json"];
configObject = { compilerOptions: { [option.name]: val } };
optionValue = { [option.name]: val };
}
else {
args = [`--${option.name}`, val];
@@ -167,6 +184,9 @@ namespace ts {
break;
}
}
const configObject = optionValue &&
(isCompilerOptions ? { compilerOptions: optionValue } : { watchOptions: optionValue });
showTSConfigCorrectly(`Shows tsconfig for single option/${option.name}`, args, configObject);
}
});

View File

@@ -0,0 +1,178 @@
namespace ts {
describe("unittests:: config:: tsconfigParsingWatchOptions:: parseConfigFileTextToJson", () => {
function createParseConfigHost(additionalFiles?: vfs.FileSet) {
return new fakes.ParseConfigHost(
new vfs.FileSystem(
/*ignoreCase*/ false,
{
cwd: "/",
files: { "/": {}, "/a.ts": "", ...additionalFiles }
}
)
);
}
function getParsedCommandJson(json: object, additionalFiles?: vfs.FileSet, existingWatchOptions?: WatchOptions) {
return parseJsonConfigFileContent(
json,
createParseConfigHost(additionalFiles),
"/",
/*existingOptions*/ undefined,
"tsconfig.json",
/*resolutionStack*/ undefined,
/*extraFileExtensions*/ undefined,
/*extendedConfigCache*/ undefined,
existingWatchOptions,
);
}
function getParsedCommandJsonNode(json: object, additionalFiles?: vfs.FileSet, existingWatchOptions?: WatchOptions) {
const parsed = parseJsonText("tsconfig.json", JSON.stringify(json));
return parseJsonSourceFileConfigFileContent(
parsed,
createParseConfigHost(additionalFiles),
"/",
/*existingOptions*/ undefined,
"tsconfig.json",
/*resolutionStack*/ undefined,
/*extraFileExtensions*/ undefined,
/*extendedConfigCache*/ undefined,
existingWatchOptions,
);
}
interface VerifyWatchOptions {
json: object;
expectedOptions: WatchOptions | undefined;
additionalFiles?: vfs.FileSet;
existingWatchOptions?: WatchOptions | undefined;
}
function verifyWatchOptions(scenario: () => VerifyWatchOptions[]) {
it("with json api", () => {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions } of scenario()) {
const parsed = getParsedCommandJson(json, additionalFiles, existingWatchOptions);
assert.deepEqual(parsed.watchOptions, expectedOptions);
}
});
it("with json source file api", () => {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions } of scenario()) {
const parsed = getParsedCommandJsonNode(json, additionalFiles, existingWatchOptions);
assert.deepEqual(parsed.watchOptions, expectedOptions);
}
});
}
describe("no watchOptions specified option", () => {
verifyWatchOptions(() => [{
json: {},
expectedOptions: undefined
}]);
});
describe("empty watchOptions specified option", () => {
verifyWatchOptions(() => [{
json: { watchOptions: {} },
expectedOptions: undefined
}]);
});
describe("extending config file", () => {
describe("when extending config file without watchOptions", () => {
verifyWatchOptions(() => [
{
json: {
extends: "./base.json",
watchOptions: { watchFile: "UseFsEvents" }
},
expectedOptions: { watchFile: WatchFileKind.UseFsEvents },
additionalFiles: { "/base.json": "{}" }
},
{
json: { extends: "./base.json", },
expectedOptions: undefined,
additionalFiles: { "/base.json": "{}" }
}
]);
});
describe("when extending config file with watchOptions", () => {
verifyWatchOptions(() => [
{
json: {
extends: "./base.json",
watchOptions: {
watchFile: "UseFsEvents",
}
},
expectedOptions: {
watchFile: WatchFileKind.UseFsEvents,
watchDirectory: WatchDirectoryKind.FixedPollingInterval
},
additionalFiles: {
"/base.json": JSON.stringify({
watchOptions: {
watchFile: "UseFsEventsOnParentDirectory",
watchDirectory: "FixedPollingInterval"
}
})
}
},
{
json: {
extends: "./base.json",
},
expectedOptions: {
watchFile: WatchFileKind.UseFsEventsOnParentDirectory,
watchDirectory: WatchDirectoryKind.FixedPollingInterval
},
additionalFiles: {
"/base.json": JSON.stringify({
watchOptions: {
watchFile: "UseFsEventsOnParentDirectory",
watchDirectory: "FixedPollingInterval"
}
})
}
}
]);
});
});
describe("different options", () => {
verifyWatchOptions(() => [
{
json: { watchOptions: { watchFile: "UseFsEvents" } },
expectedOptions: { watchFile: WatchFileKind.UseFsEvents }
},
{
json: { watchOptions: { watchDirectory: "UseFsEvents" } },
expectedOptions: { watchDirectory: WatchDirectoryKind.UseFsEvents }
},
{
json: { watchOptions: { fallbackPolling: "DynamicPriority" } },
expectedOptions: { fallbackPolling: PollingWatchKind.DynamicPriority }
},
{
json: { watchOptions: { synchronousWatchDirectory: true } },
expectedOptions: { synchronousWatchDirectory: true }
}
]);
});
describe("watch options extending passed in watch options", () => {
verifyWatchOptions(() => [
{
json: { watchOptions: { watchFile: "UseFsEvents" } },
expectedOptions: { watchFile: WatchFileKind.UseFsEvents, watchDirectory: WatchDirectoryKind.FixedPollingInterval },
existingWatchOptions: { watchDirectory: WatchDirectoryKind.FixedPollingInterval }
},
{
json: {},
expectedOptions: { watchDirectory: WatchDirectoryKind.FixedPollingInterval },
existingWatchOptions: { watchDirectory: WatchDirectoryKind.FixedPollingInterval }
},
]);
});
});
}

View File

@@ -928,13 +928,13 @@ namespace ts {
}
function verifyProgramWithoutConfigFile(system: System, rootFiles: string[], options: CompilerOptions) {
const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system)).getCurrentProgram().getProgram();
const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, /*watchOptions*/ undefined, system)).getCurrentProgram().getProgram();
verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options));
}
function verifyProgramWithConfigFile(system: System, configFileName: string) {
const program = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, system)).getCurrentProgram().getProgram();
const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, system, notImplemented)!; // TODO: GH#18217
const program = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, /*watchOptionsToExtend*/ undefined, system)).getCurrentProgram().getProgram();
const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, /*watchOptionsToExtend*/ undefined, system, notImplemented)!; // TODO: GH#18217
verifyProgramIsUptoDate(program, fileNames, options);
}

View File

@@ -2,8 +2,8 @@ namespace ts.tscWatch {
describe("unittests:: tsc-watch:: console clearing", () => {
const currentDirectoryLog = "Current directory: / CaseSensitiveFileNames: false\n";
const fileWatcherAddedLog = [
"FileWatcher:: Added:: WatchInfo: /f.ts 250 Source file\n",
"FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 Source file\n"
"FileWatcher:: Added:: WatchInfo: /f.ts 250 undefined Source file\n",
"FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 undefined Source file\n"
];
const file: File = {
@@ -35,9 +35,9 @@ namespace ts.tscWatch {
host.modifyFile(file.path, "//");
host.runQueuedTimeoutCallbacks();
checkOutputErrorsIncremental(host, emptyArray, disableConsoleClear, hasLog ? [
"FileWatcher:: Triggered with /f.ts 1:: WatchInfo: /f.ts 250 Source file\n",
"FileWatcher:: Triggered with /f.ts 1:: WatchInfo: /f.ts 250 undefined Source file\n",
"Scheduling update\n",
"Elapsed:: 0ms FileWatcher:: Triggered with /f.ts 1:: WatchInfo: /f.ts 250 Source file\n"
"Elapsed:: 0ms FileWatcher:: Triggered with /f.ts 1:: WatchInfo: /f.ts 250 undefined Source file\n"
] : undefined, hasLog ? getProgramSynchronizingLog(options) : undefined);
}
@@ -86,8 +86,8 @@ namespace ts.tscWatch {
const host = createWatchedSystem(files);
const reportDiagnostic = createDiagnosticReporter(host);
const optionsToExtend: CompilerOptions = {};
const configParseResult = parseConfigFileWithSystem(configFile.path, optionsToExtend, host, reportDiagnostic)!;
const watchCompilerHost = createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath!, optionsToExtend, host, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(host));
const configParseResult = parseConfigFileWithSystem(configFile.path, optionsToExtend, /*watchOptionsToExtend*/ undefined, host, reportDiagnostic)!;
const watchCompilerHost = createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath!, optionsToExtend, /*watchOptionsToExtend*/ undefined, host, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(host));
watchCompilerHost.configFileParsingResult = configParseResult;
createWatchProgram(watchCompilerHost);
verifyCompilation(host, compilerOptions);

View File

@@ -37,8 +37,8 @@ namespace ts.tscWatch {
close(): void;
}
export function createWatchOfConfigFile(configFileName: string, host: WatchedSystem, optionsToExtend?: CompilerOptions, maxNumberOfFilesToIterateForInvalidation?: number) {
const compilerHost = createWatchCompilerHostOfConfigFile(configFileName, optionsToExtend || {}, host);
export function createWatchOfConfigFile(configFileName: string, host: WatchedSystem, optionsToExtend?: CompilerOptions, watchOptionsToExtend?: WatchOptions, maxNumberOfFilesToIterateForInvalidation?: number) {
const compilerHost = createWatchCompilerHostOfConfigFile(configFileName, optionsToExtend || {}, watchOptionsToExtend, host);
compilerHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation;
const watch = createWatchProgram(compilerHost);
const result = (() => watch.getCurrentProgram().getProgram()) as Watch;
@@ -47,8 +47,8 @@ namespace ts.tscWatch {
return result;
}
export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], host: WatchedSystem, options: CompilerOptions = {}, maxNumberOfFilesToIterateForInvalidation?: number) {
const compilerHost = createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, host);
export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], host: WatchedSystem, options: CompilerOptions = {}, watchOptions?: WatchOptions, maxNumberOfFilesToIterateForInvalidation?: number) {
const compilerHost = createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, watchOptions, host);
compilerHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation;
const watch = createWatchProgram(compilerHost);
return () => watch.getCurrentProgram().getProgram();

View File

@@ -35,7 +35,7 @@ namespace ts.tscWatch {
function incrementalBuild(configFile: string, host: WatchedSystem, optionsToExtend?: CompilerOptions) {
const reportDiagnostic = createDiagnosticReporter(host);
const config = parseConfigFileWithSystem(configFile, optionsToExtend || {}, host, reportDiagnostic);
const config = parseConfigFileWithSystem(configFile, optionsToExtend || {}, /*watchOptionsToExtend*/ undefined, host, reportDiagnostic);
if (config) {
performIncrementalCompilation({
rootNames: config.fileNames,
@@ -547,7 +547,7 @@ namespace ts.tscWatch {
const system = createWatchedSystem([libFile, file1, fileModified, config], { currentDirectory: project });
incrementalBuild("tsconfig.json", system);
const command = parseConfigFileWithSystem("tsconfig.json", {}, system, noop)!;
const command = parseConfigFileWithSystem("tsconfig.json", {}, /*watchOptionsToExtend*/ undefined, system, noop)!;
const builderProgram = createIncrementalProgram({
rootNames: command.fileNames,
options: command.options,

View File

@@ -68,7 +68,7 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([configFile, libFile, file1, file2, file3]);
const watch = createWatchProgram(createWatchCompilerHostOfConfigFile(configFile.path, {}, host, /*createProgram*/ undefined, notImplemented));
const watch = createWatchProgram(createWatchCompilerHostOfConfigFile(configFile.path, {}, /*watchOptionsToExtend*/ undefined, host, /*createProgram*/ undefined, notImplemented));
checkProgramActualFiles(watch.getCurrentProgram().getProgram(), [file1.path, libFile.path, file2.path]);
checkProgramRootFiles(watch.getCurrentProgram().getProgram(), [file1.path, file2.path]);
@@ -931,7 +931,7 @@ namespace ts.tscWatch {
content: generateTSConfig(options, emptyArray, "\n")
};
const host = createWatchedSystem([file1, file2, libFile, tsconfig], { currentDirectory: projectRoot });
const watch = createWatchOfConfigFile(tsconfig.path, host, /*optionsToExtend*/ undefined, /*maxNumberOfFilesToIterateForInvalidation*/1);
const watch = createWatchOfConfigFile(tsconfig.path, host, /*optionsToExtend*/ undefined, /*watchOptionsToExtend*/ undefined, /*maxNumberOfFilesToIterateForInvalidation*/1);
checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]);
outputFiles.forEach(f => host.fileExists(f));

View File

@@ -379,7 +379,7 @@ declare module "fs" {
const expectedFiles = files.map(f => f.path);
it("when watching node_modules in inferred project for failed lookup", () => {
const host = createWatchedSystem(files);
const watch = createWatchOfFilesAndCompilerOptions([file1.path], host, {}, /*maxNumberOfFilesToIterateForInvalidation*/ 1);
const watch = createWatchOfFilesAndCompilerOptions([file1.path], host, {}, /*watchOptions*/ undefined, /*maxNumberOfFilesToIterateForInvalidation*/ 1);
checkProgramActualFiles(watch(), expectedFiles);
host.checkTimeoutQueueLength(0);

View File

@@ -20,7 +20,7 @@ namespace ts.tscWatch {
it("verify that module resolution with json extension works when returned without extension", () => {
const files = [libFile, mainFile, config, settingsJson];
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
const compilerHost = createWatchCompilerHostOfConfigFile(config.path, {}, host);
const compilerHost = createWatchCompilerHostOfConfigFile(config.path, {}, /*watchOptionsToExtend*/ undefined, host);
const parsedCommandResult = parseJsonConfigFileContent(configFileJson, host, config.path);
compilerHost.resolveModuleNames = (moduleNames, containingFile) => moduleNames.map(m => {
const result = resolveModuleName(m, containingFile, parsedCommandResult.options, compilerHost);
@@ -58,7 +58,7 @@ namespace ts.tscWatch {
const reportWatchStatus: WatchStatusReporter = (_, __, ___, errorCount) => {
watchedErrorCount = errorCount;
};
const compilerHost = createWatchCompilerHostOfConfigFile(config.path, {}, host, /*createProgram*/ undefined, /*reportDiagnostic*/ undefined, reportWatchStatus);
const compilerHost = createWatchCompilerHostOfConfigFile(config.path, {}, /*watchOptionsToExtend*/ undefined, host, /*createProgram*/ undefined, /*reportDiagnostic*/ undefined, reportWatchStatus);
createWatchProgram(compilerHost);
assert.equal(watchedErrorCount, 2, "The error count was expected to be 2 for the file change");
});

View File

@@ -68,7 +68,11 @@ namespace ts.tscWatch {
const projectSrcFolder = `${projectFolder}/src`;
const configFile: File = {
path: `${projectFolder}/tsconfig.json`,
content: "{}"
content: JSON.stringify({
watchOptions: {
synchronousWatchDirectory: true
}
})
};
const file: File = {
path: `${projectSrcFolder}/file1.ts`,
@@ -173,6 +177,277 @@ namespace ts.tscWatch {
checkWatchedDirectories(host, [cwd, `${cwd}/node_modules`, `${cwd}/node_modules/@types`, `${cwd}/node_modules/reala`, `${cwd}/node_modules/realb`,
`${cwd}/node_modules/reala/node_modules`, `${cwd}/node_modules/realb/node_modules`, `${cwd}/src`], /*recursive*/ false);
});
it("with non synchronous watch directory", () => {
const configFile: File = {
path: `${projectRoot}/tsconfig.json`,
content: "{}"
};
const file1: File = {
path: `${projectRoot}/src/file1.ts`,
content: `import { x } from "file2";`
};
const file2: File = {
path: `${projectRoot}/node_modules/file2/index.d.ts`,
content: `export const x = 10;`
};
const files = [libFile, file1, file2, configFile];
const host = createWatchedSystem(files, { runWithoutRecursiveWatches: true });
const watch = createWatchOfConfigFile(configFile.path, host);
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
checkOutputErrorsInitial(host, emptyArray);
const watchedDirectories = [`${projectRoot}`, `${projectRoot}/src`, `${projectRoot}/node_modules`, `${projectRoot}/node_modules/file2`, `${projectRoot}/node_modules/@types`];
checkWatchesWithFile2();
host.checkTimeoutQueueLengthAndRun(1); // To update directory callbacks for file1.js output
host.checkTimeoutQueueLengthAndRun(1); // Update program again
host.checkTimeoutQueueLength(0);
checkOutputErrorsIncremental(host, emptyArray);
checkWatchesWithFile2();
// Remove directory node_modules
host.deleteFolder(`${projectRoot}/node_modules`, /*recursive*/ true);
host.checkTimeoutQueueLength(2); // 1. For updating program and 2. for updating child watches
host.runQueuedTimeoutCallbacks(host.getNextTimeoutId() - 2); // Update program
checkOutputErrorsIncremental(host, [
getDiagnosticModuleNotFoundOfFile(watch(), file1, "file2")
]);
checkWatchesWithoutFile2();
host.checkTimeoutQueueLengthAndRun(1); // To update directory watchers
host.checkTimeoutQueueLengthAndRun(1); // To Update program
host.checkTimeoutQueueLength(0);
checkWatchesWithoutFile2();
checkOutputErrorsIncremental(host, [
getDiagnosticModuleNotFoundOfFile(watch(), file1, "file2")
]);
// npm install
host.createDirectory(`${projectRoot}/node_modules`);
host.checkTimeoutQueueLength(1); // To update folder structure
assert.deepEqual(host.getOutput(), emptyArray);
checkWatchesWithoutFile2();
host.createDirectory(`${projectRoot}/node_modules/file2`);
host.checkTimeoutQueueLength(1); // To update folder structure
assert.deepEqual(host.getOutput(), emptyArray);
checkWatchesWithoutFile2();
host.writeFile(file2.path, file2.content);
host.checkTimeoutQueueLength(1); // To update folder structure
assert.deepEqual(host.getOutput(), emptyArray);
checkWatchesWithoutFile2();
host.runQueuedTimeoutCallbacks();
host.checkTimeoutQueueLength(1); // To Update the program
assert.deepEqual(host.getOutput(), emptyArray);
checkWatchedFiles(files.filter(f => f !== file2)); // Files like without file2
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
checkNonRecursiveWatchedDirectories(watchedDirectories); // Directories like with file2
host.runQueuedTimeoutCallbacks();
host.checkTimeoutQueueLength(0);
checkOutputErrorsIncremental(host, emptyArray);
checkWatchesWithFile2();
function checkWatchesWithFile2() {
checkWatchedFiles(files);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
checkNonRecursiveWatchedDirectories(watchedDirectories);
}
function checkWatchesWithoutFile2() {
checkWatchedFiles(files.filter(f => f !== file2));
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
checkNonRecursiveWatchedDirectories(watchedDirectories.filter(f => f !== `${projectRoot}/node_modules/file2`));
}
function checkWatchedFiles(files: readonly File[]) {
checkWatchedFilesDetailed(
host,
files.map(f => f.path.toLowerCase()),
1,
arrayToMap(
files,
f => f.path.toLowerCase(),
() => [PollingInterval.Low]
)
);
}
function checkNonRecursiveWatchedDirectories(directories: readonly string[]) {
checkWatchedDirectoriesDetailed(
host,
directories,
1,
/*recursive*/ false,
arrayToMap(
directories,
identity,
() => [{
fallbackPollingInterval: PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
}
});
});
describe("handles watch compiler options", () => {
it("with watchFile option", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
watchOptions: {
watchFile: "UseFsEvents"
}
})
};
const files = [libFile, commonFile1, commonFile2, configFile];
const host = createWatchedSystem(files);
const watch = createWatchOfConfigFile(configFile.path, host, { extendedDiagnostics: true });
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
// Instead of polling watch (= watchedFiles), uses fsWatch
checkWatchedFiles(host, emptyArray);
checkWatchedDirectoriesDetailed(
host,
files.map(f => f.path.toLowerCase()),
1,
/*recursive*/ false,
arrayToMap(
files,
f => f.path.toLowerCase(),
f => [{
fallbackPollingInterval: f === configFile ? PollingInterval.High : PollingInterval.Low,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
checkWatchedDirectoriesDetailed(
host,
["/a/b", "/a/b/node_modules/@types"],
1,
/*recursive*/ true,
arrayToMap(
["/a/b", "/a/b/node_modules/@types"],
identity,
() => [{
fallbackPollingInterval: PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
});
it("with watchDirectory option", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
watchOptions: {
watchDirectory: "UseFsEvents"
}
})
};
const files = [libFile, commonFile1, commonFile2, configFile];
const host = createWatchedSystem(files, { runWithoutRecursiveWatches: true });
const watch = createWatchOfConfigFile(configFile.path, host, { extendedDiagnostics: true });
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
checkWatchedFilesDetailed(
host,
files.map(f => f.path.toLowerCase()),
1,
arrayToMap(
files,
f => f.path.toLowerCase(),
() => [PollingInterval.Low]
)
);
checkWatchedDirectoriesDetailed(
host,
["/a/b", "/a/b/node_modules/@types"],
1,
/*recursive*/ false,
arrayToMap(
["/a/b", "/a/b/node_modules/@types"],
identity,
() => [{
fallbackPollingInterval: PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
});
it("with fallbackPolling option", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
watchOptions: {
fallbackPolling: "PriorityInterval"
}
})
};
const files = [libFile, commonFile1, commonFile2, configFile];
const host = createWatchedSystem(files, { runWithoutRecursiveWatches: true, runWithFallbackPolling: true });
const watch = createWatchOfConfigFile(configFile.path, host, { extendedDiagnostics: true });
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
const filePaths = files.map(f => f.path.toLowerCase());
checkWatchedFilesDetailed(
host,
filePaths.concat(["/a/b", "/a/b/node_modules/@types"]),
1,
arrayToMap(
filePaths.concat(["/a/b", "/a/b/node_modules/@types"]),
identity,
f => [contains(filePaths, f) ? PollingInterval.Low : PollingInterval.Medium]
)
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
});
it("with watchFile as watch options to extend", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: "{}"
};
const files = [libFile, commonFile1, commonFile2, configFile];
const host = createWatchedSystem(files);
const watch = createWatchOfConfigFile(configFile.path, host, { extendedDiagnostics: true }, { watchFile: WatchFileKind.UseFsEvents });
checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path));
// Instead of polling watch (= watchedFiles), uses fsWatch
checkWatchedFiles(host, emptyArray);
checkWatchedDirectoriesDetailed(
host,
files.map(f => f.path.toLowerCase()),
1,
/*recursive*/ false,
arrayToMap(
files,
f => f.path.toLowerCase(),
f => [{
fallbackPollingInterval: f === configFile ? PollingInterval.High : PollingInterval.Low,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
checkWatchedDirectoriesDetailed(
host,
["/a/b", "/a/b/node_modules/@types"],
1,
/*recursive*/ true,
arrayToMap(
["/a/b", "/a/b/node_modules/@types"],
identity,
() => [{
fallbackPollingInterval: PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
});
});
});
}

View File

@@ -14,8 +14,9 @@ namespace ts.projectSystem {
readDirectory = "readDirectory"
}
type CalledMaps = CalledMapsWithSingleArg | CalledMapsWithFiveArgs;
type CalledWithFiveArgs = [readonly string[], readonly string[], readonly string[], number];
function createCallsTrackingHost(host: TestServerHost) {
const calledMaps: Record<CalledMapsWithSingleArg, MultiMap<true>> & Record<CalledMapsWithFiveArgs, MultiMap<[readonly string[], readonly string[], readonly string[], number]>> = {
const calledMaps: Record<CalledMapsWithSingleArg, MultiMap<true>> & Record<CalledMapsWithFiveArgs, MultiMap<CalledWithFiveArgs>> = {
fileExists: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.fileExists),
directoryExists: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.directoryExists),
getDirectories: setCallsTrackingWithSingleArgFn(CalledMapsWithSingleArg.getDirectories),
@@ -65,11 +66,11 @@ namespace ts.projectSystem {
}
function verifyCalledOnEachEntry(callback: CalledMaps, expectedKeys: Map<number>) {
TestFSWithWatch.checkMultiMapKeyCount(callback, calledMaps[callback], expectedKeys);
TestFSWithWatch.checkMap<true | CalledWithFiveArgs>(callback, calledMaps[callback], expectedKeys);
}
function verifyCalledOnEachEntryNTimes(callback: CalledMaps, expectedKeys: readonly string[], nTimes: number) {
TestFSWithWatch.checkMultiMapKeyCount(callback, calledMaps[callback], expectedKeys, nTimes);
TestFSWithWatch.checkMap<true | CalledWithFiveArgs>(callback, calledMaps[callback], expectedKeys, nTimes);
}
function verifyNoHostCalls() {
@@ -689,10 +690,10 @@ namespace ts.projectSystem {
};
files.push(debugTypesFile);
// Do not invoke recursive directory watcher for anything other than node_module/@types
const invoker = host.invokeWatchedDirectoriesRecursiveCallback;
host.invokeWatchedDirectoriesRecursiveCallback = (fullPath, relativePath) => {
const invoker = host.invokeFsWatchesRecursiveCallbacks;
host.invokeFsWatchesRecursiveCallbacks = (fullPath, eventName, entryFullPath) => {
if (fullPath.endsWith("@types")) {
invoker.call(host, fullPath, relativePath);
invoker.call(host, fullPath, eventName, entryFullPath);
}
};
host.reloadFS(files);

View File

@@ -6,7 +6,11 @@ namespace ts.projectSystem {
const projectSrcFolder = `${projectFolder}/src`;
const configFile: File = {
path: `${projectFolder}/tsconfig.json`,
content: "{}"
content: JSON.stringify({
watchOptions: {
synchronousWatchDirectory: true
}
})
};
const index: File = {
path: `${projectSrcFolder}/index.ts`,
@@ -246,4 +250,298 @@ namespace ts.projectSystem {
verifyFilePathStyle("//vda1cs4850/c$/users/username/myprojects/project/x.js");
});
});
describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler options", () => {
it("with watchFile option as host configuration", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: "{}"
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1));
const session = createSession(host);
session.executeCommandSeq<protocol.ConfigureRequest>({
command: protocol.CommandTypes.Configure,
arguments: {
watchOptions: {
watchFile: protocol.WatchFileKind.UseFsEvents
}
}
});
const service = session.getProjectService();
openFilesForSession([{ file: commonFile1, projectRootPath: "/a/b" }], session);
checkProjectActualFiles(
service.configuredProjects.get(configFile.path)!,
files.map(f => f.path).concat(commonFile1.path)
);
// Instead of polling watch (= watchedFiles), uses fsWatch
checkWatchedFiles(host, emptyArray);
checkWatchedDirectoriesDetailed(
host,
files.map(f => f.path.toLowerCase()),
1,
/*recursive*/ false,
arrayToMap(
files,
f => f.path.toLowerCase(),
f => [{
fallbackPollingInterval: f === configFile ? PollingInterval.High : PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
checkWatchedDirectoriesDetailed(
host,
["/a/b", "/a/b/node_modules/@types"],
1,
/*recursive*/ true,
arrayToMap(
["/a/b", "/a/b/node_modules/@types"],
identity,
() => [{
fallbackPollingInterval: PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
});
it("with watchDirectory option as host configuration", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: "{}"
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1), { runWithoutRecursiveWatches: true });
const session = createSession(host);
session.executeCommandSeq<protocol.ConfigureRequest>({
command: protocol.CommandTypes.Configure,
arguments: {
watchOptions: {
watchDirectory: protocol.WatchDirectoryKind.UseFsEvents
}
}
});
const service = session.getProjectService();
openFilesForSession([{ file: commonFile1, projectRootPath: "/a/b" }], session);
checkProjectActualFiles(
service.configuredProjects.get(configFile.path)!,
files.map(f => f.path).concat(commonFile1.path)
);
checkWatchedFilesDetailed(
host,
files.map(f => f.path.toLowerCase()),
1,
arrayToMap(
files,
f => f.path.toLowerCase(),
() => [PollingInterval.Low]
)
);
checkWatchedDirectoriesDetailed(
host,
["/a/b", "/a/b/node_modules/@types"],
1,
/*recursive*/ false,
arrayToMap(
["/a/b", "/a/b/node_modules/@types"],
identity,
() => [{
fallbackPollingInterval: PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
});
it("with fallbackPolling option as host configuration", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: "{}"
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1), { runWithoutRecursiveWatches: true, runWithFallbackPolling: true });
const session = createSession(host);
session.executeCommandSeq<protocol.ConfigureRequest>({
command: protocol.CommandTypes.Configure,
arguments: {
watchOptions: {
fallbackPolling: protocol.PollingWatchKind.PriorityInterval
}
}
});
const service = session.getProjectService();
openFilesForSession([{ file: commonFile1, projectRootPath: "/a/b" }], session);
checkProjectActualFiles(
service.configuredProjects.get(configFile.path)!,
files.map(f => f.path).concat(commonFile1.path)
);
const filePaths = files.map(f => f.path.toLowerCase());
checkWatchedFilesDetailed(
host,
filePaths.concat(["/a/b", "/a/b/node_modules/@types"]),
1,
arrayToMap(
filePaths.concat(["/a/b", "/a/b/node_modules/@types"]),
identity,
f => [contains(filePaths, f) ? PollingInterval.Low : PollingInterval.Medium]
)
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
});
it("with watchFile option in configFile", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
watchOptions: {
watchFile: "UseFsEvents"
}
})
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1));
const session = createSession(host);
const service = session.getProjectService();
openFilesForSession([{ file: commonFile1, projectRootPath: "/a/b" }], session);
checkProjectActualFiles(
service.configuredProjects.get(configFile.path)!,
files.map(f => f.path).concat(commonFile1.path)
);
// The closed script infos are watched using host settings
checkWatchedFilesDetailed(
host,
[libFile, commonFile2].map(f => f.path.toLowerCase()),
1,
arrayToMap(
[libFile, commonFile2],
f => f.path.toLowerCase(),
() => [PollingInterval.Low]
)
);
// Config file with the setting with fsWatch
checkWatchedDirectoriesDetailed(
host,
[configFile.path.toLowerCase()],
1,
/*recursive*/ false,
arrayToMap(
[configFile.path.toLowerCase()],
identity,
() => [{
fallbackPollingInterval: PollingInterval.High,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
checkWatchedDirectoriesDetailed(
host,
["/a/b", "/a/b/node_modules/@types"],
1,
/*recursive*/ true,
arrayToMap(
["/a/b", "/a/b/node_modules/@types"],
identity,
() => [{
fallbackPollingInterval: PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
});
it("with watchDirectory option in configFile", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
watchOptions: {
watchDirectory: "UseFsEvents"
}
})
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1), { runWithoutRecursiveWatches: true });
const session = createSession(host);
const service = session.getProjectService();
openFilesForSession([{ file: commonFile1, projectRootPath: "/a/b" }], session);
checkProjectActualFiles(
service.configuredProjects.get(configFile.path)!,
files.map(f => f.path).concat(commonFile1.path)
);
checkWatchedFilesDetailed(
host,
files.map(f => f.path.toLowerCase()),
1,
arrayToMap(
files,
f => f.path.toLowerCase(),
() => [PollingInterval.Low]
)
);
checkWatchedDirectoriesDetailed(
host,
["/a/b", "/a/b/node_modules/@types"],
1,
/*recursive*/ false,
arrayToMap(
["/a/b", "/a/b/node_modules/@types"],
identity,
() => [{
fallbackPollingInterval: PollingInterval.Medium,
fallbackOptions: { watchFile: WatchFileKind.PriorityPollingInterval }
}]
)
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
});
it("with fallbackPolling option in configFile", () => {
const configFile: File = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
watchOptions: {
fallbackPolling: "PriorityInterval"
}
})
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1), { runWithoutRecursiveWatches: true, runWithFallbackPolling: true });
const session = createSession(host);
session.executeCommandSeq<protocol.ConfigureRequest>({
command: protocol.CommandTypes.Configure,
arguments: {
watchOptions: {
fallbackPolling: protocol.PollingWatchKind.PriorityInterval
}
}
});
const service = session.getProjectService();
openFilesForSession([{ file: commonFile1, projectRootPath: "/a/b" }], session);
checkProjectActualFiles(
service.configuredProjects.get(configFile.path)!,
files.map(f => f.path).concat(commonFile1.path)
);
const filePaths = files.map(f => f.path.toLowerCase());
checkWatchedFilesDetailed(
host,
filePaths.concat(["/a/b", "/a/b/node_modules/@types"]),
1,
arrayToMap(
filePaths.concat(["/a/b", "/a/b/node_modules/@types"]),
identity,
f => [contains(filePaths, f) ? PollingInterval.Low : PollingInterval.Medium]
)
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
});
});
}