Skip to content

Commit 96d7293

Browse files
Copilotjakebailey
andauthored
Fix broken unicode escape sequence in JSX text (#1754)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: jakebailey <[email protected]>
1 parent 0442f87 commit 96d7293

File tree

5 files changed

+187
-8
lines changed

5 files changed

+187
-8
lines changed

internal/transformers/jsxtransforms/jsx.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -791,26 +791,26 @@ func fixupWhitespaceAndDecodeEntities(text string) string {
791791
initial := true
792792
// First non-whitespace character on this line.
793793
firstNonWhitespace := 0
794-
// Last non-whitespace character on this line.
795-
lastNonWhitespace := -1
794+
// End byte position of the last non-whitespace character on this line.
795+
lastNonWhitespaceEnd := -1
796796
// These initial values are special because the first line is:
797797
// firstNonWhitespace = 0 to indicate that we want leading whitespace,
798-
// but lastNonWhitespace = -1 as a special flag to indicate that we *don't* include the line if it's all whitespace.
798+
// but lastNonWhitespaceEnd = -1 as a special flag to indicate that we *don't* include the line if it's all whitespace.
799799
for i := 0; i < len(text); i++ {
800800
c, size := utf8.DecodeRuneInString(text[i:])
801801
if stringutil.IsLineBreak(c) {
802802
// If we've seen any non-whitespace characters on this line, add the 'trim' of the line.
803-
// (lastNonWhitespace === -1 is a special flag to detect whether the first line is all whitespace.)
804-
if firstNonWhitespace != -1 && lastNonWhitespace != -1 {
805-
addLineOfJsxText(acc, text[firstNonWhitespace:lastNonWhitespace+1], initial)
803+
// (lastNonWhitespaceEnd === -1 is a special flag to detect whether the first line is all whitespace.)
804+
if firstNonWhitespace != -1 && lastNonWhitespaceEnd != -1 {
805+
addLineOfJsxText(acc, text[firstNonWhitespace:lastNonWhitespaceEnd+1], initial)
806806
initial = false
807807
}
808808

809809
// Reset firstNonWhitespace for the next line.
810-
// Don't bother to reset lastNonWhitespace because we ignore it if firstNonWhitespace = -1.
810+
// Don't bother to reset lastNonWhitespaceEnd because we ignore it if firstNonWhitespace = -1.
811811
firstNonWhitespace = -1
812812
} else if !stringutil.IsWhiteSpaceSingleLine(c) {
813-
lastNonWhitespace = i
813+
lastNonWhitespaceEnd = i + size - 1 // Store the end byte position of the character
814814
if firstNonWhitespace == -1 {
815815
firstNonWhitespace = i
816816
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////
2+
3+
//// [jsxUnicodeEscapeSequence.tsx]
4+
/// <reference path="/.lib/react16.d.ts" />
5+
6+
export const InlineUnicodeChar = () => {
7+
// This should work correctly - inline character with other content
8+
return <div><span>Warning: ⚠ Error</span></div>;
9+
};
10+
11+
export const StandaloneUnicodeChar = () => {
12+
// This should reproduce the issue - unicode character on its own line
13+
return (<div><span></span>
14+
15+
</div>);
16+
};
17+
18+
export const MultipleUnicodeChars = () => {
19+
// Test multiple unicode characters
20+
return (<div>
21+
22+
23+
🚨
24+
</div>);
25+
};
26+
27+
28+
//// [jsxUnicodeEscapeSequence.js]
29+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
30+
/// <reference path="react16.d.ts" />
31+
export const InlineUnicodeChar = () => {
32+
// This should work correctly - inline character with other content
33+
return _jsx("div", { children: _jsx("span", { children: "Warning: \u26A0 Error" }) });
34+
};
35+
export const StandaloneUnicodeChar = () => {
36+
// This should reproduce the issue - unicode character on its own line
37+
return (_jsxs("div", { children: [_jsx("span", { children: "\u26A0" }), "\u26A0"] }));
38+
};
39+
export const MultipleUnicodeChars = () => {
40+
// Test multiple unicode characters
41+
return (_jsx("div", { children: "\u26A0 \u26D4 \uD83D\uDEA8" }));
42+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////
2+
3+
=== jsxUnicodeEscapeSequence.tsx ===
4+
/// <reference path="react16.d.ts" />
5+
6+
export const InlineUnicodeChar = () => {
7+
>InlineUnicodeChar : Symbol(InlineUnicodeChar, Decl(jsxUnicodeEscapeSequence.tsx, 2, 12))
8+
9+
// This should work correctly - inline character with other content
10+
return <div><span>Warning: ⚠ Error</span></div>;
11+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
12+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
13+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
14+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
15+
16+
};
17+
18+
export const StandaloneUnicodeChar = () => {
19+
>StandaloneUnicodeChar : Symbol(StandaloneUnicodeChar, Decl(jsxUnicodeEscapeSequence.tsx, 7, 12))
20+
21+
// This should reproduce the issue - unicode character on its own line
22+
return (<div><span>⚠</span>
23+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
24+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
25+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
26+
27+
28+
</div>);
29+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
30+
31+
};
32+
33+
export const MultipleUnicodeChars = () => {
34+
>MultipleUnicodeChars : Symbol(MultipleUnicodeChars, Decl(jsxUnicodeEscapeSequence.tsx, 14, 12))
35+
36+
// Test multiple unicode characters
37+
return (<div>
38+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
39+
40+
41+
42+
🚨
43+
</div>);
44+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
45+
46+
};
47+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////
2+
3+
=== jsxUnicodeEscapeSequence.tsx ===
4+
/// <reference path="react16.d.ts" />
5+
6+
export const InlineUnicodeChar = () => {
7+
>InlineUnicodeChar : () => JSX.Element
8+
>() => { // This should work correctly - inline character with other content return <div><span>Warning: ⚠ Error</span></div>;} : () => JSX.Element
9+
10+
// This should work correctly - inline character with other content
11+
return <div><span>Warning: ⚠ Error</span></div>;
12+
><div><span>Warning: ⚠ Error</span></div> : JSX.Element
13+
>div : any
14+
><span>Warning: ⚠ Error</span> : JSX.Element
15+
>span : any
16+
>span : any
17+
>div : any
18+
19+
};
20+
21+
export const StandaloneUnicodeChar = () => {
22+
>StandaloneUnicodeChar : () => JSX.Element
23+
>() => { // This should reproduce the issue - unicode character on its own line return (<div><span>⚠</span> ⚠ </div>);} : () => JSX.Element
24+
25+
// This should reproduce the issue - unicode character on its own line
26+
return (<div><span>⚠</span>
27+
>(<div><span>⚠</span> ⚠ </div>) : JSX.Element
28+
><div><span>⚠</span> ⚠ </div> : JSX.Element
29+
>div : any
30+
><span>⚠</span> : JSX.Element
31+
>span : any
32+
>span : any
33+
34+
35+
</div>);
36+
>div : any
37+
38+
};
39+
40+
export const MultipleUnicodeChars = () => {
41+
>MultipleUnicodeChars : () => JSX.Element
42+
>() => { // Test multiple unicode characters return (<div> ⚠ ⛔ 🚨 </div>);} : () => JSX.Element
43+
44+
// Test multiple unicode characters
45+
return (<div>
46+
>(<div> ⚠ ⛔ 🚨 </div>) : JSX.Element
47+
><div> ⚠ ⛔ 🚨 </div> : JSX.Element
48+
>div : any
49+
50+
51+
52+
🚨
53+
</div>);
54+
>div : any
55+
56+
};
57+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// @target: esnext
2+
// @module: preserve
3+
// @moduleResolution: bundler
4+
// @jsx: react-jsx
5+
// @strict: true
6+
7+
// Test for unicode escape sequence issue in JSX
8+
// The warning symbol (⚠, U+26A0) should be correctly encoded as \u26A0, not \uFFFD
9+
10+
// @filename: jsxUnicodeEscapeSequence.tsx
11+
12+
/// <reference path="/.lib/react16.d.ts" />
13+
14+
export const InlineUnicodeChar = () => {
15+
// This should work correctly - inline character with other content
16+
return <div><span>Warning: ⚠ Error</span></div>;
17+
};
18+
19+
export const StandaloneUnicodeChar = () => {
20+
// This should reproduce the issue - unicode character on its own line
21+
return (<div><span></span>
22+
23+
</div>);
24+
};
25+
26+
export const MultipleUnicodeChars = () => {
27+
// Test multiple unicode characters
28+
return (<div>
29+
30+
31+
🚨
32+
</div>);
33+
};

0 commit comments

Comments
 (0)