Skip to content

Commit 891f180

Browse files
SkyZeroZxthePunderWoman
authored andcommitted
fix(compiler): correctly compile long numeric HTML entities (#64297)
Fixes an issue where long numeric HTML entities (e.g. 🛈) were incorrectly compiled due to the use of 4-digit PR Close #64297
1 parent 0f81ef4 commit 891f180

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed

packages/compiler/src/ml_parser/lexer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,7 @@ class _Tokenizer {
692692
this._cursor.advance();
693693
try {
694694
const charCode = parseInt(strNum, isHex ? 16 : 10);
695-
this._endToken([String.fromCharCode(charCode), this._cursor.getChars(start)]);
695+
this._endToken([String.fromCodePoint(charCode), this._cursor.getChars(start)]);
696696
} catch {
697697
throw this._createError(
698698
_unknownEntityErrorMsg(this._cursor.getChars(start)),

packages/compiler/test/ml_parser/html_parser_spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ describe('HtmlParser', () => {
5252
]);
5353
});
5454

55+
it('should parse text nodes with HTML entities (5+ hex digits)', () => {
56+
// Test with 🛈 (U+1F6C8 - Circled Information Source)
57+
expect(humanizeDom(parser.parse('<div>&#x1F6C8;</div>', 'TestComp'))).toEqual([
58+
[html.Element, 'div', 0],
59+
[html.Text, '\u{1F6C8}', 1, [''], ['\u{1F6C8}', '&#x1F6C8;'], ['']],
60+
]);
61+
});
62+
63+
it('should parse text nodes with decimal HTML entities (5+ digits)', () => {
64+
// Test with 🛈 (U+1F6C8 - Circled Information Source) as decimal 128712
65+
expect(humanizeDom(parser.parse('<div>&#128712;</div>', 'TestComp'))).toEqual([
66+
[html.Element, 'div', 0],
67+
[html.Text, '\u{1F6C8}', 1, [''], ['\u{1F6C8}', '&#128712;'], ['']],
68+
]);
69+
});
70+
5571
it('should normalize line endings within CDATA', () => {
5672
const parsed = parser.parse('<![CDATA[ line 1 \r\n line 2 ]]>', 'TestComp');
5773
expect(humanizeDom(parsed)).toEqual([
@@ -326,6 +342,22 @@ describe('HtmlParser', () => {
326342
]);
327343
});
328344

345+
it('should parse attributes containing encoded entities (5+ hex digits)', () => {
346+
// Test with 🛈 (U+1F6C8 - Circled Information Source)
347+
expect(humanizeDom(parser.parse('<div foo="&#x1F6C8;"></div>', 'TestComp'))).toEqual([
348+
[html.Element, 'div', 0],
349+
[html.Attribute, 'foo', '\u{1F6C8}', [''], ['\u{1F6C8}', '&#x1F6C8;'], ['']],
350+
]);
351+
});
352+
353+
it('should parse attributes containing encoded decimal entities (5+ digits)', () => {
354+
// Test with 🛈 (U+1F6C8 - Circled Information Source) as decimal 128712
355+
expect(humanizeDom(parser.parse('<div foo="&#128712;"></div>', 'TestComp'))).toEqual([
356+
[html.Element, 'div', 0],
357+
[html.Attribute, 'foo', '\u{1F6C8}', [''], ['\u{1F6C8}', '&#128712;'], ['']],
358+
]);
359+
});
360+
329361
it('should parse attributes containing unquoted interpolation', () => {
330362
expect(humanizeDom(parser.parse('<div foo={{message}}></div>', 'TestComp'))).toEqual([
331363
[html.Element, 'div', 0],
@@ -1632,6 +1664,25 @@ describe('HtmlParser', () => {
16321664
]);
16331665
});
16341666

1667+
it('should decode HTML entities with 5+ hex digits in interpolations', () => {
1668+
// Test with 🛈 (U+1F6C8 - Circled Information Source)
1669+
expect(
1670+
humanizeDomSourceSpans(parser.parse('{{&#x1F6C8;}}' + '{{&#128712;}}', 'TestComp')),
1671+
).toEqual([
1672+
[
1673+
html.Text,
1674+
'{{\u{1F6C8}}}' + '{{\u{1F6C8}}}',
1675+
0,
1676+
[''],
1677+
['{{', '&#x1F6C8;', '}}'],
1678+
[''],
1679+
['{{', '&#128712;', '}}'],
1680+
[''],
1681+
'{{&#x1F6C8;}}' + '{{&#128712;}}',
1682+
],
1683+
]);
1684+
});
1685+
16351686
it('should support interpolations in text', () => {
16361687
expect(
16371688
humanizeDomSourceSpans(parser.parse('<div> pre {{ value }} post </div>', 'TestComp')),

packages/compiler/test/ml_parser/lexer_spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,6 +2136,26 @@ describe('HtmlLexer', () => {
21362136
]);
21372137
});
21382138

2139+
it('should parse entities with more than 4 hex digits', () => {
2140+
// Test 5 hex digit entity: &#x1F6C8; (🛈 - Circled Information Source)
2141+
expect(tokenizeAndHumanizeParts('&#x1F6C8;')).toEqual([
2142+
[TokenType.TEXT, ''],
2143+
[TokenType.ENCODED_ENTITY, '\u{1F6C8}', '&#x1F6C8;'],
2144+
[TokenType.TEXT, ''],
2145+
[TokenType.EOF],
2146+
]);
2147+
});
2148+
2149+
it('should parse entities with more than 4 decimal digits', () => {
2150+
// Test decimal entity: &#128712; (🛈 - Circled Information Source)
2151+
expect(tokenizeAndHumanizeParts('&#128712;')).toEqual([
2152+
[TokenType.TEXT, ''],
2153+
[TokenType.ENCODED_ENTITY, '\u{1F6C8}', '&#128712;'],
2154+
[TokenType.TEXT, ''],
2155+
[TokenType.EOF],
2156+
]);
2157+
});
2158+
21392159
it('should store the locations', () => {
21402160
expect(tokenizeAndHumanizeSourceSpans('a&amp;b')).toEqual([
21412161
[TokenType.TEXT, 'a'],

0 commit comments

Comments
 (0)