@inlang/paraglide-js 2.25.3 → 2.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compiler/runtime/create-runtime.js +24 -1
- package/dist/compiler/runtime/create-runtime.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-url.js +11 -4
- package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
- package/dist/compiler/runtime/generate-static-localized-urls.js +5 -3
- package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
- package/dist/compiler/runtime/localize-href.test.js +37 -0
- package/dist/compiler/runtime/localize-href.test.js.map +1 -1
- package/dist/compiler/runtime/localize-url.js +29 -56
- package/dist/compiler/runtime/localize-url.js.map +1 -1
- package/dist/compiler/runtime/localize-url.test.js +15 -0
- package/dist/compiler/runtime/localize-url.test.js.map +1 -1
- package/dist/compiler/runtime/route-strategy.js +9 -3
- package/dist/compiler/runtime/route-strategy.js.map +1 -1
- package/dist/compiler/runtime/type.test.js +9 -0
- package/dist/compiler/runtime/type.test.js.map +1 -1
- package/dist/compiler/runtime/url-pattern-cache.d.ts +11 -0
- package/dist/compiler/runtime/url-pattern-cache.js +42 -0
- package/dist/compiler/runtime/url-pattern-cache.js.map +1 -0
- package/dist/compiler/runtime/url-pattern-index.d.ts +24 -0
- package/dist/compiler/runtime/url-pattern-index.differential.test.d.ts +1 -0
- package/dist/compiler/runtime/url-pattern-index.differential.test.js +152 -0
- package/dist/compiler/runtime/url-pattern-index.differential.test.js.map +1 -0
- package/dist/compiler/runtime/url-pattern-index.js +168 -0
- package/dist/compiler/runtime/url-pattern-index.js.map +1 -0
- package/dist/compiler/runtime/url-pattern-index.test.d.ts +1 -0
- package/dist/compiler/runtime/url-pattern-index.test.js +262 -0
- package/dist/compiler/runtime/url-pattern-index.test.js.map +1 -0
- package/dist/compiler/server/create-server-file.js +1 -1
- package/dist/compiler/server/create-server-file.js.map +1 -1
- package/dist/compiler/server/create-server-file.test.js +12 -0
- package/dist/compiler/server/create-server-file.test.js.map +1 -1
- package/dist/services/env-variables/index.js +1 -1
- package/dist/services/env-variables/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -93,6 +93,29 @@ ${injectCode("./normalize-trailing-slash.js", {
|
|
|
93
93
|
privateExports: ["normalizeTrailingSlash"],
|
|
94
94
|
})}
|
|
95
95
|
|
|
96
|
+
${needsUrlPatternPolyfill
|
|
97
|
+
? injectCode("./url-pattern-cache.js", {
|
|
98
|
+
privateExports: ["getUrlPattern"],
|
|
99
|
+
}) +
|
|
100
|
+
"\n\n" +
|
|
101
|
+
injectCode("./url-pattern-index.js", {
|
|
102
|
+
privateExports: [
|
|
103
|
+
"getRoutingConfigVersion",
|
|
104
|
+
"currentRoutingConfigVersion",
|
|
105
|
+
"getUrlPatternCandidates",
|
|
106
|
+
"getRouteStrategyCandidates",
|
|
107
|
+
],
|
|
108
|
+
})
|
|
109
|
+
: `// Default routing does not need a matcher cache or candidate index.
|
|
110
|
+
/** @param {string} pattern @param {URL} url @returns {URLPattern} */
|
|
111
|
+
function getUrlPattern(pattern, url) { return new URLPattern(pattern, url.href); }
|
|
112
|
+
function getRoutingConfigVersion() { return 0; }
|
|
113
|
+
function currentRoutingConfigVersion() { return 0; }
|
|
114
|
+
/** @param {URL} _url @returns {typeof urlPatterns} */
|
|
115
|
+
function getUrlPatternCandidates(_url) { return urlPatterns; }
|
|
116
|
+
/** @param {URL} _url @returns {typeof routeStrategies} */
|
|
117
|
+
function getRouteStrategyCandidates(_url) { return routeStrategies; }`}
|
|
118
|
+
|
|
96
119
|
${injectCode("./exec-url-pattern.js", {
|
|
97
120
|
privateExports: ["execUrlPattern"],
|
|
98
121
|
})}
|
|
@@ -142,7 +165,7 @@ ${injectCode("./type-definitions.js").replace(`@typedef {string} Locale`, `@type
|
|
|
142
165
|
function injectCode(path, options) {
|
|
143
166
|
const code = fs.readFileSync(new URL(path, import.meta.url), "utf-8");
|
|
144
167
|
// Regex to match single-line and multi-line imports
|
|
145
|
-
const importRegex =
|
|
168
|
+
const importRegex = /^\s*import\s+[\s\S]*?from\s+['"][^'"]+['"]\s*;?/gm;
|
|
146
169
|
const sourceMapRegex = /\/\/# sourceMappingURL=.*$/gm;
|
|
147
170
|
const blockSourceMapRegex = /\/\*# sourceMappingURL=.*?\*\//g;
|
|
148
171
|
let injected = code
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-runtime.js","sourceRoot":"","sources":["../../../src/compiler/runtime/create-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport type { CompilerOptions } from \"../compiler-options.js\";\nimport { perLocaleBuildStaticLocaleExpression } from \"../per-locale-build.js\";\n\n/**\n * Returns the code for the `runtime.js` module\n */\nexport function createRuntimeFile(args: {\n\tbaseLocale: string;\n\tlocales: string[];\n\tcompilerOptions: {\n\t\tstrategy: NonNullable<CompilerOptions[\"strategy\"]>;\n\t\trouteStrategies?: CompilerOptions[\"routeStrategies\"];\n\t\tcookieName: NonNullable<CompilerOptions[\"cookieName\"]>;\n\t\tcookieMaxAge: NonNullable<CompilerOptions[\"cookieMaxAge\"]>;\n\t\tcookieDomain: CompilerOptions[\"cookieDomain\"];\n\t\turlPatterns?: CompilerOptions[\"urlPatterns\"];\n\t\ttrailingSlash?: CompilerOptions[\"trailingSlash\"];\n\t\texperimentalMiddlewareLocaleSplitting: CompilerOptions[\"experimentalMiddlewareLocaleSplitting\"];\n\t\tisServer: CompilerOptions[\"isServer\"];\n\t\texperimentalStaticLocale?: CompilerOptions[\"experimentalStaticLocale\"];\n\t\tlocalStorageKey: CompilerOptions[\"localStorageKey\"];\n\t\tdisableAsyncLocalStorage: NonNullable<\n\t\t\tCompilerOptions[\"disableAsyncLocalStorage\"]\n\t\t>;\n\t};\n}): string {\n\tconst urlPatterns = args.compilerOptions.urlPatterns ?? [];\n\tconst routeStrategies = args.compilerOptions.routeStrategies ?? [];\n\n\tlet defaultUrlPatternUsed = false;\n\n\t// add default urlPatterns for a good out of the box experience\n\tif (args.compilerOptions.urlPatterns === undefined) {\n\t\tdefaultUrlPatternUsed = true;\n\t\turlPatterns.push({\n\t\t\tpattern: `:protocol://:domain(.*)::port?/:path(.*)?`,\n\t\t\tlocalized: [],\n\t\t});\n\t\tfor (const locale of args.locales) {\n\t\t\tif (locale === args.baseLocale) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\turlPatterns[0]?.localized.push([\n\t\t\t\tlocale,\n\t\t\t\t`:protocol://:domain(.*)::port?/${locale}/:path(.*)?`,\n\t\t\t]);\n\t\t}\n\t\turlPatterns[0]?.localized.push([\n\t\t\targs.baseLocale,\n\t\t\t`:protocol://:domain(.*)::port?/:path(.*)?`,\n\t\t]);\n\t}\n\tconst strategiesUsedByRoutes = routeStrategies.flatMap((routeStrategy) =>\n\t\t\"strategy\" in routeStrategy ? routeStrategy.strategy : []\n\t);\n\tconst allUsedStrategies = new Set([\n\t\t...args.compilerOptions.strategy,\n\t\t...strategiesUsedByRoutes,\n\t]);\n\tconst needsUrlPatternPolyfill =\n\t\t!defaultUrlPatternUsed || routeStrategies.length > 0;\n\n\t// verify that urlPatterns' locales are valid\n\tfor (const urlPattern of urlPatterns) {\n\t\tfor (const [locale] of urlPattern.localized) {\n\t\t\tif (!args.locales.includes(locale)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid locale \"${locale}\" in urlPatterns. It must be one of the locales defined in the \"locales\" array.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst code = `\n${needsUrlPatternPolyfill ? `import \"@inlang/paraglide-js/urlpattern-polyfill\";` : \"/** @type {any} */\\nconst URLPattern = {}\"}\n\n${injectCode(\"./variables.js\")\n\t.replace(\n\t\t`export const baseLocale = \"en\";`,\n\t\t`export const baseLocale = \"${args.baseLocale}\";`\n\t)\n\t.replace(\n\t\t`export const locales = /** @type {readonly string[]} */ ([\"en\", \"de\"]);`,\n\t\t`export const locales = /** @type {const} */ (${JSON.stringify(args.locales)});`\n\t)\n\t.replace(\n\t\t`export const strategy = [\"globalVariable\"];`,\n\t\t`export const strategy = ${JSON.stringify(args.compilerOptions.strategy, null, 2)};`\n\t)\n\t.replace(`<cookie-name>`, `${args.compilerOptions.cookieName}`)\n\t.replace(`60 * 60 * 24 * 400`, `${args.compilerOptions.cookieMaxAge}`)\n\t.replace(`<cookie-domain>`, `${args.compilerOptions.cookieDomain}`)\n\t.replace(\n\t\t`export const routeStrategies = [];`,\n\t\t`export const routeStrategies = ${JSON.stringify(routeStrategies, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_COOKIE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_COOKIE_STRATEGY_USED = ${allUsedStrategies.has(\"cookie\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_URL_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_URL_STRATEGY_USED = ${allUsedStrategies.has(\"url\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = ${allUsedStrategies.has(\"globalVariable\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = ${allUsedStrategies.has(\"preferredLanguage\")};`\n\t)\n\t.replace(\n\t\t`export const urlPatterns = [];`,\n\t\t`export const urlPatterns = ${JSON.stringify(urlPatterns, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const trailingSlash = undefined;`,\n\t\t`export const trailingSlash = ${\n\t\t\targs.compilerOptions.trailingSlash === undefined\n\t\t\t\t? \"undefined\"\n\t\t\t\t: JSON.stringify(args.compilerOptions.trailingSlash)\n\t\t};`\n\t)\n\t.replace(\n\t\t`export const disableAsyncLocalStorage = false;`,\n\t\t`export const disableAsyncLocalStorage = ${args.compilerOptions.disableAsyncLocalStorage};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;`,\n\t\t`const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = ${defaultUrlPatternUsed};`\n\t)\n\t.replace(\n\t\t`export const experimentalMiddlewareLocaleSplitting = false;`,\n\t\t`export const experimentalMiddlewareLocaleSplitting = ${args.compilerOptions.experimentalMiddlewareLocaleSplitting};`\n\t)\n\t.replace(\n\t\t`export const isServer = typeof window === \"undefined\";`,\n\t\t`export const isServer = ${args.compilerOptions.isServer};`\n\t)\n\t.replace(\n\t\t`export const experimentalStaticLocale = undefined;`,\n\t\targs.compilerOptions.experimentalStaticLocale\n\t\t\t? `export const experimentalStaticLocale = ${\n\t\t\t\t\targs.compilerOptions.experimentalStaticLocale ===\n\t\t\t\t\tperLocaleBuildStaticLocaleExpression\n\t\t\t\t\t\t? args.compilerOptions.experimentalStaticLocale\n\t\t\t\t\t\t: `assertIsLocale(${args.compilerOptions.experimentalStaticLocale})`\n\t\t\t\t};`\n\t\t\t: `export const experimentalStaticLocale = undefined;`\n\t)\n\t.replace(\n\t\t`export const localStorageKey = \"PARAGLIDE_LOCALE\";`,\n\t\t`export const localStorageKey = \"${args.compilerOptions.localStorageKey}\";`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = ${allUsedStrategies.has(\"localStorage\")};`\n\t)}\n\n/** @type {any} */ (globalThis).__paraglide =\n\t/** @type {any} */ (globalThis).__paraglide ?? {};\n/** @type {any} */ (globalThis).__paraglide.ssr =\n\t/** @type {any} */ (globalThis).__paraglide.ssr ?? {};\n\n${injectCode(\"./get-locale.js\")}\n\n${injectCode(\"./get-text-direction.js\")}\n\n${injectCode(\"./set-locale.js\")}\n\n${injectCode(\"./get-url-origin.js\")}\n\n${injectCode(\"./check-locale.js\")}\n\n${injectCode(\"./normalize-trailing-slash.js\", {\n\tprivateExports: [\"normalizeTrailingSlash\"],\n})}\n\n${injectCode(\"./exec-url-pattern.js\", {\n\tprivateExports: [\"execUrlPattern\"],\n})}\n\n${injectCode(\"./extract-locale-from-request.js\")}\n\n${injectCode(\"./extract-locale-from-request-async.js\")}\n\n${injectCode(\"./extract-locale-from-cookie.js\", {\n\tprivateExports: [\"clearLocaleCookieCache\"],\n})}\n\n${injectCode(\"./extract-locale-from-header.js\")}\n\n${injectCode(\"./extract-locale-from-navigator.js\")}\n\n${injectCode(\"./extract-locale-from-url.js\")}\n\n${injectCode(\"./localize-url.js\")}\n\n${injectCode(\"./route-strategy.js\")}\n\n${injectCode(\"./should-redirect.js\")}\n\n${injectCode(\"./localize-href.js\")}\n\n${injectCode(\"./track-message-call.js\")}\n\n${injectCode(\"./generate-static-localized-urls.js\")}\n\n${injectCode(\"./strategy.js\")}\n\n${injectCode(\"./type-definitions.js\").replace(\n\t`@typedef {string} Locale`,\n\t`@typedef {typeof locales[number]} Locale`\n)}\n`;\n\n\treturn code;\n}\n\n/**\n * Load a file from the current directory.\n *\n * Prunes the imports on top of the file as the runtime is\n * self-contained.\n *\n * @param {string} path\n * @param {{ privateExports?: string[] }} [options]\n * @returns {string}\n */\nfunction injectCode(\n\tpath: string,\n\toptions?: { privateExports?: string[] }\n): string {\n\tconst code = fs.readFileSync(new URL(path, import.meta.url), \"utf-8\");\n\t// Regex to match single-line and multi-line imports\n\tconst importRegex = /import\\s+[\\s\\S]*?from\\s+['\"][^'\"]+['\"]\\s*;?/g;\n\tconst sourceMapRegex = /\\/\\/# sourceMappingURL=.*$/gm;\n\tconst blockSourceMapRegex = /\\/\\*# sourceMappingURL=.*?\\*\\//g;\n\tlet injected = code\n\t\t.replace(importRegex, \"\")\n\t\t.replace(sourceMapRegex, \"\")\n\t\t.replace(blockSourceMapRegex, \"\")\n\t\t.trim();\n\tfor (const exportName of options?.privateExports ?? []) {\n\t\tinjected = injected.replace(\n\t\t\tnew RegExp(`\\\\bexport\\\\s+function\\\\s+${exportName}\\\\b`, \"g\"),\n\t\t\t`function ${exportName}`\n\t\t);\n\t}\n\treturn injected;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"create-runtime.js","sourceRoot":"","sources":["../../../src/compiler/runtime/create-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport type { CompilerOptions } from \"../compiler-options.js\";\nimport { perLocaleBuildStaticLocaleExpression } from \"../per-locale-build.js\";\n\n/**\n * Returns the code for the `runtime.js` module\n */\nexport function createRuntimeFile(args: {\n\tbaseLocale: string;\n\tlocales: string[];\n\tcompilerOptions: {\n\t\tstrategy: NonNullable<CompilerOptions[\"strategy\"]>;\n\t\trouteStrategies?: CompilerOptions[\"routeStrategies\"];\n\t\tcookieName: NonNullable<CompilerOptions[\"cookieName\"]>;\n\t\tcookieMaxAge: NonNullable<CompilerOptions[\"cookieMaxAge\"]>;\n\t\tcookieDomain: CompilerOptions[\"cookieDomain\"];\n\t\turlPatterns?: CompilerOptions[\"urlPatterns\"];\n\t\ttrailingSlash?: CompilerOptions[\"trailingSlash\"];\n\t\texperimentalMiddlewareLocaleSplitting: CompilerOptions[\"experimentalMiddlewareLocaleSplitting\"];\n\t\tisServer: CompilerOptions[\"isServer\"];\n\t\texperimentalStaticLocale?: CompilerOptions[\"experimentalStaticLocale\"];\n\t\tlocalStorageKey: CompilerOptions[\"localStorageKey\"];\n\t\tdisableAsyncLocalStorage: NonNullable<\n\t\t\tCompilerOptions[\"disableAsyncLocalStorage\"]\n\t\t>;\n\t};\n}): string {\n\tconst urlPatterns = args.compilerOptions.urlPatterns ?? [];\n\tconst routeStrategies = args.compilerOptions.routeStrategies ?? [];\n\n\tlet defaultUrlPatternUsed = false;\n\n\t// add default urlPatterns for a good out of the box experience\n\tif (args.compilerOptions.urlPatterns === undefined) {\n\t\tdefaultUrlPatternUsed = true;\n\t\turlPatterns.push({\n\t\t\tpattern: `:protocol://:domain(.*)::port?/:path(.*)?`,\n\t\t\tlocalized: [],\n\t\t});\n\t\tfor (const locale of args.locales) {\n\t\t\tif (locale === args.baseLocale) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\turlPatterns[0]?.localized.push([\n\t\t\t\tlocale,\n\t\t\t\t`:protocol://:domain(.*)::port?/${locale}/:path(.*)?`,\n\t\t\t]);\n\t\t}\n\t\turlPatterns[0]?.localized.push([\n\t\t\targs.baseLocale,\n\t\t\t`:protocol://:domain(.*)::port?/:path(.*)?`,\n\t\t]);\n\t}\n\tconst strategiesUsedByRoutes = routeStrategies.flatMap((routeStrategy) =>\n\t\t\"strategy\" in routeStrategy ? routeStrategy.strategy : []\n\t);\n\tconst allUsedStrategies = new Set([\n\t\t...args.compilerOptions.strategy,\n\t\t...strategiesUsedByRoutes,\n\t]);\n\tconst needsUrlPatternPolyfill =\n\t\t!defaultUrlPatternUsed || routeStrategies.length > 0;\n\n\t// verify that urlPatterns' locales are valid\n\tfor (const urlPattern of urlPatterns) {\n\t\tfor (const [locale] of urlPattern.localized) {\n\t\t\tif (!args.locales.includes(locale)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid locale \"${locale}\" in urlPatterns. It must be one of the locales defined in the \"locales\" array.`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst code = `\n${needsUrlPatternPolyfill ? `import \"@inlang/paraglide-js/urlpattern-polyfill\";` : \"/** @type {any} */\\nconst URLPattern = {}\"}\n\n${injectCode(\"./variables.js\")\n\t.replace(\n\t\t`export const baseLocale = \"en\";`,\n\t\t`export const baseLocale = \"${args.baseLocale}\";`\n\t)\n\t.replace(\n\t\t`export const locales = /** @type {readonly string[]} */ ([\"en\", \"de\"]);`,\n\t\t`export const locales = /** @type {const} */ (${JSON.stringify(args.locales)});`\n\t)\n\t.replace(\n\t\t`export const strategy = [\"globalVariable\"];`,\n\t\t`export const strategy = ${JSON.stringify(args.compilerOptions.strategy, null, 2)};`\n\t)\n\t.replace(`<cookie-name>`, `${args.compilerOptions.cookieName}`)\n\t.replace(`60 * 60 * 24 * 400`, `${args.compilerOptions.cookieMaxAge}`)\n\t.replace(`<cookie-domain>`, `${args.compilerOptions.cookieDomain}`)\n\t.replace(\n\t\t`export const routeStrategies = [];`,\n\t\t`export const routeStrategies = ${JSON.stringify(routeStrategies, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_COOKIE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_COOKIE_STRATEGY_USED = ${allUsedStrategies.has(\"cookie\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_URL_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_URL_STRATEGY_USED = ${allUsedStrategies.has(\"url\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = ${allUsedStrategies.has(\"globalVariable\")};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = ${allUsedStrategies.has(\"preferredLanguage\")};`\n\t)\n\t.replace(\n\t\t`export const urlPatterns = [];`,\n\t\t`export const urlPatterns = ${JSON.stringify(urlPatterns, null, 2)};`\n\t)\n\t.replace(\n\t\t`export const trailingSlash = undefined;`,\n\t\t`export const trailingSlash = ${\n\t\t\targs.compilerOptions.trailingSlash === undefined\n\t\t\t\t? \"undefined\"\n\t\t\t\t: JSON.stringify(args.compilerOptions.trailingSlash)\n\t\t};`\n\t)\n\t.replace(\n\t\t`export const disableAsyncLocalStorage = false;`,\n\t\t`export const disableAsyncLocalStorage = ${args.compilerOptions.disableAsyncLocalStorage};`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;`,\n\t\t`const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = ${defaultUrlPatternUsed};`\n\t)\n\t.replace(\n\t\t`export const experimentalMiddlewareLocaleSplitting = false;`,\n\t\t`export const experimentalMiddlewareLocaleSplitting = ${args.compilerOptions.experimentalMiddlewareLocaleSplitting};`\n\t)\n\t.replace(\n\t\t`export const isServer = typeof window === \"undefined\";`,\n\t\t`export const isServer = ${args.compilerOptions.isServer};`\n\t)\n\t.replace(\n\t\t`export const experimentalStaticLocale = undefined;`,\n\t\targs.compilerOptions.experimentalStaticLocale\n\t\t\t? `export const experimentalStaticLocale = ${\n\t\t\t\t\targs.compilerOptions.experimentalStaticLocale ===\n\t\t\t\t\tperLocaleBuildStaticLocaleExpression\n\t\t\t\t\t\t? args.compilerOptions.experimentalStaticLocale\n\t\t\t\t\t\t: `assertIsLocale(${args.compilerOptions.experimentalStaticLocale})`\n\t\t\t\t};`\n\t\t\t: `export const experimentalStaticLocale = undefined;`\n\t)\n\t.replace(\n\t\t`export const localStorageKey = \"PARAGLIDE_LOCALE\";`,\n\t\t`export const localStorageKey = \"${args.compilerOptions.localStorageKey}\";`\n\t)\n\t.replace(\n\t\t`export const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;`,\n\t\t`const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = ${allUsedStrategies.has(\"localStorage\")};`\n\t)}\n\n/** @type {any} */ (globalThis).__paraglide =\n\t/** @type {any} */ (globalThis).__paraglide ?? {};\n/** @type {any} */ (globalThis).__paraglide.ssr =\n\t/** @type {any} */ (globalThis).__paraglide.ssr ?? {};\n\n${injectCode(\"./get-locale.js\")}\n\n${injectCode(\"./get-text-direction.js\")}\n\n${injectCode(\"./set-locale.js\")}\n\n${injectCode(\"./get-url-origin.js\")}\n\n${injectCode(\"./check-locale.js\")}\n\n${injectCode(\"./normalize-trailing-slash.js\", {\n\tprivateExports: [\"normalizeTrailingSlash\"],\n})}\n\n${\n\tneedsUrlPatternPolyfill\n\t\t? injectCode(\"./url-pattern-cache.js\", {\n\t\t\t\tprivateExports: [\"getUrlPattern\"],\n\t\t\t}) +\n\t\t\t\"\\n\\n\" +\n\t\t\tinjectCode(\"./url-pattern-index.js\", {\n\t\t\t\tprivateExports: [\n\t\t\t\t\t\"getRoutingConfigVersion\",\n\t\t\t\t\t\"currentRoutingConfigVersion\",\n\t\t\t\t\t\"getUrlPatternCandidates\",\n\t\t\t\t\t\"getRouteStrategyCandidates\",\n\t\t\t\t],\n\t\t\t})\n\t\t: `// Default routing does not need a matcher cache or candidate index.\n/** @param {string} pattern @param {URL} url @returns {URLPattern} */\nfunction getUrlPattern(pattern, url) { return new URLPattern(pattern, url.href); }\nfunction getRoutingConfigVersion() { return 0; }\nfunction currentRoutingConfigVersion() { return 0; }\n/** @param {URL} _url @returns {typeof urlPatterns} */\nfunction getUrlPatternCandidates(_url) { return urlPatterns; }\n/** @param {URL} _url @returns {typeof routeStrategies} */\nfunction getRouteStrategyCandidates(_url) { return routeStrategies; }`\n}\n\n${injectCode(\"./exec-url-pattern.js\", {\n\tprivateExports: [\"execUrlPattern\"],\n})}\n\n${injectCode(\"./extract-locale-from-request.js\")}\n\n${injectCode(\"./extract-locale-from-request-async.js\")}\n\n${injectCode(\"./extract-locale-from-cookie.js\", {\n\tprivateExports: [\"clearLocaleCookieCache\"],\n})}\n\n${injectCode(\"./extract-locale-from-header.js\")}\n\n${injectCode(\"./extract-locale-from-navigator.js\")}\n\n${injectCode(\"./extract-locale-from-url.js\")}\n\n${injectCode(\"./localize-url.js\")}\n\n${injectCode(\"./route-strategy.js\")}\n\n${injectCode(\"./should-redirect.js\")}\n\n${injectCode(\"./localize-href.js\")}\n\n${injectCode(\"./track-message-call.js\")}\n\n${injectCode(\"./generate-static-localized-urls.js\")}\n\n${injectCode(\"./strategy.js\")}\n\n${injectCode(\"./type-definitions.js\").replace(\n\t`@typedef {string} Locale`,\n\t`@typedef {typeof locales[number]} Locale`\n)}\n`;\n\n\treturn code;\n}\n\n/**\n * Load a file from the current directory.\n *\n * Prunes the imports on top of the file as the runtime is\n * self-contained.\n *\n * @param {string} path\n * @param {{ privateExports?: string[] }} [options]\n * @returns {string}\n */\nfunction injectCode(\n\tpath: string,\n\toptions?: { privateExports?: string[] }\n): string {\n\tconst code = fs.readFileSync(new URL(path, import.meta.url), \"utf-8\");\n\t// Regex to match single-line and multi-line imports\n\tconst importRegex = /^\\s*import\\s+[\\s\\S]*?from\\s+['\"][^'\"]+['\"]\\s*;?/gm;\n\tconst sourceMapRegex = /\\/\\/# sourceMappingURL=.*$/gm;\n\tconst blockSourceMapRegex = /\\/\\*# sourceMappingURL=.*?\\*\\//g;\n\tlet injected = code\n\t\t.replace(importRegex, \"\")\n\t\t.replace(sourceMapRegex, \"\")\n\t\t.replace(blockSourceMapRegex, \"\")\n\t\t.trim();\n\tfor (const exportName of options?.privateExports ?? []) {\n\t\tinjected = injected.replace(\n\t\t\tnew RegExp(`\\\\bexport\\\\s+function\\\\s+${exportName}\\\\b`, \"g\"),\n\t\t\t`function ${exportName}`\n\t\t);\n\t}\n\treturn injected;\n}\n"]}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { toLocale } from "./check-locale.js";
|
|
2
|
-
import { baseLocale, TREE_SHAKE_DEFAULT_URL_PATTERN_USED,
|
|
2
|
+
import { baseLocale, TREE_SHAKE_DEFAULT_URL_PATTERN_USED, } from "./variables.js";
|
|
3
3
|
import { normalizeTrailingSlash } from "./normalize-trailing-slash.js";
|
|
4
4
|
import { execUrlPattern } from "./exec-url-pattern.js";
|
|
5
|
+
import { getUrlPattern } from "./url-pattern-cache.js";
|
|
6
|
+
import { getUrlPatternCandidates, getRoutingConfigVersion, } from "./url-pattern-index.js";
|
|
7
|
+
let cachedLocaleConfigVersion = -1;
|
|
5
8
|
/**
|
|
6
9
|
* If extractLocaleFromUrl is called many times on the same page and the URL
|
|
7
10
|
* hasn't changed, we don't need to recompute it every time which can get expensive.
|
|
@@ -24,7 +27,10 @@ let cachedLocale;
|
|
|
24
27
|
*/
|
|
25
28
|
export function extractLocaleFromUrl(url) {
|
|
26
29
|
const urlString = typeof url === "string" ? url : url.href;
|
|
27
|
-
|
|
30
|
+
const configVersion = TREE_SHAKE_DEFAULT_URL_PATTERN_USED
|
|
31
|
+
? 0
|
|
32
|
+
: getRoutingConfigVersion();
|
|
33
|
+
if (cachedUrl === urlString && cachedLocaleConfigVersion === configVersion) {
|
|
28
34
|
return cachedLocale;
|
|
29
35
|
}
|
|
30
36
|
/** @type {Locale | undefined} */
|
|
@@ -38,9 +44,9 @@ export function extractLocaleFromUrl(url) {
|
|
|
38
44
|
else {
|
|
39
45
|
const urlObj = normalizeTrailingSlash(typeof url === "string" ? new URL(url) : new URL(url));
|
|
40
46
|
// Iterate over URL patterns
|
|
41
|
-
for (const element of
|
|
47
|
+
for (const element of getUrlPatternCandidates(urlObj)) {
|
|
42
48
|
for (const [locale, localizedPattern] of element.localized) {
|
|
43
|
-
const match = execUrlPattern(
|
|
49
|
+
const match = execUrlPattern(getUrlPattern(localizedPattern, urlObj), urlObj);
|
|
44
50
|
if (match) {
|
|
45
51
|
result = locale;
|
|
46
52
|
break;
|
|
@@ -50,6 +56,7 @@ export function extractLocaleFromUrl(url) {
|
|
|
50
56
|
break;
|
|
51
57
|
}
|
|
52
58
|
}
|
|
59
|
+
cachedLocaleConfigVersion = configVersion;
|
|
53
60
|
cachedUrl = urlString;
|
|
54
61
|
cachedLocale = result;
|
|
55
62
|
return result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-locale-from-url.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-url.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EACN,UAAU,EACV,mCAAmC,
|
|
1
|
+
{"version":3,"file":"extract-locale-from-url.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-url.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EACN,UAAU,EACV,mCAAmC,GACnC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACN,uBAAuB,EACvB,uBAAuB,GACvB,MAAM,wBAAwB,CAAC;AAEhC,IAAI,yBAAyB,GAAG,CAAC,CAAC,CAAC;AAEnC;;;;;GAKG;AACH,+BAA+B;AAC/B,IAAI,SAAS,CAAC;AACd,+BAA+B;AAC/B,IAAI,YAAY,CAAC;AAEjB;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAG;IACvC,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAE3D,MAAM,aAAa,GAAG,mCAAmC;QACxD,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,uBAAuB,EAAE,CAAC;IAC7B,IAAI,SAAS,KAAK,SAAS,IAAI,yBAAyB,KAAK,aAAa,EAAE,CAAC;QAC5E,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,iCAAiC;IACjC,IAAI,MAAM,CAAC;IACX,IAAI,mCAAmC,EAAE,CAAC;QACzC,MAAM,MAAM,GACX,OAAO,GAAG,KAAK,QAAQ;YACtB,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC;YACpC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,MAAM,GAAG,8BAA8B,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;SAAM,CAAC;QACP,MAAM,MAAM,GAAG,sBAAsB,CACpC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CACrD,CAAC;QAEF,4BAA4B;QAC5B,KAAK,MAAM,OAAO,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;YACvD,KAAK,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBAC5D,MAAM,KAAK,GAAG,cAAc,CAC3B,aAAa,CAAC,gBAAgB,EAAE,MAAM,CAAC,EACvC,MAAM,CACN,CAAC;gBAEF,IAAI,KAAK,EAAE,CAAC;oBACX,MAAM,GAAG,MAAM,CAAC;oBAChB,MAAM;gBACP,CAAC;YACF,CAAC;YACD,IAAI,MAAM;gBAAE,MAAM;QACnB,CAAC;IACF,CAAC;IAED,yBAAyB,GAAG,aAAa,CAAC;IAC1C,SAAS,GAAG,SAAS,CAAC;IACtB,YAAY,GAAG,MAAM,CAAC;IACtB,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAS,8BAA8B,CAAC,GAAG;IAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC;AAChD,CAAC","sourcesContent":["import { toLocale } from \"./check-locale.js\";\nimport {\n\tbaseLocale,\n\tTREE_SHAKE_DEFAULT_URL_PATTERN_USED,\n} from \"./variables.js\";\nimport { normalizeTrailingSlash } from \"./normalize-trailing-slash.js\";\nimport { execUrlPattern } from \"./exec-url-pattern.js\";\nimport { getUrlPattern } from \"./url-pattern-cache.js\";\nimport {\n\tgetUrlPatternCandidates,\n\tgetRoutingConfigVersion,\n} from \"./url-pattern-index.js\";\n\nlet cachedLocaleConfigVersion = -1;\n\n/**\n * If extractLocaleFromUrl is called many times on the same page and the URL\n * hasn't changed, we don't need to recompute it every time which can get expensive.\n * We might use a LRU cache if needed, but for now storing only the last result is enough.\n * https://github.com/opral/monorepo/pull/3575#discussion_r2066731243\n */\n/** @type {string|undefined} */\nlet cachedUrl;\n/** @type {Locale|undefined} */\nlet cachedLocale;\n\n/**\n * Extracts the locale from a given URL using native URLPattern.\n *\n * The built-in default `/:locale/...` routing is case-insensitive because it\n * canonicalizes the first path segment with `toLocale()`. Custom `urlPatterns`\n * keep URLPattern's normal exact matching semantics for path segments.\n *\n * @param {URL|string} url - The full URL from which to extract the locale.\n * @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.\n */\nexport function extractLocaleFromUrl(url) {\n\tconst urlString = typeof url === \"string\" ? url : url.href;\n\n\tconst configVersion = TREE_SHAKE_DEFAULT_URL_PATTERN_USED\n\t\t? 0\n\t\t: getRoutingConfigVersion();\n\tif (cachedUrl === urlString && cachedLocaleConfigVersion === configVersion) {\n\t\treturn cachedLocale;\n\t}\n\n\t/** @type {Locale | undefined} */\n\tlet result;\n\tif (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {\n\t\tconst urlObj =\n\t\t\ttypeof url === \"string\"\n\t\t\t\t? new URL(url, \"http://example.com\")\n\t\t\t\t: new URL(url);\n\t\tresult = defaultUrlPatternExtractLocale(normalizeTrailingSlash(urlObj));\n\t} else {\n\t\tconst urlObj = normalizeTrailingSlash(\n\t\t\ttypeof url === \"string\" ? new URL(url) : new URL(url)\n\t\t);\n\n\t\t// Iterate over URL patterns\n\t\tfor (const element of getUrlPatternCandidates(urlObj)) {\n\t\t\tfor (const [locale, localizedPattern] of element.localized) {\n\t\t\t\tconst match = execUrlPattern(\n\t\t\t\t\tgetUrlPattern(localizedPattern, urlObj),\n\t\t\t\t\turlObj\n\t\t\t\t);\n\n\t\t\t\tif (match) {\n\t\t\t\t\tresult = locale;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result) break;\n\t\t}\n\t}\n\n\tcachedLocaleConfigVersion = configVersion;\n\tcachedUrl = urlString;\n\tcachedLocale = result;\n\treturn result;\n}\n\n/**\n * https://github.com/opral/inlang-paraglide-js/issues/381\n *\n * @param {URL | string} url - The full URL from which to extract the locale.\n * @returns {Locale | undefined} The extracted locale, or undefined if no locale is found.\n */\nfunction defaultUrlPatternExtractLocale(url) {\n\tconst urlObj = new URL(url, \"http://example.com\");\n\tconst pathSegments = urlObj.pathname.split(\"/\").filter(Boolean);\n\treturn toLocale(pathSegments[0]) || baseLocale;\n}\n"]}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { localizeUrl } from "./localize-url.js";
|
|
2
2
|
import { normalizeTrailingSlash } from "./normalize-trailing-slash.js";
|
|
3
3
|
import { execUrlPattern } from "./exec-url-pattern.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getUrlPattern } from "./url-pattern-cache.js";
|
|
5
|
+
import { getUrlPatternCandidates } from "./url-pattern-index.js";
|
|
6
|
+
import { locales, baseLocale, TREE_SHAKE_DEFAULT_URL_PATTERN_USED, trailingSlash, } from "./variables.js";
|
|
5
7
|
/**
|
|
6
8
|
* Generates localized URL variants for all provided URLs based on your configured locales and URL patterns.
|
|
7
9
|
*
|
|
@@ -78,10 +80,10 @@ export function generateStaticLocalizedUrls(urls) {
|
|
|
78
80
|
const url = normalizeTrailingSlash(new URL(originalUrl));
|
|
79
81
|
// Try each URL pattern to find one that matches
|
|
80
82
|
let patternFound = false;
|
|
81
|
-
for (const pattern of
|
|
83
|
+
for (const pattern of getUrlPatternCandidates(url)) {
|
|
82
84
|
try {
|
|
83
85
|
// Try to match the unlocalized pattern
|
|
84
|
-
const unlocalizedMatch = execUrlPattern(
|
|
86
|
+
const unlocalizedMatch = execUrlPattern(getUrlPattern(pattern.pattern, url), url);
|
|
85
87
|
if (!unlocalizedMatch)
|
|
86
88
|
continue;
|
|
87
89
|
patternFound = true;
|
|
@@ -1 +1 @@
|
|
|
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,EACN,OAAO,EACP,UAAU,EACV,mCAAmC,EACnC,aAAa,
|
|
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 { normalizeTrailingSlash } from \"./normalize-trailing-slash.js\";\nimport { execUrlPattern } from \"./exec-url-pattern.js\";\nimport { getUrlPattern } from \"./url-pattern-cache.js\";\nimport { getUrlPatternCandidates } from \"./url-pattern-index.js\";\nimport {\n\tlocales,\n\tbaseLocale,\n\tTREE_SHAKE_DEFAULT_URL_PATTERN_USED,\n\ttrailingSlash,\n} from \"./variables.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// 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 getUrlPatternCandidates(url)) {\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"]}
|
|
@@ -204,4 +204,41 @@ test("keeps custom URLPattern path expressions on the generic fallback", async (
|
|
|
204
204
|
expect(runtime.localizeHref("/a", { locale: "de" })).toBe("/de/a");
|
|
205
205
|
expect(runtime.localizeHref("/about", { locale: "de" })).toBe("/about");
|
|
206
206
|
});
|
|
207
|
+
test.each([undefined, "always", "never"])("default hrefs preserve slashes unless trailingSlash is %s", async (trailingSlash) => {
|
|
208
|
+
const runtime = await createParaglide({
|
|
209
|
+
blob: await newProject({
|
|
210
|
+
settings: { baseLocale: "de", locales: ["de", "en"] },
|
|
211
|
+
}),
|
|
212
|
+
strategy: ["url", "baseLocale"],
|
|
213
|
+
trailingSlash,
|
|
214
|
+
});
|
|
215
|
+
const paths = ["/", "/about/", "/contact/"];
|
|
216
|
+
const suffix = trailingSlash === "never" ? "" : "/";
|
|
217
|
+
expect(paths.flatMap((path) => runtime.locales.map((locale) => runtime.localizeHref(path, { locale })))).toEqual([
|
|
218
|
+
"/",
|
|
219
|
+
"/en" + suffix,
|
|
220
|
+
"/about" + suffix,
|
|
221
|
+
"/en/about" + suffix,
|
|
222
|
+
"/contact" + suffix,
|
|
223
|
+
"/en/contact" + suffix,
|
|
224
|
+
]);
|
|
225
|
+
expect(runtime.localizeHref("/about", { locale: "en" })).toBe("/en/about" + (trailingSlash === "always" ? "/" : ""));
|
|
226
|
+
expect(runtime.deLocalizeHref("/en/about/?q=test#section")).toBe("/about" + suffix + "?q=test#section");
|
|
227
|
+
expect(runtime.localizeHref("/en/about/?q=test#section", { locale: "de" })).toBe("/about" + suffix + "?q=test#section");
|
|
228
|
+
});
|
|
229
|
+
test("default routes remove detected locales after repeated leading separators", async () => {
|
|
230
|
+
const runtime = await createParaglide({
|
|
231
|
+
blob: await newProject({
|
|
232
|
+
settings: { baseLocale: "en", locales: ["en", "de", "fr"] },
|
|
233
|
+
}),
|
|
234
|
+
strategy: ["url", "baseLocale"],
|
|
235
|
+
});
|
|
236
|
+
for (const suffix of ["", "/"]) {
|
|
237
|
+
const input = `https://example.com//de/about${suffix}?q=test#section`;
|
|
238
|
+
expect(runtime.extractLocaleFromUrl(input)).toBe("de");
|
|
239
|
+
expect(runtime.localizeUrl(input, { locale: "en" }).href).toBe(`https://example.com/about${suffix}?q=test#section`);
|
|
240
|
+
expect(runtime.localizeUrl(input, { locale: "fr" }).href).toBe(`https://example.com/fr/about${suffix}?q=test#section`);
|
|
241
|
+
expect(runtime.deLocalizeUrl(input).href).toBe(`https://example.com/about${suffix}?q=test#section`);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
207
244
|
//# 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":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,YAAY,CAAC;QACjD,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,+CAA+C;gBACxD,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,kCAAkC,CAAC;oBAC1C,CAAC,IAAI,EAAE,+BAA+B,CAAC;iBACvC;aACD;SACD;KACD,CAAC,CAAC;IAEH,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC5D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,YAAY,CAAC;QACjD,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,+CAA+C;gBACxD,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,kCAAkC,CAAC;oBAC1C,CAAC,IAAI,EAAE,+BAA+B,CAAC;iBACvC;aACD;SACD;KACD,CAAC,CAAC;IAEH,MAAM,CACL,OAAO,CAAC,YAAY,CAAC,0BAA0B,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAClE,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACtC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CACjE,0BAA0B,CAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,0DAA0D;AAC1D,IAAI,CAAC,wGAAwG,EAAE,KAAK,IAAI,EAAE;IACzH,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,YAAY,CAAC;QACjD,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,+BAA+B;gBACxC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,kCAAkC,CAAC;oBAC1C,CAAC,IAAI,EAAE,+BAA+B,CAAC;iBACvC;aACD;SACD;KACD,CAAC,CAAC;IAEH,qDAAqD;IACrD,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,CAAC;IAE1D,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5D,6BAA6B,CAC7B,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CACjE,0BAA0B,CAC1B,CAAC;IAEF,qDAAqD;IACrD,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5D,0BAA0B,CAC1B,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAC9D,0BAA0B,CAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,0DAA0D;AAC1D,IAAI,CAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;IACrC,MAAM,IAAI,GAAG,MAAM,CAAC;IAEpB,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,CAAC;QACjB,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,KAAK,IAAI,eAAe;gBACjC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,KAAK,IAAI,kBAAkB,CAAC;oBACnC,CAAC,IAAI,EAAE,KAAK,IAAI,eAAe,CAAC;iBAChC;aACD;SACD;KACD,CAAC,CAAC;IAEH,yCAAyC;IACzC,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC3D,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAE7D,qCAAqC;IACrC,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC9D,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;aAC3B;SACD,CAAC;QACF,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,CAAC,KAAK,CAAC;QACjB,WAAW,EAAE,SAAS;KACtB,CAAC,CAAC;IAEH,qBAAqB;IACrB,UAAU,CAAC,MAAM,GAAG,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,oBAAoB,CAAC,EAAS,CAAC;IAEvE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,aAAa,CAAC;QACzC;YACC,OAAO,EAAE,2CAA2C;YACpD,SAAS,EAAE;gBACV,CAAC,IAAI,EAAE,8CAA8C,CAAC;gBACtD,CAAC,IAAI,EAAE,8CAA8C,CAAC;gBACtD,CAAC,IAAI,EAAE,2CAA2C,CAAC;aACnD;SACD;KACD,CAAC,CAAC;IAEH,yDAAyD;IACzD,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAE3E,8BAA8B;IAC9B,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9D,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjE,oCAAoC;IACpC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3D,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAExD,gDAAgD;IAChD,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,wBAAwB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5E,2BAA2B,CAC3B,CAAC;IAEF,0BAA0B;IAC1B,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAC9D,wBAAwB,CACxB,CAAC;IAEF,YAAY;IACZ,MAAM,CACL,OAAO,CAAC,YAAY,CAAC,6BAA6B,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CACrE,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAEzC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,gCAAgC,CAAC,CAAC,CAAC,IAAI,CACpE,6BAA6B,CAC7B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;IAC9D,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,CAAC,KAAK,CAAC;QACjB,aAAa,EAAE,OAAO;KACtB,CAAC,CAAC;IAEH,UAAU,CAAC,MAAM,GAAG,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,oBAAoB,CAAC,EAAS,CAAC;IAEvE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC/D,aAAa,CACb,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;IACzE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,CAAC;KACjB,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAE3E,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;IACnF,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,CAAC;QACjB,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,YAAY;gBACrB,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,YAAY,CAAC;oBACpB,CAAC,IAAI,EAAE,eAAe,CAAC;iBACvB;aACD;SACD;KACD,CAAC,CAAC;IACH,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,CAAC;IAE3D,mEAAmE;IACnE,+DAA+D;IAC/D,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACzE,CAAC,CAAC,CAAC","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntest(\"uses the locale from getLocale() if no locale is provided\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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"]}
|
|
1
|
+
{"version":3,"file":"localize-href.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/localize-href.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,YAAY,CAAC;QACjD,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,+CAA+C;gBACxD,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,kCAAkC,CAAC;oBAC1C,CAAC,IAAI,EAAE,+BAA+B,CAAC;iBACvC;aACD;SACD;KACD,CAAC,CAAC;IAEH,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC5D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,YAAY,CAAC;QACjD,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,+CAA+C;gBACxD,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,kCAAkC,CAAC;oBAC1C,CAAC,IAAI,EAAE,+BAA+B,CAAC;iBACvC;aACD;SACD;KACD,CAAC,CAAC;IAEH,MAAM,CACL,OAAO,CAAC,YAAY,CAAC,0BAA0B,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAClE,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACtC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CACjE,0BAA0B,CAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,0DAA0D;AAC1D,IAAI,CAAC,wGAAwG,EAAE,KAAK,IAAI,EAAE;IACzH,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,YAAY,CAAC;QACjD,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,+BAA+B;gBACxC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,kCAAkC,CAAC;oBAC1C,CAAC,IAAI,EAAE,+BAA+B,CAAC;iBACvC;aACD;SACD;KACD,CAAC,CAAC;IAEH,qDAAqD;IACrD,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,CAAC;IAE1D,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5D,6BAA6B,CAC7B,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CACjE,0BAA0B,CAC1B,CAAC;IAEF,qDAAqD;IACrD,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5D,0BAA0B,CAC1B,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAC9D,0BAA0B,CAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,0DAA0D;AAC1D,IAAI,CAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;IACrC,MAAM,IAAI,GAAG,MAAM,CAAC;IAEpB,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,CAAC;QACjB,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,KAAK,IAAI,eAAe;gBACjC,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,KAAK,IAAI,kBAAkB,CAAC;oBACnC,CAAC,IAAI,EAAE,KAAK,IAAI,eAAe,CAAC;iBAChC;aACD;SACD;KACD,CAAC,CAAC;IAEH,yCAAyC;IACzC,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC3D,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAE7D,qCAAqC;IACrC,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC9D,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;aAC3B;SACD,CAAC;QACF,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,CAAC,KAAK,CAAC;QACjB,WAAW,EAAE,SAAS;KACtB,CAAC,CAAC;IAEH,qBAAqB;IACrB,UAAU,CAAC,MAAM,GAAG,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,oBAAoB,CAAC,EAAS,CAAC;IAEvE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,aAAa,CAAC;QACzC;YACC,OAAO,EAAE,2CAA2C;YACpD,SAAS,EAAE;gBACV,CAAC,IAAI,EAAE,8CAA8C,CAAC;gBACtD,CAAC,IAAI,EAAE,8CAA8C,CAAC;gBACtD,CAAC,IAAI,EAAE,2CAA2C,CAAC;aACnD;SACD;KACD,CAAC,CAAC;IAEH,yDAAyD;IACzD,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAE3E,8BAA8B;IAC9B,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9D,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjE,oCAAoC;IACpC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3D,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAExD,gDAAgD;IAChD,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,wBAAwB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5E,2BAA2B,CAC3B,CAAC;IAEF,0BAA0B;IAC1B,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAC9D,wBAAwB,CACxB,CAAC;IAEF,YAAY;IACZ,MAAM,CACL,OAAO,CAAC,YAAY,CAAC,6BAA6B,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CACrE,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAEzC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,gCAAgC,CAAC,CAAC,CAAC,IAAI,CACpE,6BAA6B,CAC7B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;IAC9D,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,CAAC,KAAK,CAAC;QACjB,aAAa,EAAE,OAAO;KACtB,CAAC,CAAC;IAEH,UAAU,CAAC,MAAM,GAAG,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,oBAAoB,CAAC,EAAS,CAAC;IAEvE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC/D,aAAa,CACb,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;IACzE,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,CAAC;KACjB,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAE3E,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;IACnF,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;aACrB;SACD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,CAAC;QACjB,WAAW,EAAE;YACZ;gBACC,OAAO,EAAE,YAAY;gBACrB,SAAS,EAAE;oBACV,CAAC,IAAI,EAAE,YAAY,CAAC;oBACpB,CAAC,IAAI,EAAE,eAAe,CAAC;iBACvB;aACD;SACD;KACD,CAAC,CAAC;IACH,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,CAAC;IAE3D,mEAAmE;IACnE,+DAA+D;IAC/D,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACzE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC,CACjD,2DAA2D,EAC3D,KAAK,EAAE,aAAa,EAAE,EAAE;IACvB,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE;SACrD,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC;QAC/B,aAAa;KACb,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACpD,MAAM,CACL,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CACtB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CACvE,CACD,CAAC,OAAO,CAAC;QACT,GAAG;QACH,KAAK,GAAG,MAAM;QACd,QAAQ,GAAG,MAAM;QACjB,WAAW,GAAG,MAAM;QACpB,UAAU,GAAG,MAAM;QACnB,aAAa,GAAG,MAAM;KACtB,CAAC,CAAC;IACH,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5D,WAAW,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CACrD,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,2BAA2B,CAAC,CAAC,CAAC,IAAI,CAC/D,QAAQ,GAAG,MAAM,GAAG,iBAAiB,CACrC,CAAC;IACF,MAAM,CACL,OAAO,CAAC,YAAY,CAAC,2BAA2B,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CACnE,CAAC,IAAI,CAAC,QAAQ,GAAG,MAAM,GAAG,iBAAiB,CAAC,CAAC;AAC/C,CAAC,CACD,CAAC;AAEF,IAAI,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;IAC3F,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE;SAC3D,CAAC;QACF,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC;KAC/B,CAAC,CAAC;IACH,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,gCAAgC,MAAM,iBAAiB,CAAC;QACtE,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAC7D,4BAA4B,MAAM,iBAAiB,CACnD,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAC7D,+BAA+B,MAAM,iBAAiB,CACtD,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAC7C,4BAA4B,MAAM,iBAAiB,CACnD,CAAC;IACH,CAAC;AACF,CAAC,CAAC,CAAC","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntest(\"uses the locale from getLocale() if no locale is provided\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\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\ntest.each([undefined, \"always\", \"never\"] as const)(\n\t\"default hrefs preserve slashes unless trailingSlash is %s\",\n\tasync (trailingSlash) => {\n\t\tconst runtime = await createParaglide({\n\t\t\tblob: await newProject({\n\t\t\t\tsettings: { baseLocale: \"de\", locales: [\"de\", \"en\"] },\n\t\t\t}),\n\t\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\t\ttrailingSlash,\n\t\t});\n\t\tconst paths = [\"/\", \"/about/\", \"/contact/\"];\n\t\tconst suffix = trailingSlash === \"never\" ? \"\" : \"/\";\n\t\texpect(\n\t\t\tpaths.flatMap((path) =>\n\t\t\t\truntime.locales.map((locale) => runtime.localizeHref(path, { locale }))\n\t\t\t)\n\t\t).toEqual([\n\t\t\t\"/\",\n\t\t\t\"/en\" + suffix,\n\t\t\t\"/about\" + suffix,\n\t\t\t\"/en/about\" + suffix,\n\t\t\t\"/contact\" + suffix,\n\t\t\t\"/en/contact\" + suffix,\n\t\t]);\n\t\texpect(runtime.localizeHref(\"/about\", { locale: \"en\" })).toBe(\n\t\t\t\"/en/about\" + (trailingSlash === \"always\" ? \"/\" : \"\")\n\t\t);\n\t\texpect(runtime.deLocalizeHref(\"/en/about/?q=test#section\")).toBe(\n\t\t\t\"/about\" + suffix + \"?q=test#section\"\n\t\t);\n\t\texpect(\n\t\t\truntime.localizeHref(\"/en/about/?q=test#section\", { locale: \"de\" })\n\t\t).toBe(\"/about\" + suffix + \"?q=test#section\");\n\t}\n);\n\ntest(\"default routes remove detected locales after repeated leading separators\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: { baseLocale: \"en\", locales: [\"en\", \"de\", \"fr\"] },\n\t\t}),\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t});\n\tfor (const suffix of [\"\", \"/\"]) {\n\t\tconst input = `https://example.com//de/about${suffix}?q=test#section`;\n\t\texpect(runtime.extractLocaleFromUrl(input)).toBe(\"de\");\n\t\texpect(runtime.localizeUrl(input, { locale: \"en\" }).href).toBe(\n\t\t\t`https://example.com/about${suffix}?q=test#section`\n\t\t);\n\t\texpect(runtime.localizeUrl(input, { locale: \"fr\" }).href).toBe(\n\t\t\t`https://example.com/fr/about${suffix}?q=test#section`\n\t\t);\n\t\texpect(runtime.deLocalizeUrl(input).href).toBe(\n\t\t\t`https://example.com/about${suffix}?q=test#section`\n\t\t);\n\t}\n});\n"]}
|
|
@@ -2,9 +2,11 @@ import { extractLocaleFromUrl } from "./extract-locale-from-url.js";
|
|
|
2
2
|
import { getLocale } from "./get-locale.js";
|
|
3
3
|
import { getUrlOrigin } from "./get-url-origin.js";
|
|
4
4
|
import { assertIsLocale, toLocale } from "./check-locale.js";
|
|
5
|
-
import { baseLocale, TREE_SHAKE_DEFAULT_URL_PATTERN_USED, trailingSlash,
|
|
5
|
+
import { baseLocale, TREE_SHAKE_DEFAULT_URL_PATTERN_USED, trailingSlash, } from "./variables.js";
|
|
6
6
|
import { normalizeTrailingSlash } from "./normalize-trailing-slash.js";
|
|
7
7
|
import { execUrlPattern } from "./exec-url-pattern.js";
|
|
8
|
+
import { getUrlPattern } from "./url-pattern-cache.js";
|
|
9
|
+
import { getUrlPatternCandidates, currentRoutingConfigVersion, } from "./url-pattern-index.js";
|
|
8
10
|
/**
|
|
9
11
|
* Lower-level URL localization function, primarily used in server contexts.
|
|
10
12
|
*
|
|
@@ -61,7 +63,7 @@ export function localizeUrl(url, options) {
|
|
|
61
63
|
? originalUrl
|
|
62
64
|
: normalizeTrailingSlash(new URL(originalUrl));
|
|
63
65
|
// Iterate over URL patterns
|
|
64
|
-
for (const element of
|
|
66
|
+
for (const element of getUrlPatternCandidates(urlObj)) {
|
|
65
67
|
// Most applications use a locale prefix (and sometimes a fixed domain)
|
|
66
68
|
// with a trailing catch-all path. Those routes do not need URLPattern's
|
|
67
69
|
// parser or matcher on every call. Keep the generic matcher below as the
|
|
@@ -110,7 +112,8 @@ function localizeUrlDefaultPattern(url, locale) {
|
|
|
110
112
|
if (currentLocale === locale) {
|
|
111
113
|
return normalizeTrailingSlash(urlObj);
|
|
112
114
|
}
|
|
113
|
-
|
|
115
|
+
// Ignore leading separators like locale detection, preserving the remaining path.
|
|
116
|
+
const pathSegments = urlObj.pathname.replace(/^\/+/, "").split("/");
|
|
114
117
|
// If current path starts with a locale, remove it
|
|
115
118
|
if (pathSegments.length > 0 && toLocale(pathSegments[0])) {
|
|
116
119
|
pathSegments.shift();
|
|
@@ -173,7 +176,7 @@ export function deLocalizeUrl(url) {
|
|
|
173
176
|
? originalUrl
|
|
174
177
|
: normalizeTrailingSlash(new URL(originalUrl));
|
|
175
178
|
// Iterate over URL patterns
|
|
176
|
-
for (const element of
|
|
179
|
+
for (const element of getUrlPatternCandidates(urlObj)) {
|
|
177
180
|
const fastPathDeLocalized = deLocalizeUrlFastPath(urlObj, element);
|
|
178
181
|
if (fastPathDeLocalized !== undefined) {
|
|
179
182
|
return fastPathDeLocalized;
|
|
@@ -205,7 +208,8 @@ export function deLocalizeUrl(url) {
|
|
|
205
208
|
*/
|
|
206
209
|
function deLocalizeUrlDefaultPattern(url) {
|
|
207
210
|
const urlObj = normalizeTrailingSlash(typeof url === "string" ? new URL(url, getUrlOrigin()) : new URL(url));
|
|
208
|
-
|
|
211
|
+
// Ignore leading separators like locale detection, preserving the remaining path.
|
|
212
|
+
const pathSegments = urlObj.pathname.replace(/^\/+/, "").split("/");
|
|
209
213
|
// If first segment is a locale, remove it
|
|
210
214
|
if (pathSegments.length > 0 && toLocale(pathSegments[0])) {
|
|
211
215
|
urlObj.pathname = "/" + pathSegments.slice(1).join("/");
|
|
@@ -335,45 +339,6 @@ export function aggregateGroups(match) {
|
|
|
335
339
|
...match.username.groups,
|
|
336
340
|
};
|
|
337
341
|
}
|
|
338
|
-
/** @type {Map<string, URLPattern>} */
|
|
339
|
-
const urlPatternCache = new Map();
|
|
340
|
-
const URL_PATTERN_CACHE_LIMIT = 128;
|
|
341
|
-
const ABSOLUTE_URL_PATTERN = /^(?:[A-Za-z][A-Za-z\d+.-]*|:[A-Za-z][A-Za-z\d_-]*):\/\//;
|
|
342
|
-
/**
|
|
343
|
-
* URLPattern's base URL affects relative patterns. Absolute patterns only
|
|
344
|
-
* depend on the pattern itself, while root-relative patterns also depend on
|
|
345
|
-
* the URL origin. Other relative patterns are deliberately not cached because
|
|
346
|
-
* their semantics depend on the complete base URL.
|
|
347
|
-
*
|
|
348
|
-
* @param {string} pattern
|
|
349
|
-
* @param {URL} url
|
|
350
|
-
* @returns {URLPattern}
|
|
351
|
-
*/
|
|
352
|
-
function getUrlPattern(pattern, url) {
|
|
353
|
-
const isAbsolutePattern = ABSOLUTE_URL_PATTERN.test(pattern);
|
|
354
|
-
const isRootRelativePattern = pattern.startsWith("/");
|
|
355
|
-
if (!isAbsolutePattern && !isRootRelativePattern) {
|
|
356
|
-
return new URLPattern(pattern, url.href);
|
|
357
|
-
}
|
|
358
|
-
const key = isAbsolutePattern
|
|
359
|
-
? pattern
|
|
360
|
-
: JSON.stringify([url.origin, pattern]);
|
|
361
|
-
const cached = urlPatternCache.get(key);
|
|
362
|
-
if (cached !== undefined) {
|
|
363
|
-
// Refresh the entry so frequently used patterns stay in the bounded cache.
|
|
364
|
-
urlPatternCache.delete(key);
|
|
365
|
-
urlPatternCache.set(key, cached);
|
|
366
|
-
return cached;
|
|
367
|
-
}
|
|
368
|
-
const compiled = new URLPattern(pattern, url.href);
|
|
369
|
-
if (urlPatternCache.size >= URL_PATTERN_CACHE_LIMIT) {
|
|
370
|
-
const oldestKey = urlPatternCache.keys().next().value;
|
|
371
|
-
if (oldestKey !== undefined)
|
|
372
|
-
urlPatternCache.delete(oldestKey);
|
|
373
|
-
}
|
|
374
|
-
urlPatternCache.set(key, compiled);
|
|
375
|
-
return compiled;
|
|
376
|
-
}
|
|
377
342
|
/**
|
|
378
343
|
* A small, deliberately conservative subset of URLPattern routing.
|
|
379
344
|
*
|
|
@@ -395,7 +360,7 @@ function getUrlPattern(pattern, url) {
|
|
|
395
360
|
* localized: Array<{ locale: string; pattern: FastPathPattern }>;
|
|
396
361
|
* }} FastPathRoute
|
|
397
362
|
*/
|
|
398
|
-
/** @type {WeakMap<object, FastPathRoute | null>} */
|
|
363
|
+
/** @type {WeakMap<object, { version: number; route: FastPathRoute | null }>} */
|
|
399
364
|
const fastPathRouteCache = new WeakMap();
|
|
400
365
|
/**
|
|
401
366
|
* @param {URL} urlObj
|
|
@@ -454,24 +419,33 @@ function deLocalizeUrlFastPath(urlObj, element) {
|
|
|
454
419
|
*/
|
|
455
420
|
function getFastPathRoute(element) {
|
|
456
421
|
const cached = fastPathRouteCache.get(element);
|
|
457
|
-
if (cached
|
|
458
|
-
return cached;
|
|
422
|
+
if (cached?.version === currentRoutingConfigVersion())
|
|
423
|
+
return cached.route;
|
|
459
424
|
const base = parseFastPathPattern(element.pattern);
|
|
460
425
|
if (base === undefined) {
|
|
461
|
-
fastPathRouteCache.set(element,
|
|
426
|
+
fastPathRouteCache.set(element, {
|
|
427
|
+
version: currentRoutingConfigVersion(),
|
|
428
|
+
route: null,
|
|
429
|
+
});
|
|
462
430
|
return null;
|
|
463
431
|
}
|
|
464
432
|
const localized = [];
|
|
465
433
|
for (const [locale, pattern] of element.localized) {
|
|
466
434
|
const parsed = parseFastPathPattern(pattern);
|
|
467
435
|
if (parsed === undefined || parsed.pathMode !== base.pathMode) {
|
|
468
|
-
fastPathRouteCache.set(element,
|
|
436
|
+
fastPathRouteCache.set(element, {
|
|
437
|
+
version: currentRoutingConfigVersion(),
|
|
438
|
+
route: null,
|
|
439
|
+
});
|
|
469
440
|
return null;
|
|
470
441
|
}
|
|
471
442
|
localized.push({ locale, pattern: parsed });
|
|
472
443
|
}
|
|
473
444
|
const route = { base, localized };
|
|
474
|
-
fastPathRouteCache.set(element,
|
|
445
|
+
fastPathRouteCache.set(element, {
|
|
446
|
+
version: currentRoutingConfigVersion(),
|
|
447
|
+
route,
|
|
448
|
+
});
|
|
475
449
|
return route;
|
|
476
450
|
}
|
|
477
451
|
/**
|
|
@@ -592,7 +566,8 @@ function matchFastPathPattern(pattern, urlObj) {
|
|
|
592
566
|
return undefined;
|
|
593
567
|
}
|
|
594
568
|
if (pattern.hostname !== undefined) {
|
|
595
|
-
const expectedPort = pattern.port ??
|
|
569
|
+
const expectedPort = pattern.port ??
|
|
570
|
+
defaultPortForProtocol(pattern.protocol ?? urlObj.protocol);
|
|
596
571
|
if (expectedPort !== urlObj.port)
|
|
597
572
|
return undefined;
|
|
598
573
|
}
|
|
@@ -614,8 +589,7 @@ function matchFastPathPattern(pattern, urlObj) {
|
|
|
614
589
|
const suffix = urlObj.pathname.slice(prefix.length);
|
|
615
590
|
if (suffix.startsWith("//"))
|
|
616
591
|
return undefined;
|
|
617
|
-
if (pattern.pathMode === "segments" &&
|
|
618
|
-
!isNonEmptyPathSegments(suffix)) {
|
|
592
|
+
if (pattern.pathMode === "segments" && !isNonEmptyPathSegments(suffix)) {
|
|
619
593
|
return undefined;
|
|
620
594
|
}
|
|
621
595
|
return suffix;
|
|
@@ -627,7 +601,7 @@ function matchFastPathPattern(pattern, urlObj) {
|
|
|
627
601
|
* @returns {boolean}
|
|
628
602
|
*/
|
|
629
603
|
function isNonEmptyPathSegments(pathname) {
|
|
630
|
-
return pathname.length > 1 && !pathname.endsWith("/") && !pathname.includes("//");
|
|
604
|
+
return (pathname.length > 1 && !pathname.endsWith("/") && !pathname.includes("//"));
|
|
631
605
|
}
|
|
632
606
|
/**
|
|
633
607
|
* URLPattern treats the default port as empty in URL instances.
|
|
@@ -654,8 +628,7 @@ function applyFastPathPattern(pattern, suffix, source) {
|
|
|
654
628
|
localized.protocol = pattern.protocol;
|
|
655
629
|
if (pattern.hostname !== undefined) {
|
|
656
630
|
localized.hostname = pattern.hostname;
|
|
657
|
-
localized.port =
|
|
658
|
-
pattern.port ?? defaultPortForProtocol(localized.protocol);
|
|
631
|
+
localized.port = pattern.port ?? defaultPortForProtocol(localized.protocol);
|
|
659
632
|
}
|
|
660
633
|
localized.pathname = joinFastPathPrefix(pattern.pathnamePrefix, suffix);
|
|
661
634
|
return localized;
|