gt 2.22.0 → 2.22.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":[],"sources":["../../src/cli/base.ts"],"sourcesContent":["import { Command, InvalidArgumentError, Option } from 'commander';\nimport { ProjectApiKeyPermission } from 'generaltranslation/api';\nimport {\n DEFAULT_TRANSLATIONS_DIR,\n DEFAULT_VITE_TRANSLATIONS_DIR,\n} from '../utils/constants.js';\nimport { createOrUpdateConfig } from '../fs/config/setupConfig.js';\nimport findFilepath from '../fs/findFilepath.js';\nimport {\n displayHeader,\n promptText,\n logErrorAndExit,\n exitSync,\n promptConfirm,\n promptMultiSelect,\n promptSelect,\n promptGlobPatterns,\n} from '../console/logging.js';\nimport { logger } from '../console/logger.js';\nimport { lottieTranslateError } from '../console/index.js';\nimport { parseGlobPatterns } from '../console/promptParsing.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport {\n FilesOptions,\n Settings,\n SupportedFrameworks,\n SupportedLibraries,\n SetupOptions,\n TranslateFlags,\n SharedFlags,\n} from '../types/index.js';\nimport { generateSettings } from '../config/generateSettings.js';\nimport chalk from 'chalk';\nimport { FILE_EXT_TO_EXT_LABEL } from '../formats/files/supportedFiles.js';\nimport { handleSetupReactCommand } from '../setup/wizard.js';\nimport {\n isPackageInstalled,\n searchForPackageJson,\n} from '../utils/packageJson.js';\nimport { getDesiredLocales } from '../setup/userInput.js';\nimport { installPackage } from '../utils/installPackage.js';\nimport { getPackageManager } from '../utils/packageManager.js';\nimport { areCredentialsSet } from '../utils/credentials.js';\nimport { provisionDevelopmentCredentials } from '../setup/developmentCredentials.js';\nimport { upload } from './commands/upload.js';\nimport { attachSharedFlags, attachTranslateFlags } from './flags.js';\nimport { handleStage } from './commands/stage.js';\nimport { handleSetupProject } from './commands/setupProject.js';\nimport { handleDownload } from './commands/download.js';\nimport {\n handleTranslate,\n postProcessTranslations,\n} from './commands/translate.js';\nimport {\n getNeedsPostprocessing,\n clearDownloaded,\n} from '../state/recentDownloads.js';\nimport { clearWarnings } from '../state/translateWarnings.js';\nimport { displayTranslateSummary } from '../console/displayTranslateSummary.js';\nimport updateConfig from '../fs/config/updateConfig.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { createLoadTranslationsFile } from '../fs/createLoadTranslationsFile.js';\nimport { saveLocalEdits } from '../api/saveLocalEdits.js';\nimport {\n hasValidCredentials,\n hasValidServiceLocales,\n} from './commands/utils/validation.js';\nimport processSharedStaticAssets, {\n mirrorAssetsToLocales,\n} from '../utils/sharedStaticAssets.js';\nimport { setupLocadex } from '../locadex/setupFlow.js';\nimport { detectFramework } from '../setup/detectFramework.js';\nimport {\n getFrameworkDisplayName,\n getReactFrameworkLibrary,\n} from '../setup/frameworkUtils.js';\nimport { INLINE_LIBRARIES, Libraries } from '../types/libraries.js';\nimport { handleEnqueue } from './commands/enqueue.js';\nimport { splitMintlifyLanguageRefs } from '../utils/splitMintlifyLanguageRefs.js';\nimport { runMergeDriver } from '../git/mergeDrivers.js';\nimport { setupGitMergeDrivers } from '../git/setupMergeDrivers.js';\nimport { warnReactPackageCompatibility } from '../utils/reactPackageCompatibility.js';\nimport {\n createDiagnosticMessage,\n formatDiagnosticErrorDetails,\n} from 'generaltranslation/diagnostics';\nimport { hasLogin, login, logout, whoAmI } from '../auth/oauth.js';\nimport { UserAuthError } from '../auth/errors.js';\nimport { resolveConfig } from '../config/resolveConfig.js';\nimport { setupViteSPA } from '../setup/setupViteSPA.js';\nimport { manifestDirectlyDeclaresGTVue } from '@generaltranslation/vue-extractor/integration';\nimport { api } from '../utils/api.js';\nimport { handleApiCommand, type ApiCommandOptions } from './commands/api.js';\n\nconst ID_COMPATIBILITY_WARNING_COMMANDS = new Set([\n 'download',\n 'enqueue',\n 'generate',\n 'setup',\n 'stage',\n 'translate',\n 'validate',\n]);\nconst workspaceRootSetupError = createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The setup wizard cannot run from a monorepo workspace root',\n why: 'GT must be configured in the specific app you want to localize',\n fix: \"Change to that app's directory and rerun `npx gt@latest`\",\n});\nfunction createProjectCommandError(\n whatHappened: string,\n error: unknown\n): string {\n return createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened,\n details: formatDiagnosticErrorDetails(error),\n });\n}\n\nconst emptyApiKeyNameError = createDiagnosticMessage({\n whatHappened: 'The key name cannot be empty',\n fix: 'Pass a non-empty value with --name',\n});\n\nfunction parseApiKeyName(value: string): string {\n const name = value.trim();\n if (!name) throw new InvalidArgumentError(emptyApiKeyNameError);\n return name;\n}\n\nconst electronSetupError = createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened:\n 'The automatic setup wizard is not ready for Electron applications',\n docsUrl: 'https://generaltranslation.com/docs/react',\n});\n\n/** .env.local never reaches production; the runtime key there is set on the host. */\nfunction productionRuntimeKeyGuidance(dashboardUrl: string): string {\n return `${chalk.dim('For runtime translation in production, create an API key in the dashboard')} ${chalk.cyan(dashboardUrl)} ${chalk.dim('and set GT_API_KEY and GT_PROJECT_ID in your hosting environment.')}`;\n}\n\nasync function loginInteractively(\n baseUrl: string | undefined,\n useBrowser = true\n): Promise<void> {\n await login({\n baseUrl,\n noBrowser: !useBrowser,\n onDeviceCode: ({ userCode, verificationUri, verificationUriComplete }) => {\n logger.message(\n `${useBrowser ? 'Opening your browser. If it does not open, on any device visit' : 'On any device, visit'} ${chalk.cyan(verificationUriComplete ?? verificationUri)} and ${verificationUriComplete ? 'confirm' : 'enter'} the code ${chalk.bold(userCode)}\\nWaiting for approval...`\n );\n },\n onAuthorizationUrl: (url) => {\n logger.message(\n `Opening your browser to sign in. If it does not open, visit:\\n${chalk.cyan(url)}`\n );\n },\n });\n}\n\nfunction createUserAuthError(whatHappened: string, error: unknown): string {\n if (error instanceof UserAuthError) return error.message;\n return createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened,\n details: formatDiagnosticErrorDetails(error),\n fix: 'Run `gt login` and try again',\n });\n}\n\nasync function exitIfUnsupportedSetupTarget(): Promise<void> {\n const packageJson = await searchForPackageJson();\n if (packageJson && isPackageInstalled('electron', packageJson, false, true)) {\n logErrorAndExit(electronSetupError);\n }\n if (\n fs.existsSync(path.join(process.cwd(), 'pnpm-workspace.yaml')) ||\n packageJson?.workspaces\n ) {\n logErrorAndExit(workspaceRootSetupError);\n }\n}\n\nexport type UploadOptions = {\n config?: string;\n apiKey?: string;\n projectId?: string;\n defaultLocale?: string;\n};\n\nexport type GitSetupOptions = {\n config?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\n};\n\ntype LocalTranslationGuidanceOptions = {\n generatedLoader: boolean;\n runtimeSetup: InlineRuntimeSetup;\n translationsDir: string;\n};\n\ntype InlineRuntimeSetup = {\n hasOtherInlineRuntime: boolean;\n hasVueRuntime: boolean;\n ranReactSetup: boolean;\n};\n\nexport class BaseCLI {\n protected library: SupportedLibraries;\n protected additionalModules: SupportedLibraries[];\n protected program: Command;\n // Constructor is shared amongst all CLI class types\n public constructor(\n program: Command,\n library: SupportedLibraries,\n additionalModules?: SupportedLibraries[]\n ) {\n this.program = program;\n this.library = library;\n this.additionalModules = additionalModules || [];\n\n this.program.option(\n '--skip-version-check',\n 'Skip the monorepo GT package version consistency check'\n );\n this.program.option(\n '--suppress-id-compatibility-warning',\n 'Suppress the React package ID compatibility warning'\n );\n this.program.option(\n '-q, --quiet',\n 'Suppress informational output; only warnings and errors are shown'\n );\n // Select console routing for this command before anything else logs:\n // root hooks run before subclass hooks (version checks) and the action's\n // settings resolution. `gt api-key create` prints the new secret on\n // stdout, so its diagnostics go to stderr; every other command gets the\n // historical default back (main() routes startup output to stderr).\n this.program.hook('preAction', (_thisCommand, actionCommand) => {\n logger.setConsoleOutput(\n actionCommand.parent?.name() === 'api-key' ? 'stderr' : 'stdout'\n );\n });\n // Apply --quiet before any other hook or command action runs so the\n // singleton logger is muted for the rest of the invocation. The flag is a\n // global root option, so commander resolves it in any position and for\n // nested commands (e.g. `gt git setup --quiet`).\n this.program.hook('preAction', () => {\n logger.setQuiet(Boolean(this.program.opts().quiet));\n });\n this.program.hook('preAction', async (thisCommand, actionCommand) => {\n // Nested commands (e.g. `gt git setup`) can share leaf names with\n // translation commands; only direct children of the root qualify\n if (actionCommand.parent !== thisCommand) return;\n if (!ID_COMPATIBILITY_WARNING_COMMANDS.has(actionCommand.name())) return;\n await warnReactPackageCompatibility(\n Boolean(this.program.opts().suppressIdCompatibilityWarning)\n );\n });\n\n this.setupInitCommand();\n this.setupConfigureCommand();\n this.setupUploadCommand();\n this.setupUserAuthCommands();\n this.setupSendDiffsCommand();\n this.setupApiCommand();\n this.setupProjectCommands();\n this.setupApiKeyCommands();\n this.setupGitCommand();\n }\n // Init is never called in a child class\n public init() {\n this.setupSetupProjectCommand();\n this.setupStageCommand();\n this.setupTranslateCommand();\n this.setupDownloadCommand();\n this.setupEnqueueCommand();\n }\n // Execute is called by the main program\n public execute() {\n // If no command is specified, run 'init'\n if (process.argv.length <= 2) {\n process.argv.push('init');\n }\n }\n\n protected setupSetupProjectCommand(): void {\n attachTranslateFlags(\n this.program\n .command('setup')\n .description(\n 'Upload source files and setup the project for translation'\n )\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Uploading source files and setting up project...');\n await this.handleSetupProject(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupStageCommand(): void {\n attachTranslateFlags(\n this.program\n .command('stage')\n .description(\n 'Submits the project to the General Translation API for translation. Translations created using this command will require human approval.'\n )\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader(\n 'Staging project for translation with approval required...'\n );\n await this.handleStage(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n /**\n * Enqueues translations for a given set of files\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected setupEnqueueCommand(): void {\n attachTranslateFlags(\n this.program\n .command('enqueue')\n .description('Enqueues translations for a given set of files')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Enqueuing translations...');\n await this.handleEnqueue(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n /**\n * Downloads translations that were originally staged\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected setupDownloadCommand(): void {\n attachTranslateFlags(\n this.program\n .command('download')\n .description('Download translations that were originally staged')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Downloading translations...');\n await this.handleDownload(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupTranslateCommand(): void {\n attachTranslateFlags(\n this.program\n .command('translate')\n .description('Translate your project using General Translation')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Starting translation...');\n await this.handleTranslate(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupSendDiffsCommand(): void {\n attachSharedFlags(\n this.program\n .command('save-local')\n .description(\n 'Save local edits for all configured files by sending diffs (no translation enqueued)'\n )\n )\n .option('--publish', 'Publish translations to the CDN', false)\n .action(async (initOptions: SharedFlags) => {\n displayHeader('Saving local edits...');\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n if (!hasValidCredentials(settings) || !hasValidServiceLocales(settings))\n return exitSync(1);\n await saveLocalEdits(settings);\n logger.endCommand('Saved local edits');\n });\n }\n\n protected setupApiCommand(): void {\n attachSharedFlags(\n this.program\n .command('api [endpoint]')\n .description('Make an authenticated request to the GT API')\n .option('-X, --method <method>', 'HTTP method', 'GET')\n .option('--input <file>', 'Request body file, or - for standard input')\n .option(\n '-H, --header <header>',\n 'Request header in \"Key: Value\" format',\n (header, headers: string[] | undefined) => [\n ...(headers ?? []),\n header,\n ]\n )\n .option('-i, --include', 'Include response status and headers')\n .option('--spec', 'Print the bundled OpenAPI specification')\n ).action((endpoint, options: ApiCommandOptions) =>\n handleApiCommand(endpoint, options)\n );\n }\n\n protected setupProjectCommands(): void {\n const projectCommand = this.program\n .command('project')\n .description('Manage General Translation projects');\n\n attachSharedFlags(\n projectCommand\n .command('create')\n .description(\n 'Create a General Translation project using an organization API key'\n )\n .requiredOption('--org-id <orgId>', 'Organization ID')\n .requiredOption('--name <name>', 'Project name')\n .requiredOption('--default-locale <locale>', 'Project default locale')\n .option('--cdn-enabled', 'Enable CDN delivery', false)\n ).action(async (options) => {\n try {\n const settings = await generateSettings(options);\n // Project creation happens before a project ID exists.\n if (!hasValidServiceLocales(settings)) return exitSync(1);\n const { project } = await api.createProject(options.orgId, {\n name: options.name,\n defaultLocale: options.defaultLocale,\n cdnEnabled: options.cdnEnabled,\n });\n logger.info(`Created ${project.name} (${project.id})`);\n } catch (error) {\n return logErrorAndExit(\n createProjectCommandError('Failed to create the project', error)\n );\n }\n });\n\n attachSharedFlags(\n projectCommand\n .command('status')\n .description('Check the status of a project setup job')\n .argument('<job-id>', 'Setup job ID')\n ).action(async (jobId, options) => {\n try {\n const settings = await generateSettings(options);\n if (!hasValidCredentials(settings)) return exitSync(1);\n const [status] = await api.checkJobStatus([jobId]);\n logger.info(status?.status ?? 'unknown');\n } catch (error) {\n return logErrorAndExit(\n createProjectCommandError(\n 'Failed to check the project setup status',\n error\n )\n );\n }\n });\n }\n\n protected setupApiKeyCommands(): void {\n const apiKeyCommand = this.program\n .command('api-key')\n .description('Manage API keys for the configured project');\n\n attachSharedFlags(\n apiKeyCommand\n .command('create')\n .description(\n 'Create a project API key with the requested permissions and print it once'\n )\n .requiredOption('--name <name>', 'Key name', parseApiKeyName)\n // Validated before any request; the server grants the requested set\n // all-or-nothing, so omitting permissions would delegate everything.\n .addOption(\n new Option('--permission <permissions...>', 'Permissions to grant')\n .choices(Object.values(ProjectApiKeyPermission))\n .makeOptionMandatory()\n )\n ).action(\n async (\n options: SharedFlags & {\n name: string;\n permission: ProjectApiKeyPermission[];\n }\n ) => {\n try {\n const settings = await generateSettings(options);\n if (!hasValidCredentials(settings)) return exitSync(1);\n const { apiKey } = await api.createProjectApiKey(settings.projectId, {\n name: options.name,\n permissions: options.permission,\n });\n // Raw stdout: the secret is shown once and never reaches the log file.\n process.stdout.write(`${apiKey.key}\\n`);\n } catch (error) {\n return logErrorAndExit(\n error instanceof UserAuthError\n ? error.message\n : createProjectCommandError('Failed to create the API key', error)\n );\n }\n }\n );\n }\n\n protected setupGitCommand(): void {\n const gitCommand = this.program\n .command('git')\n .description('Configure Git integrations for General Translation');\n\n gitCommand\n .command('setup')\n .description('Set up GT merge drivers for generated translation files')\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option('--dry-run', 'Print changes without writing files', false)\n .option(\n '--omit-config-ids',\n 'Persist omitConfigIds and remove generated config IDs'\n )\n .option(\n '--driver-command <command>',\n 'Command Git should use to invoke gt, e.g. \"pnpm exec gt\"'\n )\n .action(async (options: GitSetupOptions) => {\n displayHeader('Setting up GT Git merge drivers...');\n const settings = await generateSettings(options, undefined, {\n requireConfig: true,\n });\n const omitConfigIds = await this.resolveGitSetupOmitConfigIds(\n options,\n settings\n );\n const result = await setupGitMergeDrivers(settings, {\n dryRun: options.dryRun,\n omitConfigIds,\n driverCommand: options.driverCommand,\n });\n\n for (const line of result.addedAttributes) {\n logger.step(\n `${options.dryRun ? 'Would add' : 'Added'} ${chalk.cyan(\n line\n )} to ${chalk.cyan(result.gitattributesPath)}`\n );\n }\n if (result.addedAttributes.length === 0) {\n logger.info(`${chalk.cyan('.gitattributes')} is already configured.`);\n }\n\n for (const args of result.gitConfigCommands) {\n logger.step(\n `${options.dryRun ? 'Would run' : 'Configured'} ${chalk.cyan(\n `git config --local ${args.join(' ')}`\n )}`\n );\n }\n\n if (result.updatedConfig) {\n logger.step(\n `${options.dryRun ? 'Would persist' : 'Persisted'} ${chalk.cyan(\n 'omitConfigIds: true'\n )} in ${chalk.cyan(settings.config)}`\n );\n } else if (options.dryRun && !settings.omitConfigIds) {\n logger.info(\n `Run without ${chalk.cyan('--dry-run')} to be prompted to persist ${chalk.cyan(\n 'omitConfigIds: true'\n )}, or pass ${chalk.cyan('--omit-config-ids')}.`\n );\n }\n\n for (const warning of result.warnings) {\n logger.warn(chalk.yellow(warning));\n }\n\n logger.endCommand(\n options.dryRun\n ? 'Dry run complete.'\n : 'GT Git merge drivers configured.'\n );\n });\n\n gitCommand\n .command('merge-driver', { hidden: true })\n .argument('<driver>', 'Merge driver name')\n .argument('<base>', 'Common ancestor file')\n .argument('<ours>', 'Current branch file')\n .argument('<theirs>', 'Incoming branch file')\n .argument('[path]', 'Merged path')\n .action((driver: string, base: string, ours: string, theirs: string) => {\n if (driver !== 'gt-lock' && driver !== 'gtjson') {\n logger.error(`Unknown GT merge driver: ${driver}`);\n exitSync(1);\n }\n const result = runMergeDriver(driver, base, ours, theirs);\n if (!result.ok) {\n logger.error(result.reason);\n exitSync(1);\n }\n });\n }\n\n protected async resolveGitSetupOmitConfigIds(\n options: GitSetupOptions,\n settings: Settings\n ): Promise<boolean> {\n if (options.omitConfigIds) return true;\n // Already opted in: persist again so stale config IDs still get removed\n if (settings.omitConfigIds) return true;\n if (options.dryRun || !process.stdin.isTTY || !process.stdout.isTTY) {\n return false;\n }\n return promptConfirm({\n message:\n 'Also set omitConfigIds: true to reduce gt.config.json merge conflicts?',\n defaultValue: true,\n });\n }\n\n protected async handleSetupProject(\n initOptions: TranslateFlags\n ): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n await handleSetupProject(initOptions, settings, this.library);\n }\n\n protected async handleStage(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n if (!settings.stageTranslations) {\n // Update settings.stageTranslations to true\n settings.stageTranslations = true;\n await updateConfig(settings.config, {\n stageTranslations: true,\n });\n }\n await handleStage(initOptions, settings, this.library, true);\n }\n\n /**\n * Enqueues translations for a given set of files\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected async handleEnqueue(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await handleEnqueue(initOptions, settings, this.library);\n }\n\n /**\n * Downloads translations that were originally staged\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected async handleDownload(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await handleDownload(initOptions, settings, this.library);\n }\n\n protected async handleTranslate(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n if (!settings.stageTranslations) {\n // Lottie translations finish asynchronously server-side (layout\n // refinement runs after the translation job completes), so the immediate\n // translate flow would try to download them before they're ready. Only\n // the stage + download flow supports them.\n if (settings.files?.resolvedPaths.lottie?.length) {\n return logErrorAndExit(lottieTranslateError);\n }\n const results = await handleStage(\n initOptions,\n settings,\n this.library,\n false\n );\n if (results) {\n await handleTranslate(\n initOptions,\n settings,\n results.fileVersionData,\n results.jobData,\n results.branchData,\n results.publishMap,\n results.inlineLibrary\n );\n }\n } else {\n await handleDownload(initOptions, settings, this.library);\n }\n // Only postprocess files downloaded in this run\n const include = getNeedsPostprocessing();\n if (include.size > 0) {\n await postProcessTranslations(settings, include);\n }\n // Split Mintlify language entries into $ref files to keep docs.json small\n await splitMintlifyLanguageRefs(settings);\n // Mirror assets after translations are downloaded and locale dirs are populated\n await mirrorAssetsToLocales(settings);\n clearDownloaded();\n displayTranslateSummary();\n clearWarnings();\n }\n\n protected setupUploadCommand(): void {\n attachTranslateFlags(\n this.program\n .command('upload')\n .description(\n 'Upload source files and translations to the General Translation platform'\n )\n ).action(async (initOptions: UploadOptions) => {\n displayHeader('Starting upload...');\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n const options = { ...initOptions, ...settings };\n\n await this.handleUploadCommand(options);\n logger.endCommand('Done!');\n });\n }\n\n protected setupUserAuthCommands(): void {\n this.program\n .command('login')\n .description('Sign in to your General Translation account')\n .option(\n '--no-browser',\n 'Do not open a browser; show a sign-in URL to use on any device instead'\n )\n .action(async (options: { browser: boolean }) => {\n displayHeader('Signing in to General Translation...');\n try {\n // Tokens are bound to one API resource, so log in to the configured one.\n const baseUrl = resolveConfig(process.cwd())?.config.baseUrl;\n await loginInteractively(\n typeof baseUrl === 'string' ? baseUrl : undefined,\n options.browser\n );\n logger.endCommand('Signed in successfully.');\n } catch (error) {\n logErrorAndExit(createUserAuthError('Sign in failed', error));\n }\n });\n\n this.program\n .command('logout')\n .description('Sign out of your General Translation account')\n .action(async () => {\n try {\n await logout();\n logger.endCommand('Signed out successfully.');\n } catch (error) {\n logErrorAndExit(createUserAuthError('Sign out failed', error));\n }\n });\n\n this.program\n .command('whoami')\n .description('Show the signed-in General Translation account')\n .action(async () => {\n try {\n const user = await whoAmI();\n logger.message(user.email ?? user.name ?? user.sub);\n } catch (error) {\n logErrorAndExit(\n createUserAuthError('Could not load your account', error)\n );\n }\n });\n }\n\n protected setupInitCommand(): void {\n this.program\n .command('init')\n .description(\n 'Run the setup wizard to configure your project for General Translation'\n )\n .option(\n '--src <paths...>',\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 '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .action(async (options: SetupOptions) => {\n await exitIfUnsupportedSetupTarget();\n const settings = await generateSettings(options);\n displayHeader('Running setup wizard...');\n\n const framework = await detectFramework();\n\n const useAgent = await (async () => {\n let useAgentMessage;\n if (framework.name === 'mintlify') {\n useAgentMessage = `Mintlify project detected. Would you like to connect to GitHub so that the Locadex AI Agent can translate your project automatically?`;\n }\n if (framework.name === 'next-app') {\n useAgentMessage = `Next.js App Router detected. Would you like to connect to GitHub so that the Locadex AI Agent can set up your project automatically?`;\n }\n if (useAgentMessage) {\n return await promptConfirm({\n message: useAgentMessage,\n defaultValue: false,\n });\n }\n return false;\n })();\n\n if (useAgent) {\n await setupLocadex(settings);\n logger.endCommand(\n 'Once installed, Locadex will open a PR to your repository. See the docs for more information: https://generaltranslation.com/docs/locadex'\n );\n } else {\n // Get framework display info for the defaults message\n const frameworkDisplayName =\n framework.type === 'react'\n ? getFrameworkDisplayName(framework)\n : null;\n const library =\n framework.type === 'react'\n ? getReactFrameworkLibrary(framework)\n : null;\n\n // Build defaults description based on detected framework\n const defaultTranslationsDir =\n framework.name === 'vite'\n ? DEFAULT_VITE_TRANSLATIONS_DIR\n : DEFAULT_TRANSLATIONS_DIR;\n\n const defaultsDescription =\n framework.name === 'vite'\n ? `${library} & initializeGTSPA, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}`\n : framework.type === 'react'\n ? `${library} & GTProvider, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}`\n : `Files saved locally in ${defaultTranslationsDir}`;\n\n // Ask if user wants to use defaults\n const useDefaults = await promptConfirm({\n message: `Would you like to use the recommended General Translation defaults? ${chalk.dim(`(${defaultsDescription})`)}`,\n defaultValue: true,\n });\n\n let ranReactSetup = false;\n\n // so that people can run init in non-js projects\n if (framework.type === 'react') {\n const wrap = useDefaults\n ? true\n : await promptConfirm({\n message:\n framework.name === 'vite'\n ? `Would you like to install ${library} and configure initializeGTSPA? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`\n : `Would you like to install ${library} and add the GTProvider? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`,\n defaultValue: true,\n });\n\n if (wrap) {\n logger.info(\n `${chalk.yellow('[EXPERIMENTAL]')} Configuring project...`\n );\n await handleSetupReactCommand(options, framework, useDefaults);\n logger.endCommand(\n `Done! Since this wizard is experimental, please review the changes and make modifications as needed.\n\\nNext step: start internationalizing! See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`\n );\n ranReactSetup = true;\n }\n }\n\n if (ranReactSetup) {\n logger.startCommand('Setting up project config...');\n }\n // Configure gt.config.json\n await this.handleInitCommand(\n ranReactSetup,\n useDefaults,\n framework.name === 'vite',\n options,\n framework.name\n );\n\n logger.endCommand(\n 'Done! Check out our docs for more information on how to use General Translation: https://generaltranslation.com/docs'\n );\n }\n });\n }\n\n protected setupConfigureCommand(): void {\n this.program\n .command('configure')\n .description(\n 'Configure your project for General Translation. This will create a gt.config.json file in your codebase.'\n )\n .action(() => this.handleConfigureCommand());\n }\n\n protected async handleConfigureCommand(\n options?: SetupOptions\n ): Promise<void> {\n await exitIfUnsupportedSetupTarget();\n displayHeader('Configuring project...');\n\n logger.info(\n 'Welcome! This tool will help you configure your gt.config.json file. See the docs: https://generaltranslation.com/docs/cli/reference/config for more information.'\n );\n\n // Configure gt.config.json\n const framework = await detectFramework();\n await this.handleInitCommand(\n false,\n false,\n framework.name === 'vite',\n options\n );\n\n logger.endCommand(\n 'Done! Make sure you have an API key and project ID to use General Translation. Get them on the dashboard: https://generaltranslation.com/dashboard'\n );\n }\n\n protected async handleUploadCommand(\n settings: Settings & UploadOptions\n ): Promise<void> {\n if (!settings.files) {\n return;\n }\n\n // Process all file types at once with a single call\n await upload(settings);\n }\n\n /** Describes installed runtimes without changing historical package lookup. */\n protected getInlineRuntimeSetup(\n packageJson: Record<string, unknown>,\n ranReactSetup: boolean = false\n ): InlineRuntimeSetup {\n return {\n hasOtherInlineRuntime: INLINE_LIBRARIES.some(\n (lib) =>\n lib !== Libraries.GT_VUE && isPackageInstalled(lib, packageJson)\n ),\n hasVueRuntime: manifestDirectlyDeclaresGTVue(packageJson),\n ranReactSetup,\n };\n }\n\n /** Returns whether any inline runtime is installed for setup. */\n protected isInlineRuntimeInstalled(\n packageJson: Record<string, unknown>\n ): boolean {\n const { hasOtherInlineRuntime, hasVueRuntime } =\n this.getInlineRuntimeSetup(packageJson);\n return hasOtherInlineRuntime || hasVueRuntime;\n }\n\n /** Returns whether every installed runtime can consume CDN translations. */\n protected supportsCDNStorage(runtimeSetup: InlineRuntimeSetup): boolean {\n return !runtimeSetup.hasVueRuntime;\n }\n\n /** Returns whether setup should generate a local runtime loader. */\n protected shouldGenerateLocalTranslationLoader(\n isVite: boolean,\n runtimeSetup: InlineRuntimeSetup\n ): boolean {\n if (isVite) return false;\n if (!runtimeSetup.hasVueRuntime) return true;\n return runtimeSetup.hasOtherInlineRuntime || runtimeSetup.ranReactSetup;\n }\n\n /** Returns framework guidance after selecting local translation storage. */\n protected getLocalTranslationGuidance({\n generatedLoader,\n runtimeSetup,\n translationsDir,\n }: LocalTranslationGuidanceOptions): string | undefined {\n const guidance: string[] = [];\n if (generatedLoader) {\n guidance.push(`Created ${chalk.cyan('loadTranslations.js')} file for local translations.\nMake sure to add this function to your app configuration.\nSee https://generaltranslation.com/en/docs/next/guides/local-tx`);\n }\n if (runtimeSetup.hasVueRuntime) {\n guidance.push(`GT will write local translation files to ${translationsDir}.\nConfigure createGT({ loadTranslations }) to load files from that directory.\nSee https://www.npmjs.com/package/gt-vue`);\n }\n return guidance.length > 0 ? guidance.join('\\n') : undefined;\n }\n\n // Wizard for configuring gt.config.json\n protected async handleInitCommand(\n ranReactSetup: boolean,\n useDefaults: boolean = false,\n isVite: boolean = false,\n options?: SetupOptions,\n framework?: SupportedFrameworks\n ): Promise<void> {\n const configFilepath =\n options?.config ||\n (!isVite && fs.existsSync('src/gt.config.json')\n ? 'src/gt.config.json'\n : 'gt.config.json');\n const existingConfig = loadConfig(configFilepath);\n const { defaultLocale, locales } = await getDesiredLocales(existingConfig);\n\n const packageJson = await searchForPackageJson();\n\n // Ask if using another i18n library\n const runtimeSetup: InlineRuntimeSetup = packageJson\n ? this.getInlineRuntimeSetup(packageJson, ranReactSetup)\n : {\n hasOtherInlineRuntime: false,\n hasVueRuntime: false,\n ranReactSetup,\n };\n const isUsingGT =\n runtimeSetup.ranReactSetup ||\n runtimeSetup.hasOtherInlineRuntime ||\n runtimeSetup.hasVueRuntime;\n\n // Ask where the translations are stored\n const usingCDN = await (async () => {\n if (!isUsingGT) return false;\n if (useDefaults) return false; // Default to local\n if (!this.supportsCDNStorage(runtimeSetup)) return false;\n const selectedValue = await promptSelect({\n message: `Would you like to save translation files locally or use the General Translation CDN to store them?`,\n options: [\n { value: 'local', label: 'Save locally' },\n { value: 'cdn', label: 'Use CDN' },\n ],\n defaultValue: 'local',\n });\n return selectedValue === 'cdn';\n })();\n\n const defaultTranslationsDir = isVite\n ? DEFAULT_VITE_TRANSLATIONS_DIR\n : DEFAULT_TRANSLATIONS_DIR;\n\n // Ask where the translations are stored\n const translationsDir =\n isUsingGT && !usingCDN\n ? useDefaults\n ? defaultTranslationsDir\n : await promptText({\n message:\n 'What is the path to the directory where you would like to store your translation files?',\n defaultValue: defaultTranslationsDir,\n })\n : null;\n\n // Determine final translations directory with fallback\n const finalTranslationsDir =\n translationsDir?.trim() || defaultTranslationsDir;\n\n if (isUsingGT && !usingCDN) {\n const generatedLoader = this.shouldGenerateLocalTranslationLoader(\n isVite,\n runtimeSetup\n );\n if (generatedLoader) {\n await createLoadTranslationsFile(\n process.cwd(),\n finalTranslationsDir,\n locales\n );\n }\n const guidance = this.getLocalTranslationGuidance({\n generatedLoader,\n runtimeSetup,\n translationsDir: finalTranslationsDir,\n });\n if (guidance) logger.message(guidance);\n }\n\n const message = !isUsingGT\n ? 'What is the format of your language resource files? Select as many as applicable.\\nAdditionally, you can translate any other files you have in your project.'\n : `Do you have any additional files in this project to translate? For example, Markdown files for docs. ${chalk.dim(\n '(To continue without selecting press Enter)'\n )}`;\n const fileExtensions =\n useDefaults && isUsingGT\n ? [] // Skip for GT projects when using defaults\n : await promptMultiSelect({\n message,\n options: [\n { value: 'json', label: FILE_EXT_TO_EXT_LABEL.json },\n { value: 'md', label: FILE_EXT_TO_EXT_LABEL.md },\n { value: 'mdx', label: FILE_EXT_TO_EXT_LABEL.mdx },\n { value: 'ts', label: FILE_EXT_TO_EXT_LABEL.ts },\n { value: 'js', label: FILE_EXT_TO_EXT_LABEL.js },\n { value: 'yaml', label: FILE_EXT_TO_EXT_LABEL.yaml },\n // TWILIO_CONTENT_JSON not supported in CLI init as its too niche\n ],\n required: !isUsingGT,\n });\n\n const files: FilesOptions = {};\n for (const fileExtension of fileExtensions) {\n const label = FILE_EXT_TO_EXT_LABEL[fileExtension];\n const paths = await promptGlobPatterns({\n label,\n message: `${chalk.cyan(FILE_EXT_TO_EXT_LABEL[fileExtension])}: Enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_EXT_LABEL[fileExtension]} files you would like to translate.\\nMake sure to include [locale] in the patterns.\\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,\n defaultValue: `./**/[locale]/*.${fileExtension}`,\n });\n\n files[fileExtension] = {\n include: parseGlobPatterns(paths),\n };\n }\n\n // Add GT translations if using GT and storing locally\n if (isUsingGT && !usingCDN) {\n files.gt = {\n output: path.join(finalTranslationsDir, `[locale].json`),\n };\n }\n\n // Create gt.config.json\n await createOrUpdateConfig(configFilepath, {\n defaultLocale,\n locales,\n src: options?.src,\n files: Object.keys(files).length > 0 ? files : undefined,\n framework: isVite ? 'vite' : undefined,\n publish: isUsingGT && usingCDN,\n clearPublish: runtimeSetup.hasVueRuntime && !usingCDN,\n });\n\n logger.success(\n `Edit ${chalk.cyan(\n configFilepath\n )} to customize your translation setup. Docs: https://generaltranslation.com/docs/cli/reference/config`\n );\n\n if (ranReactSetup && isVite) {\n await setupViteSPA({\n appDirectory: process.cwd(),\n configFilepath,\n defaultLocale,\n locales,\n translationsDir: usingCDN ? undefined : finalTranslationsDir,\n });\n }\n\n // Install gt if not installed\n const isCLIInstalled = packageJson\n ? isPackageInstalled('gt', packageJson, true, true)\n : true; // if no package.json, we can't install it\n\n if (!isCLIInstalled && !(isUsingGT && isVite)) {\n const packageManager = await getPackageManager();\n const spinner = logger.createSpinner();\n spinner.start(\n `Installing gt as a dev dependency with ${packageManager.name}...`\n );\n await installPackage('gt', packageManager, true);\n spinner.stop(chalk.green('Installed gt.'));\n }\n\n const localVite = isVite && isUsingGT && !usingCDN;\n const enableLiveTranslations =\n localVite &&\n (await promptConfirm({\n message:\n 'Would you like to set up live development translations? This requires signing in or an API key.',\n defaultValue: false,\n }));\n if (!localVite || enableLiveTranslations) {\n const settings = await generateSettings({ config: configFilepath });\n const envFramework = framework ?? (isVite ? 'vite' : settings.framework);\n // The CLI translates as the signed-in user; an API key in the\n // environment takes precedence and needs no login. A development key in\n // .env.local is runtime-only and never stands in for either.\n if (\n !settings.apiKey &&\n !(await hasLogin({ baseUrl: settings.baseUrl }))\n ) {\n try {\n await loginInteractively(settings.baseUrl);\n logger.message('Signed in successfully.');\n } catch (error) {\n logErrorAndExit(createUserAuthError('Sign in failed', error));\n }\n }\n if (!areCredentialsSet(settings, envFramework)) {\n const provision =\n useDefaults || enableLiveTranslations\n ? true\n : await promptConfirm({\n message:\n 'Would you like to set up a project ID and hot-reload key in .env.local?',\n defaultValue: true,\n });\n if (provision) {\n try {\n await provisionDevelopmentCredentials(settings, envFramework);\n } catch (error) {\n logErrorAndExit(\n error instanceof UserAuthError\n ? error.message\n : createProjectCommandError(\n 'Failed to set up the development credentials',\n error\n )\n );\n }\n logger.message(productionRuntimeKeyGuidance(settings.dashboardUrl));\n }\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base.js","names":[],"sources":["../../src/cli/base.ts"],"sourcesContent":["import { Command, InvalidArgumentError, Option } from 'commander';\nimport { ProjectApiKeyPermission } from 'generaltranslation/api';\nimport {\n DEFAULT_TRANSLATIONS_DIR,\n DEFAULT_VITE_TRANSLATIONS_DIR,\n} from '../utils/constants.js';\nimport { createOrUpdateConfig } from '../fs/config/setupConfig.js';\nimport findFilepath from '../fs/findFilepath.js';\nimport {\n displayHeader,\n promptText,\n logErrorAndExit,\n exitSync,\n promptConfirm,\n promptMultiSelect,\n promptSelect,\n promptGlobPatterns,\n} from '../console/logging.js';\nimport { logger } from '../console/logger.js';\nimport { lottieTranslateError } from '../console/index.js';\nimport { parseGlobPatterns } from '../console/promptParsing.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport {\n FilesOptions,\n Settings,\n SupportedFrameworks,\n SupportedLibraries,\n SetupOptions,\n TranslateFlags,\n SharedFlags,\n} from '../types/index.js';\nimport { generateSettings } from '../config/generateSettings.js';\nimport chalk from 'chalk';\nimport { FILE_EXT_TO_EXT_LABEL } from '../formats/files/supportedFiles.js';\nimport { handleSetupReactCommand } from '../setup/wizard.js';\nimport {\n isPackageInstalled,\n searchForPackageJson,\n} from '../utils/packageJson.js';\nimport { getDesiredLocales } from '../setup/userInput.js';\nimport { installPackage } from '../utils/installPackage.js';\nimport { getPackageManager } from '../utils/packageManager.js';\nimport { areCredentialsSet } from '../utils/credentials.js';\nimport { provisionDevelopmentCredentials } from '../setup/developmentCredentials.js';\nimport { upload } from './commands/upload.js';\nimport { attachSharedFlags, attachTranslateFlags } from './flags.js';\nimport { handleStage } from './commands/stage.js';\nimport { handleSetupProject } from './commands/setupProject.js';\nimport { handleDownload } from './commands/download.js';\nimport {\n handleTranslate,\n postProcessTranslations,\n} from './commands/translate.js';\nimport {\n getNeedsPostprocessing,\n clearDownloaded,\n} from '../state/recentDownloads.js';\nimport { clearWarnings } from '../state/translateWarnings.js';\nimport { displayTranslateSummary } from '../console/displayTranslateSummary.js';\nimport updateConfig from '../fs/config/updateConfig.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { createLoadTranslationsFile } from '../fs/createLoadTranslationsFile.js';\nimport { saveLocalEdits } from '../api/saveLocalEdits.js';\nimport {\n hasValidCredentials,\n hasValidServiceLocales,\n} from './commands/utils/validation.js';\nimport processSharedStaticAssets, {\n mirrorAssetsToLocales,\n} from '../utils/sharedStaticAssets.js';\nimport { setupLocadex } from '../locadex/setupFlow.js';\nimport { detectFramework } from '../setup/detectFramework.js';\nimport {\n getFrameworkDisplayName,\n getReactFrameworkLibrary,\n} from '../setup/frameworkUtils.js';\nimport { INLINE_LIBRARIES, Libraries } from '../types/libraries.js';\nimport { handleEnqueue } from './commands/enqueue.js';\nimport { splitMintlifyLanguageRefs } from '../utils/splitMintlifyLanguageRefs.js';\nimport { runMergeDriver } from '../git/mergeDrivers.js';\nimport { setupGitMergeDrivers } from '../git/setupMergeDrivers.js';\nimport { warnReactPackageCompatibility } from '../utils/reactPackageCompatibility.js';\nimport {\n createDiagnosticMessage,\n formatDiagnosticErrorDetails,\n} from 'generaltranslation/diagnostics';\nimport { hasLogin, login, logout, whoAmI } from '../auth/oauth.js';\nimport { UserAuthError } from '../auth/errors.js';\nimport { resolveConfig } from '../config/resolveConfig.js';\nimport { setupViteSPA } from '../setup/setupViteSPA.js';\nimport { manifestDirectlyDeclaresGTVue } from '@generaltranslation/vue-extractor/integration';\nimport { api } from '../utils/api.js';\nimport { handleApiCommand, type ApiCommandOptions } from './commands/api.js';\n\nconst ID_COMPATIBILITY_WARNING_COMMANDS = new Set([\n 'download',\n 'enqueue',\n 'generate',\n 'setup',\n 'stage',\n 'translate',\n 'validate',\n]);\nconst workspaceRootSetupError = createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The setup wizard cannot run from a monorepo workspace root',\n why: 'GT must be configured in the specific app you want to localize',\n fix: \"Change to that app's directory and rerun `npx gt@latest`\",\n});\nfunction createProjectCommandError(\n whatHappened: string,\n error: unknown\n): string {\n return createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened,\n details: formatDiagnosticErrorDetails(error),\n });\n}\n\nconst emptyApiKeyNameError = createDiagnosticMessage({\n whatHappened: 'The key name cannot be empty',\n fix: 'Pass a non-empty value with --name',\n});\n\nfunction parseApiKeyName(value: string): string {\n const name = value.trim();\n if (!name) throw new InvalidArgumentError(emptyApiKeyNameError);\n return name;\n}\n\nconst electronSetupError = createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened:\n 'The automatic setup wizard is not ready for Electron applications',\n docsUrl: 'https://generaltranslation.com/docs/react',\n});\n\n/** .env.local never reaches production; the runtime key there is set on the host. */\nfunction productionRuntimeKeyGuidance(dashboardUrl: string): string {\n return `${chalk.dim('For runtime translation in production, create an API key in the dashboard')} ${chalk.cyan(dashboardUrl)} ${chalk.dim('and set GT_API_KEY and GT_PROJECT_ID in your hosting environment.')}`;\n}\n\nasync function loginInteractively(\n baseUrl: string | undefined,\n useBrowser = true\n): Promise<void> {\n await login({\n baseUrl,\n noBrowser: !useBrowser,\n onDeviceCode: ({ userCode, verificationUri, verificationUriComplete }) => {\n logger.message(\n `Visit:\\n\\n${chalk.cyan(verificationUriComplete ?? verificationUri)}\\n\\nThen ${verificationUriComplete ? 'confirm' : 'enter'} the code ${chalk.bold(userCode)}.\\nWaiting for authentication...`\n );\n },\n onAuthorizationUrl: (url) => {\n logger.message(\n `Opening your browser to sign in. If it does not open, visit:\\n${chalk.cyan(url)}`\n );\n },\n });\n}\n\nfunction createUserAuthError(whatHappened: string, error: unknown): string {\n if (error instanceof UserAuthError) return error.message;\n return createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened,\n details: formatDiagnosticErrorDetails(error),\n fix: 'Run `gt login` and try again',\n });\n}\n\nasync function exitIfUnsupportedSetupTarget(): Promise<void> {\n const packageJson = await searchForPackageJson();\n if (packageJson && isPackageInstalled('electron', packageJson, false, true)) {\n logErrorAndExit(electronSetupError);\n }\n if (\n fs.existsSync(path.join(process.cwd(), 'pnpm-workspace.yaml')) ||\n packageJson?.workspaces\n ) {\n logErrorAndExit(workspaceRootSetupError);\n }\n}\n\nexport type UploadOptions = {\n config?: string;\n apiKey?: string;\n projectId?: string;\n defaultLocale?: string;\n};\n\nexport type GitSetupOptions = {\n config?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\n};\n\ntype LocalTranslationGuidanceOptions = {\n generatedLoader: boolean;\n runtimeSetup: InlineRuntimeSetup;\n translationsDir: string;\n};\n\ntype InlineRuntimeSetup = {\n hasOtherInlineRuntime: boolean;\n hasVueRuntime: boolean;\n ranReactSetup: boolean;\n};\n\nexport class BaseCLI {\n protected library: SupportedLibraries;\n protected additionalModules: SupportedLibraries[];\n protected program: Command;\n // Constructor is shared amongst all CLI class types\n public constructor(\n program: Command,\n library: SupportedLibraries,\n additionalModules?: SupportedLibraries[]\n ) {\n this.program = program;\n this.library = library;\n this.additionalModules = additionalModules || [];\n\n this.program.option(\n '--skip-version-check',\n 'Skip the monorepo GT package version consistency check'\n );\n this.program.option(\n '--suppress-id-compatibility-warning',\n 'Suppress the React package ID compatibility warning'\n );\n this.program.option(\n '-q, --quiet',\n 'Suppress informational output; only warnings and errors are shown'\n );\n // Select console routing for this command before anything else logs:\n // root hooks run before subclass hooks (version checks) and the action's\n // settings resolution. `gt api-key create` prints the new secret on\n // stdout, so its diagnostics go to stderr; every other command gets the\n // historical default back (main() routes startup output to stderr).\n this.program.hook('preAction', (_thisCommand, actionCommand) => {\n logger.setConsoleOutput(\n actionCommand.parent?.name() === 'api-key' ? 'stderr' : 'stdout'\n );\n });\n // Apply --quiet before any other hook or command action runs so the\n // singleton logger is muted for the rest of the invocation. The flag is a\n // global root option, so commander resolves it in any position and for\n // nested commands (e.g. `gt git setup --quiet`).\n this.program.hook('preAction', () => {\n logger.setQuiet(Boolean(this.program.opts().quiet));\n });\n this.program.hook('preAction', async (thisCommand, actionCommand) => {\n // Nested commands (e.g. `gt git setup`) can share leaf names with\n // translation commands; only direct children of the root qualify\n if (actionCommand.parent !== thisCommand) return;\n if (!ID_COMPATIBILITY_WARNING_COMMANDS.has(actionCommand.name())) return;\n await warnReactPackageCompatibility(\n Boolean(this.program.opts().suppressIdCompatibilityWarning)\n );\n });\n\n this.setupInitCommand();\n this.setupConfigureCommand();\n this.setupUploadCommand();\n this.setupUserAuthCommands();\n this.setupSendDiffsCommand();\n this.setupApiCommand();\n this.setupProjectCommands();\n this.setupApiKeyCommands();\n this.setupGitCommand();\n }\n // Init is never called in a child class\n public init() {\n this.setupSetupProjectCommand();\n this.setupStageCommand();\n this.setupTranslateCommand();\n this.setupDownloadCommand();\n this.setupEnqueueCommand();\n }\n // Execute is called by the main program\n public execute() {\n // If no command is specified, run 'init'\n if (process.argv.length <= 2) {\n process.argv.push('init');\n }\n }\n\n protected setupSetupProjectCommand(): void {\n attachTranslateFlags(\n this.program\n .command('setup')\n .description(\n 'Upload source files and setup the project for translation'\n )\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Uploading source files and setting up project...');\n await this.handleSetupProject(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupStageCommand(): void {\n attachTranslateFlags(\n this.program\n .command('stage')\n .description(\n 'Submits the project to the General Translation API for translation. Translations created using this command will require human approval.'\n )\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader(\n 'Staging project for translation with approval required...'\n );\n await this.handleStage(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n /**\n * Enqueues translations for a given set of files\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected setupEnqueueCommand(): void {\n attachTranslateFlags(\n this.program\n .command('enqueue')\n .description('Enqueues translations for a given set of files')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Enqueuing translations...');\n await this.handleEnqueue(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n /**\n * Downloads translations that were originally staged\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected setupDownloadCommand(): void {\n attachTranslateFlags(\n this.program\n .command('download')\n .description('Download translations that were originally staged')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Downloading translations...');\n await this.handleDownload(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupTranslateCommand(): void {\n attachTranslateFlags(\n this.program\n .command('translate')\n .description('Translate your project using General Translation')\n ).action(async (initOptions: TranslateFlags) => {\n displayHeader('Starting translation...');\n await this.handleTranslate(initOptions);\n logger.endCommand('Done!');\n });\n }\n\n protected setupSendDiffsCommand(): void {\n attachSharedFlags(\n this.program\n .command('save-local')\n .description(\n 'Save local edits for all configured files by sending diffs (no translation enqueued)'\n )\n )\n .option('--publish', 'Publish translations to the CDN', false)\n .action(async (initOptions: SharedFlags) => {\n displayHeader('Saving local edits...');\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n if (!hasValidCredentials(settings) || !hasValidServiceLocales(settings))\n return exitSync(1);\n await saveLocalEdits(settings);\n logger.endCommand('Saved local edits');\n });\n }\n\n protected setupApiCommand(): void {\n attachSharedFlags(\n this.program\n .command('api [endpoint]')\n .description('Make an authenticated request to the GT API')\n .option('-X, --method <method>', 'HTTP method', 'GET')\n .option('--input <file>', 'Request body file, or - for standard input')\n .option(\n '-H, --header <header>',\n 'Request header in \"Key: Value\" format',\n (header, headers: string[] | undefined) => [\n ...(headers ?? []),\n header,\n ]\n )\n .option('-i, --include', 'Include response status and headers')\n .option('--spec', 'Print the bundled OpenAPI specification')\n ).action((endpoint, options: ApiCommandOptions) =>\n handleApiCommand(endpoint, options)\n );\n }\n\n protected setupProjectCommands(): void {\n const projectCommand = this.program\n .command('project')\n .description('Manage General Translation projects');\n\n attachSharedFlags(\n projectCommand\n .command('create')\n .description(\n 'Create a General Translation project using an organization API key'\n )\n .requiredOption('--org-id <orgId>', 'Organization ID')\n .requiredOption('--name <name>', 'Project name')\n .requiredOption('--default-locale <locale>', 'Project default locale')\n .option('--cdn-enabled', 'Enable CDN delivery', false)\n ).action(async (options) => {\n try {\n const settings = await generateSettings(options);\n // Project creation happens before a project ID exists.\n if (!hasValidServiceLocales(settings)) return exitSync(1);\n const { project } = await api.createProject(options.orgId, {\n name: options.name,\n defaultLocale: options.defaultLocale,\n cdnEnabled: options.cdnEnabled,\n });\n logger.info(`Created ${project.name} (${project.id})`);\n } catch (error) {\n return logErrorAndExit(\n createProjectCommandError('Failed to create the project', error)\n );\n }\n });\n\n attachSharedFlags(\n projectCommand\n .command('status')\n .description('Check the status of a project setup job')\n .argument('<job-id>', 'Setup job ID')\n ).action(async (jobId, options) => {\n try {\n const settings = await generateSettings(options);\n if (!hasValidCredentials(settings)) return exitSync(1);\n const [status] = await api.checkJobStatus([jobId]);\n logger.info(status?.status ?? 'unknown');\n } catch (error) {\n return logErrorAndExit(\n createProjectCommandError(\n 'Failed to check the project setup status',\n error\n )\n );\n }\n });\n }\n\n protected setupApiKeyCommands(): void {\n const apiKeyCommand = this.program\n .command('api-key')\n .description('Manage API keys for the configured project');\n\n attachSharedFlags(\n apiKeyCommand\n .command('create')\n .description(\n 'Create a project API key with the requested permissions and print it once'\n )\n .requiredOption('--name <name>', 'Key name', parseApiKeyName)\n // Validated before any request; the server grants the requested set\n // all-or-nothing, so omitting permissions would delegate everything.\n .addOption(\n new Option('--permission <permissions...>', 'Permissions to grant')\n .choices(Object.values(ProjectApiKeyPermission))\n .makeOptionMandatory()\n )\n ).action(\n async (\n options: SharedFlags & {\n name: string;\n permission: ProjectApiKeyPermission[];\n }\n ) => {\n try {\n const settings = await generateSettings(options);\n if (!hasValidCredentials(settings)) return exitSync(1);\n const { apiKey } = await api.createProjectApiKey(settings.projectId, {\n name: options.name,\n permissions: options.permission,\n });\n // Raw stdout: the secret is shown once and never reaches the log file.\n process.stdout.write(`${apiKey.key}\\n`);\n } catch (error) {\n return logErrorAndExit(\n error instanceof UserAuthError\n ? error.message\n : createProjectCommandError('Failed to create the API key', error)\n );\n }\n }\n );\n }\n\n protected setupGitCommand(): void {\n const gitCommand = this.program\n .command('git')\n .description('Configure Git integrations for General Translation');\n\n gitCommand\n .command('setup')\n .description('Set up GT merge drivers for generated translation files')\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option('--dry-run', 'Print changes without writing files', false)\n .option(\n '--omit-config-ids',\n 'Persist omitConfigIds and remove generated config IDs'\n )\n .option(\n '--driver-command <command>',\n 'Command Git should use to invoke gt, e.g. \"pnpm exec gt\"'\n )\n .action(async (options: GitSetupOptions) => {\n displayHeader('Setting up GT Git merge drivers...');\n const settings = await generateSettings(options, undefined, {\n requireConfig: true,\n });\n const omitConfigIds = await this.resolveGitSetupOmitConfigIds(\n options,\n settings\n );\n const result = await setupGitMergeDrivers(settings, {\n dryRun: options.dryRun,\n omitConfigIds,\n driverCommand: options.driverCommand,\n });\n\n for (const line of result.addedAttributes) {\n logger.step(\n `${options.dryRun ? 'Would add' : 'Added'} ${chalk.cyan(\n line\n )} to ${chalk.cyan(result.gitattributesPath)}`\n );\n }\n if (result.addedAttributes.length === 0) {\n logger.info(`${chalk.cyan('.gitattributes')} is already configured.`);\n }\n\n for (const args of result.gitConfigCommands) {\n logger.step(\n `${options.dryRun ? 'Would run' : 'Configured'} ${chalk.cyan(\n `git config --local ${args.join(' ')}`\n )}`\n );\n }\n\n if (result.updatedConfig) {\n logger.step(\n `${options.dryRun ? 'Would persist' : 'Persisted'} ${chalk.cyan(\n 'omitConfigIds: true'\n )} in ${chalk.cyan(settings.config)}`\n );\n } else if (options.dryRun && !settings.omitConfigIds) {\n logger.info(\n `Run without ${chalk.cyan('--dry-run')} to be prompted to persist ${chalk.cyan(\n 'omitConfigIds: true'\n )}, or pass ${chalk.cyan('--omit-config-ids')}.`\n );\n }\n\n for (const warning of result.warnings) {\n logger.warn(chalk.yellow(warning));\n }\n\n logger.endCommand(\n options.dryRun\n ? 'Dry run complete.'\n : 'GT Git merge drivers configured.'\n );\n });\n\n gitCommand\n .command('merge-driver', { hidden: true })\n .argument('<driver>', 'Merge driver name')\n .argument('<base>', 'Common ancestor file')\n .argument('<ours>', 'Current branch file')\n .argument('<theirs>', 'Incoming branch file')\n .argument('[path]', 'Merged path')\n .action((driver: string, base: string, ours: string, theirs: string) => {\n if (driver !== 'gt-lock' && driver !== 'gtjson') {\n logger.error(`Unknown GT merge driver: ${driver}`);\n exitSync(1);\n }\n const result = runMergeDriver(driver, base, ours, theirs);\n if (!result.ok) {\n logger.error(result.reason);\n exitSync(1);\n }\n });\n }\n\n protected async resolveGitSetupOmitConfigIds(\n options: GitSetupOptions,\n settings: Settings\n ): Promise<boolean> {\n if (options.omitConfigIds) return true;\n // Already opted in: persist again so stale config IDs still get removed\n if (settings.omitConfigIds) return true;\n if (options.dryRun || !process.stdin.isTTY || !process.stdout.isTTY) {\n return false;\n }\n return promptConfirm({\n message:\n 'Also set omitConfigIds: true to reduce gt.config.json merge conflicts?',\n defaultValue: true,\n });\n }\n\n protected async handleSetupProject(\n initOptions: TranslateFlags\n ): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n await handleSetupProject(initOptions, settings, this.library);\n }\n\n protected async handleStage(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n if (!settings.stageTranslations) {\n // Update settings.stageTranslations to true\n settings.stageTranslations = true;\n await updateConfig(settings.config, {\n stageTranslations: true,\n });\n }\n await handleStage(initOptions, settings, this.library, true);\n }\n\n /**\n * Enqueues translations for a given set of files\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected async handleEnqueue(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await handleEnqueue(initOptions, settings, this.library);\n }\n\n /**\n * Downloads translations that were originally staged\n * @param initOptions - The options for the command\n * @returns The results of the command\n */\n protected async handleDownload(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n await handleDownload(initOptions, settings, this.library);\n }\n\n protected async handleTranslate(initOptions: TranslateFlags): Promise<void> {\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n // Preprocess shared static assets if configured (move + rewrite sources)\n await processSharedStaticAssets(settings);\n\n if (!settings.stageTranslations) {\n // Lottie translations finish asynchronously server-side (layout\n // refinement runs after the translation job completes), so the immediate\n // translate flow would try to download them before they're ready. Only\n // the stage + download flow supports them.\n if (settings.files?.resolvedPaths.lottie?.length) {\n return logErrorAndExit(lottieTranslateError);\n }\n const results = await handleStage(\n initOptions,\n settings,\n this.library,\n false\n );\n if (results) {\n await handleTranslate(\n initOptions,\n settings,\n results.fileVersionData,\n results.jobData,\n results.branchData,\n results.publishMap,\n results.inlineLibrary\n );\n }\n } else {\n await handleDownload(initOptions, settings, this.library);\n }\n // Only postprocess files downloaded in this run\n const include = getNeedsPostprocessing();\n if (include.size > 0) {\n await postProcessTranslations(settings, include);\n }\n // Split Mintlify language entries into $ref files to keep docs.json small\n await splitMintlifyLanguageRefs(settings);\n // Mirror assets after translations are downloaded and locale dirs are populated\n await mirrorAssetsToLocales(settings);\n clearDownloaded();\n displayTranslateSummary();\n clearWarnings();\n }\n\n protected setupUploadCommand(): void {\n attachTranslateFlags(\n this.program\n .command('upload')\n .description(\n 'Upload source files and translations to the General Translation platform'\n )\n ).action(async (initOptions: UploadOptions) => {\n displayHeader('Starting upload...');\n const settings = await generateSettings(initOptions, undefined, {\n requireConfig: true,\n });\n\n const options = { ...initOptions, ...settings };\n\n await this.handleUploadCommand(options);\n logger.endCommand('Done!');\n });\n }\n\n protected setupUserAuthCommands(): void {\n this.program\n .command('login')\n .description('Sign in to your General Translation account')\n .option(\n '--no-browser',\n 'Do not open a browser; show a sign-in URL to use on any device instead'\n )\n .action(async (options: { browser: boolean }) => {\n displayHeader('Signing in to General Translation...');\n try {\n // Tokens are bound to one API resource, so log in to the configured one.\n const baseUrl = resolveConfig(process.cwd())?.config.baseUrl;\n await loginInteractively(\n typeof baseUrl === 'string' ? baseUrl : undefined,\n options.browser\n );\n logger.endCommand('You are now signed in.');\n } catch (error) {\n logErrorAndExit(createUserAuthError('Sign in failed', error));\n }\n });\n\n this.program\n .command('logout')\n .description('Sign out of your General Translation account')\n .action(async () => {\n try {\n await logout();\n logger.endCommand('Signed out successfully.');\n } catch (error) {\n logErrorAndExit(createUserAuthError('Sign out failed', error));\n }\n });\n\n this.program\n .command('whoami')\n .description('Show the signed-in General Translation account')\n .action(async () => {\n try {\n const user = await whoAmI();\n logger.message(user.email ?? user.name ?? user.sub);\n } catch (error) {\n logErrorAndExit(\n createUserAuthError('Could not load your account', error)\n );\n }\n });\n }\n\n protected setupInitCommand(): void {\n this.program\n .command('init')\n .description(\n 'Run the setup wizard to configure your project for General Translation'\n )\n .option(\n '--src <paths...>',\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 '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .action(async (options: SetupOptions) => {\n await exitIfUnsupportedSetupTarget();\n const settings = await generateSettings(options);\n displayHeader('Running setup wizard...');\n\n const framework = await detectFramework();\n\n const useAgent = await (async () => {\n let useAgentMessage;\n if (framework.name === 'mintlify') {\n useAgentMessage = `Mintlify project detected. Would you like to connect to GitHub so that the Locadex AI Agent can translate your project automatically?`;\n }\n if (framework.name === 'next-app') {\n useAgentMessage = `Next.js App Router detected. Would you like to connect to GitHub so that the Locadex AI Agent can set up your project automatically?`;\n }\n if (useAgentMessage) {\n return await promptConfirm({\n message: useAgentMessage,\n defaultValue: false,\n });\n }\n return false;\n })();\n\n if (useAgent) {\n await setupLocadex(settings);\n logger.endCommand(\n 'Once installed, Locadex will open a PR to your repository. See the docs for more information: https://generaltranslation.com/docs/locadex'\n );\n } else {\n // Get framework display info for the defaults message\n const frameworkDisplayName =\n framework.type === 'react'\n ? getFrameworkDisplayName(framework)\n : null;\n const library =\n framework.type === 'react'\n ? getReactFrameworkLibrary(framework)\n : null;\n\n // Build defaults description based on detected framework\n const defaultTranslationsDir =\n framework.name === 'vite'\n ? DEFAULT_VITE_TRANSLATIONS_DIR\n : DEFAULT_TRANSLATIONS_DIR;\n\n const defaultsDescription =\n framework.name === 'vite'\n ? `${library} & initializeGTSPA, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}`\n : framework.type === 'react'\n ? `${library} & GTProvider, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}`\n : `Files saved locally in ${defaultTranslationsDir}`;\n\n // Ask if user wants to use defaults\n const useDefaults = await promptConfirm({\n message: `Would you like to use the recommended General Translation defaults? ${chalk.dim(`(${defaultsDescription})`)}`,\n defaultValue: true,\n });\n\n let ranReactSetup = false;\n\n // so that people can run init in non-js projects\n if (framework.type === 'react') {\n const wrap = useDefaults\n ? true\n : await promptConfirm({\n message:\n framework.name === 'vite'\n ? `Would you like to install ${library} and configure initializeGTSPA? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`\n : `Would you like to install ${library} and add the GTProvider? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`,\n defaultValue: true,\n });\n\n if (wrap) {\n logger.info(\n `${chalk.yellow('[EXPERIMENTAL]')} Configuring project...`\n );\n await handleSetupReactCommand(options, framework, useDefaults);\n logger.endCommand(\n `Done! Since this wizard is experimental, please review the changes and make modifications as needed.\n\\nNext step: start internationalizing! See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`\n );\n ranReactSetup = true;\n }\n }\n\n if (ranReactSetup) {\n logger.startCommand('Setting up project config...');\n }\n // Configure gt.config.json\n await this.handleInitCommand(\n ranReactSetup,\n useDefaults,\n framework.name === 'vite',\n options,\n framework.name\n );\n\n logger.endCommand(\n 'Done! Check out our docs for more information on how to use General Translation: https://generaltranslation.com/docs'\n );\n }\n });\n }\n\n protected setupConfigureCommand(): void {\n this.program\n .command('configure')\n .description(\n 'Configure your project for General Translation. This will create a gt.config.json file in your codebase.'\n )\n .action(() => this.handleConfigureCommand());\n }\n\n protected async handleConfigureCommand(\n options?: SetupOptions\n ): Promise<void> {\n await exitIfUnsupportedSetupTarget();\n displayHeader('Configuring project...');\n\n logger.info(\n 'Welcome! This tool will help you configure your gt.config.json file. See the docs: https://generaltranslation.com/docs/cli/reference/config for more information.'\n );\n\n // Configure gt.config.json\n const framework = await detectFramework();\n await this.handleInitCommand(\n false,\n false,\n framework.name === 'vite',\n options\n );\n\n logger.endCommand(\n 'Done! Make sure you have an API key and project ID to use General Translation. Get them on the dashboard: https://generaltranslation.com/dashboard'\n );\n }\n\n protected async handleUploadCommand(\n settings: Settings & UploadOptions\n ): Promise<void> {\n if (!settings.files) {\n return;\n }\n\n // Process all file types at once with a single call\n await upload(settings);\n }\n\n /** Describes installed runtimes without changing historical package lookup. */\n protected getInlineRuntimeSetup(\n packageJson: Record<string, unknown>,\n ranReactSetup: boolean = false\n ): InlineRuntimeSetup {\n return {\n hasOtherInlineRuntime: INLINE_LIBRARIES.some(\n (lib) =>\n lib !== Libraries.GT_VUE && isPackageInstalled(lib, packageJson)\n ),\n hasVueRuntime: manifestDirectlyDeclaresGTVue(packageJson),\n ranReactSetup,\n };\n }\n\n /** Returns whether any inline runtime is installed for setup. */\n protected isInlineRuntimeInstalled(\n packageJson: Record<string, unknown>\n ): boolean {\n const { hasOtherInlineRuntime, hasVueRuntime } =\n this.getInlineRuntimeSetup(packageJson);\n return hasOtherInlineRuntime || hasVueRuntime;\n }\n\n /** Returns whether every installed runtime can consume CDN translations. */\n protected supportsCDNStorage(runtimeSetup: InlineRuntimeSetup): boolean {\n return !runtimeSetup.hasVueRuntime;\n }\n\n /** Returns whether setup should generate a local runtime loader. */\n protected shouldGenerateLocalTranslationLoader(\n isVite: boolean,\n runtimeSetup: InlineRuntimeSetup\n ): boolean {\n if (isVite) return false;\n if (!runtimeSetup.hasVueRuntime) return true;\n return runtimeSetup.hasOtherInlineRuntime || runtimeSetup.ranReactSetup;\n }\n\n /** Returns framework guidance after selecting local translation storage. */\n protected getLocalTranslationGuidance({\n generatedLoader,\n runtimeSetup,\n translationsDir,\n }: LocalTranslationGuidanceOptions): string | undefined {\n const guidance: string[] = [];\n if (generatedLoader) {\n guidance.push(`Created ${chalk.cyan('loadTranslations.js')} file for local translations.\nMake sure to add this function to your app configuration.\nSee https://generaltranslation.com/en/docs/next/guides/local-tx`);\n }\n if (runtimeSetup.hasVueRuntime) {\n guidance.push(`GT will write local translation files to ${translationsDir}.\nConfigure createGT({ loadTranslations }) to load files from that directory.\nSee https://www.npmjs.com/package/gt-vue`);\n }\n return guidance.length > 0 ? guidance.join('\\n') : undefined;\n }\n\n // Wizard for configuring gt.config.json\n protected async handleInitCommand(\n ranReactSetup: boolean,\n useDefaults: boolean = false,\n isVite: boolean = false,\n options?: SetupOptions,\n framework?: SupportedFrameworks\n ): Promise<void> {\n const configFilepath =\n options?.config ||\n (!isVite && fs.existsSync('src/gt.config.json')\n ? 'src/gt.config.json'\n : 'gt.config.json');\n const existingConfig = loadConfig(configFilepath);\n const { defaultLocale, locales } = await getDesiredLocales(existingConfig);\n\n const packageJson = await searchForPackageJson();\n\n // Ask if using another i18n library\n const runtimeSetup: InlineRuntimeSetup = packageJson\n ? this.getInlineRuntimeSetup(packageJson, ranReactSetup)\n : {\n hasOtherInlineRuntime: false,\n hasVueRuntime: false,\n ranReactSetup,\n };\n const isUsingGT =\n runtimeSetup.ranReactSetup ||\n runtimeSetup.hasOtherInlineRuntime ||\n runtimeSetup.hasVueRuntime;\n\n // Ask where the translations are stored\n const usingCDN = await (async () => {\n if (!isUsingGT) return false;\n if (useDefaults) return false; // Default to local\n if (!this.supportsCDNStorage(runtimeSetup)) return false;\n const selectedValue = await promptSelect({\n message: `Would you like to save translation files locally or use the General Translation CDN to store them?`,\n options: [\n { value: 'local', label: 'Save locally' },\n { value: 'cdn', label: 'Use CDN' },\n ],\n defaultValue: 'local',\n });\n return selectedValue === 'cdn';\n })();\n\n const defaultTranslationsDir = isVite\n ? DEFAULT_VITE_TRANSLATIONS_DIR\n : DEFAULT_TRANSLATIONS_DIR;\n\n // Ask where the translations are stored\n const translationsDir =\n isUsingGT && !usingCDN\n ? useDefaults\n ? defaultTranslationsDir\n : await promptText({\n message:\n 'What is the path to the directory where you would like to store your translation files?',\n defaultValue: defaultTranslationsDir,\n })\n : null;\n\n // Determine final translations directory with fallback\n const finalTranslationsDir =\n translationsDir?.trim() || defaultTranslationsDir;\n\n if (isUsingGT && !usingCDN) {\n const generatedLoader = this.shouldGenerateLocalTranslationLoader(\n isVite,\n runtimeSetup\n );\n if (generatedLoader) {\n await createLoadTranslationsFile(\n process.cwd(),\n finalTranslationsDir,\n locales\n );\n }\n const guidance = this.getLocalTranslationGuidance({\n generatedLoader,\n runtimeSetup,\n translationsDir: finalTranslationsDir,\n });\n if (guidance) logger.message(guidance);\n }\n\n const message = !isUsingGT\n ? 'What is the format of your language resource files? Select as many as applicable.\\nAdditionally, you can translate any other files you have in your project.'\n : `Do you have any additional files in this project to translate? For example, Markdown files for docs. ${chalk.dim(\n '(To continue without selecting press Enter)'\n )}`;\n const fileExtensions =\n useDefaults && isUsingGT\n ? [] // Skip for GT projects when using defaults\n : await promptMultiSelect({\n message,\n options: [\n { value: 'json', label: FILE_EXT_TO_EXT_LABEL.json },\n { value: 'md', label: FILE_EXT_TO_EXT_LABEL.md },\n { value: 'mdx', label: FILE_EXT_TO_EXT_LABEL.mdx },\n { value: 'ts', label: FILE_EXT_TO_EXT_LABEL.ts },\n { value: 'js', label: FILE_EXT_TO_EXT_LABEL.js },\n { value: 'yaml', label: FILE_EXT_TO_EXT_LABEL.yaml },\n // TWILIO_CONTENT_JSON not supported in CLI init as its too niche\n ],\n required: !isUsingGT,\n });\n\n const files: FilesOptions = {};\n for (const fileExtension of fileExtensions) {\n const label = FILE_EXT_TO_EXT_LABEL[fileExtension];\n const paths = await promptGlobPatterns({\n label,\n message: `${chalk.cyan(FILE_EXT_TO_EXT_LABEL[fileExtension])}: Enter a space-separated list of glob patterns matching the location of the ${FILE_EXT_TO_EXT_LABEL[fileExtension]} files you would like to translate.\\nMake sure to include [locale] in the patterns.\\nSee https://generaltranslation.com/docs/cli/reference/config#include for more information.`,\n defaultValue: `./**/[locale]/*.${fileExtension}`,\n });\n\n files[fileExtension] = {\n include: parseGlobPatterns(paths),\n };\n }\n\n // Add GT translations if using GT and storing locally\n if (isUsingGT && !usingCDN) {\n files.gt = {\n output: path.join(finalTranslationsDir, `[locale].json`),\n };\n }\n\n // Create gt.config.json\n await createOrUpdateConfig(configFilepath, {\n defaultLocale,\n locales,\n src: options?.src,\n files: Object.keys(files).length > 0 ? files : undefined,\n framework: isVite ? 'vite' : undefined,\n publish: isUsingGT && usingCDN,\n clearPublish: runtimeSetup.hasVueRuntime && !usingCDN,\n });\n\n logger.success(\n `Edit ${chalk.cyan(\n configFilepath\n )} to customize your translation setup. Docs: https://generaltranslation.com/docs/cli/reference/config`\n );\n\n if (ranReactSetup && isVite) {\n await setupViteSPA({\n appDirectory: process.cwd(),\n configFilepath,\n defaultLocale,\n locales,\n translationsDir: usingCDN ? undefined : finalTranslationsDir,\n });\n }\n\n // Install gt if not installed\n const isCLIInstalled = packageJson\n ? isPackageInstalled('gt', packageJson, true, true)\n : true; // if no package.json, we can't install it\n\n if (!isCLIInstalled && !(isUsingGT && isVite)) {\n const packageManager = await getPackageManager();\n const spinner = logger.createSpinner();\n spinner.start(\n `Installing gt as a dev dependency with ${packageManager.name}...`\n );\n await installPackage('gt', packageManager, true);\n spinner.stop(chalk.green('Installed gt.'));\n }\n\n const localVite = isVite && isUsingGT && !usingCDN;\n const enableLiveTranslations =\n localVite &&\n (await promptConfirm({\n message:\n 'Would you like to set up live development translations? This requires signing in or an API key.',\n defaultValue: false,\n }));\n if (!localVite || enableLiveTranslations) {\n const settings = await generateSettings({ config: configFilepath });\n const envFramework = framework ?? (isVite ? 'vite' : settings.framework);\n // The CLI translates as the signed-in user; an API key in the\n // environment takes precedence and needs no login. A development key in\n // .env.local is runtime-only and never stands in for either.\n if (\n !settings.apiKey &&\n !(await hasLogin({ baseUrl: settings.baseUrl }))\n ) {\n try {\n await loginInteractively(settings.baseUrl);\n logger.message('You are now signed in.');\n } catch (error) {\n logErrorAndExit(createUserAuthError('Sign in failed', error));\n }\n }\n if (!areCredentialsSet(settings, envFramework)) {\n const provision =\n useDefaults || enableLiveTranslations\n ? true\n : await promptConfirm({\n message:\n 'Would you like to set up a project ID and hot-reload key in .env.local?',\n defaultValue: true,\n });\n if (provision) {\n try {\n await provisionDevelopmentCredentials(settings, envFramework);\n } catch (error) {\n logErrorAndExit(\n error instanceof UserAuthError\n ? error.message\n : createProjectCommandError(\n 'Failed to set up the development credentials',\n error\n )\n );\n }\n logger.message(productionRuntimeKeyGuidance(settings.dashboardUrl));\n }\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -12,6 +12,7 @@ import localizeRelativeAssets from "../../utils/localizeRelativeAssets.js";
12
12
  import processAnchorIds from "../../utils/processAnchorIds.js";
13
13
  import localizeStaticImports from "../../utils/localizeStaticImports.js";
14
14
  import { postprocessMintlify } from "../../formats/files/postprocess/mintlify.js";
15
+ import { keepUntranslatedPagePaths } from "../../formats/files/postprocess/keepUntranslatedPagePaths.js";
15
16
  import { persistPostProcessHashes } from "../../utils/persistPostprocessHashes.js";
16
17
  //#region src/cli/commands/translate.ts
17
18
  async function handleTranslate(options, settings, fileVersionData, jobData, branchData, publishMap, inlineLibrary) {
@@ -45,6 +46,7 @@ async function postProcessTranslations(settings, includeFiles) {
45
46
  const nonDefaultLocales = settings.locales.filter((locale) => locale !== settings.defaultLocale);
46
47
  if (nonDefaultLocales.length > 0) await localizeStaticUrls(settings, nonDefaultLocales, postProcessIncludes);
47
48
  }
49
+ keepUntranslatedPagePaths(settings, postProcessIncludes);
48
50
  if (settings.options?.experimentalLocalizeRelativeAssets) {
49
51
  const nonDefaultLocales = settings.locales.filter((locale) => locale !== settings.defaultLocale);
50
52
  if (nonDefaultLocales.length > 0) await localizeRelativeAssets(settings, nonDefaultLocales, postProcessIncludes);
@@ -1 +1 @@
1
- {"version":3,"file":"translate.js","names":[],"sources":["../../../src/cli/commands/translate.ts"],"sourcesContent":["import { EnqueueFilesResult } from 'generaltranslation/types';\nimport { TranslateFlags } from '../../types/index.js';\nimport { Settings } from '../../types/index.js';\nimport {\n FileTranslationData,\n runDownloadWorkflow,\n} from '../../workflows/download.js';\nimport { createFileMapping } from '../../formats/files/fileMapping.js';\nimport copyFile from '../../fs/copyFile.js';\nimport flattenJsonFiles from '../../utils/flattenJsonFiles.js';\nimport localizeStaticUrls from '../../utils/localizeStaticUrls.js';\nimport localizeRelativeAssets from '../../utils/localizeRelativeAssets.js';\nimport processAnchorIds from '../../utils/processAnchorIds.js';\nimport localizeStaticImports from '../../utils/localizeStaticImports.js';\nimport { postprocessMintlify } from '../../formats/files/postprocess/mintlify.js';\nimport { BranchData } from '../../types/branch.js';\nimport { getDownloadedMeta } from '../../state/recentDownloads.js';\nimport { persistPostProcessHashes } from '../../utils/persistPostprocessHashes.js';\nimport { runPublishWorkflow } from '../../workflows/publish.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../../formats/files/supportedFiles.js';\nimport { hasNonIdentityFileFormatTransformForType } from '../../formats/files/transformFormat.js';\nimport { getRelative } from '../../fs/findFilepath.js';\nimport type { InlineLibrary } from '../../types/libraries.js';\n\n// Downloads translations that were completed\nexport async function handleTranslate(\n options: TranslateFlags,\n settings: Settings,\n fileVersionData: FileTranslationData | undefined,\n jobData: EnqueueFilesResult | undefined,\n branchData: BranchData | undefined,\n publishMap?: Map<string, boolean>,\n inlineLibrary?: InlineLibrary\n) {\n if (fileVersionData) {\n const {\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n } = settings.files;\n\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n // Check for remaining translations\n await runDownloadWorkflow({\n fileVersionData: fileVersionData,\n jobData: jobData,\n branchData: branchData,\n locales: settings.locales,\n timeoutDuration: options.timeout,\n resolveOutputPath: (sourcePath, locale) =>\n fileMapping[locale]?.[sourcePath] ?? null,\n options: settings,\n inlineLibrary,\n forceRetranslation: options.force,\n forceDownload: options.forceDownload || options.force, // if force is true should also force download\n });\n\n // Publish/unpublish files after translations are downloaded\n if (publishMap && branchData?.currentBranch.id) {\n const files = Object.entries(fileVersionData).map(([fileId, data]) => ({\n fileId,\n versionId: data.versionId,\n fileName: data.fileName,\n }));\n await runPublishWorkflow(\n files,\n publishMap,\n branchData.currentBranch.id,\n settings\n );\n }\n }\n}\n\nexport async function postProcessTranslations(\n settings: Settings,\n includeFiles?: Set<string>\n) {\n const postProcessIncludes = filterPostProcessIncludesForFormatTransforms(\n settings,\n includeFiles\n );\n if (includeFiles && postProcessIncludes?.size === 0) return;\n\n await postprocessMintlify(settings, postProcessIncludes);\n\n // Localize static urls (/docs -> /[locale]/docs) and preserve anchor IDs for non-default locales\n // Default locale is processed earlier in the flow in base.ts\n if (settings.options?.experimentalLocalizeStaticUrls) {\n const nonDefaultLocales = settings.locales.filter(\n (locale) => locale !== settings.defaultLocale\n );\n if (nonDefaultLocales.length > 0) {\n await localizeStaticUrls(\n settings,\n nonDefaultLocales,\n postProcessIncludes\n );\n }\n }\n\n // Rewrite relative asset URLs in translated md/mdx files\n if (settings.options?.experimentalLocalizeRelativeAssets) {\n const nonDefaultLocales = settings.locales.filter(\n (locale) => locale !== settings.defaultLocale\n );\n if (nonDefaultLocales.length > 0) {\n await localizeRelativeAssets(\n settings,\n nonDefaultLocales,\n postProcessIncludes\n );\n }\n }\n\n // Localize static imports (import Snippet from /snippets/file.mdx -> import Snippet from /snippets/[locale]/file.mdx)\n if (settings.options?.experimentalLocalizeStaticImports) {\n await localizeStaticImports(settings, postProcessIncludes);\n }\n\n const shouldProcessAnchorIds =\n settings.options?.experimentalLocalizeStaticUrls ||\n settings.options?.experimentalAddHeaderAnchorIds;\n\n // Add explicit anchor IDs to translated MDX/MD files to preserve navigation.\n // Uses escaped inline \\{#id\\} by default, or Mintlify's native {#id} if\n // experimentalAddHeaderAnchorIds is 'mintlify'.\n //\n // Runs last of the md/mdx passes: the others re-stringify the document, which\n // re-indents headings nested in JSX and would otherwise move them out from\n // under the anchors placed here.\n if (shouldProcessAnchorIds) {\n await processAnchorIds(settings, postProcessIncludes);\n }\n\n // Flatten json files into a single file\n if (settings.options?.experimentalFlattenJsonFiles) {\n await flattenJsonFiles(settings, postProcessIncludes);\n }\n\n // Copy files to the target locale\n if (settings.options?.copyFiles) {\n await copyFile(settings);\n }\n\n // Record postprocessed content hashes for newly downloaded files\n persistPostProcessHashes(settings, postProcessIncludes, getDownloadedMeta());\n}\n\n/**\n * Exclude only outputs whose source file was translated into a different format.\n * @param settings - The settings for the project\n * @param includeFiles - The files to include in the post-processing\n * @returns The files to exclude in the post-processing\n */\nfunction filterPostProcessIncludesForFormatTransforms(\n settings: Settings,\n includeFiles?: Set<string>\n): Set<string> | undefined {\n if (!includeFiles) return includeFiles;\n\n const transformedSourcePaths = new Set<string>();\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (!hasNonIdentityFileFormatTransformForType(settings, fileType)) continue;\n\n for (const sourcePath of settings.files.resolvedPaths[fileType] || []) {\n transformedSourcePaths.add(getRelative(sourcePath));\n }\n }\n if (transformedSourcePaths.size === 0) return includeFiles;\n\n const { resolvedPaths, placeholderPaths, transformPaths, transformFormats } =\n settings.files;\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n\n const transformedOutputPaths = new Set<string>();\n for (const localeMapping of Object.values(fileMapping)) {\n for (const [sourcePath, outputPath] of Object.entries(localeMapping)) {\n if (transformedSourcePaths.has(sourcePath)) {\n transformedOutputPaths.add(outputPath);\n }\n }\n }\n\n return new Set(\n [...includeFiles].filter(\n (filePath) => !transformedOutputPaths.has(filePath)\n )\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyBA,eAAsB,gBACpB,SACA,UACA,iBACA,SACA,YACA,YACA,eACA;AACA,KAAI,iBAAiB;EACnB,MAAM,EACJ,eACA,kBACA,gBACA,qBACE,SAAS;EAEb,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;AAED,QAAM,oBAAoB;GACP;GACR;GACG;GACZ,SAAS,SAAS;GAClB,iBAAiB,QAAQ;GACzB,oBAAoB,YAAY,WAC9B,YAAY,UAAU,eAAe;GACvC,SAAS;GACT;GACA,oBAAoB,QAAQ;GAC5B,eAAe,QAAQ,iBAAiB,QAAQ;GACjD,CAAC;AAGF,MAAI,cAAc,YAAY,cAAc,GAM1C,OAAM,mBALQ,OAAO,QAAQ,gBAAgB,CAAC,KAAK,CAAC,QAAQ,WAAW;GACrE;GACA,WAAW,KAAK;GAChB,UAAU,KAAK;GAChB,EAEM,EACL,YACA,WAAW,cAAc,IACzB,SACD;;;AAKP,eAAsB,wBACpB,UACA,cACA;CACA,MAAM,sBAAsB,6CAC1B,UACA,aACD;AACD,KAAI,gBAAgB,qBAAqB,SAAS,EAAG;AAErD,OAAM,oBAAoB,UAAU,oBAAoB;AAIxD,KAAI,SAAS,SAAS,gCAAgC;EACpD,MAAM,oBAAoB,SAAS,QAAQ,QACxC,WAAW,WAAW,SAAS,cACjC;AACD,MAAI,kBAAkB,SAAS,EAC7B,OAAM,mBACJ,UACA,mBACA,oBACD;;AAKL,KAAI,SAAS,SAAS,oCAAoC;EACxD,MAAM,oBAAoB,SAAS,QAAQ,QACxC,WAAW,WAAW,SAAS,cACjC;AACD,MAAI,kBAAkB,SAAS,EAC7B,OAAM,uBACJ,UACA,mBACA,oBACD;;AAKL,KAAI,SAAS,SAAS,kCACpB,OAAM,sBAAsB,UAAU,oBAAoB;AAc5D,KAVE,SAAS,SAAS,kCAClB,SAAS,SAAS,+BAUlB,OAAM,iBAAiB,UAAU,oBAAoB;AAIvD,KAAI,SAAS,SAAS,6BACpB,OAAM,iBAAiB,UAAU,oBAAoB;AAIvD,KAAI,SAAS,SAAS,UACpB,OAAM,SAAS,SAAS;AAI1B,0BAAyB,UAAU,qBAAqB,mBAAmB,CAAC;;;;;;;;AAS9E,SAAS,6CACP,UACA,cACyB;AACzB,KAAI,CAAC,aAAc,QAAO;CAE1B,MAAM,yCAAyB,IAAI,KAAa;AAChD,MAAK,MAAM,YAAY,2BAA2B;AAChD,MAAI,CAAC,yCAAyC,UAAU,SAAS,CAAE;AAEnE,OAAK,MAAM,cAAc,SAAS,MAAM,cAAc,aAAa,EAAE,CACnE,wBAAuB,IAAI,YAAY,WAAW,CAAC;;AAGvD,KAAI,uBAAuB,SAAS,EAAG,QAAO;CAE9C,MAAM,EAAE,eAAe,kBAAkB,gBAAgB,qBACvD,SAAS;CACX,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;CAED,MAAM,yCAAyB,IAAI,KAAa;AAChD,MAAK,MAAM,iBAAiB,OAAO,OAAO,YAAY,CACpD,MAAK,MAAM,CAAC,YAAY,eAAe,OAAO,QAAQ,cAAc,CAClE,KAAI,uBAAuB,IAAI,WAAW,CACxC,wBAAuB,IAAI,WAAW;AAK5C,QAAO,IAAI,IACT,CAAC,GAAG,aAAa,CAAC,QACf,aAAa,CAAC,uBAAuB,IAAI,SAAS,CACpD,CACF"}
1
+ {"version":3,"file":"translate.js","names":[],"sources":["../../../src/cli/commands/translate.ts"],"sourcesContent":["import { EnqueueFilesResult } from 'generaltranslation/types';\nimport { TranslateFlags } from '../../types/index.js';\nimport { Settings } from '../../types/index.js';\nimport {\n FileTranslationData,\n runDownloadWorkflow,\n} from '../../workflows/download.js';\nimport { createFileMapping } from '../../formats/files/fileMapping.js';\nimport copyFile from '../../fs/copyFile.js';\nimport flattenJsonFiles from '../../utils/flattenJsonFiles.js';\nimport localizeStaticUrls from '../../utils/localizeStaticUrls.js';\nimport localizeRelativeAssets from '../../utils/localizeRelativeAssets.js';\nimport processAnchorIds from '../../utils/processAnchorIds.js';\nimport localizeStaticImports from '../../utils/localizeStaticImports.js';\nimport { postprocessMintlify } from '../../formats/files/postprocess/mintlify.js';\nimport { keepUntranslatedPagePaths } from '../../formats/files/postprocess/keepUntranslatedPagePaths.js';\nimport { BranchData } from '../../types/branch.js';\nimport { getDownloadedMeta } from '../../state/recentDownloads.js';\nimport { persistPostProcessHashes } from '../../utils/persistPostprocessHashes.js';\nimport { runPublishWorkflow } from '../../workflows/publish.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../../formats/files/supportedFiles.js';\nimport { hasNonIdentityFileFormatTransformForType } from '../../formats/files/transformFormat.js';\nimport { getRelative } from '../../fs/findFilepath.js';\nimport type { InlineLibrary } from '../../types/libraries.js';\n\n// Downloads translations that were completed\nexport async function handleTranslate(\n options: TranslateFlags,\n settings: Settings,\n fileVersionData: FileTranslationData | undefined,\n jobData: EnqueueFilesResult | undefined,\n branchData: BranchData | undefined,\n publishMap?: Map<string, boolean>,\n inlineLibrary?: InlineLibrary\n) {\n if (fileVersionData) {\n const {\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n } = settings.files;\n\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n // Check for remaining translations\n await runDownloadWorkflow({\n fileVersionData: fileVersionData,\n jobData: jobData,\n branchData: branchData,\n locales: settings.locales,\n timeoutDuration: options.timeout,\n resolveOutputPath: (sourcePath, locale) =>\n fileMapping[locale]?.[sourcePath] ?? null,\n options: settings,\n inlineLibrary,\n forceRetranslation: options.force,\n forceDownload: options.forceDownload || options.force, // if force is true should also force download\n });\n\n // Publish/unpublish files after translations are downloaded\n if (publishMap && branchData?.currentBranch.id) {\n const files = Object.entries(fileVersionData).map(([fileId, data]) => ({\n fileId,\n versionId: data.versionId,\n fileName: data.fileName,\n }));\n await runPublishWorkflow(\n files,\n publishMap,\n branchData.currentBranch.id,\n settings\n );\n }\n }\n}\n\nexport async function postProcessTranslations(\n settings: Settings,\n includeFiles?: Set<string>\n) {\n const postProcessIncludes = filterPostProcessIncludesForFormatTransforms(\n settings,\n includeFiles\n );\n if (includeFiles && postProcessIncludes?.size === 0) return;\n\n await postprocessMintlify(settings, postProcessIncludes);\n\n // Localize static urls (/docs -> /[locale]/docs) and preserve anchor IDs for non-default locales\n // Default locale is processed earlier in the flow in base.ts\n if (settings.options?.experimentalLocalizeStaticUrls) {\n const nonDefaultLocales = settings.locales.filter(\n (locale) => locale !== settings.defaultLocale\n );\n if (nonDefaultLocales.length > 0) {\n await localizeStaticUrls(\n settings,\n nonDefaultLocales,\n postProcessIncludes\n );\n }\n }\n\n // Point transformed page paths (e.g. docs navigation) at the default-locale\n // page when the localized page was not translated\n keepUntranslatedPagePaths(settings, postProcessIncludes);\n\n // Rewrite relative asset URLs in translated md/mdx files\n if (settings.options?.experimentalLocalizeRelativeAssets) {\n const nonDefaultLocales = settings.locales.filter(\n (locale) => locale !== settings.defaultLocale\n );\n if (nonDefaultLocales.length > 0) {\n await localizeRelativeAssets(\n settings,\n nonDefaultLocales,\n postProcessIncludes\n );\n }\n }\n\n // Localize static imports (import Snippet from /snippets/file.mdx -> import Snippet from /snippets/[locale]/file.mdx)\n if (settings.options?.experimentalLocalizeStaticImports) {\n await localizeStaticImports(settings, postProcessIncludes);\n }\n\n const shouldProcessAnchorIds =\n settings.options?.experimentalLocalizeStaticUrls ||\n settings.options?.experimentalAddHeaderAnchorIds;\n\n // Add explicit anchor IDs to translated MDX/MD files to preserve navigation.\n // Uses escaped inline \\{#id\\} by default, or Mintlify's native {#id} if\n // experimentalAddHeaderAnchorIds is 'mintlify'.\n //\n // Runs last of the md/mdx passes: the others re-stringify the document, which\n // re-indents headings nested in JSX and would otherwise move them out from\n // under the anchors placed here.\n if (shouldProcessAnchorIds) {\n await processAnchorIds(settings, postProcessIncludes);\n }\n\n // Flatten json files into a single file\n if (settings.options?.experimentalFlattenJsonFiles) {\n await flattenJsonFiles(settings, postProcessIncludes);\n }\n\n // Copy files to the target locale\n if (settings.options?.copyFiles) {\n await copyFile(settings);\n }\n\n // Record postprocessed content hashes for newly downloaded files\n persistPostProcessHashes(settings, postProcessIncludes, getDownloadedMeta());\n}\n\n/**\n * Exclude only outputs whose source file was translated into a different format.\n * @param settings - The settings for the project\n * @param includeFiles - The files to include in the post-processing\n * @returns The files to exclude in the post-processing\n */\nfunction filterPostProcessIncludesForFormatTransforms(\n settings: Settings,\n includeFiles?: Set<string>\n): Set<string> | undefined {\n if (!includeFiles) return includeFiles;\n\n const transformedSourcePaths = new Set<string>();\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (!hasNonIdentityFileFormatTransformForType(settings, fileType)) continue;\n\n for (const sourcePath of settings.files.resolvedPaths[fileType] || []) {\n transformedSourcePaths.add(getRelative(sourcePath));\n }\n }\n if (transformedSourcePaths.size === 0) return includeFiles;\n\n const { resolvedPaths, placeholderPaths, transformPaths, transformFormats } =\n settings.files;\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n\n const transformedOutputPaths = new Set<string>();\n for (const localeMapping of Object.values(fileMapping)) {\n for (const [sourcePath, outputPath] of Object.entries(localeMapping)) {\n if (transformedSourcePaths.has(sourcePath)) {\n transformedOutputPaths.add(outputPath);\n }\n }\n }\n\n return new Set(\n [...includeFiles].filter(\n (filePath) => !transformedOutputPaths.has(filePath)\n )\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,eAAsB,gBACpB,SACA,UACA,iBACA,SACA,YACA,YACA,eACA;AACA,KAAI,iBAAiB;EACnB,MAAM,EACJ,eACA,kBACA,gBACA,qBACE,SAAS;EAEb,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;AAED,QAAM,oBAAoB;GACP;GACR;GACG;GACZ,SAAS,SAAS;GAClB,iBAAiB,QAAQ;GACzB,oBAAoB,YAAY,WAC9B,YAAY,UAAU,eAAe;GACvC,SAAS;GACT;GACA,oBAAoB,QAAQ;GAC5B,eAAe,QAAQ,iBAAiB,QAAQ;GACjD,CAAC;AAGF,MAAI,cAAc,YAAY,cAAc,GAM1C,OAAM,mBALQ,OAAO,QAAQ,gBAAgB,CAAC,KAAK,CAAC,QAAQ,WAAW;GACrE;GACA,WAAW,KAAK;GAChB,UAAU,KAAK;GAChB,EAEM,EACL,YACA,WAAW,cAAc,IACzB,SACD;;;AAKP,eAAsB,wBACpB,UACA,cACA;CACA,MAAM,sBAAsB,6CAC1B,UACA,aACD;AACD,KAAI,gBAAgB,qBAAqB,SAAS,EAAG;AAErD,OAAM,oBAAoB,UAAU,oBAAoB;AAIxD,KAAI,SAAS,SAAS,gCAAgC;EACpD,MAAM,oBAAoB,SAAS,QAAQ,QACxC,WAAW,WAAW,SAAS,cACjC;AACD,MAAI,kBAAkB,SAAS,EAC7B,OAAM,mBACJ,UACA,mBACA,oBACD;;AAML,2BAA0B,UAAU,oBAAoB;AAGxD,KAAI,SAAS,SAAS,oCAAoC;EACxD,MAAM,oBAAoB,SAAS,QAAQ,QACxC,WAAW,WAAW,SAAS,cACjC;AACD,MAAI,kBAAkB,SAAS,EAC7B,OAAM,uBACJ,UACA,mBACA,oBACD;;AAKL,KAAI,SAAS,SAAS,kCACpB,OAAM,sBAAsB,UAAU,oBAAoB;AAc5D,KAVE,SAAS,SAAS,kCAClB,SAAS,SAAS,+BAUlB,OAAM,iBAAiB,UAAU,oBAAoB;AAIvD,KAAI,SAAS,SAAS,6BACpB,OAAM,iBAAiB,UAAU,oBAAoB;AAIvD,KAAI,SAAS,SAAS,UACpB,OAAM,SAAS,SAAS;AAI1B,0BAAyB,UAAU,qBAAqB,mBAAmB,CAAC;;;;;;;;AAS9E,SAAS,6CACP,UACA,cACyB;AACzB,KAAI,CAAC,aAAc,QAAO;CAE1B,MAAM,yCAAyB,IAAI,KAAa;AAChD,MAAK,MAAM,YAAY,2BAA2B;AAChD,MAAI,CAAC,yCAAyC,UAAU,SAAS,CAAE;AAEnE,OAAK,MAAM,cAAc,SAAS,MAAM,cAAc,aAAa,EAAE,CACnE,wBAAuB,IAAI,YAAY,WAAW,CAAC;;AAGvD,KAAI,uBAAuB,SAAS,EAAG,QAAO;CAE9C,MAAM,EAAE,eAAe,kBAAkB,gBAAgB,qBACvD,SAAS;CACX,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;CAED,MAAM,yCAAyB,IAAI,KAAa;AAChD,MAAK,MAAM,iBAAiB,OAAO,OAAO,YAAY,CACpD,MAAK,MAAM,CAAC,YAAY,eAAe,OAAO,QAAQ,cAAc,CAClE,KAAI,uBAAuB,IAAI,WAAW,CACxC,wBAAuB,IAAI,WAAW;AAK5C,QAAO,IAAI,IACT,CAAC,GAAG,aAAa,CAAC,QACf,aAAa,CAAC,uBAAuB,IAAI,SAAS,CACpD,CACF"}
@@ -0,0 +1,10 @@
1
+ import type { Settings } from '../../../types/index.js';
2
+ /**
3
+ * With `skipUntranslatedPages`, undo `transform` rewrites that point a
4
+ * localized JSON or YAML file (such as a docs navigation file) at a page that
5
+ * was not translated. A value is restored to its source form when the source
6
+ * value names an existing .md/.mdx page and the transformed value does not.
7
+ * Values that are not page paths are left alone. Runs after all downloads, so
8
+ * every translated page from this run is on disk.
9
+ */
10
+ export declare function keepUntranslatedPagePaths(settings: Settings, includeFiles?: Set<string>): void;
@@ -0,0 +1,101 @@
1
+ import { getRelative } from "../../../fs/findFilepath.js";
2
+ import { api } from "../../../utils/api.js";
3
+ import { getConfiguredLocaleProperties } from "../../utils.js";
4
+ import { getJSONPathMatches } from "../../json/jsonPath.js";
5
+ import { findMatchingItemArray, findMatchingItemObject, generateSourceObjectPointers, validateJsonSchema } from "../../json/utils.js";
6
+ import { getJSONPointerValue, setJSONPointerValue } from "../../json/jsonPointer.js";
7
+ import { applyStructuralTransforms, unapplyStructuralTransforms } from "../../json/transformJson.js";
8
+ import { createFileMapping } from "../fileMapping.js";
9
+ import { validateYamlSchema } from "../../yaml/utils.js";
10
+ import { transformValue } from "../../json/mergeJson.js";
11
+ import { PAGE_EXTENSIONS } from "../../../utils/localizeStaticUrls.js";
12
+ import path from "node:path";
13
+ import fs from "node:fs";
14
+ import YAML from "yaml";
15
+ //#region src/formats/files/postprocess/keepUntranslatedPagePaths.ts
16
+ /**
17
+ * With `skipUntranslatedPages`, undo `transform` rewrites that point a
18
+ * localized JSON or YAML file (such as a docs navigation file) at a page that
19
+ * was not translated. A value is restored to its source form when the source
20
+ * value names an existing .md/.mdx page and the transformed value does not.
21
+ * Values that are not page paths are left alone. Runs after all downloads, so
22
+ * every translated page from this run is on disk.
23
+ */
24
+ function keepUntranslatedPagePaths(settings, includeFiles) {
25
+ const options = settings.options;
26
+ const localizeOptions = options?.experimentalLocalizeStaticUrls;
27
+ if (!options || typeof localizeOptions !== "object" || localizeOptions.skipUntranslatedPages !== true || !settings.files) return;
28
+ const { defaultLocale } = settings;
29
+ const targetLocales = settings.locales.filter((l) => l !== defaultLocale);
30
+ const isIncluded = (filePath) => !includeFiles || includeFiles.has(filePath) || includeFiles.has(getRelative(filePath));
31
+ for (const filePath of settings.files.resolvedPaths.json ?? []) {
32
+ const schema = validateJsonSchema(options, filePath);
33
+ if (!schema?.composite || !isIncluded(filePath)) continue;
34
+ const json = JSON.parse(fs.readFileSync(filePath, "utf8"));
35
+ const dir = path.dirname(filePath);
36
+ if (schema.structuralTransform) applyStructuralTransforms(json, schema.structuralTransform, schema.composite);
37
+ const localeKey = (locale) => options.experimentalCanonicalLocaleKeys ? api.resolveCanonicalLocale(locale) : locale;
38
+ let changed = false;
39
+ const sourceObjects = generateSourceObjectPointers(schema.composite, json);
40
+ for (const [pointer, { sourceObjectValue, sourceObjectOptions }] of Object.entries(sourceObjects)) {
41
+ if (!sourceObjectOptions.transform) continue;
42
+ const entriesFor = (locale) => {
43
+ if (Array.isArray(sourceObjectValue)) return Object.values(findMatchingItemArray(localeKey(locale), sourceObjectOptions, pointer, sourceObjectValue)).sort((a, b) => a.index - b.index).map(({ sourceItem }) => sourceItem);
44
+ const { sourceItem } = findMatchingItemObject(localeKey(locale), pointer, sourceObjectOptions, sourceObjectValue);
45
+ return sourceItem === void 0 ? [] : [sourceItem];
46
+ };
47
+ const sources = entriesFor(defaultLocale);
48
+ for (const locale of targetLocales) entriesFor(locale).forEach((target, i) => {
49
+ if (sources[i] === void 0) return;
50
+ changed = restorePagePaths(sources[i], target, sourceObjectOptions.transform, locale, defaultLocale, dir, dir) || changed;
51
+ });
52
+ }
53
+ if (changed) {
54
+ if (schema.structuralTransform) unapplyStructuralTransforms(json, schema.structuralTransform, schema.composite);
55
+ fs.writeFileSync(filePath, JSON.stringify(json, null, 2));
56
+ }
57
+ }
58
+ const yamlSources = settings.files.resolvedPaths.yaml ?? [];
59
+ if (!yamlSources.length) return;
60
+ const fileMapping = createFileMapping(settings.files.resolvedPaths, settings.files.placeholderPaths, settings.files.transformPaths, settings.files.transformFormats, settings.locales, defaultLocale);
61
+ for (const filePath of yamlSources) {
62
+ const schema = validateYamlSchema(options, filePath);
63
+ if (!schema?.transform) continue;
64
+ const source = YAML.parse(fs.readFileSync(filePath, "utf8"));
65
+ for (const locale of targetLocales) {
66
+ const outputPath = fileMapping[locale]?.[getRelative(filePath)];
67
+ if (!outputPath || !fs.existsSync(outputPath) || !isIncluded(outputPath)) continue;
68
+ const target = YAML.parse(fs.readFileSync(outputPath, "utf8"));
69
+ if (restorePagePaths(source, target, schema.transform, locale, defaultLocale, path.dirname(filePath), path.dirname(outputPath))) fs.writeFileSync(outputPath, YAML.stringify(target));
70
+ }
71
+ }
72
+ }
73
+ /**
74
+ * For each value a transform produced in `target` from the value at the same
75
+ * position in `source`, restore the source page path when the transformed
76
+ * page does not exist. Returns whether anything changed.
77
+ */
78
+ function restorePagePaths(source, target, transform, locale, defaultLocale, sourceDir, targetDir) {
79
+ const targetLocaleProperties = getConfiguredLocaleProperties(locale);
80
+ const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);
81
+ let changed = false;
82
+ for (const [transformPath, transformOption] of Object.entries(transform)) for (const match of getJSONPathMatches(source, transformPath) ?? []) {
83
+ if (typeof match.value !== "string") continue;
84
+ const transformed = transformValue(match.value, transformOption, targetLocaleProperties, defaultLocaleProperties);
85
+ if (transformed === match.value || getJSONPointerValue(target, match.pointer) !== transformed || pageExists(transformed, targetDir) || !pageExists(match.value, sourceDir)) continue;
86
+ const restored = sourceDir === targetDir ? match.value : path.relative(targetDir, path.join(sourceDir, match.value)).split(path.sep).join("/");
87
+ setJSONPointerValue(target, match.pointer, restored);
88
+ changed = true;
89
+ }
90
+ return changed;
91
+ }
92
+ /** Whether a path value names an .md/.mdx page, with or without extension. */
93
+ function pageExists(value, dir) {
94
+ const filePath = path.join(dir, value);
95
+ if (PAGE_EXTENSIONS.includes(path.extname(filePath))) return fs.existsSync(filePath);
96
+ return PAGE_EXTENSIONS.some((ext) => fs.existsSync(`${filePath}${ext}`));
97
+ }
98
+ //#endregion
99
+ export { keepUntranslatedPagePaths };
100
+
101
+ //# sourceMappingURL=keepUntranslatedPagePaths.js.map
@@ -0,0 +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,4 +1,5 @@
1
- import { AdditionalOptions, SourceObjectOptions } from '../../types/index.js';
1
+ import type { LocaleProperties } from '@generaltranslation/format/types';
2
+ import { AdditionalOptions, SourceObjectOptions, TransformOption } from '../../types/index.js';
2
3
  import type { JSONValue } from '../../types/data/json.js';
3
4
  export declare function mergeJson(originalContent: string, inputPath: string, options: AdditionalOptions, targets: {
4
5
  translatedContent: string;
@@ -12,3 +13,9 @@ export declare function mergeJson(originalContent: string, inputPath: string, op
12
13
  * @param defaultLocale - The default locale
13
14
  */
14
15
  export declare function applyTransformations(sourceItem: JSONValue, transform: SourceObjectOptions['transform'], targetLocale: string, defaultLocale: string): void;
16
+ /**
17
+ * Apply one transform to a string value. Locale placeholders in `replace`
18
+ * take the target locale's properties; those in `match` take the default
19
+ * locale's. Without `match`, the value is replaced outright.
20
+ */
21
+ export declare function transformValue(value: string, transformOptions: TransformOption, targetLocaleProperties: LocaleProperties, defaultLocaleProperties: LocaleProperties): string;
@@ -228,18 +228,23 @@ function applyTransformations(sourceItem, transform, targetLocale, defaultLocale
228
228
  if (!results || results.length === 0) continue;
229
229
  results.forEach((result) => {
230
230
  if (typeof result.value !== "string") return;
231
- let replaceString = transformOptions.replace;
232
- replaceString = replaceLocalePlaceholders(replaceString, targetLocaleProperties);
233
- if (transformOptions.match && typeof transformOptions.match === "string") {
234
- let matchString = transformOptions.match;
235
- matchString = replaceLocalePlaceholders(matchString, defaultLocaleProperties);
236
- result.value = result.value.replace(new RegExp(matchString, "g"), replaceString);
237
- } else result.value = replaceString;
231
+ result.value = transformValue(result.value, transformOptions, targetLocaleProperties, defaultLocaleProperties);
238
232
  setJSONPointerValue(sourceItem, result.pointer, result.value);
239
233
  });
240
234
  }
241
235
  }
236
+ /**
237
+ * Apply one transform to a string value. Locale placeholders in `replace`
238
+ * take the target locale's properties; those in `match` take the default
239
+ * locale's. Without `match`, the value is replaced outright.
240
+ */
241
+ function transformValue(value, transformOptions, targetLocaleProperties, defaultLocaleProperties) {
242
+ const replaceString = replaceLocalePlaceholders(transformOptions.replace, targetLocaleProperties);
243
+ if (!transformOptions.match || typeof transformOptions.match !== "string") return replaceString;
244
+ const matchString = replaceLocalePlaceholders(transformOptions.match, defaultLocaleProperties);
245
+ return value.replace(new RegExp(matchString, "g"), replaceString);
246
+ }
242
247
  //#endregion
243
- export { applyTransformations, mergeJson };
248
+ export { applyTransformations, mergeJson, transformValue };
244
249
 
245
250
  //# sourceMappingURL=mergeJson.js.map