Skip to content

Commit a40529a

Browse files
atscottAndrewKushnir
authored andcommitted
fix(compiler-cli): Catch FatalDiagnosticError during template type checking (#49792)
This commit updates the type checking operation to catch `FatalDiagnosticError` and surface them as diagnostics rather than crashing. Fixes angular/vscode-ng-language-service#1881 PR Close #49792
1 parent ebd14e7 commit a40529a

File tree

6 files changed

+71
-8
lines changed

6 files changed

+71
-8
lines changed

packages/compiler-cli/src/ngtsc/core/src/compiler.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import ts from 'typescript';
1111
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from '../../annotations';
1212
import {InjectableClassRegistry} from '../../annotations/common';
1313
import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../cycles';
14-
import {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, ngErrorCode} from '../../diagnostics';
14+
import {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics';
1515
import {checkForPrivateExports, ReferenceGraph} from '../../entry_point';
1616
import {absoluteFromSourceFile, AbsoluteFsPath, LogicalFileSystem, resolve} from '../../file_system';
1717
import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports';
@@ -837,8 +837,15 @@ export class NgCompiler {
837837
continue;
838838
}
839839

840-
diagnostics.push(
841-
...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
840+
try {
841+
diagnostics.push(
842+
...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
843+
} catch (err) {
844+
if (!(err instanceof FatalDiagnosticError)) {
845+
throw err;
846+
}
847+
diagnostics.push(err.toDiagnostic());
848+
}
842849
}
843850

844851
const program = this.programDriver.getProgram();
@@ -855,7 +862,14 @@ export class NgCompiler {
855862
// Get the diagnostics.
856863
const diagnostics: ts.Diagnostic[] = [];
857864
if (!sf.isDeclarationFile && !this.adapter.isShim(sf)) {
858-
diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
865+
try {
866+
diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
867+
} catch (err) {
868+
if (!(err instanceof FatalDiagnosticError)) {
869+
throw err;
870+
}
871+
diagnostics.push(err.toDiagnostic());
872+
}
859873
}
860874

861875
const program = this.programDriver.getProgram();

packages/compiler-cli/src/ngtsc/metadata/src/dts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export class DtsMetadataReader implements MetadataReader {
100100
readMapType(def.type.typeArguments[3], readStringType));
101101
const outputs = ClassPropertyMapping.fromMappedObject(
102102
readMapType(def.type.typeArguments[4], readStringType));
103+
103104
const hostDirectives = def.type.typeArguments.length > 8 ?
104105
readHostDirectivesType(this.checker, def.type.typeArguments[8], ref.bestGuessOwningModule) :
105106
null;

packages/compiler-cli/src/ngtsc/testing/fake_core/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ export interface SimpleChanges {
7171
}
7272

7373
export type ɵɵNgModuleDeclaration<ModuleT, DeclarationsT, ImportsT, ExportsT> = unknown;
74-
export type ɵɵDirectiveDeclaration<DirT, SelectorT, ExportAsT, InputsT, OutputsT, QueriesT> =
75-
unknown;
74+
export type ɵɵDirectiveDeclaration<
75+
DirT, SelectorT, ExportAsT, InputsT, OutputsT, QueriesT, A = never, B = never,
76+
HostDirectivesT = never> = unknown;
7677
export type ɵɵPipeDeclaration<PipeT, NameT> = unknown;
7778

7879
export enum ViewEncapsulation {

packages/compiler-cli/src/ngtsc/transform/src/compilation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -665,8 +665,8 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
665665
trait.analysisDiagnostics !== null) {
666666
diagnostics.push(...trait.analysisDiagnostics);
667667
}
668-
if (trait.state === TraitState.Resolved && trait.resolveDiagnostics !== null) {
669-
diagnostics.push(...trait.resolveDiagnostics);
668+
if (trait.state === TraitState.Resolved) {
669+
diagnostics.push(...(trait.resolveDiagnostics ?? []));
670670
}
671671
}
672672
}

packages/compiler-cli/src/ngtsc/transform/src/trait.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
*/
88

99
import ts from 'typescript';
10+
1011
import {SemanticSymbol} from '../../incremental/semantic_graph';
12+
1113
import {DecoratorHandler, DetectResult} from './api';
1214

1315
export enum TraitState {
@@ -194,6 +196,7 @@ class TraitImpl<D, A, S extends SemanticSymbol|null, R> {
194196
resolution: Readonly<R>|null = null;
195197
analysisDiagnostics: ts.Diagnostic[]|null = null;
196198
resolveDiagnostics: ts.Diagnostic[]|null = null;
199+
typeCheckDiagnostics: ts.Diagnostic[]|null = null;
197200

198201
constructor(handler: DecoratorHandler<D, A, S, R>, detected: DetectResult<D>) {
199202
this.handler = handler;
@@ -220,6 +223,7 @@ class TraitImpl<D, A, S extends SemanticSymbol|null, R> {
220223
this.resolution = resolution;
221224
this.state = TraitState.Resolved;
222225
this.resolveDiagnostics = diagnostics;
226+
this.typeCheckDiagnostics = null;
223227
return this as ResolvedTrait<D, A, S, R>;
224228
}
225229

packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3072,6 +3072,49 @@ suppress
30723072
`Argument of type 'string' is not assignable to parameter of type 'number'.`
30733073
]);
30743074
});
3075+
3076+
it('generates diagnostic when the library does not export the host directive', () => {
3077+
env.tsconfig({
3078+
paths: {'post': ['dist/post']},
3079+
strictTemplates: true,
3080+
_enableTemplateTypeChecker: true,
3081+
});
3082+
3083+
// export post module and component but not the host directive. This is not valid. We won't
3084+
// be able to import the host directive for template type checking.
3085+
env.write('dist/post/index.d.ts', `
3086+
export { PostComponent, PostModule } from './lib/post.component';
3087+
`);
3088+
3089+
env.write('dist/post/lib/post.component.d.ts', `
3090+
import * as i0 from "@angular/core";
3091+
export declare class HostBindDirective {
3092+
static ɵdir: i0.ɵɵDirectiveDeclaration<HostBindDirective, never, never, {}, {}, never, never, true, never>;
3093+
}
3094+
export declare class PostComponent {
3095+
static ɵcmp: i0.ɵɵComponentDeclaration<PostComponent, "lib-post", never, {}, {}, never, never, false, [{ directive: typeof HostBindDirective; inputs: {}; outputs: {}; }]>;
3096+
}
3097+
export declare class PostModule {
3098+
static ɵmod: i0.ɵɵNgModuleDeclaration<PostModule, [typeof PostComponent], never, [typeof PostComponent]>;
3099+
static ɵinj: i0.ɵɵInjectorDeclaration<PostModule>;
3100+
}
3101+
`);
3102+
env.write('test.ts', `
3103+
import {Component} from '@angular/core';
3104+
import {PostModule} from 'post';
3105+
3106+
@Component({
3107+
template: '<lib-post />',
3108+
imports: [PostModule],
3109+
standalone: true,
3110+
})
3111+
export class Main { }
3112+
`);
3113+
const diags = env.driveDiagnostics();
3114+
expect(diags.length).toBe(1);
3115+
expect(ts.flattenDiagnosticMessageText(diags[0].messageText, ''))
3116+
.toContain('HostBindDirective');
3117+
});
30753118
});
30763119
});
30773120
});

0 commit comments

Comments
 (0)