rwsdk 0.2.0 → 0.3.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 (63) 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/imports/client.js +4 -9
  15. package/dist/runtime/imports/worker.js +2 -1
  16. package/dist/runtime/register/ssr.js +2 -1
  17. package/dist/runtime/requestInfo/worker.js +9 -1
  18. package/dist/runtime/worker.d.ts +0 -3
  19. package/dist/runtime/worker.js +1 -10
  20. package/dist/scripts/debug-sync.mjs +0 -23
  21. package/dist/scripts/smoke-test.mjs +0 -10
  22. package/dist/vite/buildApp.d.mts +15 -0
  23. package/dist/vite/buildApp.mjs +53 -0
  24. package/dist/vite/configPlugin.d.mts +4 -4
  25. package/dist/vite/configPlugin.mjs +70 -76
  26. package/dist/vite/constants.d.mts +2 -0
  27. package/dist/vite/constants.mjs +12 -0
  28. package/dist/vite/createDirectiveLookupPlugin.d.mts +0 -6
  29. package/dist/vite/createDirectiveLookupPlugin.mjs +69 -145
  30. package/dist/vite/createViteAwareResolver.d.mts +4 -0
  31. package/dist/vite/createViteAwareResolver.mjs +208 -0
  32. package/dist/vite/directiveModulesDevPlugin.d.mts +8 -0
  33. package/dist/vite/directiveModulesDevPlugin.mjs +87 -0
  34. package/dist/vite/directivesFilteringPlugin.d.mts +6 -0
  35. package/dist/vite/directivesFilteringPlugin.mjs +31 -0
  36. package/dist/vite/directivesPlugin.mjs +32 -42
  37. package/dist/vite/getViteEsbuild.d.mts +1 -0
  38. package/dist/vite/getViteEsbuild.mjs +12 -0
  39. package/dist/vite/injectVitePreamblePlugin.d.mts +3 -2
  40. package/dist/vite/injectVitePreamblePlugin.mjs +8 -2
  41. package/dist/vite/linkerPlugin.d.mts +4 -0
  42. package/dist/vite/linkerPlugin.mjs +41 -0
  43. package/dist/vite/manifestPlugin.d.mts +2 -2
  44. package/dist/vite/manifestPlugin.mjs +20 -37
  45. package/dist/vite/moveStaticAssetsPlugin.mjs +2 -1
  46. package/dist/vite/prismaPlugin.mjs +1 -1
  47. package/dist/vite/reactConditionsResolverPlugin.mjs +15 -16
  48. package/dist/vite/redwoodPlugin.d.mts +0 -1
  49. package/dist/vite/redwoodPlugin.mjs +27 -9
  50. package/dist/vite/runDirectivesScan.d.mts +7 -0
  51. package/dist/vite/runDirectivesScan.mjs +156 -0
  52. package/dist/vite/ssrBridgePlugin.mjs +21 -14
  53. package/dist/vite/transformClientComponents.d.mts +0 -1
  54. package/dist/vite/transformClientComponents.mjs +1 -9
  55. package/dist/vite/transformJsxScriptTagsPlugin.d.mts +4 -3
  56. package/dist/vite/transformJsxScriptTagsPlugin.mjs +66 -84
  57. package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +67 -41
  58. package/dist/vite/transformServerFunctions.d.mts +1 -1
  59. package/dist/vite/transformServerFunctions.mjs +11 -12
  60. package/dist/vite/virtualPlugin.mjs +8 -0
  61. package/package.json +7 -1
  62. package/dist/runtime/clientNavigation.d.ts +0 -9
  63. package/dist/runtime/clientNavigation.js +0 -88
@@ -0,0 +1,208 @@
1
+ import resolve from "enhanced-resolve";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import createDebug from "debug";
5
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
6
+ const debug = createDebug("rwsdk:vite:enhanced-resolve-plugin");
7
+ // Enhanced-resolve plugin that wraps Vite plugin resolution
8
+ class VitePluginResolverPlugin {
9
+ constructor(environment, source = "resolve", target = "resolved") {
10
+ this.environment = environment;
11
+ this.source = source;
12
+ this.target = target;
13
+ // Create an enhanced-resolve instance for the plugin context
14
+ const baseOptions = mapViteResolveToEnhancedResolveOptions(this.environment.config, this.environment.name);
15
+ this.enhancedResolver = resolve.create(baseOptions);
16
+ }
17
+ apply(resolver) {
18
+ const target = resolver.ensureHook(this.target);
19
+ resolver
20
+ .getHook(this.source)
21
+ .tapAsync("VitePluginResolverPlugin", (request, resolveContext, callback) => {
22
+ const plugins = this.environment?.plugins;
23
+ if (!plugins) {
24
+ return callback();
25
+ }
26
+ // Create a plugin context with enhanced-resolve-based resolve method
27
+ const pluginContext = {
28
+ environment: this.environment,
29
+ resolve: async (id, importer) => {
30
+ return new Promise((resolve) => {
31
+ this.enhancedResolver({}, importer || this.environment.config.root, id, {}, (err, result) => {
32
+ if (!err && result) {
33
+ debug("Context resolve: %s -> %s", id, result);
34
+ resolve({ id: result });
35
+ }
36
+ else {
37
+ debug("Context resolve failed for %s: %s", id, err?.message || "not found");
38
+ resolve(null);
39
+ }
40
+ });
41
+ });
42
+ },
43
+ };
44
+ debug("Trying to resolve %s from %s", request.request, request.path);
45
+ // This function encapsulates the logic to process Vite plugins for a given request.
46
+ const runPluginProcessing = async (currentRequest) => {
47
+ debug("Available plugins:", plugins.map((p) => p.name));
48
+ for (const plugin of plugins) {
49
+ const resolveIdHandler = plugin.resolveId;
50
+ if (!resolveIdHandler)
51
+ continue;
52
+ let handlerFn;
53
+ if (typeof resolveIdHandler === "function") {
54
+ handlerFn = resolveIdHandler;
55
+ }
56
+ else if (typeof resolveIdHandler === "object" &&
57
+ typeof resolveIdHandler.handler === "function") {
58
+ handlerFn = resolveIdHandler.handler;
59
+ }
60
+ if (!handlerFn)
61
+ continue;
62
+ try {
63
+ debug("Calling plugin '%s' for '%s'", plugin.name, currentRequest.request);
64
+ const result = await handlerFn.call(pluginContext, currentRequest.request, currentRequest.path, { scan: true });
65
+ debug("Plugin '%s' returned:", plugin.name, result);
66
+ if (!result)
67
+ continue;
68
+ const resolvedId = typeof result === "string" ? result : result.id;
69
+ if (resolvedId && resolvedId !== currentRequest.request) {
70
+ debug("Plugin '%s' resolved '%s' -> '%s'", plugin.name, currentRequest.request, resolvedId);
71
+ return callback(null, {
72
+ ...currentRequest,
73
+ path: resolvedId,
74
+ });
75
+ }
76
+ else if (resolvedId === currentRequest.request) {
77
+ debug("Plugin '%s' returned unchanged ID, continuing to next plugin", plugin.name);
78
+ }
79
+ }
80
+ catch (e) {
81
+ debug("Plugin '%s' failed while resolving '%s': %s", plugin.name, currentRequest.request, e.message);
82
+ }
83
+ }
84
+ // If no plugin resolves, fall back to enhanced-resolve's default behavior
85
+ debug("No Vite plugin resolved '%s', falling back.", currentRequest.request);
86
+ // For absolute paths, check if the file exists
87
+ if (path.isAbsolute(currentRequest.request)) {
88
+ try {
89
+ if (fs.existsSync(currentRequest.request)) {
90
+ debug("File exists, resolving to: %s", currentRequest.request);
91
+ return callback(null, {
92
+ ...currentRequest,
93
+ path: currentRequest.request,
94
+ });
95
+ }
96
+ }
97
+ catch (e) {
98
+ debug("Error checking file existence: %s", e.message);
99
+ }
100
+ }
101
+ callback();
102
+ };
103
+ // For relative imports, normalize them to absolute paths first
104
+ if (request.request.startsWith("../") ||
105
+ request.request.startsWith("./")) {
106
+ try {
107
+ // Use path.dirname to get the directory of the importer file
108
+ const importerDir = path.dirname(request.path);
109
+ const absolutePath = normalizeModulePath(request.request, importerDir, { absolute: true });
110
+ debug("Absolutified %s -> %s", request.request, absolutePath);
111
+ const absoluteRequest = { ...request, request: absolutePath };
112
+ runPluginProcessing(absoluteRequest).catch((e) => {
113
+ debug("Error in plugin processing: %s", e.message);
114
+ callback();
115
+ });
116
+ }
117
+ catch (e) {
118
+ debug("Failed to absolutify %s: %s", request.request, e.message);
119
+ callback();
120
+ }
121
+ }
122
+ else {
123
+ // For non-relative imports, process them directly
124
+ runPluginProcessing(request).catch((e) => {
125
+ debug("Error in plugin processing: %s", e.message);
126
+ callback();
127
+ });
128
+ }
129
+ });
130
+ }
131
+ }
132
+ const mapAlias = (alias) => {
133
+ const mappedAlias = {};
134
+ if (Array.isArray(alias)) {
135
+ // Handle array format: { find: string | RegExp, replacement: string }
136
+ for (const { find, replacement } of alias) {
137
+ if (find instanceof RegExp) {
138
+ // For RegExp, use the source as-is
139
+ mappedAlias[find.source] = replacement;
140
+ }
141
+ else {
142
+ // For string aliases, use them as-is without modification
143
+ mappedAlias[find] = replacement;
144
+ }
145
+ }
146
+ }
147
+ else {
148
+ // Handle object format: { [find: string]: replacement }
149
+ for (const [find, replacement] of Object.entries(alias)) {
150
+ // Use the alias key as-is without modification
151
+ mappedAlias[find] = replacement;
152
+ }
153
+ }
154
+ return mappedAlias;
155
+ };
156
+ export const mapViteResolveToEnhancedResolveOptions = (viteConfig, envName) => {
157
+ const env = viteConfig.environments[envName];
158
+ if (!env) {
159
+ throw new Error(`Could not find environment configuration for "${envName}".`);
160
+ }
161
+ const envResolveOptions = (env.resolve || {});
162
+ // Merge root config aliases with environment-specific aliases
163
+ const mergedAlias = {
164
+ ...(viteConfig.resolve?.alias ? mapAlias(viteConfig.resolve.alias) : {}),
165
+ ...(envResolveOptions.alias ? mapAlias(envResolveOptions.alias) : {}),
166
+ };
167
+ // Use comprehensive extensions list similar to Vite's defaults
168
+ const extensions = envResolveOptions.extensions || [
169
+ ".mjs",
170
+ ".js",
171
+ ".mts",
172
+ ".ts",
173
+ ".jsx",
174
+ ".tsx",
175
+ ".json",
176
+ ];
177
+ const baseOptions = {
178
+ // File system is required by enhanced-resolve.
179
+ fileSystem: fs,
180
+ // Map Vite's resolve options to enhanced-resolve's options.
181
+ alias: Object.keys(mergedAlias).length > 0 ? mergedAlias : undefined,
182
+ conditionNames: envResolveOptions.conditions,
183
+ mainFields: envResolveOptions.mainFields,
184
+ extensions,
185
+ symlinks: envResolveOptions.preserveSymlinks,
186
+ // Add default node modules resolution.
187
+ modules: ["node_modules"],
188
+ roots: [viteConfig.root],
189
+ };
190
+ return baseOptions;
191
+ };
192
+ export const createViteAwareResolver = (viteConfig, envName, environment) => {
193
+ const baseOptions = mapViteResolveToEnhancedResolveOptions(viteConfig, envName);
194
+ // Add Vite plugin resolver if environment is provided
195
+ const plugins = environment
196
+ ? [new VitePluginResolverPlugin(environment)]
197
+ : [];
198
+ const enhancedResolveOptions = {
199
+ ...baseOptions,
200
+ plugins,
201
+ };
202
+ debug("Creating enhanced-resolve with options:", {
203
+ extensions: enhancedResolveOptions.extensions,
204
+ alias: enhancedResolveOptions.alias,
205
+ roots: enhancedResolveOptions.roots,
206
+ });
207
+ return resolve.create(enhancedResolveOptions);
208
+ };
@@ -0,0 +1,8 @@
1
+ import { Plugin } from "vite";
2
+ export declare const VIRTUAL_CLIENT_BARREL_ID = "virtual:rwsdk:client-module-barrel";
3
+ export declare const VIRTUAL_SERVER_BARREL_ID = "virtual:rwsdk:server-module-barrel";
4
+ export declare const directiveModulesDevPlugin: ({ clientFiles, serverFiles, projectRootDir, }: {
5
+ clientFiles: Set<string>;
6
+ serverFiles: Set<string>;
7
+ projectRootDir: string;
8
+ }) => Plugin;
@@ -0,0 +1,87 @@
1
+ import path from "path";
2
+ import { writeFileSync, mkdirSync } from "node:fs";
3
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
4
+ import { CLIENT_BARREL_PATH, SERVER_BARREL_PATH } from "../lib/constants.mjs";
5
+ import { runDirectivesScan } from "./runDirectivesScan.mjs";
6
+ export const VIRTUAL_CLIENT_BARREL_ID = "virtual:rwsdk:client-module-barrel";
7
+ export const VIRTUAL_SERVER_BARREL_ID = "virtual:rwsdk:server-module-barrel";
8
+ const CLIENT_BARREL_EXPORT_PATH = "rwsdk/__client_barrel";
9
+ const SERVER_BARREL_EXPORT_PATH = "rwsdk/__server_barrel";
10
+ const generateBarrelContent = (files, projectRootDir) => {
11
+ const imports = [...files]
12
+ .filter((file) => file.includes("node_modules"))
13
+ .map((file, i) => `import * as M${i} from '${normalizeModulePath(file, projectRootDir, {
14
+ absolute: true,
15
+ })}';`)
16
+ .join("\n");
17
+ const exports = "export default {\n" +
18
+ [...files]
19
+ .filter((file) => file.includes("node_modules"))
20
+ .map((file, i) => ` '${normalizeModulePath(file, projectRootDir)}': M${i},`)
21
+ .join("\n") +
22
+ "\n};";
23
+ return `${imports}\n\n${exports}`;
24
+ };
25
+ export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRootDir, }) => {
26
+ let scanPromise = null;
27
+ return {
28
+ name: "rwsdk:directive-modules-dev",
29
+ configureServer(server) {
30
+ if (!process.env.VITE_IS_DEV_SERVER || process.env.RWSDK_WORKER_RUN) {
31
+ return;
32
+ }
33
+ // Start the directive scan as soon as the server is configured.
34
+ // We don't await it here, allowing Vite to continue its startup.
35
+ scanPromise = runDirectivesScan({
36
+ rootConfig: server.config,
37
+ environment: server.environments.worker,
38
+ clientFiles,
39
+ serverFiles,
40
+ }).then(() => {
41
+ // After the scan is complete, write the barrel files.
42
+ const clientBarrelContent = generateBarrelContent(clientFiles, projectRootDir);
43
+ writeFileSync(CLIENT_BARREL_PATH, clientBarrelContent);
44
+ const serverBarrelContent = generateBarrelContent(serverFiles, projectRootDir);
45
+ writeFileSync(SERVER_BARREL_PATH, serverBarrelContent);
46
+ });
47
+ // context(justinvdm, 4 Sep 2025): Add middleware to block incoming
48
+ // requests until the scan is complete. This gives us a single hook for
49
+ // preventing app code being processed by vite until the scan is complete.
50
+ // This improves perceived startup time by not blocking Vite's optimizer.
51
+ server.middlewares.use(async (_req, _res, next) => {
52
+ if (scanPromise) {
53
+ await scanPromise;
54
+ }
55
+ next();
56
+ });
57
+ },
58
+ configResolved(config) {
59
+ if (config.command !== "serve") {
60
+ return;
61
+ }
62
+ // Create dummy files to give esbuild a real path to resolve.
63
+ mkdirSync(path.dirname(CLIENT_BARREL_PATH), { recursive: true });
64
+ writeFileSync(CLIENT_BARREL_PATH, "");
65
+ mkdirSync(path.dirname(SERVER_BARREL_PATH), { recursive: true });
66
+ writeFileSync(SERVER_BARREL_PATH, "");
67
+ for (const [envName, env] of Object.entries(config.environments || {})) {
68
+ env.optimizeDeps ??= {};
69
+ env.optimizeDeps.include ??= [];
70
+ env.optimizeDeps.include.push(CLIENT_BARREL_EXPORT_PATH, SERVER_BARREL_EXPORT_PATH);
71
+ env.optimizeDeps.esbuildOptions ??= {};
72
+ env.optimizeDeps.esbuildOptions.plugins ??= [];
73
+ env.optimizeDeps.esbuildOptions.plugins.push({
74
+ name: "rwsdk:block-optimizer-for-scan",
75
+ setup(build) {
76
+ build.onStart(async () => {
77
+ // context(justinvdm, 4 Sep 2025): We await the scan promise
78
+ // here because we want to block the optimizer until the scan is
79
+ // complete.
80
+ await scanPromise;
81
+ });
82
+ },
83
+ });
84
+ }
85
+ },
86
+ };
87
+ };
@@ -0,0 +1,6 @@
1
+ import { Plugin } from "vite";
2
+ export declare const directivesFilteringPlugin: ({ clientFiles, serverFiles, projectRootDir, }: {
3
+ clientFiles: Set<string>;
4
+ serverFiles: Set<string>;
5
+ projectRootDir: string;
6
+ }) => Plugin;
@@ -0,0 +1,31 @@
1
+ import debug from "debug";
2
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
3
+ const log = debug("rwsdk:vite:directives-filtering-plugin");
4
+ export const directivesFilteringPlugin = ({ clientFiles, serverFiles, projectRootDir, }) => {
5
+ return {
6
+ name: "rwsdk:directives-filtering",
7
+ enforce: "post",
8
+ async buildEnd() {
9
+ if (this.environment.name !== "worker" ||
10
+ process.env.RWSDK_BUILD_PASS !== "worker") {
11
+ return;
12
+ }
13
+ 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?.isIncluded) {
23
+ files.delete(id);
24
+ }
25
+ }
26
+ });
27
+ process.env.VERBOSE &&
28
+ log("Client/server files after filtering: client=%O, server=%O", Array.from(clientFiles), Array.from(serverFiles));
29
+ },
30
+ };
31
+ };
@@ -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,46 @@ 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
+ // Skip during directive scanning to avoid performance issues
56
+ if (process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE) {
57
+ return;
58
+ }
59
+ if (isBuild &&
60
+ this.environment?.name === "worker" &&
61
+ process.env.RWSDK_BUILD_PASS !== "worker") {
62
+ return;
63
+ }
81
64
  const normalizedId = normalizeModulePath(id, projectRootDir);
82
65
  const clientResult = await transformClientComponents(code, normalizedId, {
83
66
  environmentName: this.environment.name,
84
67
  clientFiles,
85
- addClientModule,
86
68
  });
87
69
  if (clientResult) {
88
- log("Client component transformation successful for id=%s", id);
70
+ process.env.VERBOSE &&
71
+ log("Client component transformation successful for id=%s", id);
89
72
  return {
90
73
  code: clientResult.code,
91
74
  map: clientResult.map,
92
75
  };
93
76
  }
94
- const serverResult = transformServerFunctions(code, normalizedId, this.environment.name, serverFiles, addServerModule);
77
+ const serverResult = transformServerFunctions(code, normalizedId, this.environment.name, serverFiles);
95
78
  if (serverResult) {
96
- log("Server function transformation successful for id=%s", id);
79
+ process.env.VERBOSE &&
80
+ log("Server function transformation successful for id=%s", id);
97
81
  return {
98
82
  code: serverResult.code,
99
83
  map: serverResult.map,
100
84
  };
101
85
  }
102
- process.env.VERBOSE && log("No transformation applied for id=%s", id);
86
+ // Removed: too noisy even in verbose mode
103
87
  },
104
88
  configEnvironment(env, config) {
105
- log("Configuring environment: env=%s", env);
89
+ if (isBuild &&
90
+ env === "worker" &&
91
+ process.env.RWSDK_BUILD_PASS !== "worker") {
92
+ return;
93
+ }
94
+ process.env.VERBOSE && log("Configuring environment: env=%s", env);
106
95
  config.optimizeDeps ??= {};
107
96
  config.optimizeDeps.esbuildOptions ??= {};
108
97
  config.optimizeDeps.esbuildOptions.plugins ??= [];
@@ -120,7 +109,6 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
120
109
  // dependency discovery, so we can skip transform work
121
110
  // and use heuristics instead - see below inside if block
122
111
  if (!args.path.includes("node_modules")) {
123
- log("Esbuild onLoad found app code, path=%s", args.path);
124
112
  if (clientFiles.has(normalizedPath)) {
125
113
  // context(justinvdm,2025-06-15): If this is a client file:
126
114
  // * for ssr and client envs we can skip so esbuild looks at the
@@ -171,10 +159,10 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
171
159
  environmentName: env,
172
160
  clientFiles,
173
161
  isEsbuild: true,
174
- addClientModule,
175
162
  });
176
163
  if (clientResult) {
177
- log("Esbuild client component transformation successful for environment=%s, path=%s", env, args.path);
164
+ process.env.VERBOSE &&
165
+ log("Esbuild client component transformation successful for environment=%s, path=%s", env, args.path);
178
166
  process.env.VERBOSE &&
179
167
  log("Esbuild client component transformation for environment=%s, path=%s, code: %j", env, args.path, clientResult.code);
180
168
  return {
@@ -182,9 +170,10 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
182
170
  loader: getLoader(args.path),
183
171
  };
184
172
  }
185
- const serverResult = transformServerFunctions(code, normalizedPath, env, serverFiles, addServerModule);
173
+ const serverResult = transformServerFunctions(code, normalizedPath, env, serverFiles);
186
174
  if (serverResult) {
187
- log("Esbuild server function transformation successful for environment=%s, path=%s", env, args.path);
175
+ process.env.VERBOSE &&
176
+ log("Esbuild server function transformation successful for environment=%s, path=%s", env, args.path);
188
177
  return {
189
178
  contents: serverResult.code,
190
179
  loader: getLoader(args.path),
@@ -195,7 +184,8 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
195
184
  });
196
185
  },
197
186
  });
198
- log("Environment configuration complete for env=%s", env);
187
+ process.env.VERBOSE &&
188
+ log("Environment configuration complete for env=%s", env);
199
189
  },
200
190
  };
201
191
  };
@@ -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
+ }
@@ -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,18 @@
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) {
7
+ // Skip during directive scanning to avoid performance issues
8
+ if (process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE) {
9
+ return;
10
+ }
6
11
  if (this.environment.name !== "client") {
7
12
  return;
8
13
  }
9
- if (!clientEntryPathnames.includes(id)) {
14
+ const normalizedId = normalizeModulePath(id, projectRootDir);
15
+ if (!clientEntryPoints.has(normalizedId)) {
10
16
  return;
11
17
  }
12
18
  // 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;