rwsdk 0.1.0-alpha.1 → 0.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export * from "../lib/streams/consumeEventStream";
@@ -0,0 +1 @@
1
+ export * from "../lib/streams/consumeEventStream";
@@ -113,10 +113,6 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
113
113
  server: {
114
114
  hmr: true,
115
115
  },
116
- resolve: {
117
- conditions: ["workerd"],
118
- alias: [],
119
- },
120
116
  builder: {
121
117
  buildApp: async (builder) => {
122
118
  // note(justinvdm, 27 May 2025): **Ordering is important**:
@@ -0,0 +1,6 @@
1
+ import { Plugin } from "vite";
2
+ export declare const directivesPlugin: ({ projectRootDir, clientFiles, serverFiles, }: {
3
+ projectRootDir: string;
4
+ clientFiles: Set<string>;
5
+ serverFiles: Set<string>;
6
+ }) => Plugin;
@@ -0,0 +1,80 @@
1
+ import debug from "debug";
2
+ import { transformClientComponents } from "./transformClientComponents.mjs";
3
+ import { transformServerFunctions } from "./transformServerFunctions.mjs";
4
+ import { normalizeModulePath } from "./normalizeModulePath.mjs";
5
+ const log = debug("rwsdk:vite:rsc-directives-plugin");
6
+ const verboseLog = debug("verbose:rwsdk:vite:rsc-directives-plugin");
7
+ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, }) => ({
8
+ name: "rwsdk:rsc-directives",
9
+ async transform(code, id) {
10
+ verboseLog("Transform called for id=%s, environment=%s", id, this.environment.name);
11
+ const normalizedId = normalizeModulePath(projectRootDir, id);
12
+ const clientResult = await transformClientComponents(code, normalizedId, {
13
+ environmentName: this.environment.name,
14
+ clientFiles,
15
+ });
16
+ if (clientResult) {
17
+ log("Client component transformation successful for id=%s", id);
18
+ return {
19
+ code: clientResult.code,
20
+ map: clientResult.map,
21
+ };
22
+ }
23
+ const serverResult = transformServerFunctions(code, normalizedId, this.environment.name, serverFiles);
24
+ if (serverResult) {
25
+ log("Server function transformation successful for id=%s", id);
26
+ return {
27
+ code: serverResult.code,
28
+ map: serverResult.map,
29
+ };
30
+ }
31
+ verboseLog("No transformation applied for id=%s", id);
32
+ },
33
+ configEnvironment(env, config) {
34
+ log("Configuring environment: env=%s", env);
35
+ config.optimizeDeps ??= {};
36
+ config.optimizeDeps.esbuildOptions ??= {};
37
+ config.optimizeDeps.esbuildOptions.plugins ??= [];
38
+ config.optimizeDeps.esbuildOptions.plugins.push({
39
+ name: "rsc-directives-esbuild-transform",
40
+ setup(build) {
41
+ log("Setting up esbuild plugin for environment: %s", env);
42
+ build.onLoad({ filter: /.*\.js$/ }, async (args) => {
43
+ verboseLog("Esbuild onLoad called for path=%s", args.path);
44
+ const fs = await import("node:fs/promises");
45
+ const path = await import("node:path");
46
+ let code;
47
+ try {
48
+ code = await fs.readFile(args.path, "utf-8");
49
+ }
50
+ catch {
51
+ verboseLog("Failed to read file: %s", args.path);
52
+ return undefined;
53
+ }
54
+ const clientResult = await transformClientComponents(code, normalizeModulePath(projectRootDir, args.path), {
55
+ environmentName: env,
56
+ clientFiles,
57
+ isEsbuild: true,
58
+ });
59
+ if (clientResult) {
60
+ log("Esbuild client component transformation successful for path=%s", args.path);
61
+ return {
62
+ contents: clientResult.code,
63
+ loader: path.extname(args.path).slice(1),
64
+ };
65
+ }
66
+ const serverResult = transformServerFunctions(code, normalizeModulePath(projectRootDir, args.path), env, serverFiles);
67
+ if (serverResult) {
68
+ log("Esbuild server function transformation successful for path=%s", args.path);
69
+ return {
70
+ contents: serverResult.code,
71
+ loader: path.extname(args.path).slice(1),
72
+ };
73
+ }
74
+ verboseLog("Esbuild no transformation applied for path=%s", args.path);
75
+ });
76
+ },
77
+ });
78
+ log("Environment configuration complete for env=%s", env);
79
+ },
80
+ });
@@ -2,7 +2,7 @@ import { resolve } from "node:path";
2
2
  import reactPlugin from "@vitejs/plugin-react";
3
3
  import tsconfigPaths from "vite-tsconfig-paths";
4
4
  import { transformJsxScriptTagsPlugin } from "./transformJsxScriptTagsPlugin.mjs";
5
- import { rscDirectivesPlugin } from "./rscDirectivesPlugin.mjs";
5
+ import { directivesPlugin } from "./directivesPlugin.mjs";
6
6
  import { useClientLookupPlugin } from "./useClientLookupPlugin.mjs";
7
7
  import { useServerLookupPlugin } from "./useServerLookupPlugin.mjs";
8
8
  import { miniflarePlugin } from "./miniflarePlugin.mjs";
@@ -56,7 +56,7 @@ export const redwoodPlugin = async (options = {}) => {
56
56
  configPath: options.configPath ?? (await findWranglerConfig(projectRootDir)),
57
57
  }),
58
58
  reactPlugin(),
59
- rscDirectivesPlugin({
59
+ directivesPlugin({
60
60
  projectRootDir,
61
61
  clientFiles,
62
62
  serverFiles,
@@ -48,13 +48,17 @@ export async function transformClientComponents(code, normalizedId, ctx) {
48
48
  const sourceFile = project.createSourceFile(normalizedId + ".ts", code);
49
49
  const exportInfos = [];
50
50
  let defaultExportInfo;
51
+ // Helper to get the computed local name (with alias suffix if present)
52
+ function getComputedLocalName(info) {
53
+ return `${info.local}${info.alias ? `_${info.alias}` : ""}`;
54
+ }
51
55
  // Helper to add export info
52
- function addExport(local, exported, isDefault, statementIdx) {
56
+ function addExport(local, exported, isDefault, statementIdx, alias) {
53
57
  if (isDefault) {
54
58
  defaultExportInfo = { local, exported, isDefault, statementIdx };
55
59
  }
56
60
  else {
57
- exportInfos.push({ local, exported, isDefault, statementIdx });
61
+ exportInfos.push({ local, exported, isDefault, statementIdx, alias });
58
62
  }
59
63
  }
60
64
  // Walk through statements in order to collect export information
@@ -106,13 +110,10 @@ export async function transformClientComponents(code, normalizedId, ctx) {
106
110
  const namedExports = stmt.getNamedExports();
107
111
  if (namedExports.length > 0) {
108
112
  namedExports.forEach((exp) => {
109
- const local = exp.getAliasNode()
110
- ? exp.getNameNode().getText()
111
- : exp.getName();
112
- const exported = exp.getAliasNode()
113
- ? exp.getAliasNode().getText()
114
- : exp.getName();
115
- addExport(local, exported, exported === "default", idx);
113
+ const alias = exp.getAliasNode()?.getText();
114
+ const local = alias ? exp.getNameNode().getText() : exp.getName();
115
+ const exported = alias ? alias : exp.getName();
116
+ addExport(local, exported, exported === "default", idx, alias);
116
117
  });
117
118
  }
118
119
  return;
@@ -148,14 +149,18 @@ export async function transformClientComponents(code, normalizedId, ctx) {
148
149
  moduleSpecifier: "rwsdk/worker",
149
150
  namedImports: [{ name: "registerClientReference" }],
150
151
  });
151
- // Add registerClientReference assignments for named exports in order
152
- for (const info of exportInfos) {
153
- log(":isEsbuild=%s: Registering client reference for named export: %s as %s", !!ctx.isEsbuild, info.local, info.exported);
154
- sourceFile.addStatements(`const ${info.local} = registerClientReference("${normalizedId}", "${info.exported}");`);
152
+ // Compute unique computed local names first
153
+ const computedLocalNames = new Map(exportInfos.map((info) => [getComputedLocalName(info), info]));
154
+ // Add registerClientReference assignments for unique names
155
+ for (const [computedLocalName, correspondingInfo] of computedLocalNames) {
156
+ log(":isEsbuild=%s: Registering client reference for named export: %s as %s", !!ctx.isEsbuild, correspondingInfo.local, correspondingInfo.exported);
157
+ sourceFile.addStatements(`const ${computedLocalName} = registerClientReference("${normalizedId}", "${correspondingInfo.exported}");`);
155
158
  }
156
159
  // Add grouped export statement for named exports (preserving order and alias)
157
160
  if (exportInfos.length > 0) {
158
- const exportNames = exportInfos.map((e) => e.local === e.exported ? e.local : `${e.local} as ${e.exported}`);
161
+ const exportNames = Array.from(computedLocalNames.entries()).map(([computedLocalName, correspondingInfo]) => correspondingInfo.local === correspondingInfo.exported
162
+ ? computedLocalName
163
+ : `${computedLocalName} as ${correspondingInfo.exported}`);
159
164
  log(":isEsbuild=%s: Exporting named exports: %O", !!ctx.isEsbuild, exportNames);
160
165
  sourceFile.addStatements(`export { ${exportNames.join(", ")} };`);
161
166
  }
@@ -85,8 +85,8 @@ export { Fourth as AnotherName }`)) ?? "").toEqual(`import { registerClientRefer
85
85
  const First = registerClientReference("/test/file.tsx", "First");
86
86
  const Second = registerClientReference("/test/file.tsx", "Second");
87
87
  const Third = registerClientReference("/test/file.tsx", "Third");
88
- const Fourth = registerClientReference("/test/file.tsx", "AnotherName");
89
- export { First, Second, Third, Fourth as AnotherName };
88
+ const Fourth_AnotherName = registerClientReference("/test/file.tsx", "AnotherName");
89
+ export { First, Second, Third, Fourth_AnotherName as AnotherName };
90
90
  export default registerClientReference("/test/file.tsx", "default");
91
91
  `);
92
92
  });
@@ -199,8 +199,8 @@ const MyComponent = () => {
199
199
  }
200
200
 
201
201
  export { MyComponent as CustomName }`)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
202
- const MyComponent = registerClientReference("/test/file.tsx", "CustomName");
203
- export { MyComponent as CustomName };
202
+ const MyComponent_CustomName = registerClientReference("/test/file.tsx", "CustomName");
203
+ export { MyComponent_CustomName as CustomName };
204
204
  `);
205
205
  });
206
206
  it("correctly processes multiple component exports", async () => {
@@ -229,6 +229,20 @@ const Component = registerClientReference("/test/file.tsx", "Component");
229
229
  const data = registerClientReference("/test/file.tsx", "data");
230
230
  const helper = registerClientReference("/test/file.tsx", "helper");
231
231
  export { Component, data, helper };
232
+ `);
233
+ });
234
+ it("transforms multiple exports aliases for the same component", async () => {
235
+ expect((await transform(`"use client"
236
+
237
+ export const Slot = () => {
238
+ return jsx('div', { children: 'Slot' });
239
+ }
240
+
241
+ export { Slot, Slot as Root }
242
+ `)) ?? "").toEqual(`import { registerClientReference } from "rwsdk/worker";
243
+ const Slot = registerClientReference("/test/file.tsx", "Slot");
244
+ const Slot_Root = registerClientReference("/test/file.tsx", "Root");
245
+ export { Slot, Slot_Root as Root };
232
246
  `);
233
247
  });
234
248
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rwsdk",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "./client": {
24
24
  "react-server": "./dist/runtime/entries/no-react-server.js",
25
+ "workerd": "./dist/runtime/entries/clientSSR.js",
25
26
  "types": "./dist/runtime/entries/client.d.ts",
26
27
  "default": "./dist/runtime/entries/client.js"
27
28
  },