Skip to content

Commit 936763a

Browse files
committed
Don’t create auto-import provider until a file is open that would use it
e.g., don’t create them during cross-project find-all-refs
1 parent 3d07e97 commit 936763a

File tree

4 files changed

+84
-41
lines changed

4 files changed

+84
-41
lines changed

src/compiler/core.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,18 @@ namespace ts {
619619
return false;
620620
}
621621

622+
export function someIterator<T>(iterator: Iterator<T>, predicate: (value: T) => boolean) {
623+
while (true) {
624+
const iterResult = iterator.next();
625+
if (iterResult.done) {
626+
return false;
627+
}
628+
if (predicate(iterResult.value)) {
629+
return true;
630+
}
631+
}
632+
}
633+
622634
/** Calls the callback with (start, afterEnd) index pairs for each range where 'pred' is true. */
623635
export function getRangesWhere<T>(arr: readonly T[], pred: (t: T) => boolean, cb: (start: number, afterEnd: number) => void): void {
624636
let start: number | undefined;

src/server/editorServices.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,6 @@ namespace ts.server {
11531153

11541154
// update projects to make sure that set of referenced files is correct
11551155
this.delayUpdateProjectGraphs(containingProjects, /*clearSourceMapperCache*/ false);
1156-
// TODO: update autoImportProviderVersion of projects containing info as auxiliary file
11571156
this.handleSourceMapProjects(info);
11581157
info.closeSourceMapFileWatcher();
11591158
// need to recalculate source map from declaration file

src/server/project.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1578,7 +1578,14 @@ namespace ts.server {
15781578
}
15791579

15801580
usePackageJsonAutoImportProvider() {
1581-
return this.projectService.usePackageJsonAutoImportProvider;
1581+
return this.projectService.usePackageJsonAutoImportProvider && this.isDefaultProjectForOpenFiles();
1582+
}
1583+
1584+
/*@internal*/
1585+
private isDefaultProjectForOpenFiles(): boolean {
1586+
return someIterator(
1587+
this.projectService.openFiles.keys(),
1588+
fileName => this.getScriptInfo(fileName)?.getDefaultProject() === this);
15821589
}
15831590
}
15841591

src/testRunner/unittests/tsserver/autoImportProvider.ts

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ namespace ts.projectSystem {
3838
indexTs
3939
]);
4040
openFilesForSession([indexTs], session);
41-
assert.equal(
42-
projectService.getDefaultProjectForFile(indexTs.path as server.NormalizedPath, /*ensureProject*/ true)!.getLanguageService().getAutoImportProvider(),
43-
undefined);
41+
assert.isUndefined(projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider());
4442
});
4543

4644
it("Auto import provider program is not created if dependencies are already in main program", () => {
@@ -52,9 +50,7 @@ namespace ts.projectSystem {
5250
{ path: indexTs.path, content: "import '@angular/forms';" }
5351
]);
5452
openFilesForSession([indexTs], session);
55-
assert.equal(
56-
projectService.getDefaultProjectForFile(indexTs.path as server.NormalizedPath, /*ensureProject*/ true)!.getLanguageService().getAutoImportProvider(),
57-
undefined);
53+
assert.isUndefined(projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider());
5854
});
5955

6056
it("Auto-import program is not created for projects already inside node_modules", () => {
@@ -70,9 +66,10 @@ namespace ts.projectSystem {
7066
openFilesForSession([angularFormsDts], session);
7167
checkNumberOfInferredProjects(projectService, 1);
7268
checkNumberOfConfiguredProjects(projectService, 0);
73-
assert.equal(
74-
projectService.getDefaultProjectForFile(angularFormsDts.path as server.NormalizedPath, /*ensureProject*/ true)!.getLanguageService().getAutoImportProvider(),
75-
undefined);
69+
assert.isUndefined(projectService
70+
.getDefaultProjectForFile(angularFormsDts.path as server.NormalizedPath, /*ensureProject*/ true)!
71+
.getLanguageService()
72+
.getAutoImportProvider());
7673
});
7774

7875
it("Auto-importable file is in inferred project until imported", () => {
@@ -88,6 +85,8 @@ namespace ts.projectSystem {
8885
assert.equal(
8986
projectService.getDefaultProjectForFile(angularFormsDts.path as server.NormalizedPath, /*ensureProject*/ true)?.projectKind,
9087
server.ProjectKind.Configured);
88+
89+
assert.isUndefined(projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider());
9190
});
9291

9392
it("Responds to package.json changes", () => {
@@ -100,18 +99,10 @@ namespace ts.projectSystem {
10099
]);
101100

102101
openFilesForSession([indexTs], session);
103-
assert.equal(
104-
projectService
105-
.getDefaultProjectForFile(indexTs.path as server.NormalizedPath, /*ensureProject*/ true)!
106-
.getLanguageService()
107-
.getAutoImportProvider(),
108-
undefined);
102+
assert.isUndefined(projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider());
109103

110104
host.writeFile(packageJson.path, packageJson.content);
111-
assert.ok(projectService
112-
.getDefaultProjectForFile(indexTs.path as server.NormalizedPath, /*ensureProject*/ true)!
113-
.getLanguageService()
114-
.getAutoImportProvider());
105+
assert.ok(projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider());
115106
});
116107

117108
it("Reuses autoImportProvider when program structure is unchanged", () => {
@@ -124,17 +115,11 @@ namespace ts.projectSystem {
124115
]);
125116

126117
openFilesForSession([indexTs], session);
127-
const autoImportProvider = projectService
128-
.getDefaultProjectForFile(indexTs.path as server.NormalizedPath, /*ensureProject*/ true)!
129-
.getLanguageService()
130-
.getAutoImportProvider();
118+
const autoImportProvider = projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider();
131119

132120
updateFile(indexTs.path, "console.log(0)");
133121
assert.strictEqual(
134-
projectService
135-
.getDefaultProjectForFile(indexTs.path as server.NormalizedPath, /*ensureProject*/ true)!
136-
.getLanguageService()
137-
.getAutoImportProvider(),
122+
projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider(),
138123
autoImportProvider);
139124
});
140125

@@ -150,25 +135,52 @@ namespace ts.projectSystem {
150135
]);
151136

152137
openFilesForSession([indexTs], session);
153-
projectService
154-
.getDefaultProjectForFile(indexTs.path as server.NormalizedPath, /*ensureProject*/ true)!
155-
.getLanguageService()
156-
.getAutoImportProvider();
138+
projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider();
157139

158140
// Directory watchers only fire for add/remove, not change.
159141
// This is ok since a real `npm install` will always trigger add/remove events.
160142
host.deleteFile(angularFormsDts.path);
161143
host.writeFile(angularFormsDts.path, "");
162144

163-
const autoImportProvider = projectService
164-
.getDefaultProjectForFile(indexTs.path as server.NormalizedPath, /*ensureProject*/ true)!
165-
.getLanguageService()
166-
.getAutoImportProvider();
167-
145+
const autoImportProvider = projectService.configuredProjects.get(tsconfig.path)!.getLanguageService().getAutoImportProvider();
168146
assert.equal(autoImportProvider!.getSourceFile(angularFormsDts.path)!.getText(), "");
169147
});
170148
});
171149

150+
describe("unittests:: tsserver:: autoImportProvider - monorepo", () => {
151+
it("Does not create auto import providers upon opening projects for find-all-references", () => {
152+
const files = [
153+
// node_modules
154+
angularFormsDts,
155+
angularFormsPackageJson,
156+
157+
// root
158+
{ path: tsconfig.path, content: `{ "references": [{ "path": "packages/a" }, { "path": "packages/b" }] }` },
159+
{ path: packageJson.path, content: `{ "private": true }` },
160+
161+
// packages/a
162+
{ path: "/packages/a/package.json", content: packageJson.content },
163+
{ path: "/packages/a/tsconfig.json", content: `{ "compilerOptions": { "composite": true }, "references": [{ "path": "../b" }] }` },
164+
{ path: "/packages/a/index.ts", content: "import { B } from '../b';" },
165+
166+
// packages/b
167+
{ path: "/packages/b/package.json", content: packageJson.content },
168+
{ path: "/packages/b/tsconfig.json", content: `{ "compilerOptions": { "composite": true } }` },
169+
{ path: "/packages/b/index.ts", content: `export class B {}` }
170+
];
171+
172+
const { projectService, session, findAllReferences } = setup(files);
173+
174+
openFilesForSession([files.find(f => f.path === "/packages/b/index.ts")!], session);
175+
checkNumberOfConfiguredProjects(projectService, 2); // Solution (no files), B
176+
findAllReferences("/packages/b/index.ts", 1, "export class B".length - 1);
177+
checkNumberOfConfiguredProjects(projectService, 3); // Solution (no files), A, B
178+
179+
// Project for A is created - ensure it doesn't have an autoImportProvider
180+
assert.isUndefined(projectService.configuredProjects.get("/packages/a/tsconfig.json")!.getLanguageService().getAutoImportProvider());
181+
});
182+
});
183+
172184
function setup(files: File[]) {
173185
const host = createServerHost(files);
174186
const session = createSession(host);
@@ -177,20 +189,33 @@ namespace ts.projectSystem {
177189
host,
178190
projectService,
179191
session,
180-
updateFile
192+
updateFile,
193+
findAllReferences
181194
};
182195

183196
function updateFile(path: string, newText: string) {
184-
const file = Debug.checkDefined(files.find(f => f.path === path));
197+
Debug.assertDefined(files.find(f => f.path === path));
185198
session.executeCommandSeq<protocol.ApplyChangedToOpenFilesRequest>({
186199
command: protocol.CommandTypes.ApplyChangedToOpenFiles,
187200
arguments: {
188201
openFiles: [{
189-
fileName: file.path,
202+
fileName: path,
190203
content: newText
191204
}]
192205
}
193206
});
194207
}
208+
209+
function findAllReferences(file: string, line: number, offset: number) {
210+
Debug.assertDefined(files.find(f => f.path === file));
211+
session.executeCommandSeq<protocol.ReferencesRequest>({
212+
command: protocol.CommandTypes.References,
213+
arguments: {
214+
file,
215+
line,
216+
offset
217+
}
218+
});
219+
}
195220
}
196221
}

0 commit comments

Comments
 (0)