@reckona/mreact-router 0.0.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.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -0
  3. package/dist/actions.d.ts +43 -0
  4. package/dist/actions.d.ts.map +1 -0
  5. package/dist/actions.js +577 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/adapters/aws-lambda.d.ts +45 -0
  8. package/dist/adapters/aws-lambda.d.ts.map +1 -0
  9. package/dist/adapters/aws-lambda.js +168 -0
  10. package/dist/adapters/aws-lambda.js.map +1 -0
  11. package/dist/adapters/cloudflare.d.ts +94 -0
  12. package/dist/adapters/cloudflare.d.ts.map +1 -0
  13. package/dist/adapters/cloudflare.js +390 -0
  14. package/dist/adapters/cloudflare.js.map +1 -0
  15. package/dist/adapters/devtools.d.ts +4 -0
  16. package/dist/adapters/devtools.d.ts.map +1 -0
  17. package/dist/adapters/devtools.js +5 -0
  18. package/dist/adapters/devtools.js.map +1 -0
  19. package/dist/adapters/edge.d.ts +9 -0
  20. package/dist/adapters/edge.d.ts.map +1 -0
  21. package/dist/adapters/edge.js +53 -0
  22. package/dist/adapters/edge.js.map +1 -0
  23. package/dist/adapters/node.d.ts +26 -0
  24. package/dist/adapters/node.d.ts.map +1 -0
  25. package/dist/adapters/node.js +64 -0
  26. package/dist/adapters/node.js.map +1 -0
  27. package/dist/adapters/static.d.ts +10 -0
  28. package/dist/adapters/static.d.ts.map +1 -0
  29. package/dist/adapters/static.js +34 -0
  30. package/dist/adapters/static.js.map +1 -0
  31. package/dist/assets.d.ts +18 -0
  32. package/dist/assets.d.ts.map +1 -0
  33. package/dist/assets.js +67 -0
  34. package/dist/assets.js.map +1 -0
  35. package/dist/build.d.ts +36 -0
  36. package/dist/build.d.ts.map +1 -0
  37. package/dist/build.js +322 -0
  38. package/dist/build.js.map +1 -0
  39. package/dist/cache.d.ts +54 -0
  40. package/dist/cache.d.ts.map +1 -0
  41. package/dist/cache.js +221 -0
  42. package/dist/cache.js.map +1 -0
  43. package/dist/cli.d.ts +3 -0
  44. package/dist/cli.d.ts.map +1 -0
  45. package/dist/cli.js +37 -0
  46. package/dist/cli.js.map +1 -0
  47. package/dist/client.d.ts +105 -0
  48. package/dist/client.d.ts.map +1 -0
  49. package/dist/client.js +1268 -0
  50. package/dist/client.js.map +1 -0
  51. package/dist/config.d.ts +27 -0
  52. package/dist/config.d.ts.map +1 -0
  53. package/dist/config.js +44 -0
  54. package/dist/config.js.map +1 -0
  55. package/dist/cookies.d.ts +14 -0
  56. package/dist/cookies.d.ts.map +1 -0
  57. package/dist/cookies.js +69 -0
  58. package/dist/cookies.js.map +1 -0
  59. package/dist/csp.d.ts +6 -0
  60. package/dist/csp.d.ts.map +1 -0
  61. package/dist/csp.js +70 -0
  62. package/dist/csp.js.map +1 -0
  63. package/dist/dev-server.d.ts +16 -0
  64. package/dist/dev-server.d.ts.map +1 -0
  65. package/dist/dev-server.js +103 -0
  66. package/dist/dev-server.js.map +1 -0
  67. package/dist/http.d.ts +23 -0
  68. package/dist/http.d.ts.map +1 -0
  69. package/dist/http.js +106 -0
  70. package/dist/http.js.map +1 -0
  71. package/dist/i18n.d.ts +15 -0
  72. package/dist/i18n.d.ts.map +1 -0
  73. package/dist/i18n.js +61 -0
  74. package/dist/i18n.js.map +1 -0
  75. package/dist/import-policy.d.ts +30 -0
  76. package/dist/import-policy.d.ts.map +1 -0
  77. package/dist/import-policy.js +105 -0
  78. package/dist/import-policy.js.map +1 -0
  79. package/dist/index.d.ts +60 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +34 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/logger.d.ts +47 -0
  84. package/dist/logger.d.ts.map +1 -0
  85. package/dist/logger.js +60 -0
  86. package/dist/logger.js.map +1 -0
  87. package/dist/module-runner.d.ts +9 -0
  88. package/dist/module-runner.d.ts.map +1 -0
  89. package/dist/module-runner.js +112 -0
  90. package/dist/module-runner.js.map +1 -0
  91. package/dist/native-escape.d.ts +2 -0
  92. package/dist/native-escape.d.ts.map +1 -0
  93. package/dist/native-escape.js +43 -0
  94. package/dist/native-escape.js.map +1 -0
  95. package/dist/native-route-matcher.d.ts +5 -0
  96. package/dist/native-route-matcher.d.ts.map +1 -0
  97. package/dist/native-route-matcher.js +91 -0
  98. package/dist/native-route-matcher.js.map +1 -0
  99. package/dist/navigation.d.ts +25 -0
  100. package/dist/navigation.d.ts.map +1 -0
  101. package/dist/navigation.js +125 -0
  102. package/dist/navigation.js.map +1 -0
  103. package/dist/prerender-store.d.ts +37 -0
  104. package/dist/prerender-store.d.ts.map +1 -0
  105. package/dist/prerender-store.js +158 -0
  106. package/dist/prerender-store.js.map +1 -0
  107. package/dist/render.d.ts +26 -0
  108. package/dist/render.d.ts.map +1 -0
  109. package/dist/render.js +1688 -0
  110. package/dist/render.js.map +1 -0
  111. package/dist/route-path.d.ts +2 -0
  112. package/dist/route-path.d.ts.map +1 -0
  113. package/dist/route-path.js +5 -0
  114. package/dist/route-path.js.map +1 -0
  115. package/dist/route-source.d.ts +9 -0
  116. package/dist/route-source.d.ts.map +1 -0
  117. package/dist/route-source.js +44 -0
  118. package/dist/route-source.js.map +1 -0
  119. package/dist/routes.d.ts +38 -0
  120. package/dist/routes.d.ts.map +1 -0
  121. package/dist/routes.js +168 -0
  122. package/dist/routes.js.map +1 -0
  123. package/dist/serve.d.ts +63 -0
  124. package/dist/serve.d.ts.map +1 -0
  125. package/dist/serve.js +445 -0
  126. package/dist/serve.js.map +1 -0
  127. package/dist/session.d.ts +25 -0
  128. package/dist/session.d.ts.map +1 -0
  129. package/dist/session.js +104 -0
  130. package/dist/session.js.map +1 -0
  131. package/dist/vite-config.d.ts +8 -0
  132. package/dist/vite-config.d.ts.map +1 -0
  133. package/dist/vite-config.js +17 -0
  134. package/dist/vite-config.js.map +1 -0
  135. package/dist/vite.d.ts +25 -0
  136. package/dist/vite.d.ts.map +1 -0
  137. package/dist/vite.js +150 -0
  138. package/dist/vite.js.map +1 -0
  139. package/package.json +91 -0
package/dist/client.js ADDED
@@ -0,0 +1,1268 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, stat } from "node:fs/promises";
3
+ import { dirname, extname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { collectStaticModuleSpecifiers, transform, } from "@reckona/mreact-compiler";
6
+ import { build } from "esbuild";
7
+ import { assetPath } from "./assets.js";
8
+ import { stripRouteClientOnlyExports } from "./route-source.js";
9
+ import { escapeHtmlQuotedAttribute as escapeHtmlAttribute } from "@reckona/mreact-shared/html-escape";
10
+ export async function routeToClientManifestEntry(route) {
11
+ if (route.kind === "server") {
12
+ return { path: route.path, kind: route.kind, client: false };
13
+ }
14
+ const code = await readFile(route.file, "utf8");
15
+ const inference = await inferClientRouteModule({
16
+ code: stripRouteClientOnlyExports(code),
17
+ filename: route.file,
18
+ routePath: route.path,
19
+ });
20
+ return inference.client
21
+ ? {
22
+ path: route.path,
23
+ kind: route.kind,
24
+ client: true,
25
+ routeId: routeIdForPath(route.path),
26
+ script: clientScriptForPath(route.path),
27
+ }
28
+ : { path: route.path, kind: route.kind, client: false };
29
+ }
30
+ export function createClientRouteInferenceCache() {
31
+ return {
32
+ importsByFile: new Map(),
33
+ resolvedByImport: new Map(),
34
+ sourceByFile: new Map(),
35
+ };
36
+ }
37
+ export async function isClientRouteModule(options) {
38
+ return (await inferClientRouteModule(options)).client;
39
+ }
40
+ export async function inferClientRouteModule(options) {
41
+ const cache = options.cache ?? createClientRouteInferenceCache();
42
+ try {
43
+ return await inferClientRouteModuleSource({
44
+ cache,
45
+ code: options.code,
46
+ filename: options.filename,
47
+ seen: new Set(),
48
+ });
49
+ }
50
+ catch (error) {
51
+ throw new Error(`Failed to infer client route for ${options.routePath ?? "<unknown>"} (${options.filename}).\n${errorMessage(error)}`, { cause: error });
52
+ }
53
+ }
54
+ export function isClientRouteSource(code) {
55
+ return /\bon[A-Z][A-Za-z0-9_]*=|\bcell\s*\(|\bwindow\b|\bdocument\b|\blocalStorage\b/.test(stripCommentsAndStringLiterals(code));
56
+ }
57
+ async function inferClientRouteModuleSource(options) {
58
+ if (isClientRouteSource(options.code)) {
59
+ return { client: true, clientBoundaryImports: [] };
60
+ }
61
+ if (options.seen.has(options.filename)) {
62
+ return { client: false, clientBoundaryImports: [] };
63
+ }
64
+ options.seen.add(options.filename);
65
+ const clientBoundaryImports = [];
66
+ for (const specifier of await staticImportSpecifiersForSource(options)) {
67
+ const resolved = await resolveAppLocalModule({
68
+ cache: options.cache,
69
+ importer: options.filename,
70
+ specifier,
71
+ });
72
+ if (resolved === undefined) {
73
+ continue;
74
+ }
75
+ const source = await readCachedFile(options.cache, resolved);
76
+ const imported = await inferClientRouteModuleSource({
77
+ cache: options.cache,
78
+ code: source,
79
+ filename: resolved,
80
+ seen: options.seen,
81
+ });
82
+ if (imported.client) {
83
+ clientBoundaryImports.push(specifier);
84
+ }
85
+ }
86
+ return {
87
+ client: clientBoundaryImports.length > 0,
88
+ clientBoundaryImports,
89
+ };
90
+ }
91
+ async function staticImportSpecifiersForSource(options) {
92
+ const cached = options.cache.importsByFile.get(options.filename);
93
+ if (cached !== undefined) {
94
+ return cached;
95
+ }
96
+ const imports = Promise.resolve().then(() => collectStaticModuleSpecifiers({
97
+ code: options.code,
98
+ filename: options.filename,
99
+ }));
100
+ options.cache.importsByFile.set(options.filename, imports);
101
+ return imports;
102
+ }
103
+ async function resolveAppLocalModule(options) {
104
+ if (!options.specifier.startsWith(".")) {
105
+ return undefined;
106
+ }
107
+ const cacheKey = `${options.importer}\0${options.specifier}`;
108
+ const cached = options.cache.resolvedByImport.get(cacheKey);
109
+ if (cached !== undefined) {
110
+ return cached;
111
+ }
112
+ const resolved = resolveAppLocalModuleUncached(options.importer, options.specifier);
113
+ options.cache.resolvedByImport.set(cacheKey, resolved);
114
+ return resolved;
115
+ }
116
+ async function resolveAppLocalModuleUncached(importer, specifier) {
117
+ const base = join(dirname(importer), specifier);
118
+ const candidates = sourceModuleCandidates(base);
119
+ if (candidates.length === 0) {
120
+ return undefined;
121
+ }
122
+ for (const candidate of candidates) {
123
+ if (await isFile(candidate)) {
124
+ return candidate;
125
+ }
126
+ }
127
+ throw new Error(`${importer}: could not resolve app-local import ${JSON.stringify(specifier)}.`);
128
+ }
129
+ function sourceModuleCandidates(base) {
130
+ if (hasSourceModuleExtension(base)) {
131
+ return [base, ...typescriptSourceModuleCandidates(base)];
132
+ }
133
+ if (extname(base) !== "") {
134
+ return [];
135
+ }
136
+ return [
137
+ `${base}.ts`,
138
+ `${base}.tsx`,
139
+ `${base}.mreact.tsx`,
140
+ `${base}.js`,
141
+ `${base}.jsx`,
142
+ `${base}.mjs`,
143
+ `${base}.mts`,
144
+ `${base}.cjs`,
145
+ `${base}.cts`,
146
+ join(base, "index.ts"),
147
+ join(base, "index.tsx"),
148
+ join(base, "index.mreact.tsx"),
149
+ join(base, "index.js"),
150
+ join(base, "index.jsx"),
151
+ join(base, "index.mjs"),
152
+ join(base, "index.mts"),
153
+ join(base, "index.cjs"),
154
+ join(base, "index.cts"),
155
+ ];
156
+ }
157
+ function hasSourceModuleExtension(path) {
158
+ return /\.(?:mreact\.tsx|tsx?|jsx?|mjs|mts|cjs|cts)$/.test(path);
159
+ }
160
+ function typescriptSourceModuleCandidates(path) {
161
+ if (path.endsWith(".js")) {
162
+ return [`${path.slice(0, -3)}.ts`, `${path.slice(0, -3)}.tsx`];
163
+ }
164
+ if (path.endsWith(".jsx")) {
165
+ return [`${path.slice(0, -4)}.tsx`];
166
+ }
167
+ if (path.endsWith(".mjs")) {
168
+ return [`${path.slice(0, -4)}.mts`];
169
+ }
170
+ if (path.endsWith(".cjs")) {
171
+ return [`${path.slice(0, -4)}.cts`];
172
+ }
173
+ return [];
174
+ }
175
+ async function isFile(path) {
176
+ try {
177
+ return (await stat(path)).isFile();
178
+ }
179
+ catch {
180
+ return false;
181
+ }
182
+ }
183
+ function readCachedFile(cache, filename) {
184
+ const cached = cache.sourceByFile.get(filename);
185
+ if (cached !== undefined) {
186
+ return cached;
187
+ }
188
+ const source = readFile(filename, "utf8");
189
+ cache.sourceByFile.set(filename, source);
190
+ return source;
191
+ }
192
+ function stripCommentsAndStringLiterals(code) {
193
+ let output = "";
194
+ let index = 0;
195
+ while (index < code.length) {
196
+ index = appendCodeOrMaskedLiteral(code, index, (value) => {
197
+ output += value;
198
+ });
199
+ }
200
+ return output;
201
+ }
202
+ function appendCodeOrMaskedLiteral(code, index, append) {
203
+ const char = code[index];
204
+ const next = code[index + 1];
205
+ if ((char === "'" || char === '"') && char !== undefined) {
206
+ return appendMaskedQuotedString(code, index, char, append);
207
+ }
208
+ if (char === "`") {
209
+ return appendMaskedTemplateLiteral(code, index, append);
210
+ }
211
+ if (char === "/" && next === "/") {
212
+ return appendMaskedLineComment(code, index, append);
213
+ }
214
+ if (char === "/" && next === "*") {
215
+ return appendMaskedBlockComment(code, index, append);
216
+ }
217
+ append(char ?? "");
218
+ return index + 1;
219
+ }
220
+ function appendMaskedQuotedString(code, start, quote, append) {
221
+ append(maskedChar(code[start] ?? ""));
222
+ let index = start + 1;
223
+ while (index < code.length) {
224
+ const char = code[index] ?? "";
225
+ append(maskedChar(char));
226
+ index += 1;
227
+ if (char === "\\") {
228
+ append(maskedChar(code[index] ?? ""));
229
+ index += 1;
230
+ continue;
231
+ }
232
+ if (char === quote) {
233
+ break;
234
+ }
235
+ }
236
+ return index;
237
+ }
238
+ function appendMaskedTemplateLiteral(code, start, append) {
239
+ append(maskedChar(code[start] ?? ""));
240
+ let index = start + 1;
241
+ while (index < code.length) {
242
+ const char = code[index] ?? "";
243
+ const next = code[index + 1];
244
+ if (char === "\\") {
245
+ append(maskedChar(char));
246
+ append(maskedChar(next ?? ""));
247
+ index += 2;
248
+ continue;
249
+ }
250
+ if (char === "`") {
251
+ append(maskedChar(char));
252
+ return index + 1;
253
+ }
254
+ if (char === "$" && next === "{") {
255
+ append(maskedChar(char));
256
+ append(maskedChar(next));
257
+ index = appendTemplateExpression(code, index + 2, append);
258
+ continue;
259
+ }
260
+ append(maskedChar(char));
261
+ index += 1;
262
+ }
263
+ return index;
264
+ }
265
+ function appendTemplateExpression(code, start, append) {
266
+ let index = start;
267
+ let depth = 1;
268
+ while (index < code.length) {
269
+ const char = code[index];
270
+ if (char === "{") {
271
+ depth += 1;
272
+ append(char);
273
+ index += 1;
274
+ continue;
275
+ }
276
+ if (char === "}") {
277
+ depth -= 1;
278
+ append(depth === 0 ? " " : char);
279
+ index += 1;
280
+ if (depth === 0) {
281
+ return index;
282
+ }
283
+ continue;
284
+ }
285
+ index = appendCodeOrMaskedLiteral(code, index, append);
286
+ }
287
+ return index;
288
+ }
289
+ function appendMaskedLineComment(code, start, append) {
290
+ let index = start;
291
+ while (index < code.length) {
292
+ const char = code[index] ?? "";
293
+ append(maskedChar(char));
294
+ index += 1;
295
+ if (char === "\n") {
296
+ break;
297
+ }
298
+ }
299
+ return index;
300
+ }
301
+ function appendMaskedBlockComment(code, start, append) {
302
+ append(maskedChar(code[start] ?? ""));
303
+ append(maskedChar(code[start + 1] ?? ""));
304
+ let index = start + 2;
305
+ while (index < code.length) {
306
+ const char = code[index] ?? "";
307
+ const next = code[index + 1];
308
+ append(maskedChar(char));
309
+ index += 1;
310
+ if (char === "*" && next === "/") {
311
+ append(maskedChar(next));
312
+ index += 1;
313
+ break;
314
+ }
315
+ }
316
+ return index;
317
+ }
318
+ function maskedChar(char) {
319
+ return char === "\n" || char === "\r" ? char : " ";
320
+ }
321
+ export function routeIdForPath(path) {
322
+ if (path === "/") {
323
+ return "index";
324
+ }
325
+ return path
326
+ .slice(1)
327
+ .replaceAll("/", "_")
328
+ .replaceAll(":", "_")
329
+ .replace(/[^A-Za-z0-9_$-]/g, "_");
330
+ }
331
+ export function clientScriptForPath(path) {
332
+ return `routes/${routeIdForPath(path)}.js`;
333
+ }
334
+ export function withHydrationMarkers(options) {
335
+ const marker = hydrationMarkerParts({
336
+ assetBaseUrl: options.assetBaseUrl,
337
+ clientReferenceManifest: options.clientReferenceManifest,
338
+ props: options.props,
339
+ routePath: options.routePath,
340
+ script: options.script,
341
+ });
342
+ return `${marker.prefix}${options.html}${marker.suffix}`;
343
+ }
344
+ export function withRouteMarkers(options) {
345
+ const routeId = routeIdForPath(options.routePath);
346
+ return `<div data-mreact-route-id="${escapeHtmlAttribute(routeId)}">${options.html}</div>`;
347
+ }
348
+ export function hydrationMarkerParts(options) {
349
+ const routeId = routeIdForPath(options.routePath);
350
+ const escapedRouteId = escapeHtmlAttribute(routeId);
351
+ const propsJson = escapeScriptJson(JSON.stringify(options.props));
352
+ const script = options.script ?? clientScriptForPath(options.routePath);
353
+ const scriptSrc = assetPath(script, options.assetBaseUrl ?? "/_mreact/client/");
354
+ const clientReferencesJson = options.clientReferenceManifest === undefined || options.clientReferenceManifest.length === 0
355
+ ? undefined
356
+ : escapeScriptJson(JSON.stringify(options.clientReferenceManifest));
357
+ return {
358
+ prefix: `<div data-mreact-route-id="${escapedRouteId}">`,
359
+ suffix: [
360
+ "</div>",
361
+ `<script type="application/json" id="mreact-props-${escapedRouteId}">${propsJson}</script>`,
362
+ clientReferencesJson === undefined
363
+ ? undefined
364
+ : `<script type="application/json" id="mreact-client-references-${escapedRouteId}">${clientReferencesJson}</script>`,
365
+ `<script type="module" src="${escapeHtmlAttribute(scriptSrc)}"></script>`,
366
+ ].filter((part) => part !== undefined).join(""),
367
+ };
368
+ }
369
+ export async function buildClientRouteBundle(options) {
370
+ return (await buildClientRouteOutput(options)).code;
371
+ }
372
+ export async function buildClientRouteOutput(options) {
373
+ const compiled = transform({
374
+ code: options.code,
375
+ filename: options.filename,
376
+ target: "client",
377
+ dev: options.minify !== true,
378
+ });
379
+ if (compiled.diagnostics.length > 0) {
380
+ throw new Error(`${options.filename}: ${compiled.diagnostics
381
+ .map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`)
382
+ .join("\n")}`);
383
+ }
384
+ const clientNavigation = options.clientNavigation ?? detectClientNavigationHint(options.code);
385
+ const clientReferenceManifest = options.clientReferenceManifest ?? await inferClientReferenceManifestForBundle(options);
386
+ const clientReferenceRegistry = emitClientReferenceRegistry(clientReferenceManifest);
387
+ const routeId = routeIdForPath(options.routePath);
388
+ const routeUsesCells = detectRouteCellStateHint(compiled.code);
389
+ const routeStateSignature = routeUsesCells ? routeStateSignatureForSource(compiled.code) : "";
390
+ const navigationStateDeclaration = clientNavigation
391
+ ? `const __mreactNavigationState = __mreactGlobal.__mreactNavigationState ??= {
392
+ cache: new Map(),
393
+ installed: false,
394
+ };`
395
+ : "";
396
+ const routeCellStateDeclaration = routeUsesCells
397
+ ? `const __mreactRouteStates = __mreactGlobal.__mreactRouteStates ??= new Map();
398
+ let __mreactActiveCellRecords = undefined;
399
+ let __mreactActiveCellIndex = 0;`
400
+ : "";
401
+ const routeCellHook = routeUsesCells
402
+ ? `
403
+ __mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
404
+ if (__mreactActiveCellRecords === undefined) {
405
+ return nativeCell(initial);
406
+ }
407
+
408
+ const cellKey = String(__mreactActiveCellIndex);
409
+ __mreactActiveCellIndex += 1;
410
+ const existingRecord = __mreactActiveCellRecords.get(cellKey);
411
+ const record = existingRecord ?? { value: initial };
412
+ const stateCell = nativeCell(record.value);
413
+ const setStateCell = stateCell.set;
414
+
415
+ stateCell.set = (next) => {
416
+ setStateCell((previous) => {
417
+ const resolved = typeof next === "function" ? next(previous) : next;
418
+ record.value = resolved;
419
+ return resolved;
420
+ });
421
+ };
422
+
423
+ __mreactActiveCellRecords.set(cellKey, record);
424
+ return stateCell;
425
+ };`
426
+ : "";
427
+ const routeCellHydrationStart = routeUsesCells
428
+ ? ` const __mreactPreviousState = __mreactRouteStates.get(__mreactRouteId);
429
+ const __mreactState = __mreactPreviousState?.marker === __mreactMarker &&
430
+ __mreactPreviousState?.signature === __mreactRouteStateSignature
431
+ ? __mreactPreviousState
432
+ : {
433
+ cells: new Map(),
434
+ marker: __mreactMarker,
435
+ signature: __mreactRouteStateSignature,
436
+ };
437
+ __mreactDropMismatchedRouteState(__mreactPreviousState, __mreactState);
438
+ __mreactRouteStates.set(__mreactRouteId, __mreactState);
439
+ __mreactActiveCellRecords = __mreactState.cells;
440
+ __mreactActiveCellIndex = 0;
441
+
442
+ try {
443
+ `
444
+ : "";
445
+ const routeCellHydrationEnd = routeUsesCells
446
+ ? ` } finally {
447
+ __mreactActiveCellRecords = undefined;
448
+ __mreactActiveCellIndex = 0;
449
+ }
450
+ `
451
+ : "";
452
+ const routeCellHydrationIndent = routeUsesCells ? " " : " ";
453
+ const routeCellDropFunction = routeUsesCells
454
+ ? `
455
+ function __mreactDropMismatchedRouteState(previousState, nextState) {
456
+ if (previousState === undefined || previousState === nextState) {
457
+ return;
458
+ }
459
+
460
+ if (previousState.signature !== nextState.signature && typeof console !== "undefined") {
461
+ console.warn("mreact: dropping stale route state after route cell signature changed");
462
+ }
463
+ }
464
+ `
465
+ : "";
466
+ const entry = `${compiled.code}
467
+
468
+ const __mreactRouteId = ${JSON.stringify(routeId)};
469
+ const __mreactRouteStateSignature = ${JSON.stringify(routeStateSignature)};
470
+ const __mreactGlobal = globalThis;
471
+ ${navigationStateDeclaration}
472
+ ${routeCellStateDeclaration}
473
+ ${routeCellHook}
474
+ ${clientReferenceRegistry}
475
+
476
+ export function __mreactHydrateRoute() {
477
+ __mreactApplyOutOfOrderFragments(document);
478
+ const __mreactMarker = document.querySelector(\`[data-mreact-route-id="\${__mreactRouteId}"]\`);
479
+ const __mreactPropsElement = document.getElementById(\`mreact-props-\${__mreactRouteId}\`);
480
+ const __mreactClientReferencesElement = document.getElementById(\`mreact-client-references-\${__mreactRouteId}\`);
481
+ const __mreactProps = __mreactPropsElement?.textContent === undefined
482
+ ? {}
483
+ : JSON.parse(__mreactPropsElement.textContent);
484
+ const __mreactClientReferences = __mreactClientReferencesElement?.textContent === undefined
485
+ ? []
486
+ : JSON.parse(__mreactClientReferencesElement.textContent);
487
+ const __mreactClientReferenceManifests = __mreactGlobal.__mreactClientReferenceManifests ??= new Map();
488
+ __mreactClientReferenceManifests.set(__mreactRouteId, __mreactClientReferences);
489
+ const __mreactComponent = typeof Page === "function"
490
+ ? Page
491
+ : typeof DefaultExport === "function"
492
+ ? DefaultExport
493
+ : undefined;
494
+
495
+ if (__mreactMarker === null || __mreactComponent === undefined) {
496
+ return;
497
+ }
498
+ ${routeCellHydrationStart}${routeCellHydrationIndent}if (__mreactHydrateClientBoundaries(__mreactMarker, __mreactClientReferences, __mreactClientReferenceComponents)) {
499
+ ${routeCellHydrationIndent} __mreactMarker.setAttribute("data-mreact-hydrated", "true");
500
+ ${routeCellHydrationIndent} return;
501
+ ${routeCellHydrationIndent}}
502
+ ${routeCellHydrationIndent}const __mreactNode = __mreactComponent(__mreactProps);
503
+ ${routeCellHydrationIndent}__mreactResumeRoute(__mreactMarker, __mreactNode);
504
+ ${routeCellHydrationIndent}__mreactMarker.setAttribute("data-mreact-hydrated", "true");
505
+ ${routeCellHydrationEnd}}
506
+ ${routeCellDropFunction}
507
+
508
+ __mreactHydrateRoute();
509
+ ${clientNavigation ? "__mreactInstallNavigation();" : ""}
510
+
511
+ ${clientNavigation
512
+ ? `export function __mreactNavigateToHtml(html, url) {
513
+ __mreactSaveCurrentHistoryState();
514
+ const applied = __mreactApplyNavigationHtml(html, url);
515
+
516
+ if (!applied) {
517
+ return false;
518
+ }
519
+
520
+ __mreactPushHistoryState(url);
521
+ __mreactScrollTo(0, 0);
522
+ return true;
523
+ }
524
+
525
+ export async function __mreactPrefetch(url) {
526
+ const href = __mreactNormalizeNavigationUrl(url);
527
+
528
+ if (href === undefined) {
529
+ return false;
530
+ }
531
+
532
+ if (__mreactNavigationState.cache.has(href)) {
533
+ return true;
534
+ }
535
+
536
+ const response = await fetch(href, {
537
+ headers: { "x-mreact-navigation": "1" },
538
+ });
539
+ __mreactApplyRevalidationHeader(response);
540
+ const html = await response.text();
541
+ __mreactNavigationState.cache.set(href, html);
542
+ return true;
543
+ }
544
+
545
+ export async function __mreactNavigate(url) {
546
+ const href = __mreactNormalizeNavigationUrl(url);
547
+
548
+ if (href === undefined) {
549
+ return false;
550
+ }
551
+
552
+ document.documentElement.setAttribute("data-mreact-navigation-pending", "true");
553
+
554
+ try {
555
+ const cachedHtml = __mreactNavigationState.cache.get(href);
556
+ const html = cachedHtml ?? await __mreactFetchNavigationHtml(href);
557
+
558
+ __mreactNavigationState.cache.set(href, html);
559
+ return __mreactNavigateToHtml(html, href);
560
+ } finally {
561
+ document.documentElement.removeAttribute("data-mreact-navigation-pending");
562
+ }
563
+ }
564
+
565
+ export function __mreactInvalidateNavigationCache(path) {
566
+ const normalizedPath = __mreactNormalizeNavigationPath(path);
567
+
568
+ if (normalizedPath === undefined) {
569
+ return;
570
+ }
571
+
572
+ for (const href of Array.from(__mreactNavigationState.cache.keys())) {
573
+ if (__mreactNormalizeNavigationPath(href) === normalizedPath) {
574
+ __mreactNavigationState.cache.delete(href);
575
+ }
576
+ }
577
+ }
578
+
579
+ function __mreactFetchNavigationHtml(href) {
580
+ return fetch(href, {
581
+ headers: { "x-mreact-navigation": "1" },
582
+ }).then((response) => {
583
+ __mreactApplyRevalidationHeader(response);
584
+ return response.text();
585
+ });
586
+ }
587
+
588
+ function __mreactApplyRevalidationHeader(response) {
589
+ const header = response.headers.get("x-mreact-revalidate");
590
+
591
+ if (header === null || header.trim() === "") {
592
+ return;
593
+ }
594
+
595
+ for (const path of header.split(",")) {
596
+ __mreactInvalidateNavigationCache(path.trim());
597
+ }
598
+ }
599
+
600
+ function __mreactNormalizeNavigationPath(path) {
601
+ if (typeof location === "undefined") {
602
+ return typeof path === "string" && path.length > 0 ? path : undefined;
603
+ }
604
+
605
+ try {
606
+ const url = new URL(path, location.href);
607
+ const pathname = url.pathname.replace(/\\/+$/, "");
608
+
609
+ return pathname === "" ? "/" : pathname;
610
+ } catch {
611
+ return undefined;
612
+ }
613
+ }
614
+
615
+ export function __mreactRestoreHistoryState(state) {
616
+ if (state === null || state === undefined || state.__mreact !== true || typeof state.html !== "string") {
617
+ return false;
618
+ }
619
+
620
+ const applied = __mreactApplyNavigationHtml(state.html, state.url);
621
+
622
+ if (!applied) {
623
+ return false;
624
+ }
625
+
626
+ __mreactScrollTo(Number(state.scrollX ?? 0), Number(state.scrollY ?? 0));
627
+ return true;
628
+ }
629
+
630
+ function __mreactApplyNavigationHtml(html, url) {
631
+ const template = document.createElement("template");
632
+ template.innerHTML = html.replace(/^\\s*<!doctype html>/i, "");
633
+ __mreactApplyOutOfOrderFragments(template.content);
634
+ const nextMarker = template.content.querySelector("[data-mreact-route-id]");
635
+ const currentMarker = document.querySelector("[data-mreact-route-id]");
636
+
637
+ if (nextMarker === null || currentMarker === null) {
638
+ return false;
639
+ }
640
+
641
+ __mreactResumeNode(currentMarker, nextMarker);
642
+
643
+ for (const propsElement of Array.from(document.querySelectorAll('script[type="application/json"][id^="mreact-props-"], script[type="application/json"][id^="mreact-client-references-"]'))) {
644
+ propsElement.remove();
645
+ }
646
+
647
+ for (const propsElement of Array.from(template.content.querySelectorAll('script[type="application/json"][id^="mreact-props-"], script[type="application/json"][id^="mreact-client-references-"]'))) {
648
+ document.body.appendChild(propsElement);
649
+ }
650
+
651
+ const script = template.content.querySelector('script[type="module"][src]')?.getAttribute("src");
652
+ if (script !== null && script !== undefined) {
653
+ void import(/* @vite-ignore */ script).then((module) => module.__mreactHydrateRoute?.());
654
+ }
655
+
656
+ __mreactApplyOutOfOrderFragments(document);
657
+
658
+ return true;
659
+ }
660
+
661
+ function __mreactCurrentHistoryState(url) {
662
+ return {
663
+ __mreact: true,
664
+ html: document.body.innerHTML,
665
+ scrollX: Number(globalThis.scrollX ?? 0),
666
+ scrollY: Number(globalThis.scrollY ?? 0),
667
+ url,
668
+ };
669
+ }
670
+
671
+ function __mreactPushHistoryState(url) {
672
+ if (typeof history === "undefined" || url === undefined) {
673
+ return;
674
+ }
675
+
676
+ try {
677
+ history.pushState(__mreactCurrentHistoryState(url), "", url);
678
+ } catch {
679
+ // Ignore invalid URLs in non-browser test environments.
680
+ }
681
+ }
682
+
683
+ function __mreactSaveCurrentHistoryState() {
684
+ if (typeof history === "undefined" || typeof location === "undefined") {
685
+ return;
686
+ }
687
+
688
+ try {
689
+ history.replaceState(__mreactCurrentHistoryState(location.href), "", location.href);
690
+ } catch {
691
+ // Ignore invalid URLs in non-browser test environments.
692
+ }
693
+ }
694
+
695
+ function __mreactNormalizeNavigationUrl(url) {
696
+ if (typeof location === "undefined") {
697
+ return typeof url === "string" ? url : undefined;
698
+ }
699
+
700
+ try {
701
+ return new URL(url, location.href).href;
702
+ } catch {
703
+ return undefined;
704
+ }
705
+ }
706
+
707
+ function __mreactScrollTo(x, y) {
708
+ if (typeof scrollTo === "function") {
709
+ scrollTo(x, y);
710
+ }
711
+ }
712
+
713
+ function __mreactInstallNavigation() {
714
+ if (__mreactNavigationState.installed || typeof document === "undefined") {
715
+ return;
716
+ }
717
+
718
+ __mreactNavigationState.installed = true;
719
+ __mreactSaveCurrentHistoryState();
720
+ addEventListener("popstate", (event) => {
721
+ if (!__mreactRestoreHistoryState(event.state)) {
722
+ location.reload();
723
+ }
724
+ });
725
+ document.addEventListener("pointerenter", (event) => {
726
+ const anchor = __mreactAnchorFromEvent(event);
727
+
728
+ if (anchor !== null && anchor.dataset.mreactPrefetch !== "false") {
729
+ void __mreactPrefetch(anchor.href);
730
+ }
731
+ }, true);
732
+ document.addEventListener("focusin", (event) => {
733
+ const anchor = __mreactAnchorFromEvent(event);
734
+
735
+ if (anchor !== null && anchor.dataset.mreactPrefetch !== "false") {
736
+ void __mreactPrefetch(anchor.href);
737
+ }
738
+ });
739
+ document.addEventListener("click", (event) => {
740
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
741
+ return;
742
+ }
743
+
744
+ const anchor = __mreactAnchorFromEvent(event);
745
+
746
+ if (anchor === null) {
747
+ return;
748
+ }
749
+
750
+ const nextUrl = new URL(anchor.href, location.href);
751
+
752
+ if (nextUrl.origin !== location.origin) {
753
+ return;
754
+ }
755
+
756
+ event.preventDefault();
757
+ void __mreactNavigate(nextUrl.href)
758
+ .then((navigated) => {
759
+ if (!navigated) {
760
+ location.href = nextUrl.href;
761
+ }
762
+ }).catch(() => {
763
+ location.href = nextUrl.href;
764
+ });
765
+ });
766
+ }
767
+
768
+ function __mreactAnchorFromEvent(event) {
769
+ const target = event.target;
770
+ const anchor = target instanceof Element ? target.closest("a[href]") : null;
771
+
772
+ if (!(anchor instanceof HTMLAnchorElement) || anchor.target !== "" || anchor.hasAttribute("download")) {
773
+ return null;
774
+ }
775
+
776
+ return anchor;
777
+ }
778
+ `
779
+ : ""}
780
+
781
+ function __mreactApplyOutOfOrderFragments(root) {
782
+ const fragments = Array.from(root.querySelectorAll("template[data-mreact-oob-fragment]"));
783
+
784
+ for (const fragment of fragments) {
785
+ const id = fragment.getAttribute("data-mreact-oob-fragment");
786
+
787
+ if (id === null) {
788
+ continue;
789
+ }
790
+
791
+ const placeholder = Array.from(root.querySelectorAll("[data-mreact-oob-placeholder]"))
792
+ .find((candidate) => candidate.getAttribute("data-mreact-oob-placeholder") === id);
793
+
794
+ if (placeholder === undefined) {
795
+ continue;
796
+ }
797
+
798
+ placeholder.replaceWith(fragment.content.cloneNode(true));
799
+ fragment.remove();
800
+ }
801
+ }
802
+
803
+ function __mreactHydrateClientBoundaries(marker, references, components) {
804
+ if (!Array.isArray(references) || references.length === 0) {
805
+ return false;
806
+ }
807
+
808
+ const placeholders = Array.from(marker.querySelectorAll("template[data-mreact-client-boundary]"));
809
+
810
+ if (placeholders.length === 0) {
811
+ return false;
812
+ }
813
+
814
+ for (const placeholder of placeholders) {
815
+ const name = placeholder.getAttribute("data-mreact-client-boundary");
816
+ const component = name === null ? undefined : components.get(name);
817
+
818
+ if (typeof component !== "function") {
819
+ return false;
820
+ }
821
+
822
+ const propsElement = __mreactClientBoundaryPropsElement(placeholder, name);
823
+ const props = propsElement?.textContent === undefined || propsElement.textContent === ""
824
+ ? {}
825
+ : JSON.parse(propsElement.textContent);
826
+ const node = component(props);
827
+
828
+ placeholder.replaceWith(node);
829
+ propsElement?.remove();
830
+ }
831
+
832
+ return true;
833
+ }
834
+
835
+ function __mreactClientBoundaryPropsElement(placeholder, name) {
836
+ let next = placeholder.nextSibling;
837
+
838
+ while (next !== null) {
839
+ if (
840
+ next.nodeType === Node.ELEMENT_NODE &&
841
+ next.tagName === "SCRIPT" &&
842
+ next.getAttribute("type") === "application/json" &&
843
+ next.getAttribute("data-mreact-client-boundary-props") === name
844
+ ) {
845
+ return next;
846
+ }
847
+
848
+ if (next.nodeType === Node.ELEMENT_NODE) {
849
+ return undefined;
850
+ }
851
+
852
+ next = next.nextSibling;
853
+ }
854
+
855
+ return undefined;
856
+ }
857
+
858
+ function __mreactResumeRoute(marker, nextNode) {
859
+ const current = __mreactRouteResumeTarget(marker, nextNode);
860
+
861
+ if (current === null) {
862
+ marker.appendChild(nextNode);
863
+ return;
864
+ }
865
+
866
+ __mreactResumeNode(current, nextNode);
867
+
868
+ if (current.parentNode !== marker) {
869
+ return;
870
+ }
871
+
872
+ while (marker.childNodes.length > 1) {
873
+ marker.lastChild?.remove();
874
+ }
875
+ }
876
+
877
+ function __mreactRouteResumeTarget(marker, nextNode) {
878
+ const current = marker.firstChild;
879
+
880
+ if (
881
+ current === null ||
882
+ current.nodeType !== Node.ELEMENT_NODE ||
883
+ nextNode.nodeType !== Node.ELEMENT_NODE ||
884
+ current.tagName === nextNode.tagName ||
885
+ !current.hasAttribute("data-mreact-layout-boundary")
886
+ ) {
887
+ return current;
888
+ }
889
+
890
+ return __mreactFindLayoutPageTarget(current, nextNode) ?? current;
891
+ }
892
+
893
+ function __mreactFindLayoutPageTarget(current, nextNode) {
894
+ for (const child of Array.from(current.childNodes)) {
895
+ if (child.nodeType !== Node.ELEMENT_NODE) {
896
+ continue;
897
+ }
898
+
899
+ if (
900
+ child.tagName === nextNode.tagName &&
901
+ !child.hasAttribute("data-mreact-layout-boundary") &&
902
+ !child.hasAttribute("data-mreact-template-boundary")
903
+ ) {
904
+ return child;
905
+ }
906
+
907
+ if (child.hasAttribute("data-mreact-layout-boundary")) {
908
+ const nested = __mreactFindLayoutPageTarget(child, nextNode);
909
+
910
+ if (nested !== null) {
911
+ return nested;
912
+ }
913
+ }
914
+ }
915
+
916
+ return null;
917
+ }
918
+
919
+ function __mreactResumeNode(current, next) {
920
+ if (
921
+ next.nodeType === Node.COMMENT_NODE &&
922
+ next.nodeValue === "mreact-async-boundary"
923
+ ) {
924
+ // Server stream emits the resolved <Await> content; preserve the existing
925
+ // DOM instead of replacing it with the client placeholder comment.
926
+ return;
927
+ }
928
+
929
+ if (__mreactShouldReplaceNode(current, next)) {
930
+ current.replaceWith(next);
931
+ return;
932
+ }
933
+
934
+ if (current.nodeType === Node.TEXT_NODE && next.nodeType === Node.TEXT_NODE) {
935
+ if (current.nodeValue !== next.nodeValue) {
936
+ current.nodeValue = next.nodeValue;
937
+ }
938
+ return;
939
+ }
940
+
941
+ if (current.nodeType !== Node.ELEMENT_NODE || next.nodeType !== Node.ELEMENT_NODE) {
942
+ current.replaceWith(next);
943
+ return;
944
+ }
945
+
946
+ __mreactSyncEventBindings(current, next);
947
+ __mreactSyncAttributes(current, next);
948
+ __mreactResumeChildren(current, next);
949
+ }
950
+
951
+ function __mreactShouldReplaceNode(current, next) {
952
+ if (
953
+ next.nodeType === Node.ELEMENT_NODE &&
954
+ next.hasAttribute("data-mreact-template-boundary")
955
+ ) {
956
+ return true;
957
+ }
958
+
959
+ if (current.nodeType !== next.nodeType) {
960
+ return true;
961
+ }
962
+
963
+ return current.nodeType === Node.ELEMENT_NODE &&
964
+ current.tagName !== next.tagName;
965
+ }
966
+
967
+ function __mreactSyncEventBindings(current, next) {
968
+ const previousDisposers = current.__mreactEventDisposers;
969
+
970
+ if (Array.isArray(previousDisposers)) {
971
+ for (const dispose of previousDisposers) {
972
+ dispose();
973
+ }
974
+ }
975
+
976
+ const bindings = next.__mreactEventBindings;
977
+
978
+ if (!Array.isArray(bindings) || bindings.length === 0) {
979
+ current.__mreactEventDisposers = [];
980
+ current.__mreactHasEvents = false;
981
+ return;
982
+ }
983
+
984
+ const disposers = [];
985
+
986
+ for (const binding of bindings) {
987
+ current.addEventListener(binding.type, binding.listener);
988
+ disposers.push(() => current.removeEventListener(binding.type, binding.listener));
989
+ }
990
+
991
+ current.__mreactEventDisposers = disposers;
992
+ current.__mreactHasEvents = true;
993
+ }
994
+
995
+ function __mreactSyncAttributes(current, next) {
996
+ for (const attribute of Array.from(current.attributes)) {
997
+ if (!next.hasAttribute(attribute.name)) {
998
+ current.removeAttribute(attribute.name);
999
+ }
1000
+ }
1001
+
1002
+ for (const attribute of Array.from(next.attributes)) {
1003
+ if (current.getAttribute(attribute.name) !== attribute.value) {
1004
+ current.setAttribute(attribute.name, attribute.value);
1005
+ }
1006
+ }
1007
+ }
1008
+
1009
+ function __mreactResumeChildren(current, next) {
1010
+ const nextChildren = Array.from(next.childNodes);
1011
+ const refreshTextBindings = next.__mreactHasEvents === true;
1012
+ let index = 0;
1013
+
1014
+ while (index < nextChildren.length) {
1015
+ const currentChild = current.childNodes[index];
1016
+ const nextChild = nextChildren[index];
1017
+
1018
+ if (currentChild === undefined) {
1019
+ current.appendChild(nextChild);
1020
+ index += 1;
1021
+ continue;
1022
+ }
1023
+
1024
+ // Text nodes that the client bound reactively must replace the
1025
+ // server's static text so subsequent updates land in the live DOM.
1026
+ const isReactiveText =
1027
+ nextChild.nodeType === Node.TEXT_NODE &&
1028
+ nextChild.__mreactReactiveText === true;
1029
+
1030
+ if (
1031
+ (refreshTextBindings || isReactiveText) &&
1032
+ currentChild.nodeType === Node.TEXT_NODE &&
1033
+ nextChild.nodeType === Node.TEXT_NODE
1034
+ ) {
1035
+ currentChild.replaceWith(nextChild);
1036
+ } else {
1037
+ __mreactResumeNode(currentChild, nextChild);
1038
+ }
1039
+ index += 1;
1040
+ }
1041
+
1042
+ while (current.childNodes.length > nextChildren.length) {
1043
+ current.lastChild?.remove();
1044
+ }
1045
+ }
1046
+ `;
1047
+ const bundled = await build({
1048
+ bundle: true,
1049
+ format: "esm",
1050
+ logLevel: "silent",
1051
+ minify: options.minify === true,
1052
+ define: {
1053
+ __MREACT_CLIENT_DEVTOOLS__: "false",
1054
+ },
1055
+ // issue 059: rename a strictly internal set of reactive-core / DOM scope
1056
+ // properties to single-character names. Each name in the allow-list is
1057
+ // reserved for cross-file internal state (not part of any public API
1058
+ // and not used by browser host objects), so global mangling is safe.
1059
+ // See packages/reactive-core/src/state.ts and reactive-dom/src/scope.ts.
1060
+ ...(options.minify === true
1061
+ ? {
1062
+ mangleProps: /^(singleSubscriber|subscribers|markDirty|pendingComputed|flushingComputed|nextComputationId|notificationDepth|batchDepth|activeTracker|deps|queued|disposed|disposers)$/,
1063
+ }
1064
+ : {}),
1065
+ outfile: "route.js",
1066
+ platform: "browser",
1067
+ plugins: [workspaceRuntimePlugin({ routeFile: options.filename })],
1068
+ sourcemap: options.sourceMap === true ? "external" : false,
1069
+ write: false,
1070
+ stdin: {
1071
+ contents: entry,
1072
+ loader: "tsx",
1073
+ resolveDir: dirname(options.filename),
1074
+ sourcefile: options.filename,
1075
+ },
1076
+ });
1077
+ const codeFile = bundled.outputFiles.find((file) => file.path.endsWith(".js"));
1078
+ const mapFile = bundled.outputFiles.find((file) => file.path.endsWith(".js.map"));
1079
+ return {
1080
+ code: codeFile?.text ?? bundled.outputFiles[0]?.text ?? "",
1081
+ ...(mapFile === undefined ? {} : { map: mapFile.text }),
1082
+ };
1083
+ }
1084
+ function workspaceRuntimePlugin(options) {
1085
+ const rootDir = join(dirname(fileURLToPath(import.meta.url)), "../../..");
1086
+ const routeDir = dirname(options.routeFile);
1087
+ const reactiveCorePath = join(rootDir, "packages/reactive-core/src/index.ts");
1088
+ const packageFile = (packageName, basename) => join(rootDir, "packages", packageName, "src", `${basename}.ts`);
1089
+ const runtimePaths = new Map([
1090
+ ["@reckona/mreact-compat", packageFile("react-compat", "index")],
1091
+ ["@reckona/mreact-compat/flight", packageFile("react-compat", "flight")],
1092
+ ["@reckona/mreact-compat/internal", packageFile("react-compat", "internal")],
1093
+ ["@reckona/mreact-compat/jsx-dev-runtime", packageFile("react-compat", "jsx-dev-runtime")],
1094
+ ["@reckona/mreact-compat/jsx-runtime", packageFile("react-compat", "jsx-runtime")],
1095
+ ["@reckona/mreact-compat/scheduler", packageFile("react-compat", "scheduler")],
1096
+ ["@reckona/mreact-reactive-dom", join(rootDir, "packages/reactive-dom/src/index.ts")],
1097
+ ]);
1098
+ return {
1099
+ name: "mreact-workspace-runtime",
1100
+ setup(buildApi) {
1101
+ buildApi.onResolve({ filter: /^\.\/devtools\.js$/ }, (args) => args.importer?.startsWith(join(rootDir, "packages/reactive-core/src/")) === true
1102
+ ? { namespace: "mreact-devtools-stub", path: "devtools" }
1103
+ : undefined);
1104
+ buildApi.onResolve({ filter: /^@reckona\/mreact-reactive-core$/ }, () => ({
1105
+ namespace: "mreact-hot-runtime",
1106
+ path: "reactive-core",
1107
+ }));
1108
+ buildApi.onResolve({
1109
+ filter: /^@reckona\/mreact-(?:compat|reactive-dom)(?:\/(?:flight|internal|jsx-dev-runtime|jsx-runtime|scheduler))?$/,
1110
+ }, (args) => {
1111
+ const path = runtimePaths.get(args.path);
1112
+ return path === undefined ? undefined : { path };
1113
+ });
1114
+ buildApi.onLoad({ filter: /^reactive-core$/, namespace: "mreact-hot-runtime" }, () => ({
1115
+ contents: `import { cell as nativeCell } from ${JSON.stringify(reactiveCorePath)};
1116
+ export * from ${JSON.stringify(reactiveCorePath)};
1117
+ export function cell(initial) {
1118
+ const routeCell = globalThis.__mreactRouteCell;
1119
+ return typeof routeCell === "function" ? routeCell(nativeCell, initial) : nativeCell(initial);
1120
+ }`,
1121
+ loader: "ts",
1122
+ resolveDir: rootDir,
1123
+ }));
1124
+ buildApi.onLoad({ filter: /^devtools$/, namespace: "mreact-devtools-stub" }, () => ({
1125
+ contents: `export function emitReactiveDevtoolsEvent() {}
1126
+ export function hasReactiveDevtoolsEmitter() { return false; }
1127
+ export function currentDevtoolsEmitter() { return undefined; }`,
1128
+ loader: "ts",
1129
+ }));
1130
+ buildApi.onLoad({ filter: /\.(?:mreact\.)?[cm]?[jt]sx$/ }, async (args) => {
1131
+ if (!isAppLocalSourcePath(args.path, routeDir) || args.path === options.routeFile) {
1132
+ return undefined;
1133
+ }
1134
+ const source = await readFile(args.path, "utf8");
1135
+ const output = transform({
1136
+ code: source,
1137
+ dev: true,
1138
+ filename: args.path,
1139
+ target: "client",
1140
+ });
1141
+ if (output.diagnostics.length > 0) {
1142
+ throw new Error(`${args.path}: ${output.diagnostics
1143
+ .map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`)
1144
+ .join("\n")}`);
1145
+ }
1146
+ return {
1147
+ contents: output.code,
1148
+ loader: "tsx",
1149
+ resolveDir: dirname(args.path),
1150
+ };
1151
+ });
1152
+ },
1153
+ };
1154
+ }
1155
+ function isAppLocalSourcePath(path, routeDir) {
1156
+ return path === routeDir || path.startsWith(`${routeDir}/`);
1157
+ }
1158
+ /**
1159
+ * Detects the `export const clientNavigation = false` hint in a page module
1160
+ * source. Returns the hinted value, or `true` when no hint is present (i.e.,
1161
+ * preserve the historical "navigation runtime is always present" behavior).
1162
+ *
1163
+ * Regex-based to avoid pulling the JS parser into the build path. The pattern
1164
+ * accepts the common formatting variants:
1165
+ * export const clientNavigation = false
1166
+ * export const clientNavigation = false ;
1167
+ * export const clientNavigation: boolean = false
1168
+ */
1169
+ export function detectClientNavigationHint(source) {
1170
+ const match = source.match(/export\s+const\s+clientNavigation\s*(?::\s*[^=]+)?=\s*(true|false)\s*;?/);
1171
+ return match === null ? true : match[1] === "true";
1172
+ }
1173
+ function detectRouteCellStateHint(code) {
1174
+ const callExpression = routeCellCallExpressionSource(code);
1175
+ return callExpression === undefined
1176
+ ? /\bcell\d*\s*\(/.test(code)
1177
+ : new RegExp(`(?:${callExpression})\\s*\\(`).test(code);
1178
+ }
1179
+ async function inferClientReferenceManifestForBundle(options) {
1180
+ const inference = await inferClientRouteModule({
1181
+ code: options.code,
1182
+ filename: options.filename,
1183
+ routePath: options.routePath,
1184
+ });
1185
+ if (inference.clientBoundaryImports.length === 0) {
1186
+ return [];
1187
+ }
1188
+ const output = transform({
1189
+ code: options.code,
1190
+ clientBoundaryImports: inference.clientBoundaryImports,
1191
+ dev: true,
1192
+ filename: options.filename,
1193
+ target: "server",
1194
+ });
1195
+ return output.metadata.clientReferenceManifest ?? [];
1196
+ }
1197
+ function emitClientReferenceRegistry(manifest) {
1198
+ const entries = manifest.flatMap((reference) => {
1199
+ const expression = clientReferenceExpression(reference.name);
1200
+ return expression === undefined
1201
+ ? []
1202
+ : [` [${JSON.stringify(reference.name)}, ${expression}],`];
1203
+ });
1204
+ return [
1205
+ "const __mreactClientReferenceComponents = new Map([",
1206
+ ...entries,
1207
+ "]);",
1208
+ ].join("\n");
1209
+ }
1210
+ function clientReferenceExpression(name) {
1211
+ return /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(name) ? name : undefined;
1212
+ }
1213
+ function routeStateSignatureForSource(code) {
1214
+ const callExpression = routeCellCallExpressionSource(code);
1215
+ const callsitePattern = callExpression === undefined
1216
+ ? /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(cell\d*|cell)\s*\(/g
1217
+ : new RegExp(`\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*(${callExpression})\\s*\\(`, "g");
1218
+ const cellCallsites = Array.from(code.matchAll(callsitePattern), (match) => `${match[1]}:${match[2]}`);
1219
+ const countPattern = callExpression === undefined
1220
+ ? /\bcell\d*\s*\(/g
1221
+ : new RegExp(`(?:${callExpression})\\s*\\(`, "g");
1222
+ const signature = cellCallsites.length > 0
1223
+ ? cellCallsites.join("\n")
1224
+ : `cell-count:${(code.match(countPattern) ?? []).length}`;
1225
+ return createHash("sha256").update(signature).digest("hex").slice(0, 16);
1226
+ }
1227
+ function routeCellCallExpressionSource(code) {
1228
+ const namedImports = new Set();
1229
+ const namespaceImports = new Set();
1230
+ const namedImportPattern = /import\s+\{(?<imports>[^}]*)\}\s+from\s+["']@reckona\/mreact-reactive-core["']/g;
1231
+ for (const match of code.matchAll(namedImportPattern)) {
1232
+ const imports = match.groups?.imports;
1233
+ if (imports === undefined) {
1234
+ continue;
1235
+ }
1236
+ for (const part of imports.split(",")) {
1237
+ const specifier = part.trim();
1238
+ const alias = /^cell\s+as\s+([A-Za-z_$][\w$]*)$/.exec(specifier);
1239
+ if (specifier === "cell") {
1240
+ namedImports.add("cell");
1241
+ }
1242
+ else if (alias?.[1] !== undefined) {
1243
+ namedImports.add(alias[1]);
1244
+ }
1245
+ }
1246
+ }
1247
+ const namespaceImportPattern = /import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s+["']@reckona\/mreact-reactive-core["']/g;
1248
+ for (const match of code.matchAll(namespaceImportPattern)) {
1249
+ if (match[1] !== undefined) {
1250
+ namespaceImports.add(match[1]);
1251
+ }
1252
+ }
1253
+ const alternatives = [
1254
+ ...Array.from(namedImports, (name) => `\\b${escapeRegExp(name)}`),
1255
+ ...Array.from(namespaceImports, (name) => `\\b${escapeRegExp(name)}\\.cell`),
1256
+ ];
1257
+ return alternatives.length === 0 ? undefined : `(?:${alternatives.join("|")})`;
1258
+ }
1259
+ function escapeRegExp(value) {
1260
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1261
+ }
1262
+ function errorMessage(error) {
1263
+ return error instanceof Error ? error.message : String(error);
1264
+ }
1265
+ function escapeScriptJson(value) {
1266
+ return value.replaceAll("<", "\\u003c");
1267
+ }
1268
+ //# sourceMappingURL=client.js.map