Skip to content

Commit 1bf0f86

Browse files
Copilotjakebailey
andauthored
Fix JSX entity decoder skipping entities after non-entity ampersand (#3897)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
1 parent 2ea1509 commit 1bf0f86

6 files changed

Lines changed: 143 additions & 0 deletions

File tree

internal/transformers/jsxtransforms/jsx.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,19 @@ func decodeEntities(text string) string {
877877
break
878878
}
879879

880+
// Skip past any intervening '&' characters between the current '&'
881+
// and the ';'. Each such '&' is not part of a valid entity, so emit
882+
// it (and any text before the next '&') as literals.
883+
for {
884+
nextAmp := strings.IndexByte(text[1:semi], '&')
885+
if nextAmp < 0 {
886+
break
887+
}
888+
result.WriteString(text[:nextAmp+1])
889+
text = text[nextAmp+1:]
890+
semi -= nextAmp + 1
891+
}
892+
880893
entity := text[1:semi]
881894
decoded, ok := decodeEntity(entity)
882895
if ok {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(1,11): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
2+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(1,12): error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
3+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(1,22): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
4+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(2,11): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
5+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(2,12): error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
6+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(2,32): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
7+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(3,11): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
8+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(3,12): error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
9+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(3,26): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
10+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(4,11): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
11+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(4,12): error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
12+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(4,27): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
13+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(5,11): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
14+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(5,12): error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
15+
jsxEntityDecoderAfterNonEntityAmpersand.tsx(5,26): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
16+
17+
18+
==== jsxEntityDecoderAfterNonEntityAmpersand.tsx (15 errors) ====
19+
const a = <div>&&amp;</div>;
20+
~~~~~
21+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
22+
~~~
23+
!!! error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
24+
~~~~~~
25+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
26+
const b = <div>a&b&amp;c&d&lt;e</div>;
27+
~~~~~
28+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
29+
~~~
30+
!!! error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
31+
~~~~~~
32+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
33+
const c = <div>&amp;&amp;</div>;
34+
~~~~~
35+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
36+
~~~
37+
!!! error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
38+
~~~~~~
39+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
40+
const d = <div>&amp;&&amp;</div>;
41+
~~~~~
42+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
43+
~~~
44+
!!! error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
45+
~~~~~~
46+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
47+
const e = <div>a&b&c&amp;</div>;
48+
~~~~~
49+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
50+
~~~
51+
!!! error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
52+
~~~~~~
53+
!!! error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
54+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [tests/cases/compiler/jsxEntityDecoderAfterNonEntityAmpersand.tsx] ////
2+
3+
//// [jsxEntityDecoderAfterNonEntityAmpersand.tsx]
4+
const a = <div>&&amp;</div>;
5+
const b = <div>a&b&amp;c&d&lt;e</div>;
6+
const c = <div>&amp;&amp;</div>;
7+
const d = <div>&amp;&&amp;</div>;
8+
const e = <div>a&b&c&amp;</div>;
9+
10+
11+
//// [jsxEntityDecoderAfterNonEntityAmpersand.js]
12+
"use strict";
13+
const a = React.createElement("div", null, "&&");
14+
const b = React.createElement("div", null, "a&b&c&d<e");
15+
const c = React.createElement("div", null, "&&");
16+
const d = React.createElement("div", null, "&&&");
17+
const e = React.createElement("div", null, "a&b&c&");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//// [tests/cases/compiler/jsxEntityDecoderAfterNonEntityAmpersand.tsx] ////
2+
3+
=== jsxEntityDecoderAfterNonEntityAmpersand.tsx ===
4+
const a = <div>&&amp;</div>;
5+
>a : Symbol(a, Decl(jsxEntityDecoderAfterNonEntityAmpersand.tsx, 0, 5))
6+
7+
const b = <div>a&b&amp;c&d&lt;e</div>;
8+
>b : Symbol(b, Decl(jsxEntityDecoderAfterNonEntityAmpersand.tsx, 1, 5))
9+
10+
const c = <div>&amp;&amp;</div>;
11+
>c : Symbol(c, Decl(jsxEntityDecoderAfterNonEntityAmpersand.tsx, 2, 5))
12+
13+
const d = <div>&amp;&&amp;</div>;
14+
>d : Symbol(d, Decl(jsxEntityDecoderAfterNonEntityAmpersand.tsx, 3, 5))
15+
16+
const e = <div>a&b&c&amp;</div>;
17+
>e : Symbol(e, Decl(jsxEntityDecoderAfterNonEntityAmpersand.tsx, 4, 5))
18+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/compiler/jsxEntityDecoderAfterNonEntityAmpersand.tsx] ////
2+
3+
=== jsxEntityDecoderAfterNonEntityAmpersand.tsx ===
4+
const a = <div>&&amp;</div>;
5+
>a : any
6+
><div>&&amp;</div> : any
7+
>div : any
8+
>div : any
9+
10+
const b = <div>a&b&amp;c&d&lt;e</div>;
11+
>b : any
12+
><div>a&b&amp;c&d&lt;e</div> : any
13+
>div : any
14+
>div : any
15+
16+
const c = <div>&amp;&amp;</div>;
17+
>c : any
18+
><div>&amp;&amp;</div> : any
19+
>div : any
20+
>div : any
21+
22+
const d = <div>&amp;&&amp;</div>;
23+
>d : any
24+
><div>&amp;&&amp;</div> : any
25+
>div : any
26+
>div : any
27+
28+
const e = <div>a&b&c&amp;</div>;
29+
>e : any
30+
><div>a&b&c&amp;</div> : any
31+
>div : any
32+
>div : any
33+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// @jsx: react
2+
// @target: esnext
3+
4+
const a = <div>&&amp;</div>;
5+
const b = <div>a&b&amp;c&d&lt;e</div>;
6+
const c = <div>&amp;&amp;</div>;
7+
const d = <div>&amp;&&amp;</div>;
8+
const e = <div>a&b&c&amp;</div>;

0 commit comments

Comments
 (0)