reroute-js 0.9.4 → 0.10.0
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/src/index.ts +1 -8
- package/_/blog/src/client/index.html +0 -1
- package/_/blog/src/client/routes/blog/[layout].tsx +53 -3
- package/_/blog/src/client/routes/docs.tsx +1 -1
- package/_/blog/src/index.ts +1 -8
- package/_/store/src/index.ts +1 -8
- package/cli/bin.d.ts +1 -1
- package/cli/bin.js +3666 -2682
- package/cli/bin.js.map +40 -29
- package/cli/index.d.ts +2 -2
- package/cli/index.d.ts.map +1 -1
- package/cli/index.js +74 -10
- package/cli/index.js.map +7 -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 +1 -1
- package/cli/src/commands/dev.d.ts +1 -1
- package/cli/src/commands/gen.d.ts +1 -1
- package/cli/src/commands/gen.d.ts.map +1 -1
- package/{core/src/template → cli/src/commands}/index.d.ts +2 -2
- package/cli/src/commands/index.d.ts.map +1 -0
- package/cli/src/commands/init.d.ts +1 -1
- package/cli/src/{libs → commands/lib}/command.d.ts +1 -1
- package/cli/src/commands/lib/command.d.ts.map +1 -0
- package/cli/src/{libs → commands/lib}/index.d.ts +3 -2
- package/cli/src/commands/lib/index.d.ts.map +1 -0
- package/cli/src/{libs → commands/lib}/log.d.ts +1 -1
- package/cli/src/commands/lib/log.d.ts.map +1 -0
- package/cli/src/{libs/markdown.d.ts → commands/lib/markdown/availability.d.ts} +2 -2
- package/cli/src/commands/lib/markdown/availability.d.ts.map +1 -0
- package/cli/src/commands/lib/markdown/index.d.ts +12 -0
- package/cli/src/commands/lib/markdown/index.d.ts.map +1 -0
- package/cli/src/{libs/markdown-processor.d.ts → commands/lib/markdown/processor.d.ts} +2 -2
- package/cli/src/commands/lib/markdown/processor.d.ts.map +1 -0
- package/cli/src/{libs → commands/lib}/production.d.ts +1 -1
- package/cli/src/commands/lib/production.d.ts.map +1 -0
- package/cli/src/{libs → commands/lib}/server.d.ts +1 -1
- package/cli/src/commands/lib/server.d.ts.map +1 -0
- package/cli/src/commands/lib/streaming/analyzer.d.ts +26 -0
- package/cli/src/commands/lib/streaming/analyzer.d.ts.map +1 -0
- package/cli/src/commands/lib/streaming/suspense.d.ts +20 -0
- package/cli/src/commands/lib/streaming/suspense.d.ts.map +1 -0
- package/cli/src/{libs → commands/lib}/tailwind.d.ts +1 -1
- package/cli/src/commands/lib/tailwind.d.ts.map +1 -0
- package/cli/src/{libs → commands/lib}/version.d.ts +1 -1
- package/cli/src/commands/lib/version.d.ts.map +1 -0
- package/cli/src/commands/start.d.ts +2 -2
- package/cli/src/commands/start.d.ts.map +1 -1
- package/cli/src/index.d.ts +11 -0
- package/cli/src/index.d.ts.map +1 -0
- package/core/index.d.ts +1 -1
- package/core/index.js +1169 -471
- package/core/index.js.map +22 -13
- package/core/src/bundler/hash.d.ts +1 -1
- package/core/src/bundler/index.d.ts +1 -1
- package/core/src/config.d.ts +42 -0
- package/core/src/config.d.ts.map +1 -0
- package/core/src/content/discovery.d.ts +1 -1
- package/core/src/content/index.d.ts +1 -1
- package/core/src/content/metadata.d.ts +2 -2
- package/core/src/content/metadata.d.ts.map +1 -1
- package/core/src/index.d.ts +2 -4
- package/core/src/index.d.ts.map +1 -1
- package/core/src/ssr/index.d.ts +3 -3
- package/core/src/ssr/index.d.ts.map +1 -1
- package/core/src/ssr/lib/cache.d.ts +67 -0
- package/core/src/ssr/lib/cache.d.ts.map +1 -0
- package/core/src/ssr/lib/collections.d.ts +18 -0
- package/core/src/ssr/lib/collections.d.ts.map +1 -0
- package/core/src/{utils → ssr/lib}/compression.d.ts +9 -2
- package/core/src/ssr/lib/compression.d.ts.map +1 -0
- package/core/src/ssr/lib/compute.d.ts +28 -0
- package/core/src/ssr/lib/compute.d.ts.map +1 -0
- package/core/src/ssr/{data.d.ts → lib/data.d.ts} +1 -1
- package/core/src/ssr/lib/data.d.ts.map +1 -0
- package/core/src/{template → ssr/lib}/html.d.ts +2 -2
- package/core/src/ssr/lib/html.d.ts.map +1 -0
- package/core/src/ssr/lib/index.d.ts +26 -0
- package/core/src/ssr/lib/index.d.ts.map +1 -0
- package/core/src/ssr/lib/metadata.d.ts +20 -0
- package/core/src/ssr/lib/metadata.d.ts.map +1 -0
- package/core/src/{utils → ssr/lib}/mime.d.ts +1 -1
- package/core/src/ssr/lib/mime.d.ts.map +1 -0
- package/core/src/ssr/{modules.d.ts → lib/modules.d.ts} +2 -2
- package/core/src/ssr/lib/modules.d.ts.map +1 -0
- package/core/src/{utils → ssr/lib}/path.d.ts +1 -1
- package/core/src/ssr/lib/path.d.ts.map +1 -0
- package/core/src/ssr/lib/preload.d.ts +29 -0
- package/core/src/ssr/lib/preload.d.ts.map +1 -0
- package/core/src/ssr/lib/scripts.d.ts +31 -0
- package/core/src/ssr/lib/scripts.d.ts.map +1 -0
- package/core/src/ssr/{seed.d.ts → lib/seed.d.ts} +1 -1
- package/core/src/ssr/lib/seed.d.ts.map +1 -0
- package/core/src/ssr/lib/styles.d.ts +14 -0
- package/core/src/ssr/lib/styles.d.ts.map +1 -0
- package/core/src/ssr/lib/template.d.ts +19 -0
- package/core/src/ssr/lib/template.d.ts.map +1 -0
- package/core/src/{types.d.ts → ssr/lib/types.d.ts} +1 -1
- package/core/src/ssr/lib/types.d.ts.map +1 -0
- package/core/src/ssr/render.d.ts +2 -1
- package/core/src/ssr/render.d.ts.map +1 -1
- package/core/src/ssr/stream.d.ts +30 -0
- package/core/src/ssr/stream.d.ts.map +1 -0
- package/elysia/index.d.ts +1 -1
- package/elysia/index.js +1917 -794
- package/elysia/index.js.map +34 -19
- package/elysia/src/index.d.ts +1 -1
- package/elysia/src/libs/cache.d.ts +1 -1
- package/elysia/src/libs/http.d.ts +1 -1
- package/elysia/src/libs/image.d.ts +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 +1 -1
- package/elysia/src/routes/content.d.ts +1 -1
- package/elysia/src/routes/image.d.ts +2 -1
- package/elysia/src/routes/image.d.ts.map +1 -1
- package/elysia/src/routes/internal.d.ts +36 -0
- package/elysia/src/routes/internal.d.ts.map +1 -0
- package/elysia/src/routes/ssr.d.ts +3 -1
- package/elysia/src/routes/ssr.d.ts.map +1 -1
- package/elysia/src/routes/static.d.ts +2 -1
- package/elysia/src/routes/static.d.ts.map +1 -1
- package/elysia/src/types.d.ts +11 -4
- package/elysia/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/react/index.d.ts +1 -1
- package/react/index.js +295 -53
- package/react/index.js.map +10 -9
- package/react/src/components/ClientOnly.d.ts +1 -1
- package/react/src/components/ContentRoute.d.ts +1 -1
- package/react/src/components/ContentRoute.d.ts.map +1 -1
- package/react/src/components/Image.d.ts +1 -1
- package/react/src/components/LazyRoute.d.ts +26 -0
- package/react/src/components/LazyRoute.d.ts.map +1 -0
- package/react/src/components/Link.d.ts +1 -1
- package/react/src/components/Link.d.ts.map +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 +2 -1
- package/react/src/hooks/index.d.ts.map +1 -1
- package/react/src/hooks/useContent.d.ts +1 -1
- package/react/src/hooks/useData.d.ts +1 -1
- package/react/src/hooks/useData.d.ts.map +1 -1
- package/react/src/hooks/useLayoutData.d.ts +18 -0
- package/react/src/hooks/useLayoutData.d.ts.map +1 -0
- 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 +2 -2
- package/react/src/index.d.ts.map +1 -1
- package/react/src/{utils → lib}/content.d.ts +1 -1
- package/react/src/lib/content.d.ts.map +1 -0
- package/react/src/{utils → lib}/head.d.ts +1 -1
- package/react/src/lib/head.d.ts.map +1 -0
- package/react/src/{utils → lib}/index.d.ts +2 -2
- package/react/src/lib/index.d.ts.map +1 -0
- package/react/src/{utils → lib}/lazy-route.d.ts +1 -1
- package/react/src/lib/lazy-route.d.ts.map +1 -0
- package/react/src/lib/route-loader.d.ts +31 -0
- package/react/src/lib/route-loader.d.ts.map +1 -0
- package/react/src/providers/ContentProvider.d.ts +1 -1
- package/react/src/providers/ContentProvider.d.ts.map +1 -1
- package/react/src/providers/RerouteProvider.d.ts +1 -1
- package/react/src/providers/RerouteProvider.d.ts.map +1 -1
- package/react/src/providers/RouterProvider.d.ts +1 -1
- package/react/src/providers/RouterProvider.d.ts.map +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/cli/src/libs/command.d.ts.map +0 -1
- package/cli/src/libs/index.d.ts.map +0 -1
- package/cli/src/libs/log.d.ts.map +0 -1
- package/cli/src/libs/markdown-processor.d.ts.map +0 -1
- package/cli/src/libs/markdown.d.ts.map +0 -1
- package/cli/src/libs/production.d.ts.map +0 -1
- package/cli/src/libs/server.d.ts.map +0 -1
- package/cli/src/libs/tailwind.d.ts.map +0 -1
- package/cli/src/libs/version.d.ts.map +0 -1
- package/core/src/ssr/data.d.ts.map +0 -1
- package/core/src/ssr/modules.d.ts.map +0 -1
- package/core/src/ssr/seed.d.ts.map +0 -1
- package/core/src/template/html.d.ts.map +0 -1
- package/core/src/template/index.d.ts.map +0 -1
- package/core/src/types.d.ts.map +0 -1
- package/core/src/utils/cache.d.ts +0 -21
- package/core/src/utils/cache.d.ts.map +0 -1
- package/core/src/utils/compression.d.ts.map +0 -1
- package/core/src/utils/index.d.ts +0 -14
- package/core/src/utils/index.d.ts.map +0 -1
- package/core/src/utils/mime.d.ts.map +0 -1
- package/core/src/utils/path.d.ts.map +0 -1
- package/elysia/src/routes/dev.d.ts +0 -18
- package/elysia/src/routes/dev.d.ts.map +0 -1
- package/react/src/utils/content.d.ts.map +0 -1
- package/react/src/utils/head.d.ts.map +0 -1
- package/react/src/utils/index.d.ts.map +0 -1
- package/react/src/utils/lazy-route.d.ts.map +0 -1
package/core/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* reroute-js v0.
|
|
2
|
+
* reroute-js v0.10.0
|
|
3
3
|
*
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright 2025 stewones <hi@stewan.io>
|
|
@@ -17,10 +17,29 @@ async function generateContentHash(content) {
|
|
|
17
17
|
hex += b.toString(16).padStart(2, "0");
|
|
18
18
|
return hex.slice(0, 8);
|
|
19
19
|
}
|
|
20
|
+
// packages/core/src/config.ts
|
|
21
|
+
import { pathToFileURL } from "node:url";
|
|
22
|
+
function defineConfig(config) {
|
|
23
|
+
return config;
|
|
24
|
+
}
|
|
25
|
+
async function loadConfig(cwd) {
|
|
26
|
+
const configPath = `${cwd}/reroute.config.ts`;
|
|
27
|
+
const configFile = Bun.file(configPath);
|
|
28
|
+
if (!await configFile.exists()) {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const mod = await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`);
|
|
33
|
+
return mod.default || mod;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.warn("[reroute] Failed to load reroute.config.ts:", error);
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
20
39
|
// packages/core/src/content/discovery.ts
|
|
21
40
|
import { readdir, stat } from "node:fs/promises";
|
|
22
41
|
|
|
23
|
-
// packages/core/src/
|
|
42
|
+
// packages/core/src/ssr/lib/path.ts
|
|
24
43
|
function join(...parts) {
|
|
25
44
|
return parts.join("/").replace(/\/+/g, "/");
|
|
26
45
|
}
|
|
@@ -78,10 +97,10 @@ async function discoverCollections(clientDir) {
|
|
|
78
97
|
}
|
|
79
98
|
// packages/core/src/content/metadata.ts
|
|
80
99
|
import { stat as stat2 } from "node:fs/promises";
|
|
81
|
-
import { pathToFileURL } from "node:url";
|
|
100
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
82
101
|
async function getContentMeta(absPath, isWatchMode) {
|
|
83
102
|
try {
|
|
84
|
-
const url =
|
|
103
|
+
const url = pathToFileURL2(absPath).href;
|
|
85
104
|
const mod = await import(`${url}${isWatchMode ? `?t=${Date.now()}` : ""}`);
|
|
86
105
|
const meta = mod.meta || mod.frontmatter || {};
|
|
87
106
|
if (meta && typeof meta === "object")
|
|
@@ -113,12 +132,448 @@ ${parts.join(`
|
|
|
113
132
|
function escapeHtml(input) {
|
|
114
133
|
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
115
134
|
}
|
|
116
|
-
// packages/core/src/ssr/
|
|
135
|
+
// packages/core/src/ssr/lib/cache.ts
|
|
136
|
+
class SSRDataCache {
|
|
137
|
+
cache = new Map;
|
|
138
|
+
static cleanupInterval = null;
|
|
139
|
+
constructor() {
|
|
140
|
+
if (!SSRDataCache.cleanupInterval) {
|
|
141
|
+
SSRDataCache.cleanupInterval = setInterval(() => {
|
|
142
|
+
this.cleanup();
|
|
143
|
+
}, 60000);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
get(key) {
|
|
147
|
+
const entry = this.cache.get(key);
|
|
148
|
+
if (!entry)
|
|
149
|
+
return;
|
|
150
|
+
const age = Date.now() - entry.timestamp;
|
|
151
|
+
if (age > entry.maxAge) {
|
|
152
|
+
this.cache.delete(key);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
return entry.data;
|
|
156
|
+
}
|
|
157
|
+
set(key, data, maxAge) {
|
|
158
|
+
if (maxAge <= 0)
|
|
159
|
+
return;
|
|
160
|
+
this.cache.set(key, {
|
|
161
|
+
data,
|
|
162
|
+
timestamp: Date.now(),
|
|
163
|
+
maxAge
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
has(key) {
|
|
167
|
+
return this.get(key) !== undefined;
|
|
168
|
+
}
|
|
169
|
+
clear() {
|
|
170
|
+
this.cache.clear();
|
|
171
|
+
}
|
|
172
|
+
cleanup() {
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
175
|
+
if (now - entry.timestamp > entry.maxAge) {
|
|
176
|
+
this.cache.delete(key);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
stats() {
|
|
181
|
+
return {
|
|
182
|
+
size: this.cache.size,
|
|
183
|
+
keys: Array.from(this.cache.keys())
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
destroy() {
|
|
187
|
+
if (SSRDataCache.cleanupInterval) {
|
|
188
|
+
clearInterval(SSRDataCache.cleanupInterval);
|
|
189
|
+
SSRDataCache.cleanupInterval = null;
|
|
190
|
+
}
|
|
191
|
+
this.clear();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
class LRUCache {
|
|
196
|
+
cache = new Map;
|
|
197
|
+
maxSize;
|
|
198
|
+
constructor(maxSize = 100) {
|
|
199
|
+
this.maxSize = maxSize;
|
|
200
|
+
}
|
|
201
|
+
get(key) {
|
|
202
|
+
const value = this.cache.get(key);
|
|
203
|
+
if (value !== undefined) {
|
|
204
|
+
this.cache.delete(key);
|
|
205
|
+
this.cache.set(key, value);
|
|
206
|
+
}
|
|
207
|
+
return value;
|
|
208
|
+
}
|
|
209
|
+
set(key, value) {
|
|
210
|
+
this.cache.delete(key);
|
|
211
|
+
if (this.cache.size >= this.maxSize) {
|
|
212
|
+
const firstKey = this.cache.keys().next().value;
|
|
213
|
+
this.cache.delete(firstKey);
|
|
214
|
+
}
|
|
215
|
+
this.cache.set(key, value);
|
|
216
|
+
}
|
|
217
|
+
has(key) {
|
|
218
|
+
return this.cache.has(key);
|
|
219
|
+
}
|
|
220
|
+
clear() {
|
|
221
|
+
this.cache.clear();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
var getGlobalCache = () => {
|
|
225
|
+
const g = globalThis;
|
|
226
|
+
if (!g.__REROUTE_SSR_CACHE__) {
|
|
227
|
+
g.__REROUTE_SSR_CACHE__ = new SSRDataCache;
|
|
228
|
+
g.__REROUTE_SSR_CACHE_INIT__ = true;
|
|
229
|
+
}
|
|
230
|
+
return g.__REROUTE_SSR_CACHE__;
|
|
231
|
+
};
|
|
232
|
+
var ssrCache = getGlobalCache();
|
|
233
|
+
// packages/core/src/ssr/lib/collections.ts
|
|
117
234
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
235
|
+
async function loadCollections(cwd, isWatchMode) {
|
|
236
|
+
try {
|
|
237
|
+
const p = join(cwd, ".reroute", "content.ts");
|
|
238
|
+
const mod = await import(pathToFileURL3(p).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
239
|
+
return mod?.byCollection || {};
|
|
240
|
+
} catch {
|
|
241
|
+
return {};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function setGlobalCollections(byCollectionForSSR) {
|
|
245
|
+
try {
|
|
246
|
+
globalThis.__REROUTE_COLLECTIONS__ = byCollectionForSSR;
|
|
247
|
+
} catch {}
|
|
248
|
+
}
|
|
249
|
+
// packages/core/src/ssr/lib/compression.ts
|
|
250
|
+
import { brotliCompressSync } from "node:zlib";
|
|
251
|
+
|
|
252
|
+
// packages/core/src/ssr/lib/mime.ts
|
|
253
|
+
function getMimeType(filePath) {
|
|
254
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
255
|
+
const mimeTypes = {
|
|
256
|
+
html: "text/html",
|
|
257
|
+
css: "text/css",
|
|
258
|
+
js: "application/javascript",
|
|
259
|
+
ts: "application/javascript",
|
|
260
|
+
json: "application/json",
|
|
261
|
+
png: "image/png",
|
|
262
|
+
jpg: "image/jpeg",
|
|
263
|
+
jpeg: "image/jpeg",
|
|
264
|
+
gif: "image/gif",
|
|
265
|
+
svg: "image/svg+xml",
|
|
266
|
+
ico: "image/x-icon",
|
|
267
|
+
woff: "font/woff",
|
|
268
|
+
woff2: "font/woff2",
|
|
269
|
+
ttf: "font/ttf"
|
|
270
|
+
};
|
|
271
|
+
return mimeTypes[ext || ""] || "application/octet-stream";
|
|
272
|
+
}
|
|
273
|
+
function isCompressible(contentType) {
|
|
274
|
+
if (!contentType)
|
|
275
|
+
return false;
|
|
276
|
+
const ct = contentType.split(";")[0].trim();
|
|
277
|
+
return ct.startsWith("text/") || ct === "application/javascript" || ct === "application/json" || ct === "application/xml" || ct === "image/svg+xml" || ct === "font/ttf" || ct === "font/woff";
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// packages/core/src/ssr/lib/compression.ts
|
|
281
|
+
function acceptsGzip(acceptEncoding) {
|
|
282
|
+
return Boolean(acceptEncoding && /gzip/i.test(acceptEncoding));
|
|
283
|
+
}
|
|
284
|
+
function acceptsBrotli(acceptEncoding) {
|
|
285
|
+
return Boolean(acceptEncoding && /\bbr\b/i.test(acceptEncoding));
|
|
286
|
+
}
|
|
287
|
+
function toBytes(input) {
|
|
288
|
+
return typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
289
|
+
}
|
|
290
|
+
function gzipIfAccepted(body, contentType, acceptEncoding) {
|
|
291
|
+
const extraHeaders = {};
|
|
292
|
+
if (isCompressible(contentType)) {
|
|
293
|
+
if (acceptsBrotli(acceptEncoding)) {
|
|
294
|
+
try {
|
|
295
|
+
const compressed = brotliCompressSync(toBytes(body));
|
|
296
|
+
extraHeaders["Content-Encoding"] = "br";
|
|
297
|
+
extraHeaders.Vary = "Accept-Encoding";
|
|
298
|
+
return { body: compressed, extraHeaders };
|
|
299
|
+
} catch {}
|
|
300
|
+
}
|
|
301
|
+
if (acceptsGzip(acceptEncoding)) {
|
|
302
|
+
try {
|
|
303
|
+
const compressed = Bun.gzipSync(toBytes(body));
|
|
304
|
+
extraHeaders["Content-Encoding"] = "gzip";
|
|
305
|
+
extraHeaders.Vary = "Accept-Encoding";
|
|
306
|
+
return { body: compressed, extraHeaders };
|
|
307
|
+
} catch {}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return { body, extraHeaders };
|
|
311
|
+
}
|
|
312
|
+
function compressStreamIfAccepted(stream, contentType, _acceptEncoding) {
|
|
313
|
+
const extraHeaders = {};
|
|
314
|
+
if (!isCompressible(contentType)) {
|
|
315
|
+
return { stream, extraHeaders };
|
|
316
|
+
}
|
|
317
|
+
return { stream, extraHeaders };
|
|
318
|
+
}
|
|
319
|
+
// packages/core/src/ssr/lib/compute.ts
|
|
320
|
+
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
321
|
+
var isThenable = (value) => {
|
|
322
|
+
return typeof value === "object" && value !== null && typeof value.then === "function";
|
|
323
|
+
};
|
|
324
|
+
async function computeSSRData(pathname, clientDir, cwd, isWatchMode, options = {}) {
|
|
325
|
+
const ssrData = {};
|
|
326
|
+
let error;
|
|
327
|
+
const pendingTasks = [];
|
|
328
|
+
const streaming = options.streaming === true;
|
|
329
|
+
const maxAge = options.maxAge || 0;
|
|
330
|
+
const maxAgeMs = maxAge * 1000;
|
|
331
|
+
const toPromise = (input) => input instanceof Promise ? input : Promise.resolve(input);
|
|
332
|
+
const storeResult = (key, task) => {
|
|
333
|
+
pendingTasks.push(task.then((value) => {
|
|
334
|
+
ssrData[key] = value;
|
|
335
|
+
}).catch(() => {}));
|
|
336
|
+
};
|
|
337
|
+
try {
|
|
338
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
339
|
+
const m = await import(pathToFileURL4(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
340
|
+
try {
|
|
341
|
+
const layouts = m?.layouts;
|
|
342
|
+
if (Array.isArray(layouts)) {
|
|
343
|
+
const matchingLayouts = layouts.filter((layout) => {
|
|
344
|
+
const pattern = String(layout?.pattern || "/");
|
|
345
|
+
if (pattern === "/")
|
|
346
|
+
return true;
|
|
347
|
+
return pathname.startsWith(pattern);
|
|
348
|
+
}).sort((a, b) => {
|
|
349
|
+
const aDepth = String(a?.pattern || "/").split("/").filter(Boolean).length;
|
|
350
|
+
const bDepth = String(b?.pattern || "/").split("/").filter(Boolean).length;
|
|
351
|
+
return aDepth - bDepth;
|
|
352
|
+
});
|
|
353
|
+
for (const layout of matchingLayouts) {
|
|
354
|
+
if (typeof layout?.path === "string") {
|
|
355
|
+
try {
|
|
356
|
+
const abs = join(clientDir, "routes", String(layout.path));
|
|
357
|
+
const mod = await import(pathToFileURL4(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
358
|
+
const ssr = mod?.ssr;
|
|
359
|
+
const dataFn = ssr?.data;
|
|
360
|
+
if (typeof dataFn === "function") {
|
|
361
|
+
const pattern = String(layout.pattern || "/");
|
|
362
|
+
const layoutKey = `__layout:${pattern}`;
|
|
363
|
+
const cacheKey = `layout:${pattern}:${pathname}`;
|
|
364
|
+
const cached = maxAgeMs > 0 ? ssrCache.get(cacheKey) : undefined;
|
|
365
|
+
if (cached !== undefined) {
|
|
366
|
+
if (streaming) {
|
|
367
|
+
ssrData[layoutKey] = cached;
|
|
368
|
+
} else {
|
|
369
|
+
ssrData[layoutKey] = cached;
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
const task = toPromise(dataFn({ pathname, params: {} })).catch((err) => {
|
|
373
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
374
|
+
console.error(`[reroute] Layout SSR data() failed for ${layout.pattern}:`, msg);
|
|
375
|
+
throw err;
|
|
376
|
+
});
|
|
377
|
+
if (streaming) {
|
|
378
|
+
ssrData[layoutKey] = task;
|
|
379
|
+
storeResult(layoutKey, task.then((value) => {
|
|
380
|
+
if (maxAgeMs > 0) {
|
|
381
|
+
ssrCache.set(cacheKey, value, maxAgeMs);
|
|
382
|
+
}
|
|
383
|
+
return value;
|
|
384
|
+
}));
|
|
385
|
+
} else {
|
|
386
|
+
const result = await task;
|
|
387
|
+
ssrData[layoutKey] = result;
|
|
388
|
+
if (maxAgeMs > 0) {
|
|
389
|
+
ssrCache.set(cacheKey, result, maxAgeMs);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
} catch {}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
} catch {}
|
|
399
|
+
try {
|
|
400
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
401
|
+
if (parts.length >= 2) {
|
|
402
|
+
const g = globalThis;
|
|
403
|
+
const key = `${parts[0]}:${parts[1]}`;
|
|
404
|
+
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
405
|
+
const dataFn = exp?.ssr?.data;
|
|
406
|
+
if (typeof dataFn === "function") {
|
|
407
|
+
const cacheKey = `content:${pathname}:${parts[1]}`;
|
|
408
|
+
const cached = maxAgeMs > 0 ? ssrCache.get(cacheKey) : undefined;
|
|
409
|
+
if (cached !== undefined) {
|
|
410
|
+
ssrData[pathname] = cached;
|
|
411
|
+
} else {
|
|
412
|
+
const task = toPromise(dataFn({
|
|
413
|
+
pathname,
|
|
414
|
+
params: { name: parts[1] }
|
|
415
|
+
})).catch((err) => {
|
|
416
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
417
|
+
error = `SSR data() failed: ${msg}`;
|
|
418
|
+
console.error(`[reroute] ${error}`);
|
|
419
|
+
throw err;
|
|
420
|
+
});
|
|
421
|
+
if (streaming) {
|
|
422
|
+
ssrData[pathname] = task;
|
|
423
|
+
storeResult(pathname, task.then((value) => {
|
|
424
|
+
if (maxAgeMs > 0) {
|
|
425
|
+
ssrCache.set(cacheKey, value, maxAgeMs);
|
|
426
|
+
}
|
|
427
|
+
return value;
|
|
428
|
+
}));
|
|
429
|
+
} else {
|
|
430
|
+
const result = await task;
|
|
431
|
+
ssrData[pathname] = result;
|
|
432
|
+
if (maxAgeMs > 0) {
|
|
433
|
+
ssrCache.set(cacheKey, result, maxAgeMs);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
} catch {}
|
|
440
|
+
try {
|
|
441
|
+
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
442
|
+
const r = match?.route;
|
|
443
|
+
const params = match?.params || {};
|
|
444
|
+
if (r && typeof r.path === "string") {
|
|
445
|
+
try {
|
|
446
|
+
const abs = join(clientDir, "routes", String(r.path));
|
|
447
|
+
const mod = await import(pathToFileURL4(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
448
|
+
const ssr = mod?.ssr;
|
|
449
|
+
const dataFn = ssr?.data;
|
|
450
|
+
if (typeof dataFn === "function") {
|
|
451
|
+
const cacheKey = `route:${pathname}:${JSON.stringify(params)}`;
|
|
452
|
+
const cached = maxAgeMs > 0 ? ssrCache.get(cacheKey) : undefined;
|
|
453
|
+
if (cached !== undefined) {
|
|
454
|
+
if (streaming && typeof cached === "object" && cached !== null && !Array.isArray(cached)) {
|
|
455
|
+
for (const [key, value] of Object.entries(cached)) {
|
|
456
|
+
ssrData[key] = value;
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
ssrData[pathname] = cached;
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
const rawResult = dataFn({ pathname, params });
|
|
463
|
+
if (streaming) {
|
|
464
|
+
if (typeof rawResult === "object" && rawResult !== null && !Array.isArray(rawResult) && !(rawResult instanceof Promise)) {
|
|
465
|
+
const entries = Object.entries(rawResult);
|
|
466
|
+
const hasMultipleKeys = entries.length > 1;
|
|
467
|
+
const hasPromises = entries.some(([_, v]) => isThenable(v));
|
|
468
|
+
if (hasMultipleKeys || hasPromises) {
|
|
469
|
+
const promisesToResolve = [];
|
|
470
|
+
for (const [key, val] of entries) {
|
|
471
|
+
if (isThenable(val)) {
|
|
472
|
+
const wrappedPromise = val.then((resolved) => {
|
|
473
|
+
return resolved;
|
|
474
|
+
}).catch((err) => {
|
|
475
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
476
|
+
error = `SSR data() failed for key ${key}: ${msg}`;
|
|
477
|
+
console.error(`[reroute] ${error}`);
|
|
478
|
+
throw err;
|
|
479
|
+
});
|
|
480
|
+
ssrData[key] = wrappedPromise;
|
|
481
|
+
storeResult(key, wrappedPromise);
|
|
482
|
+
promisesToResolve.push(wrappedPromise.then((resolved) => [key, resolved]));
|
|
483
|
+
} else {
|
|
484
|
+
ssrData[key] = val;
|
|
485
|
+
promisesToResolve.push(Promise.resolve([key, val]));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (maxAgeMs > 0) {
|
|
489
|
+
Promise.all(promisesToResolve).then((resolvedEntries) => {
|
|
490
|
+
const resolvedResult = {};
|
|
491
|
+
for (const [key, value] of resolvedEntries) {
|
|
492
|
+
resolvedResult[key] = value;
|
|
493
|
+
}
|
|
494
|
+
ssrCache.set(cacheKey, resolvedResult, maxAgeMs);
|
|
495
|
+
}).catch(() => {});
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
const task = toPromise(rawResult).catch((err) => {
|
|
499
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
500
|
+
error = `SSR data() failed: ${msg}`;
|
|
501
|
+
console.error(`[reroute] ${error}`);
|
|
502
|
+
throw err;
|
|
503
|
+
});
|
|
504
|
+
ssrData[pathname] = task;
|
|
505
|
+
storeResult(pathname, task.then((value) => {
|
|
506
|
+
if (maxAgeMs > 0) {
|
|
507
|
+
ssrCache.set(cacheKey, value, maxAgeMs);
|
|
508
|
+
}
|
|
509
|
+
return value;
|
|
510
|
+
}));
|
|
511
|
+
}
|
|
512
|
+
} else {
|
|
513
|
+
const task = toPromise(rawResult).catch((err) => {
|
|
514
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
515
|
+
error = `SSR data() failed: ${msg}`;
|
|
516
|
+
console.error(`[reroute] ${error}`);
|
|
517
|
+
throw err;
|
|
518
|
+
});
|
|
519
|
+
ssrData[pathname] = task;
|
|
520
|
+
storeResult(pathname, task.then((value) => {
|
|
521
|
+
if (maxAgeMs > 0) {
|
|
522
|
+
ssrCache.set(cacheKey, value, maxAgeMs);
|
|
523
|
+
}
|
|
524
|
+
return value;
|
|
525
|
+
}));
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
const task = toPromise(rawResult).catch((err) => {
|
|
529
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
530
|
+
error = `SSR data() failed: ${msg}`;
|
|
531
|
+
console.error(`[reroute] ${error}`);
|
|
532
|
+
throw err;
|
|
533
|
+
});
|
|
534
|
+
const result = await task;
|
|
535
|
+
ssrData[pathname] = result;
|
|
536
|
+
if (maxAgeMs > 0) {
|
|
537
|
+
ssrCache.set(cacheKey, result, maxAgeMs);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
} catch {}
|
|
543
|
+
}
|
|
544
|
+
} catch {}
|
|
545
|
+
} catch {}
|
|
546
|
+
const layoutKeys = Object.keys(ssrData).filter((key) => key.startsWith("__layout:"));
|
|
547
|
+
if (streaming) {} else {
|
|
548
|
+
const layoutData = {};
|
|
549
|
+
for (const key of layoutKeys) {
|
|
550
|
+
Object.assign(layoutData, ssrData[key]);
|
|
551
|
+
}
|
|
552
|
+
if (pathname in ssrData) {
|
|
553
|
+
ssrData[pathname] = {
|
|
554
|
+
...layoutData,
|
|
555
|
+
...typeof ssrData[pathname] === "object" && ssrData[pathname] !== null ? ssrData[pathname] : {}
|
|
556
|
+
};
|
|
557
|
+
} else if (Object.keys(layoutData).length > 0) {
|
|
558
|
+
ssrData[pathname] = layoutData;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const pending = streaming && pendingTasks.length ? Promise.allSettled(pendingTasks).then(() => {
|
|
562
|
+
return;
|
|
563
|
+
}) : undefined;
|
|
564
|
+
return { data: ssrData, error, pending };
|
|
565
|
+
}
|
|
566
|
+
function setGlobalSSRData(data) {
|
|
567
|
+
try {
|
|
568
|
+
globalThis.__REROUTE_DATA__ = data;
|
|
569
|
+
} catch {}
|
|
570
|
+
}
|
|
571
|
+
// packages/core/src/ssr/lib/data.ts
|
|
572
|
+
import { pathToFileURL as pathToFileURL6 } from "node:url";
|
|
118
573
|
|
|
119
|
-
// packages/core/src/ssr/modules.ts
|
|
574
|
+
// packages/core/src/ssr/lib/modules.ts
|
|
120
575
|
import { readdir as readdir2, stat as stat3 } from "node:fs/promises";
|
|
121
|
-
import { pathToFileURL as
|
|
576
|
+
import { pathToFileURL as pathToFileURL5 } from "node:url";
|
|
122
577
|
async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
123
578
|
try {
|
|
124
579
|
const parts = pathname.split("/").filter(Boolean);
|
|
@@ -128,15 +583,29 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode)
|
|
|
128
583
|
const name = parts[1];
|
|
129
584
|
try {
|
|
130
585
|
const registryPath = join(cwd, ".reroute", "content.ts");
|
|
131
|
-
const reg = await import(
|
|
586
|
+
const reg = await import(pathToFileURL5(registryPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
132
587
|
const get = reg?.getContentEntry;
|
|
133
588
|
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
134
589
|
const moduleUrl = entry?.module;
|
|
135
590
|
if (moduleUrl?.endsWith(".js")) {
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
|
|
591
|
+
const raw = moduleUrl.split("?")[0] || "";
|
|
592
|
+
const normalized = raw.replace(/^\/+/, "");
|
|
593
|
+
const candidates = [];
|
|
594
|
+
if (normalized) {
|
|
595
|
+
candidates.push(join(cwd, normalized));
|
|
596
|
+
if (!normalized.startsWith(".reroute/")) {
|
|
597
|
+
candidates.push(join(cwd, ".reroute", normalized));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
for (const abs of candidates) {
|
|
601
|
+
try {
|
|
602
|
+
if (!await Bun.file(abs).exists())
|
|
603
|
+
continue;
|
|
604
|
+
const href = pathToFileURL5(abs).href + (isWatchMode ? `?t=${Date.now()}` : "");
|
|
605
|
+
const mod = await import(href);
|
|
606
|
+
return mod;
|
|
607
|
+
} catch {}
|
|
608
|
+
}
|
|
140
609
|
}
|
|
141
610
|
} catch {}
|
|
142
611
|
try {
|
|
@@ -156,7 +625,7 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode)
|
|
|
156
625
|
} catch {}
|
|
157
626
|
}
|
|
158
627
|
const absChunk = join(chunkDir, latest);
|
|
159
|
-
const href =
|
|
628
|
+
const href = pathToFileURL5(absChunk).href + (isWatchMode ? `?t=${Date.now()}` : "");
|
|
160
629
|
const mod = await import(href);
|
|
161
630
|
return mod;
|
|
162
631
|
}
|
|
@@ -170,7 +639,7 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode)
|
|
|
170
639
|
else if (await Bun.file(srcTs).exists())
|
|
171
640
|
absSrc = srcTs;
|
|
172
641
|
if (absSrc) {
|
|
173
|
-
const href =
|
|
642
|
+
const href = pathToFileURL5(absSrc).href;
|
|
174
643
|
const mod = await (isWatchMode ? import(`${href}?t=${Date.now()}`) : import(href));
|
|
175
644
|
return mod;
|
|
176
645
|
}
|
|
@@ -179,7 +648,7 @@ async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode)
|
|
|
179
648
|
return null;
|
|
180
649
|
}
|
|
181
650
|
|
|
182
|
-
// packages/core/src/ssr/seed.ts
|
|
651
|
+
// packages/core/src/ssr/lib/seed.ts
|
|
183
652
|
async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
184
653
|
try {
|
|
185
654
|
const parts = pathname.split("/").filter(Boolean);
|
|
@@ -206,12 +675,74 @@ async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
|
206
675
|
} catch {}
|
|
207
676
|
}
|
|
208
677
|
|
|
209
|
-
// packages/core/src/ssr/data.ts
|
|
678
|
+
// packages/core/src/ssr/lib/data.ts
|
|
210
679
|
async function computeSSRDataForPath(params) {
|
|
211
680
|
const { pathname, clientDir, cwd, isWatchMode } = params;
|
|
681
|
+
const allData = {};
|
|
212
682
|
try {
|
|
213
683
|
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
214
684
|
} catch {}
|
|
685
|
+
try {
|
|
686
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
687
|
+
const m = await import(pathToFileURL6(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
688
|
+
const layouts = m?.layouts;
|
|
689
|
+
if (Array.isArray(layouts)) {
|
|
690
|
+
const matchingLayouts = layouts.filter((layout) => {
|
|
691
|
+
const pattern = String(layout?.pattern || "/");
|
|
692
|
+
if (pattern === "/")
|
|
693
|
+
return true;
|
|
694
|
+
return pathname.startsWith(pattern);
|
|
695
|
+
}).sort((a, b) => {
|
|
696
|
+
const aDepth = String(a?.pattern || "/").split("/").filter(Boolean).length;
|
|
697
|
+
const bDepth = String(b?.pattern || "/").split("/").filter(Boolean).length;
|
|
698
|
+
return aDepth - bDepth;
|
|
699
|
+
});
|
|
700
|
+
for (const layout of matchingLayouts) {
|
|
701
|
+
if (typeof layout?.path === "string") {
|
|
702
|
+
try {
|
|
703
|
+
const abs = join(clientDir, "routes", String(layout.path));
|
|
704
|
+
const mod = await import(pathToFileURL6(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
705
|
+
const ssr = mod?.ssr;
|
|
706
|
+
const dataFn = ssr?.data;
|
|
707
|
+
if (typeof dataFn === "function") {
|
|
708
|
+
const pattern = String(layout.pattern || "/");
|
|
709
|
+
const layoutKey = `__layout:${pattern}`;
|
|
710
|
+
const rawResult = dataFn({ pathname, params: {} });
|
|
711
|
+
if (rawResult instanceof Promise) {
|
|
712
|
+
allData[layoutKey] = {
|
|
713
|
+
__lazy__: true,
|
|
714
|
+
__fetch__: `/__reroute_data?path=${encodeURIComponent(pathname)}&key=${encodeURIComponent(layoutKey)}`
|
|
715
|
+
};
|
|
716
|
+
} else if (typeof rawResult === "object" && rawResult !== null && !Array.isArray(rawResult)) {
|
|
717
|
+
const entries = Object.entries(rawResult);
|
|
718
|
+
const hasPromises = entries.some(([_, v]) => v instanceof Promise);
|
|
719
|
+
if (hasPromises) {
|
|
720
|
+
const flattened = {
|
|
721
|
+
__flatten__: true
|
|
722
|
+
};
|
|
723
|
+
for (const [key, value] of entries) {
|
|
724
|
+
if (value instanceof Promise) {
|
|
725
|
+
flattened[key] = {
|
|
726
|
+
__lazy__: true,
|
|
727
|
+
__fetch__: `/__reroute_data?path=${encodeURIComponent(pathname)}&key=${encodeURIComponent(key)}`
|
|
728
|
+
};
|
|
729
|
+
} else {
|
|
730
|
+
flattened[key] = value;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
allData[layoutKey] = flattened;
|
|
734
|
+
} else {
|
|
735
|
+
allData[layoutKey] = rawResult;
|
|
736
|
+
}
|
|
737
|
+
} else {
|
|
738
|
+
allData[layoutKey] = rawResult;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
} catch {}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
} catch {}
|
|
215
746
|
try {
|
|
216
747
|
const parts = pathname.split("/").filter(Boolean);
|
|
217
748
|
if (parts.length >= 2) {
|
|
@@ -220,155 +751,101 @@ async function computeSSRDataForPath(params) {
|
|
|
220
751
|
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
221
752
|
const dataFn = exp?.ssr?.data;
|
|
222
753
|
if (typeof dataFn === "function") {
|
|
223
|
-
|
|
754
|
+
const rawResult = dataFn({
|
|
755
|
+
pathname,
|
|
756
|
+
params: { name: parts[1] }
|
|
757
|
+
});
|
|
758
|
+
if (rawResult instanceof Promise) {
|
|
759
|
+
return {
|
|
760
|
+
__lazy__: true,
|
|
761
|
+
__fetch__: `/__reroute_data?path=${encodeURIComponent(pathname)}&key=${encodeURIComponent(pathname)}`
|
|
762
|
+
};
|
|
763
|
+
} else if (typeof rawResult === "object" && rawResult !== null && !Array.isArray(rawResult)) {
|
|
764
|
+
const entries = Object.entries(rawResult);
|
|
765
|
+
const hasPromises = entries.some(([_, v]) => v instanceof Promise);
|
|
766
|
+
if (hasPromises) {
|
|
767
|
+
const flattened = { __flatten__: true };
|
|
768
|
+
for (const [key2, value] of entries) {
|
|
769
|
+
if (value instanceof Promise) {
|
|
770
|
+
flattened[key2] = {
|
|
771
|
+
__lazy__: true,
|
|
772
|
+
__fetch__: `/__reroute_data?path=${encodeURIComponent(pathname)}&key=${encodeURIComponent(key2)}`
|
|
773
|
+
};
|
|
774
|
+
} else {
|
|
775
|
+
flattened[key2] = value;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return flattened;
|
|
779
|
+
} else {
|
|
780
|
+
return rawResult;
|
|
781
|
+
}
|
|
782
|
+
} else {
|
|
783
|
+
return rawResult;
|
|
784
|
+
}
|
|
224
785
|
}
|
|
225
786
|
}
|
|
226
787
|
} catch {}
|
|
227
788
|
try {
|
|
228
789
|
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
229
|
-
const m = await import(
|
|
790
|
+
const m = await import(pathToFileURL6(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
230
791
|
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
231
792
|
const r = match?.route;
|
|
232
793
|
const paramsValue = match?.params || {};
|
|
233
794
|
if (r && typeof r.path === "string") {
|
|
234
795
|
try {
|
|
235
796
|
const abs = join(clientDir, "routes", String(r.path));
|
|
236
|
-
const mod = await import(
|
|
797
|
+
const mod = await import(pathToFileURL6(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
237
798
|
const ssr = mod?.ssr;
|
|
238
799
|
const dataFn = ssr?.data;
|
|
239
800
|
if (typeof dataFn === "function") {
|
|
240
|
-
|
|
801
|
+
const rawResult = dataFn({
|
|
802
|
+
pathname,
|
|
803
|
+
params: paramsValue
|
|
804
|
+
});
|
|
805
|
+
if (rawResult instanceof Promise) {
|
|
806
|
+
allData[pathname] = {
|
|
807
|
+
__lazy__: true,
|
|
808
|
+
__fetch__: `/__reroute_data?path=${encodeURIComponent(pathname)}&key=${encodeURIComponent(pathname)}`
|
|
809
|
+
};
|
|
810
|
+
} else if (typeof rawResult === "object" && rawResult !== null && !Array.isArray(rawResult)) {
|
|
811
|
+
const obj = rawResult;
|
|
812
|
+
const entries = Object.entries(obj);
|
|
813
|
+
const hasPromises = entries.some(([_, v]) => v instanceof Promise);
|
|
814
|
+
if (hasPromises) {
|
|
815
|
+
const flattened = { __flatten__: true };
|
|
816
|
+
for (const [key, value] of entries) {
|
|
817
|
+
if (value instanceof Promise) {
|
|
818
|
+
flattened[key] = {
|
|
819
|
+
__lazy__: true,
|
|
820
|
+
__fetch__: `/__reroute_data?path=${encodeURIComponent(pathname)}&key=${encodeURIComponent(key)}`
|
|
821
|
+
};
|
|
822
|
+
} else {
|
|
823
|
+
flattened[key] = value;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
allData[pathname] = flattened;
|
|
827
|
+
} else {
|
|
828
|
+
allData[pathname] = rawResult;
|
|
829
|
+
}
|
|
830
|
+
} else {
|
|
831
|
+
allData[pathname] = rawResult;
|
|
832
|
+
}
|
|
241
833
|
}
|
|
242
834
|
} catch {}
|
|
243
835
|
}
|
|
244
836
|
} catch {}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
var keys = Object.keys(object);
|
|
254
|
-
if (Object.getOwnPropertySymbols) {
|
|
255
|
-
var symbols = Object.getOwnPropertySymbols(object);
|
|
256
|
-
enumerableOnly && (symbols = symbols.filter(function(sym) {
|
|
257
|
-
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
258
|
-
})), keys.push.apply(keys, symbols);
|
|
259
|
-
}
|
|
260
|
-
return keys;
|
|
261
|
-
}
|
|
262
|
-
function _objectSpread(target) {
|
|
263
|
-
for (var i = 1;i < arguments.length; i++) {
|
|
264
|
-
var source = arguments[i] != null ? arguments[i] : {};
|
|
265
|
-
i % 2 ? ownKeys(Object(source), true).forEach(function(key) {
|
|
266
|
-
_defineProperty(target, key, source[key]);
|
|
267
|
-
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
|
|
268
|
-
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
269
|
-
});
|
|
837
|
+
const dataKeys = Object.keys(allData);
|
|
838
|
+
if (dataKeys.length > 1) {
|
|
839
|
+
return {
|
|
840
|
+
__reroute_multiple__: true,
|
|
841
|
+
...allData
|
|
842
|
+
};
|
|
843
|
+
} else if (dataKeys.length === 1) {
|
|
844
|
+
return allData[dataKeys[0]];
|
|
270
845
|
}
|
|
271
|
-
return
|
|
846
|
+
return null;
|
|
272
847
|
}
|
|
273
|
-
|
|
274
|
-
key = _toPropertyKey(key);
|
|
275
|
-
if (key in obj) {
|
|
276
|
-
Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
|
|
277
|
-
} else {
|
|
278
|
-
obj[key] = value;
|
|
279
|
-
}
|
|
280
|
-
return obj;
|
|
281
|
-
}
|
|
282
|
-
function _toPropertyKey(arg) {
|
|
283
|
-
var key = _toPrimitive(arg, "string");
|
|
284
|
-
return typeof key === "symbol" ? key : String(key);
|
|
285
|
-
}
|
|
286
|
-
function _toPrimitive(input, hint) {
|
|
287
|
-
if (typeof input !== "object" || input === null)
|
|
288
|
-
return input;
|
|
289
|
-
var prim = input[Symbol.toPrimitive];
|
|
290
|
-
if (prim !== undefined) {
|
|
291
|
-
var res = prim.call(input, hint || "default");
|
|
292
|
-
if (typeof res !== "object")
|
|
293
|
-
return res;
|
|
294
|
-
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
295
|
-
}
|
|
296
|
-
return (hint === "string" ? String : Number)(input);
|
|
297
|
-
}
|
|
298
|
-
var dedent = createDedent({});
|
|
299
|
-
var dedent_default = dedent;
|
|
300
|
-
function createDedent(options) {
|
|
301
|
-
dedent2.withOptions = (newOptions) => createDedent(_objectSpread(_objectSpread({}, options), newOptions));
|
|
302
|
-
return dedent2;
|
|
303
|
-
function dedent2(strings, ...values) {
|
|
304
|
-
const raw = typeof strings === "string" ? [strings] : strings.raw;
|
|
305
|
-
const {
|
|
306
|
-
alignValues = false,
|
|
307
|
-
escapeSpecialCharacters = Array.isArray(strings),
|
|
308
|
-
trimWhitespace = true
|
|
309
|
-
} = options;
|
|
310
|
-
let result = "";
|
|
311
|
-
for (let i = 0;i < raw.length; i++) {
|
|
312
|
-
let next = raw[i];
|
|
313
|
-
if (escapeSpecialCharacters) {
|
|
314
|
-
next = next.replace(/\\\n[ \t]*/g, "").replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\\{/g, "{");
|
|
315
|
-
}
|
|
316
|
-
result += next;
|
|
317
|
-
if (i < values.length) {
|
|
318
|
-
const value = alignValues ? alignValue(values[i], result) : values[i];
|
|
319
|
-
result += value;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
const lines = result.split(`
|
|
323
|
-
`);
|
|
324
|
-
let mindent = null;
|
|
325
|
-
for (const l of lines) {
|
|
326
|
-
const m = l.match(/^(\s+)\S+/);
|
|
327
|
-
if (m) {
|
|
328
|
-
const indent = m[1].length;
|
|
329
|
-
if (!mindent) {
|
|
330
|
-
mindent = indent;
|
|
331
|
-
} else {
|
|
332
|
-
mindent = Math.min(mindent, indent);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
if (mindent !== null) {
|
|
337
|
-
const m = mindent;
|
|
338
|
-
result = lines.map((l) => l[0] === " " || l[0] === "\t" ? l.slice(m) : l).join(`
|
|
339
|
-
`);
|
|
340
|
-
}
|
|
341
|
-
if (trimWhitespace) {
|
|
342
|
-
result = result.trim();
|
|
343
|
-
}
|
|
344
|
-
if (escapeSpecialCharacters) {
|
|
345
|
-
result = result.replace(/\\n/g, `
|
|
346
|
-
`);
|
|
347
|
-
}
|
|
348
|
-
return result;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
function alignValue(value, precedingText) {
|
|
352
|
-
if (typeof value !== "string" || !value.includes(`
|
|
353
|
-
`)) {
|
|
354
|
-
return value;
|
|
355
|
-
}
|
|
356
|
-
const currentLine = precedingText.slice(precedingText.lastIndexOf(`
|
|
357
|
-
`) + 1);
|
|
358
|
-
const indentMatch = currentLine.match(/^(\s+)/);
|
|
359
|
-
if (indentMatch) {
|
|
360
|
-
const indent = indentMatch[1];
|
|
361
|
-
return value.replace(/\n/g, `
|
|
362
|
-
${indent}`);
|
|
363
|
-
}
|
|
364
|
-
return value;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// packages/core/src/ssr/render.ts
|
|
368
|
-
import { cloneElement } from "react";
|
|
369
|
-
import { renderToString } from "react-dom/server";
|
|
370
|
-
|
|
371
|
-
// packages/core/src/template/html.ts
|
|
848
|
+
// packages/core/src/ssr/lib/html.ts
|
|
372
849
|
async function loadIndexHtml(clientDir) {
|
|
373
850
|
const templatePath = join(clientDir, "index.html");
|
|
374
851
|
try {
|
|
@@ -426,12 +903,30 @@ function applyIndexTemplate(templateHtml, appHtml, {
|
|
|
426
903
|
</head>`);
|
|
427
904
|
}
|
|
428
905
|
html = html.replace(/<script\b[^>]*src\s*=\s*["'][^"']+\.(ts|tsx)(?:[?#][^"']*)?["'][^>]*>\s*<\/script>/gi, "");
|
|
906
|
+
let cleanAppHtml = appHtml;
|
|
907
|
+
const preloadLinks = [];
|
|
908
|
+
const linkPrefixRegex = /^(\s*(?:<link\b[^>]*>\s*)+)/;
|
|
909
|
+
const prefixMatch = appHtml.match(linkPrefixRegex);
|
|
910
|
+
if (prefixMatch?.[1]) {
|
|
911
|
+
const linkMatches = prefixMatch[1].match(/<link\b[^>]*>/g);
|
|
912
|
+
if (linkMatches) {
|
|
913
|
+
preloadLinks.push(...linkMatches);
|
|
914
|
+
cleanAppHtml = appHtml.slice(prefixMatch[1].length);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
if (preloadLinks.length > 0) {
|
|
918
|
+
const preloadHtml = preloadLinks.join(`
|
|
919
|
+
`);
|
|
920
|
+
html = html.replace(/<\/head>/i, ` ${preloadHtml}
|
|
921
|
+
</head>`);
|
|
922
|
+
}
|
|
429
923
|
const appIdEscaped = appId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
430
924
|
const rootDivRegex = new RegExp(`(<div[^>]*\\bid=(\\"|')${appIdEscaped}\\2[^>]*>)([\\s\\S]*?)(<\\/div>)`, "i");
|
|
431
925
|
if (rootDivRegex.test(html)) {
|
|
432
|
-
|
|
926
|
+
const escapedAppHtml = cleanAppHtml.replace(/\$/g, "$$$$");
|
|
927
|
+
html = html.replace(rootDivRegex, `$1${escapedAppHtml}$4`);
|
|
433
928
|
} else {
|
|
434
|
-
html = html.replace(/<\/body>/i, ` <div id="${appId}">${
|
|
929
|
+
html = html.replace(/<\/body>/i, ` <div id="${appId}">${cleanAppHtml}</div>
|
|
435
930
|
</body>`);
|
|
436
931
|
}
|
|
437
932
|
if (hydrationScript) {
|
|
@@ -440,33 +935,144 @@ function applyIndexTemplate(templateHtml, appHtml, {
|
|
|
440
935
|
}
|
|
441
936
|
return html;
|
|
442
937
|
}
|
|
443
|
-
// packages/core/src/ssr/
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
cwd,
|
|
450
|
-
isWatchMode,
|
|
451
|
-
bundleUrl,
|
|
452
|
-
head = "",
|
|
453
|
-
lang = "en",
|
|
454
|
-
appId = "root",
|
|
455
|
-
minify = false
|
|
456
|
-
} = options;
|
|
457
|
-
const scripts = [bundleUrl];
|
|
458
|
-
let hydrationScript = "";
|
|
459
|
-
let extraHead = "";
|
|
938
|
+
// packages/core/src/ssr/lib/metadata.ts
|
|
939
|
+
import { pathToFileURL as pathToFileURL7 } from "node:url";
|
|
940
|
+
async function extractPageMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride) {
|
|
941
|
+
let perPageHead = "";
|
|
942
|
+
let pageLang;
|
|
943
|
+
let statusOverride = currentStatusOverride;
|
|
460
944
|
try {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
945
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
946
|
+
const m = await import(pathToFileURL7(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
947
|
+
const layouts = m?.layouts;
|
|
948
|
+
if (Array.isArray(layouts)) {
|
|
949
|
+
const matchingLayouts = layouts.filter((layout) => {
|
|
950
|
+
const pattern = String(layout?.pattern || "/");
|
|
951
|
+
if (pattern === "/")
|
|
952
|
+
return true;
|
|
953
|
+
return pathname.startsWith(pattern);
|
|
954
|
+
}).sort((a, b) => {
|
|
955
|
+
const aDepth = String(a?.pattern || "/").split("/").filter(Boolean).length;
|
|
956
|
+
const bDepth = String(b?.pattern || "/").split("/").filter(Boolean).length;
|
|
957
|
+
return aDepth - bDepth;
|
|
958
|
+
});
|
|
959
|
+
for (const layout of matchingLayouts) {
|
|
960
|
+
if (typeof layout?.path === "string") {
|
|
961
|
+
try {
|
|
962
|
+
const abs = join(clientDir, "routes", String(layout.path));
|
|
963
|
+
const mod = await import(pathToFileURL7(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
964
|
+
const meta = mod?.meta;
|
|
965
|
+
const ssr = mod?.ssr;
|
|
966
|
+
if (meta)
|
|
967
|
+
perPageHead += buildHeadFromMeta(meta);
|
|
968
|
+
if (ssr) {
|
|
969
|
+
const ssrHead = typeof ssr.head === "string" ? ssr.head : Array.isArray(ssr.head) ? String(ssr.head.join(`
|
|
970
|
+
`)) : "";
|
|
971
|
+
if (ssrHead)
|
|
972
|
+
perPageHead += `
|
|
973
|
+
${ssrHead}`;
|
|
974
|
+
if (typeof ssr.lang === "string" && ssr.lang.trim() && !pageLang) {
|
|
975
|
+
pageLang = ssr.lang.trim();
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
} catch {}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
464
981
|
}
|
|
465
982
|
} catch {}
|
|
466
|
-
let statusOverride;
|
|
467
983
|
try {
|
|
468
|
-
|
|
984
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
985
|
+
if (parts.length >= 2) {
|
|
986
|
+
const key = `${parts[0]}:${parts[1]}`;
|
|
987
|
+
const g = globalThis;
|
|
988
|
+
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
989
|
+
const meta = exp?.meta;
|
|
990
|
+
const ssr = exp?.ssr;
|
|
991
|
+
perPageHead += buildHeadFromMeta(meta);
|
|
992
|
+
const ssrHead = typeof ssr?.head === "string" ? ssr.head : Array.isArray(ssr?.head) ? String(ssr.head.join(`
|
|
993
|
+
`)) : "";
|
|
994
|
+
if (ssrHead)
|
|
995
|
+
perPageHead += `
|
|
996
|
+
${ssrHead}`;
|
|
997
|
+
if (typeof ssr?.lang === "string" && ssr.lang.trim()) {
|
|
998
|
+
pageLang = ssr.lang.trim();
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
} catch {}
|
|
1002
|
+
try {
|
|
1003
|
+
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
1004
|
+
const m = await import(pathToFileURL7(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
1005
|
+
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
1006
|
+
const r = match?.route;
|
|
1007
|
+
if (!r) {
|
|
1008
|
+
statusOverride = statusOverride || 404;
|
|
1009
|
+
}
|
|
1010
|
+
if (r && typeof r.path === "string") {
|
|
1011
|
+
try {
|
|
1012
|
+
const abs = join(clientDir, "routes", String(r.path));
|
|
1013
|
+
const mod = await import(pathToFileURL7(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
1014
|
+
const meta = mod?.meta;
|
|
1015
|
+
const ssr = mod?.ssr;
|
|
1016
|
+
if (meta)
|
|
1017
|
+
perPageHead += buildHeadFromMeta(meta);
|
|
1018
|
+
if (ssr) {
|
|
1019
|
+
const ssrHead = typeof ssr.head === "string" ? ssr.head : Array.isArray(ssr.head) ? String(ssr.head.join(`
|
|
1020
|
+
`)) : "";
|
|
1021
|
+
if (ssrHead)
|
|
1022
|
+
perPageHead += `
|
|
1023
|
+
${ssrHead}`;
|
|
1024
|
+
if (typeof ssr.lang === "string" && ssr.lang.trim()) {
|
|
1025
|
+
pageLang = ssr.lang.trim();
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
} catch {}
|
|
1029
|
+
} else {
|
|
1030
|
+
try {
|
|
1031
|
+
const list = m?.notFoundRoutes;
|
|
1032
|
+
if (Array.isArray(list)) {
|
|
1033
|
+
let chosen;
|
|
1034
|
+
let bestLen = -1;
|
|
1035
|
+
for (const nf of list) {
|
|
1036
|
+
const pat = String(nf?.pattern || "/");
|
|
1037
|
+
if (!pathname.startsWith(pat))
|
|
1038
|
+
continue;
|
|
1039
|
+
const len = pat.split("/").filter(Boolean).length;
|
|
1040
|
+
if (len >= bestLen) {
|
|
1041
|
+
bestLen = len;
|
|
1042
|
+
chosen = nf;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
if (chosen && typeof chosen.path === "string") {
|
|
1046
|
+
try {
|
|
1047
|
+
const abs = join(clientDir, "routes", String(chosen.path));
|
|
1048
|
+
const mod = await import(pathToFileURL7(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
1049
|
+
const meta = mod?.meta;
|
|
1050
|
+
const ssr = mod?.ssr;
|
|
1051
|
+
if (meta)
|
|
1052
|
+
perPageHead += buildHeadFromMeta(meta);
|
|
1053
|
+
const ssrHead = typeof ssr?.head === "string" ? ssr.head : Array.isArray(ssr?.head) ? String(ssr.head.join(`
|
|
1054
|
+
`)) : "";
|
|
1055
|
+
if (ssrHead)
|
|
1056
|
+
perPageHead += `
|
|
1057
|
+
${ssrHead}`;
|
|
1058
|
+
if (typeof ssr?.lang === "string" && ssr.lang?.trim()) {
|
|
1059
|
+
pageLang = String(ssr.lang).trim();
|
|
1060
|
+
}
|
|
1061
|
+
} catch {}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
} catch {}
|
|
1065
|
+
}
|
|
469
1066
|
} catch {}
|
|
1067
|
+
return { perPageHead, pageLang, statusOverride };
|
|
1068
|
+
}
|
|
1069
|
+
// packages/core/src/ssr/lib/preload.ts
|
|
1070
|
+
import { readdir as readdir3, stat as stat4 } from "node:fs/promises";
|
|
1071
|
+
import { pathToFileURL as pathToFileURL8 } from "node:url";
|
|
1072
|
+
async function preloadContentModule(pathname, clientDir, cwd, isWatchMode) {
|
|
1073
|
+
let extraHead = "";
|
|
1074
|
+
let hydrationScript = "";
|
|
1075
|
+
let statusOverride;
|
|
470
1076
|
try {
|
|
471
1077
|
const parts = pathname.split("/").filter(Boolean);
|
|
472
1078
|
if (parts.length >= 2) {
|
|
@@ -486,7 +1092,7 @@ async function renderSSRDocument(options) {
|
|
|
486
1092
|
}
|
|
487
1093
|
try {
|
|
488
1094
|
const registryPath = join(cwd, ".reroute", "content.ts");
|
|
489
|
-
const reg = await import(
|
|
1095
|
+
const reg = await import(pathToFileURL8(registryPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
490
1096
|
const get = reg?.getContentEntry;
|
|
491
1097
|
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
492
1098
|
const moduleUrl = entry?.module;
|
|
@@ -540,63 +1146,89 @@ async function renderSSRDocument(options) {
|
|
|
540
1146
|
}
|
|
541
1147
|
}
|
|
542
1148
|
} catch {}
|
|
543
|
-
|
|
544
|
-
|
|
1149
|
+
return { modulePath: undefined, extraHead, hydrationScript, statusOverride };
|
|
1150
|
+
}
|
|
1151
|
+
function resetSSRAccessTracking() {
|
|
545
1152
|
try {
|
|
546
|
-
|
|
547
|
-
const parts = pathname.split("/").filter(Boolean);
|
|
548
|
-
if (parts.length >= 2) {
|
|
549
|
-
const g = globalThis;
|
|
550
|
-
const key = `${parts[0]}:${parts[1]}`;
|
|
551
|
-
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
552
|
-
const dataFn = exp?.ssr?.data;
|
|
553
|
-
if (typeof dataFn === "function") {
|
|
554
|
-
const out = await dataFn({
|
|
555
|
-
pathname,
|
|
556
|
-
params: { name: parts[1] }
|
|
557
|
-
});
|
|
558
|
-
__SSR_DATA__[pathname] = out;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
} catch {}
|
|
562
|
-
try {
|
|
563
|
-
const routesPath = join(cwd, ".reroute", "routes.ts");
|
|
564
|
-
const m = await import(pathToFileURL4(routesPath).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
565
|
-
const match = typeof m.matchRoute === "function" ? m.matchRoute(pathname) : null;
|
|
566
|
-
const r = match?.route;
|
|
567
|
-
const params = match?.params || {};
|
|
568
|
-
if (r && typeof r.path === "string") {
|
|
569
|
-
try {
|
|
570
|
-
const abs = join(clientDir, "routes", String(r.path));
|
|
571
|
-
const mod = await import(pathToFileURL4(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
572
|
-
const ssr = mod?.ssr;
|
|
573
|
-
const dataFn = ssr?.data;
|
|
574
|
-
if (typeof dataFn === "function") {
|
|
575
|
-
const out = await dataFn({ pathname, params });
|
|
576
|
-
__SSR_DATA__[pathname] = out;
|
|
577
|
-
}
|
|
578
|
-
} catch {}
|
|
579
|
-
}
|
|
580
|
-
} catch {}
|
|
1153
|
+
globalThis.__REROUTE_SSR_ACCESSED__ = {};
|
|
581
1154
|
} catch {}
|
|
1155
|
+
}
|
|
1156
|
+
function createBundlePreload(bundleUrl) {
|
|
582
1157
|
try {
|
|
583
|
-
|
|
1158
|
+
if (typeof bundleUrl === "string" && bundleUrl.endsWith(".js")) {
|
|
1159
|
+
return `
|
|
1160
|
+
<link rel="modulepreload" href="${bundleUrl}" />`;
|
|
1161
|
+
}
|
|
584
1162
|
} catch {}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
1163
|
+
return "";
|
|
1164
|
+
}
|
|
1165
|
+
// packages/core/src/ssr/lib/scripts.ts
|
|
1166
|
+
function escapeJsonForScript(json) {
|
|
1167
|
+
return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
1168
|
+
}
|
|
1169
|
+
function createDataScript(ssrData, errorMsg) {
|
|
1170
|
+
let script = "";
|
|
1171
|
+
if (ssrData && Object.keys(ssrData).length > 0) {
|
|
590
1172
|
try {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
1173
|
+
const dataJson = escapeJsonForScript(JSON.stringify(ssrData));
|
|
1174
|
+
script += `<script>(function(){try{var w=(typeof window!=='undefined'?window:globalThis);w.__REROUTE_DATA__=Object.assign({},w.__REROUTE_DATA__||{},${dataJson});try{w.dispatchEvent(new CustomEvent('__reroute_data__',{detail:{keys:Object.keys(${dataJson})}}));}catch(e){}}catch(e){}})();</script>`;
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
console.error("[reroute] Failed to serialize SSR data:", error);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (errorMsg) {
|
|
1180
|
+
const escapedMsg = JSON.stringify(errorMsg);
|
|
1181
|
+
script += `<script>(function(){try{console.warn('%c[reroute] SSR Data Error','background: #ff5555; color: white; padding: 2px 6px; border-radius: 3px;',${escapedMsg});}catch(e){}})();</script>`;
|
|
1182
|
+
}
|
|
1183
|
+
return script;
|
|
1184
|
+
}
|
|
1185
|
+
function createCollectionScript(subset, partial) {
|
|
1186
|
+
const subsetJson = escapeJsonForScript(JSON.stringify(subset));
|
|
1187
|
+
const partialJson = escapeJsonForScript(JSON.stringify(partial));
|
|
1188
|
+
return `<script>(function(){try{var w=(typeof window!=='undefined'?window:globalThis);w.__REROUTE_COLLECTIONS__=w.__REROUTE_COLLECTIONS__||{};Object.assign(w.__REROUTE_COLLECTIONS__,${subsetJson});w.__REROUTE_COLLECTIONS_PARTIAL__=w.__REROUTE_COLLECTIONS_PARTIAL__||{};Object.assign(w.__REROUTE_COLLECTIONS_PARTIAL__,${partialJson});}catch(e){}})();</script>`;
|
|
1189
|
+
}
|
|
1190
|
+
function processCollections(accessedCollections, byCollectionForSSR) {
|
|
1191
|
+
const sortByDate = (order) => (a, b) => {
|
|
1192
|
+
const da = a?.meta?.date ? Date.parse(String(a.meta.date)) : 0;
|
|
1193
|
+
const db = b?.meta?.date ? Date.parse(String(b.meta.date)) : 0;
|
|
1194
|
+
return order === "asc" ? da - db : db - da;
|
|
1195
|
+
};
|
|
1196
|
+
const usage = {};
|
|
1197
|
+
if (accessedCollections && typeof accessedCollections.forEach === "function") {
|
|
1198
|
+
accessedCollections.forEach((c) => {
|
|
1199
|
+
if (typeof c === "string")
|
|
1200
|
+
usage[c] = { limit: Number.POSITIVE_INFINITY, sort: "custom" };
|
|
1201
|
+
});
|
|
1202
|
+
} else if (accessedCollections && typeof accessedCollections === "object") {
|
|
1203
|
+
for (const [k, v] of Object.entries(accessedCollections)) {
|
|
1204
|
+
const lim = typeof v?.limit === "number" ? v.limit : Number.POSITIVE_INFINITY;
|
|
1205
|
+
const s = typeof v?.sort === "string" ? v.sort : "custom";
|
|
1206
|
+
usage[k] = { limit: lim, sort: s };
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
const collections = Object.keys(usage);
|
|
1210
|
+
const subset = {};
|
|
1211
|
+
const partial = {};
|
|
1212
|
+
for (const c of collections) {
|
|
1213
|
+
const conf = usage[c];
|
|
1214
|
+
const full = byCollectionForSSR?.[c] || [];
|
|
1215
|
+
let arr = full;
|
|
1216
|
+
if (Number.isFinite(conf.limit) && conf.limit > 0 && conf.sort !== "custom") {
|
|
1217
|
+
if (conf.sort === "date-desc") {
|
|
1218
|
+
arr = [...full].sort(sortByDate("desc")).slice(0, conf.limit);
|
|
1219
|
+
} else if (conf.sort === "date-asc") {
|
|
1220
|
+
arr = [...full].sort(sortByDate("asc")).slice(0, conf.limit);
|
|
1221
|
+
} else if (conf.sort === "none") {
|
|
1222
|
+
arr = full.slice(0, conf.limit);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
subset[c] = arr;
|
|
1226
|
+
partial[c] = Array.isArray(arr) && Array.isArray(full) ? arr.length < full.length : false;
|
|
1227
|
+
}
|
|
1228
|
+
return { subset, partial };
|
|
1229
|
+
}
|
|
1230
|
+
// packages/core/src/ssr/lib/styles.ts
|
|
1231
|
+
async function inlineTailwindCSS(clientDir, minify = false) {
|
|
600
1232
|
try {
|
|
601
1233
|
const candidates = [
|
|
602
1234
|
join(clientDir, "..", ".reroute", "theme.css"),
|
|
@@ -611,169 +1243,204 @@ async function renderSSRDocument(options) {
|
|
|
611
1243
|
break;
|
|
612
1244
|
}
|
|
613
1245
|
}
|
|
614
|
-
if (cssPath)
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
619
|
-
inlineStyleTag = `<style data-reroute="tailwind">${css}</style>`;
|
|
1246
|
+
if (!cssPath)
|
|
1247
|
+
return "";
|
|
1248
|
+
let css = await Bun.file(cssPath).text();
|
|
1249
|
+
if (minify) {
|
|
1250
|
+
css = css.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/\s*([{}:;,>+~])\s*/g, "$1").replace(/;}/g, "}").trim();
|
|
620
1251
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
1252
|
+
return `<style data-reroute="tailwind">${css}</style>`;
|
|
1253
|
+
} catch {
|
|
1254
|
+
return "";
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
// packages/core/src/ssr/lib/template.ts
|
|
1258
|
+
function splitTemplate(html, appId = "root") {
|
|
1259
|
+
const appIdEscaped = appId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1260
|
+
const rootDivRegex = new RegExp(`(<div[^>]*\\bid=["']${appIdEscaped}["'][^>]*>)([\\s\\S]*?)(<\\/div>)`, "i");
|
|
1261
|
+
const match = html.match(rootDivRegex);
|
|
1262
|
+
if (!match || match.index === undefined) {
|
|
1263
|
+
throw new Error(`Could not find app root div with id="${appId}"`);
|
|
1264
|
+
}
|
|
1265
|
+
const [fullMatch, rootStart, , rootEnd] = match;
|
|
1266
|
+
const matchIndex = match.index;
|
|
1267
|
+
return {
|
|
1268
|
+
htmlHead: html.substring(0, matchIndex),
|
|
1269
|
+
rootStart,
|
|
1270
|
+
rootEnd,
|
|
1271
|
+
htmlTail: html.substring(matchIndex + fullMatch.length)
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
// node_modules/.bun/dedent@1.7.0/node_modules/dedent/dist/dedent.mjs
|
|
1275
|
+
function ownKeys(object, enumerableOnly) {
|
|
1276
|
+
var keys = Object.keys(object);
|
|
1277
|
+
if (Object.getOwnPropertySymbols) {
|
|
1278
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
1279
|
+
enumerableOnly && (symbols = symbols.filter(function(sym) {
|
|
1280
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
1281
|
+
})), keys.push.apply(keys, symbols);
|
|
1282
|
+
}
|
|
1283
|
+
return keys;
|
|
1284
|
+
}
|
|
1285
|
+
function _objectSpread(target) {
|
|
1286
|
+
for (var i = 1;i < arguments.length; i++) {
|
|
1287
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
1288
|
+
i % 2 ? ownKeys(Object(source), true).forEach(function(key) {
|
|
1289
|
+
_defineProperty(target, key, source[key]);
|
|
1290
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
|
|
1291
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
return target;
|
|
1295
|
+
}
|
|
1296
|
+
function _defineProperty(obj, key, value) {
|
|
1297
|
+
key = _toPropertyKey(key);
|
|
1298
|
+
if (key in obj) {
|
|
1299
|
+
Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
|
|
1300
|
+
} else {
|
|
1301
|
+
obj[key] = value;
|
|
1302
|
+
}
|
|
1303
|
+
return obj;
|
|
1304
|
+
}
|
|
1305
|
+
function _toPropertyKey(arg) {
|
|
1306
|
+
var key = _toPrimitive(arg, "string");
|
|
1307
|
+
return typeof key === "symbol" ? key : String(key);
|
|
1308
|
+
}
|
|
1309
|
+
function _toPrimitive(input, hint) {
|
|
1310
|
+
if (typeof input !== "object" || input === null)
|
|
1311
|
+
return input;
|
|
1312
|
+
var prim = input[Symbol.toPrimitive];
|
|
1313
|
+
if (prim !== undefined) {
|
|
1314
|
+
var res = prim.call(input, hint || "default");
|
|
1315
|
+
if (typeof res !== "object")
|
|
1316
|
+
return res;
|
|
1317
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
1318
|
+
}
|
|
1319
|
+
return (hint === "string" ? String : Number)(input);
|
|
1320
|
+
}
|
|
1321
|
+
var dedent = createDedent({});
|
|
1322
|
+
var dedent_default = dedent;
|
|
1323
|
+
function createDedent(options) {
|
|
1324
|
+
dedent2.withOptions = (newOptions) => createDedent(_objectSpread(_objectSpread({}, options), newOptions));
|
|
1325
|
+
return dedent2;
|
|
1326
|
+
function dedent2(strings, ...values) {
|
|
1327
|
+
const raw = typeof strings === "string" ? [strings] : strings.raw;
|
|
1328
|
+
const {
|
|
1329
|
+
alignValues = false,
|
|
1330
|
+
escapeSpecialCharacters = Array.isArray(strings),
|
|
1331
|
+
trimWhitespace = true
|
|
1332
|
+
} = options;
|
|
1333
|
+
let result = "";
|
|
1334
|
+
for (let i = 0;i < raw.length; i++) {
|
|
1335
|
+
let next = raw[i];
|
|
1336
|
+
if (escapeSpecialCharacters) {
|
|
1337
|
+
next = next.replace(/\\\n[ \t]*/g, "").replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\\{/g, "{");
|
|
1338
|
+
}
|
|
1339
|
+
result += next;
|
|
1340
|
+
if (i < values.length) {
|
|
1341
|
+
const value = alignValues ? alignValue(values[i], result) : values[i];
|
|
1342
|
+
result += value;
|
|
641
1343
|
}
|
|
642
1344
|
}
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
} else if (conf.sort === "date-asc") {
|
|
655
|
-
arr = [...full].sort(sortByDate("asc")).slice(0, conf.limit);
|
|
656
|
-
} else if (conf.sort === "none") {
|
|
657
|
-
arr = full.slice(0, conf.limit);
|
|
658
|
-
}
|
|
1345
|
+
const lines = result.split(`
|
|
1346
|
+
`);
|
|
1347
|
+
let mindent = null;
|
|
1348
|
+
for (const l of lines) {
|
|
1349
|
+
const m = l.match(/^(\s+)\S+/);
|
|
1350
|
+
if (m) {
|
|
1351
|
+
const indent = m[1].length;
|
|
1352
|
+
if (!mindent) {
|
|
1353
|
+
mindent = indent;
|
|
1354
|
+
} else {
|
|
1355
|
+
mindent = Math.min(mindent, indent);
|
|
659
1356
|
}
|
|
660
|
-
subset[c] = arr;
|
|
661
|
-
partial[c] = Array.isArray(arr) && Array.isArray(full) ? arr.length < full.length : false;
|
|
662
1357
|
}
|
|
663
|
-
const subsetJson = JSON.stringify(subset);
|
|
664
|
-
const partialJson = JSON.stringify(partial);
|
|
665
|
-
hydrationScript += `
|
|
666
|
-
<script>
|
|
667
|
-
(function(){ try {
|
|
668
|
-
var w = (typeof window!== 'undefined'? window : globalThis);
|
|
669
|
-
w.__REROUTE_COLLECTIONS__ = w.__REROUTE_COLLECTIONS__ || {};
|
|
670
|
-
Object.assign(w.__REROUTE_COLLECTIONS__, ${subsetJson});
|
|
671
|
-
w.__REROUTE_COLLECTIONS_PARTIAL__ = w.__REROUTE_COLLECTIONS_PARTIAL__ || {};
|
|
672
|
-
Object.assign(w.__REROUTE_COLLECTIONS_PARTIAL__, ${partialJson});
|
|
673
|
-
} catch(e){} })();
|
|
674
|
-
</script>`;
|
|
675
1358
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
hydrationScript += `<script type="module" src="/__reroute_watch.js"></script>`;
|
|
1359
|
+
if (mindent !== null) {
|
|
1360
|
+
const m = mindent;
|
|
1361
|
+
result = lines.map((l) => l[0] === " " || l[0] === "\t" ? l.slice(m) : l).join(`
|
|
1362
|
+
`);
|
|
1363
|
+
}
|
|
1364
|
+
if (trimWhitespace) {
|
|
1365
|
+
result = result.trim();
|
|
1366
|
+
}
|
|
1367
|
+
if (escapeSpecialCharacters) {
|
|
1368
|
+
result = result.replace(/\\n/g, `
|
|
1369
|
+
`);
|
|
1370
|
+
}
|
|
1371
|
+
return result;
|
|
690
1372
|
}
|
|
691
|
-
|
|
692
|
-
|
|
1373
|
+
}
|
|
1374
|
+
function alignValue(value, precedingText) {
|
|
1375
|
+
if (typeof value !== "string" || !value.includes(`
|
|
1376
|
+
`)) {
|
|
1377
|
+
return value;
|
|
1378
|
+
}
|
|
1379
|
+
const currentLine = precedingText.slice(precedingText.lastIndexOf(`
|
|
1380
|
+
`) + 1);
|
|
1381
|
+
const indentMatch = currentLine.match(/^(\s+)/);
|
|
1382
|
+
if (indentMatch) {
|
|
1383
|
+
const indent = indentMatch[1];
|
|
1384
|
+
return value.replace(/\n/g, `
|
|
1385
|
+
${indent}`);
|
|
1386
|
+
}
|
|
1387
|
+
return value;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// packages/core/src/ssr/render.ts
|
|
1391
|
+
import { cloneElement } from "react";
|
|
1392
|
+
import { renderToString } from "react-dom/server";
|
|
1393
|
+
async function renderSSRDocument(options) {
|
|
1394
|
+
const {
|
|
1395
|
+
pathname,
|
|
1396
|
+
rootComponent,
|
|
1397
|
+
clientDir,
|
|
1398
|
+
cwd,
|
|
1399
|
+
isWatchMode,
|
|
1400
|
+
bundleUrl,
|
|
1401
|
+
head = "",
|
|
1402
|
+
lang = "en",
|
|
1403
|
+
appId = "root",
|
|
1404
|
+
minify = false,
|
|
1405
|
+
maxAge = 0
|
|
1406
|
+
} = options;
|
|
1407
|
+
const scripts2 = [bundleUrl];
|
|
1408
|
+
resetSSRAccessTracking();
|
|
1409
|
+
const bundlePreload = createBundlePreload(bundleUrl);
|
|
1410
|
+
const preloadResult = await preloadContentModule(pathname, clientDir, cwd, isWatchMode);
|
|
1411
|
+
const extraHead = bundlePreload + preloadResult.extraHead;
|
|
1412
|
+
let hydrationScript = preloadResult.hydrationScript;
|
|
1413
|
+
let statusOverride = preloadResult.statusOverride;
|
|
1414
|
+
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
1415
|
+
const { data: __SSR_DATA__, error: __SSR_DATA_ERROR__ } = await computeSSRData(pathname, clientDir, cwd, isWatchMode, { maxAge });
|
|
1416
|
+
setGlobalSSRData(__SSR_DATA__);
|
|
1417
|
+
const __byCollectionForSSR = await loadCollections(cwd, isWatchMode);
|
|
1418
|
+
setGlobalCollections(__byCollectionForSSR);
|
|
1419
|
+
const componentWithPathname = cloneElement(rootComponent, {
|
|
1420
|
+
pathname
|
|
1421
|
+
});
|
|
1422
|
+
const appHtml = renderToString(componentWithPathname);
|
|
1423
|
+
const baseTemplate = await loadIndexHtml(clientDir);
|
|
1424
|
+
const inlineStyleTag = await inlineTailwindCSS(clientDir, minify);
|
|
693
1425
|
try {
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
const ssr = exp?.ssr;
|
|
701
|
-
perPageHead += buildHeadFromMeta(meta);
|
|
702
|
-
const ssrHead = typeof ssr?.head === "string" ? ssr.head : Array.isArray(ssr?.head) ? String(ssr.head.join(`
|
|
703
|
-
`)) : "";
|
|
704
|
-
if (ssrHead)
|
|
705
|
-
perPageHead += `
|
|
706
|
-
${ssrHead}`;
|
|
707
|
-
if (typeof ssr?.lang === "string" && ssr.lang.trim()) {
|
|
708
|
-
pageLang = ssr.lang.trim();
|
|
1426
|
+
const g = globalThis;
|
|
1427
|
+
const acc = g.__REROUTE_SSR_ACCESSED__;
|
|
1428
|
+
if (acc) {
|
|
1429
|
+
const { subset, partial } = processCollections(acc, __byCollectionForSSR);
|
|
1430
|
+
if (Object.keys(subset).length > 0) {
|
|
1431
|
+
hydrationScript += createCollectionScript(subset, partial);
|
|
709
1432
|
}
|
|
710
1433
|
}
|
|
711
1434
|
} catch {}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
try {
|
|
722
|
-
const abs = join(clientDir, "routes", String(r.path));
|
|
723
|
-
const mod = await import(pathToFileURL4(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
724
|
-
const meta = mod?.meta;
|
|
725
|
-
const ssr = mod?.ssr;
|
|
726
|
-
if (meta)
|
|
727
|
-
perPageHead += buildHeadFromMeta(meta);
|
|
728
|
-
if (ssr) {
|
|
729
|
-
const ssrHead = typeof ssr.head === "string" ? ssr.head : Array.isArray(ssr.head) ? String(ssr.head.join(`
|
|
730
|
-
`)) : "";
|
|
731
|
-
if (ssrHead)
|
|
732
|
-
perPageHead += `
|
|
733
|
-
${ssrHead}`;
|
|
734
|
-
if (typeof ssr.lang === "string" && ssr.lang.trim()) {
|
|
735
|
-
pageLang = ssr.lang.trim();
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
} catch {}
|
|
739
|
-
} else {
|
|
740
|
-
try {
|
|
741
|
-
const list = m?.notFoundRoutes;
|
|
742
|
-
if (Array.isArray(list)) {
|
|
743
|
-
let chosen;
|
|
744
|
-
let bestLen = -1;
|
|
745
|
-
for (const nf of list) {
|
|
746
|
-
const pat = String(nf?.pattern || "/");
|
|
747
|
-
if (!pathname.startsWith(pat))
|
|
748
|
-
continue;
|
|
749
|
-
const len = pat.split("/").filter(Boolean).length;
|
|
750
|
-
if (len >= bestLen) {
|
|
751
|
-
bestLen = len;
|
|
752
|
-
chosen = nf;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
if (chosen && typeof chosen.path === "string") {
|
|
756
|
-
try {
|
|
757
|
-
const abs = join(clientDir, "routes", String(chosen.path));
|
|
758
|
-
const mod = await import(pathToFileURL4(abs).href + (isWatchMode ? `?t=${Date.now()}` : ""));
|
|
759
|
-
const meta = mod?.meta;
|
|
760
|
-
const ssr = mod?.ssr;
|
|
761
|
-
if (meta)
|
|
762
|
-
perPageHead += buildHeadFromMeta(meta);
|
|
763
|
-
const ssrHead = typeof ssr?.head === "string" ? ssr.head : Array.isArray(ssr?.head) ? String(ssr.head.join(`
|
|
764
|
-
`)) : "";
|
|
765
|
-
if (ssrHead)
|
|
766
|
-
perPageHead += `
|
|
767
|
-
${ssrHead}`;
|
|
768
|
-
if (typeof ssr?.lang === "string" && ssr.lang?.trim()) {
|
|
769
|
-
pageLang = String(ssr.lang).trim();
|
|
770
|
-
}
|
|
771
|
-
} catch {}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
} catch {}
|
|
775
|
-
}
|
|
776
|
-
} catch {}
|
|
1435
|
+
hydrationScript += createDataScript(__SSR_DATA__, isWatchMode ? __SSR_DATA_ERROR__ : undefined);
|
|
1436
|
+
hydrationScript += scripts2.map((src) => `<script type="module" src="${src}"></script>`).join("");
|
|
1437
|
+
if (isWatchMode) {
|
|
1438
|
+
hydrationScript += `<script type="module" src="/__reroute_watch.js"></script>`;
|
|
1439
|
+
}
|
|
1440
|
+
const metadataResult = await extractPageMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride);
|
|
1441
|
+
const perPageHead = metadataResult.perPageHead;
|
|
1442
|
+
const pageLang = metadataResult.pageLang;
|
|
1443
|
+
statusOverride = metadataResult.statusOverride;
|
|
777
1444
|
const combinedHead = dedent_default([dedent_default(head) || "", dedent_default(extraHead), dedent_default(perPageHead)].filter(Boolean).join(`
|
|
778
1445
|
`));
|
|
779
1446
|
const html2 = applyIndexTemplate(baseTemplate, appHtml, {
|
|
@@ -788,129 +1455,160 @@ ${ssrHead}`;
|
|
|
788
1455
|
status: statusOverride || 200
|
|
789
1456
|
};
|
|
790
1457
|
}
|
|
791
|
-
// packages/core/src/
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
function gzipIfAccepted(body, contentType, acceptEncoding) {
|
|
870
|
-
const extraHeaders = {};
|
|
871
|
-
if (isCompressible(contentType)) {
|
|
872
|
-
if (acceptsBrotli(acceptEncoding)) {
|
|
873
|
-
try {
|
|
874
|
-
const compressed = brotliCompressSync(toBytes(body));
|
|
875
|
-
extraHeaders["Content-Encoding"] = "br";
|
|
876
|
-
extraHeaders.Vary = "Accept-Encoding";
|
|
877
|
-
return { body: compressed, extraHeaders };
|
|
878
|
-
} catch {}
|
|
879
|
-
}
|
|
880
|
-
if (acceptsGzip(acceptEncoding)) {
|
|
1458
|
+
// packages/core/src/ssr/stream.ts
|
|
1459
|
+
import { cloneElement as cloneElement2 } from "react";
|
|
1460
|
+
import { renderToReadableStream } from "react-dom/server";
|
|
1461
|
+
var isThenable2 = (value) => {
|
|
1462
|
+
return typeof value === "object" && value !== null && typeof value.then === "function";
|
|
1463
|
+
};
|
|
1464
|
+
async function renderSSRDocumentStream(options) {
|
|
1465
|
+
const {
|
|
1466
|
+
pathname,
|
|
1467
|
+
rootComponent,
|
|
1468
|
+
clientDir,
|
|
1469
|
+
cwd,
|
|
1470
|
+
isWatchMode,
|
|
1471
|
+
bundleUrl,
|
|
1472
|
+
head = "",
|
|
1473
|
+
lang = "en",
|
|
1474
|
+
appId = "root",
|
|
1475
|
+
maxAge = 0
|
|
1476
|
+
} = options;
|
|
1477
|
+
await seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
1478
|
+
const { data: __SSR_DATA__, error: __SSR_DATA_ERROR__ } = await computeSSRData(pathname, clientDir, cwd, isWatchMode, {
|
|
1479
|
+
streaming: true,
|
|
1480
|
+
maxAge
|
|
1481
|
+
});
|
|
1482
|
+
setGlobalSSRData(__SSR_DATA__);
|
|
1483
|
+
const __byCollectionForSSR = await loadCollections(cwd, isWatchMode);
|
|
1484
|
+
setGlobalCollections(__byCollectionForSSR);
|
|
1485
|
+
resetSSRAccessTracking();
|
|
1486
|
+
const bundlePreload = createBundlePreload(bundleUrl);
|
|
1487
|
+
const preloadResult = await preloadContentModule(pathname, clientDir, cwd, isWatchMode);
|
|
1488
|
+
let extraHead = bundlePreload + preloadResult.extraHead;
|
|
1489
|
+
const hydrationScript = preloadResult.hydrationScript;
|
|
1490
|
+
let statusOverride = preloadResult.statusOverride;
|
|
1491
|
+
const encoder = new TextEncoder;
|
|
1492
|
+
const { readable, writable } = new TransformStream;
|
|
1493
|
+
const writer = writable.getWriter();
|
|
1494
|
+
const baseTemplate = await loadIndexHtml(clientDir);
|
|
1495
|
+
const { htmlHead, rootStart, rootEnd, htmlTail } = splitTemplate(baseTemplate, appId);
|
|
1496
|
+
const headWithLang = htmlHead.replace(/<html([^>]*)>/i, `<html$1 lang="${lang}">`);
|
|
1497
|
+
const inlineStyleTag = await inlineTailwindCSS(clientDir, false);
|
|
1498
|
+
const metadataResult = await extractPageMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride);
|
|
1499
|
+
extraHead += metadataResult.perPageHead;
|
|
1500
|
+
const pageLang = metadataResult.pageLang;
|
|
1501
|
+
statusOverride = metadataResult.statusOverride;
|
|
1502
|
+
(async () => {
|
|
1503
|
+
try {
|
|
1504
|
+
const combinedHead = [inlineStyleTag, head, extraHead].filter(Boolean).join(`
|
|
1505
|
+
`);
|
|
1506
|
+
const headContent = `${headWithLang.replace(/<html([^>]*)>/i, `<html$1 lang="${pageLang || lang}">`)}${combinedHead ? `
|
|
1507
|
+
${combinedHead}` : ""}</head><body>`;
|
|
1508
|
+
await writer.write(encoder.encode(headContent));
|
|
1509
|
+
await writer.write(encoder.encode(rootStart));
|
|
1510
|
+
const componentWithPathname = cloneElement2(rootComponent, {
|
|
1511
|
+
pathname
|
|
1512
|
+
});
|
|
1513
|
+
let streamError = null;
|
|
1514
|
+
const streamPromise = renderToReadableStream(componentWithPathname, {
|
|
1515
|
+
onError(error) {
|
|
1516
|
+
console.error("[reroute] SSR stream error:", error);
|
|
1517
|
+
streamError = error instanceof Error ? error : new Error(String(error));
|
|
1518
|
+
}
|
|
1519
|
+
}).catch((err) => {
|
|
1520
|
+
streamError = err;
|
|
1521
|
+
throw err;
|
|
1522
|
+
});
|
|
1523
|
+
const reactStream = await streamPromise;
|
|
1524
|
+
if (streamError) {
|
|
1525
|
+
throw streamError;
|
|
1526
|
+
}
|
|
1527
|
+
const reader = reactStream.getReader();
|
|
1528
|
+
while (true) {
|
|
1529
|
+
const { done, value } = await reader.read();
|
|
1530
|
+
if (done)
|
|
1531
|
+
break;
|
|
1532
|
+
await writer.write(value);
|
|
1533
|
+
}
|
|
1534
|
+
await writer.write(encoder.encode(rootEnd));
|
|
1535
|
+
let collectionScripts = "";
|
|
881
1536
|
try {
|
|
882
|
-
const
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1537
|
+
const g = globalThis;
|
|
1538
|
+
const acc = g.__REROUTE_SSR_ACCESSED__;
|
|
1539
|
+
if (acc) {
|
|
1540
|
+
const { subset, partial } = processCollections(acc, __byCollectionForSSR);
|
|
1541
|
+
if (Object.keys(subset).length > 0) {
|
|
1542
|
+
collectionScripts = createCollectionScript(subset, partial);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
886
1545
|
} catch {}
|
|
1546
|
+
const ssrDataSnapshot = {};
|
|
1547
|
+
for (const key of Object.keys(__SSR_DATA__)) {
|
|
1548
|
+
const value = __SSR_DATA__[key];
|
|
1549
|
+
if (!isThenable2(value)) {
|
|
1550
|
+
ssrDataSnapshot[key] = value;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
const dataScript = createDataScript(ssrDataSnapshot, isWatchMode ? __SSR_DATA_ERROR__ : undefined);
|
|
1554
|
+
let allScripts = dataScript + collectionScripts + hydrationScript;
|
|
1555
|
+
allScripts += `<script type="module" src="${bundleUrl}"></script>`;
|
|
1556
|
+
if (isWatchMode) {
|
|
1557
|
+
allScripts += `<script type="module" src="/__reroute_watch.js"></script>`;
|
|
1558
|
+
}
|
|
1559
|
+
await writer.write(encoder.encode(allScripts + htmlTail));
|
|
1560
|
+
await writer.close();
|
|
1561
|
+
} catch (error) {
|
|
1562
|
+
console.error("[reroute] Stream error:", error);
|
|
1563
|
+
await writer.abort(error);
|
|
887
1564
|
}
|
|
888
|
-
}
|
|
889
|
-
return {
|
|
1565
|
+
})();
|
|
1566
|
+
return { stream: readable, status: statusOverride || 200 };
|
|
890
1567
|
}
|
|
891
1568
|
export {
|
|
892
1569
|
toBytes,
|
|
893
1570
|
stripStart,
|
|
894
1571
|
stripEnd,
|
|
1572
|
+
ssrCache,
|
|
1573
|
+
splitTemplate,
|
|
1574
|
+
setGlobalSSRData,
|
|
1575
|
+
setGlobalCollections,
|
|
895
1576
|
seedSSRModuleForPath,
|
|
1577
|
+
resetSSRAccessTracking,
|
|
1578
|
+
renderSSRDocumentStream,
|
|
896
1579
|
renderSSRDocument,
|
|
1580
|
+
processCollections,
|
|
1581
|
+
preloadContentModule,
|
|
897
1582
|
loadIndexHtml,
|
|
1583
|
+
loadConfig,
|
|
1584
|
+
loadCollections,
|
|
898
1585
|
listContentFiles,
|
|
899
1586
|
join,
|
|
900
1587
|
isCompressible,
|
|
1588
|
+
inlineTailwindCSS,
|
|
1589
|
+
importContentModuleForPath,
|
|
901
1590
|
gzipIfAccepted,
|
|
902
1591
|
getMimeType,
|
|
903
1592
|
getContentMeta,
|
|
904
1593
|
generateContentHash,
|
|
1594
|
+
extractPageMetadata,
|
|
905
1595
|
extname,
|
|
1596
|
+
escapeJsonForScript,
|
|
906
1597
|
discoverCollections,
|
|
1598
|
+
defineConfig,
|
|
1599
|
+
createDataScript,
|
|
1600
|
+
createCollectionScript,
|
|
1601
|
+
createBundlePreload,
|
|
907
1602
|
computeSSRDataForPath,
|
|
1603
|
+
computeSSRData,
|
|
1604
|
+
compressStreamIfAccepted,
|
|
908
1605
|
buildHeadFromMeta,
|
|
909
1606
|
basename,
|
|
910
1607
|
applyIndexTemplate,
|
|
911
1608
|
acceptsGzip,
|
|
912
1609
|
acceptsBrotli,
|
|
1610
|
+
SSRDataCache,
|
|
913
1611
|
LRUCache
|
|
914
1612
|
};
|
|
915
1613
|
|
|
916
|
-
//# debugId=
|
|
1614
|
+
//# debugId=393BB814878CEFAA64756E2164756E21
|