@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.
Files changed (35) hide show
  1. package/dist/compiler/runtime/create-runtime.js +24 -1
  2. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  3. package/dist/compiler/runtime/extract-locale-from-url.js +11 -4
  4. package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
  5. package/dist/compiler/runtime/generate-static-localized-urls.js +5 -3
  6. package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
  7. package/dist/compiler/runtime/localize-href.test.js +37 -0
  8. package/dist/compiler/runtime/localize-href.test.js.map +1 -1
  9. package/dist/compiler/runtime/localize-url.js +29 -56
  10. package/dist/compiler/runtime/localize-url.js.map +1 -1
  11. package/dist/compiler/runtime/localize-url.test.js +15 -0
  12. package/dist/compiler/runtime/localize-url.test.js.map +1 -1
  13. package/dist/compiler/runtime/route-strategy.js +9 -3
  14. package/dist/compiler/runtime/route-strategy.js.map +1 -1
  15. package/dist/compiler/runtime/type.test.js +9 -0
  16. package/dist/compiler/runtime/type.test.js.map +1 -1
  17. package/dist/compiler/runtime/url-pattern-cache.d.ts +11 -0
  18. package/dist/compiler/runtime/url-pattern-cache.js +42 -0
  19. package/dist/compiler/runtime/url-pattern-cache.js.map +1 -0
  20. package/dist/compiler/runtime/url-pattern-index.d.ts +24 -0
  21. package/dist/compiler/runtime/url-pattern-index.differential.test.d.ts +1 -0
  22. package/dist/compiler/runtime/url-pattern-index.differential.test.js +152 -0
  23. package/dist/compiler/runtime/url-pattern-index.differential.test.js.map +1 -0
  24. package/dist/compiler/runtime/url-pattern-index.js +168 -0
  25. package/dist/compiler/runtime/url-pattern-index.js.map +1 -0
  26. package/dist/compiler/runtime/url-pattern-index.test.d.ts +1 -0
  27. package/dist/compiler/runtime/url-pattern-index.test.js +262 -0
  28. package/dist/compiler/runtime/url-pattern-index.test.js.map +1 -0
  29. package/dist/compiler/server/create-server-file.js +1 -1
  30. package/dist/compiler/server/create-server-file.js.map +1 -1
  31. package/dist/compiler/server/create-server-file.test.js +12 -0
  32. package/dist/compiler/server/create-server-file.test.js.map +1 -1
  33. package/dist/services/env-variables/index.js +1 -1
  34. package/dist/services/env-variables/index.js.map +1 -1
  35. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"route-strategy.js","sourceRoot":"","sources":["../../../src/compiler/runtime/route-strategy.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE3D,iCAAiC;AACjC,IAAI,sBAAsB,CAAC;AAC3B,2FAA2F;AAC3F,IAAI,mBAAmB,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAG;IAC5C,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAC3D,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED,MAAM,SAAS,GAAG,sBAAsB,CACvC,IAAI,GAAG,CAAC,SAAS,EAAE,oBAAoB,CAAC,CACxC,CAAC;IACF,MAAM,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,aAAa,GAClB,YAAY,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI;QACnC,CAAC,CAAC,CAAC,SAAS,CAAC;QACb,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC;IACV,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;QAC1C,KAAK,MAAM,aAAa,IAAI,eAAe,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;YACvE,IAAI,cAAc,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,CAAC;gBAC3C,KAAK,GAAG,aAAa,CAAC;gBACtB,MAAM;YACP,CAAC;QACF,CAAC;QACD,IAAI,KAAK;YAAE,MAAM;IAClB,CAAC;IAED,sBAAsB,GAAG,SAAS,CAAC;IACnC,mBAAmB,GAAG,KAAK,CAAC;IAC5B,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAG;IACpC,MAAM,aAAa,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;IACrD,IACC,aAAa;QACb,aAAa,CAAC,OAAO,KAAK,IAAI;QAC9B,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,EACpC,CAAC;QACF,OAAO,aAAa,CAAC,QAAQ,CAAC;IAC/B,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAG;IAC5C,OAAO,yBAAyB,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AACzD,CAAC","sourcesContent":["import { deLocalizeUrl } from \"./localize-url.js\";\nimport { normalizeTrailingSlash } from \"./normalize-trailing-slash.js\";\nimport { execUrlPattern } from \"./exec-url-pattern.js\";\nimport { routeStrategies, strategy } from \"./variables.js\";\n\n/** @type {string | undefined} */\nlet cachedRouteStrategyUrl;\n/** @type {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined} */\nlet cachedRouteStrategy;\n\n/**\n * Match route policy against both the public URL and its canonical URL.\n *\n * The function is deliberately separate from variables.js: configuration is\n * inert data, while canonicalization and route selection form a routing layer.\n *\n * @param {string | URL} url\n * @returns {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined}\n */\nexport function findMatchingRouteStrategy(url) {\n\tif (routeStrategies.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst urlString = typeof url === \"string\" ? url : url.href;\n\tif (cachedRouteStrategyUrl === urlString) {\n\t\treturn cachedRouteStrategy;\n\t}\n\n\tconst publicUrl = normalizeTrailingSlash(\n\t\tnew URL(urlString, \"http://example.com\")\n\t);\n\tconst canonicalUrl = deLocalizeUrl(publicUrl);\n\tconst candidateUrls =\n\t\tcanonicalUrl.href === publicUrl.href\n\t\t\t? [publicUrl]\n\t\t\t: [publicUrl, canonicalUrl];\n\tlet match;\n\tfor (const candidateUrl of candidateUrls) {\n\t\tfor (const routeStrategy of routeStrategies) {\n\t\t\tconst pattern = new URLPattern(routeStrategy.match, candidateUrl.href);\n\t\t\tif (execUrlPattern(pattern, candidateUrl)) {\n\t\t\t\tmatch = routeStrategy;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (match) break;\n\t}\n\n\tcachedRouteStrategyUrl = urlString;\n\tcachedRouteStrategy = match;\n\treturn match;\n}\n\n/**\n * Returns the strategy to use for a specific URL.\n *\n * If no route strategy matches (or the matching rule is `exclude: true`),\n * the global strategy is returned.\n *\n * @param {string | URL} url\n * @returns {typeof strategy}\n */\nexport function getStrategyForUrl(url) {\n\tconst routeStrategy = findMatchingRouteStrategy(url);\n\tif (\n\t\trouteStrategy &&\n\t\trouteStrategy.exclude !== true &&\n\t\tArray.isArray(routeStrategy.strategy)\n\t) {\n\t\treturn routeStrategy.strategy;\n\t}\n\treturn strategy;\n}\n\n/**\n * Returns whether the given URL is excluded from middleware i18n processing.\n *\n * @param {string | URL} url\n * @returns {boolean}\n */\nexport function isExcludedByRouteStrategy(url) {\n\treturn findMatchingRouteStrategy(url)?.exclude === true;\n}\n"]}
1
+ {"version":3,"file":"route-strategy.js","sourceRoot":"","sources":["../../../src/compiler/runtime/route-strategy.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,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,0BAA0B,EAC1B,uBAAuB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE3D,IAAI,wBAAwB,GAAG,CAAC,CAAC,CAAC;AAElC,iCAAiC;AACjC,IAAI,sBAAsB,CAAC;AAC3B,2FAA2F;AAC3F,IAAI,mBAAmB,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAG;IAC5C,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAC3D,MAAM,aAAa,GAAG,uBAAuB,EAAE,CAAC;IAChD,IACC,sBAAsB,KAAK,SAAS;QACpC,wBAAwB,KAAK,aAAa,EACzC,CAAC;QACF,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED,MAAM,SAAS,GAAG,sBAAsB,CACvC,IAAI,GAAG,CAAC,SAAS,EAAE,oBAAoB,CAAC,CACxC,CAAC;IACF,MAAM,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,aAAa,GAClB,YAAY,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI;QACnC,CAAC,CAAC,CAAC,SAAS,CAAC;QACb,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC;IACV,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;QAC1C,KAAK,MAAM,aAAa,IAAI,0BAA0B,CAAC,YAAY,CAAC,EAAE,CAAC;YACtE,MAAM,OAAO,GAAG,aAAa,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;YACjE,IAAI,cAAc,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,CAAC;gBAC3C,KAAK,GAAG,aAAa,CAAC;gBACtB,MAAM;YACP,CAAC;QACF,CAAC;QACD,IAAI,KAAK;YAAE,MAAM;IAClB,CAAC;IAED,wBAAwB,GAAG,aAAa,CAAC;IACzC,sBAAsB,GAAG,SAAS,CAAC;IACnC,mBAAmB,GAAG,KAAK,CAAC;IAC5B,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAG;IACpC,MAAM,aAAa,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;IACrD,IACC,aAAa;QACb,aAAa,CAAC,OAAO,KAAK,IAAI;QAC9B,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,EACpC,CAAC;QACF,OAAO,aAAa,CAAC,QAAQ,CAAC;IAC/B,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAG;IAC5C,OAAO,yBAAyB,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AACzD,CAAC","sourcesContent":["import { deLocalizeUrl } 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 {\n\tgetRouteStrategyCandidates,\n\tgetRoutingConfigVersion,\n} from \"./url-pattern-index.js\";\nimport { routeStrategies, strategy } from \"./variables.js\";\n\nlet cachedRouteConfigVersion = -1;\n\n/** @type {string | undefined} */\nlet cachedRouteStrategyUrl;\n/** @type {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined} */\nlet cachedRouteStrategy;\n\n/**\n * Match route policy against both the public URL and its canonical URL.\n *\n * The function is deliberately separate from variables.js: configuration is\n * inert data, while canonicalization and route selection form a routing layer.\n *\n * @param {string | URL} url\n * @returns {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined}\n */\nexport function findMatchingRouteStrategy(url) {\n\tif (routeStrategies.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst urlString = typeof url === \"string\" ? url : url.href;\n\tconst configVersion = getRoutingConfigVersion();\n\tif (\n\t\tcachedRouteStrategyUrl === urlString &&\n\t\tcachedRouteConfigVersion === configVersion\n\t) {\n\t\treturn cachedRouteStrategy;\n\t}\n\n\tconst publicUrl = normalizeTrailingSlash(\n\t\tnew URL(urlString, \"http://example.com\")\n\t);\n\tconst canonicalUrl = deLocalizeUrl(publicUrl);\n\tconst candidateUrls =\n\t\tcanonicalUrl.href === publicUrl.href\n\t\t\t? [publicUrl]\n\t\t\t: [publicUrl, canonicalUrl];\n\tlet match;\n\tfor (const candidateUrl of candidateUrls) {\n\t\tfor (const routeStrategy of getRouteStrategyCandidates(candidateUrl)) {\n\t\t\tconst pattern = getUrlPattern(routeStrategy.match, candidateUrl);\n\t\t\tif (execUrlPattern(pattern, candidateUrl)) {\n\t\t\t\tmatch = routeStrategy;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (match) break;\n\t}\n\n\tcachedRouteConfigVersion = configVersion;\n\tcachedRouteStrategyUrl = urlString;\n\tcachedRouteStrategy = match;\n\treturn match;\n}\n\n/**\n * Returns the strategy to use for a specific URL.\n *\n * If no route strategy matches (or the matching rule is `exclude: true`),\n * the global strategy is returned.\n *\n * @param {string | URL} url\n * @returns {typeof strategy}\n */\nexport function getStrategyForUrl(url) {\n\tconst routeStrategy = findMatchingRouteStrategy(url);\n\tif (\n\t\trouteStrategy &&\n\t\trouteStrategy.exclude !== true &&\n\t\tArray.isArray(routeStrategy.strategy)\n\t) {\n\t\treturn routeStrategy.strategy;\n\t}\n\treturn strategy;\n}\n\n/**\n * Returns whether the given URL is excluded from middleware i18n processing.\n *\n * @param {string | URL} url\n * @returns {boolean}\n */\nexport function isExcludedByRouteStrategy(url) {\n\treturn findMatchingRouteStrategy(url)?.exclude === true;\n}\n"]}
@@ -49,6 +49,15 @@ test("cookie cache clearer remains internal in the generated runtime", () => {
49
49
  expect(jsdocRuntime).toContain("function clearLocaleCookieCache");
50
50
  expect(jsdocRuntime).not.toContain("export function clearLocaleCookieCache");
51
51
  });
52
+ test("import statements in JSDoc examples survive in the generated runtime", () => {
53
+ const runtime = createRuntimeFile({
54
+ baseLocale: "en",
55
+ locales: ["en", "de"],
56
+ compilerOptions: defaultCompilerOptions,
57
+ });
58
+ expect(runtime).toContain(` * import { m } from './messages.js'\n * m.hello({ name: 'world' }, { locale: "en" })`);
59
+ expect(runtime).not.toMatch(/^\s*import\s/m);
60
+ });
52
61
  test("runtime type", async () => {
53
62
  const project = await typescriptProject({
54
63
  useInMemoryFileSystem: true,
@@ -1 +1 @@
1
- {"version":3,"file":"type.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/type.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"vitest\";\nimport {\n\tcreateProject as typescriptProject,\n\tts,\n\ttype ProjectOptions,\n} from \"@ts-morph/bootstrap\";\nimport { createRuntimeFile } from \"./create-runtime.js\";\nimport fs from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"url\";\nimport { defaultCompilerOptions } from \"../compiler-options.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst superStrictRuleOutAnyErrorTsSettings: ProjectOptions[\"compilerOptions\"] =\n\t{\n\t\toutDir: \"dist\",\n\t\tdeclaration: true,\n\t\tallowJs: true,\n\t\tcheckJs: true,\n\t\tnoImplicitAny: true,\n\t\tnoUnusedLocals: true,\n\t\tnoUnusedParameters: true,\n\t\tnoImplicitReturns: true,\n\t\tnoImplicitThis: true,\n\t\tnoUncheckedIndexedAccess: true,\n\t\tnoPropertyAccessFromIndexSignature: true,\n\t\tmodule: ts.ModuleKind.Node16,\n\t\tstrict: true,\n\t};\n\ntest(\"createRuntimeFile throws if urlPatterns contains an unknown locale\", () => {\n\texpect(() =>\n\t\tcreateRuntimeFile({\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\", \"de\"],\n\t\t\tcompilerOptions: {\n\t\t\t\t...defaultCompilerOptions,\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"https://example.com/:path*\",\n\t\t\t\t\t\tlocalized: [[\"fr\", \"https://example.com/fr/:path*\"]],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t})\n\t).toThrow(\n\t\t'Invalid locale \"fr\" in urlPatterns. It must be one of the locales defined in the \"locales\" array.'\n\t);\n});\n\ntest(\"cookie cache clearer remains internal in the generated runtime\", () => {\n\tconst jsdocRuntime = createRuntimeFile({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tcompilerOptions: {\n\t\t\t...defaultCompilerOptions,\n\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t},\n\t});\n\n\texpect(jsdocRuntime).toContain(\"function clearLocaleCookieCache\");\n\texpect(jsdocRuntime).not.toContain(\"export function clearLocaleCookieCache\");\n});\n\ntest(\"runtime type\", async () => {\n\tconst project = await typescriptProject({\n\t\tuseInMemoryFileSystem: true,\n\t\tcompilerOptions: superStrictRuleOutAnyErrorTsSettings,\n\t});\n\n\tconst jsdocRuntime = createRuntimeFile({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\"],\n\t\tcompilerOptions: defaultCompilerOptions,\n\t}).replace(\n\t\t'import * as pathToRegexp from \"@inlang/paraglide-js/path-to-regexp\";',\n\t\t\"\"\n\t);\n\n\tconst file = (path: string) => {\n\t\treturn [path, fs.readFileSync(resolve(__dirname, path), \"utf-8\")!] as const;\n\t};\n\n\tproject.createSourceFile(\"./runtime.js\", jsdocRuntime);\n\tproject.createSourceFile(...file(\"./type.ts\"));\n\tproject.createSourceFile(...file(\"./ambient.d.ts\"));\n\n\t// add the imports for the types in the runtime type\n\tfor (const name of fs.readdirSync(__dirname)) {\n\t\tif (name.endsWith(\".js\")) {\n\t\t\tproject.createSourceFile(...file(name));\n\t\t}\n\t}\n\n\tproject.createSourceFile(\n\t\t\"./test.ts\",\n\t\t`\n import * as runtime from \"./runtime.js\"\n import type { Runtime } from \"./type.js\"\n\n runtime satisfies Runtime\n\n\t\t// getLocale() should return type should be a union of language tags, not a generic string\n runtime.getLocale() satisfies \"de\" | \"en\" | \"en-US\"\n\n // locales should have a narrow type, not a generic string\n runtime.locales satisfies Readonly<Array<\"de\" | \"en\" | \"en-US\">>\n\n // setLocale() should fail if the given language tag is not included in locales\n // @ts-expect-error - invalid locale\n runtime.setLocale(\"fr\")\n\n // setLocale() should not fail if the given language tag is included in locales\n runtime.setLocale(\"de\")\n\n\t\t// getTextDirection() should return a strict direction union\n\t\truntime.getTextDirection() satisfies \"ltr\" | \"rtl\"\n\n\t\t// isLocale should narrow the type of it's argument\n\t\tconst thing = 5;\n\n\t\tlet a: \"de\" | \"en\" | \"en-US\";\n\n\t\tif(runtime.isLocale(thing)) {\n\t\t\ta = thing\n\t\t} else {\n\t\t\t// @ts-expect-error - thing is not a language tag\n\t\t\ta = thing\n\t\t}\n\n\t\t// to make ts not complain about unused variables\n\t\truntime.toLocale(\"EN\") satisfies \"de\" | \"en\" | \"en-US\" | undefined\n\t\truntime.toLocale(123) satisfies \"de\" | \"en\" | \"en-US\" | undefined\n\n\t\tconsole.log(a)\n `\n\t);\n\n\tconst program = project.createProgram();\n\tconst diagnostics = ts\n\t\t.getPreEmitDiagnostics(program)\n\t\t// ignore 'export' modifier cannot be applied to ambient modules and module augmentations since they are always visible. /ambient.d.ts\n\t\t.filter((d) => d.code !== 2668)\n\t\t.filter((d) => d.messageText.toString().includes(\"path-to-regexp\"));\n\tfor (const diagnostic of diagnostics) {\n\t\tconsole.error(diagnostic.messageText, diagnostic.file?.fileName);\n\t}\n\texpect(diagnostics.length).toEqual(0);\n});\n"]}
1
+ {"version":3,"file":"type.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/type.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"vitest\";\nimport {\n\tcreateProject as typescriptProject,\n\tts,\n\ttype ProjectOptions,\n} from \"@ts-morph/bootstrap\";\nimport { createRuntimeFile } from \"./create-runtime.js\";\nimport fs from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"url\";\nimport { defaultCompilerOptions } from \"../compiler-options.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst superStrictRuleOutAnyErrorTsSettings: ProjectOptions[\"compilerOptions\"] =\n\t{\n\t\toutDir: \"dist\",\n\t\tdeclaration: true,\n\t\tallowJs: true,\n\t\tcheckJs: true,\n\t\tnoImplicitAny: true,\n\t\tnoUnusedLocals: true,\n\t\tnoUnusedParameters: true,\n\t\tnoImplicitReturns: true,\n\t\tnoImplicitThis: true,\n\t\tnoUncheckedIndexedAccess: true,\n\t\tnoPropertyAccessFromIndexSignature: true,\n\t\tmodule: ts.ModuleKind.Node16,\n\t\tstrict: true,\n\t};\n\ntest(\"createRuntimeFile throws if urlPatterns contains an unknown locale\", () => {\n\texpect(() =>\n\t\tcreateRuntimeFile({\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\", \"de\"],\n\t\t\tcompilerOptions: {\n\t\t\t\t...defaultCompilerOptions,\n\t\t\t\turlPatterns: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpattern: \"https://example.com/:path*\",\n\t\t\t\t\t\tlocalized: [[\"fr\", \"https://example.com/fr/:path*\"]],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t})\n\t).toThrow(\n\t\t'Invalid locale \"fr\" in urlPatterns. It must be one of the locales defined in the \"locales\" array.'\n\t);\n});\n\ntest(\"cookie cache clearer remains internal in the generated runtime\", () => {\n\tconst jsdocRuntime = createRuntimeFile({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tcompilerOptions: {\n\t\t\t...defaultCompilerOptions,\n\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t},\n\t});\n\n\texpect(jsdocRuntime).toContain(\"function clearLocaleCookieCache\");\n\texpect(jsdocRuntime).not.toContain(\"export function clearLocaleCookieCache\");\n});\n\ntest(\"import statements in JSDoc examples survive in the generated runtime\", () => {\n\tconst runtime = createRuntimeFile({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\", \"de\"],\n\t\tcompilerOptions: defaultCompilerOptions,\n\t});\n\n\texpect(runtime).toContain(\n\t\t` * import { m } from './messages.js'\\n * m.hello({ name: 'world' }, { locale: \"en\" })`\n\t);\n\texpect(runtime).not.toMatch(/^\\s*import\\s/m);\n});\n\ntest(\"runtime type\", async () => {\n\tconst project = await typescriptProject({\n\t\tuseInMemoryFileSystem: true,\n\t\tcompilerOptions: superStrictRuleOutAnyErrorTsSettings,\n\t});\n\n\tconst jsdocRuntime = createRuntimeFile({\n\t\tbaseLocale: \"en\",\n\t\tlocales: [\"en\"],\n\t\tcompilerOptions: defaultCompilerOptions,\n\t}).replace(\n\t\t'import * as pathToRegexp from \"@inlang/paraglide-js/path-to-regexp\";',\n\t\t\"\"\n\t);\n\n\tconst file = (path: string) => {\n\t\treturn [path, fs.readFileSync(resolve(__dirname, path), \"utf-8\")!] as const;\n\t};\n\n\tproject.createSourceFile(\"./runtime.js\", jsdocRuntime);\n\tproject.createSourceFile(...file(\"./type.ts\"));\n\tproject.createSourceFile(...file(\"./ambient.d.ts\"));\n\n\t// add the imports for the types in the runtime type\n\tfor (const name of fs.readdirSync(__dirname)) {\n\t\tif (name.endsWith(\".js\")) {\n\t\t\tproject.createSourceFile(...file(name));\n\t\t}\n\t}\n\n\tproject.createSourceFile(\n\t\t\"./test.ts\",\n\t\t`\n import * as runtime from \"./runtime.js\"\n import type { Runtime } from \"./type.js\"\n\n runtime satisfies Runtime\n\n\t\t// getLocale() should return type should be a union of language tags, not a generic string\n runtime.getLocale() satisfies \"de\" | \"en\" | \"en-US\"\n\n // locales should have a narrow type, not a generic string\n runtime.locales satisfies Readonly<Array<\"de\" | \"en\" | \"en-US\">>\n\n // setLocale() should fail if the given language tag is not included in locales\n // @ts-expect-error - invalid locale\n runtime.setLocale(\"fr\")\n\n // setLocale() should not fail if the given language tag is included in locales\n runtime.setLocale(\"de\")\n\n\t\t// getTextDirection() should return a strict direction union\n\t\truntime.getTextDirection() satisfies \"ltr\" | \"rtl\"\n\n\t\t// isLocale should narrow the type of it's argument\n\t\tconst thing = 5;\n\n\t\tlet a: \"de\" | \"en\" | \"en-US\";\n\n\t\tif(runtime.isLocale(thing)) {\n\t\t\ta = thing\n\t\t} else {\n\t\t\t// @ts-expect-error - thing is not a language tag\n\t\t\ta = thing\n\t\t}\n\n\t\t// to make ts not complain about unused variables\n\t\truntime.toLocale(\"EN\") satisfies \"de\" | \"en\" | \"en-US\" | undefined\n\t\truntime.toLocale(123) satisfies \"de\" | \"en\" | \"en-US\" | undefined\n\n\t\tconsole.log(a)\n `\n\t);\n\n\tconst program = project.createProgram();\n\tconst diagnostics = ts\n\t\t.getPreEmitDiagnostics(program)\n\t\t// ignore 'export' modifier cannot be applied to ambient modules and module augmentations since they are always visible. /ambient.d.ts\n\t\t.filter((d) => d.code !== 2668)\n\t\t.filter((d) => d.messageText.toString().includes(\"path-to-regexp\"));\n\tfor (const diagnostic of diagnostics) {\n\t\tconsole.error(diagnostic.messageText, diagnostic.file?.fileName);\n\t}\n\texpect(diagnostics.length).toEqual(0);\n});\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * URLPattern's base URL affects relative patterns. Absolute patterns only
3
+ * depend on the pattern itself, while root-relative patterns also depend on
4
+ * the URL origin. Other relative patterns are deliberately not cached because
5
+ * their semantics depend on the complete base URL.
6
+ *
7
+ * @param {string} pattern
8
+ * @param {URL} url
9
+ * @returns {URLPattern}
10
+ */
11
+ export declare function getUrlPattern(pattern: string, url: URL): URLPattern;
@@ -0,0 +1,42 @@
1
+ /** @type {Map<string, URLPattern>} */
2
+ const urlPatternCache = new Map();
3
+ const URL_PATTERN_CACHE_LIMIT = 1024;
4
+ const ABSOLUTE_URL_PATTERN = /^(?:[A-Za-z][A-Za-z\d+.-]*|:[A-Za-z][A-Za-z\d_-]*):\/\//;
5
+ /**
6
+ * URLPattern's base URL affects relative patterns. Absolute patterns only
7
+ * depend on the pattern itself, while root-relative patterns also depend on
8
+ * the URL origin. Other relative patterns are deliberately not cached because
9
+ * their semantics depend on the complete base URL.
10
+ *
11
+ * @param {string} pattern
12
+ * @param {URL} url
13
+ * @returns {URLPattern}
14
+ */
15
+ export function getUrlPattern(pattern, url) {
16
+ const isAbsolutePattern = ABSOLUTE_URL_PATTERN.test(pattern);
17
+ // Opaque/file origins are not a unique base key (different file hosts all
18
+ // have origin "null"). Keep their constructor semantics uncached.
19
+ const isRootRelativePattern = pattern.startsWith("/") && /^https?:$/.test(url.protocol);
20
+ if (!isAbsolutePattern && !isRootRelativePattern) {
21
+ return new URLPattern(pattern, url.href);
22
+ }
23
+ const key = isAbsolutePattern
24
+ ? pattern
25
+ : JSON.stringify([url.origin, pattern]);
26
+ const cached = urlPatternCache.get(key);
27
+ if (cached !== undefined) {
28
+ // Refresh the entry so frequently used patterns stay in the bounded cache.
29
+ urlPatternCache.delete(key);
30
+ urlPatternCache.set(key, cached);
31
+ return cached;
32
+ }
33
+ const compiled = new URLPattern(pattern, url.href);
34
+ if (urlPatternCache.size >= URL_PATTERN_CACHE_LIMIT) {
35
+ const oldestKey = urlPatternCache.keys().next().value;
36
+ if (oldestKey !== undefined)
37
+ urlPatternCache.delete(oldestKey);
38
+ }
39
+ urlPatternCache.set(key, compiled);
40
+ return compiled;
41
+ }
42
+ //# sourceMappingURL=url-pattern-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-pattern-cache.js","sourceRoot":"","sources":["../../../src/compiler/runtime/url-pattern-cache.js"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,MAAM,eAAe,GAAG,IAAI,GAAG,EAAE,CAAC;AAElC,MAAM,uBAAuB,GAAG,IAAI,CAAC;AACrC,MAAM,oBAAoB,GACzB,yDAAyD,CAAC;AAE3D;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,OAAO,EAAE,GAAG;IACzC,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7D,0EAA0E;IAC1E,kEAAkE;IAClE,MAAM,qBAAqB,GAC1B,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC3D,IAAI,CAAC,iBAAiB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAClD,OAAO,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,GAAG,GAAG,iBAAiB;QAC5B,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1B,2EAA2E;QAC3E,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,eAAe,CAAC,IAAI,IAAI,uBAAuB,EAAE,CAAC;QACrD,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QACtD,IAAI,SAAS,KAAK,SAAS;YAAE,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;IACD,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACnC,OAAO,QAAQ,CAAC;AACjB,CAAC","sourcesContent":["/** @type {Map<string, URLPattern>} */\nconst urlPatternCache = new Map();\n\nconst URL_PATTERN_CACHE_LIMIT = 1024;\nconst ABSOLUTE_URL_PATTERN =\n\t/^(?:[A-Za-z][A-Za-z\\d+.-]*|:[A-Za-z][A-Za-z\\d_-]*):\\/\\//;\n\n/**\n * URLPattern's base URL affects relative patterns. Absolute patterns only\n * depend on the pattern itself, while root-relative patterns also depend on\n * the URL origin. Other relative patterns are deliberately not cached because\n * their semantics depend on the complete base URL.\n *\n * @param {string} pattern\n * @param {URL} url\n * @returns {URLPattern}\n */\nexport function getUrlPattern(pattern, url) {\n\tconst isAbsolutePattern = ABSOLUTE_URL_PATTERN.test(pattern);\n\t// Opaque/file origins are not a unique base key (different file hosts all\n\t// have origin \"null\"). Keep their constructor semantics uncached.\n\tconst isRootRelativePattern =\n\t\tpattern.startsWith(\"/\") && /^https?:$/.test(url.protocol);\n\tif (!isAbsolutePattern && !isRootRelativePattern) {\n\t\treturn new URLPattern(pattern, url.href);\n\t}\n\n\tconst key = isAbsolutePattern\n\t\t? pattern\n\t\t: JSON.stringify([url.origin, pattern]);\n\tconst cached = urlPatternCache.get(key);\n\tif (cached !== undefined) {\n\t\t// Refresh the entry so frequently used patterns stay in the bounded cache.\n\t\turlPatternCache.delete(key);\n\t\turlPatternCache.set(key, cached);\n\t\treturn cached;\n\t}\n\n\tconst compiled = new URLPattern(pattern, url.href);\n\tif (urlPatternCache.size >= URL_PATTERN_CACHE_LIMIT) {\n\t\tconst oldestKey = urlPatternCache.keys().next().value;\n\t\tif (oldestKey !== undefined) urlPatternCache.delete(oldestKey);\n\t}\n\turlPatternCache.set(key, compiled);\n\treturn compiled;\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import { urlPatterns, routeStrategies } from "./variables.js";
2
+ export type PrefixNode = {
3
+ children: Map<string, PrefixNode>;
4
+ ranks: Set<number>;
5
+ };
6
+ export type PatternIndex = {
7
+ items: any[];
8
+ snapshots: any[];
9
+ root: PrefixNode;
10
+ };
11
+ /**
12
+ * Validate the public mutable configuration before reusing derived routing data.
13
+ * String/reference comparisons intentionally remain O(n): they preserve edits
14
+ * through retained array/object references without changing the public objects.
15
+ * Native matching of unrelated routes is the expensive work this index avoids.
16
+ * @returns {number}
17
+ */
18
+ export declare function getRoutingConfigVersion(): number;
19
+ /** @returns {number} */
20
+ export declare function currentRoutingConfigVersion(): number;
21
+ /** @param {URL} url @returns {typeof urlPatterns} */
22
+ export declare function getUrlPatternCandidates(url: URL): typeof urlPatterns;
23
+ /** @param {URL} url @returns {typeof routeStrategies} */
24
+ export declare function getRouteStrategyCandidates(url: URL): typeof routeStrategies;
@@ -0,0 +1,152 @@
1
+ import { expect, test } from "vitest";
2
+ import { URLPattern as PolyfillURLPattern } from "urlpattern-polyfill";
3
+ import { createRuntimeFile } from "./create-runtime.js";
4
+ import { defaultCompilerOptions } from "../compiler-options.js";
5
+ const engines = [PolyfillURLPattern];
6
+ const nativeURLPattern = Reflect.get(globalThis, "URLPattern");
7
+ if (nativeURLPattern && nativeURLPattern !== PolyfillURLPattern) {
8
+ engines.push(nativeURLPattern);
9
+ }
10
+ let moduleId = 0;
11
+ const patterns = [
12
+ "/section/:slug",
13
+ "/section/:slug?",
14
+ "/section/:slug*",
15
+ "/section/:slug+",
16
+ "/section/:slug(.*)?",
17
+ "/section/:slug(.*)",
18
+ "/section/literal",
19
+ "/section/:slug/details",
20
+ "/section/:a/:b?",
21
+ "/section/:slug(\\d+)",
22
+ "/section/{literal}?",
23
+ "/section%2Fliteral/:slug",
24
+ "/section//:slug",
25
+ "/section/../other/:slug",
26
+ "https://example.com/section/:slug",
27
+ "section/:slug",
28
+ "/section/:slug/:slug",
29
+ "/section/:slug([)",
30
+ ];
31
+ const paths = [
32
+ "/",
33
+ "/section",
34
+ "/section/",
35
+ "/section/literal",
36
+ "/section/story",
37
+ "/section/123",
38
+ "/section/story/details",
39
+ "/section/story/other",
40
+ "/section//story",
41
+ "/section%2Fliteral/story",
42
+ "/other/story",
43
+ "/unknown",
44
+ ];
45
+ function outcome(fn) {
46
+ try {
47
+ const result = fn();
48
+ return {
49
+ value: result instanceof URL
50
+ ? result.href
51
+ : Array.isArray(result)
52
+ ? result.map((url) => url.href)
53
+ : result,
54
+ };
55
+ }
56
+ catch (error) {
57
+ return {
58
+ error: error instanceof Error ? [error.name, error.message] : String(error),
59
+ };
60
+ }
61
+ }
62
+ for (const ctor of engines) {
63
+ test(`${ctor === PolyfillURLPattern ? "polyfill" : "native"} candidate index agrees with native ordered scans`, async () => {
64
+ const previous = Reflect.get(globalThis, "URLPattern");
65
+ Object.defineProperty(globalThis, "URLPattern", {
66
+ configurable: true,
67
+ writable: true,
68
+ value: ctor,
69
+ });
70
+ let comparisons = 0;
71
+ try {
72
+ for (const pattern of patterns) {
73
+ for (const trailingSlash of [undefined, "always", "never"]) {
74
+ for (const catchAllFirst of [false, true]) {
75
+ const specific = {
76
+ pattern,
77
+ localized: [
78
+ ["en", pattern],
79
+ ["de", `/de${pattern}`],
80
+ ],
81
+ };
82
+ const catchAll = {
83
+ pattern: "/:path(.*)?",
84
+ localized: [
85
+ ["de", "/de/:path(.*)?"],
86
+ ["en", "/:path(.*)?"],
87
+ ],
88
+ };
89
+ const code = createRuntimeFile({
90
+ baseLocale: "en",
91
+ locales: ["en", "de", "fr"],
92
+ compilerOptions: {
93
+ ...defaultCompilerOptions,
94
+ strategy: ["url", "baseLocale"],
95
+ trailingSlash,
96
+ urlPatterns: catchAllFirst
97
+ ? [catchAll, specific]
98
+ : [specific, catchAll],
99
+ routeStrategies: [
100
+ { match: pattern, exclude: true },
101
+ { match: "/:path(.*)?", strategy: ["url"] },
102
+ ],
103
+ },
104
+ }).replace('import "@inlang/paraglide-js/urlpattern-polyfill";', "");
105
+ // The reference keeps native construction/matching and all filling
106
+ // logic, but enumerates the original arrays without pruning.
107
+ const scan = code
108
+ .replace("return patternCandidates(urlPatternIndex, url, urlPatterns);", "return urlPatterns;")
109
+ .replace("return patternCandidates(routeStrategyIndex, url, routeStrategies);", "return routeStrategies;");
110
+ expect(scan).not.toBe(code);
111
+ expect(scan).not.toContain("return patternCandidates(");
112
+ const load = (source) => import(
113
+ /* @vite-ignore */ "data:text/javascript;base64," +
114
+ Buffer.from(source + `\n// isolated fixture ${moduleId++}`).toString("base64"));
115
+ const indexed = await load(code);
116
+ const original = await load(scan);
117
+ for (const pathname of paths.flatMap((path) => [
118
+ path,
119
+ `/de${path}`,
120
+ ])) {
121
+ for (const asObject of [false, true]) {
122
+ const href = `https://user:pass@example.com${pathname}?redirect=%2Faccount#section`;
123
+ const input = asObject ? new URL(href) : href;
124
+ const operations = [
125
+ (runtime) => runtime.localizeUrl(input, { locale: "de" }),
126
+ (runtime) => runtime.localizeUrl(input, { locale: "fr" }),
127
+ (runtime) => runtime.deLocalizeUrl(input),
128
+ (runtime) => runtime.extractLocaleFromUrl(input),
129
+ (runtime) => runtime.findMatchingRouteStrategy(input),
130
+ (runtime) => runtime.generateStaticLocalizedUrls([input]),
131
+ ];
132
+ for (const operation of operations) {
133
+ expect(outcome(() => operation(indexed)), `${pattern}, slash=${trailingSlash}, catchAllFirst=${catchAllFirst}, ${href}`).toEqual(outcome(() => operation(original)));
134
+ comparisons++;
135
+ }
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ expect(comparisons).toBe(31104);
142
+ }
143
+ finally {
144
+ Object.defineProperty(globalThis, "URLPattern", {
145
+ configurable: true,
146
+ writable: true,
147
+ value: previous,
148
+ });
149
+ }
150
+ }, 30000);
151
+ }
152
+ //# sourceMappingURL=url-pattern-index.differential.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-pattern-index.differential.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/url-pattern-index.differential.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,IAAI,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,OAAO,GAAG,CAAC,kBAAkD,CAAC,CAAC;AACrE,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,YAAY,CAEjD,CAAC;AACb,IAAI,gBAAgB,IAAI,gBAAgB,KAAK,kBAAkB,EAAE,CAAC;IACjE,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AAChC,CAAC;AACD,IAAI,QAAQ,GAAG,CAAC,CAAC;AAEjB,MAAM,QAAQ,GAAG;IAChB,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,iBAAiB;IACjB,qBAAqB;IACrB,oBAAoB;IACpB,kBAAkB;IAClB,wBAAwB;IACxB,iBAAiB;IACjB,sBAAsB;IACtB,qBAAqB;IACrB,0BAA0B;IAC1B,iBAAiB;IACjB,yBAAyB;IACzB,mCAAmC;IACnC,eAAe;IACf,sBAAsB;IACtB,mBAAmB;CACnB,CAAC;AACF,MAAM,KAAK,GAAG;IACb,GAAG;IACH,UAAU;IACV,WAAW;IACX,kBAAkB;IAClB,gBAAgB;IAChB,cAAc;IACd,wBAAwB;IACxB,sBAAsB;IACtB,iBAAiB;IACjB,0BAA0B;IAC1B,cAAc;IACd,UAAU;CACV,CAAC;AAEF,SAAS,OAAO,CAAC,EAAiB;IACjC,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,EAAE,EAAE,CAAC;QACpB,OAAO;YACN,KAAK,EACJ,MAAM,YAAY,GAAG;gBACpB,CAAC,CAAC,MAAM,CAAC,IAAI;gBACb,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;oBACtB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;oBAC/B,CAAC,CAAC,MAAM;SACX,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO;YACN,KAAK,EACJ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SACrE,CAAC;IACH,CAAC;AACF,CAAC;AAED,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,mDAAmD,EAAE,KAAK,IAAI,EAAE;QAC1H,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACvD,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,YAAY,EAAE;YAC/C,YAAY,EAAE,IAAI;YAClB,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,IAAI;SACX,CAAC,CAAC;QACH,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC;YACJ,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAChC,KAAK,MAAM,aAAa,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAU,EAAE,CAAC;oBACrE,KAAK,MAAM,aAAa,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;wBAC3C,MAAM,QAAQ,GAAG;4BAChB,OAAO;4BACP,SAAS,EAAE;gCACV,CAAC,IAAI,EAAE,OAAO,CAAC;gCACf,CAAC,IAAI,EAAE,MAAM,OAAO,EAAE,CAAC;6BACI;yBAC5B,CAAC;wBACF,MAAM,QAAQ,GAAG;4BAChB,OAAO,EAAE,aAAa;4BACtB,SAAS,EAAE;gCACV,CAAC,IAAI,EAAE,gBAAgB,CAAC;gCACxB,CAAC,IAAI,EAAE,aAAa,CAAC;6BACM;yBAC5B,CAAC;wBACF,MAAM,IAAI,GAAG,iBAAiB,CAAC;4BAC9B,UAAU,EAAE,IAAI;4BAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;4BAC3B,eAAe,EAAE;gCAChB,GAAG,sBAAsB;gCACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC;gCAC/B,aAAa;gCACb,WAAW,EAAE,aAAa;oCACzB,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC;oCACtB,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC;gCACvB,eAAe,EAAE;oCAChB,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;oCACjC,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE;iCAC3C;6BACD;yBACD,CAAC,CAAC,OAAO,CACT,oDAAoD,EACpD,EAAE,CACF,CAAC;wBACF,mEAAmE;wBACnE,6DAA6D;wBAC7D,MAAM,IAAI,GAAG,IAAI;6BACf,OAAO,CACP,8DAA8D,EAC9D,qBAAqB,CACrB;6BACA,OAAO,CACP,qEAAqE,EACrE,yBAAyB,CACzB,CAAC;wBACH,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBAC5B,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;wBACxD,MAAM,IAAI,GAAG,CAAC,MAAc,EAAE,EAAE,CAC/B,MAAM;wBACL,kBAAkB,CAAC,8BAA8B;4BAChD,MAAM,CAAC,IAAI,CACV,MAAM,GAAG,yBAAyB,QAAQ,EAAE,EAAE,CAC9C,CAAC,QAAQ,CAAC,QAAQ,CAAC,CACrB,CAAC;wBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;wBACjC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;wBAClC,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;4BAC9C,IAAI;4BACJ,MAAM,IAAI,EAAE;yBACZ,CAAC,EAAE,CAAC;4BACJ,KAAK,MAAM,QAAQ,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;gCACtC,MAAM,IAAI,GAAG,gCAAgC,QAAQ,8BAA8B,CAAC;gCACpF,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gCAC9C,MAAM,UAAU,GAAG;oCAClB,CAAC,OAAuB,EAAE,EAAE,CAC3B,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;oCAC7C,CAAC,OAAuB,EAAE,EAAE,CAC3B,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;oCAC7C,CAAC,OAAuB,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC;oCACzD,CAAC,OAAuB,EAAE,EAAE,CAC3B,OAAO,CAAC,oBAAoB,CAAC,KAAK,CAAC;oCACpC,CAAC,OAAuB,EAAE,EAAE,CAC3B,OAAO,CAAC,yBAAyB,CAAC,KAAK,CAAC;oCACzC,CAAC,OAAuB,EAAE,EAAE,CAC3B,OAAO,CAAC,2BAA2B,CAAC,CAAC,KAAK,CAAC,CAAC;iCAC7C,CAAC;gCACF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;oCACpC,MAAM,CACL,OAAO,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EACjC,GAAG,OAAO,WAAW,aAAa,mBAAmB,aAAa,KAAK,IAAI,EAAE,CAC7E,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oCAC9C,WAAW,EAAE,CAAC;gCACf,CAAC;4BACF,CAAC;wBACF,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;YACD,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;gBAAS,CAAC;YACV,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,YAAY,EAAE;gBAC/C,YAAY,EAAE,IAAI;gBAClB,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,QAAQ;aACf,CAAC,CAAC;QACJ,CAAC;IACF,CAAC,EAAE,KAAK,CAAC,CAAC;AACX,CAAC","sourcesContent":["import { expect, test } from \"vitest\";\nimport { URLPattern as PolyfillURLPattern } from \"urlpattern-polyfill\";\nimport { createRuntimeFile } from \"./create-runtime.js\";\nimport { defaultCompilerOptions } from \"../compiler-options.js\";\n\nconst engines = [PolyfillURLPattern as unknown as typeof URLPattern];\nconst nativeURLPattern = Reflect.get(globalThis, \"URLPattern\") as\n\t| typeof URLPattern\n\t| undefined;\nif (nativeURLPattern && nativeURLPattern !== PolyfillURLPattern) {\n\tengines.push(nativeURLPattern);\n}\nlet moduleId = 0;\n\nconst patterns = [\n\t\"/section/:slug\",\n\t\"/section/:slug?\",\n\t\"/section/:slug*\",\n\t\"/section/:slug+\",\n\t\"/section/:slug(.*)?\",\n\t\"/section/:slug(.*)\",\n\t\"/section/literal\",\n\t\"/section/:slug/details\",\n\t\"/section/:a/:b?\",\n\t\"/section/:slug(\\\\d+)\",\n\t\"/section/{literal}?\",\n\t\"/section%2Fliteral/:slug\",\n\t\"/section//:slug\",\n\t\"/section/../other/:slug\",\n\t\"https://example.com/section/:slug\",\n\t\"section/:slug\",\n\t\"/section/:slug/:slug\",\n\t\"/section/:slug([)\",\n];\nconst paths = [\n\t\"/\",\n\t\"/section\",\n\t\"/section/\",\n\t\"/section/literal\",\n\t\"/section/story\",\n\t\"/section/123\",\n\t\"/section/story/details\",\n\t\"/section/story/other\",\n\t\"/section//story\",\n\t\"/section%2Fliteral/story\",\n\t\"/other/story\",\n\t\"/unknown\",\n];\n\nfunction outcome(fn: () => unknown) {\n\ttry {\n\t\tconst result = fn();\n\t\treturn {\n\t\t\tvalue:\n\t\t\t\tresult instanceof URL\n\t\t\t\t\t? result.href\n\t\t\t\t\t: Array.isArray(result)\n\t\t\t\t\t\t? result.map((url) => url.href)\n\t\t\t\t\t\t: result,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\terror:\n\t\t\t\terror instanceof Error ? [error.name, error.message] : String(error),\n\t\t};\n\t}\n}\n\nfor (const ctor of engines) {\n\ttest(`${ctor === PolyfillURLPattern ? \"polyfill\" : \"native\"} candidate index agrees with native ordered scans`, async () => {\n\t\tconst previous = Reflect.get(globalThis, \"URLPattern\");\n\t\tObject.defineProperty(globalThis, \"URLPattern\", {\n\t\t\tconfigurable: true,\n\t\t\twritable: true,\n\t\t\tvalue: ctor,\n\t\t});\n\t\tlet comparisons = 0;\n\t\ttry {\n\t\t\tfor (const pattern of patterns) {\n\t\t\t\tfor (const trailingSlash of [undefined, \"always\", \"never\"] as const) {\n\t\t\t\t\tfor (const catchAllFirst of [false, true]) {\n\t\t\t\t\t\tconst specific = {\n\t\t\t\t\t\t\tpattern,\n\t\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t\t[\"en\", pattern],\n\t\t\t\t\t\t\t\t[\"de\", `/de${pattern}`],\n\t\t\t\t\t\t\t] as Array<[string, string]>,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst catchAll = {\n\t\t\t\t\t\t\tpattern: \"/:path(.*)?\",\n\t\t\t\t\t\t\tlocalized: [\n\t\t\t\t\t\t\t\t[\"de\", \"/de/:path(.*)?\"],\n\t\t\t\t\t\t\t\t[\"en\", \"/:path(.*)?\"],\n\t\t\t\t\t\t\t] as Array<[string, string]>,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst code = createRuntimeFile({\n\t\t\t\t\t\t\tbaseLocale: \"en\",\n\t\t\t\t\t\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\t\t\t\t\t\tcompilerOptions: {\n\t\t\t\t\t\t\t\t...defaultCompilerOptions,\n\t\t\t\t\t\t\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\t\t\t\t\t\t\ttrailingSlash,\n\t\t\t\t\t\t\t\turlPatterns: catchAllFirst\n\t\t\t\t\t\t\t\t\t? [catchAll, specific]\n\t\t\t\t\t\t\t\t\t: [specific, catchAll],\n\t\t\t\t\t\t\t\trouteStrategies: [\n\t\t\t\t\t\t\t\t\t{ match: pattern, exclude: true },\n\t\t\t\t\t\t\t\t\t{ match: \"/:path(.*)?\", strategy: [\"url\"] },\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}).replace(\n\t\t\t\t\t\t\t'import \"@inlang/paraglide-js/urlpattern-polyfill\";',\n\t\t\t\t\t\t\t\"\"\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// The reference keeps native construction/matching and all filling\n\t\t\t\t\t\t// logic, but enumerates the original arrays without pruning.\n\t\t\t\t\t\tconst scan = code\n\t\t\t\t\t\t\t.replace(\n\t\t\t\t\t\t\t\t\"return patternCandidates(urlPatternIndex, url, urlPatterns);\",\n\t\t\t\t\t\t\t\t\"return urlPatterns;\"\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.replace(\n\t\t\t\t\t\t\t\t\"return patternCandidates(routeStrategyIndex, url, routeStrategies);\",\n\t\t\t\t\t\t\t\t\"return routeStrategies;\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\texpect(scan).not.toBe(code);\n\t\t\t\t\t\texpect(scan).not.toContain(\"return patternCandidates(\");\n\t\t\t\t\t\tconst load = (source: string) =>\n\t\t\t\t\t\t\timport(\n\t\t\t\t\t\t\t\t/* @vite-ignore */ \"data:text/javascript;base64,\" +\n\t\t\t\t\t\t\t\t\tBuffer.from(\n\t\t\t\t\t\t\t\t\t\tsource + `\\n// isolated fixture ${moduleId++}`\n\t\t\t\t\t\t\t\t\t).toString(\"base64\")\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tconst indexed = await load(code);\n\t\t\t\t\t\tconst original = await load(scan);\n\t\t\t\t\t\tfor (const pathname of paths.flatMap((path) => [\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t`/de${path}`,\n\t\t\t\t\t\t])) {\n\t\t\t\t\t\t\tfor (const asObject of [false, true]) {\n\t\t\t\t\t\t\t\tconst href = `https://user:pass@example.com${pathname}?redirect=%2Faccount#section`;\n\t\t\t\t\t\t\t\tconst input = asObject ? new URL(href) : href;\n\t\t\t\t\t\t\t\tconst operations = [\n\t\t\t\t\t\t\t\t\t(runtime: typeof indexed) =>\n\t\t\t\t\t\t\t\t\t\truntime.localizeUrl(input, { locale: \"de\" }),\n\t\t\t\t\t\t\t\t\t(runtime: typeof indexed) =>\n\t\t\t\t\t\t\t\t\t\truntime.localizeUrl(input, { locale: \"fr\" }),\n\t\t\t\t\t\t\t\t\t(runtime: typeof indexed) => runtime.deLocalizeUrl(input),\n\t\t\t\t\t\t\t\t\t(runtime: typeof indexed) =>\n\t\t\t\t\t\t\t\t\t\truntime.extractLocaleFromUrl(input),\n\t\t\t\t\t\t\t\t\t(runtime: typeof indexed) =>\n\t\t\t\t\t\t\t\t\t\truntime.findMatchingRouteStrategy(input),\n\t\t\t\t\t\t\t\t\t(runtime: typeof indexed) =>\n\t\t\t\t\t\t\t\t\t\truntime.generateStaticLocalizedUrls([input]),\n\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\tfor (const operation of operations) {\n\t\t\t\t\t\t\t\t\texpect(\n\t\t\t\t\t\t\t\t\t\toutcome(() => operation(indexed)),\n\t\t\t\t\t\t\t\t\t\t`${pattern}, slash=${trailingSlash}, catchAllFirst=${catchAllFirst}, ${href}`\n\t\t\t\t\t\t\t\t\t).toEqual(outcome(() => operation(original)));\n\t\t\t\t\t\t\t\t\tcomparisons++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\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\texpect(comparisons).toBe(31104);\n\t\t} finally {\n\t\t\tObject.defineProperty(globalThis, \"URLPattern\", {\n\t\t\t\tconfigurable: true,\n\t\t\t\twritable: true,\n\t\t\t\tvalue: previous,\n\t\t\t});\n\t\t}\n\t}, 30000);\n}\n"]}
@@ -0,0 +1,168 @@
1
+ import { urlPatterns, routeStrategies } from "./variables.js";
2
+ /** @typedef {{ children: Map<string, PrefixNode>, ranks: Set<number> }} PrefixNode */
3
+ /** @typedef {{ items: any[], snapshots: any[], root: PrefixNode }} PatternIndex */
4
+ /** @type {PatternIndex | undefined} */
5
+ let urlPatternIndex;
6
+ /** @type {PatternIndex | undefined} */
7
+ let routeStrategyIndex;
8
+ let routingConfigVersion = 0;
9
+ /**
10
+ * Validate the public mutable configuration before reusing derived routing data.
11
+ * String/reference comparisons intentionally remain O(n): they preserve edits
12
+ * through retained array/object references without changing the public objects.
13
+ * Native matching of unrelated routes is the expensive work this index avoids.
14
+ * @returns {number}
15
+ */
16
+ export function getRoutingConfigVersion() {
17
+ if (!indexIsCurrent(urlPatternIndex, urlPatterns, false)) {
18
+ urlPatternIndex = buildPatternIndex(urlPatterns, false);
19
+ routingConfigVersion++;
20
+ }
21
+ if (!indexIsCurrent(routeStrategyIndex, routeStrategies, true)) {
22
+ routeStrategyIndex = buildPatternIndex(routeStrategies, true);
23
+ routingConfigVersion++;
24
+ }
25
+ return routingConfigVersion;
26
+ }
27
+ /** @returns {number} */
28
+ export function currentRoutingConfigVersion() {
29
+ return routingConfigVersion;
30
+ }
31
+ /** @param {URL} url @returns {typeof urlPatterns} */
32
+ export function getUrlPatternCandidates(url) {
33
+ getRoutingConfigVersion();
34
+ return patternCandidates(urlPatternIndex, url, urlPatterns);
35
+ }
36
+ /** @param {URL} url @returns {typeof routeStrategies} */
37
+ export function getRouteStrategyCandidates(url) {
38
+ getRoutingConfigVersion();
39
+ return patternCandidates(routeStrategyIndex, url, routeStrategies);
40
+ }
41
+ /**
42
+ * Only a complete, conservative root-relative grammar is eligible. Unknown
43
+ * syntax stays unconditional, including invalid patterns whose constructor
44
+ * errors must remain observable. Stop indexing at the first dynamic segment.
45
+ * @param {string} pattern
46
+ * @returns {string[]}
47
+ */
48
+ function mandatoryPathSegments(pattern) {
49
+ if (typeof pattern !== "string" ||
50
+ !pattern.startsWith("/") ||
51
+ pattern.startsWith("//"))
52
+ return [];
53
+ const segments = pattern.slice(1).split("/");
54
+ const names = new Set();
55
+ const literal = /^[A-Za-z0-9_-]+$/;
56
+ const parameter = /^:([A-Za-z_][A-Za-z0-9_]*)(?:[?*+]|\(\.\*\)\??)?$/;
57
+ for (let i = 0; i < segments.length; i++) {
58
+ const segment = segments[i] ?? "";
59
+ if (segment === "" && i === segments.length - 1)
60
+ continue;
61
+ if (literal.test(segment))
62
+ continue;
63
+ const match = parameter.exec(segment);
64
+ if (!match || names.has(match[1]))
65
+ return [];
66
+ names.add(match[1]);
67
+ }
68
+ const prefix = [];
69
+ for (const segment of segments) {
70
+ if (!literal.test(segment))
71
+ break;
72
+ prefix.push(segment);
73
+ }
74
+ return prefix;
75
+ }
76
+ /** @param {PatternIndex | undefined} index @param {any[]} items @param {boolean} policies */
77
+ function indexIsCurrent(index, items, policies) {
78
+ if (!index ||
79
+ index.items !== items ||
80
+ index.snapshots.length !== items.length)
81
+ return false;
82
+ for (let i = 0; i < items.length; i++) {
83
+ const item = items[i];
84
+ const saved = index.snapshots[i];
85
+ if (item !== saved.item ||
86
+ (policies ? item?.match : item?.pattern) !== saved.pattern)
87
+ return false;
88
+ if (policies)
89
+ continue;
90
+ const localized = item?.localized;
91
+ if (!Array.isArray(localized) ||
92
+ saved.localized !== localized ||
93
+ saved.entries.length !== localized.length)
94
+ return false;
95
+ for (let j = 0; j < localized.length; j++) {
96
+ const entry = localized[j];
97
+ const previous = saved.entries[j];
98
+ if (entry !== previous.entry ||
99
+ entry?.[0] !== previous.locale ||
100
+ entry?.[1] !== previous.pattern)
101
+ return false;
102
+ }
103
+ }
104
+ return true;
105
+ }
106
+ /** @param {any[]} items @param {boolean} policies @returns {PatternIndex} */
107
+ function buildPatternIndex(items, policies) {
108
+ /** @type {PrefixNode} */
109
+ const root = { children: new Map(), ranks: new Set() };
110
+ const snapshots = [];
111
+ for (let rank = 0; rank < items.length; rank++) {
112
+ const item = items[rank];
113
+ const pattern = policies ? item?.match : item?.pattern;
114
+ const localized = policies ? undefined : item?.localized;
115
+ const entries = Array.isArray(localized)
116
+ ? Array.from(localized, (entry) => ({
117
+ entry,
118
+ locale: entry?.[0],
119
+ pattern: entry?.[1],
120
+ }))
121
+ : [];
122
+ snapshots.push({ item, pattern, localized, entries });
123
+ const paths = [pattern, ...entries.map((entry) => entry.pattern)].map(mandatoryPathSegments);
124
+ // A malformed element must also remain in the original native scan.
125
+ if ((!policies &&
126
+ (!Array.isArray(localized) ||
127
+ entries.some(({ entry }) => !Array.isArray(entry)))) ||
128
+ paths.some((path) => path.length === 0)) {
129
+ root.ranks.add(rank);
130
+ continue;
131
+ }
132
+ for (const path of paths) {
133
+ let node = root;
134
+ for (const segment of path) {
135
+ let child = node.children.get(segment);
136
+ if (!child) {
137
+ child = { children: new Map(), ranks: new Set() };
138
+ node.children.set(segment, child);
139
+ }
140
+ node = child;
141
+ }
142
+ node.ranks.add(rank);
143
+ }
144
+ }
145
+ return { items, snapshots, root };
146
+ }
147
+ /** @param {PatternIndex | undefined} index @param {URL} url @param {any[]} items @returns {any[]} */
148
+ function patternCandidates(index, url, items) {
149
+ // Root-relative constructor/base behavior for other schemes is deliberately
150
+ // left to URLPattern, as are configurations with no prunable prefixes.
151
+ if (!index ||
152
+ !/^https?:$/.test(url.protocol) ||
153
+ index.root.ranks.size === items.length)
154
+ return items;
155
+ const ranks = new Set(index.root.ranks);
156
+ let node = index.root;
157
+ for (const segment of url.pathname.slice(1).split("/")) {
158
+ const child = node.children.get(segment);
159
+ if (!child)
160
+ break;
161
+ node = child;
162
+ for (const rank of node.ranks)
163
+ ranks.add(rank);
164
+ }
165
+ // Specificity must never change precedence, including unconditional patterns.
166
+ return [...ranks].sort((a, b) => a - b).map((rank) => items[rank]);
167
+ }
168
+ //# sourceMappingURL=url-pattern-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-pattern-index.js","sourceRoot":"","sources":["../../../src/compiler/runtime/url-pattern-index.js"],"names":[],"mappings":"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","sourcesContent":["import { urlPatterns, routeStrategies } from \"./variables.js\";\n\n/** @typedef {{ children: Map<string, PrefixNode>, ranks: Set<number> }} PrefixNode */\n/** @typedef {{ items: any[], snapshots: any[], root: PrefixNode }} PatternIndex */\n\n/** @type {PatternIndex | undefined} */\nlet urlPatternIndex;\n/** @type {PatternIndex | undefined} */\nlet routeStrategyIndex;\nlet routingConfigVersion = 0;\n\n/**\n * Validate the public mutable configuration before reusing derived routing data.\n * String/reference comparisons intentionally remain O(n): they preserve edits\n * through retained array/object references without changing the public objects.\n * Native matching of unrelated routes is the expensive work this index avoids.\n * @returns {number}\n */\nexport function getRoutingConfigVersion() {\n\tif (!indexIsCurrent(urlPatternIndex, urlPatterns, false)) {\n\t\turlPatternIndex = buildPatternIndex(urlPatterns, false);\n\t\troutingConfigVersion++;\n\t}\n\tif (!indexIsCurrent(routeStrategyIndex, routeStrategies, true)) {\n\t\trouteStrategyIndex = buildPatternIndex(routeStrategies, true);\n\t\troutingConfigVersion++;\n\t}\n\treturn routingConfigVersion;\n}\n\n/** @returns {number} */\nexport function currentRoutingConfigVersion() {\n\treturn routingConfigVersion;\n}\n\n/** @param {URL} url @returns {typeof urlPatterns} */\nexport function getUrlPatternCandidates(url) {\n\tgetRoutingConfigVersion();\n\treturn patternCandidates(urlPatternIndex, url, urlPatterns);\n}\n\n/** @param {URL} url @returns {typeof routeStrategies} */\nexport function getRouteStrategyCandidates(url) {\n\tgetRoutingConfigVersion();\n\treturn patternCandidates(routeStrategyIndex, url, routeStrategies);\n}\n\n/**\n * Only a complete, conservative root-relative grammar is eligible. Unknown\n * syntax stays unconditional, including invalid patterns whose constructor\n * errors must remain observable. Stop indexing at the first dynamic segment.\n * @param {string} pattern\n * @returns {string[]}\n */\nfunction mandatoryPathSegments(pattern) {\n\tif (\n\t\ttypeof pattern !== \"string\" ||\n\t\t!pattern.startsWith(\"/\") ||\n\t\tpattern.startsWith(\"//\")\n\t)\n\t\treturn [];\n\tconst segments = pattern.slice(1).split(\"/\");\n\tconst names = new Set();\n\tconst literal = /^[A-Za-z0-9_-]+$/;\n\tconst parameter = /^:([A-Za-z_][A-Za-z0-9_]*)(?:[?*+]|\\(\\.\\*\\)\\??)?$/;\n\tfor (let i = 0; i < segments.length; i++) {\n\t\tconst segment = segments[i] ?? \"\";\n\t\tif (segment === \"\" && i === segments.length - 1) continue;\n\t\tif (literal.test(segment)) continue;\n\t\tconst match = parameter.exec(segment);\n\t\tif (!match || names.has(match[1])) return [];\n\t\tnames.add(match[1]);\n\t}\n\tconst prefix = [];\n\tfor (const segment of segments) {\n\t\tif (!literal.test(segment)) break;\n\t\tprefix.push(segment);\n\t}\n\treturn prefix;\n}\n\n/** @param {PatternIndex | undefined} index @param {any[]} items @param {boolean} policies */\nfunction indexIsCurrent(index, items, policies) {\n\tif (\n\t\t!index ||\n\t\tindex.items !== items ||\n\t\tindex.snapshots.length !== items.length\n\t)\n\t\treturn false;\n\tfor (let i = 0; i < items.length; i++) {\n\t\tconst item = items[i];\n\t\tconst saved = index.snapshots[i];\n\t\tif (\n\t\t\titem !== saved.item ||\n\t\t\t(policies ? item?.match : item?.pattern) !== saved.pattern\n\t\t)\n\t\t\treturn false;\n\t\tif (policies) continue;\n\t\tconst localized = item?.localized;\n\t\tif (\n\t\t\t!Array.isArray(localized) ||\n\t\t\tsaved.localized !== localized ||\n\t\t\tsaved.entries.length !== localized.length\n\t\t)\n\t\t\treturn false;\n\t\tfor (let j = 0; j < localized.length; j++) {\n\t\t\tconst entry = localized[j];\n\t\t\tconst previous = saved.entries[j];\n\t\t\tif (\n\t\t\t\tentry !== previous.entry ||\n\t\t\t\tentry?.[0] !== previous.locale ||\n\t\t\t\tentry?.[1] !== previous.pattern\n\t\t\t)\n\t\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/** @param {any[]} items @param {boolean} policies @returns {PatternIndex} */\nfunction buildPatternIndex(items, policies) {\n\t/** @type {PrefixNode} */\n\tconst root = { children: new Map(), ranks: new Set() };\n\tconst snapshots = [];\n\tfor (let rank = 0; rank < items.length; rank++) {\n\t\tconst item = items[rank];\n\t\tconst pattern = policies ? item?.match : item?.pattern;\n\t\tconst localized = policies ? undefined : item?.localized;\n\t\tconst entries = Array.isArray(localized)\n\t\t\t? Array.from(localized, (entry) => ({\n\t\t\t\t\tentry,\n\t\t\t\t\tlocale: entry?.[0],\n\t\t\t\t\tpattern: entry?.[1],\n\t\t\t\t}))\n\t\t\t: [];\n\t\tsnapshots.push({ item, pattern, localized, entries });\n\t\tconst paths = [pattern, ...entries.map((entry) => entry.pattern)].map(\n\t\t\tmandatoryPathSegments\n\t\t);\n\t\t// A malformed element must also remain in the original native scan.\n\t\tif (\n\t\t\t(!policies &&\n\t\t\t\t(!Array.isArray(localized) ||\n\t\t\t\t\tentries.some(({ entry }) => !Array.isArray(entry)))) ||\n\t\t\tpaths.some((path) => path.length === 0)\n\t\t) {\n\t\t\troot.ranks.add(rank);\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const path of paths) {\n\t\t\tlet node = root;\n\t\t\tfor (const segment of path) {\n\t\t\t\tlet child = node.children.get(segment);\n\t\t\t\tif (!child) {\n\t\t\t\t\tchild = { children: new Map(), ranks: new Set() };\n\t\t\t\t\tnode.children.set(segment, child);\n\t\t\t\t}\n\t\t\t\tnode = child;\n\t\t\t}\n\t\t\tnode.ranks.add(rank);\n\t\t}\n\t}\n\treturn { items, snapshots, root };\n}\n\n/** @param {PatternIndex | undefined} index @param {URL} url @param {any[]} items @returns {any[]} */\nfunction patternCandidates(index, url, items) {\n\t// Root-relative constructor/base behavior for other schemes is deliberately\n\t// left to URLPattern, as are configurations with no prunable prefixes.\n\tif (\n\t\t!index ||\n\t\t!/^https?:$/.test(url.protocol) ||\n\t\tindex.root.ranks.size === items.length\n\t)\n\t\treturn items;\n\tconst ranks = new Set(index.root.ranks);\n\tlet node = index.root;\n\tfor (const segment of url.pathname.slice(1).split(\"/\")) {\n\t\tconst child = node.children.get(segment);\n\t\tif (!child) break;\n\t\tnode = child;\n\t\tfor (const rank of node.ranks) ranks.add(rank);\n\t}\n\t// Specificity must never change precedence, including unconditional patterns.\n\treturn [...ranks].sort((a, b) => a - b).map((rank) => items[rank]);\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};