fix(dev-server): preserve serialized object component props

This commit is contained in:
Clintchiz committed 2026-08-26 13:48:48 +05:30
1 parent 26af30cfe6
commit a85cbf240d
4 files changed
+58 -3

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.8.56",
"version": "0.8.57",
"type": "module",
"main": "src/index.ts",
"exports": {
+9 -1
View File
@@ -1746,7 +1746,15 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
// component defaults creates the real child boundary; the bridged prop
// binding synchronizes the live value when the branch is instantiated.
for (const [key, value] of Object.entries(props)) {
if (/^\{[\s\S]*\}$/.test(value) && !/^\{t:/.test(value)) delete props[key];
if (/^\{[\s\S]*\}$/.test(value) && !/^\{t:/.test(value)) {
// SSR object props also start/end with braces. Preserve serialized
// JSON; only unresolved expressions should fall back to defaults.
try {
JSON.parse(value);
} catch {
delete props[key];
}
}
}
if (normalizedName === "languageswitcher" && deps.i18n) {
props.locales ??= JSON.stringify(
@@ -0,0 +1,47 @@
import { expect, test } from "bun:test";
import { createProductionHandlers } from "../src/prod.ts";
test("component resolution preserves JSON user/permission props and drops unresolved bindings", async () => {
let received: Record<string, string> = {};
const handlers = createProductionHandlers(
{
pages: [
{
raw: "/",
mod: {
default: () =>
'<div data-component="account-shell" identity="{&quot;displayName&quot;:&quot;Alice&quot;}" permissions="{&quot;read&quot;:true,&quot;update&quot;:false}" preferences="{&quot;theme&quot;:&quot;dark&quot;}" pending="{account.pending}"><p>Account content</p></div>',
},
},
],
api: [],
realtime: [],
middleware: [],
layouts: [],
components: [
{
name: "AccountShell",
mod: {
render: (props: Record<string, string>) => {
received = props;
const identity = JSON.parse(props.identity ?? "{}");
const permissions = JSON.parse(props.permissions ?? "{}");
return permissions.read
? "<main>" + identity.displayName + "<slot /></main>"
: "<main>Denied</main>";
},
},
},
],
},
{},
);
const response = await handlers.fetch(new Request("http://localhost/"), { upgrade: () => false });
expect(response?.status).toBe(200);
const html = await response!.text();
expect(html).toContain("Alice");
expect(html).toContain("Account content");
expect(JSON.parse(received.permissions!)).toEqual({ read: true, update: false });
expect(JSON.parse(received.preferences!)).toEqual({ theme: "dark" });
expect(received.pending).toBeUndefined();
});