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/core/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();
|
|
@@ -5666,6 +6195,7 @@ init_core();
|
|
|
5666
6195
|
|
|
5667
6196
|
export {
|
|
5668
6197
|
toPromise,
|
|
6198
|
+
toOgLocale,
|
|
5669
6199
|
toBytes,
|
|
5670
6200
|
stripStart,
|
|
5671
6201
|
stripHtmlTags,
|
|
@@ -5677,6 +6207,7 @@ export {
|
|
|
5677
6207
|
setGlobalSSRData,
|
|
5678
6208
|
setGlobalCollections,
|
|
5679
6209
|
seedSSRModuleForPath,
|
|
6210
|
+
resolveLocale,
|
|
5680
6211
|
resetSSRAccessTracking,
|
|
5681
6212
|
renderSSRDocumentStream,
|
|
5682
6213
|
renderSSRDocument,
|
|
@@ -5685,9 +6216,17 @@ export {
|
|
|
5685
6216
|
processCollections,
|
|
5686
6217
|
preloadContentModule,
|
|
5687
6218
|
performSSRSetup,
|
|
6219
|
+
parseCookieValue,
|
|
6220
|
+
parseContentPath,
|
|
6221
|
+
normalizePathname,
|
|
6222
|
+
negotiateLocale,
|
|
5688
6223
|
mergeLayoutData,
|
|
5689
6224
|
matchRoutes,
|
|
5690
6225
|
matchRoutePattern,
|
|
6226
|
+
lookupMessage,
|
|
6227
|
+
looksLikeLocale,
|
|
6228
|
+
localizeHref,
|
|
6229
|
+
localizeContentHref,
|
|
5691
6230
|
loadRoutesModule,
|
|
5692
6231
|
loadOGImageComponent,
|
|
5693
6232
|
loadIndexHtml,
|
|
@@ -5700,11 +6239,13 @@ export {
|
|
|
5700
6239
|
inlineTailwindCSS,
|
|
5701
6240
|
importContentModuleForPath,
|
|
5702
6241
|
htmlToMarkdown,
|
|
6242
|
+
hreflangTags,
|
|
5703
6243
|
gzipIfAccepted,
|
|
5704
6244
|
getSitemapExcludedRoutes,
|
|
5705
6245
|
getRedirectSourcePaths,
|
|
5706
6246
|
getPatternDepth,
|
|
5707
6247
|
getMimeType,
|
|
6248
|
+
getMessage,
|
|
5708
6249
|
getMatchingLayouts,
|
|
5709
6250
|
getContentMeta,
|
|
5710
6251
|
generateSitemapXML,
|
|
@@ -5721,6 +6262,7 @@ export {
|
|
|
5721
6262
|
generateContentHash,
|
|
5722
6263
|
generateCollectionScripts,
|
|
5723
6264
|
generateAtomXML,
|
|
6265
|
+
formatMessage,
|
|
5724
6266
|
formatAsText,
|
|
5725
6267
|
formatAsMarkdown,
|
|
5726
6268
|
findOGImageForPath,
|
|
@@ -5740,10 +6282,12 @@ export {
|
|
|
5740
6282
|
discoverRoutes,
|
|
5741
6283
|
discoverRobotsData,
|
|
5742
6284
|
discoverRSSData,
|
|
6285
|
+
discoverI18n,
|
|
5743
6286
|
discoverCollections,
|
|
5744
6287
|
discoverCollectionContent,
|
|
5745
6288
|
defineConfig,
|
|
5746
6289
|
deduplicateMetaTags,
|
|
6290
|
+
createTranslator,
|
|
5747
6291
|
createLlmsScript,
|
|
5748
6292
|
createLayoutDataKey,
|
|
5749
6293
|
createLayoutCacheKey,
|
|
@@ -5751,6 +6295,8 @@ export {
|
|
|
5751
6295
|
createDataScript,
|
|
5752
6296
|
createCollectionScript,
|
|
5753
6297
|
createBundlePreload,
|
|
6298
|
+
contentNegotiateRedirect,
|
|
6299
|
+
contentKey,
|
|
5754
6300
|
computeSSRDataForPath,
|
|
5755
6301
|
computeSSRData,
|
|
5756
6302
|
computeRouteData,
|
|
@@ -5759,6 +6305,7 @@ export {
|
|
|
5759
6305
|
compressStreamIfAccepted,
|
|
5760
6306
|
compareRouteSpecificity,
|
|
5761
6307
|
cleanupPathScopedSSRData,
|
|
6308
|
+
buildLocaleCookie,
|
|
5762
6309
|
buildHeadFromMeta,
|
|
5763
6310
|
blockSearchCrawlers,
|
|
5764
6311
|
blockAITrainingCrawlers,
|
|
@@ -5774,7 +6321,10 @@ export {
|
|
|
5774
6321
|
SEARCH_CRAWLERS,
|
|
5775
6322
|
LRUCache,
|
|
5776
6323
|
DefaultOGTemplate,
|
|
6324
|
+
DEFAULT_LOCALES_DIR,
|
|
6325
|
+
DEFAULT_LOCALE,
|
|
6326
|
+
DEFAULT_COOKIE,
|
|
5777
6327
|
AI_CRAWLERS
|
|
5778
6328
|
};
|
|
5779
6329
|
|
|
5780
|
-
//# debugId=
|
|
6330
|
+
//# debugId=C4F445FA1614E66D64756E2164756E21
|