Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions internal/transformers/jsxtransforms/jsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
42 changes: 42 additions & 0 deletions testdata/baselines/reference/compiler/jsxUnicodeEscapeSequence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////

//// [jsxUnicodeEscapeSequence.tsx]
/// <reference path="/.lib/react16.d.ts" />

export const InlineUnicodeChar = () => {
// This should work correctly - inline character with other content
return <div><span>Warning: ⚠ Error</span></div>;
};

export const StandaloneUnicodeChar = () => {
// This should reproduce the issue - unicode character on its own line
return (<div><span>⚠</span>
</div>);
};

export const MultipleUnicodeChars = () => {
// Test multiple unicode characters
return (<div>
🚨
</div>);
};


//// [jsxUnicodeEscapeSequence.js]
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/// <reference path="react16.d.ts" />
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" }));
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////

=== jsxUnicodeEscapeSequence.tsx ===
/// <reference path="react16.d.ts" />

export const InlineUnicodeChar = () => {
>InlineUnicodeChar : Symbol(InlineUnicodeChar, Decl(jsxUnicodeEscapeSequence.tsx, 2, 12))

// This should work correctly - inline character with other content
return <div><span>Warning: ⚠ Error</span></div>;
>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><span>⚠</span>
>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>);
>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>
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))

🚨
</div>);
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))

};

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//// [tests/cases/compiler/jsxUnicodeEscapeSequence.tsx] ////

=== jsxUnicodeEscapeSequence.tsx ===
/// <reference path="react16.d.ts" />

export const InlineUnicodeChar = () => {
>InlineUnicodeChar : () => JSX.Element
>() => { // This should work correctly - inline character with other content return <div><span>Warning: ⚠ Error</span></div>;} : () => JSX.Element

// This should work correctly - inline character with other content
return <div><span>Warning: ⚠ Error</span></div>;
><div><span>Warning: ⚠ Error</span></div> : JSX.Element
>div : any
><span>Warning: ⚠ Error</span> : 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 (<div><span>⚠</span> ⚠ </div>);} : () => JSX.Element

// This should reproduce the issue - unicode character on its own line
return (<div><span>⚠</span>
>(<div><span>⚠</span> ⚠ </div>) : JSX.Element
><div><span>⚠</span> ⚠ </div> : JSX.Element
>div : any
><span>⚠</span> : JSX.Element
>span : any
>span : any

</div>);
>div : any

};

export const MultipleUnicodeChars = () => {
>MultipleUnicodeChars : () => JSX.Element
>() => { // Test multiple unicode characters return (<div> ⚠ ⛔ 🚨 </div>);} : () => JSX.Element

// Test multiple unicode characters
return (<div>
>(<div> ⚠ ⛔ 🚨 </div>) : JSX.Element
><div> ⚠ ⛔ 🚨 </div> : JSX.Element
>div : any

🚨
</div>);
>div : any

};

33 changes: 33 additions & 0 deletions testdata/tests/cases/compiler/jsxUnicodeEscapeSequence.tsx
Original file line number Diff line number Diff line change
@@ -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

/// <reference path="/.lib/react16.d.ts" />

export const InlineUnicodeChar = () => {
// This should work correctly - inline character with other content
return <div><span>Warning: ⚠ Error</span></div>;
};

export const StandaloneUnicodeChar = () => {
// This should reproduce the issue - unicode character on its own line
return (<div><span>⚠</span>
</div>);
};

export const MultipleUnicodeChars = () => {
// Test multiple unicode characters
return (<div>
🚨
</div>);
};