reroute-js 0.45.11 → 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.
Files changed (244) hide show
  1. package/README.md +100 -2
  2. package/_/blog/src/client/routes/blog/[layout].tsx +45 -13
  3. package/_/blog/src/client/routes/blog/content/pt-BR/1-hello-world.tsx +27 -0
  4. package/_/blog/src/client/routes/index.tsx +4 -4
  5. package/_/search/src/client/routes/docs/content/content-collections.md +18 -0
  6. package/cli/bin.d.ts +2 -2
  7. package/cli/bin.js +1175 -431
  8. package/cli/bin.js.map +33 -24
  9. package/cli/index.d.ts +2 -2
  10. package/cli/index.js +5 -5
  11. package/cli/index.js.map +1 -1
  12. package/cli/src/cli.d.ts +2 -2
  13. package/cli/src/commands/analyze.d.ts +2 -2
  14. package/cli/src/commands/build.d.ts +2 -2
  15. package/cli/src/commands/dev.d.ts +2 -2
  16. package/cli/src/commands/gen.d.ts +2 -2
  17. package/cli/src/commands/gen.d.ts.map +1 -1
  18. package/cli/src/commands/index.d.ts +2 -2
  19. package/cli/src/commands/init.d.ts +2 -2
  20. package/cli/src/commands/lib/assets.d.ts +2 -2
  21. package/cli/src/commands/lib/bundler.d.ts +2 -2
  22. package/cli/src/commands/lib/command.d.ts +2 -2
  23. package/cli/src/commands/lib/env.d.ts +2 -2
  24. package/cli/src/commands/lib/index.d.ts +2 -2
  25. package/cli/src/commands/lib/log.d.ts +2 -2
  26. package/cli/src/commands/lib/markdown/availability.d.ts +2 -2
  27. package/cli/src/commands/lib/markdown/index.d.ts +2 -2
  28. package/cli/src/commands/lib/markdown/processor.d.ts +2 -2
  29. package/cli/src/commands/lib/production.d.ts +2 -2
  30. package/cli/src/commands/lib/server.d.ts +2 -2
  31. package/cli/src/commands/lib/streaming/analyzer.d.ts +2 -2
  32. package/cli/src/commands/lib/streaming/suspense.d.ts +2 -2
  33. package/cli/src/commands/lib/tailwind.d.ts +2 -2
  34. package/cli/src/commands/lib/terminal-ui.d.ts +2 -2
  35. package/cli/src/commands/lib/version.d.ts +2 -2
  36. package/cli/src/commands/og.d.ts +2 -2
  37. package/cli/src/commands/start.d.ts +2 -2
  38. package/cli/src/index.d.ts +2 -2
  39. package/core/index.d.ts +2 -2
  40. package/core/index.js +827 -192
  41. package/core/index.js.map +32 -23
  42. package/core/src/bundler/hash.d.ts +2 -2
  43. package/core/src/bundler/index.d.ts +2 -2
  44. package/core/src/config.d.ts +6 -3
  45. package/core/src/config.d.ts.map +1 -1
  46. package/core/src/content/discovery.d.ts +2 -2
  47. package/core/src/content/index.d.ts +2 -2
  48. package/core/src/content/metadata.d.ts +2 -2
  49. package/core/src/i18n/catalog.d.ts +17 -0
  50. package/core/src/i18n/catalog.d.ts.map +1 -0
  51. package/core/src/i18n/discover.d.ts +19 -0
  52. package/core/src/i18n/discover.d.ts.map +1 -0
  53. package/core/src/i18n/index.d.ts +17 -0
  54. package/core/src/i18n/index.d.ts.map +1 -0
  55. package/core/src/i18n/messages.d.ts +18 -0
  56. package/core/src/i18n/messages.d.ts.map +1 -0
  57. package/core/src/i18n/negotiate.d.ts +18 -0
  58. package/core/src/i18n/negotiate.d.ts.map +1 -0
  59. package/core/src/i18n/pathname.d.ts +34 -0
  60. package/core/src/i18n/pathname.d.ts.map +1 -0
  61. package/core/src/i18n/resolve.d.ts +22 -0
  62. package/core/src/i18n/resolve.d.ts.map +1 -0
  63. package/core/src/i18n/types.d.ts +68 -0
  64. package/core/src/i18n/types.d.ts.map +1 -0
  65. package/core/src/index.d.ts +3 -2
  66. package/core/src/index.d.ts.map +1 -1
  67. package/core/src/llms/extractor.d.ts +2 -2
  68. package/core/src/llms/formatter.d.ts +2 -2
  69. package/core/src/llms/full-generator.d.ts +2 -2
  70. package/core/src/llms/index-generator.d.ts +2 -2
  71. package/core/src/llms/index.d.ts +2 -2
  72. package/core/src/og/discovery.d.ts +2 -2
  73. package/core/src/og/discovery.d.ts.map +1 -1
  74. package/core/src/og/index.d.ts +2 -2
  75. package/core/src/og/meta.d.ts +2 -2
  76. package/core/src/og/render.d.ts +2 -2
  77. package/core/src/og/types.d.ts +2 -2
  78. package/core/src/robots/discovery.d.ts +2 -2
  79. package/core/src/robots/generator.d.ts +2 -2
  80. package/core/src/robots/index.d.ts +2 -2
  81. package/core/src/robots/policies.d.ts +2 -2
  82. package/core/src/rss/discovery.d.ts +4 -2
  83. package/core/src/rss/discovery.d.ts.map +1 -1
  84. package/core/src/rss/generator.d.ts +2 -2
  85. package/core/src/rss/index.d.ts +2 -2
  86. package/core/src/sitemap/discovery.d.ts +7 -2
  87. package/core/src/sitemap/discovery.d.ts.map +1 -1
  88. package/core/src/sitemap/generator.d.ts +2 -2
  89. package/core/src/sitemap/generator.d.ts.map +1 -1
  90. package/core/src/sitemap/index.d.ts +2 -2
  91. package/core/src/ssr/index.d.ts +2 -2
  92. package/core/src/ssr/lib/cache.d.ts +2 -2
  93. package/core/src/ssr/lib/cleanup.d.ts +2 -2
  94. package/core/src/ssr/lib/collections.d.ts +2 -2
  95. package/core/src/ssr/lib/compression.d.ts +2 -2
  96. package/core/src/ssr/lib/compute/content.d.ts +2 -9
  97. package/core/src/ssr/lib/compute/content.d.ts.map +1 -1
  98. package/core/src/ssr/lib/compute/index.d.ts +2 -2
  99. package/core/src/ssr/lib/compute/index.d.ts.map +1 -1
  100. package/core/src/ssr/lib/compute/layouts.d.ts +2 -2
  101. package/core/src/ssr/lib/compute/routes.d.ts +2 -2
  102. package/core/src/ssr/lib/compute/routes.d.ts.map +1 -1
  103. package/core/src/ssr/lib/content-path.d.ts +22 -0
  104. package/core/src/ssr/lib/content-path.d.ts.map +1 -0
  105. package/core/src/ssr/lib/data.d.ts +2 -2
  106. package/core/src/ssr/lib/data.d.ts.map +1 -1
  107. package/core/src/ssr/lib/html.d.ts +2 -2
  108. package/core/src/ssr/lib/imports.d.ts +2 -2
  109. package/core/src/ssr/lib/index.d.ts +3 -2
  110. package/core/src/ssr/lib/index.d.ts.map +1 -1
  111. package/core/src/ssr/lib/layouts.d.ts +2 -2
  112. package/core/src/ssr/lib/metadata.d.ts +2 -2
  113. package/core/src/ssr/lib/metadata.d.ts.map +1 -1
  114. package/core/src/ssr/lib/mime.d.ts +2 -2
  115. package/core/src/ssr/lib/modules.d.ts +4 -4
  116. package/core/src/ssr/lib/modules.d.ts.map +1 -1
  117. package/core/src/ssr/lib/path.d.ts +2 -2
  118. package/core/src/ssr/lib/preload.d.ts +2 -2
  119. package/core/src/ssr/lib/preload.d.ts.map +1 -1
  120. package/core/src/ssr/lib/route-match.d.ts +20 -0
  121. package/core/src/ssr/lib/route-match.d.ts.map +1 -0
  122. package/core/src/ssr/lib/scripts/collections.d.ts +2 -2
  123. package/core/src/ssr/lib/scripts/data.d.ts +3 -3
  124. package/core/src/ssr/lib/scripts/data.d.ts.map +1 -1
  125. package/core/src/ssr/lib/scripts/escape.d.ts +2 -2
  126. package/core/src/ssr/lib/scripts/feeds.d.ts +2 -2
  127. package/core/src/ssr/lib/scripts/index.d.ts +2 -2
  128. package/core/src/ssr/lib/seed.d.ts +2 -2
  129. package/core/src/ssr/lib/seed.d.ts.map +1 -1
  130. package/core/src/ssr/lib/serialize.d.ts +2 -2
  131. package/core/src/ssr/lib/setup.d.ts +3 -3
  132. package/core/src/ssr/lib/setup.d.ts.map +1 -1
  133. package/core/src/ssr/lib/styles.d.ts +2 -2
  134. package/core/src/ssr/lib/template.d.ts +2 -2
  135. package/core/src/ssr/lib/types.d.ts +2 -2
  136. package/core/src/ssr/render.d.ts +4 -2
  137. package/core/src/ssr/render.d.ts.map +1 -1
  138. package/core/src/ssr/stream.d.ts +4 -2
  139. package/core/src/ssr/stream.d.ts.map +1 -1
  140. package/elysia/index.d.ts +2 -2
  141. package/elysia/index.js +1072 -276
  142. package/elysia/index.js.map +37 -27
  143. package/elysia/src/index.d.ts +2 -2
  144. package/elysia/src/libs/assets.d.ts +2 -2
  145. package/elysia/src/libs/cache.d.ts +2 -2
  146. package/elysia/src/libs/caching.d.ts +2 -2
  147. package/elysia/src/libs/http.d.ts +2 -2
  148. package/elysia/src/libs/i18n.d.ts +26 -0
  149. package/elysia/src/libs/i18n.d.ts.map +1 -0
  150. package/elysia/src/libs/image.d.ts +2 -2
  151. package/elysia/src/libs/index.d.ts +2 -2
  152. package/elysia/src/libs/llms.d.ts +2 -2
  153. package/elysia/src/libs/response.d.ts +2 -2
  154. package/elysia/src/libs/serving.d.ts +2 -2
  155. package/elysia/src/plugin.d.ts +2 -2
  156. package/elysia/src/routes/artifacts.d.ts +2 -2
  157. package/elysia/src/routes/content.d.ts +2 -2
  158. package/elysia/src/routes/image.d.ts +2 -2
  159. package/elysia/src/routes/index.d.ts +2 -2
  160. package/elysia/src/routes/internal.d.ts +2 -2
  161. package/elysia/src/routes/llms.d.ts +2 -2
  162. package/elysia/src/routes/og.d.ts +2 -2
  163. package/elysia/src/routes/og.d.ts.map +1 -1
  164. package/elysia/src/routes/redirects.d.ts +2 -2
  165. package/elysia/src/routes/robots.d.ts +2 -2
  166. package/elysia/src/routes/rss.d.ts +2 -2
  167. package/elysia/src/routes/rss.d.ts.map +1 -1
  168. package/elysia/src/routes/search.d.ts +2 -2
  169. package/elysia/src/routes/search.d.ts.map +1 -1
  170. package/elysia/src/routes/sitemap.d.ts +2 -2
  171. package/elysia/src/routes/ssr.d.ts +2 -2
  172. package/elysia/src/routes/ssr.d.ts.map +1 -1
  173. package/elysia/src/routes/static.d.ts +2 -2
  174. package/elysia/src/types.d.ts +2 -2
  175. package/package.json +7 -6
  176. package/react/index.d.ts +2 -2
  177. package/react/index.js +565 -171
  178. package/react/index.js.map +12 -10
  179. package/react/src/components/ClientOnly.d.ts +2 -2
  180. package/react/src/components/ContentRoute.d.ts +3 -3
  181. package/react/src/components/ContentRoute.d.ts.map +1 -1
  182. package/react/src/components/Image.d.ts +2 -2
  183. package/react/src/components/LazyRoute.d.ts +2 -2
  184. package/react/src/components/Link.d.ts +2 -2
  185. package/react/src/components/Link.d.ts.map +1 -1
  186. package/react/src/components/Markdown.d.ts +2 -2
  187. package/react/src/components/Outlet.d.ts +2 -2
  188. package/react/src/components/index.d.ts +2 -2
  189. package/react/src/hooks/index.d.ts +2 -2
  190. package/react/src/hooks/useContent.d.ts +2 -2
  191. package/react/src/hooks/useContent.d.ts.map +1 -1
  192. package/react/src/hooks/useData.d.ts +2 -2
  193. package/react/src/hooks/useFeed.d.ts +2 -2
  194. package/react/src/hooks/useLayoutData.d.ts +2 -2
  195. package/react/src/hooks/useLlms.d.ts +2 -2
  196. package/react/src/hooks/useNavigate.d.ts +2 -2
  197. package/react/src/hooks/useParams.d.ts +2 -2
  198. package/react/src/hooks/useRouter.d.ts +2 -2
  199. package/react/src/hooks/useSearch.d.ts +2 -2
  200. package/react/src/hooks/useSearch.d.ts.map +1 -1
  201. package/react/src/hooks/useSearchParams.d.ts +2 -2
  202. package/react/src/hooks/useToc.d.ts +2 -2
  203. package/react/src/i18n/LocaleProvider.d.ts +49 -0
  204. package/react/src/i18n/LocaleProvider.d.ts.map +1 -0
  205. package/react/src/i18n/helpers.d.ts +24 -0
  206. package/react/src/i18n/helpers.d.ts.map +1 -0
  207. package/react/src/i18n/index.d.ts +13 -0
  208. package/react/src/i18n/index.d.ts.map +1 -0
  209. package/react/src/index.d.ts +3 -2
  210. package/react/src/index.d.ts.map +1 -1
  211. package/react/src/lib/collection.d.ts +2 -2
  212. package/react/src/lib/content.d.ts +4 -3
  213. package/react/src/lib/content.d.ts.map +1 -1
  214. package/react/src/lib/head.d.ts +2 -2
  215. package/react/src/lib/index.d.ts +2 -2
  216. package/react/src/lib/lazy-route.d.ts +2 -2
  217. package/react/src/lib/route-loader.d.ts +2 -2
  218. package/react/src/providers/ContentProvider.d.ts +3 -3
  219. package/react/src/providers/ContentProvider.d.ts.map +1 -1
  220. package/react/src/providers/RerouteProvider.d.ts +8 -6
  221. package/react/src/providers/RerouteProvider.d.ts.map +1 -1
  222. package/react/src/providers/RouterProvider.d.ts +2 -2
  223. package/react/src/providers/index.d.ts +2 -2
  224. package/react/src/types/any.d.ts +2 -2
  225. package/react/src/types/index.d.ts +2 -2
  226. package/react/src/types/router.d.ts +2 -2
  227. package/telemetry/react.d.ts +2 -2
  228. package/telemetry/react.js +8 -4
  229. package/telemetry/react.js.map +2 -2
  230. package/telemetry/server.d.ts +2 -2
  231. package/telemetry/server.js +60 -5
  232. package/telemetry/server.js.map +22 -17
  233. package/telemetry/src/react/api.d.ts +2 -2
  234. package/telemetry/src/react/context.d.ts +2 -2
  235. package/telemetry/src/react/index.d.ts +2 -2
  236. package/telemetry/src/react/telemetry.d.ts +2 -2
  237. package/telemetry/src/server/context.d.ts +2 -2
  238. package/telemetry/src/server/headers/extractor.d.ts +2 -2
  239. package/telemetry/src/server/headers/index.d.ts +2 -2
  240. package/telemetry/src/server/headers/presets.d.ts +2 -2
  241. package/telemetry/src/server/index.d.ts +2 -2
  242. package/telemetry/src/server/instrumentation.d.ts +2 -2
  243. package/telemetry/src/server/plugin.d.ts +2 -2
  244. package/telemetry/src/server/sourcemap.d.ts +2 -2
package/cli/bin.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
  /**
4
- * reroute-js v0.45.11
4
+ * reroute-js v0.46.0
5
5
  *
6
6
  * @license MIT
7
7
  * @copyright 2026 stewones <hi@stewan.io>
8
- * @see https://github.com/stewones/reroute
8
+ * @see https://github.com/Intenseloop/reroute
9
9
  *
10
10
  * Built with Bun <3
11
11
  */
@@ -15,25 +15,43 @@ var __getProtoOf = Object.getPrototypeOf;
15
15
  var __defProp = Object.defineProperty;
16
16
  var __getOwnPropNames = Object.getOwnPropertyNames;
17
17
  var __hasOwnProp = Object.prototype.hasOwnProperty;
18
+ function __accessProp(key) {
19
+ return this[key];
20
+ }
21
+ var __toESMCache_node;
22
+ var __toESMCache_esm;
18
23
  var __toESM = (mod, isNodeMode, target) => {
24
+ var canCache = mod != null && typeof mod === "object";
25
+ if (canCache) {
26
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
27
+ var cached = cache.get(mod);
28
+ if (cached)
29
+ return cached;
30
+ }
19
31
  target = mod != null ? __create(__getProtoOf(mod)) : {};
20
32
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
21
33
  for (let key of __getOwnPropNames(mod))
22
34
  if (!__hasOwnProp.call(to, key))
23
35
  __defProp(to, key, {
24
- get: () => mod[key],
36
+ get: __accessProp.bind(mod, key),
25
37
  enumerable: true
26
38
  });
39
+ if (canCache)
40
+ cache.set(mod, to);
27
41
  return to;
28
42
  };
29
43
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
44
+ var __returnValue = (v) => v;
45
+ function __exportSetter(name, newValue) {
46
+ this[name] = __returnValue.bind(null, newValue);
47
+ }
30
48
  var __export = (target, all) => {
31
49
  for (var name in all)
32
50
  __defProp(target, name, {
33
51
  get: all[name],
34
52
  enumerable: true,
35
53
  configurable: true,
36
- set: (newValue) => all[name] = () => newValue
54
+ set: __exportSetter.bind(all, name)
37
55
  });
38
56
  };
39
57
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -48,7 +66,7 @@ async function getVersionString() {
48
66
  }
49
67
  async function getVersion() {
50
68
  if (true) {
51
- return "0.45.11";
69
+ return "0.46.0";
52
70
  }
53
71
  const possiblePaths = [
54
72
  path.join(import.meta.dir, "../../../../package.json"),
@@ -66,7 +84,7 @@ async function getVersion() {
66
84
  }
67
85
  async function getCommit() {
68
86
  if (true) {
69
- return "c2463d3";
87
+ return "9116acf";
70
88
  }
71
89
  return "dev";
72
90
  }
@@ -518,6 +536,364 @@ var init_content = __esm(() => {
518
536
  init_metadata();
519
537
  });
520
538
 
539
+ // packages/core/src/i18n/catalog.ts
540
+ function getMessage(catalog, key) {
541
+ if (!(catalog && key))
542
+ return;
543
+ const parts = key.split(".");
544
+ let current = catalog;
545
+ for (const part of parts) {
546
+ if (current == null || typeof current === "string")
547
+ return;
548
+ current = current[part];
549
+ }
550
+ return typeof current === "string" ? current : undefined;
551
+ }
552
+ function lookupMessage(catalogs, locale, defaultLocale, key) {
553
+ return getMessage(catalogs[locale], key) ?? (locale !== defaultLocale ? getMessage(catalogs[defaultLocale], key) : undefined);
554
+ }
555
+ var init_catalog = () => {};
556
+
557
+ // packages/core/src/i18n/discover.ts
558
+ import { readdir as readdir3 } from "node:fs/promises";
559
+ import { join as join3 } from "node:path";
560
+ async function loadCatalog(filePath) {
561
+ try {
562
+ const text = await Bun.file(filePath).text();
563
+ return JSON.parse(text);
564
+ } catch {
565
+ return null;
566
+ }
567
+ }
568
+ async function loadLocaleCatalogs(absDir) {
569
+ const entries = await readdir3(absDir, { withFileTypes: true });
570
+ const catalogs = {};
571
+ const locales = [];
572
+ for (const entry of entries) {
573
+ if (!(entry.isFile() && entry.name.endsWith(".json")))
574
+ continue;
575
+ const locale = entry.name.slice(0, -".json".length);
576
+ if (!locale)
577
+ continue;
578
+ const catalog = await loadCatalog(join3(absDir, entry.name));
579
+ if (!catalog)
580
+ continue;
581
+ catalogs[locale] = catalog;
582
+ locales.push(locale);
583
+ }
584
+ return { catalogs, locales };
585
+ }
586
+ function sortLocales(locales, defaultLocale) {
587
+ locales.sort((a, b) => {
588
+ if (a === defaultLocale)
589
+ return -1;
590
+ if (b === defaultLocale)
591
+ return 1;
592
+ return a.localeCompare(b);
593
+ });
594
+ }
595
+ async function discoverI18n(cwd, options = {}) {
596
+ const localesDir = options.localesDir ?? DEFAULT_LOCALES_DIR;
597
+ const defaultLocale = options.defaultLocale ?? DEFAULT_LOCALE;
598
+ const cookie = options.cookie ?? DEFAULT_COOKIE;
599
+ const redirectContentOnNegotiate = options.redirectContentOnNegotiate ?? true;
600
+ const absDir = join3(cwd, localesDir);
601
+ const disabled = {
602
+ enabled: false,
603
+ defaultLocale,
604
+ locales: [],
605
+ localesDir,
606
+ cookie,
607
+ redirectContentOnNegotiate,
608
+ catalogs: {}
609
+ };
610
+ let catalogs;
611
+ let locales;
612
+ try {
613
+ ({ catalogs, locales } = await loadLocaleCatalogs(absDir));
614
+ } catch {
615
+ return disabled;
616
+ }
617
+ if (locales.length === 0) {
618
+ return disabled;
619
+ }
620
+ sortLocales(locales, defaultLocale);
621
+ if (!catalogs[defaultLocale]) {
622
+ console.warn(`[reroute/i18n] defaultLocale "${defaultLocale}" has no catalog; using "${locales[0]}"`);
623
+ }
624
+ return {
625
+ enabled: true,
626
+ defaultLocale: catalogs[defaultLocale] ? defaultLocale : locales[0],
627
+ locales,
628
+ localesDir,
629
+ cookie,
630
+ redirectContentOnNegotiate,
631
+ catalogs
632
+ };
633
+ }
634
+ var DEFAULT_LOCALE = "en-US", DEFAULT_LOCALES_DIR = "locales", DEFAULT_COOKIE = "reroute-locale";
635
+ var init_discover = () => {};
636
+
637
+ // packages/core/src/i18n/messages.ts
638
+ import { IntlMessageFormat } from "intl-messageformat";
639
+ function formatMessage(message, locale, values) {
640
+ if (!values || Object.keys(values).length === 0) {
641
+ if (!/\{[^}]+\}/.test(message))
642
+ return message;
643
+ }
644
+ const cacheKey = `${locale}\x00${message}`;
645
+ let formatter = formatterCache.get(cacheKey);
646
+ if (!formatter) {
647
+ formatter = new IntlMessageFormat(message, locale);
648
+ formatterCache.set(cacheKey, formatter);
649
+ }
650
+ try {
651
+ const result = formatter.format(values);
652
+ return typeof result === "string" ? result : String(result);
653
+ } catch {
654
+ return message;
655
+ }
656
+ }
657
+ function createTranslator(catalogs, locale, defaultLocale) {
658
+ return (key, values) => {
659
+ const message = lookupMessage(catalogs, locale, defaultLocale, key);
660
+ if (message == null)
661
+ return key;
662
+ return formatMessage(message, locale, values);
663
+ };
664
+ }
665
+ var formatterCache;
666
+ var init_messages = __esm(() => {
667
+ init_catalog();
668
+ formatterCache = new Map;
669
+ });
670
+
671
+ // packages/core/src/i18n/negotiate.ts
672
+ function negotiateLocale(acceptLanguage, availableLocales, defaultLocale) {
673
+ if (!availableLocales.length)
674
+ return defaultLocale;
675
+ if (!acceptLanguage)
676
+ return defaultLocale;
677
+ const availableLower = new Map(availableLocales.map((l) => [l.toLowerCase(), l]));
678
+ const byLang = new Map;
679
+ for (const locale of availableLocales) {
680
+ const lang = locale.split("-")[0].toLowerCase();
681
+ if (!byLang.has(lang))
682
+ byLang.set(lang, locale);
683
+ }
684
+ const ranges = acceptLanguage.split(",").map((part) => {
685
+ const [tag, ...params] = part.trim().split(";");
686
+ let q = 1;
687
+ for (const p of params) {
688
+ const [k, v] = p.trim().split("=");
689
+ if (k === "q" && v)
690
+ q = Number.parseFloat(v) || 0;
691
+ }
692
+ return { tag: tag.trim().toLowerCase(), q };
693
+ }).filter((r) => r.tag && r.q > 0).sort((a, b) => b.q - a.q);
694
+ for (const { tag } of ranges) {
695
+ if (tag === "*")
696
+ return defaultLocale;
697
+ const exact = availableLower.get(tag);
698
+ if (exact)
699
+ return exact;
700
+ const langOnly = byLang.get(tag.split("-")[0]);
701
+ if (langOnly)
702
+ return langOnly;
703
+ }
704
+ return defaultLocale;
705
+ }
706
+ function parseCookieValue(cookieHeader, name) {
707
+ if (!cookieHeader)
708
+ return;
709
+ const parts = cookieHeader.split(";");
710
+ for (const part of parts) {
711
+ const idx = part.indexOf("=");
712
+ if (idx === -1)
713
+ continue;
714
+ const key = part.slice(0, idx).trim();
715
+ if (key === name) {
716
+ return decodeURIComponent(part.slice(idx + 1).trim());
717
+ }
718
+ }
719
+ return;
720
+ }
721
+ function buildLocaleCookie(name, locale, maxAgeSeconds = 60 * 60 * 24 * 365) {
722
+ return `${name}=${encodeURIComponent(locale)}; Path=/; Max-Age=${maxAgeSeconds}; SameSite=Lax`;
723
+ }
724
+ var init_negotiate = () => {};
725
+
726
+ // packages/core/src/i18n/pathname.ts
727
+ var exports_pathname = {};
728
+ __export(exports_pathname, {
729
+ toOgLocale: () => toOgLocale,
730
+ parseContentPath: () => parseContentPath,
731
+ normalizePathname: () => normalizePathname,
732
+ looksLikeLocale: () => looksLikeLocale,
733
+ localizeHref: () => localizeHref,
734
+ localizeContentHref: () => localizeContentHref,
735
+ hreflangTags: () => hreflangTags,
736
+ contentKey: () => contentKey
737
+ });
738
+ function looksLikeLocale(segment) {
739
+ return /^[a-z]{2}(?:-[A-Za-z0-9]{2,8})*$/i.test(segment);
740
+ }
741
+ function normalizePathname(pathname) {
742
+ const pathOnly = pathname.split("?")[0].split("#")[0];
743
+ if (!pathOnly || pathOnly === "/")
744
+ return "/";
745
+ return pathOnly.startsWith("/") ? pathOnly : `/${pathOnly}`;
746
+ }
747
+ function parseContentPath(pathname, availableLocales = [], collections) {
748
+ const normalized = normalizePathname(pathname);
749
+ const parts = normalized.split("/").filter(Boolean);
750
+ if (parts.length < 2)
751
+ return;
752
+ const [collection, second, third] = parts;
753
+ if (collections && !collections.includes(collection)) {
754
+ return;
755
+ }
756
+ const localeSet = new Set(availableLocales.map((l) => l.toLowerCase()));
757
+ if (parts.length >= 3 && third) {
758
+ const secondIsLocale = localeSet.size > 0 ? localeSet.has(second.toLowerCase()) : looksLikeLocale(second);
759
+ if (secondIsLocale) {
760
+ const locale = availableLocales.find((l) => l.toLowerCase() === second.toLowerCase()) || second;
761
+ return {
762
+ collection,
763
+ name: third,
764
+ locale,
765
+ routingPathname: `/${collection}/${third}`,
766
+ pathname: normalized
767
+ };
768
+ }
769
+ }
770
+ return {
771
+ collection,
772
+ name: second,
773
+ routingPathname: `/${collection}/${second}`,
774
+ pathname: normalized
775
+ };
776
+ }
777
+ function localizeContentHref(collection, name, locale, defaultLocale) {
778
+ if (!locale || locale === defaultLocale) {
779
+ return `/${collection}/${name}`;
780
+ }
781
+ return `/${collection}/${locale}/${name}`;
782
+ }
783
+ function localizeHref(to, locale, defaultLocale, availableLocales = [], collections) {
784
+ try {
785
+ const url = new URL(to, "http://local");
786
+ const parsed = parseContentPath(url.pathname, availableLocales, collections);
787
+ if (!parsed)
788
+ return to;
789
+ url.pathname = localizeContentHref(parsed.collection, parsed.name, locale, defaultLocale);
790
+ return `${url.pathname}${url.search}${url.hash}`;
791
+ } catch {
792
+ return to;
793
+ }
794
+ }
795
+ function contentKey(collection, name, locale, defaultLocale = "en-US") {
796
+ if (locale && locale !== defaultLocale) {
797
+ return `${collection}:${name}:${locale}`;
798
+ }
799
+ return `${collection}:${name}`;
800
+ }
801
+ function toOgLocale(locale) {
802
+ return locale.replace(/-/g, "_");
803
+ }
804
+ function hreflangTags(baseUrl, collection, name, locales, defaultLocale) {
805
+ const base = baseUrl.replace(/\/$/, "");
806
+ const tags = [];
807
+ for (const locale of locales) {
808
+ const path2 = localizeContentHref(collection, name, locale, defaultLocale);
809
+ tags.push(`<link rel="alternate" hreflang="${locale}" href="${base}${path2}" />`);
810
+ }
811
+ const defaultPath = localizeContentHref(collection, name, defaultLocale, defaultLocale);
812
+ tags.push(`<link rel="alternate" hreflang="x-default" href="${base}${defaultPath}" />`);
813
+ return tags.join(`
814
+ `);
815
+ }
816
+ var init_pathname = () => {};
817
+
818
+ // packages/core/src/i18n/resolve.ts
819
+ function resolveLocale(input) {
820
+ const {
821
+ pathname,
822
+ cookieHeader,
823
+ acceptLanguage,
824
+ availableLocales,
825
+ defaultLocale,
826
+ cookieName = DEFAULT_COOKIE,
827
+ collections
828
+ } = input;
829
+ const parsed = parseContentPath(pathname, availableLocales, collections);
830
+ const availableLower = new Set(availableLocales.map((l) => l.toLowerCase()));
831
+ const isAvailable = (locale) => !!locale && availableLower.has(locale.toLowerCase());
832
+ if (parsed?.locale && isAvailable(parsed.locale)) {
833
+ const locale = availableLocales.find((l) => l.toLowerCase() === parsed.locale?.toLowerCase()) || parsed.locale;
834
+ if (locale === defaultLocale && parsed.pathname !== parsed.routingPathname) {
835
+ return {
836
+ locale,
837
+ routingPathname: parsed.routingPathname,
838
+ fromPath: true,
839
+ redirectTo: parsed.routingPathname,
840
+ parsed: { ...parsed, locale }
841
+ };
842
+ }
843
+ return {
844
+ locale,
845
+ routingPathname: parsed.routingPathname,
846
+ fromPath: true,
847
+ parsed: { ...parsed, locale }
848
+ };
849
+ }
850
+ const routingPathname = parsed?.routingPathname ?? pathname.split("?")[0];
851
+ const cookieLocale = parseCookieValue(cookieHeader, cookieName);
852
+ if (isAvailable(cookieLocale)) {
853
+ const locale = availableLocales.find((l) => l.toLowerCase() === cookieLocale.toLowerCase()) || cookieLocale;
854
+ return {
855
+ locale,
856
+ routingPathname,
857
+ fromPath: false,
858
+ parsed: parsed ? { ...parsed, locale: undefined } : undefined
859
+ };
860
+ }
861
+ const negotiated = negotiateLocale(acceptLanguage, availableLocales, defaultLocale);
862
+ return {
863
+ locale: negotiated,
864
+ routingPathname,
865
+ fromPath: false,
866
+ parsed: parsed ? { ...parsed, locale: undefined } : undefined
867
+ };
868
+ }
869
+ function contentNegotiateRedirect(resolution, defaultLocale, enabled) {
870
+ if (!enabled)
871
+ return;
872
+ if (resolution.fromPath)
873
+ return;
874
+ if (resolution.locale === defaultLocale)
875
+ return;
876
+ const parsed = resolution.parsed;
877
+ if (!parsed)
878
+ return;
879
+ return localizeContentHref(parsed.collection, parsed.name, resolution.locale, defaultLocale);
880
+ }
881
+ var init_resolve = __esm(() => {
882
+ init_discover();
883
+ init_negotiate();
884
+ init_pathname();
885
+ });
886
+
887
+ // packages/core/src/i18n/index.ts
888
+ var init_i18n = __esm(() => {
889
+ init_catalog();
890
+ init_discover();
891
+ init_messages();
892
+ init_negotiate();
893
+ init_pathname();
894
+ init_resolve();
895
+ });
896
+
521
897
  // packages/telemetry/src/server/context.ts
522
898
  import { AsyncLocalStorage } from "node:async_hooks";
523
899
  var contextStorage;
@@ -1112,6 +1488,148 @@ function compressStreamIfAccepted(stream, contentType, _acceptEncoding) {
1112
1488
  }
1113
1489
  var init_compression = () => {};
1114
1490
 
1491
+ // packages/core/src/ssr/lib/route-match.ts
1492
+ function getPatternParts(pattern) {
1493
+ return pattern.split("/").filter(Boolean);
1494
+ }
1495
+ function isDynamicPatternPart(patternPart) {
1496
+ return patternPart.startsWith(":");
1497
+ }
1498
+ function isCatchAllPatternPart(patternPart) {
1499
+ return patternPart.startsWith(":...");
1500
+ }
1501
+ function isCatchAllParamName(paramName) {
1502
+ return paramName.startsWith("...");
1503
+ }
1504
+ function getPatternPartSpecificity(patternPart) {
1505
+ if (isCatchAllPatternPart(patternPart)) {
1506
+ return 2;
1507
+ }
1508
+ if (isDynamicPatternPart(patternPart)) {
1509
+ return 1;
1510
+ }
1511
+ return 0;
1512
+ }
1513
+ function compareRouteSpecificity(a, b) {
1514
+ const aPatternParts = getPatternParts(a.pattern);
1515
+ const bPatternParts = getPatternParts(b.pattern);
1516
+ const aDepth = aPatternParts.length;
1517
+ const bDepth = bPatternParts.length;
1518
+ if (aDepth !== bDepth) {
1519
+ return bDepth - aDepth;
1520
+ }
1521
+ for (let i = 0;i < aPatternParts.length; i++) {
1522
+ const aSpecificity = getPatternPartSpecificity(aPatternParts[i]);
1523
+ const bSpecificity = getPatternPartSpecificity(bPatternParts[i]);
1524
+ if (aSpecificity !== bSpecificity) {
1525
+ return aSpecificity - bSpecificity;
1526
+ }
1527
+ if (aSpecificity === 0 && aPatternParts[i] !== bPatternParts[i]) {
1528
+ return aPatternParts[i].localeCompare(bPatternParts[i]);
1529
+ }
1530
+ }
1531
+ return a.pattern.localeCompare(b.pattern);
1532
+ }
1533
+ function matchCatchAllParam(paramName, patternIndex, patternLength, pathnameParts, pathnameIndex, params) {
1534
+ if (patternIndex !== patternLength - 1 || pathnameIndex >= pathnameParts.length) {
1535
+ return false;
1536
+ }
1537
+ params[paramName.slice(3)] = pathnameParts.slice(pathnameIndex).join("/");
1538
+ return true;
1539
+ }
1540
+ function matchDynamicParam(paramName, pathnameParts, pathnameIndex, params) {
1541
+ const pathnamePart = pathnameParts[pathnameIndex];
1542
+ if (pathnamePart === undefined) {
1543
+ return null;
1544
+ }
1545
+ params[paramName] = pathnamePart;
1546
+ return pathnameIndex + 1;
1547
+ }
1548
+ function matchPatternPart(patternPart, patternIndex, patternLength, pathnameParts, pathnameIndex, params) {
1549
+ if (isDynamicPatternPart(patternPart)) {
1550
+ const paramName = patternPart.slice(1);
1551
+ if (isCatchAllParamName(paramName)) {
1552
+ return matchCatchAllParam(paramName, patternIndex, patternLength, pathnameParts, pathnameIndex, params) ? "catch-all" : null;
1553
+ }
1554
+ return matchDynamicParam(paramName, pathnameParts, pathnameIndex, params);
1555
+ }
1556
+ if (patternPart !== pathnameParts[pathnameIndex]) {
1557
+ return null;
1558
+ }
1559
+ return pathnameIndex + 1;
1560
+ }
1561
+ function matchRoutePattern(pattern, pathname) {
1562
+ const patternParts = getPatternParts(pattern);
1563
+ const pathnameParts = pathname.split("/").filter(Boolean);
1564
+ const params = {};
1565
+ let pathnameIndex = 0;
1566
+ for (let i = 0;i < patternParts.length; i++) {
1567
+ const nextPathnameIndex = matchPatternPart(patternParts[i], i, patternParts.length, pathnameParts, pathnameIndex, params);
1568
+ if (nextPathnameIndex === null) {
1569
+ return null;
1570
+ }
1571
+ if (nextPathnameIndex === "catch-all") {
1572
+ return params;
1573
+ }
1574
+ pathnameIndex = nextPathnameIndex;
1575
+ }
1576
+ return pathnameIndex === pathnameParts.length ? params : null;
1577
+ }
1578
+ function matchRoutes(routes, pathname) {
1579
+ for (const route of routes) {
1580
+ const params = matchRoutePattern(route.pattern, pathname);
1581
+ if (params) {
1582
+ return { route, params };
1583
+ }
1584
+ }
1585
+ return null;
1586
+ }
1587
+
1588
+ // packages/core/src/ssr/lib/content-path.ts
1589
+ var exports_content_path = {};
1590
+ __export(exports_content_path, {
1591
+ resolveContentPathParts: () => resolveContentPathParts
1592
+ });
1593
+ function resolveContentPathParts(pathname) {
1594
+ const pathOnly = pathname.split("?")[0].split("#")[0];
1595
+ const parts = pathOnly.split("/").filter(Boolean);
1596
+ if (parts.length < 2)
1597
+ return null;
1598
+ const g = globalThis;
1599
+ const locales = g.__REROUTE_I18N__?.locales || [];
1600
+ const defaultLocale = g.__REROUTE_I18N__?.defaultLocale || "en-US";
1601
+ const parsed = parseContentPath(pathOnly, locales);
1602
+ if (parsed) {
1603
+ const locale = parsed.locale || defaultLocale;
1604
+ return {
1605
+ collection: parsed.collection,
1606
+ name: parsed.name,
1607
+ locale: parsed.locale,
1608
+ key: contentKey(parsed.collection, parsed.name, locale, defaultLocale),
1609
+ routingPathname: parsed.routingPathname
1610
+ };
1611
+ }
1612
+ if (parts.length >= 3 && looksLikeLocale(parts[1])) {
1613
+ const locale = parts[1];
1614
+ return {
1615
+ collection: parts[0],
1616
+ name: parts[2],
1617
+ locale,
1618
+ key: contentKey(parts[0], parts[2], locale, defaultLocale),
1619
+ routingPathname: `/${parts[0]}/${parts[2]}`
1620
+ };
1621
+ }
1622
+ return {
1623
+ collection: parts[0],
1624
+ name: parts[1],
1625
+ key: `${parts[0]}:${parts[1]}`,
1626
+ routingPathname: `/${parts[0]}/${parts[1]}`
1627
+ };
1628
+ }
1629
+ var init_content_path = __esm(() => {
1630
+ init_pathname();
1631
+ });
1632
+
1115
1633
  // packages/core/src/ssr/lib/compute/content.ts
1116
1634
  function checkStatusCache(statusCacheKey, maxAgeMs) {
1117
1635
  if (maxAgeMs <= 0)
@@ -1164,6 +1682,14 @@ async function handleNonStreamingMode(ssrData, pathname, task, cacheKey, statusC
1164
1682
  }
1165
1683
  return result;
1166
1684
  }
1685
+ async function runContentDataTask(streaming, ssrData, pathname, task, set, cacheKey, statusCacheKey, maxAgeMs, pendingTasks) {
1686
+ if (streaming) {
1687
+ handleStreamingMode(ssrData, pathname, task, set, cacheKey, statusCacheKey, maxAgeMs, pendingTasks);
1688
+ } else {
1689
+ await handleNonStreamingMode(ssrData, pathname, task, cacheKey, statusCacheKey, maxAgeMs);
1690
+ }
1691
+ return set.status !== 200 ? set.status : undefined;
1692
+ }
1167
1693
  async function computeContentData(pathname, options, searchParams, sharedSsrData) {
1168
1694
  const streaming = options.streaming === true;
1169
1695
  const ssrData = streaming && sharedSsrData ? sharedSsrData : {};
@@ -1173,18 +1699,20 @@ async function computeContentData(pathname, options, searchParams, sharedSsrData
1173
1699
  const maxAge = options.maxAge || 0;
1174
1700
  const maxAgeMs = maxAge * 1000;
1175
1701
  try {
1176
- const parts = pathname.split("/").filter(Boolean);
1177
- if (parts.length < 2) {
1702
+ const resolved = resolveContentPathParts(pathname);
1703
+ if (!resolved) {
1178
1704
  return { data: ssrData, pendingTasks };
1179
1705
  }
1706
+ const parts = [resolved.collection, resolved.name];
1707
+ if (resolved.locale)
1708
+ parts.splice(1, 0, resolved.locale);
1180
1709
  const g = globalThis;
1181
- const key = `${parts[0]}:${parts[1]}`;
1182
- const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
1710
+ const exp = g.__REROUTE_SSR_EXPORTS__?.[resolved.key];
1183
1711
  const dataFn = exp?.ssr?.data;
1184
1712
  if (typeof dataFn !== "function") {
1185
1713
  return { data: ssrData, pendingTasks };
1186
1714
  }
1187
- const cacheKey = `content:${pathname}:${parts[1]}`;
1715
+ const cacheKey = `content:${pathname}:${resolved.name}:${resolved.locale || ""}`;
1188
1716
  const statusCacheKey = `status:${cacheKey}`;
1189
1717
  const cachedStatus = checkStatusCache(statusCacheKey, maxAgeMs);
1190
1718
  if (cachedStatus && cachedStatus !== 200) {
@@ -1197,22 +1725,13 @@ async function computeContentData(pathname, options, searchParams, sharedSsrData
1197
1725
  }
1198
1726
  const set = { status: 200 };
1199
1727
  const task = createDataTask(dataFn, pathname, parts, searchParams, set, options.headers);
1200
- if (streaming) {
1201
- handleStreamingMode(ssrData, pathname, task, set, cacheKey, statusCacheKey, maxAgeMs, pendingTasks);
1202
- if (set.status !== 200) {
1203
- statusOverride = set.status;
1204
- }
1205
- } else {
1206
- await handleNonStreamingMode(ssrData, pathname, task, cacheKey, statusCacheKey, maxAgeMs);
1207
- if (set.status !== 200) {
1208
- statusOverride = set.status;
1209
- }
1210
- }
1728
+ statusOverride = await runContentDataTask(streaming, ssrData, pathname, task, set, cacheKey, statusCacheKey, maxAgeMs, pendingTasks) ?? statusOverride;
1211
1729
  } catch {}
1212
1730
  return { data: ssrData, error, statusOverride, pendingTasks };
1213
1731
  }
1214
1732
  var init_content2 = __esm(() => {
1215
1733
  init_cache();
1734
+ init_content_path();
1216
1735
  init_imports();
1217
1736
  });
1218
1737
 
@@ -1411,7 +1930,8 @@ async function computeRouteData(pathname, clientDir, _cwd, isWatchMode, options,
1411
1930
  }).catch(() => {}));
1412
1931
  };
1413
1932
  try {
1414
- const pathnameOnly = pathname.split("?")[0];
1933
+ const resolved = resolveContentPathParts(pathname);
1934
+ const pathnameOnly = (resolved?.routingPathname || pathname).split("?")[0];
1415
1935
  const match = typeof routesModule?.matchRoute === "function" ? routesModule.matchRoute(pathnameOnly) : null;
1416
1936
  const route = match?.route;
1417
1937
  const params = match?.params || {};
@@ -1574,44 +2094,18 @@ function handleFlattenedResult(entries, set, ctx, cache, setError, setStatus) {
1574
2094
  }
1575
2095
  var init_routes = __esm(() => {
1576
2096
  init_cache();
2097
+ init_content_path();
1577
2098
  init_imports();
1578
2099
  });
1579
2100
 
1580
2101
  // packages/core/src/ssr/lib/compute/index.ts
1581
- function matchPatternUtil(pattern, pathname) {
1582
- const patternParts = pattern.split("/").filter(Boolean);
1583
- const pathnameParts = pathname.split("/").filter(Boolean);
1584
- if (patternParts.length !== pathnameParts.length) {
1585
- return null;
1586
- }
1587
- const params = {};
1588
- for (let i = 0;i < patternParts.length; i++) {
1589
- const patternPart = patternParts[i];
1590
- const pathnamePart = pathnameParts[i];
1591
- if (patternPart.startsWith(":")) {
1592
- params[patternPart.slice(1)] = pathnamePart;
1593
- } else if (patternPart !== pathnamePart) {
1594
- return null;
1595
- }
1596
- }
1597
- return params;
1598
- }
1599
2102
  function createBundledRoutesModule(bundledRoutes, bundledLayouts) {
1600
2103
  const bundledNotFoundRoutes = globalThis.__REROUTE_NOT_FOUND_ROUTES__;
1601
2104
  return {
1602
2105
  routes: bundledRoutes,
1603
2106
  layouts: bundledLayouts,
1604
2107
  notFoundRoutes: bundledNotFoundRoutes,
1605
- matchRoute: (pathname) => {
1606
- for (const route of bundledRoutes) {
1607
- const pattern = String(route?.pattern || "");
1608
- const match = matchPatternUtil(pattern, pathname);
1609
- if (match) {
1610
- return { route, params: match };
1611
- }
1612
- }
1613
- return null;
1614
- }
2108
+ matchRoute: (pathname) => matchRoutes(bundledRoutes, pathname)
1615
2109
  };
1616
2110
  }
1617
2111
  async function loadRoutesModule(cwd, isWatchMode) {
@@ -1693,33 +2187,31 @@ var init_compute = __esm(() => {
1693
2187
  });
1694
2188
 
1695
2189
  // packages/core/src/ssr/lib/modules.ts
1696
- import { readdir as readdir3, stat as stat3 } from "node:fs/promises";
2190
+ import { readdir as readdir4, stat as stat3 } from "node:fs/promises";
1697
2191
  async function importContentModuleForPath(pathname, clientDir, cwd, isWatchMode) {
1698
2192
  try {
1699
- const pathOnly = pathname.split("?")[0].split("#")[0];
1700
- const parts = pathOnly.split("/").filter(Boolean);
1701
- if (parts.length < 2)
2193
+ const resolved = resolveContentPathParts(pathname);
2194
+ if (!resolved)
1702
2195
  return null;
1703
- const collection = parts[0];
1704
- const name = parts[1];
1705
- const registryModule = await tryRegistryImport(collection, name, cwd, isWatchMode);
2196
+ const { collection, name, locale } = resolved;
2197
+ const registryModule = await tryRegistryImport(collection, name, cwd, isWatchMode, locale);
1706
2198
  if (registryModule)
1707
2199
  return registryModule;
1708
2200
  const chunkModule = await tryChunkImport(collection, name, cwd, isWatchMode);
1709
2201
  if (chunkModule)
1710
2202
  return chunkModule;
1711
- const sourceModule = await trySourceImport(collection, name, clientDir, isWatchMode);
2203
+ const sourceModule = await trySourceImport(collection, name, clientDir, isWatchMode, locale);
1712
2204
  if (sourceModule)
1713
2205
  return sourceModule;
1714
2206
  } catch {}
1715
2207
  return null;
1716
2208
  }
1717
- async function tryRegistryImport(collection, name, cwd, isWatchMode) {
2209
+ async function tryRegistryImport(collection, name, cwd, isWatchMode, locale) {
1718
2210
  try {
1719
2211
  const registryPath = join2(cwd, ".reroute", "content.ts");
1720
2212
  const reg = await dynamicImport(registryPath, isWatchMode);
1721
2213
  const get = reg?.getContentEntry;
1722
- const entry = typeof get === "function" ? get(collection, name) : undefined;
2214
+ const entry = typeof get === "function" ? get(collection, name, locale) : undefined;
1723
2215
  const moduleUrl = entry?.module;
1724
2216
  if (!moduleUrl?.endsWith(".js"))
1725
2217
  return null;
@@ -1745,7 +2237,7 @@ async function tryRegistryImport(collection, name, cwd, isWatchMode) {
1745
2237
  async function tryChunkImport(collection, name, cwd, isWatchMode) {
1746
2238
  try {
1747
2239
  const chunkDir = join2(cwd, ".reroute", "chunks", collection);
1748
- const files = await readdir3(chunkDir);
2240
+ const files = await readdir4(chunkDir);
1749
2241
  const matches = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
1750
2242
  if (!matches.length)
1751
2243
  return null;
@@ -1765,23 +2257,23 @@ async function tryChunkImport(collection, name, cwd, isWatchMode) {
1765
2257
  } catch {}
1766
2258
  return null;
1767
2259
  }
1768
- async function trySourceImport(collection, name, clientDir, isWatchMode) {
2260
+ async function trySourceImport(collection, name, clientDir, isWatchMode, locale) {
1769
2261
  try {
1770
- const srcTsx = join2(clientDir, "routes", collection, "content", `${name}.tsx`);
1771
- const srcTs = join2(clientDir, "routes", collection, "content", `${name}.ts`);
1772
- let absSrc = null;
1773
- if (await Bun.file(srcTsx).exists()) {
1774
- absSrc = srcTsx;
1775
- } else if (await Bun.file(srcTs).exists()) {
1776
- absSrc = srcTs;
2262
+ const candidates = [];
2263
+ if (locale) {
2264
+ candidates.push(join2(clientDir, "routes", collection, "content", locale, `${name}.tsx`), join2(clientDir, "routes", collection, "content", locale, `${name}.ts`));
1777
2265
  }
1778
- if (absSrc) {
1779
- return await dynamicImport(absSrc, isWatchMode);
2266
+ candidates.push(join2(clientDir, "routes", collection, "content", `${name}.tsx`), join2(clientDir, "routes", collection, "content", `${name}.ts`));
2267
+ for (const absSrc of candidates) {
2268
+ if (await Bun.file(absSrc).exists()) {
2269
+ return await dynamicImport(absSrc, isWatchMode);
2270
+ }
1780
2271
  }
1781
2272
  } catch {}
1782
2273
  return null;
1783
2274
  }
1784
2275
  var init_modules = __esm(() => {
2276
+ init_content_path();
1785
2277
  init_imports();
1786
2278
  });
1787
2279
 
@@ -1812,14 +2304,11 @@ function touchModule(key) {
1812
2304
  }
1813
2305
  async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
1814
2306
  try {
1815
- const pathOnly = pathname.split("?")[0].split("#")[0];
1816
- const parts = pathOnly.split("/").filter(Boolean);
1817
- if (parts.length < 2) {
2307
+ const resolved = resolveContentPathParts(pathname);
2308
+ if (!resolved) {
1818
2309
  return;
1819
2310
  }
1820
- const collection = parts[0];
1821
- const name = parts[1];
1822
- const key = `${collection}:${name}`;
2311
+ const { key } = resolved;
1823
2312
  const mod = await importContentModuleForPath(pathname, clientDir, cwd, isWatchMode);
1824
2313
  if (!mod) {
1825
2314
  return;
@@ -1839,42 +2328,25 @@ async function seedSSRModuleForPath(pathname, clientDir, cwd, isWatchMode) {
1839
2328
  }
1840
2329
  var MAX_SSR_MODULES = 200, moduleAccessOrder;
1841
2330
  var init_seed = __esm(() => {
2331
+ init_content_path();
1842
2332
  init_modules();
1843
2333
  moduleAccessOrder = [];
1844
2334
  });
1845
2335
 
1846
2336
  // packages/core/src/ssr/lib/data.ts
1847
- function matchPattern(pattern, pathname) {
1848
- const patternParts = pattern.split("/").filter(Boolean);
1849
- const pathnameParts = pathname.split("/").filter(Boolean);
1850
- if (patternParts.length !== pathnameParts.length) {
1851
- return null;
1852
- }
1853
- const params = {};
1854
- for (let i = 0;i < patternParts.length; i++) {
1855
- const patternPart = patternParts[i];
1856
- const pathnamePart = pathnameParts[i];
1857
- if (patternPart.startsWith(":")) {
1858
- params[patternPart.slice(1)] = pathnamePart;
1859
- } else if (patternPart !== pathnamePart) {
1860
- return null;
1861
- }
1862
- }
1863
- return params;
1864
- }
1865
2337
  function loadContentData(pathname, searchParams) {
1866
2338
  try {
1867
- const parts = pathname.split("/").filter(Boolean);
1868
- if (parts.length < 2)
2339
+ const resolved = resolveContentPathParts(pathname);
2340
+ if (!resolved)
1869
2341
  return null;
1870
- const key = `${parts[0]}:${parts[1]}`;
2342
+ const key = resolved.key;
1871
2343
  const g = globalThis;
1872
2344
  const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
1873
2345
  const dataFn = exp?.ssr?.data;
1874
2346
  if (typeof dataFn === "function") {
1875
2347
  const rawResult = dataFn({
1876
2348
  pathname,
1877
- params: { name: parts[1] },
2349
+ params: { name: resolved.name },
1878
2350
  searchParams
1879
2351
  });
1880
2352
  const result = processDataResult(rawResult, pathname, pathname);
@@ -1896,16 +2368,7 @@ async function loadRouteDataInternal(pathname, clientDir, cwd, isWatchMode, sear
1896
2368
  routes = m?.routes;
1897
2369
  matchRouteFn = m?.matchRoute;
1898
2370
  } else {
1899
- matchRouteFn = (pathname2) => {
1900
- for (const route of routes || []) {
1901
- const pattern = String(route?.pattern || "");
1902
- const match2 = matchPattern(pattern, pathname2);
1903
- if (match2) {
1904
- return { route, params: match2 };
1905
- }
1906
- }
1907
- return null;
1908
- };
2371
+ matchRouteFn = (pathname2) => matchRoutes(routes || [], pathname2);
1909
2372
  }
1910
2373
  const pathnameOnly = pathname.split("?")[0];
1911
2374
  const match = typeof matchRouteFn === "function" ? matchRouteFn(pathnameOnly) : null;
@@ -2046,6 +2509,7 @@ function createLazyMarker(pathname, key) {
2046
2509
  };
2047
2510
  }
2048
2511
  var init_data = __esm(() => {
2512
+ init_content_path();
2049
2513
  init_imports();
2050
2514
  init_seed();
2051
2515
  });
@@ -2246,18 +2710,53 @@ function replaceAppRoot(html, appHtml, appId) {
2246
2710
  var init_html = () => {};
2247
2711
 
2248
2712
  // packages/core/src/og/discovery.ts
2713
+ import { readdir as readdir5 } from "node:fs/promises";
2249
2714
  import { pathToFileURL as pathToFileURL4 } from "node:url";
2715
+ function isCatchAllRouteDirectory(name) {
2716
+ return CATCH_ALL_ROUTE_DIRECTORY.test(name);
2717
+ }
2718
+ async function listDynamicRouteDirectories(baseDir) {
2719
+ try {
2720
+ const entries = await readdir5(baseDir, { withFileTypes: true });
2721
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => DYNAMIC_ROUTE_DIRECTORY.test(name)).sort((a, b) => {
2722
+ const aIsCatchAll = isCatchAllRouteDirectory(a);
2723
+ const bIsCatchAll = isCatchAllRouteDirectory(b);
2724
+ if (aIsCatchAll !== bIsCatchAll) {
2725
+ return Number(aIsCatchAll) - Number(bIsCatchAll);
2726
+ }
2727
+ return a.localeCompare(b);
2728
+ });
2729
+ } catch {
2730
+ return [];
2731
+ }
2732
+ }
2733
+ async function collectOGCandidatePaths(currentDir, parts, depth, pathsToTry, allowPartialFallback) {
2734
+ if (depth === parts.length) {
2735
+ pathsToTry.push(join2(currentDir, "[og].tsx"));
2736
+ return;
2737
+ }
2738
+ const literalDir = join2(currentDir, parts[depth]);
2739
+ try {
2740
+ await readdir5(literalDir);
2741
+ await collectOGCandidatePaths(literalDir, parts, depth + 1, pathsToTry, true);
2742
+ } catch {}
2743
+ const dynamicDirs = await listDynamicRouteDirectories(currentDir);
2744
+ for (const dynamicDir of dynamicDirs) {
2745
+ if (isCatchAllRouteDirectory(dynamicDir)) {
2746
+ pathsToTry.push(join2(currentDir, dynamicDir, "[og].tsx"));
2747
+ continue;
2748
+ }
2749
+ await collectOGCandidatePaths(join2(currentDir, dynamicDir), parts, depth + 1, pathsToTry, false);
2750
+ }
2751
+ if (allowPartialFallback) {
2752
+ pathsToTry.push(join2(currentDir, "[og].tsx"));
2753
+ }
2754
+ }
2250
2755
  async function findOGImageForPath(pathname, clientDir) {
2251
2756
  const parts = pathname.split("/").filter(Boolean).filter((p) => !p.startsWith("__reroute"));
2252
2757
  const routesDir = join2(clientDir, "routes");
2253
2758
  const pathsToTry = [];
2254
- if (parts.length > 0) {
2255
- for (let i = parts.length;i > 0; i--) {
2256
- const dirPath = join2(routesDir, ...parts.slice(0, i), "[og].tsx");
2257
- pathsToTry.push(dirPath);
2258
- }
2259
- }
2260
- pathsToTry.push(join2(routesDir, "[og].tsx"));
2759
+ await collectOGCandidatePaths(routesDir, parts, 0, pathsToTry, true);
2261
2760
  const uniquePaths = [...new Set(pathsToTry)];
2262
2761
  for (const path2 of uniquePaths) {
2263
2762
  try {
@@ -2281,28 +2780,28 @@ async function loadOGImageComponent(ogPath, isWatchMode) {
2281
2780
  function extractParamsFromPath(pathname, ogPath, clientDir) {
2282
2781
  const params = {};
2283
2782
  const ogDir = ogPath.replace(/\/\[og\]\.tsx$/, "");
2284
- const pagesDir = join2(clientDir, "pages");
2285
- const relativePath = ogDir.replace(pagesDir, "").split("/").filter(Boolean);
2783
+ const routesDir = join2(clientDir, "routes");
2784
+ const relativePath = ogDir.replace(routesDir, "").split("/").filter(Boolean);
2286
2785
  const pathnameParts = pathname.split("/").filter(Boolean);
2287
2786
  for (let i = 0;i < relativePath.length && i < pathnameParts.length; i++) {
2288
2787
  const part = relativePath[i];
2289
- const match = part.match(/^\[([^\]]+)\]$/);
2290
- if (match) {
2291
- const paramName = match[1];
2292
- params[paramName] = pathnameParts[i];
2788
+ const catchAllMatch = part.match(/^\[\.\.\.([^\]]+)\]$/);
2789
+ if (catchAllMatch) {
2790
+ params[catchAllMatch[1]] = pathnameParts.slice(i).join("/");
2791
+ break;
2293
2792
  }
2294
- }
2295
- if (pathnameParts.length > relativePath.length && relativePath.length > 0) {
2296
- const lastRelativePart = relativePath[relativePath.length - 1];
2297
- const match = lastRelativePart.match(/^\[([^\]]+)\]$/);
2298
- if (match) {
2299
- const paramName = match[1];
2300
- params[paramName] = pathnameParts[relativePath.length - 1];
2793
+ const dynamicMatch = part.match(/^\[([^\]]+)\]$/);
2794
+ if (dynamicMatch) {
2795
+ params[dynamicMatch[1]] = pathnameParts[i];
2301
2796
  }
2302
2797
  }
2303
2798
  return params;
2304
2799
  }
2305
- var init_discovery2 = () => {};
2800
+ var CATCH_ALL_ROUTE_DIRECTORY, DYNAMIC_ROUTE_DIRECTORY;
2801
+ var init_discovery2 = __esm(() => {
2802
+ CATCH_ALL_ROUTE_DIRECTORY = /^\[\.\.\.([^\]]+)\]$/;
2803
+ DYNAMIC_ROUTE_DIRECTORY = /^\[(?:\.\.\.)?[^\]]+\]$/;
2804
+ });
2306
2805
 
2307
2806
  // packages/core/src/og/meta.ts
2308
2807
  async function generateOGImageMetaTags(pathname, clientDir, ogConfig) {
@@ -2393,7 +2892,9 @@ async function extractPageMetadata(pathname, clientDir, cwd, isWatchMode, curren
2393
2892
  let perPageHead = "";
2394
2893
  let pageLang;
2395
2894
  let statusOverride = currentStatusOverride;
2396
- await extractLayoutMetadata(pathname, clientDir, cwd, isWatchMode, (head) => {
2895
+ const resolved = resolveContentPathParts(pathname);
2896
+ const routingPathname = resolved?.routingPathname || pathname;
2897
+ await extractLayoutMetadata(routingPathname, clientDir, cwd, isWatchMode, (head) => {
2397
2898
  perPageHead += head;
2398
2899
  }, (lang) => {
2399
2900
  if (!pageLang)
@@ -2428,8 +2929,8 @@ ${ogMetaTags}`;
2428
2929
  perPageHead += head;
2429
2930
  }, (lang) => {
2430
2931
  pageLang = lang;
2431
- }, ogConfig, titleSuffix);
2432
- const routeResult = await extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix);
2932
+ }, ogConfig, titleSuffix, baseUrl);
2933
+ const routeResult = await extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname);
2433
2934
  perPageHead += routeResult.head;
2434
2935
  if (routeResult.lang)
2435
2936
  pageLang = routeResult.lang;
@@ -2469,14 +2970,42 @@ async function extractLayoutMetadata(pathname, clientDir, cwd, isWatchMode, addH
2469
2970
  }
2470
2971
  } catch {}
2471
2972
  }
2472
- function extractContentMetadata(pathname, addHead, setLang, ogConfig, titleSuffix) {
2973
+ function resolveContentLocales(collection, name, fallbackLocales) {
2974
+ const g = globalThis;
2975
+ const byCollection = g.__REROUTE_COLLECTIONS__;
2976
+ const items = byCollection?.[collection] || [];
2977
+ const localesForEntry = [
2978
+ ...new Set(items.filter((it) => it.name === name && it.locale).map((it) => String(it.locale)))
2979
+ ];
2980
+ return localesForEntry.length > 0 ? localesForEntry : fallbackLocales;
2981
+ }
2982
+ function addContentI18nHead(addHead, baseUrl, collection, name, locales, defaultLocale, activeLocale) {
2983
+ if (locales.length > 1) {
2984
+ addHead(`
2985
+ ${hreflangTags(baseUrl, collection, name, locales, defaultLocale)}`);
2986
+ }
2987
+ addHead(`
2988
+ <meta property="og:locale" content="${toOgLocale(activeLocale)}" />`);
2989
+ for (const loc of locales) {
2990
+ if (loc === activeLocale)
2991
+ continue;
2992
+ addHead(`
2993
+ <meta property="og:locale:alternate" content="${toOgLocale(loc)}" />`);
2994
+ }
2995
+ const canonicalPath = localizeContentHref(collection, name, activeLocale, defaultLocale);
2996
+ const base = baseUrl.replace(/\/$/, "");
2997
+ addHead(`
2998
+ <link rel="canonical" href="${base}${canonicalPath}" />`);
2999
+ addHead(`
3000
+ <meta property="og:url" content="${base}${canonicalPath}" />`);
3001
+ }
3002
+ function extractContentMetadata(pathname, addHead, setLang, ogConfig, titleSuffix, baseUrl) {
2473
3003
  try {
2474
- const parts = pathname.split("/").filter(Boolean);
2475
- if (parts.length < 2)
3004
+ const resolved = resolveContentPathParts(pathname);
3005
+ if (!resolved)
2476
3006
  return;
2477
- const key = `${parts[0]}:${parts[1]}`;
2478
3007
  const g = globalThis;
2479
- const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
3008
+ const exp = g.__REROUTE_SSR_EXPORTS__?.[resolved.key];
2480
3009
  const meta = exp?.meta;
2481
3010
  const ssr = exp?.ssr;
2482
3011
  addHead(buildHeadFromMeta(meta, ogConfig, titleSuffix));
@@ -2487,15 +3016,20 @@ ${ssrHead}`);
2487
3016
  const lang = extractSSRLang(ssr);
2488
3017
  if (lang)
2489
3018
  setLang(lang);
3019
+ const i18n = g.__REROUTE_I18N__;
3020
+ if (i18n?.enabled && baseUrl && i18n.locales && i18n.locales.length > 1) {
3021
+ const locales = resolveContentLocales(resolved.collection, resolved.name, i18n.locales);
3022
+ addContentI18nHead(addHead, baseUrl, resolved.collection, resolved.name, locales, i18n.defaultLocale || "en-US", i18n.locale || i18n.defaultLocale || "en-US");
3023
+ }
2490
3024
  } catch {}
2491
3025
  }
2492
3026
  function getContentMetaFromExports(pathname) {
2493
3027
  try {
2494
- const parts = pathname.split("/").filter(Boolean);
2495
- if (parts.length < 2)
3028
+ const resolved = resolveContentPathParts(pathname);
3029
+ if (!resolved)
2496
3030
  return;
2497
- const slug = parts[1];
2498
- const key = `${parts[0]}:${slug}`;
3031
+ const slug = resolved.name;
3032
+ const key = resolved.key;
2499
3033
  const g = globalThis;
2500
3034
  const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
2501
3035
  const meta = exp?.meta;
@@ -2509,9 +3043,10 @@ function getContentMetaFromExports(pathname) {
2509
3043
  return;
2510
3044
  }
2511
3045
  }
2512
- async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix) {
3046
+ async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname) {
2513
3047
  const pathnameOnly = pathname.split("?")[0];
2514
- const match = typeof m.matchRoute === "function" ? m.matchRoute(pathnameOnly) : null;
3048
+ const matchPath = (routingPathname || pathname).split("?")[0];
3049
+ const match = typeof m.matchRoute === "function" ? m.matchRoute(matchPath) : null;
2515
3050
  const r = match?.route;
2516
3051
  if (!r) {
2517
3052
  return { head: "", statusOverride: statusOverride || 404 };
@@ -2551,7 +3086,7 @@ ${ssrHead}`;
2551
3086
  }
2552
3087
  return { head: "", statusOverride };
2553
3088
  }
2554
- async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ssrData, ogConfig, titleSuffix) {
3089
+ async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ssrData, ogConfig, titleSuffix, routingPathname) {
2555
3090
  let head = "";
2556
3091
  let lang;
2557
3092
  let statusOverride = currentStatusOverride;
@@ -2561,14 +3096,14 @@ async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, curre
2561
3096
  if (!m) {
2562
3097
  return { head, lang, statusOverride: statusOverride || 404 };
2563
3098
  }
2564
- const pathnameOnly = pathname.split("?")[0];
2565
- const match = typeof m.matchRoute === "function" ? m.matchRoute(pathnameOnly) : null;
3099
+ const matchPath = (routingPathname || pathname).split("?")[0];
3100
+ const match = typeof m.matchRoute === "function" ? m.matchRoute(matchPath) : null;
2566
3101
  const r = match?.route;
2567
3102
  if (!r) {
2568
3103
  statusOverride = statusOverride || 404;
2569
3104
  }
2570
3105
  if (r && typeof r.path === "string") {
2571
- const result = await extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix);
3106
+ const result = await extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname);
2572
3107
  head = result.head;
2573
3108
  lang = result.lang;
2574
3109
  statusOverride = result.statusOverride;
@@ -2743,34 +3278,34 @@ function applyTitleSuffixToHead(headContent, titleSuffix) {
2743
3278
  }
2744
3279
  var init_metadata2 = __esm(() => {
2745
3280
  init_metadata();
3281
+ init_pathname();
2746
3282
  init_meta();
2747
3283
  init_compute();
3284
+ init_content_path();
2748
3285
  init_imports();
2749
3286
  });
2750
3287
 
2751
3288
  // packages/core/src/ssr/lib/preload.ts
2752
- import { readdir as readdir4, stat as stat4 } from "node:fs/promises";
3289
+ import { readdir as readdir6, stat as stat4 } from "node:fs/promises";
2753
3290
  async function preloadContentModule(pathname, clientDir, cwd, isWatchMode) {
2754
3291
  let extraHead = "";
2755
3292
  let hydrationScript = "";
2756
3293
  let statusOverride;
2757
3294
  try {
2758
- const pathOnly = pathname.split("?")[0].split("#")[0];
2759
- const parts = pathOnly.split("/").filter(Boolean);
2760
- if (parts.length < 2) {
3295
+ const { resolveContentPathParts: resolveContentPathParts2 } = await Promise.resolve().then(() => (init_content_path(), exports_content_path));
3296
+ const resolved = resolveContentPathParts2(pathname);
3297
+ if (!resolved) {
2761
3298
  return { extraHead, hydrationScript };
2762
3299
  }
2763
- const collection = parts[0];
2764
- const name = parts[1];
3300
+ const { collection, name, key, locale } = resolved;
2765
3301
  if (!await isContentCollection(clientDir, collection)) {
2766
3302
  return { extraHead, hydrationScript };
2767
3303
  }
2768
- const modulePath = await findContentModulePath(collection, name, clientDir, cwd, isWatchMode);
3304
+ const modulePath = await findContentModulePath(collection, name, clientDir, cwd, isWatchMode, locale);
2769
3305
  if (!modulePath) {
2770
3306
  statusOverride = 404;
2771
3307
  return { extraHead, hydrationScript, statusOverride };
2772
3308
  }
2773
- const key = `${collection}:${name}`;
2774
3309
  extraHead += `
2775
3310
  <link rel="modulepreload" href="${modulePath}" />`;
2776
3311
  hydrationScript += `<script>
@@ -2812,12 +3347,12 @@ async function findLatestCandidate(chunkDir, candidates) {
2812
3347
  }
2813
3348
  return latest;
2814
3349
  }
2815
- async function findContentModulePath(collection, name, clientDir, cwd, isWatchMode) {
3350
+ async function findContentModulePath(collection, name, clientDir, cwd, isWatchMode, locale) {
2816
3351
  try {
2817
3352
  const registryPath = join2(cwd, ".reroute", "content.ts");
2818
3353
  const reg = await dynamicImport(registryPath, isWatchMode);
2819
3354
  const get = reg?.getContentEntry;
2820
- const entry = typeof get === "function" ? get(collection, name) : undefined;
3355
+ const entry = typeof get === "function" ? get(collection, name, locale) : undefined;
2821
3356
  const moduleUrl = entry?.module;
2822
3357
  if (moduleUrl?.endsWith(".js")) {
2823
3358
  return moduleUrl;
@@ -2825,7 +3360,7 @@ async function findContentModulePath(collection, name, clientDir, cwd, isWatchMo
2825
3360
  } catch {}
2826
3361
  try {
2827
3362
  const chunkDir = join2(cwd, ".reroute", "chunks", collection);
2828
- const files = await readdir4(chunkDir);
3363
+ const files = await readdir6(chunkDir);
2829
3364
  const candidates = files.filter((n) => n.startsWith(`${name}.`) && n.endsWith(".js"));
2830
3365
  if (candidates.length) {
2831
3366
  const latest = await findLatestCandidate(chunkDir, candidates);
@@ -2833,13 +3368,18 @@ async function findContentModulePath(collection, name, clientDir, cwd, isWatchMo
2833
3368
  }
2834
3369
  } catch {}
2835
3370
  try {
2836
- const tsx = join2(clientDir, "routes", collection, "content", `${name}.tsx`);
2837
- const ts = join2(clientDir, "routes", collection, "content", `${name}.ts`);
2838
- if (await Bun.file(tsx).exists()) {
2839
- return `/${join2("routes", collection, "content", `${name}.tsx`).replace(/\\+/g, "/")}`;
2840
- }
2841
- if (await Bun.file(ts).exists()) {
2842
- return `/${join2("routes", collection, "content", `${name}.ts`).replace(/\\+/g, "/")}`;
3371
+ const contentRoot = join2(clientDir, "routes", collection, "content");
3372
+ const candidates = locale ? [
3373
+ join2(contentRoot, locale, `${name}.tsx`),
3374
+ join2(contentRoot, locale, `${name}.ts`),
3375
+ join2(contentRoot, `${name}.tsx`),
3376
+ join2(contentRoot, `${name}.ts`)
3377
+ ] : [join2(contentRoot, `${name}.tsx`), join2(contentRoot, `${name}.ts`)];
3378
+ for (const abs of candidates) {
3379
+ if (await Bun.file(abs).exists()) {
3380
+ const rel = abs.startsWith(clientDir) ? abs.slice(clientDir.length).replace(/^[/\\]/, "") : join2("routes", collection, "content", `${name}.tsx`);
3381
+ return `/${rel.replace(/\\+/g, "/")}`;
3382
+ }
2843
3383
  }
2844
3384
  } catch {}
2845
3385
  return null;
@@ -3033,11 +3573,18 @@ function duplicateQueryParamData(ssrData) {
3033
3573
  return duplicated;
3034
3574
  }
3035
3575
  function getContentKeyFromPathname(pathname) {
3036
- const pathOnly = pathname.split("?")[0].split("#")[0];
3037
- const parts = pathOnly.split("/").filter(Boolean);
3038
- if (parts.length < 2)
3039
- return null;
3040
- return `${parts[0]}:${parts[1]}`;
3576
+ return resolveContentPathParts(pathname)?.key ?? null;
3577
+ }
3578
+ function createI18nScript(i18n) {
3579
+ if (!i18n?.enabled)
3580
+ return "";
3581
+ try {
3582
+ const json2 = escapeJsonForScript(JSON.stringify(i18n));
3583
+ return `<script>(function(){try{var w=(typeof window!=='undefined'?window:globalThis);w.__REROUTE_I18N__=${json2};}catch(e){}})();</script>`;
3584
+ } catch (error) {
3585
+ console.error("[reroute] Failed to serialize i18n hydration:", error);
3586
+ return "";
3587
+ }
3041
3588
  }
3042
3589
  function createContentCaptureScript() {
3043
3590
  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>`;
@@ -3113,13 +3660,14 @@ function createSSRPathnameScript(pathname) {
3113
3660
  return "";
3114
3661
  }
3115
3662
  }
3116
- function createDataScript(ssrData, errorMsg, pathname, browserTelemetryConfig, traceContext, titleSuffix) {
3663
+ function createDataScript(ssrData, errorMsg, pathname, browserTelemetryConfig, traceContext, titleSuffix, i18n) {
3117
3664
  let script = "";
3118
3665
  script += createContentCaptureScript();
3119
3666
  script += createSSRPathnameScript(pathname);
3120
3667
  if (pathname) {
3121
3668
  script += createSSRExportsScript(pathname);
3122
3669
  }
3670
+ script += createI18nScript(i18n);
3123
3671
  script += createTelemetryConfigScript(browserTelemetryConfig);
3124
3672
  script += createTraceContextScript(traceContext);
3125
3673
  script += createTitleSuffixScript(titleSuffix);
@@ -3139,7 +3687,9 @@ function createDataScript(ssrData, errorMsg, pathname, browserTelemetryConfig, t
3139
3687
  }
3140
3688
  return script;
3141
3689
  }
3142
- var init_data2 = () => {};
3690
+ var init_data2 = __esm(() => {
3691
+ init_content_path();
3692
+ });
3143
3693
 
3144
3694
  // packages/core/src/ssr/lib/scripts/feeds.ts
3145
3695
  function createFeedsScript() {
@@ -3367,10 +3917,10 @@ function generateCollectionScripts(byCollectionForSSR) {
3367
3917
  } catch {}
3368
3918
  return "";
3369
3919
  }
3370
- function generateHydrationScripts(ssrData, isWatchMode, ssrError, collectionScripts = "", preloadScript = "", bundleUrl = "", pathname = "", browserTelemetryConfig, traceContext, titleSuffix) {
3920
+ function generateHydrationScripts(ssrData, isWatchMode, ssrError, collectionScripts = "", preloadScript = "", bundleUrl = "", pathname = "", browserTelemetryConfig, traceContext, titleSuffix, i18n) {
3371
3921
  let scripts = preloadScript;
3372
3922
  scripts += collectionScripts;
3373
- scripts += createDataScript(ssrData, isWatchMode ? ssrError : undefined, pathname, browserTelemetryConfig, traceContext, titleSuffix);
3923
+ scripts += createDataScript(ssrData, isWatchMode ? ssrError : undefined, pathname, browserTelemetryConfig, traceContext, titleSuffix, i18n);
3374
3924
  scripts += createFeedsScript();
3375
3925
  scripts += createLlmsScript();
3376
3926
  if (bundleUrl) {
@@ -3533,7 +4083,7 @@ async function renderSSRDocument(options) {
3533
4083
  const browserTelemetryConfig = serializeBrowserTelemetryConfig2(browserTelemetryConfigRaw);
3534
4084
  const traceContext = getTraceContextForInjection(options.parentSpan);
3535
4085
  const hydrationScript = await withSpan("ssr.generate.scripts", async (span) => {
3536
- const script = generateHydrationScripts(setup2.ssrData, isWatchMode, setup2.ssrError, collectionScripts, setup2.preloadHydrationScript, bundleUrl, pathname, browserTelemetryConfig, traceContext, options.titleSuffix);
4086
+ const script = generateHydrationScripts(setup2.ssrData, isWatchMode, setup2.ssrError, collectionScripts, setup2.preloadHydrationScript, bundleUrl, pathname, browserTelemetryConfig, traceContext, options.titleSuffix, options.i18n);
3537
4087
  span.setAttribute("reroute.scripts.size", script.length);
3538
4088
  return script;
3539
4089
  });
@@ -3551,12 +4101,13 @@ async function renderSSRDocument(options) {
3551
4101
  dedent(setup2.metadataResult.perPageHead)
3552
4102
  ].filter(Boolean).join(`
3553
4103
  `));
4104
+ const i18nLocale = options.i18n?.enabled ? String(options.i18n.locale || "") : "";
3554
4105
  const html2 = withSpanSync("ssr.apply.template", (span) => {
3555
4106
  const result = applyIndexTemplate(baseTemplate, appHtml, {
3556
4107
  head: [setup2.inlineStyleTag, combinedHead].filter(Boolean).join(`
3557
4108
  `),
3558
4109
  hydrationScript,
3559
- lang: setup2.metadataResult.pageLang || lang,
4110
+ lang: setup2.metadataResult.pageLang || i18nLocale || lang,
3560
4111
  appId
3561
4112
  });
3562
4113
  span.setAttributes({
@@ -4734,6 +5285,55 @@ async function discoverRoutes(cwd, isWatchMode) {
4734
5285
  }
4735
5286
  return { staticRoutes, dynamicRoutes };
4736
5287
  }
5288
+ function groupNamedCollectionItems(items) {
5289
+ const byName = new Map;
5290
+ for (const item of items) {
5291
+ const name = String(item.name || "");
5292
+ if (!name)
5293
+ continue;
5294
+ const list = byName.get(name) || [];
5295
+ list.push(item);
5296
+ byName.set(name, list);
5297
+ }
5298
+ return byName;
5299
+ }
5300
+ function resolveItemHref(collection, item, defaultLocale, localize) {
5301
+ const name = String(item.name || "");
5302
+ const locale = item.locale || defaultLocale;
5303
+ if (name)
5304
+ return localize(collection, name, locale, defaultLocale);
5305
+ return String(item.href || "");
5306
+ }
5307
+ function buildNamedCollectionSitemapEntries(collection, name, variants, defaultLocale, base, localize) {
5308
+ const locales = [
5309
+ ...new Set(variants.map((v) => v.locale || defaultLocale).filter(Boolean))
5310
+ ];
5311
+ const alternates = locales.length > 1 ? [
5312
+ ...locales.map((locale) => ({
5313
+ hreflang: locale,
5314
+ href: `${base}${localize(collection, name, locale, defaultLocale)}`
5315
+ })),
5316
+ {
5317
+ hreflang: "x-default",
5318
+ href: `${base}${localize(collection, name, defaultLocale, defaultLocale)}`
5319
+ }
5320
+ ] : undefined;
5321
+ const entries = [];
5322
+ for (const item of variants) {
5323
+ const href = resolveItemHref(collection, item, defaultLocale, localize);
5324
+ if (!href)
5325
+ continue;
5326
+ const meta = item.meta || {};
5327
+ entries.push({
5328
+ loc: `${base}${href}`,
5329
+ lastmod: meta.date ? new Date(meta.date).toISOString() : undefined,
5330
+ changefreq: "weekly",
5331
+ priority: 0.7,
5332
+ alternates
5333
+ });
5334
+ }
5335
+ return entries;
5336
+ }
4737
5337
  async function discoverCollectionContent(cwd, collection, isWatchMode, baseUrl) {
4738
5338
  const entries = [];
4739
5339
  try {
@@ -4745,18 +5345,26 @@ async function discoverCollectionContent(cwd, collection, isWatchMode, baseUrl)
4745
5345
  const url = `${Bun.pathToFileURL(collectionFile).href}${isWatchMode ? `?t=${Date.now()}` : ""}`;
4746
5346
  const mod = await import(url);
4747
5347
  const items = mod.items || [];
5348
+ const defaultLocale = mod.defaultLocale || "en-US";
5349
+ const { localizeContentHref: localizeContentHref2 } = await Promise.resolve().then(() => (init_pathname(), exports_pathname));
5350
+ const base = baseUrl.replace(/\/$/, "");
5351
+ const byName = groupNamedCollectionItems(items);
5352
+ for (const [name, variants] of byName) {
5353
+ entries.push(...buildNamedCollectionSitemapEntries(collection, name, variants, defaultLocale, base, localizeContentHref2));
5354
+ }
4748
5355
  for (const item of items) {
5356
+ if (item.name)
5357
+ continue;
4749
5358
  const href = String(item.href || "");
5359
+ if (!href)
5360
+ continue;
4750
5361
  const meta = item.meta || {};
4751
- if (href) {
4752
- const entry = {
4753
- loc: `${baseUrl}${href}`,
4754
- lastmod: meta.date ? new Date(meta.date).toISOString() : undefined,
4755
- changefreq: "weekly",
4756
- priority: 0.7
4757
- };
4758
- entries.push(entry);
4759
- }
5362
+ entries.push({
5363
+ loc: `${base}${href}`,
5364
+ lastmod: meta.date ? new Date(meta.date).toISOString() : undefined,
5365
+ changefreq: "weekly",
5366
+ priority: 0.7
5367
+ });
4760
5368
  }
4761
5369
  } catch (error) {
4762
5370
  console.error(`[reroute] Failed to discover collection ${collection}:`, error);
@@ -5051,11 +5659,17 @@ async function discoverCollectionFeedItems(cwd, collection, isWatchMode, baseUrl
5051
5659
  const url = `${Bun.pathToFileURL(collectionFile).href}${isWatchMode ? `?t=${Date.now()}` : ""}`;
5052
5660
  const mod = await import(url);
5053
5661
  const contentItems = mod.items || [];
5662
+ const defaultLocale = mod.defaultLocale || "en-US";
5663
+ const { localizeContentHref: localizeContentHref2 } = await Promise.resolve().then(() => (init_pathname(), exports_pathname));
5054
5664
  for (const item of contentItems) {
5055
- const href = String(item.href || "");
5665
+ const name = String(item.name || "");
5666
+ const locale = item.locale || defaultLocale;
5667
+ const href = name ? localizeContentHref2(collection, name, locale, defaultLocale) : String(item.href || "");
5056
5668
  const meta = item.meta || {};
5057
5669
  if (href) {
5058
- items.push(createRSSItemFromMetaAndHref(href, meta, baseUrl, item, rssConfig));
5670
+ const rssItem = createRSSItemFromMetaAndHref(href, meta, baseUrl, item, rssConfig);
5671
+ rssItem.locale = locale;
5672
+ items.push(rssItem);
5059
5673
  }
5060
5674
  }
5061
5675
  } catch (error) {
@@ -5321,7 +5935,7 @@ function generateRSSXML(feed, options = {}) {
5321
5935
  <link>${escapeXml(feed.link)}</link>
5322
5936
  <description>${escapeXml(feed.description)}</description>
5323
5937
  <lastBuildDate>${lastBuildDate}</lastBuildDate>
5324
- <language>en</language>
5938
+ <language>${escapeXml(feed.language || "en")}</language>
5325
5939
  <atom:link href="${escapeXml(feed.link)}" rel="self" type="application/rss+xml" />
5326
5940
  ${itemsXML}
5327
5941
  </channel>
@@ -5483,6 +6097,7 @@ async function generateSitemap(discovery4, baseUrl, options = {}) {
5483
6097
  return createSingleSitemap(allEntries);
5484
6098
  }
5485
6099
  function generateSitemapXML(entries) {
6100
+ const hasAlternates = entries.some((e) => e.alternates && e.alternates.length > 0);
5486
6101
  const urlsetEntries = entries.map((entry) => {
5487
6102
  let xml = ` <url>
5488
6103
  <loc>${escapeXml2(entry.loc)}</loc>`;
@@ -5498,13 +6113,20 @@ function generateSitemapXML(entries) {
5498
6113
  xml += `
5499
6114
  <priority>${entry.priority.toFixed(1)}</priority>`;
5500
6115
  }
6116
+ if (entry.alternates?.length) {
6117
+ for (const alt of entry.alternates) {
6118
+ xml += `
6119
+ <xhtml:link rel="alternate" hreflang="${escapeXml2(alt.hreflang)}" href="${escapeXml2(alt.href)}" />`;
6120
+ }
6121
+ }
5501
6122
  xml += `
5502
6123
  </url>`;
5503
6124
  return xml;
5504
6125
  }).join(`
5505
6126
  `);
6127
+ const xhtmlNs = hasAlternates ? ' xmlns:xhtml="http://www.w3.org/1999/xhtml"' : "";
5506
6128
  return `<?xml version="1.0" encoding="UTF-8"?>
5507
- <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
6129
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"${xhtmlNs}>
5508
6130
  ${urlsetEntries}
5509
6131
  </urlset>`;
5510
6132
  }
@@ -5602,7 +6224,8 @@ async function renderSSRDocumentStream(options) {
5602
6224
  ...parts
5603
6225
  };
5604
6226
  });
5605
- const pageLang = setup2.metadataResult.pageLang || lang;
6227
+ const i18nLocale = options.i18n?.enabled ? String(options.i18n.locale || "") : "";
6228
+ const pageLang = setup2.metadataResult.pageLang || i18nLocale || lang;
5606
6229
  let headWithLang = htmlHead.replace(/<html([^>]*)>/i, (_m, attrs) => {
5607
6230
  const hasLang = /(^|\s)lang\s*=/.test(attrs);
5608
6231
  const newAttrs = hasLang ? attrs.replace(/lang\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/i, `lang="${pageLang}"`) : `${attrs} lang="${pageLang}"`;
@@ -5645,7 +6268,8 @@ async function renderSSRDocumentStream(options) {
5645
6268
  parentSpan: options.parentSpan,
5646
6269
  timeout: options.timeout,
5647
6270
  ssrExport: setup2.metadataResult.ssrExport,
5648
- titleSuffix: options.titleSuffix
6271
+ titleSuffix: options.titleSuffix,
6272
+ i18n: options.i18n
5649
6273
  });
5650
6274
  if (debug)
5651
6275
  console.log(`[renderStream] ${pathname} - returning stream: ${(performance.now() - t0).toFixed(2)}ms`);
@@ -5758,7 +6382,7 @@ function buildStreamingScripts(ctx) {
5758
6382
  ssrDataSnapshot[key] = value;
5759
6383
  }
5760
6384
  }
5761
- const dataScript = createDataScript(ssrDataSnapshot, ctx.isWatchMode ? ctx.ssrError : undefined, ctx.pathname, ctx.browserTelemetryConfig, ctx.traceContext, ctx.titleSuffix);
6385
+ const dataScript = createDataScript(ssrDataSnapshot, ctx.isWatchMode ? ctx.ssrError : undefined, ctx.pathname, ctx.browserTelemetryConfig, ctx.traceContext, ctx.titleSuffix, ctx.i18n);
5762
6386
  const feedsScript = createFeedsScript();
5763
6387
  const llmsScript = createLlmsScript();
5764
6388
  let allScripts = dataScript + feedsScript + llmsScript + collectionScripts + ctx.preloadHydrationScript;
@@ -5890,6 +6514,7 @@ var init_src = __esm(() => {
5890
6514
  init_bundler();
5891
6515
  init_config();
5892
6516
  init_content();
6517
+ init_i18n();
5893
6518
  init_llms();
5894
6519
  init_og();
5895
6520
  init_robots();
@@ -5902,6 +6527,7 @@ var init_src = __esm(() => {
5902
6527
  var exports_core = {};
5903
6528
  __export(exports_core, {
5904
6529
  toPromise: () => toPromise,
6530
+ toOgLocale: () => toOgLocale,
5905
6531
  toBytes: () => toBytes,
5906
6532
  stripStart: () => stripStart,
5907
6533
  stripHtmlTags: () => stripHtmlTags,
@@ -5913,6 +6539,7 @@ __export(exports_core, {
5913
6539
  setGlobalSSRData: () => setGlobalSSRData,
5914
6540
  setGlobalCollections: () => setGlobalCollections,
5915
6541
  seedSSRModuleForPath: () => seedSSRModuleForPath,
6542
+ resolveLocale: () => resolveLocale,
5916
6543
  resetSSRAccessTracking: () => resetSSRAccessTracking,
5917
6544
  renderSSRDocumentStream: () => renderSSRDocumentStream,
5918
6545
  renderSSRDocument: () => renderSSRDocument,
@@ -5921,7 +6548,17 @@ __export(exports_core, {
5921
6548
  processCollections: () => processCollections,
5922
6549
  preloadContentModule: () => preloadContentModule,
5923
6550
  performSSRSetup: () => performSSRSetup,
6551
+ parseCookieValue: () => parseCookieValue,
6552
+ parseContentPath: () => parseContentPath,
6553
+ normalizePathname: () => normalizePathname,
6554
+ negotiateLocale: () => negotiateLocale,
5924
6555
  mergeLayoutData: () => mergeLayoutData,
6556
+ matchRoutes: () => matchRoutes,
6557
+ matchRoutePattern: () => matchRoutePattern,
6558
+ lookupMessage: () => lookupMessage,
6559
+ looksLikeLocale: () => looksLikeLocale,
6560
+ localizeHref: () => localizeHref,
6561
+ localizeContentHref: () => localizeContentHref,
5925
6562
  loadRoutesModule: () => loadRoutesModule,
5926
6563
  loadOGImageComponent: () => loadOGImageComponent,
5927
6564
  loadIndexHtml: () => loadIndexHtml,
@@ -5934,11 +6571,13 @@ __export(exports_core, {
5934
6571
  inlineTailwindCSS: () => inlineTailwindCSS,
5935
6572
  importContentModuleForPath: () => importContentModuleForPath,
5936
6573
  htmlToMarkdown: () => htmlToMarkdown,
6574
+ hreflangTags: () => hreflangTags,
5937
6575
  gzipIfAccepted: () => gzipIfAccepted,
5938
6576
  getSitemapExcludedRoutes: () => getSitemapExcludedRoutes,
5939
6577
  getRedirectSourcePaths: () => getRedirectSourcePaths,
5940
6578
  getPatternDepth: () => getPatternDepth,
5941
6579
  getMimeType: () => getMimeType,
6580
+ getMessage: () => getMessage,
5942
6581
  getMatchingLayouts: () => getMatchingLayouts,
5943
6582
  getContentMeta: () => getContentMeta,
5944
6583
  generateSitemapXML: () => generateSitemapXML,
@@ -5955,6 +6594,7 @@ __export(exports_core, {
5955
6594
  generateContentHash: () => generateContentHash,
5956
6595
  generateCollectionScripts: () => generateCollectionScripts,
5957
6596
  generateAtomXML: () => generateAtomXML,
6597
+ formatMessage: () => formatMessage,
5958
6598
  formatAsText: () => formatAsText,
5959
6599
  formatAsMarkdown: () => formatAsMarkdown,
5960
6600
  findOGImageForPath: () => findOGImageForPath,
@@ -5974,10 +6614,12 @@ __export(exports_core, {
5974
6614
  discoverRoutes: () => discoverRoutes,
5975
6615
  discoverRobotsData: () => discoverRobotsData,
5976
6616
  discoverRSSData: () => discoverRSSData,
6617
+ discoverI18n: () => discoverI18n,
5977
6618
  discoverCollections: () => discoverCollections,
5978
6619
  discoverCollectionContent: () => discoverCollectionContent,
5979
6620
  defineConfig: () => defineConfig,
5980
6621
  deduplicateMetaTags: () => deduplicateMetaTags,
6622
+ createTranslator: () => createTranslator,
5981
6623
  createLlmsScript: () => createLlmsScript,
5982
6624
  createLayoutDataKey: () => createLayoutDataKey,
5983
6625
  createLayoutCacheKey: () => createLayoutCacheKey,
@@ -5985,13 +6627,17 @@ __export(exports_core, {
5985
6627
  createDataScript: () => createDataScript,
5986
6628
  createCollectionScript: () => createCollectionScript,
5987
6629
  createBundlePreload: () => createBundlePreload,
6630
+ contentNegotiateRedirect: () => contentNegotiateRedirect,
6631
+ contentKey: () => contentKey,
5988
6632
  computeSSRDataForPath: () => computeSSRDataForPath,
5989
6633
  computeSSRData: () => computeSSRData,
5990
6634
  computeRouteData: () => computeRouteData,
5991
6635
  computeLayoutData: () => computeLayoutData,
5992
6636
  computeContentData: () => computeContentData,
5993
6637
  compressStreamIfAccepted: () => compressStreamIfAccepted,
6638
+ compareRouteSpecificity: () => compareRouteSpecificity,
5994
6639
  cleanupPathScopedSSRData: () => cleanupPathScopedSSRData,
6640
+ buildLocaleCookie: () => buildLocaleCookie,
5995
6641
  buildHeadFromMeta: () => buildHeadFromMeta,
5996
6642
  blockSearchCrawlers: () => blockSearchCrawlers,
5997
6643
  blockAITrainingCrawlers: () => blockAITrainingCrawlers,
@@ -6007,6 +6653,9 @@ __export(exports_core, {
6007
6653
  SEARCH_CRAWLERS: () => SEARCH_CRAWLERS,
6008
6654
  LRUCache: () => LRUCache,
6009
6655
  DefaultOGTemplate: () => DefaultOGTemplate,
6656
+ DEFAULT_LOCALES_DIR: () => DEFAULT_LOCALES_DIR,
6657
+ DEFAULT_LOCALE: () => DEFAULT_LOCALE,
6658
+ DEFAULT_COOKIE: () => DEFAULT_COOKIE,
6010
6659
  AI_CRAWLERS: () => AI_CRAWLERS
6011
6660
  });
6012
6661
  var init_core = __esm(() => {
@@ -6031,7 +6680,7 @@ function getNamingConfig(options) {
6031
6680
 
6032
6681
  // packages/cli/src/commands/lib/env.ts
6033
6682
  import { existsSync as existsSync2, readFileSync } from "node:fs";
6034
- import { join as join3 } from "node:path";
6683
+ import { join as join4 } from "node:path";
6035
6684
  function parseEnvFile(content2) {
6036
6685
  const env = {};
6037
6686
  const lines = content2.split(`
@@ -6057,7 +6706,7 @@ function loadEnvFiles(cwd, mode = "development") {
6057
6706
  const env = {};
6058
6707
  const loadedFiles = [];
6059
6708
  for (const file of envFiles) {
6060
- const filePath = join3(cwd, file);
6709
+ const filePath = join4(cwd, file);
6061
6710
  if (existsSync2(filePath)) {
6062
6711
  try {
6063
6712
  const content2 = readFileSync(filePath, "utf-8");
@@ -6101,11 +6750,11 @@ var init_env = () => {};
6101
6750
 
6102
6751
  // packages/cli/src/commands/lib/markdown/availability.ts
6103
6752
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
6104
- import { dirname, join as join4 } from "node:path";
6753
+ import { dirname, join as join5 } from "node:path";
6105
6754
  function isPackageAvailable(cwd, packageName) {
6106
6755
  let currentDir = cwd;
6107
6756
  for (let i = 0;i < 5; i++) {
6108
- const packageJsonPath = join4(currentDir, "package.json");
6757
+ const packageJsonPath = join5(currentDir, "package.json");
6109
6758
  if (existsSync3(packageJsonPath)) {
6110
6759
  try {
6111
6760
  const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
@@ -6177,7 +6826,7 @@ var init_availability = () => {};
6177
6826
 
6178
6827
  // packages/cli/src/commands/lib/markdown/processor.ts
6179
6828
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
6180
- import { basename as basename2, dirname as dirname2, extname as extname2, join as join5, relative, resolve } from "node:path";
6829
+ import { basename as basename2, dirname as dirname2, extname as extname2, join as join6, relative, resolve } from "node:path";
6181
6830
  import matter from "gray-matter";
6182
6831
  function convertPropertiesToJsx(properties) {
6183
6832
  const newProperties = {};
@@ -6236,7 +6885,7 @@ function processMarkdownFile(filePath) {
6236
6885
  function isMdxAvailable2(cwd) {
6237
6886
  let currentDir = cwd;
6238
6887
  for (let i = 0;i < 5; i++) {
6239
- const packageJsonPath = join5(currentDir, "package.json");
6888
+ const packageJsonPath = join6(currentDir, "package.json");
6240
6889
  if (existsSync4(packageJsonPath)) {
6241
6890
  try {
6242
6891
  const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
@@ -6260,10 +6909,10 @@ function isMdxAvailable2(cwd) {
6260
6909
  }
6261
6910
  function tryLoadModule(cwd, moduleName) {
6262
6911
  const pathsToTry = [
6263
- join5(cwd, `node_modules/${moduleName}`),
6264
- join5(cwd, `../node_modules/${moduleName}`),
6265
- join5(cwd, `../../node_modules/${moduleName}`),
6266
- join5(cwd, `../../../node_modules/${moduleName}`)
6912
+ join6(cwd, `node_modules/${moduleName}`),
6913
+ join6(cwd, `../node_modules/${moduleName}`),
6914
+ join6(cwd, `../../node_modules/${moduleName}`),
6915
+ join6(cwd, `../../../node_modules/${moduleName}`)
6267
6916
  ];
6268
6917
  for (const modulePath of pathsToTry) {
6269
6918
  try {
@@ -6593,7 +7242,7 @@ function isProductionMode(args = []) {
6593
7242
  }
6594
7243
 
6595
7244
  // packages/cli/src/commands/lib/streaming/analyzer.ts
6596
- import { dirname as dirname3, join as join6 } from "node:path";
7245
+ import { dirname as dirname3, join as join7 } from "node:path";
6597
7246
  function hasSSRDataExport(source) {
6598
7247
  if (SSR_DATA_INLINE_EXPORT_REGEX.test(source)) {
6599
7248
  return true;
@@ -6621,10 +7270,10 @@ async function analyzeRouteForStreaming(options) {
6621
7270
  }
6622
7271
  async function findSkeletonForTarget(routesDir, relativePath, type) {
6623
7272
  const dirName = normalizeRelativePath(dirname3(relativePath));
6624
- const baseDir = dirName === "." ? routesDir : join6(routesDir, dirName).replace(/\\/g, "/");
7273
+ const baseDir = dirName === "." ? routesDir : join7(routesDir, dirName).replace(/\\/g, "/");
6625
7274
  const candidates = type === "layout" ? ["[layout+skeleton].tsx", "[layout+skeleton].ts"] : ["[skeleton].tsx", "[skeleton].ts"];
6626
7275
  for (const candidate of candidates) {
6627
- const candidatePath = join6(baseDir, candidate);
7276
+ const candidatePath = join7(baseDir, candidate);
6628
7277
  try {
6629
7278
  const file = Bun.file(candidatePath);
6630
7279
  if (await file.exists()) {
@@ -6645,12 +7294,12 @@ var init_analyzer = __esm(() => {
6645
7294
  });
6646
7295
 
6647
7296
  // packages/cli/src/commands/lib/streaming/suspense.ts
6648
- import { dirname as dirname4, join as join7, relative as relative2 } from "node:path";
7297
+ import { dirname as dirname4, join as join8, relative as relative2 } from "node:path";
6649
7298
  async function generateStreamingWrapper(options) {
6650
7299
  const { cwd, relativeFilePath, analysis, config: config2 } = options;
6651
- const streamingModulePath = join7(cwd, ".reroute", "streaming", relativeFilePath).replace(/\.(tsx|ts)$/, ".tsx");
7300
+ const streamingModulePath = join8(cwd, ".reroute", "streaming", relativeFilePath).replace(/\.(tsx|ts)$/, ".tsx");
6652
7301
  const wrapperDir = dirname4(streamingModulePath);
6653
- const originalModuleWithoutExt = removeExtension(join7(cwd, "src", "client", "routes", relativeFilePath));
7302
+ const originalModuleWithoutExt = removeExtension(join8(cwd, "src", "client", "routes", relativeFilePath));
6654
7303
  const originalImportPath = toImportPath(wrapperDir, originalModuleWithoutExt);
6655
7304
  const skeletonImportBlock = await resolveSkeletonBlock({
6656
7305
  analysis,
@@ -6692,7 +7341,7 @@ async function resolveSkeletonBlock(args) {
6692
7341
  }
6693
7342
  function resolveConfiguredSkeletonPath(value, cwd) {
6694
7343
  if (typeof value === "string" && value.trim().length > 0) {
6695
- return value.startsWith("/") ? value : join7(cwd, value);
7344
+ return value.startsWith("/") ? value : join8(cwd, value);
6696
7345
  }
6697
7346
  if (value && !warnedSkeletonType) {
6698
7347
  console.warn("[reroute/streaming] default skeletons must be provided as string module paths in reroute.config.ts");
@@ -6729,13 +7378,13 @@ var init_suspense = () => {};
6729
7378
  // packages/cli/src/commands/lib/tailwind.ts
6730
7379
  import { spawn } from "node:child_process";
6731
7380
  import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
6732
- import { join as join8 } from "node:path";
7381
+ import { join as join9 } from "node:path";
6733
7382
  function isTailwindAvailable(cwd) {
6734
7383
  const pathsToTry = [
6735
- join8(cwd, "package.json"),
6736
- join8(cwd, "../package.json"),
6737
- join8(cwd, "../../package.json"),
6738
- join8(cwd, "../../../package.json")
7384
+ join9(cwd, "package.json"),
7385
+ join9(cwd, "../package.json"),
7386
+ join9(cwd, "../../package.json"),
7387
+ join9(cwd, "../../../package.json")
6739
7388
  ];
6740
7389
  for (const packageJsonPath of pathsToTry) {
6741
7390
  if (!existsSync5(packageJsonPath)) {
@@ -6756,8 +7405,8 @@ function isTailwindAvailable(cwd) {
6756
7405
  return false;
6757
7406
  }
6758
7407
  function getTailwindPaths(cwd) {
6759
- const input = join8(cwd, "src/client/theme.css");
6760
- const output = join8(cwd, ".reroute/theme.css");
7408
+ const input = join9(cwd, "src/client/theme.css");
7409
+ const output = join9(cwd, ".reroute/theme.css");
6761
7410
  return { input, output };
6762
7411
  }
6763
7412
  function hasTailwindInput(cwd) {
@@ -6774,10 +7423,10 @@ function hasTailwindInput(cwd) {
6774
7423
  }
6775
7424
  function resolveTailwindBin(cwd) {
6776
7425
  const pathsToTry = [
6777
- join8(cwd, "node_modules/.bin/tailwindcss"),
6778
- join8(cwd, "../node_modules/.bin/tailwindcss"),
6779
- join8(cwd, "../../node_modules/.bin/tailwindcss"),
6780
- join8(cwd, "../../../node_modules/.bin/tailwindcss")
7426
+ join9(cwd, "node_modules/.bin/tailwindcss"),
7427
+ join9(cwd, "../node_modules/.bin/tailwindcss"),
7428
+ join9(cwd, "../../node_modules/.bin/tailwindcss"),
7429
+ join9(cwd, "../../../node_modules/.bin/tailwindcss")
6781
7430
  ];
6782
7431
  for (const binPath of pathsToTry) {
6783
7432
  if (existsSync5(binPath)) {
@@ -6848,12 +7497,12 @@ __export(exports_gen, {
6848
7497
  default: () => gen
6849
7498
  });
6850
7499
  import { watch } from "node:fs";
6851
- import { mkdir as mkdir2, readdir as readdir5, rm } from "node:fs/promises";
6852
- import { dirname as dirname5, join as join9 } from "node:path";
7500
+ import { mkdir as mkdir2, readdir as readdir7, rm } from "node:fs/promises";
7501
+ import { dirname as dirname5, join as join10 } from "node:path";
6853
7502
  import { pathToFileURL as pathToFileURL8 } from "node:url";
6854
7503
  async function bundleThemeCssIntoArtifacts(cwd) {
6855
- const themeCssPath = join9(cwd, ".reroute", "theme.css");
6856
- const artifactsPath = join9(cwd, OUTPUT_ARTIFACTS);
7504
+ const themeCssPath = join10(cwd, ".reroute", "theme.css");
7505
+ const artifactsPath = join10(cwd, OUTPUT_ARTIFACTS);
6857
7506
  try {
6858
7507
  const cssContent = await Bun.file(themeCssPath).text();
6859
7508
  if (!cssContent)
@@ -6879,11 +7528,11 @@ async function bundleThemeCssIntoArtifacts(cwd) {
6879
7528
  async function ensureOutputDir(cwd, options = {}) {
6880
7529
  const { clean = true } = options;
6881
7530
  try {
6882
- const outputPath = join9(cwd, OUTPUT_DIR);
7531
+ const outputPath = join10(cwd, OUTPUT_DIR);
6883
7532
  if (clean) {
6884
7533
  let needsCleanup = false;
6885
7534
  try {
6886
- const entries = await readdir5(outputPath);
7535
+ const entries = await readdir7(outputPath);
6887
7536
  needsCleanup = entries.length > 0;
6888
7537
  } catch {}
6889
7538
  if (needsCleanup) {
@@ -6892,9 +7541,9 @@ async function ensureOutputDir(cwd, options = {}) {
6892
7541
  }
6893
7542
  }
6894
7543
  await mkdir2(outputPath, { recursive: true });
6895
- await mkdir2(join9(cwd, OUTPUT_COLLECTIONS_DIR), { recursive: true });
6896
- await mkdir2(join9(cwd, ".reroute/bundles"), { recursive: true });
6897
- await mkdir2(join9(cwd, ".reroute/streaming"), { recursive: true });
7544
+ await mkdir2(join10(cwd, OUTPUT_COLLECTIONS_DIR), { recursive: true });
7545
+ await mkdir2(join10(cwd, ".reroute/bundles"), { recursive: true });
7546
+ await mkdir2(join10(cwd, ".reroute/streaming"), { recursive: true });
6898
7547
  } catch {}
6899
7548
  }
6900
7549
  async function writeStubFiles(cwd) {
@@ -6952,11 +7601,11 @@ import './artifacts';
6952
7601
  import './routes'; // Import routes (bundles all route components)
6953
7602
  import '../src/index';
6954
7603
  `;
6955
- await Bun.write(join9(cwd, OUTPUT_ROUTES), STUB_ROUTES);
6956
- await Bun.write(join9(cwd, OUTPUT_CONTENT_TS), STUB_CONTENT);
6957
- await Bun.write(join9(cwd, OUTPUT_ARTIFACTS), STUB_ARTIFACTS);
6958
- await Bun.write(join9(cwd, OUTPUT_INDEX), STUB_INDEX);
6959
- await Bun.write(join9(cwd, ".reroute/entry.ts"), STUB_ENTRY);
7604
+ await Bun.write(join10(cwd, OUTPUT_ROUTES), STUB_ROUTES);
7605
+ await Bun.write(join10(cwd, OUTPUT_CONTENT_TS), STUB_CONTENT);
7606
+ await Bun.write(join10(cwd, OUTPUT_ARTIFACTS), STUB_ARTIFACTS);
7607
+ await Bun.write(join10(cwd, OUTPUT_INDEX), STUB_INDEX);
7608
+ await Bun.write(join10(cwd, ".reroute/entry.ts"), STUB_ENTRY);
6960
7609
  console.log("[reroute/gen] Initialized stub files");
6961
7610
  }
6962
7611
  function shouldSkipFile(relativePath, entryName) {
@@ -6976,10 +7625,10 @@ function shouldSkipFile(relativePath, entryName) {
6976
7625
  }
6977
7626
  async function scanDirectory(dir, base = "") {
6978
7627
  const files = [];
6979
- const entries = await readdir5(dir, { withFileTypes: true });
7628
+ const entries = await readdir7(dir, { withFileTypes: true });
6980
7629
  for (const entry of entries) {
6981
- const fullPath = join9(dir, entry.name);
6982
- const relativePath = join9(base, entry.name);
7630
+ const fullPath = join10(dir, entry.name);
7631
+ const relativePath = join10(base, entry.name);
6983
7632
  if (entry.isDirectory()) {
6984
7633
  if (entry.name === "content")
6985
7634
  continue;
@@ -6995,10 +7644,10 @@ async function scanDirectory(dir, base = "") {
6995
7644
  }
6996
7645
  async function scan404Files(dir, base = "") {
6997
7646
  const files = [];
6998
- const entries = await readdir5(dir, { withFileTypes: true });
7647
+ const entries = await readdir7(dir, { withFileTypes: true });
6999
7648
  for (const entry of entries) {
7000
- const fullPath = join9(dir, entry.name);
7001
- const relativePath = join9(base, entry.name);
7649
+ const fullPath = join10(dir, entry.name);
7650
+ const relativePath = join10(base, entry.name);
7002
7651
  if (entry.isDirectory()) {
7003
7652
  const nested = await scan404Files(fullPath, relativePath);
7004
7653
  files.push(...nested);
@@ -7011,10 +7660,10 @@ async function scan404Files(dir, base = "") {
7011
7660
  }
7012
7661
  async function scanOgFiles(dir, base = "") {
7013
7662
  const files = [];
7014
- const entries = await readdir5(dir, { withFileTypes: true });
7663
+ const entries = await readdir7(dir, { withFileTypes: true });
7015
7664
  for (const entry of entries) {
7016
- const fullPath = join9(dir, entry.name);
7017
- const relativePath = join9(base, entry.name);
7665
+ const fullPath = join10(dir, entry.name);
7666
+ const relativePath = join10(base, entry.name);
7018
7667
  if (entry.isDirectory()) {
7019
7668
  if (entry.name === "content")
7020
7669
  continue;
@@ -7068,17 +7717,7 @@ function fileToRoute(filePath) {
7068
7717
  };
7069
7718
  }
7070
7719
  function sortRoutes(routes) {
7071
- return routes.sort((a, b) => {
7072
- const aDepth = a.pattern.split("/").length;
7073
- const bDepth = b.pattern.split("/").length;
7074
- if (aDepth !== bDepth)
7075
- return bDepth - aDepth;
7076
- const aDynamicCount = a.params.length;
7077
- const bDynamicCount = b.params.length;
7078
- if (aDynamicCount !== bDynamicCount)
7079
- return aDynamicCount - bDynamicCount;
7080
- return a.pattern.localeCompare(b.pattern);
7081
- });
7720
+ return routes.sort((a, b) => compareRouteSpecificity(a, b));
7082
7721
  }
7083
7722
  function generateRouteTree(files) {
7084
7723
  const routes = [];
@@ -7237,26 +7876,38 @@ export function matchRoute(pathname: string): { route: Route; params: Record<str
7237
7876
  function matchPattern(route: Route, pathname: string): Record<string, string> | null {
7238
7877
  const patternParts = route.pattern.split('/').filter(Boolean);
7239
7878
  const pathnameParts = pathname.split('/').filter(Boolean);
7240
-
7241
- if (patternParts.length !== pathnameParts.length) {
7242
- return null;
7243
- }
7244
-
7245
7879
  const params: Record<string, string> = {};
7880
+ let pathnameIndex = 0;
7246
7881
 
7247
7882
  for (let i = 0; i < patternParts.length; i++) {
7248
7883
  const patternPart = patternParts[i];
7249
- const pathnamePart = pathnameParts[i];
7250
7884
 
7251
7885
  if (patternPart.startsWith(':')) {
7252
7886
  const paramName = patternPart.slice(1);
7887
+ if (paramName.startsWith('...')) {
7888
+ if (i !== patternParts.length - 1 || pathnameIndex >= pathnameParts.length) {
7889
+ return null;
7890
+ }
7891
+
7892
+ params[paramName.slice(3)] = pathnameParts.slice(pathnameIndex).join('/');
7893
+ return params;
7894
+ }
7895
+
7896
+ const pathnamePart = pathnameParts[pathnameIndex];
7897
+ if (pathnamePart === undefined) {
7898
+ return null;
7899
+ }
7900
+
7253
7901
  params[paramName] = pathnamePart;
7254
- } else if (patternPart !== pathnamePart) {
7902
+ pathnameIndex += 1;
7903
+ } else if (patternPart !== pathnameParts[pathnameIndex]) {
7255
7904
  return null;
7905
+ } else {
7906
+ pathnameIndex += 1;
7256
7907
  }
7257
7908
  }
7258
7909
 
7259
- return params;
7910
+ return pathnameIndex === pathnameParts.length ? params : null;
7260
7911
  }
7261
7912
 
7262
7913
  // Set globals for compiled binary support (sitemap/RSS/robots discovery)
@@ -7266,7 +7917,7 @@ function matchPattern(route: Route, pathname: string): Record<string, string> |
7266
7917
  `;
7267
7918
  }
7268
7919
  async function buildStreamingWrappersForTree(cwd, routesPath, tree, config2) {
7269
- const streamingDir = join9(cwd, ".reroute", "streaming");
7920
+ const streamingDir = join10(cwd, ".reroute", "streaming");
7270
7921
  const analyses = new Map;
7271
7922
  const candidates = [
7272
7923
  ...tree.routes.map((route) => ({
@@ -7285,7 +7936,7 @@ async function buildStreamingWrappersForTree(cwd, routesPath, tree, config2) {
7285
7936
  for (const candidate of candidates) {
7286
7937
  if (!/\.(tsx|ts)$/.test(candidate.filePath))
7287
7938
  continue;
7288
- const absolutePath = join9(routesPath, candidate.filePath);
7939
+ const absolutePath = join10(routesPath, candidate.filePath);
7289
7940
  const analysis = await analyzeRouteForStreaming({
7290
7941
  absolutePath,
7291
7942
  relativePath: candidate.filePath,
@@ -7294,7 +7945,7 @@ async function buildStreamingWrappersForTree(cwd, routesPath, tree, config2) {
7294
7945
  });
7295
7946
  if (!analysis.needsStreaming)
7296
7947
  continue;
7297
- const wrapperPath = join9(streamingDir, candidate.filePath).replace(/\.(tsx|ts)$/, ".tsx");
7948
+ const wrapperPath = join10(streamingDir, candidate.filePath).replace(/\.(tsx|ts)$/, ".tsx");
7298
7949
  await mkdir2(dirname5(wrapperPath), { recursive: true });
7299
7950
  const code = await generateStreamingWrapper({
7300
7951
  cwd,
@@ -7311,10 +7962,42 @@ async function buildStreamingWrappersForTree(cwd, routesPath, tree, config2) {
7311
7962
  }
7312
7963
  return analyses;
7313
7964
  }
7965
+ function isLocaleDirName(name) {
7966
+ return /^[a-z]{2}(?:-[A-Za-z0-9]{2,8})*$/i.test(name);
7967
+ }
7968
+ function isContentFileName(name) {
7969
+ return /\.(tsx|ts|md|mdx)$/.test(name) && !name.startsWith("_");
7970
+ }
7971
+ async function listLocaleContentFiles(collectionDir, locale) {
7972
+ try {
7973
+ const nested = await readdir7(join10(collectionDir, locale), {
7974
+ withFileTypes: true
7975
+ });
7976
+ const files = [];
7977
+ for (const n of nested) {
7978
+ if (n.isFile() && isContentFileName(n.name)) {
7979
+ files.push({ relativePath: join10(locale, n.name), locale });
7980
+ }
7981
+ }
7982
+ return files;
7983
+ } catch {
7984
+ return [];
7985
+ }
7986
+ }
7314
7987
  async function listContentFiles2(collectionDir) {
7315
7988
  try {
7316
- const entries = await readdir5(collectionDir, { withFileTypes: true });
7317
- return entries.filter((e) => e.isFile() && /\.(tsx|ts|md|mdx)$/.test(e.name) && !e.name.startsWith("_")).map((e) => e.name);
7989
+ const entries = await readdir7(collectionDir, { withFileTypes: true });
7990
+ const files = [];
7991
+ for (const entry of entries) {
7992
+ if (entry.isFile() && isContentFileName(entry.name)) {
7993
+ files.push({ relativePath: entry.name });
7994
+ continue;
7995
+ }
7996
+ if (entry.isDirectory() && isLocaleDirName(entry.name)) {
7997
+ files.push(...await listLocaleContentFiles(collectionDir, entry.name));
7998
+ }
7999
+ }
8000
+ return files;
7318
8001
  } catch {
7319
8002
  return [];
7320
8003
  }
@@ -7371,11 +8054,14 @@ ${spaces}}`;
7371
8054
  }
7372
8055
  return serializeValue(config2);
7373
8056
  }
7374
- async function buildSingleContentChunk(cwd, collection, file, contentDir, args) {
7375
- const name = file.replace(/\.(tsx|ts|md|mdx)$/, "");
7376
- const absSrc = join9(contentDir, file);
8057
+ async function buildSingleContentChunk(cwd, collection, fileRef, contentDir, args, defaultLocale) {
8058
+ const file = fileRef.relativePath;
8059
+ const locale = fileRef.locale || defaultLocale;
8060
+ const baseName = file.split("/").pop() || file;
8061
+ const name = baseName.replace(/\.(tsx|ts|md|mdx)$/, "");
8062
+ const absSrc = join10(contentDir, file);
7377
8063
  const isMarkdown = /\.(md|mdx)$/.test(file);
7378
- const buildSrc = isMarkdown ? join9(cwd, ".reroute", "markdown", collection, "content", `${name}.tsx`) : absSrc;
8064
+ const buildSrc = isMarkdown ? join10(cwd, ".reroute", "markdown", collection, "content", file.replace(/\.(md|mdx)$/, ".tsx")) : absSrc;
7379
8065
  if (isMarkdown) {
7380
8066
  try {
7381
8067
  const exists = await Bun.file(buildSrc).exists();
@@ -7421,10 +8107,11 @@ async function buildSingleContentChunk(cwd, collection, file, contentDir, args)
7421
8107
  return null;
7422
8108
  }
7423
8109
  const hash2 = await sha8(code);
7424
- const chunkRelDir = join9(".reroute", "bundles");
7425
- const chunkAbsDir = join9(cwd, chunkRelDir);
7426
- const outFile = `${collection}-${name}.${hash2}.js`;
7427
- const absOut = join9(chunkAbsDir, outFile);
8110
+ const chunkRelDir = join10(".reroute", "bundles");
8111
+ const chunkAbsDir = join10(cwd, chunkRelDir);
8112
+ const localeSuffix = locale !== defaultLocale ? `${locale}-` : "";
8113
+ const outFile = `${collection}-${localeSuffix}${name}.${hash2}.js`;
8114
+ const absOut = join10(chunkAbsDir, outFile);
7428
8115
  if (sourcemapContent) {
7429
8116
  code = code.replace(/\/\/# sourceMappingURL=.*\.js\.map/, `//# sourceMappingURL=${outFile}.map`);
7430
8117
  }
@@ -7454,26 +8141,27 @@ async function buildSingleContentChunk(cwd, collection, file, contentDir, args)
7454
8141
  slug: name,
7455
8142
  href: `/${collection}/${name}`,
7456
8143
  moduleUrl: `/bundles/${outFile}`,
7457
- meta
8144
+ meta,
8145
+ locale
7458
8146
  };
7459
8147
  }
7460
- async function buildContentChunks(cwd, args = []) {
7461
- const routesRoot = join9(cwd, ROUTES_DIR);
8148
+ async function buildContentChunks(cwd, args = [], defaultLocale = "en-US") {
8149
+ const routesRoot = join10(cwd, ROUTES_DIR);
7462
8150
  console.log(`[reroute/content] scan ${routesRoot}`);
7463
- const collections2 = await readdir5(routesRoot, { withFileTypes: true });
8151
+ const collections2 = await readdir7(routesRoot, { withFileTypes: true });
7464
8152
  const results = [];
7465
8153
  for (const c of collections2) {
7466
8154
  if (!c.isDirectory())
7467
8155
  continue;
7468
8156
  const collection = c.name;
7469
- const contentDir = join9(routesRoot, collection, "content");
8157
+ const contentDir = join10(routesRoot, collection, "content");
7470
8158
  console.log(`[reroute/content] collection ${collection} dir ${contentDir}`);
7471
8159
  const files = await listContentFiles2(contentDir);
7472
8160
  console.log(`[reroute/content] files ${files.length}`);
7473
8161
  if (!files.length)
7474
8162
  continue;
7475
8163
  for (const file of files) {
7476
- const result = await buildSingleContentChunk(cwd, collection, file, contentDir, args);
8164
+ const result = await buildSingleContentChunk(cwd, collection, file, contentDir, args, defaultLocale);
7477
8165
  if (result) {
7478
8166
  results.push(result);
7479
8167
  }
@@ -7486,7 +8174,7 @@ async function buildContentChunks(cwd, args = []) {
7486
8174
  lines.push("");
7487
8175
  lines.push("export const contents = [");
7488
8176
  for (const e of results) {
7489
- lines.push(" { collection: '" + e.collection + "', slug: '" + e.slug + "', name: '" + e.name + "', href: '" + e.href + "', module: '" + e.moduleUrl + "', meta: " + JSON.stringify(e.meta || {}) + " },");
8177
+ lines.push(" { collection: '" + e.collection + "', slug: '" + e.slug + "', name: '" + e.name + "', href: '" + e.href + "', module: '" + e.moduleUrl + "', locale: '" + e.locale + "', meta: " + JSON.stringify(e.meta || {}) + " },");
7490
8178
  }
7491
8179
  lines.push("] as const;");
7492
8180
  lines.push("");
@@ -7494,20 +8182,31 @@ async function buildContentChunks(cwd, args = []) {
7494
8182
  lines.push("for (const c of contents) { (byCollection[c.collection] ||= []).push(c as any); }");
7495
8183
  lines.push("");
7496
8184
  lines.push("export const byCollectionAndName: Record<string, Record<string, any>> = {};");
7497
- lines.push("for (const c of contents) { ((byCollectionAndName[c.collection] ||= {})[c.name] = c as any); }");
8185
+ lines.push("for (const c of contents) {");
8186
+ lines.push(` const cur = (byCollectionAndName[c.collection] ||= {})[c.name];`);
8187
+ lines.push(` if (!cur || c.locale === ${JSON.stringify(defaultLocale)}) (byCollectionAndName[c.collection] ||= {})[c.name] = c as any;`);
8188
+ lines.push("}");
8189
+ lines.push("");
8190
+ lines.push("export const byCollectionNameLocale: Record<string, Record<string, Record<string, any>>> = {};");
8191
+ lines.push("for (const c of contents) { (((byCollectionNameLocale[c.collection] ||= {})[c.name] ||= {})[c.locale] = c as any); }");
7498
8192
  lines.push("");
7499
8193
  lines.push("export type Collections = keyof typeof byCollection;");
7500
8194
  lines.push("export type Names<C extends Collections> = keyof (typeof byCollectionAndName)[C];");
7501
8195
  lines.push("export type ContentEntry = (typeof contents)[number];");
7502
8196
  lines.push("");
7503
- lines.push("export function getContentEntry<C extends string, N extends string>(collection: C, name: N) {");
8197
+ lines.push("export function getContentEntry<C extends string, N extends string>(collection: C, name: N, locale?: string) {");
8198
+ lines.push(" if (locale) {");
8199
+ lines.push(" const byLocale = (byCollectionNameLocale as any)[collection]?.[name];");
8200
+ lines.push(" if (byLocale?.[locale]) return byLocale[locale];");
8201
+ lines.push(` if (byLocale?.[${JSON.stringify(defaultLocale)}]) return byLocale[${JSON.stringify(defaultLocale)}];`);
8202
+ lines.push(" }");
7504
8203
  lines.push(" const m = (byCollectionAndName as any)[collection];");
7505
8204
  lines.push(" return m ? (m as Record<string, any>)[name] : undefined;");
7506
8205
  lines.push("}");
7507
8206
  lines.push("");
7508
8207
  const writes = [];
7509
8208
  writes.push({
7510
- path: join9(cwd, OUTPUT_CONTENT_TS),
8209
+ path: join10(cwd, OUTPUT_CONTENT_TS),
7511
8210
  content: lines.join(`
7512
8211
  `)
7513
8212
  });
@@ -7519,20 +8218,29 @@ async function buildContentChunks(cwd, args = []) {
7519
8218
  js.push("");
7520
8219
  js.push("export const items = [");
7521
8220
  for (const e of items) {
7522
- js.push(` { collection: '${e.collection}', slug: '${e.slug}', name: '${e.name}', href: '${e.href}', module: '${e.moduleUrl}', meta: ${JSON.stringify(e.meta || {})} },`);
8221
+ js.push(` { collection: '${e.collection}', slug: '${e.slug}', name: '${e.name}', href: '${e.href}', module: '${e.moduleUrl}', locale: '${e.locale}', meta: ${JSON.stringify(e.meta || {})} },`);
7523
8222
  }
7524
8223
  js.push("];");
7525
8224
  js.push("");
7526
8225
  js.push("export const byName = {};");
7527
- js.push("for (const it of items) { byName[it.name] = it; }");
8226
+ js.push("export const byNameLocale = {};");
8227
+ js.push("for (const it of items) {");
8228
+ js.push(" ((byNameLocale[it.name] ||= {})[it.locale] = it);");
8229
+ js.push(` if (!byName[it.name] || it.locale === ${JSON.stringify(defaultLocale)}) byName[it.name] = it;`);
8230
+ js.push("}");
8231
+ js.push("");
8232
+ js.push(`export const defaultLocale = ${JSON.stringify(defaultLocale)};`);
7528
8233
  js.push("");
7529
- js.push("export function get(collectionName, name) {");
8234
+ js.push("export function get(collectionName, name, locale) {");
7530
8235
  js.push(` if (collectionName !== '${collection}') return undefined;`);
8236
+ js.push(" if (locale && byNameLocale[name]) {");
8237
+ js.push(" return byNameLocale[name][locale] || byNameLocale[name][defaultLocale] || byName[name];");
8238
+ js.push(" }");
7531
8239
  js.push(" return byName[name];");
7532
8240
  js.push("}");
7533
8241
  js.push("");
7534
8242
  writes.push({
7535
- path: join9(cwd, OUTPUT_COLLECTIONS_DIR, `${collection}.js`),
8243
+ path: join10(cwd, OUTPUT_COLLECTIONS_DIR, `${collection}.js`),
7536
8244
  content: js.join(`
7537
8245
  `)
7538
8246
  });
@@ -7556,8 +8264,10 @@ async function buildSearchIndex(cwd, config2, args = []) {
7556
8264
  minHeadingLevel = 1,
7557
8265
  maxHeadingLevel = 6
7558
8266
  } = searchConfig;
7559
- const routesRoot = join9(cwd, ROUTES_DIR);
7560
- const collections2 = await readdir5(routesRoot, { withFileTypes: true });
8267
+ const routesRoot = join10(cwd, ROUTES_DIR);
8268
+ const collections2 = await readdir7(routesRoot, { withFileTypes: true });
8269
+ const i18n2 = await discoverI18n(cwd, config2.i18n);
8270
+ const defaultLocale = i18n2.defaultLocale;
7561
8271
  const searchIndex = [];
7562
8272
  for (const c of collections2) {
7563
8273
  if (!c.isDirectory())
@@ -7566,14 +8276,17 @@ async function buildSearchIndex(cwd, config2, args = []) {
7566
8276
  if (targetCollections.length > 0 && !targetCollections.includes(collection)) {
7567
8277
  continue;
7568
8278
  }
7569
- const contentDir = join9(routesRoot, collection, "content");
8279
+ const contentDir = join10(routesRoot, collection, "content");
7570
8280
  try {
7571
- const files = await readdir5(contentDir);
7572
- for (const file of files) {
8281
+ const files = await listContentFiles2(contentDir);
8282
+ for (const fileRef of files) {
8283
+ const file = fileRef.relativePath;
7573
8284
  if (!/\.(md|mdx)$/.test(file))
7574
8285
  continue;
7575
- const name = file.replace(/\.(md|mdx)$/, "");
7576
- const filePath = join9(contentDir, file);
8286
+ const baseName = file.split("/").pop() || file;
8287
+ const name = baseName.replace(/\.(md|mdx)$/, "");
8288
+ const locale = fileRef.locale || defaultLocale;
8289
+ const filePath = join10(contentDir, file);
7577
8290
  try {
7578
8291
  const processed = processMarkdownFile(filePath);
7579
8292
  const { frontmatter, content: content2 } = processed;
@@ -7587,16 +8300,18 @@ async function buildSearchIndex(cwd, config2, args = []) {
7587
8300
  maxLevel: maxHeadingLevel
7588
8301
  });
7589
8302
  const excerpt = String(frontmatter?.excerpt || frontmatter?.description || stripMarkdown(content2).slice(0, 160));
8303
+ const href = localizeContentHref(collection, name, locale, defaultLocale);
7590
8304
  const entry = {
7591
- id: `${collection}:${name}`,
8305
+ id: locale !== defaultLocale ? `${collection}:${name}:${locale}` : `${collection}:${name}`,
7592
8306
  collection,
7593
8307
  name,
7594
8308
  slug: name,
7595
- href: `/${collection}/${name}`,
8309
+ href,
7596
8310
  title: String(frontmatter?.title || name),
7597
8311
  excerpt,
7598
8312
  headings,
7599
- meta: {}
8313
+ meta: {},
8314
+ locale
7600
8315
  };
7601
8316
  for (const field of metaFields) {
7602
8317
  if (frontmatter[field] !== undefined) {
@@ -7619,13 +8334,13 @@ async function buildSearchIndex(cwd, config2, args = []) {
7619
8334
  generated: new Date().toISOString(),
7620
8335
  entries: searchIndex
7621
8336
  };
7622
- await Bun.write(join9(cwd, OUTPUT_SEARCH_INDEX), JSON.stringify(indexData, null, 2));
7623
- console.log(`[reroute/search] Generated search index with ${searchIndex.length} entries: ${join9(cwd, OUTPUT_SEARCH_INDEX)}`);
8337
+ await Bun.write(join10(cwd, OUTPUT_SEARCH_INDEX), JSON.stringify(indexData, null, 2));
8338
+ console.log(`[reroute/search] Generated search index with ${searchIndex.length} entries: ${join10(cwd, OUTPUT_SEARCH_INDEX)}`);
7624
8339
  }
7625
8340
  async function preprocessMarkdownFiles(cwd, config2) {
7626
8341
  console.log("[reroute/markdown] Starting markdown preprocessing...");
7627
- const routesPath = join9(cwd, ROUTES_DIR);
7628
- const markdownDir = join9(cwd, ".reroute", "markdown");
8342
+ const routesPath = join10(cwd, ROUTES_DIR);
8343
+ const markdownDir = join10(cwd, ".reroute", "markdown");
7629
8344
  try {
7630
8345
  await rm(markdownDir, { force: true, recursive: true });
7631
8346
  } catch {}
@@ -7633,10 +8348,10 @@ async function preprocessMarkdownFiles(cwd, config2) {
7633
8348
  async function scanMarkdown(dir, base = "") {
7634
8349
  const files = [];
7635
8350
  try {
7636
- const entries = await readdir5(dir, { withFileTypes: true });
8351
+ const entries = await readdir7(dir, { withFileTypes: true });
7637
8352
  for (const entry of entries) {
7638
- const fullPath = join9(dir, entry.name);
7639
- const relativePath = join9(base, entry.name);
8353
+ const fullPath = join10(dir, entry.name);
8354
+ const relativePath = join10(base, entry.name);
7640
8355
  if (entry.isDirectory()) {
7641
8356
  const nested = await scanMarkdown(fullPath, relativePath);
7642
8357
  files.push(...nested);
@@ -7658,8 +8373,8 @@ async function preprocessMarkdownFiles(cwd, config2) {
7658
8373
  }
7659
8374
  console.log(`[reroute/markdown] Found ${markdownFiles.length} markdown file(s)`);
7660
8375
  for (const file of markdownFiles) {
7661
- const sourcePath = join9(routesPath, file);
7662
- const targetPath = join9(markdownDir, file.replace(/\.mdx?$/, ".tsx"));
8376
+ const sourcePath = join10(routesPath, file);
8377
+ const targetPath = join10(markdownDir, file.replace(/\.mdx?$/, ".tsx"));
7663
8378
  try {
7664
8379
  const processed = processMarkdownFile(sourcePath);
7665
8380
  const isMdx = file.endsWith(".mdx");
@@ -7685,8 +8400,8 @@ async function preprocessMarkdownFiles(cwd, config2) {
7685
8400
  }
7686
8401
  async function buildEntrypointBundle(cwd, args = []) {
7687
8402
  const buildStartTime = performance.now();
7688
- const clientDir = join9(cwd, "src", "client");
7689
- const entrypoint = join9(clientDir, "index.tsx");
8403
+ const clientDir = join10(cwd, "src", "client");
8404
+ const entrypoint = join10(clientDir, "index.tsx");
7690
8405
  const exists = await Bun.file(entrypoint).exists();
7691
8406
  if (!exists) {
7692
8407
  console.log("[reroute/gen] No index.tsx found, skipping bundle build");
@@ -7694,7 +8409,7 @@ async function buildEntrypointBundle(cwd, args = []) {
7694
8409
  }
7695
8410
  console.log("[reroute/gen] Building entrypoint bundle...");
7696
8411
  const isProd = args.includes("--prod");
7697
- const bundlesDir = join9(cwd, ".reroute", "bundles");
8412
+ const bundlesDir = join10(cwd, ".reroute", "bundles");
7698
8413
  await mkdir2(bundlesDir, { recursive: true });
7699
8414
  const mode = isProd ? "production" : "development";
7700
8415
  const publicEnv = getPublicEnvVars(cwd, mode);
@@ -7793,10 +8508,10 @@ async function buildEntrypointBundle(cwd, args = []) {
7793
8508
  }
7794
8509
  const newFiles = new Set(outputs.map((o) => o.fileName));
7795
8510
  try {
7796
- const existingFiles = await readdir5(bundlesDir);
8511
+ const existingFiles = await readdir7(bundlesDir);
7797
8512
  for (const file of existingFiles) {
7798
8513
  if (/^(index|chunk|asset)\.[a-z0-9]+\.(js|css|js\.map|css\.map)(\.gz|\.br)?$/i.test(file) && !newFiles.has(file.replace(/\.(gz|br)$/, ""))) {
7799
- await rm(join9(bundlesDir, file), { force: true });
8514
+ await rm(join10(bundlesDir, file), { force: true });
7800
8515
  }
7801
8516
  }
7802
8517
  } catch (_error) {}
@@ -7812,7 +8527,7 @@ async function buildEntrypointBundle(cwd, args = []) {
7812
8527
  }
7813
8528
  }
7814
8529
  await Promise.all(outputs.map(async (output) => {
7815
- const destPath = join9(bundlesDir, output.fileName);
8530
+ const destPath = join10(bundlesDir, output.fileName);
7816
8531
  await Bun.write(destPath, output.content);
7817
8532
  if (shouldPreCompress) {
7818
8533
  const shouldCompress = /\.(js|css)$/i.test(output.fileName) && !output.fileName.endsWith(".map");
@@ -7881,7 +8596,7 @@ async function generate(cwd, args = [], options = {}) {
7881
8596
  await writeStubFiles(cwd);
7882
8597
  }
7883
8598
  await perfTrack("Markdown preprocessing", () => preprocessMarkdownFiles(cwd, config2));
7884
- const routesPath = join9(cwd, ROUTES_DIR);
8599
+ const routesPath = join10(cwd, ROUTES_DIR);
7885
8600
  try {
7886
8601
  await perfTrack("Routes generation", async () => {
7887
8602
  const files = await scanDirectory(routesPath);
@@ -7890,19 +8605,36 @@ async function generate(cwd, args = [], options = {}) {
7890
8605
  const tree = generateRouteTree(all.map((f) => f.replace(/\\/g, "/")));
7891
8606
  const streamingAnalyses = streamingEnabled ? await buildStreamingWrappersForTree(cwd, routesPath, tree, config2) : new Map;
7892
8607
  const ts = generateTypeScript(tree, streamingAnalyses);
7893
- await Bun.write(join9(cwd, OUTPUT_ROUTES), ts);
8608
+ await Bun.write(join10(cwd, OUTPUT_ROUTES), ts);
7894
8609
  });
7895
8610
  if (!showPerformance) {
7896
- console.log(`[reroute/gen] Generated routes: ${join9(cwd, OUTPUT_ROUTES)}`);
8611
+ console.log(`[reroute/gen] Generated routes: ${join10(cwd, OUTPUT_ROUTES)}`);
7897
8612
  }
7898
8613
  } catch (error) {
7899
8614
  console.error("[reroute/gen] Failed to generate routes:", error);
7900
8615
  throw error;
7901
8616
  }
8617
+ const i18n2 = await discoverI18n(cwd, config2.i18n);
8618
+ const defaultLocale = i18n2.defaultLocale;
7902
8619
  try {
7903
- await perfTrack("Content building", () => buildContentChunks(cwd, args));
8620
+ await Bun.write(join10(cwd, ".reroute", "i18n.json"), JSON.stringify({
8621
+ enabled: i18n2.enabled,
8622
+ defaultLocale: i18n2.defaultLocale,
8623
+ locales: i18n2.locales,
8624
+ cookie: i18n2.cookie,
8625
+ redirectContentOnNegotiate: i18n2.redirectContentOnNegotiate,
8626
+ catalogs: i18n2.catalogs
8627
+ }, null, 2));
8628
+ if (i18n2.enabled && !showPerformance) {
8629
+ console.log(`[reroute/gen] i18n enabled: ${i18n2.locales.join(", ")} (default ${i18n2.defaultLocale})`);
8630
+ }
8631
+ } catch (error) {
8632
+ console.warn("[reroute/gen] Failed to write i18n.json:", error);
8633
+ }
8634
+ try {
8635
+ await perfTrack("Content building", () => buildContentChunks(cwd, args, defaultLocale));
7904
8636
  if (!showPerformance) {
7905
- console.log(`[reroute/gen] Generated content: ${join9(cwd, OUTPUT_CONTENT_TS)} + collections/*.js`);
8637
+ console.log(`[reroute/gen] Generated content: ${join10(cwd, OUTPUT_CONTENT_TS)} + collections/*.js`);
7906
8638
  }
7907
8639
  } catch (error) {
7908
8640
  console.error("[reroute/gen] Failed to generate content:", error);
@@ -7910,22 +8642,25 @@ async function generate(cwd, args = [], options = {}) {
7910
8642
  }
7911
8643
  try {
7912
8644
  await perfTrack("Manifest generation", async () => {
7913
- const routesPath2 = join9(cwd, OUTPUT_ROUTES);
8645
+ const routesPath2 = join10(cwd, OUTPUT_ROUTES);
7914
8646
  const routesModule = await import(`file://${routesPath2}?t=${Date.now()}`);
7915
8647
  const routes = routesModule.routes || [];
7916
8648
  const staticRoutes = routes.filter((r) => !r.params || r.params.length === 0).map((r) => r.pattern);
7917
8649
  const contentRoutes = [];
7918
- const collectionsDir = join9(cwd, ".reroute", "collections");
8650
+ const collectionsDir = join10(cwd, ".reroute", "collections");
7919
8651
  try {
7920
- const collectionFiles = await readdir5(collectionsDir);
8652
+ const collectionFiles = await readdir7(collectionsDir);
7921
8653
  for (const file of collectionFiles) {
7922
8654
  if (file.endsWith(".js")) {
7923
- const collectionPath = join9(collectionsDir, file);
8655
+ const collectionPath = join10(collectionsDir, file);
7924
8656
  const collectionModule = await import(`file://${collectionPath}?t=${Date.now()}`);
7925
8657
  const items = collectionModule.items || [];
7926
8658
  for (const item of items) {
7927
8659
  if (item.href) {
7928
8660
  contentRoutes.push(item.href);
8661
+ if (i18n2.enabled && item.locale && item.locale !== defaultLocale && item.name) {
8662
+ contentRoutes.push(localizeContentHref(item.collection || file.replace(/\.js$/, ""), item.name, item.locale, defaultLocale));
8663
+ }
7929
8664
  }
7930
8665
  }
7931
8666
  }
@@ -7942,7 +8677,7 @@ async function generate(cwd, args = [], options = {}) {
7942
8677
  dynamicRoutes,
7943
8678
  version: 1
7944
8679
  };
7945
- await Bun.write(join9(cwd, ".reroute", "manifest.json"), JSON.stringify(manifest, null, 2));
8680
+ await Bun.write(join10(cwd, ".reroute", "manifest.json"), JSON.stringify(manifest, null, 2));
7946
8681
  });
7947
8682
  } catch (error) {
7948
8683
  console.error("[reroute/gen] Failed to generate manifest:", error);
@@ -7963,7 +8698,7 @@ async function generate(cwd, args = [], options = {}) {
7963
8698
  mdComponentsLines.push("");
7964
8699
  mdComponentsLines.push(`import markdownComponents from '${componentsPath}';`);
7965
8700
  mdComponentsLines.push("export { markdownComponents };");
7966
- await Bun.write(join9(cwd, ".reroute/markdown-components.ts"), mdComponentsLines.join(`
8701
+ await Bun.write(join10(cwd, ".reroute/markdown-components.ts"), mdComponentsLines.join(`
7967
8702
  `));
7968
8703
  }
7969
8704
  const indexLines = [];
@@ -7983,7 +8718,7 @@ async function generate(cwd, args = [], options = {}) {
7983
8718
  const ogTemplatePath = config2.ogImage.defaultTemplate.startsWith(".") ? `../${config2.ogImage.defaultTemplate}` : config2.ogImage.defaultTemplate;
7984
8719
  indexLines.push(`import OGTemplate from '${ogTemplatePath}';`);
7985
8720
  }
7986
- const ogFiles = await scanOgFiles(join9(cwd, ROUTES_DIR));
8721
+ const ogFiles = await scanOgFiles(join10(cwd, ROUTES_DIR));
7987
8722
  const ogImports = [];
7988
8723
  for (let i = 0;i < ogFiles.length; i++) {
7989
8724
  const ogFile = ogFiles[i];
@@ -8029,7 +8764,7 @@ async function generate(cwd, args = [], options = {}) {
8029
8764
  }
8030
8765
  indexLines.push("};");
8031
8766
  }
8032
- const templatePath = join9(cwd, "src", "client", "index.html");
8767
+ const templatePath = join10(cwd, "src", "client", "index.html");
8033
8768
  try {
8034
8769
  const templateContent = await Bun.file(templatePath).text();
8035
8770
  indexLines.push("");
@@ -8041,7 +8776,7 @@ async function generate(cwd, args = [], options = {}) {
8041
8776
  indexLines.push(`(globalThis as any).__REROUTE_CONFIG__ = ${serializeConfig(config2)};`);
8042
8777
  indexLines.push("");
8043
8778
  indexLines.push("export type RerouteBundle = typeof bundle;");
8044
- await Bun.write(join9(cwd, OUTPUT_ARTIFACTS), indexLines.join(`
8779
+ await Bun.write(join10(cwd, OUTPUT_ARTIFACTS), indexLines.join(`
8045
8780
  `));
8046
8781
  const barrelLines = [
8047
8782
  "// \uD83D\uDEA8 Auto-generated by Reroute - DO NOT EDIT \uD83D\uDEA8",
@@ -8051,7 +8786,7 @@ async function generate(cwd, args = [], options = {}) {
8051
8786
  "",
8052
8787
  "export * from './artifacts';"
8053
8788
  ];
8054
- await Bun.write(join9(cwd, OUTPUT_INDEX), barrelLines.join(`
8789
+ await Bun.write(join10(cwd, OUTPUT_INDEX), barrelLines.join(`
8055
8790
  `));
8056
8791
  const entryLines = [
8057
8792
  "// \uD83D\uDEA8 Auto-generated by Reroute - DO NOT EDIT \uD83D\uDEA8",
@@ -8067,10 +8802,10 @@ async function generate(cwd, args = [], options = {}) {
8067
8802
  "import './routes'; // Import routes (bundles all route components)",
8068
8803
  "import '../src/index'; // Import user's server entry"
8069
8804
  ];
8070
- await Bun.write(join9(cwd, ".reroute/entry.ts"), entryLines.join(`
8805
+ await Bun.write(join10(cwd, ".reroute/entry.ts"), entryLines.join(`
8071
8806
  `));
8072
8807
  if (!showPerformance) {
8073
- console.log(`[reroute/gen] Generated artifacts: ${join9(cwd, OUTPUT_ARTIFACTS)}`);
8808
+ console.log(`[reroute/gen] Generated artifacts: ${join10(cwd, OUTPUT_ARTIFACTS)}`);
8074
8809
  }
8075
8810
  await perfTrack("Bundle building", () => buildEntrypointBundle(cwd, args));
8076
8811
  const endTime = performance.now();
@@ -8197,7 +8932,7 @@ async function gen(args) {
8197
8932
  const mediumDebounce = createDebouncer(150);
8198
8933
  const slowDebounce = createDebouncer(300);
8199
8934
  const loadedConfig = await loadConfig(cwd);
8200
- const configPath = join9(cwd, "reroute.config.ts");
8935
+ const configPath = join10(cwd, "reroute.config.ts");
8201
8936
  let configWatcher = null;
8202
8937
  try {
8203
8938
  configWatcher = watch(configPath, () => {
@@ -8219,7 +8954,7 @@ async function gen(args) {
8219
8954
  ];
8220
8955
  const envWatchers = [];
8221
8956
  for (const envFile of envFiles) {
8222
- const envPath = join9(cwd, envFile);
8957
+ const envPath = join10(cwd, envFile);
8223
8958
  try {
8224
8959
  const envWatcher = watch(envPath, () => {
8225
8960
  mediumDebounce(() => {
@@ -8238,15 +8973,15 @@ async function gen(args) {
8238
8973
  let packagesWatcher = null;
8239
8974
  const isRerouteWorkspace = async () => {
8240
8975
  try {
8241
- const packagesPath = join9(cwd, "..", "..", "packages");
8242
- const exists = await Bun.file(join9(packagesPath, "core", "index.ts")).exists();
8976
+ const packagesPath = join10(cwd, "..", "..", "packages");
8977
+ const exists = await Bun.file(join10(packagesPath, "core", "index.ts")).exists();
8243
8978
  return exists;
8244
8979
  } catch {
8245
8980
  return false;
8246
8981
  }
8247
8982
  };
8248
8983
  if (await isRerouteWorkspace()) {
8249
- const packagesPath = join9(cwd, "..", "..", "packages");
8984
+ const packagesPath = join10(cwd, "..", "..", "packages");
8250
8985
  console.log("[reroute/watch] \uD83D\uDD27 Workspace development mode enabled");
8251
8986
  console.log(`[reroute/watch] \uD83D\uDD27 Watching ${packagesPath}...`);
8252
8987
  packagesWatcher = watch(packagesPath, { recursive: true }, (_ev, filename) => {
@@ -8269,7 +9004,7 @@ async function gen(args) {
8269
9004
  });
8270
9005
  });
8271
9006
  }
8272
- const routesPath = join9(cwd, ROUTES_DIR);
9007
+ const routesPath = join10(cwd, ROUTES_DIR);
8273
9008
  console.log(`[reroute/watch] \uD83D\uDCC1 Watching ${routesPath}...`);
8274
9009
  const routesWatcher = watch(routesPath, { recursive: true }, (_ev, filename) => {
8275
9010
  if (!filename)
@@ -8291,7 +9026,16 @@ async function gen(args) {
8291
9026
  }
8292
9027
  console.log("[reroute/watch] ├─ Building content chunks...");
8293
9028
  const contentStart = performance.now();
8294
- await buildContentChunks(cwd, args);
9029
+ const watchI18n = await discoverI18n(cwd, loadedConfig.i18n);
9030
+ await Bun.write(join10(cwd, ".reroute", "i18n.json"), JSON.stringify({
9031
+ enabled: watchI18n.enabled,
9032
+ defaultLocale: watchI18n.defaultLocale,
9033
+ locales: watchI18n.locales,
9034
+ cookie: watchI18n.cookie,
9035
+ redirectContentOnNegotiate: watchI18n.redirectContentOnNegotiate,
9036
+ catalogs: watchI18n.catalogs
9037
+ }, null, 2));
9038
+ await buildContentChunks(cwd, args, watchI18n.defaultLocale);
8295
9039
  const contentTime = ((performance.now() - contentStart) / 1000).toFixed(2);
8296
9040
  console.log(`[reroute/watch] └─ Content: ${contentTime}s`);
8297
9041
  if (loadedConfig.search?.enabled) {
@@ -8320,7 +9064,7 @@ async function gen(args) {
8320
9064
  }, "routes change");
8321
9065
  });
8322
9066
  });
8323
- const clientPath = join9(cwd, "src", "client");
9067
+ const clientPath = join10(cwd, "src", "client");
8324
9068
  console.log(`[reroute/watch] \uD83D\uDCE6 Watching ${clientPath}...`);
8325
9069
  const twWatcher = watch(clientPath, { recursive: true }, (_ev, filename) => {
8326
9070
  if (!filename)
@@ -13178,7 +13922,7 @@ var require_program = __commonJS((exports, module) => {
13178
13922
  }
13179
13923
  break;
13180
13924
  case "underline":
13181
- if (!blink) {} else {}
13925
+ if (!blink) {}
13182
13926
  break;
13183
13927
  case "line":
13184
13928
  if (!blink) {
@@ -100442,7 +101186,7 @@ var require_lodash = __commonJS((exports, module) => {
100442
101186
  }
100443
101187
  return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined2, comparator) : [];
100444
101188
  });
100445
- function join10(array, separator) {
101189
+ function join11(array, separator) {
100446
101190
  return array == null ? "" : nativeJoin.call(array, separator);
100447
101191
  }
100448
101192
  function last(array) {
@@ -102374,7 +103118,7 @@ __p += '`;
102374
103118
  lodash.isUndefined = isUndefined;
102375
103119
  lodash.isWeakMap = isWeakMap;
102376
103120
  lodash.isWeakSet = isWeakSet;
102377
- lodash.join = join10;
103121
+ lodash.join = join11;
102378
103122
  lodash.kebabCase = kebabCase;
102379
103123
  lodash.last = last;
102380
103124
  lodash.lastIndexOf = lastIndexOf;
@@ -103056,19 +103800,19 @@ __export(exports_dev, {
103056
103800
  });
103057
103801
  import { spawn as spawn2 } from "node:child_process";
103058
103802
  import { existsSync as existsSync6 } from "node:fs";
103059
- import { join as join10 } from "node:path";
103803
+ import { join as join11 } from "node:path";
103060
103804
  function getRerouteCommand() {
103061
- const binPath = join10(import.meta.dir, "..", "..", "bin.ts");
103805
+ const binPath = join11(import.meta.dir, "..", "..", "bin.ts");
103062
103806
  const isBunDev = existsSync6(binPath);
103063
103807
  if (isBunDev) {
103064
103808
  return `bun ${binPath}`;
103065
103809
  }
103066
103810
  const cwd = process.cwd();
103067
103811
  const nodeModulesPaths = [
103068
- join10(cwd, "node_modules", ".bin", "reroute"),
103069
- join10(cwd, "..", "node_modules", ".bin", "reroute"),
103070
- join10(cwd, "..", "..", "node_modules", ".bin", "reroute"),
103071
- join10(cwd, "..", "..", "..", "node_modules", ".bin", "reroute")
103812
+ join11(cwd, "node_modules", ".bin", "reroute"),
103813
+ join11(cwd, "..", "node_modules", ".bin", "reroute"),
103814
+ join11(cwd, "..", "..", "node_modules", ".bin", "reroute"),
103815
+ join11(cwd, "..", "..", "..", "node_modules", ".bin", "reroute")
103072
103816
  ];
103073
103817
  for (const binPath2 of nodeModulesPaths) {
103074
103818
  if (existsSync6(binPath2)) {
@@ -103224,19 +103968,19 @@ __export(exports_start, {
103224
103968
  });
103225
103969
  import { spawn as spawn3 } from "node:child_process";
103226
103970
  import { existsSync as existsSync7 } from "node:fs";
103227
- import { join as join11 } from "node:path";
103971
+ import { join as join12 } from "node:path";
103228
103972
  function getRerouteCommand2() {
103229
- const binPath = join11(import.meta.dir, "..", "..", "bin.ts");
103973
+ const binPath = join12(import.meta.dir, "..", "..", "bin.ts");
103230
103974
  const isBunDev = existsSync7(binPath);
103231
103975
  if (isBunDev) {
103232
103976
  return `bun ${binPath}`;
103233
103977
  }
103234
103978
  const cwd = process.cwd();
103235
103979
  const nodeModulesPaths = [
103236
- join11(cwd, "node_modules", ".bin", "reroute"),
103237
- join11(cwd, "..", "node_modules", ".bin", "reroute"),
103238
- join11(cwd, "..", "..", "node_modules", ".bin", "reroute"),
103239
- join11(cwd, "..", "..", "..", "node_modules", ".bin", "reroute")
103980
+ join12(cwd, "node_modules", ".bin", "reroute"),
103981
+ join12(cwd, "..", "node_modules", ".bin", "reroute"),
103982
+ join12(cwd, "..", "..", "node_modules", ".bin", "reroute"),
103983
+ join12(cwd, "..", "..", "..", "node_modules", ".bin", "reroute")
103240
103984
  ];
103241
103985
  for (const binPath2 of nodeModulesPaths) {
103242
103986
  if (existsSync7(binPath2)) {
@@ -103364,12 +104108,12 @@ import {
103364
104108
  mkdirSync,
103365
104109
  readdirSync
103366
104110
  } from "node:fs";
103367
- import { join as join12 } from "node:path";
104111
+ import { join as join13 } from "node:path";
103368
104112
  function copyAssetsToReroute(cwd, sourceAssetsDir, rerouteAssetsDir) {
103369
104113
  if (!existsSync8(sourceAssetsDir)) {
103370
104114
  return 0;
103371
104115
  }
103372
- const rerouteDir = join12(cwd, ".reroute");
104116
+ const rerouteDir = join13(cwd, ".reroute");
103373
104117
  if (!existsSync8(rerouteDir)) {
103374
104118
  mkdirSync(rerouteDir, { recursive: true });
103375
104119
  }
@@ -103381,7 +104125,7 @@ function countFiles(dir) {
103381
104125
  const entries = readdirSync(dir, { withFileTypes: true });
103382
104126
  for (const entry of entries) {
103383
104127
  if (entry.isDirectory()) {
103384
- count += countFiles(join12(dir, entry.name));
104128
+ count += countFiles(join13(dir, entry.name));
103385
104129
  } else {
103386
104130
  count++;
103387
104131
  }
@@ -103397,19 +104141,19 @@ __export(exports_build, {
103397
104141
  });
103398
104142
  import { spawn as spawn4 } from "node:child_process";
103399
104143
  import { existsSync as existsSync9, readFileSync as readFileSync5, rmSync, unlinkSync } from "node:fs";
103400
- import { join as join13 } from "node:path";
104144
+ import { join as join14 } from "node:path";
103401
104145
  function getRerouteCommand3() {
103402
- const binPath = join13(import.meta.dir, "..", "..", "bin.ts");
104146
+ const binPath = join14(import.meta.dir, "..", "..", "bin.ts");
103403
104147
  const isBunDev = existsSync9(binPath);
103404
104148
  if (isBunDev) {
103405
104149
  return `bun ${binPath}`;
103406
104150
  }
103407
104151
  const cwd = process.cwd();
103408
104152
  const nodeModulesPaths = [
103409
- join13(cwd, "node_modules", ".bin", "reroute"),
103410
- join13(cwd, "..", "node_modules", ".bin", "reroute"),
103411
- join13(cwd, "..", "..", "node_modules", ".bin", "reroute"),
103412
- join13(cwd, "..", "..", "..", "node_modules", ".bin", "reroute")
104153
+ join14(cwd, "node_modules", ".bin", "reroute"),
104154
+ join14(cwd, "..", "node_modules", ".bin", "reroute"),
104155
+ join14(cwd, "..", "..", "node_modules", ".bin", "reroute"),
104156
+ join14(cwd, "..", "..", "..", "node_modules", ".bin", "reroute")
103413
104157
  ];
103414
104158
  for (const binPath2 of nodeModulesPaths) {
103415
104159
  if (existsSync9(binPath2)) {
@@ -103420,7 +104164,7 @@ function getRerouteCommand3() {
103420
104164
  }
103421
104165
  function getOutputName() {
103422
104166
  const cwd = process.cwd();
103423
- const packageJsonPath = join13(cwd, "package.json");
104167
+ const packageJsonPath = join14(cwd, "package.json");
103424
104168
  if (existsSync9(packageJsonPath)) {
103425
104169
  try {
103426
104170
  const packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
@@ -103452,7 +104196,7 @@ function parseBuildArgs(args) {
103452
104196
  return { outfile, compileBinary, noCompression, externalArgs };
103453
104197
  }
103454
104198
  function cleanDistFolder(cwd) {
103455
- const distDir = join13(cwd, "dist");
104199
+ const distDir = join14(cwd, "dist");
103456
104200
  if (existsSync9(distDir)) {
103457
104201
  process.stdout.write(colorizeLogPrefixAnsi(`[reroute/build] Cleaning dist folder...
103458
104202
  `));
@@ -103460,7 +104204,7 @@ function cleanDistFolder(cwd) {
103460
104204
  }
103461
104205
  }
103462
104206
  async function cleanRerouteFolder(cwd) {
103463
- const rerouteDir = join13(cwd, ".reroute");
104207
+ const rerouteDir = join14(cwd, ".reroute");
103464
104208
  if (existsSync9(rerouteDir)) {
103465
104209
  process.stdout.write(colorizeLogPrefixAnsi(`[reroute/build] Cleaning .reroute folder...
103466
104210
  `));
@@ -103517,8 +104261,8 @@ async function copyAssets(cwd) {
103517
104261
  const appSrc = config2.app?.src ?? "./src/client/App";
103518
104262
  const dirname6 = __require("node:path").dirname;
103519
104263
  const assetsPath = dirname6(appSrc);
103520
- const sourceAssetsDir = join13(cwd, assetsPath, "assets");
103521
- const rerouteAssetsDir = join13(cwd, ".reroute", "assets");
104264
+ const sourceAssetsDir = join14(cwd, assetsPath, "assets");
104265
+ const rerouteAssetsDir = join14(cwd, ".reroute", "assets");
103522
104266
  process.stdout.write(colorizeLogPrefixAnsi(`[reroute/build] Copying assets...
103523
104267
  `));
103524
104268
  const fileCount = copyAssetsToReroute(cwd, sourceAssetsDir, rerouteAssetsDir);
@@ -103533,9 +104277,9 @@ async function copyAssets(cwd) {
103533
104277
  async function buildBundle(cwd, buildArgs) {
103534
104278
  const external = [...buildArgs.externalArgs, "sharp", "@vercel/og"];
103535
104279
  const buildResult = await Bun.build({
103536
- entrypoints: [join13(cwd, ".reroute/entry.ts")],
104280
+ entrypoints: [join14(cwd, ".reroute/entry.ts")],
103537
104281
  target: "bun",
103538
- outdir: join13(cwd, "dist"),
104282
+ outdir: join14(cwd, "dist"),
103539
104283
  sourcemap: "external",
103540
104284
  ...getCommonBuildOptions(),
103541
104285
  external,
@@ -103578,7 +104322,7 @@ function cleanupBundledFiles(rerouteDir) {
103578
104322
  "markdown-components.ts"
103579
104323
  ];
103580
104324
  for (const file of tsFilesToRemove) {
103581
- const filePath = join13(rerouteDir, file);
104325
+ const filePath = join14(rerouteDir, file);
103582
104326
  if (existsSync9(filePath)) {
103583
104327
  try {
103584
104328
  unlinkSync(filePath);
@@ -103588,7 +104332,7 @@ function cleanupBundledFiles(rerouteDir) {
103588
104332
  }
103589
104333
  const dirsToRemove = ["markdown", "streaming"];
103590
104334
  for (const dir of dirsToRemove) {
103591
- const dirPath = join13(rerouteDir, dir);
104335
+ const dirPath = join14(rerouteDir, dir);
103592
104336
  if (existsSync9(dirPath)) {
103593
104337
  try {
103594
104338
  rmSync(dirPath, { recursive: true });
@@ -103622,7 +104366,7 @@ async function build(args) {
103622
104366
  await runGenCommand(rerouteCmd, useShell, buildArgs.noCompression);
103623
104367
  await copyAssets(cwd);
103624
104368
  await buildBundle(cwd, buildArgs);
103625
- const rerouteDir = join13(cwd, ".reroute");
104369
+ const rerouteDir = join14(cwd, ".reroute");
103626
104370
  cleanupBundledFiles(rerouteDir);
103627
104371
  logBuildComplete(buildStartTime, buildArgs);
103628
104372
  process.exit(0);
@@ -103644,19 +104388,19 @@ __export(exports_command, {
103644
104388
  getRerouteCommand: () => getRerouteCommand4
103645
104389
  });
103646
104390
  import { existsSync as existsSync10 } from "node:fs";
103647
- import { join as join14 } from "node:path";
104391
+ import { join as join15 } from "node:path";
103648
104392
  function getRerouteCommand4() {
103649
- const binPath = join14(import.meta.dir, "..", "..", "bin.ts");
104393
+ const binPath = join15(import.meta.dir, "..", "..", "bin.ts");
103650
104394
  const isBunDev = existsSync10(binPath);
103651
104395
  if (isBunDev) {
103652
104396
  return `bun ${binPath}`;
103653
104397
  }
103654
104398
  const cwd = process.cwd();
103655
104399
  const nodeModulesPaths = [
103656
- join14(cwd, "node_modules", ".bin", "reroute"),
103657
- join14(cwd, "..", "node_modules", ".bin", "reroute"),
103658
- join14(cwd, "..", "..", "node_modules", ".bin", "reroute"),
103659
- join14(cwd, "..", "..", "..", "node_modules", ".bin", "reroute")
104400
+ join15(cwd, "node_modules", ".bin", "reroute"),
104401
+ join15(cwd, "..", "node_modules", ".bin", "reroute"),
104402
+ join15(cwd, "..", "..", "node_modules", ".bin", "reroute"),
104403
+ join15(cwd, "..", "..", "..", "node_modules", ".bin", "reroute")
103660
104404
  ];
103661
104405
  for (const binPath2 of nodeModulesPaths) {
103662
104406
  if (existsSync10(binPath2)) {
@@ -103673,24 +104417,24 @@ __export(exports_analyze, {
103673
104417
  default: () => analyze
103674
104418
  });
103675
104419
  import { existsSync as existsSync11, statSync } from "node:fs";
103676
- import { readdir as readdir6, readFile, stat as stat5, writeFile } from "node:fs/promises";
103677
- import { basename as basename3, join as join15, relative as relative3 } from "node:path";
104420
+ import { readdir as readdir8, readFile, stat as stat5, writeFile } from "node:fs/promises";
104421
+ import { basename as basename3, join as join16, relative as relative3 } from "node:path";
103678
104422
  import Table from "cli-table3";
103679
104423
  async function findChunkBundles(cwd) {
103680
104424
  const bundles = [];
103681
- const chunksDir = join15(cwd, ".reroute", "chunks");
104425
+ const chunksDir = join16(cwd, ".reroute", "chunks");
103682
104426
  if (!existsSync11(chunksDir))
103683
104427
  return bundles;
103684
- const collections2 = await readdir6(chunksDir, { withFileTypes: true });
104428
+ const collections2 = await readdir8(chunksDir, { withFileTypes: true });
103685
104429
  for (const collection of collections2) {
103686
104430
  if (!collection.isDirectory())
103687
104431
  continue;
103688
- const collectionDir = join15(chunksDir, collection.name);
103689
- const files = await readdir6(collectionDir);
104432
+ const collectionDir = join16(chunksDir, collection.name);
104433
+ const files = await readdir8(collectionDir);
103690
104434
  for (const file of files) {
103691
104435
  if (!file.endsWith(".js"))
103692
104436
  continue;
103693
- const filePath = join15(collectionDir, file);
104437
+ const filePath = join16(collectionDir, file);
103694
104438
  const stats = await stat5(filePath);
103695
104439
  const content2 = await Bun.file(filePath).arrayBuffer();
103696
104440
  const gzippedSize = Bun.gzipSync(new Uint8Array(content2)).length;
@@ -103708,12 +104452,12 @@ async function findChunkBundles(cwd) {
103708
104452
  }
103709
104453
  async function findDistBundles(cwd) {
103710
104454
  const bundles = [];
103711
- const distDir = join15(cwd, "dist");
104455
+ const distDir = join16(cwd, "dist");
103712
104456
  if (!existsSync11(distDir))
103713
104457
  return bundles;
103714
- const files = await readdir6(distDir);
104458
+ const files = await readdir8(distDir);
103715
104459
  for (const file of files) {
103716
- const filePath = join15(distDir, file);
104460
+ const filePath = join16(distDir, file);
103717
104461
  const stats = statSync(filePath);
103718
104462
  if (stats.isFile() && !file.endsWith(".map") && !file.endsWith(".d.ts")) {
103719
104463
  const content2 = await Bun.file(filePath).arrayBuffer();
@@ -103739,9 +104483,9 @@ async function findBundles(cwd) {
103739
104483
  }
103740
104484
  async function findBrowserEntry(cwd) {
103741
104485
  try {
103742
- const clientDir = join15(cwd, "src", "client");
103743
- const tsxEntry = join15(clientDir, "index.tsx");
103744
- const tsEntry = join15(clientDir, "index.ts");
104486
+ const clientDir = join16(cwd, "src", "client");
104487
+ const tsxEntry = join16(clientDir, "index.tsx");
104488
+ const tsEntry = join16(clientDir, "index.ts");
103745
104489
  let sourceEntry = null;
103746
104490
  if (existsSync11(tsxEntry)) {
103747
104491
  sourceEntry = tsxEntry;
@@ -103774,12 +104518,12 @@ async function findBrowserEntry(cwd) {
103774
104518
  console.warn("[reroute/analyze] Browser build produced no outputs");
103775
104519
  return null;
103776
104520
  }
103777
- const tempDir = join15(cwd, ".reroute", "browser");
104521
+ const tempDir = join16(cwd, ".reroute", "browser");
103778
104522
  await import("node:fs/promises").then((fs) => fs.mkdir(tempDir, { recursive: true }));
103779
104523
  const output = result.outputs[0];
103780
104524
  const code = await output.text();
103781
104525
  const actualHash = await generateContentHash2(code);
103782
- const bundlePath = join15(tempDir, `index.${actualHash}.js`);
104526
+ const bundlePath = join16(tempDir, `index.${actualHash}.js`);
103783
104527
  await writeFile(bundlePath, code, "utf-8");
103784
104528
  const mapOutput = result.outputs.find((o) => o.path.endsWith(".map"));
103785
104529
  if (mapOutput) {
@@ -103831,7 +104575,7 @@ function calculateStats(bundles) {
103831
104575
  }
103832
104576
  async function getInstalledPackageSizes(cwd) {
103833
104577
  const packageSizes = new Map;
103834
- const nodeModulesDir = join15(cwd, "node_modules");
104578
+ const nodeModulesDir = join16(cwd, "node_modules");
103835
104579
  if (!existsSync11(nodeModulesDir))
103836
104580
  return packageSizes;
103837
104581
  try {
@@ -103887,7 +104631,7 @@ async function buildDependencyTree(entryPath, cwd) {
103887
104631
  if (!pkgNode) {
103888
104632
  pkgNode = {
103889
104633
  name: pkgName || "unknown",
103890
- path: join15(cwd, "node_modules", pkgName || ""),
104634
+ path: join16(cwd, "node_modules", pkgName || ""),
103891
104635
  size: 0,
103892
104636
  children: []
103893
104637
  };
@@ -103908,7 +104652,7 @@ async function buildDependencyTree(entryPath, cwd) {
103908
104652
  if (!child) {
103909
104653
  child = {
103910
104654
  name: part,
103911
- path: join15(cwd, ...parts.slice(0, i + 1)),
104655
+ path: join16(cwd, ...parts.slice(0, i + 1)),
103912
104656
  size: 0,
103913
104657
  children: []
103914
104658
  };
@@ -103939,7 +104683,7 @@ async function buildDependencyTree(entryPath, cwd) {
103939
104683
  const modulePattern = /\/\/\s*(.+?\.(?:ts|tsx|js|jsx|mjs|cjs))\s*$/gm;
103940
104684
  const nodeModulesNode = {
103941
104685
  name: "node_modules",
103942
- path: join15(cwd, "node_modules"),
104686
+ path: join16(cwd, "node_modules"),
103943
104687
  size: 0,
103944
104688
  children: []
103945
104689
  };
@@ -104005,7 +104749,7 @@ async function buildTreeFromSourceMap(bundlePath, cwd) {
104005
104749
  if (sourcePath.startsWith("/") || sourcePath.match(/^[A-Za-z]:\\/))
104006
104750
  return sourcePath;
104007
104751
  const bundleDir = bundlePath.substring(0, bundlePath.lastIndexOf("/"));
104008
- return join15(bundleDir, sourceRoot || "", sourcePath);
104752
+ return join16(bundleDir, sourceRoot || "", sourcePath);
104009
104753
  }, computeSizes = function(node) {
104010
104754
  if (node.children && node.children.length > 0) {
104011
104755
  node.size = node.children.reduce((s, c) => s + computeSizes(c), 0);
@@ -104077,7 +104821,7 @@ async function buildTreeFromSourceMap(bundlePath, cwd) {
104077
104821
  };
104078
104822
  const nodeModulesNode = {
104079
104823
  name: "node_modules",
104080
- path: join15(cwd, "node_modules"),
104824
+ path: join16(cwd, "node_modules"),
104081
104825
  size: 0,
104082
104826
  children: []
104083
104827
  };
@@ -104117,7 +104861,7 @@ async function buildTreeFromSourceMap(bundlePath, cwd) {
104117
104861
  if (!pkgNode) {
104118
104862
  pkgNode = {
104119
104863
  name: pkgName || "unknown",
104120
- path: join15(cwd, "node_modules", pkgName || ""),
104864
+ path: join16(cwd, "node_modules", pkgName || ""),
104121
104865
  size: 0,
104122
104866
  children: []
104123
104867
  };
@@ -104138,7 +104882,7 @@ async function buildTreeFromSourceMap(bundlePath, cwd) {
104138
104882
  if (!child) {
104139
104883
  child = {
104140
104884
  name: part,
104141
- path: join15(cwd, ...parts.slice(0, i2 + 1)),
104885
+ path: join16(cwd, ...parts.slice(0, i2 + 1)),
104142
104886
  size: 0,
104143
104887
  children: []
104144
104888
  };
@@ -104168,11 +104912,11 @@ async function generateHTMLReport(stats, depTree, mainInfo, browserDepTree, brow
104168
104912
  let actualServerBinarySize = null;
104169
104913
  let actualServerBinaryGzipped = null;
104170
104914
  try {
104171
- const distDir = join15(process.cwd(), "dist");
104915
+ const distDir = join16(process.cwd(), "dist");
104172
104916
  if (existsSync11(distDir)) {
104173
- const files = await readdir6(distDir);
104917
+ const files = await readdir8(distDir);
104174
104918
  for (const file of files) {
104175
- const filePath = join15(distDir, file);
104919
+ const filePath = join16(distDir, file);
104176
104920
  const stats2 = statSync(filePath);
104177
104921
  if (stats2.isFile() && !file.includes(".")) {
104178
104922
  actualServerBinarySize = stats2.size;
@@ -105654,11 +106398,11 @@ async function printConsoleReport(stats, depTree, mainInfo, browserDepTree, brow
105654
106398
  let actualServerBinarySize = null;
105655
106399
  let actualServerBinaryGzipped = null;
105656
106400
  try {
105657
- const distDir = join15(process.cwd(), "dist");
106401
+ const distDir = join16(process.cwd(), "dist");
105658
106402
  if (existsSync11(distDir)) {
105659
- const files = await readdir6(distDir);
106403
+ const files = await readdir8(distDir);
105660
106404
  for (const file of files) {
105661
- const filePath = join15(distDir, file);
106405
+ const filePath = join16(distDir, file);
105662
106406
  const stats2 = statSync(filePath);
105663
106407
  if (stats2.isFile() && !file.includes(".")) {
105664
106408
  actualServerBinarySize = stats2.size;
@@ -105692,7 +106436,7 @@ async function analyze(args) {
105692
106436
  const isProd = isProductionMode(args);
105693
106437
  const searchIndex = args.indexOf("--search");
105694
106438
  const initialSearch = searchIndex !== -1 && args[searchIndex + 1] ? args[searchIndex + 1] : "";
105695
- const rerouteIndexPath = join15(cwd, ".reroute", "index.ts");
106439
+ const rerouteIndexPath = join16(cwd, ".reroute", "index.ts");
105696
106440
  if (!existsSync11(rerouteIndexPath)) {
105697
106441
  console.log(colorizeLogPrefixAnsi(`[reroute/analyze] .reroute artifacts not found, running build...
105698
106442
  `));
@@ -105755,12 +106499,12 @@ async function analyze(args) {
105755
106499
  let entry = null;
105756
106500
  let depTree = null;
105757
106501
  let mainInfo = null;
105758
- const srcEntry = join15(cwd, "src", "index.ts");
106502
+ const srcEntry = join16(cwd, "src", "index.ts");
105759
106503
  if (existsSync11(srcEntry)) {
105760
106504
  console.log(colorizeLogPrefixAnsi(`[reroute/analyze] Building server binary for analysis...
105761
106505
  `));
105762
106506
  const { mkdir: mkdir3 } = await import("node:fs/promises");
105763
- const serverAnalysisDir = join15(cwd, ".reroute", "server");
106507
+ const serverAnalysisDir = join16(cwd, ".reroute", "server");
105764
106508
  await mkdir3(serverAnalysisDir, { recursive: true });
105765
106509
  const result = await Bun.build({
105766
106510
  entrypoints: [srcEntry],
@@ -105773,7 +106517,7 @@ async function analyze(args) {
105773
106517
  if (result.success && result.outputs.length > 0) {
105774
106518
  const output = result.outputs[0];
105775
106519
  const code = await output.text();
105776
- const analysisEntry = join15(serverAnalysisDir, "index.js");
106520
+ const analysisEntry = join16(serverAnalysisDir, "index.js");
105777
106521
  await writeFile(analysisEntry, code, "utf-8");
105778
106522
  const mapOutput = result.outputs.find((o) => o.path.endsWith(".map"));
105779
106523
  if (mapOutput) {
@@ -105791,7 +106535,7 @@ async function analyze(args) {
105791
106535
  `));
105792
106536
  const installedSizes = await getInstalledPackageSizes(cwd);
105793
106537
  if (depTree) {
105794
- const packageJsonPath = join15(cwd, "package.json");
106538
+ const packageJsonPath = join16(cwd, "package.json");
105795
106539
  if (existsSync11(packageJsonPath)) {
105796
106540
  try {
105797
106541
  const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
@@ -105803,7 +106547,7 @@ async function analyze(args) {
105803
106547
  if (!trackedPkgs.has(pkgName) && installedSizes.has(pkgName)) {
105804
106548
  nodeModulesNode.children?.push({
105805
106549
  name: pkgName,
105806
- path: join15(cwd, "node_modules", pkgName),
106550
+ path: join16(cwd, "node_modules", pkgName),
105807
106551
  size: installedSizes.get(pkgName) || 0
105808
106552
  });
105809
106553
  }
@@ -105834,10 +106578,10 @@ async function analyze(args) {
105834
106578
  const fullOutput = args.includes("--full");
105835
106579
  await printConsoleReport(stats, depTree, mainInfo, browserDepTree, browserInfo, fullOutput);
105836
106580
  if (outputHtml || openBrowser) {
105837
- const htmlPath = join15(cwd, ".reroute", "analysis.html");
106581
+ const htmlPath = join16(cwd, ".reroute", "analysis.html");
105838
106582
  const html2 = await generateHTMLReport(stats, depTree, mainInfo, browserDepTree, browserInfo, initialSearch);
105839
106583
  const { mkdir: mkdir3 } = await import("node:fs/promises");
105840
- await mkdir3(join15(cwd, ".reroute"), { recursive: true });
106584
+ await mkdir3(join16(cwd, ".reroute"), { recursive: true });
105841
106585
  await writeFile(htmlPath, html2, "utf-8");
105842
106586
  console.log(colorizeLogPrefixAnsi(`[reroute/analyze] HTML report generated: ${relative3(cwd, htmlPath)}
105843
106587
  `));
@@ -105900,8 +106644,8 @@ __export(exports_og, {
105900
106644
  default: () => og2
105901
106645
  });
105902
106646
  import { spawn as spawn5 } from "node:child_process";
105903
- import { readdir as readdir7 } from "node:fs/promises";
105904
- import { join as join16 } from "node:path";
106647
+ import { readdir as readdir9 } from "node:fs/promises";
106648
+ import { join as join17 } from "node:path";
105905
106649
  function getPortFromConfig(baseUrl) {
105906
106650
  if (!baseUrl)
105907
106651
  return 3000;
@@ -105978,10 +106722,10 @@ Error output:`, errorOutput);
105978
106722
  }
105979
106723
  async function scanForOgTemplates(dir, base, routes) {
105980
106724
  try {
105981
- const entries = await readdir7(dir, { withFileTypes: true });
106725
+ const entries = await readdir9(dir, { withFileTypes: true });
105982
106726
  for (const entry of entries) {
105983
- const fullPath = join16(dir, entry.name);
105984
- const relativePath = join16(base, entry.name);
106727
+ const fullPath = join17(dir, entry.name);
106728
+ const relativePath = join17(base, entry.name);
105985
106729
  if (entry.isDirectory()) {
105986
106730
  if (entry.name === "content")
105987
106731
  continue;
@@ -105998,13 +106742,13 @@ function findFirstContentFile(contentFiles) {
105998
106742
  }
105999
106743
  async function addContentRoutes(routesPath, routes) {
106000
106744
  try {
106001
- const entries = await readdir7(routesPath, { withFileTypes: true });
106745
+ const entries = await readdir9(routesPath, { withFileTypes: true });
106002
106746
  for (const entry of entries) {
106003
106747
  if (!entry.isDirectory())
106004
106748
  continue;
106005
- const contentDir = join16(routesPath, entry.name, "content");
106749
+ const contentDir = join17(routesPath, entry.name, "content");
106006
106750
  try {
106007
- const contentFiles = await readdir7(contentDir);
106751
+ const contentFiles = await readdir9(contentDir);
106008
106752
  if (contentFiles.length === 0)
106009
106753
  continue;
106010
106754
  const firstFile = findFirstContentFile(contentFiles);
@@ -106019,7 +106763,7 @@ async function addContentRoutes(routesPath, routes) {
106019
106763
  async function findRoutesWithOG(cwd) {
106020
106764
  const routes = [];
106021
106765
  try {
106022
- const manifestPath = join16(cwd, ".reroute", "manifest.json");
106766
+ const manifestPath = join17(cwd, ".reroute", "manifest.json");
106023
106767
  const manifestFile = Bun.file(manifestPath);
106024
106768
  const manifest = await manifestFile.json();
106025
106769
  if (manifest.staticRoutes && Array.isArray(manifest.staticRoutes)) {
@@ -106030,7 +106774,7 @@ async function findRoutesWithOG(cwd) {
106030
106774
  }
106031
106775
  } catch {
106032
106776
  console.warn("⚠️ .reroute/manifest.json not found, scanning routes directory...");
106033
- const routesPath = join16(cwd, ROUTES_DIR2);
106777
+ const routesPath = join17(cwd, ROUTES_DIR2);
106034
106778
  await scanForOgTemplates(routesPath, "", routes);
106035
106779
  await addContentRoutes(routesPath, routes);
106036
106780
  }
@@ -106282,7 +107026,7 @@ async function getVersionString2() {
106282
107026
  }
106283
107027
  async function getVersion2() {
106284
107028
  if (true) {
106285
- return "0.45.11";
107029
+ return "0.46.0";
106286
107030
  }
106287
107031
  const possiblePaths = [
106288
107032
  path3.join(import.meta.dir, "../../../package.json"),
@@ -106299,10 +107043,10 @@ async function getVersion2() {
106299
107043
  }
106300
107044
  async function getCommit2() {
106301
107045
  if (true) {
106302
- return "c2463d3";
107046
+ return "9116acf";
106303
107047
  }
106304
107048
  return "dev";
106305
107049
  }
106306
107050
  main();
106307
107051
 
106308
- //# debugId=AD9BC92E9CCB37B264756E2164756E21
107052
+ //# debugId=0B12295AB270224764756E2164756E21