refactor(compiler-cli): decouple schema checker from typescript

Makes the `DomSchemaChecker` from TypeScript APIs.
This commit is contained in:
Kristiyan Kostadinov 2026-03-26 15:58:23 +01:00 committed by Pawel Kozlowski
parent 682aaf943f
commit d1c1bc3200
9 changed files with 120 additions and 99 deletions

View file

@ -8,20 +8,21 @@
// TCB generation exports for ng-hybrid-preprocessor
export {generateTypeCheckBlock} from '../src/ngtsc/typecheck/src/type_check_block';
export type {
TypeCheckingConfig,
TcbComponentMetadata,
TcbTypeCheckBlockMetadata,
TcbTypeParameter,
TypeCheckId,
TcbDirectiveMetadata,
TemplateDiagnostic,
TcbReferenceMetadata,
SourceMapping,
OutOfBandDiagnosticRecorder,
export {
type TypeCheckingConfig,
type TcbComponentMetadata,
type TcbTypeCheckBlockMetadata,
type TcbTypeParameter,
type TypeCheckId,
type TcbDirectiveMetadata,
type TemplateDiagnostic,
type TcbReferenceMetadata,
type SourceMapping,
type OutOfBandDiagnosticRecorder,
type DomSchemaChecker,
OutOfBadDiagnosticCategory,
} from '../src/ngtsc/typecheck/api';
export {DomSchemaChecker, RegistryDomSchemaChecker} from '../src/ngtsc/typecheck/src/dom';
export {RegistryDomSchemaChecker} from '../src/ngtsc/typecheck/src/dom';
export {Environment} from '../src/ngtsc/typecheck/src/environment';
export {TcbGenericContextBehavior} from '../src/ngtsc/typecheck/src/ops/context';
export {ImportManager} from '../src/ngtsc/translator';

View file

@ -13,3 +13,4 @@ export * from './context';
export * from './scope';
export * from './symbols';
export * from './oob';
export * from './schema';

View file

@ -0,0 +1,82 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ParseSourceSpan, SchemaMetadata, TmplAstHostElement} from '@angular/compiler';
import {TypeCheckId} from './api';
/**
* Checks every non-Angular element/property processed in a template and potentially produces
* diagnostics related to improper usage.
*
* A `DomSchemaChecker`'s job is to check DOM nodes and their attributes written used in templates
* and produce diagnostics if the nodes don't conform to the DOM specification. It acts as a
* collector for these diagnostics, and can be queried later to retrieve the list of any that have
* been generated.
*/
export interface DomSchemaChecker<T> {
/**
* Get the diagnostics that have been generated via `checkElement` and `checkProperty` calls
* thus far.
*/
readonly diagnostics: ReadonlyArray<T>;
/**
* Check a non-Angular element and record any diagnostics about it.
*
* @param id Template ID, suitable for resolution with a `TcbSourceResolver`.
* @param tagName Tag name of the element in question
* @param sourceSpanForDiagnostics Span that should be used when reporting diagnostics.
* @param schemas Any active schemas for the template, which might affect the validity of the
* element.
* @param hostIsStandalone Indicates whether the element's host is a standalone component.
*/
checkElement(
id: TypeCheckId,
tagName: string,
sourceSpanForDiagnostics: ParseSourceSpan,
schemas: SchemaMetadata[],
hostIsStandalone: boolean,
): void;
/**
* Check a property binding on an element and record any diagnostics about it.
*
* @param id the type check ID, suitable for resolution with a `TcbSourceResolver`.
* @param tagName tag name of the element.
* @param name the name of the property being checked.
* @param span the source span of the binding. This is redundant with `element.attributes` but is
* passed separately to avoid having to look up the particular property name.
* @param schemas any active schemas for the template, which might affect the validity of the
* property.
*/
checkTemplateElementProperty(
id: string,
tagName: string,
name: string,
span: ParseSourceSpan,
schemas: SchemaMetadata[],
hostIsStandalone: boolean,
): void;
/**
* Check a property binding on a host element and record any diagnostics about it.
* @param id the type check ID, suitable for resolution with a `TcbSourceResolver`.
* @param element the element node in question.
* @param name the name of the property being checked.
* @param span the source span of the binding.
* @param schemas any active schemas for the template, which might affect the validity of the
* property.
*/
checkHostElementProperty(
id: string,
element: TmplAstHostElement,
name: string,
span: ParseSourceSpan,
schemas: SchemaMetadata[],
): void;
}

View file

@ -37,11 +37,12 @@ import {
TypeCtorMetadata,
TemplateContext,
OutOfBandDiagnosticRecorder,
DomSchemaChecker,
} from '../api';
import {makeTemplateDiagnostic} from '../diagnostics';
import {adaptTypeCheckBlockMetadata} from './tcb_adapter';
import {DomSchemaChecker, RegistryDomSchemaChecker} from './dom';
import {RegistryDomSchemaChecker} from './dom';
import {Environment} from './environment';
import {OutOfBandDiagnosticRecorderImpl} from './oob';
import {ReferenceEmitEnvironment} from './reference_emit_environment';
@ -134,7 +135,7 @@ export interface PendingShimData {
/**
* The `DomSchemaChecker` in use for this template, which records any schema-related diagnostics.
*/
domSchemaChecker: DomSchemaChecker;
domSchemaChecker: DomSchemaChecker<TemplateDiagnostic>;
/**
* Shim file in the process of being generated.
@ -678,7 +679,7 @@ class InlineTcbOp implements Op {
readonly meta: TypeCheckBlockMetadata,
readonly config: TypeCheckingConfig,
readonly reflector: ReflectionHost,
readonly domSchemaChecker: DomSchemaChecker,
readonly domSchemaChecker: DomSchemaChecker<unknown>,
readonly oobRecorder: OutOfBandDiagnosticRecorder<unknown>,
) {}

View file

@ -15,7 +15,7 @@ import {
import ts from 'typescript';
import {ErrorCode, ngErrorCode} from '../../diagnostics';
import {TemplateDiagnostic, TypeCheckId} from '../api';
import {DomSchemaChecker, TemplateDiagnostic, TypeCheckId} from '../api';
import {makeTemplateDiagnostic} from '../diagnostics';
import {TypeCheckSourceResolver} from './tcb_util';
@ -23,83 +23,11 @@ import {TypeCheckSourceResolver} from './tcb_util';
export const REGISTRY = new DomElementSchemaRegistry();
const REMOVE_XHTML_REGEX = /^:xhtml:/;
/**
* Checks every non-Angular element/property processed in a template and potentially produces
* `ts.Diagnostic`s related to improper usage.
*
* A `DomSchemaChecker`'s job is to check DOM nodes and their attributes written used in templates
* and produce `ts.Diagnostic`s if the nodes don't conform to the DOM specification. It acts as a
* collector for these diagnostics, and can be queried later to retrieve the list of any that have
* been generated.
*/
export interface DomSchemaChecker {
/**
* Get the `ts.Diagnostic`s that have been generated via `checkElement` and `checkProperty` calls
* thus far.
*/
readonly diagnostics: ReadonlyArray<TemplateDiagnostic>;
/**
* Check a non-Angular element and record any diagnostics about it.
*
* @param id Template ID, suitable for resolution with a `TcbSourceResolver`.
* @param tagName Tag name of the element in question
* @param sourceSpanForDiagnostics Span that should be used when reporting diagnostics.
* @param schemas Any active schemas for the template, which might affect the validity of the
* element.
* @param hostIsStandalone Indicates whether the element's host is a standalone component.
*/
checkElement(
id: TypeCheckId,
tagName: string,
sourceSpanForDiagnostics: ParseSourceSpan,
schemas: SchemaMetadata[],
hostIsStandalone: boolean,
): void;
/**
* Check a property binding on an element and record any diagnostics about it.
*
* @param id the type check ID, suitable for resolution with a `TcbSourceResolver`.
* @param tagName tag name of the element.
* @param name the name of the property being checked.
* @param span the source span of the binding. This is redundant with `element.attributes` but is
* passed separately to avoid having to look up the particular property name.
* @param schemas any active schemas for the template, which might affect the validity of the
* property.
*/
checkTemplateElementProperty(
id: string,
tagName: string,
name: string,
span: ParseSourceSpan,
schemas: SchemaMetadata[],
hostIsStandalone: boolean,
): void;
/**
* Check a property binding on a host element and record any diagnostics about it.
* @param id the type check ID, suitable for resolution with a `TcbSourceResolver`.
* @param element the element node in question.
* @param name the name of the property being checked.
* @param span the source span of the binding.
* @param schemas any active schemas for the template, which might affect the validity of the
* property.
*/
checkHostElementProperty(
id: string,
element: TmplAstHostElement,
name: string,
span: ParseSourceSpan,
schemas: SchemaMetadata[],
): void;
}
/**
* Checks non-Angular elements and properties against the `DomElementSchemaRegistry`, a schema
* maintained by the Angular team via extraction from a browser IDL.
*/
export class RegistryDomSchemaChecker implements DomSchemaChecker {
export class RegistryDomSchemaChecker implements DomSchemaChecker<TemplateDiagnostic> {
private _diagnostics: TemplateDiagnostic[] = [];
get diagnostics(): ReadonlyArray<TemplateDiagnostic> {

View file

@ -7,12 +7,12 @@
*/
import {BoundTarget, SchemaMetadata} from '@angular/compiler';
import {DomSchemaChecker} from '../dom';
import {
TypeCheckId,
TcbDirectiveMetadata,
TcbPipeMetadata,
OutOfBandDiagnosticRecorder,
DomSchemaChecker,
} from '../../api';
import {Environment} from '../environment';
@ -56,7 +56,7 @@ export class Context {
constructor(
readonly env: Environment,
readonly domSchemaChecker: DomSchemaChecker,
readonly domSchemaChecker: DomSchemaChecker<unknown>,
readonly oobRecorder: OutOfBandDiagnosticRecorder<unknown>,
readonly id: TypeCheckId,
readonly boundTarget: BoundTarget<TcbDirectiveMetadata>,

View file

@ -6,8 +6,12 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {OutOfBandDiagnosticRecorder, TcbComponentMetadata, TcbTypeCheckBlockMetadata} from '../api';
import {DomSchemaChecker} from './dom';
import {
DomSchemaChecker,
OutOfBandDiagnosticRecorder,
TcbComponentMetadata,
TcbTypeCheckBlockMetadata,
} from '../api';
import {Environment} from './environment';
import {createHostBindingsBlockGuard} from './host_bindings';
import {Context} from './ops/context';
@ -43,7 +47,7 @@ export function generateTypeCheckBlock(
component: TcbComponentMetadata,
name: string,
meta: TcbTypeCheckBlockMetadata,
domSchemaChecker: DomSchemaChecker,
domSchemaChecker: DomSchemaChecker<unknown>,
oobRecorder: OutOfBandDiagnosticRecorder<unknown>,
): string {
const tcb = new Context(

View file

@ -11,9 +11,13 @@ import {AbsoluteFsPath} from '../../file_system';
import {Reference, ReferenceEmitter} from '../../imports';
import {ClassDeclaration, ReflectionHost} from '../../reflection';
import {ImportManager} from '../../translator';
import {OutOfBandDiagnosticRecorder, TypeCheckBlockMetadata, TypeCheckingConfig} from '../api';
import {
DomSchemaChecker,
OutOfBandDiagnosticRecorder,
TypeCheckBlockMetadata,
TypeCheckingConfig,
} from '../api';
import {DomSchemaChecker} from './dom';
import {Environment} from './environment';
import {ensureTypeCheckFilePreparationImports} from './tcb_util';
import {generateTypeCheckBlock} from './type_check_block';
@ -64,7 +68,7 @@ export class TypeCheckFile extends Environment {
addTypeCheckBlock(
ref: Reference<ClassDeclaration<ts.ClassDeclaration>>,
meta: TypeCheckBlockMetadata,
domSchemaChecker: DomSchemaChecker,
domSchemaChecker: DomSchemaChecker<unknown>,
oobRecorder: OutOfBandDiagnosticRecorder<unknown>,
genericContextBehavior: TcbGenericContextBehavior,
): void {

View file

@ -99,12 +99,12 @@ import {
TypeCheckingConfig,
} from '../api/api';
import {TemplateTypeCheckerImpl} from '../src/checker';
import {DomSchemaChecker} from '../src/dom';
import {TypeCheckShimGenerator} from '../src/shim';
import {TypeCheckFile} from '../src/type_check_file';
import {sfExtensionData} from '../../shims';
import {freshCompilationTicket, NgCompiler, NgCompilerHost} from '../../core';
import {TcbGenericContextBehavior} from '../src/ops/context';
import {DomSchemaChecker} from '../api/schema';
export function typescriptLibDts(): TestFile {
return {
@ -1027,7 +1027,7 @@ function parseInputOutputMappingArray(values: string[]) {
);
}
export class NoopSchemaChecker implements DomSchemaChecker {
export class NoopSchemaChecker implements DomSchemaChecker<TemplateDiagnostic> {
get diagnostics(): ReadonlyArray<TemplateDiagnostic> {
return [];
}