mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
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.
This commit is contained in:
parent
550b3ba01b
commit
39f62fa408
5 changed files with 57 additions and 4 deletions
|
|
@ -104,6 +104,14 @@ export interface LinkedEditingRanges {
|
|||
* whose API surface is a strict superset of TypeScript's language service.
|
||||
*/
|
||||
export interface NgLanguageService extends ts.LanguageService {
|
||||
/**
|
||||
* Triggers the Angular compiler's analysis pipeline without performing
|
||||
* per-file type checking. This is a lighter alternative to calling
|
||||
* `getSemanticDiagnostics()` when the goal is only to ensure that the
|
||||
* Angular project has been analyzed (e.g. during project initialization).
|
||||
*/
|
||||
ensureProjectAnalyzed(): void;
|
||||
|
||||
getTcb(fileName: string, position: number): GetTcbResponse | undefined;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -101,6 +101,18 @@ export class LanguageService {
|
|||
return this.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the Angular compiler's analysis pipeline without performing
|
||||
* per-file type checking.
|
||||
*/
|
||||
ensureProjectAnalyzed(): void {
|
||||
this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
|
||||
// Accessing the template type checker forces compiler analysis through
|
||||
// public API without requiring per-file diagnostics computation.
|
||||
compiler.getTemplateTypeChecker();
|
||||
});
|
||||
}
|
||||
|
||||
getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {
|
||||
return this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
|
||||
let diagnostics: ts.Diagnostic[] = [];
|
||||
|
|
|
|||
|
|
@ -346,8 +346,13 @@ export function create(info: ts.server.PluginCreateInfo): NgLanguageService {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
function ensureProjectAnalyzed(): void {
|
||||
ngLS.ensureProjectAnalyzed();
|
||||
}
|
||||
|
||||
return {
|
||||
...tsLS,
|
||||
ensureProjectAnalyzed,
|
||||
getSyntacticDiagnostics,
|
||||
getSemanticDiagnostics,
|
||||
getSuggestionDiagnostics,
|
||||
|
|
|
|||
|
|
@ -31,4 +31,26 @@ describe('getExternalFiles()', () => {
|
|||
expect(externalFiles?.length).toBe(3);
|
||||
expect(externalFiles?.[0].endsWith('app.component.ngtypecheck.ts')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return all typecheck files when using ensureProjectAnalyzed', () => {
|
||||
const {project, tsLS} = setup();
|
||||
const plugin = initialize({typescript: ts});
|
||||
|
||||
let externalFiles = plugin.getExternalFiles?.(project, ts.ProgramUpdateLevel.Full);
|
||||
expect(externalFiles).toEqual([]);
|
||||
// Trigger compilation using the lighter ensureProjectAnalyzed() method
|
||||
// instead of getSemanticDiagnostics(). This initializes the Angular compiler
|
||||
// (analysis + resolution) without per-file type-checking overhead.
|
||||
const ngLS = new LanguageService(project, tsLS, {});
|
||||
ngLS.ensureProjectAnalyzed();
|
||||
// After ensureProjectAnalyzed(), the Angular compiler state is initialized.
|
||||
// Typecheck files are created lazily during diagnostics, so they don't exist yet.
|
||||
// But subsequent getSemanticDiagnostics() calls should work correctly since
|
||||
// the compiler is already analyzed.
|
||||
ngLS.getSemanticDiagnostics(APP_COMPONENT);
|
||||
externalFiles = plugin.getExternalFiles?.(project, ts.ProgramUpdateLevel.Full);
|
||||
// Includes 1 typecheck file, 1 template, and 1 css files
|
||||
expect(externalFiles?.length).toBe(3);
|
||||
expect(externalFiles?.[0].endsWith('app.component.ngtypecheck.ts')).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -292,13 +292,19 @@ export class Session {
|
|||
if (!project.hasRoots()) {
|
||||
return;
|
||||
}
|
||||
const fileName = project.getRootScriptInfos()[0].fileName;
|
||||
const label = `Global analysis - getSemanticDiagnostics for ${fileName}`;
|
||||
const languageService = project.getLanguageService();
|
||||
if (!isNgLanguageService(languageService)) {
|
||||
return;
|
||||
}
|
||||
const label = `Global analysis - ensureProjectAnalyzed for ${project.getProjectName()}`;
|
||||
if (isDebugMode) {
|
||||
console.time(label);
|
||||
}
|
||||
// Getting semantic diagnostics will trigger a global analysis.
|
||||
project.getLanguageService().getSemanticDiagnostics(fileName);
|
||||
// Trigger Angular compilation without per-file type checking overhead.
|
||||
// Previously this used getSemanticDiagnostics() which also ran per-file
|
||||
// TypeScript type checking on the first root file — wasted work since those
|
||||
// results were never consumed.
|
||||
languageService.ensureProjectAnalyzed();
|
||||
if (isDebugMode) {
|
||||
console.timeEnd(label);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue