rwsdk 0.2.0-alpha.0 → 0.2.0-alpha.10

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.
@@ -3,7 +3,7 @@ import { env } from "cloudflare:workers";
3
3
  export const defineScript = (fn) => {
4
4
  const app = defineApp([
5
5
  async () => {
6
- await fn({ env });
6
+ await fn({ env: env });
7
7
  return new Response("Done!");
8
8
  },
9
9
  ]);
@@ -42,6 +42,11 @@ export const defineApp = (routes) => {
42
42
  ssr: true,
43
43
  databases: new Map(),
44
44
  scriptsToBeLoaded: new Set(),
45
+ pageRouteResolved: undefined,
46
+ };
47
+ const userResponseInit = {
48
+ status: 200,
49
+ headers: new Headers(),
45
50
  };
46
51
  const outerRequestInfo = {
47
52
  request,
@@ -50,6 +55,7 @@ export const defineApp = (routes) => {
50
55
  params: {},
51
56
  ctx: {},
52
57
  rw,
58
+ response: userResponseInit,
53
59
  };
54
60
  const createPageElement = (requestInfo, Page) => {
55
61
  let pageElement;
@@ -59,16 +65,7 @@ export const defineApp = (routes) => {
59
65
  pageElement = _jsx(Page, { ctx: ctx, params: params });
60
66
  }
61
67
  else {
62
- // context(justinvdm, 24 Apr 2025): We need to wrap the page in a component that throws the response to bubble it up and break out of react rendering context
63
- // This way, we're able to return a response from the page component while still staying within react rendering context
64
- const PageWrapper = async () => {
65
- const result = await Page(requestInfo);
66
- if (result instanceof Response) {
67
- throw result;
68
- }
69
- return result;
70
- };
71
- pageElement = _jsx(PageWrapper, {});
68
+ pageElement = _jsx(Page, { ...requestInfo });
72
69
  }
73
70
  if (isSmokeTest) {
74
71
  pageElement = _jsx(SmokeTestWrapper, { children: pageElement });
@@ -97,10 +94,12 @@ export const defineApp = (routes) => {
97
94
  onError,
98
95
  });
99
96
  if (isRSCRequest) {
97
+ const responseHeaders = new Headers(userResponseInit.headers);
98
+ responseHeaders.set("content-type", "text/x-component; charset=utf-8");
100
99
  return new Response(rscPayloadStream, {
101
- headers: {
102
- "content-type": "text/x-component; charset=utf-8",
103
- },
100
+ status: userResponseInit.status,
101
+ statusText: userResponseInit.statusText,
102
+ headers: responseHeaders,
104
103
  });
105
104
  }
106
105
  let injectRSCPayloadStream;
@@ -120,10 +119,12 @@ export const defineApp = (routes) => {
120
119
  if (injectRSCPayloadStream) {
121
120
  html = html.pipeThrough(injectRSCPayloadStream);
122
121
  }
122
+ const responseHeaders = new Headers(userResponseInit.headers);
123
+ responseHeaders.set("content-type", "text/html; charset=utf-8");
123
124
  return new Response(html, {
124
- headers: {
125
- "content-type": "text/html; charset=utf-8",
126
- },
125
+ status: userResponseInit.status,
126
+ statusText: userResponseInit.statusText,
127
+ headers: responseHeaders,
127
128
  });
128
129
  };
129
130
  const response = await runWithRequestInfo(outerRequestInfo, async () => new Promise(async (resolve, reject) => {
@@ -143,11 +144,22 @@ export const defineApp = (routes) => {
143
144
  // context(justinvdm, 18 Mar 2025): In some cases, such as a .fetch() call to a durable object instance, or Response.redirect(),
144
145
  // we need to return a mutable response object.
145
146
  const mutableResponse = new Response(response.body, response);
147
+ // Merge user headers from the legacy headers object
146
148
  for (const [key, value] of userHeaders.entries()) {
147
149
  if (!response.headers.has(key)) {
148
150
  mutableResponse.headers.set(key, value);
149
151
  }
150
152
  }
153
+ // Merge headers from user response init (these take precedence)
154
+ if (userResponseInit.headers) {
155
+ const userResponseHeaders = new Headers(userResponseInit.headers);
156
+ for (const [key, value] of userResponseHeaders.entries()) {
157
+ if (!response.headers.has(key)) {
158
+ mutableResponse.headers.set(key, value);
159
+ }
160
+ }
161
+ }
162
+ await rw.pageRouteResolved?.promise;
151
163
  return mutableResponse;
152
164
  }
153
165
  catch (e) {
@@ -24,108 +24,12 @@ const getPackageManagerInfo = (targetDir) => {
24
24
  }
25
25
  return pnpmResult;
26
26
  };
27
- /**
28
- * @summary Workaround for pnpm's local tarball dependency resolution.
29
- *
30
- * @description
31
- * When installing a new version of the SDK from a local tarball (e.g., during
32
- * development with `rwsync`), pnpm creates a new, uniquely-named directory in
33
- * the `.pnpm` store (e.g., `rwsdk@file+...`).
34
- *
35
- * A challenge arises when other packages list `rwsdk` as a peer dependency.
36
- * pnpm may not consistently update the symlinks for these peer dependencies
37
- * to point to the newest `rwsdk` instance. This can result in a state where
38
- * multiple versions of `rwsdk` coexist in `node_modules`, with some parts of
39
- * the application using a stale version.
40
- *
41
- * This function addresses the issue by:
42
- * 1. Identifying the most recently installed `rwsdk` instance in the `.pnpm`
43
- * store after a `pnpm install` run.
44
- * 2. Forcefully updating the top-level `node_modules/rwsdk` symlink to point
45
- * to this new instance.
46
- * 3. Traversing all other `rwsdk`-related directories in the `.pnpm` store
47
- * and updating their internal `rwsdk` symlinks to also point to the correct
48
- * new instance.
49
- *
50
- * I am sorry for this ugly hack, I am sure there is a better way, and that it is me
51
- * doing something wrong. The aim is not to go down this rabbit hole right now
52
- * -- @justinvdm
53
- */
54
- const hackyPnpmSymlinkFix = async (targetDir) => {
55
- console.log("💣 Performing pnpm symlink fix...");
56
- const pnpmDir = path.join(targetDir, "node_modules", ".pnpm");
57
- if (!existsSync(pnpmDir)) {
58
- console.log(" 🤔 No .pnpm directory found.");
59
- return;
60
- }
61
- try {
62
- const entries = await fs.readdir(pnpmDir);
63
- // Find ALL rwsdk directories, not just file-based ones, to handle
64
- // all kinds of stale peer dependencies.
65
- const rwsdkDirs = entries.filter((e) => e.startsWith("rwsdk@"));
66
- console.log(" Found rwsdk directories:", rwsdkDirs);
67
- if (rwsdkDirs.length === 0) {
68
- console.log(" 🤔 No rwsdk directories found to hack.");
69
- return;
70
- }
71
- let latestDir = "";
72
- let latestMtime = new Date(0);
73
- for (const dir of rwsdkDirs) {
74
- const fullPath = path.join(pnpmDir, dir);
75
- const stats = await fs.stat(fullPath);
76
- if (stats.mtime > latestMtime) {
77
- latestMtime = stats.mtime;
78
- latestDir = dir;
79
- }
80
- }
81
- console.log(" Latest rwsdk directory:", latestDir);
82
- if (!latestDir) {
83
- console.log(" 🤔 Could not determine the latest rwsdk directory.");
84
- return;
85
- }
86
- const goldenSourcePath = path.join(pnpmDir, latestDir, "node_modules", "rwsdk");
87
- if (!existsSync(goldenSourcePath)) {
88
- console.error(` ❌ Golden source path does not exist: ${goldenSourcePath}`);
89
- return;
90
- }
91
- console.log(` 🎯 Golden rwsdk path is: ${goldenSourcePath}`);
92
- // 1. Fix top-level symlink
93
- const topLevelSymlink = path.join(targetDir, "node_modules", "rwsdk");
94
- await fs.rm(topLevelSymlink, { recursive: true, force: true });
95
- await fs.symlink(goldenSourcePath, topLevelSymlink, "dir");
96
- console.log(` ✅ Symlinked ${topLevelSymlink} -> ${goldenSourcePath}`);
97
- // 2. Fix peer dependency symlinks
98
- const allPnpmDirs = await fs.readdir(pnpmDir);
99
- for (const dir of allPnpmDirs) {
100
- if (dir === latestDir || !dir.includes("rwsdk"))
101
- continue;
102
- const peerSymlink = path.join(pnpmDir, dir, "node_modules", "rwsdk");
103
- if (existsSync(peerSymlink)) {
104
- await fs.rm(peerSymlink, { recursive: true, force: true });
105
- await fs.symlink(goldenSourcePath, peerSymlink, "dir");
106
- console.log(` ✅ Hijacked symlink in ${dir}`);
107
- }
108
- }
109
- }
110
- catch (error) {
111
- console.error(" ❌ Failed during hacky pnpm symlink fix:", error);
112
- }
113
- };
114
- const performFullSync = async (sdkDir, targetDir, cacheBust = false) => {
27
+ const performFullSync = async (sdkDir, targetDir) => {
115
28
  const sdkPackageJsonPath = path.join(sdkDir, "package.json");
116
29
  let originalSdkPackageJson = null;
117
30
  let tarballPath = "";
118
31
  let tarballName = "";
119
32
  try {
120
- if (cacheBust) {
121
- console.log("💥 Cache-busting version for full sync...");
122
- originalSdkPackageJson = await fs.readFile(sdkPackageJsonPath, "utf-8");
123
- const packageJson = JSON.parse(originalSdkPackageJson);
124
- const now = Date.now();
125
- // This is a temporary version used for cache busting
126
- packageJson.version = `${packageJson.version}-dev.${now}`;
127
- await fs.writeFile(sdkPackageJsonPath, JSON.stringify(packageJson, null, 2));
128
- }
129
33
  console.log("📦 Packing SDK...");
130
34
  const packResult = await $({ cwd: sdkDir }) `npm pack`;
131
35
  tarballName = packResult.stdout?.trim() ?? "";
@@ -145,47 +49,25 @@ const performFullSync = async (sdkDir, targetDir, cacheBust = false) => {
145
49
  .readFile(lockfilePath, "utf-8")
146
50
  .catch(() => null);
147
51
  try {
148
- if (pm.name === "pnpm") {
149
- console.log("🛠️ Using pnpm overrides to install SDK...");
150
- if (originalPackageJson) {
151
- const targetPackageJson = JSON.parse(originalPackageJson);
152
- targetPackageJson.pnpm = targetPackageJson.pnpm || {};
153
- targetPackageJson.pnpm.overrides =
154
- targetPackageJson.pnpm.overrides || {};
155
- targetPackageJson.pnpm.overrides.rwsdk = `file:${tarballPath}`;
156
- await fs.writeFile(packageJsonPath, JSON.stringify(targetPackageJson, null, 2));
157
- }
158
- // We use install here, which respects the overrides.
159
- // We also don't want to fail if the lockfile is out of date.
160
- await $("pnpm", ["install", "--no-frozen-lockfile"], {
161
- cwd: targetDir,
162
- stdio: "inherit",
163
- });
164
- if (process.env.RWSDK_PNPM_SYMLINK_FIX) {
165
- await hackyPnpmSymlinkFix(targetDir);
166
- }
52
+ const cmd = pm.name;
53
+ const args = [pm.command];
54
+ if (pm.name === "yarn") {
55
+ args.push(`file:${tarballPath}`);
167
56
  }
168
57
  else {
169
- const cmd = pm.name;
170
- const args = [pm.command];
171
- if (pm.name === "yarn") {
172
- args.push(`file:${tarballPath}`);
173
- }
174
- else {
175
- args.push(tarballPath);
176
- }
177
- await $(cmd, args, {
178
- cwd: targetDir,
179
- stdio: "inherit",
180
- });
58
+ args.push(tarballPath);
181
59
  }
60
+ await $(cmd, args, {
61
+ cwd: targetDir,
62
+ stdio: "inherit",
63
+ });
182
64
  }
183
65
  finally {
184
66
  if (originalPackageJson) {
185
67
  console.log("Restoring package.json...");
186
68
  await fs.writeFile(packageJsonPath, originalPackageJson);
187
69
  }
188
- if (originalLockfile && pm.name !== "pnpm") {
70
+ if (originalLockfile) {
189
71
  console.log(`Restoring ${pm.lockFile}...`);
190
72
  await fs.writeFile(lockfilePath, originalLockfile);
191
73
  }
@@ -218,13 +100,17 @@ const performFastSync = async (sdkDir, targetDir) => {
218
100
  // Always copy package.json
219
101
  await fs.copyFile(path.join(sdkDir, "package.json"), path.join(targetDir, "node_modules/rwsdk/package.json"));
220
102
  };
103
+ const areDependenciesEqual = (deps1, deps2) => {
104
+ // Simple string comparison for this use case is sufficient
105
+ return JSON.stringify(deps1 ?? {}) === JSON.stringify(deps2 ?? {});
106
+ };
221
107
  const performSync = async (sdkDir, targetDir) => {
222
108
  console.log("🏗️ Rebuilding SDK...");
223
109
  await $ `pnpm build`;
224
110
  const forceFullSync = Boolean(process.env.RWSDK_FORCE_FULL_SYNC);
225
111
  if (forceFullSync) {
226
112
  console.log("🏃 Force full sync mode is enabled.");
227
- await performFullSync(sdkDir, targetDir, true);
113
+ await performFullSync(sdkDir, targetDir);
228
114
  console.log("✅ Done syncing");
229
115
  return;
230
116
  }
@@ -234,15 +120,17 @@ const performSync = async (sdkDir, targetDir) => {
234
120
  if (existsSync(installedSdkPackageJsonPath)) {
235
121
  const sdkPackageJsonContent = await fs.readFile(sdkPackageJsonPath, "utf-8");
236
122
  const installedSdkPackageJsonContent = await fs.readFile(installedSdkPackageJsonPath, "utf-8");
237
- if (sdkPackageJsonContent === installedSdkPackageJsonContent) {
123
+ const sdkPkg = JSON.parse(sdkPackageJsonContent);
124
+ const installedPkg = JSON.parse(installedSdkPackageJsonContent);
125
+ if (areDependenciesEqual(sdkPkg.dependencies, installedPkg.dependencies) &&
126
+ areDependenciesEqual(sdkPkg.devDependencies, installedPkg.devDependencies) &&
127
+ areDependenciesEqual(sdkPkg.peerDependencies, installedPkg.peerDependencies)) {
238
128
  packageJsonChanged = false;
239
129
  }
240
130
  }
241
131
  if (packageJsonChanged) {
242
132
  console.log("📦 package.json changed, performing full sync...");
243
- // We always cache-bust on a full sync now to ensure pnpm's overrides
244
- // see a new version and the hacky symlink fix runs on a clean slate.
245
- await performFullSync(sdkDir, targetDir, true);
133
+ await performFullSync(sdkDir, targetDir);
246
134
  }
247
135
  else {
248
136
  await performFastSync(sdkDir, targetDir);
@@ -261,8 +149,7 @@ export const debugSync = async (opts) => {
261
149
  return;
262
150
  }
263
151
  // --- Watch Mode Logic ---
264
- // Use global lock based on SDK directory since all instances sync from the same source
265
- const lockfilePath = path.join(sdkDir, ".rwsync.lock");
152
+ const lockfilePath = path.join(targetDir, "node_modules", ".rwsync.lock");
266
153
  let release;
267
154
  // Ensure the directory for the lockfile exists
268
155
  await fs.mkdir(path.dirname(lockfilePath), { recursive: true });
@@ -273,7 +160,7 @@ export const debugSync = async (opts) => {
273
160
  }
274
161
  catch (e) {
275
162
  if (e.code === "ELOCKED") {
276
- console.error(`❌ Another rwsync process is already running for this SDK.`);
163
+ console.error(`❌ Another rwsync process is already watching ${targetDir}.`);
277
164
  console.error(` If this is not correct, please remove the lockfile at ${lockfilePath}`);
278
165
  process.exit(1);
279
166
  }
@@ -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
@@ -1,11 +1,10 @@
1
1
  /**
2
- * Finds __vite_ssr_import__ and __vite_ssr_dynamic_import__ specifiers in the code.
3
- * @param id The file identifier for language detection.
4
- * @param code The code to search for SSR imports.
5
- * @param log Optional logger function for debug output.
6
- * @returns Object with arrays of static and dynamic import specifiers.
2
+ * Finds callsites for __vite_ssr_import__ and __vite_ssr_dynamic_import__ with their ranges.
3
+ * The returned ranges can be used with MagicString to overwrite the entire call expression.
7
4
  */
8
- export declare function findSsrImportSpecifiers(id: string, code: string, log?: (...args: any[]) => void): {
9
- imports: string[];
10
- dynamicImports: string[];
11
- };
5
+ export declare function findSsrImportCallSites(id: string, code: string, log?: (...args: any[]) => void): Array<{
6
+ start: number;
7
+ end: number;
8
+ specifier: string;
9
+ kind: "import" | "dynamic_import";
10
+ }>;
@@ -1,67 +1,69 @@
1
1
  import { parse as sgParse, Lang as SgLang, Lang } from "@ast-grep/napi";
2
2
  import path from "path";
3
3
  /**
4
- * Finds __vite_ssr_import__ and __vite_ssr_dynamic_import__ specifiers in the code.
5
- * @param id The file identifier for language detection.
6
- * @param code The code to search for SSR imports.
7
- * @param log Optional logger function for debug output.
8
- * @returns Object with arrays of static and dynamic import specifiers.
4
+ * Finds callsites for __vite_ssr_import__ and __vite_ssr_dynamic_import__ with their ranges.
5
+ * The returned ranges can be used with MagicString to overwrite the entire call expression.
9
6
  */
10
- export function findSsrImportSpecifiers(id, code, log) {
7
+ export function findSsrImportCallSites(id, code, log) {
11
8
  const ext = path.extname(id).toLowerCase();
12
9
  const lang = ext === ".tsx" || ext === ".jsx" ? Lang.Tsx : SgLang.TypeScript;
13
10
  const logger = process.env.VERBOSE ? (log ?? (() => { })) : () => { };
14
- const imports = [];
15
- const dynamicImports = [];
11
+ const results = [];
16
12
  try {
17
13
  const root = sgParse(lang, code);
18
14
  const patterns = [
19
15
  {
20
16
  pattern: `__vite_ssr_import__("$SPECIFIER")`,
21
- list: imports,
17
+ kind: "import",
22
18
  },
23
19
  {
24
20
  pattern: `__vite_ssr_import__('$SPECIFIER')`,
25
- list: imports,
21
+ kind: "import",
26
22
  },
27
23
  {
28
24
  pattern: `__vite_ssr_dynamic_import__("$SPECIFIER")`,
29
- list: dynamicImports,
25
+ kind: "dynamic_import",
30
26
  },
31
27
  {
32
28
  pattern: `__vite_ssr_dynamic_import__('$SPECIFIER')`,
33
- list: dynamicImports,
29
+ kind: "dynamic_import",
34
30
  },
35
31
  {
36
32
  pattern: `__vite_ssr_import__("$SPECIFIER", $$$REST)`,
37
- list: imports,
33
+ kind: "import",
38
34
  },
39
35
  {
40
36
  pattern: `__vite_ssr_import__('$SPECIFIER', $$$REST)`,
41
- list: imports,
37
+ kind: "import",
42
38
  },
43
39
  {
44
40
  pattern: `__vite_ssr_dynamic_import__("$SPECIFIER", $$$REST)`,
45
- list: dynamicImports,
41
+ kind: "dynamic_import",
46
42
  },
47
43
  {
48
44
  pattern: `__vite_ssr_dynamic_import__('$SPECIFIER', $$$REST)`,
49
- list: dynamicImports,
45
+ kind: "dynamic_import",
50
46
  },
51
47
  ];
52
- for (const { pattern, list } of patterns) {
48
+ for (const { pattern, kind } of patterns) {
53
49
  const matches = root.root().findAll(pattern);
54
50
  for (const match of matches) {
55
51
  const specifier = match.getMatch("SPECIFIER")?.text();
56
52
  if (specifier) {
57
- list.push(specifier);
58
- logger(`Found SSR import specifier: %s in pattern: %s`, specifier, pattern);
53
+ const range = match.range();
54
+ results.push({
55
+ start: range.start.index,
56
+ end: range.end.index,
57
+ specifier,
58
+ kind,
59
+ });
60
+ logger(`Found SSR import callsite: %s [%s] at %d-%d`, specifier, kind, range.start.index, range.end.index);
59
61
  }
60
62
  }
61
63
  }
62
64
  }
63
65
  catch (err) {
64
- logger("Error parsing code for SSR imports: %O", err);
66
+ logger("Error parsing code for SSR import callsites: %O", err);
65
67
  }
66
- return { imports, dynamicImports };
68
+ return results;
67
69
  }
@@ -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
  };
@@ -109,19 +109,19 @@ export const miniflareHMRPlugin = (givenOptions) => [
109
109
  let clientDirectiveChanged = false;
110
110
  let serverDirectiveChanged = false;
111
111
  if (!clientFiles.has(ctx.file) && hasClientDirective) {
112
- clientFiles.add(ctx.file);
112
+ clientFiles.add(normalizeModulePath(ctx.file, givenOptions.rootDir));
113
113
  clientDirectiveChanged = true;
114
114
  }
115
115
  else if (clientFiles.has(ctx.file) && !hasClientDirective) {
116
- clientFiles.delete(ctx.file);
116
+ clientFiles.delete(normalizeModulePath(ctx.file, givenOptions.rootDir));
117
117
  clientDirectiveChanged = true;
118
118
  }
119
119
  if (!serverFiles.has(ctx.file) && hasServerDirective) {
120
- serverFiles.add(ctx.file);
120
+ serverFiles.add(normalizeModulePath(ctx.file, givenOptions.rootDir));
121
121
  serverDirectiveChanged = true;
122
122
  }
123
123
  else if (serverFiles.has(ctx.file) && !hasServerDirective) {
124
- serverFiles.delete(ctx.file);
124
+ serverFiles.delete(normalizeModulePath(ctx.file, givenOptions.rootDir));
125
125
  serverDirectiveChanged = true;
126
126
  }
127
127
  if (clientDirectiveChanged) {
@@ -129,12 +129,14 @@ export const miniflareHMRPlugin = (givenOptions) => [
129
129
  invalidateModule(ctx.server, environment, "virtual:use-client-lookup.js");
130
130
  });
131
131
  invalidateModule(ctx.server, environment, VIRTUAL_SSR_PREFIX + "/@id/virtual:use-client-lookup.js");
132
+ invalidateModule(ctx.server, environment, VIRTUAL_SSR_PREFIX + "virtual:use-client-lookup.js");
132
133
  }
133
134
  if (serverDirectiveChanged) {
134
135
  ["client", "ssr", environment].forEach((environment) => {
135
136
  invalidateModule(ctx.server, environment, "virtual:use-server-lookup.js");
136
137
  });
137
138
  invalidateModule(ctx.server, environment, VIRTUAL_SSR_PREFIX + "/@id/virtual:use-server-lookup.js");
139
+ invalidateModule(ctx.server, environment, VIRTUAL_SSR_PREFIX + "virtual:use-server-lookup.js");
138
140
  }
139
141
  // todo(justinvdm, 12 Dec 2024): Skip client references
140
142
  const modules = Array.from(ctx.server.environments[environment].moduleGraph.getModulesByFile(ctx.file) ?? []);