reroute-js 0.45.12-beta.1 → 0.46.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/README.md +100 -2
- package/_/blog/src/client/routes/blog/[layout].tsx +45 -13
- package/_/blog/src/client/routes/blog/content/pt-BR/1-hello-world.tsx +27 -0
- package/_/blog/src/client/routes/index.tsx +4 -4
- package/_/search/src/client/routes/docs/content/content-collections.md +18 -0
- package/cli/bin.d.ts +2 -2
- package/cli/bin.js +980 -337
- package/cli/bin.js.map +29 -21
- package/cli/index.d.ts +2 -2
- package/cli/index.js +5 -5
- package/cli/index.js.map +1 -1
- package/cli/src/cli.d.ts +2 -2
- package/cli/src/commands/analyze.d.ts +2 -2
- package/cli/src/commands/build.d.ts +2 -2
- package/cli/src/commands/dev.d.ts +2 -2
- package/cli/src/commands/gen.d.ts +2 -2
- package/cli/src/commands/gen.d.ts.map +1 -1
- package/cli/src/commands/index.d.ts +2 -2
- package/cli/src/commands/init.d.ts +2 -2
- package/cli/src/commands/lib/assets.d.ts +2 -2
- package/cli/src/commands/lib/bundler.d.ts +2 -2
- package/cli/src/commands/lib/command.d.ts +2 -2
- package/cli/src/commands/lib/env.d.ts +2 -2
- package/cli/src/commands/lib/index.d.ts +2 -2
- package/cli/src/commands/lib/log.d.ts +2 -2
- package/cli/src/commands/lib/markdown/availability.d.ts +2 -2
- package/cli/src/commands/lib/markdown/index.d.ts +2 -2
- package/cli/src/commands/lib/markdown/processor.d.ts +2 -2
- package/cli/src/commands/lib/production.d.ts +2 -2
- package/cli/src/commands/lib/server.d.ts +2 -2
- package/cli/src/commands/lib/streaming/analyzer.d.ts +2 -2
- package/cli/src/commands/lib/streaming/suspense.d.ts +2 -2
- package/cli/src/commands/lib/tailwind.d.ts +2 -2
- package/cli/src/commands/lib/terminal-ui.d.ts +2 -2
- package/cli/src/commands/lib/version.d.ts +2 -2
- package/cli/src/commands/og.d.ts +2 -2
- package/cli/src/commands/start.d.ts +2 -2
- package/cli/src/index.d.ts +2 -2
- package/core/index.d.ts +2 -2
- package/core/index.js +667 -117
- package/core/index.js.map +28 -20
- package/core/src/bundler/hash.d.ts +2 -2
- package/core/src/bundler/index.d.ts +2 -2
- package/core/src/config.d.ts +6 -3
- package/core/src/config.d.ts.map +1 -1
- package/core/src/content/discovery.d.ts +2 -2
- package/core/src/content/index.d.ts +2 -2
- package/core/src/content/metadata.d.ts +2 -2
- package/core/src/i18n/catalog.d.ts +17 -0
- package/core/src/i18n/catalog.d.ts.map +1 -0
- package/core/src/i18n/discover.d.ts +19 -0
- package/core/src/i18n/discover.d.ts.map +1 -0
- package/core/src/i18n/index.d.ts +17 -0
- package/core/src/i18n/index.d.ts.map +1 -0
- package/core/src/i18n/messages.d.ts +18 -0
- package/core/src/i18n/messages.d.ts.map +1 -0
- package/core/src/i18n/negotiate.d.ts +18 -0
- package/core/src/i18n/negotiate.d.ts.map +1 -0
- package/core/src/i18n/pathname.d.ts +34 -0
- package/core/src/i18n/pathname.d.ts.map +1 -0
- package/core/src/i18n/resolve.d.ts +22 -0
- package/core/src/i18n/resolve.d.ts.map +1 -0
- package/core/src/i18n/types.d.ts +68 -0
- package/core/src/i18n/types.d.ts.map +1 -0
- package/core/src/index.d.ts +3 -2
- package/core/src/index.d.ts.map +1 -1
- package/core/src/llms/extractor.d.ts +2 -2
- package/core/src/llms/formatter.d.ts +2 -2
- package/core/src/llms/full-generator.d.ts +2 -2
- package/core/src/llms/index-generator.d.ts +2 -2
- package/core/src/llms/index.d.ts +2 -2
- package/core/src/og/discovery.d.ts +2 -2
- package/core/src/og/index.d.ts +2 -2
- package/core/src/og/meta.d.ts +2 -2
- package/core/src/og/render.d.ts +2 -2
- package/core/src/og/types.d.ts +2 -2
- package/core/src/robots/discovery.d.ts +2 -2
- package/core/src/robots/generator.d.ts +2 -2
- package/core/src/robots/index.d.ts +2 -2
- package/core/src/robots/policies.d.ts +2 -2
- package/core/src/rss/discovery.d.ts +4 -2
- package/core/src/rss/discovery.d.ts.map +1 -1
- package/core/src/rss/generator.d.ts +2 -2
- package/core/src/rss/index.d.ts +2 -2
- package/core/src/sitemap/discovery.d.ts +7 -2
- package/core/src/sitemap/discovery.d.ts.map +1 -1
- package/core/src/sitemap/generator.d.ts +2 -2
- package/core/src/sitemap/generator.d.ts.map +1 -1
- package/core/src/sitemap/index.d.ts +2 -2
- package/core/src/ssr/index.d.ts +2 -2
- package/core/src/ssr/lib/cache.d.ts +2 -2
- package/core/src/ssr/lib/cleanup.d.ts +2 -2
- package/core/src/ssr/lib/collections.d.ts +2 -2
- package/core/src/ssr/lib/compression.d.ts +2 -2
- package/core/src/ssr/lib/compute/content.d.ts +2 -9
- package/core/src/ssr/lib/compute/content.d.ts.map +1 -1
- package/core/src/ssr/lib/compute/index.d.ts +2 -2
- package/core/src/ssr/lib/compute/layouts.d.ts +2 -2
- package/core/src/ssr/lib/compute/routes.d.ts +2 -2
- package/core/src/ssr/lib/compute/routes.d.ts.map +1 -1
- package/core/src/ssr/lib/content-path.d.ts +22 -0
- package/core/src/ssr/lib/content-path.d.ts.map +1 -0
- package/core/src/ssr/lib/data.d.ts +2 -2
- package/core/src/ssr/lib/data.d.ts.map +1 -1
- package/core/src/ssr/lib/html.d.ts +2 -2
- package/core/src/ssr/lib/imports.d.ts +2 -2
- package/core/src/ssr/lib/index.d.ts +2 -2
- package/core/src/ssr/lib/layouts.d.ts +2 -2
- package/core/src/ssr/lib/metadata.d.ts +2 -2
- package/core/src/ssr/lib/metadata.d.ts.map +1 -1
- package/core/src/ssr/lib/mime.d.ts +2 -2
- package/core/src/ssr/lib/modules.d.ts +4 -4
- package/core/src/ssr/lib/modules.d.ts.map +1 -1
- package/core/src/ssr/lib/path.d.ts +2 -2
- package/core/src/ssr/lib/preload.d.ts +2 -2
- package/core/src/ssr/lib/preload.d.ts.map +1 -1
- package/core/src/ssr/lib/route-match.d.ts +2 -2
- package/core/src/ssr/lib/scripts/collections.d.ts +2 -2
- package/core/src/ssr/lib/scripts/data.d.ts +3 -3
- package/core/src/ssr/lib/scripts/data.d.ts.map +1 -1
- package/core/src/ssr/lib/scripts/escape.d.ts +2 -2
- package/core/src/ssr/lib/scripts/feeds.d.ts +2 -2
- package/core/src/ssr/lib/scripts/index.d.ts +2 -2
- package/core/src/ssr/lib/seed.d.ts +2 -2
- package/core/src/ssr/lib/seed.d.ts.map +1 -1
- package/core/src/ssr/lib/serialize.d.ts +2 -2
- package/core/src/ssr/lib/setup.d.ts +3 -3
- package/core/src/ssr/lib/setup.d.ts.map +1 -1
- package/core/src/ssr/lib/styles.d.ts +2 -2
- package/core/src/ssr/lib/template.d.ts +2 -2
- package/core/src/ssr/lib/types.d.ts +2 -2
- package/core/src/ssr/render.d.ts +4 -2
- package/core/src/ssr/render.d.ts.map +1 -1
- package/core/src/ssr/stream.d.ts +4 -2
- package/core/src/ssr/stream.d.ts.map +1 -1
- package/elysia/index.d.ts +2 -2
- package/elysia/index.js +872 -177
- package/elysia/index.js.map +32 -23
- package/elysia/src/index.d.ts +2 -2
- package/elysia/src/libs/assets.d.ts +2 -2
- package/elysia/src/libs/cache.d.ts +2 -2
- package/elysia/src/libs/caching.d.ts +2 -2
- package/elysia/src/libs/http.d.ts +2 -2
- package/elysia/src/libs/i18n.d.ts +26 -0
- package/elysia/src/libs/i18n.d.ts.map +1 -0
- package/elysia/src/libs/image.d.ts +2 -2
- package/elysia/src/libs/index.d.ts +2 -2
- package/elysia/src/libs/llms.d.ts +2 -2
- package/elysia/src/libs/response.d.ts +2 -2
- package/elysia/src/libs/serving.d.ts +2 -2
- package/elysia/src/plugin.d.ts +2 -2
- package/elysia/src/routes/artifacts.d.ts +2 -2
- package/elysia/src/routes/content.d.ts +2 -2
- package/elysia/src/routes/image.d.ts +2 -2
- package/elysia/src/routes/index.d.ts +2 -2
- package/elysia/src/routes/internal.d.ts +2 -2
- package/elysia/src/routes/llms.d.ts +2 -2
- package/elysia/src/routes/og.d.ts +2 -2
- package/elysia/src/routes/redirects.d.ts +2 -2
- package/elysia/src/routes/robots.d.ts +2 -2
- package/elysia/src/routes/rss.d.ts +2 -2
- package/elysia/src/routes/rss.d.ts.map +1 -1
- package/elysia/src/routes/search.d.ts +2 -2
- package/elysia/src/routes/search.d.ts.map +1 -1
- package/elysia/src/routes/sitemap.d.ts +2 -2
- package/elysia/src/routes/ssr.d.ts +2 -2
- package/elysia/src/routes/ssr.d.ts.map +1 -1
- package/elysia/src/routes/static.d.ts +2 -2
- package/elysia/src/types.d.ts +2 -2
- package/package.json +7 -6
- package/react/index.d.ts +2 -2
- package/react/index.js +560 -170
- package/react/index.js.map +12 -10
- package/react/src/components/ClientOnly.d.ts +2 -2
- package/react/src/components/ContentRoute.d.ts +3 -3
- package/react/src/components/ContentRoute.d.ts.map +1 -1
- package/react/src/components/Image.d.ts +2 -2
- package/react/src/components/LazyRoute.d.ts +2 -2
- package/react/src/components/Link.d.ts +2 -2
- package/react/src/components/Link.d.ts.map +1 -1
- package/react/src/components/Markdown.d.ts +2 -2
- package/react/src/components/Outlet.d.ts +2 -2
- package/react/src/components/index.d.ts +2 -2
- package/react/src/hooks/index.d.ts +2 -2
- package/react/src/hooks/useContent.d.ts +2 -2
- package/react/src/hooks/useContent.d.ts.map +1 -1
- package/react/src/hooks/useData.d.ts +2 -2
- package/react/src/hooks/useFeed.d.ts +2 -2
- package/react/src/hooks/useLayoutData.d.ts +2 -2
- package/react/src/hooks/useLlms.d.ts +2 -2
- package/react/src/hooks/useNavigate.d.ts +2 -2
- package/react/src/hooks/useParams.d.ts +2 -2
- package/react/src/hooks/useRouter.d.ts +2 -2
- package/react/src/hooks/useSearch.d.ts +2 -2
- package/react/src/hooks/useSearch.d.ts.map +1 -1
- package/react/src/hooks/useSearchParams.d.ts +2 -2
- package/react/src/hooks/useToc.d.ts +2 -2
- package/react/src/i18n/LocaleProvider.d.ts +49 -0
- package/react/src/i18n/LocaleProvider.d.ts.map +1 -0
- package/react/src/i18n/helpers.d.ts +24 -0
- package/react/src/i18n/helpers.d.ts.map +1 -0
- package/react/src/i18n/index.d.ts +13 -0
- package/react/src/i18n/index.d.ts.map +1 -0
- package/react/src/index.d.ts +3 -2
- package/react/src/index.d.ts.map +1 -1
- package/react/src/lib/collection.d.ts +2 -2
- package/react/src/lib/content.d.ts +4 -3
- package/react/src/lib/content.d.ts.map +1 -1
- package/react/src/lib/head.d.ts +2 -2
- package/react/src/lib/index.d.ts +2 -2
- package/react/src/lib/lazy-route.d.ts +2 -2
- package/react/src/lib/route-loader.d.ts +2 -2
- package/react/src/providers/ContentProvider.d.ts +3 -3
- package/react/src/providers/ContentProvider.d.ts.map +1 -1
- package/react/src/providers/RerouteProvider.d.ts +8 -6
- package/react/src/providers/RerouteProvider.d.ts.map +1 -1
- package/react/src/providers/RouterProvider.d.ts +2 -2
- package/react/src/providers/index.d.ts +2 -2
- package/react/src/types/any.d.ts +2 -2
- package/react/src/types/index.d.ts +2 -2
- package/react/src/types/router.d.ts +2 -2
- package/telemetry/react.d.ts +2 -2
- package/telemetry/react.js +3 -3
- package/telemetry/react.js.map +1 -1
- package/telemetry/server.d.ts +2 -2
- package/telemetry/server.js +56 -4
- package/telemetry/server.js.map +20 -15
- package/telemetry/src/react/api.d.ts +2 -2
- package/telemetry/src/react/context.d.ts +2 -2
- package/telemetry/src/react/index.d.ts +2 -2
- package/telemetry/src/react/telemetry.d.ts +2 -2
- package/telemetry/src/server/context.d.ts +2 -2
- package/telemetry/src/server/headers/extractor.d.ts +2 -2
- package/telemetry/src/server/headers/index.d.ts +2 -2
- package/telemetry/src/server/headers/presets.d.ts +2 -2
- package/telemetry/src/server/index.d.ts +2 -2
- package/telemetry/src/server/instrumentation.d.ts +2 -2
- package/telemetry/src/server/plugin.d.ts +2 -2
- package/telemetry/src/server/sourcemap.d.ts +2 -2
package/elysia/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* reroute-js v0.
|
|
2
|
+
* reroute-js v0.46.0
|
|
3
3
|
*
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright 2026 stewones <hi@stewan.io>
|
|
6
|
-
* @see https://github.com/
|
|
6
|
+
* @see https://github.com/Intenseloop/reroute
|
|
7
7
|
*
|
|
8
8
|
* Built with Bun <3
|
|
9
9
|
*/
|
|
@@ -200,6 +200,364 @@ var init_content = __esm(() => {
|
|
|
200
200
|
init_metadata();
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
+
// packages/core/src/i18n/catalog.ts
|
|
204
|
+
function getMessage(catalog, key) {
|
|
205
|
+
if (!(catalog && key))
|
|
206
|
+
return;
|
|
207
|
+
const parts = key.split(".");
|
|
208
|
+
let current = catalog;
|
|
209
|
+
for (const part of parts) {
|
|
210
|
+
if (current == null || typeof current === "string")
|
|
211
|
+
return;
|
|
212
|
+
current = current[part];
|
|
213
|
+
}
|
|
214
|
+
return typeof current === "string" ? current : undefined;
|
|
215
|
+
}
|
|
216
|
+
function lookupMessage(catalogs, locale, defaultLocale, key) {
|
|
217
|
+
return getMessage(catalogs[locale], key) ?? (locale !== defaultLocale ? getMessage(catalogs[defaultLocale], key) : undefined);
|
|
218
|
+
}
|
|
219
|
+
var init_catalog = () => {};
|
|
220
|
+
|
|
221
|
+
// packages/core/src/i18n/discover.ts
|
|
222
|
+
import { readdir as readdir2 } from "node:fs/promises";
|
|
223
|
+
import { join as join2 } from "node:path";
|
|
224
|
+
async function loadCatalog(filePath) {
|
|
225
|
+
try {
|
|
226
|
+
const text = await Bun.file(filePath).text();
|
|
227
|
+
return JSON.parse(text);
|
|
228
|
+
} catch {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function loadLocaleCatalogs(absDir) {
|
|
233
|
+
const entries = await readdir2(absDir, { withFileTypes: true });
|
|
234
|
+
const catalogs = {};
|
|
235
|
+
const locales = [];
|
|
236
|
+
for (const entry of entries) {
|
|
237
|
+
if (!(entry.isFile() && entry.name.endsWith(".json")))
|
|
238
|
+
continue;
|
|
239
|
+
const locale = entry.name.slice(0, -".json".length);
|
|
240
|
+
if (!locale)
|
|
241
|
+
continue;
|
|
242
|
+
const catalog = await loadCatalog(join2(absDir, entry.name));
|
|
243
|
+
if (!catalog)
|
|
244
|
+
continue;
|
|
245
|
+
catalogs[locale] = catalog;
|
|
246
|
+
locales.push(locale);
|
|
247
|
+
}
|
|
248
|
+
return { catalogs, locales };
|
|
249
|
+
}
|
|
250
|
+
function sortLocales(locales, defaultLocale) {
|
|
251
|
+
locales.sort((a, b) => {
|
|
252
|
+
if (a === defaultLocale)
|
|
253
|
+
return -1;
|
|
254
|
+
if (b === defaultLocale)
|
|
255
|
+
return 1;
|
|
256
|
+
return a.localeCompare(b);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async function discoverI18n(cwd, options = {}) {
|
|
260
|
+
const localesDir = options.localesDir ?? DEFAULT_LOCALES_DIR;
|
|
261
|
+
const defaultLocale = options.defaultLocale ?? DEFAULT_LOCALE;
|
|
262
|
+
const cookie = options.cookie ?? DEFAULT_COOKIE;
|
|
263
|
+
const redirectContentOnNegotiate = options.redirectContentOnNegotiate ?? true;
|
|
264
|
+
const absDir = join2(cwd, localesDir);
|
|
265
|
+
const disabled = {
|
|
266
|
+
enabled: false,
|
|
267
|
+
defaultLocale,
|
|
268
|
+
locales: [],
|
|
269
|
+
localesDir,
|
|
270
|
+
cookie,
|
|
271
|
+
redirectContentOnNegotiate,
|
|
272
|
+
catalogs: {}
|
|
273
|
+
};
|
|
274
|
+
let catalogs;
|
|
275
|
+
let locales;
|
|
276
|
+
try {
|
|
277
|
+
({ catalogs, locales } = await loadLocaleCatalogs(absDir));
|
|
278
|
+
} catch {
|
|
279
|
+
return disabled;
|
|
280
|
+
}
|
|
281
|
+
if (locales.length === 0) {
|
|
282
|
+
return disabled;
|
|
283
|
+
}
|
|
284
|
+
sortLocales(locales, defaultLocale);
|
|
285
|
+
if (!catalogs[defaultLocale]) {
|
|
286
|
+
console.warn(`[reroute/i18n] defaultLocale "${defaultLocale}" has no catalog; using "${locales[0]}"`);
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
enabled: true,
|
|
290
|
+
defaultLocale: catalogs[defaultLocale] ? defaultLocale : locales[0],
|
|
291
|
+
locales,
|
|
292
|
+
localesDir,
|
|
293
|
+
cookie,
|
|
294
|
+
redirectContentOnNegotiate,
|
|
295
|
+
catalogs
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
var DEFAULT_LOCALE = "en-US", DEFAULT_LOCALES_DIR = "locales", DEFAULT_COOKIE = "reroute-locale";
|
|
299
|
+
var init_discover = () => {};
|
|
300
|
+
|
|
301
|
+
// packages/core/src/i18n/messages.ts
|
|
302
|
+
import { IntlMessageFormat } from "intl-messageformat";
|
|
303
|
+
function formatMessage(message, locale, values) {
|
|
304
|
+
if (!values || Object.keys(values).length === 0) {
|
|
305
|
+
if (!/\{[^}]+\}/.test(message))
|
|
306
|
+
return message;
|
|
307
|
+
}
|
|
308
|
+
const cacheKey = `${locale}\x00${message}`;
|
|
309
|
+
let formatter = formatterCache.get(cacheKey);
|
|
310
|
+
if (!formatter) {
|
|
311
|
+
formatter = new IntlMessageFormat(message, locale);
|
|
312
|
+
formatterCache.set(cacheKey, formatter);
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
const result = formatter.format(values);
|
|
316
|
+
return typeof result === "string" ? result : String(result);
|
|
317
|
+
} catch {
|
|
318
|
+
return message;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function createTranslator(catalogs, locale, defaultLocale) {
|
|
322
|
+
return (key, values) => {
|
|
323
|
+
const message = lookupMessage(catalogs, locale, defaultLocale, key);
|
|
324
|
+
if (message == null)
|
|
325
|
+
return key;
|
|
326
|
+
return formatMessage(message, locale, values);
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
var formatterCache;
|
|
330
|
+
var init_messages = __esm(() => {
|
|
331
|
+
init_catalog();
|
|
332
|
+
formatterCache = new Map;
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// packages/core/src/i18n/negotiate.ts
|
|
336
|
+
function negotiateLocale(acceptLanguage, availableLocales, defaultLocale) {
|
|
337
|
+
if (!availableLocales.length)
|
|
338
|
+
return defaultLocale;
|
|
339
|
+
if (!acceptLanguage)
|
|
340
|
+
return defaultLocale;
|
|
341
|
+
const availableLower = new Map(availableLocales.map((l) => [l.toLowerCase(), l]));
|
|
342
|
+
const byLang = new Map;
|
|
343
|
+
for (const locale of availableLocales) {
|
|
344
|
+
const lang = locale.split("-")[0].toLowerCase();
|
|
345
|
+
if (!byLang.has(lang))
|
|
346
|
+
byLang.set(lang, locale);
|
|
347
|
+
}
|
|
348
|
+
const ranges = acceptLanguage.split(",").map((part) => {
|
|
349
|
+
const [tag, ...params] = part.trim().split(";");
|
|
350
|
+
let q = 1;
|
|
351
|
+
for (const p of params) {
|
|
352
|
+
const [k, v] = p.trim().split("=");
|
|
353
|
+
if (k === "q" && v)
|
|
354
|
+
q = Number.parseFloat(v) || 0;
|
|
355
|
+
}
|
|
356
|
+
return { tag: tag.trim().toLowerCase(), q };
|
|
357
|
+
}).filter((r) => r.tag && r.q > 0).sort((a, b) => b.q - a.q);
|
|
358
|
+
for (const { tag } of ranges) {
|
|
359
|
+
if (tag === "*")
|
|
360
|
+
return defaultLocale;
|
|
361
|
+
const exact = availableLower.get(tag);
|
|
362
|
+
if (exact)
|
|
363
|
+
return exact;
|
|
364
|
+
const langOnly = byLang.get(tag.split("-")[0]);
|
|
365
|
+
if (langOnly)
|
|
366
|
+
return langOnly;
|
|
367
|
+
}
|
|
368
|
+
return defaultLocale;
|
|
369
|
+
}
|
|
370
|
+
function parseCookieValue(cookieHeader, name) {
|
|
371
|
+
if (!cookieHeader)
|
|
372
|
+
return;
|
|
373
|
+
const parts = cookieHeader.split(";");
|
|
374
|
+
for (const part of parts) {
|
|
375
|
+
const idx = part.indexOf("=");
|
|
376
|
+
if (idx === -1)
|
|
377
|
+
continue;
|
|
378
|
+
const key = part.slice(0, idx).trim();
|
|
379
|
+
if (key === name) {
|
|
380
|
+
return decodeURIComponent(part.slice(idx + 1).trim());
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
function buildLocaleCookie(name, locale, maxAgeSeconds = 60 * 60 * 24 * 365) {
|
|
386
|
+
return `${name}=${encodeURIComponent(locale)}; Path=/; Max-Age=${maxAgeSeconds}; SameSite=Lax`;
|
|
387
|
+
}
|
|
388
|
+
var init_negotiate = () => {};
|
|
389
|
+
|
|
390
|
+
// packages/core/src/i18n/pathname.ts
|
|
391
|
+
var exports_pathname = {};
|
|
392
|
+
__export(exports_pathname, {
|
|
393
|
+
toOgLocale: () => toOgLocale,
|
|
394
|
+
parseContentPath: () => parseContentPath,
|
|
395
|
+
normalizePathname: () => normalizePathname,
|
|
396
|
+
looksLikeLocale: () => looksLikeLocale,
|
|
397
|
+
localizeHref: () => localizeHref,
|
|
398
|
+
localizeContentHref: () => localizeContentHref,
|
|
399
|
+
hreflangTags: () => hreflangTags,
|
|
400
|
+
contentKey: () => contentKey
|
|
401
|
+
});
|
|
402
|
+
function looksLikeLocale(segment) {
|
|
403
|
+
return /^[a-z]{2}(?:-[A-Za-z0-9]{2,8})*$/i.test(segment);
|
|
404
|
+
}
|
|
405
|
+
function normalizePathname(pathname) {
|
|
406
|
+
const pathOnly = pathname.split("?")[0].split("#")[0];
|
|
407
|
+
if (!pathOnly || pathOnly === "/")
|
|
408
|
+
return "/";
|
|
409
|
+
return pathOnly.startsWith("/") ? pathOnly : `/${pathOnly}`;
|
|
410
|
+
}
|
|
411
|
+
function parseContentPath(pathname, availableLocales = [], collections) {
|
|
412
|
+
const normalized = normalizePathname(pathname);
|
|
413
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
414
|
+
if (parts.length < 2)
|
|
415
|
+
return;
|
|
416
|
+
const [collection, second, third] = parts;
|
|
417
|
+
if (collections && !collections.includes(collection)) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const localeSet = new Set(availableLocales.map((l) => l.toLowerCase()));
|
|
421
|
+
if (parts.length >= 3 && third) {
|
|
422
|
+
const secondIsLocale = localeSet.size > 0 ? localeSet.has(second.toLowerCase()) : looksLikeLocale(second);
|
|
423
|
+
if (secondIsLocale) {
|
|
424
|
+
const locale = availableLocales.find((l) => l.toLowerCase() === second.toLowerCase()) || second;
|
|
425
|
+
return {
|
|
426
|
+
collection,
|
|
427
|
+
name: third,
|
|
428
|
+
locale,
|
|
429
|
+
routingPathname: `/${collection}/${third}`,
|
|
430
|
+
pathname: normalized
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
collection,
|
|
436
|
+
name: second,
|
|
437
|
+
routingPathname: `/${collection}/${second}`,
|
|
438
|
+
pathname: normalized
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function localizeContentHref(collection, name, locale, defaultLocale) {
|
|
442
|
+
if (!locale || locale === defaultLocale) {
|
|
443
|
+
return `/${collection}/${name}`;
|
|
444
|
+
}
|
|
445
|
+
return `/${collection}/${locale}/${name}`;
|
|
446
|
+
}
|
|
447
|
+
function localizeHref(to, locale, defaultLocale, availableLocales = [], collections) {
|
|
448
|
+
try {
|
|
449
|
+
const url = new URL(to, "http://local");
|
|
450
|
+
const parsed = parseContentPath(url.pathname, availableLocales, collections);
|
|
451
|
+
if (!parsed)
|
|
452
|
+
return to;
|
|
453
|
+
url.pathname = localizeContentHref(parsed.collection, parsed.name, locale, defaultLocale);
|
|
454
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
455
|
+
} catch {
|
|
456
|
+
return to;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function contentKey(collection, name, locale, defaultLocale = "en-US") {
|
|
460
|
+
if (locale && locale !== defaultLocale) {
|
|
461
|
+
return `${collection}:${name}:${locale}`;
|
|
462
|
+
}
|
|
463
|
+
return `${collection}:${name}`;
|
|
464
|
+
}
|
|
465
|
+
function toOgLocale(locale) {
|
|
466
|
+
return locale.replace(/-/g, "_");
|
|
467
|
+
}
|
|
468
|
+
function hreflangTags(baseUrl, collection, name, locales, defaultLocale) {
|
|
469
|
+
const base = baseUrl.replace(/\/$/, "");
|
|
470
|
+
const tags = [];
|
|
471
|
+
for (const locale of locales) {
|
|
472
|
+
const path = localizeContentHref(collection, name, locale, defaultLocale);
|
|
473
|
+
tags.push(`<link rel="alternate" hreflang="${locale}" href="${base}${path}" />`);
|
|
474
|
+
}
|
|
475
|
+
const defaultPath = localizeContentHref(collection, name, defaultLocale, defaultLocale);
|
|
476
|
+
tags.push(`<link rel="alternate" hreflang="x-default" href="${base}${defaultPath}" />`);
|
|
477
|
+
return tags.join(`
|
|
478
|
+
`);
|
|
479
|
+
}
|
|
480
|
+
var init_pathname = () => {};
|
|
481
|
+
|
|
482
|
+
// packages/core/src/i18n/resolve.ts
|
|
483
|
+
function resolveLocale(input) {
|
|
484
|
+
const {
|
|
485
|
+
pathname,
|
|
486
|
+
cookieHeader,
|
|
487
|
+
acceptLanguage,
|
|
488
|
+
availableLocales,
|
|
489
|
+
defaultLocale,
|
|
490
|
+
cookieName = DEFAULT_COOKIE,
|
|
491
|
+
collections
|
|
492
|
+
} = input;
|
|
493
|
+
const parsed = parseContentPath(pathname, availableLocales, collections);
|
|
494
|
+
const availableLower = new Set(availableLocales.map((l) => l.toLowerCase()));
|
|
495
|
+
const isAvailable = (locale) => !!locale && availableLower.has(locale.toLowerCase());
|
|
496
|
+
if (parsed?.locale && isAvailable(parsed.locale)) {
|
|
497
|
+
const locale = availableLocales.find((l) => l.toLowerCase() === parsed.locale?.toLowerCase()) || parsed.locale;
|
|
498
|
+
if (locale === defaultLocale && parsed.pathname !== parsed.routingPathname) {
|
|
499
|
+
return {
|
|
500
|
+
locale,
|
|
501
|
+
routingPathname: parsed.routingPathname,
|
|
502
|
+
fromPath: true,
|
|
503
|
+
redirectTo: parsed.routingPathname,
|
|
504
|
+
parsed: { ...parsed, locale }
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
locale,
|
|
509
|
+
routingPathname: parsed.routingPathname,
|
|
510
|
+
fromPath: true,
|
|
511
|
+
parsed: { ...parsed, locale }
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
const routingPathname = parsed?.routingPathname ?? pathname.split("?")[0];
|
|
515
|
+
const cookieLocale = parseCookieValue(cookieHeader, cookieName);
|
|
516
|
+
if (isAvailable(cookieLocale)) {
|
|
517
|
+
const locale = availableLocales.find((l) => l.toLowerCase() === cookieLocale.toLowerCase()) || cookieLocale;
|
|
518
|
+
return {
|
|
519
|
+
locale,
|
|
520
|
+
routingPathname,
|
|
521
|
+
fromPath: false,
|
|
522
|
+
parsed: parsed ? { ...parsed, locale: undefined } : undefined
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
const negotiated = negotiateLocale(acceptLanguage, availableLocales, defaultLocale);
|
|
526
|
+
return {
|
|
527
|
+
locale: negotiated,
|
|
528
|
+
routingPathname,
|
|
529
|
+
fromPath: false,
|
|
530
|
+
parsed: parsed ? { ...parsed, locale: undefined } : undefined
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function contentNegotiateRedirect(resolution, defaultLocale, enabled) {
|
|
534
|
+
if (!enabled)
|
|
535
|
+
return;
|
|
536
|
+
if (resolution.fromPath)
|
|
537
|
+
return;
|
|
538
|
+
if (resolution.locale === defaultLocale)
|
|
539
|
+
return;
|
|
540
|
+
const parsed = resolution.parsed;
|
|
541
|
+
if (!parsed)
|
|
542
|
+
return;
|
|
543
|
+
return localizeContentHref(parsed.collection, parsed.name, resolution.locale, defaultLocale);
|
|
544
|
+
}
|
|
545
|
+
var init_resolve = __esm(() => {
|
|
546
|
+
init_discover();
|
|
547
|
+
init_negotiate();
|
|
548
|
+
init_pathname();
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// packages/core/src/i18n/index.ts
|
|
552
|
+
var init_i18n = __esm(() => {
|
|
553
|
+
init_catalog();
|
|
554
|
+
init_discover();
|
|
555
|
+
init_messages();
|
|
556
|
+
init_negotiate();
|
|
557
|
+
init_pathname();
|
|
558
|
+
init_resolve();
|
|
559
|
+
});
|
|
560
|
+
|
|
203
561
|
// packages/telemetry/src/server/context.ts
|
|
204
562
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
205
563
|
var contextStorage;
|
|
@@ -891,6 +1249,51 @@ function matchRoutes(routes, pathname) {
|
|
|
891
1249
|
return null;
|
|
892
1250
|
}
|
|
893
1251
|
|
|
1252
|
+
// packages/core/src/ssr/lib/content-path.ts
|
|
1253
|
+
var exports_content_path = {};
|
|
1254
|
+
__export(exports_content_path, {
|
|
1255
|
+
resolveContentPathParts: () => resolveContentPathParts
|
|
1256
|
+
});
|
|
1257
|
+
function resolveContentPathParts(pathname) {
|
|
1258
|
+
const pathOnly = pathname.split("?")[0].split("#")[0];
|
|
1259
|
+
const parts = pathOnly.split("/").filter(Boolean);
|
|
1260
|
+
if (parts.length < 2)
|
|
1261
|
+
return null;
|
|
1262
|
+
const g = globalThis;
|
|
1263
|
+
const locales = g.__REROUTE_I18N__?.locales || [];
|
|
1264
|
+
const defaultLocale = g.__REROUTE_I18N__?.defaultLocale || "en-US";
|
|
1265
|
+
const parsed = parseContentPath(pathOnly, locales);
|
|
1266
|
+
if (parsed) {
|
|
1267
|
+
const locale = parsed.locale || defaultLocale;
|
|
1268
|
+
return {
|
|
1269
|
+
collection: parsed.collection,
|
|
1270
|
+
name: parsed.name,
|
|
1271
|
+
locale: parsed.locale,
|
|
1272
|
+
key: contentKey(parsed.collection, parsed.name, locale, defaultLocale),
|
|
1273
|
+
routingPathname: parsed.routingPathname
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
if (parts.length >= 3 && looksLikeLocale(parts[1])) {
|
|
1277
|
+
const locale = parts[1];
|
|
1278
|
+
return {
|
|
1279
|
+
collection: parts[0],
|
|
1280
|
+
name: parts[2],
|
|
1281
|
+
locale,
|
|
1282
|
+
key: contentKey(parts[0], parts[2], locale, defaultLocale),
|
|
1283
|
+
routingPathname: `/${parts[0]}/${parts[2]}`
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
return {
|
|
1287
|
+
collection: parts[0],
|
|
1288
|
+
name: parts[1],
|
|
1289
|
+
key: `${parts[0]}:${parts[1]}`,
|
|
1290
|
+
routingPathname: `/${parts[0]}/${parts[1]}`
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
var init_content_path = __esm(() => {
|
|
1294
|
+
init_pathname();
|
|
1295
|
+
});
|
|
1296
|
+
|
|
894
1297
|
// packages/core/src/ssr/lib/compute/content.ts
|
|
895
1298
|
function checkStatusCache(statusCacheKey, maxAgeMs) {
|
|
896
1299
|
if (maxAgeMs <= 0)
|
|
@@ -943,6 +1346,14 @@ async function handleNonStreamingMode(ssrData, pathname, task, cacheKey, statusC
|
|
|
943
1346
|
}
|
|
944
1347
|
return result;
|
|
945
1348
|
}
|
|
1349
|
+
async function runContentDataTask(streaming, ssrData, pathname, task, set, cacheKey, statusCacheKey, maxAgeMs, pendingTasks) {
|
|
1350
|
+
if (streaming) {
|
|
1351
|
+
handleStreamingMode(ssrData, pathname, task, set, cacheKey, statusCacheKey, maxAgeMs, pendingTasks);
|
|
1352
|
+
} else {
|
|
1353
|
+
await handleNonStreamingMode(ssrData, pathname, task, cacheKey, statusCacheKey, maxAgeMs);
|
|
1354
|
+
}
|
|
1355
|
+
return set.status !== 200 ? set.status : undefined;
|
|
1356
|
+
}
|
|
946
1357
|
async function computeContentData(pathname, options, searchParams, sharedSsrData) {
|
|
947
1358
|
const streaming = options.streaming === true;
|
|
948
1359
|
const ssrData = streaming && sharedSsrData ? sharedSsrData : {};
|
|
@@ -952,18 +1363,20 @@ async function computeContentData(pathname, options, searchParams, sharedSsrData
|
|
|
952
1363
|
const maxAge = options.maxAge || 0;
|
|
953
1364
|
const maxAgeMs = maxAge * 1000;
|
|
954
1365
|
try {
|
|
955
|
-
const
|
|
956
|
-
if (
|
|
1366
|
+
const resolved = resolveContentPathParts(pathname);
|
|
1367
|
+
if (!resolved) {
|
|
957
1368
|
return { data: ssrData, pendingTasks };
|
|
958
1369
|
}
|
|
1370
|
+
const parts = [resolved.collection, resolved.name];
|
|
1371
|
+
if (resolved.locale)
|
|
1372
|
+
parts.splice(1, 0, resolved.locale);
|
|
959
1373
|
const g = globalThis;
|
|
960
|
-
const
|
|
961
|
-
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
1374
|
+
const exp = g.__REROUTE_SSR_EXPORTS__?.[resolved.key];
|
|
962
1375
|
const dataFn = exp?.ssr?.data;
|
|
963
1376
|
if (typeof dataFn !== "function") {
|
|
964
1377
|
return { data: ssrData, pendingTasks };
|
|
965
1378
|
}
|
|
966
|
-
const cacheKey = `content:${pathname}:${
|
|
1379
|
+
const cacheKey = `content:${pathname}:${resolved.name}:${resolved.locale || ""}`;
|
|
967
1380
|
const statusCacheKey = `status:${cacheKey}`;
|
|
968
1381
|
const cachedStatus = checkStatusCache(statusCacheKey, maxAgeMs);
|
|
969
1382
|
if (cachedStatus && cachedStatus !== 200) {
|
|
@@ -976,22 +1389,13 @@ async function computeContentData(pathname, options, searchParams, sharedSsrData
|
|
|
976
1389
|
}
|
|
977
1390
|
const set = { status: 200 };
|
|
978
1391
|
const task = createDataTask(dataFn, pathname, parts, searchParams, set, options.headers);
|
|
979
|
-
|
|
980
|
-
handleStreamingMode(ssrData, pathname, task, set, cacheKey, statusCacheKey, maxAgeMs, pendingTasks);
|
|
981
|
-
if (set.status !== 200) {
|
|
982
|
-
statusOverride = set.status;
|
|
983
|
-
}
|
|
984
|
-
} else {
|
|
985
|
-
await handleNonStreamingMode(ssrData, pathname, task, cacheKey, statusCacheKey, maxAgeMs);
|
|
986
|
-
if (set.status !== 200) {
|
|
987
|
-
statusOverride = set.status;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
1392
|
+
statusOverride = await runContentDataTask(streaming, ssrData, pathname, task, set, cacheKey, statusCacheKey, maxAgeMs, pendingTasks) ?? statusOverride;
|
|
990
1393
|
} catch {}
|
|
991
1394
|
return { data: ssrData, error, statusOverride, pendingTasks };
|
|
992
1395
|
}
|
|
993
1396
|
var init_content2 = __esm(() => {
|
|
994
1397
|
init_cache();
|
|
1398
|
+
init_content_path();
|
|
995
1399
|
init_imports();
|
|
996
1400
|
});
|
|
997
1401
|
|
|
@@ -1190,7 +1594,8 @@ async function computeRouteData(pathname, clientDir, _cwd, isWatchMode, options,
|
|
|
1190
1594
|
}).catch(() => {}));
|
|
1191
1595
|
};
|
|
1192
1596
|
try {
|
|
1193
|
-
const
|
|
1597
|
+
const resolved = resolveContentPathParts(pathname);
|
|
1598
|
+
const pathnameOnly = (resolved?.routingPathname || pathname).split("?")[0];
|
|
1194
1599
|
const match = typeof routesModule?.matchRoute === "function" ? routesModule.matchRoute(pathnameOnly) : null;
|
|
1195
1600
|
const route = match?.route;
|
|
1196
1601
|
const params = match?.params || {};
|
|
@@ -1353,6 +1758,7 @@ function handleFlattenedResult(entries, set, ctx, cache, setError, setStatus) {
|
|
|
1353
1758
|
}
|
|
1354
1759
|
var init_routes = __esm(() => {
|
|
1355
1760
|
init_cache();
|
|
1761
|
+
init_content_path();
|
|
1356
1762
|
init_imports();
|
|
1357
1763
|
});
|
|
1358
1764
|
|
|
@@ -1445,33 +1851,31 @@ var init_compute = __esm(() => {
|
|
|
1445
1851
|
});
|
|
1446
1852
|
|
|
1447
1853
|
// packages/core/src/ssr/lib/modules.ts
|
|
1448
|
-
import { readdir as
|
|
1854
|
+
import { readdir as readdir3, stat as stat3 } from "node:fs/promises";
|
|
1449
1855
|
async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
1450
1856
|
try {
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
if (parts.length < 2)
|
|
1857
|
+
const resolved = resolveContentPathParts(pathname);
|
|
1858
|
+
if (!resolved)
|
|
1454
1859
|
return null;
|
|
1455
|
-
const collection =
|
|
1456
|
-
const
|
|
1457
|
-
const registryModule = await tryRegistryImport(collection, name, cwd, isWatchMode);
|
|
1860
|
+
const { collection, name, locale } = resolved;
|
|
1861
|
+
const registryModule = await tryRegistryImport(collection, name, cwd, isWatchMode, locale);
|
|
1458
1862
|
if (registryModule)
|
|
1459
1863
|
return registryModule;
|
|
1460
1864
|
const chunkModule = await tryChunkImport(collection, name, cwd, isWatchMode);
|
|
1461
1865
|
if (chunkModule)
|
|
1462
1866
|
return chunkModule;
|
|
1463
|
-
const sourceModule = await trySourceImport(collection, name, clientDir, isWatchMode);
|
|
1867
|
+
const sourceModule = await trySourceImport(collection, name, clientDir, isWatchMode, locale);
|
|
1464
1868
|
if (sourceModule)
|
|
1465
1869
|
return sourceModule;
|
|
1466
1870
|
} catch {}
|
|
1467
1871
|
return null;
|
|
1468
1872
|
}
|
|
1469
|
-
async function tryRegistryImport(collection, name, cwd, isWatchMode) {
|
|
1873
|
+
async function tryRegistryImport(collection, name, cwd, isWatchMode, locale) {
|
|
1470
1874
|
try {
|
|
1471
1875
|
const registryPath = join(cwd, ".reroute", "content.ts");
|
|
1472
1876
|
const reg = await dynamicImport(registryPath, isWatchMode);
|
|
1473
1877
|
const get = reg?.getContentEntry;
|
|
1474
|
-
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
1878
|
+
const entry = typeof get === "function" ? get(collection, name, locale) : undefined;
|
|
1475
1879
|
const moduleUrl = entry?.module;
|
|
1476
1880
|
if (!moduleUrl?.endsWith(".js"))
|
|
1477
1881
|
return null;
|
|
@@ -1497,7 +1901,7 @@ async function tryRegistryImport(collection, name, cwd, isWatchMode) {
|
|
|
1497
1901
|
async function tryChunkImport(collection, name, cwd, isWatchMode) {
|
|
1498
1902
|
try {
|
|
1499
1903
|
const chunkDir = join(cwd, ".reroute", "chunks", collection);
|
|
1500
|
-
const files = await
|
|
1904
|
+
const files = await readdir3(chunkDir);
|
|
1501
1905
|
const matches = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
1502
1906
|
if (!matches.length)
|
|
1503
1907
|
return null;
|
|
@@ -1517,23 +1921,23 @@ async function tryChunkImport(collection, name, cwd, isWatchMode) {
|
|
|
1517
1921
|
} catch {}
|
|
1518
1922
|
return null;
|
|
1519
1923
|
}
|
|
1520
|
-
async function trySourceImport(collection, name, clientDir, isWatchMode) {
|
|
1924
|
+
async function trySourceImport(collection, name, clientDir, isWatchMode, locale) {
|
|
1521
1925
|
try {
|
|
1522
|
-
const
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
if (await Bun.file(srcTsx).exists()) {
|
|
1526
|
-
absSrc = srcTsx;
|
|
1527
|
-
} else if (await Bun.file(srcTs).exists()) {
|
|
1528
|
-
absSrc = srcTs;
|
|
1926
|
+
const candidates = [];
|
|
1927
|
+
if (locale) {
|
|
1928
|
+
candidates.push(join(clientDir, "routes", collection, "content", locale, `${name}.tsx`), join(clientDir, "routes", collection, "content", locale, `${name}.ts`));
|
|
1529
1929
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1930
|
+
candidates.push(join(clientDir, "routes", collection, "content", `${name}.tsx`), join(clientDir, "routes", collection, "content", `${name}.ts`));
|
|
1931
|
+
for (const absSrc of candidates) {
|
|
1932
|
+
if (await Bun.file(absSrc).exists()) {
|
|
1933
|
+
return await dynamicImport(absSrc, isWatchMode);
|
|
1934
|
+
}
|
|
1532
1935
|
}
|
|
1533
1936
|
} catch {}
|
|
1534
1937
|
return null;
|
|
1535
1938
|
}
|
|
1536
1939
|
var init_modules = __esm(() => {
|
|
1940
|
+
init_content_path();
|
|
1537
1941
|
init_imports();
|
|
1538
1942
|
});
|
|
1539
1943
|
|
|
@@ -1564,14 +1968,11 @@ function touchModule(key) {
|
|
|
1564
1968
|
}
|
|
1565
1969
|
async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
1566
1970
|
try {
|
|
1567
|
-
const
|
|
1568
|
-
|
|
1569
|
-
if (parts.length < 2) {
|
|
1971
|
+
const resolved = resolveContentPathParts(pathname);
|
|
1972
|
+
if (!resolved) {
|
|
1570
1973
|
return;
|
|
1571
1974
|
}
|
|
1572
|
-
const
|
|
1573
|
-
const name = parts[1];
|
|
1574
|
-
const key = `${collection}:${name}`;
|
|
1975
|
+
const { key } = resolved;
|
|
1575
1976
|
const mod = await importContentModuleForPath(pathname, clientDir, cwd, isWatchMode);
|
|
1576
1977
|
if (!mod) {
|
|
1577
1978
|
return;
|
|
@@ -1591,6 +1992,7 @@ async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
|
|
|
1591
1992
|
}
|
|
1592
1993
|
var MAX_SSR_MODULES = 200, moduleAccessOrder;
|
|
1593
1994
|
var init_seed = __esm(() => {
|
|
1995
|
+
init_content_path();
|
|
1594
1996
|
init_modules();
|
|
1595
1997
|
moduleAccessOrder = [];
|
|
1596
1998
|
});
|
|
@@ -1598,17 +2000,17 @@ var init_seed = __esm(() => {
|
|
|
1598
2000
|
// packages/core/src/ssr/lib/data.ts
|
|
1599
2001
|
function loadContentData(pathname, searchParams) {
|
|
1600
2002
|
try {
|
|
1601
|
-
const
|
|
1602
|
-
if (
|
|
2003
|
+
const resolved = resolveContentPathParts(pathname);
|
|
2004
|
+
if (!resolved)
|
|
1603
2005
|
return null;
|
|
1604
|
-
const key =
|
|
2006
|
+
const key = resolved.key;
|
|
1605
2007
|
const g = globalThis;
|
|
1606
2008
|
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
1607
2009
|
const dataFn = exp?.ssr?.data;
|
|
1608
2010
|
if (typeof dataFn === "function") {
|
|
1609
2011
|
const rawResult = dataFn({
|
|
1610
2012
|
pathname,
|
|
1611
|
-
params: { name:
|
|
2013
|
+
params: { name: resolved.name },
|
|
1612
2014
|
searchParams
|
|
1613
2015
|
});
|
|
1614
2016
|
const result = processDataResult(rawResult, pathname, pathname);
|
|
@@ -1771,6 +2173,7 @@ function createLazyMarker(pathname, key) {
|
|
|
1771
2173
|
};
|
|
1772
2174
|
}
|
|
1773
2175
|
var init_data = __esm(() => {
|
|
2176
|
+
init_content_path();
|
|
1774
2177
|
init_imports();
|
|
1775
2178
|
init_seed();
|
|
1776
2179
|
});
|
|
@@ -1971,14 +2374,14 @@ function replaceAppRoot(html, appHtml, appId) {
|
|
|
1971
2374
|
var init_html = () => {};
|
|
1972
2375
|
|
|
1973
2376
|
// packages/core/src/og/discovery.ts
|
|
1974
|
-
import { readdir as
|
|
2377
|
+
import { readdir as readdir4 } from "node:fs/promises";
|
|
1975
2378
|
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
1976
2379
|
function isCatchAllRouteDirectory(name) {
|
|
1977
2380
|
return CATCH_ALL_ROUTE_DIRECTORY.test(name);
|
|
1978
2381
|
}
|
|
1979
2382
|
async function listDynamicRouteDirectories(baseDir) {
|
|
1980
2383
|
try {
|
|
1981
|
-
const entries = await
|
|
2384
|
+
const entries = await readdir4(baseDir, { withFileTypes: true });
|
|
1982
2385
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => DYNAMIC_ROUTE_DIRECTORY.test(name)).sort((a, b) => {
|
|
1983
2386
|
const aIsCatchAll = isCatchAllRouteDirectory(a);
|
|
1984
2387
|
const bIsCatchAll = isCatchAllRouteDirectory(b);
|
|
@@ -1998,7 +2401,7 @@ async function collectOGCandidatePaths(currentDir, parts, depth, pathsToTry, all
|
|
|
1998
2401
|
}
|
|
1999
2402
|
const literalDir = join(currentDir, parts[depth]);
|
|
2000
2403
|
try {
|
|
2001
|
-
await
|
|
2404
|
+
await readdir4(literalDir);
|
|
2002
2405
|
await collectOGCandidatePaths(literalDir, parts, depth + 1, pathsToTry, true);
|
|
2003
2406
|
} catch {}
|
|
2004
2407
|
const dynamicDirs = await listDynamicRouteDirectories(currentDir);
|
|
@@ -2153,7 +2556,9 @@ async function extractPageMetadata(pathname, clientDir, cwd, isWatchMode, curren
|
|
|
2153
2556
|
let perPageHead = "";
|
|
2154
2557
|
let pageLang;
|
|
2155
2558
|
let statusOverride = currentStatusOverride;
|
|
2156
|
-
|
|
2559
|
+
const resolved = resolveContentPathParts(pathname);
|
|
2560
|
+
const routingPathname = resolved?.routingPathname || pathname;
|
|
2561
|
+
await extractLayoutMetadata(routingPathname, clientDir, cwd, isWatchMode, (head) => {
|
|
2157
2562
|
perPageHead += head;
|
|
2158
2563
|
}, (lang) => {
|
|
2159
2564
|
if (!pageLang)
|
|
@@ -2188,8 +2593,8 @@ ${ogMetaTags}`;
|
|
|
2188
2593
|
perPageHead += head;
|
|
2189
2594
|
}, (lang) => {
|
|
2190
2595
|
pageLang = lang;
|
|
2191
|
-
}, ogConfig, titleSuffix);
|
|
2192
|
-
const routeResult = await extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix);
|
|
2596
|
+
}, ogConfig, titleSuffix, baseUrl);
|
|
2597
|
+
const routeResult = await extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname);
|
|
2193
2598
|
perPageHead += routeResult.head;
|
|
2194
2599
|
if (routeResult.lang)
|
|
2195
2600
|
pageLang = routeResult.lang;
|
|
@@ -2229,14 +2634,42 @@ async function extractLayoutMetadata(pathname, clientDir, cwd, isWatchMode, addH
|
|
|
2229
2634
|
}
|
|
2230
2635
|
} catch {}
|
|
2231
2636
|
}
|
|
2232
|
-
function
|
|
2637
|
+
function resolveContentLocales(collection, name, fallbackLocales) {
|
|
2638
|
+
const g = globalThis;
|
|
2639
|
+
const byCollection = g.__REROUTE_COLLECTIONS__;
|
|
2640
|
+
const items = byCollection?.[collection] || [];
|
|
2641
|
+
const localesForEntry = [
|
|
2642
|
+
...new Set(items.filter((it) => it.name === name && it.locale).map((it) => String(it.locale)))
|
|
2643
|
+
];
|
|
2644
|
+
return localesForEntry.length > 0 ? localesForEntry : fallbackLocales;
|
|
2645
|
+
}
|
|
2646
|
+
function addContentI18nHead(addHead, baseUrl, collection, name, locales, defaultLocale, activeLocale) {
|
|
2647
|
+
if (locales.length > 1) {
|
|
2648
|
+
addHead(`
|
|
2649
|
+
${hreflangTags(baseUrl, collection, name, locales, defaultLocale)}`);
|
|
2650
|
+
}
|
|
2651
|
+
addHead(`
|
|
2652
|
+
<meta property="og:locale" content="${toOgLocale(activeLocale)}" />`);
|
|
2653
|
+
for (const loc of locales) {
|
|
2654
|
+
if (loc === activeLocale)
|
|
2655
|
+
continue;
|
|
2656
|
+
addHead(`
|
|
2657
|
+
<meta property="og:locale:alternate" content="${toOgLocale(loc)}" />`);
|
|
2658
|
+
}
|
|
2659
|
+
const canonicalPath = localizeContentHref(collection, name, activeLocale, defaultLocale);
|
|
2660
|
+
const base = baseUrl.replace(/\/$/, "");
|
|
2661
|
+
addHead(`
|
|
2662
|
+
<link rel="canonical" href="${base}${canonicalPath}" />`);
|
|
2663
|
+
addHead(`
|
|
2664
|
+
<meta property="og:url" content="${base}${canonicalPath}" />`);
|
|
2665
|
+
}
|
|
2666
|
+
function extractContentMetadata(pathname, addHead, setLang, ogConfig, titleSuffix, baseUrl) {
|
|
2233
2667
|
try {
|
|
2234
|
-
const
|
|
2235
|
-
if (
|
|
2668
|
+
const resolved = resolveContentPathParts(pathname);
|
|
2669
|
+
if (!resolved)
|
|
2236
2670
|
return;
|
|
2237
|
-
const key = `${parts[0]}:${parts[1]}`;
|
|
2238
2671
|
const g = globalThis;
|
|
2239
|
-
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
2672
|
+
const exp = g.__REROUTE_SSR_EXPORTS__?.[resolved.key];
|
|
2240
2673
|
const meta = exp?.meta;
|
|
2241
2674
|
const ssr = exp?.ssr;
|
|
2242
2675
|
addHead(buildHeadFromMeta(meta, ogConfig, titleSuffix));
|
|
@@ -2247,15 +2680,20 @@ ${ssrHead}`);
|
|
|
2247
2680
|
const lang = extractSSRLang(ssr);
|
|
2248
2681
|
if (lang)
|
|
2249
2682
|
setLang(lang);
|
|
2683
|
+
const i18n = g.__REROUTE_I18N__;
|
|
2684
|
+
if (i18n?.enabled && baseUrl && i18n.locales && i18n.locales.length > 1) {
|
|
2685
|
+
const locales = resolveContentLocales(resolved.collection, resolved.name, i18n.locales);
|
|
2686
|
+
addContentI18nHead(addHead, baseUrl, resolved.collection, resolved.name, locales, i18n.defaultLocale || "en-US", i18n.locale || i18n.defaultLocale || "en-US");
|
|
2687
|
+
}
|
|
2250
2688
|
} catch {}
|
|
2251
2689
|
}
|
|
2252
2690
|
function getContentMetaFromExports(pathname) {
|
|
2253
2691
|
try {
|
|
2254
|
-
const
|
|
2255
|
-
if (
|
|
2692
|
+
const resolved = resolveContentPathParts(pathname);
|
|
2693
|
+
if (!resolved)
|
|
2256
2694
|
return;
|
|
2257
|
-
const slug =
|
|
2258
|
-
const key =
|
|
2695
|
+
const slug = resolved.name;
|
|
2696
|
+
const key = resolved.key;
|
|
2259
2697
|
const g = globalThis;
|
|
2260
2698
|
const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
|
|
2261
2699
|
const meta = exp?.meta;
|
|
@@ -2269,9 +2707,10 @@ function getContentMetaFromExports(pathname) {
|
|
|
2269
2707
|
return;
|
|
2270
2708
|
}
|
|
2271
2709
|
}
|
|
2272
|
-
async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix) {
|
|
2710
|
+
async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname) {
|
|
2273
2711
|
const pathnameOnly = pathname.split("?")[0];
|
|
2274
|
-
const
|
|
2712
|
+
const matchPath = (routingPathname || pathname).split("?")[0];
|
|
2713
|
+
const match = typeof m.matchRoute === "function" ? m.matchRoute(matchPath) : null;
|
|
2275
2714
|
const r = match?.route;
|
|
2276
2715
|
if (!r) {
|
|
2277
2716
|
return { head: "", statusOverride: statusOverride || 404 };
|
|
@@ -2311,7 +2750,7 @@ ${ssrHead}`;
|
|
|
2311
2750
|
}
|
|
2312
2751
|
return { head: "", statusOverride };
|
|
2313
2752
|
}
|
|
2314
|
-
async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ssrData, ogConfig, titleSuffix) {
|
|
2753
|
+
async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ssrData, ogConfig, titleSuffix, routingPathname) {
|
|
2315
2754
|
let head = "";
|
|
2316
2755
|
let lang;
|
|
2317
2756
|
let statusOverride = currentStatusOverride;
|
|
@@ -2321,14 +2760,14 @@ async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, curre
|
|
|
2321
2760
|
if (!m) {
|
|
2322
2761
|
return { head, lang, statusOverride: statusOverride || 404 };
|
|
2323
2762
|
}
|
|
2324
|
-
const
|
|
2325
|
-
const match = typeof m.matchRoute === "function" ? m.matchRoute(
|
|
2763
|
+
const matchPath = (routingPathname || pathname).split("?")[0];
|
|
2764
|
+
const match = typeof m.matchRoute === "function" ? m.matchRoute(matchPath) : null;
|
|
2326
2765
|
const r = match?.route;
|
|
2327
2766
|
if (!r) {
|
|
2328
2767
|
statusOverride = statusOverride || 404;
|
|
2329
2768
|
}
|
|
2330
2769
|
if (r && typeof r.path === "string") {
|
|
2331
|
-
const result = await extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix);
|
|
2770
|
+
const result = await extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname);
|
|
2332
2771
|
head = result.head;
|
|
2333
2772
|
lang = result.lang;
|
|
2334
2773
|
statusOverride = result.statusOverride;
|
|
@@ -2503,34 +2942,34 @@ function applyTitleSuffixToHead(headContent, titleSuffix) {
|
|
|
2503
2942
|
}
|
|
2504
2943
|
var init_metadata2 = __esm(() => {
|
|
2505
2944
|
init_metadata();
|
|
2945
|
+
init_pathname();
|
|
2506
2946
|
init_meta();
|
|
2507
2947
|
init_compute();
|
|
2948
|
+
init_content_path();
|
|
2508
2949
|
init_imports();
|
|
2509
2950
|
});
|
|
2510
2951
|
|
|
2511
2952
|
// packages/core/src/ssr/lib/preload.ts
|
|
2512
|
-
import { readdir as
|
|
2953
|
+
import { readdir as readdir5, stat as stat4 } from "node:fs/promises";
|
|
2513
2954
|
async function preloadContentModule(pathname, clientDir, cwd, isWatchMode) {
|
|
2514
2955
|
let extraHead = "";
|
|
2515
2956
|
let hydrationScript = "";
|
|
2516
2957
|
let statusOverride;
|
|
2517
2958
|
try {
|
|
2518
|
-
const
|
|
2519
|
-
const
|
|
2520
|
-
if (
|
|
2959
|
+
const { resolveContentPathParts: resolveContentPathParts2 } = await Promise.resolve().then(() => (init_content_path(), exports_content_path));
|
|
2960
|
+
const resolved = resolveContentPathParts2(pathname);
|
|
2961
|
+
if (!resolved) {
|
|
2521
2962
|
return { extraHead, hydrationScript };
|
|
2522
2963
|
}
|
|
2523
|
-
const collection =
|
|
2524
|
-
const name = parts[1];
|
|
2964
|
+
const { collection, name, key, locale } = resolved;
|
|
2525
2965
|
if (!await isContentCollection(clientDir, collection)) {
|
|
2526
2966
|
return { extraHead, hydrationScript };
|
|
2527
2967
|
}
|
|
2528
|
-
const modulePath = await findContentModulePath(collection, name, clientDir, cwd, isWatchMode);
|
|
2968
|
+
const modulePath = await findContentModulePath(collection, name, clientDir, cwd, isWatchMode, locale);
|
|
2529
2969
|
if (!modulePath) {
|
|
2530
2970
|
statusOverride = 404;
|
|
2531
2971
|
return { extraHead, hydrationScript, statusOverride };
|
|
2532
2972
|
}
|
|
2533
|
-
const key = `${collection}:${name}`;
|
|
2534
2973
|
extraHead += `
|
|
2535
2974
|
<link rel="modulepreload" href="${modulePath}" />`;
|
|
2536
2975
|
hydrationScript += `<script>
|
|
@@ -2572,12 +3011,12 @@ async function findLatestCandidate(chunkDir, candidates) {
|
|
|
2572
3011
|
}
|
|
2573
3012
|
return latest;
|
|
2574
3013
|
}
|
|
2575
|
-
async function findContentModulePath(collection, name, clientDir, cwd, isWatchMode) {
|
|
3014
|
+
async function findContentModulePath(collection, name, clientDir, cwd, isWatchMode, locale) {
|
|
2576
3015
|
try {
|
|
2577
3016
|
const registryPath = join(cwd, ".reroute", "content.ts");
|
|
2578
3017
|
const reg = await dynamicImport(registryPath, isWatchMode);
|
|
2579
3018
|
const get = reg?.getContentEntry;
|
|
2580
|
-
const entry = typeof get === "function" ? get(collection, name) : undefined;
|
|
3019
|
+
const entry = typeof get === "function" ? get(collection, name, locale) : undefined;
|
|
2581
3020
|
const moduleUrl = entry?.module;
|
|
2582
3021
|
if (moduleUrl?.endsWith(".js")) {
|
|
2583
3022
|
return moduleUrl;
|
|
@@ -2585,7 +3024,7 @@ async function findContentModulePath(collection, name, clientDir, cwd, isWatchMo
|
|
|
2585
3024
|
} catch {}
|
|
2586
3025
|
try {
|
|
2587
3026
|
const chunkDir = join(cwd, ".reroute", "chunks", collection);
|
|
2588
|
-
const files = await
|
|
3027
|
+
const files = await readdir5(chunkDir);
|
|
2589
3028
|
const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
|
|
2590
3029
|
if (candidates.length) {
|
|
2591
3030
|
const latest = await findLatestCandidate(chunkDir, candidates);
|
|
@@ -2593,13 +3032,18 @@ async function findContentModulePath(collection, name, clientDir, cwd, isWatchMo
|
|
|
2593
3032
|
}
|
|
2594
3033
|
} catch {}
|
|
2595
3034
|
try {
|
|
2596
|
-
const
|
|
2597
|
-
const
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
3035
|
+
const contentRoot = join(clientDir, "routes", collection, "content");
|
|
3036
|
+
const candidates = locale ? [
|
|
3037
|
+
join(contentRoot, locale, `${name}.tsx`),
|
|
3038
|
+
join(contentRoot, locale, `${name}.ts`),
|
|
3039
|
+
join(contentRoot, `${name}.tsx`),
|
|
3040
|
+
join(contentRoot, `${name}.ts`)
|
|
3041
|
+
] : [join(contentRoot, `${name}.tsx`), join(contentRoot, `${name}.ts`)];
|
|
3042
|
+
for (const abs of candidates) {
|
|
3043
|
+
if (await Bun.file(abs).exists()) {
|
|
3044
|
+
const rel = abs.startsWith(clientDir) ? abs.slice(clientDir.length).replace(/^[/\\]/, "") : join("routes", collection, "content", `${name}.tsx`);
|
|
3045
|
+
return `/${rel.replace(/\\+/g, "/")}`;
|
|
3046
|
+
}
|
|
2603
3047
|
}
|
|
2604
3048
|
} catch {}
|
|
2605
3049
|
return null;
|
|
@@ -2793,11 +3237,18 @@ function duplicateQueryParamData(ssrData) {
|
|
|
2793
3237
|
return duplicated;
|
|
2794
3238
|
}
|
|
2795
3239
|
function getContentKeyFromPathname(pathname) {
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
3240
|
+
return resolveContentPathParts(pathname)?.key ?? null;
|
|
3241
|
+
}
|
|
3242
|
+
function createI18nScript(i18n) {
|
|
3243
|
+
if (!i18n?.enabled)
|
|
3244
|
+
return "";
|
|
3245
|
+
try {
|
|
3246
|
+
const json = escapeJsonForScript(JSON.stringify(i18n));
|
|
3247
|
+
return `<script>(function(){try{var w=(typeof window!=='undefined'?window:globalThis);w.__REROUTE_I18N__=${json};}catch(e){}})();</script>`;
|
|
3248
|
+
} catch (error) {
|
|
3249
|
+
console.error("[reroute] Failed to serialize i18n hydration:", error);
|
|
3250
|
+
return "";
|
|
3251
|
+
}
|
|
2801
3252
|
}
|
|
2802
3253
|
function createContentCaptureScript() {
|
|
2803
3254
|
return `<script>(function(){try{var w=window;w.__REROUTE_INITIAL_HYDRATION__=true;var h={};document.querySelectorAll('[data-reroute-key]').forEach(function(el){var k=el.getAttribute('data-reroute-key');if(k)h[k]=el.innerHTML;});w.__REROUTE_CONTENT_HTML__=h;var r=document.querySelector('[data-reroute-route="ssr"]');if(r)w.__REROUTE_ROUTE_HTML__=r.innerHTML;}catch(e){}})();</script>`;
|
|
@@ -2873,13 +3324,14 @@ function createSSRPathnameScript(pathname) {
|
|
|
2873
3324
|
return "";
|
|
2874
3325
|
}
|
|
2875
3326
|
}
|
|
2876
|
-
function createDataScript(ssrData, errorMsg, pathname, browserTelemetryConfig, traceContext, titleSuffix) {
|
|
3327
|
+
function createDataScript(ssrData, errorMsg, pathname, browserTelemetryConfig, traceContext, titleSuffix, i18n) {
|
|
2877
3328
|
let script = "";
|
|
2878
3329
|
script += createContentCaptureScript();
|
|
2879
3330
|
script += createSSRPathnameScript(pathname);
|
|
2880
3331
|
if (pathname) {
|
|
2881
3332
|
script += createSSRExportsScript(pathname);
|
|
2882
3333
|
}
|
|
3334
|
+
script += createI18nScript(i18n);
|
|
2883
3335
|
script += createTelemetryConfigScript(browserTelemetryConfig);
|
|
2884
3336
|
script += createTraceContextScript(traceContext);
|
|
2885
3337
|
script += createTitleSuffixScript(titleSuffix);
|
|
@@ -2899,7 +3351,9 @@ function createDataScript(ssrData, errorMsg, pathname, browserTelemetryConfig, t
|
|
|
2899
3351
|
}
|
|
2900
3352
|
return script;
|
|
2901
3353
|
}
|
|
2902
|
-
var init_data2 = () => {
|
|
3354
|
+
var init_data2 = __esm(() => {
|
|
3355
|
+
init_content_path();
|
|
3356
|
+
});
|
|
2903
3357
|
|
|
2904
3358
|
// packages/core/src/ssr/lib/scripts/feeds.ts
|
|
2905
3359
|
function createFeedsScript() {
|
|
@@ -3127,10 +3581,10 @@ function generateCollectionScripts(byCollectionForSSR) {
|
|
|
3127
3581
|
} catch {}
|
|
3128
3582
|
return "";
|
|
3129
3583
|
}
|
|
3130
|
-
function generateHydrationScripts(ssrData, isWatchMode, ssrError, collectionScripts = "", preloadScript = "", bundleUrl = "", pathname = "", browserTelemetryConfig, traceContext, titleSuffix) {
|
|
3584
|
+
function generateHydrationScripts(ssrData, isWatchMode, ssrError, collectionScripts = "", preloadScript = "", bundleUrl = "", pathname = "", browserTelemetryConfig, traceContext, titleSuffix, i18n) {
|
|
3131
3585
|
let scripts = preloadScript;
|
|
3132
3586
|
scripts += collectionScripts;
|
|
3133
|
-
scripts += createDataScript(ssrData, isWatchMode ? ssrError : undefined, pathname, browserTelemetryConfig, traceContext, titleSuffix);
|
|
3587
|
+
scripts += createDataScript(ssrData, isWatchMode ? ssrError : undefined, pathname, browserTelemetryConfig, traceContext, titleSuffix, i18n);
|
|
3134
3588
|
scripts += createFeedsScript();
|
|
3135
3589
|
scripts += createLlmsScript();
|
|
3136
3590
|
if (bundleUrl) {
|
|
@@ -3293,7 +3747,7 @@ async function renderSSRDocument(options) {
|
|
|
3293
3747
|
const browserTelemetryConfig = serializeBrowserTelemetryConfig2(browserTelemetryConfigRaw);
|
|
3294
3748
|
const traceContext = getTraceContextForInjection(options.parentSpan);
|
|
3295
3749
|
const hydrationScript = await withSpan("ssr.generate.scripts", async (span) => {
|
|
3296
|
-
const script = generateHydrationScripts(setup2.ssrData, isWatchMode, setup2.ssrError, collectionScripts, setup2.preloadHydrationScript, bundleUrl, pathname, browserTelemetryConfig, traceContext, options.titleSuffix);
|
|
3750
|
+
const script = generateHydrationScripts(setup2.ssrData, isWatchMode, setup2.ssrError, collectionScripts, setup2.preloadHydrationScript, bundleUrl, pathname, browserTelemetryConfig, traceContext, options.titleSuffix, options.i18n);
|
|
3297
3751
|
span.setAttribute("reroute.scripts.size", script.length);
|
|
3298
3752
|
return script;
|
|
3299
3753
|
});
|
|
@@ -3311,12 +3765,13 @@ async function renderSSRDocument(options) {
|
|
|
3311
3765
|
dedent(setup2.metadataResult.perPageHead)
|
|
3312
3766
|
].filter(Boolean).join(`
|
|
3313
3767
|
`));
|
|
3768
|
+
const i18nLocale = options.i18n?.enabled ? String(options.i18n.locale || "") : "";
|
|
3314
3769
|
const html2 = withSpanSync("ssr.apply.template", (span) => {
|
|
3315
3770
|
const result = applyIndexTemplate(baseTemplate, appHtml, {
|
|
3316
3771
|
head: [setup2.inlineStyleTag, combinedHead].filter(Boolean).join(`
|
|
3317
3772
|
`),
|
|
3318
3773
|
hydrationScript,
|
|
3319
|
-
lang: setup2.metadataResult.pageLang || lang,
|
|
3774
|
+
lang: setup2.metadataResult.pageLang || i18nLocale || lang,
|
|
3320
3775
|
appId
|
|
3321
3776
|
});
|
|
3322
3777
|
span.setAttributes({
|
|
@@ -4494,6 +4949,55 @@ async function discoverRoutes(cwd, isWatchMode) {
|
|
|
4494
4949
|
}
|
|
4495
4950
|
return { staticRoutes, dynamicRoutes };
|
|
4496
4951
|
}
|
|
4952
|
+
function groupNamedCollectionItems(items) {
|
|
4953
|
+
const byName = new Map;
|
|
4954
|
+
for (const item of items) {
|
|
4955
|
+
const name = String(item.name || "");
|
|
4956
|
+
if (!name)
|
|
4957
|
+
continue;
|
|
4958
|
+
const list = byName.get(name) || [];
|
|
4959
|
+
list.push(item);
|
|
4960
|
+
byName.set(name, list);
|
|
4961
|
+
}
|
|
4962
|
+
return byName;
|
|
4963
|
+
}
|
|
4964
|
+
function resolveItemHref(collection, item, defaultLocale, localize) {
|
|
4965
|
+
const name = String(item.name || "");
|
|
4966
|
+
const locale = item.locale || defaultLocale;
|
|
4967
|
+
if (name)
|
|
4968
|
+
return localize(collection, name, locale, defaultLocale);
|
|
4969
|
+
return String(item.href || "");
|
|
4970
|
+
}
|
|
4971
|
+
function buildNamedCollectionSitemapEntries(collection, name, variants, defaultLocale, base, localize) {
|
|
4972
|
+
const locales = [
|
|
4973
|
+
...new Set(variants.map((v) => v.locale || defaultLocale).filter(Boolean))
|
|
4974
|
+
];
|
|
4975
|
+
const alternates = locales.length > 1 ? [
|
|
4976
|
+
...locales.map((locale) => ({
|
|
4977
|
+
hreflang: locale,
|
|
4978
|
+
href: `${base}${localize(collection, name, locale, defaultLocale)}`
|
|
4979
|
+
})),
|
|
4980
|
+
{
|
|
4981
|
+
hreflang: "x-default",
|
|
4982
|
+
href: `${base}${localize(collection, name, defaultLocale, defaultLocale)}`
|
|
4983
|
+
}
|
|
4984
|
+
] : undefined;
|
|
4985
|
+
const entries = [];
|
|
4986
|
+
for (const item of variants) {
|
|
4987
|
+
const href = resolveItemHref(collection, item, defaultLocale, localize);
|
|
4988
|
+
if (!href)
|
|
4989
|
+
continue;
|
|
4990
|
+
const meta = item.meta || {};
|
|
4991
|
+
entries.push({
|
|
4992
|
+
loc: `${base}${href}`,
|
|
4993
|
+
lastmod: meta.date ? new Date(meta.date).toISOString() : undefined,
|
|
4994
|
+
changefreq: "weekly",
|
|
4995
|
+
priority: 0.7,
|
|
4996
|
+
alternates
|
|
4997
|
+
});
|
|
4998
|
+
}
|
|
4999
|
+
return entries;
|
|
5000
|
+
}
|
|
4497
5001
|
async function discoverCollectionContent(cwd, collection, isWatchMode, baseUrl) {
|
|
4498
5002
|
const entries = [];
|
|
4499
5003
|
try {
|
|
@@ -4505,18 +5009,26 @@ async function discoverCollectionContent(cwd, collection, isWatchMode, baseUrl)
|
|
|
4505
5009
|
const url = `${Bun.pathToFileURL(collectionFile).href}${isWatchMode ? `?t=${Date.now()}` : ""}`;
|
|
4506
5010
|
const mod = await import(url);
|
|
4507
5011
|
const items = mod.items || [];
|
|
5012
|
+
const defaultLocale = mod.defaultLocale || "en-US";
|
|
5013
|
+
const { localizeContentHref: localizeContentHref2 } = await Promise.resolve().then(() => (init_pathname(), exports_pathname));
|
|
5014
|
+
const base = baseUrl.replace(/\/$/, "");
|
|
5015
|
+
const byName = groupNamedCollectionItems(items);
|
|
5016
|
+
for (const [name, variants] of byName) {
|
|
5017
|
+
entries.push(...buildNamedCollectionSitemapEntries(collection, name, variants, defaultLocale, base, localizeContentHref2));
|
|
5018
|
+
}
|
|
4508
5019
|
for (const item of items) {
|
|
5020
|
+
if (item.name)
|
|
5021
|
+
continue;
|
|
4509
5022
|
const href = String(item.href || "");
|
|
5023
|
+
if (!href)
|
|
5024
|
+
continue;
|
|
4510
5025
|
const meta = item.meta || {};
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
};
|
|
4518
|
-
entries.push(entry);
|
|
4519
|
-
}
|
|
5026
|
+
entries.push({
|
|
5027
|
+
loc: `${base}${href}`,
|
|
5028
|
+
lastmod: meta.date ? new Date(meta.date).toISOString() : undefined,
|
|
5029
|
+
changefreq: "weekly",
|
|
5030
|
+
priority: 0.7
|
|
5031
|
+
});
|
|
4520
5032
|
}
|
|
4521
5033
|
} catch (error) {
|
|
4522
5034
|
console.error(`[reroute] Failed to discover collection ${collection}:`, error);
|
|
@@ -4811,11 +5323,17 @@ async function discoverCollectionFeedItems(cwd, collection, isWatchMode, baseUrl
|
|
|
4811
5323
|
const url = `${Bun.pathToFileURL(collectionFile).href}${isWatchMode ? `?t=${Date.now()}` : ""}`;
|
|
4812
5324
|
const mod = await import(url);
|
|
4813
5325
|
const contentItems = mod.items || [];
|
|
5326
|
+
const defaultLocale = mod.defaultLocale || "en-US";
|
|
5327
|
+
const { localizeContentHref: localizeContentHref2 } = await Promise.resolve().then(() => (init_pathname(), exports_pathname));
|
|
4814
5328
|
for (const item of contentItems) {
|
|
4815
|
-
const
|
|
5329
|
+
const name = String(item.name || "");
|
|
5330
|
+
const locale = item.locale || defaultLocale;
|
|
5331
|
+
const href = name ? localizeContentHref2(collection, name, locale, defaultLocale) : String(item.href || "");
|
|
4816
5332
|
const meta = item.meta || {};
|
|
4817
5333
|
if (href) {
|
|
4818
|
-
|
|
5334
|
+
const rssItem = createRSSItemFromMetaAndHref(href, meta, baseUrl, item, rssConfig);
|
|
5335
|
+
rssItem.locale = locale;
|
|
5336
|
+
items.push(rssItem);
|
|
4819
5337
|
}
|
|
4820
5338
|
}
|
|
4821
5339
|
} catch (error) {
|
|
@@ -5081,7 +5599,7 @@ function generateRSSXML(feed, options = {}) {
|
|
|
5081
5599
|
<link>${escapeXml(feed.link)}</link>
|
|
5082
5600
|
<description>${escapeXml(feed.description)}</description>
|
|
5083
5601
|
<lastBuildDate>${lastBuildDate}</lastBuildDate>
|
|
5084
|
-
<language
|
|
5602
|
+
<language>${escapeXml(feed.language || "en")}</language>
|
|
5085
5603
|
<atom:link href="${escapeXml(feed.link)}" rel="self" type="application/rss+xml" />
|
|
5086
5604
|
${itemsXML}
|
|
5087
5605
|
</channel>
|
|
@@ -5243,6 +5761,7 @@ async function generateSitemap(discovery4, baseUrl, options = {}) {
|
|
|
5243
5761
|
return createSingleSitemap(allEntries);
|
|
5244
5762
|
}
|
|
5245
5763
|
function generateSitemapXML(entries) {
|
|
5764
|
+
const hasAlternates = entries.some((e) => e.alternates && e.alternates.length > 0);
|
|
5246
5765
|
const urlsetEntries = entries.map((entry) => {
|
|
5247
5766
|
let xml = ` <url>
|
|
5248
5767
|
<loc>${escapeXml2(entry.loc)}</loc>`;
|
|
@@ -5258,13 +5777,20 @@ function generateSitemapXML(entries) {
|
|
|
5258
5777
|
xml += `
|
|
5259
5778
|
<priority>${entry.priority.toFixed(1)}</priority>`;
|
|
5260
5779
|
}
|
|
5780
|
+
if (entry.alternates?.length) {
|
|
5781
|
+
for (const alt of entry.alternates) {
|
|
5782
|
+
xml += `
|
|
5783
|
+
<xhtml:link rel="alternate" hreflang="${escapeXml2(alt.hreflang)}" href="${escapeXml2(alt.href)}" />`;
|
|
5784
|
+
}
|
|
5785
|
+
}
|
|
5261
5786
|
xml += `
|
|
5262
5787
|
</url>`;
|
|
5263
5788
|
return xml;
|
|
5264
5789
|
}).join(`
|
|
5265
5790
|
`);
|
|
5791
|
+
const xhtmlNs = hasAlternates ? ' xmlns:xhtml="http://www.w3.org/1999/xhtml"' : "";
|
|
5266
5792
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
5267
|
-
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
5793
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"${xhtmlNs}>
|
|
5268
5794
|
${urlsetEntries}
|
|
5269
5795
|
</urlset>`;
|
|
5270
5796
|
}
|
|
@@ -5362,7 +5888,8 @@ async function renderSSRDocumentStream(options) {
|
|
|
5362
5888
|
...parts
|
|
5363
5889
|
};
|
|
5364
5890
|
});
|
|
5365
|
-
const
|
|
5891
|
+
const i18nLocale = options.i18n?.enabled ? String(options.i18n.locale || "") : "";
|
|
5892
|
+
const pageLang = setup2.metadataResult.pageLang || i18nLocale || lang;
|
|
5366
5893
|
let headWithLang = htmlHead.replace(/<html([^>]*)>/i, (_m, attrs) => {
|
|
5367
5894
|
const hasLang = /(^|\s)lang\s*=/.test(attrs);
|
|
5368
5895
|
const newAttrs = hasLang ? attrs.replace(/lang\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/i, `lang="${pageLang}"`) : `${attrs} lang="${pageLang}"`;
|
|
@@ -5405,7 +5932,8 @@ async function renderSSRDocumentStream(options) {
|
|
|
5405
5932
|
parentSpan: options.parentSpan,
|
|
5406
5933
|
timeout: options.timeout,
|
|
5407
5934
|
ssrExport: setup2.metadataResult.ssrExport,
|
|
5408
|
-
titleSuffix: options.titleSuffix
|
|
5935
|
+
titleSuffix: options.titleSuffix,
|
|
5936
|
+
i18n: options.i18n
|
|
5409
5937
|
});
|
|
5410
5938
|
if (debug)
|
|
5411
5939
|
console.log(`[renderStream] ${pathname} - returning stream: ${(performance.now() - t0).toFixed(2)}ms`);
|
|
@@ -5518,7 +6046,7 @@ function buildStreamingScripts(ctx) {
|
|
|
5518
6046
|
ssrDataSnapshot[key] = value;
|
|
5519
6047
|
}
|
|
5520
6048
|
}
|
|
5521
|
-
const dataScript = createDataScript(ssrDataSnapshot, ctx.isWatchMode ? ctx.ssrError : undefined, ctx.pathname, ctx.browserTelemetryConfig, ctx.traceContext, ctx.titleSuffix);
|
|
6049
|
+
const dataScript = createDataScript(ssrDataSnapshot, ctx.isWatchMode ? ctx.ssrError : undefined, ctx.pathname, ctx.browserTelemetryConfig, ctx.traceContext, ctx.titleSuffix, ctx.i18n);
|
|
5522
6050
|
const feedsScript = createFeedsScript();
|
|
5523
6051
|
const llmsScript = createLlmsScript();
|
|
5524
6052
|
let allScripts = dataScript + feedsScript + llmsScript + collectionScripts + ctx.preloadHydrationScript;
|
|
@@ -5650,6 +6178,7 @@ var init_src = __esm(() => {
|
|
|
5650
6178
|
init_bundler();
|
|
5651
6179
|
init_config();
|
|
5652
6180
|
init_content();
|
|
6181
|
+
init_i18n();
|
|
5653
6182
|
init_llms();
|
|
5654
6183
|
init_og();
|
|
5655
6184
|
init_robots();
|
|
@@ -5662,6 +6191,7 @@ var init_src = __esm(() => {
|
|
|
5662
6191
|
var exports_core = {};
|
|
5663
6192
|
__export(exports_core, {
|
|
5664
6193
|
toPromise: () => toPromise,
|
|
6194
|
+
toOgLocale: () => toOgLocale,
|
|
5665
6195
|
toBytes: () => toBytes,
|
|
5666
6196
|
stripStart: () => stripStart,
|
|
5667
6197
|
stripHtmlTags: () => stripHtmlTags,
|
|
@@ -5673,6 +6203,7 @@ __export(exports_core, {
|
|
|
5673
6203
|
setGlobalSSRData: () => setGlobalSSRData,
|
|
5674
6204
|
setGlobalCollections: () => setGlobalCollections,
|
|
5675
6205
|
seedSSRModuleForPath: () => seedSSRModuleForPath,
|
|
6206
|
+
resolveLocale: () => resolveLocale,
|
|
5676
6207
|
resetSSRAccessTracking: () => resetSSRAccessTracking,
|
|
5677
6208
|
renderSSRDocumentStream: () => renderSSRDocumentStream,
|
|
5678
6209
|
renderSSRDocument: () => renderSSRDocument,
|
|
@@ -5681,9 +6212,17 @@ __export(exports_core, {
|
|
|
5681
6212
|
processCollections: () => processCollections,
|
|
5682
6213
|
preloadContentModule: () => preloadContentModule,
|
|
5683
6214
|
performSSRSetup: () => performSSRSetup,
|
|
6215
|
+
parseCookieValue: () => parseCookieValue,
|
|
6216
|
+
parseContentPath: () => parseContentPath,
|
|
6217
|
+
normalizePathname: () => normalizePathname,
|
|
6218
|
+
negotiateLocale: () => negotiateLocale,
|
|
5684
6219
|
mergeLayoutData: () => mergeLayoutData,
|
|
5685
6220
|
matchRoutes: () => matchRoutes,
|
|
5686
6221
|
matchRoutePattern: () => matchRoutePattern,
|
|
6222
|
+
lookupMessage: () => lookupMessage,
|
|
6223
|
+
looksLikeLocale: () => looksLikeLocale,
|
|
6224
|
+
localizeHref: () => localizeHref,
|
|
6225
|
+
localizeContentHref: () => localizeContentHref,
|
|
5687
6226
|
loadRoutesModule: () => loadRoutesModule,
|
|
5688
6227
|
loadOGImageComponent: () => loadOGImageComponent,
|
|
5689
6228
|
loadIndexHtml: () => loadIndexHtml,
|
|
@@ -5696,11 +6235,13 @@ __export(exports_core, {
|
|
|
5696
6235
|
inlineTailwindCSS: () => inlineTailwindCSS,
|
|
5697
6236
|
importContentModuleForPath: () => importContentModuleForPath,
|
|
5698
6237
|
htmlToMarkdown: () => htmlToMarkdown,
|
|
6238
|
+
hreflangTags: () => hreflangTags,
|
|
5699
6239
|
gzipIfAccepted: () => gzipIfAccepted,
|
|
5700
6240
|
getSitemapExcludedRoutes: () => getSitemapExcludedRoutes,
|
|
5701
6241
|
getRedirectSourcePaths: () => getRedirectSourcePaths,
|
|
5702
6242
|
getPatternDepth: () => getPatternDepth,
|
|
5703
6243
|
getMimeType: () => getMimeType,
|
|
6244
|
+
getMessage: () => getMessage,
|
|
5704
6245
|
getMatchingLayouts: () => getMatchingLayouts,
|
|
5705
6246
|
getContentMeta: () => getContentMeta,
|
|
5706
6247
|
generateSitemapXML: () => generateSitemapXML,
|
|
@@ -5717,6 +6258,7 @@ __export(exports_core, {
|
|
|
5717
6258
|
generateContentHash: () => generateContentHash,
|
|
5718
6259
|
generateCollectionScripts: () => generateCollectionScripts,
|
|
5719
6260
|
generateAtomXML: () => generateAtomXML,
|
|
6261
|
+
formatMessage: () => formatMessage,
|
|
5720
6262
|
formatAsText: () => formatAsText,
|
|
5721
6263
|
formatAsMarkdown: () => formatAsMarkdown,
|
|
5722
6264
|
findOGImageForPath: () => findOGImageForPath,
|
|
@@ -5736,10 +6278,12 @@ __export(exports_core, {
|
|
|
5736
6278
|
discoverRoutes: () => discoverRoutes,
|
|
5737
6279
|
discoverRobotsData: () => discoverRobotsData,
|
|
5738
6280
|
discoverRSSData: () => discoverRSSData,
|
|
6281
|
+
discoverI18n: () => discoverI18n,
|
|
5739
6282
|
discoverCollections: () => discoverCollections,
|
|
5740
6283
|
discoverCollectionContent: () => discoverCollectionContent,
|
|
5741
6284
|
defineConfig: () => defineConfig,
|
|
5742
6285
|
deduplicateMetaTags: () => deduplicateMetaTags,
|
|
6286
|
+
createTranslator: () => createTranslator,
|
|
5743
6287
|
createLlmsScript: () => createLlmsScript,
|
|
5744
6288
|
createLayoutDataKey: () => createLayoutDataKey,
|
|
5745
6289
|
createLayoutCacheKey: () => createLayoutCacheKey,
|
|
@@ -5747,6 +6291,8 @@ __export(exports_core, {
|
|
|
5747
6291
|
createDataScript: () => createDataScript,
|
|
5748
6292
|
createCollectionScript: () => createCollectionScript,
|
|
5749
6293
|
createBundlePreload: () => createBundlePreload,
|
|
6294
|
+
contentNegotiateRedirect: () => contentNegotiateRedirect,
|
|
6295
|
+
contentKey: () => contentKey,
|
|
5750
6296
|
computeSSRDataForPath: () => computeSSRDataForPath,
|
|
5751
6297
|
computeSSRData: () => computeSSRData,
|
|
5752
6298
|
computeRouteData: () => computeRouteData,
|
|
@@ -5755,6 +6301,7 @@ __export(exports_core, {
|
|
|
5755
6301
|
compressStreamIfAccepted: () => compressStreamIfAccepted,
|
|
5756
6302
|
compareRouteSpecificity: () => compareRouteSpecificity,
|
|
5757
6303
|
cleanupPathScopedSSRData: () => cleanupPathScopedSSRData,
|
|
6304
|
+
buildLocaleCookie: () => buildLocaleCookie,
|
|
5758
6305
|
buildHeadFromMeta: () => buildHeadFromMeta,
|
|
5759
6306
|
blockSearchCrawlers: () => blockSearchCrawlers,
|
|
5760
6307
|
blockAITrainingCrawlers: () => blockAITrainingCrawlers,
|
|
@@ -5770,6 +6317,9 @@ __export(exports_core, {
|
|
|
5770
6317
|
SEARCH_CRAWLERS: () => SEARCH_CRAWLERS,
|
|
5771
6318
|
LRUCache: () => LRUCache,
|
|
5772
6319
|
DefaultOGTemplate: () => DefaultOGTemplate,
|
|
6320
|
+
DEFAULT_LOCALES_DIR: () => DEFAULT_LOCALES_DIR,
|
|
6321
|
+
DEFAULT_LOCALE: () => DEFAULT_LOCALE,
|
|
6322
|
+
DEFAULT_COOKIE: () => DEFAULT_COOKIE,
|
|
5773
6323
|
AI_CRAWLERS: () => AI_CRAWLERS
|
|
5774
6324
|
});
|
|
5775
6325
|
var init_core = __esm(() => {
|
|
@@ -5784,7 +6334,7 @@ import { createElement as createElement2 } from "react";
|
|
|
5784
6334
|
|
|
5785
6335
|
// packages/elysia/src/libs/image.ts
|
|
5786
6336
|
import { existsSync } from "node:fs";
|
|
5787
|
-
import { join as
|
|
6337
|
+
import { join as join3 } from "node:path";
|
|
5788
6338
|
import sharp from "sharp";
|
|
5789
6339
|
var imageCache = new Map;
|
|
5790
6340
|
var MAX_CACHE_SIZE = 100;
|
|
@@ -5827,8 +6377,8 @@ async function loadFromDiskCache(cacheKey) {
|
|
|
5827
6377
|
if (!diskCacheDir)
|
|
5828
6378
|
return null;
|
|
5829
6379
|
try {
|
|
5830
|
-
const cachePath =
|
|
5831
|
-
const imagePath =
|
|
6380
|
+
const cachePath = join3(diskCacheDir, `${cacheKey}.json`);
|
|
6381
|
+
const imagePath = join3(diskCacheDir, `${cacheKey}.img`);
|
|
5832
6382
|
if (!(existsSync(cachePath) && existsSync(imagePath))) {
|
|
5833
6383
|
return null;
|
|
5834
6384
|
}
|
|
@@ -5850,8 +6400,8 @@ async function saveToDiskCache(cacheKey, result) {
|
|
|
5850
6400
|
if (!diskCacheDir)
|
|
5851
6401
|
return;
|
|
5852
6402
|
try {
|
|
5853
|
-
const cachePath =
|
|
5854
|
-
const imagePath =
|
|
6403
|
+
const cachePath = join3(diskCacheDir, `${cacheKey}.json`);
|
|
6404
|
+
const imagePath = join3(diskCacheDir, `${cacheKey}.img`);
|
|
5855
6405
|
await Bun.write(cachePath, JSON.stringify({
|
|
5856
6406
|
contentType: result.contentType,
|
|
5857
6407
|
format: result.format,
|
|
@@ -6272,7 +6822,7 @@ function registerContentRoutes(app, cwd, collectionCache, directive, maxAge, isW
|
|
|
6272
6822
|
// packages/elysia/src/routes/image.ts
|
|
6273
6823
|
init_core();
|
|
6274
6824
|
import { existsSync as existsSync2 } from "node:fs";
|
|
6275
|
-
import { join as
|
|
6825
|
+
import { join as join4 } from "node:path";
|
|
6276
6826
|
function parseNumericParam(value) {
|
|
6277
6827
|
if (!value)
|
|
6278
6828
|
return;
|
|
@@ -6319,8 +6869,8 @@ function registerImageRoutes(app, options) {
|
|
|
6319
6869
|
}
|
|
6320
6870
|
function resolveInternalImagePath(url, clientDir2) {
|
|
6321
6871
|
const cleanUrl = url.startsWith("/") ? url.slice(1) : url;
|
|
6322
|
-
const imagePath =
|
|
6323
|
-
const normalizedPath =
|
|
6872
|
+
const imagePath = join4(clientDir2, cleanUrl);
|
|
6873
|
+
const normalizedPath = join4(clientDir2, cleanUrl);
|
|
6324
6874
|
if (!normalizedPath.startsWith(clientDir2)) {
|
|
6325
6875
|
return { error: "Access denied" };
|
|
6326
6876
|
}
|
|
@@ -7424,14 +7974,14 @@ function registerOGRoutes(app, options) {
|
|
|
7424
7974
|
}
|
|
7425
7975
|
return;
|
|
7426
7976
|
}
|
|
7427
|
-
function getFontPaths(fontPath, baseDir,
|
|
7977
|
+
function getFontPaths(fontPath, baseDir, join5) {
|
|
7428
7978
|
const fontRelativePath = fontPath.replace(/^(\.\/)?src\/client\/assets\//, "").replace(/^assets\//, "");
|
|
7429
7979
|
return [
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7980
|
+
join5(clientDir, "assets", fontRelativePath),
|
|
7981
|
+
join5(baseDir, ".reroute", "assets", fontRelativePath),
|
|
7982
|
+
join5(cwd, ".reroute", "assets", fontRelativePath),
|
|
7983
|
+
join5(cwd, fontPath),
|
|
7984
|
+
join5(baseDir, fontPath)
|
|
7435
7985
|
];
|
|
7436
7986
|
}
|
|
7437
7987
|
async function tryLoadFont(font, possiblePaths, existsSync3) {
|
|
@@ -7454,14 +8004,14 @@ function registerOGRoutes(app, options) {
|
|
|
7454
8004
|
if (!ogConfig.fonts || ogConfig.fonts.length === 0) {
|
|
7455
8005
|
return [];
|
|
7456
8006
|
}
|
|
7457
|
-
const { join:
|
|
8007
|
+
const { join: join5, dirname } = await import("node:path");
|
|
7458
8008
|
const { existsSync: existsSync3 } = await import("node:fs");
|
|
7459
8009
|
const isCompiledBinary = typeof Bun !== "undefined" && process.argv[0]?.endsWith(process.argv[1]);
|
|
7460
8010
|
const baseDir = isCompiledBinary ? dirname(process.argv[1]) : cwd;
|
|
7461
8011
|
const loadedFonts = [];
|
|
7462
8012
|
for (const font of ogConfig.fonts) {
|
|
7463
8013
|
try {
|
|
7464
|
-
const possiblePaths = getFontPaths(font.path, baseDir,
|
|
8014
|
+
const possiblePaths = getFontPaths(font.path, baseDir, join5);
|
|
7465
8015
|
const loadedFont = await tryLoadFont(font, possiblePaths, existsSync3);
|
|
7466
8016
|
if (loadedFont) {
|
|
7467
8017
|
loadedFonts.push(loadedFont);
|
|
@@ -7789,42 +8339,52 @@ async function registerMainFeedRoute(app, cwd, collections2, baseUrl, isWatchMod
|
|
|
7789
8339
|
function registerCollectionFeedRoutes(app, collections2, options) {
|
|
7790
8340
|
for (const collection of collections2) {
|
|
7791
8341
|
options.availableFeeds.push(`/${collection}/feed.xml`);
|
|
7792
|
-
app.get(`/${collection}/feed.xml`, async () =>
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
options.
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
8342
|
+
app.get(`/${collection}/feed.xml`, async ({ request }) => handleCollectionFeed(collection, request, options));
|
|
8343
|
+
}
|
|
8344
|
+
}
|
|
8345
|
+
function filterFeedItemsByLang(items, lang) {
|
|
8346
|
+
if (!lang)
|
|
8347
|
+
return items;
|
|
8348
|
+
return items.filter((item) => !item.locale || item.locale === lang);
|
|
8349
|
+
}
|
|
8350
|
+
function buildCollectionFeed(collection, items, lang, options) {
|
|
8351
|
+
return {
|
|
8352
|
+
title: createFeedTitle(options.rssConfig?.title, collection),
|
|
8353
|
+
link: lang ? `${options.baseUrl}/${collection}/feed.xml?lang=${encodeURIComponent(lang)}` : `${options.baseUrl}/${collection}/feed.xml`,
|
|
8354
|
+
description: options.rssConfig?.description ?? `Latest ${collection} updates`,
|
|
8355
|
+
items,
|
|
8356
|
+
language: lang || items[0]?.locale || "en"
|
|
8357
|
+
};
|
|
8358
|
+
}
|
|
8359
|
+
async function handleCollectionFeed(collection, request, options) {
|
|
8360
|
+
const lang = new URL(request.url).searchParams.get("lang") || undefined;
|
|
8361
|
+
const cacheKey = lang ? `rss:${collection}:${lang}` : `rss:${collection}`;
|
|
8362
|
+
try {
|
|
8363
|
+
const cached = options.rssCache.get(cacheKey);
|
|
8364
|
+
if (cached) {
|
|
8365
|
+
return feedResponse(cached, options.format, {
|
|
8366
|
+
maxAge: options.maxAge,
|
|
8367
|
+
cacheStatus: "HIT"
|
|
8368
|
+
});
|
|
8369
|
+
}
|
|
8370
|
+
const discovery5 = await getOrDiscoverRSSData(options.cwd, [collection], options.baseUrl, options.isWatchMode, options.rssConfig);
|
|
8371
|
+
const items = filterFeedItemsByLang(discovery5.collections.get(collection) ?? [], lang);
|
|
8372
|
+
if (items.length === 0)
|
|
8373
|
+
throw new NotFoundError8;
|
|
8374
|
+
const xml = generateFeed(buildCollectionFeed(collection, items, lang, options), {
|
|
8375
|
+
format: options.format,
|
|
8376
|
+
limit: options.limit
|
|
8377
|
+
});
|
|
8378
|
+
options.rssCache.set(cacheKey, xml);
|
|
8379
|
+
return feedResponse(xml, options.format, {
|
|
8380
|
+
maxAge: options.maxAge,
|
|
8381
|
+
cacheStatus: "MISS"
|
|
7827
8382
|
});
|
|
8383
|
+
} catch (error) {
|
|
8384
|
+
if (error instanceof NotFoundError8)
|
|
8385
|
+
throw error;
|
|
8386
|
+
console.error(`[reroute] Failed to generate ${collection} feed:`, error);
|
|
8387
|
+
throw new NotFoundError8;
|
|
7828
8388
|
}
|
|
7829
8389
|
}
|
|
7830
8390
|
async function discoverRoutesWithFeeds(ssrDataRoutes, collections2, options) {
|
|
@@ -8000,11 +8560,11 @@ function scoreEntry(entry, terms) {
|
|
|
8000
8560
|
return { score, matchedHeadings };
|
|
8001
8561
|
}
|
|
8002
8562
|
function searchEntries(entries, query, options = {}) {
|
|
8003
|
-
const { collections: collections2, page = 1, pageSize = 20 } = options;
|
|
8563
|
+
const { collections: collections2, page = 1, pageSize = 20, locale } = options;
|
|
8004
8564
|
const terms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 1);
|
|
8005
8565
|
if (terms.length === 0)
|
|
8006
8566
|
return { results: [], total: 0 };
|
|
8007
|
-
const scored = entries.filter((entry) => !collections2?.length || collections2.includes(entry.collection)).map((entry) => ({ entry, ...scoreEntry(entry, terms) })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score);
|
|
8567
|
+
const scored = entries.filter((entry) => !collections2?.length || collections2.includes(entry.collection)).filter((entry) => !(locale && entry.locale) || entry.locale === locale).map((entry) => ({ entry, ...scoreEntry(entry, terms) })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score);
|
|
8008
8568
|
const total = scored.length;
|
|
8009
8569
|
const startIndex = (page - 1) * pageSize;
|
|
8010
8570
|
const paginatedScored = scored.slice(startIndex, startIndex + pageSize);
|
|
@@ -8055,10 +8615,12 @@ function registerSearchRoute(app, cwd, directive, maxAge, searchConfig) {
|
|
|
8055
8615
|
const page = query.page ? Math.max(1, Number.parseInt(String(query.page), 10)) : 1;
|
|
8056
8616
|
const requestedPageSize = query.pageSize ? Number.parseInt(String(query.pageSize), 10) : defaultPageSize;
|
|
8057
8617
|
const pageSize = Math.min(Math.max(1, requestedPageSize), maxPageSize);
|
|
8618
|
+
const locale = query.lang ? String(query.lang) : undefined;
|
|
8058
8619
|
const { results, total } = searchEntries(index.entries, searchQuery, {
|
|
8059
8620
|
collections: collections2,
|
|
8060
8621
|
page,
|
|
8061
|
-
pageSize
|
|
8622
|
+
pageSize,
|
|
8623
|
+
locale
|
|
8062
8624
|
});
|
|
8063
8625
|
return Response.json({
|
|
8064
8626
|
total,
|
|
@@ -8162,9 +8724,115 @@ function registerSitemapRoutes(app, options) {
|
|
|
8162
8724
|
// packages/elysia/src/routes/ssr.ts
|
|
8163
8725
|
init_core();
|
|
8164
8726
|
init_server2();
|
|
8165
|
-
import { join as
|
|
8727
|
+
import { join as join6 } from "node:path";
|
|
8166
8728
|
import { trace as trace4 } from "@opentelemetry/api";
|
|
8167
8729
|
import { NotFoundError as NotFoundError10 } from "elysia";
|
|
8730
|
+
|
|
8731
|
+
// packages/elysia/src/libs/i18n.ts
|
|
8732
|
+
init_core();
|
|
8733
|
+
import { join as join5 } from "node:path";
|
|
8734
|
+
var cachedI18n = null;
|
|
8735
|
+
async function loadI18nState(cwd) {
|
|
8736
|
+
const path2 = join5(cwd, ".reroute", "i18n.json");
|
|
8737
|
+
try {
|
|
8738
|
+
const file = Bun.file(path2);
|
|
8739
|
+
if (!await file.exists()) {
|
|
8740
|
+
return {
|
|
8741
|
+
enabled: false,
|
|
8742
|
+
defaultLocale: "en-US",
|
|
8743
|
+
locales: [],
|
|
8744
|
+
localesDir: "locales",
|
|
8745
|
+
cookie: "reroute-locale",
|
|
8746
|
+
redirectContentOnNegotiate: true,
|
|
8747
|
+
catalogs: {}
|
|
8748
|
+
};
|
|
8749
|
+
}
|
|
8750
|
+
const stat5 = await file.stat();
|
|
8751
|
+
const mtime = stat5?.mtime?.getTime?.() ?? Date.now();
|
|
8752
|
+
if (cachedI18n && cachedI18n.cwd === cwd && cachedI18n.mtime === mtime) {
|
|
8753
|
+
return cachedI18n.data;
|
|
8754
|
+
}
|
|
8755
|
+
const data = await file.json();
|
|
8756
|
+
cachedI18n = { cwd, data, mtime };
|
|
8757
|
+
return data;
|
|
8758
|
+
} catch {
|
|
8759
|
+
return {
|
|
8760
|
+
enabled: false,
|
|
8761
|
+
defaultLocale: "en-US",
|
|
8762
|
+
locales: [],
|
|
8763
|
+
localesDir: "locales",
|
|
8764
|
+
cookie: "reroute-locale",
|
|
8765
|
+
redirectContentOnNegotiate: true,
|
|
8766
|
+
catalogs: {}
|
|
8767
|
+
};
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8770
|
+
async function listContentCollections(cwd) {
|
|
8771
|
+
try {
|
|
8772
|
+
const { readdir: readdir6 } = await import("node:fs/promises");
|
|
8773
|
+
const dir = join5(cwd, ".reroute", "collections");
|
|
8774
|
+
const entries = await readdir6(dir);
|
|
8775
|
+
return entries.filter((f) => f.endsWith(".js")).map((f) => f.replace(/\.js$/, ""));
|
|
8776
|
+
} catch {
|
|
8777
|
+
return [];
|
|
8778
|
+
}
|
|
8779
|
+
}
|
|
8780
|
+
async function resolveI18nForRequest(cwd, pathname, headers2, collections2) {
|
|
8781
|
+
const i18n2 = await loadI18nState(cwd);
|
|
8782
|
+
const disabled = {
|
|
8783
|
+
enabled: false,
|
|
8784
|
+
locale: i18n2.defaultLocale,
|
|
8785
|
+
defaultLocale: i18n2.defaultLocale,
|
|
8786
|
+
locales: [],
|
|
8787
|
+
cookie: i18n2.cookie,
|
|
8788
|
+
catalogs: {},
|
|
8789
|
+
hydration: { enabled: false }
|
|
8790
|
+
};
|
|
8791
|
+
if (!i18n2.enabled || i18n2.locales.length === 0) {
|
|
8792
|
+
return disabled;
|
|
8793
|
+
}
|
|
8794
|
+
const knownCollections = collections2?.length ? collections2 : await listContentCollections(cwd);
|
|
8795
|
+
const resolution = resolveLocale({
|
|
8796
|
+
pathname,
|
|
8797
|
+
cookieHeader: headers2.cookie,
|
|
8798
|
+
acceptLanguage: headers2["accept-language"],
|
|
8799
|
+
availableLocales: i18n2.locales,
|
|
8800
|
+
defaultLocale: i18n2.defaultLocale,
|
|
8801
|
+
cookieName: i18n2.cookie,
|
|
8802
|
+
collections: knownCollections.length > 0 ? knownCollections : undefined
|
|
8803
|
+
});
|
|
8804
|
+
let redirectTo = resolution.redirectTo;
|
|
8805
|
+
if (!redirectTo && i18n2.redirectContentOnNegotiate && knownCollections.length > 0) {
|
|
8806
|
+
redirectTo = contentNegotiateRedirect(resolution, i18n2.defaultLocale, true);
|
|
8807
|
+
}
|
|
8808
|
+
const cookieValue = buildLocaleCookie(i18n2.cookie, resolution.locale);
|
|
8809
|
+
const hydration = {
|
|
8810
|
+
enabled: true,
|
|
8811
|
+
locale: resolution.locale,
|
|
8812
|
+
defaultLocale: i18n2.defaultLocale,
|
|
8813
|
+
locales: i18n2.locales,
|
|
8814
|
+
cookie: i18n2.cookie,
|
|
8815
|
+
catalogs: i18n2.catalogs,
|
|
8816
|
+
collections: knownCollections
|
|
8817
|
+
};
|
|
8818
|
+
try {
|
|
8819
|
+
globalThis.__REROUTE_I18N__ = hydration;
|
|
8820
|
+
} catch {}
|
|
8821
|
+
return {
|
|
8822
|
+
enabled: true,
|
|
8823
|
+
locale: resolution.locale,
|
|
8824
|
+
defaultLocale: i18n2.defaultLocale,
|
|
8825
|
+
locales: i18n2.locales,
|
|
8826
|
+
cookie: i18n2.cookie,
|
|
8827
|
+
catalogs: i18n2.catalogs,
|
|
8828
|
+
collections: knownCollections,
|
|
8829
|
+
setCookie: cookieValue,
|
|
8830
|
+
redirectTo,
|
|
8831
|
+
hydration
|
|
8832
|
+
};
|
|
8833
|
+
}
|
|
8834
|
+
|
|
8835
|
+
// packages/elysia/src/routes/ssr.ts
|
|
8168
8836
|
if (typeof process !== "undefined") {
|
|
8169
8837
|
process.on("unhandledRejection", (reason, _promise) => {
|
|
8170
8838
|
console.error("[reroute] Unhandled Promise Rejection:", reason);
|
|
@@ -8186,6 +8854,28 @@ function isOtelAvailable3() {
|
|
|
8186
8854
|
return false;
|
|
8187
8855
|
}
|
|
8188
8856
|
}
|
|
8857
|
+
async function applyI18nForSSR(cwd, pathname, headers2, set, fallbackLang) {
|
|
8858
|
+
const i18nResult = await resolveI18nForRequest(cwd, pathname, headers2);
|
|
8859
|
+
if (i18nResult.redirectTo && i18nResult.redirectTo !== pathname) {
|
|
8860
|
+
const redirectHeaders = {
|
|
8861
|
+
Location: i18nResult.redirectTo
|
|
8862
|
+
};
|
|
8863
|
+
if (i18nResult.setCookie) {
|
|
8864
|
+
redirectHeaders["Set-Cookie"] = i18nResult.setCookie;
|
|
8865
|
+
}
|
|
8866
|
+
return {
|
|
8867
|
+
redirect: new Response(null, { status: 302, headers: redirectHeaders }),
|
|
8868
|
+
effectiveLang: fallbackLang
|
|
8869
|
+
};
|
|
8870
|
+
}
|
|
8871
|
+
if (i18nResult.setCookie) {
|
|
8872
|
+
set.headers["set-cookie"] = i18nResult.setCookie;
|
|
8873
|
+
}
|
|
8874
|
+
return {
|
|
8875
|
+
hydration: i18nResult.enabled ? i18nResult.hydration : undefined,
|
|
8876
|
+
effectiveLang: i18nResult.enabled ? i18nResult.locale : fallbackLang
|
|
8877
|
+
};
|
|
8878
|
+
}
|
|
8189
8879
|
function registerSSRRoutes(app, options) {
|
|
8190
8880
|
const {
|
|
8191
8881
|
rootComponent,
|
|
@@ -8225,7 +8915,7 @@ function registerSSRRoutes(app, options) {
|
|
|
8225
8915
|
let contentRoutes = [];
|
|
8226
8916
|
let dynamicRoutes = [];
|
|
8227
8917
|
try {
|
|
8228
|
-
const manifestPath =
|
|
8918
|
+
const manifestPath = join6(cwd, ".reroute", "manifest.json");
|
|
8229
8919
|
const { readFileSync } = __require("node:fs");
|
|
8230
8920
|
const manifestText = readFileSync(manifestPath, "utf-8");
|
|
8231
8921
|
const manifest = JSON.parse(manifestText);
|
|
@@ -8261,6 +8951,9 @@ function registerSSRRoutes(app, options) {
|
|
|
8261
8951
|
request.headers.forEach((value, key) => {
|
|
8262
8952
|
headers2[key.toLowerCase()] = value;
|
|
8263
8953
|
});
|
|
8954
|
+
const i18n2 = await applyI18nForSSR(cwd, pathname, headers2, set, lang);
|
|
8955
|
+
if (i18n2.redirect)
|
|
8956
|
+
return i18n2.redirect;
|
|
8264
8957
|
const parentSpan = store.telemetrySpan;
|
|
8265
8958
|
return withParentSpan(parentSpan, async () => {
|
|
8266
8959
|
let isFirstStreamingRequest = false;
|
|
@@ -8343,7 +9036,7 @@ function registerSSRRoutes(app, options) {
|
|
|
8343
9036
|
cachedIndexHtml,
|
|
8344
9037
|
debug,
|
|
8345
9038
|
head,
|
|
8346
|
-
lang,
|
|
9039
|
+
lang: i18n2.effectiveLang,
|
|
8347
9040
|
appId,
|
|
8348
9041
|
maxAge: serverMaxAge,
|
|
8349
9042
|
searchParams,
|
|
@@ -8354,7 +9047,8 @@ function registerSSRRoutes(app, options) {
|
|
|
8354
9047
|
parentSpan: renderSpan,
|
|
8355
9048
|
headers: headers2,
|
|
8356
9049
|
timeout: options.streamingTimeout,
|
|
8357
|
-
titleSuffix: options.titleSuffix
|
|
9050
|
+
titleSuffix: options.titleSuffix,
|
|
9051
|
+
i18n: i18n2.hydration
|
|
8358
9052
|
});
|
|
8359
9053
|
renderSpan?.setAttribute("http.response.status_code", result.status);
|
|
8360
9054
|
if (debug)
|
|
@@ -8525,7 +9219,7 @@ function registerSSRRoutes(app, options) {
|
|
|
8525
9219
|
isWatchMode,
|
|
8526
9220
|
bundleUrl,
|
|
8527
9221
|
head,
|
|
8528
|
-
lang,
|
|
9222
|
+
lang: i18n2.effectiveLang,
|
|
8529
9223
|
appId,
|
|
8530
9224
|
maxAge: serverMaxAge,
|
|
8531
9225
|
searchParams,
|
|
@@ -8535,7 +9229,8 @@ function registerSSRRoutes(app, options) {
|
|
|
8535
9229
|
autoCanonical: options.autoCanonical,
|
|
8536
9230
|
parentSpan: renderSpan,
|
|
8537
9231
|
headers: headers2,
|
|
8538
|
-
titleSuffix: options.titleSuffix
|
|
9232
|
+
titleSuffix: options.titleSuffix,
|
|
9233
|
+
i18n: i18n2.hydration
|
|
8539
9234
|
});
|
|
8540
9235
|
renderSpan?.setAttribute("http.response.status_code", renderResult.status);
|
|
8541
9236
|
return renderResult;
|
|
@@ -9113,4 +9808,4 @@ export {
|
|
|
9113
9808
|
reroute
|
|
9114
9809
|
};
|
|
9115
9810
|
|
|
9116
|
-
//# debugId=
|
|
9811
|
+
//# debugId=27029E2CFF5EA81E64756E2164756E21
|