Add way to exclude files and directories to watch (#39243)

* Parse excludeDirectories and excludeFiles

* Use watch factory in typings installer

* Some refactoring for watchFactory

* Create Noop watcher if file or directory being watched is excluded

* Baselines without using exclude watch options

* Baselines including exclude option

* Handle exclude options in the system watches

* Add test without exclude option for recursive directory watching

* Test baselines with exclude option

* Always set sysLog

* Test for exclude option in server

* Add exclude options in the config file and fix the test

* Fix host configuration for server

* Handle host configuration for watch options

* Fix sysLog time log so baselines can be clean

* Handle reloadProjects to reload the project from scratch

* Ensure that file updates are reflected

* Feedback

* Feedback
This commit is contained in:
Sheetal Nandi
2020-11-04 13:30:06 -08:00
committed by GitHub
parent 6c6315db58
commit 76cf8fd78b
29 changed files with 1912 additions and 275 deletions

View File

@@ -187,6 +187,7 @@
"unittests/tsserver/projects.ts",
"unittests/tsserver/refactors.ts",
"unittests/tsserver/reload.ts",
"unittests/tsserver/reloadProjects.ts",
"unittests/tsserver/rename.ts",
"unittests/tsserver/resolutionCache.ts",
"unittests/tsserver/session.ts",

View File

@@ -663,6 +663,64 @@ namespace ts {
watchOptions: { fallbackPolling: undefined }
});
});
it("parse --excludeDirectories", () => {
assertParseResult(["--excludeDirectories", "**/temp", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: {},
watchOptions: { excludeDirectories: ["**/temp"] }
});
});
it("errors on invalid excludeDirectories", () => {
assertParseResult(["--excludeDirectories", "**/../*", "0.ts"],
{
errors: [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined
}
],
fileNames: ["0.ts"],
options: {},
watchOptions: { excludeDirectories: [] }
});
});
it("parse --excludeFiles", () => {
assertParseResult(["--excludeFiles", "**/temp/*.ts", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: {},
watchOptions: { excludeFiles: ["**/temp/*.ts"] }
});
});
it("errors on invalid excludeFiles", () => {
assertParseResult(["--excludeFiles", "**/../*", "0.ts"],
{
errors: [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined
}
],
fileNames: ["0.ts"],
options: {},
watchOptions: { excludeFiles: [] }
});
});
});
});
@@ -912,6 +970,54 @@ namespace ts {
watchOptions: { fallbackPolling: undefined }
});
});
it("errors on invalid excludeDirectories", () => {
assertParseResult(["--excludeDirectories", "**/../*"],
{
errors: [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined
}
],
projects: ["."],
buildOptions: {},
watchOptions: { excludeDirectories: [] }
});
});
it("parse --excludeFiles", () => {
assertParseResult(["--excludeFiles", "**/temp/*.ts"],
{
errors: [],
projects: ["."],
buildOptions: {},
watchOptions: { excludeFiles: ["**/temp/*.ts"] }
});
});
it("errors on invalid excludeFiles", () => {
assertParseResult(["--excludeFiles", "**/../*"],
{
errors: [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined
}
],
projects: ["."],
buildOptions: {},
watchOptions: { excludeFiles: [] }
});
});
});
});
}

View File

@@ -45,20 +45,33 @@ namespace ts {
expectedOptions: WatchOptions | undefined;
additionalFiles?: vfs.FileSet;
existingWatchOptions?: WatchOptions | undefined;
expectedErrors?: (sourceFile?: SourceFile) => Diagnostic[];
}
function verifyWatchOptions(scenario: () => VerifyWatchOptions[]) {
it("with json api", () => {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions } of scenario()) {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions, expectedErrors } of scenario()) {
const parsed = getParsedCommandJson(json, additionalFiles, existingWatchOptions);
assert.deepEqual(parsed.watchOptions, expectedOptions);
assert.deepEqual(parsed.watchOptions, expectedOptions, `With ${JSON.stringify(json)}`);
if (length(parsed.errors)) {
assert.deepEqual(parsed.errors, expectedErrors?.());
}
else {
assert.equal(0, length(expectedErrors?.()), `Expected no errors`);
}
}
});
it("with json source file api", () => {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions } of scenario()) {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions, expectedErrors } of scenario()) {
const parsed = getParsedCommandJsonNode(json, additionalFiles, existingWatchOptions);
assert.deepEqual(parsed.watchOptions, expectedOptions);
if (length(parsed.errors)) {
assert.deepEqual(parsed.errors, expectedErrors?.(parsed.options.configFile));
}
else {
assert.equal(0, length(expectedErrors?.(parsed.options.configFile)), `Expected no errors`);
}
}
});
}
@@ -156,7 +169,47 @@ namespace ts {
{
json: { watchOptions: { synchronousWatchDirectory: true } },
expectedOptions: { synchronousWatchDirectory: true }
}
},
{
json: { watchOptions: { excludeDirectories: ["**/temp"] } },
expectedOptions: { excludeDirectories: ["/**/temp"] }
},
{
json: { watchOptions: { excludeFiles: ["**/temp/*.ts"] } },
expectedOptions: { excludeFiles: ["/**/temp/*.ts"] }
},
{
json: { watchOptions: { excludeDirectories: ["**/../*"] } },
expectedOptions: { excludeDirectories: [] },
expectedErrors: sourceFile => [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: sourceFile,
start: sourceFile && sourceFile.text.indexOf(`"**/../*"`),
length: sourceFile && `"**/../*"`.length,
reportsDeprecated: undefined,
reportsUnnecessary: undefined
}
]
},
{
json: { watchOptions: { excludeFiles: ["**/../*"] } },
expectedOptions: { excludeFiles: [] },
expectedErrors: sourceFile => [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: sourceFile,
start: sourceFile && sourceFile.text.indexOf(`"**/../*"`),
length: sourceFile && `"**/../*"`.length,
reportsDeprecated: undefined,
reportsUnnecessary: undefined
}
]
},
]);
});

View File

@@ -411,6 +411,91 @@ namespace ts.tscWatch {
},
changes: emptyArray
});
describe("exclude options", () => {
function sys(watchOptions: WatchOptions, runWithoutRecursiveWatches?: boolean): WatchedSystem {
const configFile: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ exclude: ["node_modules"], watchOptions })
};
const main: File = {
path: `${projectRoot}/src/main.ts`,
content: `import { foo } from "bar"; foo();`
};
const bar: File = {
path: `${projectRoot}/node_modules/bar/index.d.ts`,
content: `export { foo } from "./foo";`
};
const foo: File = {
path: `${projectRoot}/node_modules/bar/foo.d.ts`,
content: `export function foo(): string;`
};
const fooBar: File = {
path: `${projectRoot}/node_modules/bar/fooBar.d.ts`,
content: `export function fooBar(): string;`
};
const temp: File = {
path: `${projectRoot}/node_modules/bar/temp/index.d.ts`,
content: "export function temp(): string;"
};
const files = [libFile, main, bar, foo, fooBar, temp, configFile];
return createWatchedSystem(files, { currentDirectory: projectRoot, runWithoutRecursiveWatches });
}
function verifyWorker(...additionalFlags: string[]) {
verifyTscWatch({
scenario,
subScenario: `watchOptions/with excludeFiles option${additionalFlags.join("")}`,
commandLineArgs: ["-w", ...additionalFlags],
sys: () => sys({ excludeFiles: ["node_modules/*"] }),
changes: [
{
caption: "Change foo",
change: sys => replaceFileText(sys, `${projectRoot}/node_modules/bar/foo.d.ts`, "foo", "fooBar"),
timeouts: sys => sys.checkTimeoutQueueLength(0),
}
]
});
verifyTscWatch({
scenario,
subScenario: `watchOptions/with excludeDirectories option${additionalFlags.join("")}`,
commandLineArgs: ["-w", ...additionalFlags],
sys: () => sys({ excludeDirectories: ["node_modules"] }),
changes: [
{
caption: "delete fooBar",
change: sys => sys.deleteFile(`${projectRoot}/node_modules/bar/fooBar.d.ts`),
timeouts: sys => sys.checkTimeoutQueueLength(0), }
]
});
verifyTscWatch({
scenario,
subScenario: `watchOptions/with excludeDirectories option with recursive directory watching${additionalFlags.join("")}`,
commandLineArgs: ["-w", ...additionalFlags],
sys: () => sys({ excludeDirectories: ["**/temp"] }, /*runWithoutRecursiveWatches*/ true),
changes: [
{
caption: "Directory watch updates because of main.js creation",
change: noop,
timeouts: sys => {
sys.checkTimeoutQueueLengthAndRun(1); // To update directory callbacks for main.js output
sys.checkTimeoutQueueLength(0);
},
},
{
caption: "add new folder to temp",
change: sys => sys.ensureFileOrFolder({ path: `${projectRoot}/node_modules/bar/temp/fooBar/index.d.ts`, content: "export function temp(): string;" }),
timeouts: sys => sys.checkTimeoutQueueLength(0),
}
]
});
}
verifyWorker();
verifyWorker("-extendedDiagnostics");
});
});
});
}

View File

@@ -0,0 +1,139 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: reloadProjects", () => {
const configFile: File = {
path: `${tscWatch.projectRoot}/tsconfig.json`,
content: JSON.stringify({
watchOptions: { excludeDirectories: ["node_modules"] }
})
};
const file1: File = {
path: `${tscWatch.projectRoot}/file1.ts`,
content: `import { foo } from "module1";
foo();
import { bar } from "./file2";
bar();`
};
const file2: File = {
path: `${tscWatch.projectRoot}/file2.ts`,
content: `export function bar(){}`
};
const moduleFile: File = {
path: `${tscWatch.projectRoot}/node_modules/module1/index.d.ts`,
content: `export function foo(): string;`
};
function verifyFileUpdates(host: TestServerHost, service: TestProjectService, project: server.Project) {
// update file
const updatedText = `${file2.content}
bar();`;
host.writeFile(file2.path, updatedText);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
assert.equal(project.getCurrentProgram()?.getSourceFile(file2.path)?.text, updatedText);
// delete file
host.deleteFile(file2.path);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
assert.isUndefined(project.getCurrentProgram()?.getSourceFile(file2.path)?.text);
assert.isUndefined(service.getScriptInfo(file2.path));
}
it("configured project", () => {
const host = createServerHost([configFile, libFile, file1, file2]);
const service = createProjectService(host);
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
service.openClientFile(file1.path);
checkNumberOfProjects(service, { configuredProjects: 1 });
const project = service.configuredProjects.get(configFile.path)!;
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path]);
// Install module1
host.ensureFileOrFolder(moduleFile);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
checkNumberOfProjects(service, { configuredProjects: 1 });
assert.strictEqual(service.configuredProjects.get(configFile.path), project);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path, moduleFile.path]);
verifyFileUpdates(host, service, project);
});
it("inferred project", () => {
const host = createServerHost([libFile, file1, file2]);
const service = createProjectService(host, /*parameters*/ undefined, { useInferredProjectPerProjectRoot: true, });
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
const timeoutId = host.getNextTimeoutId();
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
host.clearTimeout(timeoutId);
service.openClientFile(file1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, tscWatch.projectRoot);
checkNumberOfProjects(service, { inferredProjects: 1 });
const project = service.inferredProjects[0];
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
// Install module1
host.ensureFileOrFolder(moduleFile);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
checkNumberOfProjects(service, { inferredProjects: 1 });
assert.strictEqual(service.inferredProjects[0], project);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, moduleFile.path]);
verifyFileUpdates(host, service, project);
});
it("external project", () => {
const host = createServerHost([libFile, file1, file2]);
const service = createProjectService(host);
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
service.openExternalProject({
projectFileName: `${tscWatch.projectRoot}/project.sln`,
options: { excludeDirectories: ["node_modules"] },
rootFiles: [{ fileName: file1.path }, { fileName: file2.path }]
});
service.openClientFile(file1.path);
checkNumberOfProjects(service, { externalProjects: 1 });
const project = service.externalProjects[0];
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
// Install module1
host.ensureFileOrFolder(moduleFile);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
checkNumberOfProjects(service, { externalProjects: 1 });
assert.strictEqual(service.externalProjects[0], project);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, moduleFile.path]);
verifyFileUpdates(host, service, project);
});
it("external project with config file", () => {
const host = createServerHost([libFile, file1, file2, configFile]);
const service = createProjectService(host);
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
service.openExternalProject({
projectFileName: `${tscWatch.projectRoot}/project.sln`,
options: { excludeDirectories: ["node_modules"] },
rootFiles: [{ fileName: file1.path }, { fileName: file2.path }, { fileName: configFile.path }]
});
service.openClientFile(file1.path);
checkNumberOfProjects(service, { configuredProjects: 1 });
const project = service.configuredProjects.get(configFile.path)!;
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path]);
// Install module1
host.ensureFileOrFolder(moduleFile);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
checkNumberOfProjects(service, { configuredProjects: 1 });
assert.strictEqual(service.configuredProjects.get(configFile.path), project);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path, moduleFile.path]);
verifyFileUpdates(host, service, project);
});
});
}

View File

@@ -565,6 +565,202 @@ namespace ts.projectSystem {
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
});
describe("excludeDirectories", () => {
function setupFiles() {
const main: File = {
path: `${tscWatch.projectRoot}/src/main.ts`,
content: `import { foo } from "bar"; foo();`
};
const bar: File = {
path: `${tscWatch.projectRoot}/node_modules/bar/index.d.ts`,
content: `export { foo } from "./foo";`
};
const foo: File = {
path: `${tscWatch.projectRoot}/node_modules/bar/foo.d.ts`,
content: `export function foo(): string;`
};
return { main, bar, foo };
}
function setupConfigureHost(service: TestProjectService, configureHost: boolean | undefined) {
if (configureHost) {
service.setHostConfiguration({
watchOptions: { excludeDirectories: ["node_modules"] }
});
}
}
function setup(configureHost?: boolean) {
const configFile: File = {
path: `${tscWatch.projectRoot}/tsconfig.json`,
content: JSON.stringify({ include: ["src"], watchOptions: { excludeDirectories: ["node_modules"] } })
};
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo, configFile];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host);
setupConfigureHost(service, configureHost);
service.openClientFile(main.path);
return { host, configFile };
}
it("with excludeDirectories option in configFile", () => {
const { host, configFile } = setup();
checkWatchedFilesDetailed(host, [configFile.path, libFile.path], 1);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
arrayToMap(
[`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`],
identity,
f => f === `${tscWatch.projectRoot}/node_modules` ? 1 : 2,
),
/*recursive*/ true,
);
});
it("with excludeDirectories option in configuration", () => {
const { host, configFile } = setup(/*configureHost*/ true);
checkWatchedFilesDetailed(host, [configFile.path, libFile.path], 1);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`],
2,
/*recursive*/ true,
);
});
function setupExternalProject(configureHost?: boolean) {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host);
setupConfigureHost(service, configureHost);
service.openExternalProject(<protocol.ExternalProject>{
projectFileName: `${tscWatch.projectRoot}/project.csproj`,
rootFiles: toExternalFiles([main.path, bar.path, foo.path]),
options: { excludeDirectories: ["node_modules"] }
});
service.openClientFile(main.path);
return host;
}
it("external project watch options", () => {
const host = setupExternalProject();
checkWatchedFilesDetailed(host, [libFile.path], 1);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`],
1,
/*recursive*/ true,
);
});
it("external project watch options in host configuration", () => {
const host = setupExternalProject(/*configureHost*/ true);
checkWatchedFilesDetailed(host, [libFile.path], 1);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`],
1,
/*recursive*/ true,
);
});
it("external project watch options errors", () => {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host);
service.openExternalProject(<protocol.ExternalProject>{
projectFileName: `${tscWatch.projectRoot}/project.csproj`,
rootFiles: toExternalFiles([main.path, bar.path, foo.path]),
options: { excludeDirectories: ["**/../*"] }
});
service.openClientFile(main.path);
const project = service.externalProjects[0];
assert.deepEqual(project.getAllProjectErrors(), [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined,
reportsDeprecated: undefined,
reportsUnnecessary: undefined,
}
]);
});
function setupInferredProject(configureHost?: boolean) {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host, {}, { useInferredProjectPerProjectRoot: true });
setupConfigureHost(service, configureHost);
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
service.openClientFile(main.path, main.content, ScriptKind.TS, tscWatch.projectRoot);
return host;
}
it("inferred project watch options", () => {
const host = setupInferredProject();
checkWatchedFilesDetailed(
host,
[libFile.path, `${tscWatch.projectRoot}/tsconfig.json`, `${tscWatch.projectRoot}/jsconfig.json`, `${tscWatch.projectRoot}/src/tsconfig.json`, `${tscWatch.projectRoot}/src/jsconfig.json`],
1
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`],
1,
/*recursive*/ true,
);
});
it("inferred project watch options in host configuration", () => {
const host = setupInferredProject(/*configureHost*/ true);
checkWatchedFilesDetailed(
host,
[libFile.path, `${tscWatch.projectRoot}/tsconfig.json`, `${tscWatch.projectRoot}/jsconfig.json`, `${tscWatch.projectRoot}/src/tsconfig.json`, `${tscWatch.projectRoot}/src/jsconfig.json`],
1
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`],
1,
/*recursive*/ true,
);
});
it("inferred project watch options errors", () => {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host, {}, { useInferredProjectPerProjectRoot: true });
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["**/../*"] }, tscWatch.projectRoot);
service.openClientFile(main.path, main.content, ScriptKind.TS, tscWatch.projectRoot);
const project = service.inferredProjects[0];
assert.deepEqual(project.getAllProjectErrors(), [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined,
reportsDeprecated: undefined,
reportsUnnecessary: undefined,
}
]);
});
});
});
describe("unittests:: tsserver:: watchEnvironment:: file names on case insensitive file system", () => {