reroute-js 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 (136) hide show
  1. package/README.md +144 -0
  2. package/cli/bin.d.ts +3 -0
  3. package/cli/bin.d.ts.map +1 -0
  4. package/cli/bin.js +812 -0
  5. package/cli/bin.js.map +15 -0
  6. package/cli/src/commands/build.d.ts +8 -0
  7. package/cli/src/commands/build.d.ts.map +1 -0
  8. package/cli/src/commands/dev.d.ts +8 -0
  9. package/cli/src/commands/dev.d.ts.map +1 -0
  10. package/cli/src/commands/gen.d.ts +3 -0
  11. package/cli/src/commands/gen.d.ts.map +1 -0
  12. package/cli/src/commands/init.d.ts +8 -0
  13. package/cli/src/commands/init.d.ts.map +1 -0
  14. package/cli/src/index.d.ts +8 -0
  15. package/cli/src/index.d.ts.map +1 -0
  16. package/cli/src/libs/tailwind.d.ts +18 -0
  17. package/cli/src/libs/tailwind.d.ts.map +1 -0
  18. package/core/index.d.ts +2 -0
  19. package/core/index.d.ts.map +1 -0
  20. package/core/index.js +1174 -0
  21. package/core/index.js.map +25 -0
  22. package/core/src/bundler/hash.d.ts +2 -0
  23. package/core/src/bundler/hash.d.ts.map +1 -0
  24. package/core/src/bundler/index.d.ts +3 -0
  25. package/core/src/bundler/index.d.ts.map +1 -0
  26. package/core/src/bundler/transpile.d.ts +4 -0
  27. package/core/src/bundler/transpile.d.ts.map +1 -0
  28. package/core/src/content/builder.d.ts +2 -0
  29. package/core/src/content/builder.d.ts.map +1 -0
  30. package/core/src/content/discovery.d.ts +5 -0
  31. package/core/src/content/discovery.d.ts.map +1 -0
  32. package/core/src/content/index.d.ts +5 -0
  33. package/core/src/content/index.d.ts.map +1 -0
  34. package/core/src/content/metadata.d.ts +9 -0
  35. package/core/src/content/metadata.d.ts.map +1 -0
  36. package/core/src/content/registry.d.ts +2 -0
  37. package/core/src/content/registry.d.ts.map +1 -0
  38. package/core/src/index.d.ts +7 -0
  39. package/core/src/index.d.ts.map +1 -0
  40. package/core/src/ssr/data.d.ts +9 -0
  41. package/core/src/ssr/data.d.ts.map +1 -0
  42. package/core/src/ssr/index.d.ts +4 -0
  43. package/core/src/ssr/index.d.ts.map +1 -0
  44. package/core/src/ssr/modules.d.ts +8 -0
  45. package/core/src/ssr/modules.d.ts.map +1 -0
  46. package/core/src/ssr/render.d.ts +20 -0
  47. package/core/src/ssr/render.d.ts.map +1 -0
  48. package/core/src/ssr/seed.d.ts +2 -0
  49. package/core/src/ssr/seed.d.ts.map +1 -0
  50. package/core/src/template/html.d.ts +4 -0
  51. package/core/src/template/html.d.ts.map +1 -0
  52. package/core/src/template/index.d.ts +2 -0
  53. package/core/src/template/index.d.ts.map +1 -0
  54. package/core/src/types.d.ts +50 -0
  55. package/core/src/types.d.ts.map +1 -0
  56. package/core/src/utils/cache.d.ts +12 -0
  57. package/core/src/utils/cache.d.ts.map +1 -0
  58. package/core/src/utils/compression.d.ts +5 -0
  59. package/core/src/utils/compression.d.ts.map +1 -0
  60. package/core/src/utils/index.d.ts +5 -0
  61. package/core/src/utils/index.d.ts.map +1 -0
  62. package/core/src/utils/mime.d.ts +3 -0
  63. package/core/src/utils/mime.d.ts.map +1 -0
  64. package/core/src/utils/path.d.ts +6 -0
  65. package/core/src/utils/path.d.ts.map +1 -0
  66. package/elysia/index.d.ts +2 -0
  67. package/elysia/index.d.ts.map +1 -0
  68. package/elysia/index.js +1829 -0
  69. package/elysia/index.js.map +32 -0
  70. package/elysia/src/index.d.ts +3 -0
  71. package/elysia/src/index.d.ts.map +1 -0
  72. package/elysia/src/plugin.d.ts +32 -0
  73. package/elysia/src/plugin.d.ts.map +1 -0
  74. package/elysia/src/routes/artifacts.d.ts +3 -0
  75. package/elysia/src/routes/artifacts.d.ts.map +1 -0
  76. package/elysia/src/routes/content.d.ts +3 -0
  77. package/elysia/src/routes/content.d.ts.map +1 -0
  78. package/elysia/src/routes/dev.d.ts +7 -0
  79. package/elysia/src/routes/dev.d.ts.map +1 -0
  80. package/elysia/src/routes/ssr.d.ts +18 -0
  81. package/elysia/src/routes/ssr.d.ts.map +1 -0
  82. package/elysia/src/routes/static.d.ts +19 -0
  83. package/elysia/src/routes/static.d.ts.map +1 -0
  84. package/elysia/src/types.d.ts +31 -0
  85. package/elysia/src/types.d.ts.map +1 -0
  86. package/elysia/src/utils/http.d.ts +5 -0
  87. package/elysia/src/utils/http.d.ts.map +1 -0
  88. package/package.json +74 -0
  89. package/react/index.d.ts +2 -0
  90. package/react/index.d.ts.map +1 -0
  91. package/react/index.js +1152 -0
  92. package/react/index.js.map +23 -0
  93. package/react/src/components/ContentRoute.d.ts +13 -0
  94. package/react/src/components/ContentRoute.d.ts.map +1 -0
  95. package/react/src/components/Link.d.ts +8 -0
  96. package/react/src/components/Link.d.ts.map +1 -0
  97. package/react/src/components/Outlet.d.ts +7 -0
  98. package/react/src/components/Outlet.d.ts.map +1 -0
  99. package/react/src/components/index.d.ts +4 -0
  100. package/react/src/components/index.d.ts.map +1 -0
  101. package/react/src/hooks/index.d.ts +7 -0
  102. package/react/src/hooks/index.d.ts.map +1 -0
  103. package/react/src/hooks/useContent.d.ts +26 -0
  104. package/react/src/hooks/useContent.d.ts.map +1 -0
  105. package/react/src/hooks/useData.d.ts +10 -0
  106. package/react/src/hooks/useData.d.ts.map +1 -0
  107. package/react/src/hooks/useNavigate.d.ts +6 -0
  108. package/react/src/hooks/useNavigate.d.ts.map +1 -0
  109. package/react/src/hooks/useParams.d.ts +6 -0
  110. package/react/src/hooks/useParams.d.ts.map +1 -0
  111. package/react/src/hooks/useRouter.d.ts +7 -0
  112. package/react/src/hooks/useRouter.d.ts.map +1 -0
  113. package/react/src/hooks/useSearchParams.d.ts +6 -0
  114. package/react/src/hooks/useSearchParams.d.ts.map +1 -0
  115. package/react/src/index.d.ts +6 -0
  116. package/react/src/index.d.ts.map +1 -0
  117. package/react/src/providers/ContentProvider.d.ts +35 -0
  118. package/react/src/providers/ContentProvider.d.ts.map +1 -0
  119. package/react/src/providers/RerouteProvider.d.ts +25 -0
  120. package/react/src/providers/RerouteProvider.d.ts.map +1 -0
  121. package/react/src/providers/RouterProvider.d.ts +23 -0
  122. package/react/src/providers/RouterProvider.d.ts.map +1 -0
  123. package/react/src/providers/index.d.ts +4 -0
  124. package/react/src/providers/index.d.ts.map +1 -0
  125. package/react/src/types/any.d.ts +3 -0
  126. package/react/src/types/any.d.ts.map +1 -0
  127. package/react/src/types/index.d.ts +3 -0
  128. package/react/src/types/index.d.ts.map +1 -0
  129. package/react/src/types/router.d.ts +32 -0
  130. package/react/src/types/router.d.ts.map +1 -0
  131. package/react/src/utils/content.d.ts +8 -0
  132. package/react/src/utils/content.d.ts.map +1 -0
  133. package/react/src/utils/head.d.ts +6 -0
  134. package/react/src/utils/head.d.ts.map +1 -0
  135. package/react/src/utils/index.d.ts +3 -0
  136. package/react/src/utils/index.d.ts.map +1 -0
@@ -0,0 +1,1829 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __export = (target, all) => {
19
+ for (var name in all)
20
+ __defProp(target, name, {
21
+ get: all[name],
22
+ enumerable: true,
23
+ configurable: true,
24
+ set: (newValue) => all[name] = () => newValue
25
+ });
26
+ };
27
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
28
+
29
+ // packages/core/src/utils/path.ts
30
+ var exports_path = {};
31
+ __export(exports_path, {
32
+ stripStart: () => stripStart,
33
+ stripEnd: () => stripEnd,
34
+ join: () => join,
35
+ extname: () => extname,
36
+ basename: () => basename
37
+ });
38
+ function join(...parts) {
39
+ return parts.join("/").replace(/\/+/g, "/");
40
+ }
41
+ function extname(p) {
42
+ const i = p.lastIndexOf(".");
43
+ return i >= 0 ? p.slice(i) : "";
44
+ }
45
+ function basename(p, ext) {
46
+ const name = p.replace(/\\/g, "/").split("/").pop() || p;
47
+ return ext && name.endsWith(ext) ? name.slice(0, -ext.length) : name;
48
+ }
49
+ function stripStart(p, ch) {
50
+ return p.startsWith(ch) ? p.slice(ch.length) : p;
51
+ }
52
+ function stripEnd(p, ch) {
53
+ return p.endsWith(ch) ? p.slice(0, -ch.length) : p;
54
+ }
55
+
56
+ // packages/elysia/src/plugin.ts
57
+ import { watch as fsWatch } from "node:fs";
58
+
59
+ // packages/core/src/bundler/hash.ts
60
+ async function generateContentHash(content) {
61
+ const data = new TextEncoder().encode(content);
62
+ const buf = await crypto.subtle.digest("SHA-256", data);
63
+ let hex = "";
64
+ for (const b of new Uint8Array(buf))
65
+ hex += b.toString(16).padStart(2, "0");
66
+ return hex.slice(0, 8);
67
+ }
68
+ // packages/core/src/bundler/transpile.ts
69
+ async function transpileFile(filePath, originalPath, options, bundleCache) {
70
+ const src = await Bun.file(filePath).text();
71
+ const cacheKey = `${filePath}-${await generateContentHash(src)}`;
72
+ if (bundleCache.has(cacheKey)) {
73
+ return bundleCache.get(cacheKey);
74
+ }
75
+ console.log(`[reroute] Building ${originalPath}${options.minify ? " (minified)" : ""}...`);
76
+ async function doBuild(sm) {
77
+ return await Bun.build({
78
+ entrypoints: [filePath],
79
+ target: "browser",
80
+ format: "esm",
81
+ minify: options.minify,
82
+ splitting: false,
83
+ sourcemap: sm,
84
+ define: {
85
+ "process.env.NODE_ENV": options.minify ? '"production"' : '"development"'
86
+ }
87
+ });
88
+ }
89
+ let result;
90
+ try {
91
+ result = await doBuild(options.sourcemap ? "external" : "none");
92
+ if (!result.success) {
93
+ console.warn(`[reroute] Build failed for ${originalPath}, retrying without sourcemap...`);
94
+ result = await doBuild("none");
95
+ }
96
+ } catch (error) {
97
+ console.warn(`[reroute] Build errored for ${originalPath} (sourcemap external). Retrying without sourcemap...`, error);
98
+ result = await doBuild("none");
99
+ }
100
+ if (!result.success) {
101
+ console.error(`[reroute] Failed to build ${filePath}`);
102
+ for (const log of result.logs)
103
+ console.error(log);
104
+ throw new Error(`Failed to transpile ${filePath}`);
105
+ }
106
+ const output = result.outputs[0];
107
+ const code = await output.text();
108
+ let sourceMap;
109
+ if (options.sourcemap && result.outputs.length > 1) {
110
+ const mapOutput = result.outputs.find((o) => o.path.endsWith(".map"));
111
+ if (mapOutput) {
112
+ sourceMap = await mapOutput.text();
113
+ }
114
+ }
115
+ const contentHash = await generateContentHash(code);
116
+ const bundleInfo = {
117
+ hash: contentHash,
118
+ code,
119
+ sourceMap
120
+ };
121
+ bundleCache.set(cacheKey, bundleInfo);
122
+ const sizeKB = (code.length / 1024).toFixed(2);
123
+ const gzippedSize = Bun.gzipSync(new TextEncoder().encode(code)).length;
124
+ const gzippedKB = (gzippedSize / 1024).toFixed(2);
125
+ console.log(`[reroute] Built ${originalPath} -> ${sizeKB} KB (${gzippedKB} KB gzipped)`);
126
+ return bundleInfo;
127
+ }
128
+ async function getBundleUrlsFor(modules, clientDir, prefix, options, bundleCache) {
129
+ const mods = Array.isArray(modules) ? modules : [modules];
130
+ const urls = [];
131
+ const { join: join2 } = await Promise.resolve().then(() => exports_path);
132
+ for (const mod of mods) {
133
+ const rel = mod.replace(/^\.\//, "");
134
+ const fullPath = join2(clientDir, rel);
135
+ try {
136
+ const bundleInfo = await transpileFile(fullPath, rel, options, bundleCache);
137
+ const base = basename(rel, extname(rel));
138
+ urls.push(`${prefix}/${base}.${bundleInfo.hash}.js`);
139
+ } catch (error) {
140
+ console.error(`[reroute] Error getting bundle URL for ${rel}:`, error);
141
+ urls.push(`${prefix}/${rel}`);
142
+ }
143
+ }
144
+ return urls;
145
+ }
146
+ // packages/core/src/content/builder.ts
147
+ import { mkdir, readdir } from "node:fs/promises";
148
+ import { pathToFileURL } from "node:url";
149
+ async function sha8(text) {
150
+ const data = new TextEncoder().encode(text);
151
+ const buf = await crypto.subtle.digest("SHA-256", data);
152
+ let hex = "";
153
+ for (const b of new Uint8Array(buf))
154
+ hex += b.toString(16).padStart(2, "0");
155
+ return hex.slice(0, 8);
156
+ }
157
+ async function buildChunkFor(absSrc, cwd, collection, name) {
158
+ try {
159
+ const result = await Bun.build({
160
+ entrypoints: [absSrc],
161
+ target: "browser",
162
+ format: "esm",
163
+ splitting: false,
164
+ sourcemap: "none",
165
+ minify: false,
166
+ define: { "process.env.NODE_ENV": '"production"' }
167
+ });
168
+ if (!result.success)
169
+ return null;
170
+ const code = await result.outputs[0]?.text();
171
+ const hash2 = await sha8(code);
172
+ const relDir = join(".reroute", "chunks", collection);
173
+ const absDir = join(cwd, relDir);
174
+ const outFile = `${name}.${hash2}.js`;
175
+ const outPath = join(absDir, outFile);
176
+ await mkdir(absDir, { recursive: true });
177
+ const exists = await Bun.file(outPath).exists();
178
+ if (!exists)
179
+ await Bun.write(outPath, code);
180
+ const url = `/${join(relDir, outFile).replace(/\\+/g, "/")}`;
181
+ return { outPath, url, code };
182
+ } catch (e) {
183
+ console.error("[reroute] Failed to build content chunk:", absSrc, e);
184
+ return null;
185
+ }
186
+ }
187
+ async function getMetaFor(absSrc, isWatchMode) {
188
+ try {
189
+ const url = pathToFileURL(absSrc).href + (isWatchMode ? `?t=${Date.now()}` : "");
190
+ const m = await import(url);
191
+ return m.meta || m.frontmatter || {};
192
+ } catch {
193
+ return {};
194
+ }
195
+ }
196
+ async function rebuildContentArtifacts(cwd, clientDir, isWatchMode = true) {
197
+ const routesRoot = join(clientDir, "routes");
198
+ const items = [];
199
+ try {
200
+ const entries = await readdir(routesRoot, { withFileTypes: true });
201
+ for (const dir of entries) {
202
+ if (!dir.isDirectory())
203
+ continue;
204
+ const collection = dir.name;
205
+ const contentDir = join(routesRoot, collection, "content");
206
+ let contentFiles = [];
207
+ try {
208
+ const list = await readdir(contentDir, { withFileTypes: true });
209
+ contentFiles = list.filter((e) => e.isFile() && /\.(tsx|ts)$/.test(e.name) && !e.name.startsWith("_")).map((e) => e.name);
210
+ } catch {
211
+ continue;
212
+ }
213
+ for (const file of contentFiles) {
214
+ const name = file.replace(/\.(tsx|ts)$/, "");
215
+ const absSrc = join(contentDir, file);
216
+ const chunk = await buildChunkFor(absSrc, cwd, collection, name);
217
+ if (!chunk)
218
+ continue;
219
+ const meta = await getMetaFor(absSrc, isWatchMode);
220
+ items.push({
221
+ collection,
222
+ name,
223
+ slug: name,
224
+ href: `/${collection}/${name}`,
225
+ moduleUrl: chunk.url,
226
+ meta
227
+ });
228
+ }
229
+ }
230
+ } catch (e) {
231
+ console.error("[reroute] Failed to scan content routes:", e);
232
+ return;
233
+ }
234
+ try {
235
+ const lines = [];
236
+ lines.push("// \uD83D\uDEA8 Auto-generated by Reroute - DO NOT EDIT \uD83D\uDEA8");
237
+ lines.push("/* eslint-disable */");
238
+ lines.push("// @ts-nocheck");
239
+ lines.push("");
240
+ lines.push("export const contents = [");
241
+ for (const e of items) {
242
+ lines.push(" { collection: '" + e.collection + "', slug: '" + e.slug + "', name: '" + e.name + "', href: '" + e.href + "', module: '" + e.moduleUrl + "', meta: " + JSON.stringify(e.meta || {}) + " },");
243
+ }
244
+ lines.push("] as const;");
245
+ lines.push("");
246
+ lines.push("export const byCollection: Record<string, any[]> = {};");
247
+ lines.push("for (const c of contents) { (byCollection[c.collection] ||= []).push(c as any); }");
248
+ lines.push("");
249
+ lines.push("export const byCollectionAndName: Record<string, Record<string, any>> = {};");
250
+ lines.push("for (const c of contents) { ((byCollectionAndName[c.collection] ||= {})[c.name] = c as any); }");
251
+ lines.push("");
252
+ lines.push("export type Collections = keyof typeof byCollection;");
253
+ lines.push("export type Names<C extends Collections> = keyof (typeof byCollectionAndName)[C];");
254
+ lines.push("export type ContentEntry = (typeof contents)[number];");
255
+ lines.push("");
256
+ lines.push("export function getContentEntry<C extends string, N extends string>(collection: C, name: N) {");
257
+ lines.push(" const m = (byCollectionAndName as any)[collection];");
258
+ lines.push(" return m ? (m as Record<string, any>)[name] : undefined;");
259
+ lines.push("}");
260
+ lines.push("");
261
+ await mkdir(join(cwd, ".reroute"), { recursive: true });
262
+ await Bun.write(join(cwd, ".reroute", "content.ts"), lines.join(`
263
+ `));
264
+ } catch (e) {
265
+ console.error("[reroute] Failed to write .reroute/content.ts:", e);
266
+ }
267
+ try {
268
+ const byCol = new Map;
269
+ for (const it of items) {
270
+ const arr = byCol.get(it.collection) || [];
271
+ arr.push(it);
272
+ byCol.set(it.collection, arr);
273
+ }
274
+ await mkdir(join(cwd, ".reroute", "collections"), { recursive: true });
275
+ for (const [collection, list] of byCol) {
276
+ const js = [];
277
+ js.push("// \uD83D\uDEA8 Auto-generated by Reroute - DO NOT EDIT \uD83D\uDEA8");
278
+ js.push("");
279
+ js.push("export const items = [");
280
+ for (const e of list) {
281
+ js.push(` { collection: '${e.collection}', slug: '${e.slug}', name: '${e.name}', href: '${e.href}', module: '${e.moduleUrl}', meta: ${JSON.stringify(e.meta || {})} },`);
282
+ }
283
+ js.push("];");
284
+ js.push("");
285
+ js.push("export const byName = {};");
286
+ js.push("for (const it of items) { byName[it.name] = it; }");
287
+ js.push("");
288
+ js.push("export function get(collectionName, name) {");
289
+ js.push(` if (collectionName !== '${collection}') return undefined;`);
290
+ js.push(" return byName[name];");
291
+ js.push("}");
292
+ js.push("");
293
+ await Bun.write(join(cwd, ".reroute", "collections", `${collection}.js`), js.join(`
294
+ `));
295
+ }
296
+ } catch (e) {
297
+ console.error("[reroute] Failed to write .reroute/collections/*:", e);
298
+ }
299
+ console.log("[reroute] Content artifacts up-to-date");
300
+ }
301
+ // packages/core/src/content/discovery.ts
302
+ import { readdir as readdir2, stat as stat2 } from "node:fs/promises";
303
+
304
+ // packages/core/src/content/metadata.ts
305
+ import { stat } from "node:fs/promises";
306
+ import { pathToFileURL as pathToFileURL2 } from "node:url";
307
+ async function getContentMeta(absPath, isWatchMode) {
308
+ try {
309
+ const url = pathToFileURL2(absPath).href;
310
+ const mod = await import(`${url}${isWatchMode ? `?t=${Date.now()}` : ""}`);
311
+ const meta = mod.meta || mod.frontmatter || {};
312
+ if (meta && typeof meta === "object")
313
+ return meta;
314
+ return {};
315
+ } catch {
316
+ try {
317
+ const s = await stat(absPath);
318
+ return { date: new Date(s.mtimeMs).toISOString() };
319
+ } catch {
320
+ return {};
321
+ }
322
+ }
323
+ }
324
+ function buildHeadFromMeta(meta) {
325
+ if (!meta || typeof meta !== "object")
326
+ return "";
327
+ const parts = [];
328
+ const title = typeof meta.title === "string" ? meta.title : undefined;
329
+ const description = typeof meta.description === "string" ? meta.description : typeof meta.excerpt === "string" ? meta.excerpt : undefined;
330
+ if (title)
331
+ parts.push(`<title>${escapeHtml(title)}</title>`);
332
+ if (description)
333
+ parts.push(`<meta name="description" content="${escapeHtml(description)}" />`);
334
+ return parts.length ? `
335
+ ${parts.join(`
336
+ `)}` : "";
337
+ }
338
+ function escapeHtml(input) {
339
+ return input.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
340
+ }
341
+
342
+ // packages/core/src/content/discovery.ts
343
+ async function listContentFiles(dir, baseRel) {
344
+ const out = [];
345
+ try {
346
+ const entries = await readdir2(dir, { withFileTypes: true });
347
+ for (const entry of entries) {
348
+ const full = join(dir, entry.name);
349
+ const rel = join(baseRel, entry.name);
350
+ if (entry.isDirectory()) {
351
+ const nested = await listContentFiles(full, rel);
352
+ out.push(...nested);
353
+ } else if (entry.isFile()) {
354
+ if (/\.(tsx|ts)$/.test(entry.name) && !entry.name.startsWith("_")) {
355
+ out.push(rel);
356
+ }
357
+ }
358
+ }
359
+ } catch {}
360
+ return out;
361
+ }
362
+ async function buildContentDTOs(collectionPath, clientDir, prefix, isWatchMode) {
363
+ const normalized = stripStart(stripEnd(collectionPath, "/"), "/");
364
+ const contentRelDir = join("routes", normalized, "content");
365
+ const contentAbsDir = join(clientDir, contentRelDir);
366
+ const files = await listContentFiles(contentAbsDir, "");
367
+ const items = [];
368
+ for (const rel of files) {
369
+ const fullRelPath = join(contentRelDir, rel);
370
+ const absPath = join(clientDir, fullRelPath);
371
+ const noExt = rel.replace(/\.(tsx|ts)$/, "");
372
+ const name = basename(noExt);
373
+ const moduleUrl = `${prefix}/${fullRelPath}`.replace(/\/+/g, "/");
374
+ const meta = await getContentMeta(absPath, isWatchMode);
375
+ const href = `/${normalized}/${name}`.replace(/\\+/g, "/");
376
+ items.push({
377
+ slug: noExt,
378
+ name,
379
+ path: fullRelPath,
380
+ module: moduleUrl,
381
+ meta,
382
+ href
383
+ });
384
+ }
385
+ return items;
386
+ }
387
+ async function discoverCollections(clientDir) {
388
+ const root = join(clientDir, "routes");
389
+ const collections = new Set;
390
+ try {
391
+ const entries = await readdir2(root, { withFileTypes: true });
392
+ for (const entry of entries) {
393
+ if (entry.isDirectory()) {
394
+ const contentDir = join(root, entry.name, "content");
395
+ try {
396
+ await stat2(contentDir);
397
+ collections.add(entry.name);
398
+ } catch {}
399
+ }
400
+ }
401
+ } catch {}
402
+ return Array.from(collections);
403
+ }
404
+ // packages/core/src/content/registry.ts
405
+ async function generateContentRegistry(cwd) {
406
+ try {
407
+ const p = join(cwd, ".reroute", "content.ts");
408
+ const exists = await Bun.file(p).exists();
409
+ console.log(exists ? "[reroute] Content registry up-to-date" : "[reroute] Warning: .reroute/content.ts not found");
410
+ } catch {}
411
+ }
412
+ // packages/core/src/ssr/data.ts
413
+ import { pathToFileURL as pathToFileURL4 } from "node:url";
414
+
415
+ // packages/core/src/ssr/modules.ts
416
+ import { readdir as readdir3, stat as stat3 } from "node:fs/promises";
417
+ import { pathToFileURL as pathToFileURL3 } from "node:url";
418
+ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode) {
419
+ try {
420
+ const parts = pathname.split("/").filter(Boolean);
421
+ if (parts.length < 2)
422
+ return null;
423
+ const collection = parts[0];
424
+ const name = parts[1];
425
+ try {
426
+ const registryPath = join(cwd, ".reroute", "content.ts");
427
+ const reg = await import(pathToFileURL3(registryPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
428
+ const get = reg?.getContentEntry;
429
+ const entry = typeof get === "function" ? get(collection, name) : undefined;
430
+ const moduleUrl = entry?.module;
431
+ if (moduleUrl?.endsWith(".js")) {
432
+ const abs = join(cwd, moduleUrl.replace(/^\//, ""));
433
+ const href = pathToFileURL3(abs).href + (isWatchMode ? `?t=${Date.now()}` : "");
434
+ const mod = await import(href);
435
+ return mod;
436
+ }
437
+ } catch {}
438
+ try {
439
+ const chunkDir = join(cwd, ".reroute", "chunks", collection);
440
+ const files = await readdir3(chunkDir);
441
+ const matches = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
442
+ if (matches.length) {
443
+ let latest = matches[0];
444
+ let latestM = 0;
445
+ for (const f of matches) {
446
+ try {
447
+ const s = await stat3(join(chunkDir, f));
448
+ if (s.mtimeMs >= latestM) {
449
+ latestM = s.mtimeMs;
450
+ latest = f;
451
+ }
452
+ } catch {}
453
+ }
454
+ const absChunk = join(chunkDir, latest);
455
+ const href = pathToFileURL3(absChunk).href + (isWatchMode ? `?t=${Date.now()}` : "");
456
+ const mod = await import(href);
457
+ return mod;
458
+ }
459
+ } catch {}
460
+ try {
461
+ const srcTsx = join(clientDir, "routes", collection, "content", `${name}.tsx`);
462
+ const srcTs = join(clientDir, "routes", collection, "content", `${name}.ts`);
463
+ let absSrc = null;
464
+ if (await Bun.file(srcTsx).exists())
465
+ absSrc = srcTsx;
466
+ else if (await Bun.file(srcTs).exists())
467
+ absSrc = srcTs;
468
+ if (absSrc) {
469
+ const href = pathToFileURL3(absSrc).href;
470
+ const mod = await (isWatchMode ? import(`${href}?t=${Date.now()}`) : import(href));
471
+ return mod;
472
+ }
473
+ } catch {}
474
+ } catch {}
475
+ return null;
476
+ }
477
+
478
+ // packages/core/src/ssr/seed.ts
479
+ async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
480
+ try {
481
+ const parts = pathname.split("/").filter(Boolean);
482
+ if (parts.length < 2)
483
+ return;
484
+ const collection = parts[0];
485
+ const name = parts[1];
486
+ const key = `${collection}:${name}`;
487
+ const mod = await importContentModuleForPath(pathname, clientDir, cwd, isWatchMode);
488
+ if (!mod)
489
+ return;
490
+ const C = mod.default || mod;
491
+ globalThis.__REROUTE_SSR_MODULES__ = globalThis.__REROUTE_SSR_MODULES__ || {};
492
+ globalThis.__REROUTE_SSR_MODULES__[key] = C;
493
+ try {
494
+ const pageMeta = mod.meta || mod.frontmatter || {};
495
+ const pageSSR = mod.ssr || {};
496
+ globalThis.__REROUTE_SSR_EXPORTS__ = globalThis.__REROUTE_SSR_EXPORTS__ || {};
497
+ globalThis.__REROUTE_SSR_EXPORTS__[key] = {
498
+ meta: pageMeta,
499
+ ssr: pageSSR
500
+ };
501
+ } catch {}
502
+ } catch {}
503
+ }
504
+
505
+ // packages/core/src/ssr/data.ts
506
+ async function computeSSRDataForPath(params) {
507
+ const { pathname, clientDir, cwd, isWatchMode } = params;
508
+ try {
509
+ await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
510
+ } catch {}
511
+ try {
512
+ const parts = pathname.split("/").filter(Boolean);
513
+ if (parts.length >= 2) {
514
+ const key = `${parts[0]}:${parts[1]}`;
515
+ const g = globalThis;
516
+ const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
517
+ const dataFn = exp?.ssr?.data;
518
+ if (typeof dataFn === "function") {
519
+ return await dataFn({ pathname, params: { name: parts[1] } });
520
+ }
521
+ }
522
+ } catch {}
523
+ try {
524
+ const routesPath = join(cwd, ".reroute", "routes.ts");
525
+ const m = await import(pathToFileURL4(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
526
+ const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
527
+ const r = match?.route;
528
+ const paramsValue = match?.params || {};
529
+ if (r && typeof r.path === "string") {
530
+ try {
531
+ const abs = join(clientDir, "routes", String(r.path));
532
+ const mod = await import(pathToFileURL4(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
533
+ const ssr = mod?.ssr;
534
+ const dataFn = ssr?.data;
535
+ if (typeof dataFn === "function") {
536
+ return await dataFn({ pathname, params: paramsValue });
537
+ }
538
+ } catch {}
539
+ }
540
+ } catch {}
541
+ return null;
542
+ }
543
+ // packages/core/src/ssr/render.ts
544
+ import { readdir as readdir4, stat as stat4 } from "node:fs/promises";
545
+ import { pathToFileURL as pathToFileURL5 } from "node:url";
546
+
547
+ // node_modules/dedent/dist/dedent.mjs
548
+ function ownKeys(object, enumerableOnly) {
549
+ var keys = Object.keys(object);
550
+ if (Object.getOwnPropertySymbols) {
551
+ var symbols = Object.getOwnPropertySymbols(object);
552
+ enumerableOnly && (symbols = symbols.filter(function(sym) {
553
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
554
+ })), keys.push.apply(keys, symbols);
555
+ }
556
+ return keys;
557
+ }
558
+ function _objectSpread(target) {
559
+ for (var i = 1;i < arguments.length; i++) {
560
+ var source = arguments[i] != null ? arguments[i] : {};
561
+ i % 2 ? ownKeys(Object(source), true).forEach(function(key) {
562
+ _defineProperty(target, key, source[key]);
563
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
564
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
565
+ });
566
+ }
567
+ return target;
568
+ }
569
+ function _defineProperty(obj, key, value) {
570
+ key = _toPropertyKey(key);
571
+ if (key in obj) {
572
+ Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
573
+ } else {
574
+ obj[key] = value;
575
+ }
576
+ return obj;
577
+ }
578
+ function _toPropertyKey(arg) {
579
+ var key = _toPrimitive(arg, "string");
580
+ return typeof key === "symbol" ? key : String(key);
581
+ }
582
+ function _toPrimitive(input, hint) {
583
+ if (typeof input !== "object" || input === null)
584
+ return input;
585
+ var prim = input[Symbol.toPrimitive];
586
+ if (prim !== undefined) {
587
+ var res = prim.call(input, hint || "default");
588
+ if (typeof res !== "object")
589
+ return res;
590
+ throw new TypeError("@@toPrimitive must return a primitive value.");
591
+ }
592
+ return (hint === "string" ? String : Number)(input);
593
+ }
594
+ var dedent = createDedent({});
595
+ var dedent_default = dedent;
596
+ function createDedent(options) {
597
+ dedent2.withOptions = (newOptions) => createDedent(_objectSpread(_objectSpread({}, options), newOptions));
598
+ return dedent2;
599
+ function dedent2(strings, ...values) {
600
+ const raw = typeof strings === "string" ? [strings] : strings.raw;
601
+ const {
602
+ alignValues = false,
603
+ escapeSpecialCharacters = Array.isArray(strings),
604
+ trimWhitespace = true
605
+ } = options;
606
+ let result = "";
607
+ for (let i = 0;i < raw.length; i++) {
608
+ let next = raw[i];
609
+ if (escapeSpecialCharacters) {
610
+ next = next.replace(/\\\n[ \t]*/g, "").replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\\{/g, "{");
611
+ }
612
+ result += next;
613
+ if (i < values.length) {
614
+ const value = alignValues ? alignValue(values[i], result) : values[i];
615
+ result += value;
616
+ }
617
+ }
618
+ const lines = result.split(`
619
+ `);
620
+ let mindent = null;
621
+ for (const l of lines) {
622
+ const m = l.match(/^(\s+)\S+/);
623
+ if (m) {
624
+ const indent = m[1].length;
625
+ if (!mindent) {
626
+ mindent = indent;
627
+ } else {
628
+ mindent = Math.min(mindent, indent);
629
+ }
630
+ }
631
+ }
632
+ if (mindent !== null) {
633
+ const m = mindent;
634
+ result = lines.map((l) => l[0] === " " || l[0] === "\t" ? l.slice(m) : l).join(`
635
+ `);
636
+ }
637
+ if (trimWhitespace) {
638
+ result = result.trim();
639
+ }
640
+ if (escapeSpecialCharacters) {
641
+ result = result.replace(/\\n/g, `
642
+ `);
643
+ }
644
+ return result;
645
+ }
646
+ }
647
+ function alignValue(value, precedingText) {
648
+ if (typeof value !== "string" || !value.includes(`
649
+ `)) {
650
+ return value;
651
+ }
652
+ const currentLine = precedingText.slice(precedingText.lastIndexOf(`
653
+ `) + 1);
654
+ const indentMatch = currentLine.match(/^(\s+)/);
655
+ if (indentMatch) {
656
+ const indent = indentMatch[1];
657
+ return value.replace(/\n/g, `
658
+ ${indent}`);
659
+ }
660
+ return value;
661
+ }
662
+
663
+ // packages/core/src/ssr/render.ts
664
+ import { cloneElement } from "react";
665
+ import { renderToString } from "react-dom/server";
666
+
667
+ // packages/core/src/template/html.ts
668
+ async function loadIndexHtml(clientDir) {
669
+ const templatePath = join(clientDir, "index.html");
670
+ try {
671
+ const content = await Bun.file(templatePath).text();
672
+ return content;
673
+ } catch (error) {
674
+ console.error(`[reroute] Failed to load template from ${templatePath}:`, error);
675
+ return `<!doctype html>
676
+ <html lang="en">
677
+ <head>
678
+ <meta charset="UTF-8" />
679
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
680
+ <title>Reroute</title>
681
+ </head>
682
+ <body>
683
+ <div id="root"></div>
684
+ </body>
685
+ </html>`;
686
+ }
687
+ }
688
+ function applyIndexTemplate(templateHtml, appHtml, {
689
+ head = "",
690
+ hydrationScript = "",
691
+ lang = "en",
692
+ appId = "root"
693
+ }) {
694
+ let html = templateHtml;
695
+ html = html.replace(/<html([^>]*)>/i, (_m, attrs) => {
696
+ const hasLang = /(^|\s)lang\s*=/.test(attrs);
697
+ const newAttrs = hasLang ? attrs.replace(/lang\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/i, `lang="${lang}"`) : `${attrs} lang="${lang}"`;
698
+ return `<html${newAttrs}>`;
699
+ });
700
+ let headToInject = head || "";
701
+ try {
702
+ const titleMatch = headToInject.match(/<title[\s\S]*?<\/title>/i);
703
+ if (titleMatch) {
704
+ const titleTag = titleMatch[0];
705
+ if (/<title[\s\S]*?<\/title>/i.test(html)) {
706
+ html = html.replace(/<title[\s\S]*?<\/title>/i, titleTag);
707
+ } else {}
708
+ headToInject = headToInject.replace(titleTag, "");
709
+ }
710
+ const descRe = /<meta\s+name\s*=\s*['"]description['"][^>]*>/i;
711
+ const descMatch = headToInject.match(descRe);
712
+ if (descMatch) {
713
+ const descTag = descMatch[0];
714
+ if (descRe.test(html)) {
715
+ html = html.replace(descRe, descTag);
716
+ }
717
+ headToInject = headToInject.replace(descTag, "");
718
+ }
719
+ } catch {}
720
+ if (headToInject) {
721
+ html = html.replace(/<\/head>/i, `${headToInject}
722
+ </head>`);
723
+ }
724
+ html = html.replace(/<script\b[^>]*src\s*=\s*["'][^"']+\.(ts|tsx)(?:[?#][^"']*)?["'][^>]*>\s*<\/script>/gi, "");
725
+ const appIdEscaped = appId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
726
+ const rootDivRegex = new RegExp(`(<div[^>]*\\bid=(\\"|')${appIdEscaped}\\2[^>]*>)([\\s\\S]*?)(<\\/div>)`, "i");
727
+ if (rootDivRegex.test(html)) {
728
+ html = html.replace(rootDivRegex, `$1${appHtml}$4`);
729
+ } else {
730
+ html = html.replace(/<\/body>/i, ` <div id="${appId}">${appHtml}</div>
731
+ </body>`);
732
+ }
733
+ if (hydrationScript) {
734
+ html = html.replace(/<\/body>/i, ` ${hydrationScript}
735
+ </body>`);
736
+ }
737
+ return html;
738
+ }
739
+ // packages/core/src/ssr/render.ts
740
+ async function renderSSRDocument(options) {
741
+ const {
742
+ pathname,
743
+ rootComponent,
744
+ clientDir,
745
+ cwd,
746
+ isWatchMode,
747
+ bundleUrl,
748
+ head = "",
749
+ lang = "en",
750
+ appId = "root",
751
+ minify = false
752
+ } = options;
753
+ const scripts = [bundleUrl];
754
+ let hydrationScript = "";
755
+ let extraHead = "";
756
+ let statusOverride;
757
+ try {
758
+ globalThis.__REROUTE_SSR_ACCESSED__ = {};
759
+ } catch {}
760
+ try {
761
+ const parts = pathname.split("/").filter(Boolean);
762
+ if (parts.length >= 2) {
763
+ const collection = parts[0];
764
+ const name = parts[1];
765
+ let modulePath = "";
766
+ let isContentCollection = false;
767
+ try {
768
+ const maybeDir = join(clientDir, "routes", collection, "content");
769
+ const s = await stat4(maybeDir);
770
+ isContentCollection = typeof s?.isDirectory === "function" ? s.isDirectory() : true;
771
+ } catch {
772
+ isContentCollection = false;
773
+ }
774
+ if (!isContentCollection) {
775
+ throw new Error("skip-content-preload");
776
+ }
777
+ try {
778
+ const registryPath = join(cwd, ".reroute", "content.ts");
779
+ const reg = await import(pathToFileURL5(registryPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
780
+ const get = reg?.getContentEntry;
781
+ const entry = typeof get === "function" ? get(collection, name) : undefined;
782
+ const moduleUrl = entry?.module;
783
+ if (moduleUrl?.endsWith(".js")) {
784
+ modulePath = moduleUrl;
785
+ }
786
+ } catch {}
787
+ if (!modulePath) {
788
+ try {
789
+ const chunkDir = join(cwd, ".reroute", "chunks", collection);
790
+ const files = await readdir4(chunkDir);
791
+ const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
792
+ if (candidates.length) {
793
+ let latest = candidates[0];
794
+ let latestM = 0;
795
+ for (const candidateName of candidates) {
796
+ try {
797
+ const s = await stat4(join(chunkDir, candidateName));
798
+ if (s.mtimeMs >= latestM) {
799
+ latestM = s.mtimeMs;
800
+ latest = candidateName;
801
+ }
802
+ } catch {}
803
+ }
804
+ modulePath = `/${join(".reroute", "chunks", collection, latest).replace(/\\+/g, "/")}`;
805
+ }
806
+ } catch {}
807
+ }
808
+ if (!modulePath) {
809
+ const tsx = join(clientDir, "routes", collection, "content", `${name}.tsx`);
810
+ const ts = join(clientDir, "routes", collection, "content", `${name}.ts`);
811
+ let srcUrl = "";
812
+ if (await Bun.file(tsx).exists()) {
813
+ srcUrl = `/${join("routes", collection, "content", `${name}.tsx`).replace(/\\+/g, "/")}`;
814
+ } else if (await Bun.file(ts).exists()) {
815
+ srcUrl = `/${join("routes", collection, "content", `${name}.ts`).replace(/\\+/g, "/")}`;
816
+ }
817
+ if (srcUrl) {
818
+ modulePath = srcUrl;
819
+ }
820
+ }
821
+ if (!modulePath) {
822
+ statusOverride = 404;
823
+ }
824
+ if (modulePath) {
825
+ const key = `${collection}:${name}`;
826
+ extraHead += `
827
+ <link rel="modulepreload" href="${modulePath}" />`;
828
+ const qs = `src=${encodeURIComponent(modulePath)}&key=${encodeURIComponent(key)}`;
829
+ hydrationScript += `<script type="module" src="/__reroute_preload?${qs}"></script>`;
830
+ }
831
+ }
832
+ } catch {}
833
+ await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
834
+ const __SSR_DATA__ = {};
835
+ try {
836
+ try {
837
+ const parts = pathname.split("/").filter(Boolean);
838
+ if (parts.length >= 2) {
839
+ const g = globalThis;
840
+ const key = `${parts[0]}:${parts[1]}`;
841
+ const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
842
+ const dataFn = exp?.ssr?.data;
843
+ if (typeof dataFn === "function") {
844
+ const out = await dataFn({
845
+ pathname,
846
+ params: { name: parts[1] }
847
+ });
848
+ __SSR_DATA__[pathname] = out;
849
+ }
850
+ }
851
+ } catch {}
852
+ try {
853
+ const routesPath = join(cwd, ".reroute", "routes.ts");
854
+ const m = await import(pathToFileURL5(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
855
+ const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
856
+ const r = match?.route;
857
+ const params = match?.params || {};
858
+ if (r && typeof r.path === "string") {
859
+ try {
860
+ const abs = join(clientDir, "routes", String(r.path));
861
+ const mod = await import(pathToFileURL5(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
862
+ const ssr = mod?.ssr;
863
+ const dataFn = ssr?.data;
864
+ if (typeof dataFn === "function") {
865
+ const out = await dataFn({ pathname, params });
866
+ __SSR_DATA__[pathname] = out;
867
+ }
868
+ } catch {}
869
+ }
870
+ } catch {}
871
+ } catch {}
872
+ try {
873
+ globalThis.__REROUTE_DATA__ = __SSR_DATA__;
874
+ } catch {}
875
+ let __byCollectionForSSR = {};
876
+ try {
877
+ const p = join(cwd, ".reroute", "content.ts");
878
+ const mod = await import(pathToFileURL5(p).href + (isWatchMode ? `?t=${Date.now()}` : ""));
879
+ __byCollectionForSSR = mod?.byCollection || {};
880
+ try {
881
+ globalThis.__REROUTE_COLLECTIONS__ = __byCollectionForSSR;
882
+ } catch {}
883
+ } catch {}
884
+ const componentWithPathname = cloneElement(rootComponent, {
885
+ pathname
886
+ });
887
+ const appHtml = renderToString(componentWithPathname);
888
+ const baseTemplate = await loadIndexHtml(clientDir);
889
+ let inlineStyleTag = "";
890
+ try {
891
+ const candidates = [
892
+ join(clientDir, "..", ".reroute", "theme.css"),
893
+ join(clientDir, "..", "..", ".reroute", "theme.css"),
894
+ join(clientDir, "..", "..", "..", ".reroute", "theme.css"),
895
+ join(clientDir, "..", "..", "..", "..", ".reroute", "theme.css")
896
+ ];
897
+ let cssPath = "";
898
+ for (const p of candidates) {
899
+ if (await Bun.file(p).exists()) {
900
+ cssPath = p;
901
+ break;
902
+ }
903
+ }
904
+ if (cssPath) {
905
+ let css = await Bun.file(cssPath).text();
906
+ if (minify) {
907
+ css = css.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/\s*([{}:;,>+~])\s*/g, "$1").replace(/;}/g, "}").trim();
908
+ }
909
+ inlineStyleTag = `<style data-reroute="tailwind">${css}</style>`;
910
+ }
911
+ } catch {}
912
+ try {
913
+ const g = globalThis;
914
+ const acc = g.__REROUTE_SSR_ACCESSED__;
915
+ const sortByDate = (order) => (a, b) => {
916
+ const da = a?.meta?.date ? Date.parse(String(a.meta.date)) : 0;
917
+ const db = b?.meta?.date ? Date.parse(String(b.meta.date)) : 0;
918
+ return order === "asc" ? da - db : db - da;
919
+ };
920
+ const usage = {};
921
+ if (acc && typeof acc.forEach === "function") {
922
+ acc.forEach((c) => {
923
+ if (typeof c === "string")
924
+ usage[c] = { limit: Number.POSITIVE_INFINITY, sort: "custom" };
925
+ });
926
+ } else if (acc && typeof acc === "object") {
927
+ for (const [k, v] of Object.entries(acc)) {
928
+ const lim = typeof v?.limit === "number" ? v.limit : Number.POSITIVE_INFINITY;
929
+ const s = typeof v?.sort === "string" ? v.sort : "custom";
930
+ usage[k] = { limit: lim, sort: s };
931
+ }
932
+ }
933
+ const collections = Object.keys(usage);
934
+ if (collections.length) {
935
+ const subset = {};
936
+ const partial = {};
937
+ for (const c of collections) {
938
+ const conf = usage[c];
939
+ const full = __byCollectionForSSR?.[c] || [];
940
+ let arr = full;
941
+ if (Number.isFinite(conf.limit) && conf.limit > 0 && conf.sort !== "custom") {
942
+ if (conf.sort === "date-desc") {
943
+ arr = [...full].sort(sortByDate("desc")).slice(0, conf.limit);
944
+ } else if (conf.sort === "date-asc") {
945
+ arr = [...full].sort(sortByDate("asc")).slice(0, conf.limit);
946
+ } else if (conf.sort === "none") {
947
+ arr = full.slice(0, conf.limit);
948
+ }
949
+ }
950
+ subset[c] = arr;
951
+ partial[c] = Array.isArray(arr) && Array.isArray(full) ? arr.length < full.length : false;
952
+ }
953
+ const subsetJson = JSON.stringify(subset);
954
+ const partialJson = JSON.stringify(partial);
955
+ hydrationScript += `
956
+ <script>
957
+ (function(){ try {
958
+ var w = (typeof window!== 'undefined'? window : globalThis);
959
+ w.__REROUTE_COLLECTIONS__ = w.__REROUTE_COLLECTIONS__ || {};
960
+ Object.assign(w.__REROUTE_COLLECTIONS__, ${subsetJson});
961
+ w.__REROUTE_COLLECTIONS_PARTIAL__ = w.__REROUTE_COLLECTIONS_PARTIAL__ || {};
962
+ Object.assign(w.__REROUTE_COLLECTIONS_PARTIAL__, ${partialJson});
963
+ } catch(e){} })();
964
+ </script>`;
965
+ }
966
+ } catch {}
967
+ try {
968
+ const seededJson = JSON.stringify(__SSR_DATA__);
969
+ hydrationScript += `
970
+ <script>
971
+ (function(){ try {
972
+ var w = (typeof window!== 'undefined'? window : globalThis);
973
+ w.__REROUTE_DATA__ = Object.assign({}, w.__REROUTE_DATA__ || {}, ${seededJson});
974
+ } catch(e){} })();
975
+ </script>`;
976
+ } catch {}
977
+ hydrationScript += scripts.map((src) => `<script type="module" src="${src}"></script>`).join("");
978
+ if (isWatchMode) {
979
+ hydrationScript += `<script type="module" src="/__reroute_watch.js"></script>`;
980
+ }
981
+ let perPageHead = "";
982
+ let pageLang;
983
+ try {
984
+ const parts = pathname.split("/").filter(Boolean);
985
+ if (parts.length >= 2) {
986
+ const key = `${parts[0]}:${parts[1]}`;
987
+ const g = globalThis;
988
+ const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
989
+ const meta = exp?.meta;
990
+ const ssr = exp?.ssr;
991
+ perPageHead += buildHeadFromMeta(meta);
992
+ const ssrHead = typeof ssr?.head === "string" ? ssr.head : Array.isArray(ssr?.head) ? String(ssr.head.join(`
993
+ `)) : "";
994
+ if (ssrHead)
995
+ perPageHead += `
996
+ ${ssrHead}`;
997
+ if (typeof ssr?.lang === "string" && ssr.lang.trim()) {
998
+ pageLang = ssr.lang.trim();
999
+ }
1000
+ }
1001
+ } catch {}
1002
+ try {
1003
+ const routesPath = join(cwd, ".reroute", "routes.ts");
1004
+ const m = await import(pathToFileURL5(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
1005
+ const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
1006
+ const r = match?.route;
1007
+ if (!r) {
1008
+ statusOverride = statusOverride || 404;
1009
+ }
1010
+ if (r && typeof r.path === "string") {
1011
+ try {
1012
+ const abs = join(clientDir, "routes", String(r.path));
1013
+ const mod = await import(pathToFileURL5(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
1014
+ const meta = mod?.meta;
1015
+ const ssr = mod?.ssr;
1016
+ if (meta)
1017
+ perPageHead += buildHeadFromMeta(meta);
1018
+ if (ssr) {
1019
+ const ssrHead = typeof ssr.head === "string" ? ssr.head : Array.isArray(ssr.head) ? String(ssr.head.join(`
1020
+ `)) : "";
1021
+ if (ssrHead)
1022
+ perPageHead += `
1023
+ ${ssrHead}`;
1024
+ if (typeof ssr.lang === "string" && ssr.lang.trim()) {
1025
+ pageLang = ssr.lang.trim();
1026
+ }
1027
+ }
1028
+ } catch {}
1029
+ } else {
1030
+ try {
1031
+ const list = m?.notFoundRoutes;
1032
+ if (Array.isArray(list)) {
1033
+ let chosen;
1034
+ let bestLen = -1;
1035
+ for (const nf of list) {
1036
+ const pat = String(nf?.pattern || "/");
1037
+ if (!pathname.startsWith(pat))
1038
+ continue;
1039
+ const len = pat.split("/").filter(Boolean).length;
1040
+ if (len >= bestLen) {
1041
+ bestLen = len;
1042
+ chosen = nf;
1043
+ }
1044
+ }
1045
+ if (chosen && typeof chosen.path === "string") {
1046
+ try {
1047
+ const abs = join(clientDir, "routes", String(chosen.path));
1048
+ const mod = await import(pathToFileURL5(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
1049
+ const meta = mod?.meta;
1050
+ const ssr = mod?.ssr;
1051
+ if (meta)
1052
+ perPageHead += buildHeadFromMeta(meta);
1053
+ const ssrHead = typeof ssr?.head === "string" ? ssr.head : Array.isArray(ssr?.head) ? String(ssr.head.join(`
1054
+ `)) : "";
1055
+ if (ssrHead)
1056
+ perPageHead += `
1057
+ ${ssrHead}`;
1058
+ if (typeof ssr?.lang === "string" && ssr.lang?.trim()) {
1059
+ pageLang = String(ssr.lang).trim();
1060
+ }
1061
+ } catch {}
1062
+ }
1063
+ }
1064
+ } catch {}
1065
+ }
1066
+ } catch {}
1067
+ const combinedHead = dedent_default([dedent_default(head) || "", dedent_default(extraHead), dedent_default(perPageHead)].filter(Boolean).join(`
1068
+ `));
1069
+ const html2 = applyIndexTemplate(baseTemplate, appHtml, {
1070
+ head: [inlineStyleTag, combinedHead].filter(Boolean).join(`
1071
+ `),
1072
+ hydrationScript,
1073
+ lang: pageLang || lang,
1074
+ appId
1075
+ });
1076
+ return {
1077
+ html: html2,
1078
+ status: statusOverride || 200
1079
+ };
1080
+ }
1081
+ // packages/core/src/utils/cache.ts
1082
+ class LRUCache {
1083
+ cache;
1084
+ maxSize;
1085
+ constructor(maxSize = 100) {
1086
+ this.cache = new Map;
1087
+ this.maxSize = maxSize;
1088
+ }
1089
+ get(key) {
1090
+ if (!this.cache.has(key)) {
1091
+ return;
1092
+ }
1093
+ const value = this.cache.get(key);
1094
+ this.cache.delete(key);
1095
+ this.cache.set(key, value);
1096
+ return value;
1097
+ }
1098
+ set(key, value) {
1099
+ if (this.cache.has(key)) {
1100
+ this.cache.delete(key);
1101
+ }
1102
+ this.cache.set(key, value);
1103
+ if (this.cache.size > this.maxSize) {
1104
+ const firstKey = this.cache.keys().next().value;
1105
+ this.cache.delete(firstKey);
1106
+ }
1107
+ }
1108
+ has(key) {
1109
+ return this.cache.has(key);
1110
+ }
1111
+ clear() {
1112
+ this.cache.clear();
1113
+ }
1114
+ get size() {
1115
+ return this.cache.size;
1116
+ }
1117
+ }
1118
+ // packages/core/src/utils/mime.ts
1119
+ function getMimeType(filePath) {
1120
+ const ext = filePath.split(".").pop()?.toLowerCase();
1121
+ const mimeTypes = {
1122
+ html: "text/html",
1123
+ css: "text/css",
1124
+ js: "application/javascript",
1125
+ ts: "application/javascript",
1126
+ json: "application/json",
1127
+ png: "image/png",
1128
+ jpg: "image/jpeg",
1129
+ jpeg: "image/jpeg",
1130
+ gif: "image/gif",
1131
+ svg: "image/svg+xml",
1132
+ ico: "image/x-icon",
1133
+ woff: "font/woff",
1134
+ woff2: "font/woff2",
1135
+ ttf: "font/ttf"
1136
+ };
1137
+ return mimeTypes[ext || ""] || "application/octet-stream";
1138
+ }
1139
+ function isCompressible(contentType) {
1140
+ if (!contentType)
1141
+ return false;
1142
+ const ct = contentType.split(";")[0].trim();
1143
+ return ct.startsWith("text/") || ct === "application/javascript" || ct === "application/json" || ct === "application/xml" || ct === "image/svg+xml";
1144
+ }
1145
+
1146
+ // packages/core/src/utils/compression.ts
1147
+ function acceptsGzip(acceptEncoding) {
1148
+ return Boolean(acceptEncoding && /gzip/i.test(acceptEncoding));
1149
+ }
1150
+ function toBytes(input) {
1151
+ return typeof input === "string" ? new TextEncoder().encode(input) : input;
1152
+ }
1153
+ function gzipIfAccepted(body, contentType, acceptEncoding) {
1154
+ const extraHeaders = {};
1155
+ if (acceptsGzip(acceptEncoding) && isCompressible(contentType)) {
1156
+ try {
1157
+ const compressed = Bun.gzipSync(toBytes(body));
1158
+ extraHeaders["Content-Encoding"] = "gzip";
1159
+ extraHeaders.Vary = "Accept-Encoding";
1160
+ return { body: compressed, extraHeaders };
1161
+ } catch {}
1162
+ }
1163
+ return { body, extraHeaders };
1164
+ }
1165
+ // packages/elysia/src/routes/artifacts.ts
1166
+ import { NotFoundError } from "elysia";
1167
+ function registerArtifactsRoutes(app, cwd) {
1168
+ app.get("/.reroute/*", async ({ params, headers }) => {
1169
+ const sub = String(params["*"] || "").replace(/^\/+/, "");
1170
+ if (!sub)
1171
+ throw new NotFoundError;
1172
+ const abs = join(cwd, ".reroute", sub);
1173
+ try {
1174
+ const exists = await Bun.file(abs).exists();
1175
+ if (!exists)
1176
+ throw new NotFoundError;
1177
+ const buf = await Bun.file(abs).arrayBuffer();
1178
+ const content2 = new Uint8Array(buf);
1179
+ const contentType = getMimeType(abs);
1180
+ const { body, extraHeaders } = gzipIfAccepted(content2, contentType, headers["accept-encoding"]);
1181
+ return new Response(body, {
1182
+ headers: {
1183
+ "Content-Type": contentType,
1184
+ "Cache-Control": "public, max-age=31536000, immutable",
1185
+ ...extraHeaders
1186
+ }
1187
+ });
1188
+ } catch (e) {
1189
+ if (e instanceof NotFoundError)
1190
+ throw e;
1191
+ console.error("[reroute] Failed to serve .reroute file:", abs, e);
1192
+ throw new NotFoundError;
1193
+ }
1194
+ });
1195
+ return app;
1196
+ }
1197
+
1198
+ // packages/elysia/src/routes/content.ts
1199
+ import { NotFoundError as NotFoundError2 } from "elysia";
1200
+ function registerContentRoutes(app, clientDir, prefix, isWatchMode) {
1201
+ app.get("/__content/*", async ({ params, set }) => {
1202
+ const collectionPath = String(params["*"] || "").trim();
1203
+ if (!collectionPath)
1204
+ throw new NotFoundError2;
1205
+ const items = await buildContentDTOs(collectionPath, clientDir, prefix, isWatchMode);
1206
+ const body = JSON.stringify({ collection: collectionPath, items });
1207
+ set.status = 200;
1208
+ set.headers["content-type"] = "application/json; charset=utf-8";
1209
+ return new Response(body);
1210
+ });
1211
+ return app;
1212
+ }
1213
+
1214
+ // packages/elysia/src/routes/dev.ts
1215
+ import { NotFoundError as NotFoundError3 } from "elysia";
1216
+
1217
+ // packages/elysia/src/utils/http.ts
1218
+ var makeETag = (s) => {
1219
+ try {
1220
+ const h = globalThis?.Bun?.hash?.(s) ?? 0;
1221
+ return `W/"${s.length.toString(16)}-${Number(h).toString(36)}"`;
1222
+ } catch {
1223
+ return `W/"${s.length.toString(16)}-0"`;
1224
+ }
1225
+ };
1226
+ var getIfNoneMatch = (headers) => {
1227
+ try {
1228
+ if (!headers)
1229
+ return;
1230
+ const h = headers;
1231
+ if (typeof h?.get === "function")
1232
+ return String(h.get("if-none-match") || "");
1233
+ return String(h["if-none-match"] || h["If-None-Match"] || "");
1234
+ } catch {
1235
+ return;
1236
+ }
1237
+ };
1238
+ function jsonWithCache(payload, cacheControl, reqHeaders) {
1239
+ const body = typeof payload === "string" ? payload : JSON.stringify(payload);
1240
+ const etag = makeETag(body);
1241
+ const inm = getIfNoneMatch(reqHeaders) || "";
1242
+ if (etag && inm && inm === etag) {
1243
+ return new Response(null, {
1244
+ status: 304,
1245
+ headers: {
1246
+ ETag: etag,
1247
+ "Cache-Control": cacheControl
1248
+ }
1249
+ });
1250
+ }
1251
+ return new Response(body, {
1252
+ headers: {
1253
+ "Content-Type": "application/json; charset=utf-8",
1254
+ "Cache-Control": cacheControl,
1255
+ ETag: etag
1256
+ }
1257
+ });
1258
+ }
1259
+ function jsonError(message, status = 500) {
1260
+ return new Response(JSON.stringify({ error: message }), {
1261
+ status,
1262
+ headers: { "Content-Type": "application/json; charset=utf-8" }
1263
+ });
1264
+ }
1265
+
1266
+ // packages/elysia/src/routes/dev.ts
1267
+ function registerDevRoutes(app, liveReloadClients, isWatchMode, opts) {
1268
+ app.get("/__reroute_preload", async ({ query }) => {
1269
+ const rawSrc = String(query.src || "").trim();
1270
+ const key = String(query.key || "").trim();
1271
+ let src2 = rawSrc;
1272
+ try {
1273
+ src2 = decodeURIComponent(rawSrc);
1274
+ } catch {}
1275
+ const safeChunk = src2.startsWith("/.reroute/chunks/") && /^[A-Za-z0-9_./-]+\.js$/.test(src2);
1276
+ const safeSource = src2.startsWith("/routes/") && /^[A-Za-z0-9_./\-[\]]+\.(tsx?|js)$/.test(src2);
1277
+ const safe = (safeChunk || safeSource) && !src2.includes("..");
1278
+ if (!safe || !key)
1279
+ throw new NotFoundError3;
1280
+ const code = `// preload for ${key}
1281
+ ` + `import * as __m from '${src2}';
1282
+ ` + `const __C = __m.default || __m;
1283
+ ` + `(globalThis.__REROUTE_MODULES__ ||= {});
1284
+ ` + `globalThis.__REROUTE_MODULES__['${key}'] = __C;
1285
+ ` + `export {};`;
1286
+ return new Response(code, {
1287
+ headers: {
1288
+ "content-type": "application/javascript; charset=utf-8",
1289
+ "cache-control": isWatchMode ? "no-cache" : "public, max-age=31536000, immutable"
1290
+ }
1291
+ });
1292
+ });
1293
+ app.get("/__reroute_data", async ({ query, headers }) => {
1294
+ const p = String(query.path || query.p || "/");
1295
+ let pathname = "/";
1296
+ try {
1297
+ const u = new URL(p, "http://localhost");
1298
+ pathname = u.pathname + u.search;
1299
+ } catch {
1300
+ pathname = String(p);
1301
+ }
1302
+ try {
1303
+ const data2 = await computeSSRDataForPath({
1304
+ pathname,
1305
+ clientDir: opts?.clientDir || "",
1306
+ cwd: opts?.cwd || "",
1307
+ isWatchMode
1308
+ });
1309
+ return jsonWithCache({ data: data2 }, opts?.dataCacheControl || "no-cache", headers);
1310
+ } catch (_e) {
1311
+ return jsonError("failed", 500);
1312
+ }
1313
+ });
1314
+ if (!isWatchMode)
1315
+ return app;
1316
+ console.log("[reroute] Registering SSE endpoint at /__reroute_watch");
1317
+ app.get("/__reroute_watch", () => {
1318
+ const encoder = new TextEncoder;
1319
+ let interval;
1320
+ const stream = new ReadableStream({
1321
+ start(controller) {
1322
+ liveReloadClients.add(controller);
1323
+ console.log("[reroute] Browser connected for live reload");
1324
+ controller.enqueue(encoder.encode(`retry: 1000
1325
+ `));
1326
+ controller.enqueue(encoder.encode(`data: connected
1327
+
1328
+ `));
1329
+ interval = setInterval(() => {
1330
+ try {
1331
+ controller.enqueue(encoder.encode(`: ping
1332
+
1333
+ `));
1334
+ } catch {}
1335
+ }, 25000);
1336
+ },
1337
+ cancel() {
1338
+ if (interval)
1339
+ clearInterval(interval);
1340
+ console.log("[reroute] Browser disconnected from live reload");
1341
+ }
1342
+ });
1343
+ return new Response(stream, {
1344
+ headers: {
1345
+ "Content-Type": "text/event-stream",
1346
+ "Cache-Control": "no-cache",
1347
+ Connection: "keep-alive"
1348
+ }
1349
+ });
1350
+ });
1351
+ app.get("/__reroute_watch.js", () => {
1352
+ const code = `// live-reload client
1353
+ ` + `const url='/__reroute_watch';
1354
+ ` + `let es; let reloading=false;
1355
+ ` + `function waitServerThenReload(){
1356
+ ` + ` const ping = () => fetch(location.origin, { cache: 'no-store' })
1357
+ ` + ` .then(()=>location.reload())
1358
+ ` + ` .catch(()=>setTimeout(ping, 300));
1359
+ ` + ` ping();
1360
+ ` + `}
1361
+ ` + `function connect(){
1362
+ ` + ` es=new EventSource(url);
1363
+ ` + ` es.onopen=()=>console.log('[reroute] watching changes');
1364
+ ` + ` es.onmessage=(e)=>{ if(e.data==='reload'){ reloading=true; try{ es.close(); }catch{} waitServerThenReload(); } };
1365
+ ` + ` es.onerror=()=>{ if(!reloading){ reloading=true; try{ es.close(); }catch{} waitServerThenReload(); } };
1366
+ ` + `}
1367
+ ` + `connect();
1368
+ export {};`;
1369
+ return new Response(code, {
1370
+ headers: {
1371
+ "content-type": "application/javascript; charset=utf-8",
1372
+ "cache-control": "no-cache"
1373
+ }
1374
+ });
1375
+ });
1376
+ return app;
1377
+ }
1378
+
1379
+ // packages/elysia/src/routes/ssr.ts
1380
+ import { NotFoundError as NotFoundError4 } from "elysia";
1381
+ function registerSSRRoutes(app, options) {
1382
+ const {
1383
+ rootComponent,
1384
+ clientDir,
1385
+ cwd,
1386
+ isWatchMode,
1387
+ getBundleUrl,
1388
+ head = "",
1389
+ lang = "en",
1390
+ appId = "root",
1391
+ collections,
1392
+ minify = false,
1393
+ dataCacheControl
1394
+ } = options;
1395
+ for (const collection of collections) {
1396
+ app.get(`/${collection}/*`, async ({ request }) => {
1397
+ const method = request.method || "GET";
1398
+ if (method !== "GET")
1399
+ throw new NotFoundError4;
1400
+ const accept = request.headers.get?.("accept") || "";
1401
+ if (!/text\/html/i.test(String(accept)))
1402
+ throw new NotFoundError4;
1403
+ try {
1404
+ const pathname = new URL(request.url).pathname;
1405
+ const bundleUrl = await getBundleUrl();
1406
+ const result = await renderSSRDocument({
1407
+ pathname,
1408
+ rootComponent,
1409
+ clientDir,
1410
+ cwd,
1411
+ isWatchMode,
1412
+ bundleUrl,
1413
+ head,
1414
+ lang,
1415
+ appId,
1416
+ minify
1417
+ });
1418
+ return new Response(result.html, {
1419
+ status: result.status,
1420
+ headers: {
1421
+ "content-type": "text/html; charset=utf-8",
1422
+ Vary: "Accept"
1423
+ }
1424
+ });
1425
+ } catch (e) {
1426
+ console.error(`[reroute] SSR ${collection} render error:`, e);
1427
+ throw new NotFoundError4;
1428
+ }
1429
+ });
1430
+ }
1431
+ app.onError(async ({ code, request, set }) => {
1432
+ if (code === "NOT_FOUND") {
1433
+ const url = new URL(request.url);
1434
+ const pathname = url.pathname;
1435
+ const method = request.method || "GET";
1436
+ const isInternal = pathname.startsWith("/__reroute_watch") || pathname.startsWith("/__content/") || pathname.startsWith("/__reroute_preload") || pathname.startsWith("/__reroute_data") || pathname.startsWith("/__reroute_watch.js");
1437
+ if (pathname === "/__reroute_data" && method === "GET") {
1438
+ try {
1439
+ const p = url.searchParams.get("path") || "/";
1440
+ const data2 = await computeSSRDataForPath({
1441
+ pathname: p,
1442
+ clientDir,
1443
+ cwd,
1444
+ isWatchMode
1445
+ });
1446
+ return jsonWithCache({ data: data2 }, dataCacheControl || "no-cache", request.headers);
1447
+ } catch (_e) {
1448
+ return jsonError("failed", 500);
1449
+ }
1450
+ }
1451
+ if (method === "GET" && !isInternal) {
1452
+ try {
1453
+ if (isWatchMode) {
1454
+ await generateContentRegistry(cwd);
1455
+ }
1456
+ const bundleUrl = await getBundleUrl();
1457
+ const result = await renderSSRDocument({
1458
+ pathname,
1459
+ rootComponent,
1460
+ clientDir,
1461
+ cwd,
1462
+ isWatchMode,
1463
+ bundleUrl,
1464
+ head,
1465
+ lang,
1466
+ appId,
1467
+ minify
1468
+ });
1469
+ set.status = result.status;
1470
+ set.headers["content-type"] = "text/html; charset=utf-8";
1471
+ return new Response(result.html);
1472
+ } catch {}
1473
+ }
1474
+ }
1475
+ });
1476
+ return app;
1477
+ }
1478
+
1479
+ // packages/elysia/src/routes/static.ts
1480
+ import { NotFoundError as NotFoundError5 } from "elysia";
1481
+ function registerStaticRoutes(app, options) {
1482
+ const {
1483
+ clientDir,
1484
+ prefix,
1485
+ maxAge,
1486
+ directive,
1487
+ indexHTML,
1488
+ staticHeaders,
1489
+ minify,
1490
+ sourcemap,
1491
+ shouldIgnore,
1492
+ bundleCache,
1493
+ fileCache,
1494
+ isWatchMode
1495
+ } = options;
1496
+ app.get(`${prefix}/*`, async ({ params, headers }) => {
1497
+ const requestPath = params["*"];
1498
+ const hashedJsMatch = requestPath.match(/^(.+)\.([a-f0-9]{8})\.js$/);
1499
+ if (hashedJsMatch) {
1500
+ const [, filename, hash2] = hashedJsMatch;
1501
+ const tsxPath = join(clientDir, `${filename}.tsx`);
1502
+ const tsPath = join(clientDir, `${filename}.ts`);
1503
+ let filePath2 = null;
1504
+ if (await Bun.file(tsxPath).exists()) {
1505
+ filePath2 = tsxPath;
1506
+ } else if (await Bun.file(tsPath).exists()) {
1507
+ filePath2 = tsPath;
1508
+ }
1509
+ if (filePath2) {
1510
+ try {
1511
+ const bundleInfo = await transpileFile(filePath2, `${filename}.tsx`, { minify, sourcemap }, bundleCache);
1512
+ if (bundleInfo.hash === hash2) {
1513
+ const contentType = "application/javascript; charset=utf-8";
1514
+ const { body, extraHeaders } = gzipIfAccepted(bundleInfo.code, contentType, headers["accept-encoding"]);
1515
+ return new Response(body, {
1516
+ headers: {
1517
+ "Content-Type": contentType,
1518
+ "Cache-Control": `${directive}, max-age=${365 * 24 * 60 * 60}, immutable`,
1519
+ ...extraHeaders,
1520
+ ...staticHeaders
1521
+ }
1522
+ });
1523
+ }
1524
+ } catch (error) {
1525
+ console.error(`[reroute] Error serving bundle:`, error);
1526
+ }
1527
+ }
1528
+ }
1529
+ const sourceMapMatch = requestPath.match(/^(.+)\.([a-f0-9]{8})\.js\.map$/);
1530
+ if (sourceMapMatch) {
1531
+ const [, filename, hash2] = sourceMapMatch;
1532
+ const tsxPath = join(clientDir, `${filename}.tsx`);
1533
+ const tsPath = join(clientDir, `${filename}.ts`);
1534
+ let filePath2 = null;
1535
+ if (await Bun.file(tsxPath).exists()) {
1536
+ filePath2 = tsxPath;
1537
+ } else if (await Bun.file(tsPath).exists()) {
1538
+ filePath2 = tsPath;
1539
+ }
1540
+ if (filePath2) {
1541
+ try {
1542
+ const bundleInfo = await transpileFile(filePath2, `${filename}.tsx`, { minify, sourcemap }, bundleCache);
1543
+ if (bundleInfo.hash === hash2 && bundleInfo.sourceMap) {
1544
+ const contentType = "application/json; charset=utf-8";
1545
+ const { body, extraHeaders } = gzipIfAccepted(bundleInfo.sourceMap, contentType, headers["accept-encoding"]);
1546
+ return new Response(body, {
1547
+ headers: {
1548
+ "Content-Type": contentType,
1549
+ "Cache-Control": `${directive}, max-age=${365 * 24 * 60 * 60}, immutable`,
1550
+ ...extraHeaders,
1551
+ ...staticHeaders
1552
+ }
1553
+ });
1554
+ }
1555
+ } catch (error) {
1556
+ console.error(`[reroute] Error serving sourcemap:`, error);
1557
+ }
1558
+ }
1559
+ }
1560
+ if (requestPath.endsWith(".tsx") || requestPath.endsWith(".ts")) {
1561
+ try {
1562
+ const m = requestPath.match(/^routes\/([^/]+)\/content\/([^/]+)\.(tsx|ts)$/);
1563
+ if (m) {
1564
+ const collection = m[1];
1565
+ const name = m[2];
1566
+ try {
1567
+ const cwd = process.cwd();
1568
+ const regPath = join(cwd, ".reroute", "content.ts");
1569
+ const reg = await import((await import("node:url")).pathToFileURL(regPath).href + `?t=${Date.now()}`);
1570
+ const get = reg?.getContentEntry;
1571
+ const entry = typeof get === "function" ? get(collection, name) : undefined;
1572
+ const mod = entry?.module;
1573
+ if (mod?.endsWith(".js")) {
1574
+ return new Response(null, {
1575
+ status: 302,
1576
+ headers: { Location: mod }
1577
+ });
1578
+ }
1579
+ } catch {}
1580
+ try {
1581
+ const chunkDir = join(process.cwd(), ".reroute", "chunks", collection);
1582
+ const files = await (await import("node:fs/promises")).readdir(chunkDir);
1583
+ const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
1584
+ if (candidates.length) {
1585
+ let latest = candidates[0];
1586
+ let latestM = 0;
1587
+ const { stat: stat5 } = await import("node:fs/promises");
1588
+ for (const f of candidates) {
1589
+ try {
1590
+ const s = await stat5(join(chunkDir, f));
1591
+ if (s.mtimeMs >= latestM) {
1592
+ latestM = s.mtimeMs;
1593
+ latest = f;
1594
+ }
1595
+ } catch {}
1596
+ }
1597
+ const loc = `/${join(".reroute", "chunks", collection, latest).replace(/\\+/g, "/")}`;
1598
+ return new Response(null, {
1599
+ status: 302,
1600
+ headers: { Location: loc }
1601
+ });
1602
+ }
1603
+ } catch {}
1604
+ }
1605
+ } catch {}
1606
+ const filePath2 = join(clientDir, requestPath);
1607
+ if (shouldIgnore(filePath2))
1608
+ throw new NotFoundError5;
1609
+ try {
1610
+ const exists = await Bun.file(filePath2).exists();
1611
+ if (!exists)
1612
+ throw new NotFoundError5;
1613
+ const bundleInfo = await transpileFile(filePath2, requestPath, { minify, sourcemap }, bundleCache);
1614
+ const contentType = "application/javascript; charset=utf-8";
1615
+ const { body, extraHeaders } = gzipIfAccepted(bundleInfo.code, contentType, headers["accept-encoding"]);
1616
+ return new Response(body, {
1617
+ headers: {
1618
+ "Content-Type": contentType,
1619
+ "Cache-Control": isWatchMode ? "no-cache" : `${directive}, max-age=${maxAge}`,
1620
+ ...extraHeaders,
1621
+ ...staticHeaders
1622
+ }
1623
+ });
1624
+ } catch (error) {
1625
+ if (error instanceof NotFoundError5)
1626
+ throw error;
1627
+ console.error(`[reroute] Error:`, error);
1628
+ throw new NotFoundError5;
1629
+ }
1630
+ }
1631
+ const filePath = join(clientDir, requestPath);
1632
+ try {
1633
+ let actualFilePath = filePath;
1634
+ let exists = await Bun.file(actualFilePath).exists();
1635
+ if (!exists && indexHTML) {
1636
+ actualFilePath = join(filePath, "index.html");
1637
+ exists = await Bun.file(actualFilePath).exists();
1638
+ }
1639
+ if (!exists)
1640
+ throw new NotFoundError5;
1641
+ if (shouldIgnore(actualFilePath))
1642
+ throw new NotFoundError5;
1643
+ const cached = fileCache.get(filePath);
1644
+ if (cached)
1645
+ return cached.clone();
1646
+ const contentBuf = await Bun.file(actualFilePath).arrayBuffer();
1647
+ const content2 = new Uint8Array(contentBuf);
1648
+ const contentType = getMimeType(actualFilePath);
1649
+ const { body, extraHeaders } = gzipIfAccepted(content2, contentType, headers["accept-encoding"]);
1650
+ const response = new Response(body, {
1651
+ headers: {
1652
+ "Content-Type": contentType,
1653
+ "Cache-Control": maxAge ? `${directive}, max-age=${maxAge}` : directive,
1654
+ ...extraHeaders,
1655
+ ...staticHeaders
1656
+ }
1657
+ });
1658
+ fileCache.set(filePath, response);
1659
+ return response.clone();
1660
+ } catch (error) {
1661
+ if (error instanceof NotFoundError5)
1662
+ throw error;
1663
+ console.error(`[reroute] Error serving ${filePath}:`, error);
1664
+ throw new NotFoundError5;
1665
+ }
1666
+ });
1667
+ return app;
1668
+ }
1669
+
1670
+ // packages/elysia/src/plugin.ts
1671
+ var reroute = (options = {}) => async (app) => {
1672
+ const ssrHead = options.head || "";
1673
+ const ssrLang = options.lang || "en";
1674
+ const ssrAppId = options.appId || "root";
1675
+ const entrypoint = options.entrypoint || "index.tsx";
1676
+ const rootComponent = options.app;
1677
+ const assets = options.assets || "src/client";
1678
+ let prefix = options.prefix || "/";
1679
+ const ignorePatterns = options.ignorePatterns || [
1680
+ ".DS_Store",
1681
+ ".git",
1682
+ ".env",
1683
+ "index.html",
1684
+ /favicon\.ico/,
1685
+ /\.well-known/
1686
+ ];
1687
+ const staticHeaders = options.staticHeaders;
1688
+ const maxAge = options.maxAge ?? 3600;
1689
+ const directive = options.directive || "public";
1690
+ const indexHTML = options.indexHTML ?? true;
1691
+ const minify = options.minify ?? false;
1692
+ const sourcemap = options.sourcemap ?? true;
1693
+ if (prefix === "/")
1694
+ prefix = "";
1695
+ const fileCache = new LRUCache(100);
1696
+ const bundleCache = new Map;
1697
+ const cwd = typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : "/";
1698
+ const liveReloadClients = new Set;
1699
+ const notifyReload = () => {
1700
+ const encoder = new TextEncoder;
1701
+ const message = encoder.encode(`data: reload
1702
+
1703
+ `);
1704
+ for (const controller of liveReloadClients) {
1705
+ try {
1706
+ controller.enqueue(message);
1707
+ } catch {
1708
+ liveReloadClients.delete(controller);
1709
+ }
1710
+ }
1711
+ };
1712
+ const clientDir = assets.startsWith("/") ? assets : `${cwd.replace(/\/$/, "")}/${assets.replace(/^\.\//, "")}`;
1713
+ const join2 = (...parts) => parts.join("/").replace(/\/+/g, "/");
1714
+ const shouldIgnore = !ignorePatterns.length ? () => false : (file) => ignorePatterns.find((pattern) => typeof pattern === "string" ? file.includes(pattern) : pattern.test(file));
1715
+ const isWatchMode = process.execArgv[0] === "--watch";
1716
+ const dataCacheControl = `${directive}, max-age=${maxAge}`;
1717
+ if (isWatchMode) {
1718
+ console.log(`[reroute] Live reload enabled`);
1719
+ }
1720
+ await generateContentRegistry(cwd);
1721
+ let cachedBundleUrlPromise = null;
1722
+ const ensureBundleUrl = () => {
1723
+ if (!cachedBundleUrlPromise) {
1724
+ cachedBundleUrlPromise = getBundleUrlsFor(entrypoint, clientDir, prefix, { minify, sourcemap }, bundleCache).then((u) => u[0]);
1725
+ }
1726
+ return cachedBundleUrlPromise;
1727
+ };
1728
+ registerContentRoutes(app, clientDir, prefix, isWatchMode);
1729
+ registerDevRoutes(app, liveReloadClients, isWatchMode, {
1730
+ clientDir,
1731
+ cwd,
1732
+ dataCacheControl
1733
+ });
1734
+ app.get("/__reroute_data", async ({ query, headers }) => {
1735
+ const p = String(query.path || query.p || "/");
1736
+ let pathname = "/";
1737
+ try {
1738
+ const u = new URL(p, "http://localhost");
1739
+ pathname = u.pathname + u.search;
1740
+ } catch {
1741
+ pathname = String(p);
1742
+ }
1743
+ try {
1744
+ const data2 = await computeSSRDataForPath({
1745
+ pathname,
1746
+ clientDir,
1747
+ cwd,
1748
+ isWatchMode
1749
+ });
1750
+ return jsonWithCache({ data: data2 }, dataCacheControl, headers);
1751
+ } catch (_e) {
1752
+ return jsonError("failed", 500);
1753
+ }
1754
+ });
1755
+ if (isWatchMode) {
1756
+ try {
1757
+ let debounce;
1758
+ const schedule = () => {
1759
+ clearTimeout(debounce);
1760
+ debounce = setTimeout(async () => {
1761
+ try {
1762
+ bundleCache.clear();
1763
+ cachedBundleUrlPromise = null;
1764
+ await rebuildContentArtifacts(cwd, clientDir, true);
1765
+ notifyReload();
1766
+ console.log("[reroute] Rebuilt on change and notified clients");
1767
+ } catch (e) {
1768
+ console.error("[reroute] Dev rebuild failed:", e);
1769
+ }
1770
+ }, 120);
1771
+ };
1772
+ fsWatch(clientDir, { recursive: true }, (_event, filename) => {
1773
+ try {
1774
+ const name = String(filename || "");
1775
+ if (!name)
1776
+ return;
1777
+ if (!/\.(ts|tsx|js|jsx|html|css|json)$/i.test(name))
1778
+ return;
1779
+ if (shouldIgnore(join2(clientDir, name)))
1780
+ return;
1781
+ schedule();
1782
+ } catch {}
1783
+ });
1784
+ console.log(`[reroute] Watching ${clientDir} for changes`);
1785
+ } catch (e) {
1786
+ console.warn("[reroute] Failed to start dev file watcher:", e);
1787
+ }
1788
+ }
1789
+ registerArtifactsRoutes(app, cwd);
1790
+ if (rootComponent) {
1791
+ const collections = await discoverCollections(clientDir);
1792
+ if (collections.length > 0) {
1793
+ console.log(`[reroute] Discovered collections: ${collections.join(", ")}`);
1794
+ }
1795
+ registerSSRRoutes(app, {
1796
+ rootComponent,
1797
+ clientDir,
1798
+ cwd,
1799
+ isWatchMode,
1800
+ getBundleUrl: ensureBundleUrl,
1801
+ head: ssrHead,
1802
+ lang: ssrLang,
1803
+ appId: ssrAppId,
1804
+ collections,
1805
+ minify,
1806
+ dataCacheControl
1807
+ });
1808
+ }
1809
+ registerStaticRoutes(app, {
1810
+ clientDir,
1811
+ prefix,
1812
+ maxAge,
1813
+ directive,
1814
+ indexHTML,
1815
+ staticHeaders,
1816
+ minify,
1817
+ sourcemap,
1818
+ shouldIgnore,
1819
+ bundleCache,
1820
+ fileCache,
1821
+ isWatchMode
1822
+ });
1823
+ return app;
1824
+ };
1825
+ export {
1826
+ reroute
1827
+ };
1828
+
1829
+ //# debugId=E29EB407CD742AE464756E2164756E21