gt 2.22.3 → 2.22.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.22.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2347](https://github.com/generaltranslation/gt/pull/2347) [`f3f7051`](https://github.com/generaltranslation/gt/commit/f3f7051c0e27ae4b6521805e610164c2a768d168) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Skip localizing root index paths when file doesn't exist
8
+
3
9
  ## 2.22.3
4
10
 
5
11
  ### Patch Changes
@@ -8,7 +8,7 @@ import { applyStructuralTransforms, unapplyStructuralTransforms } from "../../js
8
8
  import { createFileMapping } from "../fileMapping.js";
9
9
  import { validateYamlSchema } from "../../yaml/utils.js";
10
10
  import { transformValue } from "../../json/mergeJson.js";
11
- import { PAGE_EXTENSIONS } from "../../../utils/localizeStaticUrls.js";
11
+ import { INDEX_PAGE, PAGE_EXTENSIONS } from "../../../utils/localizeStaticUrls.js";
12
12
  import path from "node:path";
13
13
  import fs from "node:fs";
14
14
  import YAML from "yaml";
@@ -89,9 +89,13 @@ function restorePagePaths(source, target, transform, locale, defaultLocale, sour
89
89
  }
90
90
  return changed;
91
91
  }
92
- /** Whether a path value names an .md/.mdx page, with or without extension. */
92
+ /**
93
+ * Whether a path value names an .md/.mdx page, with or without extension. An
94
+ * empty value or one ending in `/` names a folder's index page
95
+ */
93
96
  function pageExists(value, dir) {
94
97
  const filePath = path.join(dir, value);
98
+ if (value === "" || value.endsWith("/")) return PAGE_EXTENSIONS.some((ext) => fs.existsSync(path.join(filePath, `${INDEX_PAGE}${ext}`)));
95
99
  if (PAGE_EXTENSIONS.includes(path.extname(filePath))) return fs.existsSync(filePath);
96
100
  return PAGE_EXTENSIONS.some((ext) => fs.existsSync(`${filePath}${ext}`));
97
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"keepUntranslatedPagePaths.js","names":[],"sources":["../../../../src/formats/files/postprocess/keepUntranslatedPagePaths.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport type { Settings, TransformOptions } from '../../../types/index.js';\nimport type { JSONValue } from '../../../types/data/json.js';\nimport { getRelative } from '../../../fs/findFilepath.js';\nimport { PAGE_EXTENSIONS } from '../../../utils/localizeStaticUrls.js';\nimport { getConfiguredLocaleProperties } from '../../utils.js';\nimport { getJSONPathMatches } from '../../json/jsonPath.js';\nimport {\n getJSONPointerValue,\n setJSONPointerValue,\n} from '../../json/jsonPointer.js';\nimport { transformValue } from '../../json/mergeJson.js';\nimport {\n applyStructuralTransforms,\n unapplyStructuralTransforms,\n} from '../../json/transformJson.js';\nimport { api } from '../../../utils/api.js';\nimport {\n findMatchingItemArray,\n findMatchingItemObject,\n generateSourceObjectPointers,\n validateJsonSchema,\n} from '../../json/utils.js';\nimport { validateYamlSchema } from '../../yaml/utils.js';\nimport { createFileMapping } from '../fileMapping.js';\n\n/**\n * With `skipUntranslatedPages`, undo `transform` rewrites that point a\n * localized JSON or YAML file (such as a docs navigation file) at a page that\n * was not translated. A value is restored to its source form when the source\n * value names an existing .md/.mdx page and the transformed value does not.\n * Values that are not page paths are left alone. Runs after all downloads, so\n * every translated page from this run is on disk.\n */\nexport function keepUntranslatedPagePaths(\n settings: Settings,\n includeFiles?: Set<string>\n): void {\n const options = settings.options;\n const localizeOptions = options?.experimentalLocalizeStaticUrls;\n if (\n !options ||\n typeof localizeOptions !== 'object' ||\n localizeOptions.skipUntranslatedPages !== true ||\n !settings.files\n ) {\n return;\n }\n const { defaultLocale } = settings;\n const targetLocales = settings.locales.filter((l) => l !== defaultLocale);\n const isIncluded = (filePath: string) =>\n !includeFiles ||\n includeFiles.has(filePath) ||\n includeFiles.has(getRelative(filePath));\n\n // Composite JSON: every locale's entry lives in the source file itself.\n for (const filePath of settings.files.resolvedPaths.json ?? []) {\n const schema = validateJsonSchema(options, filePath);\n if (!schema?.composite || !isIncluded(filePath)) continue;\n const json = JSON.parse(fs.readFileSync(filePath, 'utf8')) as JSONValue;\n const dir = path.dirname(filePath);\n // Match entries the way mergeJson wrote them: same structural transforms\n // and the same locale keys.\n if (schema.structuralTransform) {\n applyStructuralTransforms(\n json,\n schema.structuralTransform,\n schema.composite\n );\n }\n const localeKey = (locale: string) =>\n options.experimentalCanonicalLocaleKeys\n ? api.resolveCanonicalLocale(locale)\n : locale;\n let changed = false;\n const sourceObjects = generateSourceObjectPointers(schema.composite, json);\n for (const [\n pointer,\n { sourceObjectValue, sourceObjectOptions },\n ] of Object.entries(sourceObjects)) {\n if (!sourceObjectOptions.transform) continue;\n // Every entry for a locale, in order. mergeJson builds one target entry\n // per default-locale entry in the same order, so they pair by position.\n const entriesFor = (locale: string): JSONValue[] => {\n if (Array.isArray(sourceObjectValue)) {\n return Object.values(\n findMatchingItemArray(\n localeKey(locale),\n sourceObjectOptions,\n pointer,\n sourceObjectValue\n )\n )\n .sort((a, b) => a.index - b.index)\n .map(({ sourceItem }) => sourceItem);\n }\n const { sourceItem } = findMatchingItemObject(\n localeKey(locale),\n pointer,\n sourceObjectOptions,\n sourceObjectValue as Record<string, JSONValue>\n );\n return sourceItem === undefined ? [] : [sourceItem];\n };\n const sources = entriesFor(defaultLocale);\n for (const locale of targetLocales) {\n entriesFor(locale).forEach((target, i) => {\n if (sources[i] === undefined) return;\n changed =\n restorePagePaths(\n sources[i],\n target,\n sourceObjectOptions.transform!,\n locale,\n defaultLocale,\n dir,\n dir\n ) || changed;\n });\n }\n }\n if (changed) {\n if (schema.structuralTransform) {\n unapplyStructuralTransforms(\n json,\n schema.structuralTransform,\n schema.composite\n );\n }\n fs.writeFileSync(filePath, JSON.stringify(json, null, 2));\n }\n }\n\n // YAML: each locale has its own output file, mapped from the source file.\n const yamlSources = settings.files.resolvedPaths.yaml ?? [];\n if (!yamlSources.length) return;\n const fileMapping = createFileMapping(\n settings.files.resolvedPaths,\n settings.files.placeholderPaths,\n settings.files.transformPaths,\n settings.files.transformFormats,\n settings.locales,\n defaultLocale\n );\n for (const filePath of yamlSources) {\n const schema = validateYamlSchema(options, filePath);\n if (!schema?.transform) continue;\n const source = YAML.parse(fs.readFileSync(filePath, 'utf8')) as JSONValue;\n for (const locale of targetLocales) {\n const outputPath = fileMapping[locale]?.[getRelative(filePath)];\n if (!outputPath || !fs.existsSync(outputPath) || !isIncluded(outputPath))\n continue;\n const target = YAML.parse(\n fs.readFileSync(outputPath, 'utf8')\n ) as JSONValue;\n const restored = restorePagePaths(\n source,\n target,\n schema.transform,\n locale,\n defaultLocale,\n path.dirname(filePath),\n path.dirname(outputPath)\n );\n if (restored) fs.writeFileSync(outputPath, YAML.stringify(target));\n }\n }\n}\n\n/**\n * For each value a transform produced in `target` from the value at the same\n * position in `source`, restore the source page path when the transformed\n * page does not exist. Returns whether anything changed.\n */\nfunction restorePagePaths(\n source: JSONValue,\n target: JSONValue,\n transform: TransformOptions,\n locale: string,\n defaultLocale: string,\n sourceDir: string,\n targetDir: string\n): boolean {\n const targetLocaleProperties = getConfiguredLocaleProperties(locale);\n const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);\n let changed = false;\n for (const [transformPath, transformOption] of Object.entries(transform)) {\n for (const match of getJSONPathMatches(source, transformPath) ?? []) {\n if (typeof match.value !== 'string') continue;\n const transformed = transformValue(\n match.value,\n transformOption,\n targetLocaleProperties,\n defaultLocaleProperties\n );\n if (\n transformed === match.value ||\n getJSONPointerValue(target, match.pointer) !== transformed ||\n pageExists(transformed, targetDir) ||\n !pageExists(match.value, sourceDir)\n ) {\n continue;\n }\n const restored =\n sourceDir === targetDir\n ? match.value\n : path\n .relative(targetDir, path.join(sourceDir, match.value))\n .split(path.sep)\n .join('/');\n setJSONPointerValue(target, match.pointer, restored);\n changed = true;\n }\n }\n return changed;\n}\n\n/** Whether a path value names an .md/.mdx page, with or without extension. */\nfunction pageExists(value: string, dir: string): boolean {\n const filePath = path.join(dir, value);\n if (PAGE_EXTENSIONS.includes(path.extname(filePath))) {\n return fs.existsSync(filePath);\n }\n return PAGE_EXTENSIONS.some((ext) => fs.existsSync(`${filePath}${ext}`));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"keepUntranslatedPagePaths.js","names":[],"sources":["../../../../src/formats/files/postprocess/keepUntranslatedPagePaths.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport type { Settings, TransformOptions } from '../../../types/index.js';\nimport type { JSONValue } from '../../../types/data/json.js';\nimport { getRelative } from '../../../fs/findFilepath.js';\nimport {\n INDEX_PAGE,\n PAGE_EXTENSIONS,\n} from '../../../utils/localizeStaticUrls.js';\nimport { getConfiguredLocaleProperties } from '../../utils.js';\nimport { getJSONPathMatches } from '../../json/jsonPath.js';\nimport {\n getJSONPointerValue,\n setJSONPointerValue,\n} from '../../json/jsonPointer.js';\nimport { transformValue } from '../../json/mergeJson.js';\nimport {\n applyStructuralTransforms,\n unapplyStructuralTransforms,\n} from '../../json/transformJson.js';\nimport { api } from '../../../utils/api.js';\nimport {\n findMatchingItemArray,\n findMatchingItemObject,\n generateSourceObjectPointers,\n validateJsonSchema,\n} from '../../json/utils.js';\nimport { validateYamlSchema } from '../../yaml/utils.js';\nimport { createFileMapping } from '../fileMapping.js';\n\n/**\n * With `skipUntranslatedPages`, undo `transform` rewrites that point a\n * localized JSON or YAML file (such as a docs navigation file) at a page that\n * was not translated. A value is restored to its source form when the source\n * value names an existing .md/.mdx page and the transformed value does not.\n * Values that are not page paths are left alone. Runs after all downloads, so\n * every translated page from this run is on disk.\n */\nexport function keepUntranslatedPagePaths(\n settings: Settings,\n includeFiles?: Set<string>\n): void {\n const options = settings.options;\n const localizeOptions = options?.experimentalLocalizeStaticUrls;\n if (\n !options ||\n typeof localizeOptions !== 'object' ||\n localizeOptions.skipUntranslatedPages !== true ||\n !settings.files\n ) {\n return;\n }\n const { defaultLocale } = settings;\n const targetLocales = settings.locales.filter((l) => l !== defaultLocale);\n const isIncluded = (filePath: string) =>\n !includeFiles ||\n includeFiles.has(filePath) ||\n includeFiles.has(getRelative(filePath));\n\n // Composite JSON: every locale's entry lives in the source file itself.\n for (const filePath of settings.files.resolvedPaths.json ?? []) {\n const schema = validateJsonSchema(options, filePath);\n if (!schema?.composite || !isIncluded(filePath)) continue;\n const json = JSON.parse(fs.readFileSync(filePath, 'utf8')) as JSONValue;\n const dir = path.dirname(filePath);\n // Match entries the way mergeJson wrote them: same structural transforms\n // and the same locale keys.\n if (schema.structuralTransform) {\n applyStructuralTransforms(\n json,\n schema.structuralTransform,\n schema.composite\n );\n }\n const localeKey = (locale: string) =>\n options.experimentalCanonicalLocaleKeys\n ? api.resolveCanonicalLocale(locale)\n : locale;\n let changed = false;\n const sourceObjects = generateSourceObjectPointers(schema.composite, json);\n for (const [\n pointer,\n { sourceObjectValue, sourceObjectOptions },\n ] of Object.entries(sourceObjects)) {\n if (!sourceObjectOptions.transform) continue;\n // Every entry for a locale, in order. mergeJson builds one target entry\n // per default-locale entry in the same order, so they pair by position.\n const entriesFor = (locale: string): JSONValue[] => {\n if (Array.isArray(sourceObjectValue)) {\n return Object.values(\n findMatchingItemArray(\n localeKey(locale),\n sourceObjectOptions,\n pointer,\n sourceObjectValue\n )\n )\n .sort((a, b) => a.index - b.index)\n .map(({ sourceItem }) => sourceItem);\n }\n const { sourceItem } = findMatchingItemObject(\n localeKey(locale),\n pointer,\n sourceObjectOptions,\n sourceObjectValue as Record<string, JSONValue>\n );\n return sourceItem === undefined ? [] : [sourceItem];\n };\n const sources = entriesFor(defaultLocale);\n for (const locale of targetLocales) {\n entriesFor(locale).forEach((target, i) => {\n if (sources[i] === undefined) return;\n changed =\n restorePagePaths(\n sources[i],\n target,\n sourceObjectOptions.transform!,\n locale,\n defaultLocale,\n dir,\n dir\n ) || changed;\n });\n }\n }\n if (changed) {\n if (schema.structuralTransform) {\n unapplyStructuralTransforms(\n json,\n schema.structuralTransform,\n schema.composite\n );\n }\n fs.writeFileSync(filePath, JSON.stringify(json, null, 2));\n }\n }\n\n // YAML: each locale has its own output file, mapped from the source file.\n const yamlSources = settings.files.resolvedPaths.yaml ?? [];\n if (!yamlSources.length) return;\n const fileMapping = createFileMapping(\n settings.files.resolvedPaths,\n settings.files.placeholderPaths,\n settings.files.transformPaths,\n settings.files.transformFormats,\n settings.locales,\n defaultLocale\n );\n for (const filePath of yamlSources) {\n const schema = validateYamlSchema(options, filePath);\n if (!schema?.transform) continue;\n const source = YAML.parse(fs.readFileSync(filePath, 'utf8')) as JSONValue;\n for (const locale of targetLocales) {\n const outputPath = fileMapping[locale]?.[getRelative(filePath)];\n if (!outputPath || !fs.existsSync(outputPath) || !isIncluded(outputPath))\n continue;\n const target = YAML.parse(\n fs.readFileSync(outputPath, 'utf8')\n ) as JSONValue;\n const restored = restorePagePaths(\n source,\n target,\n schema.transform,\n locale,\n defaultLocale,\n path.dirname(filePath),\n path.dirname(outputPath)\n );\n if (restored) fs.writeFileSync(outputPath, YAML.stringify(target));\n }\n }\n}\n\n/**\n * For each value a transform produced in `target` from the value at the same\n * position in `source`, restore the source page path when the transformed\n * page does not exist. Returns whether anything changed.\n */\nfunction restorePagePaths(\n source: JSONValue,\n target: JSONValue,\n transform: TransformOptions,\n locale: string,\n defaultLocale: string,\n sourceDir: string,\n targetDir: string\n): boolean {\n const targetLocaleProperties = getConfiguredLocaleProperties(locale);\n const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);\n let changed = false;\n for (const [transformPath, transformOption] of Object.entries(transform)) {\n for (const match of getJSONPathMatches(source, transformPath) ?? []) {\n if (typeof match.value !== 'string') continue;\n const transformed = transformValue(\n match.value,\n transformOption,\n targetLocaleProperties,\n defaultLocaleProperties\n );\n if (\n transformed === match.value ||\n getJSONPointerValue(target, match.pointer) !== transformed ||\n pageExists(transformed, targetDir) ||\n !pageExists(match.value, sourceDir)\n ) {\n continue;\n }\n const restored =\n sourceDir === targetDir\n ? match.value\n : path\n .relative(targetDir, path.join(sourceDir, match.value))\n .split(path.sep)\n .join('/');\n setJSONPointerValue(target, match.pointer, restored);\n changed = true;\n }\n }\n return changed;\n}\n\n/**\n * Whether a path value names an .md/.mdx page, with or without extension. An\n * empty value or one ending in `/` names a folder's index page\n */\nfunction pageExists(value: string, dir: string): boolean {\n const filePath = path.join(dir, value);\n if (value === '' || value.endsWith('/')) {\n return PAGE_EXTENSIONS.some((ext) =>\n fs.existsSync(path.join(filePath, `${INDEX_PAGE}${ext}`))\n );\n }\n if (PAGE_EXTENSIONS.includes(path.extname(filePath))) {\n return fs.existsSync(filePath);\n }\n return PAGE_EXTENSIONS.some((ext) => fs.existsSync(`${filePath}${ext}`));\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.22.3";
1
+ export declare const PACKAGE_VERSION = "2.22.4";
@@ -1,5 +1,5 @@
1
1
  //#region src/generated/version.ts
2
- const PACKAGE_VERSION = "2.22.3";
2
+ const PACKAGE_VERSION = "2.22.4";
3
3
  //#endregion
4
4
  export { PACKAGE_VERSION };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.22.3';\n"],"mappings":";AACA,MAAa,kBAAkB"}
1
+ {"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.22.4';\n"],"mappings":";AACA,MAAa,kBAAkB"}
@@ -1,5 +1,6 @@
1
1
  import type { StaticLocalizationSettings } from '../types/index.js';
2
2
  export declare const PAGE_EXTENSIONS: string[];
3
+ export declare const INDEX_PAGE = "index";
3
4
  export type StaticUrlSettings = StaticLocalizationSettings;
4
5
  /**
5
6
  * Localizes static urls in content files.
@@ -381,6 +381,6 @@ function checkIfPathMatchesPattern(originalUrlArray, patternHeadArray) {
381
381
  return true;
382
382
  }
383
383
  //#endregion
384
- export { PAGE_EXTENSIONS, localizeStaticUrls as default, transformUrlPath };
384
+ export { INDEX_PAGE, PAGE_EXTENSIONS, localizeStaticUrls as default, transformUrlPath };
385
385
 
386
386
  //# sourceMappingURL=localizeStaticUrls.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"localizeStaticUrls.js","names":["path","fs","parseBabel"],"sources":["../../src/utils/localizeStaticUrls.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport type { StaticLocalizationSettings } from '../types/index.js';\nimport { createFileMapping } from '../formats/files/fileMapping.js';\nimport micromatch from 'micromatch';\nimport { unified } from 'unified';\nimport remarkMdx from 'remark-mdx';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport remarkStringify from 'remark-stringify';\nimport { visit } from 'unist-util-visit';\nimport type { Root, Link, Literal } from 'mdast';\nimport type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx';\nimport { escapeHtmlInTextNodes, normalizeCJKCharacters } from 'gt-remark';\nimport { parse as parseBabel } from '@babel/parser';\nimport { parseMdxForRoundTrip, restoreAnchorIds } from './mdxAnchorSyntax.js';\n\nconst { isMatch } = micromatch;\n\n/**\n * URL-bearing JSX attributes that we localize. Intentionally limited to link\n * attributes (`href`) — NOT asset attributes like `src`, which usually point at\n * shared, locale-agnostic assets (e.g. `/docs/images/...`) that would 404 if a\n * locale prefix were added. Extend deliberately.\n */\nconst LOCALIZABLE_URL_ATTRIBUTES = new Set(['href']);\n\n// Docs routing conventions: page file formats, and the file that serves a\n// folder URL (`/a/` -> `a/index.mdx`).\nexport const PAGE_EXTENSIONS = ['.mdx', '.md'];\nconst INDEX_PAGE = 'index';\n\n/**\n * The page file a root-relative docs URL points to, relative to the working\n * directory like file mapping keys: `/a/b#x` -> `a/b.mdx`, `a/b.md`, or\n * `a/b/index.mdx`.\n */\nfunction findPage(url: string): string | undefined {\n const base = url.split(/[?#]/)[0].replace(/^\\/+|\\/+$/g, '');\n return PAGE_EXTENSIONS.flatMap((ext) => [\n `${base}${ext}`,\n path.join(base, `${INDEX_PAGE}${ext}`),\n ]).find((page) => fs.existsSync(page));\n}\n\n/**\n * Whether a URL points to a source page with no translation on disk, either at\n * its mapped output path or at the localized URL. URLs with no source page,\n * such as pages generated from an OpenAPI spec, are not missing a translation.\n */\nfunction translationMissing(\n url: string,\n localizedUrl: string,\n localizedFiles: Record<string, string>\n): boolean {\n const sourcePage = findPage(url);\n if (!sourcePage) return false;\n const mappedPage = localizedFiles[sourcePage];\n return !(mappedPage && fs.existsSync(mappedPage)) && !findPage(localizedUrl);\n}\n\n/**\n * Localize URL string literals that live inside an MDX expression's raw source.\n *\n * URLs can be nested arbitrarily deep inside `{...}` expressions — e.g. an\n * `<a href>` buried inside a component prop:\n * <ParamField type={<span><a href=\"/docs/x\">Error</a></span>} />\n * Such hrefs never appear as mdast nodes (they live in the expression's embedded\n * JS), so the mdast visitors can't reach them. We re-parse the expression source\n * with Babel — positions are local to `source`, which avoids any document-offset\n * mapping — find JSX url-attribute string literals at any depth, and surgically\n * rewrite them in place.\n *\n * `rootStringIsUrl` covers `href={\"/docs/x\"}`, where the expression itself is a\n * bare string literal that a url-named attribute should treat as a URL.\n *\n * Only static string literals are localized; dynamically-computed URLs\n * (template literals, identifiers, concatenation) are left untouched — a\n * fundamental limit of static localization, not a parser shortcoming.\n */\nfunction localizeUrlsInExpressionSource(\n source: string,\n transformUrl: (url: string, linkType: 'markdown' | 'href') => string | null,\n rootStringIsUrl: boolean = false\n): string {\n if (!source.trim()) return source;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let ast: any;\n try {\n ast = parseBabel(source, {\n sourceType: 'module',\n errorRecovery: true,\n plugins: ['jsx', 'typescript'],\n });\n } catch {\n // Dynamic / unparseable expression — leave it untouched.\n return source;\n }\n\n const replacements: Array<{ start: number; end: number; text: string }> = [];\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pushLiteralReplacement = (literal: any) => {\n if (\n !literal ||\n // A bare string at statement position is parsed by Babel as a\n // DirectiveLiteral (cf. \"use strict\") rather than a StringLiteral.\n (literal.type !== 'StringLiteral' &&\n literal.type !== 'DirectiveLiteral') ||\n typeof literal.value !== 'string' ||\n typeof literal.start !== 'number' ||\n typeof literal.end !== 'number'\n ) {\n return;\n }\n const newUrl = transformUrl(literal.value, 'href');\n if (!newUrl) return;\n // Preserve the original quote style; URLs never contain quote chars.\n const quote = source[literal.start] === \"'\" ? \"'\" : '\"';\n replacements.push({\n start: literal.start,\n end: literal.end,\n text: `${quote}${newUrl}${quote}`,\n });\n };\n\n // `href={\"/docs/x\"}`: the entire expression is the URL string.\n if (rootStringIsUrl) {\n const program = ast.program;\n const body = (program?.body ?? []) as Array<{\n type: string;\n expression?: unknown;\n }>;\n if (body.length === 1 && body[0]?.type === 'ExpressionStatement') {\n pushLiteralReplacement(body[0].expression);\n } else if (body.length === 0 && program?.directives?.length === 1) {\n // Babel parses a lone string literal as a directive, not a statement.\n pushLiteralReplacement(program.directives[0]?.value);\n }\n }\n\n // Walk the AST for JSX url-attributes anywhere in the expression.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const seen = new Set<any>();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const walk = (node: any) => {\n if (!node || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n for (const item of node) walk(item);\n return;\n }\n if (seen.has(node)) return;\n seen.add(node);\n\n if (\n node.type === 'JSXAttribute' &&\n node.name?.type === 'JSXIdentifier' &&\n LOCALIZABLE_URL_ATTRIBUTES.has(node.name.name) &&\n node.value\n ) {\n if (node.value.type === 'StringLiteral') {\n pushLiteralReplacement(node.value);\n } else if (\n node.value.type === 'JSXExpressionContainer' &&\n node.value.expression?.type === 'StringLiteral'\n ) {\n pushLiteralReplacement(node.value.expression);\n }\n }\n\n for (const key in node) {\n if (\n key === 'loc' ||\n key === 'start' ||\n key === 'end' ||\n key === 'range'\n ) {\n continue;\n }\n const child = node[key];\n if (child && typeof child === 'object') walk(child);\n }\n };\n walk(ast.program ?? ast);\n\n if (replacements.length === 0) return source;\n\n // Apply end-to-start so earlier offsets remain valid.\n let result = source;\n replacements\n .sort((a, b) => b.start - a.start)\n .forEach(({ start, end, text }) => {\n result = result.slice(0, start) + text + result.slice(end);\n });\n return result;\n}\n\nexport type StaticUrlSettings = StaticLocalizationSettings;\n\n/**\n * Localizes static urls in content files.\n * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)\n * @param settings - The settings object containing the project configuration.\n * @returns void\n *\n * @TODO This is an experimental feature, and only works in very specific cases. This needs to be improved before\n * it can be enabled by default.\n *\n * Before this becomes a non-experimental feature, we need to:\n * - Support more file types\n * - Support more complex paths\n */\nexport default async function localizeStaticUrls(\n settings: StaticUrlSettings,\n targetLocales?: string[],\n includeFiles?: Set<string>\n) {\n if (\n !settings.files ||\n (Object.keys(settings.files.placeholderPaths).length === 1 &&\n settings.files.placeholderPaths.gt)\n ) {\n return;\n }\n const { resolvedPaths: sourceFiles } = settings.files;\n const localizeOptions = settings.options?.experimentalLocalizeStaticUrls;\n const skipUntranslatedPages =\n typeof localizeOptions === 'object' &&\n localizeOptions.skipUntranslatedPages === true;\n\n // Use filtered locales if provided, otherwise use all locales\n const locales = targetLocales || settings.locales;\n\n const fileMapping = createFileMapping(\n sourceFiles,\n settings.files.placeholderPaths,\n settings.files.transformPaths ?? {},\n settings.files.transformFormats ?? {},\n settings.locales, // Always use all locales for mapping, filter later\n settings.defaultLocale\n );\n\n // Process all file types at once with a single call\n const processPromises = [];\n\n // First, process default locale files (from source files)\n // This is needed because they might not be in the fileMapping if they're not being translated\n // Only process default locale if it's in the target locales filter\n if (\n !fileMapping[settings.defaultLocale] &&\n locales.includes(settings.defaultLocale) &&\n !includeFiles // when filtering, skip default-locale pass\n ) {\n const defaultLocaleFiles: string[] = [];\n\n // Collect all .md and .mdx files from sourceFiles\n if (sourceFiles.md) {\n defaultLocaleFiles.push(...sourceFiles.md);\n }\n if (sourceFiles.mdx) {\n defaultLocaleFiles.push(...sourceFiles.mdx);\n }\n\n if (defaultLocaleFiles.length > 0) {\n const defaultPromise = Promise.all(\n defaultLocaleFiles.map(async (filePath: string) => {\n // Check if file exists before processing\n if (!fs.existsSync(filePath)) {\n return;\n }\n // Get file content\n const fileContent = await fs.promises.readFile(filePath, 'utf8');\n // Localize the file using default locale\n const result = localizeStaticUrlsForFile(\n fileContent,\n settings.defaultLocale,\n settings.defaultLocale, // Process as default locale\n settings.options?.experimentalHideDefaultLocale || false,\n settings.options?.docsUrlPattern,\n settings.options?.excludeStaticUrls,\n settings.options?.baseDomain\n );\n // Only write the file if there were changes\n if (result.hasChanges) {\n await fs.promises.writeFile(filePath, result.content);\n }\n })\n );\n processPromises.push(defaultPromise);\n }\n }\n\n // Then process all other locales from fileMapping\n const mappingPromises = Object.entries(fileMapping)\n .filter(([locale]) => locales.includes(locale)) // Filter by target locales\n .map(async ([locale, filesMap]) => {\n // Get all files that are md or mdx\n const targetFiles = Object.values(filesMap).filter(\n (p) =>\n PAGE_EXTENSIONS.some((ext) => p.endsWith(ext)) &&\n (!includeFiles || includeFiles.has(p))\n );\n\n // Replace the placeholder path with the target path\n await Promise.all(\n targetFiles.map(async (filePath) => {\n // Check if file exists before processing\n if (!fs.existsSync(filePath)) {\n return;\n }\n // Get file content\n const fileContent = await fs.promises.readFile(filePath, 'utf8');\n // Localize the file (handles both URLs and hrefs in single AST pass)\n const result = localizeStaticUrlsForFile(\n fileContent,\n settings.defaultLocale,\n locale,\n settings.options?.experimentalHideDefaultLocale || false,\n settings.options?.docsUrlPattern,\n settings.options?.excludeStaticUrls,\n settings.options?.baseDomain,\n skipUntranslatedPages ? filesMap : undefined\n );\n // Only write the file if there were changes\n if (result.hasChanges) {\n await fs.promises.writeFile(filePath, result.content);\n }\n })\n );\n });\n processPromises.push(...mappingPromises);\n\n await Promise.all(processPromises);\n}\n\ninterface UrlTransformResult {\n content: string;\n hasChanges: boolean;\n transformedUrls: Array<{\n originalPath: string;\n newPath: string;\n type: 'markdown' | 'href';\n }>;\n}\n\n/**\n * Determines if a URL should be processed based on pattern matching\n */\nfunction shouldProcessUrl(\n originalUrl: string,\n patternHead: string,\n targetLocale: string,\n defaultLocale: string,\n baseDomain?: string\n): boolean {\n // Check fragment-only URLs like \"#id-name\"\n if (/^\\s*#/.test(originalUrl)) {\n return false;\n }\n\n const patternWithoutSlash = patternHead.replace(/\\/$/, '');\n\n // Handle absolute URLs with baseDomain\n let urlToCheck = originalUrl;\n if (baseDomain && originalUrl.startsWith(baseDomain)) {\n urlToCheck = originalUrl.substring(baseDomain.length);\n }\n\n if (targetLocale === defaultLocale) {\n // For default locale processing, check if URL contains the pattern\n return urlToCheck.includes(patternWithoutSlash);\n } else {\n // For non-default locales, check if URL starts with pattern\n return urlToCheck.startsWith(patternWithoutSlash);\n }\n}\n\n/**\n * Determines if a URL should be processed based on the base domain\n */\nfunction shouldProcessAbsoluteUrl(\n originalUrl: string,\n baseDomain: string\n): boolean {\n return originalUrl.startsWith(baseDomain);\n}\n\n/**\n * Checks if a URL should be excluded based on exclusion patterns\n */\nfunction isUrlExcluded(\n originalUrl: string,\n exclude: string[],\n defaultLocale: string\n): boolean {\n const excludePatterns = exclude.map((p) =>\n p.replace(/\\[locale\\]/g, defaultLocale)\n );\n return excludePatterns.some((pattern) => isMatch(originalUrl, pattern));\n}\n\n/**\n * Main URL transformation function that delegates to specific scenarios\n */\nexport function transformUrlPath(\n originalUrl: string,\n patternHead: string,\n targetLocale: string,\n defaultLocale: string,\n hideDefaultLocale: boolean\n): string | null {\n const originalPathArray = originalUrl\n .split('/')\n .filter((path) => path !== '');\n const patternHeadArray = patternHead.split('/').filter((path) => path !== '');\n\n // check if the pattern head matches the original path\n if (!checkIfPathMatchesPattern(originalPathArray, patternHeadArray)) {\n return null;\n }\n\n if (patternHeadArray.length > originalPathArray.length) {\n return null; // Pattern is longer than the URL path\n }\n\n let result = null;\n if (targetLocale === defaultLocale) {\n if (hideDefaultLocale) {\n // check if default locale is already present\n if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {\n return null;\n }\n\n // remove default locale\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n ...originalPathArray.slice(patternHeadArray.length + 1),\n ];\n\n result = newPathArray.join('/');\n } else {\n // check if default locale is already present\n if (originalPathArray?.[patternHeadArray.length] === defaultLocale) {\n return null;\n }\n\n // insert default locale\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n defaultLocale,\n ...originalPathArray.slice(patternHeadArray.length),\n ];\n\n result = newPathArray.join('/');\n }\n } else if (hideDefaultLocale) {\n // Avoid duplicating target locale if already present\n if (originalPathArray?.[patternHeadArray.length] === targetLocale) {\n return null;\n }\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n targetLocale,\n ...originalPathArray.slice(patternHeadArray.length),\n ];\n\n result = newPathArray.join('/');\n } else {\n // check default locale\n if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {\n return null;\n }\n\n // replace default locale with target locale\n const newPathArray = [...originalPathArray];\n newPathArray[patternHeadArray.length] = targetLocale;\n\n result = newPathArray.join('/');\n }\n\n // check for leading and trailing slashes\n if (originalUrl.startsWith('/')) {\n result = '/' + result;\n }\n if (originalUrl.endsWith('/')) {\n result = result + '/';\n }\n\n return result;\n}\n\n/**\n * AST-based transformation for MDX files using remark-mdx\n */\nfunction transformMdxUrls(\n mdxContent: string,\n defaultLocale: string,\n targetLocale: string,\n hideDefaultLocale: boolean,\n pattern: string = '/[locale]',\n exclude: string[] = [],\n baseDomain?: string,\n localizedFiles?: Record<string, string>\n): UrlTransformResult {\n const transformedUrls: Array<{\n originalPath: string;\n newPath: string;\n type: 'markdown' | 'href';\n }> = [];\n\n if (!pattern.startsWith('/')) {\n pattern = '/' + pattern;\n }\n\n const patternHead = pattern.split('[locale]')[0];\n\n // Quick check: if the file doesn't contain the pattern, skip expensive AST parsing\n // For default locale processing, we also need to check if content might need adjustment\n if (targetLocale === defaultLocale) {\n // For default locale files, we always need to check as we're looking for either:\n // - paths without locale (when hideDefaultLocale=false)\n // - paths with default locale (when hideDefaultLocale=true)\n const patternWithoutSlash = patternHead.replace(/\\/$/, '');\n if (!mdxContent.includes(patternWithoutSlash)) {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n } else {\n // For non-default locales, use the original logic\n if (!mdxContent.includes(patternHead.replace(/\\/$/, ''))) {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n }\n\n // Parse the MDX content into an AST\n let processedAst: Root;\n let neutralizedAnchors: boolean;\n try {\n const parsed = parseMdxForRoundTrip(mdxContent);\n processedAst = parsed.ast;\n neutralizedAnchors = parsed.neutralized;\n } catch {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls,\n };\n }\n\n // Helper function to transform URL based on pattern\n const transformUrl = (\n originalUrl: string,\n linkType: 'markdown' | 'href'\n ): string | null => {\n // For Markdown links [text](path), only process absolute-root paths starting with '/'\n // Relative markdown links should remain relative to the current page and not be localized.\n if (linkType === 'markdown') {\n const isFragment = /^\\s*#/.test(originalUrl);\n const isAbsoluteRoot = originalUrl.startsWith('/');\n const looksAbsoluteWithDomain = baseDomain\n ? shouldProcessAbsoluteUrl(originalUrl, baseDomain)\n : false;\n if (!isAbsoluteRoot && !looksAbsoluteWithDomain && !isFragment) {\n return null;\n }\n }\n // Check if URL should be processed\n if (\n !shouldProcessUrl(\n originalUrl,\n patternHead,\n targetLocale,\n defaultLocale,\n baseDomain\n )\n ) {\n return null;\n }\n\n // Skip absolute URLs (http://, https://, //, etc.)\n if (baseDomain && shouldProcessAbsoluteUrl(originalUrl, baseDomain)) {\n // Get everything after the base domain\n const afterDomain = originalUrl.substring(baseDomain.length);\n\n const transformedPath = transformUrlPath(\n afterDomain,\n patternHead,\n targetLocale,\n defaultLocale,\n hideDefaultLocale\n );\n if (!transformedPath) {\n return null;\n }\n if (\n localizedFiles &&\n translationMissing(afterDomain, transformedPath, localizedFiles)\n ) {\n return null;\n }\n transformedUrls.push({\n originalPath: originalUrl,\n newPath: transformedPath,\n type: linkType,\n });\n return transformedPath ? baseDomain + transformedPath : null;\n }\n\n // Exclude colon-prefixed URLs (http://, https://, //, etc.)\n if (originalUrl.split('?')[0].includes(':')) {\n return null;\n }\n\n // Transform the URL based on locale and configuration\n const newUrl = transformUrlPath(\n originalUrl,\n patternHead,\n targetLocale,\n defaultLocale,\n hideDefaultLocale\n );\n\n if (!newUrl) {\n return null;\n }\n\n // Check exclusions\n if (isUrlExcluded(originalUrl, exclude, defaultLocale)) {\n return null;\n }\n\n // Links to pages with no translation keep the default locale.\n if (\n localizedFiles &&\n translationMissing(originalUrl, newUrl, localizedFiles)\n ) {\n return null;\n }\n\n transformedUrls.push({\n originalPath: originalUrl,\n newPath: newUrl,\n type: linkType,\n });\n return newUrl;\n };\n\n // Visit markdown link nodes: [text](url)\n visit(processedAst, 'link', (node: Link) => {\n if (node.url) {\n const newUrl = transformUrl(node.url, 'markdown');\n if (newUrl) {\n node.url = newUrl;\n }\n }\n });\n\n // Visit JSX/HTML elements for href attributes: <a href=\"url\"> or <Card href=\"url\">\n visit(processedAst, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node) => {\n const jsxNode = node as MdxJsxFlowElement | MdxJsxTextElement;\n if (!jsxNode.attributes) return;\n for (const attr of jsxNode.attributes) {\n if (attr.type !== 'mdxJsxAttribute' || !attr.value) continue;\n\n // Plain string attribute value, e.g. <a href=\"/docs/x\">\n if (typeof attr.value === 'string') {\n if (LOCALIZABLE_URL_ATTRIBUTES.has(attr.name)) {\n const newUrl = transformUrl(attr.value, 'href');\n if (newUrl) {\n attr.value = newUrl;\n }\n }\n continue;\n }\n\n // Expression attribute value. Two shapes are handled:\n // href={\"/docs/x\"} (url attr, bare string)\n // type={<span><a href=\"/docs/x\">…</a></span>} (url attr nested deep\n // inside a non-url prop)\n // The nested case never surfaces as an mdast node — it lives in the\n // expression's embedded JS — so it's localized via the source-level walk.\n if (\n typeof attr.value === 'object' &&\n attr.value.type === 'mdxJsxAttributeValueExpression' &&\n typeof attr.value.value === 'string'\n ) {\n const newValue = localizeUrlsInExpressionSource(\n attr.value.value,\n transformUrl,\n LOCALIZABLE_URL_ATTRIBUTES.has(attr.name)\n );\n if (newValue !== attr.value.value) {\n attr.value.value = newValue;\n }\n }\n }\n });\n\n // Visit standalone MDX expressions for URLs inside embedded JSX, e.g.\n // {isBeta && <a href=\"/docs/x\">Beta</a>}\n visit(processedAst, ['mdxFlowExpression', 'mdxTextExpression'], (node) => {\n const exprNode = node as unknown as Literal;\n if (typeof exprNode.value === 'string' && exprNode.value) {\n const newValue = localizeUrlsInExpressionSource(\n exprNode.value,\n transformUrl\n );\n if (newValue !== exprNode.value) {\n exprNode.value = newValue;\n }\n }\n });\n\n // Visit raw JSX nodes for href attributes in JSX strings\n visit(processedAst, 'jsx', (node: Literal) => {\n if (node.value && typeof node.value === 'string') {\n const jsxContent = node.value;\n\n // Use regex to find href attributes in the JSX string\n const hrefRegex = /href\\s*=\\s*[\"']([^\"']+)[\"']/g;\n let match;\n const replacements: Array<{\n start: number;\n end: number;\n oldHrefAttr: string;\n newHrefAttr: string;\n }> = [];\n\n // Reset regex lastIndex to avoid issues with global flag\n hrefRegex.lastIndex = 0;\n\n while ((match = hrefRegex.exec(jsxContent)) !== null) {\n const originalHref = match[1];\n const newUrl = transformUrl(originalHref, 'href');\n\n if (newUrl) {\n // Store replacement info\n const oldHrefAttr = match[0]; // The full match like 'href=\"/quickstart\"'\n const quote = oldHrefAttr.includes('\"') ? '\"' : \"'\";\n const newHrefAttr = `href=${quote}${newUrl}${quote}`;\n\n replacements.push({\n start: match.index!,\n end: match.index! + oldHrefAttr.length,\n oldHrefAttr,\n newHrefAttr,\n });\n }\n }\n\n // Apply replacements in reverse order (from end to start) to avoid position shifts\n if (replacements.length > 0) {\n let newJsxContent = jsxContent;\n replacements\n .sort((a, b) => b.start - a.start)\n .forEach(({ start, end, newHrefAttr }) => {\n newJsxContent =\n newJsxContent.slice(0, start) +\n newHrefAttr +\n newJsxContent.slice(end);\n });\n\n node.value = newJsxContent;\n }\n }\n });\n\n // Convert the modified AST back to MDX string\n let content: string;\n try {\n const stringifyProcessor = unified()\n .use(remarkFrontmatter, ['yaml', 'toml'])\n .use(remarkMdx)\n .use(normalizeCJKCharacters)\n .use(escapeHtmlInTextNodes)\n .use(remarkStringify, {\n handlers: {\n // Handler to prevent escaping (avoids '&lt;' -> '\\&lt;')\n text(node: Literal) {\n return node.value;\n },\n },\n });\n\n const outTree = stringifyProcessor.runSync(processedAst) as Root;\n content = stringifyProcessor.stringify(outTree);\n if (neutralizedAnchors) {\n content = restoreAnchorIds(content);\n }\n } catch (error) {\n console.warn(\n `Failed to stringify MDX content: ${error instanceof Error ? error.message : String(error)}`\n );\n console.warn(\n 'Returning original content unchanged due to stringify error.'\n );\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n\n // Handle newline formatting to match original input\n if (content.endsWith('\\n') && !mdxContent.endsWith('\\n')) {\n content = content.slice(0, -1);\n }\n\n // Preserve leading newlines from original content\n if (mdxContent.startsWith('\\n') && !content.startsWith('\\n')) {\n content = '\\n' + content;\n }\n\n return {\n content,\n hasChanges: transformedUrls.length > 0,\n transformedUrls,\n };\n}\n\n// AST-based transformation for MDX files using remark\nfunction localizeStaticUrlsForFile(\n file: string,\n defaultLocale: string,\n targetLocale: string,\n hideDefaultLocale: boolean,\n pattern: string = '/[locale]', // eg /docs/[locale] or /[locale]\n exclude: string[] = [],\n baseDomain?: string,\n localizedFiles?: Record<string, string>\n): UrlTransformResult {\n // Use AST-based transformation for MDX files\n return transformMdxUrls(\n file,\n defaultLocale,\n targetLocale,\n hideDefaultLocale,\n pattern,\n exclude,\n baseDomain || '',\n localizedFiles\n );\n}\n\nfunction checkIfPathMatchesPattern(\n originalUrlArray: string[],\n patternHeadArray: string[]\n): boolean {\n // check if the pattern head matches the original path\n for (let i = 0; i < patternHeadArray.length; i++) {\n if (patternHeadArray[i] !== originalUrlArray?.[i]) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"localizeStaticUrls.js","names":["path","fs","parseBabel"],"sources":["../../src/utils/localizeStaticUrls.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport type { StaticLocalizationSettings } from '../types/index.js';\nimport { createFileMapping } from '../formats/files/fileMapping.js';\nimport micromatch from 'micromatch';\nimport { unified } from 'unified';\nimport remarkMdx from 'remark-mdx';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport remarkStringify from 'remark-stringify';\nimport { visit } from 'unist-util-visit';\nimport type { Root, Link, Literal } from 'mdast';\nimport type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx';\nimport { escapeHtmlInTextNodes, normalizeCJKCharacters } from 'gt-remark';\nimport { parse as parseBabel } from '@babel/parser';\nimport { parseMdxForRoundTrip, restoreAnchorIds } from './mdxAnchorSyntax.js';\n\nconst { isMatch } = micromatch;\n\n/**\n * URL-bearing JSX attributes that we localize. Intentionally limited to link\n * attributes (`href`) — NOT asset attributes like `src`, which usually point at\n * shared, locale-agnostic assets (e.g. `/docs/images/...`) that would 404 if a\n * locale prefix were added. Extend deliberately.\n */\nconst LOCALIZABLE_URL_ATTRIBUTES = new Set(['href']);\n\n// Docs routing conventions: page file formats, and the file that serves a\n// folder URL (`/a/` -> `a/index.mdx`).\nexport const PAGE_EXTENSIONS = ['.mdx', '.md'];\nexport const INDEX_PAGE = 'index';\n\n/**\n * The page file a root-relative docs URL points to, relative to the working\n * directory like file mapping keys: `/a/b#x` -> `a/b.mdx`, `a/b.md`, or\n * `a/b/index.mdx`.\n */\nfunction findPage(url: string): string | undefined {\n const base = url.split(/[?#]/)[0].replace(/^\\/+|\\/+$/g, '');\n return PAGE_EXTENSIONS.flatMap((ext) => [\n `${base}${ext}`,\n path.join(base, `${INDEX_PAGE}${ext}`),\n ]).find((page) => fs.existsSync(page));\n}\n\n/**\n * Whether a URL points to a source page with no translation on disk, either at\n * its mapped output path or at the localized URL. URLs with no source page,\n * such as pages generated from an OpenAPI spec, are not missing a translation.\n */\nfunction translationMissing(\n url: string,\n localizedUrl: string,\n localizedFiles: Record<string, string>\n): boolean {\n const sourcePage = findPage(url);\n if (!sourcePage) return false;\n const mappedPage = localizedFiles[sourcePage];\n return !(mappedPage && fs.existsSync(mappedPage)) && !findPage(localizedUrl);\n}\n\n/**\n * Localize URL string literals that live inside an MDX expression's raw source.\n *\n * URLs can be nested arbitrarily deep inside `{...}` expressions — e.g. an\n * `<a href>` buried inside a component prop:\n * <ParamField type={<span><a href=\"/docs/x\">Error</a></span>} />\n * Such hrefs never appear as mdast nodes (they live in the expression's embedded\n * JS), so the mdast visitors can't reach them. We re-parse the expression source\n * with Babel — positions are local to `source`, which avoids any document-offset\n * mapping — find JSX url-attribute string literals at any depth, and surgically\n * rewrite them in place.\n *\n * `rootStringIsUrl` covers `href={\"/docs/x\"}`, where the expression itself is a\n * bare string literal that a url-named attribute should treat as a URL.\n *\n * Only static string literals are localized; dynamically-computed URLs\n * (template literals, identifiers, concatenation) are left untouched — a\n * fundamental limit of static localization, not a parser shortcoming.\n */\nfunction localizeUrlsInExpressionSource(\n source: string,\n transformUrl: (url: string, linkType: 'markdown' | 'href') => string | null,\n rootStringIsUrl: boolean = false\n): string {\n if (!source.trim()) return source;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let ast: any;\n try {\n ast = parseBabel(source, {\n sourceType: 'module',\n errorRecovery: true,\n plugins: ['jsx', 'typescript'],\n });\n } catch {\n // Dynamic / unparseable expression — leave it untouched.\n return source;\n }\n\n const replacements: Array<{ start: number; end: number; text: string }> = [];\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pushLiteralReplacement = (literal: any) => {\n if (\n !literal ||\n // A bare string at statement position is parsed by Babel as a\n // DirectiveLiteral (cf. \"use strict\") rather than a StringLiteral.\n (literal.type !== 'StringLiteral' &&\n literal.type !== 'DirectiveLiteral') ||\n typeof literal.value !== 'string' ||\n typeof literal.start !== 'number' ||\n typeof literal.end !== 'number'\n ) {\n return;\n }\n const newUrl = transformUrl(literal.value, 'href');\n if (!newUrl) return;\n // Preserve the original quote style; URLs never contain quote chars.\n const quote = source[literal.start] === \"'\" ? \"'\" : '\"';\n replacements.push({\n start: literal.start,\n end: literal.end,\n text: `${quote}${newUrl}${quote}`,\n });\n };\n\n // `href={\"/docs/x\"}`: the entire expression is the URL string.\n if (rootStringIsUrl) {\n const program = ast.program;\n const body = (program?.body ?? []) as Array<{\n type: string;\n expression?: unknown;\n }>;\n if (body.length === 1 && body[0]?.type === 'ExpressionStatement') {\n pushLiteralReplacement(body[0].expression);\n } else if (body.length === 0 && program?.directives?.length === 1) {\n // Babel parses a lone string literal as a directive, not a statement.\n pushLiteralReplacement(program.directives[0]?.value);\n }\n }\n\n // Walk the AST for JSX url-attributes anywhere in the expression.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const seen = new Set<any>();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const walk = (node: any) => {\n if (!node || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n for (const item of node) walk(item);\n return;\n }\n if (seen.has(node)) return;\n seen.add(node);\n\n if (\n node.type === 'JSXAttribute' &&\n node.name?.type === 'JSXIdentifier' &&\n LOCALIZABLE_URL_ATTRIBUTES.has(node.name.name) &&\n node.value\n ) {\n if (node.value.type === 'StringLiteral') {\n pushLiteralReplacement(node.value);\n } else if (\n node.value.type === 'JSXExpressionContainer' &&\n node.value.expression?.type === 'StringLiteral'\n ) {\n pushLiteralReplacement(node.value.expression);\n }\n }\n\n for (const key in node) {\n if (\n key === 'loc' ||\n key === 'start' ||\n key === 'end' ||\n key === 'range'\n ) {\n continue;\n }\n const child = node[key];\n if (child && typeof child === 'object') walk(child);\n }\n };\n walk(ast.program ?? ast);\n\n if (replacements.length === 0) return source;\n\n // Apply end-to-start so earlier offsets remain valid.\n let result = source;\n replacements\n .sort((a, b) => b.start - a.start)\n .forEach(({ start, end, text }) => {\n result = result.slice(0, start) + text + result.slice(end);\n });\n return result;\n}\n\nexport type StaticUrlSettings = StaticLocalizationSettings;\n\n/**\n * Localizes static urls in content files.\n * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)\n * @param settings - The settings object containing the project configuration.\n * @returns void\n *\n * @TODO This is an experimental feature, and only works in very specific cases. This needs to be improved before\n * it can be enabled by default.\n *\n * Before this becomes a non-experimental feature, we need to:\n * - Support more file types\n * - Support more complex paths\n */\nexport default async function localizeStaticUrls(\n settings: StaticUrlSettings,\n targetLocales?: string[],\n includeFiles?: Set<string>\n) {\n if (\n !settings.files ||\n (Object.keys(settings.files.placeholderPaths).length === 1 &&\n settings.files.placeholderPaths.gt)\n ) {\n return;\n }\n const { resolvedPaths: sourceFiles } = settings.files;\n const localizeOptions = settings.options?.experimentalLocalizeStaticUrls;\n const skipUntranslatedPages =\n typeof localizeOptions === 'object' &&\n localizeOptions.skipUntranslatedPages === true;\n\n // Use filtered locales if provided, otherwise use all locales\n const locales = targetLocales || settings.locales;\n\n const fileMapping = createFileMapping(\n sourceFiles,\n settings.files.placeholderPaths,\n settings.files.transformPaths ?? {},\n settings.files.transformFormats ?? {},\n settings.locales, // Always use all locales for mapping, filter later\n settings.defaultLocale\n );\n\n // Process all file types at once with a single call\n const processPromises = [];\n\n // First, process default locale files (from source files)\n // This is needed because they might not be in the fileMapping if they're not being translated\n // Only process default locale if it's in the target locales filter\n if (\n !fileMapping[settings.defaultLocale] &&\n locales.includes(settings.defaultLocale) &&\n !includeFiles // when filtering, skip default-locale pass\n ) {\n const defaultLocaleFiles: string[] = [];\n\n // Collect all .md and .mdx files from sourceFiles\n if (sourceFiles.md) {\n defaultLocaleFiles.push(...sourceFiles.md);\n }\n if (sourceFiles.mdx) {\n defaultLocaleFiles.push(...sourceFiles.mdx);\n }\n\n if (defaultLocaleFiles.length > 0) {\n const defaultPromise = Promise.all(\n defaultLocaleFiles.map(async (filePath: string) => {\n // Check if file exists before processing\n if (!fs.existsSync(filePath)) {\n return;\n }\n // Get file content\n const fileContent = await fs.promises.readFile(filePath, 'utf8');\n // Localize the file using default locale\n const result = localizeStaticUrlsForFile(\n fileContent,\n settings.defaultLocale,\n settings.defaultLocale, // Process as default locale\n settings.options?.experimentalHideDefaultLocale || false,\n settings.options?.docsUrlPattern,\n settings.options?.excludeStaticUrls,\n settings.options?.baseDomain\n );\n // Only write the file if there were changes\n if (result.hasChanges) {\n await fs.promises.writeFile(filePath, result.content);\n }\n })\n );\n processPromises.push(defaultPromise);\n }\n }\n\n // Then process all other locales from fileMapping\n const mappingPromises = Object.entries(fileMapping)\n .filter(([locale]) => locales.includes(locale)) // Filter by target locales\n .map(async ([locale, filesMap]) => {\n // Get all files that are md or mdx\n const targetFiles = Object.values(filesMap).filter(\n (p) =>\n PAGE_EXTENSIONS.some((ext) => p.endsWith(ext)) &&\n (!includeFiles || includeFiles.has(p))\n );\n\n // Replace the placeholder path with the target path\n await Promise.all(\n targetFiles.map(async (filePath) => {\n // Check if file exists before processing\n if (!fs.existsSync(filePath)) {\n return;\n }\n // Get file content\n const fileContent = await fs.promises.readFile(filePath, 'utf8');\n // Localize the file (handles both URLs and hrefs in single AST pass)\n const result = localizeStaticUrlsForFile(\n fileContent,\n settings.defaultLocale,\n locale,\n settings.options?.experimentalHideDefaultLocale || false,\n settings.options?.docsUrlPattern,\n settings.options?.excludeStaticUrls,\n settings.options?.baseDomain,\n skipUntranslatedPages ? filesMap : undefined\n );\n // Only write the file if there were changes\n if (result.hasChanges) {\n await fs.promises.writeFile(filePath, result.content);\n }\n })\n );\n });\n processPromises.push(...mappingPromises);\n\n await Promise.all(processPromises);\n}\n\ninterface UrlTransformResult {\n content: string;\n hasChanges: boolean;\n transformedUrls: Array<{\n originalPath: string;\n newPath: string;\n type: 'markdown' | 'href';\n }>;\n}\n\n/**\n * Determines if a URL should be processed based on pattern matching\n */\nfunction shouldProcessUrl(\n originalUrl: string,\n patternHead: string,\n targetLocale: string,\n defaultLocale: string,\n baseDomain?: string\n): boolean {\n // Check fragment-only URLs like \"#id-name\"\n if (/^\\s*#/.test(originalUrl)) {\n return false;\n }\n\n const patternWithoutSlash = patternHead.replace(/\\/$/, '');\n\n // Handle absolute URLs with baseDomain\n let urlToCheck = originalUrl;\n if (baseDomain && originalUrl.startsWith(baseDomain)) {\n urlToCheck = originalUrl.substring(baseDomain.length);\n }\n\n if (targetLocale === defaultLocale) {\n // For default locale processing, check if URL contains the pattern\n return urlToCheck.includes(patternWithoutSlash);\n } else {\n // For non-default locales, check if URL starts with pattern\n return urlToCheck.startsWith(patternWithoutSlash);\n }\n}\n\n/**\n * Determines if a URL should be processed based on the base domain\n */\nfunction shouldProcessAbsoluteUrl(\n originalUrl: string,\n baseDomain: string\n): boolean {\n return originalUrl.startsWith(baseDomain);\n}\n\n/**\n * Checks if a URL should be excluded based on exclusion patterns\n */\nfunction isUrlExcluded(\n originalUrl: string,\n exclude: string[],\n defaultLocale: string\n): boolean {\n const excludePatterns = exclude.map((p) =>\n p.replace(/\\[locale\\]/g, defaultLocale)\n );\n return excludePatterns.some((pattern) => isMatch(originalUrl, pattern));\n}\n\n/**\n * Main URL transformation function that delegates to specific scenarios\n */\nexport function transformUrlPath(\n originalUrl: string,\n patternHead: string,\n targetLocale: string,\n defaultLocale: string,\n hideDefaultLocale: boolean\n): string | null {\n const originalPathArray = originalUrl\n .split('/')\n .filter((path) => path !== '');\n const patternHeadArray = patternHead.split('/').filter((path) => path !== '');\n\n // check if the pattern head matches the original path\n if (!checkIfPathMatchesPattern(originalPathArray, patternHeadArray)) {\n return null;\n }\n\n if (patternHeadArray.length > originalPathArray.length) {\n return null; // Pattern is longer than the URL path\n }\n\n let result = null;\n if (targetLocale === defaultLocale) {\n if (hideDefaultLocale) {\n // check if default locale is already present\n if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {\n return null;\n }\n\n // remove default locale\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n ...originalPathArray.slice(patternHeadArray.length + 1),\n ];\n\n result = newPathArray.join('/');\n } else {\n // check if default locale is already present\n if (originalPathArray?.[patternHeadArray.length] === defaultLocale) {\n return null;\n }\n\n // insert default locale\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n defaultLocale,\n ...originalPathArray.slice(patternHeadArray.length),\n ];\n\n result = newPathArray.join('/');\n }\n } else if (hideDefaultLocale) {\n // Avoid duplicating target locale if already present\n if (originalPathArray?.[patternHeadArray.length] === targetLocale) {\n return null;\n }\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n targetLocale,\n ...originalPathArray.slice(patternHeadArray.length),\n ];\n\n result = newPathArray.join('/');\n } else {\n // check default locale\n if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {\n return null;\n }\n\n // replace default locale with target locale\n const newPathArray = [...originalPathArray];\n newPathArray[patternHeadArray.length] = targetLocale;\n\n result = newPathArray.join('/');\n }\n\n // check for leading and trailing slashes\n if (originalUrl.startsWith('/')) {\n result = '/' + result;\n }\n if (originalUrl.endsWith('/')) {\n result = result + '/';\n }\n\n return result;\n}\n\n/**\n * AST-based transformation for MDX files using remark-mdx\n */\nfunction transformMdxUrls(\n mdxContent: string,\n defaultLocale: string,\n targetLocale: string,\n hideDefaultLocale: boolean,\n pattern: string = '/[locale]',\n exclude: string[] = [],\n baseDomain?: string,\n localizedFiles?: Record<string, string>\n): UrlTransformResult {\n const transformedUrls: Array<{\n originalPath: string;\n newPath: string;\n type: 'markdown' | 'href';\n }> = [];\n\n if (!pattern.startsWith('/')) {\n pattern = '/' + pattern;\n }\n\n const patternHead = pattern.split('[locale]')[0];\n\n // Quick check: if the file doesn't contain the pattern, skip expensive AST parsing\n // For default locale processing, we also need to check if content might need adjustment\n if (targetLocale === defaultLocale) {\n // For default locale files, we always need to check as we're looking for either:\n // - paths without locale (when hideDefaultLocale=false)\n // - paths with default locale (when hideDefaultLocale=true)\n const patternWithoutSlash = patternHead.replace(/\\/$/, '');\n if (!mdxContent.includes(patternWithoutSlash)) {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n } else {\n // For non-default locales, use the original logic\n if (!mdxContent.includes(patternHead.replace(/\\/$/, ''))) {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n }\n\n // Parse the MDX content into an AST\n let processedAst: Root;\n let neutralizedAnchors: boolean;\n try {\n const parsed = parseMdxForRoundTrip(mdxContent);\n processedAst = parsed.ast;\n neutralizedAnchors = parsed.neutralized;\n } catch {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls,\n };\n }\n\n // Helper function to transform URL based on pattern\n const transformUrl = (\n originalUrl: string,\n linkType: 'markdown' | 'href'\n ): string | null => {\n // For Markdown links [text](path), only process absolute-root paths starting with '/'\n // Relative markdown links should remain relative to the current page and not be localized.\n if (linkType === 'markdown') {\n const isFragment = /^\\s*#/.test(originalUrl);\n const isAbsoluteRoot = originalUrl.startsWith('/');\n const looksAbsoluteWithDomain = baseDomain\n ? shouldProcessAbsoluteUrl(originalUrl, baseDomain)\n : false;\n if (!isAbsoluteRoot && !looksAbsoluteWithDomain && !isFragment) {\n return null;\n }\n }\n // Check if URL should be processed\n if (\n !shouldProcessUrl(\n originalUrl,\n patternHead,\n targetLocale,\n defaultLocale,\n baseDomain\n )\n ) {\n return null;\n }\n\n // Skip absolute URLs (http://, https://, //, etc.)\n if (baseDomain && shouldProcessAbsoluteUrl(originalUrl, baseDomain)) {\n // Get everything after the base domain\n const afterDomain = originalUrl.substring(baseDomain.length);\n\n const transformedPath = transformUrlPath(\n afterDomain,\n patternHead,\n targetLocale,\n defaultLocale,\n hideDefaultLocale\n );\n if (!transformedPath) {\n return null;\n }\n if (\n localizedFiles &&\n translationMissing(afterDomain, transformedPath, localizedFiles)\n ) {\n return null;\n }\n transformedUrls.push({\n originalPath: originalUrl,\n newPath: transformedPath,\n type: linkType,\n });\n return transformedPath ? baseDomain + transformedPath : null;\n }\n\n // Exclude colon-prefixed URLs (http://, https://, //, etc.)\n if (originalUrl.split('?')[0].includes(':')) {\n return null;\n }\n\n // Transform the URL based on locale and configuration\n const newUrl = transformUrlPath(\n originalUrl,\n patternHead,\n targetLocale,\n defaultLocale,\n hideDefaultLocale\n );\n\n if (!newUrl) {\n return null;\n }\n\n // Check exclusions\n if (isUrlExcluded(originalUrl, exclude, defaultLocale)) {\n return null;\n }\n\n // Links to pages with no translation keep the default locale.\n if (\n localizedFiles &&\n translationMissing(originalUrl, newUrl, localizedFiles)\n ) {\n return null;\n }\n\n transformedUrls.push({\n originalPath: originalUrl,\n newPath: newUrl,\n type: linkType,\n });\n return newUrl;\n };\n\n // Visit markdown link nodes: [text](url)\n visit(processedAst, 'link', (node: Link) => {\n if (node.url) {\n const newUrl = transformUrl(node.url, 'markdown');\n if (newUrl) {\n node.url = newUrl;\n }\n }\n });\n\n // Visit JSX/HTML elements for href attributes: <a href=\"url\"> or <Card href=\"url\">\n visit(processedAst, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node) => {\n const jsxNode = node as MdxJsxFlowElement | MdxJsxTextElement;\n if (!jsxNode.attributes) return;\n for (const attr of jsxNode.attributes) {\n if (attr.type !== 'mdxJsxAttribute' || !attr.value) continue;\n\n // Plain string attribute value, e.g. <a href=\"/docs/x\">\n if (typeof attr.value === 'string') {\n if (LOCALIZABLE_URL_ATTRIBUTES.has(attr.name)) {\n const newUrl = transformUrl(attr.value, 'href');\n if (newUrl) {\n attr.value = newUrl;\n }\n }\n continue;\n }\n\n // Expression attribute value. Two shapes are handled:\n // href={\"/docs/x\"} (url attr, bare string)\n // type={<span><a href=\"/docs/x\">…</a></span>} (url attr nested deep\n // inside a non-url prop)\n // The nested case never surfaces as an mdast node — it lives in the\n // expression's embedded JS — so it's localized via the source-level walk.\n if (\n typeof attr.value === 'object' &&\n attr.value.type === 'mdxJsxAttributeValueExpression' &&\n typeof attr.value.value === 'string'\n ) {\n const newValue = localizeUrlsInExpressionSource(\n attr.value.value,\n transformUrl,\n LOCALIZABLE_URL_ATTRIBUTES.has(attr.name)\n );\n if (newValue !== attr.value.value) {\n attr.value.value = newValue;\n }\n }\n }\n });\n\n // Visit standalone MDX expressions for URLs inside embedded JSX, e.g.\n // {isBeta && <a href=\"/docs/x\">Beta</a>}\n visit(processedAst, ['mdxFlowExpression', 'mdxTextExpression'], (node) => {\n const exprNode = node as unknown as Literal;\n if (typeof exprNode.value === 'string' && exprNode.value) {\n const newValue = localizeUrlsInExpressionSource(\n exprNode.value,\n transformUrl\n );\n if (newValue !== exprNode.value) {\n exprNode.value = newValue;\n }\n }\n });\n\n // Visit raw JSX nodes for href attributes in JSX strings\n visit(processedAst, 'jsx', (node: Literal) => {\n if (node.value && typeof node.value === 'string') {\n const jsxContent = node.value;\n\n // Use regex to find href attributes in the JSX string\n const hrefRegex = /href\\s*=\\s*[\"']([^\"']+)[\"']/g;\n let match;\n const replacements: Array<{\n start: number;\n end: number;\n oldHrefAttr: string;\n newHrefAttr: string;\n }> = [];\n\n // Reset regex lastIndex to avoid issues with global flag\n hrefRegex.lastIndex = 0;\n\n while ((match = hrefRegex.exec(jsxContent)) !== null) {\n const originalHref = match[1];\n const newUrl = transformUrl(originalHref, 'href');\n\n if (newUrl) {\n // Store replacement info\n const oldHrefAttr = match[0]; // The full match like 'href=\"/quickstart\"'\n const quote = oldHrefAttr.includes('\"') ? '\"' : \"'\";\n const newHrefAttr = `href=${quote}${newUrl}${quote}`;\n\n replacements.push({\n start: match.index!,\n end: match.index! + oldHrefAttr.length,\n oldHrefAttr,\n newHrefAttr,\n });\n }\n }\n\n // Apply replacements in reverse order (from end to start) to avoid position shifts\n if (replacements.length > 0) {\n let newJsxContent = jsxContent;\n replacements\n .sort((a, b) => b.start - a.start)\n .forEach(({ start, end, newHrefAttr }) => {\n newJsxContent =\n newJsxContent.slice(0, start) +\n newHrefAttr +\n newJsxContent.slice(end);\n });\n\n node.value = newJsxContent;\n }\n }\n });\n\n // Convert the modified AST back to MDX string\n let content: string;\n try {\n const stringifyProcessor = unified()\n .use(remarkFrontmatter, ['yaml', 'toml'])\n .use(remarkMdx)\n .use(normalizeCJKCharacters)\n .use(escapeHtmlInTextNodes)\n .use(remarkStringify, {\n handlers: {\n // Handler to prevent escaping (avoids '&lt;' -> '\\&lt;')\n text(node: Literal) {\n return node.value;\n },\n },\n });\n\n const outTree = stringifyProcessor.runSync(processedAst) as Root;\n content = stringifyProcessor.stringify(outTree);\n if (neutralizedAnchors) {\n content = restoreAnchorIds(content);\n }\n } catch (error) {\n console.warn(\n `Failed to stringify MDX content: ${error instanceof Error ? error.message : String(error)}`\n );\n console.warn(\n 'Returning original content unchanged due to stringify error.'\n );\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n\n // Handle newline formatting to match original input\n if (content.endsWith('\\n') && !mdxContent.endsWith('\\n')) {\n content = content.slice(0, -1);\n }\n\n // Preserve leading newlines from original content\n if (mdxContent.startsWith('\\n') && !content.startsWith('\\n')) {\n content = '\\n' + content;\n }\n\n return {\n content,\n hasChanges: transformedUrls.length > 0,\n transformedUrls,\n };\n}\n\n// AST-based transformation for MDX files using remark\nfunction localizeStaticUrlsForFile(\n file: string,\n defaultLocale: string,\n targetLocale: string,\n hideDefaultLocale: boolean,\n pattern: string = '/[locale]', // eg /docs/[locale] or /[locale]\n exclude: string[] = [],\n baseDomain?: string,\n localizedFiles?: Record<string, string>\n): UrlTransformResult {\n // Use AST-based transformation for MDX files\n return transformMdxUrls(\n file,\n defaultLocale,\n targetLocale,\n hideDefaultLocale,\n pattern,\n exclude,\n baseDomain || '',\n localizedFiles\n );\n}\n\nfunction checkIfPathMatchesPattern(\n originalUrlArray: string[],\n patternHeadArray: string[]\n): boolean {\n // check if the pattern head matches the original path\n for (let i = 0; i < patternHeadArray.length; i++) {\n if (patternHeadArray[i] !== originalUrlArray?.[i]) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,EAAE,YAAY;;;;;;;AAQpB,MAAM,6BAA6B,IAAI,IAAI,CAAC,OAAO,CAAC;AAIpD,MAAa,kBAAkB,CAAC,QAAQ,MAAM;AAC9C,MAAa,aAAa;;;;;;AAO1B,SAAS,SAAS,KAAiC;CACjD,MAAM,OAAO,IAAI,MAAM,OAAO,CAAC,GAAG,QAAQ,cAAc,GAAG;AAC3D,QAAO,gBAAgB,SAAS,QAAQ,CACtC,GAAG,OAAO,OACVA,OAAK,KAAK,MAAM,GAAG,aAAa,MAAM,CACvC,CAAC,CAAC,MAAM,SAASC,KAAG,WAAW,KAAK,CAAC;;;;;;;AAQxC,SAAS,mBACP,KACA,cACA,gBACS;CACT,MAAM,aAAa,SAAS,IAAI;AAChC,KAAI,CAAC,WAAY,QAAO;CACxB,MAAM,aAAa,eAAe;AAClC,QAAO,EAAE,cAAcA,KAAG,WAAW,WAAW,KAAK,CAAC,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;AAsB9E,SAAS,+BACP,QACA,cACA,kBAA2B,OACnB;AACR,KAAI,CAAC,OAAO,MAAM,CAAE,QAAO;CAG3B,IAAI;AACJ,KAAI;AACF,QAAMC,MAAW,QAAQ;GACvB,YAAY;GACZ,eAAe;GACf,SAAS,CAAC,OAAO,aAAa;GAC/B,CAAC;SACI;AAEN,SAAO;;CAGT,MAAM,eAAoE,EAAE;CAG5E,MAAM,0BAA0B,YAAiB;AAC/C,MACE,CAAC,WAGA,QAAQ,SAAS,mBAChB,QAAQ,SAAS,sBACnB,OAAO,QAAQ,UAAU,YACzB,OAAO,QAAQ,UAAU,YACzB,OAAO,QAAQ,QAAQ,SAEvB;EAEF,MAAM,SAAS,aAAa,QAAQ,OAAO,OAAO;AAClD,MAAI,CAAC,OAAQ;EAEb,MAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,MAAM;AACpD,eAAa,KAAK;GAChB,OAAO,QAAQ;GACf,KAAK,QAAQ;GACb,MAAM,GAAG,QAAQ,SAAS;GAC3B,CAAC;;AAIJ,KAAI,iBAAiB;EACnB,MAAM,UAAU,IAAI;EACpB,MAAM,OAAQ,SAAS,QAAQ,EAAE;AAIjC,MAAI,KAAK,WAAW,KAAK,KAAK,IAAI,SAAS,sBACzC,wBAAuB,KAAK,GAAG,WAAW;WACjC,KAAK,WAAW,KAAK,SAAS,YAAY,WAAW,EAE9D,wBAAuB,QAAQ,WAAW,IAAI,MAAM;;CAMxD,MAAM,uBAAO,IAAI,KAAU;CAE3B,MAAM,QAAQ,SAAc;AAC1B,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,MAAI,MAAM,QAAQ,KAAK,EAAE;AACvB,QAAK,MAAM,QAAQ,KAAM,MAAK,KAAK;AACnC;;AAEF,MAAI,KAAK,IAAI,KAAK,CAAE;AACpB,OAAK,IAAI,KAAK;AAEd,MACE,KAAK,SAAS,kBACd,KAAK,MAAM,SAAS,mBACpB,2BAA2B,IAAI,KAAK,KAAK,KAAK,IAC9C,KAAK;OAED,KAAK,MAAM,SAAS,gBACtB,wBAAuB,KAAK,MAAM;YAElC,KAAK,MAAM,SAAS,4BACpB,KAAK,MAAM,YAAY,SAAS,gBAEhC,wBAAuB,KAAK,MAAM,WAAW;;AAIjD,OAAK,MAAM,OAAO,MAAM;AACtB,OACE,QAAQ,SACR,QAAQ,WACR,QAAQ,SACR,QAAQ,QAER;GAEF,MAAM,QAAQ,KAAK;AACnB,OAAI,SAAS,OAAO,UAAU,SAAU,MAAK,MAAM;;;AAGvD,MAAK,IAAI,WAAW,IAAI;AAExB,KAAI,aAAa,WAAW,EAAG,QAAO;CAGtC,IAAI,SAAS;AACb,cACG,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CACjC,SAAS,EAAE,OAAO,KAAK,WAAW;AACjC,WAAS,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,OAAO,MAAM,IAAI;GAC1D;AACJ,QAAO;;;;;;;;;;;;;;;AAkBT,eAA8B,mBAC5B,UACA,eACA,cACA;AACA,KACE,CAAC,SAAS,SACT,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC,WAAW,KACvD,SAAS,MAAM,iBAAiB,GAElC;CAEF,MAAM,EAAE,eAAe,gBAAgB,SAAS;CAChD,MAAM,kBAAkB,SAAS,SAAS;CAC1C,MAAM,wBACJ,OAAO,oBAAoB,YAC3B,gBAAgB,0BAA0B;CAG5C,MAAM,UAAU,iBAAiB,SAAS;CAE1C,MAAM,cAAc,kBAClB,aACA,SAAS,MAAM,kBACf,SAAS,MAAM,kBAAkB,EAAE,EACnC,SAAS,MAAM,oBAAoB,EAAE,EACrC,SAAS,SACT,SAAS,cACV;CAGD,MAAM,kBAAkB,EAAE;AAK1B,KACE,CAAC,YAAY,SAAS,kBACtB,QAAQ,SAAS,SAAS,cAAc,IACxC,CAAC,cACD;EACA,MAAM,qBAA+B,EAAE;AAGvC,MAAI,YAAY,GACd,oBAAmB,KAAK,GAAG,YAAY,GAAG;AAE5C,MAAI,YAAY,IACd,oBAAmB,KAAK,GAAG,YAAY,IAAI;AAG7C,MAAI,mBAAmB,SAAS,GAAG;GACjC,MAAM,iBAAiB,QAAQ,IAC7B,mBAAmB,IAAI,OAAO,aAAqB;AAEjD,QAAI,CAACD,KAAG,WAAW,SAAS,CAC1B;IAKF,MAAM,SAAS,0BACb,MAHwBA,KAAG,SAAS,SAAS,UAAU,OAAO,EAI9D,SAAS,eACT,SAAS,eACT,SAAS,SAAS,iCAAiC,OACnD,SAAS,SAAS,gBAClB,SAAS,SAAS,mBAClB,SAAS,SAAS,WACnB;AAED,QAAI,OAAO,WACT,OAAMA,KAAG,SAAS,UAAU,UAAU,OAAO,QAAQ;KAEvD,CACH;AACD,mBAAgB,KAAK,eAAe;;;CAKxC,MAAM,kBAAkB,OAAO,QAAQ,YAAY,CAChD,QAAQ,CAAC,YAAY,QAAQ,SAAS,OAAO,CAAC,CAC9C,IAAI,OAAO,CAAC,QAAQ,cAAc;EAEjC,MAAM,cAAc,OAAO,OAAO,SAAS,CAAC,QACzC,MACC,gBAAgB,MAAM,QAAQ,EAAE,SAAS,IAAI,CAAC,KAC7C,CAAC,gBAAgB,aAAa,IAAI,EAAE,EACxC;AAGD,QAAM,QAAQ,IACZ,YAAY,IAAI,OAAO,aAAa;AAElC,OAAI,CAACA,KAAG,WAAW,SAAS,CAC1B;GAKF,MAAM,SAAS,0BACb,MAHwBA,KAAG,SAAS,SAAS,UAAU,OAAO,EAI9D,SAAS,eACT,QACA,SAAS,SAAS,iCAAiC,OACnD,SAAS,SAAS,gBAClB,SAAS,SAAS,mBAClB,SAAS,SAAS,YAClB,wBAAwB,WAAW,KAAA,EACpC;AAED,OAAI,OAAO,WACT,OAAMA,KAAG,SAAS,UAAU,UAAU,OAAO,QAAQ;IAEvD,CACH;GACD;AACJ,iBAAgB,KAAK,GAAG,gBAAgB;AAExC,OAAM,QAAQ,IAAI,gBAAgB;;;;;AAgBpC,SAAS,iBACP,aACA,aACA,cACA,eACA,YACS;AAET,KAAI,QAAQ,KAAK,YAAY,CAC3B,QAAO;CAGT,MAAM,sBAAsB,YAAY,QAAQ,OAAO,GAAG;CAG1D,IAAI,aAAa;AACjB,KAAI,cAAc,YAAY,WAAW,WAAW,CAClD,cAAa,YAAY,UAAU,WAAW,OAAO;AAGvD,KAAI,iBAAiB,cAEnB,QAAO,WAAW,SAAS,oBAAoB;KAG/C,QAAO,WAAW,WAAW,oBAAoB;;;;;AAOrD,SAAS,yBACP,aACA,YACS;AACT,QAAO,YAAY,WAAW,WAAW;;;;;AAM3C,SAAS,cACP,aACA,SACA,eACS;AAIT,QAHwB,QAAQ,KAAK,MACnC,EAAE,QAAQ,eAAe,cAAc,CAEnB,CAAC,MAAM,YAAY,QAAQ,aAAa,QAAQ,CAAC;;;;;AAMzE,SAAgB,iBACd,aACA,aACA,cACA,eACA,mBACe;CACf,MAAM,oBAAoB,YACvB,MAAM,IAAI,CACV,QAAQ,SAAS,SAAS,GAAG;CAChC,MAAM,mBAAmB,YAAY,MAAM,IAAI,CAAC,QAAQ,SAAS,SAAS,GAAG;AAG7E,KAAI,CAAC,0BAA0B,mBAAmB,iBAAiB,CACjE,QAAO;AAGT,KAAI,iBAAiB,SAAS,kBAAkB,OAC9C,QAAO;CAGT,IAAI,SAAS;AACb,KAAI,iBAAiB,cACnB,KAAI,mBAAmB;AAErB,MAAI,oBAAoB,iBAAiB,YAAY,cACnD,QAAO;AAST,WAAS,CAJP,GAAG,kBAAkB,MAAM,GAAG,iBAAiB,OAAO,EACtD,GAAG,kBAAkB,MAAM,iBAAiB,SAAS,EAAE,CAGpC,CAAC,KAAK,IAAI;QAC1B;AAEL,MAAI,oBAAoB,iBAAiB,YAAY,cACnD,QAAO;AAUT,WAAS;GALP,GAAG,kBAAkB,MAAM,GAAG,iBAAiB,OAAO;GACtD;GACA,GAAG,kBAAkB,MAAM,iBAAiB,OAAO;GAGhC,CAAC,KAAK,IAAI;;UAExB,mBAAmB;AAE5B,MAAI,oBAAoB,iBAAiB,YAAY,aACnD,QAAO;AAQT,WAAS;GALP,GAAG,kBAAkB,MAAM,GAAG,iBAAiB,OAAO;GACtD;GACA,GAAG,kBAAkB,MAAM,iBAAiB,OAAO;GAGhC,CAAC,KAAK,IAAI;QAC1B;AAEL,MAAI,oBAAoB,iBAAiB,YAAY,cACnD,QAAO;EAIT,MAAM,eAAe,CAAC,GAAG,kBAAkB;AAC3C,eAAa,iBAAiB,UAAU;AAExC,WAAS,aAAa,KAAK,IAAI;;AAIjC,KAAI,YAAY,WAAW,IAAI,CAC7B,UAAS,MAAM;AAEjB,KAAI,YAAY,SAAS,IAAI,CAC3B,UAAS,SAAS;AAGpB,QAAO;;;;;AAMT,SAAS,iBACP,YACA,eACA,cACA,mBACA,UAAkB,aAClB,UAAoB,EAAE,EACtB,YACA,gBACoB;CACpB,MAAM,kBAID,EAAE;AAEP,KAAI,CAAC,QAAQ,WAAW,IAAI,CAC1B,WAAU,MAAM;CAGlB,MAAM,cAAc,QAAQ,MAAM,WAAW,CAAC;AAI9C,KAAI,iBAAiB,eAAe;EAIlC,MAAM,sBAAsB,YAAY,QAAQ,OAAO,GAAG;AAC1D,MAAI,CAAC,WAAW,SAAS,oBAAoB,CAC3C,QAAO;GACL,SAAS;GACT,YAAY;GACZ,iBAAiB,EAAE;GACpB;YAIC,CAAC,WAAW,SAAS,YAAY,QAAQ,OAAO,GAAG,CAAC,CACtD,QAAO;EACL,SAAS;EACT,YAAY;EACZ,iBAAiB,EAAE;EACpB;CAKL,IAAI;CACJ,IAAI;AACJ,KAAI;EACF,MAAM,SAAS,qBAAqB,WAAW;AAC/C,iBAAe,OAAO;AACtB,uBAAqB,OAAO;SACtB;AACN,SAAO;GACL,SAAS;GACT,YAAY;GACZ;GACD;;CAIH,MAAM,gBACJ,aACA,aACkB;AAGlB,MAAI,aAAa,YAAY;GAC3B,MAAM,aAAa,QAAQ,KAAK,YAAY;GAC5C,MAAM,iBAAiB,YAAY,WAAW,IAAI;GAClD,MAAM,0BAA0B,aAC5B,yBAAyB,aAAa,WAAW,GACjD;AACJ,OAAI,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,WAClD,QAAO;;AAIX,MACE,CAAC,iBACC,aACA,aACA,cACA,eACA,WACD,CAED,QAAO;AAIT,MAAI,cAAc,yBAAyB,aAAa,WAAW,EAAE;GAEnE,MAAM,cAAc,YAAY,UAAU,WAAW,OAAO;GAE5D,MAAM,kBAAkB,iBACtB,aACA,aACA,cACA,eACA,kBACD;AACD,OAAI,CAAC,gBACH,QAAO;AAET,OACE,kBACA,mBAAmB,aAAa,iBAAiB,eAAe,CAEhE,QAAO;AAET,mBAAgB,KAAK;IACnB,cAAc;IACd,SAAS;IACT,MAAM;IACP,CAAC;AACF,UAAO,kBAAkB,aAAa,kBAAkB;;AAI1D,MAAI,YAAY,MAAM,IAAI,CAAC,GAAG,SAAS,IAAI,CACzC,QAAO;EAIT,MAAM,SAAS,iBACb,aACA,aACA,cACA,eACA,kBACD;AAED,MAAI,CAAC,OACH,QAAO;AAIT,MAAI,cAAc,aAAa,SAAS,cAAc,CACpD,QAAO;AAIT,MACE,kBACA,mBAAmB,aAAa,QAAQ,eAAe,CAEvD,QAAO;AAGT,kBAAgB,KAAK;GACnB,cAAc;GACd,SAAS;GACT,MAAM;GACP,CAAC;AACF,SAAO;;AAIT,OAAM,cAAc,SAAS,SAAe;AAC1C,MAAI,KAAK,KAAK;GACZ,MAAM,SAAS,aAAa,KAAK,KAAK,WAAW;AACjD,OAAI,OACF,MAAK,MAAM;;GAGf;AAGF,OAAM,cAAc,CAAC,qBAAqB,oBAAoB,GAAG,SAAS;EACxE,MAAM,UAAU;AAChB,MAAI,CAAC,QAAQ,WAAY;AACzB,OAAK,MAAM,QAAQ,QAAQ,YAAY;AACrC,OAAI,KAAK,SAAS,qBAAqB,CAAC,KAAK,MAAO;AAGpD,OAAI,OAAO,KAAK,UAAU,UAAU;AAClC,QAAI,2BAA2B,IAAI,KAAK,KAAK,EAAE;KAC7C,MAAM,SAAS,aAAa,KAAK,OAAO,OAAO;AAC/C,SAAI,OACF,MAAK,QAAQ;;AAGjB;;AASF,OACE,OAAO,KAAK,UAAU,YACtB,KAAK,MAAM,SAAS,oCACpB,OAAO,KAAK,MAAM,UAAU,UAC5B;IACA,MAAM,WAAW,+BACf,KAAK,MAAM,OACX,cACA,2BAA2B,IAAI,KAAK,KAAK,CAC1C;AACD,QAAI,aAAa,KAAK,MAAM,MAC1B,MAAK,MAAM,QAAQ;;;GAIzB;AAIF,OAAM,cAAc,CAAC,qBAAqB,oBAAoB,GAAG,SAAS;EACxE,MAAM,WAAW;AACjB,MAAI,OAAO,SAAS,UAAU,YAAY,SAAS,OAAO;GACxD,MAAM,WAAW,+BACf,SAAS,OACT,aACD;AACD,OAAI,aAAa,SAAS,MACxB,UAAS,QAAQ;;GAGrB;AAGF,OAAM,cAAc,QAAQ,SAAkB;AAC5C,MAAI,KAAK,SAAS,OAAO,KAAK,UAAU,UAAU;GAChD,MAAM,aAAa,KAAK;GAGxB,MAAM,YAAY;GAClB,IAAI;GACJ,MAAM,eAKD,EAAE;AAGP,aAAU,YAAY;AAEtB,WAAQ,QAAQ,UAAU,KAAK,WAAW,MAAM,MAAM;IACpD,MAAM,eAAe,MAAM;IAC3B,MAAM,SAAS,aAAa,cAAc,OAAO;AAEjD,QAAI,QAAQ;KAEV,MAAM,cAAc,MAAM;KAC1B,MAAM,QAAQ,YAAY,SAAS,KAAI,GAAG,OAAM;KAChD,MAAM,cAAc,QAAQ,QAAQ,SAAS;AAE7C,kBAAa,KAAK;MAChB,OAAO,MAAM;MACb,KAAK,MAAM,QAAS,YAAY;MAChC;MACA;MACD,CAAC;;;AAKN,OAAI,aAAa,SAAS,GAAG;IAC3B,IAAI,gBAAgB;AACpB,iBACG,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CACjC,SAAS,EAAE,OAAO,KAAK,kBAAkB;AACxC,qBACE,cAAc,MAAM,GAAG,MAAM,GAC7B,cACA,cAAc,MAAM,IAAI;MAC1B;AAEJ,SAAK,QAAQ;;;GAGjB;CAGF,IAAI;AACJ,KAAI;EACF,MAAM,qBAAqB,SAAS,CACjC,IAAI,mBAAmB,CAAC,QAAQ,OAAO,CAAC,CACxC,IAAI,UAAU,CACd,IAAI,uBAAuB,CAC3B,IAAI,sBAAsB,CAC1B,IAAI,iBAAiB,EACpB,UAAU,EAER,KAAK,MAAe;AAClB,UAAO,KAAK;KAEf,EACF,CAAC;EAEJ,MAAM,UAAU,mBAAmB,QAAQ,aAAa;AACxD,YAAU,mBAAmB,UAAU,QAAQ;AAC/C,MAAI,mBACF,WAAU,iBAAiB,QAAQ;UAE9B,OAAO;AACd,UAAQ,KACN,oCAAoC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAC3F;AACD,UAAQ,KACN,+DACD;AACD,SAAO;GACL,SAAS;GACT,YAAY;GACZ,iBAAiB,EAAE;GACpB;;AAIH,KAAI,QAAQ,SAAS,KAAK,IAAI,CAAC,WAAW,SAAS,KAAK,CACtD,WAAU,QAAQ,MAAM,GAAG,GAAG;AAIhC,KAAI,WAAW,WAAW,KAAK,IAAI,CAAC,QAAQ,WAAW,KAAK,CAC1D,WAAU,OAAO;AAGnB,QAAO;EACL;EACA,YAAY,gBAAgB,SAAS;EACrC;EACD;;AAIH,SAAS,0BACP,MACA,eACA,cACA,mBACA,UAAkB,aAClB,UAAoB,EAAE,EACtB,YACA,gBACoB;AAEpB,QAAO,iBACL,MACA,eACA,cACA,mBACA,SACA,SACA,cAAc,IACd,eACD;;AAGH,SAAS,0BACP,kBACA,kBACS;AAET,MAAK,IAAI,IAAI,GAAG,IAAI,iBAAiB,QAAQ,IAC3C,KAAI,iBAAiB,OAAO,mBAAmB,GAC7C,QAAO;AAIX,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gt",
3
- "version": "2.22.3",
3
+ "version": "2.22.4",
4
4
  "main": "dist/index.js",
5
5
  "bin": "bin/main.js",
6
6
  "files": [