Repository: angular
Issue: Long numeric HTML Entities in templates are compiled wrong
Labels: area: compiler,P3,compiler: parser,bug

### Which @angular/* package(s) are the source of the bug?

Don't known / other

### Is this a regression?

No

### Description

In HTML you can use numeric HTML Entities (hex or decimal) to represent characters (e.g. emojis and other symbols). E.g. `&#x41;` for the capital letter "A".

When Angular compiles a template containing an HTML entity with a length of 5 hexadecimal characters (e.g. `&#x1F6C8;`), the resulting template contains a wrong unicode character at the expected point. 


E.g. to display the "[Circled Information Source](https://www.compart.com/de/unicode/U+1F6C8)" someone can use the HTML code `&#128712;` or `&#x1F6C8;`. If you put one of these entities into your template, the resulting glyph is just a different unicode glyph. In UTF-8 encoding the template should contain (hex) `F0 9F 9B 88` but contains (hex) `EF 9B 88`. The contained (hex) `EF 9B 88` is just the 2-byte cropped entity `&#xF6C8;` (missing the leftmost `1`) in UTF-8 encoding.



### Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/stackblitz-starters-pntjeb?file=src%2Fmain.ts

### Please provide the exception or error you saw

The wrong glyph is rendered for long HTML Entities. The correct glyph is rendered if the Unicode glyph is contained directly inside of the templates content.

![image](https://github.com/angular/angular/assets/923638/cde68092-2c02-4446-8857-414d557caf1a)



### Please provide the environment you discovered this bug in (run `ng version`)

```true
Angular CLI: 16.1.7
Node: 18.17.0
Package Manager: npm 9.6.7
OS: darwin arm64

Angular: 16.1.8
... common, compiler, compiler-cli, core, platform-browser

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1601.7
@angular-devkit/build-angular   16.1.7
@angular-devkit/core            16.1.7
@angular-devkit/schematics      16.1.7
@angular/cli                    16.1.7
@schematics/angular             16.1.7
rxjs                            7.8.1
typescript                      5.1.6
zone.js                         0.13.1
```


### Anything else?

# What I think is wrong

Angular seems to translate these HTML Entities to Unicode escape sequences in JS Strings (e.g. HTML `&#xFFFF;` gets JS `"\uFFFF"`). From my observations my blind guess would be, that Angular tries to simply translate these into old-style JS Unicode escapes. These old style escapes support 4 hexadecimal chars only (`"\u0000"` to `"\uFFFF"`). So these longer Strings get simply chopped of after the 4 right-most hexadecimal chars.

I guess Angular would compile HTML `&#x1FFFF;` into JS `"\uFFFF"` (missing `1`).

## Why do I think so?

It just looks like it in the compiled templates of my example. See the comments in the compiled templates below.

Production build:

```javascript
const compiled = {
    // ...
    template: function (n, r) {
        if (1 & n && (gt(0, "div")(1, "h3"), Ue(2, "HTML Entity "), gt(3, "code"), Ue(4, "&#x1F6C8;"), 
            ut(), Ue(5, " in Angular templates"), ut(), gt(6, "p"), Ue(7, "Writing it as HTML Entity:"),
            
            //                                           vvvvvvvv HTML Entity text in template
            ut(), gt(8, "p")(9, "span", null, 0), Ue(11, "\uf6c8"), ut(), Ue(12, " (hex in browser "), 
            
            
            gt(13, "code"), Ue(14), ut(), Ue(15, ")"), ut(), gt(16, "p"), 
            Ue(17, "should be equal to writing it as Unicode String in Template:"), ut(), 
            
            //                                       vvvvvvvvvvv Unicode text in template
            gt(18, "p")(19, "span", null, 1), Ue(21, "\u{1f6c8}"), ut(), Ue(22, " (hex in browser "), 
            
            
            gt(23, "code"), Ue(24), ut(), Ue(25, ")"), ut(), 
            Gc(26, zA, 5, 0, "p", 2), Gc(27, ZA, 4, 0, "ng-template", null, 3, Jm), gt(29, "p"), 
            Ue(30, "Hacky variant by setting HTML at runtime:"), ut(), gt(31, "p"), qc(32, "span", null, 4), 
            Ue(34, " (hex in browser "), gt(35, "code"), Ue(36), ut(), Ue(37, ")"), ut()()), 2 & n) {
            const o = function Jp(e) {
                return function zr(e, t) {
                    return e[t]
                }(function Qv() {
                    return W.lFrame.contextLView
                }(), he + e)
            }(28);
            fi(14), wi(r.hexOne), fi(10), wi(r.hexTwo), fi(2), 
                zc("ngIf", r.hexOne != r.hexTwo)("ngIfElse", o), fi(10), wi(r.hexThree)
        }
    },
    // ...
}
```


... or in development build:

```javascript
const compiled = {
  // ...
  template: function AddOneButtonComponent_Template(rf, ctx) {
    if (rf & 1) {
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](0, "div")(1, "h3");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](2, "HTML Entity ");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](3, "code");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](4, "&#x1F6C8;");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](5, " in Angular templates");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](6, "p");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](7, "Writing it as HTML Entity:");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](8, "p")(9, "span", null, 0);

      //                                                       vvvvvvvv HTML Entity text in template
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](11, "\uF6C8");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](12, " (hex in browser ");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](13, "code");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](14);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](15, ")");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](16, "p");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](17, "should be equal to writing it as Unicode String in Template:");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](18, "p")(19, "span", null, 1);

      //                                                       vvvvvvvvvvvvvv Unicode text in template
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](21, "\uD83D\uDEC8");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](22, " (hex in browser ");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](23, "code");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](24);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](25, ")");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtemplate"](26, AddOneButtonComponent_p_26_Template, 5, 0, "p", 2);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtemplate"](27, AddOneButtonComponent_ng_template_27_Template, 4, 0, "ng-template", null, 3, _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtemplateRefExtractor"]);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](29, "p");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](30, "Hacky variant by setting HTML at runtime:");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](31, "p");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelement"](32, "span", null, 4);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](34, " (hex in browser ");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementStart"](35, "code");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](36);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]();
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtext"](37, ")");
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵelementEnd"]()();
    }
    if (rf & 2) {
      const _r3 = _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵreference"](28);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵadvance"](14);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtextInterpolate"](ctx.hexOne);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵadvance"](10);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtextInterpolate"](ctx.hexTwo);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵadvance"](2);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵproperty"]("ngIf", ctx.hexOne != ctx.hexTwo)("ngIfElse", _r3);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵadvance"](10);
      _angular_core__WEBPACK_IMPORTED_MODULE_2__["ɵɵtextInterpolate"](ctx.hexThree);
    }
  }
  // ...
}
```