Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a7fa187

Browse filesBrowse files
authored
Merge pull request microsoft#19058 from Microsoft/whenWatchesFail
Swallow the directory watcher exceptions and ignore them
2 parents d0168af + 52d7c72 commit a7fa187
Copy full SHA for a7fa187

14 files changed

+79-25Lines changed: 79 additions & 25 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14012,7 +14012,7 @@ namespace ts {
1401214012
*/
1401314013
function isUnhyphenatedJsxName(name: string | __String) {
1401414014
// - is the only character supported in JSX attribute names that isn't valid in JavaScript identifiers
14015-
return (name as string).indexOf("-") < 0;
14015+
return !stringContains(name as string, "-");
1401614016
}
1401714017

1401814018
/**
Collapse file

‎src/compiler/core.ts‎

Copy file name to clipboardExpand all lines: src/compiler/core.ts
+6-2Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,7 +1648,7 @@ namespace ts {
16481648
}
16491649

16501650
export function isUrl(path: string) {
1651-
return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1;
1651+
return path && !isRootedDiskPath(path) && stringContains(path, "://");
16521652
}
16531653

16541654
export function pathIsRelative(path: string): boolean {
@@ -1917,8 +1917,12 @@ namespace ts {
19171917
return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos;
19181918
}
19191919

1920+
export function stringContains(str: string, substring: string): boolean {
1921+
return str.indexOf(substring) !== -1;
1922+
}
1923+
19201924
export function hasExtension(fileName: string): boolean {
1921-
return getBaseFileName(fileName).indexOf(".") >= 0;
1925+
return stringContains(getBaseFileName(fileName), ".");
19221926
}
19231927

19241928
export function fileExtensionIs(path: string, extension: string): boolean {
Collapse file

‎src/compiler/declarationEmitter.ts‎

Copy file name to clipboardExpand all lines: src/compiler/declarationEmitter.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ namespace ts {
172172

173173
function hasInternalAnnotation(range: CommentRange) {
174174
const comment = currentText.substring(range.pos, range.end);
175-
return comment.indexOf("@internal") >= 0;
175+
return stringContains(comment, "@internal");
176176
}
177177

178178
function stripInternal(node: Node) {
Collapse file

‎src/compiler/emitter.ts‎

Copy file name to clipboardExpand all lines: src/compiler/emitter.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,7 @@ namespace ts {
12261226
// check if numeric literal is a decimal literal that was originally written with a dot
12271227
const text = getLiteralTextOfNode(<LiteralExpression>expression);
12281228
return !expression.numericLiteralFlags
1229-
&& text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0;
1229+
&& !stringContains(text, tokenToString(SyntaxKind.DotToken));
12301230
}
12311231
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
12321232
// check if constant enum value is integer
Collapse file

‎src/compiler/moduleNameResolver.ts‎

Copy file name to clipboardExpand all lines: src/compiler/moduleNameResolver.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,7 @@ namespace ts {
10611061
export function getPackageNameFromAtTypesDirectory(mangledName: string): string {
10621062
const withoutAtTypePrefix = removePrefix(mangledName, "@types/");
10631063
if (withoutAtTypePrefix !== mangledName) {
1064-
return withoutAtTypePrefix.indexOf(mangledScopedPackageSeparator) !== -1 ?
1064+
return stringContains(withoutAtTypePrefix, mangledScopedPackageSeparator) ?
10651065
"@" + withoutAtTypePrefix.replace(mangledScopedPackageSeparator, ts.directorySeparator) :
10661066
withoutAtTypePrefix;
10671067
}
Collapse file

‎src/compiler/resolutionCache.ts‎

Copy file name to clipboardExpand all lines: src/compiler/resolutionCache.ts
+6-9Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -329,17 +329,14 @@ namespace ts {
329329
let dir = getDirectoryPath(getNormalizedAbsolutePath(failedLookupLocation, getCurrentDirectory()));
330330
let dirPath = getDirectoryPath(failedLookupLocationPath);
331331

332-
// If the directory is node_modules use it to watch
333-
if (isNodeModulesDirectory(dirPath)) {
334-
return { dir, dirPath };
332+
// If directory path contains node module, get the most parent node_modules directory for watching
333+
while (stringContains(dirPath, "/node_modules/")) {
334+
dir = getDirectoryPath(dir);
335+
dirPath = getDirectoryPath(dirPath);
335336
}
336337

337-
// If directory path contains node module, get the node_modules directory for watching
338-
if (dirPath.indexOf("/node_modules/") !== -1) {
339-
while (!isNodeModulesDirectory(dirPath)) {
340-
dir = getDirectoryPath(dir);
341-
dirPath = getDirectoryPath(dirPath);
342-
}
338+
// If the directory is node_modules use it to watch
339+
if (isNodeModulesDirectory(dirPath)) {
343340
return { dir, dirPath };
344341
}
345342

Collapse file

‎src/harness/unittests/tsserverProjectSystem.ts‎

Copy file name to clipboardExpand all lines: src/harness/unittests/tsserverProjectSystem.ts
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,43 @@ namespace ts.projectSystem {
23972397
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
23982398

23992399
});
2400+
2401+
it("Failed lookup locations are uses parent most node_modules directory", () => {
2402+
const file1: FileOrFolder = {
2403+
path: "/a/b/src/file1.ts",
2404+
content: 'import { classc } from "module1"'
2405+
};
2406+
const module1: FileOrFolder = {
2407+
path: "/a/b/node_modules/module1/index.d.ts",
2408+
content: `import { class2 } from "module2";
2409+
export classc { method2a(): class2; }`
2410+
};
2411+
const module2: FileOrFolder = {
2412+
path: "/a/b/node_modules/module2/index.d.ts",
2413+
content: "export class2 { method2() { return 10; } }"
2414+
};
2415+
const module3: FileOrFolder = {
2416+
path: "/a/b/node_modules/module/node_modules/module3/index.d.ts",
2417+
content: "export class3 { method2() { return 10; } }"
2418+
};
2419+
const configFile: FileOrFolder = {
2420+
path: "/a/b/src/tsconfig.json",
2421+
content: JSON.stringify({ files: [file1.path] })
2422+
};
2423+
const files = [file1, module1, module2, module3, configFile, libFile];
2424+
const host = createServerHost(files);
2425+
const projectService = createProjectService(host);
2426+
projectService.openClientFile(file1.path);
2427+
checkNumberOfProjects(projectService, { configuredProjects: 1 });
2428+
const project = projectService.configuredProjects.get(configFile.path);
2429+
assert.isDefined(project);
2430+
checkProjectActualFiles(project, [file1.path, libFile.path, module1.path, module2.path, configFile.path]);
2431+
checkWatchedFiles(host, [libFile.path, module1.path, module2.path, configFile.path]);
2432+
checkWatchedDirectories(host, [], /*recursive*/ false);
2433+
const watchedRecursiveDirectories = getTypeRootsFromLocation("/a/b/src");
2434+
watchedRecursiveDirectories.push("/a/b/src", "/a/b/node_modules");
2435+
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
2436+
});
24002437
});
24012438

24022439
describe("Proper errors", () => {
Collapse file

‎src/server/editorServices.ts‎

Copy file name to clipboardExpand all lines: src/server/editorServices.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,7 @@ namespace ts.server {
12181218
projectRootPath?: NormalizedPath) {
12191219
let searchPath = asNormalizedPath(getDirectoryPath(info.fileName));
12201220

1221-
while (!projectRootPath || searchPath.indexOf(projectRootPath) >= 0) {
1221+
while (!projectRootPath || stringContains(searchPath, projectRootPath)) {
12221222
const canonicalSearchPath = normalizedPathToPath(searchPath, this.currentDirectory, this.toCanonicalFileName);
12231223
const tsconfigFileName = asNormalizedPath(combinePaths(searchPath, "tsconfig.json"));
12241224
let result = action(tsconfigFileName, combinePaths(canonicalSearchPath, "tsconfig.json"));
Collapse file

‎src/server/server.ts‎

Copy file name to clipboardExpand all lines: src/server/server.ts
+19-3Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -753,10 +753,23 @@ namespace ts.server {
753753
const sys = <ServerHost>ts.sys;
754754
// use watchGuard process on Windows when node version is 4 or later
755755
const useWatchGuard = process.platform === "win32" && getNodeMajorVersion() >= 4;
756+
const originalWatchDirectory: ServerHost["watchDirectory"] = sys.watchDirectory.bind(sys);
757+
const noopWatcher: FileWatcher = { close: noop };
758+
// This is the function that catches the exceptions when watching directory, and yet lets project service continue to function
759+
// Eg. on linux the number of watches are limited and one could easily exhaust watches and the exception ENOSPC is thrown when creating watcher at that point
760+
function watchDirectorySwallowingException(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher {
761+
try {
762+
return originalWatchDirectory(path, callback, recursive);
763+
}
764+
catch (e) {
765+
logger.info(`Exception when creating directory watcher: ${e.message}`);
766+
return noopWatcher;
767+
}
768+
}
769+
756770
if (useWatchGuard) {
757771
const currentDrive = extractWatchDirectoryCacheKey(sys.resolvePath(sys.getCurrentDirectory()), /*currentDriveKey*/ undefined);
758772
const statusCache = createMap<boolean>();
759-
const originalWatchDirectory = sys.watchDirectory;
760773
sys.watchDirectory = function (path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher {
761774
const cacheKey = extractWatchDirectoryCacheKey(path, currentDrive);
762775
let status = cacheKey && statusCache.get(cacheKey);
@@ -790,14 +803,17 @@ namespace ts.server {
790803
}
791804
if (status) {
792805
// this drive is safe to use - call real 'watchDirectory'
793-
return originalWatchDirectory.call(sys, path, callback, recursive);
806+
return watchDirectorySwallowingException(path, callback, recursive);
794807
}
795808
else {
796809
// this drive is unsafe - return no-op watcher
797-
return { close() { } };
810+
return noopWatcher;
798811
}
799812
};
800813
}
814+
else {
815+
sys.watchDirectory = watchDirectorySwallowingException;
816+
}
801817

802818
// Override sys.write because fs.writeSync is not reliable on Node 4
803819
sys.write = (s: string) => writeMessage(new Buffer(s, "utf8"));
Collapse file

‎src/server/session.ts‎

Copy file name to clipboardExpand all lines: src/server/session.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1608,7 +1608,7 @@ namespace ts.server {
16081608
}
16091609

16101610
// No need to analyze lib.d.ts
1611-
const fileNamesInProject = fileNames.filter(value => value.indexOf("lib.d.ts") < 0);
1611+
const fileNamesInProject = fileNames.filter(value => !stringContains(value, "lib.d.ts"));
16121612
if (fileNamesInProject.length === 0) {
16131613
return;
16141614
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.