mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(compiler-cli): Catch FatalDiagnosticError during template type checking (#49527)
This commit updates the type checking operation to catch `FatalDiagnosticError` and surface them as diagnostics rather than crashing. Fixes https://github.com/angular/vscode-ng-language-service/issues/1881 PR Close #49527
This commit is contained in:
parent
079f4bc1ef
commit
8a75a8ad26
6 changed files with 71 additions and 8 deletions
|
|
@ -11,7 +11,7 @@ import ts from 'typescript';
|
|||
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from '../../annotations';
|
||||
import {InjectableClassRegistry} from '../../annotations/common';
|
||||
import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../cycles';
|
||||
import {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, ngErrorCode} from '../../diagnostics';
|
||||
import {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics';
|
||||
import {checkForPrivateExports, ReferenceGraph} from '../../entry_point';
|
||||
import {absoluteFromSourceFile, AbsoluteFsPath, LogicalFileSystem, resolve} from '../../file_system';
|
||||
import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports';
|
||||
|
|
@ -831,8 +831,15 @@ export class NgCompiler {
|
|||
continue;
|
||||
}
|
||||
|
||||
diagnostics.push(
|
||||
...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
|
||||
try {
|
||||
diagnostics.push(
|
||||
...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
|
||||
} catch (err) {
|
||||
if (!(err instanceof FatalDiagnosticError)) {
|
||||
throw err;
|
||||
}
|
||||
diagnostics.push(err.toDiagnostic());
|
||||
}
|
||||
}
|
||||
|
||||
const program = this.programDriver.getProgram();
|
||||
|
|
@ -849,7 +856,14 @@ export class NgCompiler {
|
|||
// Get the diagnostics.
|
||||
const diagnostics: ts.Diagnostic[] = [];
|
||||
if (!sf.isDeclarationFile && !this.adapter.isShim(sf)) {
|
||||
diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
|
||||
try {
|
||||
diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
|
||||
} catch (err) {
|
||||
if (!(err instanceof FatalDiagnosticError)) {
|
||||
throw err;
|
||||
}
|
||||
diagnostics.push(err.toDiagnostic());
|
||||
}
|
||||
}
|
||||
|
||||
const program = this.programDriver.getProgram();
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ export class DtsMetadataReader implements MetadataReader {
|
|||
const inputs = ClassPropertyMapping.fromMappedObject(readInputsType(def.type.typeArguments[3]));
|
||||
const outputs = ClassPropertyMapping.fromMappedObject(
|
||||
readMapType(def.type.typeArguments[4], readStringType));
|
||||
|
||||
const hostDirectives = def.type.typeArguments.length > 8 ?
|
||||
readHostDirectivesType(this.checker, def.type.typeArguments[8], ref.bestGuessOwningModule) :
|
||||
null;
|
||||
|
|
|
|||
|
|
@ -71,8 +71,9 @@ export interface SimpleChanges {
|
|||
}
|
||||
|
||||
export type ɵɵNgModuleDeclaration<ModuleT, DeclarationsT, ImportsT, ExportsT> = unknown;
|
||||
export type ɵɵDirectiveDeclaration<DirT, SelectorT, ExportAsT, InputsT, OutputsT, QueriesT> =
|
||||
unknown;
|
||||
export type ɵɵDirectiveDeclaration<
|
||||
DirT, SelectorT, ExportAsT, InputsT, OutputsT, QueriesT, A = never, B = never,
|
||||
HostDirectivesT = never> = unknown;
|
||||
export type ɵɵPipeDeclaration<PipeT, NameT> = unknown;
|
||||
|
||||
export enum ViewEncapsulation {
|
||||
|
|
|
|||
|
|
@ -654,8 +654,8 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
|
|||
trait.analysisDiagnostics !== null) {
|
||||
diagnostics.push(...trait.analysisDiagnostics);
|
||||
}
|
||||
if (trait.state === TraitState.Resolved && trait.resolveDiagnostics !== null) {
|
||||
diagnostics.push(...trait.resolveDiagnostics);
|
||||
if (trait.state === TraitState.Resolved) {
|
||||
diagnostics.push(...(trait.resolveDiagnostics ?? []));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@
|
|||
*/
|
||||
|
||||
import ts from 'typescript';
|
||||
|
||||
import {SemanticSymbol} from '../../incremental/semantic_graph';
|
||||
|
||||
import {DecoratorHandler, DetectResult} from './api';
|
||||
|
||||
export enum TraitState {
|
||||
|
|
@ -194,6 +196,7 @@ class TraitImpl<D, A, S extends SemanticSymbol|null, R> {
|
|||
resolution: Readonly<R>|null = null;
|
||||
analysisDiagnostics: ts.Diagnostic[]|null = null;
|
||||
resolveDiagnostics: ts.Diagnostic[]|null = null;
|
||||
typeCheckDiagnostics: ts.Diagnostic[]|null = null;
|
||||
|
||||
constructor(handler: DecoratorHandler<D, A, S, R>, detected: DetectResult<D>) {
|
||||
this.handler = handler;
|
||||
|
|
@ -220,6 +223,7 @@ class TraitImpl<D, A, S extends SemanticSymbol|null, R> {
|
|||
this.resolution = resolution;
|
||||
this.state = TraitState.Resolved;
|
||||
this.resolveDiagnostics = diagnostics;
|
||||
this.typeCheckDiagnostics = null;
|
||||
return this as ResolvedTrait<D, A, S, R>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3072,6 +3072,49 @@ suppress
|
|||
`Argument of type 'string' is not assignable to parameter of type 'number'.`
|
||||
]);
|
||||
});
|
||||
|
||||
it('generates diagnostic when the library does not export the host directive', () => {
|
||||
env.tsconfig({
|
||||
paths: {'post': ['dist/post']},
|
||||
strictTemplates: true,
|
||||
_enableTemplateTypeChecker: true,
|
||||
});
|
||||
|
||||
// export post module and component but not the host directive. This is not valid. We won't
|
||||
// be able to import the host directive for template type checking.
|
||||
env.write('dist/post/index.d.ts', `
|
||||
export { PostComponent, PostModule } from './lib/post.component';
|
||||
`);
|
||||
|
||||
env.write('dist/post/lib/post.component.d.ts', `
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class HostBindDirective {
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<HostBindDirective, never, never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
export declare class PostComponent {
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<PostComponent, "lib-post", never, {}, {}, never, never, false, [{ directive: typeof HostBindDirective; inputs: {}; outputs: {}; }]>;
|
||||
}
|
||||
export declare class PostModule {
|
||||
static ɵmod: i0.ɵɵNgModuleDeclaration<PostModule, [typeof PostComponent], never, [typeof PostComponent]>;
|
||||
static ɵinj: i0.ɵɵInjectorDeclaration<PostModule>;
|
||||
}
|
||||
`);
|
||||
env.write('test.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
import {PostModule} from 'post';
|
||||
|
||||
@Component({
|
||||
template: '<lib-post />',
|
||||
imports: [PostModule],
|
||||
standalone: true,
|
||||
})
|
||||
export class Main { }
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(ts.flattenDiagnosticMessageText(diags[0].messageText, ''))
|
||||
.toContain('HostBindDirective');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue