rwsdk 1.0.0-alpha.9 → 1.0.0-beta.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 (159) hide show
  1. package/dist/lib/constants.mjs +1 -2
  2. package/dist/lib/e2e/browser.d.mts +1 -1
  3. package/dist/lib/e2e/browser.mjs +3 -4
  4. package/dist/lib/e2e/dev.mjs +62 -52
  5. package/dist/lib/e2e/environment.d.mts +2 -6
  6. package/dist/lib/e2e/environment.mjs +59 -72
  7. package/dist/lib/e2e/index.d.mts +5 -4
  8. package/dist/lib/e2e/index.mjs +5 -4
  9. package/dist/lib/e2e/poll.d.mts +8 -0
  10. package/dist/lib/e2e/poll.mjs +31 -0
  11. package/dist/lib/e2e/release.mjs +4 -4
  12. package/dist/lib/e2e/retry.d.mts +4 -0
  13. package/dist/lib/e2e/retry.mjs +16 -0
  14. package/dist/lib/e2e/setup.d.mts +2 -0
  15. package/dist/lib/e2e/setup.mjs +1 -0
  16. package/dist/lib/e2e/tarball.d.mts +3 -2
  17. package/dist/lib/e2e/tarball.mjs +5 -5
  18. package/dist/lib/e2e/testHarness.d.mts +59 -50
  19. package/dist/lib/e2e/testHarness.mjs +289 -343
  20. package/dist/lib/getShortName.mjs +1 -2
  21. package/dist/lib/getShortName.test.mjs +2 -2
  22. package/dist/lib/getSrcPaths.js +2 -2
  23. package/dist/lib/hasPkgScript.test.mjs +2 -2
  24. package/dist/lib/jsonUtils.test.mjs +2 -2
  25. package/dist/lib/normalizeModulePath.test.mjs +2 -2
  26. package/dist/lib/setupEnvFiles.mjs +2 -2
  27. package/dist/lib/smokeTests/artifacts.mjs +2 -2
  28. package/dist/lib/smokeTests/browser.d.mts +1 -1
  29. package/dist/lib/smokeTests/browser.mjs +6 -7
  30. package/dist/lib/smokeTests/cleanup.mjs +6 -9
  31. package/dist/lib/smokeTests/codeUpdates.mjs +5 -5
  32. package/dist/lib/smokeTests/development.mjs +2 -2
  33. package/dist/lib/smokeTests/environment.d.mts +2 -3
  34. package/dist/lib/smokeTests/environment.mjs +17 -3
  35. package/dist/lib/smokeTests/release.d.mts +2 -2
  36. package/dist/lib/smokeTests/release.mjs +3 -3
  37. package/dist/lib/smokeTests/reporting.mjs +2 -2
  38. package/dist/lib/smokeTests/runSmokeTests.mjs +4 -4
  39. package/dist/lib/smokeTests/utils.mjs +3 -3
  40. package/dist/lib/testUtils/stubEnvVars.mjs +1 -1
  41. package/dist/llms/rules/middleware.d.ts +1 -1
  42. package/dist/llms/rules/middleware.js +4 -4
  43. package/dist/runtime/client/client.d.ts +2 -2
  44. package/dist/runtime/client/client.js +2 -2
  45. package/dist/runtime/client/navigation.test.js +1 -1
  46. package/dist/runtime/client/types.d.ts +1 -1
  47. package/dist/runtime/entries/client.d.ts +2 -2
  48. package/dist/runtime/entries/client.js +2 -2
  49. package/dist/runtime/entries/router.d.ts +1 -1
  50. package/dist/runtime/entries/router.js +1 -1
  51. package/dist/runtime/entries/worker.d.ts +5 -6
  52. package/dist/runtime/entries/worker.js +5 -6
  53. package/dist/runtime/imports/worker.js +1 -1
  54. package/dist/runtime/lib/auth/session.d.ts +2 -2
  55. package/dist/runtime/lib/auth/session.js +5 -5
  56. package/dist/runtime/lib/db/DOWorkerDialect.d.ts +1 -1
  57. package/dist/runtime/lib/db/DOWorkerDialect.js +1 -1
  58. package/dist/runtime/lib/db/SqliteDurableObject.js +2 -2
  59. package/dist/runtime/lib/db/index.d.ts +2 -2
  60. package/dist/runtime/lib/db/index.js +2 -2
  61. package/dist/runtime/lib/db/migrations.d.ts +1 -1
  62. package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +3 -3
  63. package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +1 -1
  64. package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +2 -2
  65. package/dist/runtime/lib/db/typeInference/builders/createView.d.ts +1 -1
  66. package/dist/runtime/lib/db/typeInference/builders/dropTable.d.ts +1 -1
  67. package/dist/runtime/lib/db/typeInference/builders/dropView.d.ts +1 -1
  68. package/dist/runtime/lib/db/typeInference/builders/schema.d.ts +3 -3
  69. package/dist/runtime/lib/db/typeInference/database.d.ts +2 -2
  70. package/dist/runtime/lib/memoizeOnId.test.js +1 -1
  71. package/dist/runtime/lib/realtime/client.js +2 -2
  72. package/dist/runtime/lib/realtime/durableObject.js +1 -1
  73. package/dist/runtime/lib/realtime/protocol.test.js +1 -1
  74. package/dist/runtime/lib/realtime/shared.test.js +1 -1
  75. package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +1 -1
  76. package/dist/runtime/lib/realtime/worker.js +2 -2
  77. package/dist/runtime/lib/router.d.ts +1 -1
  78. package/dist/runtime/lib/router.test.js +2 -3
  79. package/dist/runtime/lib/rwContext.d.ts +1 -1
  80. package/dist/runtime/lib/stitchDocumentAndAppStreams.d.ts +18 -0
  81. package/dist/runtime/lib/stitchDocumentAndAppStreams.js +143 -0
  82. package/dist/runtime/lib/turnstile/useTurnstile.js +1 -1
  83. package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +1 -1
  84. package/dist/runtime/register/worker.d.ts +1 -1
  85. package/dist/runtime/register/worker.js +34 -22
  86. package/dist/runtime/render/assembleDocument.d.ts +1 -1
  87. package/dist/runtime/render/createThenableFromReadableStream.js +1 -1
  88. package/dist/runtime/render/preloads.d.ts +2 -2
  89. package/dist/runtime/render/renderDocumentHtmlStream.js +6 -6
  90. package/dist/runtime/render/renderHtmlStream.d.ts +1 -1
  91. package/dist/runtime/render/renderToRscStream.d.ts +4 -1
  92. package/dist/runtime/render/renderToRscStream.js +11 -1
  93. package/dist/runtime/render/renderToStream.d.ts +1 -1
  94. package/dist/runtime/render/renderToStream.js +2 -2
  95. package/dist/runtime/render/stylesheets.d.ts +1 -1
  96. package/dist/runtime/requestInfo/types.d.ts +0 -2
  97. package/dist/runtime/requestInfo/worker.d.ts +1 -1
  98. package/dist/runtime/requestInfo/worker.js +1 -9
  99. package/dist/runtime/script.js +1 -1
  100. package/dist/runtime/ssrBridge.d.ts +2 -2
  101. package/dist/runtime/ssrBridge.js +2 -2
  102. package/dist/runtime/worker.d.ts +1 -1
  103. package/dist/runtime/worker.js +3 -11
  104. package/dist/scripts/addon.d.mts +1 -0
  105. package/dist/scripts/addon.mjs +70 -0
  106. package/dist/scripts/debug-sync.mjs +106 -137
  107. package/dist/scripts/ensure-deploy-env.mjs +6 -6
  108. package/dist/scripts/migrate-new.mjs +3 -4
  109. package/dist/scripts/smoke-test.mjs +2 -2
  110. package/dist/scripts/worker-run.mjs +7 -9
  111. package/dist/vite/buildApp.mjs +1 -1
  112. package/dist/vite/checkIsUsingPrisma.test.mjs +1 -1
  113. package/dist/vite/configPlugin.mjs +33 -6
  114. package/dist/vite/createDirectiveLookupPlugin.mjs +1 -1
  115. package/dist/vite/createDirectiveLookupPlugin.test.mjs +2 -2
  116. package/dist/vite/createViteAwareResolver.d.mts +1 -2
  117. package/dist/vite/createViteAwareResolver.mjs +1 -1
  118. package/dist/vite/directiveModulesDevPlugin.mjs +4 -4
  119. package/dist/vite/directiveModulesDevPlugin.test.mjs +2 -2
  120. package/dist/vite/directivesPlugin.mjs +3 -3
  121. package/dist/vite/directivesPlugin.test.mjs +1 -1
  122. package/dist/vite/ensureAliasArray.test.mjs +1 -1
  123. package/dist/vite/findSpecifiers.mjs +1 -1
  124. package/dist/vite/findSpecifiers.test.mjs +2 -2
  125. package/dist/vite/findSsrSpecifiers.mjs +1 -1
  126. package/dist/vite/findSsrSpecifiers.test.mjs +1 -1
  127. package/dist/vite/getViteEsbuild.mjs +1 -1
  128. package/dist/vite/hasDirective.test.mjs +1 -1
  129. package/dist/vite/index.d.mts +1 -1
  130. package/dist/vite/invalidateCacheIfPrismaClientChanged.mjs +2 -2
  131. package/dist/vite/isJsFile.test.mjs +1 -1
  132. package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +3 -3
  133. package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +29 -24
  134. package/dist/vite/linkerPlugin.mjs +2 -2
  135. package/dist/vite/linkerPlugin.test.mjs +1 -1
  136. package/dist/vite/miniflareHMRPlugin.mjs +5 -5
  137. package/dist/vite/miniflareHMRPlugin.test.mjs +1 -1
  138. package/dist/vite/prismaPlugin.mjs +1 -1
  139. package/dist/vite/redwoodPlugin.d.mts +2 -0
  140. package/dist/vite/redwoodPlugin.mjs +36 -17
  141. package/dist/vite/redwoodPlugin.test.mjs +2 -2
  142. package/dist/vite/resolveForcedPaths.d.mts +4 -0
  143. package/dist/vite/resolveForcedPaths.mjs +9 -0
  144. package/dist/vite/runDirectivesScan.d.mts +2 -1
  145. package/dist/vite/runDirectivesScan.mjs +53 -17
  146. package/dist/vite/runDirectivesScan.test.mjs +2 -2
  147. package/dist/vite/ssrBridgePlugin.mjs +10 -3
  148. package/dist/vite/transformClientComponents.mjs +8 -6
  149. package/dist/vite/transformClientComponents.test.mjs +117 -59
  150. package/dist/vite/transformJsxScriptTagsPlugin.mjs +1 -1
  151. package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +2 -2
  152. package/dist/vite/transformServerFunctions.d.mts +1 -1
  153. package/dist/vite/transformServerFunctions.mjs +5 -5
  154. package/dist/vite/transformServerFunctions.test.mjs +3 -3
  155. package/package.json +54 -44
  156. package/dist/runtime/imports/resolveSSRValue.d.ts +0 -1
  157. package/dist/runtime/imports/resolveSSRValue.js +0 -8
  158. package/dist/runtime/lib/injectHtmlAtMarker.d.ts +0 -11
  159. package/dist/runtime/lib/injectHtmlAtMarker.js +0 -90
@@ -1 +1 @@
1
- export { type RedwoodPluginOptions, redwoodPlugin as redwood, } from "./redwoodPlugin.mjs";
1
+ export { redwoodPlugin as redwood, type RedwoodPluginOptions, } from "./redwoodPlugin.mjs";
@@ -1,6 +1,6 @@
1
- import { resolve } from "node:path";
2
- import { remove, pathExists } from "fs-extra";
1
+ import { pathExists, remove } from "fs-extra";
3
2
  import { stat } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
4
  export const invalidateCacheIfPrismaClientChanged = async ({ projectRootDir, }) => {
5
5
  const viteDepsCachePath = resolve(projectRootDir, "node_modules", ".vite", `deps_worker`, `@prisma_client.js`);
6
6
  // Get mtimes for comparison
@@ -1,4 +1,4 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, expect, it } from "vitest";
2
2
  import { isJsFile } from "./isJsFile.mjs";
3
3
  describe("isJsFile", () => {
4
4
  const matchingExtensions = [
@@ -1,6 +1,6 @@
1
- import { Plugin } from "vite";
2
1
  import enhancedResolve from "enhanced-resolve";
3
- export declare const ENV_REACT_IMPORTS: {
2
+ import { Plugin } from "vite";
3
+ export declare const ENV_PREDEFINED_IMPORTS: {
4
4
  worker: string[];
5
5
  ssr: string[];
6
6
  client: string[];
@@ -10,6 +10,6 @@ export declare const ENV_RESOLVERS: {
10
10
  worker: enhancedResolve.ResolveFunction;
11
11
  client: enhancedResolve.ResolveFunction;
12
12
  };
13
- export declare const reactConditionsResolverPlugin: ({ projectRootDir, }: {
13
+ export declare const knownDepsResolverPlugin: ({ projectRootDir, }: {
14
14
  projectRootDir: string;
15
15
  }) => Plugin[];
@@ -1,10 +1,15 @@
1
1
  import debug from "debug";
2
- import { ROOT_DIR } from "../lib/constants.mjs";
3
2
  import enhancedResolve from "enhanced-resolve";
3
+ import { ROOT_DIR } from "../lib/constants.mjs";
4
4
  import { ensureAliasArray } from "./ensureAliasArray.mjs";
5
- const log = debug("rwsdk:vite:react-conditions-resolver-plugin");
6
- const REACT_PREFIXES = ["react", "react-dom", "react-server-dom-webpack"];
7
- export const ENV_REACT_IMPORTS = {
5
+ const log = debug("rwsdk:vite:known-deps-resolver-plugin");
6
+ const KNOWN_PREFIXES = [
7
+ "react",
8
+ "react-dom",
9
+ "react-server-dom-webpack",
10
+ "rwsdk",
11
+ ];
12
+ export const ENV_PREDEFINED_IMPORTS = {
8
13
  worker: [
9
14
  "react",
10
15
  "react-dom",
@@ -42,10 +47,10 @@ export const ENV_RESOLVERS = {
42
47
  conditionNames: ["browser", "default"],
43
48
  }),
44
49
  };
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) {
50
+ function resolveKnownImport(id, envName, projectRootDir, isPrefixedImport = false) {
51
+ if (!isPrefixedImport) {
52
+ const isKnownImport = KNOWN_PREFIXES.some((prefix) => id === prefix || id.startsWith(`${prefix}/`));
53
+ if (!isKnownImport) {
49
54
  return undefined;
50
55
  }
51
56
  }
@@ -70,13 +75,13 @@ function resolveReactImport(id, envName, projectRootDir, isReactImportKnown = fa
70
75
  }
71
76
  return resolved;
72
77
  }
73
- function resolveEnvImportMappings(env, projectRootDir) {
78
+ function resolvePredefinedEnvImportMappings(env, projectRootDir) {
74
79
  process.env.VERBOSE &&
75
80
  log("Resolving environment import mappings for env=%s", env);
76
81
  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);
82
+ const predefinedImports = ENV_PREDEFINED_IMPORTS[env];
83
+ for (const importRequest of predefinedImports) {
84
+ const resolved = resolveKnownImport(importRequest, env, projectRootDir, true);
80
85
  if (resolved) {
81
86
  mappings.set(importRequest, resolved);
82
87
  process.env.VERBOSE &&
@@ -87,27 +92,27 @@ function resolveEnvImportMappings(env, projectRootDir) {
87
92
  log("Environment import mappings complete for env=%s: %d mappings", env, mappings.size);
88
93
  return mappings;
89
94
  }
90
- export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
91
- log("Initializing react conditions resolver plugin");
95
+ export const knownDepsResolverPlugin = ({ projectRootDir, }) => {
96
+ log("Initializing known dependencies resolver plugin");
92
97
  let isBuild = false;
93
98
  const ENV_IMPORT_MAPPINGS = Object.fromEntries(Object.keys(ENV_RESOLVERS).map((env) => [
94
99
  env,
95
- resolveEnvImportMappings(env, projectRootDir),
100
+ resolvePredefinedEnvImportMappings(env, projectRootDir),
96
101
  ]));
97
102
  // Log a clean summary instead of all the individual mappings
98
103
  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);
104
+ log("Known dependencies resolver configured with %d total mappings across %d environments", totalMappings, Object.keys(ENV_IMPORT_MAPPINGS).length);
100
105
  function createEsbuildResolverPlugin(envName, mappings) {
101
106
  if (!mappings) {
102
107
  return null;
103
108
  }
104
109
  return {
105
- name: `rwsdk:react-conditions-resolver-esbuild-${envName}`,
110
+ name: `rwsdk:known-dependencies-resolver-esbuild-${envName}`,
106
111
  setup(build) {
107
112
  build.onResolve({ filter: /.*/ }, (args) => {
108
113
  let resolved = mappings.get(args.path);
109
114
  if (!resolved) {
110
- resolved = resolveReactImport(args.path, envName, projectRootDir);
115
+ resolved = resolveKnownImport(args.path, envName, projectRootDir);
111
116
  }
112
117
  if (resolved && args.importer !== "") {
113
118
  if (args.path === "react-server-dom-webpack/client.edge") {
@@ -123,7 +128,7 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
123
128
  }
124
129
  return [
125
130
  {
126
- name: "rwsdk:react-conditions-resolver:config",
131
+ name: "rwsdk:known-dependencies-resolver:config",
127
132
  enforce: "post",
128
133
  config(config, { command }) {
129
134
  isBuild = command === "build";
@@ -133,7 +138,7 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
133
138
  log("Setting up resolve aliases and optimizeDeps for each environment");
134
139
  // Set up aliases and optimizeDeps for each environment
135
140
  for (const [envName, mappings] of Object.entries(ENV_IMPORT_MAPPINGS)) {
136
- const reactImports = ENV_REACT_IMPORTS[envName];
141
+ const predefinedImports = ENV_PREDEFINED_IMPORTS[envName];
137
142
  // Ensure environment config exists
138
143
  if (!config.environments) {
139
144
  config.environments = {};
@@ -151,7 +156,7 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
151
156
  envConfig.optimizeDeps.esbuildOptions.plugins ??= [];
152
157
  envConfig.optimizeDeps.esbuildOptions.plugins.push(esbuildPlugin);
153
158
  envConfig.optimizeDeps.include ??= [];
154
- envConfig.optimizeDeps.include.push(...reactImports);
159
+ envConfig.optimizeDeps.include.push(...predefinedImports);
155
160
  log("Added esbuild plugin and optimizeDeps includes for environment: %s", envName);
156
161
  }
157
162
  const aliases = ensureAliasArray(envConfig);
@@ -161,12 +166,12 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
161
166
  process.env.VERBOSE &&
162
167
  log("Added alias for env=%s: %s -> %s", envName, find, replacement);
163
168
  }
164
- log("Environment %s configured with %d aliases and %d optimizeDeps includes", envName, mappings.size, reactImports.length);
169
+ log("Environment %s configured with %d aliases and %d optimizeDeps includes", envName, mappings.size, predefinedImports.length);
165
170
  }
166
171
  },
167
172
  },
168
173
  {
169
- name: "rwsdk:react-conditions-resolver:resolveId",
174
+ name: "rwsdk:known-dependencies-resolver:resolveId",
170
175
  enforce: "pre",
171
176
  async resolveId(id, importer) {
172
177
  // Skip during directive scanning to avoid performance issues
@@ -188,7 +193,7 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
188
193
  }
189
194
  let resolved = mappings.get(id);
190
195
  if (!resolved) {
191
- resolved = resolveReactImport(id, envName, projectRootDir);
196
+ resolved = resolveKnownImport(id, envName, projectRootDir);
192
197
  }
193
198
  if (resolved) {
194
199
  process.env.VERBOSE &&
@@ -1,7 +1,7 @@
1
- import path from "node:path";
1
+ import debug from "debug";
2
2
  import fsp from "node:fs/promises";
3
+ import path from "node:path";
3
4
  import { CLIENT_MANIFEST_RELATIVE_PATH } from "../lib/constants.mjs";
4
- import debug from "debug";
5
5
  import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
6
6
  const log = debug("rwsdk:vite:linker-plugin");
7
7
  export function linkWorkerBundle({ code, manifestContent, projectRootDir, }) {
@@ -1,4 +1,4 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, expect, it } from "vitest";
2
2
  import { linkWorkerBundle } from "./linkerPlugin.mjs";
3
3
  describe("linkWorkerBundle", () => {
4
4
  const projectRootDir = "/test/project";
@@ -1,13 +1,13 @@
1
+ import debug from "debug";
2
+ import { readFile } from "node:fs/promises";
1
3
  import { resolve } from "node:path";
2
4
  import colors from "picocolors";
3
- import { readFile } from "node:fs/promises";
4
- import debug from "debug";
5
- import { VIRTUAL_SSR_PREFIX } from "./ssrBridgePlugin.mjs";
5
+ import { getShortName } from "../lib/getShortName.mjs";
6
6
  import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
7
7
  import { hasDirective as sourceHasDirective } from "./hasDirective.mjs";
8
- import { isJsFile } from "./isJsFile.mjs";
9
8
  import { invalidateModule } from "./invalidateModule.mjs";
10
- import { getShortName } from "../lib/getShortName.mjs";
9
+ import { isJsFile } from "./isJsFile.mjs";
10
+ import { VIRTUAL_SSR_PREFIX } from "./ssrBridgePlugin.mjs";
11
11
  const log = debug("rwsdk:vite:hmr-plugin");
12
12
  let hasErrored = false;
13
13
  const hasDirective = async (filepath, directive) => {
@@ -1,4 +1,4 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, expect, it } from "vitest";
2
2
  import { hasEntryAsAncestor } from "./miniflareHMRPlugin.mjs";
3
3
  const createModule = (file) => ({
4
4
  file,
@@ -1,7 +1,7 @@
1
1
  import { resolve } from "node:path";
2
- import { invalidateCacheIfPrismaClientChanged } from "./invalidateCacheIfPrismaClientChanged.mjs";
3
2
  import { checkPrismaStatus } from "./checkIsUsingPrisma.mjs";
4
3
  import { ensureAliasArray } from "./ensureAliasArray.mjs";
4
+ import { invalidateCacheIfPrismaClientChanged } from "./invalidateCacheIfPrismaClientChanged.mjs";
5
5
  export const prismaPlugin = async ({ projectRootDir, }) => {
6
6
  if (!checkPrismaStatus({ projectRootDir }).isUsingPrisma) {
7
7
  return;
@@ -6,6 +6,8 @@ export type RedwoodPluginOptions = {
6
6
  includeCloudflarePlugin?: boolean;
7
7
  includeReactPlugin?: boolean;
8
8
  configPath?: string;
9
+ forceClientPaths?: string[];
10
+ forceServerPaths?: string[];
9
11
  entry?: {
10
12
  worker?: string;
11
13
  };
@@ -1,32 +1,33 @@
1
+ import { cloudflare } from "@cloudflare/vite-plugin";
1
2
  import { resolve } from "node:path";
2
3
  import { unstable_readConfig } from "wrangler";
3
- import { cloudflare } from "@cloudflare/vite-plugin";
4
4
  import { devServerConstantPlugin } from "./devServerConstant.mjs";
5
5
  import { hasOwnCloudflareVitePlugin } from "./hasOwnCloudflareVitePlugin.mjs";
6
6
  import { hasOwnReactVitePlugin } from "./hasOwnReactVitePlugin.mjs";
7
7
  import reactPlugin from "@vitejs/plugin-react";
8
8
  import tsconfigPaths from "vite-tsconfig-paths";
9
- import { transformJsxScriptTagsPlugin } from "./transformJsxScriptTagsPlugin.mjs";
10
- import { directivesPlugin } from "./directivesPlugin.mjs";
11
- import { useClientLookupPlugin } from "./useClientLookupPlugin.mjs";
12
- import { useServerLookupPlugin } from "./useServerLookupPlugin.mjs";
13
- import { miniflareHMRPlugin } from "./miniflareHMRPlugin.mjs";
14
- import { moveStaticAssetsPlugin } from "./moveStaticAssetsPlugin.mjs";
15
- import { configPlugin } from "./configPlugin.mjs";
9
+ import { pathExists } from "fs-extra";
16
10
  import { $ } from "../lib/$.mjs";
17
- import { reactConditionsResolverPlugin } from "./reactConditionsResolverPlugin.mjs";
18
11
  import { findWranglerConfig } from "../lib/findWranglerConfig.mjs";
19
- import { pathExists } from "fs-extra";
20
- import { injectVitePreamble } from "./injectVitePreamblePlugin.mjs";
21
- import { vitePreamblePlugin } from "./vitePreamblePlugin.mjs";
22
- import { prismaPlugin } from "./prismaPlugin.mjs";
23
- import { ssrBridgePlugin } from "./ssrBridgePlugin.mjs";
24
12
  import { hasPkgScript } from "../lib/hasPkgScript.mjs";
13
+ import { configPlugin } from "./configPlugin.mjs";
25
14
  import { devServerTimingPlugin } from "./devServerTimingPlugin.mjs";
26
- import { manifestPlugin } from "./manifestPlugin.mjs";
27
- import { linkerPlugin } from "./linkerPlugin.mjs";
28
15
  import { directiveModulesDevPlugin } from "./directiveModulesDevPlugin.mjs";
29
16
  import { directivesFilteringPlugin } from "./directivesFilteringPlugin.mjs";
17
+ import { directivesPlugin } from "./directivesPlugin.mjs";
18
+ import { injectVitePreamble } from "./injectVitePreamblePlugin.mjs";
19
+ import { knownDepsResolverPlugin } from "./knownDepsResolverPlugin.mjs";
20
+ import { linkerPlugin } from "./linkerPlugin.mjs";
21
+ import { manifestPlugin } from "./manifestPlugin.mjs";
22
+ import { miniflareHMRPlugin } from "./miniflareHMRPlugin.mjs";
23
+ import { moveStaticAssetsPlugin } from "./moveStaticAssetsPlugin.mjs";
24
+ import { prismaPlugin } from "./prismaPlugin.mjs";
25
+ import { resolveForcedPaths } from "./resolveForcedPaths.mjs";
26
+ import { ssrBridgePlugin } from "./ssrBridgePlugin.mjs";
27
+ import { transformJsxScriptTagsPlugin } from "./transformJsxScriptTagsPlugin.mjs";
28
+ import { useClientLookupPlugin } from "./useClientLookupPlugin.mjs";
29
+ import { useServerLookupPlugin } from "./useServerLookupPlugin.mjs";
30
+ import { vitePreamblePlugin } from "./vitePreamblePlugin.mjs";
30
31
  export const determineWorkerEntryPathname = async ({ projectRootDir, workerConfigPath, options, readConfig = unstable_readConfig, }) => {
31
32
  if (options.entry?.worker) {
32
33
  return resolve(projectRootDir, options.entry.worker);
@@ -39,6 +40,24 @@ const serverFiles = new Set();
39
40
  const clientEntryPoints = new Set();
40
41
  export const redwoodPlugin = async (options = {}) => {
41
42
  const projectRootDir = process.cwd();
43
+ if (options.forceClientPaths) {
44
+ const clientPaths = await resolveForcedPaths({
45
+ patterns: options.forceClientPaths,
46
+ projectRootDir,
47
+ });
48
+ for (const p of clientPaths) {
49
+ clientFiles.add(p);
50
+ }
51
+ }
52
+ if (options.forceServerPaths) {
53
+ const serverPaths = await resolveForcedPaths({
54
+ patterns: options.forceServerPaths,
55
+ projectRootDir,
56
+ });
57
+ for (const p of serverPaths) {
58
+ serverFiles.add(p);
59
+ }
60
+ }
42
61
  const workerConfigPath = options.configPath ??
43
62
  (process.env.RWSDK_WRANGLER_CONFIG
44
63
  ? resolve(projectRootDir, process.env.RWSDK_WRANGLER_CONFIG)
@@ -87,7 +106,7 @@ export const redwoodPlugin = async (options = {}) => {
87
106
  serverFiles,
88
107
  projectRootDir,
89
108
  }),
90
- reactConditionsResolverPlugin({ projectRootDir }),
109
+ knownDepsResolverPlugin({ projectRootDir }),
91
110
  tsconfigPaths({ root: projectRootDir }),
92
111
  shouldIncludeCloudflarePlugin
93
112
  ? cloudflare({
@@ -1,6 +1,6 @@
1
- import { describe, it, expect } from "vitest";
2
- import { determineWorkerEntryPathname } from "./redwoodPlugin.mjs";
3
1
  import path from "node:path";
2
+ import { describe, expect, it } from "vitest";
3
+ import { determineWorkerEntryPathname } from "./redwoodPlugin.mjs";
4
4
  describe("determineWorkerEntryPathname", () => {
5
5
  const projectRootDir = "/test/project";
6
6
  it("should use the entry path from options if provided", async () => {
@@ -0,0 +1,4 @@
1
+ export declare function resolveForcedPaths({ patterns, projectRootDir, }: {
2
+ patterns: string[];
3
+ projectRootDir: string;
4
+ }): Promise<string[]>;
@@ -0,0 +1,9 @@
1
+ import { glob } from "glob";
2
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
3
+ export async function resolveForcedPaths({ patterns, projectRootDir, }) {
4
+ return (await glob(patterns, {
5
+ cwd: projectRootDir,
6
+ absolute: true,
7
+ realpath: true,
8
+ })).map((filepath) => normalizeModulePath(filepath, projectRootDir));
9
+ }
@@ -17,11 +17,12 @@ export declare function classifyModule({ contents, inheritedEnv, }: {
17
17
  isClient: boolean;
18
18
  isServer: boolean;
19
19
  };
20
+ export type EsbuildLoader = "js" | "jsx" | "ts" | "tsx" | "default";
20
21
  export declare const runDirectivesScan: ({ rootConfig, environments, clientFiles, serverFiles, entries: initialEntries, }: {
21
22
  rootConfig: ResolvedConfig;
22
23
  environments: Record<string, Environment>;
23
24
  clientFiles: Set<string>;
24
25
  serverFiles: Set<string>;
25
- entries: string[];
26
+ entries?: string[];
26
27
  }) => Promise<void>;
27
28
  export {};
@@ -1,12 +1,14 @@
1
+ // @ts-ignore
2
+ import { compile } from "@mdx-js/mdx";
3
+ import debug from "debug";
1
4
  import fsp from "node:fs/promises";
2
- import { hasDirective } from "./hasDirective.mjs";
3
5
  import path from "node:path";
4
- import debug from "debug";
5
- import { getViteEsbuild } from "./getViteEsbuild.mjs";
6
- import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
7
6
  import { INTERMEDIATES_OUTPUT_DIR } from "../lib/constants.mjs";
7
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
8
8
  import { externalModules } from "./constants.mjs";
9
9
  import { createViteAwareResolver } from "./createViteAwareResolver.mjs";
10
+ import { getViteEsbuild } from "./getViteEsbuild.mjs";
11
+ import { hasDirective } from "./hasDirective.mjs";
10
12
  const log = debug("rwsdk:vite:run-directives-scan");
11
13
  // Copied from Vite's source code.
12
14
  // https://github.com/vitejs/vite/blob/main/packages/vite/src/shared/utils.ts
@@ -50,7 +52,7 @@ export function classifyModule({ contents, inheritedEnv, }) {
50
52
  return { moduleEnv, isClient, isServer };
51
53
  }
52
54
  export const runDirectivesScan = async ({ rootConfig, environments, clientFiles, serverFiles, entries: initialEntries, }) => {
53
- console.log("\n🔍 Scanning for 'use client' and 'use server' directives...");
55
+ deferredLog("\n (rwsdk) Scanning for 'use client' and 'use server' directives...");
54
56
  // Set environment variable to indicate scanning is in progress
55
57
  process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE = "true";
56
58
  try {
@@ -94,7 +96,7 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
94
96
  setup(build) {
95
97
  // Match Vite's behavior by externalizing assets and special queries.
96
98
  // This prevents esbuild from trying to bundle them, which would fail.
97
- const scriptFilter = /\.(c|m)?[jt]sx?$/;
99
+ const scriptFilter = /\.(c|m)?[jt]sx?$|\.mdx$/;
98
100
  const specialQueryFilter = /[?&](?:url|raw|worker|sharedworker|inline)\b/;
99
101
  // This regex is used to identify if a path has any file extension.
100
102
  const hasExtensionRegex = /\.[^/]+$/;
@@ -162,7 +164,7 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
162
164
  log("Marking as external:", args.path, "resolved to:", resolvedPath);
163
165
  return { external: true };
164
166
  });
165
- build.onLoad({ filter: /\.(m|c)?[jt]sx?$/ }, async (args) => {
167
+ build.onLoad({ filter: /\.(m|c)?[jt]sx?$|\.mdx$/ }, async (args) => {
166
168
  log("onLoad called for:", args.path);
167
169
  if (!args.path.startsWith("/") ||
168
170
  args.path.includes("virtual:") ||
@@ -175,25 +177,52 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
175
177
  return null;
176
178
  }
177
179
  try {
178
- const contents = await readFileWithCache(args.path);
180
+ const originalContents = await readFileWithCache(args.path);
179
181
  const inheritedEnv = args.pluginData?.inheritedEnv || "worker";
180
182
  const { moduleEnv, isClient, isServer } = classifyModule({
181
- contents,
183
+ contents: originalContents,
182
184
  inheritedEnv,
183
185
  });
184
186
  // Store the definitive environment for this module, so it can be used when it becomes an importer.
185
- moduleEnvironments.set(args.path, moduleEnv);
186
- log("Set environment for", args.path, "to", moduleEnv);
187
+ const realPath = await fsp.realpath(args.path);
188
+ moduleEnvironments.set(realPath, moduleEnv);
189
+ log("Set environment for", realPath, "to", moduleEnv);
187
190
  // Finally, populate the output sets if the file has a directive.
188
191
  if (isClient) {
189
- log("Discovered 'use client' in:", args.path);
190
- clientFiles.add(normalizeModulePath(args.path, rootConfig.root));
192
+ log("Discovered 'use client' in:", realPath);
193
+ clientFiles.add(normalizeModulePath(realPath, rootConfig.root));
191
194
  }
192
195
  if (isServer) {
193
- log("Discovered 'use server' in:", args.path);
194
- serverFiles.add(normalizeModulePath(args.path, rootConfig.root));
196
+ log("Discovered 'use server' in:", realPath);
197
+ serverFiles.add(normalizeModulePath(realPath, rootConfig.root));
195
198
  }
196
- return { contents, loader: "default" };
199
+ let code;
200
+ let loader;
201
+ if (args.path.endsWith(".mdx")) {
202
+ const result = await compile(originalContents, {
203
+ jsx: true,
204
+ jsxImportSource: "react",
205
+ });
206
+ code = String(result.value);
207
+ loader = "tsx";
208
+ }
209
+ else if (/\.(m|c)?tsx$/.test(args.path)) {
210
+ code = originalContents;
211
+ loader = "tsx";
212
+ }
213
+ else if (/\.(m|c)?ts$/.test(args.path)) {
214
+ code = originalContents;
215
+ loader = "ts";
216
+ }
217
+ else if (/\.(m|c)?jsx$/.test(args.path)) {
218
+ code = originalContents;
219
+ loader = "jsx";
220
+ }
221
+ else {
222
+ code = originalContents;
223
+ loader = "js";
224
+ }
225
+ return { contents: code, loader };
197
226
  }
198
227
  catch (e) {
199
228
  log("Could not read file during scan, skipping:", args.path, e);
@@ -219,6 +248,13 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
219
248
  finally {
220
249
  // Always clear the scanning flag when done
221
250
  delete process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE;
222
- console.log(" Scan complete.");
251
+ deferredLog(" (rwsdk) Done scanning for 'use client' and 'use server' directives.");
252
+ process.env.VERBOSE &&
253
+ log("Client/server files after scanning: client=%O, server=%O", Array.from(clientFiles), Array.from(serverFiles));
223
254
  }
224
255
  };
256
+ const deferredLog = (message) => {
257
+ setTimeout(() => {
258
+ console.log(message);
259
+ }, 500);
260
+ };
@@ -1,5 +1,5 @@
1
- import { describe, it, expect, vi } from "vitest";
2
- import { resolveModuleWithEnvironment, classifyModule, } from "./runDirectivesScan.mjs";
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { classifyModule, resolveModuleWithEnvironment, } from "./runDirectivesScan.mjs";
3
3
  describe("runDirectivesScan helpers", () => {
4
4
  describe("resolveModuleWithEnvironment", () => {
5
5
  it("should use the client resolver when importerEnv is 'client'", async () => {
@@ -1,7 +1,7 @@
1
1
  import debug from "debug";
2
- import { findSsrImportCallSites } from "./findSsrSpecifiers.mjs";
3
- import { INTERMEDIATE_SSR_BRIDGE_PATH } from "../lib/constants.mjs";
4
2
  import MagicString from "magic-string";
3
+ import { INTERMEDIATE_SSR_BRIDGE_PATH } from "../lib/constants.mjs";
4
+ import { findSsrImportCallSites } from "./findSsrSpecifiers.mjs";
5
5
  const log = debug("rwsdk:vite:ssr-bridge-plugin");
6
6
  export const VIRTUAL_SSR_PREFIX = "virtual:rwsdk:ssr:";
7
7
  export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
@@ -34,7 +34,8 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
34
34
  build.onResolve({ filter: /.*$/ }, (args) => {
35
35
  process.env.VERBOSE &&
36
36
  log("Esbuild onResolve called for path=%s, args=%O", args.path, args);
37
- if (args.path === "rwsdk/__ssr_bridge") {
37
+ if (args.path === "rwsdk/__ssr_bridge" ||
38
+ args.path.startsWith(VIRTUAL_SSR_PREFIX)) {
38
39
  log("Marking as external: %s", args.path);
39
40
  return {
40
41
  path: args.path,
@@ -77,6 +78,12 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
77
78
  }
78
79
  else {
79
80
  // In build mode, the behavior depends on the build pass
81
+ if (id.startsWith(VIRTUAL_SSR_PREFIX)) {
82
+ if (this.environment.name === "worker") {
83
+ log("Virtual SSR module case (build-worker pass): resolving to external");
84
+ return { id, external: true };
85
+ }
86
+ }
80
87
  if (id === "rwsdk/__ssr_bridge" && this.environment.name === "worker") {
81
88
  if (process.env.RWSDK_BUILD_PASS === "worker") {
82
89
  // First pass: resolve to a temporary, external path
@@ -1,11 +1,12 @@
1
- import MagicString from "magic-string";
2
1
  import debug from "debug";
3
- import { hasDirective } from "./hasDirective.mjs";
2
+ import MagicString from "magic-string";
4
3
  import { findExports } from "./findSpecifiers.mjs";
4
+ import { hasDirective } from "./hasDirective.mjs";
5
5
  const logVite = debug("rwsdk:vite:transform-client-components:vite");
6
6
  const logEsbuild = debug("rwsdk:vite:transform-client-components:esbuild");
7
7
  export async function transformClientComponents(code, normalizedId, ctx) {
8
- if (!hasDirective(code, "use client")) {
8
+ if (!ctx.clientFiles?.has(normalizedId) &&
9
+ !hasDirective(code, "use client")) {
9
10
  return;
10
11
  }
11
12
  const log = ctx.isEsbuild ? logEsbuild : logVite;
@@ -76,13 +77,14 @@ export async function transformClientComponents(code, normalizedId, ctx) {
76
77
  // 4. Non-SSR files: replace all implementation with registerClientReference logic
77
78
  // Generate completely new code for worker/client environments
78
79
  const s = new MagicString("");
79
- // Add import declaration
80
+ s.append('import { ssrLoadModule } from "rwsdk/__ssr_bridge";\n');
80
81
  s.append('import { registerClientReference } from "rwsdk/worker";\n');
82
+ s.append(`const SSRModule = await ssrLoadModule("${normalizedId}");\n`);
81
83
  // Compute unique computed local names first
82
84
  const computedLocalNames = new Map(processedExports.map((info) => [getComputedLocalName(info), info]));
83
85
  // Add registerClientReference assignments for unique names
84
86
  for (const [computedLocalName, correspondingInfo] of computedLocalNames) {
85
- s.append(`const ${computedLocalName} = registerClientReference("${normalizedId}", "${correspondingInfo.exported}");\n`);
87
+ s.append(`const ${computedLocalName} = registerClientReference(SSRModule, "${normalizedId}", "${correspondingInfo.exported}");\n`);
86
88
  }
87
89
  // Add grouped export statement for named exports (preserving order and alias)
88
90
  if (processedExports.length > 0) {
@@ -94,7 +96,7 @@ export async function transformClientComponents(code, normalizedId, ctx) {
94
96
  // Add default export if present
95
97
  if (defaultExportInfo) {
96
98
  log(":isEsbuild=%s: Registering client reference for default export: %s", !!ctx.isEsbuild, defaultExportInfo.exported);
97
- s.append(`export default registerClientReference("${normalizedId}", "${defaultExportInfo.exported}");\n`);
99
+ s.append(`export default registerClientReference(SSRModule, "${normalizedId}", "${defaultExportInfo.exported}");\n`);
98
100
  }
99
101
  const sourceMap = s.generateMap({
100
102
  source: normalizedId,