Skip to content

Commit d213792

Browse files
kbrillathePunderWoman
authored andcommitted
perf(language-service): use lightweight project warmup for Angular analysis
avoid per-file semantic diagnostics work when warming up a newly loaded project. add ensureProjectAnalyzed() to the language-service API and use it from the server startup path. implement warmup through public compiler API access with existing perf tracing, and add legacy test coverage for the new warmup flow. (cherry picked from commit 39f62fa)
1 parent 1d4783c commit d213792

File tree

5 files changed

+57
-4
lines changed

5 files changed

+57
-4
lines changed

packages/language-service/api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ export interface LinkedEditingRanges {
104104
* whose API surface is a strict superset of TypeScript's language service.
105105
*/
106106
export interface NgLanguageService extends ts.LanguageService {
107+
/**
108+
* Triggers the Angular compiler's analysis pipeline without performing
109+
* per-file type checking. This is a lighter alternative to calling
110+
* `getSemanticDiagnostics()` when the goal is only to ensure that the
111+
* Angular project has been analyzed (e.g. during project initialization).
112+
*/
113+
ensureProjectAnalyzed(): void;
114+
107115
getTcb(fileName: string, position: number): GetTcbResponse | undefined;
108116

109117
/**

packages/language-service/src/language_service.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ export class LanguageService {
101101
return this.options;
102102
}
103103

104+
/**
105+
* Triggers the Angular compiler's analysis pipeline without performing
106+
* per-file type checking.
107+
*/
108+
ensureProjectAnalyzed(): void {
109+
this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
110+
// Accessing the template type checker forces compiler analysis through
111+
// public API without requiring per-file diagnostics computation.
112+
compiler.getTemplateTypeChecker();
113+
});
114+
}
115+
104116
getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {
105117
return this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
106118
let diagnostics: ts.Diagnostic[] = [];

packages/language-service/src/ts_plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,13 @@ export function create(info: ts.server.PluginCreateInfo): NgLanguageService {
346346
return undefined;
347347
}
348348

349+
function ensureProjectAnalyzed(): void {
350+
ngLS.ensureProjectAnalyzed();
351+
}
352+
349353
return {
350354
...tsLS,
355+
ensureProjectAnalyzed,
351356
getSyntacticDiagnostics,
352357
getSemanticDiagnostics,
353358
getSuggestionDiagnostics,

packages/language-service/test/legacy/ts_plugin_spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,26 @@ describe('getExternalFiles()', () => {
3131
expect(externalFiles?.length).toBe(3);
3232
expect(externalFiles?.[0].endsWith('app.component.ngtypecheck.ts')).toBeTrue();
3333
});
34+
35+
it('should return all typecheck files when using ensureProjectAnalyzed', () => {
36+
const {project, tsLS} = setup();
37+
const plugin = initialize({typescript: ts});
38+
39+
let externalFiles = plugin.getExternalFiles?.(project, ts.ProgramUpdateLevel.Full);
40+
expect(externalFiles).toEqual([]);
41+
// Trigger compilation using the lighter ensureProjectAnalyzed() method
42+
// instead of getSemanticDiagnostics(). This initializes the Angular compiler
43+
// (analysis + resolution) without per-file type-checking overhead.
44+
const ngLS = new LanguageService(project, tsLS, {});
45+
ngLS.ensureProjectAnalyzed();
46+
// After ensureProjectAnalyzed(), the Angular compiler state is initialized.
47+
// Typecheck files are created lazily during diagnostics, so they don't exist yet.
48+
// But subsequent getSemanticDiagnostics() calls should work correctly since
49+
// the compiler is already analyzed.
50+
ngLS.getSemanticDiagnostics(APP_COMPONENT);
51+
externalFiles = plugin.getExternalFiles?.(project, ts.ProgramUpdateLevel.Full);
52+
// Includes 1 typecheck file, 1 template, and 1 css files
53+
expect(externalFiles?.length).toBe(3);
54+
expect(externalFiles?.[0].endsWith('app.component.ngtypecheck.ts')).toBeTrue();
55+
});
3456
});

vscode-ng-language-service/server/src/session.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,13 +292,19 @@ export class Session {
292292
if (!project.hasRoots()) {
293293
return;
294294
}
295-
const fileName = project.getRootScriptInfos()[0].fileName;
296-
const label = `Global analysis - getSemanticDiagnostics for ${fileName}`;
295+
const languageService = project.getLanguageService();
296+
if (!isNgLanguageService(languageService)) {
297+
return;
298+
}
299+
const label = `Global analysis - ensureProjectAnalyzed for ${project.getProjectName()}`;
297300
if (isDebugMode) {
298301
console.time(label);
299302
}
300-
// Getting semantic diagnostics will trigger a global analysis.
301-
project.getLanguageService().getSemanticDiagnostics(fileName);
303+
// Trigger Angular compilation without per-file type checking overhead.
304+
// Previously this used getSemanticDiagnostics() which also ran per-file
305+
// TypeScript type checking on the first root file — wasted work since those
306+
// results were never consumed.
307+
languageService.ensureProjectAnalyzed();
302308
if (isDebugMode) {
303309
console.timeEnd(label);
304310
}

0 commit comments

Comments
 (0)