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 (
+ ⚠ + ⛔ + 🚨 +
); +};