|
8 | 8 |
|
9 | 9 | import {CompilerOptions, createNgCompilerOptions} from '@angular/compiler-cli';
|
10 | 10 | import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core';
|
11 |
| -import {NgCompilerAdapter} from '@angular/compiler-cli/src/ngtsc/core/api'; |
12 |
| -import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; |
| 11 | +import {absoluteFromSourceFile, AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system'; |
13 | 12 | import {PatchedProgramIncrementalBuildStrategy} from '@angular/compiler-cli/src/ngtsc/incremental';
|
14 |
| -import {isShim} from '@angular/compiler-cli/src/ngtsc/shims'; |
15 | 13 | import {TypeCheckShimGenerator} from '@angular/compiler-cli/src/ngtsc/typecheck';
|
16 | 14 | import {OptimizeFor, TypeCheckingProgramStrategy} from '@angular/compiler-cli/src/ngtsc/typecheck/api';
|
17 | 15 | import * as ts from 'typescript/lib/tsserverlibrary';
|
18 | 16 |
|
19 | 17 | import {DefinitionBuilder} from './definitions';
|
| 18 | +import {isExternalTemplate, isTypeScriptFile, LanguageServiceAdapter} from './language_service_adapter'; |
20 | 19 | import {QuickInfoBuilder} from './quick_info';
|
21 | 20 |
|
22 | 21 | export class LanguageService {
|
23 | 22 | private options: CompilerOptions;
|
24 | 23 | private lastKnownProgram: ts.Program|null = null;
|
25 | 24 | private readonly strategy: TypeCheckingProgramStrategy;
|
26 |
| - private readonly adapter: NgCompilerAdapter; |
| 25 | + private readonly adapter: LanguageServiceAdapter; |
27 | 26 |
|
28 | 27 | constructor(project: ts.server.Project, private readonly tsLS: ts.LanguageService) {
|
29 | 28 | this.options = parseNgCompilerOptions(project);
|
30 | 29 | this.strategy = createTypeCheckingProgramStrategy(project);
|
31 |
| - this.adapter = createNgCompilerAdapter(project); |
| 30 | + this.adapter = new LanguageServiceAdapter(project); |
32 | 31 | this.watchConfigFile(project);
|
33 | 32 | }
|
34 | 33 |
|
35 | 34 | getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {
|
36 | 35 | const program = this.strategy.getProgram();
|
37 |
| - const compiler = this.createCompiler(program); |
38 |
| - if (fileName.endsWith('.ts')) { |
| 36 | + const compiler = this.createCompiler(program, fileName); |
| 37 | + const ttc = compiler.getTemplateTypeChecker(); |
| 38 | + const diagnostics: ts.Diagnostic[] = []; |
| 39 | + if (isTypeScriptFile(fileName)) { |
39 | 40 | const sourceFile = program.getSourceFile(fileName);
|
40 |
| - if (!sourceFile) { |
41 |
| - return []; |
| 41 | + if (sourceFile) { |
| 42 | + diagnostics.push(...ttc.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile)); |
| 43 | + } |
| 44 | + } else { |
| 45 | + const components = compiler.getComponentsWithTemplateFile(fileName); |
| 46 | + for (const component of components) { |
| 47 | + if (ts.isClassDeclaration(component)) { |
| 48 | + diagnostics.push(...ttc.getDiagnosticsForComponent(component)); |
| 49 | + } |
42 | 50 | }
|
43 |
| - const ttc = compiler.getTemplateTypeChecker(); |
44 |
| - const diagnostics = ttc.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile); |
45 |
| - this.lastKnownProgram = compiler.getNextProgram(); |
46 |
| - return diagnostics; |
47 | 51 | }
|
48 |
| - throw new Error('Ivy LS currently does not support external template'); |
| 52 | + this.lastKnownProgram = compiler.getNextProgram(); |
| 53 | + return diagnostics; |
49 | 54 | }
|
50 | 55 |
|
51 | 56 | getDefinitionAndBoundSpan(fileName: string, position: number): ts.DefinitionInfoAndBoundSpan
|
52 | 57 | |undefined {
|
53 | 58 | const program = this.strategy.getProgram();
|
54 |
| - const compiler = this.createCompiler(program); |
| 59 | + const compiler = this.createCompiler(program, fileName); |
55 | 60 | return new DefinitionBuilder(this.tsLS, compiler).getDefinitionAndBoundSpan(fileName, position);
|
56 | 61 | }
|
57 | 62 |
|
58 | 63 | getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo|undefined {
|
59 | 64 | const program = this.strategy.getProgram();
|
60 |
| - const compiler = this.createCompiler(program); |
| 65 | + const compiler = this.createCompiler(program, fileName); |
61 | 66 | return new QuickInfoBuilder(this.tsLS, compiler).get(fileName, position);
|
62 | 67 | }
|
63 | 68 |
|
64 |
| - private createCompiler(program: ts.Program): NgCompiler { |
| 69 | + /** |
| 70 | + * Create a new instance of Ivy compiler. |
| 71 | + * If the specified `fileName` refers to an external template, check if it has |
| 72 | + * changed since the last time it was read. If it has changed, signal the |
| 73 | + * compiler to reload the file via the adapter. |
| 74 | + */ |
| 75 | + private createCompiler(program: ts.Program, fileName: string): NgCompiler { |
| 76 | + if (isExternalTemplate(fileName)) { |
| 77 | + this.adapter.registerTemplateUpdate(fileName); |
| 78 | + } |
65 | 79 | return new NgCompiler(
|
66 | 80 | this.adapter,
|
67 | 81 | this.options,
|
@@ -107,31 +121,6 @@ export function parseNgCompilerOptions(project: ts.server.Project): CompilerOpti
|
107 | 121 | return createNgCompilerOptions(basePath, config, project.getCompilationSettings());
|
108 | 122 | }
|
109 | 123 |
|
110 |
| -function createNgCompilerAdapter(project: ts.server.Project): NgCompilerAdapter { |
111 |
| - return { |
112 |
| - entryPoint: null, // entry point is only needed if code is emitted |
113 |
| - constructionDiagnostics: [], |
114 |
| - ignoreForEmit: new Set(), |
115 |
| - factoryTracker: null, // no .ngfactory shims |
116 |
| - unifiedModulesHost: null, // only used in Bazel |
117 |
| - rootDirs: project.getCompilationSettings().rootDirs?.map(absoluteFrom) || [], |
118 |
| - isShim, |
119 |
| - fileExists(fileName: string): boolean { |
120 |
| - return project.fileExists(fileName); |
121 |
| - }, |
122 |
| - readFile(fileName: string): string | |
123 |
| - undefined { |
124 |
| - return project.readFile(fileName); |
125 |
| - }, |
126 |
| - getCurrentDirectory(): string { |
127 |
| - return project.getCurrentDirectory(); |
128 |
| - }, |
129 |
| - getCanonicalFileName(fileName: string): string { |
130 |
| - return project.projectService.toCanonicalFileName(fileName); |
131 |
| - }, |
132 |
| - }; |
133 |
| -} |
134 |
| - |
135 | 124 | function createTypeCheckingProgramStrategy(project: ts.server.Project):
|
136 | 125 | TypeCheckingProgramStrategy {
|
137 | 126 | return {
|
|
0 commit comments