gt 2.20.2-auto-jsx.0 → 2.20.3
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 +8 -2
- package/dist/cli/flags.js +1 -1
- package/dist/cli/flags.js.map +1 -1
- package/dist/config/generateSettings.js +1 -1
- package/dist/config/generateSettings.js.map +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/utils/addExplicitAnchorIds.js +17 -3
- package/dist/utils/addExplicitAnchorIds.js.map +1 -1
- package/dist/workflows/stage.js +1 -1
- package/dist/workflows/stage.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# gtx-cli
|
|
2
2
|
|
|
3
|
-
## 2.20.
|
|
3
|
+
## 2.20.3
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- [#2260](https://github.com/generaltranslation/gt/pull/2260) [`8a3c49c`](https://github.com/generaltranslation/gt/commit/8a3c49cbf48b09d1687eb3e5202bd12c4221d3ad) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - `gt translate` no longer saves local edits to translated output by default. Saving local edits is now opt-in via `--save-local` or `options.saveLocal: true` in `gt.config.json`, so running the CLI locally does not overwrite production translations.
|
|
8
|
+
|
|
9
|
+
## 2.20.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#2256](https://github.com/generaltranslation/gt/pull/2256) [`210e98e`](https://github.com/generaltranslation/gt/commit/210e98eaf746e4340b29f8e6751bf4959d5c1840) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - fix(cli): wrap headings with author-written `{#id}` anchors in a `<div id>` like every other heading when `experimentalAddHeaderAnchorIds` is `mintlify`. Re-attaching the inline `{#id}` broke Mintlify builds when the heading sat inside nested JSX, because the MDX serializer indents it four spaces and Mintlify no longer recognizes the anchor there.
|
|
8
14
|
|
|
9
15
|
## 2.20.1
|
|
10
16
|
|
package/dist/cli/flags.js
CHANGED
|
@@ -12,7 +12,7 @@ function attachTranslateFlags(command) {
|
|
|
12
12
|
if (isNaN(parsedValue)) throw new Error("Invalid timeout: not a number.");
|
|
13
13
|
if (parsedValue < 0) throw new Error("Invalid timeout: must be a positive number.");
|
|
14
14
|
return parsedValue;
|
|
15
|
-
}, DEFAULT_TIMEOUT).option("--save-local", "Detect and save local edits before enqueuing translations (default:
|
|
15
|
+
}, DEFAULT_TIMEOUT).option("--save-local", "Detect and save local edits before enqueuing translations (default: false; configurable via options.saveLocal in gt.config.json)").option("--no-save-local", "Skip detecting and saving local edits before enqueuing translations").option("--publish", "Publish translations to the CDN", false).option("--experimental-localize-static-urls", "Triggering this will run a script after the cli tool that localizes all urls in content files. Currently only supported for md and mdx files.", false).option("--experimental-hide-default-locale", "When localizing static locales, hide the default locale from the path", false).option("--experimental-flatten-json-files", "Triggering this will flatten the json files into a single file. This is useful for projects that have a lot of json files.", false).option("--experimental-localize-static-imports", "Triggering this will run a script after the cli tool that localizes all static imports in content files. Currently only supported for md and mdx files.", false).option("--experimental-localize-relative-assets", "Triggering this will rewrite relative image asset URLs in translated md/mdx files to valid paths.", false).option("--force", "Force a retranslation, invalidating all existing cached translations if they exist.", false).option("--force-download", "Force download and overwrite local files, bypassing gt-lock.json checks.", false).option("--omit-config-ids", "Do not write _versionId or _branchId to gt.config.json").option("--experimental-clear-locale-dirs", "Clear locale directories before downloading new translations", false).option("--branch <branch>", "Specify a custom branch to use for translations").option("--disable-branch-detection", "Disable additional branch detection and optimizations and use the manually specified branch", false).option("--enable-branching", "Enable branching for the project").option("--remote-name <name>", "Specify a custom remote name to use for branch detection", DEFAULT_GIT_REMOTE_NAME).option("--tag [value]", "Tag this translation run (auto-resolves from git if no value provided)").option("-m, --message <message>", "Message to attach to the translation tag");
|
|
16
16
|
return command;
|
|
17
17
|
}
|
|
18
18
|
/**
|
package/dist/cli/flags.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flags.js","names":[],"sources":["../../src/cli/flags.ts"],"sourcesContent":["import { Command } from 'commander';\nimport findFilepath from '../fs/findFilepath.js';\nimport { DEFAULT_GIT_REMOTE_NAME } from '../utils/constants.js';\n\nconst DEFAULT_TIMEOUT = 900;\n\nexport function attachSharedFlags(command: Command) {\n command\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option('--api-key <key>', 'API key for General Translation cloud service')\n .option('--project-id <id>', 'General Translation project ID');\n return command;\n}\n\nexport function attachTranslateFlags(command: Command) {\n attachSharedFlags(command)\n .option('--version-id <id>', 'General Translation version ID')\n .option(\n '--default-language, --default-locale <locale>',\n 'Default locale (e.g., en)'\n )\n .option(\n '--new, --locales <locales...>',\n 'Space-separated list of locales (e.g., en fr es)'\n )\n .option(\n '--dry-run',\n 'Dry run, do not send updates to the General Translation API',\n false\n )\n .option(\n '--timeout <seconds>',\n 'Translation wait timeout in seconds',\n (value) => {\n const parsedValue = parseInt(value, 10);\n if (isNaN(parsedValue)) {\n throw new Error('Invalid timeout: not a number.');\n }\n if (parsedValue < 0) {\n throw new Error('Invalid timeout: must be a positive number.');\n }\n return parsedValue;\n },\n DEFAULT_TIMEOUT\n )\n .option(\n '--save-local',\n 'Detect and save local edits before enqueuing translations (default:
|
|
1
|
+
{"version":3,"file":"flags.js","names":[],"sources":["../../src/cli/flags.ts"],"sourcesContent":["import { Command } from 'commander';\nimport findFilepath from '../fs/findFilepath.js';\nimport { DEFAULT_GIT_REMOTE_NAME } from '../utils/constants.js';\n\nconst DEFAULT_TIMEOUT = 900;\n\nexport function attachSharedFlags(command: Command) {\n command\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option('--api-key <key>', 'API key for General Translation cloud service')\n .option('--project-id <id>', 'General Translation project ID');\n return command;\n}\n\nexport function attachTranslateFlags(command: Command) {\n attachSharedFlags(command)\n .option('--version-id <id>', 'General Translation version ID')\n .option(\n '--default-language, --default-locale <locale>',\n 'Default locale (e.g., en)'\n )\n .option(\n '--new, --locales <locales...>',\n 'Space-separated list of locales (e.g., en fr es)'\n )\n .option(\n '--dry-run',\n 'Dry run, do not send updates to the General Translation API',\n false\n )\n .option(\n '--timeout <seconds>',\n 'Translation wait timeout in seconds',\n (value) => {\n const parsedValue = parseInt(value, 10);\n if (isNaN(parsedValue)) {\n throw new Error('Invalid timeout: not a number.');\n }\n if (parsedValue < 0) {\n throw new Error('Invalid timeout: must be a positive number.');\n }\n return parsedValue;\n },\n DEFAULT_TIMEOUT\n )\n .option(\n '--save-local',\n 'Detect and save local edits before enqueuing translations (default: false; configurable via options.saveLocal in gt.config.json)'\n )\n .option(\n '--no-save-local',\n 'Skip detecting and saving local edits before enqueuing translations'\n )\n .option('--publish', 'Publish translations to the CDN', false)\n .option(\n '--experimental-localize-static-urls',\n 'Triggering this will run a script after the cli tool that localizes all urls in content files. Currently only supported for md and mdx files.',\n false\n )\n .option(\n '--experimental-hide-default-locale',\n 'When localizing static locales, hide the default locale from the path',\n false\n )\n .option(\n '--experimental-flatten-json-files',\n 'Triggering this will flatten the json files into a single file. This is useful for projects that have a lot of json files.',\n false\n )\n .option(\n '--experimental-localize-static-imports',\n 'Triggering this will run a script after the cli tool that localizes all static imports in content files. Currently only supported for md and mdx files.',\n false\n )\n .option(\n '--experimental-localize-relative-assets',\n 'Triggering this will rewrite relative image asset URLs in translated md/mdx files to valid paths.',\n false\n )\n .option(\n '--force',\n 'Force a retranslation, invalidating all existing cached translations if they exist.',\n false\n )\n .option(\n '--force-download',\n 'Force download and overwrite local files, bypassing gt-lock.json checks.',\n false\n )\n .option(\n '--omit-config-ids',\n 'Do not write _versionId or _branchId to gt.config.json'\n )\n .option(\n '--experimental-clear-locale-dirs',\n 'Clear locale directories before downloading new translations',\n false\n )\n .option(\n '--branch <branch>',\n 'Specify a custom branch to use for translations'\n )\n .option(\n '--disable-branch-detection',\n 'Disable additional branch detection and optimizations and use the manually specified branch',\n false\n )\n .option('--enable-branching', 'Enable branching for the project')\n .option(\n '--remote-name <name>',\n 'Specify a custom remote name to use for branch detection',\n DEFAULT_GIT_REMOTE_NAME\n )\n .option(\n '--tag [value]',\n 'Tag this translation run (auto-resolves from git if no value provided)'\n )\n .option(\n '-m, --message <message>',\n 'Message to attach to the translation tag'\n );\n return command;\n}\n\n/**\n * Attaches flags necessary for parsing inline content\n * @param command - The command to attach the flags to\n * @returns The command with the inline content parsing flags attached\n */\nfunction attachInlineContentParsingFlags(\n command: Command,\n sourceHelp?: string\n) {\n return command\n .option(\n '--tsconfig, --jsconfig <path>',\n 'Path to custom jsconfig or tsconfig file',\n findFilepath(['./tsconfig.json', './jsconfig.json'])\n )\n .option('--dictionary <path>', 'Path to dictionary file')\n .option(\n '--src <paths...>',\n sourceHelp ??\n \"Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'\"\n )\n .option(\n '--inline',\n 'Include inline content in translations (e.g., inline jsx translations, inline string translations, etc.)',\n true\n );\n}\n\n/**\n * Attaches flags necessary for validating a project\n * @param command\n * @returns The command with the validate flags attached\n */\nexport function attachValidateFlags(command: Command, sourceHelp?: string) {\n return attachInlineContentParsingFlags(\n command.option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n ),\n sourceHelp\n );\n}\n\n/**\n * Attaches flags necessary for translating a project\n * @param command\n * @returns The command with the translate flags attached\n */\nexport function attachInlineTranslateFlags(\n command: Command,\n sourceHelp?: string\n) {\n return attachInlineContentParsingFlags(\n command.option(\n '--ignore-errors',\n 'Ignore errors encountered while scanning for inline content',\n false\n ),\n sourceHelp\n );\n}\n"],"mappings":";;;AAIA,MAAM,kBAAkB;AAExB,SAAgB,kBAAkB,SAAkB;AAClD,SACG,OACC,uBACA,sDACA,aAAa,CAAC,iBAAiB,CAAC,CACjC,CACA,OAAO,mBAAmB,gDAAgD,CAC1E,OAAO,qBAAqB,iCAAiC;AAChE,QAAO;;AAGT,SAAgB,qBAAqB,SAAkB;AACrD,mBAAkB,QAAQ,CACvB,OAAO,qBAAqB,iCAAiC,CAC7D,OACC,iDACA,4BACD,CACA,OACC,iCACA,mDACD,CACA,OACC,aACA,+DACA,MACD,CACA,OACC,uBACA,wCACC,UAAU;EACT,MAAM,cAAc,SAAS,OAAO,GAAG;AACvC,MAAI,MAAM,YAAY,CACpB,OAAM,IAAI,MAAM,iCAAiC;AAEnD,MAAI,cAAc,EAChB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,SAAO;IAET,gBACD,CACA,OACC,gBACA,mIACD,CACA,OACC,mBACA,sEACD,CACA,OAAO,aAAa,mCAAmC,MAAM,CAC7D,OACC,uCACA,iJACA,MACD,CACA,OACC,sCACA,yEACA,MACD,CACA,OACC,qCACA,8HACA,MACD,CACA,OACC,0CACA,2JACA,MACD,CACA,OACC,2CACA,qGACA,MACD,CACA,OACC,WACA,uFACA,MACD,CACA,OACC,oBACA,4EACA,MACD,CACA,OACC,qBACA,yDACD,CACA,OACC,oCACA,gEACA,MACD,CACA,OACC,qBACA,kDACD,CACA,OACC,8BACA,+FACA,MACD,CACA,OAAO,sBAAsB,mCAAmC,CAChE,OACC,wBACA,4DACA,wBACD,CACA,OACC,iBACA,yEACD,CACA,OACC,2BACA,2CACD;AACH,QAAO;;;;;;;AAQT,SAAS,gCACP,SACA,YACA;AACA,QAAO,QACJ,OACC,iCACA,4CACA,aAAa,CAAC,mBAAmB,kBAAkB,CAAC,CACrD,CACA,OAAO,uBAAuB,0BAA0B,CACxD,OACC,oBACA,cACE,0MACH,CACA,OACC,YACA,4GACA,KACD;;;;;;;AAQL,SAAgB,oBAAoB,SAAkB,YAAqB;AACzE,QAAO,gCACL,QAAQ,OACN,uBACA,sDACA,aAAa,CAAC,iBAAiB,CAAC,CACjC,EACD,WACD;;;;;;;AAQH,SAAgB,2BACd,SACA,YACA;AACA,QAAO,gCACL,QAAQ,OACN,mBACA,+DACA,MACD,EACD,WACD"}
|
|
@@ -120,7 +120,7 @@ async function generateSettings(flags, cwd = process.cwd(), options) {
|
|
|
120
120
|
experimentalFlattenJsonFiles: gtConfig.options?.experimentalFlattenJsonFiles || flags.experimentalFlattenJsonFiles,
|
|
121
121
|
experimentalClearLocaleDirs: gtConfig.options?.experimentalClearLocaleDirs || flags.experimentalClearLocaleDirs,
|
|
122
122
|
clearLocaleDirsExclude: gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,
|
|
123
|
-
saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ??
|
|
123
|
+
saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? false
|
|
124
124
|
};
|
|
125
125
|
if (mergedOptions.omitConfigIds && (mergedOptions.publish === true || mergedOptions.files.gtJson.publish === true)) logger.warn(chalk.yellow("Config IDs will be omitted even though CDN publishing is enabled. Remote cache/CDN consumers may load the latest available translations instead of a pinned version."));
|
|
126
126
|
if (mergedOptions.options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateSettings.js","names":[],"sources":["../../src/config/generateSettings.ts"],"sourcesContent":["import {\n displayProjectId,\n exitSync,\n logErrorAndExit,\n warnApiKeyInConfig,\n warnDeprecatedField,\n} from '../console/logging.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { FilesOptions, Settings } from '../types/index.js';\nimport {\n defaultBaseUrl,\n libraryDefaultLocale,\n} from 'generaltranslation/internal';\nimport { resolveFiles } from '../fs/config/parseFilesConfig.js';\nimport { validateSettings } from './validateSettings.js';\nimport {\n DEFAULT_GIT_REMOTE_NAME,\n GT_DASHBOARD_URL,\n} from '../utils/constants.js';\nimport { resolveProjectId } from '../fs/utils.js';\nimport crypto from 'node:crypto';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { resolveConfig } from './resolveConfig.js';\nimport { configureApiClient } from '../utils/api.js';\nimport { gt } from '../utils/gt.js';\nimport { generatePreset } from './optionPresets.js';\nimport { GT_PARSING_FLAGS_DEFAULT } from './defaults.js';\nimport { normalizeFilesOptions } from '../formats/files/transformFormat.js';\nimport { determineLibrary } from '../fs/determineFramework/index.js';\nimport { logger } from '../console/logger.js';\n\nexport const DEFAULT_SRC_PATTERNS = [\n 'src/**/*.{js,jsx,ts,tsx}',\n 'app/**/*.{js,jsx,ts,tsx}',\n 'pages/**/*.{js,jsx,ts,tsx}',\n 'components/**/*.{js,jsx,ts,tsx}',\n];\n\nexport const DEFAULT_PYTHON_SRC_PATTERNS = ['**/*.py'];\nexport const DEFAULT_PYTHON_SRC_EXCLUDES = [\n 'venv/**',\n '.venv/**',\n '__pycache__/**',\n '**/migrations/**',\n '**/tests/**',\n '**/test_*.py',\n '**/*_test.py',\n];\n\ntype GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {\n config?: string;\n projectId?: string;\n locales?: string[];\n options?: Settings['options'];\n files?: unknown;\n publish?: boolean;\n omitConfigIds?: boolean;\n saveLocal?: boolean;\n tag?: string | boolean;\n message?: string;\n branch?: string;\n enableBranching?: boolean;\n disableBranchDetection?: boolean;\n remoteName?: string;\n experimentalLocalizeStaticImports?: boolean;\n experimentalLocalizeStaticUrls?: boolean;\n experimentalLocalizeRelativeAssets?: boolean;\n experimentalHideDefaultLocale?: boolean;\n experimentalFlattenJsonFiles?: boolean;\n experimentalClearLocaleDirs?: boolean;\n clearLocaleDirsExclude?: string[];\n [key: string]: unknown;\n};\n\nfunction hasConfiguredTranslationFiles(files: unknown): boolean {\n if (!files || typeof files !== 'object' || Array.isArray(files)) {\n return false;\n }\n return Object.keys(files).some((key) => key !== 'gt');\n}\n\n/**\n * Generates settings from any\n * @param flags - The CLI flags to generate settings from\n * @param cwd - The current working directory\n * @param options - Additional options\n * @param options.requireConfig - If true, exit with an error when no config file is found\n * @returns The generated settings\n */\nexport async function generateSettings(\n flags: GenerateSettingsInput,\n cwd: string = process.cwd(),\n options?: { requireConfig?: boolean }\n): Promise<Settings> {\n // Load config file\n let gtConfig: GenerateSettingsInput = {};\n\n if (flags.config && !flags.config.endsWith('.json')) {\n flags.config = `${flags.config}.json`;\n }\n if (flags.config) {\n gtConfig = loadConfig(flags.config);\n } else {\n const config = resolveConfig(cwd);\n if (config) {\n gtConfig = config.config as GenerateSettingsInput;\n flags.config = config.path;\n } else {\n if (options?.requireConfig) {\n return logErrorAndExit(\n 'No gt.config.json file was found. Run `npx gt init` to create one, pass --config, or run this command from your project root.'\n );\n }\n gtConfig = {};\n }\n }\n\n // Warn if apiKey is present in gt.config.json\n if (gtConfig.apiKey) {\n warnApiKeyInConfig(flags.config ?? 'gt.config.json');\n exitSync(1);\n }\n const projectIdEnv = resolveProjectId();\n // Resolve mismatched projectIds\n if (\n gtConfig.projectId &&\n flags.projectId &&\n gtConfig.projectId !== flags.projectId\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but the CLI flag uses ${chalk.green(flags.projectId)}. Use the same projectId in all configs.`\n );\n } else if (\n gtConfig.projectId &&\n projectIdEnv &&\n gtConfig.projectId !== projectIdEnv\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but GT_PROJECT_ID uses ${chalk.green(projectIdEnv)}. Use the same projectId in all configs.`\n );\n }\n\n if (\n flags.options?.docsUrlPattern &&\n !flags.options?.docsUrlPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static URLs could not be localized because the URL pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated URL.'\n );\n }\n\n if (\n flags.options?.docsImportPattern &&\n !flags.options?.docsImportPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static imports could not be localized because the import pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated import path.'\n );\n }\n\n if (flags.options?.copyFiles) {\n for (const file of flags.options.copyFiles) {\n if (!file.includes('[locale]')) {\n logErrorAndExit(\n 'Files could not be copied because the file path is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the copied path.'\n );\n }\n }\n }\n\n // Warn on deprecated includeSourceCodeContext\n const configuredFiles = gtConfig.files as FilesOptions | undefined;\n if (configuredFiles?.gt?.includeSourceCodeContext != null) {\n warnDeprecatedField(\n 'files.gt.includeSourceCodeContext',\n 'files.gt.parsingFlags.includeSourceCodeContext'\n );\n }\n\n // merge options\n const mergedOptions: Settings = { ...gtConfig, ...flags } as Settings;\n\n if (\n determineLibrary().library === 'base' &&\n !hasConfiguredTranslationFiles(mergedOptions.files)\n ) {\n logger.warn(\n chalk.yellow(\n 'No package.json or Python project file found in the current directory. Run this command from the root of your project.'\n )\n );\n }\n\n // Add defaultLocale if not provided\n mergedOptions.defaultLocale =\n mergedOptions.defaultLocale || libraryDefaultLocale;\n\n // merge locales\n mergedOptions.locales = Array.from(\n new Set([...(gtConfig.locales || []), ...(flags.locales || [])])\n );\n // Separate defaultLocale from locales\n mergedOptions.locales = mergedOptions.locales.filter(\n (locale) => locale !== mergedOptions.defaultLocale\n );\n\n // Add apiKey if not provided\n mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;\n\n // Add projectId if not provided\n mergedOptions.projectId = mergedOptions.projectId || resolveProjectId();\n\n // Add baseUrl if not provided\n mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;\n\n // Add dashboardUrl if not provided\n mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;\n\n // Add locales if not provided\n mergedOptions.locales = mergedOptions.locales || [];\n\n // Only set config path if one was actually found or explicitly provided.\n // Do not default to a phantom path — that would cause downstream writes\n // (e.g. updateConfig) to silently create a config file.\n if (!mergedOptions.config) {\n mergedOptions.config = '';\n }\n\n // Display projectId if present\n if (mergedOptions.projectId) {\n displayProjectId(mergedOptions.projectId);\n }\n\n // Add stageTranslations if not provided\n // For human review, always stage the project\n mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;\n\n // Top-level default for whether translated files require approval before use.\n // Effective policy is hash-changing, so reject anything but a real boolean.\n if (\n mergedOptions.requiresReview !== undefined &&\n typeof mergedOptions.requiresReview !== 'boolean'\n ) {\n logErrorAndExit(\n 'requiresReview in gt.config.json must be a boolean. Use files.<type>.requiresReview for glob-scoped overrides.'\n );\n }\n mergedOptions.requiresReview = mergedOptions.requiresReview ?? false;\n\n // Add publish — only set if explicitly configured or passed via flag.\n // When neither is set, leave undefined so the publish step knows\n // there is no global publish intent.\n if (flags.publish) {\n mergedOptions.publish = true;\n } else if (gtConfig.publish !== undefined) {\n mergedOptions.publish = gtConfig.publish;\n } else {\n mergedOptions.publish = undefined;\n }\n\n mergedOptions.omitConfigIds =\n flags.omitConfigIds === true || gtConfig.omitConfigIds === true;\n\n // Don't default src here — each pipeline (JS/Python) has its own defaults.\n // Only set src if the user explicitly provided it via flags or config.\n\n // Resolve all glob patterns in the files object\n const compositePatterns = Object.entries(\n mergedOptions.options?.jsonSchema || {}\n )\n .filter(([, schema]) => schema.composite)\n .map(([key]) => key);\n mergedOptions.files = mergedOptions.files\n ? resolveFiles(\n normalizeFilesOptions(mergedOptions.files as FilesOptions),\n mergedOptions.defaultLocale,\n mergedOptions.locales,\n cwd,\n compositePatterns,\n mergedOptions.requiresReview\n )\n : {\n resolvedPaths: {},\n placeholderPaths: {},\n transformPaths: {},\n transformFormats: {},\n publishPaths: new Set<string>(),\n unpublishPaths: new Set<string>(),\n requiresReviewPaths: new Set<string>(),\n parsingFlags: {},\n gtJson: {\n parsingFlags: GT_PARSING_FLAGS_DEFAULT,\n },\n };\n\n mergedOptions.options = {\n ...mergedOptions.options,\n mintlify: {\n ...mergedOptions.options?.mintlify,\n inferTitleFromFilename:\n gtConfig.options?.mintlify?.inferTitleFromFilename ||\n mergedOptions.options?.mintlify?.inferTitleFromFilename,\n },\n experimentalLocalizeStaticImports:\n gtConfig.options?.experimentalLocalizeStaticImports ||\n flags.experimentalLocalizeStaticImports,\n experimentalLocalizeStaticUrls:\n gtConfig.options?.experimentalLocalizeStaticUrls ||\n flags.experimentalLocalizeStaticUrls,\n experimentalLocalizeRelativeAssets:\n gtConfig.options?.experimentalLocalizeRelativeAssets ||\n flags.experimentalLocalizeRelativeAssets,\n experimentalHideDefaultLocale:\n gtConfig.options?.experimentalHideDefaultLocale ||\n flags.experimentalHideDefaultLocale,\n experimentalFlattenJsonFiles:\n gtConfig.options?.experimentalFlattenJsonFiles ||\n flags.experimentalFlattenJsonFiles,\n experimentalClearLocaleDirs:\n gtConfig.options?.experimentalClearLocaleDirs ||\n flags.experimentalClearLocaleDirs,\n clearLocaleDirsExclude:\n gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,\n saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? true,\n };\n\n if (\n mergedOptions.omitConfigIds &&\n (mergedOptions.publish === true ||\n mergedOptions.files.gtJson.publish === true)\n ) {\n logger.warn(\n chalk.yellow(\n 'Config IDs will be omitted even though CDN publishing is enabled. Remote cache/CDN consumers may load the latest available translations instead of a pinned version.'\n )\n );\n }\n\n // Add additional options if provided\n if (mergedOptions.options) {\n if (mergedOptions.options.jsonSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {\n const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];\n if (jsonSchema.preset) {\n mergedOptions.options.jsonSchema[fileGlob] = {\n ...generatePreset(jsonSchema.preset, 'json'),\n ...jsonSchema,\n };\n }\n }\n }\n if (mergedOptions.options.yamlSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {\n const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];\n if (yamlSchema.preset) {\n mergedOptions.options.yamlSchema[fileGlob] = {\n ...generatePreset(yamlSchema.preset, 'yaml'),\n ...yamlSchema,\n };\n }\n }\n }\n }\n\n // Add parsing options if not provided\n mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};\n mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions\n .conditionNames || [\n 'development',\n 'browser',\n 'module',\n 'import',\n 'require',\n 'default',\n ];\n\n // Add branch options if not provided\n const branchOptions = mergedOptions.branchOptions || {};\n // If --branch is set, enable branching\n branchOptions.enabled =\n flags.enableBranching ??\n gtConfig.branchOptions?.enabled ??\n (flags.branch ? true : false);\n branchOptions.currentBranch =\n flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;\n branchOptions.autoDetectBranches = flags.disableBranchDetection\n ? false\n : (gtConfig.branchOptions?.autoDetectBranches ?? true);\n branchOptions.remoteName =\n flags.remoteName ??\n gtConfig.branchOptions?.remoteName ??\n DEFAULT_GIT_REMOTE_NAME;\n mergedOptions.branchOptions = branchOptions;\n\n // Map -m/--message flag to tagMessage\n if (flags.message) {\n mergedOptions.tagMessage = flags.message;\n }\n\n // Resolve tag:\n // --tag (bare) or -m without --tag: try git SHA, fall back to random hex\n // --tag <value>: use as-is\n // No flags: no tag\n if (flags.tag === true || (!flags.tag && mergedOptions.tagMessage)) {\n try {\n mergedOptions.tag = execSync('git rev-parse --short HEAD', {\n encoding: 'utf-8',\n }).trim();\n // If no message provided, use git commit message\n if (!mergedOptions.tagMessage) {\n mergedOptions.tagMessage = execSync('git log -1 --format=%s', {\n encoding: 'utf-8',\n }).trim();\n }\n } catch {\n // Not in a git repo or git unavailable — fall back to random hex\n mergedOptions.tag = crypto.randomBytes(4).toString('hex');\n }\n }\n\n mergedOptions.configDirectory = path.join(cwd, '.gt');\n\n validateSettings(mergedOptions);\n\n // Keep both clients on the same resolved credentials while consumers migrate.\n configureApiClient({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n customMapping: mergedOptions.customMapping,\n });\n gt.setConfig({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n sourceLocale: mergedOptions.defaultLocale,\n customMapping: mergedOptions.customMapping,\n });\n\n return mergedOptions;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generateSettings.js","names":[],"sources":["../../src/config/generateSettings.ts"],"sourcesContent":["import {\n displayProjectId,\n exitSync,\n logErrorAndExit,\n warnApiKeyInConfig,\n warnDeprecatedField,\n} from '../console/logging.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { FilesOptions, Settings } from '../types/index.js';\nimport {\n defaultBaseUrl,\n libraryDefaultLocale,\n} from 'generaltranslation/internal';\nimport { resolveFiles } from '../fs/config/parseFilesConfig.js';\nimport { validateSettings } from './validateSettings.js';\nimport {\n DEFAULT_GIT_REMOTE_NAME,\n GT_DASHBOARD_URL,\n} from '../utils/constants.js';\nimport { resolveProjectId } from '../fs/utils.js';\nimport crypto from 'node:crypto';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { resolveConfig } from './resolveConfig.js';\nimport { configureApiClient } from '../utils/api.js';\nimport { gt } from '../utils/gt.js';\nimport { generatePreset } from './optionPresets.js';\nimport { GT_PARSING_FLAGS_DEFAULT } from './defaults.js';\nimport { normalizeFilesOptions } from '../formats/files/transformFormat.js';\nimport { determineLibrary } from '../fs/determineFramework/index.js';\nimport { logger } from '../console/logger.js';\n\nexport const DEFAULT_SRC_PATTERNS = [\n 'src/**/*.{js,jsx,ts,tsx}',\n 'app/**/*.{js,jsx,ts,tsx}',\n 'pages/**/*.{js,jsx,ts,tsx}',\n 'components/**/*.{js,jsx,ts,tsx}',\n];\n\nexport const DEFAULT_PYTHON_SRC_PATTERNS = ['**/*.py'];\nexport const DEFAULT_PYTHON_SRC_EXCLUDES = [\n 'venv/**',\n '.venv/**',\n '__pycache__/**',\n '**/migrations/**',\n '**/tests/**',\n '**/test_*.py',\n '**/*_test.py',\n];\n\ntype GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {\n config?: string;\n projectId?: string;\n locales?: string[];\n options?: Settings['options'];\n files?: unknown;\n publish?: boolean;\n omitConfigIds?: boolean;\n saveLocal?: boolean;\n tag?: string | boolean;\n message?: string;\n branch?: string;\n enableBranching?: boolean;\n disableBranchDetection?: boolean;\n remoteName?: string;\n experimentalLocalizeStaticImports?: boolean;\n experimentalLocalizeStaticUrls?: boolean;\n experimentalLocalizeRelativeAssets?: boolean;\n experimentalHideDefaultLocale?: boolean;\n experimentalFlattenJsonFiles?: boolean;\n experimentalClearLocaleDirs?: boolean;\n clearLocaleDirsExclude?: string[];\n [key: string]: unknown;\n};\n\nfunction hasConfiguredTranslationFiles(files: unknown): boolean {\n if (!files || typeof files !== 'object' || Array.isArray(files)) {\n return false;\n }\n return Object.keys(files).some((key) => key !== 'gt');\n}\n\n/**\n * Generates settings from any\n * @param flags - The CLI flags to generate settings from\n * @param cwd - The current working directory\n * @param options - Additional options\n * @param options.requireConfig - If true, exit with an error when no config file is found\n * @returns The generated settings\n */\nexport async function generateSettings(\n flags: GenerateSettingsInput,\n cwd: string = process.cwd(),\n options?: { requireConfig?: boolean }\n): Promise<Settings> {\n // Load config file\n let gtConfig: GenerateSettingsInput = {};\n\n if (flags.config && !flags.config.endsWith('.json')) {\n flags.config = `${flags.config}.json`;\n }\n if (flags.config) {\n gtConfig = loadConfig(flags.config);\n } else {\n const config = resolveConfig(cwd);\n if (config) {\n gtConfig = config.config as GenerateSettingsInput;\n flags.config = config.path;\n } else {\n if (options?.requireConfig) {\n return logErrorAndExit(\n 'No gt.config.json file was found. Run `npx gt init` to create one, pass --config, or run this command from your project root.'\n );\n }\n gtConfig = {};\n }\n }\n\n // Warn if apiKey is present in gt.config.json\n if (gtConfig.apiKey) {\n warnApiKeyInConfig(flags.config ?? 'gt.config.json');\n exitSync(1);\n }\n const projectIdEnv = resolveProjectId();\n // Resolve mismatched projectIds\n if (\n gtConfig.projectId &&\n flags.projectId &&\n gtConfig.projectId !== flags.projectId\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but the CLI flag uses ${chalk.green(flags.projectId)}. Use the same projectId in all configs.`\n );\n } else if (\n gtConfig.projectId &&\n projectIdEnv &&\n gtConfig.projectId !== projectIdEnv\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but GT_PROJECT_ID uses ${chalk.green(projectIdEnv)}. Use the same projectId in all configs.`\n );\n }\n\n if (\n flags.options?.docsUrlPattern &&\n !flags.options?.docsUrlPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static URLs could not be localized because the URL pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated URL.'\n );\n }\n\n if (\n flags.options?.docsImportPattern &&\n !flags.options?.docsImportPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static imports could not be localized because the import pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated import path.'\n );\n }\n\n if (flags.options?.copyFiles) {\n for (const file of flags.options.copyFiles) {\n if (!file.includes('[locale]')) {\n logErrorAndExit(\n 'Files could not be copied because the file path is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the copied path.'\n );\n }\n }\n }\n\n // Warn on deprecated includeSourceCodeContext\n const configuredFiles = gtConfig.files as FilesOptions | undefined;\n if (configuredFiles?.gt?.includeSourceCodeContext != null) {\n warnDeprecatedField(\n 'files.gt.includeSourceCodeContext',\n 'files.gt.parsingFlags.includeSourceCodeContext'\n );\n }\n\n // merge options\n const mergedOptions: Settings = { ...gtConfig, ...flags } as Settings;\n\n if (\n determineLibrary().library === 'base' &&\n !hasConfiguredTranslationFiles(mergedOptions.files)\n ) {\n logger.warn(\n chalk.yellow(\n 'No package.json or Python project file found in the current directory. Run this command from the root of your project.'\n )\n );\n }\n\n // Add defaultLocale if not provided\n mergedOptions.defaultLocale =\n mergedOptions.defaultLocale || libraryDefaultLocale;\n\n // merge locales\n mergedOptions.locales = Array.from(\n new Set([...(gtConfig.locales || []), ...(flags.locales || [])])\n );\n // Separate defaultLocale from locales\n mergedOptions.locales = mergedOptions.locales.filter(\n (locale) => locale !== mergedOptions.defaultLocale\n );\n\n // Add apiKey if not provided\n mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;\n\n // Add projectId if not provided\n mergedOptions.projectId = mergedOptions.projectId || resolveProjectId();\n\n // Add baseUrl if not provided\n mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;\n\n // Add dashboardUrl if not provided\n mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;\n\n // Add locales if not provided\n mergedOptions.locales = mergedOptions.locales || [];\n\n // Only set config path if one was actually found or explicitly provided.\n // Do not default to a phantom path — that would cause downstream writes\n // (e.g. updateConfig) to silently create a config file.\n if (!mergedOptions.config) {\n mergedOptions.config = '';\n }\n\n // Display projectId if present\n if (mergedOptions.projectId) {\n displayProjectId(mergedOptions.projectId);\n }\n\n // Add stageTranslations if not provided\n // For human review, always stage the project\n mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;\n\n // Top-level default for whether translated files require approval before use.\n // Effective policy is hash-changing, so reject anything but a real boolean.\n if (\n mergedOptions.requiresReview !== undefined &&\n typeof mergedOptions.requiresReview !== 'boolean'\n ) {\n logErrorAndExit(\n 'requiresReview in gt.config.json must be a boolean. Use files.<type>.requiresReview for glob-scoped overrides.'\n );\n }\n mergedOptions.requiresReview = mergedOptions.requiresReview ?? false;\n\n // Add publish — only set if explicitly configured or passed via flag.\n // When neither is set, leave undefined so the publish step knows\n // there is no global publish intent.\n if (flags.publish) {\n mergedOptions.publish = true;\n } else if (gtConfig.publish !== undefined) {\n mergedOptions.publish = gtConfig.publish;\n } else {\n mergedOptions.publish = undefined;\n }\n\n mergedOptions.omitConfigIds =\n flags.omitConfigIds === true || gtConfig.omitConfigIds === true;\n\n // Don't default src here — each pipeline (JS/Python) has its own defaults.\n // Only set src if the user explicitly provided it via flags or config.\n\n // Resolve all glob patterns in the files object\n const compositePatterns = Object.entries(\n mergedOptions.options?.jsonSchema || {}\n )\n .filter(([, schema]) => schema.composite)\n .map(([key]) => key);\n mergedOptions.files = mergedOptions.files\n ? resolveFiles(\n normalizeFilesOptions(mergedOptions.files as FilesOptions),\n mergedOptions.defaultLocale,\n mergedOptions.locales,\n cwd,\n compositePatterns,\n mergedOptions.requiresReview\n )\n : {\n resolvedPaths: {},\n placeholderPaths: {},\n transformPaths: {},\n transformFormats: {},\n publishPaths: new Set<string>(),\n unpublishPaths: new Set<string>(),\n requiresReviewPaths: new Set<string>(),\n parsingFlags: {},\n gtJson: {\n parsingFlags: GT_PARSING_FLAGS_DEFAULT,\n },\n };\n\n mergedOptions.options = {\n ...mergedOptions.options,\n mintlify: {\n ...mergedOptions.options?.mintlify,\n inferTitleFromFilename:\n gtConfig.options?.mintlify?.inferTitleFromFilename ||\n mergedOptions.options?.mintlify?.inferTitleFromFilename,\n },\n experimentalLocalizeStaticImports:\n gtConfig.options?.experimentalLocalizeStaticImports ||\n flags.experimentalLocalizeStaticImports,\n experimentalLocalizeStaticUrls:\n gtConfig.options?.experimentalLocalizeStaticUrls ||\n flags.experimentalLocalizeStaticUrls,\n experimentalLocalizeRelativeAssets:\n gtConfig.options?.experimentalLocalizeRelativeAssets ||\n flags.experimentalLocalizeRelativeAssets,\n experimentalHideDefaultLocale:\n gtConfig.options?.experimentalHideDefaultLocale ||\n flags.experimentalHideDefaultLocale,\n experimentalFlattenJsonFiles:\n gtConfig.options?.experimentalFlattenJsonFiles ||\n flags.experimentalFlattenJsonFiles,\n experimentalClearLocaleDirs:\n gtConfig.options?.experimentalClearLocaleDirs ||\n flags.experimentalClearLocaleDirs,\n clearLocaleDirsExclude:\n gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,\n saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? false,\n };\n\n if (\n mergedOptions.omitConfigIds &&\n (mergedOptions.publish === true ||\n mergedOptions.files.gtJson.publish === true)\n ) {\n logger.warn(\n chalk.yellow(\n 'Config IDs will be omitted even though CDN publishing is enabled. Remote cache/CDN consumers may load the latest available translations instead of a pinned version.'\n )\n );\n }\n\n // Add additional options if provided\n if (mergedOptions.options) {\n if (mergedOptions.options.jsonSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {\n const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];\n if (jsonSchema.preset) {\n mergedOptions.options.jsonSchema[fileGlob] = {\n ...generatePreset(jsonSchema.preset, 'json'),\n ...jsonSchema,\n };\n }\n }\n }\n if (mergedOptions.options.yamlSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {\n const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];\n if (yamlSchema.preset) {\n mergedOptions.options.yamlSchema[fileGlob] = {\n ...generatePreset(yamlSchema.preset, 'yaml'),\n ...yamlSchema,\n };\n }\n }\n }\n }\n\n // Add parsing options if not provided\n mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};\n mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions\n .conditionNames || [\n 'development',\n 'browser',\n 'module',\n 'import',\n 'require',\n 'default',\n ];\n\n // Add branch options if not provided\n const branchOptions = mergedOptions.branchOptions || {};\n // If --branch is set, enable branching\n branchOptions.enabled =\n flags.enableBranching ??\n gtConfig.branchOptions?.enabled ??\n (flags.branch ? true : false);\n branchOptions.currentBranch =\n flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;\n branchOptions.autoDetectBranches = flags.disableBranchDetection\n ? false\n : (gtConfig.branchOptions?.autoDetectBranches ?? true);\n branchOptions.remoteName =\n flags.remoteName ??\n gtConfig.branchOptions?.remoteName ??\n DEFAULT_GIT_REMOTE_NAME;\n mergedOptions.branchOptions = branchOptions;\n\n // Map -m/--message flag to tagMessage\n if (flags.message) {\n mergedOptions.tagMessage = flags.message;\n }\n\n // Resolve tag:\n // --tag (bare) or -m without --tag: try git SHA, fall back to random hex\n // --tag <value>: use as-is\n // No flags: no tag\n if (flags.tag === true || (!flags.tag && mergedOptions.tagMessage)) {\n try {\n mergedOptions.tag = execSync('git rev-parse --short HEAD', {\n encoding: 'utf-8',\n }).trim();\n // If no message provided, use git commit message\n if (!mergedOptions.tagMessage) {\n mergedOptions.tagMessage = execSync('git log -1 --format=%s', {\n encoding: 'utf-8',\n }).trim();\n }\n } catch {\n // Not in a git repo or git unavailable — fall back to random hex\n mergedOptions.tag = crypto.randomBytes(4).toString('hex');\n }\n }\n\n mergedOptions.configDirectory = path.join(cwd, '.gt');\n\n validateSettings(mergedOptions);\n\n // Keep both clients on the same resolved credentials while consumers migrate.\n configureApiClient({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n customMapping: mergedOptions.customMapping,\n });\n gt.setConfig({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n sourceLocale: mergedOptions.defaultLocale,\n customMapping: mergedOptions.customMapping,\n });\n\n return mergedOptions;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "2.20.
|
|
1
|
+
export declare const PACKAGE_VERSION = "2.20.3";
|
|
@@ -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.20.
|
|
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.20.3';\n"],"mappings":";AACA,MAAa,kBAAkB"}
|
|
@@ -184,13 +184,17 @@ function applyAnchorIds(translatedContent, translatedHeadings, idMappings, useDi
|
|
|
184
184
|
if (!mapping) continue;
|
|
185
185
|
if (heading.startLine < 1 || heading.endLine > lines.length) continue;
|
|
186
186
|
const index = heading.startLine - 1;
|
|
187
|
-
if (!useDivWrapping
|
|
187
|
+
if (!useDivWrapping) {
|
|
188
188
|
if (heading.textEndColumn < 1) continue;
|
|
189
189
|
const anchor = escapeAnchors && !mapping.explicit ? `\\{#${mapping.id}\\}` : `{#${mapping.id}}`;
|
|
190
|
-
const
|
|
191
|
-
lines[index] = `${
|
|
190
|
+
const { text, trailer } = splitHeadingLine(lines[index], heading);
|
|
191
|
+
lines[index] = `${text} ${anchor}${trailer}`;
|
|
192
192
|
continue;
|
|
193
193
|
}
|
|
194
|
+
if (heading.textEndColumn >= 1) {
|
|
195
|
+
const { text, trailer } = splitHeadingLine(lines[index], heading);
|
|
196
|
+
lines[index] = `${text}${trailer}`;
|
|
197
|
+
}
|
|
194
198
|
if (heading.wrapperId) {
|
|
195
199
|
const { line, startColumn, endColumn } = heading.wrapperId;
|
|
196
200
|
const wrapper = lines[line - 1];
|
|
@@ -204,6 +208,16 @@ function applyAnchorIds(translatedContent, translatedHeadings, idMappings, useDi
|
|
|
204
208
|
return lines.join("\n");
|
|
205
209
|
}
|
|
206
210
|
/**
|
|
211
|
+
* Splits a heading line into its text, minus any trailing inline anchor, and
|
|
212
|
+
* whatever follows the text (a closing `##` sequence, for instance).
|
|
213
|
+
*/
|
|
214
|
+
function splitHeadingLine(line, heading) {
|
|
215
|
+
return {
|
|
216
|
+
text: line.slice(0, heading.textEndColumn - 1).replace(TRAILING_ANCHOR, ""),
|
|
217
|
+
trailer: line.slice(heading.textEndColumn - 1)
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
207
221
|
* Normalizes every inline anchor: escaped for MDX, bare for Markdown.
|
|
208
222
|
*/
|
|
209
223
|
function normalizeInlineAnchors(content, escapeAnchors) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"addExplicitAnchorIds.js","names":[],"sources":["../../src/utils/addExplicitAnchorIds.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\nimport type { Heading, Node } from 'mdast';\nimport type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx';\nimport { logger } from '../console/logger.js';\nimport type { AdditionalOptions } from '../types/index.js';\nimport {\n forEachLineOutsideCodeFences,\n mapLinesOutsideCodeFences,\n parseMdxTolerantly,\n} from './mdxAnchorSyntax.js';\n\ntype AnchorIdSettings = {\n options?: Pick<AdditionalOptions, 'experimentalAddHeaderAnchorIds'>;\n};\n\n/** An ATX heading line, split into indentation, marker and text. */\nconst ATX_HEADING = /^([ \\t]*)(#{1,6}[ \\t]+)(.*)$/;\n\n/** A trailing custom anchor ID, in either the plain or MDX-escaped form. */\nconst TRAILING_ANCHOR = /\\s*(?:\\\\\\{#[^}]+\\\\\\}|\\{#[^}]+\\})\\s*$/;\n\n/**\n * Generates a slug from heading text\n */\nfunction generateSlug(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '') // Remove special chars except spaces and hyphens\n .trim()\n .replace(/\\s+/g, '-') // Replace spaces with hyphens\n .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen\n .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens\n}\n\n/**\n * Extracts text content from heading nodes\n */\nfunction extractHeadingText(heading: Heading): string {\n let text = '';\n\n visit(heading, ['text', 'inlineCode'], (node: Node) => {\n if ('value' in node && typeof node.value === 'string') {\n text += node.value;\n }\n });\n\n return text;\n}\n\n/**\n * Line-by-line heading extractor used when MDX parsing fails outright.\n */\nfunction extractHeadingsWithFallback(mdxContent: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n let position = 0;\n\n forEachLineOutsideCodeFences(mdxContent, (line, index) => {\n const headingMatch = line.match(ATX_HEADING);\n if (!headingMatch) return;\n\n const [, indent, marker, rawText] = headingMatch;\n const { cleanedText, explicitId } = parseHeadingContent(rawText);\n if (!cleanedText && !explicitId) return;\n\n headings.push({\n text: cleanedText,\n level: marker.trim().length,\n slug: explicitId ?? generateSlug(cleanedText),\n position: position++,\n startLine: index + 1,\n endLine: index + 1,\n startColumn: indent.length + 1,\n // Without a parser there is nothing finer to go on than end of line.\n textEndColumn: line.length + 1,\n wrapperId: null,\n explicit: explicitId !== undefined,\n });\n });\n\n assignUniqueSlugs(headings);\n return headings;\n}\n\nfunction parseHeadingContent(text: string): {\n cleanedText: string;\n explicitId?: string;\n} {\n // Support both {#id} and escaped \\{#id\\} forms\n const anchorMatch = text.match(/(\\\\\\{#([^}]+)\\\\\\}|\\{#([^}]+)\\})\\s*$/);\n\n if (!anchorMatch) {\n return { cleanedText: text };\n }\n\n const explicitId = anchorMatch[2] || anchorMatch[3];\n const cleanedText = text.replace(anchorMatch[0], '').trimEnd();\n\n return { cleanedText, explicitId };\n}\n\n/**\n * Suffixes repeated slugs `-2`, `-3`, ... as Mintlify does. Author-written IDs\n * are never renumbered, only reserved.\n */\nfunction assignUniqueSlugs(headings: HeadingInfo[]): void {\n // Reserve every explicit ID up front, including ones later in the document,\n // so a generated slug never claims an ID an author asked for.\n const used = new Set(\n headings\n .filter((heading) => heading.explicit)\n .map((heading) => heading.slug)\n );\n\n for (const heading of headings) {\n if (heading.explicit) continue;\n\n // Headings with no slug-able characters would produce id=\"\".\n const base = heading.slug || 'section';\n let slug = base;\n let suffix = 1;\n while (used.has(slug)) {\n suffix += 1;\n slug = `${base}-${suffix}`;\n }\n\n heading.slug = slug;\n used.add(slug);\n }\n}\n\n/** A source range on a single line, as 1-based inclusive/exclusive columns. */\ninterface ColumnRange {\n line: number;\n startColumn: number;\n endColumn: number;\n}\n\n/**\n * Represents a heading with its position and metadata\n */\nexport interface HeadingInfo {\n text: string;\n level: number;\n slug: string;\n position: number;\n /** 1-based line the heading starts on. */\n startLine: number;\n /** 1-based line the heading ends on (differs from startLine for setext). */\n endLine: number;\n /** 1-based column of the heading marker; anything left of it is indentation. */\n startColumn: number;\n /** 1-based column just past the text, before any closing `##`; -1 if unknown. */\n textEndColumn: number;\n /** `id` attribute of a wrapper element already anchoring this heading. */\n wrapperId: ColumnRange | null;\n /** Whether the author wrote an explicit `{#id}`. */\n explicit: boolean;\n}\n\n/**\n * Finds the `id` of a wrapper element already anchoring this heading. Requiring\n * the heading to be its only child rules out containers like `<Tab>`.\n */\nfunction findWrapperId(\n heading: Heading,\n parent: Node | undefined\n): ColumnRange | null {\n if (!parent || parent.type !== 'mdxJsxFlowElement') return null;\n\n const element = parent as MdxJsxFlowElement;\n if (element.children.length !== 1 || element.children[0] !== heading) {\n return null;\n }\n\n const id = element.attributes.find(\n (attribute) =>\n attribute.type === 'mdxJsxAttribute' && attribute.name === 'id'\n );\n const position = id?.position;\n if (!position || position.start.line !== position.end.line) return null;\n\n return {\n line: position.start.line,\n startColumn: position.start.column,\n endColumn: position.end.column,\n };\n}\n\n/**\n * Extracts heading information from content (read-only, no modifications).\n * Source and translation are matched by position, so both must parse the same\n * way — the fallback extractor misses headings nested in JSX.\n */\nexport function extractHeadingInfo(mdxContent: string): HeadingInfo[] {\n let ast;\n try {\n ast = parseMdxTolerantly(mdxContent);\n } catch {\n // Fallback: line-by-line extraction skipping fenced code blocks\n return extractHeadingsWithFallback(mdxContent);\n }\n\n const headings: HeadingInfo[] = [];\n let position = 0;\n\n visit(ast, 'heading', (heading: Heading, _index, parent) => {\n const headingText = extractHeadingText(heading);\n const { cleanedText, explicitId } = parseHeadingContent(headingText);\n if (!cleanedText && !explicitId) return;\n\n const lastChild = heading.children[heading.children.length - 1];\n\n headings.push({\n text: cleanedText,\n level: heading.depth,\n slug: explicitId ?? generateSlug(cleanedText),\n position: position++,\n startLine: heading.position?.start.line ?? -1,\n endLine: heading.position?.end.line ?? -1,\n startColumn: heading.position?.start.column ?? 1,\n textEndColumn:\n lastChild?.position?.end.column ?? heading.position?.end.column ?? -1,\n wrapperId: findWrapperId(heading, parent),\n explicit: explicitId !== undefined,\n });\n });\n\n assignUniqueSlugs(headings);\n return headings;\n}\n\n/**\n * Applies anchor IDs to translated content based on source heading mapping\n */\nexport function addExplicitAnchorIds(\n translatedContent: string,\n sourceHeadingMap: HeadingInfo[],\n settings?: AnchorIdSettings,\n sourcePath?: string,\n translatedPath?: string,\n fileTypeHint?: 'md' | 'mdx'\n): {\n content: string;\n hasChanges: boolean;\n addedIds: Array<{ heading: string; id: string }>;\n} {\n const addedIds: Array<{ heading: string; id: string }> = [];\n const useDivWrapping =\n settings?.options?.experimentalAddHeaderAnchorIds === 'mintlify';\n\n // Extract headings from translated content\n const translatedHeadings = extractHeadingInfo(translatedContent);\n\n // Pre-processing validation: check if header counts match\n if (sourceHeadingMap.length !== translatedHeadings.length) {\n const sourceFile = sourcePath\n ? `Source file: ${sourcePath}`\n : 'Source file';\n const translatedFile = translatedPath\n ? `translated file: ${translatedPath}`\n : 'translated file';\n\n logger.warn(\n `Header count mismatch detected! ${sourceFile} has ${sourceHeadingMap.length} headers but ${translatedFile} has ${translatedHeadings.length} headers. ` +\n `This likely means your source file was edited after translation was requested, causing a mismatch between ` +\n `the number of headers in your source file vs the translated file. Re-translate this file to resolve the issue.`\n );\n }\n\n // Create ID mapping based on positional matching\n const idMappings = new Map<number, { id: string; explicit: boolean }>();\n sourceHeadingMap.forEach((sourceHeading, index) => {\n const translatedHeading = translatedHeadings[index];\n // Match by position and level for safety\n if (translatedHeading && translatedHeading.level === sourceHeading.level) {\n idMappings.set(index, {\n id: sourceHeading.slug,\n explicit: sourceHeading.explicit,\n });\n addedIds.push({\n heading: translatedHeading.text,\n id: sourceHeading.slug,\n });\n }\n });\n\n const translatedIsMdx = translatedPath\n ? translatedPath.toLowerCase().endsWith('.mdx')\n : true; // default to mdx-style escaping when unknown\n const shouldEscapeAnchors =\n fileTypeHint === 'mdx'\n ? true\n : fileTypeHint === 'md'\n ? false\n : translatedIsMdx;\n\n if (idMappings.size === 0) {\n // Normalize anchors the translation carried over.\n const content = useDivWrapping\n ? translatedContent\n : normalizeInlineAnchors(translatedContent, shouldEscapeAnchors);\n return {\n content,\n hasChanges: content !== translatedContent,\n addedIds: [],\n };\n }\n\n let content = applyAnchorIds(\n translatedContent,\n translatedHeadings,\n idMappings,\n useDivWrapping,\n shouldEscapeAnchors\n );\n\n if (!useDivWrapping) {\n content = normalizeInlineAnchors(content, shouldEscapeAnchors);\n }\n\n return {\n content,\n hasChanges: content !== translatedContent,\n addedIds,\n };\n}\n\n/**\n * Writes anchor IDs onto the translated document, locating headings by parser\n * line position rather than by text. Edits run bottom-up to keep lines valid.\n */\nfunction applyAnchorIds(\n translatedContent: string,\n translatedHeadings: HeadingInfo[],\n idMappings: Map<number, { id: string; explicit: boolean }>,\n useDivWrapping: boolean,\n escapeAnchors: boolean\n): string {\n const lines = translatedContent.split('\\n');\n\n const ordered = [...translatedHeadings].sort(\n (a, b) => b.startLine - a.startLine\n );\n\n for (const heading of ordered) {\n const mapping = idMappings.get(heading.position);\n if (!mapping) continue;\n if (heading.startLine < 1 || heading.endLine > lines.length) continue;\n\n const index = heading.startLine - 1;\n\n // Author-written IDs stay inline; derived ones go in a wrapper.\n const inline = !useDivWrapping || mapping.explicit;\n\n if (inline) {\n // Setext headings have no column to append to.\n if (heading.textEndColumn < 1) continue;\n\n const escape = escapeAnchors && !mapping.explicit;\n const anchor = escape ? `\\\\{#${mapping.id}\\\\}` : `{#${mapping.id}}`;\n const line = lines[index];\n const text = line\n .slice(0, heading.textEndColumn - 1)\n .replace(TRAILING_ANCHOR, '');\n const trailer = line.slice(heading.textEndColumn - 1);\n\n lines[index] = `${text} ${anchor}${trailer}`;\n continue;\n }\n\n if (heading.wrapperId) {\n // Already wrapped: fix the ID in place rather than nesting another.\n const { line, startColumn, endColumn } = heading.wrapperId;\n const wrapper = lines[line - 1];\n lines[line - 1] =\n wrapper.slice(0, startColumn - 1) +\n `id=\"${mapping.id}\"` +\n wrapper.slice(endColumn - 1);\n continue;\n }\n\n const indent = lines[index].slice(0, Math.max(0, heading.startColumn - 1));\n const body = lines.slice(index, heading.endLine).map((line) => ` ${line}`);\n\n lines.splice(\n index,\n heading.endLine - heading.startLine + 1,\n `${indent}<div id=\"${mapping.id}\">`,\n ...body,\n `${indent}</div>`\n );\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Normalizes every inline anchor: escaped for MDX, bare for Markdown.\n */\nfunction normalizeInlineAnchors(\n content: string,\n escapeAnchors: boolean\n): string {\n return mapLinesOutsideCodeFences(content, (line) => {\n const atx = line.match(ATX_HEADING);\n if (!atx) return line;\n\n const escaped = atx[3].match(/\\\\\\{#([A-Za-z0-9_-]+)\\\\\\}\\s*$/);\n const bare = atx[3].match(/(?<!\\\\)\\{#([A-Za-z0-9_-]+)\\}\\s*$/);\n\n if (escapeAnchors && bare) {\n const text = atx[3].replace(TRAILING_ANCHOR, '');\n return `${atx[1]}${atx[2]}${text} \\\\{#${bare[1]}\\\\}`;\n }\n if (!escapeAnchors && escaped) {\n const text = atx[3].replace(TRAILING_ANCHOR, '');\n return `${atx[1]}${atx[2]}${text} {#${escaped[1]}}`;\n }\n return line;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"addExplicitAnchorIds.js","names":[],"sources":["../../src/utils/addExplicitAnchorIds.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\nimport type { Heading, Node } from 'mdast';\nimport type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx';\nimport { logger } from '../console/logger.js';\nimport type { AdditionalOptions } from '../types/index.js';\nimport {\n forEachLineOutsideCodeFences,\n mapLinesOutsideCodeFences,\n parseMdxTolerantly,\n} from './mdxAnchorSyntax.js';\n\ntype AnchorIdSettings = {\n options?: Pick<AdditionalOptions, 'experimentalAddHeaderAnchorIds'>;\n};\n\n/** An ATX heading line, split into indentation, marker and text. */\nconst ATX_HEADING = /^([ \\t]*)(#{1,6}[ \\t]+)(.*)$/;\n\n/** A trailing custom anchor ID, in either the plain or MDX-escaped form. */\nconst TRAILING_ANCHOR = /\\s*(?:\\\\\\{#[^}]+\\\\\\}|\\{#[^}]+\\})\\s*$/;\n\n/**\n * Generates a slug from heading text\n */\nfunction generateSlug(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '') // Remove special chars except spaces and hyphens\n .trim()\n .replace(/\\s+/g, '-') // Replace spaces with hyphens\n .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen\n .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens\n}\n\n/**\n * Extracts text content from heading nodes\n */\nfunction extractHeadingText(heading: Heading): string {\n let text = '';\n\n visit(heading, ['text', 'inlineCode'], (node: Node) => {\n if ('value' in node && typeof node.value === 'string') {\n text += node.value;\n }\n });\n\n return text;\n}\n\n/**\n * Line-by-line heading extractor used when MDX parsing fails outright.\n */\nfunction extractHeadingsWithFallback(mdxContent: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n let position = 0;\n\n forEachLineOutsideCodeFences(mdxContent, (line, index) => {\n const headingMatch = line.match(ATX_HEADING);\n if (!headingMatch) return;\n\n const [, indent, marker, rawText] = headingMatch;\n const { cleanedText, explicitId } = parseHeadingContent(rawText);\n if (!cleanedText && !explicitId) return;\n\n headings.push({\n text: cleanedText,\n level: marker.trim().length,\n slug: explicitId ?? generateSlug(cleanedText),\n position: position++,\n startLine: index + 1,\n endLine: index + 1,\n startColumn: indent.length + 1,\n // Without a parser there is nothing finer to go on than end of line.\n textEndColumn: line.length + 1,\n wrapperId: null,\n explicit: explicitId !== undefined,\n });\n });\n\n assignUniqueSlugs(headings);\n return headings;\n}\n\nfunction parseHeadingContent(text: string): {\n cleanedText: string;\n explicitId?: string;\n} {\n // Support both {#id} and escaped \\{#id\\} forms\n const anchorMatch = text.match(/(\\\\\\{#([^}]+)\\\\\\}|\\{#([^}]+)\\})\\s*$/);\n\n if (!anchorMatch) {\n return { cleanedText: text };\n }\n\n const explicitId = anchorMatch[2] || anchorMatch[3];\n const cleanedText = text.replace(anchorMatch[0], '').trimEnd();\n\n return { cleanedText, explicitId };\n}\n\n/**\n * Suffixes repeated slugs `-2`, `-3`, ... as Mintlify does. Author-written IDs\n * are never renumbered, only reserved.\n */\nfunction assignUniqueSlugs(headings: HeadingInfo[]): void {\n // Reserve every explicit ID up front, including ones later in the document,\n // so a generated slug never claims an ID an author asked for.\n const used = new Set(\n headings\n .filter((heading) => heading.explicit)\n .map((heading) => heading.slug)\n );\n\n for (const heading of headings) {\n if (heading.explicit) continue;\n\n // Headings with no slug-able characters would produce id=\"\".\n const base = heading.slug || 'section';\n let slug = base;\n let suffix = 1;\n while (used.has(slug)) {\n suffix += 1;\n slug = `${base}-${suffix}`;\n }\n\n heading.slug = slug;\n used.add(slug);\n }\n}\n\n/** A source range on a single line, as 1-based inclusive/exclusive columns. */\ninterface ColumnRange {\n line: number;\n startColumn: number;\n endColumn: number;\n}\n\n/**\n * Represents a heading with its position and metadata\n */\nexport interface HeadingInfo {\n text: string;\n level: number;\n slug: string;\n position: number;\n /** 1-based line the heading starts on. */\n startLine: number;\n /** 1-based line the heading ends on (differs from startLine for setext). */\n endLine: number;\n /** 1-based column of the heading marker; anything left of it is indentation. */\n startColumn: number;\n /** 1-based column just past the text, before any closing `##`; -1 if unknown. */\n textEndColumn: number;\n /** `id` attribute of a wrapper element already anchoring this heading. */\n wrapperId: ColumnRange | null;\n /** Whether the author wrote an explicit `{#id}`. */\n explicit: boolean;\n}\n\n/**\n * Finds the `id` of a wrapper element already anchoring this heading. Requiring\n * the heading to be its only child rules out containers like `<Tab>`.\n */\nfunction findWrapperId(\n heading: Heading,\n parent: Node | undefined\n): ColumnRange | null {\n if (!parent || parent.type !== 'mdxJsxFlowElement') return null;\n\n const element = parent as MdxJsxFlowElement;\n if (element.children.length !== 1 || element.children[0] !== heading) {\n return null;\n }\n\n const id = element.attributes.find(\n (attribute) =>\n attribute.type === 'mdxJsxAttribute' && attribute.name === 'id'\n );\n const position = id?.position;\n if (!position || position.start.line !== position.end.line) return null;\n\n return {\n line: position.start.line,\n startColumn: position.start.column,\n endColumn: position.end.column,\n };\n}\n\n/**\n * Extracts heading information from content (read-only, no modifications).\n * Source and translation are matched by position, so both must parse the same\n * way — the fallback extractor misses headings nested in JSX.\n */\nexport function extractHeadingInfo(mdxContent: string): HeadingInfo[] {\n let ast;\n try {\n ast = parseMdxTolerantly(mdxContent);\n } catch {\n // Fallback: line-by-line extraction skipping fenced code blocks\n return extractHeadingsWithFallback(mdxContent);\n }\n\n const headings: HeadingInfo[] = [];\n let position = 0;\n\n visit(ast, 'heading', (heading: Heading, _index, parent) => {\n const headingText = extractHeadingText(heading);\n const { cleanedText, explicitId } = parseHeadingContent(headingText);\n if (!cleanedText && !explicitId) return;\n\n const lastChild = heading.children[heading.children.length - 1];\n\n headings.push({\n text: cleanedText,\n level: heading.depth,\n slug: explicitId ?? generateSlug(cleanedText),\n position: position++,\n startLine: heading.position?.start.line ?? -1,\n endLine: heading.position?.end.line ?? -1,\n startColumn: heading.position?.start.column ?? 1,\n textEndColumn:\n lastChild?.position?.end.column ?? heading.position?.end.column ?? -1,\n wrapperId: findWrapperId(heading, parent),\n explicit: explicitId !== undefined,\n });\n });\n\n assignUniqueSlugs(headings);\n return headings;\n}\n\n/**\n * Applies anchor IDs to translated content based on source heading mapping\n */\nexport function addExplicitAnchorIds(\n translatedContent: string,\n sourceHeadingMap: HeadingInfo[],\n settings?: AnchorIdSettings,\n sourcePath?: string,\n translatedPath?: string,\n fileTypeHint?: 'md' | 'mdx'\n): {\n content: string;\n hasChanges: boolean;\n addedIds: Array<{ heading: string; id: string }>;\n} {\n const addedIds: Array<{ heading: string; id: string }> = [];\n const useDivWrapping =\n settings?.options?.experimentalAddHeaderAnchorIds === 'mintlify';\n\n // Extract headings from translated content\n const translatedHeadings = extractHeadingInfo(translatedContent);\n\n // Pre-processing validation: check if header counts match\n if (sourceHeadingMap.length !== translatedHeadings.length) {\n const sourceFile = sourcePath\n ? `Source file: ${sourcePath}`\n : 'Source file';\n const translatedFile = translatedPath\n ? `translated file: ${translatedPath}`\n : 'translated file';\n\n logger.warn(\n `Header count mismatch detected! ${sourceFile} has ${sourceHeadingMap.length} headers but ${translatedFile} has ${translatedHeadings.length} headers. ` +\n `This likely means your source file was edited after translation was requested, causing a mismatch between ` +\n `the number of headers in your source file vs the translated file. Re-translate this file to resolve the issue.`\n );\n }\n\n // Create ID mapping based on positional matching\n const idMappings = new Map<number, { id: string; explicit: boolean }>();\n sourceHeadingMap.forEach((sourceHeading, index) => {\n const translatedHeading = translatedHeadings[index];\n // Match by position and level for safety\n if (translatedHeading && translatedHeading.level === sourceHeading.level) {\n idMappings.set(index, {\n id: sourceHeading.slug,\n explicit: sourceHeading.explicit,\n });\n addedIds.push({\n heading: translatedHeading.text,\n id: sourceHeading.slug,\n });\n }\n });\n\n const translatedIsMdx = translatedPath\n ? translatedPath.toLowerCase().endsWith('.mdx')\n : true; // default to mdx-style escaping when unknown\n const shouldEscapeAnchors =\n fileTypeHint === 'mdx'\n ? true\n : fileTypeHint === 'md'\n ? false\n : translatedIsMdx;\n\n if (idMappings.size === 0) {\n // Normalize anchors the translation carried over.\n const content = useDivWrapping\n ? translatedContent\n : normalizeInlineAnchors(translatedContent, shouldEscapeAnchors);\n return {\n content,\n hasChanges: content !== translatedContent,\n addedIds: [],\n };\n }\n\n let content = applyAnchorIds(\n translatedContent,\n translatedHeadings,\n idMappings,\n useDivWrapping,\n shouldEscapeAnchors\n );\n\n if (!useDivWrapping) {\n content = normalizeInlineAnchors(content, shouldEscapeAnchors);\n }\n\n return {\n content,\n hasChanges: content !== translatedContent,\n addedIds,\n };\n}\n\n/**\n * Writes anchor IDs onto the translated document, locating headings by parser\n * line position rather than by text. Edits run bottom-up to keep lines valid.\n */\nfunction applyAnchorIds(\n translatedContent: string,\n translatedHeadings: HeadingInfo[],\n idMappings: Map<number, { id: string; explicit: boolean }>,\n useDivWrapping: boolean,\n escapeAnchors: boolean\n): string {\n const lines = translatedContent.split('\\n');\n\n const ordered = [...translatedHeadings].sort(\n (a, b) => b.startLine - a.startLine\n );\n\n for (const heading of ordered) {\n const mapping = idMappings.get(heading.position);\n if (!mapping) continue;\n if (heading.startLine < 1 || heading.endLine > lines.length) continue;\n\n const index = heading.startLine - 1;\n\n if (!useDivWrapping) {\n // Setext headings have no column to append to.\n if (heading.textEndColumn < 1) continue;\n\n const escape = escapeAnchors && !mapping.explicit;\n const anchor = escape ? `\\\\{#${mapping.id}\\\\}` : `{#${mapping.id}}`;\n const { text, trailer } = splitHeadingLine(lines[index], heading);\n\n lines[index] = `${text} ${anchor}${trailer}`;\n continue;\n }\n\n // In wrapper mode every heading gets a wrapper, including ones whose ID\n // the author wrote inline. Mintlify's `{#id}` pre-pass does not recognize\n // headings indented four or more spaces, which the MDX serializer produces\n // for headings nested in JSX, so a re-attached inline ID fails to compile.\n // A wrapper anchors the heading at any indentation. Drop any inline ID the\n // translation carried over so the two forms never appear together.\n if (heading.textEndColumn >= 1) {\n const { text, trailer } = splitHeadingLine(lines[index], heading);\n lines[index] = `${text}${trailer}`;\n }\n\n if (heading.wrapperId) {\n // Already wrapped: fix the ID in place rather than nesting another.\n const { line, startColumn, endColumn } = heading.wrapperId;\n const wrapper = lines[line - 1];\n lines[line - 1] =\n wrapper.slice(0, startColumn - 1) +\n `id=\"${mapping.id}\"` +\n wrapper.slice(endColumn - 1);\n continue;\n }\n\n const indent = lines[index].slice(0, Math.max(0, heading.startColumn - 1));\n const body = lines.slice(index, heading.endLine).map((line) => ` ${line}`);\n\n lines.splice(\n index,\n heading.endLine - heading.startLine + 1,\n `${indent}<div id=\"${mapping.id}\">`,\n ...body,\n `${indent}</div>`\n );\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Splits a heading line into its text, minus any trailing inline anchor, and\n * whatever follows the text (a closing `##` sequence, for instance).\n */\nfunction splitHeadingLine(\n line: string,\n heading: HeadingInfo\n): { text: string; trailer: string } {\n return {\n text: line.slice(0, heading.textEndColumn - 1).replace(TRAILING_ANCHOR, ''),\n trailer: line.slice(heading.textEndColumn - 1),\n };\n}\n\n/**\n * Normalizes every inline anchor: escaped for MDX, bare for Markdown.\n */\nfunction normalizeInlineAnchors(\n content: string,\n escapeAnchors: boolean\n): string {\n return mapLinesOutsideCodeFences(content, (line) => {\n const atx = line.match(ATX_HEADING);\n if (!atx) return line;\n\n const escaped = atx[3].match(/\\\\\\{#([A-Za-z0-9_-]+)\\\\\\}\\s*$/);\n const bare = atx[3].match(/(?<!\\\\)\\{#([A-Za-z0-9_-]+)\\}\\s*$/);\n\n if (escapeAnchors && bare) {\n const text = atx[3].replace(TRAILING_ANCHOR, '');\n return `${atx[1]}${atx[2]}${text} \\\\{#${bare[1]}\\\\}`;\n }\n if (!escapeAnchors && escaped) {\n const text = atx[3].replace(TRAILING_ANCHOR, '');\n return `${atx[1]}${atx[2]}${text} {#${escaped[1]}}`;\n }\n return line;\n });\n}\n"],"mappings":";;;;;AAgBA,MAAM,cAAc;;AAGpB,MAAM,kBAAkB;;;;AAKxB,SAAS,aAAa,MAAsB;AAC1C,QAAO,KACJ,aAAa,CACb,QAAQ,aAAa,GAAG,CACxB,MAAM,CACN,QAAQ,QAAQ,IAAI,CACpB,QAAQ,OAAO,IAAI,CACnB,QAAQ,UAAU,GAAG;;;;;AAM1B,SAAS,mBAAmB,SAA0B;CACpD,IAAI,OAAO;AAEX,OAAM,SAAS,CAAC,QAAQ,aAAa,GAAG,SAAe;AACrD,MAAI,WAAW,QAAQ,OAAO,KAAK,UAAU,SAC3C,SAAQ,KAAK;GAEf;AAEF,QAAO;;;;;AAMT,SAAS,4BAA4B,YAAmC;CACtE,MAAM,WAA0B,EAAE;CAClC,IAAI,WAAW;AAEf,8BAA6B,aAAa,MAAM,UAAU;EACxD,MAAM,eAAe,KAAK,MAAM,YAAY;AAC5C,MAAI,CAAC,aAAc;EAEnB,MAAM,GAAG,QAAQ,QAAQ,WAAW;EACpC,MAAM,EAAE,aAAa,eAAe,oBAAoB,QAAQ;AAChE,MAAI,CAAC,eAAe,CAAC,WAAY;AAEjC,WAAS,KAAK;GACZ,MAAM;GACN,OAAO,OAAO,MAAM,CAAC;GACrB,MAAM,cAAc,aAAa,YAAY;GAC7C,UAAU;GACV,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,aAAa,OAAO,SAAS;GAE7B,eAAe,KAAK,SAAS;GAC7B,WAAW;GACX,UAAU,eAAe,KAAA;GAC1B,CAAC;GACF;AAEF,mBAAkB,SAAS;AAC3B,QAAO;;AAGT,SAAS,oBAAoB,MAG3B;CAEA,MAAM,cAAc,KAAK,MAAM,sCAAsC;AAErE,KAAI,CAAC,YACH,QAAO,EAAE,aAAa,MAAM;CAG9B,MAAM,aAAa,YAAY,MAAM,YAAY;AAGjD,QAAO;EAAE,aAFW,KAAK,QAAQ,YAAY,IAAI,GAAG,CAAC,SAEjC;EAAE;EAAY;;;;;;AAOpC,SAAS,kBAAkB,UAA+B;CAGxD,MAAM,OAAO,IAAI,IACf,SACG,QAAQ,YAAY,QAAQ,SAAS,CACrC,KAAK,YAAY,QAAQ,KAAK,CAClC;AAED,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,QAAQ,SAAU;EAGtB,MAAM,OAAO,QAAQ,QAAQ;EAC7B,IAAI,OAAO;EACX,IAAI,SAAS;AACb,SAAO,KAAK,IAAI,KAAK,EAAE;AACrB,aAAU;AACV,UAAO,GAAG,KAAK,GAAG;;AAGpB,UAAQ,OAAO;AACf,OAAK,IAAI,KAAK;;;;;;;AAqClB,SAAS,cACP,SACA,QACoB;AACpB,KAAI,CAAC,UAAU,OAAO,SAAS,oBAAqB,QAAO;CAE3D,MAAM,UAAU;AAChB,KAAI,QAAQ,SAAS,WAAW,KAAK,QAAQ,SAAS,OAAO,QAC3D,QAAO;CAOT,MAAM,WAJK,QAAQ,WAAW,MAC3B,cACC,UAAU,SAAS,qBAAqB,UAAU,SAAS,KAE5C,EAAE;AACrB,KAAI,CAAC,YAAY,SAAS,MAAM,SAAS,SAAS,IAAI,KAAM,QAAO;AAEnE,QAAO;EACL,MAAM,SAAS,MAAM;EACrB,aAAa,SAAS,MAAM;EAC5B,WAAW,SAAS,IAAI;EACzB;;;;;;;AAQH,SAAgB,mBAAmB,YAAmC;CACpE,IAAI;AACJ,KAAI;AACF,QAAM,mBAAmB,WAAW;SAC9B;AAEN,SAAO,4BAA4B,WAAW;;CAGhD,MAAM,WAA0B,EAAE;CAClC,IAAI,WAAW;AAEf,OAAM,KAAK,YAAY,SAAkB,QAAQ,WAAW;EAE1D,MAAM,EAAE,aAAa,eAAe,oBADhB,mBAAmB,QAC4B,CAAC;AACpE,MAAI,CAAC,eAAe,CAAC,WAAY;EAEjC,MAAM,YAAY,QAAQ,SAAS,QAAQ,SAAS,SAAS;AAE7D,WAAS,KAAK;GACZ,MAAM;GACN,OAAO,QAAQ;GACf,MAAM,cAAc,aAAa,YAAY;GAC7C,UAAU;GACV,WAAW,QAAQ,UAAU,MAAM,QAAQ;GAC3C,SAAS,QAAQ,UAAU,IAAI,QAAQ;GACvC,aAAa,QAAQ,UAAU,MAAM,UAAU;GAC/C,eACE,WAAW,UAAU,IAAI,UAAU,QAAQ,UAAU,IAAI,UAAU;GACrE,WAAW,cAAc,SAAS,OAAO;GACzC,UAAU,eAAe,KAAA;GAC1B,CAAC;GACF;AAEF,mBAAkB,SAAS;AAC3B,QAAO;;;;;AAMT,SAAgB,qBACd,mBACA,kBACA,UACA,YACA,gBACA,cAKA;CACA,MAAM,WAAmD,EAAE;CAC3D,MAAM,iBACJ,UAAU,SAAS,mCAAmC;CAGxD,MAAM,qBAAqB,mBAAmB,kBAAkB;AAGhE,KAAI,iBAAiB,WAAW,mBAAmB,QAAQ;EACzD,MAAM,aAAa,aACf,gBAAgB,eAChB;EACJ,MAAM,iBAAiB,iBACnB,oBAAoB,mBACpB;AAEJ,SAAO,KACL,mCAAmC,WAAW,OAAO,iBAAiB,OAAO,eAAe,eAAe,OAAO,mBAAmB,OAAO,oOAG7I;;CAIH,MAAM,6BAAa,IAAI,KAAgD;AACvE,kBAAiB,SAAS,eAAe,UAAU;EACjD,MAAM,oBAAoB,mBAAmB;AAE7C,MAAI,qBAAqB,kBAAkB,UAAU,cAAc,OAAO;AACxE,cAAW,IAAI,OAAO;IACpB,IAAI,cAAc;IAClB,UAAU,cAAc;IACzB,CAAC;AACF,YAAS,KAAK;IACZ,SAAS,kBAAkB;IAC3B,IAAI,cAAc;IACnB,CAAC;;GAEJ;CAEF,MAAM,kBAAkB,iBACpB,eAAe,aAAa,CAAC,SAAS,OAAO,GAC7C;CACJ,MAAM,sBACJ,iBAAiB,QACb,OACA,iBAAiB,OACf,QACA;AAER,KAAI,WAAW,SAAS,GAAG;EAEzB,MAAM,UAAU,iBACZ,oBACA,uBAAuB,mBAAmB,oBAAoB;AAClE,SAAO;GACL;GACA,YAAY,YAAY;GACxB,UAAU,EAAE;GACb;;CAGH,IAAI,UAAU,eACZ,mBACA,oBACA,YACA,gBACA,oBACD;AAED,KAAI,CAAC,eACH,WAAU,uBAAuB,SAAS,oBAAoB;AAGhE,QAAO;EACL;EACA,YAAY,YAAY;EACxB;EACD;;;;;;AAOH,SAAS,eACP,mBACA,oBACA,YACA,gBACA,eACQ;CACR,MAAM,QAAQ,kBAAkB,MAAM,KAAK;CAE3C,MAAM,UAAU,CAAC,GAAG,mBAAmB,CAAC,MACrC,GAAG,MAAM,EAAE,YAAY,EAAE,UAC3B;AAED,MAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,UAAU,WAAW,IAAI,QAAQ,SAAS;AAChD,MAAI,CAAC,QAAS;AACd,MAAI,QAAQ,YAAY,KAAK,QAAQ,UAAU,MAAM,OAAQ;EAE7D,MAAM,QAAQ,QAAQ,YAAY;AAElC,MAAI,CAAC,gBAAgB;AAEnB,OAAI,QAAQ,gBAAgB,EAAG;GAG/B,MAAM,SADS,iBAAiB,CAAC,QAAQ,WACjB,OAAO,QAAQ,GAAG,OAAO,KAAK,QAAQ,GAAG;GACjE,MAAM,EAAE,MAAM,YAAY,iBAAiB,MAAM,QAAQ,QAAQ;AAEjE,SAAM,SAAS,GAAG,KAAK,GAAG,SAAS;AACnC;;AASF,MAAI,QAAQ,iBAAiB,GAAG;GAC9B,MAAM,EAAE,MAAM,YAAY,iBAAiB,MAAM,QAAQ,QAAQ;AACjE,SAAM,SAAS,GAAG,OAAO;;AAG3B,MAAI,QAAQ,WAAW;GAErB,MAAM,EAAE,MAAM,aAAa,cAAc,QAAQ;GACjD,MAAM,UAAU,MAAM,OAAO;AAC7B,SAAM,OAAO,KACX,QAAQ,MAAM,GAAG,cAAc,EAAE,GACjC,OAAO,QAAQ,GAAG,KAClB,QAAQ,MAAM,YAAY,EAAE;AAC9B;;EAGF,MAAM,SAAS,MAAM,OAAO,MAAM,GAAG,KAAK,IAAI,GAAG,QAAQ,cAAc,EAAE,CAAC;EAC1E,MAAM,OAAO,MAAM,MAAM,OAAO,QAAQ,QAAQ,CAAC,KAAK,SAAS,KAAK,OAAO;AAE3E,QAAM,OACJ,OACA,QAAQ,UAAU,QAAQ,YAAY,GACtC,GAAG,OAAO,WAAW,QAAQ,GAAG,KAChC,GAAG,MACH,GAAG,OAAO,QACX;;AAGH,QAAO,MAAM,KAAK,KAAK;;;;;;AAOzB,SAAS,iBACP,MACA,SACmC;AACnC,QAAO;EACL,MAAM,KAAK,MAAM,GAAG,QAAQ,gBAAgB,EAAE,CAAC,QAAQ,iBAAiB,GAAG;EAC3E,SAAS,KAAK,MAAM,QAAQ,gBAAgB,EAAE;EAC/C;;;;;AAMH,SAAS,uBACP,SACA,eACQ;AACR,QAAO,0BAA0B,UAAU,SAAS;EAClD,MAAM,MAAM,KAAK,MAAM,YAAY;AACnC,MAAI,CAAC,IAAK,QAAO;EAEjB,MAAM,UAAU,IAAI,GAAG,MAAM,gCAAgC;EAC7D,MAAM,OAAO,IAAI,GAAG,MAAM,mCAAmC;AAE7D,MAAI,iBAAiB,MAAM;GACzB,MAAM,OAAO,IAAI,GAAG,QAAQ,iBAAiB,GAAG;AAChD,UAAO,GAAG,IAAI,KAAK,IAAI,KAAK,KAAK,OAAO,KAAK,GAAG;;AAElD,MAAI,CAAC,iBAAiB,SAAS;GAC7B,MAAM,OAAO,IAAI,GAAG,QAAQ,iBAAiB,GAAG;AAChD,UAAO,GAAG,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK,QAAQ,GAAG;;AAEnD,SAAO;GACP"}
|
package/dist/workflows/stage.js
CHANGED
|
@@ -35,7 +35,7 @@ async function runStageFilesWorkflow({ files, options, settings, inlineLibrary }
|
|
|
35
35
|
files,
|
|
36
36
|
branchData
|
|
37
37
|
});
|
|
38
|
-
if (settings.options?.saveLocal
|
|
38
|
+
if (settings.options?.saveLocal === true) await userEditDiffsStep.run(uploadedFiles);
|
|
39
39
|
if (settings.tag) try {
|
|
40
40
|
await new TagStep(api, settings, !!options.tag).run(uploadedFiles);
|
|
41
41
|
} catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage.js","names":[],"sources":["../../src/workflows/stage.ts"],"sourcesContent":["import { logCollectedFiles, logErrorAndExit } from '../console/logging.js';\nimport { branchResolutionError, withOriginalError } from '../console/index.js';\nimport { logger } from '../console/logger.js';\nimport { Settings, TranslateFlags } from '../types/index.js';\nimport { api } from '../utils/api.js';\nimport { EnqueueFilesResult, FileToUpload } from 'generaltranslation/types';\nimport { UploadSourcesStep } from './steps/UploadSourcesStep.js';\nimport { SetupStep } from './steps/SetupStep.js';\nimport { EnqueueStep } from './steps/EnqueueStep.js';\nimport { BranchStep } from './steps/BranchStep.js';\nimport { TagStep } from './steps/TagStep.js';\nimport { UserEditDiffsStep } from './steps/UserEditDiffsStep.js';\nimport { BranchData } from '../types/branch.js';\nimport { calculateTimeoutMs } from '../utils/calculateTimeoutMs.js';\nimport { filterFilesForEnqueue } from './utils/filterFilesForEnqueue.js';\nimport { syncFonts } from './utils/syncFonts.js';\nimport type { InlineLibrary } from '../types/libraries.js';\n\n/**\n * Sends multiple files for translation to the API using a workflow pattern\n * @param files - Array of file objects to translate\n * @param options - The options for the API call\n * @param settings - Settings configuration\n * @returns The translated content or version ID\n */\nexport async function runStageFilesWorkflow({\n files,\n options,\n settings,\n inlineLibrary,\n}: {\n files: FileToUpload[];\n options: TranslateFlags;\n settings: Settings;\n inlineLibrary?: InlineLibrary;\n}): Promise<{\n branchData: BranchData;\n enqueueResult: EnqueueFilesResult;\n}> {\n try {\n // Log files to be translated\n logCollectedFiles(files, undefined, inlineLibrary);\n\n // Sync fonts before enqueueing so the translation jobs (e.g. Lottie\n // layout refinement) can use them instead of fallback fonts.\n await syncFonts(settings);\n\n // Calculate timeout for setup step\n const timeoutMs = calculateTimeoutMs(options.timeout);\n\n // Create workflow with steps\n const branchStep = new BranchStep(api, settings);\n const uploadStep = new UploadSourcesStep(api, settings);\n const userEditDiffsStep = new UserEditDiffsStep(settings);\n const setupStep = new SetupStep(api, settings, timeoutMs);\n const enqueueStep = new EnqueueStep(api, settings, options.force);\n\n // first run the branch step\n const branchData = await branchStep.run();\n if (!branchData) {\n return logErrorAndExit(branchResolutionError);\n }\n\n // then run the upload step\n const uploadedFiles = await uploadStep.run({ files, branchData });\n\n // optionally run the user edit diffs step\n if (settings.options?.saveLocal
|
|
1
|
+
{"version":3,"file":"stage.js","names":[],"sources":["../../src/workflows/stage.ts"],"sourcesContent":["import { logCollectedFiles, logErrorAndExit } from '../console/logging.js';\nimport { branchResolutionError, withOriginalError } from '../console/index.js';\nimport { logger } from '../console/logger.js';\nimport { Settings, TranslateFlags } from '../types/index.js';\nimport { api } from '../utils/api.js';\nimport { EnqueueFilesResult, FileToUpload } from 'generaltranslation/types';\nimport { UploadSourcesStep } from './steps/UploadSourcesStep.js';\nimport { SetupStep } from './steps/SetupStep.js';\nimport { EnqueueStep } from './steps/EnqueueStep.js';\nimport { BranchStep } from './steps/BranchStep.js';\nimport { TagStep } from './steps/TagStep.js';\nimport { UserEditDiffsStep } from './steps/UserEditDiffsStep.js';\nimport { BranchData } from '../types/branch.js';\nimport { calculateTimeoutMs } from '../utils/calculateTimeoutMs.js';\nimport { filterFilesForEnqueue } from './utils/filterFilesForEnqueue.js';\nimport { syncFonts } from './utils/syncFonts.js';\nimport type { InlineLibrary } from '../types/libraries.js';\n\n/**\n * Sends multiple files for translation to the API using a workflow pattern\n * @param files - Array of file objects to translate\n * @param options - The options for the API call\n * @param settings - Settings configuration\n * @returns The translated content or version ID\n */\nexport async function runStageFilesWorkflow({\n files,\n options,\n settings,\n inlineLibrary,\n}: {\n files: FileToUpload[];\n options: TranslateFlags;\n settings: Settings;\n inlineLibrary?: InlineLibrary;\n}): Promise<{\n branchData: BranchData;\n enqueueResult: EnqueueFilesResult;\n}> {\n try {\n // Log files to be translated\n logCollectedFiles(files, undefined, inlineLibrary);\n\n // Sync fonts before enqueueing so the translation jobs (e.g. Lottie\n // layout refinement) can use them instead of fallback fonts.\n await syncFonts(settings);\n\n // Calculate timeout for setup step\n const timeoutMs = calculateTimeoutMs(options.timeout);\n\n // Create workflow with steps\n const branchStep = new BranchStep(api, settings);\n const uploadStep = new UploadSourcesStep(api, settings);\n const userEditDiffsStep = new UserEditDiffsStep(settings);\n const setupStep = new SetupStep(api, settings, timeoutMs);\n const enqueueStep = new EnqueueStep(api, settings, options.force);\n\n // first run the branch step\n const branchData = await branchStep.run();\n if (!branchData) {\n return logErrorAndExit(branchResolutionError);\n }\n\n // then run the upload step\n const uploadedFiles = await uploadStep.run({ files, branchData });\n\n // optionally run the user edit diffs step (opt-in via --save-local or options.saveLocal)\n if (settings.options?.saveLocal === true) {\n await userEditDiffsStep.run(uploadedFiles);\n }\n\n // then run the tag step (non-fatal — tagging failure should not block translations)\n if (settings.tag) {\n try {\n const userProvidedTag = !!options.tag;\n const tagStep = new TagStep(api, settings, userProvidedTag);\n await tagStep.run(uploadedFiles);\n } catch {\n logger.warn('Failed to create translation tag. Continuing...');\n }\n }\n\n // then run the setup step\n await setupStep.run(uploadedFiles);\n\n // then run the enqueue step\n const { filesToEnqueue, skippedFiles } = await filterFilesForEnqueue({\n gt: api,\n files: uploadedFiles,\n locales: settings.locales,\n force: options.force,\n });\n if (skippedFiles.length > 0) {\n logger.info(\n `Skipped enqueue for ${skippedFiles.length} already translated file${skippedFiles.length === 1 ? '' : 's'}`\n );\n }\n\n const enqueueResult = await enqueueStep.run(filesToEnqueue);\n\n return { branchData, enqueueResult };\n } catch (error) {\n return logErrorAndExit(\n withOriginalError(\n 'Files could not be sent for translation. Check the files, branch configuration, and API credentials, then try again.',\n error\n )\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyBA,eAAsB,sBAAsB,EAC1C,OACA,SACA,UACA,iBASC;AACD,KAAI;AAEF,oBAAkB,OAAO,KAAA,GAAW,cAAc;AAIlD,QAAM,UAAU,SAAS;EAGzB,MAAM,YAAY,mBAAmB,QAAQ,QAAQ;EAGrD,MAAM,aAAa,IAAI,WAAW,KAAK,SAAS;EAChD,MAAM,aAAa,IAAI,kBAAkB,KAAK,SAAS;EACvD,MAAM,oBAAoB,IAAI,kBAAkB,SAAS;EACzD,MAAM,YAAY,IAAI,UAAU,KAAK,UAAU,UAAU;EACzD,MAAM,cAAc,IAAI,YAAY,KAAK,UAAU,QAAQ,MAAM;EAGjE,MAAM,aAAa,MAAM,WAAW,KAAK;AACzC,MAAI,CAAC,WACH,QAAO,gBAAgB,sBAAsB;EAI/C,MAAM,gBAAgB,MAAM,WAAW,IAAI;GAAE;GAAO;GAAY,CAAC;AAGjE,MAAI,SAAS,SAAS,cAAc,KAClC,OAAM,kBAAkB,IAAI,cAAc;AAI5C,MAAI,SAAS,IACX,KAAI;AAGF,SAAM,IADc,QAAQ,KAAK,UAAU,CADlB,CAAC,QAAQ,IAErB,CAAC,IAAI,cAAc;UAC1B;AACN,UAAO,KAAK,kDAAkD;;AAKlE,QAAM,UAAU,IAAI,cAAc;EAGlC,MAAM,EAAE,gBAAgB,iBAAiB,MAAM,sBAAsB;GACnE,IAAI;GACJ,OAAO;GACP,SAAS,SAAS;GAClB,OAAO,QAAQ;GAChB,CAAC;AACF,MAAI,aAAa,SAAS,EACxB,QAAO,KACL,uBAAuB,aAAa,OAAO,0BAA0B,aAAa,WAAW,IAAI,KAAK,MACvG;AAKH,SAAO;GAAE;GAAY,eAAA,MAFO,YAAY,IAAI,eAAe;GAEvB;UAC7B,OAAO;AACd,SAAO,gBACL,kBACE,wHACA,MACD,CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gt",
|
|
3
|
-
"version": "2.20.
|
|
3
|
+
"version": "2.20.3",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": "bin/main.js",
|
|
6
6
|
"files": [
|
|
@@ -116,10 +116,10 @@
|
|
|
116
116
|
"unist-util-visit": "^5.0.0",
|
|
117
117
|
"yaml": "^2.8.0",
|
|
118
118
|
"@generaltranslation/format": "0.1.8",
|
|
119
|
-
"@generaltranslation/icu": "0.1.2",
|
|
120
119
|
"@generaltranslation/python-extractor": "0.2.47",
|
|
121
|
-
"@generaltranslation/
|
|
120
|
+
"@generaltranslation/icu": "0.1.2",
|
|
122
121
|
"@generaltranslation/vue-extractor": "0.1.7",
|
|
122
|
+
"@generaltranslation/supported-locales": "2.1.27",
|
|
123
123
|
"generaltranslation": "9.2.0",
|
|
124
124
|
"gt-remark": "1.0.12"
|
|
125
125
|
},
|