fix: synchronize mounted component props
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.8.50",
|
||||
"version": "0.8.51",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1769,7 +1769,17 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
|
||||
},
|
||||
)
|
||||
: renderComponent();
|
||||
result += await renderComponents(rendered, translate, language, depth + 1);
|
||||
// Prop bindings are authored by the parent mount but consumed by the
|
||||
// rendered child's scope. The mount itself is replaced during SSR, so
|
||||
// carry those reserved attributes onto the child's scope root.
|
||||
const propBindings = Array.from(
|
||||
attrStr!.matchAll(/\s(data-wrn-prop-bind-[A-Za-z0-9_-]+(?:="[^"]*")?)/g),
|
||||
(match) => ` ${match[1]}`,
|
||||
).join("");
|
||||
const bridged = propBindings
|
||||
? rendered.replace(/(<[A-Za-z][A-Za-z0-9-]*\b[^>]*\bdata-scope="[^"]*")/, `$1${propBindings}`)
|
||||
: rendered;
|
||||
result += await renderComponents(bridged, translate, language, depth + 1);
|
||||
} catch (err) {
|
||||
console.error(`[wrnexus] component '${name}' failed to render`, err);
|
||||
deps.devToolbar?.collector.add(
|
||||
|
||||
@@ -90,6 +90,32 @@ test("a component in an initially hidden client branch is server-prepared for in
|
||||
expect(branches[0].body).not.toContain("data-component=");
|
||||
});
|
||||
|
||||
test("SSR carries a parent prop binding onto the rendered child scope", async () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "wrnexus-component-prop-"));
|
||||
roots.push(root);
|
||||
const app = join(root, "app");
|
||||
mkdirSync(join(app, "pages"), { recursive: true });
|
||||
mkdirSync(join(app, "components"), { recursive: true });
|
||||
writeFileSync(join(app, "pages/index.ts"), "export default () => '';\n");
|
||||
writeFileSync(join(app, "components/Child.wrn"), "component Child { props { value = 0 } view { <b>{value}</b> } }\n");
|
||||
const marker = "["value","{count}"]";
|
||||
const handlers = createHandlers({
|
||||
mode: "development",
|
||||
hmr: false,
|
||||
router: buildRouter(app),
|
||||
loadModule: async (file) =>
|
||||
basename(file) === "Child.wrn"
|
||||
? { render: () => '<div data-scope="value: 1" data-wrn-scope="e30="><b>1</b></div>' }
|
||||
: { default: () => `<div data-component="Child" value="1" data-wrn-prop-bind-0="${marker}"></div>` },
|
||||
getMiddleware: async () => [],
|
||||
assets: { serve: async () => null },
|
||||
} satisfies RuntimeDeps);
|
||||
const response = await handlers.fetch(new Request("https://example.test/"), { upgrade: () => false });
|
||||
const html = await response!.text();
|
||||
expect(html).toContain(`data-wrn-prop-bind-0="${marker}"`);
|
||||
expect(html).not.toContain('data-component="Child"');
|
||||
});
|
||||
|
||||
test("translated text is present in the raw server response", async () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-i18n-"));
|
||||
roots.push(root);
|
||||
|
||||
Reference in New Issue
Block a user