diff --git a/packages/compiler/src/ml_parser/lexer.ts b/packages/compiler/src/ml_parser/lexer.ts
index de96aa74fba..c58f0bc6d45 100644
--- a/packages/compiler/src/ml_parser/lexer.ts
+++ b/packages/compiler/src/ml_parser/lexer.ts
@@ -692,7 +692,7 @@ class _Tokenizer {
this._cursor.advance();
try {
const charCode = parseInt(strNum, isHex ? 16 : 10);
- this._endToken([String.fromCharCode(charCode), this._cursor.getChars(start)]);
+ this._endToken([String.fromCodePoint(charCode), this._cursor.getChars(start)]);
} catch {
throw this._createError(
_unknownEntityErrorMsg(this._cursor.getChars(start)),
diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts
index fa70619e00e..332082a17c6 100644
--- a/packages/compiler/test/ml_parser/html_parser_spec.ts
+++ b/packages/compiler/test/ml_parser/html_parser_spec.ts
@@ -52,6 +52,22 @@ describe('HtmlParser', () => {
]);
});
+ it('should parse text nodes with HTML entities (5+ hex digits)', () => {
+ // Test with 🛈 (U+1F6C8 - Circled Information Source)
+ expect(humanizeDom(parser.parse('
🛈
', 'TestComp'))).toEqual([
+ [html.Element, 'div', 0],
+ [html.Text, '\u{1F6C8}', 1, [''], ['\u{1F6C8}', '🛈'], ['']],
+ ]);
+ });
+
+ it('should parse text nodes with decimal HTML entities (5+ digits)', () => {
+ // Test with 🛈 (U+1F6C8 - Circled Information Source) as decimal 128712
+ expect(humanizeDom(parser.parse('🛈
', 'TestComp'))).toEqual([
+ [html.Element, 'div', 0],
+ [html.Text, '\u{1F6C8}', 1, [''], ['\u{1F6C8}', '🛈'], ['']],
+ ]);
+ });
+
it('should normalize line endings within CDATA', () => {
const parsed = parser.parse('', 'TestComp');
expect(humanizeDom(parsed)).toEqual([
@@ -326,6 +342,22 @@ describe('HtmlParser', () => {
]);
});
+ it('should parse attributes containing encoded entities (5+ hex digits)', () => {
+ // Test with 🛈 (U+1F6C8 - Circled Information Source)
+ expect(humanizeDom(parser.parse('', 'TestComp'))).toEqual([
+ [html.Element, 'div', 0],
+ [html.Attribute, 'foo', '\u{1F6C8}', [''], ['\u{1F6C8}', '🛈'], ['']],
+ ]);
+ });
+
+ it('should parse attributes containing encoded decimal entities (5+ digits)', () => {
+ // Test with 🛈 (U+1F6C8 - Circled Information Source) as decimal 128712
+ expect(humanizeDom(parser.parse('', 'TestComp'))).toEqual([
+ [html.Element, 'div', 0],
+ [html.Attribute, 'foo', '\u{1F6C8}', [''], ['\u{1F6C8}', '🛈'], ['']],
+ ]);
+ });
+
it('should parse attributes containing unquoted interpolation', () => {
expect(humanizeDom(parser.parse('', 'TestComp'))).toEqual([
[html.Element, 'div', 0],
@@ -1632,6 +1664,25 @@ describe('HtmlParser', () => {
]);
});
+ it('should decode HTML entities with 5+ hex digits in interpolations', () => {
+ // Test with 🛈 (U+1F6C8 - Circled Information Source)
+ expect(
+ humanizeDomSourceSpans(parser.parse('{{🛈}}' + '{{🛈}}', 'TestComp')),
+ ).toEqual([
+ [
+ html.Text,
+ '{{\u{1F6C8}}}' + '{{\u{1F6C8}}}',
+ 0,
+ [''],
+ ['{{', '🛈', '}}'],
+ [''],
+ ['{{', '🛈', '}}'],
+ [''],
+ '{{🛈}}' + '{{🛈}}',
+ ],
+ ]);
+ });
+
it('should support interpolations in text', () => {
expect(
humanizeDomSourceSpans(parser.parse(' pre {{ value }} post
', 'TestComp')),
diff --git a/packages/compiler/test/ml_parser/lexer_spec.ts b/packages/compiler/test/ml_parser/lexer_spec.ts
index 6587fc799cd..bc57f2d8c61 100644
--- a/packages/compiler/test/ml_parser/lexer_spec.ts
+++ b/packages/compiler/test/ml_parser/lexer_spec.ts
@@ -2136,6 +2136,26 @@ describe('HtmlLexer', () => {
]);
});
+ it('should parse entities with more than 4 hex digits', () => {
+ // Test 5 hex digit entity: 🛈 (🛈 - Circled Information Source)
+ expect(tokenizeAndHumanizeParts('🛈')).toEqual([
+ [TokenType.TEXT, ''],
+ [TokenType.ENCODED_ENTITY, '\u{1F6C8}', '🛈'],
+ [TokenType.TEXT, ''],
+ [TokenType.EOF],
+ ]);
+ });
+
+ it('should parse entities with more than 4 decimal digits', () => {
+ // Test decimal entity: 🛈 (🛈 - Circled Information Source)
+ expect(tokenizeAndHumanizeParts('🛈')).toEqual([
+ [TokenType.TEXT, ''],
+ [TokenType.ENCODED_ENTITY, '\u{1F6C8}', '🛈'],
+ [TokenType.TEXT, ''],
+ [TokenType.EOF],
+ ]);
+ });
+
it('should store the locations', () => {
expect(tokenizeAndHumanizeSourceSpans('a&b')).toEqual([
[TokenType.TEXT, 'a'],