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.
- package/README.md +144 -0
- package/cli/bin.d.ts +3 -0
- package/cli/bin.d.ts.map +1 -0
- package/cli/bin.js +812 -0
- package/cli/bin.js.map +15 -0
- package/cli/src/commands/build.d.ts +8 -0
- package/cli/src/commands/build.d.ts.map +1 -0
- package/cli/src/commands/dev.d.ts +8 -0
- package/cli/src/commands/dev.d.ts.map +1 -0
- package/cli/src/commands/gen.d.ts +3 -0
- package/cli/src/commands/gen.d.ts.map +1 -0
- package/cli/src/commands/init.d.ts +8 -0
- package/cli/src/commands/init.d.ts.map +1 -0
- package/cli/src/index.d.ts +8 -0
- package/cli/src/index.d.ts.map +1 -0
- package/cli/src/libs/tailwind.d.ts +18 -0
- package/cli/src/libs/tailwind.d.ts.map +1 -0
- package/core/index.d.ts +2 -0
- package/core/index.d.ts.map +1 -0
- package/core/index.js +1174 -0
- package/core/index.js.map +25 -0
- package/core/src/bundler/hash.d.ts +2 -0
- package/core/src/bundler/hash.d.ts.map +1 -0
- package/core/src/bundler/index.d.ts +3 -0
- package/core/src/bundler/index.d.ts.map +1 -0
- package/core/src/bundler/transpile.d.ts +4 -0
- package/core/src/bundler/transpile.d.ts.map +1 -0
- package/core/src/content/builder.d.ts +2 -0
- package/core/src/content/builder.d.ts.map +1 -0
- package/core/src/content/discovery.d.ts +5 -0
- package/core/src/content/discovery.d.ts.map +1 -0
- package/core/src/content/index.d.ts +5 -0
- package/core/src/content/index.d.ts.map +1 -0
- package/core/src/content/metadata.d.ts +9 -0
- package/core/src/content/metadata.d.ts.map +1 -0
- package/core/src/content/registry.d.ts +2 -0
- package/core/src/content/registry.d.ts.map +1 -0
- package/core/src/index.d.ts +7 -0
- package/core/src/index.d.ts.map +1 -0
- package/core/src/ssr/data.d.ts +9 -0
- package/core/src/ssr/data.d.ts.map +1 -0
- package/core/src/ssr/index.d.ts +4 -0
- package/core/src/ssr/index.d.ts.map +1 -0
- package/core/src/ssr/modules.d.ts +8 -0
- package/core/src/ssr/modules.d.ts.map +1 -0
- package/core/src/ssr/render.d.ts +20 -0
- package/core/src/ssr/render.d.ts.map +1 -0
- package/core/src/ssr/seed.d.ts +2 -0
- package/core/src/ssr/seed.d.ts.map +1 -0
- package/core/src/template/html.d.ts +4 -0
- package/core/src/template/html.d.ts.map +1 -0
- package/core/src/template/index.d.ts +2 -0
- package/core/src/template/index.d.ts.map +1 -0
- package/core/src/types.d.ts +50 -0
- package/core/src/types.d.ts.map +1 -0
- package/core/src/utils/cache.d.ts +12 -0
- package/core/src/utils/cache.d.ts.map +1 -0
- package/core/src/utils/compression.d.ts +5 -0
- package/core/src/utils/compression.d.ts.map +1 -0
- package/core/src/utils/index.d.ts +5 -0
- package/core/src/utils/index.d.ts.map +1 -0
- package/core/src/utils/mime.d.ts +3 -0
- package/core/src/utils/mime.d.ts.map +1 -0
- package/core/src/utils/path.d.ts +6 -0
- package/core/src/utils/path.d.ts.map +1 -0
- package/elysia/index.d.ts +2 -0
- package/elysia/index.d.ts.map +1 -0
- package/elysia/index.js +1829 -0
- package/elysia/index.js.map +32 -0
- package/elysia/src/index.d.ts +3 -0
- package/elysia/src/index.d.ts.map +1 -0
- package/elysia/src/plugin.d.ts +32 -0
- package/elysia/src/plugin.d.ts.map +1 -0
- package/elysia/src/routes/artifacts.d.ts +3 -0
- package/elysia/src/routes/artifacts.d.ts.map +1 -0
- package/elysia/src/routes/content.d.ts +3 -0
- package/elysia/src/routes/content.d.ts.map +1 -0
- package/elysia/src/routes/dev.d.ts +7 -0
- package/elysia/src/routes/dev.d.ts.map +1 -0
- package/elysia/src/routes/ssr.d.ts +18 -0
- package/elysia/src/routes/ssr.d.ts.map +1 -0
- package/elysia/src/routes/static.d.ts +19 -0
- package/elysia/src/routes/static.d.ts.map +1 -0
- package/elysia/src/types.d.ts +31 -0
- package/elysia/src/types.d.ts.map +1 -0
- package/elysia/src/utils/http.d.ts +5 -0
- package/elysia/src/utils/http.d.ts.map +1 -0
- package/package.json +74 -0
- package/react/index.d.ts +2 -0
- package/react/index.d.ts.map +1 -0
- package/react/index.js +1152 -0
- package/react/index.js.map +23 -0
- package/react/src/components/ContentRoute.d.ts +13 -0
- package/react/src/components/ContentRoute.d.ts.map +1 -0
- package/react/src/components/Link.d.ts +8 -0
- package/react/src/components/Link.d.ts.map +1 -0
- package/react/src/components/Outlet.d.ts +7 -0
- package/react/src/components/Outlet.d.ts.map +1 -0
- package/react/src/components/index.d.ts +4 -0
- package/react/src/components/index.d.ts.map +1 -0
- package/react/src/hooks/index.d.ts +7 -0
- package/react/src/hooks/index.d.ts.map +1 -0
- package/react/src/hooks/useContent.d.ts +26 -0
- package/react/src/hooks/useContent.d.ts.map +1 -0
- package/react/src/hooks/useData.d.ts +10 -0
- package/react/src/hooks/useData.d.ts.map +1 -0
- package/react/src/hooks/useNavigate.d.ts +6 -0
- package/react/src/hooks/useNavigate.d.ts.map +1 -0
- package/react/src/hooks/useParams.d.ts +6 -0
- package/react/src/hooks/useParams.d.ts.map +1 -0
- package/react/src/hooks/useRouter.d.ts +7 -0
- package/react/src/hooks/useRouter.d.ts.map +1 -0
- package/react/src/hooks/useSearchParams.d.ts +6 -0
- package/react/src/hooks/useSearchParams.d.ts.map +1 -0
- package/react/src/index.d.ts +6 -0
- package/react/src/index.d.ts.map +1 -0
- package/react/src/providers/ContentProvider.d.ts +35 -0
- package/react/src/providers/ContentProvider.d.ts.map +1 -0
- package/react/src/providers/RerouteProvider.d.ts +25 -0
- package/react/src/providers/RerouteProvider.d.ts.map +1 -0
- package/react/src/providers/RouterProvider.d.ts +23 -0
- package/react/src/providers/RouterProvider.d.ts.map +1 -0
- package/react/src/providers/index.d.ts +4 -0
- package/react/src/providers/index.d.ts.map +1 -0
- package/react/src/types/any.d.ts +3 -0
- package/react/src/types/any.d.ts.map +1 -0
- package/react/src/types/index.d.ts +3 -0
- package/react/src/types/index.d.ts.map +1 -0
- package/react/src/types/router.d.ts +32 -0
- package/react/src/types/router.d.ts.map +1 -0
- package/react/src/utils/content.d.ts +8 -0
- package/react/src/utils/content.d.ts.map +1 -0
- package/react/src/utils/head.d.ts +6 -0
- package/react/src/utils/head.d.ts.map +1 -0
- package/react/src/utils/index.d.ts +3 -0
- package/react/src/utils/index.d.ts.map +1 -0
package/core/index.js
ADDED
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// packages/core/src/utils/path.ts
|
|
13
|
+
var exports_path = {};
|
|
14
|
+
__export(exports_path, {
|
|
15
|
+
stripStart: () => stripStart,
|
|
16
|
+
stripEnd: () => stripEnd,
|
|
17
|
+
join: () => join,
|
|
18
|
+
extname: () => extname,
|
|
19
|
+
basename: () => basename
|
|
20
|
+
});
|
|
21
|
+
function join(...parts) {
|
|
22
|
+
return parts.join("/").replace(/\/+/g, "/");
|
|
23
|
+
}
|
|
24
|
+
function extname(p) {
|
|
25
|
+
const i = p.lastIndexOf(".");
|
|
26
|
+
return i >= 0 ? p.slice(i) : "";
|
|
27
|
+
}
|
|
28
|
+
function basename(p, ext) {
|
|
29
|
+
const name = p.replace(/\\/g, "/").split("/").pop() || p;
|
|
30
|
+
return ext && name.endsWith(ext) ? name.slice(0, -ext.length) : name;
|
|
31
|
+
}
|
|
32
|
+
function stripStart(p, ch) {
|
|
33
|
+
return p.startsWith(ch) ? p.slice(ch.length) : p;
|
|
34
|
+
}
|
|
35
|
+
function stripEnd(p, ch) {
|
|
36
|
+
return p.endsWith(ch) ? p.slice(0, -ch.length) : p;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// packages/core/src/bundler/hash.ts
|
|
40
|
+
async function generateContentHash(content) {
|
|
41
|
+
const data = new TextEncoder().encode(content);
|
|
42
|
+
const buf = await crypto.subtle.digest("SHA-256", data);
|
|
43
|
+
let hex = "";
|
|
44
|
+
for (const b of new Uint8Array(buf))
|
|
45
|
+
hex += b.toString(16).padStart(2, "0");
|
|
46
|
+
return hex.slice(0, 8);
|
|
47
|
+
}
|
|
48
|
+
// packages/core/src/bundler/transpile.ts
|
|
49
|
+
async function transpileFile(filePath, originalPath, options, bundleCache) {
|
|
50
|
+
const src = await Bun.file(filePath).text();
|
|
51
|
+
const cacheKey = `${filePath}-${await generateContentHash(src)}`;
|
|
52
|
+
if (bundleCache.has(cacheKey)) {
|
|
53
|
+
return bundleCache.get(cacheKey);
|
|
54
|
+
}
|
|
55
|
+
console.log(`[reroute] Building ${originalPath}${options.minify ? " (minified)" : ""}...`);
|
|
56
|
+
async function doBuild(sm) {
|
|
57
|
+
return await Bun.build({
|
|
58
|
+
entrypoints: [filePath],
|
|
59
|
+
target: "browser",
|
|
60
|
+
format: "esm",
|
|
61
|
+
minify: options.minify,
|
|
62
|
+
splitting: false,
|
|
63
|
+
sourcemap: sm,
|
|
64
|
+
define: {
|
|
65
|
+
"process.env.NODE_ENV": options.minify ? '"production"' : '"development"'
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
let result;
|
|
70
|
+
try {
|
|
71
|
+
result = await doBuild(options.sourcemap ? "external" : "none");
|
|
72
|
+
if (!result.success) {
|
|
73
|
+
console.warn(`[reroute] Build failed for ${originalPath}, retrying without sourcemap...`);
|
|
74
|
+
result = await doBuild("none");
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.warn(`[reroute] Build errored for ${originalPath} (sourcemap external). Retrying without sourcemap...`, error);
|
|
78
|
+
result = await doBuild("none");
|
|
79
|
+
}
|
|
80
|
+
if (!result.success) {
|
|
81
|
+
console.error(`[reroute] Failed to build ${filePath}`);
|
|
82
|
+
for (const log of result.logs)
|
|
83
|
+
console.error(log);
|
|
84
|
+
throw new Error(`Failed to transpile ${filePath}`);
|
|
85
|
+
}
|
|
86
|
+
const output = result.outputs[0];
|
|
87
|
+
const code = await output.text();
|
|
88
|
+
let sourceMap;
|
|
89
|
+
if (options.sourcemap && result.outputs.length > 1) {
|
|
90
|
+
const mapOutput = result.outputs.find((o) => o.path.endsWith(".map"));
|
|
91
|
+
if (mapOutput) {
|
|
92
|
+
sourceMap = await mapOutput.text();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const contentHash = await generateContentHash(code);
|
|
96
|
+
const bundleInfo = {
|
|
97
|
+
hash: contentHash,
|
|
98
|
+
code,
|
|
99
|
+
sourceMap
|
|
100
|
+
};
|
|
101
|
+
bundleCache.set(cacheKey, bundleInfo);
|
|
102
|
+
const sizeKB = (code.length / 1024).toFixed(2);
|
|
103
|
+
const gzippedSize = Bun.gzipSync(new TextEncoder().encode(code)).length;
|
|
104
|
+
const gzippedKB = (gzippedSize / 1024).toFixed(2);
|
|
105
|
+
console.log(`[reroute] Built ${originalPath} -> ${sizeKB} KB (${gzippedKB} KB gzipped)`);
|
|
106
|
+
return bundleInfo;
|
|
107
|
+
}
|
|
108
|
+
async function getBundleUrlsFor(modules, clientDir, prefix, options, bundleCache) {
|
|
109
|
+
const mods = Array.isArray(modules) ? modules : [modules];
|
|
110
|
+
const urls = [];
|
|
111
|
+
const { join: join2 } = await Promise.resolve().then(() => exports_path);
|
|
112
|
+
for (const mod of mods) {
|
|
113
|
+
const rel = mod.replace(/^\.\//, "");
|
|
114
|
+
const fullPath = join2(clientDir, rel);
|
|
115
|
+
try {
|
|
116
|
+
const bundleInfo = await transpileFile(fullPath, rel, options, bundleCache);
|
|
117
|
+
const base = basename(rel, extname(rel));
|
|
118
|
+
urls.push(`${prefix}/${base}.${bundleInfo.hash}.js`);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(`[reroute] Error getting bundle URL for ${rel}:`, error);
|
|
121
|
+
urls.push(`${prefix}/${rel}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return urls;
|
|
125
|
+
}
|
|
126
|
+
// packages/core/src/content/builder.ts
|
|
127
|
+
import { mkdir, readdir } from "node:fs/promises";
|
|
128
|
+
import { pathToFileURL } from "node:url";
|
|
129
|
+
async function sha8(text) {
|
|
130
|
+
const data = new TextEncoder().encode(text);
|
|
131
|
+
const buf = await crypto.subtle.digest("SHA-256", data);
|
|
132
|
+
let hex = "";
|
|
133
|
+
for (const b of new Uint8Array(buf))
|
|
134
|
+
hex += b.toString(16).padStart(2, "0");
|
|
135
|
+
return hex.slice(0, 8);
|
|
136
|
+
}
|
|
137
|
+
async function buildChunkFor(absSrc, cwd, collection, name) {
|
|
138
|
+
try {
|
|
139
|
+
const result = await Bun.build({
|
|
140
|
+
entrypoints: [absSrc],
|
|
141
|
+
target: "browser",
|
|
142
|
+
format: "esm",
|
|
143
|
+
splitting: false,
|
|
144
|
+
sourcemap: "none",
|
|
145
|
+
minify: false,
|
|
146
|
+
define: { "process.env.NODE_ENV": '"production"' }
|
|
147
|
+
});
|
|
148
|
+
if (!result.success)
|
|
149
|
+
return null;
|
|
150
|
+
const code = await result.outputs[0]?.text();
|
|
151
|
+
const hash2 = await sha8(code);
|
|
152
|
+
const relDir = join(".reroute", "chunks", collection);
|
|
153
|
+
const absDir = join(cwd, relDir);
|
|
154
|
+
const outFile = `${name}.${hash2}.js`;
|
|
155
|
+
const outPath = join(absDir, outFile);
|
|
156
|
+
await mkdir(absDir, { recursive: true });
|
|
157
|
+
const exists = await Bun.file(outPath).exists();
|
|
158
|
+
if (!exists)
|
|
159
|
+
await Bun.write(outPath, code);
|
|
160
|
+
const url = `/${join(relDir, outFile).replace(/\\+/g, "/")}`;
|
|
161
|
+
return { outPath, url, code };
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.error("[reroute] Failed to build content chunk:", absSrc, e);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function getMetaFor(absSrc, isWatchMode) {
|
|
168
|
+
try {
|
|
169
|
+
const url = pathToFileURL(absSrc).href + (isWatchMode ? `?t=${Date.now()}` : "");
|
|
170
|
+
const m = await import(url);
|
|
171
|
+
return m.meta || m.frontmatter || {};
|
|
172
|
+
} catch {
|
|
173
|
+
return {};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function rebuildContentArtifacts(cwd, clientDir, isWatchMode = true) {
|
|
177
|
+
const routesRoot = join(clientDir, "routes");
|
|
178
|
+
const items = [];
|
|
179
|
+
try {
|
|
180
|
+
const entries = await readdir(routesRoot, { withFileTypes: true });
|
|
181
|
+
for (const dir of entries) {
|
|
182
|
+
if (!dir.isDirectory())
|
|
183
|
+
continue;
|
|
184
|
+
const collection = dir.name;
|
|
185
|
+
const contentDir = join(routesRoot, collection, "content");
|
|
186
|
+
let contentFiles = [];
|
|
187
|
+
try {
|
|
188
|
+
const list = await readdir(contentDir, { withFileTypes: true });
|
|
189
|
+
contentFiles = list.filter((e) => e.isFile() && /\.(tsx|ts)$/.test(e.name) && !e.name.startsWith("_")).map((e) => e.name);
|
|
190
|
+
} catch {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
for (const file of contentFiles) {
|
|
194
|
+
const name = file.replace(/\.(tsx|ts)$/, "");
|
|
195
|
+
const absSrc = join(contentDir, file);
|
|
196
|
+
const chunk = await buildChunkFor(absSrc, cwd, collection, name);
|
|
197
|
+
if (!chunk)
|
|
198
|
+
continue;
|
|
199
|
+
const meta = await getMetaFor(absSrc, isWatchMode);
|
|
200
|
+
items.push({
|
|
201
|
+
collection,
|
|
202
|
+
name,
|
|
203
|
+
slug: name,
|
|
204
|
+
href: `/${collection}/${name}`,
|
|
205
|
+
moduleUrl: chunk.url,
|
|
206
|
+
meta
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch (e) {
|
|
211
|
+
console.error("[reroute] Failed to scan content routes:", e);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const lines = [];
|
|
216
|
+
lines.push("// \uD83D\uDEA8 Auto-generated by Reroute - DO NOT EDIT \uD83D\uDEA8");
|
|
217
|
+
lines.push("/* eslint-disable */");
|
|
218
|
+
lines.push("// @ts-nocheck");
|
|
219
|
+
lines.push("");
|
|
220
|
+
lines.push("export const contents = [");
|
|
221
|
+
for (const e of items) {
|
|
222
|
+
lines.push(" { collection: '" + e.collection + "', slug: '" + e.slug + "', name: '" + e.name + "', href: '" + e.href + "', module: '" + e.moduleUrl + "', meta: " + JSON.stringify(e.meta || {}) + " },");
|
|
223
|
+
}
|
|
224
|
+
lines.push("] as const;");
|
|
225
|
+
lines.push("");
|
|
226
|
+
lines.push("export const byCollection: Record<string, any[]> = {};");
|
|
227
|
+
lines.push("for (const c of contents) { (byCollection[c.collection] ||= []).push(c as any); }");
|
|
228
|
+
lines.push("");
|
|
229
|
+
lines.push("export const byCollectionAndName: Record<string, Record<string, any>> = {};");
|
|
230
|
+
lines.push("for (const c of contents) { ((byCollectionAndName[c.collection] ||= {})[c.name] = c as any); }");
|
|
231
|
+
lines.push("");
|
|
232
|
+
lines.push("export type Collections = keyof typeof byCollection;");
|
|
233
|
+
lines.push("export type Names<C extends Collections> = keyof (typeof byCollectionAndName)[C];");
|
|
234
|
+
lines.push("export type ContentEntry = (typeof contents)[number];");
|
|
235
|
+
lines.push("");
|
|
236
|
+
lines.push("export function getContentEntry<C extends string, N extends string>(collection: C, name: N) {");
|
|
237
|
+
lines.push(" const m = (byCollectionAndName as any)[collection];");
|
|
238
|
+
lines.push(" return m ? (m as Record<string, any>)[name] : undefined;");
|
|
239
|
+
lines.push("}");
|
|
240
|
+
lines.push("");
|
|
241
|
+
await mkdir(join(cwd, ".reroute"), { recursive: true });
|
|
242
|
+
await Bun.write(join(cwd, ".reroute", "content.ts"), lines.join(`
|
|
243
|
+
`));
|
|
244
|
+
} catch (e) {
|
|
245
|
+
console.error("[reroute] Failed to write .reroute/content.ts:", e);
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const byCol = new Map;
|
|
249
|
+
for (const it of items) {
|
|
250
|
+
const arr = byCol.get(it.collection) || [];
|
|
251
|
+
arr.push(it);
|
|
252
|
+
byCol.set(it.collection, arr);
|
|
253
|
+
}
|
|
254
|
+
await mkdir(join(cwd, ".reroute", "collections"), { recursive: true });
|
|
255
|
+
for (const [collection, list] of byCol) {
|
|
256
|
+
const js = [];
|
|
257
|
+
js.push("// \uD83D\uDEA8 Auto-generated by Reroute - DO NOT EDIT \uD83D\uDEA8");
|
|
258
|
+
js.push("");
|
|
259
|
+
js.push("export const items = [");
|
|
260
|
+
for (const e of list) {
|
|
261
|
+
js.push(` { collection: '${e.collection}', slug: '${e.slug}', name: '${e.name}', href: '${e.href}', module: '${e.moduleUrl}', meta: ${JSON.stringify(e.meta || {})} },`);
|
|
262
|
+
}
|
|
263
|
+
js.push("];");
|
|
264
|
+
js.push("");
|
|
265
|
+
js.push("export const byName = {};");
|
|
266
|
+
js.push("for (const it of items) { byName[it.name] = it; }");
|
|
267
|
+
js.push("");
|
|
268
|
+
js.push("export function get(collectionName, name) {");
|
|
269
|
+
js.push(` if (collectionName !== '${collection}') return undefined;`);
|
|
270
|
+
js.push(" return byName[name];");
|
|
271
|
+
js.push("}");
|
|
272
|
+
js.push("");
|
|
273
|
+
await Bun.write(join(cwd, ".reroute", "collections", `${collection}.js`), js.join(`
|
|
274
|
+
`));
|
|
275
|
+
}
|
|
276
|
+
} catch (e) {
|
|
277
|
+
console.error("[reroute] Failed to write .reroute/collections/*:", e);
|
|
278
|
+
}
|
|
279
|
+
console.log("[reroute] Content artifacts up-to-date");
|
|
280
|
+
}
|
|
281
|
+
// packages/core/src/content/discovery.ts
|
|
282
|
+
import { readdir as readdir2, stat as stat2 } from "node:fs/promises";
|
|
283
|
+
|
|
284
|
+
// packages/core/src/content/metadata.ts
|
|
285
|
+
import { stat } from "node:fs/promises";
|
|
286
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
287
|
+
async function getContentMeta(absPath, isWatchMode) {
|
|
288
|
+
try {
|
|
289
|
+
const url = pathToFileURL2(absPath).href;
|
|
290
|
+
const mod = await import(`${url}${isWatchMode ? `?t=${Date.now()}` : ""}`);
|
|
291
|
+
const meta = mod.meta || mod.frontmatter || {};
|
|
292
|
+
if (meta && typeof meta === "object")
|
|
293
|
+
return meta;
|
|
294
|
+
return {};
|
|
295
|
+
} catch {
|
|
296
|
+
try {
|
|
297
|
+
const s = await stat(absPath);
|
|
298
|
+
return { date: new Date(s.mtimeMs).toISOString() };
|
|
299
|
+
} catch {
|
|
300
|
+
return {};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function buildHeadFromMeta(meta) {
|
|
305
|
+
if (!meta || typeof meta !== "object")
|
|
306
|
+
return "";
|
|
307
|
+
const parts = [];
|
|
308
|
+
const title = typeof meta.title === "string" ? meta.title : undefined;
|
|
309
|
+
const description = typeof meta.description === "string" ? meta.description : typeof meta.excerpt === "string" ? meta.excerpt : undefined;
|
|
310
|
+
if (title)
|
|
311
|
+
parts.push(`<title>${escapeHtml(title)}</title>`);
|
|
312
|
+
if (description)
|
|
313
|
+
parts.push(`<meta name="description" content="${escapeHtml(description)}" />`);
|
|
314
|
+
return parts.length ? `
|
|
315
|
+
${parts.join(`
|
|
316
|
+
`)}` : "";
|
|
317
|
+
}
|
|
318
|
+
function escapeHtml(input) {
|
|
319
|
+
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// packages/core/src/content/discovery.ts
|
|
323
|
+
async function listContentFiles(dir, baseRel) {
|
|
324
|
+
const out = [];
|
|
325
|
+
try {
|
|
326
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
327
|
+
for (const entry of entries) {
|
|
328
|
+
const full = join(dir, entry.name);
|
|
329
|
+
const rel = join(baseRel, entry.name);
|
|
330
|
+
if (entry.isDirectory()) {
|
|
331
|
+
const nested = await listContentFiles(full, rel);
|
|
332
|
+
out.push(...nested);
|
|
333
|
+
} else if (entry.isFile()) {
|
|
334
|
+
if (/\.(tsx|ts)$/.test(entry.name) && !entry.name.startsWith("_")) {
|
|
335
|
+
out.push(rel);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
} catch {}
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
async function buildContentDTOs(collectionPath, clientDir, prefix, isWatchMode) {
|
|
343
|
+
const normalized = stripStart(stripEnd(collectionPath, "/"), "/");
|
|
344
|
+
const contentRelDir = join("routes", normalized, "content");
|
|
345
|
+
const contentAbsDir = join(clientDir, contentRelDir);
|
|
346
|
+
const files = await listContentFiles(contentAbsDir, "");
|
|
347
|
+
const items = [];
|
|
348
|
+
for (const rel of files) {
|
|
349
|
+
const fullRelPath = join(contentRelDir, rel);
|
|
350
|
+
const absPath = join(clientDir, fullRelPath);
|
|
351
|
+
const noExt = rel.replace(/\.(tsx|ts)$/, "");
|
|
352
|
+
const name = basename(noExt);
|
|
353
|
+
const moduleUrl = `${prefix}/${fullRelPath}`.replace(/\/+/g, "/");
|
|
354
|
+
const meta = await getContentMeta(absPath, isWatchMode);
|
|
355
|
+
const href = `/${normalized}/${name}`.replace(/\\+/g, "/");
|
|
356
|
+
items.push({
|
|
357
|
+
slug: noExt,
|
|
358
|
+
name,
|
|
359
|
+
path: fullRelPath,
|
|
360
|
+
module: moduleUrl,
|
|
361
|
+
meta,
|
|
362
|
+
href
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
return items;
|
|
366
|
+
}
|
|
367
|
+
async function discoverCollections(clientDir) {
|
|
368
|
+
const root = join(clientDir, "routes");
|
|
369
|
+
const collections = new Set;
|
|
370
|
+
try {
|
|
371
|
+
const entries = await readdir2(root, { withFileTypes: true });
|
|
372
|
+
for (const entry of entries) {
|
|
373
|
+
if (entry.isDirectory()) {
|
|
374
|
+
const contentDir = join(root, entry.name, "content");
|
|
375
|
+
try {
|
|
376
|
+
await stat2(contentDir);
|
|
377
|
+
collections.add(entry.name);
|
|
378
|
+
} catch {}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} catch {}
|
|
382
|
+
return Array.from(collections);
|
|
383
|
+
}
|
|
384
|
+
// packages/core/src/content/registry.ts
|
|
385
|
+
async function generateContentRegistry(cwd) {
|
|
386
|
+
try {
|
|
387
|
+
const p = join(cwd, ".reroute", "content.ts");
|
|
388
|
+
const exists = await Bun.file(p).exists();
|
|
389
|
+
console.log(exists ? "[reroute] Content registry up-to-date" : "[reroute] Warning: .reroute/content.ts not found");
|
|
390
|
+
} catch {}
|
|
391
|
+
}
|
|
392
|
+
// packages/core/src/ssr/data.ts
|
|
393
|
+
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
394
|
+
|
|
395
|
+
// packages/core/src/ssr/modules.ts
|
|
396
|
+
import { readdir as readdir3, stat as stat3 } from "node:fs/promises";
|
|
397
|
+
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
398
|
+
async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
399
|
+
try {
|
|
400
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
401
|
+
if (parts.length < 2)
|
|
402
|
+
return null;
|
|
403
|
+
const collection = parts[0];
|
|
404
|
+
const name = parts[1];
|
|
405
|
+
try {
|
|
406
|
+
const registryPath = join(cwd, ".reroute", "content.ts");
|
|
407
|
+
const reg = await import(pathToFileURL3(registryPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
408
|
+
const get = reg?.getContentEntry;
|
|
409
|
+
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
410
|
+
const moduleUrl = entry?.module;
|
|
411
|
+
if (moduleUrl?.endsWith(".js")) {
|
|
412
|
+
const abs = join(cwd, moduleUrl.replace(/^\//, ""));
|
|
413
|
+
const href = pathToFileURL3(abs).href + (isWatchMode ? `?t=${Date.now()}` : "");
|
|
414
|
+
const mod = await import(href);
|
|
415
|
+
return mod;
|
|
416
|
+
}
|
|
417
|
+
} catch {}
|
|
418
|
+
try {
|
|
419
|
+
const chunkDir = join(cwd, ".reroute", "chunks", collection);
|
|
420
|
+
const files = await readdir3(chunkDir);
|
|
421
|
+
const matches = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
422
|
+
if (matches.length) {
|
|
423
|
+
let latest = matches[0];
|
|
424
|
+
let latestM = 0;
|
|
425
|
+
for (const f of matches) {
|
|
426
|
+
try {
|
|
427
|
+
const s = await stat3(join(chunkDir, f));
|
|
428
|
+
if (s.mtimeMs >= latestM) {
|
|
429
|
+
latestM = s.mtimeMs;
|
|
430
|
+
latest = f;
|
|
431
|
+
}
|
|
432
|
+
} catch {}
|
|
433
|
+
}
|
|
434
|
+
const absChunk = join(chunkDir, latest);
|
|
435
|
+
const href = pathToFileURL3(absChunk).href + (isWatchMode ? `?t=${Date.now()}` : "");
|
|
436
|
+
const mod = await import(href);
|
|
437
|
+
return mod;
|
|
438
|
+
}
|
|
439
|
+
} catch {}
|
|
440
|
+
try {
|
|
441
|
+
const srcTsx = join(clientDir, "routes", collection, "content", `${name}.tsx`);
|
|
442
|
+
const srcTs = join(clientDir, "routes", collection, "content", `${name}.ts`);
|
|
443
|
+
let absSrc = null;
|
|
444
|
+
if (await Bun.file(srcTsx).exists())
|
|
445
|
+
absSrc = srcTsx;
|
|
446
|
+
else if (await Bun.file(srcTs).exists())
|
|
447
|
+
absSrc = srcTs;
|
|
448
|
+
if (absSrc) {
|
|
449
|
+
const href = pathToFileURL3(absSrc).href;
|
|
450
|
+
const mod = await (isWatchMode ? import(`${href}?t=${Date.now()}`) : import(href));
|
|
451
|
+
return mod;
|
|
452
|
+
}
|
|
453
|
+
} catch {}
|
|
454
|
+
} catch {}
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// packages/core/src/ssr/seed.ts
|
|
459
|
+
async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
460
|
+
try {
|
|
461
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
462
|
+
if (parts.length < 2)
|
|
463
|
+
return;
|
|
464
|
+
const collection = parts[0];
|
|
465
|
+
const name = parts[1];
|
|
466
|
+
const key = `${collection}:${name}`;
|
|
467
|
+
const mod = await importContentModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
468
|
+
if (!mod)
|
|
469
|
+
return;
|
|
470
|
+
const C = mod.default || mod;
|
|
471
|
+
globalThis.__REROUTE_SSR_MODULES__ = globalThis.__REROUTE_SSR_MODULES__ || {};
|
|
472
|
+
globalThis.__REROUTE_SSR_MODULES__[key] = C;
|
|
473
|
+
try {
|
|
474
|
+
const pageMeta = mod.meta || mod.frontmatter || {};
|
|
475
|
+
const pageSSR = mod.ssr || {};
|
|
476
|
+
globalThis.__REROUTE_SSR_EXPORTS__ = globalThis.__REROUTE_SSR_EXPORTS__ || {};
|
|
477
|
+
globalThis.__REROUTE_SSR_EXPORTS__[key] = {
|
|
478
|
+
meta: pageMeta,
|
|
479
|
+
ssr: pageSSR
|
|
480
|
+
};
|
|
481
|
+
} catch {}
|
|
482
|
+
} catch {}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// packages/core/src/ssr/data.ts
|
|
486
|
+
async function computeSSRDataForPath(params) {
|
|
487
|
+
const { pathname, clientDir, cwd, isWatchMode } = params;
|
|
488
|
+
try {
|
|
489
|
+
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
490
|
+
} catch {}
|
|
491
|
+
try {
|
|
492
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
493
|
+
if (parts.length >= 2) {
|
|
494
|
+
const key = `${parts[0]}:${parts[1]}`;
|
|
495
|
+
const g = globalThis;
|
|
496
|
+
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
497
|
+
const dataFn = exp?.ssr?.data;
|
|
498
|
+
if (typeof dataFn === "function") {
|
|
499
|
+
return await dataFn({ pathname, params: { name: parts[1] } });
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
} catch {}
|
|
503
|
+
try {
|
|
504
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
505
|
+
const m = await import(pathToFileURL4(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
506
|
+
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
507
|
+
const r = match?.route;
|
|
508
|
+
const paramsValue = match?.params || {};
|
|
509
|
+
if (r && typeof r.path === "string") {
|
|
510
|
+
try {
|
|
511
|
+
const abs = join(clientDir, "routes", String(r.path));
|
|
512
|
+
const mod = await import(pathToFileURL4(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
513
|
+
const ssr = mod?.ssr;
|
|
514
|
+
const dataFn = ssr?.data;
|
|
515
|
+
if (typeof dataFn === "function") {
|
|
516
|
+
return await dataFn({ pathname, params: paramsValue });
|
|
517
|
+
}
|
|
518
|
+
} catch {}
|
|
519
|
+
}
|
|
520
|
+
} catch {}
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
// packages/core/src/ssr/render.ts
|
|
524
|
+
import { readdir as readdir4, stat as stat4 } from "node:fs/promises";
|
|
525
|
+
import { pathToFileURL as pathToFileURL5 } from "node:url";
|
|
526
|
+
|
|
527
|
+
// node_modules/dedent/dist/dedent.mjs
|
|
528
|
+
function ownKeys(object, enumerableOnly) {
|
|
529
|
+
var keys = Object.keys(object);
|
|
530
|
+
if (Object.getOwnPropertySymbols) {
|
|
531
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
532
|
+
enumerableOnly && (symbols = symbols.filter(function(sym) {
|
|
533
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
534
|
+
})), keys.push.apply(keys, symbols);
|
|
535
|
+
}
|
|
536
|
+
return keys;
|
|
537
|
+
}
|
|
538
|
+
function _objectSpread(target) {
|
|
539
|
+
for (var i = 1;i < arguments.length; i++) {
|
|
540
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
541
|
+
i % 2 ? ownKeys(Object(source), true).forEach(function(key) {
|
|
542
|
+
_defineProperty(target, key, source[key]);
|
|
543
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
|
|
544
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return target;
|
|
548
|
+
}
|
|
549
|
+
function _defineProperty(obj, key, value) {
|
|
550
|
+
key = _toPropertyKey(key);
|
|
551
|
+
if (key in obj) {
|
|
552
|
+
Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
|
|
553
|
+
} else {
|
|
554
|
+
obj[key] = value;
|
|
555
|
+
}
|
|
556
|
+
return obj;
|
|
557
|
+
}
|
|
558
|
+
function _toPropertyKey(arg) {
|
|
559
|
+
var key = _toPrimitive(arg, "string");
|
|
560
|
+
return typeof key === "symbol" ? key : String(key);
|
|
561
|
+
}
|
|
562
|
+
function _toPrimitive(input, hint) {
|
|
563
|
+
if (typeof input !== "object" || input === null)
|
|
564
|
+
return input;
|
|
565
|
+
var prim = input[Symbol.toPrimitive];
|
|
566
|
+
if (prim !== undefined) {
|
|
567
|
+
var res = prim.call(input, hint || "default");
|
|
568
|
+
if (typeof res !== "object")
|
|
569
|
+
return res;
|
|
570
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
571
|
+
}
|
|
572
|
+
return (hint === "string" ? String : Number)(input);
|
|
573
|
+
}
|
|
574
|
+
var dedent = createDedent({});
|
|
575
|
+
var dedent_default = dedent;
|
|
576
|
+
function createDedent(options) {
|
|
577
|
+
dedent2.withOptions = (newOptions) => createDedent(_objectSpread(_objectSpread({}, options), newOptions));
|
|
578
|
+
return dedent2;
|
|
579
|
+
function dedent2(strings, ...values) {
|
|
580
|
+
const raw = typeof strings === "string" ? [strings] : strings.raw;
|
|
581
|
+
const {
|
|
582
|
+
alignValues = false,
|
|
583
|
+
escapeSpecialCharacters = Array.isArray(strings),
|
|
584
|
+
trimWhitespace = true
|
|
585
|
+
} = options;
|
|
586
|
+
let result = "";
|
|
587
|
+
for (let i = 0;i < raw.length; i++) {
|
|
588
|
+
let next = raw[i];
|
|
589
|
+
if (escapeSpecialCharacters) {
|
|
590
|
+
next = next.replace(/\\\n[ \t]*/g, "").replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\\{/g, "{");
|
|
591
|
+
}
|
|
592
|
+
result += next;
|
|
593
|
+
if (i < values.length) {
|
|
594
|
+
const value = alignValues ? alignValue(values[i], result) : values[i];
|
|
595
|
+
result += value;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const lines = result.split(`
|
|
599
|
+
`);
|
|
600
|
+
let mindent = null;
|
|
601
|
+
for (const l of lines) {
|
|
602
|
+
const m = l.match(/^(\s+)\S+/);
|
|
603
|
+
if (m) {
|
|
604
|
+
const indent = m[1].length;
|
|
605
|
+
if (!mindent) {
|
|
606
|
+
mindent = indent;
|
|
607
|
+
} else {
|
|
608
|
+
mindent = Math.min(mindent, indent);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (mindent !== null) {
|
|
613
|
+
const m = mindent;
|
|
614
|
+
result = lines.map((l) => l[0] === " " || l[0] === "\t" ? l.slice(m) : l).join(`
|
|
615
|
+
`);
|
|
616
|
+
}
|
|
617
|
+
if (trimWhitespace) {
|
|
618
|
+
result = result.trim();
|
|
619
|
+
}
|
|
620
|
+
if (escapeSpecialCharacters) {
|
|
621
|
+
result = result.replace(/\\n/g, `
|
|
622
|
+
`);
|
|
623
|
+
}
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function alignValue(value, precedingText) {
|
|
628
|
+
if (typeof value !== "string" || !value.includes(`
|
|
629
|
+
`)) {
|
|
630
|
+
return value;
|
|
631
|
+
}
|
|
632
|
+
const currentLine = precedingText.slice(precedingText.lastIndexOf(`
|
|
633
|
+
`) + 1);
|
|
634
|
+
const indentMatch = currentLine.match(/^(\s+)/);
|
|
635
|
+
if (indentMatch) {
|
|
636
|
+
const indent = indentMatch[1];
|
|
637
|
+
return value.replace(/\n/g, `
|
|
638
|
+
${indent}`);
|
|
639
|
+
}
|
|
640
|
+
return value;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// packages/core/src/ssr/render.ts
|
|
644
|
+
import { cloneElement } from "react";
|
|
645
|
+
import { renderToString } from "react-dom/server";
|
|
646
|
+
|
|
647
|
+
// packages/core/src/template/html.ts
|
|
648
|
+
async function loadIndexHtml(clientDir) {
|
|
649
|
+
const templatePath = join(clientDir, "index.html");
|
|
650
|
+
try {
|
|
651
|
+
const content = await Bun.file(templatePath).text();
|
|
652
|
+
return content;
|
|
653
|
+
} catch (error) {
|
|
654
|
+
console.error(`[reroute] Failed to load template from ${templatePath}:`, error);
|
|
655
|
+
return `<!doctype html>
|
|
656
|
+
<html lang="en">
|
|
657
|
+
<head>
|
|
658
|
+
<meta charset="UTF-8" />
|
|
659
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
660
|
+
<title>Reroute</title>
|
|
661
|
+
</head>
|
|
662
|
+
<body>
|
|
663
|
+
<div id="root"></div>
|
|
664
|
+
</body>
|
|
665
|
+
</html>`;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function applyIndexTemplate(templateHtml, appHtml, {
|
|
669
|
+
head = "",
|
|
670
|
+
hydrationScript = "",
|
|
671
|
+
lang = "en",
|
|
672
|
+
appId = "root"
|
|
673
|
+
}) {
|
|
674
|
+
let html = templateHtml;
|
|
675
|
+
html = html.replace(/<html([^>]*)>/i, (_m, attrs) => {
|
|
676
|
+
const hasLang = /(^|\s)lang\s*=/.test(attrs);
|
|
677
|
+
const newAttrs = hasLang ? attrs.replace(/lang\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/i, `lang="${lang}"`) : `${attrs} lang="${lang}"`;
|
|
678
|
+
return `<html${newAttrs}>`;
|
|
679
|
+
});
|
|
680
|
+
let headToInject = head || "";
|
|
681
|
+
try {
|
|
682
|
+
const titleMatch = headToInject.match(/<title[\s\S]*?<\/title>/i);
|
|
683
|
+
if (titleMatch) {
|
|
684
|
+
const titleTag = titleMatch[0];
|
|
685
|
+
if (/<title[\s\S]*?<\/title>/i.test(html)) {
|
|
686
|
+
html = html.replace(/<title[\s\S]*?<\/title>/i, titleTag);
|
|
687
|
+
} else {}
|
|
688
|
+
headToInject = headToInject.replace(titleTag, "");
|
|
689
|
+
}
|
|
690
|
+
const descRe = /<meta\s+name\s*=\s*['"]description['"][^>]*>/i;
|
|
691
|
+
const descMatch = headToInject.match(descRe);
|
|
692
|
+
if (descMatch) {
|
|
693
|
+
const descTag = descMatch[0];
|
|
694
|
+
if (descRe.test(html)) {
|
|
695
|
+
html = html.replace(descRe, descTag);
|
|
696
|
+
}
|
|
697
|
+
headToInject = headToInject.replace(descTag, "");
|
|
698
|
+
}
|
|
699
|
+
} catch {}
|
|
700
|
+
if (headToInject) {
|
|
701
|
+
html = html.replace(/<\/head>/i, `${headToInject}
|
|
702
|
+
</head>`);
|
|
703
|
+
}
|
|
704
|
+
html = html.replace(/<script\b[^>]*src\s*=\s*["'][^"']+\.(ts|tsx)(?:[?#][^"']*)?["'][^>]*>\s*<\/script>/gi, "");
|
|
705
|
+
const appIdEscaped = appId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
706
|
+
const rootDivRegex = new RegExp(`(<div[^>]*\\bid=(\\"|')${appIdEscaped}\\2[^>]*>)([\\s\\S]*?)(<\\/div>)`, "i");
|
|
707
|
+
if (rootDivRegex.test(html)) {
|
|
708
|
+
html = html.replace(rootDivRegex, `$1${appHtml}$4`);
|
|
709
|
+
} else {
|
|
710
|
+
html = html.replace(/<\/body>/i, ` <div id="${appId}">${appHtml}</div>
|
|
711
|
+
</body>`);
|
|
712
|
+
}
|
|
713
|
+
if (hydrationScript) {
|
|
714
|
+
html = html.replace(/<\/body>/i, ` ${hydrationScript}
|
|
715
|
+
</body>`);
|
|
716
|
+
}
|
|
717
|
+
return html;
|
|
718
|
+
}
|
|
719
|
+
// packages/core/src/ssr/render.ts
|
|
720
|
+
async function renderSSRDocument(options) {
|
|
721
|
+
const {
|
|
722
|
+
pathname,
|
|
723
|
+
rootComponent,
|
|
724
|
+
clientDir,
|
|
725
|
+
cwd,
|
|
726
|
+
isWatchMode,
|
|
727
|
+
bundleUrl,
|
|
728
|
+
head = "",
|
|
729
|
+
lang = "en",
|
|
730
|
+
appId = "root",
|
|
731
|
+
minify = false
|
|
732
|
+
} = options;
|
|
733
|
+
const scripts = [bundleUrl];
|
|
734
|
+
let hydrationScript = "";
|
|
735
|
+
let extraHead = "";
|
|
736
|
+
let statusOverride;
|
|
737
|
+
try {
|
|
738
|
+
globalThis.__REROUTE_SSR_ACCESSED__ = {};
|
|
739
|
+
} catch {}
|
|
740
|
+
try {
|
|
741
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
742
|
+
if (parts.length >= 2) {
|
|
743
|
+
const collection = parts[0];
|
|
744
|
+
const name = parts[1];
|
|
745
|
+
let modulePath = "";
|
|
746
|
+
let isContentCollection = false;
|
|
747
|
+
try {
|
|
748
|
+
const maybeDir = join(clientDir, "routes", collection, "content");
|
|
749
|
+
const s = await stat4(maybeDir);
|
|
750
|
+
isContentCollection = typeof s?.isDirectory === "function" ? s.isDirectory() : true;
|
|
751
|
+
} catch {
|
|
752
|
+
isContentCollection = false;
|
|
753
|
+
}
|
|
754
|
+
if (!isContentCollection) {
|
|
755
|
+
throw new Error("skip-content-preload");
|
|
756
|
+
}
|
|
757
|
+
try {
|
|
758
|
+
const registryPath = join(cwd, ".reroute", "content.ts");
|
|
759
|
+
const reg = await import(pathToFileURL5(registryPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
760
|
+
const get = reg?.getContentEntry;
|
|
761
|
+
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
762
|
+
const moduleUrl = entry?.module;
|
|
763
|
+
if (moduleUrl?.endsWith(".js")) {
|
|
764
|
+
modulePath = moduleUrl;
|
|
765
|
+
}
|
|
766
|
+
} catch {}
|
|
767
|
+
if (!modulePath) {
|
|
768
|
+
try {
|
|
769
|
+
const chunkDir = join(cwd, ".reroute", "chunks", collection);
|
|
770
|
+
const files = await readdir4(chunkDir);
|
|
771
|
+
const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
772
|
+
if (candidates.length) {
|
|
773
|
+
let latest = candidates[0];
|
|
774
|
+
let latestM = 0;
|
|
775
|
+
for (const candidateName of candidates) {
|
|
776
|
+
try {
|
|
777
|
+
const s = await stat4(join(chunkDir, candidateName));
|
|
778
|
+
if (s.mtimeMs >= latestM) {
|
|
779
|
+
latestM = s.mtimeMs;
|
|
780
|
+
latest = candidateName;
|
|
781
|
+
}
|
|
782
|
+
} catch {}
|
|
783
|
+
}
|
|
784
|
+
modulePath = `/${join(".reroute", "chunks", collection, latest).replace(/\\+/g, "/")}`;
|
|
785
|
+
}
|
|
786
|
+
} catch {}
|
|
787
|
+
}
|
|
788
|
+
if (!modulePath) {
|
|
789
|
+
const tsx = join(clientDir, "routes", collection, "content", `${name}.tsx`);
|
|
790
|
+
const ts = join(clientDir, "routes", collection, "content", `${name}.ts`);
|
|
791
|
+
let srcUrl = "";
|
|
792
|
+
if (await Bun.file(tsx).exists()) {
|
|
793
|
+
srcUrl = `/${join("routes", collection, "content", `${name}.tsx`).replace(/\\+/g, "/")}`;
|
|
794
|
+
} else if (await Bun.file(ts).exists()) {
|
|
795
|
+
srcUrl = `/${join("routes", collection, "content", `${name}.ts`).replace(/\\+/g, "/")}`;
|
|
796
|
+
}
|
|
797
|
+
if (srcUrl) {
|
|
798
|
+
modulePath = srcUrl;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (!modulePath) {
|
|
802
|
+
statusOverride = 404;
|
|
803
|
+
}
|
|
804
|
+
if (modulePath) {
|
|
805
|
+
const key = `${collection}:${name}`;
|
|
806
|
+
extraHead += `
|
|
807
|
+
<link rel="modulepreload" href="${modulePath}" />`;
|
|
808
|
+
const qs = `src=${encodeURIComponent(modulePath)}&key=${encodeURIComponent(key)}`;
|
|
809
|
+
hydrationScript += `<script type="module" src="/__reroute_preload?${qs}"></script>`;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
} catch {}
|
|
813
|
+
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
814
|
+
const __SSR_DATA__ = {};
|
|
815
|
+
try {
|
|
816
|
+
try {
|
|
817
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
818
|
+
if (parts.length >= 2) {
|
|
819
|
+
const g = globalThis;
|
|
820
|
+
const key = `${parts[0]}:${parts[1]}`;
|
|
821
|
+
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
822
|
+
const dataFn = exp?.ssr?.data;
|
|
823
|
+
if (typeof dataFn === "function") {
|
|
824
|
+
const out = await dataFn({
|
|
825
|
+
pathname,
|
|
826
|
+
params: { name: parts[1] }
|
|
827
|
+
});
|
|
828
|
+
__SSR_DATA__[pathname] = out;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
} catch {}
|
|
832
|
+
try {
|
|
833
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
834
|
+
const m = await import(pathToFileURL5(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
835
|
+
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
836
|
+
const r = match?.route;
|
|
837
|
+
const params = match?.params || {};
|
|
838
|
+
if (r && typeof r.path === "string") {
|
|
839
|
+
try {
|
|
840
|
+
const abs = join(clientDir, "routes", String(r.path));
|
|
841
|
+
const mod = await import(pathToFileURL5(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
842
|
+
const ssr = mod?.ssr;
|
|
843
|
+
const dataFn = ssr?.data;
|
|
844
|
+
if (typeof dataFn === "function") {
|
|
845
|
+
const out = await dataFn({ pathname, params });
|
|
846
|
+
__SSR_DATA__[pathname] = out;
|
|
847
|
+
}
|
|
848
|
+
} catch {}
|
|
849
|
+
}
|
|
850
|
+
} catch {}
|
|
851
|
+
} catch {}
|
|
852
|
+
try {
|
|
853
|
+
globalThis.__REROUTE_DATA__ = __SSR_DATA__;
|
|
854
|
+
} catch {}
|
|
855
|
+
let __byCollectionForSSR = {};
|
|
856
|
+
try {
|
|
857
|
+
const p = join(cwd, ".reroute", "content.ts");
|
|
858
|
+
const mod = await import(pathToFileURL5(p).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
859
|
+
__byCollectionForSSR = mod?.byCollection || {};
|
|
860
|
+
try {
|
|
861
|
+
globalThis.__REROUTE_COLLECTIONS__ = __byCollectionForSSR;
|
|
862
|
+
} catch {}
|
|
863
|
+
} catch {}
|
|
864
|
+
const componentWithPathname = cloneElement(rootComponent, {
|
|
865
|
+
pathname
|
|
866
|
+
});
|
|
867
|
+
const appHtml = renderToString(componentWithPathname);
|
|
868
|
+
const baseTemplate = await loadIndexHtml(clientDir);
|
|
869
|
+
let inlineStyleTag = "";
|
|
870
|
+
try {
|
|
871
|
+
const candidates = [
|
|
872
|
+
join(clientDir, "..", ".reroute", "theme.css"),
|
|
873
|
+
join(clientDir, "..", "..", ".reroute", "theme.css"),
|
|
874
|
+
join(clientDir, "..", "..", "..", ".reroute", "theme.css"),
|
|
875
|
+
join(clientDir, "..", "..", "..", "..", ".reroute", "theme.css")
|
|
876
|
+
];
|
|
877
|
+
let cssPath = "";
|
|
878
|
+
for (const p of candidates) {
|
|
879
|
+
if (await Bun.file(p).exists()) {
|
|
880
|
+
cssPath = p;
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (cssPath) {
|
|
885
|
+
let css = await Bun.file(cssPath).text();
|
|
886
|
+
if (minify) {
|
|
887
|
+
css = css.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/\s*([{}:;,>+~])\s*/g, "$1").replace(/;}/g, "}").trim();
|
|
888
|
+
}
|
|
889
|
+
inlineStyleTag = `<style data-reroute="tailwind">${css}</style>`;
|
|
890
|
+
}
|
|
891
|
+
} catch {}
|
|
892
|
+
try {
|
|
893
|
+
const g = globalThis;
|
|
894
|
+
const acc = g.__REROUTE_SSR_ACCESSED__;
|
|
895
|
+
const sortByDate = (order) => (a, b) => {
|
|
896
|
+
const da = a?.meta?.date ? Date.parse(String(a.meta.date)) : 0;
|
|
897
|
+
const db = b?.meta?.date ? Date.parse(String(b.meta.date)) : 0;
|
|
898
|
+
return order === "asc" ? da - db : db - da;
|
|
899
|
+
};
|
|
900
|
+
const usage = {};
|
|
901
|
+
if (acc && typeof acc.forEach === "function") {
|
|
902
|
+
acc.forEach((c) => {
|
|
903
|
+
if (typeof c === "string")
|
|
904
|
+
usage[c] = { limit: Number.POSITIVE_INFINITY, sort: "custom" };
|
|
905
|
+
});
|
|
906
|
+
} else if (acc && typeof acc === "object") {
|
|
907
|
+
for (const [k, v] of Object.entries(acc)) {
|
|
908
|
+
const lim = typeof v?.limit === "number" ? v.limit : Number.POSITIVE_INFINITY;
|
|
909
|
+
const s = typeof v?.sort === "string" ? v.sort : "custom";
|
|
910
|
+
usage[k] = { limit: lim, sort: s };
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
const collections = Object.keys(usage);
|
|
914
|
+
if (collections.length) {
|
|
915
|
+
const subset = {};
|
|
916
|
+
const partial = {};
|
|
917
|
+
for (const c of collections) {
|
|
918
|
+
const conf = usage[c];
|
|
919
|
+
const full = __byCollectionForSSR?.[c] || [];
|
|
920
|
+
let arr = full;
|
|
921
|
+
if (Number.isFinite(conf.limit) && conf.limit > 0 && conf.sort !== "custom") {
|
|
922
|
+
if (conf.sort === "date-desc") {
|
|
923
|
+
arr = [...full].sort(sortByDate("desc")).slice(0, conf.limit);
|
|
924
|
+
} else if (conf.sort === "date-asc") {
|
|
925
|
+
arr = [...full].sort(sortByDate("asc")).slice(0, conf.limit);
|
|
926
|
+
} else if (conf.sort === "none") {
|
|
927
|
+
arr = full.slice(0, conf.limit);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
subset[c] = arr;
|
|
931
|
+
partial[c] = Array.isArray(arr) && Array.isArray(full) ? arr.length < full.length : false;
|
|
932
|
+
}
|
|
933
|
+
const subsetJson = JSON.stringify(subset);
|
|
934
|
+
const partialJson = JSON.stringify(partial);
|
|
935
|
+
hydrationScript += `
|
|
936
|
+
<script>
|
|
937
|
+
(function(){ try {
|
|
938
|
+
var w = (typeof window!== 'undefined'? window : globalThis);
|
|
939
|
+
w.__REROUTE_COLLECTIONS__ = w.__REROUTE_COLLECTIONS__ || {};
|
|
940
|
+
Object.assign(w.__REROUTE_COLLECTIONS__, ${subsetJson});
|
|
941
|
+
w.__REROUTE_COLLECTIONS_PARTIAL__ = w.__REROUTE_COLLECTIONS_PARTIAL__ || {};
|
|
942
|
+
Object.assign(w.__REROUTE_COLLECTIONS_PARTIAL__, ${partialJson});
|
|
943
|
+
} catch(e){} })();
|
|
944
|
+
</script>`;
|
|
945
|
+
}
|
|
946
|
+
} catch {}
|
|
947
|
+
try {
|
|
948
|
+
const seededJson = JSON.stringify(__SSR_DATA__);
|
|
949
|
+
hydrationScript += `
|
|
950
|
+
<script>
|
|
951
|
+
(function(){ try {
|
|
952
|
+
var w = (typeof window!== 'undefined'? window : globalThis);
|
|
953
|
+
w.__REROUTE_DATA__ = Object.assign({}, w.__REROUTE_DATA__ || {}, ${seededJson});
|
|
954
|
+
} catch(e){} })();
|
|
955
|
+
</script>`;
|
|
956
|
+
} catch {}
|
|
957
|
+
hydrationScript += scripts.map((src) => `<script type="module" src="${src}"></script>`).join("");
|
|
958
|
+
if (isWatchMode) {
|
|
959
|
+
hydrationScript += `<script type="module" src="/__reroute_watch.js"></script>`;
|
|
960
|
+
}
|
|
961
|
+
let perPageHead = "";
|
|
962
|
+
let pageLang;
|
|
963
|
+
try {
|
|
964
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
965
|
+
if (parts.length >= 2) {
|
|
966
|
+
const key = `${parts[0]}:${parts[1]}`;
|
|
967
|
+
const g = globalThis;
|
|
968
|
+
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
969
|
+
const meta = exp?.meta;
|
|
970
|
+
const ssr = exp?.ssr;
|
|
971
|
+
perPageHead += buildHeadFromMeta(meta);
|
|
972
|
+
const ssrHead = typeof ssr?.head === "string" ? ssr.head : Array.isArray(ssr?.head) ? String(ssr.head.join(`
|
|
973
|
+
`)) : "";
|
|
974
|
+
if (ssrHead)
|
|
975
|
+
perPageHead += `
|
|
976
|
+
${ssrHead}`;
|
|
977
|
+
if (typeof ssr?.lang === "string" && ssr.lang.trim()) {
|
|
978
|
+
pageLang = ssr.lang.trim();
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
} catch {}
|
|
982
|
+
try {
|
|
983
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
984
|
+
const m = await import(pathToFileURL5(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
985
|
+
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
986
|
+
const r = match?.route;
|
|
987
|
+
if (!r) {
|
|
988
|
+
statusOverride = statusOverride || 404;
|
|
989
|
+
}
|
|
990
|
+
if (r && typeof r.path === "string") {
|
|
991
|
+
try {
|
|
992
|
+
const abs = join(clientDir, "routes", String(r.path));
|
|
993
|
+
const mod = await import(pathToFileURL5(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
994
|
+
const meta = mod?.meta;
|
|
995
|
+
const ssr = mod?.ssr;
|
|
996
|
+
if (meta)
|
|
997
|
+
perPageHead += buildHeadFromMeta(meta);
|
|
998
|
+
if (ssr) {
|
|
999
|
+
const ssrHead = typeof ssr.head === "string" ? ssr.head : Array.isArray(ssr.head) ? String(ssr.head.join(`
|
|
1000
|
+
`)) : "";
|
|
1001
|
+
if (ssrHead)
|
|
1002
|
+
perPageHead += `
|
|
1003
|
+
${ssrHead}`;
|
|
1004
|
+
if (typeof ssr.lang === "string" && ssr.lang.trim()) {
|
|
1005
|
+
pageLang = ssr.lang.trim();
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
} catch {}
|
|
1009
|
+
} else {
|
|
1010
|
+
try {
|
|
1011
|
+
const list = m?.notFoundRoutes;
|
|
1012
|
+
if (Array.isArray(list)) {
|
|
1013
|
+
let chosen;
|
|
1014
|
+
let bestLen = -1;
|
|
1015
|
+
for (const nf of list) {
|
|
1016
|
+
const pat = String(nf?.pattern || "/");
|
|
1017
|
+
if (!pathname.startsWith(pat))
|
|
1018
|
+
continue;
|
|
1019
|
+
const len = pat.split("/").filter(Boolean).length;
|
|
1020
|
+
if (len >= bestLen) {
|
|
1021
|
+
bestLen = len;
|
|
1022
|
+
chosen = nf;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (chosen && typeof chosen.path === "string") {
|
|
1026
|
+
try {
|
|
1027
|
+
const abs = join(clientDir, "routes", String(chosen.path));
|
|
1028
|
+
const mod = await import(pathToFileURL5(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
1029
|
+
const meta = mod?.meta;
|
|
1030
|
+
const ssr = mod?.ssr;
|
|
1031
|
+
if (meta)
|
|
1032
|
+
perPageHead += buildHeadFromMeta(meta);
|
|
1033
|
+
const ssrHead = typeof ssr?.head === "string" ? ssr.head : Array.isArray(ssr?.head) ? String(ssr.head.join(`
|
|
1034
|
+
`)) : "";
|
|
1035
|
+
if (ssrHead)
|
|
1036
|
+
perPageHead += `
|
|
1037
|
+
${ssrHead}`;
|
|
1038
|
+
if (typeof ssr?.lang === "string" && ssr.lang?.trim()) {
|
|
1039
|
+
pageLang = String(ssr.lang).trim();
|
|
1040
|
+
}
|
|
1041
|
+
} catch {}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
} catch {}
|
|
1045
|
+
}
|
|
1046
|
+
} catch {}
|
|
1047
|
+
const combinedHead = dedent_default([dedent_default(head) || "", dedent_default(extraHead), dedent_default(perPageHead)].filter(Boolean).join(`
|
|
1048
|
+
`));
|
|
1049
|
+
const html2 = applyIndexTemplate(baseTemplate, appHtml, {
|
|
1050
|
+
head: [inlineStyleTag, combinedHead].filter(Boolean).join(`
|
|
1051
|
+
`),
|
|
1052
|
+
hydrationScript,
|
|
1053
|
+
lang: pageLang || lang,
|
|
1054
|
+
appId
|
|
1055
|
+
});
|
|
1056
|
+
return {
|
|
1057
|
+
html: html2,
|
|
1058
|
+
status: statusOverride || 200
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
// packages/core/src/utils/cache.ts
|
|
1062
|
+
class LRUCache {
|
|
1063
|
+
cache;
|
|
1064
|
+
maxSize;
|
|
1065
|
+
constructor(maxSize = 100) {
|
|
1066
|
+
this.cache = new Map;
|
|
1067
|
+
this.maxSize = maxSize;
|
|
1068
|
+
}
|
|
1069
|
+
get(key) {
|
|
1070
|
+
if (!this.cache.has(key)) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
const value = this.cache.get(key);
|
|
1074
|
+
this.cache.delete(key);
|
|
1075
|
+
this.cache.set(key, value);
|
|
1076
|
+
return value;
|
|
1077
|
+
}
|
|
1078
|
+
set(key, value) {
|
|
1079
|
+
if (this.cache.has(key)) {
|
|
1080
|
+
this.cache.delete(key);
|
|
1081
|
+
}
|
|
1082
|
+
this.cache.set(key, value);
|
|
1083
|
+
if (this.cache.size > this.maxSize) {
|
|
1084
|
+
const firstKey = this.cache.keys().next().value;
|
|
1085
|
+
this.cache.delete(firstKey);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
has(key) {
|
|
1089
|
+
return this.cache.has(key);
|
|
1090
|
+
}
|
|
1091
|
+
clear() {
|
|
1092
|
+
this.cache.clear();
|
|
1093
|
+
}
|
|
1094
|
+
get size() {
|
|
1095
|
+
return this.cache.size;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
// packages/core/src/utils/mime.ts
|
|
1099
|
+
function getMimeType(filePath) {
|
|
1100
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
1101
|
+
const mimeTypes = {
|
|
1102
|
+
html: "text/html",
|
|
1103
|
+
css: "text/css",
|
|
1104
|
+
js: "application/javascript",
|
|
1105
|
+
ts: "application/javascript",
|
|
1106
|
+
json: "application/json",
|
|
1107
|
+
png: "image/png",
|
|
1108
|
+
jpg: "image/jpeg",
|
|
1109
|
+
jpeg: "image/jpeg",
|
|
1110
|
+
gif: "image/gif",
|
|
1111
|
+
svg: "image/svg+xml",
|
|
1112
|
+
ico: "image/x-icon",
|
|
1113
|
+
woff: "font/woff",
|
|
1114
|
+
woff2: "font/woff2",
|
|
1115
|
+
ttf: "font/ttf"
|
|
1116
|
+
};
|
|
1117
|
+
return mimeTypes[ext || ""] || "application/octet-stream";
|
|
1118
|
+
}
|
|
1119
|
+
function isCompressible(contentType) {
|
|
1120
|
+
if (!contentType)
|
|
1121
|
+
return false;
|
|
1122
|
+
const ct = contentType.split(";")[0].trim();
|
|
1123
|
+
return ct.startsWith("text/") || ct === "application/javascript" || ct === "application/json" || ct === "application/xml" || ct === "image/svg+xml";
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// packages/core/src/utils/compression.ts
|
|
1127
|
+
function acceptsGzip(acceptEncoding) {
|
|
1128
|
+
return Boolean(acceptEncoding && /gzip/i.test(acceptEncoding));
|
|
1129
|
+
}
|
|
1130
|
+
function toBytes(input) {
|
|
1131
|
+
return typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
1132
|
+
}
|
|
1133
|
+
function gzipIfAccepted(body, contentType, acceptEncoding) {
|
|
1134
|
+
const extraHeaders = {};
|
|
1135
|
+
if (acceptsGzip(acceptEncoding) && isCompressible(contentType)) {
|
|
1136
|
+
try {
|
|
1137
|
+
const compressed = Bun.gzipSync(toBytes(body));
|
|
1138
|
+
extraHeaders["Content-Encoding"] = "gzip";
|
|
1139
|
+
extraHeaders.Vary = "Accept-Encoding";
|
|
1140
|
+
return { body: compressed, extraHeaders };
|
|
1141
|
+
} catch {}
|
|
1142
|
+
}
|
|
1143
|
+
return { body, extraHeaders };
|
|
1144
|
+
}
|
|
1145
|
+
export {
|
|
1146
|
+
transpileFile,
|
|
1147
|
+
toBytes,
|
|
1148
|
+
stripStart,
|
|
1149
|
+
stripEnd,
|
|
1150
|
+
seedSSRModuleForPath,
|
|
1151
|
+
renderSSRDocument,
|
|
1152
|
+
rebuildContentArtifacts,
|
|
1153
|
+
loadIndexHtml,
|
|
1154
|
+
listContentFiles,
|
|
1155
|
+
join,
|
|
1156
|
+
isCompressible,
|
|
1157
|
+
gzipIfAccepted,
|
|
1158
|
+
getMimeType,
|
|
1159
|
+
getContentMeta,
|
|
1160
|
+
getBundleUrlsFor,
|
|
1161
|
+
generateContentRegistry,
|
|
1162
|
+
generateContentHash,
|
|
1163
|
+
extname,
|
|
1164
|
+
discoverCollections,
|
|
1165
|
+
computeSSRDataForPath,
|
|
1166
|
+
buildHeadFromMeta,
|
|
1167
|
+
buildContentDTOs,
|
|
1168
|
+
basename,
|
|
1169
|
+
applyIndexTemplate,
|
|
1170
|
+
acceptsGzip,
|
|
1171
|
+
LRUCache
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
//# debugId=3C32F25BA85F27F764756E2164756E21
|