Skip to content

Commit df3bec6

Browse files
authored
Tweak module resolution failed lookup watching (#53591)
1 parent 1af17f8 commit df3bec6

File tree

987 files changed

+27184
-40892
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

987 files changed

+27184
-40892
lines changed

src/compiler/path.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,9 @@ function pathComponents(path: string, rootLength: number) {
451451
return [root, ...rest];
452452
}
453453

454+
/** @internal */
455+
export type PathPathComponents = Path[] & { __pathComponensBrand: any };
456+
454457
/**
455458
* Parse a path into an array containing a root component (at index 0) and zero or more path
456459
* components (at indices > 0). The result is not normalized.
@@ -484,6 +487,9 @@ function pathComponents(path: string, rootLength: number) {
484487
*
485488
* @internal
486489
*/
490+
export function getPathComponents(path: Path): PathPathComponents;
491+
/** @internal */
492+
export function getPathComponents(path: string, currentDirectory?: string): string[];
487493
export function getPathComponents(path: string, currentDirectory = "") {
488494
path = combinePaths(currentDirectory, path);
489495
return pathComponents(path, getRootLength(path));
@@ -501,11 +507,11 @@ export function getPathComponents(path: string, currentDirectory = "") {
501507
*
502508
* @internal
503509
*/
504-
export function getPathFromPathComponents(pathComponents: readonly string[]) {
505-
if (pathComponents.length === 0) return "";
510+
export function getPathFromPathComponents<T extends string>(pathComponents: readonly T[], length?: number) {
511+
if (pathComponents.length === 0) return "" as T;
506512

507513
const root = pathComponents[0] && ensureTrailingDirectorySeparator(pathComponents[0]);
508-
return root + pathComponents.slice(1).join(directorySeparator);
514+
return root + pathComponents.slice(1, length).join(directorySeparator) as T;
509515
}
510516

511517
//// Path Normalization

src/compiler/resolutionCache.ts

Lines changed: 140 additions & 145 deletions
Large diffs are not rendered by default.

src/server/editorServices.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import {
5454
getFileNamesFromConfigSpecs,
5555
getFileWatcherEventKind,
5656
getNormalizedAbsolutePath,
57+
getPathComponents,
5758
getSnapshotText,
5859
getWatchFactory,
5960
hasExtension,
@@ -1892,7 +1893,7 @@ export class ProjectService {
18921893
// created when any of the script infos are added as root of inferred project
18931894
if (this.configFileExistenceImpactsRootOfInferredProject(configFileExistenceInfo)) {
18941895
// If we cannot watch config file existence without configured project, close the configured file watcher
1895-
if (!canWatchDirectoryOrFile(getDirectoryPath(canonicalConfigFilePath) as Path)) {
1896+
if (!canWatchDirectoryOrFile(getPathComponents(getDirectoryPath(canonicalConfigFilePath) as Path))) {
18961897
configFileExistenceInfo.watcher!.close();
18971898
configFileExistenceInfo.watcher = noopConfigFileWatcher;
18981899
}
@@ -1979,7 +1980,7 @@ export class ProjectService {
19791980
(configFileExistenceInfo.openFilesImpactedByConfigFile ||= new Map()).set(info.path, true);
19801981

19811982
// If there is no configured project for this config file, add the file watcher
1982-
configFileExistenceInfo.watcher ||= canWatchDirectoryOrFile(getDirectoryPath(canonicalConfigFilePath) as Path) ?
1983+
configFileExistenceInfo.watcher ||= canWatchDirectoryOrFile(getPathComponents(getDirectoryPath(canonicalConfigFilePath) as Path)) ?
19831984
this.watchFactory.watchFile(
19841985
configFileName,
19851986
(_filename, eventKind) => this.onConfigFileChanged(canonicalConfigFilePath, eventKind),
@@ -2703,12 +2704,12 @@ export class ProjectService {
27032704
}
27042705

27052706
// Single inferred project does not have a project root and hence no current directory
2706-
return this.createInferredProject(/*currentDirectory*/ undefined, /*isSingleInferredProject*/ true);
2707+
return this.createInferredProject("", /*isSingleInferredProject*/ true);
27072708
}
27082709

2709-
private getOrCreateSingleInferredWithoutProjectRoot(currentDirectory: string | undefined): InferredProject {
2710+
private getOrCreateSingleInferredWithoutProjectRoot(currentDirectory: string): InferredProject {
27102711
Debug.assert(!this.useSingleInferredProject);
2711-
const expectedCurrentDirectory = this.toCanonicalFileName(this.getNormalizedAbsolutePath(currentDirectory || ""));
2712+
const expectedCurrentDirectory = this.toCanonicalFileName(this.getNormalizedAbsolutePath(currentDirectory));
27122713
// Reuse the project with same current directory but no roots
27132714
for (const inferredProject of this.inferredProjects) {
27142715
if (!inferredProject.projectRootPath &&
@@ -2721,7 +2722,7 @@ export class ProjectService {
27212722
return this.createInferredProject(currentDirectory);
27222723
}
27232724

2724-
private createInferredProject(currentDirectory: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: NormalizedPath): InferredProject {
2725+
private createInferredProject(currentDirectory: string, isSingleInferredProject?: boolean, projectRootPath?: NormalizedPath): InferredProject {
27252726
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects!; // TODO: GH#18217
27262727
let watchOptionsAndErrors: WatchOptionsAndErrors | false | undefined;
27272728
let typeAcquisition: TypeAcquisition | undefined;

src/server/project.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -465,11 +465,11 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
465465
public compileOnSaveEnabled: boolean,
466466
protected watchOptions: WatchOptions | undefined,
467467
directoryStructureHost: DirectoryStructureHost,
468-
currentDirectory: string | undefined,
468+
currentDirectory: string,
469469
) {
470470
this.projectName = projectName;
471471
this.directoryStructureHost = directoryStructureHost;
472-
this.currentDirectory = this.projectService.getNormalizedAbsolutePath(currentDirectory || "");
472+
this.currentDirectory = this.projectService.getNormalizedAbsolutePath(currentDirectory);
473473
this.getCanonicalFileName = this.projectService.toCanonicalFileName;
474474

475475
this.cancellationToken = new ThrottledCancellationToken(this.projectService.cancellationToken, this.projectService.throttleWaitMilliseconds);
@@ -514,7 +514,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
514514
// Use the current directory as resolution root only if the project created using current directory string
515515
this.resolutionCache = createResolutionCache(
516516
this,
517-
currentDirectory && this.currentDirectory,
517+
this.currentDirectory,
518518
/*logChangesWhenResolvingModule*/ true
519519
);
520520
this.languageService = createLanguageService(this, this.documentRegistry, this.projectService.serverMode);
@@ -2059,7 +2059,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
20592059
getNoDtsResolutionProject(rootFileNames: readonly string[]): Project {
20602060
Debug.assert(this.projectService.serverMode === LanguageServiceMode.Semantic);
20612061
if (!this.noDtsResolutionProject) {
2062-
this.noDtsResolutionProject = new AuxiliaryProject(this.projectService, this.documentRegistry, this.getCompilerOptionsForNoDtsResolutionProject());
2062+
this.noDtsResolutionProject = new AuxiliaryProject(this.projectService, this.documentRegistry, this.getCompilerOptionsForNoDtsResolutionProject(), this.currentDirectory);
20632063
}
20642064

20652065
enumerateInsertsAndDeletes<NormalizedPath, NormalizedPath>(
@@ -2176,7 +2176,7 @@ export class InferredProject extends Project {
21762176
compilerOptions: CompilerOptions,
21772177
watchOptions: WatchOptions | undefined,
21782178
projectRootPath: NormalizedPath | undefined,
2179-
currentDirectory: string | undefined,
2179+
currentDirectory: string,
21802180
pluginConfigOverrides: Map<string, any> | undefined,
21812181
typeAcquisition: TypeAcquisition | undefined) {
21822182
super(projectService.newInferredProjectName(),
@@ -2246,7 +2246,7 @@ export class InferredProject extends Project {
22462246
}
22472247

22482248
class AuxiliaryProject extends Project {
2249-
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) {
2249+
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions, currentDirectory: string) {
22502250
super(projectService.newAuxiliaryProjectName(),
22512251
ProjectKind.Auxiliary,
22522252
projectService,
@@ -2257,7 +2257,7 @@ class AuxiliaryProject extends Project {
22572257
/*compileOnSaveEnabled*/ false,
22582258
/*watchOptions*/ undefined,
22592259
projectService.host,
2260-
/*currentDirectory*/ undefined);
2260+
currentDirectory);
22612261
}
22622262

22632263
override isOrphan(): boolean {

src/services/rename.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ function wouldRenameInOtherNodeModules(
166166
}
167167

168168
function getPackagePathComponents(filePath: Path): string[] | undefined {
169-
const components = getPathComponents(filePath);
169+
const components = getPathComponents(filePath) as string[];
170170
const nodeModulesIdx = components.lastIndexOf("node_modules");
171171
if (nodeModulesIdx === -1) {
172172
return undefined;

src/testRunner/unittests/canWatch.ts

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ describe("unittests:: canWatch::", () => {
1414
paths.forEach(path => baselineCanWatchDirectoryOrFile(combinePaths(path, "package.json"), maxLengths));
1515
baseline.push("", "");
1616
function baselineCanWatchDirectoryOrFile(path: ts.Path, maxLengths: readonly number[]) {
17-
pushRow(baseline, [path, `${ts.canWatchDirectoryOrFile(path)}`], maxLengths);
17+
pushRow(baseline, [path, `${ts.canWatchDirectoryOrFile(ts.getPathComponents(path))}`], maxLengths);
1818
}
1919
},
2020
);
@@ -25,13 +25,12 @@ describe("unittests:: canWatch::", () => {
2525
(paths, longestPathLength, baseline) => {
2626
const testType = "canWatchAtTypes";
2727
const maxLengths = [longestPathLength + "/node_modules/@types".length, testType.length] as const;
28-
baselineCanWatchForRoot(paths, baseline, root => {
29-
pushHeader(baseline, ["Directory", testType], maxLengths);
30-
paths.forEach(path => {
31-
path = combinePaths(path, "node_modules/@types");
32-
pushRow(baseline, [path, `${ts.canWatchAtTypes(path, root)}`], maxLengths);
33-
});
28+
pushHeader(baseline, ["Directory", testType], maxLengths);
29+
paths.forEach(path => {
30+
path = combinePaths(path, "node_modules/@types");
31+
pushRow(baseline, [path, `${ts.canWatchAtTypes(path)}`], maxLengths);
3432
});
33+
baseline.push("", "");
3534
},
3635
);
3736

@@ -41,13 +40,12 @@ describe("unittests:: canWatch::", () => {
4140
(paths, longestPathLength, baseline) => {
4241
const testType = "canWatchAffectingLocation";
4342
const maxLengths = [longestPathLength + "/package.json".length, testType.length] as const;
44-
baselineCanWatchForRoot(paths, baseline, _root => {
45-
pushHeader(baseline, ["File", testType], maxLengths);
46-
paths.forEach(path => {
47-
path = combinePaths(path, "package.json");
48-
pushRow(baseline, [path, `${ts.canWatchAffectingLocation(path)}`], maxLengths);
49-
});
43+
pushHeader(baseline, ["File", testType], maxLengths);
44+
paths.forEach(path => {
45+
path = combinePaths(path, "package.json");
46+
pushRow(baseline, [path, `${ts.canWatchAffectingLocation(path)}`], maxLengths);
5047
});
48+
baseline.push("", "");
5149
},
5250
);
5351

@@ -65,21 +63,21 @@ describe("unittests:: canWatch::", () => {
6563
const recursive = "Recursive";
6664
const maxLength = longestPathLength + ts.combinePaths(forPath, "dir/subdir/somefile.d.ts").length;
6765
const maxLengths = [maxLength, maxLength, recursive.length] as const;
68-
baselineCanWatchForRoot(paths, baseline, root => {
66+
baselineCanWatchForRoot(paths, baseline, (rootPathCompoments, root) => {
6967
pushHeader(baseline, ["Location", "getDirectoryToWatchFailedLookupLocation", recursive], maxLengths);
7068
paths.forEach(path => {
71-
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "somefile.d.ts"), root, maxLengths);
72-
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "dir/somefile.d.ts"), root, maxLengths);
73-
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "dir/subdir/somefile.d.ts"), root, maxLengths);
69+
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "somefile.d.ts"), root, rootPathCompoments, maxLengths);
70+
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "dir/somefile.d.ts"), root, rootPathCompoments, maxLengths);
71+
baselineGetDirectoryToWatchFailedLookupLocation(combinePaths(path, forPath, "dir/subdir/somefile.d.ts"), root, rootPathCompoments, maxLengths);
7472
});
7573
});
76-
function baselineGetDirectoryToWatchFailedLookupLocation(path: ts.Path, root: ts.Path | undefined, maxLengths: readonly number[]) {
74+
function baselineGetDirectoryToWatchFailedLookupLocation(path: ts.Path, root: ts.Path, rootPathCompoments: Readonly<ts.PathPathComponents>, maxLengths: readonly number[]) {
7775
const result = ts.getDirectoryToWatchFailedLookupLocation(
7876
path,
7977
path,
8078
root,
8179
root,
82-
root !== undefined ? root.split(ts.directorySeparator).length : 0,
80+
rootPathCompoments,
8381
ts.returnUndefined,
8482
);
8583
pushRow(baseline, [path, result ? result.dir : "", result ? `${!result.nonRecursive}` : ""], maxLengths);
@@ -94,16 +92,18 @@ describe("unittests:: canWatch::", () => {
9492
(paths, longestPathLength, baseline) => {
9593
const maxLength = longestPathLength + "/node_modules/@types".length;
9694
const maxLengths = [maxLength, maxLength] as const;
97-
baselineCanWatchForRoot(paths, baseline, root => {
95+
baselineCanWatchForRoot(paths, baseline, (rootPathCompoments, root) => {
9896
pushHeader(baseline, ["Directory", "getDirectoryToWatchFailedLookupLocationFromTypeRoot"], maxLengths);
9997
paths.forEach(path => {
10098
path = combinePaths(path, "node_modules/@types");
10199
// This is invoked only on paths that are watched
102-
if (!ts.canWatchAtTypes(path, root)) return;
100+
if (!ts.canWatchAtTypes(path)) return;
103101
const result = ts.getDirectoryToWatchFailedLookupLocationFromTypeRoot(
104102
path,
105103
path,
106104
root,
105+
rootPathCompoments,
106+
ts.returnUndefined,
107107
ts.returnTrue,
108108
);
109109
pushRow(baseline, [path, result !== undefined ? result : ""], maxLengths);
@@ -112,16 +112,14 @@ describe("unittests:: canWatch::", () => {
112112
},
113113
);
114114

115-
function baselineCanWatchForRoot(paths: readonly ts.Path[], baseline: string[], baselineForRoot: (root: ts.Path | undefined) => void) {
116-
paths.forEach(baselineRoot);
117-
baselineRoot(/*rootDirForResolution*/ undefined);
118-
baseline.push("", "");
119-
120-
function baselineRoot(rootDirForResolution: ts.Path | undefined) {
115+
function baselineCanWatchForRoot(paths: readonly ts.Path[], baseline: string[], baselineForRoot: (rootPathCompoments: Readonly<ts.PathPathComponents>, root: ts.Path) => void) {
116+
paths.forEach(rootDirForResolution => {
121117
const root = ts.getRootDirectoryOfResolutionCache(rootDirForResolution, ts.returnUndefined) as ts.Path;
122-
baseline.push("", `## RootDirForResolution: ${rootDirForResolution}`, "", `Root: ${root}`);
123-
baselineForRoot(root);
124-
}
118+
assert(root === rootDirForResolution);
119+
baseline.push("", `## RootDirForResolution: ${rootDirForResolution}`);
120+
baselineForRoot(ts.getPathComponents(root), root);
121+
});
122+
baseline.push("", "");
125123
}
126124

127125
function baselineCanWatch(

0 commit comments

Comments
 (0)