import { describe, expect, it } from "vitest";
import { createInitialState } from "@/transform/state/implementation";
import type { JSXElement } from "@/transform/jsx/implementation";
import type { NestedTransformers } from "@/transform/tree/implementation";
import { transformJSXForSSRNode } from "./implementation";
const nested: NestedTransformers = {
transformJSXNode: () => ({
type: "CallExpression",
callee: { type: "Identifier", name: "fromTree" },
arguments: [],
optional: false,
}),
transformJSXForSSRNode,
};
function makeElement(
children: JSXElement["children"] = [{ type: "JSXText", value: "Hello" }],
): JSXElement {
return {
type: "JSXElement",
openingElement: {
type: "JSXOpeningElement",
name: { type: "JSXIdentifier", name: "div" },
attributes: [],
selfClosing: false,
},
children,
closingElement: null,
};
}
describe("transform/mode-ssr", () => {
it("returns a static string literal when no dynamic parts exist", () => {
const state = createInitialState("ssr");
const output = transformJSXForSSRNode(
makeElement([{ type: "JSXText", value: "Hello" }]),
state,
nested,
) as unknown as { type: string; value?: unknown };
expect(output.type).toBe("Literal");
expect(output.value).toBe("
Hello
");
});
it("excludes event dynamic parts from compiled SSR output", () => {
const state = createInitialState("ssr");
const output = transformJSXForSSRNode(
{
type: "JSXElement",
openingElement: {
type: "JSXOpeningElement",
name: { type: "JSXIdentifier", name: "button" },
attributes: [
{
type: "JSXAttribute",
name: { type: "JSXIdentifier", name: "onClick" },
value: {
type: "JSXExpressionContainer",
expression: { type: "Identifier", name: "handleClick" },
},
},
],
selfClosing: false,
},
children: [
{
type: "JSXExpressionContainer",
expression: { type: "Identifier", name: "label" },
},
],
closingElement: null,
},
state,
nested,
);
expect(JSON.stringify(output)).toContain('"name":"renderDynamicText"');
expect(JSON.stringify(output)).not.toContain("handleClick");
});
it("registers compiled SSR helper imports", () => {
const state = createInitialState("ssr");
const output = transformJSXForSSRNode(makeElement(), state, nested);
expect(output.type).toBe("Literal");
expect(state.runtimeImports.has("renderDynamicText")).toBe(false);
});
it("uses helper calls for dynamic parts", () => {
const state = createInitialState("ssr");
const output = transformJSXForSSRNode(
makeElement([
{
type: "JSXExpressionContainer",
expression: { type: "Identifier", name: "name" },
},
]),
state,
nested,
);
expect(JSON.stringify(output)).toContain('"name":"renderDynamicText"');
});
it("keeps text/insert marker ids stable even when attr dynamic part appears first", () => {
const state = createInitialState("ssr");
const output = transformJSXForSSRNode(
{
type: "JSXElement",
openingElement: {
type: "JSXOpeningElement",
name: { type: "JSXIdentifier", name: "div" },
attributes: [
{
type: "JSXAttribute",
name: { type: "JSXIdentifier", name: "class" },
value: {
type: "JSXExpressionContainer",
expression: {
type: "MemberExpression",
object: { type: "Identifier", name: "state" },
property: { type: "Identifier", name: "value" },
computed: false,
optional: false,
},
},
},
],
selfClosing: false,
},
children: [
{
type: "JSXExpressionContainer",
expression: {
type: "LogicalExpression",
operator: "&&",
left: { type: "Identifier", name: "visible" },
right: {
type: "JSXElement",
openingElement: {
type: "JSXOpeningElement",
name: { type: "JSXIdentifier", name: "span" },
attributes: [],
selfClosing: false,
},
children: [{ type: "JSXText", value: "ok" }],
closingElement: null,
},
},
},
],
closingElement: null,
},
state,
nested,
);
const serialized = JSON.stringify(output);
expect(serialized).toContain("");
expect(serialized).toContain('"name":"renderDynamicAttr"');
});
it("falls back to renderToString for custom elements", () => {
const state = createInitialState("ssr");
const output = transformJSXForSSRNode(
{
type: "JSXElement",
openingElement: {
type: "JSXOpeningElement",
name: { type: "JSXIdentifier", name: "my-widget" },
attributes: [],
selfClosing: false,
},
children: [{ type: "JSXText", value: "Hello" }],
closingElement: null,
},
state,
nested,
) as unknown as { callee?: { name?: string } };
expect(output.callee?.name).toBe("renderToString");
expect(state.runtimeImports.has("renderToString")).toBe(true);
});
});