mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(compiler-cli): introduce NG8023 compile-time diagnostic for duplicate selectors
Add NG8023 extended diagnostic to report duplicate component selectors during compilation. This replaces the former NG0300 runtime error, ensuring the failure occurs at build time instead of runtime. Closes angular#48377 BREAKING CHANGE: Elements with multiple matching selectors will now throw at compile time.
This commit is contained in:
@@ -1450,7 +1450,7 @@ runInEachFileSystem((os: string) => {
|
||||
class DirectiveA {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp',
|
||||
selector: 'comp-a',
|
||||
template: '...',
|
||||
standalone: false,
|
||||
})
|
||||
@@ -1471,7 +1471,7 @@ runInEachFileSystem((os: string) => {
|
||||
class DirectiveB {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp',
|
||||
selector: 'comp-b',
|
||||
template: '...',
|
||||
standalone: false,
|
||||
})
|
||||
@@ -1481,7 +1481,8 @@ runInEachFileSystem((os: string) => {
|
||||
selector: 'app',
|
||||
template: \`
|
||||
<div dir></div>
|
||||
<comp></comp>
|
||||
<comp-a></comp-a>
|
||||
<comp-b></comp-b>
|
||||
\`,
|
||||
standalone: false,
|
||||
})
|
||||
@@ -1556,7 +1557,7 @@ runInEachFileSystem((os: string) => {
|
||||
class DirectiveA {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp',
|
||||
selector: 'comp-a',
|
||||
template: '...',
|
||||
standalone: false,
|
||||
})
|
||||
@@ -1577,7 +1578,7 @@ runInEachFileSystem((os: string) => {
|
||||
class DirectiveB {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp',
|
||||
selector: 'comp-b',
|
||||
template: '...',
|
||||
standalone: false,
|
||||
})
|
||||
@@ -1595,7 +1596,8 @@ runInEachFileSystem((os: string) => {
|
||||
selector: 'app',
|
||||
template: \`
|
||||
<div dir></div>
|
||||
<comp></comp>
|
||||
<comp-a></comp-a>
|
||||
<comp-b></comp-b>
|
||||
\`,
|
||||
standalone: false,
|
||||
})
|
||||
|
||||
@@ -8791,5 +8791,115 @@ suppress
|
||||
expect(diags.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiple matching components', () => {
|
||||
it('should report an error when multiple components match the same element', () => {
|
||||
env.tsconfig({strictTemplates: true});
|
||||
env.write(
|
||||
'test.ts',
|
||||
`
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: '',
|
||||
standalone: false,
|
||||
})
|
||||
export class CompA {}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: '',
|
||||
standalone: false,
|
||||
})
|
||||
export class CompB {}
|
||||
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<my-comp />',
|
||||
standalone: false,
|
||||
})
|
||||
export class TestCmp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [TestCmp, CompA, CompB],
|
||||
})
|
||||
export class Module {}
|
||||
`,
|
||||
);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].code).toBe(ngErrorCode(ErrorCode.MULTIPLE_MATCHING_COMPONENTS));
|
||||
expect(diags[0].messageText).toContain(
|
||||
'Multiple components match node with tagname my-comp',
|
||||
);
|
||||
});
|
||||
|
||||
it('should report an error when multiple components with attribute selectors match the same element', () => {
|
||||
env.tsconfig({strictTemplates: true});
|
||||
env.write(
|
||||
'test.ts',
|
||||
`
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: '[stroked-button]',
|
||||
template: '',
|
||||
})
|
||||
export class StrokedBtn {}
|
||||
|
||||
@Component({
|
||||
selector: '[raised-button]',
|
||||
template: '',
|
||||
})
|
||||
export class RaisedBtn {}
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: '<button stroked-button raised-button></button>',
|
||||
imports: [StrokedBtn, RaisedBtn],
|
||||
})
|
||||
export class App {}
|
||||
`,
|
||||
);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].code).toBe(ngErrorCode(ErrorCode.MULTIPLE_MATCHING_COMPONENTS));
|
||||
expect(diags[0].messageText).toContain(
|
||||
'Multiple components match node with tagname button',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not report an error when a single component and directives match', () => {
|
||||
env.tsconfig({strictTemplates: true});
|
||||
env.write(
|
||||
'test.ts',
|
||||
`
|
||||
import {Component, Directive} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: '',
|
||||
})
|
||||
export class CompA {}
|
||||
|
||||
@Directive({
|
||||
selector: 'my-comp',
|
||||
})
|
||||
export class DirB {}
|
||||
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<my-comp />',
|
||||
imports: [CompA, DirB],
|
||||
})
|
||||
export class TestCmp {}
|
||||
`,
|
||||
);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user