rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
265 lines (242 loc) • 9.17 kB
JavaScript
import { describe, it, expect } from "vitest";
import { transformClientComponents } from "./transformClientComponents.mjs";
describe("transformClientComponents", () => {
async function transform(code) {
const result = await transformClientComponents(code, "/test/file.tsx", {
environmentName: "worker",
});
return result?.code;
}
it("transforms arrow function component", async () => {
expect((await transform(`"use client"
export const Component = () => {
return jsx('div', { children: 'Hello' });
}
`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const Component = registerClientReference("/test/file.tsx", "Component");
export { Component };
`);
});
it("transforms async arrow function component", async () => {
expect((await transform(`"use client"
export const Component = async () => {
return jsx('div', { children: 'Hello' });
}
`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const Component = registerClientReference("/test/file.tsx", "Component");
export { Component };
`);
});
it("transforms function declaration component", async () => {
expect((await transform(`"use client"
export function Component() {
return jsx('div', { children: 'Hello' });
}`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const Component = registerClientReference("/test/file.tsx", "Component");
export { Component };
`);
});
it("transforms default export arrow function component", async () => {
expect((await transform(`"use client"
export default () => {
return jsx('div', { children: 'Hello' });
}`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
export default registerClientReference("/test/file.tsx", "default");
`);
});
it("transforms default export function declaration component", async () => {
expect((await transform(`"use client"
export default function Component({ prop1, prop2 }) {
return jsx('div', { children: 'Hello' });
}`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
export default registerClientReference("/test/file.tsx", "default");
`);
});
it("transforms mixed export styles (inline, grouped, and default)", async () => {
expect((await transform(`"use client"
export const First = () => {
return jsx('div', { children: 'First' });
}
const Second = () => {
return jsx('div', { children: 'Second' });
}
function Third() {
return jsx('div', { children: 'Third' });
}
const Fourth = () => {
return jsx('div', { children: 'Fourth' });
}
export default function Main() {
return jsx('div', { children: 'Main' });
}
export { Second, Third }
export { Fourth as AnotherName }`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const First = registerClientReference("/test/file.tsx", "First");
const Second = registerClientReference("/test/file.tsx", "Second");
const Third = registerClientReference("/test/file.tsx", "Third");
const Fourth_AnotherName = registerClientReference("/test/file.tsx", "AnotherName");
export { First, Second, Third, Fourth_AnotherName as AnotherName };
export default registerClientReference("/test/file.tsx", "default");
`);
});
it("transforms function declaration that is exported default separately", async () => {
expect((await transform(`
"use client"
function Component({ prop1, prop2 }) {
return jsx('div', { children: 'Hello' });
}
export default Component;`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
export default registerClientReference("/test/file.tsx", "default");
`);
});
it("Works in dev", async () => {
expect((await transform(`"use client"
import { jsxDEV } from "react/jsx-dev-runtime";
import { sendMessage } from "./functions";
import { useState } from "react";
import { consumeEventStream } from "rwsdk/client";
export function Chat() {
const [message, setMessage] = useState("");
const [reply, setReply] = useState("");
const onClick = async () => {
setReply("");
(await sendMessage(message)).pipeTo(
consumeEventStream({
onChunk: (event) => {
setReply(
(prev) => prev + (event.data === "[DONE]" ? "" : JSON.parse(event.data).response)
);
}
})
);
};
return /* @__PURE__ */ jsxDEV("div", { children: [
/* @__PURE__ */ jsxDEV(
"input",
{
type: "text",
value: message,
placeholder: "Type a message...",
onChange: (e) => setMessage(e.target.value),
style: {
width: "80%",
padding: "10px",
marginRight: "8px",
borderRadius: "4px",
border: "1px solid #ccc"
}
},
void 0,
false,
{
fileName: "/Users/justin/rw/sdk/experiments/ai-stream/src/app/pages/Chat/Chat.tsx",
lineNumber: 28,
columnNumber: 7
},
this
),
/* @__PURE__ */ jsxDEV(
"button",
{
onClick,
style: {
padding: "10px 20px",
borderRadius: "4px",
border: "none",
backgroundColor: "#007bff",
color: "white",
cursor: "pointer"
},
children: "Send"
},
void 0,
false,
{
fileName: "/Users/justin/rw/sdk/experiments/ai-stream/src/app/pages/Chat/Chat.tsx",
lineNumber: 41,
columnNumber: 7
},
this
),
/* @__PURE__ */ jsxDEV("div", { children: reply }, void 0, false, {
fileName: "/Users/justin/rw/sdk/experiments/ai-stream/src/app/pages/Chat/Chat.tsx",
lineNumber: 54,
columnNumber: 7
}, this)
] }, void 0, true, {
fileName: "/Users/justin/rw/sdk/experiments/ai-stream/src/app/pages/Chat/Chat.tsx",
lineNumber: 27,
columnNumber: 5
}, this);
}
`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const Chat = registerClientReference("/test/file.tsx", "Chat");
export { Chat };
`);
});
it("Does not transform when 'use client' is not directive", async () => {
expect(await transform(`const message = "use client";`)).toEqual(undefined);
});
it("properly handles export alias", async () => {
expect((await transform(`"use client"
const MyComponent = () => {
return jsx('div', { children: 'Hello' });
}
export { MyComponent as CustomName }`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const MyComponent_CustomName = registerClientReference("/test/file.tsx", "CustomName");
export { MyComponent_CustomName as CustomName };
`);
});
it("correctly processes multiple component exports", async () => {
expect((await transform(`"use client"
const First = () => jsx('div', { children: 'First' });
const Second = () => jsx('div', { children: 'Second' });
const Third = () => jsx('div', { children: 'Third' });
export { First, Second, Third }`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const First = registerClientReference("/test/file.tsx", "First");
const Second = registerClientReference("/test/file.tsx", "Second");
const Third = registerClientReference("/test/file.tsx", "Third");
export { First, Second, Third };
`);
});
it("handles combination of JSX and non-JSX exports", async () => {
expect((await transform(`"use client"
const Component = () => jsx('div', {});
const data = { value: 42 };
const helper = () => console.log('helper');
export { Component, data, helper }`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const Component = registerClientReference("/test/file.tsx", "Component");
const data = registerClientReference("/test/file.tsx", "data");
const helper = registerClientReference("/test/file.tsx", "helper");
export { Component, data, helper };
`);
});
it("transforms multiple exports aliases for the same component", async () => {
expect((await transform(`"use client"
export const Slot = () => {
return jsx('div', { children: 'Slot' });
}
export { Slot, Slot as Root }
`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
const Slot = registerClientReference("/test/file.tsx", "Slot");
const Slot_Root = registerClientReference("/test/file.tsx", "Root");
export { Slot, Slot_Root as Root };
`);
});
});
describe("transformClientComponents logic branches (from transformClientComponents.mts)", () => {
it("returns code as-is if file does not start with 'use client'", async () => {
const code = "const foo = 1;";
const result = await transformClientComponents(code, "/test/file.tsx", {
environmentName: "worker",
});
expect(result).toBeUndefined();
});
it("removes directive but does not transform if not a virtual SSR file", async () => {
const code = '"use client"\nexport const foo = 1;';
const result = await transformClientComponents(code, "/test/file.tsx", {
environmentName: "ssr",
});
expect(result?.code).toEqual("export const foo = 1;");
});
});