rwsdk 0.1.13 → 0.1.15-test.20250714213423
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.
- package/dist/runtime/imports/client.js +1 -1
- package/dist/runtime/imports/ssr.js +1 -1
- package/dist/runtime/imports/worker.js +1 -1
- package/dist/runtime/register/ssr.js +1 -1
- package/dist/vite/configPlugin.d.mts +1 -2
- package/dist/vite/configPlugin.mjs +10 -3
- package/dist/vite/createDirectiveLookupPlugin.mjs +27 -26
- package/dist/vite/directivesPlugin.mjs +11 -7
- package/dist/vite/findSpecifiers.mjs +2 -2
- package/dist/vite/findSsrSpecifiers.mjs +17 -1
- package/dist/vite/injectVitePreamblePlugin.d.mts +1 -2
- package/dist/vite/injectVitePreamblePlugin.mjs +2 -2
- package/dist/vite/invalidateModule.d.mts +6 -2
- package/dist/vite/invalidateModule.mjs +23 -7
- package/dist/vite/isJsFile.d.mts +1 -0
- package/dist/vite/isJsFile.d.ts +1 -0
- package/dist/vite/isJsFile.js +3 -0
- package/dist/vite/isJsFile.mjs +3 -0
- package/dist/vite/miniflareHMRPlugin.d.mts +2 -0
- package/dist/vite/miniflareHMRPlugin.mjs +72 -73
- package/dist/vite/mode.d.mts +5 -0
- package/dist/vite/mode.mjs +25 -0
- package/dist/vite/modePlugin.d.mts +2 -0
- package/dist/vite/modePlugin.mjs +10 -0
- package/dist/vite/reactConditionsResolverPlugin.mjs +21 -12
- package/dist/vite/redwoodPlugin.d.mts +0 -1
- package/dist/vite/redwoodPlugin.mjs +3 -4
- package/dist/vite/ssrBridgePlugin.mjs +26 -14
- package/dist/vite/transformClientComponents.mjs +9 -8
- package/dist/vite/transformServerFunctions.mjs +18 -12
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ export const loadModule = memoize(async (id) => {
|
|
|
5
5
|
return await import(/* @vite-ignore */ id);
|
|
6
6
|
}
|
|
7
7
|
else {
|
|
8
|
-
const { useClientLookup } = await import("virtual:use-client-lookup");
|
|
8
|
+
const { useClientLookup } = await import("virtual:use-client-lookup.js");
|
|
9
9
|
const moduleFn = useClientLookup[id];
|
|
10
10
|
if (!moduleFn) {
|
|
11
11
|
throw new Error(`(client) No module found for '${id}' in module lookup for "use client" directive`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import memoize from "lodash/memoize";
|
|
2
2
|
export const ssrLoadModule = memoize(async (id) => {
|
|
3
|
-
const { useClientLookup } = await import("virtual:use-client-lookup");
|
|
3
|
+
const { useClientLookup } = await import("virtual:use-client-lookup.js");
|
|
4
4
|
const moduleFn = useClientLookup[id];
|
|
5
5
|
if (!moduleFn) {
|
|
6
6
|
throw new Error(`(ssr) No module found for '${id}' in module lookup for "use client" directive`);
|
|
@@ -2,7 +2,7 @@ import memoize from "lodash/memoize";
|
|
|
2
2
|
import { requestInfo } from "../requestInfo/worker";
|
|
3
3
|
import { ssrWebpackRequire as baseSsrWebpackRequire } from "rwsdk/__ssr_bridge";
|
|
4
4
|
export const loadServerModule = memoize(async (id) => {
|
|
5
|
-
const { useServerLookup } = await import("virtual:use-server-lookup");
|
|
5
|
+
const { useServerLookup } = await import("virtual:use-server-lookup.js");
|
|
6
6
|
const moduleFn = useServerLookup[id];
|
|
7
7
|
if (!moduleFn) {
|
|
8
8
|
throw new Error(`(worker) No module found for '${id}' in module lookup for "use server" directive`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import memoize from "lodash/memoize";
|
|
2
2
|
import { createServerReference as baseCreateServerReference } from "react-server-dom-webpack/client.edge";
|
|
3
3
|
export const loadServerModule = memoize(async (id) => {
|
|
4
|
-
const { useServerLookup } = await import("virtual:use-server-lookup");
|
|
4
|
+
const { useServerLookup } = await import("virtual:use-server-lookup.js");
|
|
5
5
|
const moduleFn = useServerLookup[id];
|
|
6
6
|
if (!moduleFn) {
|
|
7
7
|
throw new Error(`(worker) No module found for '${id}' in module lookup for "use server" directive`);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
2
|
export declare const cloudflareBuiltInModules: string[];
|
|
3
3
|
export declare const externalModules: string[];
|
|
4
|
-
export declare const configPlugin: ({
|
|
5
|
-
mode: "development" | "production";
|
|
4
|
+
export declare const configPlugin: ({ silent, projectRootDir, clientEntryPathnames, workerEntryPathname, }: {
|
|
6
5
|
silent: boolean;
|
|
7
6
|
projectRootDir: string;
|
|
8
7
|
clientEntryPathnames: string[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path, { resolve } from "node:path";
|
|
2
|
+
import { builtinModules } from "node:module";
|
|
2
3
|
import enhancedResolve from "enhanced-resolve";
|
|
3
4
|
import { SSR_BRIDGE_PATH } from "../lib/constants.mjs";
|
|
4
|
-
import { builtinModules } from "node:module";
|
|
5
5
|
// port(justinvdm, 09 Jun 2025):
|
|
6
6
|
// https://github.com/cloudflare/workers-sdk/blob/d533f5ee7da69c205d8d5e2a5f264d2370fc612b/packages/vite-plugin-cloudflare/src/cloudflare-environment.ts#L123-L128
|
|
7
7
|
export const cloudflareBuiltInModules = [
|
|
@@ -15,9 +15,10 @@ export const externalModules = [
|
|
|
15
15
|
...builtinModules,
|
|
16
16
|
...builtinModules.map((m) => `node:${m}`),
|
|
17
17
|
];
|
|
18
|
-
export const configPlugin = ({
|
|
18
|
+
export const configPlugin = ({ silent, projectRootDir, clientEntryPathnames, workerEntryPathname, }) => ({
|
|
19
19
|
name: "rwsdk:config",
|
|
20
|
-
config: (_
|
|
20
|
+
config: async (_) => {
|
|
21
|
+
const mode = process.env.NODE_ENV;
|
|
21
22
|
const baseConfig = {
|
|
22
23
|
appType: "custom",
|
|
23
24
|
mode,
|
|
@@ -78,6 +79,9 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
|
|
|
78
79
|
jsx: "automatic",
|
|
79
80
|
jsxImportSource: "react",
|
|
80
81
|
plugins: [],
|
|
82
|
+
define: {
|
|
83
|
+
"process.env.NODE_ENV": JSON.stringify(mode),
|
|
84
|
+
},
|
|
81
85
|
},
|
|
82
86
|
},
|
|
83
87
|
build: {
|
|
@@ -121,6 +125,9 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
|
|
|
121
125
|
esbuildOptions: {
|
|
122
126
|
jsx: "automatic",
|
|
123
127
|
jsxImportSource: "react",
|
|
128
|
+
define: {
|
|
129
|
+
"process.env.NODE_ENV": JSON.stringify(mode),
|
|
130
|
+
},
|
|
124
131
|
},
|
|
125
132
|
},
|
|
126
133
|
build: {
|
|
@@ -9,7 +9,6 @@ import { getSrcPaths } from "../lib/getSrcPaths.js";
|
|
|
9
9
|
import { hasDirective } from "./hasDirective.mjs";
|
|
10
10
|
export const findFilesContainingDirective = async ({ projectRootDir, files, directive, debugNamespace, }) => {
|
|
11
11
|
const log = debug(debugNamespace);
|
|
12
|
-
const verboseLog = debug(`verbose:${debugNamespace}`);
|
|
13
12
|
log("Starting search for '%s' files in projectRootDir=%s", directive, projectRootDir);
|
|
14
13
|
const filesToScan = await getSrcPaths(projectRootDir);
|
|
15
14
|
log("Found %d files to scan for '%s' directive", filesToScan.length, directive);
|
|
@@ -17,10 +16,10 @@ export const findFilesContainingDirective = async ({ projectRootDir, files, dire
|
|
|
17
16
|
try {
|
|
18
17
|
const stats = await stat(file);
|
|
19
18
|
if (!stats.isFile()) {
|
|
20
|
-
|
|
19
|
+
process.env.VERBOSE && log("Skipping %s (not a file)", file);
|
|
21
20
|
continue;
|
|
22
21
|
}
|
|
23
|
-
|
|
22
|
+
process.env.VERBOSE && log("Scanning file: %s", file);
|
|
24
23
|
const content = await readFile(file, "utf-8");
|
|
25
24
|
if (hasDirective(content, directive)) {
|
|
26
25
|
const normalizedPath = normalizeModulePath(projectRootDir, file);
|
|
@@ -33,11 +32,11 @@ export const findFilesContainingDirective = async ({ projectRootDir, files, dire
|
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
log("Completed scan. Found %d %s files total", files.size, directive);
|
|
36
|
-
|
|
35
|
+
process.env.VERBOSE &&
|
|
36
|
+
log("Found files for %s: %j", directive, Array.from(files));
|
|
37
37
|
};
|
|
38
38
|
const resolveOptimizedDep = async (projectRootDir, id, environment, debugNamespace) => {
|
|
39
39
|
const log = debug(debugNamespace);
|
|
40
|
-
const verboseLog = debug(`verbose:${debugNamespace}`);
|
|
41
40
|
try {
|
|
42
41
|
const depsDir = environment === "client" ? "deps" : `deps_${environment}`;
|
|
43
42
|
const nodeModulesDepsDirPath = path.join("node_modules", ".vite", depsDir);
|
|
@@ -57,25 +56,26 @@ const resolveOptimizedDep = async (projectRootDir, id, environment, debugNamespa
|
|
|
57
56
|
log("Found optimized dependency: filePath=%s, optimizedPath=%s", id, optimizedPath);
|
|
58
57
|
return optimizedPath;
|
|
59
58
|
}
|
|
60
|
-
|
|
59
|
+
process.env.VERBOSE &&
|
|
60
|
+
log("File not found in optimized dependencies: id=%s", id);
|
|
61
61
|
return undefined;
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
|
-
|
|
64
|
+
process.env.VERBOSE &&
|
|
65
|
+
log("Error resolving optimized dependency for id=%s: %s", id, error);
|
|
65
66
|
return undefined;
|
|
66
67
|
}
|
|
67
68
|
};
|
|
68
69
|
const addOptimizedDepsEntries = async ({ projectRootDir, directive, environment, debugNamespace, files, }) => {
|
|
69
70
|
const log = debug(debugNamespace);
|
|
70
|
-
const verboseLog = debug(`verbose:${debugNamespace}`);
|
|
71
71
|
try {
|
|
72
72
|
const depsDir = environment === "client" ? "deps" : `deps_${environment}`;
|
|
73
73
|
const depsDirPath = path.join(projectRootDir, "node_modules", ".vite", depsDir);
|
|
74
74
|
const manifestPath = path.join(depsDirPath, "_metadata.json");
|
|
75
|
-
|
|
75
|
+
process.env.VERBOSE && log("Checking for manifest at: %s", manifestPath);
|
|
76
76
|
const manifestExists = await pathExists(manifestPath);
|
|
77
77
|
if (!manifestExists) {
|
|
78
|
-
|
|
78
|
+
process.env.VERBOSE && log("Manifest not found at %s", manifestPath);
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
const manifestContent = await readFile(manifestPath, "utf-8");
|
|
@@ -89,7 +89,8 @@ const addOptimizedDepsEntries = async ({ projectRootDir, directive, environment,
|
|
|
89
89
|
contents = await readFile(resolvedSrcPath, "utf-8");
|
|
90
90
|
}
|
|
91
91
|
catch (error) {
|
|
92
|
-
|
|
92
|
+
process.env.VERBOSE &&
|
|
93
|
+
log("Error reading file %s: %s", resolvedSrcPath, error);
|
|
93
94
|
continue;
|
|
94
95
|
}
|
|
95
96
|
if (hasDirective(contents, directive)) {
|
|
@@ -103,13 +104,13 @@ const addOptimizedDepsEntries = async ({ projectRootDir, directive, environment,
|
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
catch (error) {
|
|
106
|
-
|
|
107
|
+
process.env.VERBOSE &&
|
|
108
|
+
log("Error adding optimized deps entries: %s", error);
|
|
107
109
|
}
|
|
108
110
|
};
|
|
109
111
|
export const createDirectiveLookupPlugin = async ({ projectRootDir, files, config, }) => {
|
|
110
112
|
const debugNamespace = `rwsdk:vite:${config.pluginName}`;
|
|
111
113
|
const log = debug(debugNamespace);
|
|
112
|
-
const verboseLog = debug(`verbose:${debugNamespace}`);
|
|
113
114
|
let isDev = false;
|
|
114
115
|
log("Initializing %s plugin with projectRootDir=%s", config.pluginName, projectRootDir);
|
|
115
116
|
await findFilesContainingDirective({
|
|
@@ -149,11 +150,12 @@ export const createDirectiveLookupPlugin = async ({ projectRootDir, files, confi
|
|
|
149
150
|
const escapedVirtualModuleName = config.virtualModuleName.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
150
151
|
const escapedPrefixedModuleName = `/@id/${config.virtualModuleName}`.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
151
152
|
build.onResolve({
|
|
152
|
-
filter: new RegExp(`^(${escapedVirtualModuleName}|${escapedPrefixedModuleName})$`),
|
|
153
|
+
filter: new RegExp(`^(${escapedVirtualModuleName}|${escapedPrefixedModuleName})\.js$`),
|
|
153
154
|
}, () => {
|
|
154
|
-
|
|
155
|
+
process.env.VERBOSE &&
|
|
156
|
+
log("Esbuild onResolve: marking %s as external", config.virtualModuleName);
|
|
155
157
|
return {
|
|
156
|
-
path: config.virtualModuleName
|
|
158
|
+
path: `${config.virtualModuleName}.js`,
|
|
157
159
|
external: true,
|
|
158
160
|
};
|
|
159
161
|
});
|
|
@@ -166,7 +168,8 @@ export const createDirectiveLookupPlugin = async ({ projectRootDir, files, confi
|
|
|
166
168
|
viteConfig.optimizeDeps.include ??= [];
|
|
167
169
|
for (const file of files) {
|
|
168
170
|
const actualFilePath = path.join(projectRootDir, file);
|
|
169
|
-
|
|
171
|
+
process.env.VERBOSE &&
|
|
172
|
+
log("Adding to optimizeDeps.entries: %s", actualFilePath);
|
|
170
173
|
const entries = Array.isArray(viteConfig.optimizeDeps.entries)
|
|
171
174
|
? viteConfig.optimizeDeps.entries
|
|
172
175
|
: [].concat(viteConfig.optimizeDeps.entries ?? []);
|
|
@@ -180,19 +183,17 @@ export const createDirectiveLookupPlugin = async ({ projectRootDir, files, confi
|
|
|
180
183
|
}
|
|
181
184
|
},
|
|
182
185
|
resolveId(source) {
|
|
183
|
-
|
|
184
|
-
if (source === config.virtualModuleName
|
|
185
|
-
source === `/@id/${config.virtualModuleName}` ||
|
|
186
|
-
source === `/@id/${config.virtualModuleName}.js`) {
|
|
186
|
+
process.env.VERBOSE && log("Resolving id=%s", source);
|
|
187
|
+
if (source === `${config.virtualModuleName}.js`) {
|
|
187
188
|
log("Resolving %s module", config.virtualModuleName);
|
|
188
189
|
// context(justinvdm, 16 Jun 2025): Include .js extension
|
|
189
190
|
// so it goes through vite processing chain
|
|
190
|
-
return
|
|
191
|
+
return source;
|
|
191
192
|
}
|
|
192
|
-
|
|
193
|
+
process.env.VERBOSE && log("No resolution for id=%s", source);
|
|
193
194
|
},
|
|
194
195
|
async load(id) {
|
|
195
|
-
|
|
196
|
+
process.env.VERBOSE && log("Loading id=%s", id);
|
|
196
197
|
if (id === config.virtualModuleName + ".js") {
|
|
197
198
|
log("Loading %s module with %d files", config.virtualModuleName, files.size);
|
|
198
199
|
const environment = this.environment?.name || "client";
|
|
@@ -218,13 +219,13 @@ export const ${config.exportName} = {
|
|
|
218
219
|
const code = s.toString();
|
|
219
220
|
const map = s.generateMap();
|
|
220
221
|
log("Generated virtual module code length: %d", code.length);
|
|
221
|
-
|
|
222
|
+
process.env.VERBOSE && log("Generated virtual module code: %s", code);
|
|
222
223
|
return {
|
|
223
224
|
code,
|
|
224
225
|
map,
|
|
225
226
|
};
|
|
226
227
|
}
|
|
227
|
-
|
|
228
|
+
process.env.VERBOSE && log("No load handling for id=%s", id);
|
|
228
229
|
},
|
|
229
230
|
};
|
|
230
231
|
};
|
|
@@ -5,7 +5,6 @@ import { transformClientComponents } from "./transformClientComponents.mjs";
|
|
|
5
5
|
import { transformServerFunctions } from "./transformServerFunctions.mjs";
|
|
6
6
|
import { normalizeModulePath } from "./normalizeModulePath.mjs";
|
|
7
7
|
const log = debug("rwsdk:vite:rsc-directives-plugin");
|
|
8
|
-
const verboseLog = debug("verbose:rwsdk:vite:rsc-directives-plugin");
|
|
9
8
|
const getLoader = (filePath) => {
|
|
10
9
|
const ext = path.extname(filePath).slice(1);
|
|
11
10
|
switch (ext) {
|
|
@@ -76,7 +75,8 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
|
|
|
76
75
|
});
|
|
77
76
|
},
|
|
78
77
|
async transform(code, id) {
|
|
79
|
-
|
|
78
|
+
process.env.VERBOSE &&
|
|
79
|
+
log("Transform called for id=%s, environment=%s", id, this.environment.name);
|
|
80
80
|
const normalizedId = normalizeModulePath(projectRootDir, id);
|
|
81
81
|
const clientResult = await transformClientComponents(code, normalizedId, {
|
|
82
82
|
environmentName: this.environment.name,
|
|
@@ -98,7 +98,7 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
|
|
|
98
98
|
map: serverResult.map,
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
|
-
|
|
101
|
+
process.env.VERBOSE && log("No transformation applied for id=%s", id);
|
|
102
102
|
},
|
|
103
103
|
configEnvironment(env, config) {
|
|
104
104
|
log("Configuring environment: env=%s", env);
|
|
@@ -110,7 +110,8 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
|
|
|
110
110
|
setup(build) {
|
|
111
111
|
log("Setting up esbuild plugin for environment: %s", env);
|
|
112
112
|
build.onLoad({ filter: /\.(js|ts|jsx|tsx|mts|mjs|cjs)$/ }, async (args) => {
|
|
113
|
-
|
|
113
|
+
process.env.VERBOSE &&
|
|
114
|
+
log("Esbuild onLoad called for environment=%s, path=%s", env, args.path);
|
|
114
115
|
const normalizedPath = normalizeModulePath(projectRootDir, args.path);
|
|
115
116
|
// context(justinvdm,2025-06-15): If we're in app code,
|
|
116
117
|
// we will be doing the transform work in the vite plugin hooks,
|
|
@@ -161,7 +162,8 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
|
|
|
161
162
|
code = await fs.readFile(args.path, "utf-8");
|
|
162
163
|
}
|
|
163
164
|
catch {
|
|
164
|
-
|
|
165
|
+
process.env.VERBOSE &&
|
|
166
|
+
log("Failed to read file: %s, environment=%s", args.path, env);
|
|
165
167
|
return undefined;
|
|
166
168
|
}
|
|
167
169
|
const clientResult = await transformClientComponents(code, normalizedPath, {
|
|
@@ -172,7 +174,8 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
|
|
|
172
174
|
});
|
|
173
175
|
if (clientResult) {
|
|
174
176
|
log("Esbuild client component transformation successful for environment=%s, path=%s", env, args.path);
|
|
175
|
-
|
|
177
|
+
process.env.VERBOSE &&
|
|
178
|
+
log("Esbuild client component transformation for environment=%s, path=%s, code: %j", env, args.path, clientResult.code);
|
|
176
179
|
return {
|
|
177
180
|
contents: clientResult.code,
|
|
178
181
|
loader: getLoader(args.path),
|
|
@@ -186,7 +189,8 @@ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, })
|
|
|
186
189
|
loader: getLoader(args.path),
|
|
187
190
|
};
|
|
188
191
|
}
|
|
189
|
-
|
|
192
|
+
process.env.VERBOSE &&
|
|
193
|
+
log("Esbuild no transformation applied for environment=%s, path=%s", env, args.path);
|
|
190
194
|
});
|
|
191
195
|
},
|
|
192
196
|
});
|
|
@@ -52,7 +52,7 @@ export const EXPORT_PATTERNS = [
|
|
|
52
52
|
export function findImportSpecifiers(id, code, ignoredImportPatterns, log) {
|
|
53
53
|
const ext = path.extname(id).toLowerCase();
|
|
54
54
|
const lang = ext === ".tsx" || ext === ".jsx" ? Lang.Tsx : SgLang.TypeScript;
|
|
55
|
-
const logger = log ?? (() => { });
|
|
55
|
+
const logger = process.env.VERBOSE ? (log ?? (() => { })) : () => { };
|
|
56
56
|
const results = [];
|
|
57
57
|
try {
|
|
58
58
|
// sgParse and lang must be provided by the consumer
|
|
@@ -101,7 +101,7 @@ export function findImportSpecifiers(id, code, ignoredImportPatterns, log) {
|
|
|
101
101
|
export function findExports(id, code, log) {
|
|
102
102
|
const ext = path.extname(id).toLowerCase();
|
|
103
103
|
const lang = ext === ".tsx" || ext === ".jsx" ? Lang.Tsx : SgLang.TypeScript;
|
|
104
|
-
const logger = log ?? (() => { });
|
|
104
|
+
const logger = process.env.VERBOSE ? (log ?? (() => { })) : () => { };
|
|
105
105
|
const results = [];
|
|
106
106
|
const seen = new Set(); // Track seen exports to avoid duplicates
|
|
107
107
|
try {
|
|
@@ -10,7 +10,7 @@ import path from "path";
|
|
|
10
10
|
export function findSsrImportSpecifiers(id, code, log) {
|
|
11
11
|
const ext = path.extname(id).toLowerCase();
|
|
12
12
|
const lang = ext === ".tsx" || ext === ".jsx" ? Lang.Tsx : SgLang.TypeScript;
|
|
13
|
-
const logger = log ?? (() => { });
|
|
13
|
+
const logger = process.env.VERBOSE ? (log ?? (() => { })) : () => { };
|
|
14
14
|
const imports = [];
|
|
15
15
|
const dynamicImports = [];
|
|
16
16
|
try {
|
|
@@ -32,6 +32,22 @@ export function findSsrImportSpecifiers(id, code, log) {
|
|
|
32
32
|
pattern: `__vite_ssr_dynamic_import__('$SPECIFIER')`,
|
|
33
33
|
list: dynamicImports,
|
|
34
34
|
},
|
|
35
|
+
{
|
|
36
|
+
pattern: `__vite_ssr_import__("$SPECIFIER", $$$REST)`,
|
|
37
|
+
list: imports,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
pattern: `__vite_ssr_import__('$SPECIFIER', $$$REST)`,
|
|
41
|
+
list: imports,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
pattern: `__vite_ssr_dynamic_import__("$SPECIFIER", $$$REST)`,
|
|
45
|
+
list: dynamicImports,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
pattern: `__vite_ssr_dynamic_import__('$SPECIFIER', $$$REST)`,
|
|
49
|
+
list: dynamicImports,
|
|
50
|
+
},
|
|
35
51
|
];
|
|
36
52
|
for (const { pattern, list } of patterns) {
|
|
37
53
|
const matches = root.root().findAll(pattern);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type Plugin } from "vite";
|
|
2
|
-
export declare const injectVitePreamble: ({ clientEntryPathnames,
|
|
2
|
+
export declare const injectVitePreamble: ({ clientEntryPathnames, }: {
|
|
3
3
|
clientEntryPathnames: string[];
|
|
4
|
-
mode: "development" | "production";
|
|
5
4
|
}) => Plugin;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import MagicString from "magic-string";
|
|
2
|
-
export const injectVitePreamble = ({ clientEntryPathnames,
|
|
2
|
+
export const injectVitePreamble = ({ clientEntryPathnames, }) => ({
|
|
3
3
|
name: "rwsdk:inject-vite-preamble",
|
|
4
4
|
apply: "serve",
|
|
5
5
|
transform(code, id) {
|
|
@@ -10,7 +10,7 @@ export const injectVitePreamble = ({ clientEntryPathnames, mode, }) => ({
|
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
// Only inject preamble in development mode
|
|
13
|
-
if (
|
|
13
|
+
if (process.env.NODE_ENV !== "development") {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
const s = new MagicString(code);
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import type { ViteDevServer } from "vite";
|
|
2
|
-
|
|
1
|
+
import type { EnvironmentModuleNode, ViteDevServer } from "vite";
|
|
2
|
+
interface InvalidatableModuleOptions {
|
|
3
|
+
invalidateImportersRecursively?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare const invalidateModule: (devServer: ViteDevServer, environment: string, target: string | EnvironmentModuleNode, options?: InvalidatableModuleOptions, seen?: Set<EnvironmentModuleNode>) => void;
|
|
6
|
+
export {};
|
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
2
|
const log = debug("rwsdk:vite:invalidate-module");
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export const invalidateModule = (devServer, environment, target, options = {}, seen = new Set()) => {
|
|
4
|
+
let moduleNode;
|
|
5
|
+
if (typeof target === "string") {
|
|
6
|
+
const id = target;
|
|
7
|
+
const [rawId, _query] = id.split("?");
|
|
8
|
+
moduleNode =
|
|
9
|
+
devServer?.environments[environment]?.moduleGraph.idToModuleMap.get(rawId);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
moduleNode = target;
|
|
13
|
+
}
|
|
8
14
|
if (moduleNode) {
|
|
9
|
-
|
|
15
|
+
if (seen.has(moduleNode)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
seen.add(moduleNode);
|
|
19
|
+
devServer.environments[environment]?.moduleGraph.invalidateModule(moduleNode, seen);
|
|
20
|
+
log("Invalidating module: id=%s, environment=%s", moduleNode.id, environment);
|
|
21
|
+
if (options.invalidateImportersRecursively) {
|
|
22
|
+
for (const importer of moduleNode.importers) {
|
|
23
|
+
invalidateModule(devServer, environment, importer, options, seen);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
10
26
|
}
|
|
11
27
|
else {
|
|
12
|
-
|
|
28
|
+
log("Module not found: id=%s, environment=%s", typeof target === "string" ? target : target.id, environment);
|
|
13
29
|
}
|
|
14
30
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isJsFile(filepath: string): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isJsFile(filepath: string): boolean;
|
|
@@ -4,13 +4,23 @@ import { readFile } from "node:fs/promises";
|
|
|
4
4
|
import debug from "debug";
|
|
5
5
|
import { VIRTUAL_SSR_PREFIX } from "./ssrBridgePlugin.mjs";
|
|
6
6
|
import { normalizeModulePath } from "./normalizeModulePath.mjs";
|
|
7
|
+
import { hasDirective as sourceHasDirective } from "./hasDirective.mjs";
|
|
8
|
+
import { isJsFile } from "./isJsFile.mjs";
|
|
9
|
+
import { invalidateModule } from "./invalidateModule.mjs";
|
|
7
10
|
import { getShortName } from "../lib/getShortName.mjs";
|
|
8
|
-
|
|
9
|
-
const
|
|
11
|
+
const log = debug("rwsdk:vite:hmr-plugin");
|
|
12
|
+
const hasDirective = async (filepath, directive) => {
|
|
13
|
+
if (!isJsFile(filepath)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const content = await readFile(filepath, "utf-8");
|
|
17
|
+
return sourceHasDirective(content, directive);
|
|
18
|
+
};
|
|
10
19
|
const hasEntryAsAncestor = (module, entryFile, seen = new Set()) => {
|
|
11
20
|
// Prevent infinite recursion
|
|
12
|
-
if (seen.has(module))
|
|
21
|
+
if (seen.has(module)) {
|
|
13
22
|
return false;
|
|
23
|
+
}
|
|
14
24
|
seen.add(module);
|
|
15
25
|
// Check direct importers
|
|
16
26
|
for (const importer of module.importers) {
|
|
@@ -22,92 +32,81 @@ const hasEntryAsAncestor = (module, entryFile, seen = new Set()) => {
|
|
|
22
32
|
}
|
|
23
33
|
return false;
|
|
24
34
|
};
|
|
25
|
-
// Cache for "use client" status results
|
|
26
|
-
const useClientCache = new Map();
|
|
27
|
-
// Function to invalidate cache for a file
|
|
28
|
-
const invalidateUseClientCache = (file) => {
|
|
29
|
-
useClientCache.delete(file);
|
|
30
|
-
};
|
|
31
|
-
const isUseClientModule = async (ctx, file, seen = new Set()) => {
|
|
32
|
-
// Prevent infinite recursion
|
|
33
|
-
if (seen.has(file))
|
|
34
|
-
return false;
|
|
35
|
-
seen.add(file);
|
|
36
|
-
try {
|
|
37
|
-
// Check cache first
|
|
38
|
-
if (useClientCache.has(file)) {
|
|
39
|
-
return useClientCache.get(file);
|
|
40
|
-
}
|
|
41
|
-
// Read and check the file
|
|
42
|
-
const content = (await pathExists(file))
|
|
43
|
-
? await readFile(file, "utf-8")
|
|
44
|
-
: "";
|
|
45
|
-
const hasUseClient = content.includes("'use client'") || content.includes('"use client"');
|
|
46
|
-
if (hasUseClient) {
|
|
47
|
-
useClientCache.set(file, true);
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
// Get the module from the module graph to find importers
|
|
51
|
-
const module = ctx.server.moduleGraph.getModuleById(file);
|
|
52
|
-
if (!module) {
|
|
53
|
-
useClientCache.set(file, false);
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
// Check all importers recursively
|
|
57
|
-
for (const importer of module.importers) {
|
|
58
|
-
if (await isUseClientModule(ctx, importer.url, seen)) {
|
|
59
|
-
useClientCache.set(file, true);
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
useClientCache.set(file, false);
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
useClientCache.set(file, false);
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
35
|
export const miniflareHMRPlugin = (givenOptions) => [
|
|
72
36
|
{
|
|
73
37
|
name: "rwsdk:miniflare-hmr",
|
|
74
38
|
async hotUpdate(ctx) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
39
|
+
const { clientFiles, serverFiles, viteEnvironment: { name: environment }, workerEntryPathname: entry, } = givenOptions;
|
|
40
|
+
if (process.env.VERBOSE) {
|
|
41
|
+
log(`Hot update: (env=${this.environment.name}) ${ctx.file}\nModule graph:\n\n${dumpFullModuleGraph(ctx.server, this.environment.name)}`);
|
|
42
|
+
}
|
|
43
|
+
if (this.environment.name === "ssr") {
|
|
44
|
+
log("SSR update, invalidating recursively", ctx.file);
|
|
45
|
+
invalidateModule(ctx.server, "ssr", ctx.file, {
|
|
46
|
+
invalidateImportersRecursively: true,
|
|
47
|
+
});
|
|
48
|
+
invalidateModule(ctx.server, environment, VIRTUAL_SSR_PREFIX +
|
|
49
|
+
normalizeModulePath(givenOptions.rootDir, ctx.file), { invalidateImportersRecursively: true });
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
78
52
|
if (!["client", environment].includes(this.environment.name)) {
|
|
79
53
|
return [];
|
|
80
54
|
}
|
|
55
|
+
const hasClientDirective = await hasDirective(ctx.file, "use client");
|
|
56
|
+
const hasServerDirective = !hasClientDirective && (await hasDirective(ctx.file, "use server"));
|
|
57
|
+
let clientDirectiveChanged = false;
|
|
58
|
+
let serverDirectiveChanged = false;
|
|
59
|
+
if (!clientFiles.has(ctx.file) && hasClientDirective) {
|
|
60
|
+
clientFiles.add(ctx.file);
|
|
61
|
+
clientDirectiveChanged = true;
|
|
62
|
+
}
|
|
63
|
+
else if (clientFiles.has(ctx.file) && !hasClientDirective) {
|
|
64
|
+
clientFiles.delete(ctx.file);
|
|
65
|
+
clientDirectiveChanged = true;
|
|
66
|
+
}
|
|
67
|
+
if (!serverFiles.has(ctx.file) && hasServerDirective) {
|
|
68
|
+
serverFiles.add(ctx.file);
|
|
69
|
+
serverDirectiveChanged = true;
|
|
70
|
+
}
|
|
71
|
+
else if (serverFiles.has(ctx.file) && !hasServerDirective) {
|
|
72
|
+
serverFiles.delete(ctx.file);
|
|
73
|
+
serverDirectiveChanged = true;
|
|
74
|
+
}
|
|
75
|
+
if (clientDirectiveChanged) {
|
|
76
|
+
["client", "ssr", environment].forEach((environment) => {
|
|
77
|
+
invalidateModule(ctx.server, environment, "virtual:use-client-lookup.js");
|
|
78
|
+
});
|
|
79
|
+
invalidateModule(ctx.server, environment, VIRTUAL_SSR_PREFIX + "/@id/virtual:use-client-lookup.js");
|
|
80
|
+
}
|
|
81
|
+
if (serverDirectiveChanged) {
|
|
82
|
+
["client", "ssr", environment].forEach((environment) => {
|
|
83
|
+
invalidateModule(ctx.server, environment, "virtual:use-server-lookup.js");
|
|
84
|
+
});
|
|
85
|
+
invalidateModule(ctx.server, environment, VIRTUAL_SSR_PREFIX + "/@id/virtual:use-server-lookup.js");
|
|
86
|
+
}
|
|
81
87
|
// todo(justinvdm, 12 Dec 2024): Skip client references
|
|
82
88
|
const modules = Array.from(ctx.server.environments[environment].moduleGraph.getModulesByFile(ctx.file) ?? []);
|
|
83
89
|
const isWorkerUpdate = ctx.file === entry ||
|
|
84
90
|
modules.some((module) => hasEntryAsAncestor(module, entry));
|
|
85
|
-
// The worker doesnt need an update
|
|
86
|
-
// => Short circuit HMR
|
|
87
|
-
if (!isWorkerUpdate) {
|
|
88
|
-
return [];
|
|
89
|
-
}
|
|
90
91
|
// The worker needs an update, but this is the client environment
|
|
91
92
|
// => Notify for HMR update of any css files imported by in worker, that are also in the client module graph
|
|
92
93
|
// Why: There may have been changes to css classes referenced, which might css modules to change
|
|
93
94
|
if (this.environment.name === "client") {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
95
|
+
if (isWorkerUpdate) {
|
|
96
|
+
for (const [_, module] of ctx.server.environments[environment]
|
|
97
|
+
.moduleGraph.idToModuleMap) {
|
|
98
|
+
// todo(justinvdm, 13 Dec 2024): We check+update _all_ css files in worker module graph,
|
|
99
|
+
// but it could just be a subset of css files that are actually affected, depending
|
|
100
|
+
// on the importers and imports of the changed file. We should be smarter about this.
|
|
101
|
+
if (module.file && module.file.endsWith(".css")) {
|
|
102
|
+
const clientModules = ctx.server.environments.client.moduleGraph.getModulesByFile(module.file);
|
|
103
|
+
for (const clientModule of clientModules ?? []) {
|
|
104
|
+
invalidateModule(ctx.server, "client", clientModule);
|
|
105
|
+
}
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
|
-
|
|
108
|
-
return (await isUseClientModule(ctx, ctx.file))
|
|
109
|
-
? [...ctx.modules, ...cssModules]
|
|
110
|
-
: cssModules;
|
|
109
|
+
return ctx.modules;
|
|
111
110
|
}
|
|
112
111
|
// The worker needs an update, and the hot check is for the worker environment
|
|
113
112
|
// => Notify for custom RSC-based HMR update, then short circuit HMR
|
|
@@ -122,12 +121,12 @@ export const miniflareHMRPlugin = (givenOptions) => [
|
|
|
122
121
|
?.values()
|
|
123
122
|
.next().value;
|
|
124
123
|
if (m) {
|
|
125
|
-
ctx.server
|
|
124
|
+
invalidateModule(ctx.server, environment, m);
|
|
126
125
|
}
|
|
127
126
|
const virtualSSRModule = ctx.server.environments[environment].moduleGraph.idToModuleMap.get(VIRTUAL_SSR_PREFIX +
|
|
128
127
|
normalizeModulePath(givenOptions.rootDir, ctx.file));
|
|
129
128
|
if (virtualSSRModule) {
|
|
130
|
-
ctx.server
|
|
129
|
+
invalidateModule(ctx.server, environment, virtualSSRModule);
|
|
131
130
|
}
|
|
132
131
|
ctx.server.environments.client.hot.send({
|
|
133
132
|
type: "custom",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
const log = debug("rwsdk:vite:mode");
|
|
3
|
+
const initialNodeEnv = process.env.NODE_ENV;
|
|
4
|
+
let mode;
|
|
5
|
+
export const resolveMode = (env) => {
|
|
6
|
+
if (mode) {
|
|
7
|
+
return mode;
|
|
8
|
+
}
|
|
9
|
+
if (initialNodeEnv) {
|
|
10
|
+
mode = initialNodeEnv === "development" ? "development" : "production";
|
|
11
|
+
log(`Resolved mode: %s (from initial NODE_ENV=%s)`, mode, initialNodeEnv);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
mode =
|
|
15
|
+
env.command === "serve" || env.isPreview ? "development" : "production";
|
|
16
|
+
log(`Resolved mode: %s (env.command=%s, env.isPreview=%s)`, mode, env.isPreview, env.command);
|
|
17
|
+
}
|
|
18
|
+
return mode;
|
|
19
|
+
};
|
|
20
|
+
export const ensureMode = () => {
|
|
21
|
+
if (mode === undefined) {
|
|
22
|
+
throw new Error("RedwoodSDK: mode is not resolved yet");
|
|
23
|
+
}
|
|
24
|
+
return mode;
|
|
25
|
+
};
|
|
@@ -3,7 +3,6 @@ import { ROOT_DIR } from "../lib/constants.mjs";
|
|
|
3
3
|
import enhancedResolve from "enhanced-resolve";
|
|
4
4
|
import { ensureAliasArray } from "./ensureAliasArray.mjs";
|
|
5
5
|
const log = debug("rwsdk:vite:react-conditions-resolver-plugin");
|
|
6
|
-
const verboseLog = debug("verbose:rwsdk:vite:react-conditions-resolver-plugin");
|
|
7
6
|
export const ENV_REACT_IMPORTS = {
|
|
8
7
|
worker: [
|
|
9
8
|
"react",
|
|
@@ -47,18 +46,22 @@ export const ENV_IMPORT_MAPPINGS = Object.fromEntries(Object.keys(ENV_RESOLVERS)
|
|
|
47
46
|
resolveEnvImportMappings(env),
|
|
48
47
|
]));
|
|
49
48
|
function resolveEnvImportMappings(env) {
|
|
50
|
-
|
|
49
|
+
process.env.VERBOSE &&
|
|
50
|
+
log("Resolving environment import mappings for env=%s", env);
|
|
51
51
|
const mappings = new Map();
|
|
52
52
|
const reactImports = ENV_REACT_IMPORTS[env];
|
|
53
53
|
for (const importRequest of reactImports) {
|
|
54
|
-
|
|
54
|
+
process.env.VERBOSE &&
|
|
55
|
+
log("Resolving import request=%s for env=%s", importRequest, env);
|
|
55
56
|
let resolved = false;
|
|
56
57
|
try {
|
|
57
58
|
resolved = ENV_RESOLVERS[env](ROOT_DIR, importRequest);
|
|
58
|
-
|
|
59
|
+
process.env.VERBOSE &&
|
|
60
|
+
log("Successfully resolved %s to %s for env=%s", importRequest, resolved, env);
|
|
59
61
|
}
|
|
60
62
|
catch {
|
|
61
|
-
|
|
63
|
+
process.env.VERBOSE &&
|
|
64
|
+
log("Failed to resolve %s for env=%s", importRequest, env);
|
|
62
65
|
}
|
|
63
66
|
if (resolved) {
|
|
64
67
|
mappings.set(importRequest, resolved);
|
|
@@ -77,10 +80,12 @@ function createEsbuildResolverPlugin(envName) {
|
|
|
77
80
|
name: `rwsdk:react-conditions-resolver-esbuild-${envName}`,
|
|
78
81
|
setup(build) {
|
|
79
82
|
build.onResolve({ filter: /.*/ }, (args) => {
|
|
80
|
-
|
|
83
|
+
process.env.VERBOSE &&
|
|
84
|
+
log("ESBuild resolving %s for env=%s, args=%O", args.path, envName, args);
|
|
81
85
|
const resolved = mappings.get(args.path);
|
|
82
86
|
if (resolved && args.importer !== "") {
|
|
83
|
-
|
|
87
|
+
process.env.VERBOSE &&
|
|
88
|
+
log("ESBuild resolving %s -> %s for env=%s", args.path, resolved, envName);
|
|
84
89
|
if (args.path === "react-server-dom-webpack/client.edge") {
|
|
85
90
|
return;
|
|
86
91
|
}
|
|
@@ -89,7 +94,8 @@ function createEsbuildResolverPlugin(envName) {
|
|
|
89
94
|
};
|
|
90
95
|
}
|
|
91
96
|
else {
|
|
92
|
-
|
|
97
|
+
process.env.VERBOSE &&
|
|
98
|
+
log("ESBuild no resolution found for %s for env=%s", args.path, envName);
|
|
93
99
|
}
|
|
94
100
|
});
|
|
95
101
|
},
|
|
@@ -124,7 +130,7 @@ export const reactConditionsResolverPlugin = () => {
|
|
|
124
130
|
envConfig.optimizeDeps ??= {};
|
|
125
131
|
envConfig.optimizeDeps.esbuildOptions ??= {};
|
|
126
132
|
envConfig.optimizeDeps.esbuildOptions.define ??= {};
|
|
127
|
-
envConfig.optimizeDeps.esbuildOptions.define["process.env.NODE_ENV"] = JSON.stringify(process.env.NODE_ENV
|
|
133
|
+
envConfig.optimizeDeps.esbuildOptions.define["process.env.NODE_ENV"] = JSON.stringify(process.env.NODE_ENV);
|
|
128
134
|
envConfig.optimizeDeps.esbuildOptions.plugins ??= [];
|
|
129
135
|
envConfig.optimizeDeps.esbuildOptions.plugins.push(esbuildPlugin);
|
|
130
136
|
envConfig.optimizeDeps.include ??= [];
|
|
@@ -152,10 +158,12 @@ export const reactConditionsResolverPlugin = () => {
|
|
|
152
158
|
if (!envName) {
|
|
153
159
|
return;
|
|
154
160
|
}
|
|
155
|
-
|
|
161
|
+
process.env.VERBOSE &&
|
|
162
|
+
log("Resolving id=%s, environment=%s, importer=%s", id, envName, importer);
|
|
156
163
|
const mappings = ENV_IMPORT_MAPPINGS[envName];
|
|
157
164
|
if (!mappings) {
|
|
158
|
-
|
|
165
|
+
process.env.VERBOSE &&
|
|
166
|
+
log("No mappings found for environment: %s", envName);
|
|
159
167
|
return;
|
|
160
168
|
}
|
|
161
169
|
const resolved = mappings.get(id);
|
|
@@ -163,7 +171,8 @@ export const reactConditionsResolverPlugin = () => {
|
|
|
163
171
|
log("Resolved %s -> %s for env=%s", id, resolved, envName);
|
|
164
172
|
return resolved;
|
|
165
173
|
}
|
|
166
|
-
|
|
174
|
+
process.env.VERBOSE &&
|
|
175
|
+
log("No resolution found for id=%s in env=%s", id, envName);
|
|
167
176
|
},
|
|
168
177
|
},
|
|
169
178
|
];
|
|
@@ -30,8 +30,6 @@ const determineWorkerEntryPathname = async (projectRootDir, workerConfigPath, op
|
|
|
30
30
|
};
|
|
31
31
|
export const redwoodPlugin = async (options = {}) => {
|
|
32
32
|
const projectRootDir = process.cwd();
|
|
33
|
-
const mode = options.mode ??
|
|
34
|
-
(process.env.NODE_ENV === "development" ? "development" : "production");
|
|
35
33
|
const workerConfigPath = options.configPath ?? (await findWranglerConfig(projectRootDir));
|
|
36
34
|
const workerEntryPathname = await determineWorkerEntryPathname(projectRootDir, workerConfigPath, options);
|
|
37
35
|
const clientEntryPathnames = (Array.isArray(options.entry?.client)
|
|
@@ -57,7 +55,6 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
57
55
|
return [
|
|
58
56
|
devServerTimingPlugin(),
|
|
59
57
|
configPlugin({
|
|
60
|
-
mode,
|
|
61
58
|
silent: options.silent ?? false,
|
|
62
59
|
projectRootDir,
|
|
63
60
|
clientEntryPathnames,
|
|
@@ -77,6 +74,8 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
77
74
|
})
|
|
78
75
|
: [],
|
|
79
76
|
miniflareHMRPlugin({
|
|
77
|
+
clientFiles,
|
|
78
|
+
serverFiles,
|
|
80
79
|
rootDir: projectRootDir,
|
|
81
80
|
viteEnvironment: { name: "worker" },
|
|
82
81
|
workerEntryPathname,
|
|
@@ -88,7 +87,7 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
88
87
|
serverFiles,
|
|
89
88
|
}),
|
|
90
89
|
vitePreamblePlugin(),
|
|
91
|
-
injectVitePreamble({ clientEntryPathnames
|
|
90
|
+
injectVitePreamble({ clientEntryPathnames }),
|
|
92
91
|
useClientLookupPlugin({
|
|
93
92
|
projectRootDir,
|
|
94
93
|
clientFiles,
|
|
@@ -2,7 +2,6 @@ import debug from "debug";
|
|
|
2
2
|
import { SSR_BRIDGE_PATH } from "../lib/constants.mjs";
|
|
3
3
|
import { findSsrImportSpecifiers } from "./findSsrSpecifiers.mjs";
|
|
4
4
|
const log = debug("rwsdk:vite:ssr-bridge-plugin");
|
|
5
|
-
const verboseLog = debug("verbose:rwsdk:vite:ssr-bridge-plugin");
|
|
6
5
|
export const VIRTUAL_SSR_PREFIX = "virtual:rwsdk:ssr:";
|
|
7
6
|
export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
|
|
8
7
|
log("Initializing SSR bridge plugin with SSR_BRIDGE_PATH=%s", SSR_BRIDGE_PATH);
|
|
@@ -33,7 +32,8 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
|
|
|
33
32
|
setup(build) {
|
|
34
33
|
log("Setting up esbuild plugin to mark rwsdk/__ssr paths as external for worker");
|
|
35
34
|
build.onResolve({ filter: /.*$/ }, (args) => {
|
|
36
|
-
|
|
35
|
+
process.env.VERBOSE &&
|
|
36
|
+
log("Esbuild onResolve called for path=%s, args=%O", args.path, args);
|
|
37
37
|
if (args.path === "rwsdk/__ssr_bridge") {
|
|
38
38
|
log("Marking as external: %s", args.path);
|
|
39
39
|
return {
|
|
@@ -48,7 +48,8 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
async resolveId(id) {
|
|
51
|
-
|
|
51
|
+
process.env.VERBOSE &&
|
|
52
|
+
log("Resolving id=%s, environment=%s, isDev=%s", id, this.environment?.name, isDev);
|
|
52
53
|
if (isDev) {
|
|
53
54
|
// context(justinvdm, 27 May 2025): In dev, we need to dynamically load
|
|
54
55
|
// SSR modules, so we return the virtual id so that the dynamic loading
|
|
@@ -77,10 +78,11 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
|
|
|
77
78
|
return SSR_BRIDGE_PATH;
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
+
process.env.VERBOSE && log("No resolution for id=%s", id);
|
|
81
82
|
},
|
|
82
83
|
async load(id) {
|
|
83
|
-
|
|
84
|
+
process.env.VERBOSE &&
|
|
85
|
+
log("Loading id=%s, isDev=%s, environment=%s", id, isDev, this.environment.name);
|
|
84
86
|
if (id.startsWith(VIRTUAL_SSR_PREFIX) &&
|
|
85
87
|
this.environment.name === "worker") {
|
|
86
88
|
const realId = id.slice(VIRTUAL_SSR_PREFIX.length);
|
|
@@ -88,32 +90,42 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
|
|
|
88
90
|
if (isDev) {
|
|
89
91
|
log("Dev mode: fetching SSR module for realPath=%s", realId);
|
|
90
92
|
const result = await devServer?.environments.ssr.fetchModule(realId);
|
|
91
|
-
|
|
93
|
+
process.env.VERBOSE &&
|
|
94
|
+
log("Fetch module result: id=%s, result=%O", realId, result);
|
|
92
95
|
const code = "code" in result ? result.code : undefined;
|
|
93
96
|
log("Fetched SSR module code length: %d", code?.length || 0);
|
|
94
|
-
const { imports, dynamicImports } = findSsrImportSpecifiers(realId, code || "",
|
|
95
|
-
const allSpecifiers = [
|
|
97
|
+
const { imports, dynamicImports } = findSsrImportSpecifiers(realId, code || "", log);
|
|
98
|
+
const allSpecifiers = [
|
|
99
|
+
...new Set([...imports, ...dynamicImports]),
|
|
100
|
+
].map((id) => id.startsWith("/@id/") ? id.slice("/@id/".length) : id);
|
|
96
101
|
const switchCases = allSpecifiers
|
|
97
|
-
.map((specifier) => ` case "${specifier}":
|
|
102
|
+
.map((specifier) => ` case "${specifier}": import("${VIRTUAL_SSR_PREFIX}${specifier}");`)
|
|
98
103
|
.join("\n");
|
|
99
104
|
const transformedCode = `
|
|
100
105
|
await (async function(__vite_ssr_import__, __vite_ssr_dynamic_import__) {${code}})(
|
|
101
|
-
(
|
|
102
|
-
(
|
|
106
|
+
__ssrImport.bind(null, false),
|
|
107
|
+
__ssrImport.bind(null, true)
|
|
103
108
|
);
|
|
104
109
|
|
|
105
|
-
function
|
|
110
|
+
function __ssrImport(isDynamic, id, ...args) {
|
|
111
|
+
id = id.startsWith('/@id/') ? id.slice('/@id/'.length) : id;
|
|
112
|
+
|
|
106
113
|
switch (id) {
|
|
107
114
|
${switchCases}
|
|
108
115
|
}
|
|
116
|
+
|
|
117
|
+
return isDynamic
|
|
118
|
+
? __vite_ssr_dynamic_import__("/@id/${VIRTUAL_SSR_PREFIX}" + id, ...args)
|
|
119
|
+
: __vite_ssr_import__("/@id/${VIRTUAL_SSR_PREFIX}" + id, ...args);
|
|
109
120
|
}
|
|
110
121
|
`;
|
|
111
122
|
log("Transformed SSR module code length: %d", transformedCode.length);
|
|
112
|
-
|
|
123
|
+
process.env.VERBOSE &&
|
|
124
|
+
log("Transformed SSR module code for realId=%s: %s", realId, transformedCode);
|
|
113
125
|
return transformedCode;
|
|
114
126
|
}
|
|
115
127
|
}
|
|
116
|
-
|
|
128
|
+
process.env.VERBOSE && log("No load handling for id=%s", id);
|
|
117
129
|
},
|
|
118
130
|
};
|
|
119
131
|
return ssrBridgePlugin;
|
|
@@ -4,21 +4,19 @@ import { hasDirective } from "./hasDirective.mjs";
|
|
|
4
4
|
import { findExports } from "./findSpecifiers.mjs";
|
|
5
5
|
const logVite = debug("rwsdk:vite:transform-client-components:vite");
|
|
6
6
|
const logEsbuild = debug("rwsdk:vite:transform-client-components:esbuild");
|
|
7
|
-
const verboseLogVite = debug("verbose:rwsdk:vite:transform-client-components:vite");
|
|
8
|
-
const verboseLogEsbuild = debug("verbose:rwsdk:vite:transform-client-components:esbuild");
|
|
9
7
|
export async function transformClientComponents(code, normalizedId, ctx) {
|
|
10
8
|
const log = ctx.isEsbuild ? logEsbuild : logVite;
|
|
11
|
-
const verboseLog = ctx.isEsbuild ? verboseLogEsbuild : verboseLogVite;
|
|
12
9
|
log("Called transformClientComponents for id: id=%s", normalizedId);
|
|
13
10
|
if (!hasDirective(code, "use client")) {
|
|
14
11
|
log("Skipping: no 'use client' directive in id=%s", normalizedId);
|
|
15
|
-
|
|
12
|
+
process.env.VERBOSE &&
|
|
13
|
+
log(":VERBOSE: Returning code unchanged for id=%s:\n%s", normalizedId, code);
|
|
16
14
|
return;
|
|
17
15
|
}
|
|
18
16
|
log("Processing 'use client' module: id=%s", normalizedId);
|
|
19
17
|
ctx.addClientModule?.(ctx.environmentName, normalizedId);
|
|
20
18
|
// Parse exports using the findExports helper
|
|
21
|
-
const exportInfos = findExports(normalizedId, code,
|
|
19
|
+
const exportInfos = findExports(normalizedId, code, log);
|
|
22
20
|
const processedExports = [];
|
|
23
21
|
let defaultExportInfo;
|
|
24
22
|
// Helper to get the computed local name (with alias suffix if present)
|
|
@@ -62,7 +60,8 @@ export async function transformClientComponents(code, normalizedId, ctx) {
|
|
|
62
60
|
const start = match.index;
|
|
63
61
|
const end = match.index + match[0].length;
|
|
64
62
|
s.remove(start, end);
|
|
65
|
-
|
|
63
|
+
process.env.VERBOSE &&
|
|
64
|
+
log("Removed 'use client' directive from normalizedId=%s", normalizedId);
|
|
66
65
|
break; // Only remove the first one
|
|
67
66
|
}
|
|
68
67
|
const sourceMap = s.generateMap({
|
|
@@ -70,7 +69,8 @@ export async function transformClientComponents(code, normalizedId, ctx) {
|
|
|
70
69
|
includeContent: true,
|
|
71
70
|
hires: true,
|
|
72
71
|
});
|
|
73
|
-
|
|
72
|
+
process.env.VERBOSE &&
|
|
73
|
+
log(":VERBOSE: SSR transformed code for %s:\n%s", normalizedId, s.toString());
|
|
74
74
|
return {
|
|
75
75
|
code: s.toString(),
|
|
76
76
|
map: sourceMap,
|
|
@@ -107,7 +107,8 @@ export async function transformClientComponents(code, normalizedId, ctx) {
|
|
|
107
107
|
hires: true,
|
|
108
108
|
});
|
|
109
109
|
const finalResult = s.toString();
|
|
110
|
-
|
|
110
|
+
process.env.VERBOSE &&
|
|
111
|
+
log(":VERBOSE: Transformed code (env=%s, normalizedId=%s):\n%s", normalizedId, ctx.environmentName, finalResult);
|
|
111
112
|
return {
|
|
112
113
|
code: finalResult,
|
|
113
114
|
map: sourceMap,
|
|
@@ -5,15 +5,14 @@ import { findExports } from "./findSpecifiers.mjs";
|
|
|
5
5
|
import { parse as sgParse, Lang as SgLang, Lang } from "@ast-grep/napi";
|
|
6
6
|
import path from "path";
|
|
7
7
|
const log = debug("rwsdk:vite:transform-server-functions");
|
|
8
|
-
const verboseLog = debug("verbose:rwsdk:vite:transform-server-functions");
|
|
9
8
|
export const findExportedFunctions = (code, normalizedId) => {
|
|
10
9
|
return findExportInfo(code, normalizedId).localFunctions;
|
|
11
10
|
};
|
|
12
11
|
export const findExportInfo = (code, normalizedId) => {
|
|
13
|
-
|
|
12
|
+
process.env.VERBOSE && log("Finding exported functions in source file");
|
|
14
13
|
const localFunctions = new Set();
|
|
15
14
|
const reExports = [];
|
|
16
|
-
const exportInfos = findExports(normalizedId || "file.ts", code,
|
|
15
|
+
const exportInfos = findExports(normalizedId || "file.ts", code, log);
|
|
17
16
|
for (const exportInfo of exportInfos) {
|
|
18
17
|
if (exportInfo.isReExport && exportInfo.moduleSpecifier) {
|
|
19
18
|
// For re-exports, we need to determine the original name by parsing the code
|
|
@@ -29,11 +28,13 @@ export const findExportInfo = (code, normalizedId) => {
|
|
|
29
28
|
originalName: originalName,
|
|
30
29
|
moduleSpecifier: exportInfo.moduleSpecifier,
|
|
31
30
|
});
|
|
32
|
-
|
|
31
|
+
process.env.VERBOSE &&
|
|
32
|
+
log("Found re-exported function: %s (original: %s) from %s", exportInfo.name, originalName, exportInfo.moduleSpecifier);
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
35
|
localFunctions.add(exportInfo.name);
|
|
36
|
-
|
|
36
|
+
process.env.VERBOSE &&
|
|
37
|
+
log("Found exported function: %s", exportInfo.name);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
log("Found %d local functions: %O", localFunctions.size, Array.from(localFunctions));
|
|
@@ -55,7 +56,7 @@ function findDefaultFunctionName(code, normalizedId) {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
catch (err) {
|
|
58
|
-
|
|
59
|
+
process.env.VERBOSE && log("Error finding default function name: %O", err);
|
|
59
60
|
}
|
|
60
61
|
return null;
|
|
61
62
|
}
|
|
@@ -79,15 +80,17 @@ function hasDefaultExport(code, normalizedId) {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
catch (err) {
|
|
82
|
-
|
|
83
|
+
process.env.VERBOSE && log("Error checking for default export: %O", err);
|
|
83
84
|
}
|
|
84
85
|
return false;
|
|
85
86
|
}
|
|
86
87
|
export const transformServerFunctions = (code, normalizedId, environment, serverFiles, addServerModule) => {
|
|
87
|
-
|
|
88
|
+
process.env.VERBOSE &&
|
|
89
|
+
log("Transform server functions called for normalizedId=%s, environment=%s", normalizedId, environment);
|
|
88
90
|
if (!hasDirective(code, "use server")) {
|
|
89
91
|
log("Skipping: no 'use server' directive in id=%s", normalizedId);
|
|
90
|
-
|
|
92
|
+
process.env.VERBOSE &&
|
|
93
|
+
log(":VERBOSE: Returning code unchanged for id=%s:\n%s", normalizedId, code);
|
|
91
94
|
return;
|
|
92
95
|
}
|
|
93
96
|
log("Processing 'use server' module: normalizedId=%s, environment=%s", normalizedId, environment);
|
|
@@ -144,7 +147,8 @@ export const transformServerFunctions = (code, normalizedId, environment, server
|
|
|
144
147
|
const start = match.index;
|
|
145
148
|
const end = match.index + match[0].length;
|
|
146
149
|
s.remove(start, end);
|
|
147
|
-
|
|
150
|
+
process.env.VERBOSE &&
|
|
151
|
+
log("Removed 'use server' directive from normalizedId=%s", normalizedId);
|
|
148
152
|
break; // Only remove the first one
|
|
149
153
|
}
|
|
150
154
|
// Add imports at the very beginning
|
|
@@ -239,7 +243,8 @@ export const transformServerFunctions = (code, normalizedId, environment, server
|
|
|
239
243
|
}
|
|
240
244
|
}
|
|
241
245
|
catch (err) {
|
|
242
|
-
|
|
246
|
+
process.env.VERBOSE &&
|
|
247
|
+
log("Error processing default function: %O", err);
|
|
243
248
|
}
|
|
244
249
|
}
|
|
245
250
|
// Add registration calls at the end
|
|
@@ -286,5 +291,6 @@ export const transformServerFunctions = (code, normalizedId, environment, server
|
|
|
286
291
|
}),
|
|
287
292
|
};
|
|
288
293
|
}
|
|
289
|
-
|
|
294
|
+
process.env.VERBOSE &&
|
|
295
|
+
log("No transformation applied for environment=%s, normalizedId=%s", environment, normalizedId);
|
|
290
296
|
};
|