diff --git a/internal/transformers/jsxtransforms/jsx.go b/internal/transformers/jsxtransforms/jsx.go
index cfd2137c57..ff9ad4d36b 100644
--- a/internal/transformers/jsxtransforms/jsx.go
+++ b/internal/transformers/jsxtransforms/jsx.go
@@ -791,26 +791,26 @@ func fixupWhitespaceAndDecodeEntities(text string) string {
initial := true
// First non-whitespace character on this line.
firstNonWhitespace := 0
- // Last non-whitespace character on this line.
- lastNonWhitespace := -1
+ // End byte position of the last non-whitespace character on this line.
+ lastNonWhitespaceEnd := -1
// These initial values are special because the first line is:
// firstNonWhitespace = 0 to indicate that we want leading whitespace,
- // but lastNonWhitespace = -1 as a special flag to indicate that we *don't* include the line if it's all whitespace.
+ // but lastNonWhitespaceEnd = -1 as a special flag to indicate that we *don't* include the line if it's all whitespace.
for i := 0; i < len(text); i++ {
c, size := utf8.DecodeRuneInString(text[i:])
if stringutil.IsLineBreak(c) {
// If we've seen any non-whitespace characters on this line, add the 'trim' of the line.
- // (lastNonWhitespace === -1 is a special flag to detect whether the first line is all whitespace.)
- if firstNonWhitespace != -1 && lastNonWhitespace != -1 {
- addLineOfJsxText(acc, text[firstNonWhitespace:lastNonWhitespace+1], initial)
+ // (lastNonWhitespaceEnd === -1 is a special flag to detect whether the first line is all whitespace.)
+ if firstNonWhitespace != -1 && lastNonWhitespaceEnd != -1 {
+ addLineOfJsxText(acc, text[firstNonWhitespace:lastNonWhitespaceEnd+1], initial)
initial = false
}
// Reset firstNonWhitespace for the next line.
- // Don't bother to reset lastNonWhitespace because we ignore it if firstNonWhitespace = -1.
+ // Don't bother to reset lastNonWhitespaceEnd because we ignore it if firstNonWhitespace = -1.
firstNonWhitespace = -1
} else if !stringutil.IsWhiteSpaceSingleLine(c) {
- lastNonWhitespace = i
+ lastNonWhitespaceEnd = i + size - 1 // Store the end byte position of the character
if firstNonWhitespace == -1 {
firstNonWhitespace = i
}
diff --git a/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.js b/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.js
new file mode 100644
index 0000000000..a179ad9931
--- /dev/null
+++ b/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.js
@@ -0,0 +1,42 @@
+//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////
+
+//// [jsxUnicodeEscapeSequence.tsx]
+///
+
+export const InlineUnicodeChar = () => {
+ // This should work correctly - inline character with other content
+ return
Warning: ⚠ Error
;
+};
+
+export const StandaloneUnicodeChar = () => {
+ // This should reproduce the issue - unicode character on its own line
+ return (⚠
+ ⚠
+
);
+};
+
+export const MultipleUnicodeChars = () => {
+ // Test multiple unicode characters
+ return (
+ ⚠
+ ⛔
+ 🚨
+
);
+};
+
+
+//// [jsxUnicodeEscapeSequence.js]
+import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
+///
+export const InlineUnicodeChar = () => {
+ // This should work correctly - inline character with other content
+ return _jsx("div", { children: _jsx("span", { children: "Warning: \u26A0 Error" }) });
+};
+export const StandaloneUnicodeChar = () => {
+ // This should reproduce the issue - unicode character on its own line
+ return (_jsxs("div", { children: [_jsx("span", { children: "\u26A0" }), "\u26A0"] }));
+};
+export const MultipleUnicodeChars = () => {
+ // Test multiple unicode characters
+ return (_jsx("div", { children: "\u26A0 \u26D4 \uD83D\uDEA8" }));
+};
diff --git a/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.symbols b/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.symbols
new file mode 100644
index 0000000000..36e64cf2b9
--- /dev/null
+++ b/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.symbols
@@ -0,0 +1,47 @@
+//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////
+
+=== jsxUnicodeEscapeSequence.tsx ===
+///
+
+export const InlineUnicodeChar = () => {
+>InlineUnicodeChar : Symbol(InlineUnicodeChar, Decl(jsxUnicodeEscapeSequence.tsx, 2, 12))
+
+ // This should work correctly - inline character with other content
+ return Warning: ⚠ Error
;
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
+>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
+>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
+
+};
+
+export const StandaloneUnicodeChar = () => {
+>StandaloneUnicodeChar : Symbol(StandaloneUnicodeChar, Decl(jsxUnicodeEscapeSequence.tsx, 7, 12))
+
+ // This should reproduce the issue - unicode character on its own line
+ return (⚠
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
+>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
+>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
+
+ ⚠
+
);
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
+
+};
+
+export const MultipleUnicodeChars = () => {
+>MultipleUnicodeChars : Symbol(MultipleUnicodeChars, Decl(jsxUnicodeEscapeSequence.tsx, 14, 12))
+
+ // Test multiple unicode characters
+ return (
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
+
+ ⚠
+ ⛔
+ 🚨
+
);
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
+
+};
+
diff --git a/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.types b/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.types
new file mode 100644
index 0000000000..002033c89f
--- /dev/null
+++ b/testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.types
@@ -0,0 +1,57 @@
+//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////
+
+=== jsxUnicodeEscapeSequence.tsx ===
+///
+
+export const InlineUnicodeChar = () => {
+>InlineUnicodeChar : () => JSX.Element
+>() => { // This should work correctly - inline character with other content return Warning: ⚠ Error
;} : () => JSX.Element
+
+ // This should work correctly - inline character with other content
+ return Warning: ⚠ Error
;
+>Warning: ⚠ Error
: JSX.Element
+>div : any
+>Warning: ⚠ Error : JSX.Element
+>span : any
+>span : any
+>div : any
+
+};
+
+export const StandaloneUnicodeChar = () => {
+>StandaloneUnicodeChar : () => JSX.Element
+>() => { // This should reproduce the issue - unicode character on its own line return (⚠ ⚠
);} : () => JSX.Element
+
+ // This should reproduce the issue - unicode character on its own line
+ return (⚠
+>(
⚠ ⚠
) : JSX.Element
+>
⚠ ⚠
: JSX.Element
+>div : any
+>
⚠ : JSX.Element
+>span : any
+>span : any
+
+ ⚠
+
);
+>div : any
+
+};
+
+export const MultipleUnicodeChars = () => {
+>MultipleUnicodeChars : () => JSX.Element
+>() => { // Test multiple unicode characters return ( ⚠ ⛔ 🚨
);} : () => JSX.Element
+
+ // Test multiple unicode characters
+ return (
+>(
⚠ ⛔ 🚨
) : JSX.Element
+>
⚠ ⛔ 🚨
: JSX.Element
+>div : any
+
+ ⚠
+ ⛔
+ 🚨
+
);
+>div : any
+
+};
+
diff --git a/testdata/tests/cases/compiler/jsxUnicodeEscapeSequence.tsx b/testdata/tests/cases/compiler/jsxUnicodeEscapeSequence.tsx
new file mode 100644
index 0000000000..570eb244ab
--- /dev/null
+++ b/testdata/tests/cases/compiler/jsxUnicodeEscapeSequence.tsx
@@ -0,0 +1,33 @@
+// @target: esnext
+// @module: preserve
+// @moduleResolution: bundler
+// @jsx: react-jsx
+// @strict: true
+
+// Test for unicode escape sequence issue in JSX
+// The warning symbol (⚠, U+26A0) should be correctly encoded as \u26A0, not \uFFFD
+
+// @filename: jsxUnicodeEscapeSequence.tsx
+
+///
+
+export const InlineUnicodeChar = () => {
+ // This should work correctly - inline character with other content
+ return Warning: ⚠ Error
;
+};
+
+export const StandaloneUnicodeChar = () => {
+ // This should reproduce the issue - unicode character on its own line
+ return (⚠
+ ⚠
+
);
+};
+
+export const MultipleUnicodeChars = () => {
+ // Test multiple unicode characters
+ return (
+ ⚠
+ ⛔
+ 🚨
+
);
+};