Skip to content

Commit be0c681

Browse files
brenelzclaude
andcommitted
restore JSX renderAsset, fix beta.15 crash with NoHydration wrapper
The raw-HTML-string workaround existed for a babel-plugin-jsx-dom-expressions spread crash that's fixed in 0.50.0-next.14. The remaining blocker was beta.15's owner-based hydration keys: useAssets thunks run at shell-injection time with no owner, so ssrElement's getNextContextId call threw and killed the server process. Wrapping the asset JSX in <NoHydration> provides an owner with hydration-key generation disabled — correct for head tags, which never hydrate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent d53dbba commit be0c681

1 file changed

Lines changed: 27 additions & 51 deletions

File tree

packages/start/src/server/assets/render.tsx

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,39 @@
11
// @refresh skip
2-
//
3-
// NOTE: This file intentionally avoids JSX to work around a crash in
4-
// babel-plugin-jsx-dom-expressions@0.41.0-next.9 when spread attributes are
5-
// used on native HTML elements during SSR transformation. The previous
6-
// implementation used JSX (<style {...props.attrs}> etc.) which triggered the
7-
// bug. Instead we build the HTML strings directly — these assets are only
8-
// ever rendered during SSR so reactive JSX is unnecessary.
9-
2+
import { NoHydration } from "@solidjs/web";
103
import type { JSX } from "@solidjs/web";
114

12-
/** Escape HTML special characters for safe attribute / text insertion. */
13-
function esc(s: string): string {
14-
return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15-
}
16-
17-
function renderAttrs(obj: Record<string, any>): string {
18-
let out = "";
19-
for (const [k, v] of Object.entries(obj)) {
20-
if (v == null || v === false || v === undefined) continue;
21-
if (v === true) {
22-
out += ` ${esc(k)}`;
23-
} else {
24-
out += ` ${esc(k)}="${esc(String(v))}"`;
25-
}
26-
}
27-
return out;
28-
}
5+
const assetMap = {
6+
style: (props: { attrs: JSX.StyleHTMLAttributes<HTMLStyleElement>; children?: JSX.Element }) => (
7+
<style {...props.attrs}>{props.children}</style>
8+
),
9+
link: (props: { attrs: JSX.LinkHTMLAttributes<HTMLLinkElement> }) => <link {...props.attrs} />,
10+
script: (props: {
11+
attrs: JSX.ScriptHTMLAttributes<HTMLScriptElement>;
12+
key: string | undefined;
13+
}) => {
14+
return props.attrs.src ? (
15+
<script {...props.attrs} id={props.key}>
16+
{" "}
17+
</script>
18+
) : null;
19+
},
20+
noscript: (props: { attrs: JSX.HTMLAttributes<HTMLElement>; children: JSX.Element }) => (
21+
<noscript {...props.attrs}>{props.children}</noscript>
22+
),
23+
};
2924

3025
export function renderAsset(asset: Asset, nonce?: string) {
3126
let {
3227
tag,
33-
attrs: { key, ...rest } = { key: undefined },
28+
attrs: { key, ...attrs } = { key: undefined },
3429
children,
3530
} = asset as any;
36-
37-
const allAttrs = { ...rest, nonce } as Record<string, any>;
38-
39-
let html: string;
40-
switch (tag as string) {
41-
case "style":
42-
html = `<style${renderAttrs(allAttrs)}>${children ?? ""}</style>`;
43-
break;
44-
case "link":
45-
html = `<link${renderAttrs(allAttrs)} />`;
46-
break;
47-
case "script":
48-
if (!allAttrs.src) return { t: "" };
49-
html = `<script${renderAttrs(allAttrs)}${key ? ` id="${esc(key)}"` : ""}> </script>`;
50-
break;
51-
case "noscript":
52-
html = `<noscript${renderAttrs(allAttrs)}>${children ?? ""}</noscript>`;
53-
break;
54-
default:
55-
html = "";
56-
}
57-
58-
// Return as an SSR node object ({ t: string }) so that @solidjs/web's
59-
// escape() passes it through unmodified and resolveSSRNode() uses it directly.
60-
return { t: html };
31+
// Asset thunks run at shell-injection time with no reactive owner; NoHydration
32+
// provides one (with hydration-key generation disabled) so ssrElement doesn't
33+
// throw. Head asset tags never hydrate, so they shouldn't carry _hk anyway.
34+
return (
35+
<NoHydration>{(assetMap as any)[tag]({ attrs: { ...attrs, nonce }, key, children })}</NoHydration>
36+
);
6137
}
6238

6339
export type Asset =

0 commit comments

Comments
 (0)