rwsdk 0.2.0-alpha.9 → 0.3.0

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 (93) hide show
  1. package/dist/lib/constants.d.mts +6 -1
  2. package/dist/lib/constants.mjs +6 -1
  3. package/dist/lib/smokeTests/browser.mjs +5 -21
  4. package/dist/lib/smokeTests/codeUpdates.d.mts +1 -1
  5. package/dist/lib/smokeTests/codeUpdates.mjs +41 -5
  6. package/dist/lib/smokeTests/development.d.mts +1 -1
  7. package/dist/lib/smokeTests/development.mjs +4 -10
  8. package/dist/lib/smokeTests/release.d.mts +1 -1
  9. package/dist/lib/smokeTests/release.mjs +4 -9
  10. package/dist/lib/smokeTests/runSmokeTests.mjs +2 -2
  11. package/dist/lib/smokeTests/templates/SmokeTest.template.js +3 -2
  12. package/dist/lib/testUtils/stubEnvVars.d.mts +2 -0
  13. package/dist/lib/testUtils/stubEnvVars.mjs +11 -0
  14. package/dist/runtime/client/client.d.ts +10 -0
  15. package/dist/runtime/{client.js → client/client.js} +13 -10
  16. package/dist/runtime/{clientNavigation.test.js → client/navigation.test.js} +1 -1
  17. package/dist/runtime/client/setWebpackRequire.d.ts +1 -0
  18. package/dist/runtime/client/setWebpackRequire.js +2 -0
  19. package/dist/runtime/{client.d.ts → client/types.d.ts} +4 -10
  20. package/dist/runtime/client/types.js +1 -0
  21. package/dist/runtime/entries/client.d.ts +2 -2
  22. package/dist/runtime/entries/client.js +2 -2
  23. package/dist/runtime/imports/client.d.ts +3 -3
  24. package/dist/runtime/imports/client.js +11 -15
  25. package/dist/runtime/imports/ssr.d.ts +3 -3
  26. package/dist/runtime/imports/ssr.js +3 -3
  27. package/dist/runtime/imports/worker.d.ts +3 -3
  28. package/dist/runtime/imports/worker.js +5 -4
  29. package/dist/runtime/lib/manifest.d.ts +11 -2
  30. package/dist/runtime/lib/manifest.js +1 -1
  31. package/dist/runtime/lib/memoizeOnId.d.ts +1 -0
  32. package/dist/runtime/lib/memoizeOnId.js +11 -0
  33. package/dist/runtime/lib/realtime/client.d.ts +1 -1
  34. package/dist/runtime/lib/realtime/client.js +1 -1
  35. package/dist/runtime/lib/router.d.ts +3 -3
  36. package/dist/runtime/lib/router.js +77 -33
  37. package/dist/runtime/register/ssr.d.ts +1 -1
  38. package/dist/runtime/register/ssr.js +4 -3
  39. package/dist/runtime/render/preloads.d.ts +6 -0
  40. package/dist/runtime/render/preloads.js +40 -0
  41. package/dist/runtime/render/renderRscThenableToHtmlStream.js +2 -1
  42. package/dist/runtime/render/stylesheets.js +1 -1
  43. package/dist/runtime/requestInfo/types.d.ts +3 -1
  44. package/dist/runtime/requestInfo/worker.js +9 -1
  45. package/dist/runtime/worker.d.ts +0 -3
  46. package/dist/runtime/worker.js +2 -11
  47. package/dist/scripts/debug-sync.mjs +142 -39
  48. package/dist/scripts/smoke-test.mjs +0 -10
  49. package/dist/scripts/worker-run.mjs +8 -3
  50. package/dist/vite/buildApp.d.mts +15 -0
  51. package/dist/vite/buildApp.mjs +53 -0
  52. package/dist/vite/configPlugin.d.mts +4 -2
  53. package/dist/vite/configPlugin.mjs +69 -62
  54. package/dist/vite/createDirectiveLookupPlugin.d.mts +0 -6
  55. package/dist/vite/createDirectiveLookupPlugin.mjs +61 -145
  56. package/dist/vite/directiveModulesDevPlugin.d.mts +8 -0
  57. package/dist/vite/directiveModulesDevPlugin.mjs +62 -0
  58. package/dist/vite/directivesFilteringPlugin.d.mts +6 -0
  59. package/dist/vite/directivesFilteringPlugin.mjs +31 -0
  60. package/dist/vite/directivesPlugin.mjs +28 -42
  61. package/dist/vite/getViteEsbuild.d.mts +1 -0
  62. package/dist/vite/getViteEsbuild.mjs +12 -0
  63. package/dist/vite/hasOwnReactVitePlugin.d.mts +3 -0
  64. package/dist/vite/hasOwnReactVitePlugin.mjs +14 -0
  65. package/dist/vite/injectVitePreamblePlugin.d.mts +3 -2
  66. package/dist/vite/injectVitePreamblePlugin.mjs +4 -2
  67. package/dist/vite/linkerPlugin.d.mts +4 -0
  68. package/dist/vite/linkerPlugin.mjs +41 -0
  69. package/dist/vite/manifestPlugin.d.mts +2 -2
  70. package/dist/vite/manifestPlugin.mjs +12 -37
  71. package/dist/vite/miniflareHMRPlugin.mjs +17 -2
  72. package/dist/vite/moveStaticAssetsPlugin.mjs +2 -1
  73. package/dist/vite/prismaPlugin.mjs +1 -1
  74. package/dist/vite/reactConditionsResolverPlugin.d.mts +3 -4
  75. package/dist/vite/reactConditionsResolverPlugin.mjs +74 -56
  76. package/dist/vite/redwoodPlugin.d.mts +1 -1
  77. package/dist/vite/redwoodPlugin.mjs +36 -12
  78. package/dist/vite/runDirectivesScan.d.mts +7 -0
  79. package/dist/vite/runDirectivesScan.mjs +152 -0
  80. package/dist/vite/ssrBridgePlugin.mjs +13 -14
  81. package/dist/vite/transformClientComponents.d.mts +0 -1
  82. package/dist/vite/transformClientComponents.mjs +1 -9
  83. package/dist/vite/transformJsxScriptTagsPlugin.d.mts +4 -3
  84. package/dist/vite/transformJsxScriptTagsPlugin.mjs +151 -158
  85. package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +393 -136
  86. package/dist/vite/transformServerFunctions.d.mts +1 -1
  87. package/dist/vite/transformServerFunctions.mjs +11 -12
  88. package/package.json +28 -4
  89. /package/dist/runtime/{imports → client}/ClientOnly.d.ts +0 -0
  90. /package/dist/runtime/{imports → client}/ClientOnly.js +0 -0
  91. /package/dist/runtime/{clientNavigation.d.ts → client/navigation.d.ts} +0 -0
  92. /package/dist/runtime/{clientNavigation.js → client/navigation.js} +0 -0
  93. /package/dist/runtime/{clientNavigation.test.d.ts → client/navigation.test.d.ts} +0 -0
@@ -28,36 +28,12 @@ const getLoader = (filePath) => {
28
28
  export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, }) => {
29
29
  let devServer;
30
30
  let isAfterFirstResponse = false;
31
- const addModule = (kind, environment, id) => {
32
- const files = kind === "client" ? clientFiles : serverFiles;
33
- const rawId = id.split("?")[0];
34
- const resolvedId = normalizeModulePath(rawId, projectRootDir);
35
- const fullPath = normalizeModulePath(rawId, projectRootDir, {
36
- absolute: true,
37
- });
38
- const isNodeModule = id.includes("node_modules");
39
- const hadFile = files.has(id);
40
- log("Adding %s module to %s and invalidating cache: id=%s", kind, files, resolvedId);
41
- files.add(resolvedId);
42
- if (devServer && isNodeModule) {
43
- const lookupModule = kind === "client"
44
- ? "virtual:use-client-lookup"
45
- : "virtual:use-server-lookup";
46
- log("Registering missing import for %s module resolvedId=%s in environment %s, fullPath=%s", kind, resolvedId, environment, fullPath);
47
- devServer.environments[environment].depsOptimizer?.registerMissingImport(resolvedId, fullPath);
48
- if (isAfterFirstResponse && !hadFile) {
49
- log("Invalidating cache for lookup module %s after adding module id=%s", lookupModule, id);
50
- }
51
- }
52
- };
53
- const addClientModule = (environment, id) => {
54
- addModule("client", environment, id);
55
- };
56
- const addServerModule = (environment, id) => {
57
- addModule("server", environment, id);
58
- };
31
+ let isBuild = false;
59
32
  return {
60
33
  name: "rwsdk:rsc-directives",
34
+ configResolved(config) {
35
+ isBuild = config.command === "build";
36
+ },
61
37
  configureServer(server) {
62
38
  devServer = server;
63
39
  devServer.middlewares.use((_req, res, next) => {
@@ -76,33 +52,42 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
76
52
  });
77
53
  },
78
54
  async transform(code, id) {
79
- process.env.VERBOSE &&
80
- log("Transform called for id=%s, environment=%s", id, this.environment.name);
55
+ if (isBuild &&
56
+ this.environment?.name === "worker" &&
57
+ process.env.RWSDK_BUILD_PASS !== "worker") {
58
+ return;
59
+ }
81
60
  const normalizedId = normalizeModulePath(id, projectRootDir);
82
61
  const clientResult = await transformClientComponents(code, normalizedId, {
83
62
  environmentName: this.environment.name,
84
63
  clientFiles,
85
- addClientModule,
86
64
  });
87
65
  if (clientResult) {
88
- log("Client component transformation successful for id=%s", id);
66
+ process.env.VERBOSE &&
67
+ log("Client component transformation successful for id=%s", id);
89
68
  return {
90
69
  code: clientResult.code,
91
70
  map: clientResult.map,
92
71
  };
93
72
  }
94
- const serverResult = transformServerFunctions(code, normalizedId, this.environment.name, serverFiles, addServerModule);
73
+ const serverResult = transformServerFunctions(code, normalizedId, this.environment.name, serverFiles);
95
74
  if (serverResult) {
96
- log("Server function transformation successful for id=%s", id);
75
+ process.env.VERBOSE &&
76
+ log("Server function transformation successful for id=%s", id);
97
77
  return {
98
78
  code: serverResult.code,
99
79
  map: serverResult.map,
100
80
  };
101
81
  }
102
- process.env.VERBOSE && log("No transformation applied for id=%s", id);
82
+ // Removed: too noisy even in verbose mode
103
83
  },
104
84
  configEnvironment(env, config) {
105
- log("Configuring environment: env=%s", env);
85
+ if (isBuild &&
86
+ env === "worker" &&
87
+ process.env.RWSDK_BUILD_PASS !== "worker") {
88
+ return;
89
+ }
90
+ process.env.VERBOSE && log("Configuring environment: env=%s", env);
106
91
  config.optimizeDeps ??= {};
107
92
  config.optimizeDeps.esbuildOptions ??= {};
108
93
  config.optimizeDeps.esbuildOptions.plugins ??= [];
@@ -120,7 +105,6 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
120
105
  // dependency discovery, so we can skip transform work
121
106
  // and use heuristics instead - see below inside if block
122
107
  if (!args.path.includes("node_modules")) {
123
- log("Esbuild onLoad found app code, path=%s", args.path);
124
108
  if (clientFiles.has(normalizedPath)) {
125
109
  // context(justinvdm,2025-06-15): If this is a client file:
126
110
  // * for ssr and client envs we can skip so esbuild looks at the
@@ -171,10 +155,10 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
171
155
  environmentName: env,
172
156
  clientFiles,
173
157
  isEsbuild: true,
174
- addClientModule,
175
158
  });
176
159
  if (clientResult) {
177
- log("Esbuild client component transformation successful for environment=%s, path=%s", env, args.path);
160
+ process.env.VERBOSE &&
161
+ log("Esbuild client component transformation successful for environment=%s, path=%s", env, args.path);
178
162
  process.env.VERBOSE &&
179
163
  log("Esbuild client component transformation for environment=%s, path=%s, code: %j", env, args.path, clientResult.code);
180
164
  return {
@@ -182,9 +166,10 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
182
166
  loader: getLoader(args.path),
183
167
  };
184
168
  }
185
- const serverResult = transformServerFunctions(code, normalizedPath, env, serverFiles, addServerModule);
169
+ const serverResult = transformServerFunctions(code, normalizedPath, env, serverFiles);
186
170
  if (serverResult) {
187
- log("Esbuild server function transformation successful for environment=%s, path=%s", env, args.path);
171
+ process.env.VERBOSE &&
172
+ log("Esbuild server function transformation successful for environment=%s, path=%s", env, args.path);
188
173
  return {
189
174
  contents: serverResult.code,
190
175
  loader: getLoader(args.path),
@@ -195,7 +180,8 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
195
180
  });
196
181
  },
197
182
  });
198
- log("Environment configuration complete for env=%s", env);
183
+ process.env.VERBOSE &&
184
+ log("Environment configuration complete for env=%s", env);
199
185
  },
200
186
  };
201
187
  };
@@ -0,0 +1 @@
1
+ export declare function getViteEsbuild(projectRootDir: string): Promise<any>;
@@ -0,0 +1,12 @@
1
+ import path from "node:path";
2
+ import { createRequire } from "node:module";
3
+ const require = createRequire(import.meta.url);
4
+ export async function getViteEsbuild(projectRootDir) {
5
+ const vitePath = require.resolve("vite/package.json", {
6
+ paths: [projectRootDir],
7
+ });
8
+ const viteDir = path.dirname(vitePath);
9
+ const esbuildPath = require.resolve("esbuild", { paths: [viteDir] });
10
+ const esbuildModule = await import(esbuildPath);
11
+ return esbuildModule.default || esbuildModule;
12
+ }
@@ -0,0 +1,3 @@
1
+ export declare function hasOwnReactVitePlugin({ rootProjectDir, }: {
2
+ rootProjectDir: string;
3
+ }): Promise<boolean>;
@@ -0,0 +1,14 @@
1
+ import { readFile } from "fs/promises";
2
+ import path from "path";
3
+ export async function hasOwnReactVitePlugin({ rootProjectDir, }) {
4
+ const packageJsonPath = path.join(rootProjectDir, "package.json");
5
+ try {
6
+ const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
7
+ return !!(packageJson.dependencies?.["@vitejs/plugin-react"] ||
8
+ packageJson.devDependencies?.["@vitejs/plugin-react"]);
9
+ }
10
+ catch (error) {
11
+ console.error("Error reading package.json:", error);
12
+ return false;
13
+ }
14
+ }
@@ -1,4 +1,5 @@
1
1
  import { type Plugin } from "vite";
2
- export declare const injectVitePreamble: ({ clientEntryPathnames, }: {
3
- clientEntryPathnames: string[];
2
+ export declare const injectVitePreamble: ({ clientEntryPoints, projectRootDir, }: {
3
+ clientEntryPoints: Set<string>;
4
+ projectRootDir: string;
4
5
  }) => Plugin;
@@ -1,12 +1,14 @@
1
1
  import MagicString from "magic-string";
2
- export const injectVitePreamble = ({ clientEntryPathnames, }) => ({
2
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
3
+ export const injectVitePreamble = ({ clientEntryPoints, projectRootDir, }) => ({
3
4
  name: "rwsdk:inject-vite-preamble",
4
5
  apply: "serve",
5
6
  transform(code, id) {
6
7
  if (this.environment.name !== "client") {
7
8
  return;
8
9
  }
9
- if (!clientEntryPathnames.includes(id)) {
10
+ const normalizedId = normalizeModulePath(id, projectRootDir);
11
+ if (!clientEntryPoints.has(normalizedId)) {
10
12
  return;
11
13
  }
12
14
  // Only inject preamble in development mode
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "vite";
2
+ export declare const linkerPlugin: ({ projectRootDir, }: {
3
+ projectRootDir: string;
4
+ }) => Plugin;
@@ -0,0 +1,41 @@
1
+ import path from "node:path";
2
+ import fsp from "node:fs/promises";
3
+ import { CLIENT_MANIFEST_RELATIVE_PATH } from "../lib/constants.mjs";
4
+ import debug from "debug";
5
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
6
+ const log = debug("rwsdk:vite:linker-plugin");
7
+ export const linkerPlugin = ({ projectRootDir, }) => {
8
+ return {
9
+ name: "rwsdk:linker",
10
+ async renderChunk(code) {
11
+ if (this.environment.name !== "worker" ||
12
+ process.env.RWSDK_BUILD_PASS !== "linker") {
13
+ return null;
14
+ }
15
+ log("Rendering final worker chunk");
16
+ let newCode = code;
17
+ // Read the manifest from the filesystem.
18
+ const manifestContent = await fsp.readFile(path.resolve(projectRootDir, CLIENT_MANIFEST_RELATIVE_PATH), "utf-8");
19
+ const manifest = JSON.parse(manifestContent);
20
+ // 1. Replace the manifest placeholder with the actual manifest content.
21
+ log("Injecting manifest into worker bundle");
22
+ newCode = newCode.replace('"__RWSDK_MANIFEST_PLACEHOLDER__"', manifestContent);
23
+ // 2. Replace asset placeholders with their final hashed paths.
24
+ log("Replacing asset placeholders in final worker bundle");
25
+ for (const [key, value] of Object.entries(manifest)) {
26
+ const normalizedKey = normalizeModulePath(key, projectRootDir, {
27
+ isViteStyle: false,
28
+ });
29
+ newCode = newCode.replaceAll(`rwsdk_asset:${normalizedKey}`, `/${value.file}`);
30
+ }
31
+ // 3. Deprefix any remaining placeholders that were not in the manifest.
32
+ // This handles public assets that don't go through the bundler.
33
+ log("Deprefixing remaining asset placeholders");
34
+ newCode = newCode.replaceAll("rwsdk_asset:", "");
35
+ return {
36
+ code: newCode,
37
+ map: null,
38
+ };
39
+ },
40
+ };
41
+ };
@@ -1,4 +1,4 @@
1
1
  import { type Plugin } from "vite";
2
- export declare const manifestPlugin: ({ manifestPath, }: {
3
- manifestPath: string;
2
+ export declare const manifestPlugin: ({ projectRootDir, }: {
3
+ projectRootDir: string;
4
4
  }) => Plugin;
@@ -1,57 +1,32 @@
1
- import { readFile } from "node:fs/promises";
2
1
  import debug from "debug";
3
- import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
4
2
  const log = debug("rwsdk:vite:manifest-plugin");
5
3
  const virtualModuleId = "virtual:rwsdk:manifest.js";
6
4
  const resolvedVirtualModuleId = "\0" + virtualModuleId;
7
- export const manifestPlugin = ({ manifestPath, }) => {
5
+ export const manifestPlugin = ({ projectRootDir, }) => {
8
6
  let isBuild = false;
9
- let root;
10
7
  return {
11
- name: "rwsdk:manifest",
8
+ name: "rwsdk:vite:manifest-plugin",
9
+ enforce: "pre",
12
10
  configResolved(config) {
13
- log("Config resolved, command=%s", config.command);
14
11
  isBuild = config.command === "build";
15
- root = config.root;
16
12
  },
17
13
  resolveId(id) {
18
14
  if (id === virtualModuleId) {
19
- process.env.VERBOSE && log("Resolving virtual module id=%s", id);
20
15
  return resolvedVirtualModuleId;
21
16
  }
22
17
  },
23
18
  async load(id) {
24
19
  if (id === resolvedVirtualModuleId) {
25
- process.env.VERBOSE && log("Loading virtual module id=%s", id);
26
- if (!isBuild) {
27
- process.env.VERBOSE && log("Not a build, returning empty manifest");
28
- return `export default {}`;
20
+ if (isBuild) {
21
+ // context(justinvdm, 28 Aug 2025): During the build, we don't have
22
+ // the manifest yet. We insert a placeholder that the linker plugin
23
+ // will replace in the final phase.
24
+ log("Returning manifest placeholder for build");
25
+ return `export default "__RWSDK_MANIFEST_PLACEHOLDER__"`;
29
26
  }
30
- log("Reading manifest from %s", manifestPath);
31
- const manifestContent = await readFile(manifestPath, "utf-8");
32
- const manifest = JSON.parse(manifestContent);
33
- const normalizedManifest = {};
34
- for (const key in manifest) {
35
- const normalizedKey = normalizeModulePath(key, root, {
36
- isViteStyle: false,
37
- });
38
- const entry = manifest[key];
39
- delete manifest[key];
40
- normalizedManifest[normalizedKey] = entry;
41
- entry.file = normalizeModulePath(entry.file, root, {
42
- isViteStyle: false,
43
- });
44
- const normalizedCss = [];
45
- if (entry.css) {
46
- for (const css of entry.css) {
47
- normalizedCss.push(normalizeModulePath(css, root, {
48
- isViteStyle: false,
49
- }));
50
- }
51
- entry.css = normalizedCss;
52
- }
53
- }
54
- return `export default ${JSON.stringify(normalizedManifest)}`;
27
+ // In dev, we can return an empty object.
28
+ log("Not a build, returning empty manifest");
29
+ return `export default {}`;
55
30
  }
56
31
  },
57
32
  configEnvironment(name, config) {
@@ -66,6 +66,9 @@ export const miniflareHMRPlugin = (givenOptions) => [
66
66
  };
67
67
  },
68
68
  async hotUpdate(ctx) {
69
+ if (ctx.file.includes(".wrangler")) {
70
+ return;
71
+ }
69
72
  if (hasErrored) {
70
73
  const shortName = getShortName(ctx.file, ctx.server.config.root);
71
74
  this.environment.logger.info(`${colors.cyan(`attempting to recover from error`)}: update to ${colors.dim(shortName)}`, {
@@ -77,6 +80,7 @@ export const miniflareHMRPlugin = (givenOptions) => [
77
80
  type: "full-reload",
78
81
  path: "*",
79
82
  });
83
+ log("hmr: Full reload after error");
80
84
  return [];
81
85
  }
82
86
  const { clientFiles, serverFiles, viteEnvironment: { name: environment }, workerEntryPathname: entry, } = givenOptions;
@@ -84,6 +88,7 @@ export const miniflareHMRPlugin = (givenOptions) => [
84
88
  log(`Hot update: (env=${this.environment.name}) ${ctx.file}\nModule graph:\n\n${dumpFullModuleGraph(ctx.server, this.environment.name)}`);
85
89
  }
86
90
  if (!isJsFile(ctx.file) && !ctx.file.endsWith(".css")) {
91
+ log(`hmr: not a js file, skipping`);
87
92
  return;
88
93
  }
89
94
  if (this.environment.name === "ssr") {
@@ -94,14 +99,17 @@ export const miniflareHMRPlugin = (givenOptions) => [
94
99
  server: ctx.server,
95
100
  });
96
101
  if (!isUseClientUpdate) {
102
+ log("hmr: not a use client update, short circuiting");
97
103
  return [];
98
104
  }
99
105
  invalidateModule(ctx.server, "ssr", ctx.file);
100
106
  invalidateModule(ctx.server, environment, VIRTUAL_SSR_PREFIX +
101
107
  normalizeModulePath(ctx.file, givenOptions.rootDir));
108
+ log("hmr: invalidated ssr module");
102
109
  return [];
103
110
  }
104
111
  if (!["client", environment].includes(this.environment.name)) {
112
+ log(`hmr: incorrect env, skipping (env=${this.environment.name}, worker env=${environment})`);
105
113
  return [];
106
114
  }
107
115
  const hasClientDirective = await hasDirective(ctx.file, "use client");
@@ -165,8 +173,10 @@ export const miniflareHMRPlugin = (givenOptions) => [
165
173
  server: ctx.server,
166
174
  });
167
175
  if (!isUseClientUpdate && !ctx.file.endsWith(".css")) {
176
+ log("hmr: not a use client update and not css, short circuiting");
168
177
  return [];
169
178
  }
179
+ log("hmr: returning client modules for hmr");
170
180
  return ctx.modules;
171
181
  }
172
182
  // The worker needs an update, and the hot check is for the worker environment
@@ -184,8 +194,12 @@ export const miniflareHMRPlugin = (givenOptions) => [
184
194
  if (m) {
185
195
  invalidateModule(ctx.server, environment, m);
186
196
  }
187
- const virtualSSRModule = ctx.server.environments[environment].moduleGraph.idToModuleMap.get(VIRTUAL_SSR_PREFIX +
188
- normalizeModulePath(ctx.file, givenOptions.rootDir));
197
+ let virtualSSRModuleId = VIRTUAL_SSR_PREFIX +
198
+ normalizeModulePath(ctx.file, givenOptions.rootDir);
199
+ if (ctx.file.endsWith(".css")) {
200
+ virtualSSRModuleId += ".js";
201
+ }
202
+ const virtualSSRModule = ctx.server.environments[environment].moduleGraph.idToModuleMap.get(virtualSSRModuleId);
189
203
  if (virtualSSRModule) {
190
204
  invalidateModule(ctx.server, environment, virtualSSRModule);
191
205
  }
@@ -196,6 +210,7 @@ export const miniflareHMRPlugin = (givenOptions) => [
196
210
  file: ctx.file,
197
211
  },
198
212
  });
213
+ log("hmr: sent rsc update");
199
214
  return [];
200
215
  }
201
216
  },
@@ -3,7 +3,8 @@ export const moveStaticAssetsPlugin = ({ rootDir, }) => ({
3
3
  name: "rwsdk:move-static-assets",
4
4
  apply: "build",
5
5
  async closeBundle() {
6
- if (this.environment.name === "worker") {
6
+ if (this.environment.name === "worker" &&
7
+ process.env.RWSDK_BUILD_PASS === "linker") {
7
8
  await $sh({
8
9
  cwd: rootDir,
9
10
  }) `mv dist/worker/assets/*.css dist/client/assets/ || true`;
@@ -17,7 +17,7 @@ export const prismaPlugin = async ({ projectRootDir, }) => {
17
17
  return {
18
18
  name: "rwsdk:prisma",
19
19
  configEnvironment(name, config) {
20
- if (name !== "worker") {
20
+ if (name !== "worker" || !process.env.VITE_IS_DEV_SERVER) {
21
21
  return;
22
22
  }
23
23
  const wasmPath = resolve(projectRootDir, "node_modules/.prisma/client/wasm.js");
@@ -10,7 +10,6 @@ export declare const ENV_RESOLVERS: {
10
10
  worker: enhancedResolve.ResolveFunction;
11
11
  client: enhancedResolve.ResolveFunction;
12
12
  };
13
- export declare const ENV_IMPORT_MAPPINGS: {
14
- [k: string]: Map<string, string>;
15
- };
16
- export declare const reactConditionsResolverPlugin: () => Plugin[];
13
+ export declare const reactConditionsResolverPlugin: ({ projectRootDir, }: {
14
+ projectRootDir: string;
15
+ }) => Plugin[];
@@ -3,6 +3,7 @@ import { ROOT_DIR } from "../lib/constants.mjs";
3
3
  import enhancedResolve from "enhanced-resolve";
4
4
  import { ensureAliasArray } from "./ensureAliasArray.mjs";
5
5
  const log = debug("rwsdk:vite:react-conditions-resolver-plugin");
6
+ const REACT_PREFIXES = ["react", "react-dom", "react-server-dom-webpack"];
6
7
  export const ENV_REACT_IMPORTS = {
7
8
  worker: [
8
9
  "react",
@@ -41,69 +42,85 @@ export const ENV_RESOLVERS = {
41
42
  conditionNames: ["browser", "default"],
42
43
  }),
43
44
  };
44
- export const ENV_IMPORT_MAPPINGS = Object.fromEntries(Object.keys(ENV_RESOLVERS).map((env) => [
45
- env,
46
- resolveEnvImportMappings(env),
47
- ]));
48
- function resolveEnvImportMappings(env) {
49
- process.env.VERBOSE &&
50
- log("Resolving environment import mappings for env=%s", env);
51
- const mappings = new Map();
52
- const reactImports = ENV_REACT_IMPORTS[env];
53
- for (const importRequest of reactImports) {
45
+ function resolveReactImport(id, envName, projectRootDir, isReactImportKnown = false) {
46
+ if (!isReactImportKnown) {
47
+ const isReactImport = REACT_PREFIXES.some((prefix) => id === prefix || id.startsWith(`${prefix}/`));
48
+ if (!isReactImport) {
49
+ return undefined;
50
+ }
51
+ }
52
+ let resolved;
53
+ try {
54
+ resolved = ENV_RESOLVERS[envName](projectRootDir, id) || undefined;
55
+ process.env.VERBOSE &&
56
+ log("Successfully resolved %s to %s for env=%s from project root", id, resolved, envName);
57
+ }
58
+ catch {
54
59
  process.env.VERBOSE &&
55
- log("Resolving import request=%s for env=%s", importRequest, env);
56
- let resolved = false;
60
+ log("Failed to resolve %s for env=%s from project root, trying ROOT_DIR", id, envName);
57
61
  try {
58
- resolved = ENV_RESOLVERS[env](ROOT_DIR, importRequest);
62
+ resolved = ENV_RESOLVERS[envName](ROOT_DIR, id) || undefined;
59
63
  process.env.VERBOSE &&
60
- log("Successfully resolved %s to %s for env=%s", importRequest, resolved, env);
64
+ log("Successfully resolved %s to %s for env=%s from rwsdk root", id, resolved, envName);
61
65
  }
62
66
  catch {
63
67
  process.env.VERBOSE &&
64
- log("Failed to resolve %s for env=%s", importRequest, env);
68
+ log("Failed to resolve %s for env=%s", id, envName);
65
69
  }
70
+ }
71
+ return resolved;
72
+ }
73
+ function resolveEnvImportMappings(env, projectRootDir) {
74
+ process.env.VERBOSE &&
75
+ log("Resolving environment import mappings for env=%s", env);
76
+ const mappings = new Map();
77
+ const reactImports = ENV_REACT_IMPORTS[env];
78
+ for (const importRequest of reactImports) {
79
+ const resolved = resolveReactImport(importRequest, env, projectRootDir, true);
66
80
  if (resolved) {
67
81
  mappings.set(importRequest, resolved);
68
- log("Added mapping for %s -> %s in env=%s", importRequest, resolved, env);
82
+ process.env.VERBOSE &&
83
+ log("Added mapping for %s -> %s in env=%s", importRequest, resolved, env);
69
84
  }
70
85
  }
71
- log("Environment import mappings complete for env=%s: %d mappings", env, mappings.size);
86
+ process.env.VERBOSE &&
87
+ log("Environment import mappings complete for env=%s: %d mappings", env, mappings.size);
72
88
  return mappings;
73
89
  }
74
- function createEsbuildResolverPlugin(envName) {
75
- const mappings = ENV_IMPORT_MAPPINGS[envName];
76
- if (!mappings) {
77
- return null;
78
- }
79
- return {
80
- name: `rwsdk:react-conditions-resolver-esbuild-${envName}`,
81
- setup(build) {
82
- build.onResolve({ filter: /.*/ }, (args) => {
83
- process.env.VERBOSE &&
84
- log("ESBuild resolving %s for env=%s, args=%O", args.path, envName, args);
85
- const resolved = mappings.get(args.path);
86
- if (resolved && args.importer !== "") {
87
- process.env.VERBOSE &&
88
- log("ESBuild resolving %s -> %s for env=%s", args.path, resolved, envName);
89
- if (args.path === "react-server-dom-webpack/client.edge") {
90
- return;
91
- }
92
- return {
93
- path: resolved,
94
- };
95
- }
96
- else {
97
- process.env.VERBOSE &&
98
- log("ESBuild no resolution found for %s for env=%s", args.path, envName);
99
- }
100
- });
101
- },
102
- };
103
- }
104
- export const reactConditionsResolverPlugin = () => {
90
+ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
105
91
  log("Initializing react conditions resolver plugin");
106
92
  let isBuild = false;
93
+ const ENV_IMPORT_MAPPINGS = Object.fromEntries(Object.keys(ENV_RESOLVERS).map((env) => [
94
+ env,
95
+ resolveEnvImportMappings(env, projectRootDir),
96
+ ]));
97
+ // Log a clean summary instead of all the individual mappings
98
+ const totalMappings = Object.values(ENV_IMPORT_MAPPINGS).reduce((sum, mappings) => sum + mappings.size, 0);
99
+ log("React conditions resolver configured with %d total mappings across %d environments", totalMappings, Object.keys(ENV_IMPORT_MAPPINGS).length);
100
+ function createEsbuildResolverPlugin(envName, mappings) {
101
+ if (!mappings) {
102
+ return null;
103
+ }
104
+ return {
105
+ name: `rwsdk:react-conditions-resolver-esbuild-${envName}`,
106
+ setup(build) {
107
+ build.onResolve({ filter: /.*/ }, (args) => {
108
+ let resolved = mappings.get(args.path);
109
+ if (!resolved) {
110
+ resolved = resolveReactImport(args.path, envName, projectRootDir);
111
+ }
112
+ if (resolved && args.importer !== "") {
113
+ if (args.path === "react-server-dom-webpack/client.edge") {
114
+ return;
115
+ }
116
+ return {
117
+ path: resolved,
118
+ };
119
+ }
120
+ });
121
+ },
122
+ };
123
+ }
107
124
  return [
108
125
  {
109
126
  name: "rwsdk:react-conditions-resolver:config",
@@ -125,7 +142,7 @@ export const reactConditionsResolverPlugin = () => {
125
142
  config.environments[envName] = {};
126
143
  }
127
144
  const envConfig = config.environments[envName];
128
- const esbuildPlugin = createEsbuildResolverPlugin(envName);
145
+ const esbuildPlugin = createEsbuildResolverPlugin(envName, mappings);
129
146
  if (esbuildPlugin && mappings) {
130
147
  envConfig.optimizeDeps ??= {};
131
148
  envConfig.optimizeDeps.esbuildOptions ??= {};
@@ -141,7 +158,8 @@ export const reactConditionsResolverPlugin = () => {
141
158
  for (const [find, replacement] of mappings) {
142
159
  const findRegex = new RegExp(`^${find.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")}$`);
143
160
  aliases.push({ find: findRegex, replacement });
144
- log("Added alias for env=%s: %s -> %s", envName, find, replacement);
161
+ process.env.VERBOSE &&
162
+ log("Added alias for env=%s: %s -> %s", envName, find, replacement);
145
163
  }
146
164
  log("Environment %s configured with %d aliases and %d optimizeDeps includes", envName, mappings.size, reactImports.length);
147
165
  }
@@ -158,21 +176,21 @@ export const reactConditionsResolverPlugin = () => {
158
176
  if (!envName) {
159
177
  return;
160
178
  }
161
- process.env.VERBOSE &&
162
- log("Resolving id=%s, environment=%s, importer=%s", id, envName, importer);
163
179
  const mappings = ENV_IMPORT_MAPPINGS[envName];
164
180
  if (!mappings) {
165
181
  process.env.VERBOSE &&
166
182
  log("No mappings found for environment: %s", envName);
167
183
  return;
168
184
  }
169
- const resolved = mappings.get(id);
185
+ let resolved = mappings.get(id);
186
+ if (!resolved) {
187
+ resolved = resolveReactImport(id, envName, projectRootDir);
188
+ }
170
189
  if (resolved) {
171
- log("Resolved %s -> %s for env=%s", id, resolved, envName);
190
+ process.env.VERBOSE &&
191
+ log("Resolved %s -> %s for env=%s", id, resolved, envName);
172
192
  return resolved;
173
193
  }
174
- process.env.VERBOSE &&
175
- log("No resolution found for id=%s in env=%s", id, envName);
176
194
  },
177
195
  },
178
196
  ];
@@ -3,9 +3,9 @@ export type RedwoodPluginOptions = {
3
3
  silent?: boolean;
4
4
  rootDir?: string;
5
5
  includeCloudflarePlugin?: boolean;
6
+ includeReactPlugin?: boolean;
6
7
  configPath?: string;
7
8
  entry?: {
8
- client?: string | string[];
9
9
  worker?: string;
10
10
  };
11
11
  };