Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -1712,9 +1712,10 @@ function codegenInstructionValue(
break;
}
case 'JsxExpression': {
const isBuiltinTag = instrValue.tag.kind === 'BuiltinTag';
const attributes: Array<t.JSXAttribute | t.JSXSpreadAttribute> = [];
for (const attribute of instrValue.props) {
attributes.push(codegenJsxAttribute(cx, attribute));
attributes.push(codegenJsxAttribute(cx, attribute, isBuiltinTag));
}
let tagValue =
instrValue.tag.kind === 'Identifier'
Expand Down Expand Up @@ -2123,9 +2124,28 @@ function codegenInstructionValue(
*/
const STRING_REQUIRES_EXPR_CONTAINER_PATTERN =
/[\u{0000}-\u{001F}\u{007F}\u{0080}-\u{FFFF}\u{010000}-\u{10FFFF}]|"|\\/u;

/**
* The Babel JSX transform (@babel/plugin-transform-react-jsx) normalizes
* newlines followed by whitespace in plain JSX string attributes:
*
* value.value = value.value.replace(/\n\s+/g, " ");
*
* However, this normalization is skipped for JSXExpressionContainer values.
* When the compiler wraps strings in expression containers (because they
* match STRING_REQUIRES_EXPR_CONTAINER_PATTERN), the JSX transform bypasses
* normalization. The server code (uncompiled or differently compiled) goes
* through the standard normalization, creating a hydration mismatch.
*
* We apply the same normalization here before the expression container check,
* ensuring compiled output matches the behavior of non-compiled code.
*/
const JSX_STRING_NEWLINE_PATTERN = /\n\s+/g;

function codegenJsxAttribute(
cx: Context,
attribute: JsxAttribute,
isBuiltinTag: boolean,
): t.JSXAttribute | t.JSXSpreadAttribute {
switch (attribute.kind) {
case 'JsxAttribute': {
Expand All @@ -2145,6 +2165,18 @@ function codegenJsxAttribute(
switch (innerValue.type) {
case 'StringLiteral': {
value = innerValue;
if (
isBuiltinTag &&
!cx.fbtOperands.has(attribute.place.identifier.id)
) {
const normalized = value.value.replace(
JSX_STRING_NEWLINE_PATTERN,
' ',
);
if (normalized !== value.value) {
value = createStringLiteral(value.loc, normalized);
}
}
if (
STRING_REQUIRES_EXPR_CONTAINER_PATTERN.test(value.value) &&
!cx.fbtOperands.has(attribute.place.identifier.id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

## Input

```javascript
function Component() {
return (
<div
className={"foo\nbar"}
>
Hello
</div>
);
}

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
function Component() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <div className={"foo\nbar"}>Hello</div>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}

```

### Eval output
(kind: exception) Fixture not implemented
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function Component() {
return (
<div
className={"foo\nbar"}
>
Hello
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

## Input

```javascript
function Component() {
return (
<div
className="
flex min-h-screen items-center
dark:bg-black
"
>
Hello
</div>
);
}

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
function Component() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = (
<div className=" flex min-h-screen items-center dark:bg-black ">
Hello
</div>
);
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}

```

### Eval output
(kind: exception) Fixture not implemented
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function Component() {
return (
<div
className="
flex min-h-screen items-center
dark:bg-black
"
>
Hello
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

## Input

```javascript
function Component() {
return (
<div
className={"foo\n bar\n baz"}
>
Hello
</div>
);
}

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
function Component() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <div className="foo bar baz">Hello</div>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}

```

### Eval output
(kind: exception) Fixture not implemented
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function Component() {
return (
<div
className={"foo\n bar\n baz"}
>
Hello
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

## Input

```javascript
function Component() {
return (
<MyComponent
data={"foo\nbar"}
/>
);
}

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
function Component() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <MyComponent data={"foo\nbar"} />;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}

```

### Eval output
(kind: exception) Fixture not implemented
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function Component() {
return (
<MyComponent
data={"foo\nbar"}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

## Input

```javascript
function Component() {
return (
<div
title="line1
line2"
>
Hello
</div>
);
}

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
function Component() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <div title="line1 line2">Hello</div>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}

```

### Eval output
(kind: exception) Fixture not implemented
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function Component() {
return (
<div
title="line1
line2"
>
Hello
</div>
);
}