diff --git a/packages/compiler/src/ml_parser/lexer.ts b/packages/compiler/src/ml_parser/lexer.ts index 3c8b04ab61b4..013b2eb1505f 100644 --- a/packages/compiler/src/ml_parser/lexer.ts +++ b/packages/compiler/src/ml_parser/lexer.ts @@ -1431,7 +1431,12 @@ function isDigitEntityEnd(code: number): boolean { } function isNamedEntityEnd(code: number): boolean { - return code === chars.$SEMICOLON || code === chars.$EOF || !chars.isAsciiLetter(code); + // Named entities may contain digits (e.g. ¹, ½, ▓). + return ( + code === chars.$SEMICOLON || + code === chars.$EOF || + !(chars.isAsciiLetter(code) || chars.isDigit(code)) + ); } function isExpansionCaseStart(peek: number): boolean { diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index 47b956aea40e..2ea1422e793d 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -68,6 +68,17 @@ describe('HtmlParser', () => { ]); }); + it('should parse named HTML entities containing digits', () => { + expect(humanizeDom(parser.parse('
¹
', 'TestComp'))).toEqual([ + [html.Element, 'div', 0], + [html.Text, '\u00B9', 1, [''], ['\u00B9', '¹'], ['']], + ]); + expect(humanizeDom(parser.parse('
½
', 'TestComp'))).toEqual([ + [html.Element, 'div', 0], + [html.Text, '\u00BD', 1, [''], ['\u00BD', '½'], ['']], + ]); + }); + it('should normalize line endings within CDATA', () => { const parsed = parser.parse('', 'TestComp'); expect(humanizeDom(parsed)).toEqual([ diff --git a/packages/compiler/test/ml_parser/lexer_spec.ts b/packages/compiler/test/ml_parser/lexer_spec.ts index 9d7bbd1c5c74..e3ebac0e8270 100644 --- a/packages/compiler/test/ml_parser/lexer_spec.ts +++ b/packages/compiler/test/ml_parser/lexer_spec.ts @@ -2094,6 +2094,27 @@ describe('HtmlLexer', () => { ]); }); + it('should parse named entities containing digits', () => { + expect(tokenizeAndHumanizeParts('¹')).toEqual([ + [TokenType.TEXT, ''], + [TokenType.ENCODED_ENTITY, '\u00B9', '¹'], + [TokenType.TEXT, ''], + [TokenType.EOF], + ]); + expect(tokenizeAndHumanizeParts('½')).toEqual([ + [TokenType.TEXT, ''], + [TokenType.ENCODED_ENTITY, '\u00BD', '½'], + [TokenType.TEXT, ''], + [TokenType.EOF], + ]); + expect(tokenizeAndHumanizeParts('▓')).toEqual([ + [TokenType.TEXT, ''], + [TokenType.ENCODED_ENTITY, '\u2593', '▓'], + [TokenType.TEXT, ''], + [TokenType.EOF], + ]); + }); + it('should parse hexadecimal entities', () => { expect(tokenizeAndHumanizeParts('AA')).toEqual([ [TokenType.TEXT, ''],