feat(ivy): use the schema registry to check DOM bindings (#32171)
Previously, ngtsc attempted to use the .d.ts schema for HTML elements to check bindings to DOM properties. However, the TypeScript lib.dom.d.ts schema does not perfectly align with the Angular DomElementSchemaRegistry, and these inconsistencies would cause issues in apps. There is also the concern of supporting both CUSTOM_ELEMENTS_SCHEMA and NO_ERRORS_SCHEMA which would have been very difficult to do in the existing system. With this commit, the DomElementSchemaRegistry is employed in ngtsc to check bindings to the DOM. Previous work on producing template diagnostics is used to support generation of this different kind of error with the same high quality of error message. PR Close #32171
This commit is contained in:
@ -77,3 +77,6 @@ export enum ChangeDetectionStrategy {
|
||||
OnPush = 0,
|
||||
Default = 1
|
||||
}
|
||||
|
||||
export const CUSTOM_ELEMENTS_SCHEMA: any = false;
|
||||
export const NO_ERRORS_SCHEMA: any = false;
|
||||
|
@ -346,6 +346,129 @@ export declare class CommonModule {
|
||||
expect(diags[1].length).toEqual(15);
|
||||
});
|
||||
|
||||
describe('legacy schema checking with the DOM schema', () => {
|
||||
beforeEach(
|
||||
() => { env.tsconfig({ivyTemplateTypeCheck: true, fullTemplateTypeCheck: false}); });
|
||||
|
||||
it('should check for unknown elements', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'blah',
|
||||
template: '<foo>test</foo>',
|
||||
})
|
||||
export class FooCmp {}
|
||||
@NgModule({
|
||||
declarations: [FooCmp],
|
||||
})
|
||||
export class FooModule {}
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].messageText).toBe(`'foo' is not a valid HTML element.`);
|
||||
});
|
||||
|
||||
it('should check for unknown properties', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'blah',
|
||||
template: '<div [foo]="1">test</div>',
|
||||
})
|
||||
export class FooCmp {}
|
||||
@NgModule({
|
||||
declarations: [FooCmp],
|
||||
})
|
||||
export class FooModule {}
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].messageText).toBe(`'foo' is not a valid property of <div>.`);
|
||||
});
|
||||
|
||||
it('should convert property names when binding special properties', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'blah',
|
||||
template: '<label [for]="test">',
|
||||
})
|
||||
export class FooCmp {
|
||||
test: string = 'test';
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [FooCmp],
|
||||
})
|
||||
export class FooModule {}
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
// Should not be an error to bind [for] of <label>, even though the actual property in the
|
||||
// DOM schema.
|
||||
expect(diags.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should produce diagnostics for custom-elements-style elements when not using the CUSTOM_ELEMENTS_SCHEMA',
|
||||
() => {
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'blah',
|
||||
template: '<custom-element [foo]="1">test</custom-element>',
|
||||
})
|
||||
export class FooCmp {}
|
||||
@NgModule({
|
||||
declarations: [FooCmp],
|
||||
})
|
||||
export class FooModule {}
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(2);
|
||||
expect(diags[0].messageText).toBe(`'custom-element' is not a valid HTML element.`);
|
||||
expect(diags[1].messageText).toBe(`'foo' is not a valid property of <custom-element>.`);
|
||||
});
|
||||
|
||||
it('should not produce diagnostics for custom-elements-style elements when using the CUSTOM_ELEMENTS_SCHEMA',
|
||||
() => {
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'blah',
|
||||
template: '<custom-element [foo]="1">test</custom-element>',
|
||||
})
|
||||
export class FooCmp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [FooCmp],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
})
|
||||
export class FooModule {}
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags).toEqual([]);
|
||||
});
|
||||
|
||||
it('should not produce diagnostics when using the NO_ERRORS_SCHEMA', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'blah',
|
||||
template: '<foo [bar]="1"></foo>',
|
||||
})
|
||||
export class FooCmp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [FooCmp],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
})
|
||||
export class FooModule {}
|
||||
`);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error locations', () => {
|
||||
it('should be correct for direct templates', () => {
|
||||
env.write('test.ts', `
|
||||
|
Reference in New Issue
Block a user