diff --git a/packages/compiler-cli/src/ngtsc/typecheck/extended/checks/suffix_not_supported/index.ts b/packages/compiler-cli/src/ngtsc/typecheck/extended/checks/suffix_not_supported/index.ts index 265c2d235890..a9e0b424b3ff 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/extended/checks/suffix_not_supported/index.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/extended/checks/suffix_not_supported/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {AST, TmplAstBoundAttribute, TmplAstNode} from '@angular/compiler'; +import {AST, BindingType, TmplAstBoundAttribute, TmplAstNode} from '@angular/compiler'; import ts from 'typescript'; import {ErrorCode, ExtendedTemplateDiagnosticName} from '../../../../diagnostics'; @@ -20,6 +20,11 @@ import { const STYLE_SUFFIXES = ['px', '%', 'em']; +const SUFFIX_ERROR_MSG = formatExtendedError( + ErrorCode.SUFFIX_NOT_SUPPORTED, + `The ${STYLE_SUFFIXES.map((suffix) => `'.${suffix}'`).join(', ')} suffixes are only supported on style bindings`, +); + /** * A check which detects when the `.px`, `.%`, and `.em` suffixes are used with an attribute * binding. These suffixes are only available for style bindings. @@ -35,22 +40,13 @@ class SuffixNotSupportedCheck extends TemplateCheckWithVisitor node.name.endsWith(`.${suffix}`)) ) { return []; } - const diagnostic = ctx.makeTemplateDiagnostic( - node.keySpan, - formatExtendedError( - ErrorCode.SUFFIX_NOT_SUPPORTED, - `The ${STYLE_SUFFIXES.map((suffix) => `'.${suffix}'`).join( - ', ', - )} suffixes are only supported on style bindings`, - ), - ); - return [diagnostic]; + return [ctx.makeTemplateDiagnostic(node.keySpan, SUFFIX_ERROR_MSG)]; } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/extended/test/checks/suffix_not_supported/suffix_not_supported_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/extended/test/checks/suffix_not_supported/suffix_not_supported_spec.ts index 6ac35d83bee5..a47081bc11a2 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/extended/test/checks/suffix_not_supported/suffix_not_supported_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/extended/test/checks/suffix_not_supported/suffix_not_supported_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.dev/license */ -import {DiagnosticCategoryLabel} from '../../../../../core/api'; import ts from 'typescript'; import {ErrorCode, ExtendedTemplateDiagnosticName, ngErrorCode} from '../../../../../diagnostics'; @@ -97,5 +96,74 @@ runInEachFileSystem(() => { const diags = extendedTemplateChecker.getDiagnosticsForComponent(component); expect(diags.length).toBe(0); }); + + it('should produce suffix not supported warning for .% suffix', () => { + const fileName = absoluteFrom('/main.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'TestCmp': `
`}, + source: 'export class TestCmp {}', + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const component = getClass(sf, 'TestCmp'); + const extendedTemplateChecker = new ExtendedTemplateCheckerImpl( + templateTypeChecker, + program.getTypeChecker(), + [suffixNotSupportedFactory], + {}, + ); + const diags = extendedTemplateChecker.getDiagnosticsForComponent(component); + expect(diags.length).toBe(1); + expect(diags[0].category).toBe(ts.DiagnosticCategory.Warning); + expect(diags[0].code).toBe(ngErrorCode(ErrorCode.SUFFIX_NOT_SUPPORTED)); + expect(getSourceCodeForDiagnostic(diags[0])).toBe('attr.opacity.%'); + }); + + it('should produce suffix not supported warning for .em suffix', () => { + const fileName = absoluteFrom('/main.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'TestCmp': `
`}, + source: 'export class TestCmp {}', + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const component = getClass(sf, 'TestCmp'); + const extendedTemplateChecker = new ExtendedTemplateCheckerImpl( + templateTypeChecker, + program.getTypeChecker(), + [suffixNotSupportedFactory], + {}, + ); + const diags = extendedTemplateChecker.getDiagnosticsForComponent(component); + expect(diags.length).toBe(1); + expect(diags[0].category).toBe(ts.DiagnosticCategory.Warning); + expect(diags[0].code).toBe(ngErrorCode(ErrorCode.SUFFIX_NOT_SUPPORTED)); + expect(getSourceCodeForDiagnostic(diags[0])).toBe('attr.font-size.em'); + }); + + it('should not produce warning for attr binding without a style suffix', () => { + const fileName = absoluteFrom('/main.ts'); + const {program, templateTypeChecker} = setup([ + { + fileName, + templates: {'TestCmp': `
`}, + source: 'export class TestCmp { x = 1; }', + }, + ]); + const sf = getSourceFileOrError(program, fileName); + const component = getClass(sf, 'TestCmp'); + const extendedTemplateChecker = new ExtendedTemplateCheckerImpl( + templateTypeChecker, + program.getTypeChecker(), + [suffixNotSupportedFactory], + {}, + ); + const diags = extendedTemplateChecker.getDiagnosticsForComponent(component); + expect(diags.length).toBe(0); + }); }); });