reroute-js 0.9.0 → 0.9.2
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/_/basic/package.json +1 -0
- package/_/basic/src/client/index.html +0 -1
- package/_/blog/package.json +1 -0
- package/_/store/package.json +1 -0
- package/cli/bin.d.ts +1 -1
- package/cli/bin.js +558 -674
- package/cli/bin.js.map +19 -24
- package/cli/index.d.ts +1 -1
- package/cli/index.js +47 -24
- package/cli/index.js.map +5 -5
- package/cli/src/cli.d.ts +1 -1
- package/cli/src/commands/analyze.d.ts +1 -1
- package/cli/src/commands/build.d.ts +3 -4
- package/cli/src/commands/build.d.ts.map +1 -1
- package/cli/src/commands/dev.d.ts +2 -3
- package/cli/src/commands/dev.d.ts.map +1 -1
- package/cli/src/commands/gen.d.ts +1 -1
- package/cli/src/commands/gen.d.ts.map +1 -1
- package/cli/src/commands/init.d.ts +1 -1
- package/cli/src/commands/start.d.ts +3 -4
- package/cli/src/commands/start.d.ts.map +1 -1
- package/cli/src/libs/command.d.ts +1 -1
- package/cli/src/libs/index.d.ts +1 -1
- package/cli/src/libs/log.d.ts +1 -1
- package/cli/src/libs/log.d.ts.map +1 -1
- package/cli/src/libs/markdown-processor.d.ts +2 -1
- package/cli/src/libs/markdown-processor.d.ts.map +1 -1
- package/cli/src/libs/markdown.d.ts +1 -1
- package/cli/src/libs/production.d.ts +1 -1
- package/cli/src/libs/server.d.ts +18 -0
- package/cli/src/libs/server.d.ts.map +1 -0
- package/cli/src/libs/tailwind.d.ts +2 -2
- package/cli/src/libs/tailwind.d.ts.map +1 -1
- package/cli/src/libs/version.d.ts +1 -1
- package/core/index.d.ts +1 -1
- package/core/index.js +99 -410
- package/core/index.js.map +7 -10
- package/core/src/bundler/hash.d.ts +1 -1
- package/core/src/bundler/index.d.ts +1 -2
- package/core/src/bundler/index.d.ts.map +1 -1
- package/core/src/content/discovery.d.ts +1 -3
- package/core/src/content/discovery.d.ts.map +1 -1
- package/core/src/content/index.d.ts +1 -2
- package/core/src/content/index.d.ts.map +1 -1
- package/core/src/content/metadata.d.ts +1 -1
- package/core/src/index.d.ts +1 -1
- package/core/src/ssr/data.d.ts +1 -1
- package/core/src/ssr/index.d.ts +1 -1
- package/core/src/ssr/modules.d.ts +1 -1
- package/core/src/ssr/render.d.ts +1 -1
- package/core/src/ssr/render.d.ts.map +1 -1
- package/core/src/ssr/seed.d.ts +1 -1
- package/core/src/template/html.d.ts +1 -1
- package/core/src/template/index.d.ts +1 -1
- package/core/src/types.d.ts +2 -22
- package/core/src/types.d.ts.map +1 -1
- package/core/src/utils/cache.d.ts +1 -1
- package/core/src/utils/compression.d.ts +1 -1
- package/core/src/utils/compression.d.ts.map +1 -1
- package/core/src/utils/index.d.ts +1 -1
- package/core/src/utils/mime.d.ts +1 -1
- package/core/src/utils/path.d.ts +1 -1
- package/elysia/index.d.ts +1 -1
- package/elysia/index.js +300 -773
- package/elysia/index.js.map +14 -17
- package/elysia/src/index.d.ts +1 -1
- package/elysia/src/libs/cache.d.ts +14 -0
- package/elysia/src/libs/cache.d.ts.map +1 -0
- package/elysia/src/libs/http.d.ts +1 -1
- package/elysia/src/libs/image.d.ts +1 -1
- package/elysia/src/libs/image.d.ts.map +1 -1
- package/elysia/src/plugin.d.ts +1 -1
- package/elysia/src/plugin.d.ts.map +1 -1
- package/elysia/src/routes/artifacts.d.ts +9 -2
- package/elysia/src/routes/artifacts.d.ts.map +1 -1
- package/elysia/src/routes/content.d.ts +3 -2
- package/elysia/src/routes/content.d.ts.map +1 -1
- package/elysia/src/routes/dev.d.ts +1 -1
- package/elysia/src/routes/dev.d.ts.map +1 -1
- package/elysia/src/routes/image.d.ts +1 -1
- package/elysia/src/routes/ssr.d.ts +1 -1
- package/elysia/src/routes/ssr.d.ts.map +1 -1
- package/elysia/src/routes/static.d.ts +2 -5
- package/elysia/src/routes/static.d.ts.map +1 -1
- package/elysia/src/types.d.ts +10 -3
- package/elysia/src/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/react/index.d.ts +1 -1
- package/react/index.js +17 -2
- package/react/index.js.map +5 -4
- package/react/src/components/ClientOnly.d.ts +1 -1
- package/react/src/components/ContentRoute.d.ts +1 -1
- package/react/src/components/Image.d.ts +1 -1
- package/react/src/components/Link.d.ts +1 -1
- package/react/src/components/Markdown.d.ts +1 -1
- package/react/src/components/Outlet.d.ts +1 -1
- package/react/src/components/index.d.ts +1 -1
- package/react/src/hooks/index.d.ts +1 -1
- package/react/src/hooks/useContent.d.ts +1 -1
- package/react/src/hooks/useData.d.ts +1 -1
- package/react/src/hooks/useNavigate.d.ts +1 -1
- package/react/src/hooks/useParams.d.ts +1 -1
- package/react/src/hooks/useRouter.d.ts +1 -1
- package/react/src/hooks/useSearchParams.d.ts +1 -1
- package/react/src/index.d.ts +1 -1
- package/react/src/providers/ContentProvider.d.ts +1 -1
- package/react/src/providers/RerouteProvider.d.ts +1 -1
- package/react/src/providers/RouterProvider.d.ts +1 -1
- package/react/src/providers/index.d.ts +1 -1
- package/react/src/types/any.d.ts +1 -1
- package/react/src/types/index.d.ts +1 -1
- package/react/src/types/router.d.ts +1 -1
- package/react/src/utils/content.d.ts +1 -1
- package/react/src/utils/head.d.ts +1 -1
- package/react/src/utils/index.d.ts +2 -1
- package/react/src/utils/index.d.ts.map +1 -1
- package/react/src/utils/lazy-route.d.ts +41 -0
- package/react/src/utils/lazy-route.d.ts.map +1 -0
- package/cli/src/commands/boot.d.ts +0 -18
- package/cli/src/commands/boot.d.ts.map +0 -1
- package/core/src/bundler/transpile.d.ts +0 -14
- package/core/src/bundler/transpile.d.ts.map +0 -1
- package/core/src/content/registry.d.ts +0 -11
- package/core/src/content/registry.d.ts.map +0 -1
package/elysia/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* reroute-js v0.9.
|
|
2
|
+
* reroute-js v0.9.2
|
|
3
3
|
*
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright 2025 stewones <hi@stewan.io>
|
|
@@ -7,338 +7,33 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Built with Bun <3
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
13
|
-
var __defProp = Object.defineProperty;
|
|
14
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
-
var __toESM = (mod, isNodeMode, target) => {
|
|
17
|
-
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
18
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
19
|
-
for (let key of __getOwnPropNames(mod))
|
|
20
|
-
if (!__hasOwnProp.call(to, key))
|
|
21
|
-
__defProp(to, key, {
|
|
22
|
-
get: () => mod[key],
|
|
23
|
-
enumerable: true
|
|
24
|
-
});
|
|
25
|
-
return to;
|
|
26
|
-
};
|
|
27
|
-
var __export = (target, all) => {
|
|
28
|
-
for (var name in all)
|
|
29
|
-
__defProp(target, name, {
|
|
30
|
-
get: all[name],
|
|
31
|
-
enumerable: true,
|
|
32
|
-
configurable: true,
|
|
33
|
-
set: (newValue) => all[name] = () => newValue
|
|
34
|
-
});
|
|
35
|
-
};
|
|
36
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
10
|
+
// packages/core/src/content/discovery.ts
|
|
11
|
+
import { readdir, stat } from "node:fs/promises";
|
|
37
12
|
|
|
38
13
|
// packages/core/src/utils/path.ts
|
|
39
|
-
|
|
40
|
-
__export(exports_path, {
|
|
41
|
-
stripStart: () => stripStart,
|
|
42
|
-
stripEnd: () => stripEnd,
|
|
43
|
-
join: () => join2,
|
|
44
|
-
extname: () => extname,
|
|
45
|
-
basename: () => basename
|
|
46
|
-
});
|
|
47
|
-
function join2(...parts) {
|
|
14
|
+
function join(...parts) {
|
|
48
15
|
return parts.join("/").replace(/\/+/g, "/");
|
|
49
16
|
}
|
|
50
|
-
function extname(p) {
|
|
51
|
-
const i = p.lastIndexOf(".");
|
|
52
|
-
return i >= 0 ? p.slice(i) : "";
|
|
53
|
-
}
|
|
54
|
-
function basename(p, ext) {
|
|
55
|
-
const name = p.replace(/\\/g, "/").split("/").pop() || p;
|
|
56
|
-
return ext && name.endsWith(ext) ? name.slice(0, -ext.length) : name;
|
|
57
|
-
}
|
|
58
|
-
function stripStart(p, ch) {
|
|
59
|
-
return p.startsWith(ch) ? p.slice(ch.length) : p;
|
|
60
|
-
}
|
|
61
|
-
function stripEnd(p, ch) {
|
|
62
|
-
return p.endsWith(ch) ? p.slice(0, -ch.length) : p;
|
|
63
|
-
}
|
|
64
17
|
|
|
65
|
-
// packages/core/src/
|
|
66
|
-
async function
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
let hex = "";
|
|
70
|
-
for (const b of new Uint8Array(buf))
|
|
71
|
-
hex += b.toString(16).padStart(2, "0");
|
|
72
|
-
return hex.slice(0, 8);
|
|
73
|
-
}
|
|
74
|
-
// packages/cli/src/libs/tailwind.ts
|
|
75
|
-
import { spawn } from "node:child_process";
|
|
76
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
77
|
-
import { join } from "node:path";
|
|
78
|
-
function isTailwindAvailable(cwd) {
|
|
79
|
-
const packageJsonPath = join(cwd, "package.json");
|
|
80
|
-
if (!existsSync(packageJsonPath)) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
try {
|
|
84
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
85
|
-
const deps = {
|
|
86
|
-
...packageJson.dependencies,
|
|
87
|
-
...packageJson.devDependencies
|
|
88
|
-
};
|
|
89
|
-
return "@tailwindcss/cli" in deps;
|
|
90
|
-
} catch {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function getTailwindPaths(cwd) {
|
|
95
|
-
const input = join(cwd, "src/client/theme.css");
|
|
96
|
-
const output = join(cwd, ".reroute/theme.css");
|
|
97
|
-
return { input, output };
|
|
98
|
-
}
|
|
99
|
-
function hasTailwindInput(cwd) {
|
|
100
|
-
const { input } = getTailwindPaths(cwd);
|
|
101
|
-
if (!existsSync(input)) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
try {
|
|
105
|
-
const content = readFileSync(input, "utf-8");
|
|
106
|
-
return content.includes("tailwindcss");
|
|
107
|
-
} catch {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function resolveTailwindBin(cwd) {
|
|
112
|
-
const pathsToTry = [
|
|
113
|
-
join(cwd, "../node_modules/.bin/tailwindcss"),
|
|
114
|
-
join(cwd, "../../node_modules/.bin/tailwindcss"),
|
|
115
|
-
join(cwd, "../../../node_modules/.bin/tailwindcss")
|
|
116
|
-
];
|
|
117
|
-
for (const binPath of pathsToTry) {
|
|
118
|
-
if (existsSync(binPath)) {
|
|
119
|
-
return binPath;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return join(cwd, "node_modules/.bin/tailwindcss");
|
|
123
|
-
}
|
|
124
|
-
async function buildTailwind(cwd) {
|
|
125
|
-
const { input, output } = getTailwindPaths(cwd);
|
|
126
|
-
return new Promise((resolve, reject) => {
|
|
127
|
-
const tailwindBin = resolveTailwindBin(cwd);
|
|
128
|
-
const args = ["-i", input, "-o", output];
|
|
129
|
-
console.log(`[reroute/tailwind] build start: -i ${input} -o ${output}`);
|
|
130
|
-
const tailwind = spawn(tailwindBin, args, {
|
|
131
|
-
cwd,
|
|
132
|
-
stdio: "pipe"
|
|
133
|
-
});
|
|
134
|
-
tailwind.stdout?.on("data", (data) => {
|
|
135
|
-
const line = data.toString().trim();
|
|
136
|
-
if (line) {
|
|
137
|
-
console.log(`[reroute/tailwind] ${line}`);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
tailwind.stderr?.on("data", (data) => {
|
|
141
|
-
const line = data.toString().trim();
|
|
142
|
-
if (line) {
|
|
143
|
-
console.log(`[reroute/tailwind] ${line}`);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
tailwind.on("close", (code) => {
|
|
147
|
-
if (code === 0) {
|
|
148
|
-
console.log("[reroute/tailwind] build done");
|
|
149
|
-
resolve();
|
|
150
|
-
} else {
|
|
151
|
-
reject(new Error(`Tailwind CSS build failed with code ${code}`));
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
tailwind.on("error", (error) => {
|
|
155
|
-
reject(error);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
async function buildTailwindIfConfigured(cwd) {
|
|
160
|
-
try {
|
|
161
|
-
if (isTailwindAvailable(cwd) && hasTailwindInput(cwd)) {
|
|
162
|
-
await buildTailwind(cwd);
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
return false;
|
|
166
|
-
} catch (e) {
|
|
167
|
-
console.warn("[reroute/tailwind] build failed:", e);
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// packages/core/src/bundler/transpile.ts
|
|
172
|
-
var entryUrlToStaticDeps = new Map;
|
|
173
|
-
function isWatchMode() {
|
|
174
|
-
try {
|
|
175
|
-
return Array.isArray(process.execArgv) && process.execArgv.includes("--watch") || Array.isArray(process.argv) && process.argv.includes("--watch");
|
|
176
|
-
} catch {
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
async function transpileFile(filePath, originalPath, options, bundleCache) {
|
|
181
|
-
const src = await Bun.file(filePath).text();
|
|
182
|
-
const cacheKey = `${filePath}-${await generateContentHash(src)}`;
|
|
183
|
-
if (bundleCache.has(cacheKey)) {
|
|
184
|
-
return bundleCache.get(cacheKey);
|
|
185
|
-
}
|
|
186
|
-
console.log(`[reroute] Building ${originalPath}${options.minify ? " (minified)" : ""}...`);
|
|
187
|
-
try {
|
|
188
|
-
if (isWatchMode()) {
|
|
189
|
-
const cwd = typeof process !== "undefined" && process.cwd ? process.cwd() : "";
|
|
190
|
-
if (cwd) {
|
|
191
|
-
await buildTailwindIfConfigured(cwd);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
} catch (e) {
|
|
195
|
-
console.warn("[reroute/tailwind] rebuild failed:", e);
|
|
196
|
-
}
|
|
197
|
-
async function doBuild(sm) {
|
|
198
|
-
return await Bun.build({
|
|
199
|
-
entrypoints: [filePath],
|
|
200
|
-
target: "browser",
|
|
201
|
-
format: "esm",
|
|
202
|
-
minify: options.minify,
|
|
203
|
-
splitting: true,
|
|
204
|
-
sourcemap: sm,
|
|
205
|
-
jsx: {
|
|
206
|
-
runtime: "automatic",
|
|
207
|
-
importSource: "react",
|
|
208
|
-
development: !options.minify
|
|
209
|
-
},
|
|
210
|
-
define: {
|
|
211
|
-
"process.env.NODE_ENV": options.minify ? '"production"' : '"development"',
|
|
212
|
-
__DEV__: options.minify ? "false" : "true",
|
|
213
|
-
"import.meta.env.MODE": options.minify ? '"production"' : '"development"'
|
|
214
|
-
},
|
|
215
|
-
...options.minify ? { drop: ["console", "debugger"] } : {}
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
let result;
|
|
18
|
+
// packages/core/src/content/discovery.ts
|
|
19
|
+
async function discoverCollections(clientDir) {
|
|
20
|
+
const root = join(clientDir, "routes");
|
|
21
|
+
const collections = new Set;
|
|
219
22
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
console.error(log2);
|
|
229
|
-
throw new Error(`Failed to transpile ${filePath}`);
|
|
230
|
-
}
|
|
231
|
-
const outputs = await Promise.all(result.outputs.map(async (o) => ({
|
|
232
|
-
path: o.path,
|
|
233
|
-
text: await o.text()
|
|
234
|
-
})));
|
|
235
|
-
const entryBase = basename(originalPath, extname(originalPath));
|
|
236
|
-
let entry = outputs.find((o) => o.path.endsWith(`${entryBase}.js`));
|
|
237
|
-
if (!entry) {
|
|
238
|
-
entry = outputs.filter((o) => o.path.endsWith(".js")).sort((a, b) => b.text.length - a.text.length)[0];
|
|
239
|
-
}
|
|
240
|
-
if (!entry) {
|
|
241
|
-
throw new Error(`[reroute] No JS output generated for ${originalPath}`);
|
|
242
|
-
}
|
|
243
|
-
const code = entry.text;
|
|
244
|
-
let sourceMap;
|
|
245
|
-
if (options.sourcemap && result.outputs.length > 1) {
|
|
246
|
-
const mapOutput = result.outputs.find((o) => o.path.endsWith(".map"));
|
|
247
|
-
if (mapOutput) {
|
|
248
|
-
sourceMap = await mapOutput.text();
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
const contentHash = await generateContentHash(code);
|
|
252
|
-
const chunks = outputs.filter((o) => o !== entry && o.path.endsWith(".js")).map((o) => ({
|
|
253
|
-
fileName: basename(o.path),
|
|
254
|
-
code: o.text,
|
|
255
|
-
hash: "",
|
|
256
|
-
isEntry: false,
|
|
257
|
-
imports: []
|
|
258
|
-
}));
|
|
259
|
-
for (const c of chunks) {
|
|
260
|
-
c.hash = await generateContentHash(c.code);
|
|
261
|
-
try {
|
|
262
|
-
const bn = c.fileName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
263
|
-
const re = new RegExp(`["'\`]((?:\\./)?${bn})["'\`]`, "g");
|
|
264
|
-
const matches = [...code.matchAll(re)];
|
|
265
|
-
if (matches.length) {
|
|
266
|
-
c.isEntry = false;
|
|
23
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
if (entry.isDirectory()) {
|
|
26
|
+
const contentDir = join(root, entry.name, "content");
|
|
27
|
+
try {
|
|
28
|
+
await stat(contentDir);
|
|
29
|
+
collections.add(entry.name);
|
|
30
|
+
} catch {}
|
|
267
31
|
}
|
|
268
|
-
} catch {}
|
|
269
|
-
}
|
|
270
|
-
const staticDepNames = [];
|
|
271
|
-
try {
|
|
272
|
-
for (const c of chunks) {
|
|
273
|
-
const bn = c.fileName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
274
|
-
const re = new RegExp(`["'\`]((?:\\./)?${bn})["'\`]`, "g");
|
|
275
|
-
if (re.test(code))
|
|
276
|
-
staticDepNames.push(c.fileName);
|
|
277
32
|
}
|
|
278
33
|
} catch {}
|
|
279
|
-
|
|
280
|
-
hash: contentHash,
|
|
281
|
-
code,
|
|
282
|
-
sourceMap,
|
|
283
|
-
chunks
|
|
284
|
-
};
|
|
285
|
-
bundleCache.set(cacheKey, bundleInfo);
|
|
286
|
-
const sizeKB = (code.length / 1024).toFixed(2);
|
|
287
|
-
const gzippedSize = Bun.gzipSync(new TextEncoder().encode(code)).length;
|
|
288
|
-
const gzippedKB = (gzippedSize / 1024).toFixed(2);
|
|
289
|
-
console.log(`[reroute] Built ${originalPath} -> ${sizeKB} KB (${gzippedKB} KB gzipped)`);
|
|
290
|
-
return bundleInfo;
|
|
291
|
-
}
|
|
292
|
-
async function getBundleUrlsFor(modules, clientDir, prefix, options, bundleCache) {
|
|
293
|
-
const mods = Array.isArray(modules) ? modules : [modules];
|
|
294
|
-
const urls = [];
|
|
295
|
-
const { join: join3 } = await Promise.resolve().then(() => exports_path);
|
|
296
|
-
for (const mod of mods) {
|
|
297
|
-
const rel = mod.replace(/^\.\//, "");
|
|
298
|
-
const fullPath = join3(clientDir, rel);
|
|
299
|
-
try {
|
|
300
|
-
const bundleInfo = await transpileFile(fullPath, rel, options, bundleCache);
|
|
301
|
-
const base = basename(rel, extname(rel));
|
|
302
|
-
const entryUrl = `${prefix}/${base}.${bundleInfo.hash}.js`;
|
|
303
|
-
urls.push(entryUrl);
|
|
304
|
-
try {
|
|
305
|
-
const depNames = Array.isArray(bundleInfo.chunks) ? bundleInfo.chunks.map((c) => c.fileName).filter((name) => new RegExp(`["'\`]((?:\\./)?${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})["'\`]`, "g").test(bundleInfo.code)) : [];
|
|
306
|
-
const depUrls = depNames.map((n) => `${prefix}/${n}`.replace(/\/+/g, "/"));
|
|
307
|
-
entryUrlToStaticDeps.set(entryUrl, depUrls);
|
|
308
|
-
} catch {}
|
|
309
|
-
} catch (error) {
|
|
310
|
-
console.error(`[reroute] Error getting bundle URL for ${rel}:`, error);
|
|
311
|
-
urls.push(`${prefix}/${rel}`);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return urls;
|
|
315
|
-
}
|
|
316
|
-
function getStaticDepsForUrl(entryUrl) {
|
|
317
|
-
return entryUrlToStaticDeps.get(entryUrl) || [];
|
|
34
|
+
return Array.from(collections);
|
|
318
35
|
}
|
|
319
|
-
// packages/core/src/content/discovery.ts
|
|
320
|
-
import { readdir, stat as stat2 } from "node:fs/promises";
|
|
321
|
-
|
|
322
36
|
// packages/core/src/content/metadata.ts
|
|
323
|
-
import { stat } from "node:fs/promises";
|
|
324
|
-
import { pathToFileURL } from "node:url";
|
|
325
|
-
async function getContentMeta(absPath, isWatchMode2) {
|
|
326
|
-
try {
|
|
327
|
-
const url = pathToFileURL(absPath).href;
|
|
328
|
-
const mod = await import(`${url}${isWatchMode2 ? `?t=${Date.now()}` : ""}`);
|
|
329
|
-
const meta = mod.meta || mod.frontmatter || {};
|
|
330
|
-
if (meta && typeof meta === "object")
|
|
331
|
-
return meta;
|
|
332
|
-
return {};
|
|
333
|
-
} catch {
|
|
334
|
-
try {
|
|
335
|
-
const s = await stat(absPath);
|
|
336
|
-
return { date: new Date(s.mtimeMs).toISOString() };
|
|
337
|
-
} catch {
|
|
338
|
-
return {};
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
37
|
function buildHeadFromMeta(meta) {
|
|
343
38
|
if (!meta || typeof meta !== "object")
|
|
344
39
|
return "";
|
|
@@ -356,84 +51,13 @@ ${parts.join(`
|
|
|
356
51
|
function escapeHtml(input) {
|
|
357
52
|
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
358
53
|
}
|
|
359
|
-
|
|
360
|
-
// packages/core/src/content/discovery.ts
|
|
361
|
-
async function listContentFiles(dir, baseRel) {
|
|
362
|
-
const out = [];
|
|
363
|
-
try {
|
|
364
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
365
|
-
for (const entry of entries) {
|
|
366
|
-
const full = join2(dir, entry.name);
|
|
367
|
-
const rel = join2(baseRel, entry.name);
|
|
368
|
-
if (entry.isDirectory()) {
|
|
369
|
-
const nested = await listContentFiles(full, rel);
|
|
370
|
-
out.push(...nested);
|
|
371
|
-
} else if (entry.isFile()) {
|
|
372
|
-
if (/\.(tsx|ts)$/.test(entry.name) && !entry.name.startsWith("_")) {
|
|
373
|
-
out.push(rel);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
} catch {}
|
|
378
|
-
return out;
|
|
379
|
-
}
|
|
380
|
-
async function buildContentDTOs(collectionPath, clientDir, prefix, isWatchMode2) {
|
|
381
|
-
const normalized = stripStart(stripEnd(collectionPath, "/"), "/");
|
|
382
|
-
const contentRelDir = join2("routes", normalized, "content");
|
|
383
|
-
const contentAbsDir = join2(clientDir, contentRelDir);
|
|
384
|
-
const files = await listContentFiles(contentAbsDir, "");
|
|
385
|
-
const items = [];
|
|
386
|
-
for (const rel of files) {
|
|
387
|
-
const fullRelPath = join2(contentRelDir, rel);
|
|
388
|
-
const absPath = join2(clientDir, fullRelPath);
|
|
389
|
-
const noExt = rel.replace(/\.(tsx|ts)$/, "");
|
|
390
|
-
const name = basename(noExt);
|
|
391
|
-
const moduleUrl = `${prefix}/${fullRelPath}`.replace(/\/+/g, "/");
|
|
392
|
-
const meta = await getContentMeta(absPath, isWatchMode2);
|
|
393
|
-
const href = `/${normalized}/${name}`.replace(/\\+/g, "/");
|
|
394
|
-
items.push({
|
|
395
|
-
slug: noExt,
|
|
396
|
-
name,
|
|
397
|
-
path: fullRelPath,
|
|
398
|
-
module: moduleUrl,
|
|
399
|
-
meta,
|
|
400
|
-
href
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
return items;
|
|
404
|
-
}
|
|
405
|
-
async function discoverCollections(clientDir) {
|
|
406
|
-
const root = join2(clientDir, "routes");
|
|
407
|
-
const collections = new Set;
|
|
408
|
-
try {
|
|
409
|
-
const entries = await readdir(root, { withFileTypes: true });
|
|
410
|
-
for (const entry of entries) {
|
|
411
|
-
if (entry.isDirectory()) {
|
|
412
|
-
const contentDir = join2(root, entry.name, "content");
|
|
413
|
-
try {
|
|
414
|
-
await stat2(contentDir);
|
|
415
|
-
collections.add(entry.name);
|
|
416
|
-
} catch {}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
} catch {}
|
|
420
|
-
return Array.from(collections);
|
|
421
|
-
}
|
|
422
|
-
// packages/core/src/content/registry.ts
|
|
423
|
-
async function generateContentRegistry(cwd) {
|
|
424
|
-
try {
|
|
425
|
-
const p = join2(cwd, ".reroute", "content.ts");
|
|
426
|
-
const exists = await Bun.file(p).exists();
|
|
427
|
-
console.log(exists ? "[reroute] Content registry up-to-date" : "[reroute] Warning: .reroute/content.ts not found");
|
|
428
|
-
} catch {}
|
|
429
|
-
}
|
|
430
54
|
// packages/core/src/ssr/data.ts
|
|
431
|
-
import { pathToFileURL as
|
|
55
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
432
56
|
|
|
433
57
|
// packages/core/src/ssr/modules.ts
|
|
434
|
-
import { readdir as readdir2, stat as
|
|
435
|
-
import { pathToFileURL
|
|
436
|
-
async function importContentModuleForPath(pathname, clientDir, cwd,
|
|
58
|
+
import { readdir as readdir2, stat as stat2 } from "node:fs/promises";
|
|
59
|
+
import { pathToFileURL } from "node:url";
|
|
60
|
+
async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
437
61
|
try {
|
|
438
62
|
const parts = pathname.split("/").filter(Boolean);
|
|
439
63
|
if (parts.length < 2)
|
|
@@ -441,20 +65,20 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode2
|
|
|
441
65
|
const collection = parts[0];
|
|
442
66
|
const name = parts[1];
|
|
443
67
|
try {
|
|
444
|
-
const registryPath =
|
|
445
|
-
const reg = await import(
|
|
68
|
+
const registryPath = join(cwd, ".reroute", "content.ts");
|
|
69
|
+
const reg = await import(pathToFileURL(registryPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
446
70
|
const get = reg?.getContentEntry;
|
|
447
71
|
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
448
72
|
const moduleUrl = entry?.module;
|
|
449
73
|
if (moduleUrl?.endsWith(".js")) {
|
|
450
|
-
const abs =
|
|
451
|
-
const href =
|
|
74
|
+
const abs = join(cwd, moduleUrl.replace(/^\//, ""));
|
|
75
|
+
const href = pathToFileURL(abs).href + (isWatchMode ? `?t=${Date.now()}` : "");
|
|
452
76
|
const mod = await import(href);
|
|
453
77
|
return mod;
|
|
454
78
|
}
|
|
455
79
|
} catch {}
|
|
456
80
|
try {
|
|
457
|
-
const chunkDir =
|
|
81
|
+
const chunkDir = join(cwd, ".reroute", "chunks", collection);
|
|
458
82
|
const files = await readdir2(chunkDir);
|
|
459
83
|
const matches = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
460
84
|
if (matches.length) {
|
|
@@ -462,30 +86,30 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode2
|
|
|
462
86
|
let latestM = 0;
|
|
463
87
|
for (const f of matches) {
|
|
464
88
|
try {
|
|
465
|
-
const s = await
|
|
89
|
+
const s = await stat2(join(chunkDir, f));
|
|
466
90
|
if (s.mtimeMs >= latestM) {
|
|
467
91
|
latestM = s.mtimeMs;
|
|
468
92
|
latest = f;
|
|
469
93
|
}
|
|
470
94
|
} catch {}
|
|
471
95
|
}
|
|
472
|
-
const absChunk =
|
|
473
|
-
const href =
|
|
96
|
+
const absChunk = join(chunkDir, latest);
|
|
97
|
+
const href = pathToFileURL(absChunk).href + (isWatchMode ? `?t=${Date.now()}` : "");
|
|
474
98
|
const mod = await import(href);
|
|
475
99
|
return mod;
|
|
476
100
|
}
|
|
477
101
|
} catch {}
|
|
478
102
|
try {
|
|
479
|
-
const srcTsx =
|
|
480
|
-
const srcTs =
|
|
103
|
+
const srcTsx = join(clientDir, "routes", collection, "content", `${name}.tsx`);
|
|
104
|
+
const srcTs = join(clientDir, "routes", collection, "content", `${name}.ts`);
|
|
481
105
|
let absSrc = null;
|
|
482
106
|
if (await Bun.file(srcTsx).exists())
|
|
483
107
|
absSrc = srcTsx;
|
|
484
108
|
else if (await Bun.file(srcTs).exists())
|
|
485
109
|
absSrc = srcTs;
|
|
486
110
|
if (absSrc) {
|
|
487
|
-
const href =
|
|
488
|
-
const mod = await (
|
|
111
|
+
const href = pathToFileURL(absSrc).href;
|
|
112
|
+
const mod = await (isWatchMode ? import(`${href}?t=${Date.now()}`) : import(href));
|
|
489
113
|
return mod;
|
|
490
114
|
}
|
|
491
115
|
} catch {}
|
|
@@ -494,7 +118,7 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode2
|
|
|
494
118
|
}
|
|
495
119
|
|
|
496
120
|
// packages/core/src/ssr/seed.ts
|
|
497
|
-
async function seedSSRModuleForPath(pathname, clientDir, cwd,
|
|
121
|
+
async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
498
122
|
try {
|
|
499
123
|
const parts = pathname.split("/").filter(Boolean);
|
|
500
124
|
if (parts.length < 2)
|
|
@@ -502,7 +126,7 @@ async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode2) {
|
|
|
502
126
|
const collection = parts[0];
|
|
503
127
|
const name = parts[1];
|
|
504
128
|
const key = `${collection}:${name}`;
|
|
505
|
-
const mod = await importContentModuleForPath(pathname, clientDir, cwd,
|
|
129
|
+
const mod = await importContentModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
506
130
|
if (!mod)
|
|
507
131
|
return;
|
|
508
132
|
const C = mod.default || mod;
|
|
@@ -522,9 +146,9 @@ async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode2) {
|
|
|
522
146
|
|
|
523
147
|
// packages/core/src/ssr/data.ts
|
|
524
148
|
async function computeSSRDataForPath(params) {
|
|
525
|
-
const { pathname, clientDir, cwd, isWatchMode
|
|
149
|
+
const { pathname, clientDir, cwd, isWatchMode } = params;
|
|
526
150
|
try {
|
|
527
|
-
await seedSSRModuleForPath(pathname, clientDir, cwd,
|
|
151
|
+
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
528
152
|
} catch {}
|
|
529
153
|
try {
|
|
530
154
|
const parts = pathname.split("/").filter(Boolean);
|
|
@@ -539,15 +163,15 @@ async function computeSSRDataForPath(params) {
|
|
|
539
163
|
}
|
|
540
164
|
} catch {}
|
|
541
165
|
try {
|
|
542
|
-
const routesPath =
|
|
543
|
-
const m = await import(
|
|
166
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
167
|
+
const m = await import(pathToFileURL2(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
544
168
|
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
545
169
|
const r = match?.route;
|
|
546
170
|
const paramsValue = match?.params || {};
|
|
547
171
|
if (r && typeof r.path === "string") {
|
|
548
172
|
try {
|
|
549
|
-
const abs =
|
|
550
|
-
const mod = await import(
|
|
173
|
+
const abs = join(clientDir, "routes", String(r.path));
|
|
174
|
+
const mod = await import(pathToFileURL2(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
551
175
|
const ssr = mod?.ssr;
|
|
552
176
|
const dataFn = ssr?.data;
|
|
553
177
|
if (typeof dataFn === "function") {
|
|
@@ -559,10 +183,10 @@ async function computeSSRDataForPath(params) {
|
|
|
559
183
|
return null;
|
|
560
184
|
}
|
|
561
185
|
// packages/core/src/ssr/render.ts
|
|
562
|
-
import { readdir as readdir3, stat as
|
|
563
|
-
import { pathToFileURL as
|
|
186
|
+
import { readdir as readdir3, stat as stat3 } from "node:fs/promises";
|
|
187
|
+
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
564
188
|
|
|
565
|
-
// node_modules/dedent/dist/dedent.mjs
|
|
189
|
+
// node_modules/.bun/dedent@1.7.0/node_modules/dedent/dist/dedent.mjs
|
|
566
190
|
function ownKeys(object, enumerableOnly) {
|
|
567
191
|
var keys = Object.keys(object);
|
|
568
192
|
if (Object.getOwnPropertySymbols) {
|
|
@@ -684,7 +308,7 @@ import { renderToString } from "react-dom/server";
|
|
|
684
308
|
|
|
685
309
|
// packages/core/src/template/html.ts
|
|
686
310
|
async function loadIndexHtml(clientDir) {
|
|
687
|
-
const templatePath =
|
|
311
|
+
const templatePath = join(clientDir, "index.html");
|
|
688
312
|
try {
|
|
689
313
|
const content = await Bun.file(templatePath).text();
|
|
690
314
|
return content;
|
|
@@ -761,7 +385,7 @@ async function renderSSRDocument(options) {
|
|
|
761
385
|
rootComponent,
|
|
762
386
|
clientDir,
|
|
763
387
|
cwd,
|
|
764
|
-
isWatchMode
|
|
388
|
+
isWatchMode,
|
|
765
389
|
bundleUrl,
|
|
766
390
|
head = "",
|
|
767
391
|
lang = "en",
|
|
@@ -777,15 +401,6 @@ async function renderSSRDocument(options) {
|
|
|
777
401
|
<link rel="modulepreload" href="${bundleUrl}" />`;
|
|
778
402
|
}
|
|
779
403
|
} catch {}
|
|
780
|
-
try {
|
|
781
|
-
const staticDeps = getStaticDepsForUrl(bundleUrl);
|
|
782
|
-
if (Array.isArray(staticDeps) && staticDeps.length) {
|
|
783
|
-
for (const dep of staticDeps) {
|
|
784
|
-
extraHead += `
|
|
785
|
-
<link rel="modulepreload" href="${dep}" />`;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
} catch {}
|
|
789
404
|
let statusOverride;
|
|
790
405
|
try {
|
|
791
406
|
globalThis.__REROUTE_SSR_ACCESSED__ = {};
|
|
@@ -798,8 +413,8 @@ async function renderSSRDocument(options) {
|
|
|
798
413
|
let modulePath = "";
|
|
799
414
|
let isContentCollection = false;
|
|
800
415
|
try {
|
|
801
|
-
const maybeDir =
|
|
802
|
-
const s = await
|
|
416
|
+
const maybeDir = join(clientDir, "routes", collection, "content");
|
|
417
|
+
const s = await stat3(maybeDir);
|
|
803
418
|
isContentCollection = typeof s?.isDirectory === "function" ? s.isDirectory() : true;
|
|
804
419
|
} catch {
|
|
805
420
|
isContentCollection = false;
|
|
@@ -808,8 +423,8 @@ async function renderSSRDocument(options) {
|
|
|
808
423
|
throw new Error("skip-content-preload");
|
|
809
424
|
}
|
|
810
425
|
try {
|
|
811
|
-
const registryPath =
|
|
812
|
-
const reg = await import(
|
|
426
|
+
const registryPath = join(cwd, ".reroute", "content.ts");
|
|
427
|
+
const reg = await import(pathToFileURL3(registryPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
813
428
|
const get = reg?.getContentEntry;
|
|
814
429
|
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
815
430
|
const moduleUrl = entry?.module;
|
|
@@ -819,7 +434,7 @@ async function renderSSRDocument(options) {
|
|
|
819
434
|
} catch {}
|
|
820
435
|
if (!modulePath) {
|
|
821
436
|
try {
|
|
822
|
-
const chunkDir =
|
|
437
|
+
const chunkDir = join(cwd, ".reroute", "chunks", collection);
|
|
823
438
|
const files = await readdir3(chunkDir);
|
|
824
439
|
const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
825
440
|
if (candidates.length) {
|
|
@@ -827,25 +442,25 @@ async function renderSSRDocument(options) {
|
|
|
827
442
|
let latestM = 0;
|
|
828
443
|
for (const candidateName of candidates) {
|
|
829
444
|
try {
|
|
830
|
-
const s = await
|
|
445
|
+
const s = await stat3(join(chunkDir, candidateName));
|
|
831
446
|
if (s.mtimeMs >= latestM) {
|
|
832
447
|
latestM = s.mtimeMs;
|
|
833
448
|
latest = candidateName;
|
|
834
449
|
}
|
|
835
450
|
} catch {}
|
|
836
451
|
}
|
|
837
|
-
modulePath = `/${
|
|
452
|
+
modulePath = `/${join(".reroute", "chunks", collection, latest).replace(/\\+/g, "/")}`;
|
|
838
453
|
}
|
|
839
454
|
} catch {}
|
|
840
455
|
}
|
|
841
456
|
if (!modulePath) {
|
|
842
|
-
const tsx =
|
|
843
|
-
const ts =
|
|
457
|
+
const tsx = join(clientDir, "routes", collection, "content", `${name}.tsx`);
|
|
458
|
+
const ts = join(clientDir, "routes", collection, "content", `${name}.ts`);
|
|
844
459
|
let srcUrl = "";
|
|
845
460
|
if (await Bun.file(tsx).exists()) {
|
|
846
|
-
srcUrl = `/${
|
|
461
|
+
srcUrl = `/${join("routes", collection, "content", `${name}.tsx`).replace(/\\+/g, "/")}`;
|
|
847
462
|
} else if (await Bun.file(ts).exists()) {
|
|
848
|
-
srcUrl = `/${
|
|
463
|
+
srcUrl = `/${join("routes", collection, "content", `${name}.ts`).replace(/\\+/g, "/")}`;
|
|
849
464
|
}
|
|
850
465
|
if (srcUrl) {
|
|
851
466
|
modulePath = srcUrl;
|
|
@@ -863,7 +478,7 @@ async function renderSSRDocument(options) {
|
|
|
863
478
|
}
|
|
864
479
|
}
|
|
865
480
|
} catch {}
|
|
866
|
-
await seedSSRModuleForPath(pathname, clientDir, cwd,
|
|
481
|
+
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
867
482
|
const __SSR_DATA__ = {};
|
|
868
483
|
try {
|
|
869
484
|
try {
|
|
@@ -883,15 +498,15 @@ async function renderSSRDocument(options) {
|
|
|
883
498
|
}
|
|
884
499
|
} catch {}
|
|
885
500
|
try {
|
|
886
|
-
const routesPath =
|
|
887
|
-
const m = await import(
|
|
501
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
502
|
+
const m = await import(pathToFileURL3(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
888
503
|
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
889
504
|
const r = match?.route;
|
|
890
505
|
const params = match?.params || {};
|
|
891
506
|
if (r && typeof r.path === "string") {
|
|
892
507
|
try {
|
|
893
|
-
const abs =
|
|
894
|
-
const mod = await import(
|
|
508
|
+
const abs = join(clientDir, "routes", String(r.path));
|
|
509
|
+
const mod = await import(pathToFileURL3(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
895
510
|
const ssr = mod?.ssr;
|
|
896
511
|
const dataFn = ssr?.data;
|
|
897
512
|
if (typeof dataFn === "function") {
|
|
@@ -907,8 +522,8 @@ async function renderSSRDocument(options) {
|
|
|
907
522
|
} catch {}
|
|
908
523
|
let __byCollectionForSSR = {};
|
|
909
524
|
try {
|
|
910
|
-
const p =
|
|
911
|
-
const mod = await import(
|
|
525
|
+
const p = join(cwd, ".reroute", "content.ts");
|
|
526
|
+
const mod = await import(pathToFileURL3(p).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
912
527
|
__byCollectionForSSR = mod?.byCollection || {};
|
|
913
528
|
try {
|
|
914
529
|
globalThis.__REROUTE_COLLECTIONS__ = __byCollectionForSSR;
|
|
@@ -922,10 +537,10 @@ async function renderSSRDocument(options) {
|
|
|
922
537
|
let inlineStyleTag = "";
|
|
923
538
|
try {
|
|
924
539
|
const candidates = [
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
540
|
+
join(clientDir, "..", ".reroute", "theme.css"),
|
|
541
|
+
join(clientDir, "..", "..", ".reroute", "theme.css"),
|
|
542
|
+
join(clientDir, "..", "..", "..", ".reroute", "theme.css"),
|
|
543
|
+
join(clientDir, "..", "..", "..", "..", ".reroute", "theme.css")
|
|
929
544
|
];
|
|
930
545
|
let cssPath = "";
|
|
931
546
|
for (const p of candidates) {
|
|
@@ -1008,7 +623,7 @@ async function renderSSRDocument(options) {
|
|
|
1008
623
|
</script>`;
|
|
1009
624
|
} catch {}
|
|
1010
625
|
hydrationScript += scripts.map((src) => `<script type="module" src="${src}"></script>`).join("");
|
|
1011
|
-
if (
|
|
626
|
+
if (isWatchMode) {
|
|
1012
627
|
hydrationScript += `<script type="module" src="/__reroute_watch.js"></script>`;
|
|
1013
628
|
}
|
|
1014
629
|
let perPageHead = "";
|
|
@@ -1033,8 +648,8 @@ ${ssrHead}`;
|
|
|
1033
648
|
}
|
|
1034
649
|
} catch {}
|
|
1035
650
|
try {
|
|
1036
|
-
const routesPath =
|
|
1037
|
-
const m = await import(
|
|
651
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
652
|
+
const m = await import(pathToFileURL3(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
1038
653
|
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
1039
654
|
const r = match?.route;
|
|
1040
655
|
if (!r) {
|
|
@@ -1042,8 +657,8 @@ ${ssrHead}`;
|
|
|
1042
657
|
}
|
|
1043
658
|
if (r && typeof r.path === "string") {
|
|
1044
659
|
try {
|
|
1045
|
-
const abs =
|
|
1046
|
-
const mod = await import(
|
|
660
|
+
const abs = join(clientDir, "routes", String(r.path));
|
|
661
|
+
const mod = await import(pathToFileURL3(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
1047
662
|
const meta = mod?.meta;
|
|
1048
663
|
const ssr = mod?.ssr;
|
|
1049
664
|
if (meta)
|
|
@@ -1077,8 +692,8 @@ ${ssrHead}`;
|
|
|
1077
692
|
}
|
|
1078
693
|
if (chosen && typeof chosen.path === "string") {
|
|
1079
694
|
try {
|
|
1080
|
-
const abs =
|
|
1081
|
-
const mod = await import(
|
|
695
|
+
const abs = join(clientDir, "routes", String(chosen.path));
|
|
696
|
+
const mod = await import(pathToFileURL3(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
1082
697
|
const meta = mod?.meta;
|
|
1083
698
|
const ssr = mod?.ssr;
|
|
1084
699
|
if (meta)
|
|
@@ -1261,10 +876,8 @@ function jsonError(message, status = 500) {
|
|
|
1261
876
|
}
|
|
1262
877
|
|
|
1263
878
|
// packages/elysia/src/libs/image.ts
|
|
1264
|
-
import {
|
|
1265
|
-
import {
|
|
1266
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1267
|
-
import { join as join3 } from "node:path";
|
|
879
|
+
import { existsSync } from "node:fs";
|
|
880
|
+
import { join as join2 } from "node:path";
|
|
1268
881
|
import sharp from "sharp";
|
|
1269
882
|
var imageCache = new Map;
|
|
1270
883
|
var MAX_CACHE_SIZE = 100;
|
|
@@ -1274,7 +887,9 @@ function initImageCache(cacheDir) {
|
|
|
1274
887
|
}
|
|
1275
888
|
function getCacheKey(imagePath, options) {
|
|
1276
889
|
const optionsString = JSON.stringify(options);
|
|
1277
|
-
const
|
|
890
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
891
|
+
hasher.update(`${imagePath}:${optionsString}`);
|
|
892
|
+
const hash2 = hasher.digest("hex");
|
|
1278
893
|
return hash2;
|
|
1279
894
|
}
|
|
1280
895
|
function getContentType(format) {
|
|
@@ -1305,13 +920,13 @@ async function loadFromDiskCache(cacheKey) {
|
|
|
1305
920
|
if (!diskCacheDir)
|
|
1306
921
|
return null;
|
|
1307
922
|
try {
|
|
1308
|
-
const cachePath =
|
|
1309
|
-
const imagePath =
|
|
1310
|
-
if (!
|
|
923
|
+
const cachePath = join2(diskCacheDir, `${cacheKey}.json`);
|
|
924
|
+
const imagePath = join2(diskCacheDir, `${cacheKey}.img`);
|
|
925
|
+
if (!existsSync(cachePath) || !existsSync(imagePath)) {
|
|
1311
926
|
return null;
|
|
1312
927
|
}
|
|
1313
|
-
const metadata2 = JSON.parse(await
|
|
1314
|
-
const buffer = await
|
|
928
|
+
const metadata2 = JSON.parse(await Bun.file(cachePath).text());
|
|
929
|
+
const buffer = Buffer.from(await Bun.file(imagePath).arrayBuffer());
|
|
1315
930
|
return {
|
|
1316
931
|
buffer,
|
|
1317
932
|
contentType: metadata2.contentType,
|
|
@@ -1328,17 +943,16 @@ async function saveToDiskCache(cacheKey, result) {
|
|
|
1328
943
|
if (!diskCacheDir)
|
|
1329
944
|
return;
|
|
1330
945
|
try {
|
|
1331
|
-
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1334
|
-
await writeFile(cachePath, JSON.stringify({
|
|
946
|
+
const cachePath = join2(diskCacheDir, `${cacheKey}.json`);
|
|
947
|
+
const imagePath = join2(diskCacheDir, `${cacheKey}.img`);
|
|
948
|
+
await Bun.write(cachePath, JSON.stringify({
|
|
1335
949
|
contentType: result.contentType,
|
|
1336
950
|
format: result.format,
|
|
1337
951
|
width: result.width,
|
|
1338
952
|
height: result.height,
|
|
1339
953
|
size: result.size
|
|
1340
954
|
}));
|
|
1341
|
-
await
|
|
955
|
+
await Bun.write(imagePath, result.buffer);
|
|
1342
956
|
} catch (error) {
|
|
1343
957
|
console.error("Failed to save image to disk cache:", error);
|
|
1344
958
|
}
|
|
@@ -1369,7 +983,7 @@ async function optimizeImage(imagePath, options, acceptHeader) {
|
|
|
1369
983
|
const arrayBuffer = await response.arrayBuffer();
|
|
1370
984
|
sourceBuffer = Buffer.from(arrayBuffer);
|
|
1371
985
|
} else {
|
|
1372
|
-
sourceBuffer = await
|
|
986
|
+
sourceBuffer = Buffer.from(await Bun.file(imagePath).arrayBuffer());
|
|
1373
987
|
}
|
|
1374
988
|
const format = options.format === "auto" || !options.format ? negotiateFormat(acceptHeader, undefined) : options.format;
|
|
1375
989
|
let transformer = sharp(sourceBuffer);
|
|
@@ -1439,31 +1053,72 @@ function isImageFile(path2) {
|
|
|
1439
1053
|
|
|
1440
1054
|
// packages/elysia/src/routes/artifacts.ts
|
|
1441
1055
|
import { NotFoundError } from "elysia";
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1056
|
+
|
|
1057
|
+
// packages/elysia/src/libs/cache.ts
|
|
1058
|
+
function getEncoding(acceptEncoding) {
|
|
1059
|
+
return /\bbr\b/i.test(acceptEncoding) ? "br" : /gzip/i.test(acceptEncoding) ? "gz" : "raw";
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// packages/elysia/src/routes/artifacts.ts
|
|
1063
|
+
async function serveStaticFile(filePath, headers, fileCache, cacheControl) {
|
|
1064
|
+
const acceptEncoding = headers["accept-encoding"] || "";
|
|
1065
|
+
const encoding = getEncoding(acceptEncoding);
|
|
1066
|
+
const cacheKey = `${filePath}:${encoding}`;
|
|
1067
|
+
const cached = fileCache.get(cacheKey);
|
|
1068
|
+
if (cached)
|
|
1069
|
+
return cached.clone();
|
|
1070
|
+
const exists = await Bun.file(filePath).exists();
|
|
1071
|
+
if (!exists)
|
|
1072
|
+
throw new NotFoundError;
|
|
1073
|
+
const buf = await Bun.file(filePath).arrayBuffer();
|
|
1074
|
+
const content2 = new Uint8Array(buf);
|
|
1075
|
+
const contentType = getMimeType(filePath);
|
|
1076
|
+
const { body, extraHeaders } = gzipIfAccepted(content2, contentType, headers["accept-encoding"]);
|
|
1077
|
+
const response = new Response(body, {
|
|
1078
|
+
headers: {
|
|
1079
|
+
"Content-Type": contentType,
|
|
1080
|
+
"Cache-Control": cacheControl,
|
|
1081
|
+
...extraHeaders
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
fileCache.set(cacheKey, response);
|
|
1085
|
+
return response.clone();
|
|
1086
|
+
}
|
|
1087
|
+
function registerArtifactsRoutes(app, cwd, fileCache, options) {
|
|
1088
|
+
const { bundleMaxAge, directive, isWatchMode = false } = options;
|
|
1089
|
+
app.get("/bundles/*", async ({ params, headers }) => {
|
|
1090
|
+
const fileName = String(params["*"] || "").replace(/^\/+/, "");
|
|
1091
|
+
if (!fileName)
|
|
1446
1092
|
throw new NotFoundError;
|
|
1447
|
-
const
|
|
1093
|
+
const filePath = join(cwd, ".reroute", "bundles", fileName);
|
|
1448
1094
|
try {
|
|
1449
|
-
|
|
1450
|
-
if (
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
return
|
|
1457
|
-
headers: {
|
|
1458
|
-
"Content-Type": contentType,
|
|
1459
|
-
"Cache-Control": "public, max-age=31536000, immutable",
|
|
1460
|
-
...extraHeaders
|
|
1461
|
-
}
|
|
1462
|
-
});
|
|
1095
|
+
let cacheControl;
|
|
1096
|
+
if (isWatchMode) {
|
|
1097
|
+
cacheControl = "no-cache, no-store, must-revalidate";
|
|
1098
|
+
} else {
|
|
1099
|
+
const isContentHashed = /\.([a-z0-9]{8,})\.(js|css)$/i.test(fileName);
|
|
1100
|
+
cacheControl = isContentHashed ? "public, max-age=31536000, immutable" : bundleMaxAge > 0 ? `${directive}, max-age=${bundleMaxAge}` : "no-cache";
|
|
1101
|
+
}
|
|
1102
|
+
return await serveStaticFile(filePath, headers, fileCache, cacheControl);
|
|
1463
1103
|
} catch (e) {
|
|
1464
1104
|
if (e instanceof NotFoundError)
|
|
1465
1105
|
throw e;
|
|
1466
|
-
console.error("[reroute] Failed to serve
|
|
1106
|
+
console.error("[reroute] Failed to serve bundle:", filePath, e);
|
|
1107
|
+
throw new NotFoundError;
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
app.get("/.reroute/collections/*", async ({ params, headers }) => {
|
|
1111
|
+
const fileName = String(params["*"] || "").replace(/^\/+/, "");
|
|
1112
|
+
if (!fileName)
|
|
1113
|
+
throw new NotFoundError;
|
|
1114
|
+
const filePath = join(cwd, ".reroute", "collections", fileName);
|
|
1115
|
+
try {
|
|
1116
|
+
const cacheControl = isWatchMode ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000, immutable";
|
|
1117
|
+
return await serveStaticFile(filePath, headers, fileCache, cacheControl);
|
|
1118
|
+
} catch (e) {
|
|
1119
|
+
if (e instanceof NotFoundError)
|
|
1120
|
+
throw e;
|
|
1121
|
+
console.error("[reroute] Failed to serve collection file:", filePath, e);
|
|
1467
1122
|
throw new NotFoundError;
|
|
1468
1123
|
}
|
|
1469
1124
|
});
|
|
@@ -1472,23 +1127,46 @@ function registerArtifactsRoutes(app, cwd) {
|
|
|
1472
1127
|
|
|
1473
1128
|
// packages/elysia/src/routes/content.ts
|
|
1474
1129
|
import { NotFoundError as NotFoundError2 } from "elysia";
|
|
1475
|
-
function registerContentRoutes(app,
|
|
1476
|
-
app.get("/__content/*", async ({ params
|
|
1130
|
+
function registerContentRoutes(app, cwd, collectionCache, directive, maxAge, isWatchMode) {
|
|
1131
|
+
app.get("/__content/*", async ({ params }) => {
|
|
1477
1132
|
const collectionPath = String(params["*"] || "").trim();
|
|
1478
1133
|
if (!collectionPath)
|
|
1479
1134
|
throw new NotFoundError2;
|
|
1480
|
-
const
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1135
|
+
const cacheKey = `content:${collectionPath}`;
|
|
1136
|
+
const cached = collectionCache.get(cacheKey);
|
|
1137
|
+
if (cached)
|
|
1138
|
+
return cached.clone();
|
|
1139
|
+
try {
|
|
1140
|
+
const collectionFile = join(cwd, ".reroute", "collections", `${collectionPath}.js`);
|
|
1141
|
+
const exists = await Bun.file(collectionFile).exists();
|
|
1142
|
+
if (!exists)
|
|
1143
|
+
throw new NotFoundError2;
|
|
1144
|
+
const url = `${Bun.pathToFileURL(collectionFile).href}${isWatchMode ? `?t=${Date.now()}` : ""}`;
|
|
1145
|
+
const mod = await import(url);
|
|
1146
|
+
const items = mod.items || [];
|
|
1147
|
+
const body = JSON.stringify({ collection: collectionPath, items });
|
|
1148
|
+
const response = new Response(body, {
|
|
1149
|
+
status: 200,
|
|
1150
|
+
headers: {
|
|
1151
|
+
"content-type": "application/json; charset=utf-8",
|
|
1152
|
+
"cache-control": `${directive}, max-age=${maxAge}`
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
collectionCache.set(cacheKey, response);
|
|
1156
|
+
return response.clone();
|
|
1157
|
+
} catch (e) {
|
|
1158
|
+
if (e instanceof NotFoundError2)
|
|
1159
|
+
throw e;
|
|
1160
|
+
console.error(`[reroute] Failed to serve collection ${collectionPath}:`, e);
|
|
1161
|
+
throw new NotFoundError2;
|
|
1162
|
+
}
|
|
1485
1163
|
});
|
|
1486
1164
|
return app;
|
|
1487
1165
|
}
|
|
1488
1166
|
|
|
1489
1167
|
// packages/elysia/src/routes/dev.ts
|
|
1490
1168
|
import { NotFoundError as NotFoundError3 } from "elysia";
|
|
1491
|
-
function registerDevRoutes(app, liveReloadClients,
|
|
1169
|
+
function registerDevRoutes(app, liveReloadClients, isWatchMode, opts) {
|
|
1492
1170
|
app.get("/__reroute_preload", async ({ query }) => {
|
|
1493
1171
|
const rawSrc = String(query.src || "").trim();
|
|
1494
1172
|
const key = String(query.key || "").trim();
|
|
@@ -1507,10 +1185,11 @@ function registerDevRoutes(app, liveReloadClients, isWatchMode2, opts) {
|
|
|
1507
1185
|
` + `(globalThis.__REROUTE_MODULES__ ||= {});
|
|
1508
1186
|
` + `globalThis.__REROUTE_MODULES__['${key}'] = __C;
|
|
1509
1187
|
` + `export {};`;
|
|
1188
|
+
const cacheControl = isWatchMode ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000, immutable";
|
|
1510
1189
|
return new Response(code, {
|
|
1511
1190
|
headers: {
|
|
1512
1191
|
"content-type": "application/javascript; charset=utf-8",
|
|
1513
|
-
"cache-control":
|
|
1192
|
+
"cache-control": cacheControl
|
|
1514
1193
|
}
|
|
1515
1194
|
});
|
|
1516
1195
|
});
|
|
@@ -1524,13 +1203,13 @@ function registerDevRoutes(app, liveReloadClients, isWatchMode2, opts) {
|
|
|
1524
1203
|
pathname = String(p);
|
|
1525
1204
|
}
|
|
1526
1205
|
try {
|
|
1527
|
-
let data2 =
|
|
1206
|
+
let data2 = opts?.dataCache?.get(pathname);
|
|
1528
1207
|
if (data2 === undefined) {
|
|
1529
1208
|
data2 = await computeSSRDataForPath({
|
|
1530
1209
|
pathname,
|
|
1531
1210
|
clientDir: opts?.clientDir || "",
|
|
1532
1211
|
cwd: opts?.cwd || "",
|
|
1533
|
-
isWatchMode
|
|
1212
|
+
isWatchMode
|
|
1534
1213
|
});
|
|
1535
1214
|
opts?.dataCache?.set(pathname, data2);
|
|
1536
1215
|
}
|
|
@@ -1539,7 +1218,7 @@ function registerDevRoutes(app, liveReloadClients, isWatchMode2, opts) {
|
|
|
1539
1218
|
return jsonError("failed", 500);
|
|
1540
1219
|
}
|
|
1541
1220
|
});
|
|
1542
|
-
if (!
|
|
1221
|
+
if (!isWatchMode)
|
|
1543
1222
|
return app;
|
|
1544
1223
|
console.log("[reroute] Registering SSE endpoint at /__reroute_watch");
|
|
1545
1224
|
app.get("/__reroute_watch", () => {
|
|
@@ -1583,7 +1262,7 @@ function registerDevRoutes(app, liveReloadClients, isWatchMode2, opts) {
|
|
|
1583
1262
|
` + `function waitServerThenReload(){
|
|
1584
1263
|
` + ` const ping = () => fetch(location.origin, { cache: 'no-store' })
|
|
1585
1264
|
` + ` .then(()=>location.reload())
|
|
1586
|
-
` + ` .catch(()=>setTimeout(ping,
|
|
1265
|
+
` + ` .catch(()=>setTimeout(ping, 50));
|
|
1587
1266
|
` + ` ping();
|
|
1588
1267
|
` + `}
|
|
1589
1268
|
` + `function connect(){
|
|
@@ -1605,8 +1284,8 @@ export {};`;
|
|
|
1605
1284
|
}
|
|
1606
1285
|
|
|
1607
1286
|
// packages/elysia/src/routes/image.ts
|
|
1608
|
-
import { existsSync as
|
|
1609
|
-
import { join as
|
|
1287
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
1288
|
+
import { join as join3 } from "node:path";
|
|
1610
1289
|
function registerImageRoutes(app, options) {
|
|
1611
1290
|
const {
|
|
1612
1291
|
clientDir,
|
|
@@ -1654,13 +1333,13 @@ function registerImageRoutes(app, options) {
|
|
|
1654
1333
|
imagePath = url;
|
|
1655
1334
|
} else {
|
|
1656
1335
|
const cleanUrl = url.startsWith("/") ? url.slice(1) : url;
|
|
1657
|
-
imagePath =
|
|
1658
|
-
const normalizedPath =
|
|
1336
|
+
imagePath = join3(clientDir, cleanUrl);
|
|
1337
|
+
const normalizedPath = join3(clientDir, cleanUrl);
|
|
1659
1338
|
if (!normalizedPath.startsWith(clientDir)) {
|
|
1660
1339
|
set.status = 403;
|
|
1661
1340
|
return { error: "Access denied" };
|
|
1662
1341
|
}
|
|
1663
|
-
if (!
|
|
1342
|
+
if (!existsSync2(imagePath)) {
|
|
1664
1343
|
set.status = 404;
|
|
1665
1344
|
return { error: "Image not found" };
|
|
1666
1345
|
}
|
|
@@ -1703,7 +1382,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1703
1382
|
rootComponent,
|
|
1704
1383
|
clientDir,
|
|
1705
1384
|
cwd,
|
|
1706
|
-
isWatchMode
|
|
1385
|
+
isWatchMode,
|
|
1707
1386
|
getBundleUrl,
|
|
1708
1387
|
head = "",
|
|
1709
1388
|
lang = "en",
|
|
@@ -1716,12 +1395,18 @@ function registerSSRRoutes(app, options) {
|
|
|
1716
1395
|
dataCache,
|
|
1717
1396
|
shouldIgnorePathname
|
|
1718
1397
|
} = options;
|
|
1719
|
-
const SSR_HTML_TTL_MS =
|
|
1398
|
+
const SSR_HTML_TTL_MS = 5 * 60 * 1000;
|
|
1720
1399
|
const htmlCache = new LRUCache(200);
|
|
1721
1400
|
const makeKey = (pathname, acceptEncoding) => `${pathname}::${acceptEncoding || "identity"}`;
|
|
1722
1401
|
for (const collection of collections) {
|
|
1723
1402
|
app.get(`/${collection}/*`, async ({ request, headers }) => {
|
|
1724
1403
|
const method = request.method || "GET";
|
|
1404
|
+
if (method === "HEAD") {
|
|
1405
|
+
return new Response(null, {
|
|
1406
|
+
status: 200,
|
|
1407
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1725
1410
|
if (method !== "GET")
|
|
1726
1411
|
throw new NotFoundError4;
|
|
1727
1412
|
const accept = request.headers.get?.("accept") || "";
|
|
@@ -1731,7 +1416,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1731
1416
|
const url = new URL(request.url);
|
|
1732
1417
|
const pathname = url.pathname;
|
|
1733
1418
|
const acceptEncoding = headers["accept-encoding"] || "";
|
|
1734
|
-
if (SSR_HTML_TTL_MS > 0) {
|
|
1419
|
+
if (!isWatchMode && SSR_HTML_TTL_MS > 0) {
|
|
1735
1420
|
try {
|
|
1736
1421
|
const ck = makeKey(pathname, acceptEncoding);
|
|
1737
1422
|
const cached = htmlCache.get(ck);
|
|
@@ -1745,23 +1430,19 @@ function registerSSRRoutes(app, options) {
|
|
|
1745
1430
|
}
|
|
1746
1431
|
const bundleUrl = await getBundleUrl();
|
|
1747
1432
|
try {
|
|
1748
|
-
const
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
request?.sendEarlyHints?.(hints);
|
|
1755
|
-
} catch {}
|
|
1756
|
-
headers.link = headers.link ? `${headers.link}, ${linkValue}` : linkValue;
|
|
1757
|
-
}
|
|
1433
|
+
const linkValue = `<${bundleUrl}>; rel=modulepreload; as=script`;
|
|
1434
|
+
try {
|
|
1435
|
+
const hints = new Headers({ Link: linkValue });
|
|
1436
|
+
request?.sendEarlyHints?.(hints);
|
|
1437
|
+
} catch {}
|
|
1438
|
+
headers.link = headers.link ? `${headers.link}, ${linkValue}` : linkValue;
|
|
1758
1439
|
} catch {}
|
|
1759
1440
|
const result = await renderSSRDocument({
|
|
1760
1441
|
pathname,
|
|
1761
1442
|
rootComponent,
|
|
1762
1443
|
clientDir,
|
|
1763
1444
|
cwd,
|
|
1764
|
-
isWatchMode
|
|
1445
|
+
isWatchMode,
|
|
1765
1446
|
bundleUrl,
|
|
1766
1447
|
head,
|
|
1767
1448
|
lang,
|
|
@@ -1769,9 +1450,10 @@ function registerSSRRoutes(app, options) {
|
|
|
1769
1450
|
minify
|
|
1770
1451
|
});
|
|
1771
1452
|
const { body, extraHeaders } = gzipIfAccepted(result.html, "text/html; charset=utf-8", acceptEncoding);
|
|
1453
|
+
const cacheControlHeader = isWatchMode ? "no-cache, no-store, must-revalidate" : `${directive}, max-age=${maxAge}`;
|
|
1772
1454
|
const resHeaders = {
|
|
1773
1455
|
"content-type": "text/html; charset=utf-8",
|
|
1774
|
-
"cache-control":
|
|
1456
|
+
"cache-control": cacheControlHeader,
|
|
1775
1457
|
...headers.link ? { link: headers.link } : {},
|
|
1776
1458
|
...extraHeaders
|
|
1777
1459
|
};
|
|
@@ -1779,7 +1461,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1779
1461
|
status: result.status,
|
|
1780
1462
|
headers: resHeaders
|
|
1781
1463
|
});
|
|
1782
|
-
if (SSR_HTML_TTL_MS > 0 && result.status === 200) {
|
|
1464
|
+
if (!isWatchMode && SSR_HTML_TTL_MS > 0 && result.status === 200) {
|
|
1783
1465
|
try {
|
|
1784
1466
|
const ck = makeKey(pathname, acceptEncoding);
|
|
1785
1467
|
htmlCache.set(ck, {
|
|
@@ -1813,13 +1495,13 @@ function registerSSRRoutes(app, options) {
|
|
|
1813
1495
|
if (pathname === "/__reroute_data" && method === "GET") {
|
|
1814
1496
|
try {
|
|
1815
1497
|
const p = url.searchParams.get("path") || "/";
|
|
1816
|
-
let data2 =
|
|
1498
|
+
let data2 = dataCache?.get(p);
|
|
1817
1499
|
if (data2 === undefined) {
|
|
1818
1500
|
data2 = await computeSSRDataForPath({
|
|
1819
1501
|
pathname: p,
|
|
1820
1502
|
clientDir,
|
|
1821
1503
|
cwd,
|
|
1822
|
-
isWatchMode
|
|
1504
|
+
isWatchMode
|
|
1823
1505
|
});
|
|
1824
1506
|
dataCache?.set(p, data2);
|
|
1825
1507
|
}
|
|
@@ -1828,13 +1510,15 @@ function registerSSRRoutes(app, options) {
|
|
|
1828
1510
|
return jsonError("failed", 500);
|
|
1829
1511
|
}
|
|
1830
1512
|
}
|
|
1831
|
-
if (method === "GET" && !isInternal) {
|
|
1513
|
+
if ((method === "GET" || method === "HEAD") && !isInternal) {
|
|
1514
|
+
if (method === "HEAD") {
|
|
1515
|
+
set.status = 200;
|
|
1516
|
+
set.headers["content-type"] = "text/html; charset=utf-8";
|
|
1517
|
+
return new Response(null);
|
|
1518
|
+
}
|
|
1832
1519
|
try {
|
|
1833
|
-
if (isWatchMode2) {
|
|
1834
|
-
await generateContentRegistry(cwd);
|
|
1835
|
-
}
|
|
1836
1520
|
const acceptEncoding = request.headers.get("accept-encoding") || "";
|
|
1837
|
-
if (SSR_HTML_TTL_MS > 0) {
|
|
1521
|
+
if (!isWatchMode && SSR_HTML_TTL_MS > 0) {
|
|
1838
1522
|
try {
|
|
1839
1523
|
const ck = makeKey(pathname, acceptEncoding);
|
|
1840
1524
|
const cached = htmlCache.get(ck);
|
|
@@ -1847,23 +1531,19 @@ function registerSSRRoutes(app, options) {
|
|
|
1847
1531
|
}
|
|
1848
1532
|
const bundleUrl = await getBundleUrl();
|
|
1849
1533
|
try {
|
|
1850
|
-
const
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
request?.sendEarlyHints?.(hints);
|
|
1857
|
-
} catch {}
|
|
1858
|
-
set.headers.link = set.headers.link ? `${set.headers.link}, ${linkValue}` : linkValue;
|
|
1859
|
-
}
|
|
1534
|
+
const linkValue = `<${bundleUrl}>; rel=modulepreload; as=script`;
|
|
1535
|
+
try {
|
|
1536
|
+
const hints = new Headers({ Link: linkValue });
|
|
1537
|
+
request?.sendEarlyHints?.(hints);
|
|
1538
|
+
} catch {}
|
|
1539
|
+
set.headers.link = set.headers.link ? `${set.headers.link}, ${linkValue}` : linkValue;
|
|
1860
1540
|
} catch {}
|
|
1861
1541
|
const result = await renderSSRDocument({
|
|
1862
1542
|
pathname,
|
|
1863
1543
|
rootComponent,
|
|
1864
1544
|
clientDir,
|
|
1865
1545
|
cwd,
|
|
1866
|
-
isWatchMode
|
|
1546
|
+
isWatchMode,
|
|
1867
1547
|
bundleUrl,
|
|
1868
1548
|
head,
|
|
1869
1549
|
lang,
|
|
@@ -1873,7 +1553,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1873
1553
|
const { body, extraHeaders } = gzipIfAccepted(result.html, "text/html; charset=utf-8", acceptEncoding);
|
|
1874
1554
|
set.status = result.status;
|
|
1875
1555
|
set.headers["content-type"] = "text/html; charset=utf-8";
|
|
1876
|
-
set.headers["cache-control"] =
|
|
1556
|
+
set.headers["cache-control"] = isWatchMode ? "no-cache, no-store, must-revalidate" : `${directive}, max-age=${maxAge}`;
|
|
1877
1557
|
if (extraHeaders["Content-Encoding"]) {
|
|
1878
1558
|
set.headers["content-encoding"] = extraHeaders["Content-Encoding"];
|
|
1879
1559
|
}
|
|
@@ -1881,7 +1561,7 @@ function registerSSRRoutes(app, options) {
|
|
|
1881
1561
|
set.headers.vary = extraHeaders.Vary;
|
|
1882
1562
|
}
|
|
1883
1563
|
const response = new Response(body);
|
|
1884
|
-
if (SSR_HTML_TTL_MS > 0 && result.status === 200) {
|
|
1564
|
+
if (!isWatchMode && SSR_HTML_TTL_MS > 0 && result.status === 200) {
|
|
1885
1565
|
try {
|
|
1886
1566
|
const ck = makeKey(pathname, acceptEncoding);
|
|
1887
1567
|
htmlCache.set(ck, {
|
|
@@ -1910,220 +1590,52 @@ function registerStaticRoutes(app, options) {
|
|
|
1910
1590
|
directive,
|
|
1911
1591
|
indexHTML,
|
|
1912
1592
|
staticHeaders,
|
|
1913
|
-
minify,
|
|
1914
|
-
sourcemap,
|
|
1915
1593
|
shouldIgnore,
|
|
1916
|
-
bundleCache,
|
|
1917
1594
|
fileCache,
|
|
1918
|
-
isWatchMode
|
|
1595
|
+
isWatchMode = false
|
|
1919
1596
|
} = options;
|
|
1920
1597
|
app.get(`${prefix}/*`, async ({ params, headers }) => {
|
|
1921
1598
|
const requestPath = params["*"];
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1599
|
+
const filePath = join(clientDir, requestPath);
|
|
1600
|
+
try {
|
|
1924
1601
|
const acceptEncoding = headers["accept-encoding"] || "";
|
|
1925
|
-
const
|
|
1602
|
+
const encoding = getEncoding(acceptEncoding);
|
|
1603
|
+
const cacheKey = `${filePath}:${encoding}`;
|
|
1926
1604
|
const cached = fileCache.get(cacheKey);
|
|
1927
|
-
if (cached)
|
|
1605
|
+
if (cached)
|
|
1928
1606
|
return cached.clone();
|
|
1929
|
-
}
|
|
1930
|
-
const [, filename, hash2] = hashedJsMatch;
|
|
1931
|
-
const tsxPath = join2(clientDir, `${filename}.tsx`);
|
|
1932
|
-
const tsPath = join2(clientDir, `${filename}.ts`);
|
|
1933
|
-
let filePath2 = null;
|
|
1934
|
-
if (await Bun.file(tsxPath).exists()) {
|
|
1935
|
-
filePath2 = tsxPath;
|
|
1936
|
-
} else if (await Bun.file(tsPath).exists()) {
|
|
1937
|
-
filePath2 = tsPath;
|
|
1938
|
-
}
|
|
1939
|
-
if (filePath2) {
|
|
1940
|
-
try {
|
|
1941
|
-
const bundleInfo = await transpileFile(filePath2, `${filename}.tsx`, { minify, sourcemap }, bundleCache);
|
|
1942
|
-
if (bundleInfo.hash === hash2) {
|
|
1943
|
-
const contentType = "application/javascript; charset=utf-8";
|
|
1944
|
-
const { body, extraHeaders } = gzipIfAccepted(bundleInfo.code, contentType, acceptEncoding);
|
|
1945
|
-
try {
|
|
1946
|
-
if (Array.isArray(bundleInfo.chunks) && bundleInfo.chunks.length) {
|
|
1947
|
-
for (const chunk of bundleInfo.chunks) {
|
|
1948
|
-
const chunkPath = `${prefix}/${chunk.fileName}`.replace(/\/+/g, "/");
|
|
1949
|
-
const identityResp = new Response(chunk.code, {
|
|
1950
|
-
headers: {
|
|
1951
|
-
"Content-Type": contentType,
|
|
1952
|
-
"Cache-Control": `${directive}, max-age=${365 * 24 * 60 * 60}, immutable`,
|
|
1953
|
-
...staticHeaders
|
|
1954
|
-
}
|
|
1955
|
-
});
|
|
1956
|
-
fileCache.set(chunkPath, identityResp);
|
|
1957
|
-
const enc = headers["accept-encoding"] || "";
|
|
1958
|
-
const comp = gzipIfAccepted(chunk.code, contentType, enc);
|
|
1959
|
-
const compressedResp = new Response(comp.body, {
|
|
1960
|
-
headers: {
|
|
1961
|
-
"Content-Type": contentType,
|
|
1962
|
-
"Cache-Control": `${directive}, max-age=${365 * 24 * 60 * 60}, immutable`,
|
|
1963
|
-
...comp.extraHeaders,
|
|
1964
|
-
...staticHeaders
|
|
1965
|
-
}
|
|
1966
|
-
});
|
|
1967
|
-
fileCache.set(`${chunkPath}:${enc}`, compressedResp);
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
} catch {}
|
|
1971
|
-
const response = new Response(body, {
|
|
1972
|
-
headers: {
|
|
1973
|
-
"Content-Type": contentType,
|
|
1974
|
-
"Cache-Control": `${directive}, max-age=${365 * 24 * 60 * 60}, immutable`,
|
|
1975
|
-
...extraHeaders,
|
|
1976
|
-
...staticHeaders
|
|
1977
|
-
}
|
|
1978
|
-
});
|
|
1979
|
-
fileCache.set(cacheKey, response.clone());
|
|
1980
|
-
return response;
|
|
1981
|
-
}
|
|
1982
|
-
} catch (error) {
|
|
1983
|
-
console.error(`[reroute] Error serving bundle:`, error);
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
const sourceMapMatch = requestPath.match(/^(.+)\.([a-f0-9]{8})\.js\.map$/);
|
|
1988
|
-
if (sourceMapMatch) {
|
|
1989
|
-
const [, filename, hash2] = sourceMapMatch;
|
|
1990
|
-
const tsxPath = join2(clientDir, `${filename}.tsx`);
|
|
1991
|
-
const tsPath = join2(clientDir, `${filename}.ts`);
|
|
1992
|
-
let filePath2 = null;
|
|
1993
|
-
if (await Bun.file(tsxPath).exists()) {
|
|
1994
|
-
filePath2 = tsxPath;
|
|
1995
|
-
} else if (await Bun.file(tsPath).exists()) {
|
|
1996
|
-
filePath2 = tsPath;
|
|
1997
|
-
}
|
|
1998
|
-
if (filePath2) {
|
|
1999
|
-
try {
|
|
2000
|
-
const bundleInfo = await transpileFile(filePath2, `${filename}.tsx`, { minify, sourcemap }, bundleCache);
|
|
2001
|
-
if (bundleInfo.hash === hash2 && bundleInfo.sourceMap) {
|
|
2002
|
-
const contentType = "application/json; charset=utf-8";
|
|
2003
|
-
const { body, extraHeaders } = gzipIfAccepted(bundleInfo.sourceMap, contentType, headers["accept-encoding"]);
|
|
2004
|
-
return new Response(body, {
|
|
2005
|
-
headers: {
|
|
2006
|
-
"Content-Type": contentType,
|
|
2007
|
-
"Cache-Control": `${directive}, max-age=${365 * 24 * 60 * 60}, immutable`,
|
|
2008
|
-
...extraHeaders,
|
|
2009
|
-
...staticHeaders
|
|
2010
|
-
}
|
|
2011
|
-
});
|
|
2012
|
-
}
|
|
2013
|
-
} catch (error) {
|
|
2014
|
-
console.error(`[reroute] Error serving sourcemap:`, error);
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
if (requestPath.endsWith(".js")) {
|
|
2019
|
-
const acceptEncoding = headers["accept-encoding"] || "";
|
|
2020
|
-
const pathWithEnc = `${prefix}/${requestPath}`.replace(/\/+/g, "/");
|
|
2021
|
-
const cachedVariant = fileCache.get(`${pathWithEnc}:${acceptEncoding}`) || fileCache.get(pathWithEnc);
|
|
2022
|
-
if (cachedVariant) {
|
|
2023
|
-
return cachedVariant.clone();
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
if (requestPath.endsWith(".tsx") || requestPath.endsWith(".ts")) {
|
|
2027
|
-
try {
|
|
2028
|
-
const m = requestPath.match(/^routes\/([^/]+)\/content\/([^/]+)\.(tsx|ts)$/);
|
|
2029
|
-
if (m) {
|
|
2030
|
-
const collection = m[1];
|
|
2031
|
-
const name = m[2];
|
|
2032
|
-
try {
|
|
2033
|
-
const cwd = process.cwd();
|
|
2034
|
-
const regPath = join2(cwd, ".reroute", "content.ts");
|
|
2035
|
-
const reg = await import((await import("node:url")).pathToFileURL(regPath).href + `?t=${Date.now()}`);
|
|
2036
|
-
const get = reg?.getContentEntry;
|
|
2037
|
-
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
2038
|
-
const mod = entry?.module;
|
|
2039
|
-
if (mod?.endsWith(".js")) {
|
|
2040
|
-
return new Response(null, {
|
|
2041
|
-
status: 302,
|
|
2042
|
-
headers: { Location: mod }
|
|
2043
|
-
});
|
|
2044
|
-
}
|
|
2045
|
-
} catch {}
|
|
2046
|
-
try {
|
|
2047
|
-
const chunkDir = join2(process.cwd(), ".reroute", "chunks", collection);
|
|
2048
|
-
const files = await (await import("node:fs/promises")).readdir(chunkDir);
|
|
2049
|
-
const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
2050
|
-
if (candidates.length) {
|
|
2051
|
-
let latest = candidates[0];
|
|
2052
|
-
let latestM = 0;
|
|
2053
|
-
const { stat: stat5 } = await import("node:fs/promises");
|
|
2054
|
-
for (const f of candidates) {
|
|
2055
|
-
try {
|
|
2056
|
-
const s = await stat5(join2(chunkDir, f));
|
|
2057
|
-
if (s.mtimeMs >= latestM) {
|
|
2058
|
-
latestM = s.mtimeMs;
|
|
2059
|
-
latest = f;
|
|
2060
|
-
}
|
|
2061
|
-
} catch {}
|
|
2062
|
-
}
|
|
2063
|
-
const loc = `/${join2(".reroute", "chunks", collection, latest).replace(/\\+/g, "/")}`;
|
|
2064
|
-
return new Response(null, {
|
|
2065
|
-
status: 302,
|
|
2066
|
-
headers: { Location: loc }
|
|
2067
|
-
});
|
|
2068
|
-
}
|
|
2069
|
-
} catch {}
|
|
2070
|
-
}
|
|
2071
|
-
} catch {}
|
|
2072
|
-
const filePath2 = join2(clientDir, requestPath);
|
|
2073
|
-
if (shouldIgnore(filePath2))
|
|
2074
|
-
throw new NotFoundError5;
|
|
2075
|
-
try {
|
|
2076
|
-
const exists = await Bun.file(filePath2).exists();
|
|
2077
|
-
if (!exists)
|
|
2078
|
-
throw new NotFoundError5;
|
|
2079
|
-
const bundleInfo = await transpileFile(filePath2, requestPath, { minify, sourcemap }, bundleCache);
|
|
2080
|
-
const contentType = "application/javascript; charset=utf-8";
|
|
2081
|
-
const { body, extraHeaders } = gzipIfAccepted(bundleInfo.code, contentType, headers["accept-encoding"]);
|
|
2082
|
-
return new Response(body, {
|
|
2083
|
-
headers: {
|
|
2084
|
-
"Content-Type": contentType,
|
|
2085
|
-
"Cache-Control": isWatchMode2 ? "no-cache" : `${directive}, max-age=${maxAge}`,
|
|
2086
|
-
...extraHeaders,
|
|
2087
|
-
...staticHeaders
|
|
2088
|
-
}
|
|
2089
|
-
});
|
|
2090
|
-
} catch (error) {
|
|
2091
|
-
if (error instanceof NotFoundError5)
|
|
2092
|
-
throw error;
|
|
2093
|
-
console.error(`[reroute] Error:`, error);
|
|
2094
|
-
throw new NotFoundError5;
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
const filePath = join2(clientDir, requestPath);
|
|
2098
|
-
try {
|
|
2099
1607
|
let actualFilePath = filePath;
|
|
2100
1608
|
let exists = await Bun.file(actualFilePath).exists();
|
|
2101
1609
|
if (!exists && indexHTML) {
|
|
2102
|
-
actualFilePath =
|
|
1610
|
+
actualFilePath = join(filePath, "index.html");
|
|
2103
1611
|
exists = await Bun.file(actualFilePath).exists();
|
|
2104
1612
|
}
|
|
2105
1613
|
if (!exists)
|
|
2106
1614
|
throw new NotFoundError5;
|
|
2107
1615
|
if (shouldIgnore(actualFilePath))
|
|
2108
1616
|
throw new NotFoundError5;
|
|
2109
|
-
const cached = fileCache.get(filePath);
|
|
2110
|
-
if (cached)
|
|
2111
|
-
return cached.clone();
|
|
2112
1617
|
const contentBuf = await Bun.file(actualFilePath).arrayBuffer();
|
|
2113
1618
|
const content2 = new Uint8Array(contentBuf);
|
|
2114
1619
|
const contentType = getMimeType(actualFilePath);
|
|
2115
1620
|
const { body, extraHeaders } = gzipIfAccepted(content2, contentType, headers["accept-encoding"]);
|
|
2116
|
-
|
|
2117
|
-
|
|
1621
|
+
let cacheControl;
|
|
1622
|
+
if (isWatchMode) {
|
|
1623
|
+
const isSourceFile = /\.(tsx?|jsx?|css)$/i.test(actualFilePath);
|
|
1624
|
+
cacheControl = isSourceFile ? "no-cache, no-store, must-revalidate" : `${directive}, max-age=${maxAge}`;
|
|
1625
|
+
} else {
|
|
1626
|
+
const isLongLivedAsset = contentType.startsWith("image/") || contentType.startsWith("font/") || contentType.includes("woff") || contentType.includes("ttf") || contentType.includes("otf");
|
|
1627
|
+
const cacheMaxAge = maxAge === 0 ? 0 : isLongLivedAsset ? 365 * 24 * 60 * 60 : maxAge;
|
|
1628
|
+
cacheControl = cacheMaxAge > 0 ? `${directive}, max-age=${cacheMaxAge}${isLongLivedAsset ? ", immutable" : ""}` : "no-cache";
|
|
1629
|
+
}
|
|
2118
1630
|
const response = new Response(body, {
|
|
2119
1631
|
headers: {
|
|
2120
1632
|
"Content-Type": contentType,
|
|
2121
|
-
"Cache-Control":
|
|
1633
|
+
"Cache-Control": cacheControl,
|
|
2122
1634
|
...extraHeaders,
|
|
2123
1635
|
...staticHeaders
|
|
2124
1636
|
}
|
|
2125
1637
|
});
|
|
2126
|
-
fileCache.set(
|
|
1638
|
+
fileCache.set(cacheKey, response);
|
|
2127
1639
|
return response.clone();
|
|
2128
1640
|
} catch (error) {
|
|
2129
1641
|
if (error instanceof NotFoundError5)
|
|
@@ -2140,7 +1652,6 @@ var reroute = (options = {}) => async (app) => {
|
|
|
2140
1652
|
const ssrHead = options.head || "";
|
|
2141
1653
|
const ssrLang = options.lang || "en";
|
|
2142
1654
|
const ssrAppId = options.appId || "root";
|
|
2143
|
-
const entrypoint = options.entrypoint || "index.tsx";
|
|
2144
1655
|
const rootComponent = options.app;
|
|
2145
1656
|
const assets = options.assets || "src/client";
|
|
2146
1657
|
let prefix = options.prefix || "/";
|
|
@@ -2157,16 +1668,16 @@ var reroute = (options = {}) => async (app) => {
|
|
|
2157
1668
|
const directive = options.directive || "public";
|
|
2158
1669
|
const indexHTML = options.indexHTML ?? true;
|
|
2159
1670
|
const minify = options.minify ?? true;
|
|
2160
|
-
const sourcemap = options.sourcemap ?? true;
|
|
2161
1671
|
const imageMaxAge = options.imageMaxAge ?? 31536000;
|
|
2162
1672
|
const imageAllowedDomains = options.imageAllowedDomains || [];
|
|
2163
1673
|
const imageMaxWidth = options.imageMaxWidth ?? 3840;
|
|
2164
1674
|
const imageMaxHeight = options.imageMaxHeight ?? 2160;
|
|
1675
|
+
const bundleMaxAge = options.bundleMaxAge ?? 3600;
|
|
2165
1676
|
if (prefix === "/")
|
|
2166
1677
|
prefix = "";
|
|
2167
1678
|
const fileCache = new LRUCache(100);
|
|
2168
1679
|
const dataCache = new LRUCache(100);
|
|
2169
|
-
const
|
|
1680
|
+
const collectionCache = new LRUCache(50);
|
|
2170
1681
|
const cwd = typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : "/";
|
|
2171
1682
|
const liveReloadClients = new Set;
|
|
2172
1683
|
const notifyReload = () => {
|
|
@@ -2184,20 +1695,41 @@ var reroute = (options = {}) => async (app) => {
|
|
|
2184
1695
|
};
|
|
2185
1696
|
const clientDir = assets.startsWith("/") ? assets : `${cwd.replace(/\/$/, "")}/${assets.replace(/^\.\//, "")}`;
|
|
2186
1697
|
const shouldIgnore = !ignorePatterns.length ? () => false : (file) => ignorePatterns.find((pattern) => typeof pattern === "string" ? file.includes(pattern) : pattern.test(file));
|
|
2187
|
-
const
|
|
1698
|
+
const isWatchMode = Array.isArray(process.execArgv) && process.execArgv.includes("--watch") || Array.isArray(process.argv) && process.argv.includes("--watch");
|
|
2188
1699
|
const dataCacheControl = `${directive}, max-age=${maxAge}`;
|
|
2189
|
-
if (
|
|
1700
|
+
if (isWatchMode)
|
|
2190
1701
|
console.log(`[reroute] Live reload enabled`);
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
1702
|
+
const readBundleUrl = async () => {
|
|
1703
|
+
const bundlesDir = `${cwd}/.reroute/bundles`;
|
|
1704
|
+
try {
|
|
1705
|
+
const glob = new Bun.Glob("index.*.js");
|
|
1706
|
+
const files = await Array.fromAsync(glob.scan(bundlesDir));
|
|
1707
|
+
const indexFile = files.find((f) => !f.includes(".stub.")) || files[0];
|
|
1708
|
+
if (indexFile) {
|
|
1709
|
+
return `/bundles/${indexFile}`;
|
|
1710
|
+
} else {
|
|
1711
|
+
throw new Error("Bundle not found. Run `reroute gen` first.");
|
|
1712
|
+
}
|
|
1713
|
+
} catch {}
|
|
1714
|
+
throw new Error("Bundle not found. Run `reroute gen` first.");
|
|
2198
1715
|
};
|
|
2199
|
-
|
|
2200
|
-
|
|
1716
|
+
const initialBundleUrl = await readBundleUrl();
|
|
1717
|
+
console.log(`[reroute] Using pre-built bundle: ${initialBundleUrl}`);
|
|
1718
|
+
app.onError(({ request, set, error }) => {
|
|
1719
|
+
if ("status" in error && error.status === 404) {
|
|
1720
|
+
set.status = 404;
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
if (request.method === "HEAD") {
|
|
1724
|
+
console.log("[reroute] HEAD request received");
|
|
1725
|
+
set.status = 200;
|
|
1726
|
+
set.headers["Content-Type"] = "text/plain";
|
|
1727
|
+
return "";
|
|
1728
|
+
}
|
|
1729
|
+
console.error("[reroute] \uD83D\uDEA8 Server error:", error);
|
|
1730
|
+
});
|
|
1731
|
+
registerContentRoutes(app, cwd, collectionCache, directive, maxAge, isWatchMode);
|
|
1732
|
+
registerDevRoutes(app, liveReloadClients, isWatchMode, {
|
|
2201
1733
|
clientDir,
|
|
2202
1734
|
cwd,
|
|
2203
1735
|
dataCacheControl,
|
|
@@ -2213,13 +1745,13 @@ var reroute = (options = {}) => async (app) => {
|
|
|
2213
1745
|
pathname = String(p);
|
|
2214
1746
|
}
|
|
2215
1747
|
try {
|
|
2216
|
-
let data2 =
|
|
1748
|
+
let data2 = dataCache.get(pathname);
|
|
2217
1749
|
if (data2 === undefined) {
|
|
2218
1750
|
data2 = await computeSSRDataForPath({
|
|
2219
1751
|
pathname,
|
|
2220
1752
|
clientDir,
|
|
2221
1753
|
cwd,
|
|
2222
|
-
isWatchMode
|
|
1754
|
+
isWatchMode
|
|
2223
1755
|
});
|
|
2224
1756
|
dataCache.set(pathname, data2);
|
|
2225
1757
|
}
|
|
@@ -2228,23 +1760,21 @@ var reroute = (options = {}) => async (app) => {
|
|
|
2228
1760
|
return jsonError("failed", 500);
|
|
2229
1761
|
}
|
|
2230
1762
|
});
|
|
2231
|
-
if (
|
|
1763
|
+
if (isWatchMode) {
|
|
2232
1764
|
app.post("/__reroute_reload", () => {
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
} catch {
|
|
2240
|
-
return new Response(JSON.stringify({ ok: false }), {
|
|
2241
|
-
status: 500,
|
|
2242
|
-
headers: { "content-type": "application/json" }
|
|
2243
|
-
});
|
|
2244
|
-
}
|
|
1765
|
+
fileCache.clear();
|
|
1766
|
+
dataCache.clear();
|
|
1767
|
+
notifyReload();
|
|
1768
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
1769
|
+
headers: { "content-type": "application/json" }
|
|
1770
|
+
});
|
|
2245
1771
|
});
|
|
2246
1772
|
}
|
|
2247
|
-
registerArtifactsRoutes(app, cwd
|
|
1773
|
+
registerArtifactsRoutes(app, cwd, fileCache, {
|
|
1774
|
+
bundleMaxAge,
|
|
1775
|
+
directive,
|
|
1776
|
+
isWatchMode
|
|
1777
|
+
});
|
|
2248
1778
|
const imageCacheDir = `${cwd}/.reroute/image-cache`;
|
|
2249
1779
|
initImageCache(imageCacheDir);
|
|
2250
1780
|
registerImageRoutes(app, {
|
|
@@ -2264,8 +1794,8 @@ var reroute = (options = {}) => async (app) => {
|
|
|
2264
1794
|
rootComponent,
|
|
2265
1795
|
clientDir,
|
|
2266
1796
|
cwd,
|
|
2267
|
-
isWatchMode
|
|
2268
|
-
getBundleUrl:
|
|
1797
|
+
isWatchMode,
|
|
1798
|
+
getBundleUrl: readBundleUrl,
|
|
2269
1799
|
head: ssrHead,
|
|
2270
1800
|
lang: ssrLang,
|
|
2271
1801
|
appId: ssrAppId,
|
|
@@ -2293,12 +1823,9 @@ var reroute = (options = {}) => async (app) => {
|
|
|
2293
1823
|
directive,
|
|
2294
1824
|
indexHTML,
|
|
2295
1825
|
staticHeaders,
|
|
2296
|
-
minify,
|
|
2297
|
-
sourcemap,
|
|
2298
1826
|
shouldIgnore,
|
|
2299
|
-
bundleCache,
|
|
2300
1827
|
fileCache,
|
|
2301
|
-
isWatchMode
|
|
1828
|
+
isWatchMode
|
|
2302
1829
|
});
|
|
2303
1830
|
return app;
|
|
2304
1831
|
};
|
|
@@ -2306,4 +1833,4 @@ export {
|
|
|
2306
1833
|
reroute
|
|
2307
1834
|
};
|
|
2308
1835
|
|
|
2309
|
-
//# debugId=
|
|
1836
|
+
//# debugId=AD268A47B9D4009364756E2164756E21
|