@sebbro/paraglide-lite 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/bundler-plugins/per-locale-build/index.js +5 -3
  2. package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
  3. package/dist/bundler-plugins/per-locale-build/vite-environments.js +12 -7
  4. package/dist/bundler-plugins/per-locale-build/vite-environments.js.map +1 -1
  5. package/dist/bundler-plugins/plugin-core.d.ts +38 -2
  6. package/dist/bundler-plugins/plugin-core.js +198 -11
  7. package/dist/bundler-plugins/plugin-core.js.map +1 -1
  8. package/dist/bundler-plugins/rolldown.test.js +15 -6
  9. package/dist/bundler-plugins/rolldown.test.js.map +1 -1
  10. package/dist/bundler-plugins/unplugin.js +5 -0
  11. package/dist/bundler-plugins/unplugin.js.map +1 -1
  12. package/dist/bundler-plugins/unplugin.test.js +67 -0
  13. package/dist/bundler-plugins/unplugin.test.js.map +1 -1
  14. package/dist/bundler-plugins/virtual-modules.test.js +64 -1
  15. package/dist/bundler-plugins/virtual-modules.test.js.map +1 -1
  16. package/dist/cli/commands/compile/command.js +8 -1
  17. package/dist/cli/commands/compile/command.js.map +1 -1
  18. package/dist/cli/commands/compile/command.test.js +51 -0
  19. package/dist/cli/commands/compile/command.test.js.map +1 -1
  20. package/dist/compiler/compile-annotation.d.ts +2 -2
  21. package/dist/compiler/compile-annotation.js +9 -9
  22. package/dist/compiler/compile-annotation.js.map +1 -1
  23. package/dist/compiler/compile-local-variable.d.ts +2 -1
  24. package/dist/compiler/compile-local-variable.js +4 -4
  25. package/dist/compiler/compile-local-variable.js.map +1 -1
  26. package/dist/compiler/compile-local-variable.test.js +82 -0
  27. package/dist/compiler/compile-local-variable.test.js.map +1 -1
  28. package/dist/compiler/compile-message.js +10 -2
  29. package/dist/compiler/compile-message.js.map +1 -1
  30. package/dist/compiler/compile-message.test.js +132 -2
  31. package/dist/compiler/compile-message.test.js.map +1 -1
  32. package/dist/compiler/compile-pattern.js +4 -2
  33. package/dist/compiler/compile-pattern.js.map +1 -1
  34. package/dist/compiler/compile-pattern.test.js +31 -0
  35. package/dist/compiler/compile-pattern.test.js.map +1 -1
  36. package/dist/compiler/compile.bench.js +1 -1
  37. package/dist/compiler/compile.bench.js.map +1 -1
  38. package/dist/compiler/compile.test.js +14 -0
  39. package/dist/compiler/compile.test.js.map +1 -1
  40. package/dist/compiler/compiler-options.d.ts +9 -1
  41. package/dist/compiler/compiler-options.js.map +1 -1
  42. package/dist/compiler/emit-message-dts.d.ts +18 -0
  43. package/dist/compiler/emit-message-dts.js +166 -0
  44. package/dist/compiler/emit-message-dts.js.map +1 -0
  45. package/dist/compiler/emit-message-dts.test.d.ts +1 -0
  46. package/dist/compiler/emit-message-dts.test.js +283 -0
  47. package/dist/compiler/emit-message-dts.test.js.map +1 -0
  48. package/dist/compiler/runtime/create-runtime.d.ts +1 -0
  49. package/dist/compiler/runtime/create-runtime.js +11 -0
  50. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  51. package/dist/compiler/runtime/exec-url-pattern.d.ts +9 -0
  52. package/dist/compiler/runtime/exec-url-pattern.js +31 -0
  53. package/dist/compiler/runtime/exec-url-pattern.js.map +1 -0
  54. package/dist/compiler/runtime/extract-locale-from-url.js +9 -4
  55. package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
  56. package/dist/compiler/runtime/generate-static-localized-urls.js +13 -6
  57. package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
  58. package/dist/compiler/runtime/generate-static-localized-urls.test.js +38 -0
  59. package/dist/compiler/runtime/generate-static-localized-urls.test.js.map +1 -1
  60. package/dist/compiler/runtime/host-routing.d.ts +3 -3
  61. package/dist/compiler/runtime/host-routing.js +2 -2
  62. package/dist/compiler/runtime/host-routing.js.map +1 -1
  63. package/dist/compiler/runtime/host-routing.test.js +79 -0
  64. package/dist/compiler/runtime/host-routing.test.js.map +1 -1
  65. package/dist/compiler/runtime/localize-href.bench.d.ts +1 -0
  66. package/dist/compiler/runtime/localize-href.bench.js +68 -0
  67. package/dist/compiler/runtime/localize-href.bench.js.map +1 -0
  68. package/dist/compiler/runtime/localize-href.test.js +35 -0
  69. package/dist/compiler/runtime/localize-href.test.js.map +1 -1
  70. package/dist/compiler/runtime/localize-url.d.ts +14 -0
  71. package/dist/compiler/runtime/localize-url.js +338 -19
  72. package/dist/compiler/runtime/localize-url.js.map +1 -1
  73. package/dist/compiler/runtime/localize-url.test.js +139 -0
  74. package/dist/compiler/runtime/localize-url.test.js.map +1 -1
  75. package/dist/compiler/runtime/normalize-trailing-slash.d.ts +10 -0
  76. package/dist/compiler/runtime/normalize-trailing-slash.js +23 -0
  77. package/dist/compiler/runtime/normalize-trailing-slash.js.map +1 -0
  78. package/dist/compiler/runtime/route-strategy.js +4 -2
  79. package/dist/compiler/runtime/route-strategy.js.map +1 -1
  80. package/dist/compiler/runtime/should-redirect.js +4 -1
  81. package/dist/compiler/runtime/should-redirect.js.map +1 -1
  82. package/dist/compiler/runtime/should-redirect.test.js +26 -2
  83. package/dist/compiler/runtime/should-redirect.test.js.map +1 -1
  84. package/dist/compiler/runtime/type.d.ts +1 -0
  85. package/dist/compiler/runtime/type.js.map +1 -1
  86. package/dist/compiler/runtime/url-pattern.d.ts +11 -0
  87. package/dist/compiler/runtime/url-pattern.js +39 -1
  88. package/dist/compiler/runtime/url-pattern.js.map +1 -1
  89. package/dist/compiler/runtime/variables.d.ts +9 -3
  90. package/dist/compiler/runtime/variables.js +7 -1
  91. package/dist/compiler/runtime/variables.js.map +1 -1
  92. package/dist/compiler/server/create-server-file.js +18 -4
  93. package/dist/compiler/server/create-server-file.js.map +1 -1
  94. package/dist/compiler/server/create-server-file.test.js +76 -0
  95. package/dist/compiler/server/create-server-file.test.js.map +1 -1
  96. package/dist/compiler/server/middleware.js +6 -2
  97. package/dist/compiler/server/middleware.js.map +1 -1
  98. package/dist/compiler/server/middleware.test.js +30 -0
  99. package/dist/compiler/server/middleware.test.js.map +1 -1
  100. package/dist/compiler/strip-jsdoc.d.ts +9 -0
  101. package/dist/compiler/strip-jsdoc.js +19 -0
  102. package/dist/compiler/strip-jsdoc.js.map +1 -0
  103. package/dist/compiler/strip-jsdoc.test.d.ts +1 -0
  104. package/dist/compiler/strip-jsdoc.test.js +95 -0
  105. package/dist/compiler/strip-jsdoc.test.js.map +1 -0
  106. package/dist/compiler/variable-access.d.ts +2 -0
  107. package/dist/compiler/variable-access.js +6 -0
  108. package/dist/compiler/variable-access.js.map +1 -1
  109. package/dist/index.test.d.ts +1 -0
  110. package/dist/index.test.js +9 -0
  111. package/dist/index.test.js.map +1 -0
  112. package/dist/messages/load-messages.js +4 -5
  113. package/dist/messages/load-messages.js.map +1 -1
  114. package/dist/services/env-variables/index.js +1 -1
  115. package/dist/services/env-variables/index.js.map +1 -1
  116. package/dist/services/file-handling/write-output.bench.d.ts +1 -0
  117. package/dist/services/file-handling/write-output.bench.js +24 -0
  118. package/dist/services/file-handling/write-output.bench.js.map +1 -0
  119. package/dist/services/file-handling/write-output.d.ts +5 -0
  120. package/dist/services/file-handling/write-output.js +54 -9
  121. package/dist/services/file-handling/write-output.js.map +1 -1
  122. package/dist/services/file-handling/write-output.test.js +27 -0
  123. package/dist/services/file-handling/write-output.test.js.map +1 -1
  124. package/dist/services/logger/index.js +2 -0
  125. package/dist/services/logger/index.js.map +1 -1
  126. package/package.json +86 -86
@@ -1 +1 @@
1
- {"version":3,"file":"generate-static-localized-urls.js","sourceRoot":"","sources":["../../../src/compiler/runtime/generate-static-localized-urls.js"],"names":[],"mappings":"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","sourcesContent":["import { localizeUrl } from \"./localize-url.js\";\nimport {\n\tlocales,\n\tbaseLocale,\n\tTREE_SHAKE_DEFAULT_URL_PATTERN_USED,\n\tTREE_SHAKE_HOST_ROUTING_USED,\n\turlPatterns,\n} from \"./variables.js\";\nimport { getRoutingRule } from \"./host-routing.js\";\n\n/**\n * Generates localized URL variants for all provided URLs based on your configured locales and URL patterns.\n *\n * This function is essential for Static Site Generation (SSG) where you need to tell your framework\n * which pages to pre-render at build time. It's also useful for generating sitemaps and\n * `<link rel=\"alternate\" hreflang>` tags for SEO.\n *\n * The function respects your `urlPatterns` configuration - if you have translated pathnames\n * (e.g., `/about` → `/ueber-uns` for German), it will generate the correct localized paths.\n *\n * @see https://paraglidejs.com/static-site-generation\n *\n * @example\n * // Basic usage - generate all locale variants for a list of paths\n * const localizedUrls = generateStaticLocalizedUrls([\n * \"/\",\n * \"/about\",\n * \"/blog/post-1\",\n * ]);\n * // Returns URL objects for each locale:\n * // [\"/en/\", \"/de/\", \"/en/about\", \"/de/about\", \"/en/blog/post-1\", \"/de/blog/post-1\"]\n *\n * @example\n * // Use with framework SSG APIs\n * // SvelteKit\n * export function entries() {\n * const paths = [\"/\", \"/about\", \"/contact\"];\n * return generateStaticLocalizedUrls(paths).map(url => ({\n * locale: extractLocaleFromUrl(url)\n * }));\n * }\n *\n * @example\n * // Sitemap generation\n * const allPages = [\"/\", \"/about\", \"/blog\"];\n * const sitemapUrls = generateStaticLocalizedUrls(allPages);\n *\n * @param {(string | URL)[]} urls - List of canonical URLs or paths to generate localized versions for.\n * Can be absolute URLs (`https://example.com/about`) or paths (`/about`).\n * Paths are resolved against `http://localhost` internally.\n * @returns {URL[]} Array of URL objects representing all localized variants.\n * The order follows each input URL with all its locale variants before moving to the next URL.\n */\nexport function generateStaticLocalizedUrls(urls) {\n\t/** @type {Set<URL>} */\n\tconst localizedUrls = new Set();\n\n\t// For default URL pattern, we can optimize the generation\n\tif (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {\n\t\tfor (const urlInput of urls) {\n\t\t\tconst url =\n\t\t\t\turlInput instanceof URL\n\t\t\t\t\t? urlInput\n\t\t\t\t\t: new URL(urlInput, \"http://localhost\");\n\n\t\t\t// Base locale doesn't get a prefix\n\t\t\tlocalizedUrls.add(url);\n\n\t\t\t// Other locales get their code as prefix\n\t\t\tfor (const locale of locales) {\n\t\t\t\tif (locale !== baseLocale) {\n\t\t\t\t\tconst localizedPath = `/${locale}${url.pathname}${url.search}${url.hash}`;\n\t\t\t\t\tconst localizedUrl = new URL(localizedPath, url.origin);\n\t\t\t\t\tlocalizedUrls.add(localizedUrl);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Array.from(localizedUrls);\n\t}\n\n\t// Per-host string routing: localize each allowed locale for the URL's host\n\tif (TREE_SHAKE_HOST_ROUTING_USED) {\n\t\tfor (const urlInput of urls) {\n\t\t\tconst url =\n\t\t\t\turlInput instanceof URL\n\t\t\t\t\t? urlInput\n\t\t\t\t\t: new URL(urlInput, \"http://localhost\");\n\t\t\tconst rule = getRoutingRule(url.hostname);\n\t\t\tconst seen = new Set();\n\t\t\tfor (const locale of rule.locales) {\n\t\t\t\tconst localizedUrl = localizeUrl(url, { locale });\n\t\t\t\tif (!seen.has(localizedUrl.href)) {\n\t\t\t\t\tseen.add(localizedUrl.href);\n\t\t\t\t\tlocalizedUrls.add(localizedUrl);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Array.from(localizedUrls);\n\t}\n\n\t// For custom URL patterns, we need to use localizeUrl for each URL and locale\n\tfor (const urlInput of urls) {\n\t\tconst url =\n\t\t\turlInput instanceof URL\n\t\t\t\t? urlInput\n\t\t\t\t: new URL(urlInput, \"http://localhost\");\n\n\t\t// Try each URL pattern to find one that matches\n\t\tlet patternFound = false;\n\t\tfor (const pattern of urlPatterns) {\n\t\t\ttry {\n\t\t\t\t// Try to match the unlocalized pattern\n\t\t\t\tconst unlocalizedMatch = new URLPattern(pattern.pattern, url.href).exec(\n\t\t\t\t\turl.href\n\t\t\t\t);\n\n\t\t\t\tif (!unlocalizedMatch) continue;\n\n\t\t\t\tpatternFound = true;\n\n\t\t\t\t// Track unique localized URLs to avoid duplicates when patterns are the same\n\t\t\t\tconst seenUrls = new Set();\n\n\t\t\t\t// Generate localized URL for each locale\n\t\t\t\tfor (const [locale] of pattern.localized) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst localizedUrl = localizeUrl(url, { locale });\n\t\t\t\t\t\tconst urlString = localizedUrl.href;\n\n\t\t\t\t\t\t// Only add if we haven't seen this exact URL before\n\t\t\t\t\t\tif (!seenUrls.has(urlString)) {\n\t\t\t\t\t\t\tseenUrls.add(urlString);\n\t\t\t\t\t\t\tlocalizedUrls.add(localizedUrl);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip if localization fails for this locale\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} catch {\n\t\t\t\t// Skip if pattern matching fails\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// If no pattern matched, use the URL as is\n\t\tif (!patternFound) {\n\t\t\tlocalizedUrls.add(url);\n\t\t}\n\t}\n\n\treturn Array.from(localizedUrls);\n}\n"]}
1
+ {"version":3,"file":"generate-static-localized-urls.js","sourceRoot":"","sources":["../../../src/compiler/runtime/generate-static-localized-urls.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EACN,OAAO,EACP,UAAU,EACV,mCAAmC,EACnC,4BAA4B,EAC5B,aAAa,EACb,WAAW,GACX,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,UAAU,2BAA2B,CAAC,IAAI;IAC/C,uBAAuB;IACvB,MAAM,aAAa,GAAG,IAAI,GAAG,EAAE,CAAC;IAEhC,0DAA0D;IAC1D,IAAI,mCAAmC,EAAE,CAAC;QACzC,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,WAAW,GAChB,QAAQ,YAAY,GAAG;gBACtB,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YAC1C,MAAM,GAAG,GACR,aAAa,KAAK,SAAS;gBAC1B,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,sBAAsB,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;YAEjD,mCAAmC;YACnC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAEvB,yCAAyC;YACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC9B,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;oBAC3B,MAAM,aAAa,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;oBAC1E,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;oBACxD,aAAa,CAAC,GAAG,CAAC,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC;gBACzD,CAAC;YACF,CAAC;QACF,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAClC,CAAC;IAED,2EAA2E;IAC3E,IAAI,4BAA4B,EAAE,CAAC;QAClC,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,GAAG,GACR,QAAQ,YAAY,GAAG;gBACtB,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACnC,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;gBAClD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;oBAClC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;oBAC5B,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;gBACjC,CAAC;YACF,CAAC;QACF,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAClC,CAAC;IAED,8EAA8E;IAC9E,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,WAAW,GAChB,QAAQ,YAAY,GAAG;YACtB,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,sBAAsB,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;QAEzD,gDAAgD;QAChD,IAAI,YAAY,GAAG,KAAK,CAAC;QACzB,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;YACnC,IAAI,CAAC;gBACJ,uCAAuC;gBACvC,MAAM,gBAAgB,GAAG,cAAc,CACtC,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,EACnC,GAAG,CACH,CAAC;gBAEF,IAAI,CAAC,gBAAgB;oBAAE,SAAS;gBAEhC,YAAY,GAAG,IAAI,CAAC;gBAEpB,6EAA6E;gBAC7E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;gBAE3B,yCAAyC;gBACzC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;oBAC1C,IAAI,CAAC;wBACJ,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;wBAClD,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC;wBAEpC,oDAAoD;wBACpD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;4BAC9B,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;4BACxB,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;wBACjC,CAAC;oBACF,CAAC;oBAAC,MAAM,CAAC;wBACR,6CAA6C;wBAC7C,SAAS;oBACV,CAAC;gBACF,CAAC;gBACD,MAAM;YACP,CAAC;YAAC,MAAM,CAAC;gBACR,iCAAiC;gBACjC,SAAS;YACV,CAAC;QACF,CAAC;QAED,2CAA2C;QAC3C,IAAI,CAAC,YAAY,EAAE,CAAC;YACnB,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAClC,CAAC","sourcesContent":["import { localizeUrl } from \"./localize-url.js\";\nimport { normalizeTrailingSlash } from \"./normalize-trailing-slash.js\";\nimport { execUrlPattern } from \"./exec-url-pattern.js\";\nimport { getUrlPattern } from \"./url-pattern.js\";\nimport {\n\tlocales,\n\tbaseLocale,\n\tTREE_SHAKE_DEFAULT_URL_PATTERN_USED,\n\tTREE_SHAKE_HOST_ROUTING_USED,\n\ttrailingSlash,\n\turlPatterns,\n} from \"./variables.js\";\nimport { getRoutingRule } from \"./host-routing.js\";\n\n/**\n * Generates localized URL variants for all provided URLs based on your configured locales and URL patterns.\n *\n * This function is essential for Static Site Generation (SSG) where you need to tell your framework\n * which pages to pre-render at build time. It's also useful for generating sitemaps and\n * `<link rel=\"alternate\" hreflang>` tags for SEO.\n *\n * The function respects your `urlPatterns` configuration - if you have translated pathnames\n * (e.g., `/about` → `/ueber-uns` for German), it will generate the correct localized paths.\n *\n * @see https://paraglidejs.com/static-site-generation\n *\n * @example\n * // Basic usage - generate all locale variants for a list of paths\n * const localizedUrls = generateStaticLocalizedUrls([\n * \"/\",\n * \"/about\",\n * \"/blog/post-1\",\n * ]);\n * // Returns URL objects for each locale:\n * // [\"/en/\", \"/de/\", \"/en/about\", \"/de/about\", \"/en/blog/post-1\", \"/de/blog/post-1\"]\n *\n * @example\n * // Use with framework SSG APIs\n * // SvelteKit\n * export function entries() {\n * const paths = [\"/\", \"/about\", \"/contact\"];\n * return generateStaticLocalizedUrls(paths).map(url => ({\n * locale: extractLocaleFromUrl(url)\n * }));\n * }\n *\n * @example\n * // Sitemap generation\n * const allPages = [\"/\", \"/about\", \"/blog\"];\n * const sitemapUrls = generateStaticLocalizedUrls(allPages);\n *\n * @param {(string | URL)[]} urls - List of canonical URLs or paths to generate localized versions for.\n * Can be absolute URLs (`https://example.com/about`) or paths (`/about`).\n * Paths are resolved against `http://localhost` internally.\n * @returns {URL[]} Array of URL objects representing all localized variants.\n * The order follows each input URL with all its locale variants before moving to the next URL.\n */\nexport function generateStaticLocalizedUrls(urls) {\n\t/** @type {Set<URL>} */\n\tconst localizedUrls = new Set();\n\n\t// For default URL pattern, we can optimize the generation\n\tif (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {\n\t\tfor (const urlInput of urls) {\n\t\t\tconst originalUrl =\n\t\t\t\turlInput instanceof URL\n\t\t\t\t\t? urlInput\n\t\t\t\t\t: new URL(urlInput, \"http://localhost\");\n\t\t\tconst url =\n\t\t\t\ttrailingSlash === undefined\n\t\t\t\t\t? originalUrl\n\t\t\t\t\t: normalizeTrailingSlash(new URL(originalUrl));\n\n\t\t\t// Base locale doesn't get a prefix\n\t\t\tlocalizedUrls.add(url);\n\n\t\t\t// Other locales get their code as prefix\n\t\t\tfor (const locale of locales) {\n\t\t\t\tif (locale !== baseLocale) {\n\t\t\t\t\tconst localizedPath = `/${locale}${url.pathname}${url.search}${url.hash}`;\n\t\t\t\t\tconst localizedUrl = new URL(localizedPath, url.origin);\n\t\t\t\t\tlocalizedUrls.add(normalizeTrailingSlash(localizedUrl));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Array.from(localizedUrls);\n\t}\n\n\t// Per-host string routing: localize each allowed locale for the URL's host\n\tif (TREE_SHAKE_HOST_ROUTING_USED) {\n\t\tfor (const urlInput of urls) {\n\t\t\tconst url =\n\t\t\t\turlInput instanceof URL\n\t\t\t\t\t? urlInput\n\t\t\t\t\t: new URL(urlInput, \"http://localhost\");\n\t\t\tconst rule = getRoutingRule(url.hostname);\n\t\t\tconst seen = new Set();\n\t\t\tfor (const locale of rule.locales) {\n\t\t\t\tconst localizedUrl = localizeUrl(url, { locale });\n\t\t\t\tif (!seen.has(localizedUrl.href)) {\n\t\t\t\t\tseen.add(localizedUrl.href);\n\t\t\t\t\tlocalizedUrls.add(localizedUrl);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Array.from(localizedUrls);\n\t}\n\n\t// For custom URL patterns, we need to use localizeUrl for each URL and locale\n\tfor (const urlInput of urls) {\n\t\tconst originalUrl =\n\t\t\turlInput instanceof URL\n\t\t\t\t? urlInput\n\t\t\t\t: new URL(urlInput, \"http://localhost\");\n\t\tconst url = normalizeTrailingSlash(new URL(originalUrl));\n\n\t\t// Try each URL pattern to find one that matches\n\t\tlet patternFound = false;\n\t\tfor (const pattern of urlPatterns) {\n\t\t\ttry {\n\t\t\t\t// Try to match the unlocalized pattern\n\t\t\t\tconst unlocalizedMatch = execUrlPattern(\n\t\t\t\t\tgetUrlPattern(pattern.pattern, url),\n\t\t\t\t\turl\n\t\t\t\t);\n\n\t\t\t\tif (!unlocalizedMatch) continue;\n\n\t\t\t\tpatternFound = true;\n\n\t\t\t\t// Track unique localized URLs to avoid duplicates when patterns are the same\n\t\t\t\tconst seenUrls = new Set();\n\n\t\t\t\t// Generate localized URL for each locale\n\t\t\t\tfor (const [locale] of pattern.localized) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst localizedUrl = localizeUrl(url, { locale });\n\t\t\t\t\t\tconst urlString = localizedUrl.href;\n\n\t\t\t\t\t\t// Only add if we haven't seen this exact URL before\n\t\t\t\t\t\tif (!seenUrls.has(urlString)) {\n\t\t\t\t\t\t\tseenUrls.add(urlString);\n\t\t\t\t\t\t\tlocalizedUrls.add(localizedUrl);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip if localization fails for this locale\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t} catch {\n\t\t\t\t// Skip if pattern matching fails\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// If no pattern matched, use the URL as is\n\t\tif (!patternFound) {\n\t\t\tlocalizedUrls.add(originalUrl);\n\t\t}\n\t}\n\n\treturn Array.from(localizedUrls);\n}\n"]}
@@ -156,4 +156,42 @@ test("generates localized URLs from paths", async () => {
156
156
  // All URLs should be URL objects
157
157
  expect(urls.every((url) => url instanceof URL)).toBe(true);
158
158
  });
159
+ test("applies trailing slash canonicalization to default pattern output", async () => {
160
+ const runtime = await createParaglide({
161
+ baseLocale: "en",
162
+ locales: ["en", "de"],
163
+ strategy: ["url"],
164
+ trailingSlash: "never",
165
+ });
166
+ const urls = runtime.generateStaticLocalizedUrls(["/", "/about/"]);
167
+ expect(urls.map((url) => url.pathname).sort()).toEqual([
168
+ "/",
169
+ "/about",
170
+ "/de",
171
+ "/de/about",
172
+ ]);
173
+ });
174
+ test("canonicalizes custom pattern input but preserves unmatched URLs", async () => {
175
+ const runtime = await createParaglide({
176
+ baseLocale: "en",
177
+ locales: ["en", "de"],
178
+ strategy: ["url"],
179
+ trailingSlash: "never",
180
+ urlPatterns: [
181
+ {
182
+ pattern: "/about",
183
+ localized: [
184
+ ["en", "/about"],
185
+ ["de", "/de/ueber"],
186
+ ],
187
+ },
188
+ ],
189
+ });
190
+ const urls = runtime.generateStaticLocalizedUrls(["/about/", "/asset/"]);
191
+ expect(urls.map((url) => url.pathname).sort()).toEqual([
192
+ "/about",
193
+ "/asset/",
194
+ "/de/ueber",
195
+ ]);
196
+ });
159
197
  //# sourceMappingURL=generate-static-localized-urls.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"generate-static-localized-urls.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/generate-static-localized-urls.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\ntest(\"generates localized URLs using default URL pattern\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\tstrategy: [\"url\"],\n\t\t// undefined creates the default pattern\n\t\turlPatterns: undefined,\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\t\"http://example.com/about\",\n\t\t\"http://example.com/blog/post-1\",\n\t\t\"http://example.com/contact\",\n\t]);\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/about\",\n\t\t\"/blog/post-1\",\n\t\t\"/contact\",\n\t\t\"/de/about\",\n\t\t\"/de/blog/post-1\",\n\t\t\"/de/contact\",\n\t\t\"/fr/about\",\n\t\t\"/fr/blog/post-1\",\n\t\t\"/fr/contact\",\n\t]);\n\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"generates localized URLs using custom URL patterns\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"/store/item/:id\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"/store/item/:id\"],\n\t\t\t\t\t[\"de\", \"/laden/artikel/:id\"],\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tpattern: \"/blog/:slug\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"/blog/:slug\"],\n\t\t\t\t\t[\"de\", \"/blog/:slug\"], // Same pattern for both locales\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\t\"https://example.com/store/item/123\",\n\t\t\"https://example.com/store/item/456\",\n\t\t\"https://example.com/blog/my-post\",\n\t]);\n\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/blog/my-post\",\n\t\t\"/laden/artikel/123\",\n\t\t\"/laden/artikel/456\",\n\t\t\"/store/item/123\",\n\t\t\"/store/item/456\",\n\t]);\n\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"handles paths that don't match any pattern by including them\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/store/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/store/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.com/laden/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\t\"https://example.com/store/item/123\", // Should match pattern\n\t\t\"https://example.com/about\", // Should not match pattern\n\t\t\"https://example.com/contact\", // Should not match pattern\n\t]);\n\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/about\",\n\t\t\"/contact\",\n\t\t\"/laden/item/123\",\n\t\t\"/store/item/123\",\n\t]);\n\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"handles URL objects as input\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"/store/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"/store/:path*\"],\n\t\t\t\t\t[\"de\", \"/laden/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\tnew URL(\"https://example.com/store/item/123?color=blue#reviews\"),\n\t\tnew URL(\"https://example.com/store/cart\"),\n\t\tnew URL(\"https://example.com/about\"), // Should not match pattern\n\t]);\n\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/about\",\n\t\t\"/laden/cart\",\n\t\t\"/laden/item/123\",\n\t\t\"/store/cart\",\n\t\t\"/store/item/123\",\n\t]);\n\n\t// Verify search params and hash fragments are preserve\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"generates localized URLs from paths\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\tstrategy: [\"url\"],\n\t\t// undefined creates the default pattern\n\t\turlPatterns: undefined,\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\t\"/about\",\n\t\t\"/blog/post-1\",\n\t\t\"/contact\",\n\t]);\n\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/about\",\n\t\t\"/blog/post-1\",\n\t\t\"/contact\",\n\t\t\"/de/about\",\n\t\t\"/de/blog/post-1\",\n\t\t\"/de/contact\",\n\t\t\"/fr/about\",\n\t\t\"/fr/blog/post-1\",\n\t\t\"/fr/contact\",\n\t]);\n\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n"]}
1
+ {"version":3,"file":"generate-static-localized-urls.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/generate-static-localized-urls.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\ntest(\"generates localized URLs using default URL pattern\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\tstrategy: [\"url\"],\n\t\t// undefined creates the default pattern\n\t\turlPatterns: undefined,\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\t\"http://example.com/about\",\n\t\t\"http://example.com/blog/post-1\",\n\t\t\"http://example.com/contact\",\n\t]);\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/about\",\n\t\t\"/blog/post-1\",\n\t\t\"/contact\",\n\t\t\"/de/about\",\n\t\t\"/de/blog/post-1\",\n\t\t\"/de/contact\",\n\t\t\"/fr/about\",\n\t\t\"/fr/blog/post-1\",\n\t\t\"/fr/contact\",\n\t]);\n\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"generates localized URLs using custom URL patterns\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"/store/item/:id\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"/store/item/:id\"],\n\t\t\t\t\t[\"de\", \"/laden/artikel/:id\"],\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tpattern: \"/blog/:slug\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"/blog/:slug\"],\n\t\t\t\t\t[\"de\", \"/blog/:slug\"], // Same pattern for both locales\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\t\"https://example.com/store/item/123\",\n\t\t\"https://example.com/store/item/456\",\n\t\t\"https://example.com/blog/my-post\",\n\t]);\n\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/blog/my-post\",\n\t\t\"/laden/artikel/123\",\n\t\t\"/laden/artikel/456\",\n\t\t\"/store/item/123\",\n\t\t\"/store/item/456\",\n\t]);\n\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"handles paths that don't match any pattern by including them\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/store/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/store/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.com/laden/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\t\"https://example.com/store/item/123\", // Should match pattern\n\t\t\"https://example.com/about\", // Should not match pattern\n\t\t\"https://example.com/contact\", // Should not match pattern\n\t]);\n\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/about\",\n\t\t\"/contact\",\n\t\t\"/laden/item/123\",\n\t\t\"/store/item/123\",\n\t]);\n\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"handles URL objects as input\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"/store/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"/store/:path*\"],\n\t\t\t\t\t[\"de\", \"/laden/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\tnew URL(\"https://example.com/store/item/123?color=blue#reviews\"),\n\t\tnew URL(\"https://example.com/store/cart\"),\n\t\tnew URL(\"https://example.com/about\"), // Should not match pattern\n\t]);\n\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/about\",\n\t\t\"/laden/cart\",\n\t\t\"/laden/item/123\",\n\t\t\"/store/cart\",\n\t\t\"/store/item/123\",\n\t]);\n\n\t// Verify search params and hash fragments are preserve\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"generates localized URLs from paths\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\tstrategy: [\"url\"],\n\t\t// undefined creates the default pattern\n\t\turlPatterns: undefined,\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\n\t\t\"/about\",\n\t\t\"/blog/post-1\",\n\t\t\"/contact\",\n\t]);\n\n\tconst pathnames = urls.map((url) => url.pathname).sort();\n\n\texpect(pathnames).toEqual([\n\t\t\"/about\",\n\t\t\"/blog/post-1\",\n\t\t\"/contact\",\n\t\t\"/de/about\",\n\t\t\"/de/blog/post-1\",\n\t\t\"/de/contact\",\n\t\t\"/fr/about\",\n\t\t\"/fr/blog/post-1\",\n\t\t\"/fr/contact\",\n\t]);\n\n\t// All URLs should be URL objects\n\texpect(urls.every((url) => url instanceof URL)).toBe(true);\n});\n\ntest(\"applies trailing slash canonicalization to default pattern output\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\ttrailingSlash: \"never\",\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\"/\", \"/about/\"]);\n\texpect(urls.map((url) => url.pathname).sort()).toEqual([\n\t\t\"/\",\n\t\t\"/about\",\n\t\t\"/de\",\n\t\t\"/de/about\",\n\t]);\n});\n\ntest(\"canonicalizes custom pattern input but preserves unmatched URLs\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\ttrailingSlash: \"never\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"/about\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"/about\"],\n\t\t\t\t\t[\"de\", \"/de/ueber\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst urls = runtime.generateStaticLocalizedUrls([\"/about/\", \"/asset/\"]);\n\texpect(urls.map((url) => url.pathname).sort()).toEqual([\n\t\t\"/about\",\n\t\t\"/asset/\",\n\t\t\"/de/ueber\",\n\t]);\n});\n"]}
@@ -1,9 +1,9 @@
1
1
  export type HostRoutingRuleResolved = {
2
- baseLocale: string;
3
- locales: string[];
2
+ baseLocale: Locale;
3
+ locales: Locale[];
4
4
  };
5
5
  /**
6
- * @typedef {{ baseLocale: string, locales: string[] }} HostRoutingRuleResolved
6
+ * @typedef {{ baseLocale: Locale, locales: Locale[] }} HostRoutingRuleResolved
7
7
  */
8
8
  /**
9
9
  * @param {string} hostname
@@ -1,7 +1,7 @@
1
1
  import { toLocale } from "./check-locale.js";
2
2
  import { baseLocale, locales, routing, } from "./variables.js";
3
3
  /**
4
- * @typedef {{ baseLocale: string, locales: string[] }} HostRoutingRuleResolved
4
+ * @typedef {{ baseLocale: Locale, locales: Locale[] }} HostRoutingRuleResolved
5
5
  */
6
6
  /**
7
7
  * @param {string} hostname
@@ -11,7 +11,7 @@ export function getRoutingRule(hostname) {
11
11
  return (routing[hostname] ??
12
12
  routing["*"] ?? {
13
13
  baseLocale,
14
- locales: /** @type {string[]} */ ([...locales]),
14
+ locales: [...locales],
15
15
  });
16
16
  }
17
17
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"host-routing.js","sourceRoot":"","sources":["../../../src/compiler/runtime/host-routing.js"],"names":[],"mappings":"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","sourcesContent":["import { toLocale } from \"./check-locale.js\";\nimport {\n\tbaseLocale,\n\tlocales,\n\trouting,\n} from \"./variables.js\";\n\n/**\n * @typedef {{ baseLocale: string, locales: string[] }} HostRoutingRuleResolved\n */\n\n/**\n * @param {string} hostname\n * @returns {HostRoutingRuleResolved}\n */\nexport function getRoutingRule(hostname) {\n\treturn (\n\t\trouting[hostname] ??\n\t\trouting[\"*\"] ?? {\n\t\t\tbaseLocale,\n\t\t\tlocales: /** @type {string[]} */ ([...locales]),\n\t\t}\n\t);\n}\n\n/**\n * @param {string[]} segments\n * @param {HostRoutingRuleResolved} rule\n * @returns {Locale | undefined}\n */\nfunction localeFromFirstSegment(segments, rule) {\n\tif (segments.length === 0) {\n\t\treturn undefined;\n\t}\n\tconst canonical = toLocale(segments[0]);\n\tif (canonical && rule.locales.includes(canonical)) {\n\t\treturn canonical;\n\t}\n\treturn undefined;\n}\n\n/**\n * @param {string | URL} url\n * @returns {Locale}\n */\nexport function extractLocaleFromUrlHostRouting(url) {\n\tconst urlObj =\n\t\ttypeof url === \"string\" ? new URL(url, \"http://localhost\") : url;\n\tconst rule = getRoutingRule(urlObj.hostname);\n\tconst segments = urlObj.pathname.split(\"/\").filter(Boolean);\n\treturn localeFromFirstSegment(segments, rule) ?? rule.baseLocale;\n}\n\n/**\n * @param {string | URL} url\n * @param {Locale} locale\n * @returns {URL}\n */\nexport function localizeUrlHostRouting(url, locale) {\n\tconst urlObj =\n\t\ttypeof url === \"string\" ? new URL(url) : new URL(url.href);\n\tconst rule = getRoutingRule(urlObj.hostname);\n\tconst segments = urlObj.pathname.split(\"/\").filter(Boolean);\n\n\tif (localeFromFirstSegment(segments, rule)) {\n\t\tsegments.shift();\n\t}\n\n\tif (locale === rule.baseLocale) {\n\t\turlObj.pathname = \"/\" + segments.join(\"/\");\n\t} else if (segments.length === 0) {\n\t\turlObj.pathname = \"/\" + locale;\n\t} else {\n\t\turlObj.pathname = \"/\" + locale + \"/\" + segments.join(\"/\");\n\t}\n\n\treturn urlObj;\n}\n\n/**\n * @param {string | URL} url\n * @returns {URL}\n */\nexport function deLocalizeUrlHostRouting(url) {\n\tconst urlObj =\n\t\ttypeof url === \"string\" ? new URL(url) : new URL(url.href);\n\tconst rule = getRoutingRule(urlObj.hostname);\n\tconst segments = urlObj.pathname.split(\"/\").filter(Boolean);\n\n\tif (localeFromFirstSegment(segments, rule)) {\n\t\tsegments.shift();\n\t\turlObj.pathname = \"/\" + segments.join(\"/\");\n\t}\n\n\treturn urlObj;\n}\n"]}
1
+ {"version":3,"file":"host-routing.js","sourceRoot":"","sources":["../../../src/compiler/runtime/host-routing.js"],"names":[],"mappings":"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","sourcesContent":["import { toLocale } from \"./check-locale.js\";\nimport {\n\tbaseLocale,\n\tlocales,\n\trouting,\n} from \"./variables.js\";\n\n/**\n * @typedef {{ baseLocale: Locale, locales: Locale[] }} HostRoutingRuleResolved\n */\n\n/**\n * @param {string} hostname\n * @returns {HostRoutingRuleResolved}\n */\nexport function getRoutingRule(hostname) {\n\treturn (\n\t\trouting[hostname] ??\n\t\trouting[\"*\"] ?? {\n\t\t\tbaseLocale,\n\t\t\tlocales: [...locales],\n\t\t}\n\t);\n}\n\n/**\n * @param {string[]} segments\n * @param {HostRoutingRuleResolved} rule\n * @returns {Locale | undefined}\n */\nfunction localeFromFirstSegment(segments, rule) {\n\tif (segments.length === 0) {\n\t\treturn undefined;\n\t}\n\tconst canonical = toLocale(segments[0]);\n\tif (canonical && rule.locales.includes(canonical)) {\n\t\treturn canonical;\n\t}\n\treturn undefined;\n}\n\n/**\n * @param {string | URL} url\n * @returns {Locale}\n */\nexport function extractLocaleFromUrlHostRouting(url) {\n\tconst urlObj =\n\t\ttypeof url === \"string\" ? new URL(url, \"http://localhost\") : url;\n\tconst rule = getRoutingRule(urlObj.hostname);\n\tconst segments = urlObj.pathname.split(\"/\").filter(Boolean);\n\treturn localeFromFirstSegment(segments, rule) ?? rule.baseLocale;\n}\n\n/**\n * @param {string | URL} url\n * @param {Locale} locale\n * @returns {URL}\n */\nexport function localizeUrlHostRouting(url, locale) {\n\tconst urlObj =\n\t\ttypeof url === \"string\" ? new URL(url) : new URL(url.href);\n\tconst rule = getRoutingRule(urlObj.hostname);\n\tconst segments = urlObj.pathname.split(\"/\").filter(Boolean);\n\n\tif (localeFromFirstSegment(segments, rule)) {\n\t\tsegments.shift();\n\t}\n\n\tif (locale === rule.baseLocale) {\n\t\turlObj.pathname = \"/\" + segments.join(\"/\");\n\t} else if (segments.length === 0) {\n\t\turlObj.pathname = \"/\" + locale;\n\t} else {\n\t\turlObj.pathname = \"/\" + locale + \"/\" + segments.join(\"/\");\n\t}\n\n\treturn urlObj;\n}\n\n/**\n * @param {string | URL} url\n * @returns {URL}\n */\nexport function deLocalizeUrlHostRouting(url) {\n\tconst urlObj =\n\t\ttypeof url === \"string\" ? new URL(url) : new URL(url.href);\n\tconst rule = getRoutingRule(urlObj.hostname);\n\tconst segments = urlObj.pathname.split(\"/\").filter(Boolean);\n\n\tif (localeFromFirstSegment(segments, rule)) {\n\t\tsegments.shift();\n\t\turlObj.pathname = \"/\" + segments.join(\"/\");\n\t}\n\n\treturn urlObj;\n}\n"]}
@@ -1,5 +1,7 @@
1
1
  import { test, expect } from "vitest";
2
+ import { createProject as typescriptProject, ts, } from "@ts-morph/bootstrap";
2
3
  import { createParaglide } from "../create-paraglide.js";
4
+ import { defaultCompilerOptions } from "../compiler-options.js";
3
5
  import { createRuntimeFile, normalizeRouting } from "./create-runtime.js";
4
6
  test("normalizeRouting fills * fallback and resolves defaults", () => {
5
7
  const resolved = normalizeRouting({
@@ -125,4 +127,81 @@ test("localizeHref works with host routing under ALS origin", async () => {
125
127
  expect(runtime.localizeHref("/about", { locale: "en" })).toBe("/en/about");
126
128
  expect(runtime.localizeHref("/en/about", { locale: "fi" })).toBe("/about");
127
129
  });
130
+ /**
131
+ * Host routing types must stay narrowed to Locale in generated runtime.js.
132
+ * Runtime tests alone miss this — it is a checkJs type error, not a runtime failure.
133
+ */
134
+ test("generated host-routing runtime typechecks with narrow Locale types", async () => {
135
+ const project = await typescriptProject({
136
+ useInMemoryFileSystem: true,
137
+ compilerOptions: {
138
+ allowJs: true,
139
+ checkJs: true,
140
+ noEmit: true,
141
+ strict: true,
142
+ module: ts.ModuleKind.Node16,
143
+ moduleResolution: ts.ModuleResolutionKind.Node16,
144
+ target: ts.ScriptTarget.ES2022,
145
+ },
146
+ });
147
+ const runtime = createRuntimeFile({
148
+ baseLocale: "en",
149
+ locales: ["en", "sv", "fi"],
150
+ compilerOptions: {
151
+ ...defaultCompilerOptions,
152
+ strategy: ["url", "baseLocale"],
153
+ routing: {
154
+ "fin.example.com": { baseLocale: "fi" },
155
+ "app.example.com": {
156
+ baseLocale: "sv",
157
+ locales: ["sv", "en"],
158
+ },
159
+ },
160
+ },
161
+ }).replace('import * as pathToRegexp from "@sebbro/paraglide-lite/path-to-regexp";', "");
162
+ project.createSourceFile("./runtime.js", runtime);
163
+ project.createSourceFile("./consumer.ts", `
164
+ import {
165
+ generateStaticLocalizedUrls,
166
+ localizeUrl,
167
+ routing,
168
+ } from "./runtime.js";
169
+
170
+ // routing table values must retain Locale, not widen to string
171
+ const rule = routing["app.example.com"]!;
172
+ rule.baseLocale satisfies "en" | "sv" | "fi";
173
+ // Function that promises Locale accepts rule.baseLocale (not string)
174
+ const fallback: "en" | "sv" | "fi" = rule.baseLocale;
175
+
176
+ for (const locale of rule.locales) {
177
+ locale satisfies "en" | "sv" | "fi";
178
+ // localizeUrl requires Locale — must not reject rule.locales members
179
+ localizeUrl("https://app.example.com/shop", { locale });
180
+ }
181
+
182
+ // host-routing path inside generateStaticLocalizedUrls uses rule.locales as Locale[]
183
+ generateStaticLocalizedUrls(["https://fin.example.com/about"]);
184
+
185
+ void fallback;
186
+ `);
187
+ const program = project.createProgram();
188
+ const diagnostics = ts
189
+ .getPreEmitDiagnostics(program)
190
+ .filter((d) => {
191
+ const message = typeof d.messageText === "string"
192
+ ? d.messageText
193
+ : d.messageText.messageText;
194
+ // path-to-regexp import is stripped for the in-memory typecheck
195
+ return !message.includes("path-to-regexp");
196
+ });
197
+ for (const diagnostic of diagnostics) {
198
+ const message = typeof diagnostic.messageText === "string"
199
+ ? diagnostic.messageText
200
+ : diagnostic.messageText.messageText;
201
+ console.error(message, diagnostic.file?.fileName, diagnostic.start !== undefined && diagnostic.file
202
+ ? diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start)
203
+ : undefined);
204
+ }
205
+ expect(diagnostics).toEqual([]);
206
+ });
128
207
  //# sourceMappingURL=host-routing.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-routing.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/host-routing.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { createRuntimeFile, normalizeRouting } from \"./create-runtime.js\";\n\ntest(\"normalizeRouting fills * fallback and resolves defaults\", () => {\n\tconst resolved = normalizeRouting({\n\t\trouting: {\n\t\t\t\"fin.example.com\": { baseLocale: \"fi\" },\n\t\t\t\"app.example.com\": {\n\t\t\t\tbaseLocale: \"sv\",\n\t\t\t\tlocales: [\"sv\", \"en\", \"pt-PT\"],\n\t\t\t},\n\t\t},\n\t\tlocales: [\"en\", \"fi\", \"sv\", \"pt-PT\", \"de\"],\n\t\tbaseLocale: \"en\",\n\t});\n\n\texpect(resolved).toEqual({\n\t\t\"fin.example.com\": {\n\t\t\tbaseLocale: \"fi\",\n\t\t\tlocales: [\"en\", \"fi\", \"sv\", \"pt-PT\", \"de\"],\n\t\t},\n\t\t\"app.example.com\": {\n\t\t\tbaseLocale: \"sv\",\n\t\t\tlocales: [\"sv\", \"en\", \"pt-PT\"],\n\t\t},\n\t\t\"*\": {\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\", \"fi\", \"sv\", \"pt-PT\", \"de\"],\n\t\t},\n\t});\n});\n\ntest(\"normalizeRouting rejects unknown locales\", () => {\n\texpect(() =>\n\t\tnormalizeRouting({\n\t\t\trouting: {\n\t\t\t\t\"fin.example.com\": { baseLocale: \"nope\" },\n\t\t\t},\n\t\t\tlocales: [\"en\", \"fi\"],\n\t\t\tbaseLocale: \"en\",\n\t\t})\n\t).toThrow(/Invalid baseLocale \"nope\"/);\n});\n\ntest(\"createRuntimeFile rejects routing + urlPatterns together\", () => {\n\texpect(() =>\n\t\tcreateRuntimeFile({\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\", \"de\"],\n\t\t\tcompilerOptions: {\n\t\t\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\t\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\t\t\tcookieMaxAge: 100,\n\t\t\t\tcookieDomain: \"\",\n\t\t\t\tlocalStorageKey: \"PARAGLIDE_LOCALE\",\n\t\t\t\tisServer: \"typeof window === 'undefined'\",\n\t\t\t\texperimentalMiddlewareLocaleSplitting: false,\n\t\t\t\tdisableAsyncLocalStorage: false,\n\t\t\t\trouting: { \"*\": { baseLocale: \"en\" } },\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/:path(.*)?\",\n\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t[\"en\", \"/:path(.*)?\"],\n\t\t\t\t\t\t\t[\"de\", \"/de/:path(.*)?\"],\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t})\n\t).toThrow(/Cannot set both \"routing\" and \"urlPatterns\"/);\n});\n\ntest(\"per-host baseLocale: bare path is host default, /en is english\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fi\", \"sv\", \"de\"],\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\trouting: {\n\t\t\t\"fin.example.com\": { baseLocale: \"fi\" },\n\t\t\t\"app.example.com\": { baseLocale: \"sv\" },\n\t\t\t\"*\": { baseLocale: \"en\" },\n\t\t},\n\t});\n\n\texpect(runtime.extractLocaleFromUrl(\"https://fin.example.com/\")).toBe(\"fi\");\n\texpect(runtime.extractLocaleFromUrl(\"https://fin.example.com/about\")).toBe(\n\t\t\"fi\"\n\t);\n\texpect(runtime.extractLocaleFromUrl(\"https://fin.example.com/en\")).toBe(\"en\");\n\texpect(\n\t\truntime.extractLocaleFromUrl(\"https://fin.example.com/en/about\")\n\t).toBe(\"en\");\n\texpect(runtime.extractLocaleFromUrl(\"https://app.example.com/\")).toBe(\"sv\");\n\texpect(runtime.extractLocaleFromUrl(\"https://other.example.com/de/x\")).toBe(\n\t\t\"de\"\n\t);\n\n\texpect(\n\t\truntime.localizeUrl(\"https://fin.example.com/about\", { locale: \"en\" }).href\n\t).toBe(\"https://fin.example.com/en/about\");\n\texpect(\n\t\truntime.localizeUrl(\"https://fin.example.com/en/about\", { locale: \"fi\" })\n\t\t\t.href\n\t).toBe(\"https://fin.example.com/about\");\n\texpect(\n\t\truntime.localizeUrl(\"https://fin.example.com/about\", { locale: \"fi\" }).href\n\t).toBe(\"https://fin.example.com/about\");\n\n\texpect(runtime.deLocalizeUrl(\"https://fin.example.com/en/about\").href).toBe(\n\t\t\"https://fin.example.com/about\"\n\t);\n\texpect(runtime.deLocalizeUrl(\"https://fin.example.com/about\").href).toBe(\n\t\t\"https://fin.example.com/about\"\n\t);\n});\n\ntest(\"host locales restrict which prefixes are recognized\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"sv\", \"pt-PT\", \"de\"],\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\trouting: {\n\t\t\t\"app.example.com\": {\n\t\t\t\tbaseLocale: \"sv\",\n\t\t\t\tlocales: [\"sv\", \"en\", \"pt-PT\"],\n\t\t\t},\n\t\t},\n\t});\n\n\texpect(\n\t\truntime.extractLocaleFromUrl(\"https://app.example.com/pt-PT/shop\")\n\t).toBe(\"pt-PT\");\n\t// de is a project locale but not allowed on this host → treated as path\n\texpect(runtime.extractLocaleFromUrl(\"https://app.example.com/de/shop\")).toBe(\n\t\t\"sv\"\n\t);\n\texpect(\n\t\truntime.localizeUrl(\"https://app.example.com/shop\", { locale: \"pt-PT\" })\n\t\t\t.href\n\t).toBe(\"https://app.example.com/pt-PT/shop\");\n});\n\ntest(\"localizeHref works with host routing under ALS origin\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fi\"],\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\trouting: {\n\t\t\t\"fin.example.com\": { baseLocale: \"fi\" },\n\t\t},\n\t});\n\n\truntime.overwriteServerAsyncLocalStorage({\n\t\tgetStore: () => ({\n\t\t\tlocale: \"fi\",\n\t\t\torigin: \"https://fin.example.com\",\n\t\t}),\n\t\trun: () => undefined,\n\t});\n\n\texpect(runtime.localizeHref(\"/about\")).toBe(\"/about\");\n\texpect(runtime.localizeHref(\"/about\", { locale: \"en\" })).toBe(\"/en/about\");\n\texpect(runtime.localizeHref(\"/en/about\", { locale: \"fi\" })).toBe(\"/about\");\n});\n"]}
1
+ {"version":3,"file":"host-routing.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/host-routing.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport {\n\tcreateProject as typescriptProject,\n\tts,\n} from \"@ts-morph/bootstrap\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { defaultCompilerOptions } from \"../compiler-options.js\";\nimport { createRuntimeFile, normalizeRouting } from \"./create-runtime.js\";\n\ntest(\"normalizeRouting fills * fallback and resolves defaults\", () => {\n\tconst resolved = normalizeRouting({\n\t\trouting: {\n\t\t\t\"fin.example.com\": { baseLocale: \"fi\" },\n\t\t\t\"app.example.com\": {\n\t\t\t\tbaseLocale: \"sv\",\n\t\t\t\tlocales: [\"sv\", \"en\", \"pt-PT\"],\n\t\t\t},\n\t\t},\n\t\tlocales: [\"en\", \"fi\", \"sv\", \"pt-PT\", \"de\"],\n\t\tbaseLocale: \"en\",\n\t});\n\n\texpect(resolved).toEqual({\n\t\t\"fin.example.com\": {\n\t\t\tbaseLocale: \"fi\",\n\t\t\tlocales: [\"en\", \"fi\", \"sv\", \"pt-PT\", \"de\"],\n\t\t},\n\t\t\"app.example.com\": {\n\t\t\tbaseLocale: \"sv\",\n\t\t\tlocales: [\"sv\", \"en\", \"pt-PT\"],\n\t\t},\n\t\t\"*\": {\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\", \"fi\", \"sv\", \"pt-PT\", \"de\"],\n\t\t},\n\t});\n});\n\ntest(\"normalizeRouting rejects unknown locales\", () => {\n\texpect(() =>\n\t\tnormalizeRouting({\n\t\t\trouting: {\n\t\t\t\t\"fin.example.com\": { baseLocale: \"nope\" },\n\t\t\t},\n\t\t\tlocales: [\"en\", \"fi\"],\n\t\t\tbaseLocale: \"en\",\n\t\t})\n\t).toThrow(/Invalid baseLocale \"nope\"/);\n});\n\ntest(\"createRuntimeFile rejects routing + urlPatterns together\", () => {\n\texpect(() =>\n\t\tcreateRuntimeFile({\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\", \"de\"],\n\t\t\tcompilerOptions: {\n\t\t\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\t\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\t\t\tcookieMaxAge: 100,\n\t\t\t\tcookieDomain: \"\",\n\t\t\t\tlocalStorageKey: \"PARAGLIDE_LOCALE\",\n\t\t\t\tisServer: \"typeof window === 'undefined'\",\n\t\t\t\texperimentalMiddlewareLocaleSplitting: false,\n\t\t\t\tdisableAsyncLocalStorage: false,\n\t\t\t\trouting: { \"*\": { baseLocale: \"en\" } },\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"/:path(.*)?\",\n\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t[\"en\", \"/:path(.*)?\"],\n\t\t\t\t\t\t\t[\"de\", \"/de/:path(.*)?\"],\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t})\n\t).toThrow(/Cannot set both \"routing\" and \"urlPatterns\"/);\n});\n\ntest(\"per-host baseLocale: bare path is host default, /en is english\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fi\", \"sv\", \"de\"],\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\trouting: {\n\t\t\t\"fin.example.com\": { baseLocale: \"fi\" },\n\t\t\t\"app.example.com\": { baseLocale: \"sv\" },\n\t\t\t\"*\": { baseLocale: \"en\" },\n\t\t},\n\t});\n\n\texpect(runtime.extractLocaleFromUrl(\"https://fin.example.com/\")).toBe(\"fi\");\n\texpect(runtime.extractLocaleFromUrl(\"https://fin.example.com/about\")).toBe(\n\t\t\"fi\"\n\t);\n\texpect(runtime.extractLocaleFromUrl(\"https://fin.example.com/en\")).toBe(\"en\");\n\texpect(\n\t\truntime.extractLocaleFromUrl(\"https://fin.example.com/en/about\")\n\t).toBe(\"en\");\n\texpect(runtime.extractLocaleFromUrl(\"https://app.example.com/\")).toBe(\"sv\");\n\texpect(runtime.extractLocaleFromUrl(\"https://other.example.com/de/x\")).toBe(\n\t\t\"de\"\n\t);\n\n\texpect(\n\t\truntime.localizeUrl(\"https://fin.example.com/about\", { locale: \"en\" }).href\n\t).toBe(\"https://fin.example.com/en/about\");\n\texpect(\n\t\truntime.localizeUrl(\"https://fin.example.com/en/about\", { locale: \"fi\" })\n\t\t\t.href\n\t).toBe(\"https://fin.example.com/about\");\n\texpect(\n\t\truntime.localizeUrl(\"https://fin.example.com/about\", { locale: \"fi\" }).href\n\t).toBe(\"https://fin.example.com/about\");\n\n\texpect(runtime.deLocalizeUrl(\"https://fin.example.com/en/about\").href).toBe(\n\t\t\"https://fin.example.com/about\"\n\t);\n\texpect(runtime.deLocalizeUrl(\"https://fin.example.com/about\").href).toBe(\n\t\t\"https://fin.example.com/about\"\n\t);\n});\n\ntest(\"host locales restrict which prefixes are recognized\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"sv\", \"pt-PT\", \"de\"],\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\trouting: {\n\t\t\t\"app.example.com\": {\n\t\t\t\tbaseLocale: \"sv\",\n\t\t\t\tlocales: [\"sv\", \"en\", \"pt-PT\"],\n\t\t\t},\n\t\t},\n\t});\n\n\texpect(\n\t\truntime.extractLocaleFromUrl(\"https://app.example.com/pt-PT/shop\")\n\t).toBe(\"pt-PT\");\n\t// de is a project locale but not allowed on this host → treated as path\n\texpect(runtime.extractLocaleFromUrl(\"https://app.example.com/de/shop\")).toBe(\n\t\t\"sv\"\n\t);\n\texpect(\n\t\truntime.localizeUrl(\"https://app.example.com/shop\", { locale: \"pt-PT\" })\n\t\t\t.href\n\t).toBe(\"https://app.example.com/pt-PT/shop\");\n});\n\ntest(\"localizeHref works with host routing under ALS origin\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"fi\"],\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\trouting: {\n\t\t\t\"fin.example.com\": { baseLocale: \"fi\" },\n\t\t},\n\t});\n\n\truntime.overwriteServerAsyncLocalStorage({\n\t\tgetStore: () => ({\n\t\t\tlocale: \"fi\",\n\t\t\torigin: \"https://fin.example.com\",\n\t\t}),\n\t\trun: () => undefined,\n\t});\n\n\texpect(runtime.localizeHref(\"/about\")).toBe(\"/about\");\n\texpect(runtime.localizeHref(\"/about\", { locale: \"en\" })).toBe(\"/en/about\");\n\texpect(runtime.localizeHref(\"/en/about\", { locale: \"fi\" })).toBe(\"/about\");\n});\n\n/**\n * Host routing types must stay narrowed to Locale in generated runtime.js.\n * Runtime tests alone miss this — it is a checkJs type error, not a runtime failure.\n */\ntest(\"generated host-routing runtime typechecks with narrow Locale types\", async () => {\n\tconst project = await typescriptProject({\n\t\tuseInMemoryFileSystem: true,\n\t\tcompilerOptions: {\n\t\t\tallowJs: true,\n\t\t\tcheckJs: true,\n\t\t\tnoEmit: true,\n\t\t\tstrict: true,\n\t\t\tmodule: ts.ModuleKind.Node16,\n\t\t\tmoduleResolution: ts.ModuleResolutionKind.Node16,\n\t\t\ttarget: ts.ScriptTarget.ES2022,\n\t\t},\n\t});\n\n\tconst runtime = createRuntimeFile({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"sv\", \"fi\"],\n\t\tcompilerOptions: {\n\t\t\t...defaultCompilerOptions,\n\t\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\t\trouting: {\n\t\t\t\t\"fin.example.com\": { baseLocale: \"fi\" },\n\t\t\t\t\"app.example.com\": {\n\t\t\t\t\tbaseLocale: \"sv\",\n\t\t\t\t\tlocales: [\"sv\", \"en\"],\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}).replace(\n\t\t'import * as pathToRegexp from \"@sebbro/paraglide-lite/path-to-regexp\";',\n\t\t\"\"\n\t);\n\n\tproject.createSourceFile(\"./runtime.js\", runtime);\n\tproject.createSourceFile(\n\t\t\"./consumer.ts\",\n\t\t`\n\t\timport {\n\t\t\tgenerateStaticLocalizedUrls,\n\t\t\tlocalizeUrl,\n\t\t\trouting,\n\t\t} from \"./runtime.js\";\n\n\t\t// routing table values must retain Locale, not widen to string\n\t\tconst rule = routing[\"app.example.com\"]!;\n\t\trule.baseLocale satisfies \"en\" | \"sv\" | \"fi\";\n\t\t// Function that promises Locale accepts rule.baseLocale (not string)\n\t\tconst fallback: \"en\" | \"sv\" | \"fi\" = rule.baseLocale;\n\n\t\tfor (const locale of rule.locales) {\n\t\t\tlocale satisfies \"en\" | \"sv\" | \"fi\";\n\t\t\t// localizeUrl requires Locale — must not reject rule.locales members\n\t\t\tlocalizeUrl(\"https://app.example.com/shop\", { locale });\n\t\t}\n\n\t\t// host-routing path inside generateStaticLocalizedUrls uses rule.locales as Locale[]\n\t\tgenerateStaticLocalizedUrls([\"https://fin.example.com/about\"]);\n\n\t\tvoid fallback;\n\t\t`\n\t);\n\n\tconst program = project.createProgram();\n\tconst diagnostics = ts\n\t\t.getPreEmitDiagnostics(program)\n\t\t.filter((d) => {\n\t\t\tconst message =\n\t\t\t\ttypeof d.messageText === \"string\"\n\t\t\t\t\t? d.messageText\n\t\t\t\t\t: d.messageText.messageText;\n\t\t\t// path-to-regexp import is stripped for the in-memory typecheck\n\t\t\treturn !message.includes(\"path-to-regexp\");\n\t\t});\n\n\tfor (const diagnostic of diagnostics) {\n\t\tconst message =\n\t\t\ttypeof diagnostic.messageText === \"string\"\n\t\t\t\t? diagnostic.messageText\n\t\t\t\t: diagnostic.messageText.messageText;\n\t\tconsole.error(\n\t\t\tmessage,\n\t\t\tdiagnostic.file?.fileName,\n\t\t\tdiagnostic.start !== undefined && diagnostic.file\n\t\t\t\t? diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start)\n\t\t\t\t: undefined\n\t\t);\n\t}\n\texpect(diagnostics).toEqual([]);\n});\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ import { bench } from "vitest";
2
+ import { createParaglide } from "../create-paraglide.js";
3
+ const runtime = await createParaglide({
4
+ baseLocale: "en",
5
+ locales: ["en", "de", "fr", "es"],
6
+ isServer: "false",
7
+ strategy: ["url"],
8
+ urlPatterns: [
9
+ {
10
+ pattern: "https://example.com/:path*",
11
+ localized: [
12
+ ["en", "https://example.com/:path*"],
13
+ ["de", "https://de.example.com/:path*"],
14
+ ["fr", "https://fr.example.com/:path*"],
15
+ ["es", "https://es.example.com/:path*"],
16
+ ],
17
+ },
18
+ {
19
+ pattern: "https://example.com/blog/:path*",
20
+ localized: [
21
+ ["en", "https://example.com/blog/:path*"],
22
+ ["de", "https://de.example.com/blog/:path*"],
23
+ ["fr", "https://fr.example.com/blog/:path*"],
24
+ ["es", "https://es.example.com/blog/:path*"],
25
+ ],
26
+ },
27
+ ],
28
+ });
29
+ const genericRuntime = await createParaglide({
30
+ baseLocale: "en",
31
+ locales: ["en", "de", "fr", "es"],
32
+ isServer: "false",
33
+ strategy: ["url"],
34
+ urlPatterns: [
35
+ {
36
+ // A custom group name intentionally keeps this route on the generic
37
+ // URLPattern fallback while matching the same URLs.
38
+ pattern: "/:slug(.*)?",
39
+ localized: [
40
+ ["en", "/:slug(.*)?"],
41
+ ["de", "/de/:slug(.*)?"],
42
+ ["fr", "/fr/:slug(.*)?"],
43
+ ["es", "/es/:slug(.*)?"],
44
+ ],
45
+ },
46
+ ],
47
+ });
48
+ runtime.overwriteGetLocale(() => "en");
49
+ runtime.overwriteGetUrlOrigin(() => "https://example.com");
50
+ genericRuntime.overwriteGetLocale(() => "en");
51
+ genericRuntime.overwriteGetUrlOrigin(() => "https://example.com");
52
+ for (let i = 0; i < 1000; i++) {
53
+ runtime.localizeHref(`/page-${i % 100}.html`, { locale: "de" });
54
+ }
55
+ bench("localizeHref with repeated routes", () => {
56
+ for (let i = 0; i < 1000; i++) {
57
+ runtime.localizeHref(`/page-${i % 100}.html`, { locale: "de" });
58
+ }
59
+ }, { time: 1000 });
60
+ for (let i = 0; i < 1000; i++) {
61
+ genericRuntime.localizeHref(`/page-${i % 100}.html`, { locale: "de" });
62
+ }
63
+ bench("localizeHref with generic URLPattern fallback", () => {
64
+ for (let i = 0; i < 1000; i++) {
65
+ genericRuntime.localizeHref(`/page-${i % 100}.html`, { locale: "de" });
66
+ }
67
+ }, { time: 1000 });
68
+ //# sourceMappingURL=localize-href.bench.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localize-href.bench.js","sourceRoot":"","sources":["../../../src/compiler/runtime/localize-href.bench.ts"],"names":[],"mappings":"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","sourcesContent":["import { bench } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\nconst runtime = await createParaglide({\n\tbaseLocale: \"en\",\n\tlocales: [\"en\", \"de\", \"fr\", \"es\"],\n\tisServer: \"false\",\n\tstrategy: [\"url\"],\n\turlPatterns: [\n\t\t{\n\t\t\tpattern: \"https://example.com/:path*\",\n\t\t\tlocalized: [\n\t\t\t\t[\"en\", \"https://example.com/:path*\"],\n\t\t\t\t[\"de\", \"https://de.example.com/:path*\"],\n\t\t\t\t[\"fr\", \"https://fr.example.com/:path*\"],\n\t\t\t\t[\"es\", \"https://es.example.com/:path*\"],\n\t\t\t],\n\t\t},\n\t\t{\n\t\t\tpattern: \"https://example.com/blog/:path*\",\n\t\t\tlocalized: [\n\t\t\t\t[\"en\", \"https://example.com/blog/:path*\"],\n\t\t\t\t[\"de\", \"https://de.example.com/blog/:path*\"],\n\t\t\t\t[\"fr\", \"https://fr.example.com/blog/:path*\"],\n\t\t\t\t[\"es\", \"https://es.example.com/blog/:path*\"],\n\t\t\t],\n\t\t},\n\t],\n});\n\nconst genericRuntime = await createParaglide({\n\tbaseLocale: \"en\",\n\tlocales: [\"en\", \"de\", \"fr\", \"es\"],\n\tisServer: \"false\",\n\tstrategy: [\"url\"],\n\turlPatterns: [\n\t\t{\n\t\t\t// A custom group name intentionally keeps this route on the generic\n\t\t\t// URLPattern fallback while matching the same URLs.\n\t\t\tpattern: \"/:slug(.*)?\",\n\t\t\tlocalized: [\n\t\t\t\t[\"en\", \"/:slug(.*)?\"],\n\t\t\t\t[\"de\", \"/de/:slug(.*)?\"],\n\t\t\t\t[\"fr\", \"/fr/:slug(.*)?\"],\n\t\t\t\t[\"es\", \"/es/:slug(.*)?\"],\n\t\t\t],\n\t\t},\n\t],\n});\n\nruntime.overwriteGetLocale(() => \"en\");\nruntime.overwriteGetUrlOrigin(() => \"https://example.com\");\ngenericRuntime.overwriteGetLocale(() => \"en\");\ngenericRuntime.overwriteGetUrlOrigin(() => \"https://example.com\");\n\nfor (let i = 0; i < 1000; i++) {\n\truntime.localizeHref(`/page-${i % 100}.html`, { locale: \"de\" });\n}\n\nbench(\n\t\"localizeHref with repeated routes\",\n\t() => {\n\t\tfor (let i = 0; i < 1000; i++) {\n\t\t\truntime.localizeHref(`/page-${i % 100}.html`, { locale: \"de\" });\n\t\t}\n\t},\n\t{ time: 1000 }\n);\n\nfor (let i = 0; i < 1000; i++) {\n\tgenericRuntime.localizeHref(`/page-${i % 100}.html`, { locale: \"de\" });\n}\n\nbench(\n\t\"localizeHref with generic URLPattern fallback\",\n\t() => {\n\t\tfor (let i = 0; i < 1000; i++) {\n\t\t\tgenericRuntime.localizeHref(`/page-${i % 100}.html`, { locale: \"de\" });\n\t\t}\n\t},\n\t{ time: 1000 }\n);\n"]}
@@ -127,6 +127,19 @@ test("default url patterns to improve out of the box experience", async () => {
127
127
  expect(runtime.localizeHref("http://localhost:5173/about", { locale: "de" })).toBe("http://localhost:5173/de/about");
128
128
  expect(runtime.deLocalizeHref("http://localhost:5173/de/about")).toBe("http://localhost:5173/about");
129
129
  });
130
+ test("normalizes localized root hrefs and anchors", async () => {
131
+ const runtime = await createParaglide({
132
+ baseLocale: "en",
133
+ locales: ["en", "de"],
134
+ isServer: "false",
135
+ strategy: ["url"],
136
+ trailingSlash: "never",
137
+ });
138
+ globalThis.window = { location: new URL("http://example.com") };
139
+ expect(runtime.localizeHref("/", { locale: "de" })).toBe("/de");
140
+ expect(runtime.localizeHref("/#section", { locale: "de" })).toBe("/de#section");
141
+ expect(runtime.deLocalizeHref("/de/")).toBe("/");
142
+ });
130
143
  test("normalizes mixed-case explicit locales in localizeHref", async () => {
131
144
  const runtime = await createParaglide({
132
145
  baseLocale: "en",
@@ -136,4 +149,26 @@ test("normalizes mixed-case explicit locales in localizeHref", async () => {
136
149
  expect(runtime.localizeHref("/about", { locale: "DE" })).toBe("/de/about");
137
150
  expect(runtime.localizeHref("/de/about", { locale: "EN" })).toBe("/about");
138
151
  });
152
+ test("keeps custom URLPattern path expressions on the generic fallback", async () => {
153
+ const runtime = await createParaglide({
154
+ baseLocale: "en",
155
+ locales: ["en", "de"],
156
+ strategy: ["url"],
157
+ urlPatterns: [
158
+ {
159
+ pattern: "/:slug(.)?",
160
+ localized: [
161
+ ["en", "/:slug(.)?"],
162
+ ["de", "/de/:slug(.)?"],
163
+ ],
164
+ },
165
+ ],
166
+ });
167
+ runtime.overwriteGetLocale(() => "en");
168
+ runtime.overwriteGetUrlOrigin(() => "https://example.com");
169
+ // `(.)` matches exactly one character in URLPattern. A prefix-only
170
+ // implementation must not broaden this route into a catch-all.
171
+ expect(runtime.localizeHref("/a", { locale: "de" })).toBe("/de/a");
172
+ expect(runtime.localizeHref("/about", { locale: "de" })).toBe("/about");
173
+ });
139
174
  //# sourceMappingURL=localize-href.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"localize-href.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/localize-href.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\ntest(\"uses the locale from getLocale() if no locale is provided\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"globalVariable\", \"baseLocale\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"http://:domain(.*)/:locale(de|en)?/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", \"http://:domain(.*)/de/:path(.*)?\"],\n\t\t\t\t\t[\"en\", \"http://:domain(.*)/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\truntime.overwriteGetLocale(() => \"de\");\n\n\texpect(runtime.localizeHref(\"/hello\")).toBe(\"/de/hello\");\n\texpect(runtime.deLocalizeHref(\"/de/hello\")).toBe(\"/hello\");\n});\n\ntest(\"returns an absolute href if the provided href is absolute\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"globalVariable\", \"baseLocale\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"http://:domain(.*)/:locale(de|en)?/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", \"http://:domain(.*)/de/:path(.*)?\"],\n\t\t\t\t\t[\"en\", \"http://:domain(.*)/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\texpect(\n\t\truntime.localizeHref(\"http://example.com/hello\", { locale: \"de\" })\n\t).toBe(\"http://example.com/de/hello\");\n\texpect(runtime.deLocalizeHref(\"http://example.com/de/hello\")).toBe(\n\t\t\"http://example.com/hello\"\n\t);\n});\n\n// useful if domain based localization is used for example\ntest(\"returns an absolute href if the provided href is relative but the origin of the localized href differs\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"globalVariable\", \"baseLocale\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"http://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", \"http://de.example.com/:path(.*)?\"],\n\t\t\t\t\t[\"en\", \"http://example.com/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\t// simulating routing from current en page to de page\n\truntime.overwriteGetLocale(() => \"en\");\n\truntime.overwriteGetUrlOrigin(() => \"http://example.com\");\n\n\texpect(runtime.localizeHref(\"/hello\", { locale: \"de\" })).toBe(\n\t\t\"http://de.example.com/hello\"\n\t);\n\texpect(runtime.deLocalizeHref(\"http://de.example.com/hello\")).toBe(\n\t\t\"http://example.com/hello\"\n\t);\n\n\t// simulating routing from current de page to en page\n\truntime.overwriteGetLocale(() => \"de\");\n\n\texpect(runtime.localizeHref(\"/hello\", { locale: \"en\" })).toBe(\n\t\t\"http://example.com/hello\"\n\t);\n\texpect(runtime.deLocalizeHref(\"http://example.com/hello\")).toBe(\n\t\t\"http://example.com/hello\"\n\t);\n});\n\n// https://github.com/opral/inlang-paraglide-js/issues/362\ntest(\"adding a base path\", async () => {\n\tconst base = \"shop\";\n\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: `/{${base}/}?:path(.*)?`,\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", `/{${base}/}?de/:path(.*)?`],\n\t\t\t\t\t[\"en\", `/{${base}/}?:path(.*)?`],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\t// simulating the current locale to be en\n\truntime.overwriteGetLocale(() => \"en\");\n\n\texpect(runtime.localizeHref(\"/about\")).toBe(\"/shop/about\");\n\texpect(runtime.deLocalizeHref(\"/about\")).toBe(\"/shop/about\");\n\n\t// simulating current locale to be de\n\truntime.overwriteGetLocale(() => \"de\");\n\texpect(runtime.localizeHref(\"/about\")).toBe(\"/shop/de/about\");\n\texpect(runtime.deLocalizeHref(\"/de/about\")).toBe(\"/shop/about\");\n});\n\ntest(\"default url patterns to improve out of the box experience\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\tisServer: \"false\",\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: undefined,\n\t});\n\n\t// polyfilling window\n\tglobalThis.window = { location: new URL(\"http://example.com\") } as any;\n\n\texpect(runtime.urlPatterns).toStrictEqual([\n\t\t{\n\t\t\tpattern: \":protocol://:domain(.*)::port?/:path(.*)?\",\n\t\t\tlocalized: [\n\t\t\t\t[\"de\", \":protocol://:domain(.*)::port?/de/:path(.*)?\"],\n\t\t\t\t[\"fr\", \":protocol://:domain(.*)::port?/fr/:path(.*)?\"],\n\t\t\t\t[\"en\", \":protocol://:domain(.*)::port?/:path(.*)?\"],\n\t\t\t],\n\t\t},\n\t]);\n\n\t// expecting the baseLocale to not be included in the url\n\texpect(runtime.localizeHref(\"/about\", { locale: \"en\" })).toBe(\"/about\");\n\texpect(runtime.localizeHref(\"/about\", { locale: \"de\" })).toBe(\"/de/about\");\n\n\t// expecting root path to work\n\texpect(runtime.localizeHref(\"/\", { locale: \"en\" })).toBe(\"/\");\n\texpect(runtime.localizeHref(\"/\", { locale: \"de\" })).toBe(\"/de/\");\n\n\t// expecting de-localization to work\n\texpect(runtime.deLocalizeHref(\"/de/\")).toBe(\"/\");\n\texpect(runtime.deLocalizeHref(\"/de/about\")).toBe(\"/about\");\n\texpect(runtime.deLocalizeHref(\"/about\")).toBe(\"/about\");\n\n\t// expecting localhost to work (for development)\n\texpect(runtime.localizeHref(\"http://localhost:5173/\", { locale: \"de\" })).toBe(\n\t\t\"http://localhost:5173/de/\"\n\t);\n\n\t// de-localizing localhost\n\texpect(runtime.deLocalizeHref(\"http://localhost:5173/de\")).toBe(\n\t\t\"http://localhost:5173/\"\n\t);\n\n\t// pathnames\n\texpect(\n\t\truntime.localizeHref(\"http://localhost:5173/about\", { locale: \"de\" })\n\t).toBe(\"http://localhost:5173/de/about\");\n\n\texpect(runtime.deLocalizeHref(\"http://localhost:5173/de/about\")).toBe(\n\t\t\"http://localhost:5173/about\"\n\t);\n});\n\ntest(\"normalizes mixed-case explicit locales in localizeHref\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t});\n\n\texpect(runtime.localizeHref(\"/about\", { locale: \"DE\" })).toBe(\"/de/about\");\n\n\texpect(runtime.localizeHref(\"/de/about\", { locale: \"EN\" })).toBe(\"/about\");\n});\n"]}
1
+ {"version":3,"file":"localize-href.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/localize-href.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\ntest(\"uses the locale from getLocale() if no locale is provided\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"globalVariable\", \"baseLocale\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"http://:domain(.*)/:locale(de|en)?/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", \"http://:domain(.*)/de/:path(.*)?\"],\n\t\t\t\t\t[\"en\", \"http://:domain(.*)/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\truntime.overwriteGetLocale(() => \"de\");\n\n\texpect(runtime.localizeHref(\"/hello\")).toBe(\"/de/hello\");\n\texpect(runtime.deLocalizeHref(\"/de/hello\")).toBe(\"/hello\");\n});\n\ntest(\"returns an absolute href if the provided href is absolute\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"globalVariable\", \"baseLocale\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"http://:domain(.*)/:locale(de|en)?/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", \"http://:domain(.*)/de/:path(.*)?\"],\n\t\t\t\t\t[\"en\", \"http://:domain(.*)/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\texpect(\n\t\truntime.localizeHref(\"http://example.com/hello\", { locale: \"de\" })\n\t).toBe(\"http://example.com/de/hello\");\n\texpect(runtime.deLocalizeHref(\"http://example.com/de/hello\")).toBe(\n\t\t\"http://example.com/hello\"\n\t);\n});\n\n// useful if domain based localization is used for example\ntest(\"returns an absolute href if the provided href is relative but the origin of the localized href differs\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\", \"globalVariable\", \"baseLocale\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"http://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", \"http://de.example.com/:path(.*)?\"],\n\t\t\t\t\t[\"en\", \"http://example.com/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\t// simulating routing from current en page to de page\n\truntime.overwriteGetLocale(() => \"en\");\n\truntime.overwriteGetUrlOrigin(() => \"http://example.com\");\n\n\texpect(runtime.localizeHref(\"/hello\", { locale: \"de\" })).toBe(\n\t\t\"http://de.example.com/hello\"\n\t);\n\texpect(runtime.deLocalizeHref(\"http://de.example.com/hello\")).toBe(\n\t\t\"http://example.com/hello\"\n\t);\n\n\t// simulating routing from current de page to en page\n\truntime.overwriteGetLocale(() => \"de\");\n\n\texpect(runtime.localizeHref(\"/hello\", { locale: \"en\" })).toBe(\n\t\t\"http://example.com/hello\"\n\t);\n\texpect(runtime.deLocalizeHref(\"http://example.com/hello\")).toBe(\n\t\t\"http://example.com/hello\"\n\t);\n});\n\n// https://github.com/opral/inlang-paraglide-js/issues/362\ntest(\"adding a base path\", async () => {\n\tconst base = \"shop\";\n\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: `/{${base}/}?:path(.*)?`,\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", `/{${base}/}?de/:path(.*)?`],\n\t\t\t\t\t[\"en\", `/{${base}/}?:path(.*)?`],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\t// simulating the current locale to be en\n\truntime.overwriteGetLocale(() => \"en\");\n\n\texpect(runtime.localizeHref(\"/about\")).toBe(\"/shop/about\");\n\texpect(runtime.deLocalizeHref(\"/about\")).toBe(\"/shop/about\");\n\n\t// simulating current locale to be de\n\truntime.overwriteGetLocale(() => \"de\");\n\texpect(runtime.localizeHref(\"/about\")).toBe(\"/shop/de/about\");\n\texpect(runtime.deLocalizeHref(\"/de/about\")).toBe(\"/shop/about\");\n});\n\ntest(\"default url patterns to improve out of the box experience\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\tisServer: \"false\",\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: undefined,\n\t});\n\n\t// polyfilling window\n\tglobalThis.window = { location: new URL(\"http://example.com\") } as any;\n\n\texpect(runtime.urlPatterns).toStrictEqual([\n\t\t{\n\t\t\tpattern: \":protocol://:domain(.*)::port?/:path(.*)?\",\n\t\t\tlocalized: [\n\t\t\t\t[\"de\", \":protocol://:domain(.*)::port?/de/:path(.*)?\"],\n\t\t\t\t[\"fr\", \":protocol://:domain(.*)::port?/fr/:path(.*)?\"],\n\t\t\t\t[\"en\", \":protocol://:domain(.*)::port?/:path(.*)?\"],\n\t\t\t],\n\t\t},\n\t]);\n\n\t// expecting the baseLocale to not be included in the url\n\texpect(runtime.localizeHref(\"/about\", { locale: \"en\" })).toBe(\"/about\");\n\texpect(runtime.localizeHref(\"/about\", { locale: \"de\" })).toBe(\"/de/about\");\n\n\t// expecting root path to work\n\texpect(runtime.localizeHref(\"/\", { locale: \"en\" })).toBe(\"/\");\n\texpect(runtime.localizeHref(\"/\", { locale: \"de\" })).toBe(\"/de/\");\n\n\t// expecting de-localization to work\n\texpect(runtime.deLocalizeHref(\"/de/\")).toBe(\"/\");\n\texpect(runtime.deLocalizeHref(\"/de/about\")).toBe(\"/about\");\n\texpect(runtime.deLocalizeHref(\"/about\")).toBe(\"/about\");\n\n\t// expecting localhost to work (for development)\n\texpect(runtime.localizeHref(\"http://localhost:5173/\", { locale: \"de\" })).toBe(\n\t\t\"http://localhost:5173/de/\"\n\t);\n\n\t// de-localizing localhost\n\texpect(runtime.deLocalizeHref(\"http://localhost:5173/de\")).toBe(\n\t\t\"http://localhost:5173/\"\n\t);\n\n\t// pathnames\n\texpect(\n\t\truntime.localizeHref(\"http://localhost:5173/about\", { locale: \"de\" })\n\t).toBe(\"http://localhost:5173/de/about\");\n\n\texpect(runtime.deLocalizeHref(\"http://localhost:5173/de/about\")).toBe(\n\t\t\"http://localhost:5173/about\"\n\t);\n});\n\ntest(\"normalizes localized root hrefs and anchors\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tisServer: \"false\",\n\t\tstrategy: [\"url\"],\n\t\ttrailingSlash: \"never\",\n\t});\n\n\tglobalThis.window = { location: new URL(\"http://example.com\") } as any;\n\n\texpect(runtime.localizeHref(\"/\", { locale: \"de\" })).toBe(\"/de\");\n\texpect(runtime.localizeHref(\"/#section\", { locale: \"de\" })).toBe(\n\t\t\"/de#section\"\n\t);\n\texpect(runtime.deLocalizeHref(\"/de/\")).toBe(\"/\");\n});\n\ntest(\"normalizes mixed-case explicit locales in localizeHref\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t});\n\n\texpect(runtime.localizeHref(\"/about\", { locale: \"DE\" })).toBe(\"/de/about\");\n\n\texpect(runtime.localizeHref(\"/de/about\", { locale: \"EN\" })).toBe(\"/about\");\n});\n\ntest(\"keeps custom URLPattern path expressions on the generic fallback\", async () => {\n\tconst runtime = await createParaglide({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tstrategy: [\"url\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"/:slug(.)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"/:slug(.)?\"],\n\t\t\t\t\t[\"de\", \"/de/:slug(.)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\truntime.overwriteGetLocale(() => \"en\");\n\truntime.overwriteGetUrlOrigin(() => \"https://example.com\");\n\n\t// `(.)` matches exactly one character in URLPattern. A prefix-only\n\t// implementation must not broaden this route into a catch-all.\n\texpect(runtime.localizeHref(\"/a\", { locale: \"de\" })).toBe(\"/de/a\");\n\texpect(runtime.localizeHref(\"/about\", { locale: \"de\" })).toBe(\"/about\");\n});\n"]}
@@ -93,3 +93,17 @@ export declare function deLocalizeUrl(url: string | URL): URL;
93
93
  * @returns {Record<string, string | null | undefined>} An object containing all named groups from the match.
94
94
  */
95
95
  export declare function aggregateGroups(match: any): Record<string, string | null | undefined>;
96
+ export type FastPathPattern = {
97
+ protocol: string | undefined;
98
+ hostname: string | undefined;
99
+ port: string | undefined;
100
+ pathnamePrefix: string;
101
+ pathMode: "segments" | "catch-all-optional" | "catch-all-required";
102
+ };
103
+ export type FastPathRoute = {
104
+ base: FastPathPattern;
105
+ localized: Array<{
106
+ locale: string;
107
+ pattern: FastPathPattern;
108
+ }>;
109
+ };