rwsdk 0.2.0-alpha.5 → 0.2.0-alpha.6

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.
@@ -8,7 +8,7 @@ import puppeteer from "puppeteer-core";
8
8
  import { takeScreenshot } from "./artifacts.mjs";
9
9
  import { RETRIES } from "./constants.mjs";
10
10
  import { $ } from "../$.mjs";
11
- import { fail } from "./utils.mjs";
11
+ import { fail, withRetries } from "./utils.mjs";
12
12
  import { reportSmokeTestResult } from "./reporting.mjs";
13
13
  import { updateTestStatus } from "./state.mjs";
14
14
  import * as fs from "fs/promises";
@@ -404,7 +404,7 @@ export async function checkUrlSmoke(page, url, isRealtime, bail = false, skipCli
404
404
  ? "realtimeClientModuleStyles"
405
405
  : "initialClientModuleStyles";
406
406
  try {
407
- await checkUrlStyles(page, "red");
407
+ await withRetries(() => checkUrlStyles(page, "red"), "URL styles check");
408
408
  updateTestStatus(env, urlStylesKey, "PASSED");
409
409
  log(`${phase} URL styles check passed`);
410
410
  }
@@ -420,7 +420,7 @@ export async function checkUrlSmoke(page, url, isRealtime, bail = false, skipCli
420
420
  }
421
421
  }
422
422
  try {
423
- await checkClientModuleStyles(page, "blue");
423
+ await withRetries(() => checkClientModuleStyles(page, "blue"), "Client module styles check");
424
424
  updateTestStatus(env, clientModuleStylesKey, "PASSED");
425
425
  log(`${phase} client module styles check passed`);
426
426
  }
@@ -507,7 +507,14 @@ export async function checkUrlSmoke(page, url, isRealtime, bail = false, skipCli
507
507
  await testClientComponentHmr(page, targetDir, phase, environment, bail);
508
508
  // Test style HMR if style tests aren't skipped
509
509
  if (!skipStyleTests) {
510
- await testStyleHMR(page, targetDir);
510
+ await withRetries(() => testStyleHMR(page, targetDir), "Style HMR test", async () => {
511
+ // This logic runs before each retry of testStyleHMR
512
+ const urlStylePath = join(targetDir, "src", "app", "smokeTestUrlStyles.css");
513
+ const clientStylePath = join(targetDir, "src", "app", "components", "smokeTestClientStyles.module.css");
514
+ // Restore original styles before re-running HMR test
515
+ await fs.writeFile(urlStylePath, urlStylesTemplate);
516
+ await fs.writeFile(clientStylePath, clientStylesTemplate);
517
+ });
511
518
  }
512
519
  else {
513
520
  log("Skipping style HMR test as requested");
@@ -1182,9 +1189,9 @@ async function testStyleHMR(page, targetDir) {
1182
1189
  // Allow time for HMR to kick in
1183
1190
  await new Promise((resolve) => setTimeout(resolve, 5000));
1184
1191
  // Check URL-based stylesheet HMR
1185
- await checkUrlStyles(page, "green");
1192
+ await withRetries(() => checkUrlStyles(page, "green"), "URL styles HMR check");
1186
1193
  // Check client-module stylesheet HMR
1187
- await checkClientModuleStyles(page, "green");
1194
+ await withRetries(() => checkClientModuleStyles(page, "green"), "Client module styles HMR check");
1188
1195
  // Restore original styles
1189
1196
  await fs.writeFile(urlStylePath, urlStylesTemplate);
1190
1197
  await fs.writeFile(clientStylePath, clientStylesTemplate);
@@ -13,3 +13,12 @@ export declare function teardown(): Promise<void>;
13
13
  * Formats the path suffix from a custom path
14
14
  */
15
15
  export declare function formatPathSuffix(customPath?: string): string;
16
+ /**
17
+ * Wraps an async function with retry logic.
18
+ * @param fn The async function to execute.
19
+ * @param description A description of the operation for logging.
20
+ * @param beforeRetry A function to run before each retry attempt.
21
+ * @param maxRetries The maximum number of retries.
22
+ * @param delay The delay between retries in milliseconds.
23
+ */
24
+ export declare function withRetries<T>(fn: () => Promise<T>, description: string, beforeRetry?: () => Promise<void>, maxRetries?: number, delay?: number): Promise<T>;
@@ -145,3 +145,32 @@ export function formatPathSuffix(customPath) {
145
145
  log("Formatted path suffix: %s", suffix);
146
146
  return suffix;
147
147
  }
148
+ /**
149
+ * Wraps an async function with retry logic.
150
+ * @param fn The async function to execute.
151
+ * @param description A description of the operation for logging.
152
+ * @param beforeRetry A function to run before each retry attempt.
153
+ * @param maxRetries The maximum number of retries.
154
+ * @param delay The delay between retries in milliseconds.
155
+ */
156
+ export async function withRetries(fn, description, beforeRetry, maxRetries = 5, delay = 2000) {
157
+ for (let i = 0; i < maxRetries; i++) {
158
+ try {
159
+ if (i > 0 && beforeRetry) {
160
+ log(`Running beforeRetry hook for "${description}"`);
161
+ await beforeRetry();
162
+ }
163
+ return await fn();
164
+ }
165
+ catch (error) {
166
+ log(`Attempt ${i + 1} of ${maxRetries} failed for "${description}": ${error instanceof Error ? error.message : String(error)}`);
167
+ if (i === maxRetries - 1) {
168
+ log(`All ${maxRetries} retries failed for "${description}".`);
169
+ throw error;
170
+ }
171
+ log(`Retrying in ${delay}ms...`);
172
+ await setTimeout(delay);
173
+ }
174
+ }
175
+ throw new Error("Retry loop failed unexpectedly.");
176
+ }
@@ -4,10 +4,7 @@ export const getManifest = async (requestInfo) => {
4
4
  return manifest;
5
5
  }
6
6
  if (import.meta.env.VITE_IS_DEV_SERVER) {
7
- const url = new URL(requestInfo.request.url);
8
- url.searchParams.set("scripts", JSON.stringify(Array.from(requestInfo.rw.scriptsToBeLoaded)));
9
- url.pathname = "/__rwsdk_manifest";
10
- manifest = await fetch(url.toString()).then((res) => res.json());
7
+ manifest = {};
11
8
  }
12
9
  else {
13
10
  const { default: prodManifest } = await import("virtual:rwsdk:manifest.js");
@@ -1,9 +1,4 @@
1
1
  import { type RequestInfo } from "../requestInfo/types.js";
2
- export type CssEntry = {
3
- url: string;
4
- content: string;
5
- absolutePath: string;
6
- };
7
2
  export declare const Stylesheets: ({ requestInfo }: {
8
3
  requestInfo: RequestInfo;
9
4
  }) => import("react/jsx-runtime.js").JSX.Element;
@@ -31,15 +31,5 @@ export const Stylesheets = ({ requestInfo }) => {
31
31
  allStylesheets.add(entry);
32
32
  }
33
33
  }
34
- return (_jsx(_Fragment, { children: Array.from(allStylesheets).map((entry) => {
35
- if (typeof entry === "string") {
36
- return (_jsx("link", { rel: "stylesheet", href: entry, precedence: "first" }, entry));
37
- }
38
- if (import.meta.env.VITE_IS_DEV_SERVER) {
39
- return (_jsx("style", { "data-vite-dev-id": entry.absolutePath, dangerouslySetInnerHTML: { __html: entry.content } }, entry.url));
40
- }
41
- else {
42
- return (_jsx("link", { rel: "stylesheet", href: entry.url, precedence: "first" }, entry.url));
43
- }
44
- }) }));
34
+ return (_jsx(_Fragment, { children: Array.from(allStylesheets).map((href) => (_jsx("link", { rel: "stylesheet", href: href, precedence: "first" }, href))) }));
45
35
  };
@@ -30,8 +30,7 @@ if (fileURLToPath(import.meta.url) === process.argv[1]) {
30
30
  copyProject: false, // Default to false - don't copy project to artifacts
31
31
  realtime: false, // Default to false - don't just test realtime
32
32
  skipHmr: false, // Default to false - run HMR tests
33
- // todo(justinvdm, 2025-07-31): Remove this once style tests working with headless
34
- skipStyleTests: true, // Default to true - skip style tests
33
+ skipStyleTests: false,
35
34
  // sync: will be set below
36
35
  };
37
36
  // Log if we're in CI
@@ -55,8 +54,8 @@ if (fileURLToPath(import.meta.url) === process.argv[1]) {
55
54
  else if (arg === "--skip-hmr") {
56
55
  options.skipHmr = true;
57
56
  }
58
- else if (arg === "--run-style-tests") {
59
- options.skipStyleTests = false;
57
+ else if (arg === "--skip-style-tests") {
58
+ options.skipStyleTests = true;
60
59
  }
61
60
  else if (arg === "--keep") {
62
61
  options.keep = true;
@@ -94,7 +93,7 @@ Options:
94
93
  --skip-release Skip testing the release/production deployment
95
94
  --skip-client Skip client-side tests, only run server-side checks
96
95
  --skip-hmr Skip hot module replacement (HMR) tests
97
- --run-style-tests Enable stylesheet-related tests (disabled by default)
96
+ --skip-style-tests Skip stylesheet-related tests
98
97
  --path=PATH Project directory to test
99
98
  --artifact-dir=DIR Directory to store test artifacts (default: .artifacts)
100
99
  --keep Keep temporary test directory after tests complete
@@ -4,36 +4,6 @@ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
4
4
  const log = debug("rwsdk:vite:manifest-plugin");
5
5
  const virtualModuleId = "virtual:rwsdk:manifest.js";
6
6
  const resolvedVirtualModuleId = "\0" + virtualModuleId;
7
- const getCssForModule = (server, moduleId, css) => {
8
- const stack = [moduleId];
9
- const visited = new Set();
10
- while (stack.length > 0) {
11
- const currentModuleId = stack.pop();
12
- if (visited.has(currentModuleId)) {
13
- continue;
14
- }
15
- visited.add(currentModuleId);
16
- const moduleNode = server.environments.client.moduleGraph.getModuleById(currentModuleId);
17
- if (!moduleNode) {
18
- continue;
19
- }
20
- for (const importedModule of moduleNode.importedModules) {
21
- if (importedModule.url.endsWith(".css")) {
22
- const absolutePath = importedModule.file;
23
- css.add({
24
- url: importedModule.url,
25
- // The `ssrTransformResult` has the CSS content, because the default
26
- // transform for CSS is to a string of the CSS content.
27
- content: importedModule.ssrTransformResult?.code ?? "",
28
- absolutePath,
29
- });
30
- }
31
- if (importedModule.id) {
32
- stack.push(importedModule.id);
33
- }
34
- }
35
- }
36
- };
37
7
  export const manifestPlugin = ({ manifestPath, }) => {
38
8
  let isBuild = false;
39
9
  let root;
@@ -106,46 +76,5 @@ export const manifestPlugin = ({ manifestPath, }) => {
106
76
  },
107
77
  });
108
78
  },
109
- configureServer(server) {
110
- log("Configuring server middleware for manifest");
111
- server.middlewares.use("/__rwsdk_manifest", async (req, res, next) => {
112
- log("Manifest request received: %s", req.url);
113
- try {
114
- const url = new URL(req.url, `http://${req.headers.host}`);
115
- const scripts = JSON.parse(url.searchParams.get("scripts") || "[]");
116
- process.env.VERBOSE && log("Transforming scripts: %o", scripts);
117
- for (const script of scripts) {
118
- await server.environments.client.transformRequest(script);
119
- }
120
- const manifest = {};
121
- log("Building manifest from module graph");
122
- for (const file of server.environments.client.moduleGraph.fileToModulesMap.keys()) {
123
- const modules = server.environments.client.moduleGraph.getModulesByFile(file);
124
- if (!modules) {
125
- continue;
126
- }
127
- for (const module of modules) {
128
- if (module.file) {
129
- const css = new Set();
130
- getCssForModule(server, module.id, css);
131
- manifest[normalizeModulePath(module.file, server.config.root)] =
132
- {
133
- file: module.url,
134
- css: Array.from(css),
135
- };
136
- }
137
- }
138
- }
139
- log("Manifest built successfully");
140
- process.env.VERBOSE && log("Manifest: %o", manifest);
141
- res.setHeader("Content-Type", "application/json");
142
- res.end(JSON.stringify(manifest));
143
- }
144
- catch (e) {
145
- log("Error building manifest: %o", e);
146
- next(e);
147
- }
148
- });
149
- },
150
79
  };
151
80
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rwsdk",
3
- "version": "0.2.0-alpha.5",
3
+ "version": "0.2.0-alpha.6",
4
4
  "description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
5
5
  "type": "module",
6
6
  "bin": {