rwsdk 1.0.0-alpha.11 → 1.0.0-alpha.12
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/e2e/testHarness.d.mts +6 -0
- package/dist/lib/e2e/testHarness.mjs +19 -0
- package/dist/runtime/register/worker.js +8 -1
- package/dist/vite/directiveModulesDevPlugin.mjs +1 -1
- package/dist/vite/runDirectivesScan.d.mts +2 -1
- package/dist/vite/runDirectivesScan.mjs +39 -7
- package/package.json +2 -1
|
@@ -124,3 +124,9 @@ export declare namespace testDevAndDeploy {
|
|
|
124
124
|
* This should be used before any user interaction is simulated.
|
|
125
125
|
*/
|
|
126
126
|
export declare function waitForHydration(page: Page): Promise<void>;
|
|
127
|
+
export declare function trackPageErrors(page: Page): {
|
|
128
|
+
get: () => {
|
|
129
|
+
consoleErrors: string[];
|
|
130
|
+
failedRequests: string[];
|
|
131
|
+
};
|
|
132
|
+
};
|
|
@@ -416,3 +416,22 @@ export async function waitForHydration(page) {
|
|
|
416
416
|
// This is a pragmatic approach to ensure React has mounted.
|
|
417
417
|
await new Promise((resolve) => setTimeout(resolve, HYDRATION_TIMEOUT));
|
|
418
418
|
}
|
|
419
|
+
export function trackPageErrors(page) {
|
|
420
|
+
const consoleErrors = [];
|
|
421
|
+
const failedRequests = [];
|
|
422
|
+
page.on("requestfailed", (request) => {
|
|
423
|
+
failedRequests.push(`${request.url()} | ${request.failure()?.errorText}`);
|
|
424
|
+
});
|
|
425
|
+
page.on("console", (msg) => {
|
|
426
|
+
if (msg.type() === "error") {
|
|
427
|
+
consoleErrors.push(msg.text());
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
return {
|
|
431
|
+
get: () => ({
|
|
432
|
+
// context(justinvdm, 25 Sep 2025): Filter out irrelevant 404s (e.g. favicon)
|
|
433
|
+
consoleErrors: consoleErrors.filter((e) => !e.includes("404")),
|
|
434
|
+
failedRequests,
|
|
435
|
+
}),
|
|
436
|
+
};
|
|
437
|
+
}
|
|
@@ -9,6 +9,7 @@ export function registerServerReference(action, id, name) {
|
|
|
9
9
|
// Note: We no longer need to register in a Map since we use virtual lookup
|
|
10
10
|
return baseRegisterServerReference(action, id, name);
|
|
11
11
|
}
|
|
12
|
+
const isComponent = (target) => isValidElementType(target) && target?.toString().includes("jsx");
|
|
12
13
|
export function registerClientReference(ssrModule, id, exportName) {
|
|
13
14
|
const target = ssrModule[exportName] ?? {};
|
|
14
15
|
if (isValidElementType(target)) {
|
|
@@ -31,7 +32,13 @@ export function registerClientReference(ssrModule, id, exportName) {
|
|
|
31
32
|
}
|
|
32
33
|
finalDescriptors.$$async = { value: true };
|
|
33
34
|
finalDescriptors.$$isClientReference = { value: true };
|
|
34
|
-
|
|
35
|
+
// context(justinvdm, 25 Sep 2025): We create a wrapper function to avoid
|
|
36
|
+
// getting the SSR component's property descriptors - otherwise
|
|
37
|
+
// this will take precedence over the client reference descriptors
|
|
38
|
+
const fn = typeof target === "function"
|
|
39
|
+
? (...args) => target(...args)
|
|
40
|
+
: () => null;
|
|
41
|
+
return Object.defineProperties(fn, finalDescriptors);
|
|
35
42
|
}
|
|
36
43
|
// For non-components, return the target object directly for use in SSR.
|
|
37
44
|
return target;
|
|
@@ -84,7 +84,7 @@ export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRoo
|
|
|
84
84
|
env.optimizeDeps.include ??= [];
|
|
85
85
|
const entries = (env.optimizeDeps.entries = castArray(env.optimizeDeps.entries ?? []));
|
|
86
86
|
env.optimizeDeps.include.push(VENDOR_CLIENT_BARREL_EXPORT_PATH, VENDOR_SERVER_BARREL_EXPORT_PATH);
|
|
87
|
-
if (envName === "client") {
|
|
87
|
+
if (envName === "client" || envName === "ssr") {
|
|
88
88
|
entries.push(APP_CLIENT_BARREL_PATH);
|
|
89
89
|
}
|
|
90
90
|
else if (envName === "worker") {
|
|
@@ -17,11 +17,12 @@ export declare function classifyModule({ contents, inheritedEnv, }: {
|
|
|
17
17
|
isClient: boolean;
|
|
18
18
|
isServer: boolean;
|
|
19
19
|
};
|
|
20
|
+
export type EsbuildLoader = "js" | "jsx" | "ts" | "tsx" | "default";
|
|
20
21
|
export declare const runDirectivesScan: ({ rootConfig, environments, clientFiles, serverFiles, entries: initialEntries, }: {
|
|
21
22
|
rootConfig: ResolvedConfig;
|
|
22
23
|
environments: Record<string, Environment>;
|
|
23
24
|
clientFiles: Set<string>;
|
|
24
25
|
serverFiles: Set<string>;
|
|
25
|
-
entries
|
|
26
|
+
entries?: string[];
|
|
26
27
|
}) => Promise<void>;
|
|
27
28
|
export {};
|
|
@@ -7,6 +7,7 @@ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
|
|
|
7
7
|
import { INTERMEDIATES_OUTPUT_DIR } from "../lib/constants.mjs";
|
|
8
8
|
import { externalModules } from "./constants.mjs";
|
|
9
9
|
import { createViteAwareResolver } from "./createViteAwareResolver.mjs";
|
|
10
|
+
import { compile } from "@mdx-js/mdx";
|
|
10
11
|
const log = debug("rwsdk:vite:run-directives-scan");
|
|
11
12
|
// Copied from Vite's source code.
|
|
12
13
|
// https://github.com/vitejs/vite/blob/main/packages/vite/src/shared/utils.ts
|
|
@@ -50,7 +51,7 @@ export function classifyModule({ contents, inheritedEnv, }) {
|
|
|
50
51
|
return { moduleEnv, isClient, isServer };
|
|
51
52
|
}
|
|
52
53
|
export const runDirectivesScan = async ({ rootConfig, environments, clientFiles, serverFiles, entries: initialEntries, }) => {
|
|
53
|
-
|
|
54
|
+
deferredLog("\n… (rwsdk) Scanning for 'use client' and 'use server' directives...");
|
|
54
55
|
// Set environment variable to indicate scanning is in progress
|
|
55
56
|
process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE = "true";
|
|
56
57
|
try {
|
|
@@ -94,7 +95,7 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
|
|
|
94
95
|
setup(build) {
|
|
95
96
|
// Match Vite's behavior by externalizing assets and special queries.
|
|
96
97
|
// This prevents esbuild from trying to bundle them, which would fail.
|
|
97
|
-
const scriptFilter = /\.(c|m)?[jt]sx
|
|
98
|
+
const scriptFilter = /\.(c|m)?[jt]sx?$|\.mdx$/;
|
|
98
99
|
const specialQueryFilter = /[?&](?:url|raw|worker|sharedworker|inline)\b/;
|
|
99
100
|
// This regex is used to identify if a path has any file extension.
|
|
100
101
|
const hasExtensionRegex = /\.[^/]+$/;
|
|
@@ -162,7 +163,7 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
|
|
|
162
163
|
log("Marking as external:", args.path, "resolved to:", resolvedPath);
|
|
163
164
|
return { external: true };
|
|
164
165
|
});
|
|
165
|
-
build.onLoad({ filter: /\.(m|c)?[jt]sx
|
|
166
|
+
build.onLoad({ filter: /\.(m|c)?[jt]sx?$|\.mdx$/ }, async (args) => {
|
|
166
167
|
log("onLoad called for:", args.path);
|
|
167
168
|
if (!args.path.startsWith("/") ||
|
|
168
169
|
args.path.includes("virtual:") ||
|
|
@@ -175,10 +176,10 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
|
|
|
175
176
|
return null;
|
|
176
177
|
}
|
|
177
178
|
try {
|
|
178
|
-
const
|
|
179
|
+
const originalContents = await readFileWithCache(args.path);
|
|
179
180
|
const inheritedEnv = args.pluginData?.inheritedEnv || "worker";
|
|
180
181
|
const { moduleEnv, isClient, isServer } = classifyModule({
|
|
181
|
-
contents,
|
|
182
|
+
contents: originalContents,
|
|
182
183
|
inheritedEnv,
|
|
183
184
|
});
|
|
184
185
|
// Store the definitive environment for this module, so it can be used when it becomes an importer.
|
|
@@ -194,7 +195,33 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
|
|
|
194
195
|
log("Discovered 'use server' in:", realPath);
|
|
195
196
|
serverFiles.add(normalizeModulePath(realPath, rootConfig.root));
|
|
196
197
|
}
|
|
197
|
-
|
|
198
|
+
let code;
|
|
199
|
+
let loader;
|
|
200
|
+
if (args.path.endsWith(".mdx")) {
|
|
201
|
+
const result = await compile(originalContents, {
|
|
202
|
+
jsx: true,
|
|
203
|
+
jsxImportSource: "react",
|
|
204
|
+
});
|
|
205
|
+
code = String(result.value);
|
|
206
|
+
loader = "tsx";
|
|
207
|
+
}
|
|
208
|
+
else if (/\.(m|c)?tsx$/.test(args.path)) {
|
|
209
|
+
code = originalContents;
|
|
210
|
+
loader = "tsx";
|
|
211
|
+
}
|
|
212
|
+
else if (/\.(m|c)?ts$/.test(args.path)) {
|
|
213
|
+
code = originalContents;
|
|
214
|
+
loader = "ts";
|
|
215
|
+
}
|
|
216
|
+
else if (/\.(m|c)?jsx$/.test(args.path)) {
|
|
217
|
+
code = originalContents;
|
|
218
|
+
loader = "jsx";
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
code = originalContents;
|
|
222
|
+
loader = "js";
|
|
223
|
+
}
|
|
224
|
+
return { contents: code, loader };
|
|
198
225
|
}
|
|
199
226
|
catch (e) {
|
|
200
227
|
log("Could not read file during scan, skipping:", args.path, e);
|
|
@@ -220,8 +247,13 @@ export const runDirectivesScan = async ({ rootConfig, environments, clientFiles,
|
|
|
220
247
|
finally {
|
|
221
248
|
// Always clear the scanning flag when done
|
|
222
249
|
delete process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE;
|
|
223
|
-
|
|
250
|
+
deferredLog("✔ (rwsdk) Done scanning for 'use client' and 'use server' directives.");
|
|
224
251
|
process.env.VERBOSE &&
|
|
225
252
|
log("Client/server files after scanning: client=%O, server=%O", Array.from(clientFiles), Array.from(serverFiles));
|
|
226
253
|
}
|
|
227
254
|
};
|
|
255
|
+
const deferredLog = (message) => {
|
|
256
|
+
setTimeout(() => {
|
|
257
|
+
console.log(message);
|
|
258
|
+
}, 500);
|
|
259
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rwsdk",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.12",
|
|
4
4
|
"description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -134,6 +134,7 @@
|
|
|
134
134
|
"dependencies": {
|
|
135
135
|
"@ast-grep/napi": "^0.38.5",
|
|
136
136
|
"@cloudflare/workers-types": "^4.20250407.0",
|
|
137
|
+
"@mdx-js/mdx": "^3.1.1",
|
|
137
138
|
"@puppeteer/browsers": "^2.8.0",
|
|
138
139
|
"@types/fs-extra": "^11.0.4",
|
|
139
140
|
"@types/react": "^19.1.2",
|