rwsdk 1.4.1 → 1.5.1

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 (38) hide show
  1. package/dist/lib/e2e/environment.d.mts +1 -1
  2. package/dist/lib/e2e/environment.mjs +28 -1
  3. package/dist/lib/e2e/tarball.d.mts +2 -1
  4. package/dist/lib/e2e/tarball.mjs +2 -2
  5. package/dist/lib/e2e/testHarness.d.mts +5 -0
  6. package/dist/lib/e2e/testHarness.mjs +7 -1
  7. package/dist/lib/smokeTests/environment.mjs +1 -0
  8. package/dist/lib/smokeTests/types.d.mts +1 -0
  9. package/dist/runtime/render/assembleDocument.js +1 -1
  10. package/dist/runtime/render/renderDocumentHtmlStream.js +1 -1
  11. package/dist/runtime/render/stylesheets.js +22 -0
  12. package/dist/scripts/smoke-test.mjs +8 -0
  13. package/dist/use-synced-state/hibernation/__tests__/client-core.test.js +6 -5
  14. package/dist/use-synced-state/hibernation/connection/connection.js +1 -4
  15. package/dist/use-synced-state/hibernation/connection/timer.d.ts +1 -2
  16. package/dist/use-synced-state/hibernation/connection/timer.js +2 -21
  17. package/dist/vite/addOptimizeDepsPlugin.d.mts +10 -0
  18. package/dist/vite/addOptimizeDepsPlugin.mjs +6 -0
  19. package/dist/vite/buildApp.mjs +54 -15
  20. package/dist/vite/configPlugin.mjs +27 -20
  21. package/dist/vite/createDirectiveLookupPlugin.mjs +11 -15
  22. package/dist/vite/directiveModulesDevPlugin.mjs +86 -103
  23. package/dist/vite/directivesFilteringPlugin.mjs +55 -14
  24. package/dist/vite/directivesPlugin.mjs +44 -87
  25. package/dist/vite/knownDepsResolverPlugin.mjs +29 -36
  26. package/dist/vite/linkerPlugin.mjs +1 -1
  27. package/dist/vite/prismaPlugin.mjs +7 -10
  28. package/dist/vite/redwoodPlugin.mjs +5 -4
  29. package/dist/vite/runDirectivesScan.mjs +1 -1
  30. package/dist/vite/ssrBridgePlugin.mjs +11 -21
  31. package/dist/vite/statePlugin.mjs +8 -15
  32. package/dist/vite/transformJsxScriptTagsPlugin.mjs +3 -3
  33. package/dist/vite/viteCompat.d.mts +5 -0
  34. package/dist/vite/viteCompat.mjs +378 -0
  35. package/dist/vite/viteCompat.test.d.mts +1 -0
  36. package/dist/vite/viteCompat.test.mjs +72 -0
  37. package/dist/vite/vitePreamblePlugin.d.mts +2 -153
  38. package/package.json +8 -7
@@ -42,6 +42,91 @@ export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRoo
42
42
  client: VENDOR_CLIENT_BARREL_PATH,
43
43
  server: VENDOR_SERVER_BARREL_PATH,
44
44
  });
45
+ const appBarrelPaths = [APP_CLIENT_BARREL_PATH, APP_SERVER_BARREL_PATH];
46
+ const slugifyOptimizeEntry = (id) => id.replaceAll("/", "_").replaceAll(".", "__");
47
+ const VENDOR_CLIENT_BARREL_OPTIMIZED_ID = slugifyOptimizeEntry(VENDOR_CLIENT_BARREL_EXPORT_PATH);
48
+ const VENDOR_SERVER_BARREL_OPTIMIZED_ID = slugifyOptimizeEntry(VENDOR_SERVER_BARREL_EXPORT_PATH);
49
+ const escapeRegExp = (s) => s.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
50
+ const appBarrelFilter = new RegExp(`(${appBarrelPaths.map(escapeRegExp).join("|")})$`);
51
+ const BARREL_PREFIX = "\0rwsdk-app-barrel:";
52
+ const createAppBarrelBlockerPlugin = () => ({
53
+ name: "rwsdk:app-barrel-blocker",
54
+ async resolveId(id) {
55
+ await scanPromise;
56
+ // Handle stable vendor barrel specifiers by redirecting them to
57
+ // the in-memory temp barrel files. This lets Vite rewrite the
58
+ // specifier to its optimized dependency bundle while still serving
59
+ // our generated content.
60
+ const isClientBarrelPath = id === VENDOR_CLIENT_BARREL_EXPORT_PATH ||
61
+ id === VENDOR_CLIENT_BARREL_OPTIMIZED_ID ||
62
+ id === SDK_VENDOR_CLIENT_BARREL_PATH ||
63
+ id.endsWith("/__vendor_client_barrel.dev-virtual.js");
64
+ const isServerBarrelPath = id === VENDOR_SERVER_BARREL_EXPORT_PATH ||
65
+ id === VENDOR_SERVER_BARREL_OPTIMIZED_ID ||
66
+ id === SDK_VENDOR_SERVER_BARREL_PATH ||
67
+ id.endsWith("/__vendor_server_barrel.dev-virtual.js");
68
+ if (isClientBarrelPath) {
69
+ return VENDOR_CLIENT_BARREL_PATH;
70
+ }
71
+ if (isServerBarrelPath) {
72
+ return VENDOR_SERVER_BARREL_PATH;
73
+ }
74
+ // Handle app barrel files
75
+ if (appBarrelFilter.test(id)) {
76
+ return `${BARREL_PREFIX}${id}`;
77
+ }
78
+ // context(justinvdm, 11 Sep 2025): Prevent Vite from
79
+ // externalizing our application files. If we don't, paths
80
+ // imported in our application barrel files will be marked as
81
+ // external, and thus not scanned for dependencies.
82
+ if (id.startsWith("/") &&
83
+ (id.includes("/src/") || id.includes("/generated/")) &&
84
+ !id.includes("node_modules")) {
85
+ return id;
86
+ }
87
+ },
88
+ load(id) {
89
+ // Handle vendor barrels
90
+ if (id === VENDOR_CLIENT_BARREL_PATH ||
91
+ id === VENDOR_SERVER_BARREL_PATH) {
92
+ const isServerBarrel = id.includes("server-barrel");
93
+ const files = isServerBarrel ? serverFiles : clientFiles;
94
+ return generateVendorBarrelContent(files, projectRootDir);
95
+ }
96
+ // Handle app barrels
97
+ if (id.startsWith(BARREL_PREFIX)) {
98
+ const barrelPath = id.slice(BARREL_PREFIX.length);
99
+ const isServerBarrel = barrelPath.includes("app-server-barrel");
100
+ const files = isServerBarrel ? serverFiles : clientFiles;
101
+ return generateAppBarrelContent(files, projectRootDir);
102
+ }
103
+ },
104
+ });
105
+ const addUnique = (items, value) => {
106
+ if (!items.includes(value)) {
107
+ items.push(value);
108
+ }
109
+ };
110
+ const configureOptimizeDeps = (envName, env) => {
111
+ env.optimizeDeps ??= {};
112
+ env.optimizeDeps.include ??= [];
113
+ addUnique(env.optimizeDeps.include, VENDOR_CLIENT_BARREL_EXPORT_PATH);
114
+ addUnique(env.optimizeDeps.include, VENDOR_SERVER_BARREL_EXPORT_PATH);
115
+ const entries = (env.optimizeDeps.entries = castArray(env.optimizeDeps.entries ?? []));
116
+ addUnique(entries, VENDOR_CLIENT_BARREL_EXPORT_PATH);
117
+ addUnique(entries, VENDOR_SERVER_BARREL_EXPORT_PATH);
118
+ if (envName === "client" || envName === "ssr") {
119
+ addUnique(entries, APP_CLIENT_BARREL_PATH);
120
+ }
121
+ else if (envName === "worker") {
122
+ addUnique(entries, APP_SERVER_BARREL_PATH);
123
+ }
124
+ env.optimizeDeps.rolldownOptions ??= {};
125
+ env.optimizeDeps.rolldownOptions.plugins ??= [];
126
+ if (!env.optimizeDeps.rolldownOptions.plugins.some((plugin) => plugin.name === "rwsdk:app-barrel-blocker")) {
127
+ env.optimizeDeps.rolldownOptions.plugins.unshift(createAppBarrelBlockerPlugin());
128
+ }
129
+ };
45
130
  return {
46
131
  name: "rwsdk:directive-modules-dev",
47
132
  enforce: "pre",
@@ -105,109 +190,7 @@ export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRoo
105
190
  mkdirSync(path.dirname(VENDOR_SERVER_BARREL_PATH), { recursive: true });
106
191
  writeFileSync(VENDOR_SERVER_BARREL_PATH, "");
107
192
  for (const [envName, env] of Object.entries(config.environments || {})) {
108
- env.optimizeDeps ??= {};
109
- env.optimizeDeps.include ??= [];
110
- env.optimizeDeps.include.push(VENDOR_CLIENT_BARREL_EXPORT_PATH, VENDOR_SERVER_BARREL_EXPORT_PATH);
111
- const entries = (env.optimizeDeps.entries = castArray(env.optimizeDeps.entries ?? []));
112
- entries.push(VENDOR_CLIENT_BARREL_EXPORT_PATH, VENDOR_SERVER_BARREL_EXPORT_PATH);
113
- if (envName === "client" || envName === "ssr") {
114
- entries.push(APP_CLIENT_BARREL_PATH);
115
- }
116
- else if (envName === "worker") {
117
- entries.push(APP_SERVER_BARREL_PATH);
118
- }
119
- env.optimizeDeps.esbuildOptions ??= {};
120
- env.optimizeDeps.esbuildOptions.plugins ??= [];
121
- env.optimizeDeps.esbuildOptions.plugins.unshift({
122
- name: "rwsdk:app-barrel-blocker",
123
- setup(build) {
124
- const appBarrelPaths = [
125
- APP_CLIENT_BARREL_PATH,
126
- APP_SERVER_BARREL_PATH,
127
- ];
128
- const vendorBarrelPaths = [
129
- VENDOR_CLIENT_BARREL_PATH,
130
- VENDOR_SERVER_BARREL_PATH,
131
- ];
132
- const barrelPaths = [...appBarrelPaths, ...vendorBarrelPaths];
133
- const escapeRegExp = (s) => s.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
134
- const barrelFilter = new RegExp(`(${barrelPaths.map(escapeRegExp).join("|")})$`);
135
- build.onResolve({ filter: /.*/ }, async (args) => {
136
- // Block all resolutions until the scan is complete.
137
- await scanPromise;
138
- // Handle stable vendor barrel specifiers by redirecting them to
139
- // the in-memory temp barrel files. This lets Vite rewrite the
140
- // specifier to its optimized dependency bundle while still serving
141
- // our generated content.
142
- const isClientBarrelPath = args.path === VENDOR_CLIENT_BARREL_EXPORT_PATH ||
143
- args.path === SDK_VENDOR_CLIENT_BARREL_PATH ||
144
- args.path.endsWith("/__vendor_client_barrel.dev-virtual.js");
145
- const isServerBarrelPath = args.path === VENDOR_SERVER_BARREL_EXPORT_PATH ||
146
- args.path === SDK_VENDOR_SERVER_BARREL_PATH ||
147
- args.path.endsWith("/__vendor_server_barrel.dev-virtual.js");
148
- if (isClientBarrelPath) {
149
- return {
150
- path: VENDOR_CLIENT_BARREL_PATH,
151
- namespace: "rwsdk-barrel-ns",
152
- };
153
- }
154
- if (isServerBarrelPath) {
155
- return {
156
- path: VENDOR_SERVER_BARREL_PATH,
157
- namespace: "rwsdk-barrel-ns",
158
- };
159
- }
160
- // Handle barrel files (app + vendor)
161
- if (barrelFilter.test(args.path)) {
162
- return {
163
- path: args.path,
164
- namespace: "rwsdk-barrel-ns",
165
- };
166
- }
167
- // context(justinvdm, 11 Sep 2025): Prevent Vite from
168
- // externalizing our application files. If we don't, paths
169
- // imported in our application barrel files will be marked as
170
- // external, and thus not scanned for dependencies.
171
- if (args.path.startsWith("/") &&
172
- (args.path.includes("/src/") ||
173
- args.path.includes("/generated/")) &&
174
- !args.path.includes("node_modules")) {
175
- // By returning a result, we claim the module and prevent vite:dep-scan
176
- // from marking it as external.
177
- return {
178
- path: args.path,
179
- };
180
- }
181
- });
182
- build.onLoad({ filter: /__vendor_(client|server)_barrel\.dev-virtual\.js$/ }, async (args) => {
183
- await scanPromise;
184
- const isServerBarrel = args.path.includes("server-barrel");
185
- const files = isServerBarrel ? serverFiles : clientFiles;
186
- return {
187
- contents: generateVendorBarrelContent(files, projectRootDir),
188
- loader: "js",
189
- };
190
- });
191
- build.onLoad({ filter: /.*/, namespace: "rwsdk-barrel-ns" }, (args) => {
192
- const isServerBarrel = args.path.includes("server-barrel");
193
- const isVendorBarrel = args.path.includes("vendor");
194
- if (isVendorBarrel) {
195
- const files = isServerBarrel ? serverFiles : clientFiles;
196
- const content = generateVendorBarrelContent(files, projectRootDir);
197
- return {
198
- contents: content,
199
- loader: "js",
200
- };
201
- }
202
- const files = isServerBarrel ? serverFiles : clientFiles;
203
- const content = generateAppBarrelContent(files, projectRootDir);
204
- return {
205
- contents: content,
206
- loader: "js",
207
- };
208
- });
209
- },
210
- });
193
+ configureOptimizeDeps(envName, env);
211
194
  }
212
195
  },
213
196
  };
@@ -1,32 +1,73 @@
1
1
  import debug from "debug";
2
2
  import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
3
3
  const log = debug("rwsdk:vite:directives-filtering-plugin");
4
+ function filterDirectiveModules({ clientFiles, serverFiles, projectRootDir, includedModules, }) {
5
+ process.env.VERBOSE &&
6
+ log("Directive modules before filtering: client=%O, server=%O", Array.from(clientFiles), Array.from(serverFiles));
7
+ for (const files of [clientFiles, serverFiles]) {
8
+ for (const id of files) {
9
+ const absoluteId = normalizeModulePath(id, projectRootDir, {
10
+ absolute: true,
11
+ });
12
+ if (!includedModules.has(absoluteId)) {
13
+ files.delete(id);
14
+ }
15
+ }
16
+ }
17
+ process.env.VERBOSE &&
18
+ log("Client/server files after filtering: client=%O, server=%O", Array.from(clientFiles), Array.from(serverFiles));
19
+ }
4
20
  export const directivesFilteringPlugin = ({ clientFiles, serverFiles, projectRootDir, }) => {
5
21
  return {
6
22
  name: "rwsdk:directives-filtering",
7
23
  enforce: "post",
8
- async buildEnd() {
24
+ buildEnd() {
9
25
  if (this.environment.name !== "worker" ||
10
26
  process.env.RWSDK_BUILD_PASS !== "worker") {
11
27
  return;
12
28
  }
13
29
  log("Filtering directive modules after worker build...");
14
- process.env.VERBOSE &&
15
- log("Directive modules before filtering: client=%O, server=%O", Array.from(clientFiles), Array.from(serverFiles));
16
- [clientFiles, serverFiles].forEach((files) => {
17
- for (const id of files) {
18
- const absoluteId = normalizeModulePath(id, projectRootDir, {
19
- absolute: true,
20
- });
21
- const info = this.getModuleInfo(absoluteId);
22
- if (!info ||
23
- (typeof info.isIncluded !== "undefined" && !info.isIncluded)) {
24
- files.delete(id);
30
+ const includedModules = new Set();
31
+ for (const id of this.getModuleIds()) {
32
+ const info = this.getModuleInfo(id);
33
+ if (info && info.isIncluded !== false) {
34
+ includedModules.add(id);
35
+ }
36
+ }
37
+ filterDirectiveModules({
38
+ clientFiles,
39
+ serverFiles,
40
+ projectRootDir,
41
+ includedModules,
42
+ });
43
+ },
44
+ generateBundle(_options, bundle) {
45
+ if (this.environment.name !== "worker" ||
46
+ process.env.RWSDK_BUILD_PASS !== "worker") {
47
+ return;
48
+ }
49
+ log("Filtering directive modules from bundle output...");
50
+ // context(justinvdm, 2026-05-13): Rolldown (Vite 8+) does not expose
51
+ // ModuleInfo.isIncluded. Instead, we inspect the final output chunks.
52
+ // A module whose renderedLength is 0 (or missing) was tree-shaken to
53
+ // empty and should be removed from the directive sets.
54
+ const includedModules = new Set();
55
+ for (const output of Object.values(bundle)) {
56
+ if (output.type !== "chunk") {
57
+ continue;
58
+ }
59
+ for (const [moduleId, renderedModule] of Object.entries(output.modules)) {
60
+ if (renderedModule.renderedLength > 0) {
61
+ includedModules.add(moduleId);
25
62
  }
26
63
  }
64
+ }
65
+ filterDirectiveModules({
66
+ clientFiles,
67
+ serverFiles,
68
+ projectRootDir,
69
+ includedModules,
27
70
  });
28
- process.env.VERBOSE &&
29
- log("Client/server files after filtering: client=%O, server=%O", Array.from(clientFiles), Array.from(serverFiles));
30
71
  },
31
72
  };
32
73
  };
@@ -2,6 +2,7 @@ import debug from "debug";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
5
+ import { addOptimizeDepsPlugin } from "./addOptimizeDepsPlugin.mjs";
5
6
  import { transformClientComponents } from "./transformClientComponents.mjs";
6
7
  import { transformServerFunctions } from "./transformServerFunctions.mjs";
7
8
  const log = debug("rwsdk:vite:rsc-directives-plugin");
@@ -92,100 +93,56 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
92
93
  return;
93
94
  }
94
95
  process.env.VERBOSE && log("Configuring environment: env=%s", env);
95
- config.optimizeDeps ??= {};
96
- config.optimizeDeps.esbuildOptions ??= {};
97
- config.optimizeDeps.esbuildOptions.plugins ??= [];
98
- config.optimizeDeps.esbuildOptions.plugins.push({
99
- name: "rsc-directives-esbuild-transform",
100
- setup(build) {
101
- log("Setting up esbuild plugin for environment: %s", env);
102
- build.onLoad({ filter: /\.(js|ts|jsx|tsx|mts|mjs|cjs)$/ }, async (args) => {
103
- process.env.VERBOSE &&
104
- log("Esbuild onLoad called for environment=%s, path=%s", env, args.path);
105
- const normalizedPath = normalizeModulePath(args.path, projectRootDir);
106
- // context(justinvdm,2025-06-15): If we're in app code,
107
- // we will be doing the transform work in the vite plugin hooks,
108
- // the only reason we're in esbuild land for app code is for
109
- // dependency discovery, so we can skip transform work
110
- // and use heuristics instead - see below inside if block
111
- if (!args.path.includes("node_modules")) {
112
- if (clientFiles.has(normalizedPath)) {
113
- // context(justinvdm,2025-06-15): If this is a client file:
114
- // * for ssr and client envs we can skip so esbuild looks at the
115
- // original source code to discovery dependencies
116
- // * for worker env, the transform would have just created
117
- // references and dropped all imports, so we can just return empty code
118
- if (env === "client" || env === "ssr") {
119
- log("Esbuild onLoad skipping client module in app code for client or ssr env, path=%s", args.path);
120
- return undefined;
121
- }
122
- else {
123
- log("Esbuild onLoad returning empty code for server module in app code for worker env, path=%s to bypass esbuild dependency discovery", args.path);
124
- return {
125
- contents: "",
126
- loader: "js",
127
- };
128
- }
129
- }
130
- else if (serverFiles.has(normalizedPath)) {
131
- // context(justinvdm,2025-06-15): If this is a server file:
132
- // * for worker env, we can skip so esbuild looks at the
133
- // original source code to discovery dependencies
134
- // * for ssr and client envs, the transform would have just created
135
- // references and dropped all imports, so we can just return empty code
136
- if (env === "worker") {
137
- log("Esbuild onLoad skipping server module in app code for worker env, path=%s", args.path);
138
- return undefined;
139
- }
140
- else if (env === "ssr" || env === "client") {
141
- log("Esbuild onLoad returning empty code for server module in app code for ssr or client env, path=%s", args.path);
142
- return {
143
- contents: "",
144
- loader: "js",
145
- };
146
- }
147
- }
96
+ const directivesFileFilter = /\.(js|ts|jsx|tsx|mts|mjs|cjs)$/;
97
+ async function handleDirectivesLoad(filePath) {
98
+ const normalizedPath = normalizeModulePath(filePath, projectRootDir);
99
+ if (!filePath.includes("node_modules")) {
100
+ if (clientFiles.has(normalizedPath)) {
101
+ if (env === "client" || env === "ssr") {
102
+ return undefined;
148
103
  }
149
- let code;
150
- try {
151
- code = await fs.readFile(args.path, "utf-8");
104
+ else {
105
+ return { code: "", moduleType: "js" };
152
106
  }
153
- catch {
154
- process.env.VERBOSE &&
155
- log("Failed to read file: %s, environment=%s", args.path, env);
107
+ }
108
+ else if (serverFiles.has(normalizedPath)) {
109
+ if (env === "worker") {
156
110
  return undefined;
157
111
  }
158
- const clientResult = await transformClientComponents(code, normalizedPath, {
159
- environmentName: env,
160
- clientFiles,
161
- isEsbuild: true,
162
- });
163
- if (clientResult) {
164
- process.env.VERBOSE &&
165
- log("Esbuild client component transformation successful for environment=%s, path=%s", env, args.path);
166
- process.env.VERBOSE &&
167
- log("Esbuild client component transformation for environment=%s, path=%s, code: %j", env, args.path, clientResult.code);
168
- return {
169
- contents: clientResult.code,
170
- loader: getLoader(args.path),
171
- };
172
- }
173
- const serverResult = transformServerFunctions(code, normalizedPath, env, serverFiles);
174
- if (serverResult) {
175
- process.env.VERBOSE &&
176
- log("Esbuild server function transformation successful for environment=%s, path=%s", env, args.path);
177
- return {
178
- contents: serverResult.code,
179
- loader: getLoader(args.path),
180
- };
112
+ else if (env === "ssr" || env === "client") {
113
+ return { code: "", moduleType: "js" };
181
114
  }
182
- process.env.VERBOSE &&
183
- log("Esbuild no transformation applied for environment=%s, path=%s", env, args.path);
184
- });
115
+ }
116
+ }
117
+ let code;
118
+ try {
119
+ code = await fs.readFile(filePath, "utf-8");
120
+ }
121
+ catch {
122
+ return undefined;
123
+ }
124
+ const clientResult = await transformClientComponents(code, normalizedPath, { environmentName: env, clientFiles, isEsbuild: true });
125
+ if (clientResult) {
126
+ return { code: clientResult.code, moduleType: getLoader(filePath) };
127
+ }
128
+ const serverResult = transformServerFunctions(code, normalizedPath, env, serverFiles);
129
+ if (serverResult) {
130
+ return { code: serverResult.code, moduleType: getLoader(filePath) };
131
+ }
132
+ return undefined;
133
+ }
134
+ addOptimizeDepsPlugin(config, {
135
+ name: "rsc-directives-transform",
136
+ async load(id) {
137
+ if (!directivesFileFilter.test(id)) {
138
+ return;
139
+ }
140
+ const result = await handleDirectivesLoad(id);
141
+ if (result) {
142
+ return { code: result.code, moduleType: result.moduleType };
143
+ }
185
144
  },
186
145
  });
187
- process.env.VERBOSE &&
188
- log("Environment configuration complete for env=%s", env);
189
146
  },
190
147
  };
191
148
  };
@@ -91,44 +91,34 @@ export const knownDepsResolverPlugin = ({ projectRootDir, }) => {
91
91
  // Log a clean summary instead of all the individual mappings
92
92
  const totalMappings = Object.values(ENV_IMPORT_MAPPINGS).reduce((sum, mappings) => sum + mappings.size, 0);
93
93
  log("Known dependencies resolver configured with %d total mappings across %d environments", totalMappings, Object.keys(ENV_IMPORT_MAPPINGS).length);
94
- function createEsbuildResolverPlugin(envName, mappings) {
94
+ function createResolverPlugin(envName, mappings) {
95
95
  if (!mappings) {
96
96
  return null;
97
97
  }
98
- // Create reverse mapping from slugified names to original imports
99
- // Vite converts "react-dom/server.edge" -> "react-dom_server__edge"
100
- // Pattern: / becomes _, . becomes __
101
98
  const slugifiedToOriginal = new Map();
102
- for (const [original, resolved] of mappings) {
99
+ for (const [original] of mappings) {
103
100
  const slugified = original.replace(/\//g, "_").replace(/\./g, "__");
104
101
  slugifiedToOriginal.set(slugified, original);
105
102
  }
106
103
  return {
107
- name: `rwsdk:known-dependencies-resolver-esbuild-${envName}`,
108
- setup(build) {
109
- build.onResolve({ filter: /.*/ }, (args) => {
110
- let resolved = mappings.get(args.path);
111
- // If not found, check if it's a slugified version
112
- if (!resolved) {
113
- const originalImport = slugifiedToOriginal.get(args.path);
114
- if (originalImport) {
115
- resolved = mappings.get(originalImport);
116
- }
117
- }
118
- if (!resolved) {
119
- resolved = resolveKnownImport(args.path, envName, projectRootDir);
104
+ name: `rwsdk:known-dependencies-resolver-${envName}`,
105
+ resolveId(id) {
106
+ let resolved = mappings.get(id);
107
+ if (!resolved) {
108
+ const originalImport = slugifiedToOriginal.get(id);
109
+ if (originalImport) {
110
+ resolved = mappings.get(originalImport);
120
111
  }
121
- // Resolve for both entry points (importer === '') and regular imports
122
- // Entry points come from optimizeDeps.include and are critical to intercept
123
- if (resolved) {
124
- if (args.path === "react-server-dom-webpack/client.edge") {
125
- return;
126
- }
127
- return {
128
- path: resolved,
129
- };
112
+ }
113
+ if (!resolved) {
114
+ resolved = resolveKnownImport(id, envName, projectRootDir);
115
+ }
116
+ if (resolved) {
117
+ if (id === "react-server-dom-webpack/client.edge") {
118
+ return;
130
119
  }
131
- });
120
+ return resolved;
121
+ }
132
122
  },
133
123
  };
134
124
  }
@@ -152,17 +142,20 @@ export const knownDepsResolverPlugin = ({ projectRootDir, }) => {
152
142
  config.environments[envName] = {};
153
143
  }
154
144
  const envConfig = config.environments[envName];
155
- const esbuildPlugin = createEsbuildResolverPlugin(envName, mappings);
156
- if (esbuildPlugin && mappings) {
145
+ if (mappings) {
157
146
  envConfig.optimizeDeps ??= {};
158
- envConfig.optimizeDeps.esbuildOptions ??= {};
159
- envConfig.optimizeDeps.esbuildOptions.define ??= {};
160
- envConfig.optimizeDeps.esbuildOptions.define["process.env.NODE_ENV"] = JSON.stringify(process.env.NODE_ENV);
161
- envConfig.optimizeDeps.esbuildOptions.plugins ??= [];
162
- envConfig.optimizeDeps.esbuildOptions.plugins.push(esbuildPlugin);
147
+ envConfig.optimizeDeps.rolldownOptions ??= {};
148
+ envConfig.optimizeDeps.rolldownOptions.transform ??= {};
149
+ envConfig.optimizeDeps.rolldownOptions.transform.define ??= {};
150
+ envConfig.optimizeDeps.rolldownOptions.transform.define["process.env.NODE_ENV"] = JSON.stringify(process.env.NODE_ENV);
151
+ envConfig.optimizeDeps.rolldownOptions.plugins ??= [];
152
+ const plugin = createResolverPlugin(envName, mappings);
153
+ if (plugin) {
154
+ envConfig.optimizeDeps.rolldownOptions.plugins.push(plugin);
155
+ }
163
156
  envConfig.optimizeDeps.include ??= [];
164
157
  envConfig.optimizeDeps.include.push(...predefinedImports);
165
- log("Added esbuild plugin and optimizeDeps includes for environment: %s", envName);
158
+ log("Added optimizeDeps plugin and includes for environment: %s", envName);
166
159
  }
167
160
  const aliases = ensureAliasArray(envConfig);
168
161
  for (const [find, replacement] of mappings) {
@@ -9,7 +9,7 @@ export function linkWorkerBundle({ code, manifestContent, projectRootDir, base,
9
9
  const manifest = JSON.parse(manifestContent);
10
10
  // 1. Replace the manifest placeholder with the actual manifest content.
11
11
  log("Injecting manifest into worker bundle");
12
- newCode = newCode.replace(/['"]__RWSDK_MANIFEST_PLACEHOLDER__['"]/, manifestContent);
12
+ newCode = newCode.replace(/['"`]__RWSDK_MANIFEST_PLACEHOLDER__['"`]/, manifestContent);
13
13
  // 2. Replace asset placeholders with their final hashed paths.
14
14
  log("Replacing asset placeholders in final worker bundle");
15
15
  for (const [key, value] of Object.entries(manifest)) {
@@ -1,4 +1,5 @@
1
1
  import { resolve } from "node:path";
2
+ import { addOptimizeDepsPlugin } from "./addOptimizeDepsPlugin.mjs";
2
3
  import { checkPrismaStatus } from "./checkIsUsingPrisma.mjs";
3
4
  import { ensureAliasArray } from "./ensureAliasArray.mjs";
4
5
  import { invalidateCacheIfPrismaClientChanged } from "./invalidateCacheIfPrismaClientChanged.mjs";
@@ -21,17 +22,13 @@ export const prismaPlugin = async ({ projectRootDir, }) => {
21
22
  return;
22
23
  }
23
24
  const wasmPath = resolve(projectRootDir, "node_modules/.prisma/client/wasm.js");
24
- config.optimizeDeps ??= {};
25
- config.optimizeDeps.esbuildOptions ??= {};
26
- config.optimizeDeps.esbuildOptions.plugins ??= [];
27
- config.optimizeDeps.esbuildOptions.plugins.push({
25
+ const prismaFilter = /^.prisma\/client\/default/;
26
+ addOptimizeDepsPlugin(config, {
28
27
  name: "rwsdk:prisma",
29
- setup(build) {
30
- build.onResolve({ filter: /^.prisma\/client\/default/ }, async () => {
31
- return {
32
- path: wasmPath,
33
- };
34
- });
28
+ resolveId(id) {
29
+ if (prismaFilter.test(id)) {
30
+ return wasmPath;
31
+ }
35
32
  },
36
33
  });
37
34
  ensureAliasArray(config).push({
@@ -6,12 +6,12 @@ import { devServerConstantPlugin } from "./devServerConstant.mjs";
6
6
  import { hasOwnCloudflareVitePlugin } from "./hasOwnCloudflareVitePlugin.mjs";
7
7
  import { hasOwnReactVitePlugin } from "./hasOwnReactVitePlugin.mjs";
8
8
  import reactPlugin from "@vitejs/plugin-react";
9
- import tsconfigPaths from "vite-tsconfig-paths";
10
9
  import { pathExists } from "fs-extra";
11
10
  import { $ } from "../lib/$.mjs";
12
11
  import { findWranglerConfig } from "../lib/findWranglerConfig.mjs";
13
12
  import { hasPkgScript } from "../lib/hasPkgScript.mjs";
14
13
  import { cloudflarePreInitPlugin } from "./cloudflarePreInitPlugin.mjs";
14
+ import { compatTransform } from "./viteCompat.mjs";
15
15
  import { configPlugin } from "./configPlugin.mjs";
16
16
  import { devServerTimingPlugin } from "./devServerTimingPlugin.mjs";
17
17
  import { directiveModulesDevPlugin } from "./directiveModulesDevPlugin.mjs";
@@ -84,7 +84,7 @@ export const redwoodPlugin = async (options = {}) => {
84
84
  console.log("🚀 Project has no .wrangler directory yet, assuming fresh install: running `npm run dev:init`...");
85
85
  await $ `npm run dev:init`;
86
86
  }
87
- return [
87
+ const pluginResults = await Promise.all([
88
88
  staleDepRetryPlugin(),
89
89
  statePlugin({ projectRootDir }),
90
90
  devServerTimingPlugin(),
@@ -112,7 +112,6 @@ export const redwoodPlugin = async (options = {}) => {
112
112
  }),
113
113
  knownDepsResolverPlugin({ projectRootDir }),
114
114
  cloudflarePreInitPlugin(),
115
- tsconfigPaths({ root: projectRootDir }),
116
115
  shouldIncludeCloudflarePlugin
117
116
  ? cloudflare({
118
117
  viteEnvironment: { name: "worker" },
@@ -158,5 +157,7 @@ export const redwoodPlugin = async (options = {}) => {
158
157
  serverFiles,
159
158
  projectRootDir,
160
159
  }),
161
- ];
160
+ ]);
161
+ const flattenedPlugins = pluginResults.flat().filter((plugin) => plugin != null);
162
+ return compatTransform(flattenedPlugins);
162
163
  };
@@ -99,7 +99,7 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
99
99
  return contents;
100
100
  };
101
101
  const esbuild = await getViteEsbuild(rootConfig.root);
102
- const input = initialEntries ?? environments.worker.config.build.rollupOptions?.input;
102
+ const input = initialEntries ?? environments.worker.config.build.rolldownOptions?.input;
103
103
  let entries;
104
104
  if (Array.isArray(input)) {
105
105
  entries = input;