rwsdk 0.1.15 → 0.1.17

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.
Files changed (51) hide show
  1. package/dist/lib/compileTsModule.d.mts +1 -0
  2. package/dist/lib/compileTsModule.mjs +27 -0
  3. package/dist/runtime/entries/navigation.d.ts +1 -0
  4. package/dist/runtime/entries/navigation.js +1 -0
  5. package/dist/runtime/imports/client.js +1 -1
  6. package/dist/runtime/imports/ssr.js +1 -1
  7. package/dist/runtime/imports/worker.js +1 -1
  8. package/dist/runtime/register/ssr.js +1 -1
  9. package/dist/runtime/render/injectRSCPayload.d.ts +3 -0
  10. package/dist/runtime/render/injectRSCPayload.js +79 -0
  11. package/dist/scripts/build-vendor-bundles.d.mts +1 -0
  12. package/dist/scripts/build-vendor-bundles.mjs +92 -0
  13. package/dist/vite/aliasByEnvPlugin.d.mts +2 -0
  14. package/dist/vite/aliasByEnvPlugin.mjs +11 -0
  15. package/dist/vite/asyncSetupPlugin.d.mts +6 -0
  16. package/dist/vite/asyncSetupPlugin.mjs +23 -0
  17. package/dist/vite/copyPrismaWasmPlugin.d.mts +4 -0
  18. package/dist/vite/copyPrismaWasmPlugin.mjs +32 -0
  19. package/dist/vite/createDirectiveLookupPlugin.mjs +4 -6
  20. package/dist/vite/customReactBuildPlugin.d.mts +4 -0
  21. package/dist/vite/customReactBuildPlugin.mjs +61 -0
  22. package/dist/vite/findSsrSpecifiers.mjs +16 -0
  23. package/dist/vite/injectHmrPreambleJsxPlugin.d.mts +2 -0
  24. package/dist/vite/injectHmrPreambleJsxPlugin.mjs +22 -0
  25. package/dist/vite/miniflareHMRPlugin.mjs +59 -21
  26. package/dist/vite/miniflarePlugin.d.mts +9 -0
  27. package/dist/vite/miniflarePlugin.mjs +135 -0
  28. package/dist/vite/requestUtils.d.mts +6 -0
  29. package/dist/vite/requestUtils.mjs +35 -0
  30. package/dist/vite/setupEnvFiles.d.mts +4 -0
  31. package/dist/vite/setupEnvFiles.mjs +31 -0
  32. package/dist/vite/ssrBridgePlugin.mjs +18 -5
  33. package/dist/vite/useClientPlugin.d.mts +8 -0
  34. package/dist/vite/useClientPlugin.mjs +295 -0
  35. package/dist/vite/useClientPlugin.test.d.mts +1 -0
  36. package/dist/vite/useClientPlugin.test.mjs +1204 -0
  37. package/dist/worker/__ssr_bridge.js +8947 -0
  38. package/dist/worker/__ssr_bridge.js.map +1 -0
  39. package/package.json +1 -1
  40. package/dist/vite/invalidateClientModule.d.mts +0 -2
  41. package/dist/vite/invalidateClientModule.mjs +0 -8
  42. package/dist/vite/invalidateModule copy.d.mts +0 -2
  43. package/dist/vite/invalidateModule copy.mjs +0 -14
  44. package/dist/vite/invalidateSSRModule.d.mts +0 -2
  45. package/dist/vite/invalidateSSRModule.mjs +0 -7
  46. package/dist/vite/isJsFile.d.ts +0 -1
  47. package/dist/vite/isJsFile.js +0 -3
  48. package/dist/vite/mode.d.mts +0 -5
  49. package/dist/vite/mode.mjs +0 -25
  50. package/dist/vite/modePlugin.d.mts +0 -2
  51. package/dist/vite/modePlugin.mjs +0 -10
@@ -0,0 +1,135 @@
1
+ import { cloudflare } from "@cloudflare/vite-plugin";
2
+ import { resolve } from "node:path";
3
+ import colors from "picocolors";
4
+ import { readFile } from "node:fs/promises";
5
+ import { getShortName } from "../lib/getShortName.mjs";
6
+ import { pathExists } from "fs-extra";
7
+ const hasEntryAsAncestor = (module, entryFile, seen = new Set()) => {
8
+ // Prevent infinite recursion
9
+ if (seen.has(module))
10
+ return false;
11
+ seen.add(module);
12
+ // Check direct importers
13
+ for (const importer of module.importers) {
14
+ if (importer.file === entryFile)
15
+ return true;
16
+ // Recursively check importers
17
+ if (hasEntryAsAncestor(importer, entryFile, seen))
18
+ return true;
19
+ }
20
+ return false;
21
+ };
22
+ // Cache for "use client" status results
23
+ const useClientCache = new Map();
24
+ // Function to invalidate cache for a file
25
+ const invalidateUseClientCache = (file) => {
26
+ useClientCache.delete(file);
27
+ };
28
+ const isUseClientModule = async (ctx, file, seen = new Set()) => {
29
+ // Prevent infinite recursion
30
+ if (seen.has(file))
31
+ return false;
32
+ seen.add(file);
33
+ try {
34
+ // Check cache first
35
+ if (useClientCache.has(file)) {
36
+ return useClientCache.get(file);
37
+ }
38
+ // Read and check the file
39
+ const content = (await pathExists(file))
40
+ ? await readFile(file, "utf-8")
41
+ : "";
42
+ const hasUseClient = content.includes("'use client'") || content.includes('"use client"');
43
+ if (hasUseClient) {
44
+ useClientCache.set(file, true);
45
+ return true;
46
+ }
47
+ // Get the module from the module graph to find importers
48
+ const module = ctx.server.moduleGraph.getModuleById(file);
49
+ if (!module) {
50
+ useClientCache.set(file, false);
51
+ return false;
52
+ }
53
+ // Check all importers recursively
54
+ for (const importer of module.importers) {
55
+ if (await isUseClientModule(ctx, importer.url, seen)) {
56
+ useClientCache.set(file, true);
57
+ return true;
58
+ }
59
+ }
60
+ useClientCache.set(file, false);
61
+ return false;
62
+ }
63
+ catch (error) {
64
+ useClientCache.set(file, false);
65
+ return false;
66
+ }
67
+ };
68
+ export const miniflarePlugin = (givenOptions) => [
69
+ cloudflare(givenOptions),
70
+ {
71
+ name: "rwsdk:miniflare-hmr",
72
+ async hotUpdate(ctx) {
73
+ const environment = givenOptions.viteEnvironment?.name ?? "worker";
74
+ const entry = givenOptions.workerEntryPathname;
75
+ if (!["client", environment].includes(this.environment.name)) {
76
+ return;
77
+ }
78
+ // todo(justinvdm, 12 Dec 2024): Skip client references
79
+ const modules = Array.from(ctx.server.environments[environment].moduleGraph.getModulesByFile(ctx.file) ?? []);
80
+ const isWorkerUpdate = ctx.file === entry ||
81
+ modules.some((module) => hasEntryAsAncestor(module, entry));
82
+ // The worker doesnt need an update
83
+ // => Short circuit HMR
84
+ if (!isWorkerUpdate) {
85
+ return [];
86
+ }
87
+ // The worker needs an update, but this is the client environment
88
+ // => Notify for HMR update of any css files imported by in worker, that are also in the client module graph
89
+ // Why: There may have been changes to css classes referenced, which might css modules to change
90
+ if (this.environment.name === "client") {
91
+ const cssModules = [];
92
+ for (const [_, module] of ctx.server.environments[environment]
93
+ .moduleGraph.idToModuleMap) {
94
+ // todo(justinvdm, 13 Dec 2024): We check+update _all_ css files in worker module graph,
95
+ // but it could just be a subset of css files that are actually affected, depending
96
+ // on the importers and imports of the changed file. We should be smarter about this.
97
+ if (module.file && module.file.endsWith(".css")) {
98
+ const clientModules = ctx.server.environments.client.moduleGraph.getModulesByFile(module.file);
99
+ if (clientModules) {
100
+ cssModules.push(...clientModules.values());
101
+ }
102
+ }
103
+ }
104
+ invalidateUseClientCache(ctx.file);
105
+ return (await isUseClientModule(ctx, ctx.file))
106
+ ? [...ctx.modules, ...cssModules]
107
+ : cssModules;
108
+ }
109
+ // The worker needs an update, and the hot check is for the worker environment
110
+ // => Notify for custom RSC-based HMR update, then short circuit HMR
111
+ if (isWorkerUpdate && this.environment.name === environment) {
112
+ const shortName = getShortName(ctx.file, ctx.server.config.root);
113
+ this.environment.logger.info(`${colors.green(`worker update`)} ${colors.dim(shortName)}`, {
114
+ clear: true,
115
+ timestamp: true,
116
+ });
117
+ const m = ctx.server.environments.client.moduleGraph
118
+ .getModulesByFile(resolve(givenOptions.rootDir, "src", "app", "style.css"))
119
+ ?.values()
120
+ .next().value;
121
+ if (m) {
122
+ ctx.server.environments.client.moduleGraph.invalidateModule(m, new Set(), ctx.timestamp, true);
123
+ }
124
+ ctx.server.environments.client.hot.send({
125
+ type: "custom",
126
+ event: "rsc:update",
127
+ data: {
128
+ file: ctx.file,
129
+ },
130
+ });
131
+ return [];
132
+ }
133
+ },
134
+ },
135
+ ];
@@ -0,0 +1,6 @@
1
+ import { Miniflare, type RequestInit } from "miniflare";
2
+ import type { IncomingMessage, ServerResponse } from "node:http";
3
+ type MiniflareResponse = Awaited<ReturnType<typeof Miniflare.prototype.dispatchFetch>>;
4
+ export declare const nodeToWebRequest: (req: IncomingMessage) => Request & RequestInit;
5
+ export declare const webToNodeResponse: (webResponse: Response | MiniflareResponse, nodeResponse: ServerResponse) => Promise<void>;
6
+ export {};
@@ -0,0 +1,35 @@
1
+ export const nodeToWebRequest = (req) => {
2
+ const url = new URL(req.url, `http://${req.headers.host}`);
3
+ return new Request(url.href, {
4
+ method: req.method,
5
+ headers: req.headers,
6
+ body: req.method !== "GET" && req.method !== "HEAD"
7
+ ? req
8
+ : undefined,
9
+ // @ts-ignore
10
+ duplex: "half",
11
+ });
12
+ };
13
+ export const webToNodeResponse = async (webResponse, nodeResponse) => {
14
+ // Copy status and headers
15
+ nodeResponse.statusCode = webResponse.status;
16
+ webResponse.headers.forEach((value, key) => {
17
+ nodeResponse.setHeader(key, value);
18
+ });
19
+ // Stream the response
20
+ if (webResponse.body) {
21
+ const reader = webResponse.body.getReader();
22
+ try {
23
+ while (true) {
24
+ const { done, value } = await reader.read();
25
+ if (done)
26
+ break;
27
+ nodeResponse.write(value);
28
+ }
29
+ }
30
+ finally {
31
+ reader.releaseLock();
32
+ }
33
+ }
34
+ nodeResponse.end();
35
+ };
@@ -0,0 +1,4 @@
1
+ export type EnvSetupOptions = {
2
+ rootDir: string;
3
+ };
4
+ export declare function setupEnvFiles({ rootDir, }: EnvSetupOptions): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import { resolve } from "node:path";
2
+ import { symlink, copyFile } from "node:fs/promises";
3
+ import { pathExists } from "fs-extra";
4
+ export async function setupEnvFiles({ rootDir, }) {
5
+ const envPath = resolve(rootDir, ".env");
6
+ const devVarsPath = resolve(rootDir, ".dev.vars");
7
+ const envExamplePath = resolve(rootDir, ".env.example");
8
+ const envExists = await pathExists(envPath);
9
+ const devVarsExists = await pathExists(devVarsPath);
10
+ const envExampleExists = await pathExists(envExamplePath);
11
+ // If .env.example exists but .env doesn't, copy from example to .env
12
+ if (!envExists && !devVarsExists && envExampleExists) {
13
+ try {
14
+ await copyFile(envExamplePath, envPath);
15
+ console.log("Created .env file from .env.example");
16
+ }
17
+ catch (error) {
18
+ console.warn("Failed to copy .env.example to .env:", error);
19
+ }
20
+ }
21
+ // Create symlink from .env to .dev.vars if needed
22
+ if ((await pathExists(envPath)) && !devVarsExists) {
23
+ try {
24
+ await symlink(envPath, devVarsPath);
25
+ console.log("Created symlink from .env to .dev.vars");
26
+ }
27
+ catch (error) {
28
+ console.warn("Failed to create symlink from .env to .dev.vars:", error);
29
+ }
30
+ }
31
+ }
@@ -93,22 +93,35 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
93
93
  process.env.VERBOSE &&
94
94
  log("Fetch module result: id=%s, result=%O", realId, result);
95
95
  const code = "code" in result ? result.code : undefined;
96
+ if (realId.endsWith(".css")) {
97
+ process.env.VERBOSE &&
98
+ log("Not a JS file, returning code: %s", code);
99
+ return code ?? "";
100
+ }
96
101
  log("Fetched SSR module code length: %d", code?.length || 0);
97
102
  const { imports, dynamicImports } = findSsrImportSpecifiers(realId, code || "", log);
98
- const allSpecifiers = [...new Set([...imports, ...dynamicImports])];
103
+ const allSpecifiers = [
104
+ ...new Set([...imports, ...dynamicImports]),
105
+ ].map((id) => id.startsWith("/@id/") ? id.slice("/@id/".length) : id);
99
106
  const switchCases = allSpecifiers
100
- .map((specifier) => ` case "${specifier}": return import("${VIRTUAL_SSR_PREFIX}${specifier}");`)
107
+ .map((specifier) => ` case "${specifier}": void import("${VIRTUAL_SSR_PREFIX}${specifier}");`)
101
108
  .join("\n");
102
109
  const transformedCode = `
103
110
  await (async function(__vite_ssr_import__, __vite_ssr_dynamic_import__) {${code}})(
104
- (id, ...args) => {ssrImport(id); return __vite_ssr_import__('/@id/${VIRTUAL_SSR_PREFIX}' + id, ...args);},
105
- (id, ...args) => {ssrImport(id); return __vite_ssr_dynamic_import__('/@id/${VIRTUAL_SSR_PREFIX}' + id, ...args);}
111
+ __ssrImport.bind(null, false),
112
+ __ssrImport.bind(null, true)
106
113
  );
107
114
 
108
- function ssrImport(id) {
115
+ function __ssrImport(isDynamic, id, ...args) {
116
+ id = id.startsWith('/@id/') ? id.slice('/@id/'.length) : id;
117
+
109
118
  switch (id) {
110
119
  ${switchCases}
111
120
  }
121
+
122
+ return isDynamic
123
+ ? __vite_ssr_dynamic_import__("/@id/${VIRTUAL_SSR_PREFIX}" + id, ...args)
124
+ : __vite_ssr_import__("/@id/${VIRTUAL_SSR_PREFIX}" + id, ...args);
112
125
  }
113
126
  `;
114
127
  log("Transformed SSR module code length: %d", transformedCode.length);
@@ -0,0 +1,8 @@
1
+ import { Plugin } from "vite";
2
+ interface TransformResult {
3
+ code: string;
4
+ map?: any;
5
+ }
6
+ export declare function transformUseClientCode(code: string, relativeId: string): Promise<TransformResult | undefined>;
7
+ export declare const useClientPlugin: () => Plugin;
8
+ export {};
@@ -0,0 +1,295 @@
1
+ import { relative } from "node:path";
2
+ import { Project, Node, SyntaxKind, } from "ts-morph";
3
+ function isJsxFunction(text) {
4
+ return (text.includes("jsx(") || text.includes("jsxs(") || text.includes("jsxDEV("));
5
+ }
6
+ export async function transformUseClientCode(code, relativeId) {
7
+ const cleanCode = code.trimStart();
8
+ if (!cleanCode.startsWith('"use client"') &&
9
+ !cleanCode.startsWith("'use client'")) {
10
+ return;
11
+ }
12
+ const project = new Project({
13
+ useInMemoryFileSystem: true,
14
+ compilerOptions: {
15
+ sourceMap: true,
16
+ target: 2, // ES6
17
+ module: 1, // CommonJS
18
+ jsx: 2, // React
19
+ },
20
+ });
21
+ const sourceFile = project.createSourceFile("temp.tsx", code);
22
+ // Add import declaration properly through the AST
23
+ sourceFile.addImportDeclaration({
24
+ moduleSpecifier: "rwsdk/worker",
25
+ namedImports: [{ name: "registerClientReference" }],
26
+ });
27
+ const components = new Map();
28
+ let anonymousDefaultCount = 0;
29
+ // Pass 1: Collect all component information
30
+ // Handle function declarations
31
+ sourceFile
32
+ .getDescendantsOfKind(SyntaxKind.FunctionDeclaration)
33
+ .forEach((node) => {
34
+ const name = node.getName() || `DefaultComponent${anonymousDefaultCount++}`;
35
+ if (!name)
36
+ return;
37
+ // Only track if it's a component (has JSX return)
38
+ if (isJsxFunction(node.getText())) {
39
+ const ssrName = `${name}SSR`;
40
+ const isInlineExport = node.hasModifier(SyntaxKind.ExportKeyword);
41
+ // Check if this function is used in a default export
42
+ const isDefault = node.hasModifier(SyntaxKind.DefaultKeyword) ||
43
+ sourceFile
44
+ .getDescendantsOfKind(SyntaxKind.ExportAssignment)
45
+ .some((exp) => exp.getExpression().getText() === name);
46
+ components.set(name, {
47
+ name,
48
+ ssrName,
49
+ isDefault,
50
+ isInlineExport,
51
+ });
52
+ }
53
+ });
54
+ // Handle arrow functions and anonymous default exports
55
+ sourceFile
56
+ .getDescendantsOfKind(SyntaxKind.VariableStatement)
57
+ .forEach((statement) => {
58
+ const declarations = statement.getDeclarationList().getDeclarations();
59
+ declarations.forEach((varDecl) => {
60
+ const arrowFunc = varDecl.getFirstDescendantByKind(SyntaxKind.ArrowFunction);
61
+ if (!arrowFunc)
62
+ return;
63
+ // Only track if it's a component (has JSX return)
64
+ if (isJsxFunction(arrowFunc.getText())) {
65
+ const name = varDecl.getName();
66
+ const isDefault = !!statement.getFirstAncestorByKind(SyntaxKind.ExportAssignment);
67
+ const isInlineExport = statement.hasModifier(SyntaxKind.ExportKeyword);
68
+ if (!name &&
69
+ (isDefault || statement.getText().includes("export default"))) {
70
+ // Handle anonymous default export
71
+ const anonName = `DefaultComponent${anonymousDefaultCount++}`;
72
+ components.set(anonName, {
73
+ name: anonName,
74
+ ssrName: anonName,
75
+ isDefault: true,
76
+ isInlineExport: true,
77
+ isAnonymousDefault: true,
78
+ });
79
+ }
80
+ else if (name) {
81
+ components.set(name, {
82
+ name,
83
+ ssrName: `${name}SSR`,
84
+ isDefault,
85
+ isInlineExport,
86
+ });
87
+ }
88
+ }
89
+ });
90
+ });
91
+ // Pass 2: handle exports
92
+ // Remove use client directives
93
+ sourceFile.getDescendantsOfKind(SyntaxKind.StringLiteral).forEach((node) => {
94
+ if (node.getText() === "'use client'" ||
95
+ node.getText() === '"use client"') {
96
+ const parentExpr = node.getFirstAncestorByKind(SyntaxKind.ExpressionStatement);
97
+ if (parentExpr) {
98
+ parentExpr.remove();
99
+ }
100
+ }
101
+ });
102
+ // Create lists of nodes to modify before making any changes
103
+ const functionsToModify = [];
104
+ const variableStatementsToModify = [];
105
+ const exportDeclarationsToModify = [];
106
+ const exportAssignmentsToModify = [];
107
+ // Collect function declarations to modify
108
+ sourceFile
109
+ .getDescendantsOfKind(SyntaxKind.FunctionDeclaration)
110
+ .forEach((node) => {
111
+ const name = node.getName();
112
+ if (!name || !components.has(name))
113
+ return;
114
+ const component = components.get(name);
115
+ if (component.isInlineExport) {
116
+ functionsToModify.push({
117
+ node,
118
+ nodeText: node.getText(),
119
+ component,
120
+ });
121
+ }
122
+ });
123
+ // Collect variable statements to modify
124
+ sourceFile
125
+ .getDescendantsOfKind(SyntaxKind.VariableStatement)
126
+ .forEach((statement) => {
127
+ if (!statement.hasModifier(SyntaxKind.ExportKeyword))
128
+ return;
129
+ const declarations = statement.getDeclarationList().getDeclarations();
130
+ let hasComponent = false;
131
+ declarations.forEach((varDecl) => {
132
+ const name = varDecl.getName();
133
+ if (name && components.has(name)) {
134
+ hasComponent = true;
135
+ }
136
+ });
137
+ if (hasComponent) {
138
+ variableStatementsToModify.push({
139
+ node: statement,
140
+ stmtText: statement.getText(),
141
+ });
142
+ }
143
+ });
144
+ // Collect export declarations to modify
145
+ sourceFile
146
+ .getDescendantsOfKind(SyntaxKind.ExportDeclaration)
147
+ .forEach((node) => {
148
+ const namedExports = node.getNamedExports();
149
+ const nonComponentExports = namedExports.filter((exp) => !components.has(exp.getName()));
150
+ if (nonComponentExports.length !== namedExports.length) {
151
+ exportDeclarationsToModify.push({
152
+ node,
153
+ nonComponentExports,
154
+ });
155
+ }
156
+ });
157
+ // Collect export assignments to modify
158
+ sourceFile
159
+ .getDescendantsOfKind(SyntaxKind.ExportAssignment)
160
+ .forEach((node) => {
161
+ const expression = node.getExpression();
162
+ if (Node.isArrowFunction(expression)) {
163
+ exportAssignmentsToModify.push({
164
+ node,
165
+ expression,
166
+ });
167
+ }
168
+ else {
169
+ exportAssignmentsToModify.push({
170
+ node,
171
+ expression: null,
172
+ });
173
+ }
174
+ });
175
+ // Now apply all modifications in sequence to avoid operating on removed nodes
176
+ // Modify function declarations
177
+ functionsToModify.forEach(({ node, nodeText, component }) => {
178
+ const newText = nodeText.replace(/^export\s+(default\s+)?(async\s+)?function/, "$2function");
179
+ node.replaceWithText(newText);
180
+ });
181
+ // Modify variable statements
182
+ variableStatementsToModify.forEach(({ node, stmtText }) => {
183
+ const newText = stmtText.replace(/^export\s+/, "");
184
+ node.replaceWithText(newText);
185
+ });
186
+ // Modify export declarations
187
+ exportDeclarationsToModify.forEach(({ node, nonComponentExports }) => {
188
+ if (nonComponentExports.length === 0) {
189
+ // If all exports were components, remove the declaration
190
+ node.remove();
191
+ }
192
+ else {
193
+ // If some exports were components, update the export declaration
194
+ const newExports = nonComponentExports
195
+ .map((exp) => exp.getText())
196
+ .join(", ");
197
+ node.replaceWithText(`export { ${newExports} };`);
198
+ }
199
+ });
200
+ // Handle export assignments with arrow functions
201
+ exportAssignmentsToModify.forEach(({ node, expression }) => {
202
+ if (expression && Node.isArrowFunction(expression)) {
203
+ const anonName = `DefaultComponent${anonymousDefaultCount++}`;
204
+ const ssrName = `${anonName}SSR`;
205
+ // First add declarations
206
+ sourceFile.addStatements(`const ${ssrName} = ${expression.getText()}`);
207
+ sourceFile.addStatements(`const ${anonName} = registerClientReference("${relativeId}", "default", ${ssrName});`);
208
+ // Store info for later export
209
+ components.set(anonName, {
210
+ name: anonName,
211
+ ssrName,
212
+ isDefault: true,
213
+ isInlineExport: true,
214
+ isAnonymousDefault: true,
215
+ });
216
+ }
217
+ // Remove the original export default node
218
+ node.remove();
219
+ });
220
+ // Pass 4: rename all identifiers to SSR version - collect first
221
+ const identifiersToRename = [];
222
+ components.forEach(({ name, ssrName, isAnonymousDefault }) => {
223
+ if (isAnonymousDefault)
224
+ return;
225
+ // Find function declarations by name
226
+ const funcDecls = sourceFile.getDescendantsOfKind(SyntaxKind.FunctionDeclaration);
227
+ const funcNode = funcDecls.find((decl) => decl.getName() === name);
228
+ if (funcNode) {
229
+ identifiersToRename.push({ node: funcNode, newName: ssrName });
230
+ return;
231
+ }
232
+ // Find variable declarations by name
233
+ const varDecls = sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration);
234
+ const varNode = varDecls.find((decl) => decl.getName() === name);
235
+ if (varNode) {
236
+ const identifier = varNode.getFirstChildByKind(SyntaxKind.Identifier);
237
+ if (identifier) {
238
+ identifiersToRename.push({ node: identifier, newName: ssrName });
239
+ }
240
+ }
241
+ });
242
+ // Now apply the renames
243
+ identifiersToRename.forEach(({ node, newName }) => {
244
+ node.rename(newName);
245
+ });
246
+ // Pass 5: Add client reference registrations
247
+ // Add all declarations first
248
+ components.forEach(({ name, ssrName, isDefault, isAnonymousDefault }) => {
249
+ if (!isAnonymousDefault) {
250
+ sourceFile.addStatements(`const ${name} = registerClientReference("${relativeId}", "${isDefault ? "default" : name}", ${ssrName});`);
251
+ }
252
+ });
253
+ // Pass 6: add new exports
254
+ // Then add all exports after declarations
255
+ components.forEach(({ name, ssrName, isDefault }) => {
256
+ if (isDefault) {
257
+ // Export the registerClientReference version as default
258
+ sourceFile.addStatements(`export { ${name} as default, ${ssrName} };`);
259
+ }
260
+ else {
261
+ sourceFile.addStatements(`export { ${ssrName}, ${name} };`);
262
+ }
263
+ });
264
+ // Clean up any remaining export assignments
265
+ sourceFile
266
+ .getDescendantsOfKind(SyntaxKind.ExportAssignment)
267
+ .forEach((node) => {
268
+ // If it's not an arrow function (which we handle separately),
269
+ // just remove the export assignment
270
+ node.remove();
271
+ });
272
+ const emitOutput = sourceFile.getEmitOutput();
273
+ let sourceMap;
274
+ for (const outputFile of emitOutput.getOutputFiles()) {
275
+ if (outputFile.getFilePath().endsWith(".js.map")) {
276
+ sourceMap = JSON.parse(outputFile.getText());
277
+ }
278
+ }
279
+ return {
280
+ code: sourceFile.getFullText(),
281
+ map: sourceMap,
282
+ };
283
+ }
284
+ export const useClientPlugin = () => ({
285
+ name: "rwsdk:use-client",
286
+ async transform(code, id) {
287
+ if (id.includes(".vite/deps") ||
288
+ id.includes("node_modules") ||
289
+ this.environment.name !== "worker") {
290
+ return;
291
+ }
292
+ const relativeId = `/${relative(this.environment.getTopLevelConfig().root, id)}`;
293
+ return transformUseClientCode(code, relativeId);
294
+ },
295
+ });
@@ -0,0 +1 @@
1
+ export {};