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.
- package/dist/lib/constants.d.mts +6 -1
- package/dist/lib/constants.mjs +6 -1
- package/dist/lib/smokeTests/browser.mjs +5 -21
- package/dist/lib/smokeTests/codeUpdates.d.mts +1 -1
- package/dist/lib/smokeTests/codeUpdates.mjs +41 -5
- package/dist/lib/smokeTests/development.d.mts +1 -1
- package/dist/lib/smokeTests/development.mjs +4 -10
- package/dist/lib/smokeTests/release.d.mts +1 -1
- package/dist/lib/smokeTests/release.mjs +4 -9
- package/dist/lib/smokeTests/runSmokeTests.mjs +2 -2
- package/dist/lib/smokeTests/templates/SmokeTest.template.js +3 -2
- package/dist/lib/testUtils/stubEnvVars.d.mts +2 -0
- package/dist/lib/testUtils/stubEnvVars.mjs +11 -0
- package/dist/runtime/imports/client.js +4 -9
- package/dist/runtime/imports/worker.js +2 -1
- package/dist/runtime/register/ssr.js +2 -1
- package/dist/runtime/requestInfo/worker.js +9 -1
- package/dist/runtime/worker.d.ts +0 -3
- package/dist/runtime/worker.js +1 -10
- package/dist/scripts/debug-sync.mjs +0 -23
- package/dist/scripts/smoke-test.mjs +0 -10
- package/dist/vite/buildApp.d.mts +15 -0
- package/dist/vite/buildApp.mjs +53 -0
- package/dist/vite/configPlugin.d.mts +4 -4
- package/dist/vite/configPlugin.mjs +70 -76
- package/dist/vite/constants.d.mts +2 -0
- package/dist/vite/constants.mjs +12 -0
- package/dist/vite/createDirectiveLookupPlugin.d.mts +0 -6
- package/dist/vite/createDirectiveLookupPlugin.mjs +69 -145
- package/dist/vite/createViteAwareResolver.d.mts +4 -0
- package/dist/vite/createViteAwareResolver.mjs +208 -0
- package/dist/vite/directiveModulesDevPlugin.d.mts +8 -0
- package/dist/vite/directiveModulesDevPlugin.mjs +87 -0
- package/dist/vite/directivesFilteringPlugin.d.mts +6 -0
- package/dist/vite/directivesFilteringPlugin.mjs +31 -0
- package/dist/vite/directivesPlugin.mjs +32 -42
- package/dist/vite/getViteEsbuild.d.mts +1 -0
- package/dist/vite/getViteEsbuild.mjs +12 -0
- package/dist/vite/injectVitePreamblePlugin.d.mts +3 -2
- package/dist/vite/injectVitePreamblePlugin.mjs +8 -2
- package/dist/vite/linkerPlugin.d.mts +4 -0
- package/dist/vite/linkerPlugin.mjs +41 -0
- package/dist/vite/manifestPlugin.d.mts +2 -2
- package/dist/vite/manifestPlugin.mjs +20 -37
- package/dist/vite/moveStaticAssetsPlugin.mjs +2 -1
- package/dist/vite/prismaPlugin.mjs +1 -1
- package/dist/vite/reactConditionsResolverPlugin.mjs +15 -16
- package/dist/vite/redwoodPlugin.d.mts +0 -1
- package/dist/vite/redwoodPlugin.mjs +27 -9
- package/dist/vite/runDirectivesScan.d.mts +7 -0
- package/dist/vite/runDirectivesScan.mjs +156 -0
- package/dist/vite/ssrBridgePlugin.mjs +21 -14
- package/dist/vite/transformClientComponents.d.mts +0 -1
- package/dist/vite/transformClientComponents.mjs +1 -9
- package/dist/vite/transformJsxScriptTagsPlugin.d.mts +4 -3
- package/dist/vite/transformJsxScriptTagsPlugin.mjs +66 -84
- package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +67 -41
- package/dist/vite/transformServerFunctions.d.mts +1 -1
- package/dist/vite/transformServerFunctions.mjs +11 -12
- package/dist/vite/virtualPlugin.mjs +8 -0
- package/package.json +7 -1
- package/dist/runtime/clientNavigation.d.ts +0 -9
- 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,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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
|
77
|
+
const serverResult = transformServerFunctions(code, normalizedId, this.environment.name, serverFiles);
|
|
95
78
|
if (serverResult) {
|
|
96
|
-
|
|
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
|
-
|
|
86
|
+
// Removed: too noisy even in verbose mode
|
|
103
87
|
},
|
|
104
88
|
configEnvironment(env, config) {
|
|
105
|
-
|
|
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
|
-
|
|
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
|
|
173
|
+
const serverResult = transformServerFunctions(code, normalizedPath, env, serverFiles);
|
|
186
174
|
if (serverResult) {
|
|
187
|
-
|
|
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
|
-
|
|
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: ({
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
+
};
|