gt 2.16.1 → 2.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.16.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2040](https://github.com/generaltranslation/gt/pull/2040) [`79e6836`](https://github.com/generaltranslation/gt/commit/79e6836349191220ee8f5848b5e6ff287246f162) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Use the locale exactly as configured when substituting `{locale}` in file and JSON transforms, instead of canonicalizing it. Projects that configure a non-canonical tag such as `fr-ca` or `ja-jp` were getting content written to `docs/fr-CA/` while `[locale]` substitution and localized URLs used `docs/fr-ca/`, so every internal link in the translated output pointed at a directory that did not exist.
8
+
9
+ ## 2.16.2
10
+
11
+ ### Patch Changes
12
+
13
+ - [#2029](https://github.com/generaltranslation/gt/pull/2029) [`c1fd8a0`](https://github.com/generaltranslation/gt/commit/c1fd8a05c5325775e3ec8104e2d48d35da864de6) Thanks [@eoinest](https://github.com/eoinest)! - Configure Vite React apps with `initializeGTSPA` and bundled translation loading without adding the compiler.
14
+
3
15
  ## 2.16.1
4
16
 
5
17
  ### Patch Changes
package/dist/cli/base.js CHANGED
@@ -6,6 +6,7 @@ import { displayHeader, exitSync, logErrorAndExit, promptConfirm, promptGlobPatt
6
6
  import findFilepath from "../fs/findFilepath.js";
7
7
  import { lottieTranslateError } from "../console/index.js";
8
8
  import { INLINE_LIBRARIES } from "../types/libraries.js";
9
+ import { loadConfig } from "../fs/config/loadConfig.js";
9
10
  import { FILE_EXT_TO_EXT_LABEL } from "../formats/files/supportedFiles.js";
10
11
  import { generateSettings } from "../config/generateSettings.js";
11
12
  import { createOrUpdateConfig } from "../fs/config/setupConfig.js";
@@ -35,6 +36,7 @@ import { splitMintlifyLanguageRefs } from "../utils/splitMintlifyLanguageRefs.js
35
36
  import { runMergeDriver } from "../git/mergeDrivers.js";
36
37
  import { setupGitMergeDrivers } from "../git/setupMergeDrivers.js";
37
38
  import { warnReactPackageCompatibility } from "../utils/reactPackageCompatibility.js";
39
+ import { setupViteSPA } from "../setup/setupViteSPA.js";
38
40
  import chalk from "chalk";
39
41
  import path from "node:path";
40
42
  import fs from "node:fs";
@@ -56,8 +58,15 @@ const workspaceRootSetupError = createDiagnosticMessage({
56
58
  why: "GT must be configured in the specific app you want to localize",
57
59
  fix: "Change to that app's directory and rerun `npx gt@latest`"
58
60
  });
59
- async function exitIfWorkspaceRoot() {
61
+ const electronSetupError = createDiagnosticMessage({
62
+ source: "gt",
63
+ severity: "Error",
64
+ whatHappened: "The automatic setup wizard is not ready for Electron applications",
65
+ docsUrl: "https://generaltranslation.com/docs/react"
66
+ });
67
+ async function exitIfUnsupportedSetupTarget() {
60
68
  const packageJson = await searchForPackageJson();
69
+ if (packageJson && isPackageInstalled("electron", packageJson, false, true)) logErrorAndExit(electronSetupError);
61
70
  if (fs.existsSync(path.join(process.cwd(), "pnpm-workspace.yaml")) || packageJson?.workspaces) logErrorAndExit(workspaceRootSetupError);
62
71
  }
63
72
  var BaseCLI = class {
@@ -274,7 +283,7 @@ var BaseCLI = class {
274
283
  }
275
284
  setupInitCommand() {
276
285
  this.program.command("init").description("Run the setup wizard to configure your project for General Translation").option("--src <paths...>", "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}'").option("-c, --config <path>", "Filepath to config file, by default gt.config.json", findFilepath(["gt.config.json"])).action(async (options) => {
277
- await exitIfWorkspaceRoot();
286
+ await exitIfUnsupportedSetupTarget();
278
287
  const settings = await generateSettings(options);
279
288
  displayHeader("Running setup wizard...");
280
289
  const framework = await detectFramework();
@@ -294,7 +303,7 @@ var BaseCLI = class {
294
303
  const frameworkDisplayName = framework.type === "react" ? getFrameworkDisplayName(framework) : null;
295
304
  const library = framework.type === "react" ? getReactFrameworkLibrary(framework) : null;
296
305
  const defaultTranslationsDir = framework.name === "vite" ? DEFAULT_VITE_TRANSLATIONS_DIR : DEFAULT_TRANSLATIONS_DIR;
297
- const defaultsDescription = framework.type === "react" ? `${library} & GTProvider, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}` : `Files saved locally in ${defaultTranslationsDir}`;
306
+ const defaultsDescription = framework.name === "vite" ? `${library} & initializeGTSPA, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}` : framework.type === "react" ? `${library} & GTProvider, ${frameworkDisplayName}, Files saved locally in ${defaultTranslationsDir}` : `Files saved locally in ${defaultTranslationsDir}`;
298
307
  const useDefaults = await promptConfirm({
299
308
  message: `Would you like to use the recommended General Translation defaults? ${chalk.dim(`(${defaultsDescription})`)}`,
300
309
  defaultValue: true
@@ -302,7 +311,7 @@ var BaseCLI = class {
302
311
  let ranReactSetup = false;
303
312
  if (framework.type === "react") {
304
313
  if (useDefaults ? true : await promptConfirm({
305
- message: `Would you like to install ${library} and add the GTProvider? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`,
314
+ message: framework.name === "vite" ? `Would you like to install ${library} and configure initializeGTSPA? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart` : `Would you like to install ${library} and add the GTProvider? See the docs for more information: https://generaltranslation.com/docs/react/tutorials/quickstart`,
306
315
  defaultValue: true
307
316
  })) {
308
317
  logger.info(`${chalk.yellow("[EXPERIMENTAL]")} Configuring project...`);
@@ -320,7 +329,7 @@ var BaseCLI = class {
320
329
  }
321
330
  setupConfigureCommand() {
322
331
  this.program.command("configure").description("Configure your project for General Translation. This will create a gt.config.json file in your codebase.").action(async () => {
323
- await exitIfWorkspaceRoot();
332
+ await exitIfUnsupportedSetupTarget();
324
333
  displayHeader("Configuring project...");
325
334
  logger.info("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.");
326
335
  const framework = await detectFramework();
@@ -333,7 +342,8 @@ var BaseCLI = class {
333
342
  await upload(settings);
334
343
  }
335
344
  async handleInitCommand(ranReactSetup, useDefaults = false, isVite = false) {
336
- const { defaultLocale, locales } = await getDesiredLocales();
345
+ const configFilepath = !isVite && fs.existsSync("src/gt.config.json") ? "src/gt.config.json" : "gt.config.json";
346
+ const { defaultLocale, locales } = await getDesiredLocales(loadConfig(configFilepath));
337
347
  const packageJson = await searchForPackageJson();
338
348
  const gtInstalled = !!packageJson && INLINE_LIBRARIES.some((lib) => isPackageInstalled(lib, packageJson));
339
349
  const isUsingGT = ranReactSetup || gtInstalled;
@@ -357,7 +367,7 @@ var BaseCLI = class {
357
367
  message: "What is the path to the directory where you would like to store your translation files?",
358
368
  defaultValue: defaultTranslationsDir
359
369
  }) : null)?.trim() || defaultTranslationsDir;
360
- if (isUsingGT && !usingCDN) {
370
+ if (isUsingGT && !usingCDN && !isVite) {
361
371
  await createLoadTranslationsFile(process.cwd(), finalTranslationsDir, locales);
362
372
  logger.message(`Created ${chalk.cyan("loadTranslations.js")} file for local translations.
363
373
  Make sure to add this function to your app configuration.
@@ -404,23 +414,29 @@ See https://generaltranslation.com/en/docs/next/guides/local-tx`);
404
414
  })) };
405
415
  }
406
416
  if (isUsingGT && !usingCDN) files.gt = { output: path.join(finalTranslationsDir, `[locale].json`) };
407
- let configFilepath = "gt.config.json";
408
- if (fs.existsSync("src/gt.config.json")) configFilepath = "src/gt.config.json";
409
417
  await createOrUpdateConfig(configFilepath, {
410
418
  defaultLocale,
411
419
  locales,
412
420
  files: Object.keys(files).length > 0 ? files : void 0,
421
+ framework: isVite ? "vite" : void 0,
413
422
  publish: isUsingGT && usingCDN
414
423
  });
415
424
  logger.success(`Edit ${chalk.cyan(configFilepath)} to customize your translation setup. Docs: https://generaltranslation.com/docs/cli/reference/config`);
416
- if (!(packageJson ? isPackageInstalled("gt", packageJson, true, true) : true)) {
425
+ if (ranReactSetup && isVite) await setupViteSPA({
426
+ appDirectory: process.cwd(),
427
+ configFilepath,
428
+ defaultLocale,
429
+ locales,
430
+ translationsDir: usingCDN ? void 0 : finalTranslationsDir
431
+ });
432
+ if (!(packageJson ? isPackageInstalled("gt", packageJson, true, true) : true) && !(isUsingGT && isVite)) {
417
433
  const packageManager = await getPackageManager();
418
434
  const spinner = logger.createSpinner();
419
435
  spinner.start(`Installing gt as a dev dependency with ${packageManager.name}...`);
420
436
  await installPackage("gt", packageManager, true);
421
437
  spinner.stop(chalk.green("Installed gt."));
422
438
  }
423
- if (!areCredentialsSet()) {
439
+ if ((!isVite || !isUsingGT || usingCDN) && !areCredentialsSet()) {
424
440
  if (useDefaults ? true : await promptConfirm({
425
441
  message: "Would you like the wizard to automatically generate API keys and a project ID for you?",
426
442
  defaultValue: true
@@ -443,7 +459,7 @@ See https://generaltranslation.com/en/docs/next/guides/local-tx`);
443
459
  }
444
460
  ],
445
461
  defaultValue: "all"
446
- })), settings.framework);
462
+ })), isVite ? "vite" : settings.framework);
447
463
  }
448
464
  }
449
465
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":[],"sources":["../../src/cli/base.ts"],"sourcesContent":["import { Command } from 'commander';\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 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 { retrieveCredentials, setCredentials } from '../utils/credentials.js';\nimport { areCredentialsSet } from '../utils/credentials.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 { createLoadTranslationsFile } from '../fs/createLoadTranslationsFile.js';\nimport { saveLocalEdits } from '../api/saveLocalEdits.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 } from '../types/libraries.js';\nimport { handleEnqueue } from './commands/enqueue.js';\nimport { splitMintlifyLanguageRefs } from '../utils/splitMintlifyLanguageRefs.js';\nimport { runMergeDriver, type MergeDriverName } from '../git/mergeDrivers.js';\nimport { setupGitMergeDrivers } from '../git/setupMergeDrivers.js';\nimport { warnReactPackageCompatibility } from '../utils/reactPackageCompatibility.js';\nimport { createDiagnosticMessage } from 'generaltranslation/internal';\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});\n\nasync function exitIfWorkspaceRoot(): Promise<void> {\n const packageJson = await searchForPackageJson();\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 LoginOptions = {\n config?: string;\n keyType?: 'development' | 'production' | 'all';\n};\n\nexport type GitSetupOptions = {\n config?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\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 // 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.setupLoginCommand();\n this.setupSendDiffsCommand();\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 await saveLocalEdits(settings);\n logger.endCommand('Saved local edits');\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(\n driver as MergeDriverName,\n base,\n ours,\n theirs\n );\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 );\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 setupLoginCommand(): void {\n this.program\n .command('auth')\n .description('Generate General Translation API keys and project ID')\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option(\n '-t, --key-type <type>',\n 'Type of key to generate, production | development | all'\n )\n .action(async (options: LoginOptions) => {\n displayHeader('Authenticating with General Translation...');\n if (!options.keyType) {\n options.keyType = await promptSelect<\n 'development' | 'production' | 'all'\n >({\n message: 'What type of API key would you like to generate?',\n options: [\n { value: 'development', label: 'Development' },\n { value: 'production', label: 'Production' },\n { value: 'all', label: 'Both' },\n ],\n defaultValue: 'all',\n });\n } else {\n if (\n options.keyType !== 'development' &&\n options.keyType !== 'production' &&\n options.keyType !== 'all'\n ) {\n logErrorAndExit(\n 'Invalid key type, must be development, production, or all'\n );\n }\n }\n await this.handleLoginCommand(options);\n logger.endCommand(\n `Done! ${options.keyType} keys have been generated and saved to your .env.local file.`\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 exitIfWorkspaceRoot();\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.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: `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 );\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(async () => {\n await exitIfWorkspaceRoot();\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(false, false, framework.name === 'vite');\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\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 // Wizard for configuring gt.config.json\n protected async handleInitCommand(\n ranReactSetup: boolean,\n useDefaults: boolean = false,\n isVite: boolean = false\n ): Promise<void> {\n const { defaultLocale, locales } = await getDesiredLocales(); // Locales should still be asked for even if using defaults\n\n const packageJson = await searchForPackageJson();\n\n // Ask if using another i18n library\n const gtInstalled =\n !!packageJson &&\n INLINE_LIBRARIES.some((lib) => isPackageInstalled(lib, packageJson));\n const isUsingGT = ranReactSetup || gtInstalled;\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 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 // Create loadTranslations.js file for local translations\n await createLoadTranslationsFile(\n process.cwd(),\n finalTranslationsDir,\n locales\n );\n logger.message(\n `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 }\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 let configFilepath = 'gt.config.json';\n if (fs.existsSync('src/gt.config.json')) {\n configFilepath = 'src/gt.config.json';\n }\n\n // Create gt.config.json\n await createOrUpdateConfig(configFilepath, {\n defaultLocale,\n locales,\n files: Object.keys(files).length > 0 ? files : undefined,\n publish: isUsingGT && 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 // 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) {\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 // Set credentials\n if (!areCredentialsSet()) {\n const loginQuestion = useDefaults\n ? true\n : await promptConfirm({\n message:\n 'Would you like the wizard to automatically generate API keys and a project ID for you?',\n defaultValue: true,\n });\n if (loginQuestion) {\n const settings = await generateSettings({});\n const keyType = useDefaults\n ? 'all'\n : await promptSelect<'development' | 'production' | 'all'>({\n message: 'What type of API key would you like to generate?',\n options: [\n { value: 'development', label: 'Development' },\n { value: 'production', label: 'Production' },\n { value: 'all', label: 'Both' },\n ],\n defaultValue: 'all',\n });\n const credentials = await retrieveCredentials(settings, keyType);\n await setCredentials(credentials, settings.framework);\n }\n }\n }\n protected async handleLoginCommand(options: LoginOptions): Promise<void> {\n const settings = await generateSettings({ config: options.config });\n const keyType = options.keyType || 'all';\n const credentials = await retrieveCredentials(settings, keyType);\n await setCredentials(credentials, settings.framework);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base.js","names":[],"sources":["../../src/cli/base.ts"],"sourcesContent":["import { Command } from 'commander';\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 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 { retrieveCredentials, setCredentials } from '../utils/credentials.js';\nimport { areCredentialsSet } from '../utils/credentials.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 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 } from '../types/libraries.js';\nimport { handleEnqueue } from './commands/enqueue.js';\nimport { splitMintlifyLanguageRefs } from '../utils/splitMintlifyLanguageRefs.js';\nimport { runMergeDriver, type MergeDriverName } from '../git/mergeDrivers.js';\nimport { setupGitMergeDrivers } from '../git/setupMergeDrivers.js';\nimport { warnReactPackageCompatibility } from '../utils/reactPackageCompatibility.js';\nimport { createDiagnosticMessage } from 'generaltranslation/internal';\nimport { setupViteSPA } from '../setup/setupViteSPA.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});\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\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 LoginOptions = {\n config?: string;\n keyType?: 'development' | 'production' | 'all';\n};\n\nexport type GitSetupOptions = {\n config?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\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 // 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.setupLoginCommand();\n this.setupSendDiffsCommand();\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 await saveLocalEdits(settings);\n logger.endCommand('Saved local edits');\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(\n driver as MergeDriverName,\n base,\n ours,\n theirs\n );\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 );\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 setupLoginCommand(): void {\n this.program\n .command('auth')\n .description('Generate General Translation API keys and project ID')\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option(\n '-t, --key-type <type>',\n 'Type of key to generate, production | development | all'\n )\n .action(async (options: LoginOptions) => {\n displayHeader('Authenticating with General Translation...');\n if (!options.keyType) {\n options.keyType = await promptSelect<\n 'development' | 'production' | 'all'\n >({\n message: 'What type of API key would you like to generate?',\n options: [\n { value: 'development', label: 'Development' },\n { value: 'production', label: 'Production' },\n { value: 'all', label: 'Both' },\n ],\n defaultValue: 'all',\n });\n } else {\n if (\n options.keyType !== 'development' &&\n options.keyType !== 'production' &&\n options.keyType !== 'all'\n ) {\n logErrorAndExit(\n 'Invalid key type, must be development, production, or all'\n );\n }\n }\n await this.handleLoginCommand(options);\n logger.endCommand(\n `Done! ${options.keyType} keys have been generated and saved to your .env.local file.`\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 );\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(async () => {\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(false, false, framework.name === 'vite');\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\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 // Wizard for configuring gt.config.json\n protected async handleInitCommand(\n ranReactSetup: boolean,\n useDefaults: boolean = false,\n isVite: boolean = false\n ): Promise<void> {\n const configFilepath =\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 gtInstalled =\n !!packageJson &&\n INLINE_LIBRARIES.some((lib) => isPackageInstalled(lib, packageJson));\n const isUsingGT = ranReactSetup || gtInstalled;\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 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 && !isVite) {\n // Create loadTranslations.js file for local translations\n await createLoadTranslationsFile(\n process.cwd(),\n finalTranslationsDir,\n locales\n );\n logger.message(\n `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 }\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 files: Object.keys(files).length > 0 ? files : undefined,\n framework: isVite ? 'vite' : undefined,\n publish: isUsingGT && 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 // Set credentials\n if ((!isVite || !isUsingGT || usingCDN) && !areCredentialsSet()) {\n const loginQuestion = useDefaults\n ? true\n : await promptConfirm({\n message:\n 'Would you like the wizard to automatically generate API keys and a project ID for you?',\n defaultValue: true,\n });\n if (loginQuestion) {\n const settings = await generateSettings({});\n const keyType = useDefaults\n ? 'all'\n : await promptSelect<'development' | 'production' | 'all'>({\n message: 'What type of API key would you like to generate?',\n options: [\n { value: 'development', label: 'Development' },\n { value: 'production', label: 'Production' },\n { value: 'all', label: 'Both' },\n ],\n defaultValue: 'all',\n });\n const credentials = await retrieveCredentials(settings, keyType);\n await setCredentials(credentials, isVite ? 'vite' : settings.framework);\n }\n }\n }\n protected async handleLoginCommand(options: LoginOptions): Promise<void> {\n const settings = await generateSettings({ config: options.config });\n const keyType = options.keyType || 'all';\n const credentials = await retrieveCredentials(settings, keyType);\n await setCredentials(credentials, settings.framework);\n }\n}\n"],"mappings":"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"}
@@ -3,9 +3,8 @@ import { getRelative } from "../../fs/findFilepath.js";
3
3
  import { SUPPORTED_FILE_EXTENSIONS } from "./supportedFiles.js";
4
4
  import { replaceFileExtensionForFormat } from "./transformFormat.js";
5
5
  import { resolveLocaleFiles } from "../../fs/config/parseFilesConfig.js";
6
- import { replaceLocalePlaceholders } from "../utils.js";
6
+ import { getConfiguredLocaleProperties, replaceLocalePlaceholders } from "../utils.js";
7
7
  import path from "node:path";
8
- import { getLocaleProperties } from "@generaltranslation/format";
9
8
  //#region src/formats/files/fileMapping.ts
10
9
  /**
11
10
  * Creates a mapping between source files and their translated counterparts for each locale
@@ -39,8 +38,8 @@ function createFileMapping(filePaths, placeholderPaths, transformPaths, transfor
39
38
  return path.join(directory, transformedFileName);
40
39
  });
41
40
  else if (Array.isArray(transformPath)) {
42
- const targetLocaleProperties = getLocaleProperties(locale);
43
- const defaultLocaleProperties = getLocaleProperties(defaultLocale);
41
+ const targetLocaleProperties = getConfiguredLocaleProperties(locale);
42
+ const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);
44
43
  translatedFiles = translatedFiles.map((filePath) => {
45
44
  const relativePath = getRelative(filePath);
46
45
  for (const transform of transformPath) {
@@ -58,8 +57,8 @@ function createFileMapping(filePaths, placeholderPaths, transformPaths, transfor
58
57
  return filePath;
59
58
  });
60
59
  } else {
61
- const targetLocaleProperties = getLocaleProperties(locale);
62
- const defaultLocaleProperties = getLocaleProperties(defaultLocale);
60
+ const targetLocaleProperties = getConfiguredLocaleProperties(locale);
61
+ const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);
63
62
  if (!transformPath.replace || typeof transformPath.replace !== "string") continue;
64
63
  const replaceString = replaceLocalePlaceholders(transformPath.replace, targetLocaleProperties);
65
64
  translatedFiles = translatedFiles.map((filePath) => {
@@ -1 +1 @@
1
- {"version":3,"file":"fileMapping.js","names":[],"sources":["../../../src/formats/files/fileMapping.ts"],"sourcesContent":["import {\n ResolvedFiles,\n TransformFiles,\n TransformFormats,\n} from '../../types/index.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../files/supportedFiles.js';\nimport { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';\nimport path from 'node:path';\nimport { getRelative } from '../../fs/findFilepath.js';\nimport { getLocaleProperties } from '@generaltranslation/format';\nimport { replaceLocalePlaceholders } from '../utils.js';\nimport { FileMapping } from '../../types/files.js';\nimport { TEMPLATE_FILE_NAME } from '../../utils/constants.js';\nimport { replaceFileExtensionForFormat } from './transformFormat.js';\n\n/**\n * Creates a mapping between source files and their translated counterparts for each locale\n * @param filePaths - Resolved file paths for different file types\n * @param placeholderPaths - Placeholder paths for translated files\n * @param transformPaths - Transform paths for file naming\n * @param transformFormats - Output file format transforms for translated files\n * @param locales - List of locales to create a mapping for\n * @returns A mapping between source files and their translated counterparts for each locale, in the form of relative paths\n */\nexport function createFileMapping(\n filePaths: ResolvedFiles,\n placeholderPaths: ResolvedFiles,\n transformPaths: TransformFiles,\n transformFormats: TransformFormats,\n targetLocales: string[],\n defaultLocale: string\n): FileMapping {\n const fileMapping: FileMapping = {};\n\n for (const locale of targetLocales) {\n const translatedPaths = resolveLocaleFiles(placeholderPaths, locale);\n const localeMapping: FileMapping[string] = {};\n\n // Process each file type\n\n // Start with GTJSON Template files\n if (translatedPaths.gt) {\n const filepath = translatedPaths.gt;\n localeMapping[TEMPLATE_FILE_NAME] = getRelative(filepath);\n }\n\n for (const typeIndex of SUPPORTED_FILE_EXTENSIONS) {\n if (!filePaths[typeIndex] || !translatedPaths[typeIndex]) continue;\n\n const sourcePaths = filePaths[typeIndex];\n let translatedFiles = translatedPaths[typeIndex];\n if (!translatedFiles) continue;\n\n const transformPath = transformPaths[typeIndex];\n const transformFormat = transformFormats?.[typeIndex];\n\n if (transformPath) {\n if (typeof transformPath === 'string') {\n translatedFiles = translatedFiles.map((filePath) => {\n const directory = path.dirname(filePath);\n const fileName = path.basename(filePath);\n const baseName = fileName.split('.')[0];\n const transformedFileName = transformPath\n .replace('*', baseName)\n .replace('[locale]', locale);\n return path.join(directory, transformedFileName);\n });\n } else if (Array.isArray(transformPath)) {\n // transformPath is an array of TransformOption objects\n const targetLocaleProperties = getLocaleProperties(locale);\n const defaultLocaleProperties = getLocaleProperties(defaultLocale);\n\n translatedFiles = translatedFiles.map((filePath) => {\n const relativePath = getRelative(filePath);\n\n // Try each transform in order until one matches\n for (const transform of transformPath) {\n if (!transform.replace || typeof transform.replace !== 'string') {\n continue;\n }\n\n // Replace all locale property placeholders in the replace string\n const replaceString = replaceLocalePlaceholders(\n transform.replace,\n targetLocaleProperties\n );\n\n if (transform.match && typeof transform.match === 'string') {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transform.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n const regex = new RegExp(matchString);\n if (regex.test(relativePath)) {\n // This transform matches, apply it and break\n const transformedPath = relativePath.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n return path.resolve(transformedPath);\n }\n } else {\n // No match provided: treat as a direct replacement (override)\n return path.resolve(replaceString);\n }\n }\n\n // If no transforms matched, return the original path\n return filePath;\n });\n } else {\n // transformPath is an object\n const targetLocaleProperties = getLocaleProperties(locale);\n const defaultLocaleProperties = getLocaleProperties(defaultLocale);\n if (\n !transformPath.replace ||\n typeof transformPath.replace !== 'string'\n ) {\n continue;\n }\n // Replace all locale property placeholders\n const replaceString = replaceLocalePlaceholders(\n transformPath.replace,\n targetLocaleProperties\n );\n translatedFiles = translatedFiles.map((filePath) => {\n let relativePath = getRelative(filePath);\n if (\n transformPath.match &&\n typeof transformPath.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformPath.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n relativePath = relativePath.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n relativePath = replaceString;\n }\n return path.resolve(relativePath);\n });\n }\n }\n\n for (let i = 0; i < sourcePaths.length; i++) {\n const sourceFile = getRelative(sourcePaths[i]);\n // Format transforms keep the mapped path but rewrite the output suffix.\n const translatedFile = getRelative(\n transformFormat\n ? replaceFileExtensionForFormat(translatedFiles[i], transformFormat)\n : translatedFiles[i]\n );\n localeMapping[sourceFile] = translatedFile;\n }\n }\n\n fileMapping[locale] = localeMapping;\n }\n\n return fileMapping;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwBA,SAAgB,kBACd,WACA,kBACA,gBACA,kBACA,eACA,eACa;CACb,MAAM,cAA2B,EAAE;AAEnC,MAAK,MAAM,UAAU,eAAe;EAClC,MAAM,kBAAkB,mBAAmB,kBAAkB,OAAO;EACpE,MAAM,gBAAqC,EAAE;AAK7C,MAAI,gBAAgB,IAAI;GACtB,MAAM,WAAW,gBAAgB;AACjC,iBAAc,sBAAsB,YAAY,SAAS;;AAG3D,OAAK,MAAM,aAAa,2BAA2B;AACjD,OAAI,CAAC,UAAU,cAAc,CAAC,gBAAgB,WAAY;GAE1D,MAAM,cAAc,UAAU;GAC9B,IAAI,kBAAkB,gBAAgB;AACtC,OAAI,CAAC,gBAAiB;GAEtB,MAAM,gBAAgB,eAAe;GACrC,MAAM,kBAAkB,mBAAmB;AAE3C,OAAI,cACF,KAAI,OAAO,kBAAkB,SAC3B,mBAAkB,gBAAgB,KAAK,aAAa;IAClD,MAAM,YAAY,KAAK,QAAQ,SAAS;IAExC,MAAM,WADW,KAAK,SAAS,SACN,CAAC,MAAM,IAAI,CAAC;IACrC,MAAM,sBAAsB,cACzB,QAAQ,KAAK,SAAS,CACtB,QAAQ,YAAY,OAAO;AAC9B,WAAO,KAAK,KAAK,WAAW,oBAAoB;KAChD;YACO,MAAM,QAAQ,cAAc,EAAE;IAEvC,MAAM,yBAAyB,oBAAoB,OAAO;IAC1D,MAAM,0BAA0B,oBAAoB,cAAc;AAElE,sBAAkB,gBAAgB,KAAK,aAAa;KAClD,MAAM,eAAe,YAAY,SAAS;AAG1C,UAAK,MAAM,aAAa,eAAe;AACrC,UAAI,CAAC,UAAU,WAAW,OAAO,UAAU,YAAY,SACrD;MAIF,MAAM,gBAAgB,0BACpB,UAAU,SACV,uBACD;AAED,UAAI,UAAU,SAAS,OAAO,UAAU,UAAU,UAAU;OAE1D,IAAI,cAAc,UAAU;AAC5B,qBAAc,0BACZ,aACA,wBACD;AAGD,WAAI,IADc,OAAO,YAChB,CAAC,KAAK,aAAa,EAAE;QAE5B,MAAM,kBAAkB,aAAa,QACnC,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;AACD,eAAO,KAAK,QAAQ,gBAAgB;;YAItC,QAAO,KAAK,QAAQ,cAAc;;AAKtC,YAAO;MACP;UACG;IAEL,MAAM,yBAAyB,oBAAoB,OAAO;IAC1D,MAAM,0BAA0B,oBAAoB,cAAc;AAClE,QACE,CAAC,cAAc,WACf,OAAO,cAAc,YAAY,SAEjC;IAGF,MAAM,gBAAgB,0BACpB,cAAc,SACd,uBACD;AACD,sBAAkB,gBAAgB,KAAK,aAAa;KAClD,IAAI,eAAe,YAAY,SAAS;AACxC,SACE,cAAc,SACd,OAAO,cAAc,UAAU,UAC/B;MAEA,IAAI,cAAc,cAAc;AAChC,oBAAc,0BACZ,aACA,wBACD;AAED,qBAAe,aAAa,QAC1B,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;WAED,gBAAe;AAEjB,YAAO,KAAK,QAAQ,aAAa;MACjC;;AAIN,QAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;IAC3C,MAAM,aAAa,YAAY,YAAY,GAAG;AAO9C,kBAAc,cALS,YACrB,kBACI,8BAA8B,gBAAgB,IAAI,gBAAgB,GAClE,gBAAgB,GAEoB;;;AAI9C,cAAY,UAAU;;AAGxB,QAAO"}
1
+ {"version":3,"file":"fileMapping.js","names":[],"sources":["../../../src/formats/files/fileMapping.ts"],"sourcesContent":["import {\n ResolvedFiles,\n TransformFiles,\n TransformFormats,\n} from '../../types/index.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../files/supportedFiles.js';\nimport { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';\nimport path from 'node:path';\nimport { getRelative } from '../../fs/findFilepath.js';\nimport {\n getConfiguredLocaleProperties,\n replaceLocalePlaceholders,\n} from '../utils.js';\nimport { FileMapping } from '../../types/files.js';\nimport { TEMPLATE_FILE_NAME } from '../../utils/constants.js';\nimport { replaceFileExtensionForFormat } from './transformFormat.js';\n\n/**\n * Creates a mapping between source files and their translated counterparts for each locale\n * @param filePaths - Resolved file paths for different file types\n * @param placeholderPaths - Placeholder paths for translated files\n * @param transformPaths - Transform paths for file naming\n * @param transformFormats - Output file format transforms for translated files\n * @param locales - List of locales to create a mapping for\n * @returns A mapping between source files and their translated counterparts for each locale, in the form of relative paths\n */\nexport function createFileMapping(\n filePaths: ResolvedFiles,\n placeholderPaths: ResolvedFiles,\n transformPaths: TransformFiles,\n transformFormats: TransformFormats,\n targetLocales: string[],\n defaultLocale: string\n): FileMapping {\n const fileMapping: FileMapping = {};\n\n for (const locale of targetLocales) {\n const translatedPaths = resolveLocaleFiles(placeholderPaths, locale);\n const localeMapping: FileMapping[string] = {};\n\n // Process each file type\n\n // Start with GTJSON Template files\n if (translatedPaths.gt) {\n const filepath = translatedPaths.gt;\n localeMapping[TEMPLATE_FILE_NAME] = getRelative(filepath);\n }\n\n for (const typeIndex of SUPPORTED_FILE_EXTENSIONS) {\n if (!filePaths[typeIndex] || !translatedPaths[typeIndex]) continue;\n\n const sourcePaths = filePaths[typeIndex];\n let translatedFiles = translatedPaths[typeIndex];\n if (!translatedFiles) continue;\n\n const transformPath = transformPaths[typeIndex];\n const transformFormat = transformFormats?.[typeIndex];\n\n if (transformPath) {\n if (typeof transformPath === 'string') {\n translatedFiles = translatedFiles.map((filePath) => {\n const directory = path.dirname(filePath);\n const fileName = path.basename(filePath);\n const baseName = fileName.split('.')[0];\n const transformedFileName = transformPath\n .replace('*', baseName)\n .replace('[locale]', locale);\n return path.join(directory, transformedFileName);\n });\n } else if (Array.isArray(transformPath)) {\n // transformPath is an array of TransformOption objects\n const targetLocaleProperties = getConfiguredLocaleProperties(locale);\n const defaultLocaleProperties =\n getConfiguredLocaleProperties(defaultLocale);\n\n translatedFiles = translatedFiles.map((filePath) => {\n const relativePath = getRelative(filePath);\n\n // Try each transform in order until one matches\n for (const transform of transformPath) {\n if (!transform.replace || typeof transform.replace !== 'string') {\n continue;\n }\n\n // Replace all locale property placeholders in the replace string\n const replaceString = replaceLocalePlaceholders(\n transform.replace,\n targetLocaleProperties\n );\n\n if (transform.match && typeof transform.match === 'string') {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transform.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n const regex = new RegExp(matchString);\n if (regex.test(relativePath)) {\n // This transform matches, apply it and break\n const transformedPath = relativePath.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n return path.resolve(transformedPath);\n }\n } else {\n // No match provided: treat as a direct replacement (override)\n return path.resolve(replaceString);\n }\n }\n\n // If no transforms matched, return the original path\n return filePath;\n });\n } else {\n // transformPath is an object\n const targetLocaleProperties = getConfiguredLocaleProperties(locale);\n const defaultLocaleProperties =\n getConfiguredLocaleProperties(defaultLocale);\n if (\n !transformPath.replace ||\n typeof transformPath.replace !== 'string'\n ) {\n continue;\n }\n // Replace all locale property placeholders\n const replaceString = replaceLocalePlaceholders(\n transformPath.replace,\n targetLocaleProperties\n );\n translatedFiles = translatedFiles.map((filePath) => {\n let relativePath = getRelative(filePath);\n if (\n transformPath.match &&\n typeof transformPath.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformPath.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n relativePath = relativePath.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n relativePath = replaceString;\n }\n return path.resolve(relativePath);\n });\n }\n }\n\n for (let i = 0; i < sourcePaths.length; i++) {\n const sourceFile = getRelative(sourcePaths[i]);\n // Format transforms keep the mapped path but rewrite the output suffix.\n const translatedFile = getRelative(\n transformFormat\n ? replaceFileExtensionForFormat(translatedFiles[i], transformFormat)\n : translatedFiles[i]\n );\n localeMapping[sourceFile] = translatedFile;\n }\n }\n\n fileMapping[locale] = localeMapping;\n }\n\n return fileMapping;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,SAAgB,kBACd,WACA,kBACA,gBACA,kBACA,eACA,eACa;CACb,MAAM,cAA2B,EAAE;AAEnC,MAAK,MAAM,UAAU,eAAe;EAClC,MAAM,kBAAkB,mBAAmB,kBAAkB,OAAO;EACpE,MAAM,gBAAqC,EAAE;AAK7C,MAAI,gBAAgB,IAAI;GACtB,MAAM,WAAW,gBAAgB;AACjC,iBAAc,sBAAsB,YAAY,SAAS;;AAG3D,OAAK,MAAM,aAAa,2BAA2B;AACjD,OAAI,CAAC,UAAU,cAAc,CAAC,gBAAgB,WAAY;GAE1D,MAAM,cAAc,UAAU;GAC9B,IAAI,kBAAkB,gBAAgB;AACtC,OAAI,CAAC,gBAAiB;GAEtB,MAAM,gBAAgB,eAAe;GACrC,MAAM,kBAAkB,mBAAmB;AAE3C,OAAI,cACF,KAAI,OAAO,kBAAkB,SAC3B,mBAAkB,gBAAgB,KAAK,aAAa;IAClD,MAAM,YAAY,KAAK,QAAQ,SAAS;IAExC,MAAM,WADW,KAAK,SAAS,SACN,CAAC,MAAM,IAAI,CAAC;IACrC,MAAM,sBAAsB,cACzB,QAAQ,KAAK,SAAS,CACtB,QAAQ,YAAY,OAAO;AAC9B,WAAO,KAAK,KAAK,WAAW,oBAAoB;KAChD;YACO,MAAM,QAAQ,cAAc,EAAE;IAEvC,MAAM,yBAAyB,8BAA8B,OAAO;IACpE,MAAM,0BACJ,8BAA8B,cAAc;AAE9C,sBAAkB,gBAAgB,KAAK,aAAa;KAClD,MAAM,eAAe,YAAY,SAAS;AAG1C,UAAK,MAAM,aAAa,eAAe;AACrC,UAAI,CAAC,UAAU,WAAW,OAAO,UAAU,YAAY,SACrD;MAIF,MAAM,gBAAgB,0BACpB,UAAU,SACV,uBACD;AAED,UAAI,UAAU,SAAS,OAAO,UAAU,UAAU,UAAU;OAE1D,IAAI,cAAc,UAAU;AAC5B,qBAAc,0BACZ,aACA,wBACD;AAGD,WAAI,IADc,OAAO,YAChB,CAAC,KAAK,aAAa,EAAE;QAE5B,MAAM,kBAAkB,aAAa,QACnC,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;AACD,eAAO,KAAK,QAAQ,gBAAgB;;YAItC,QAAO,KAAK,QAAQ,cAAc;;AAKtC,YAAO;MACP;UACG;IAEL,MAAM,yBAAyB,8BAA8B,OAAO;IACpE,MAAM,0BACJ,8BAA8B,cAAc;AAC9C,QACE,CAAC,cAAc,WACf,OAAO,cAAc,YAAY,SAEjC;IAGF,MAAM,gBAAgB,0BACpB,cAAc,SACd,uBACD;AACD,sBAAkB,gBAAgB,KAAK,aAAa;KAClD,IAAI,eAAe,YAAY,SAAS;AACxC,SACE,cAAc,SACd,OAAO,cAAc,UAAU,UAC/B;MAEA,IAAI,cAAc,cAAc;AAChC,oBAAc,0BACZ,aACA,wBACD;AAED,qBAAe,aAAa,QAC1B,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;WAED,gBAAe;AAEjB,YAAO,KAAK,QAAQ,aAAa;MACjC;;AAIN,QAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;IAC3C,MAAM,aAAa,YAAY,YAAY,GAAG;AAO9C,kBAAc,cALS,YACrB,kBACI,8BAA8B,gBAAgB,IAAI,gBAAgB,GAClE,gBAAgB,GAEoB;;;AAI9C,cAAY,UAAU;;AAGxB,QAAO"}
@@ -1,12 +1,11 @@
1
1
  import { logger } from "../../console/logger.js";
2
2
  import { gt } from "../../utils/gt.js";
3
3
  import { exitSync } from "../../console/logging.js";
4
+ import { getConfiguredLocaleProperties, replaceLocalePlaceholders } from "../utils.js";
4
5
  import { getJSONPathMatches, getJSONPathValues } from "./jsonPath.js";
5
6
  import { findMatchingItemArray, findMatchingItemObject, generateSourceObjectPointers, getIdentifyingLocaleProperty, getSourceObjectOptionsArray, validateJsonSchema } from "./utils.js";
6
7
  import { getJSONPointerValue, setJSONPointerValue } from "./jsonPointer.js";
7
8
  import { applyStructuralTransforms, unapplyStructuralTransforms } from "./transformJson.js";
8
- import { replaceLocalePlaceholders } from "../utils.js";
9
- import { getLocaleProperties } from "@generaltranslation/format";
10
9
  //#region src/formats/json/mergeJson.ts
11
10
  function mergeJson(originalContent, inputPath, options, targets, defaultLocale, localeOrder = []) {
12
11
  const jsonSchema = validateJsonSchema(options, inputPath);
@@ -221,8 +220,8 @@ function omitProperties(item, properties) {
221
220
  */
222
221
  function applyTransformations(sourceItem, transform, targetLocale, defaultLocale) {
223
222
  if (!transform) return;
224
- const targetLocaleProperties = getLocaleProperties(targetLocale);
225
- const defaultLocaleProperties = getLocaleProperties(defaultLocale);
223
+ const targetLocaleProperties = getConfiguredLocaleProperties(targetLocale);
224
+ const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);
226
225
  for (const [transformPath, transformOptions] of Object.entries(transform)) {
227
226
  if (!transformOptions.replace || typeof transformOptions.replace !== "string") continue;
228
227
  const results = getJSONPathMatches(sourceItem, transformPath);
@@ -1 +1 @@
1
- {"version":3,"file":"mergeJson.js","names":[],"sources":["../../../src/formats/json/mergeJson.ts"],"sourcesContent":["import { AdditionalOptions, SourceObjectOptions } from '../../types/index.js';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport {\n findMatchingItemArray,\n findMatchingItemObject,\n generateSourceObjectPointers,\n getIdentifyingLocaleProperty,\n getSourceObjectOptionsArray,\n validateJsonSchema,\n} from './utils.js';\nimport { getLocaleProperties } from '@generaltranslation/format';\nimport { replaceLocalePlaceholders } from '../utils.js';\nimport { gt } from '../../utils/gt.js';\nimport {\n applyStructuralTransforms,\n unapplyStructuralTransforms,\n} from './transformJson.js';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches, getJSONPathValues } from './jsonPath.js';\nimport { getJSONPointerValue, setJSONPointerValue } from './jsonPointer.js';\n\ntype ParsedTarget = {\n translatedContent: string;\n targetLocale: string;\n parsedContent: JSONObject;\n};\n\nexport function mergeJson(\n originalContent: string,\n inputPath: string,\n options: AdditionalOptions,\n targets: {\n translatedContent: string;\n targetLocale: string;\n }[],\n defaultLocale: string,\n localeOrder: string[] = []\n): string[] {\n const jsonSchema = validateJsonSchema(options, inputPath);\n if (!jsonSchema) {\n return targets.map((target) => target.translatedContent);\n }\n\n let originalJson: JSONValue;\n try {\n originalJson = JSON.parse(originalContent);\n } catch {\n logger.error(`Invalid JSON file: ${inputPath}`);\n return exitSync(1);\n }\n\n const useCanonicalLocaleKeys =\n options?.experimentalCanonicalLocaleKeys ?? false;\n const canonicalDefaultLocale = useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(defaultLocale)\n : defaultLocale;\n const canonicalLocaleOrder = useCanonicalLocaleKeys\n ? localeOrder.map((locale) => gt.resolveCanonicalLocale(locale))\n : localeOrder;\n\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n applyStructuralTransforms(\n originalJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n // Handle include\n if (jsonSchema.include) {\n const output: string[] = [];\n for (const target of targets) {\n // Must clone the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n const translatedJson = JSON.parse(target.translatedContent) as JSONObject;\n for (const [jsonPointer, translatedValue] of Object.entries(\n translatedJson\n )) {\n try {\n const value = getJSONPointerValue(mergedJson, jsonPointer);\n if (!value) continue;\n setJSONPointerValue(mergedJson, jsonPointer, translatedValue);\n } catch {\n /* empty */\n }\n }\n output.push(JSON.stringify(mergedJson, null, 2));\n }\n return output;\n }\n\n if (!jsonSchema.composite) {\n logger.error('No composite property found in JSON schema');\n return exitSync(1);\n }\n\n // Handle composite\n // Create a deep copy of the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n\n // Pre-parse all target contents ONCE (avoid re-parsing per pointer)\n const parsedTargets = targets.map((target) => ({\n ...target,\n parsedContent: JSON.parse(target.translatedContent) as JSONObject,\n })) satisfies ParsedTarget[];\n\n // Create mapping of sourceObjectPointer to SourceObjectOptions\n const sourceObjectPointers = generateSourceObjectPointers(\n jsonSchema.composite,\n originalJson\n );\n\n // Find the source object\n for (const [\n sourceObjectPointer,\n { sourceObjectValue, sourceObjectOptions },\n ] of Object.entries(sourceObjectPointers)) {\n // Find the source item\n if (sourceObjectOptions.type === 'array') {\n // Validate type\n if (!Array.isArray(sourceObjectValue)) {\n logger.error(\n `Source object value is not an array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n\n // Get source item for default locale\n const matchingDefaultLocaleItems = findMatchingItemArray(\n canonicalDefaultLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n if (!Object.keys(matchingDefaultLocaleItems).length) {\n logger.warn(\n `Matching sourceItems not found at path: ${sourceObjectPointer}. Check that your JSON file includes the key field. Skipping this target`\n );\n continue;\n }\n\n const matchingDefaultLocaleItemKeys = new Set(\n Object.keys(matchingDefaultLocaleItems)\n );\n\n // For each target:\n // 1. Get the target items\n // 2. Track all array indecies to remove (will be overwritten)\n // 3. Merge matchingDefaultLocaleItems and targetItems\n // 4. Validate that the mergedItems is not empty\n // For each target item:\n // 5. Validate that all the array indecies are still present in the source json\n // 6. Override the source item with the translated values\n // 7. Apply additional mutations to the sourceItem\n // 8. Track all items to add\n // 9. Check that items to add is >= items to remove\n // 10. Remove all items for the target locale (they can be identified by the key)\n const indiciesToRemove = new Set<number>();\n const itemsToAdd: JSONValue[] = [];\n for (const target of parsedTargets) {\n let targetItems = target.parsedContent[sourceObjectPointer];\n // 1. Get the target items\n if (!targetItems) {\n // If no translation can be found, a transformation may need to happen still\n targetItems = {};\n }\n\n // 2. Track all array indecies to remove (will be overwritten)\n const targetItemsToRemove = findMatchingItemArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n Object.values(targetItemsToRemove).forEach(({ index }) =>\n indiciesToRemove.add(index)\n );\n\n // Remap mismatched positional keys to current source positions\n const sourceKeys = [...matchingDefaultLocaleItemKeys];\n const remappedTargetItems: Record<string, JSONValue> = {};\n for (const [key, value] of Object.entries(targetItems as JSONObject)) {\n if (matchingDefaultLocaleItemKeys.has(key)) {\n remappedTargetItems[key] = value;\n } else if (\n sourceKeys.length === 1 &&\n !(sourceKeys[0] in remappedTargetItems)\n ) {\n remappedTargetItems[sourceKeys[0]] = value;\n } else {\n logger.warn(\n `Skipping translated item at ${key}: cannot map to source item at path ${sourceObjectPointer}`\n );\n }\n }\n\n // Merge matchingDefaultLocaleItems and remapped targetItems\n const mergedItems = {\n ...(sourceObjectOptions.transform ? matchingDefaultLocaleItems : {}),\n ...remappedTargetItems,\n };\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n for (const [sourceItemPointer, targetItem] of Object.entries(\n mergedItems\n )) {\n // 5. Validate that all the array indecies are still present in the source json\n if (!matchingDefaultLocaleItemKeys.has(sourceItemPointer)) {\n logger.warn(\n `Skipping translated item at ${sourceItemPointer}: not present in source json at path ${sourceObjectPointer}`\n );\n continue;\n }\n\n // 6. Override the source item with the translated values\n const defaultLocaleSourceItem =\n matchingDefaultLocaleItems[sourceItemPointer].sourceItem;\n const defaultLocaleKeyPointer =\n matchingDefaultLocaleItems[sourceItemPointer].keyPointer;\n const mutatedSourceItem = structuredClone(defaultLocaleSourceItem);\n const { identifyingLocaleProperty: targetLocaleKeyProperty } =\n getSourceObjectOptionsArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n setJSONPointerValue(\n mutatedSourceItem,\n defaultLocaleKeyPointer,\n targetLocaleKeyProperty\n );\n omitProperties(mutatedSourceItem, sourceObjectOptions.omitProperties);\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries((targetItem || {}) as JSONObject)) {\n const valueToSet =\n useCanonicalLocaleKeys &&\n defaultLocaleKeyPointer &&\n translatedKeyJsonPointer === defaultLocaleKeyPointer\n ? targetLocaleKeyProperty\n : translatedValue;\n try {\n const value = getJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer,\n valueToSet\n );\n } catch {\n /* empty */\n }\n }\n\n // 7. Apply additional mutations to the sourceItem\n applyTransformations(\n mutatedSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n itemsToAdd.push(mutatedSourceItem);\n }\n }\n\n // 8. Check that items to add is >= items to remove\n if (itemsToAdd.length < indiciesToRemove.size) {\n logger.warn(\n `Items to add (${itemsToAdd.length}) is less than items to remove (${indiciesToRemove.size}) at path: ${sourceObjectPointer}. Some translated items may have been skipped.`\n );\n }\n\n // 9. Remove all items for the target locale (they can be identified by the key)\n const filteredSourceObjectValue = sourceObjectValue.filter(\n (_, index: number) => !indiciesToRemove.has(index)\n );\n\n // 10. Add all items to the original JSON\n filteredSourceObjectValue.push(...itemsToAdd);\n\n setJSONPointerValue(\n mergedJson,\n sourceObjectPointer,\n sortByLocaleOrder(\n filteredSourceObjectValue,\n sourceObjectOptions,\n canonicalLocaleOrder,\n sourceObjectPointer,\n canonicalDefaultLocale\n )\n );\n } else {\n // Validate type\n if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {\n logger.error(\n `Source object value is not an object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n const sourceObjectRecord = sourceObjectValue as JSONObject;\n // Validate localeProperty\n const matchingDefaultLocaleItem = findMatchingItemObject(\n canonicalDefaultLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n // Validate source item exists\n if (!matchingDefaultLocaleItem.sourceItem) {\n logger.error(\n `Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`\n );\n return exitSync(1);\n }\n const { sourceItem: defaultLocaleSourceItem } = matchingDefaultLocaleItem;\n\n // For each target:\n // 1. Get the target items\n // 2. Find the source item for the target locale\n // 3. Merge the target items with the source item\n // 4. Validate that the mergedItems is not empty\n // 5. Override the source item with the translated values\n // 6. Apply additional mutations to the sourceItem\n // 7. Merge the source item with the original JSON (if the source item is not a new item)\n for (const target of parsedTargets) {\n // 1. Get the target items\n let targetItems = target.parsedContent[sourceObjectPointer];\n if (targetItems == null) {\n targetItems = {};\n }\n\n // 2. Find the source item for the target locale\n const matchingTargetItem = findMatchingItemObject(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n const mutateSourceItemKey = matchingTargetItem.keyParentProperty;\n\n // If the source item is a string, use the translated string directly\n if (typeof defaultLocaleSourceItem === 'string') {\n if (typeof targetItems === 'string') {\n sourceObjectRecord[mutateSourceItemKey] = targetItems;\n }\n // If no translation found, leave the locale slot unchanged\n continue;\n }\n\n // If the target locale has a matching source item, use it to mutate the source item\n // Otherwise, fallback to the default locale source item\n const mutateSourceItem = structuredClone(defaultLocaleSourceItem);\n omitProperties(mutateSourceItem, sourceObjectOptions.omitProperties);\n\n // 3. Merge the target items with the source item (if there are transformations to perform)\n const mergedItems: Record<string, JSONValue> = {\n ...(sourceObjectOptions.transform\n ? (defaultLocaleSourceItem as JSONObject)\n : {}),\n ...(targetItems as JSONObject),\n };\n\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n // 5. Override the source item with the translated values\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries(mergedItems || {})) {\n try {\n const value = getJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer,\n translatedValue\n );\n } catch {\n /* empty */\n }\n }\n // 6. Apply additional mutations to the sourceItem\n applyTransformations(\n mutateSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n // 7. Merge the source item with the original JSON\n sourceObjectRecord[mutateSourceItemKey] = mutateSourceItem;\n }\n setJSONPointerValue(mergedJson, sourceObjectPointer, sourceObjectValue);\n }\n }\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n unapplyStructuralTransforms(\n mergedJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n return [JSON.stringify(mergedJson, null, 2)];\n}\n\nfunction sortByLocaleOrder(\n items: JSONValue[],\n sourceObjectOptions: SourceObjectOptions,\n localeOrder: string[],\n sourceObjectPointer: string,\n defaultLocale: string\n): JSONValue[] {\n const sortMode = sourceObjectOptions.experimentalSort;\n if (!sortMode || !sourceObjectOptions.key) {\n return items;\n }\n\n const itemsWithLocale = items.map((item) => {\n let localeValue: string | undefined;\n try {\n const values = getJSONPathValues(item, sourceObjectOptions.key as string);\n const value = values?.[0];\n if (typeof value === 'string') {\n localeValue = value;\n }\n } catch {\n /* empty */\n }\n return { item, localeValue };\n });\n\n if (sortMode === 'locales') {\n if (!localeOrder.length) {\n return items;\n }\n\n const orderedLocaleList = [\n defaultLocale,\n ...localeOrder.filter((locale) => locale !== defaultLocale),\n ];\n const localeOrderValues = orderedLocaleList.map((locale) =>\n getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n )\n );\n\n const orderedItems: JSONValue[] = [];\n const remainingItems = [...itemsWithLocale];\n\n for (const localeValue of localeOrderValues) {\n for (let i = 0; i < remainingItems.length; ) {\n const entry = remainingItems[i];\n if (entry.localeValue === localeValue) {\n orderedItems.push(entry.item);\n remainingItems.splice(i, 1);\n continue;\n }\n i += 1;\n }\n }\n\n remainingItems.forEach((entry) => orderedItems.push(entry.item));\n\n return orderedItems;\n }\n\n if (sortMode === 'localesAlphabetical') {\n const defaultLocaleValue = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n const defaultItems: typeof itemsWithLocale = [];\n const sortableItems: typeof itemsWithLocale = [];\n const remainingItems: typeof itemsWithLocale = [];\n\n for (const entry of itemsWithLocale) {\n if (entry.localeValue === defaultLocaleValue) {\n defaultItems.push(entry);\n continue;\n }\n if (entry.localeValue) {\n sortableItems.push(entry);\n continue;\n }\n remainingItems.push(entry);\n }\n\n sortableItems.sort((a, b) => {\n if (!a.localeValue || !b.localeValue) {\n return 0;\n }\n return a.localeValue.localeCompare(b.localeValue);\n });\n\n return [...defaultItems, ...sortableItems, ...remainingItems].map(\n (entry) => entry.item\n );\n }\n\n return items;\n}\n\n/**\n * Remove top-level properties from a generated non-default-locale entry\n * (e.g. Mintlify's `default: true` flag, which is only valid on one entry)\n */\nfunction omitProperties(\n item: JSONValue,\n properties: string[] | undefined\n): void {\n if (!properties?.length) return;\n if (!item || typeof item !== 'object' || Array.isArray(item)) return;\n for (const property of properties) {\n delete (item as JSONObject)[property];\n }\n}\n\n/**\n * Apply transformations to the sourceItem in-place\n * @param sourceItem - The source item to apply transformations to\n * @param transform - The transformations to apply\n * @param targetLocale - The target locale\n * @param defaultLocale - The default locale\n */\nexport function applyTransformations(\n sourceItem: JSONValue,\n transform: SourceObjectOptions['transform'],\n targetLocale: string,\n defaultLocale: string\n): void {\n if (!transform) return;\n\n const targetLocaleProperties = getLocaleProperties(targetLocale);\n const defaultLocaleProperties = getLocaleProperties(defaultLocale);\n\n for (const [transformPath, transformOptions] of Object.entries(transform)) {\n if (\n !transformOptions.replace ||\n typeof transformOptions.replace !== 'string'\n ) {\n continue;\n }\n const results = getJSONPathMatches(sourceItem, transformPath);\n if (!results || results.length === 0) {\n continue;\n }\n results.forEach((result) => {\n if (typeof result.value !== 'string') {\n return;\n }\n // Replace locale placeholders in the replace string\n let replaceString = transformOptions.replace;\n\n // Replace all locale property placeholders\n replaceString = replaceLocalePlaceholders(\n replaceString,\n targetLocaleProperties\n );\n\n if (\n transformOptions.match &&\n typeof transformOptions.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformOptions.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n result.value = result.value.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n result.value = replaceString;\n }\n\n // Update the actual sourceItem using JSONPointer\n setJSONPointerValue(sourceItem, result.pointer, result.value);\n });\n }\n}\n"],"mappings":";;;;;;;;;;AA4BA,SAAgB,UACd,iBACA,WACA,SACA,SAIA,eACA,cAAwB,EAAE,EAChB;CACV,MAAM,aAAa,mBAAmB,SAAS,UAAU;AACzD,KAAI,CAAC,WACH,QAAO,QAAQ,KAAK,WAAW,OAAO,kBAAkB;CAG1D,IAAI;AACJ,KAAI;AACF,iBAAe,KAAK,MAAM,gBAAgB;SACpC;AACN,SAAO,MAAM,sBAAsB,YAAY;AAC/C,SAAO,SAAS,EAAE;;CAGpB,MAAM,yBACJ,SAAS,mCAAmC;CAC9C,MAAM,yBAAyB,yBAC3B,GAAG,uBAAuB,cAAc,GACxC;CACJ,MAAM,uBAAuB,yBACzB,YAAY,KAAK,WAAW,GAAG,uBAAuB,OAAO,CAAC,GAC9D;AAEJ,KAAI,WAAW,uBAAuB,WAAW,UAC/C,2BACE,cACA,WAAW,qBACX,WAAW,UACZ;AAIH,KAAI,WAAW,SAAS;EACtB,MAAM,SAAmB,EAAE;AAC3B,OAAK,MAAM,UAAU,SAAS;GAE5B,MAAM,aAAa,gBAAgB,aAAa;GAChD,MAAM,iBAAiB,KAAK,MAAM,OAAO,kBAAkB;AAC3D,QAAK,MAAM,CAAC,aAAa,oBAAoB,OAAO,QAClD,eACD,CACC,KAAI;AAEF,QAAI,CADU,oBAAoB,YAAY,YACpC,CAAE;AACZ,wBAAoB,YAAY,aAAa,gBAAgB;WACvD;AAIV,UAAO,KAAK,KAAK,UAAU,YAAY,MAAM,EAAE,CAAC;;AAElD,SAAO;;AAGT,KAAI,CAAC,WAAW,WAAW;AACzB,SAAO,MAAM,6CAA6C;AAC1D,SAAO,SAAS,EAAE;;CAKpB,MAAM,aAAa,gBAAgB,aAAa;CAGhD,MAAM,gBAAgB,QAAQ,KAAK,YAAY;EAC7C,GAAG;EACH,eAAe,KAAK,MAAM,OAAO,kBAAkB;EACpD,EAAE;CAGH,MAAM,uBAAuB,6BAC3B,WAAW,WACX,aACD;AAGD,MAAK,MAAM,CACT,qBACA,EAAE,mBAAmB,0BAClB,OAAO,QAAQ,qBAAqB,CAEvC,KAAI,oBAAoB,SAAS,SAAS;AAExC,MAAI,CAAC,MAAM,QAAQ,kBAAkB,EAAE;AACrC,UAAO,MACL,gDAAgD,sBACjD;AACD,UAAO,SAAS,EAAE;;EAIpB,MAAM,6BAA6B,sBACjC,wBACA,qBACA,qBACA,kBACD;AACD,MAAI,CAAC,OAAO,KAAK,2BAA2B,CAAC,QAAQ;AACnD,UAAO,KACL,2CAA2C,oBAAoB,0EAChE;AACD;;EAGF,MAAM,gCAAgC,IAAI,IACxC,OAAO,KAAK,2BAA2B,CACxC;EAcD,MAAM,mCAAmB,IAAI,KAAa;EAC1C,MAAM,aAA0B,EAAE;AAClC,OAAK,MAAM,UAAU,eAAe;GAClC,IAAI,cAAc,OAAO,cAAc;AAEvC,OAAI,CAAC,YAEH,eAAc,EAAE;GAIlB,MAAM,sBAAsB,sBAC1B,yBACI,GAAG,uBAAuB,OAAO,aAAa,GAC9C,OAAO,cACX,qBACA,qBACA,kBACD;AACD,UAAO,OAAO,oBAAoB,CAAC,SAAS,EAAE,YAC5C,iBAAiB,IAAI,MAAM,CAC5B;GAGD,MAAM,aAAa,CAAC,GAAG,8BAA8B;GACrD,MAAM,sBAAiD,EAAE;AACzD,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAA0B,CAClE,KAAI,8BAA8B,IAAI,IAAI,CACxC,qBAAoB,OAAO;YAE3B,WAAW,WAAW,KACtB,EAAE,WAAW,MAAM,qBAEnB,qBAAoB,WAAW,MAAM;OAErC,QAAO,KACL,+BAA+B,IAAI,sCAAsC,sBAC1E;GAKL,MAAM,cAAc;IAClB,GAAI,oBAAoB,YAAY,6BAA6B,EAAE;IACnE,GAAG;IACJ;AAED,OAAI,OAAO,KAAK,YAAY,CAAC,WAAW,GAAG;AACzC,WAAO,KACL,+BAA+B,OAAO,aAAa,8CAA8C,oBAAoB,wBACtH;AACD;;AAGF,QAAK,MAAM,CAAC,mBAAmB,eAAe,OAAO,QACnD,YACD,EAAE;AAED,QAAI,CAAC,8BAA8B,IAAI,kBAAkB,EAAE;AACzD,YAAO,KACL,+BAA+B,kBAAkB,uCAAuC,sBACzF;AACD;;IAIF,MAAM,0BACJ,2BAA2B,mBAAmB;IAChD,MAAM,0BACJ,2BAA2B,mBAAmB;IAChD,MAAM,oBAAoB,gBAAgB,wBAAwB;IAClE,MAAM,EAAE,2BAA2B,4BACjC,4BACE,yBACI,GAAG,uBAAuB,OAAO,aAAa,GAC9C,OAAO,cACX,qBACA,oBACD;AACH,wBACE,mBACA,yBACA,wBACD;AACD,mBAAe,mBAAmB,oBAAoB,eAAe;AACrE,SAAK,MAAM,CACT,0BACA,oBACG,OAAO,QAAS,cAAc,EAAE,CAAgB,EAAE;KACrD,MAAM,aACJ,0BACA,2BACA,6BAA6B,0BACzB,0BACA;AACN,SAAI;AAKF,UAAI,CAJU,oBACZ,mBACA,yBAEQ,CAAE;AACZ,0BACE,mBACA,0BACA,WACD;aACK;;AAMV,yBACE,mBACA,oBAAoB,WACpB,OAAO,cACP,cACD;AAED,eAAW,KAAK,kBAAkB;;;AAKtC,MAAI,WAAW,SAAS,iBAAiB,KACvC,QAAO,KACL,iBAAiB,WAAW,OAAO,kCAAkC,iBAAiB,KAAK,aAAa,oBAAoB,gDAC7H;EAIH,MAAM,4BAA4B,kBAAkB,QACjD,GAAG,UAAkB,CAAC,iBAAiB,IAAI,MAAM,CACnD;AAGD,4BAA0B,KAAK,GAAG,WAAW;AAE7C,sBACE,YACA,qBACA,kBACE,2BACA,qBACA,sBACA,qBACA,uBACD,CACF;QACI;AAEL,MAAI,OAAO,sBAAsB,YAAY,sBAAsB,MAAM;AACvE,UAAO,MACL,iDAAiD,sBAClD;AACD,UAAO,SAAS,EAAE;;EAEpB,MAAM,qBAAqB;EAE3B,MAAM,4BAA4B,uBAChC,wBACA,qBACA,qBACA,mBACD;AAED,MAAI,CAAC,0BAA0B,YAAY;AACzC,UAAO,MACL,kCAAkC,oBAAoB,2EACvD;AACD,UAAO,SAAS,EAAE;;EAEpB,MAAM,EAAE,YAAY,4BAA4B;AAUhD,OAAK,MAAM,UAAU,eAAe;GAElC,IAAI,cAAc,OAAO,cAAc;AACvC,OAAI,eAAe,KACjB,eAAc,EAAE;GAYlB,MAAM,sBARqB,uBACzB,yBACI,GAAG,uBAAuB,OAAO,aAAa,GAC9C,OAAO,cACX,qBACA,qBACA,mBAE4C,CAAC;AAG/C,OAAI,OAAO,4BAA4B,UAAU;AAC/C,QAAI,OAAO,gBAAgB,SACzB,oBAAmB,uBAAuB;AAG5C;;GAKF,MAAM,mBAAmB,gBAAgB,wBAAwB;AACjE,kBAAe,kBAAkB,oBAAoB,eAAe;GAGpE,MAAM,cAAyC;IAC7C,GAAI,oBAAoB,YACnB,0BACD,EAAE;IACN,GAAI;IACL;AAGD,OAAI,OAAO,KAAK,YAAY,CAAC,WAAW,GAAG;AACzC,WAAO,KACL,+BAA+B,OAAO,aAAa,8CAA8C,oBAAoB,wBACtH;AACD;;AAIF,QAAK,MAAM,CACT,0BACA,oBACG,OAAO,QAAQ,eAAe,EAAE,CAAC,CACpC,KAAI;AAKF,QAAI,CAJU,oBACZ,kBACA,yBAEQ,CAAE;AACZ,wBACE,kBACA,0BACA,gBACD;WACK;AAKV,wBACE,kBACA,oBAAoB,WACpB,OAAO,cACP,cACD;AAGD,sBAAmB,uBAAuB;;AAE5C,sBAAoB,YAAY,qBAAqB,kBAAkB;;AAG3E,KAAI,WAAW,uBAAuB,WAAW,UAC/C,6BACE,YACA,WAAW,qBACX,WAAW,UACZ;AAGH,QAAO,CAAC,KAAK,UAAU,YAAY,MAAM,EAAE,CAAC;;AAG9C,SAAS,kBACP,OACA,qBACA,aACA,qBACA,eACa;CACb,MAAM,WAAW,oBAAoB;AACrC,KAAI,CAAC,YAAY,CAAC,oBAAoB,IACpC,QAAO;CAGT,MAAM,kBAAkB,MAAM,KAAK,SAAS;EAC1C,IAAI;AACJ,MAAI;GAEF,MAAM,QADS,kBAAkB,MAAM,oBAAoB,IACvC,GAAG;AACvB,OAAI,OAAO,UAAU,SACnB,eAAc;UAEV;AAGR,SAAO;GAAE;GAAM;GAAa;GAC5B;AAEF,KAAI,aAAa,WAAW;AAC1B,MAAI,CAAC,YAAY,OACf,QAAO;EAOT,MAAM,oBAAoB,CAHxB,eACA,GAAG,YAAY,QAAQ,WAAW,WAAW,cAAc,CAElB,CAAC,KAAK,WAC/C,6BACE,QACA,qBACA,oBACD,CACF;EAED,MAAM,eAA4B,EAAE;EACpC,MAAM,iBAAiB,CAAC,GAAG,gBAAgB;AAE3C,OAAK,MAAM,eAAe,kBACxB,MAAK,IAAI,IAAI,GAAG,IAAI,eAAe,SAAU;GAC3C,MAAM,QAAQ,eAAe;AAC7B,OAAI,MAAM,gBAAgB,aAAa;AACrC,iBAAa,KAAK,MAAM,KAAK;AAC7B,mBAAe,OAAO,GAAG,EAAE;AAC3B;;AAEF,QAAK;;AAIT,iBAAe,SAAS,UAAU,aAAa,KAAK,MAAM,KAAK,CAAC;AAEhE,SAAO;;AAGT,KAAI,aAAa,uBAAuB;EACtC,MAAM,qBAAqB,6BACzB,eACA,qBACA,oBACD;EAED,MAAM,eAAuC,EAAE;EAC/C,MAAM,gBAAwC,EAAE;EAChD,MAAM,iBAAyC,EAAE;AAEjD,OAAK,MAAM,SAAS,iBAAiB;AACnC,OAAI,MAAM,gBAAgB,oBAAoB;AAC5C,iBAAa,KAAK,MAAM;AACxB;;AAEF,OAAI,MAAM,aAAa;AACrB,kBAAc,KAAK,MAAM;AACzB;;AAEF,kBAAe,KAAK,MAAM;;AAG5B,gBAAc,MAAM,GAAG,MAAM;AAC3B,OAAI,CAAC,EAAE,eAAe,CAAC,EAAE,YACvB,QAAO;AAET,UAAO,EAAE,YAAY,cAAc,EAAE,YAAY;IACjD;AAEF,SAAO;GAAC,GAAG;GAAc,GAAG;GAAe,GAAG;GAAe,CAAC,KAC3D,UAAU,MAAM,KAClB;;AAGH,QAAO;;;;;;AAOT,SAAS,eACP,MACA,YACM;AACN,KAAI,CAAC,YAAY,OAAQ;AACzB,KAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,KAAK,CAAE;AAC9D,MAAK,MAAM,YAAY,WACrB,QAAQ,KAAoB;;;;;;;;;AAWhC,SAAgB,qBACd,YACA,WACA,cACA,eACM;AACN,KAAI,CAAC,UAAW;CAEhB,MAAM,yBAAyB,oBAAoB,aAAa;CAChE,MAAM,0BAA0B,oBAAoB,cAAc;AAElE,MAAK,MAAM,CAAC,eAAe,qBAAqB,OAAO,QAAQ,UAAU,EAAE;AACzE,MACE,CAAC,iBAAiB,WAClB,OAAO,iBAAiB,YAAY,SAEpC;EAEF,MAAM,UAAU,mBAAmB,YAAY,cAAc;AAC7D,MAAI,CAAC,WAAW,QAAQ,WAAW,EACjC;AAEF,UAAQ,SAAS,WAAW;AAC1B,OAAI,OAAO,OAAO,UAAU,SAC1B;GAGF,IAAI,gBAAgB,iBAAiB;AAGrC,mBAAgB,0BACd,eACA,uBACD;AAED,OACE,iBAAiB,SACjB,OAAO,iBAAiB,UAAU,UAClC;IAEA,IAAI,cAAc,iBAAiB;AACnC,kBAAc,0BACZ,aACA,wBACD;AAED,WAAO,QAAQ,OAAO,MAAM,QAC1B,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;SAED,QAAO,QAAQ;AAIjB,uBAAoB,YAAY,OAAO,SAAS,OAAO,MAAM;IAC7D"}
1
+ {"version":3,"file":"mergeJson.js","names":[],"sources":["../../../src/formats/json/mergeJson.ts"],"sourcesContent":["import { AdditionalOptions, SourceObjectOptions } from '../../types/index.js';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport {\n findMatchingItemArray,\n findMatchingItemObject,\n generateSourceObjectPointers,\n getIdentifyingLocaleProperty,\n getSourceObjectOptionsArray,\n validateJsonSchema,\n} from './utils.js';\nimport {\n getConfiguredLocaleProperties,\n replaceLocalePlaceholders,\n} from '../utils.js';\nimport { gt } from '../../utils/gt.js';\nimport {\n applyStructuralTransforms,\n unapplyStructuralTransforms,\n} from './transformJson.js';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches, getJSONPathValues } from './jsonPath.js';\nimport { getJSONPointerValue, setJSONPointerValue } from './jsonPointer.js';\n\ntype ParsedTarget = {\n translatedContent: string;\n targetLocale: string;\n parsedContent: JSONObject;\n};\n\nexport function mergeJson(\n originalContent: string,\n inputPath: string,\n options: AdditionalOptions,\n targets: {\n translatedContent: string;\n targetLocale: string;\n }[],\n defaultLocale: string,\n localeOrder: string[] = []\n): string[] {\n const jsonSchema = validateJsonSchema(options, inputPath);\n if (!jsonSchema) {\n return targets.map((target) => target.translatedContent);\n }\n\n let originalJson: JSONValue;\n try {\n originalJson = JSON.parse(originalContent);\n } catch {\n logger.error(`Invalid JSON file: ${inputPath}`);\n return exitSync(1);\n }\n\n const useCanonicalLocaleKeys =\n options?.experimentalCanonicalLocaleKeys ?? false;\n const canonicalDefaultLocale = useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(defaultLocale)\n : defaultLocale;\n const canonicalLocaleOrder = useCanonicalLocaleKeys\n ? localeOrder.map((locale) => gt.resolveCanonicalLocale(locale))\n : localeOrder;\n\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n applyStructuralTransforms(\n originalJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n // Handle include\n if (jsonSchema.include) {\n const output: string[] = [];\n for (const target of targets) {\n // Must clone the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n const translatedJson = JSON.parse(target.translatedContent) as JSONObject;\n for (const [jsonPointer, translatedValue] of Object.entries(\n translatedJson\n )) {\n try {\n const value = getJSONPointerValue(mergedJson, jsonPointer);\n if (!value) continue;\n setJSONPointerValue(mergedJson, jsonPointer, translatedValue);\n } catch {\n /* empty */\n }\n }\n output.push(JSON.stringify(mergedJson, null, 2));\n }\n return output;\n }\n\n if (!jsonSchema.composite) {\n logger.error('No composite property found in JSON schema');\n return exitSync(1);\n }\n\n // Handle composite\n // Create a deep copy of the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n\n // Pre-parse all target contents ONCE (avoid re-parsing per pointer)\n const parsedTargets = targets.map((target) => ({\n ...target,\n parsedContent: JSON.parse(target.translatedContent) as JSONObject,\n })) satisfies ParsedTarget[];\n\n // Create mapping of sourceObjectPointer to SourceObjectOptions\n const sourceObjectPointers = generateSourceObjectPointers(\n jsonSchema.composite,\n originalJson\n );\n\n // Find the source object\n for (const [\n sourceObjectPointer,\n { sourceObjectValue, sourceObjectOptions },\n ] of Object.entries(sourceObjectPointers)) {\n // Find the source item\n if (sourceObjectOptions.type === 'array') {\n // Validate type\n if (!Array.isArray(sourceObjectValue)) {\n logger.error(\n `Source object value is not an array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n\n // Get source item for default locale\n const matchingDefaultLocaleItems = findMatchingItemArray(\n canonicalDefaultLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n if (!Object.keys(matchingDefaultLocaleItems).length) {\n logger.warn(\n `Matching sourceItems not found at path: ${sourceObjectPointer}. Check that your JSON file includes the key field. Skipping this target`\n );\n continue;\n }\n\n const matchingDefaultLocaleItemKeys = new Set(\n Object.keys(matchingDefaultLocaleItems)\n );\n\n // For each target:\n // 1. Get the target items\n // 2. Track all array indecies to remove (will be overwritten)\n // 3. Merge matchingDefaultLocaleItems and targetItems\n // 4. Validate that the mergedItems is not empty\n // For each target item:\n // 5. Validate that all the array indecies are still present in the source json\n // 6. Override the source item with the translated values\n // 7. Apply additional mutations to the sourceItem\n // 8. Track all items to add\n // 9. Check that items to add is >= items to remove\n // 10. Remove all items for the target locale (they can be identified by the key)\n const indiciesToRemove = new Set<number>();\n const itemsToAdd: JSONValue[] = [];\n for (const target of parsedTargets) {\n let targetItems = target.parsedContent[sourceObjectPointer];\n // 1. Get the target items\n if (!targetItems) {\n // If no translation can be found, a transformation may need to happen still\n targetItems = {};\n }\n\n // 2. Track all array indecies to remove (will be overwritten)\n const targetItemsToRemove = findMatchingItemArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n Object.values(targetItemsToRemove).forEach(({ index }) =>\n indiciesToRemove.add(index)\n );\n\n // Remap mismatched positional keys to current source positions\n const sourceKeys = [...matchingDefaultLocaleItemKeys];\n const remappedTargetItems: Record<string, JSONValue> = {};\n for (const [key, value] of Object.entries(targetItems as JSONObject)) {\n if (matchingDefaultLocaleItemKeys.has(key)) {\n remappedTargetItems[key] = value;\n } else if (\n sourceKeys.length === 1 &&\n !(sourceKeys[0] in remappedTargetItems)\n ) {\n remappedTargetItems[sourceKeys[0]] = value;\n } else {\n logger.warn(\n `Skipping translated item at ${key}: cannot map to source item at path ${sourceObjectPointer}`\n );\n }\n }\n\n // Merge matchingDefaultLocaleItems and remapped targetItems\n const mergedItems = {\n ...(sourceObjectOptions.transform ? matchingDefaultLocaleItems : {}),\n ...remappedTargetItems,\n };\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n for (const [sourceItemPointer, targetItem] of Object.entries(\n mergedItems\n )) {\n // 5. Validate that all the array indecies are still present in the source json\n if (!matchingDefaultLocaleItemKeys.has(sourceItemPointer)) {\n logger.warn(\n `Skipping translated item at ${sourceItemPointer}: not present in source json at path ${sourceObjectPointer}`\n );\n continue;\n }\n\n // 6. Override the source item with the translated values\n const defaultLocaleSourceItem =\n matchingDefaultLocaleItems[sourceItemPointer].sourceItem;\n const defaultLocaleKeyPointer =\n matchingDefaultLocaleItems[sourceItemPointer].keyPointer;\n const mutatedSourceItem = structuredClone(defaultLocaleSourceItem);\n const { identifyingLocaleProperty: targetLocaleKeyProperty } =\n getSourceObjectOptionsArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n setJSONPointerValue(\n mutatedSourceItem,\n defaultLocaleKeyPointer,\n targetLocaleKeyProperty\n );\n omitProperties(mutatedSourceItem, sourceObjectOptions.omitProperties);\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries((targetItem || {}) as JSONObject)) {\n const valueToSet =\n useCanonicalLocaleKeys &&\n defaultLocaleKeyPointer &&\n translatedKeyJsonPointer === defaultLocaleKeyPointer\n ? targetLocaleKeyProperty\n : translatedValue;\n try {\n const value = getJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer,\n valueToSet\n );\n } catch {\n /* empty */\n }\n }\n\n // 7. Apply additional mutations to the sourceItem\n applyTransformations(\n mutatedSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n itemsToAdd.push(mutatedSourceItem);\n }\n }\n\n // 8. Check that items to add is >= items to remove\n if (itemsToAdd.length < indiciesToRemove.size) {\n logger.warn(\n `Items to add (${itemsToAdd.length}) is less than items to remove (${indiciesToRemove.size}) at path: ${sourceObjectPointer}. Some translated items may have been skipped.`\n );\n }\n\n // 9. Remove all items for the target locale (they can be identified by the key)\n const filteredSourceObjectValue = sourceObjectValue.filter(\n (_, index: number) => !indiciesToRemove.has(index)\n );\n\n // 10. Add all items to the original JSON\n filteredSourceObjectValue.push(...itemsToAdd);\n\n setJSONPointerValue(\n mergedJson,\n sourceObjectPointer,\n sortByLocaleOrder(\n filteredSourceObjectValue,\n sourceObjectOptions,\n canonicalLocaleOrder,\n sourceObjectPointer,\n canonicalDefaultLocale\n )\n );\n } else {\n // Validate type\n if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {\n logger.error(\n `Source object value is not an object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n const sourceObjectRecord = sourceObjectValue as JSONObject;\n // Validate localeProperty\n const matchingDefaultLocaleItem = findMatchingItemObject(\n canonicalDefaultLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n // Validate source item exists\n if (!matchingDefaultLocaleItem.sourceItem) {\n logger.error(\n `Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`\n );\n return exitSync(1);\n }\n const { sourceItem: defaultLocaleSourceItem } = matchingDefaultLocaleItem;\n\n // For each target:\n // 1. Get the target items\n // 2. Find the source item for the target locale\n // 3. Merge the target items with the source item\n // 4. Validate that the mergedItems is not empty\n // 5. Override the source item with the translated values\n // 6. Apply additional mutations to the sourceItem\n // 7. Merge the source item with the original JSON (if the source item is not a new item)\n for (const target of parsedTargets) {\n // 1. Get the target items\n let targetItems = target.parsedContent[sourceObjectPointer];\n if (targetItems == null) {\n targetItems = {};\n }\n\n // 2. Find the source item for the target locale\n const matchingTargetItem = findMatchingItemObject(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n const mutateSourceItemKey = matchingTargetItem.keyParentProperty;\n\n // If the source item is a string, use the translated string directly\n if (typeof defaultLocaleSourceItem === 'string') {\n if (typeof targetItems === 'string') {\n sourceObjectRecord[mutateSourceItemKey] = targetItems;\n }\n // If no translation found, leave the locale slot unchanged\n continue;\n }\n\n // If the target locale has a matching source item, use it to mutate the source item\n // Otherwise, fallback to the default locale source item\n const mutateSourceItem = structuredClone(defaultLocaleSourceItem);\n omitProperties(mutateSourceItem, sourceObjectOptions.omitProperties);\n\n // 3. Merge the target items with the source item (if there are transformations to perform)\n const mergedItems: Record<string, JSONValue> = {\n ...(sourceObjectOptions.transform\n ? (defaultLocaleSourceItem as JSONObject)\n : {}),\n ...(targetItems as JSONObject),\n };\n\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n // 5. Override the source item with the translated values\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries(mergedItems || {})) {\n try {\n const value = getJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer,\n translatedValue\n );\n } catch {\n /* empty */\n }\n }\n // 6. Apply additional mutations to the sourceItem\n applyTransformations(\n mutateSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n // 7. Merge the source item with the original JSON\n sourceObjectRecord[mutateSourceItemKey] = mutateSourceItem;\n }\n setJSONPointerValue(mergedJson, sourceObjectPointer, sourceObjectValue);\n }\n }\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n unapplyStructuralTransforms(\n mergedJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n return [JSON.stringify(mergedJson, null, 2)];\n}\n\nfunction sortByLocaleOrder(\n items: JSONValue[],\n sourceObjectOptions: SourceObjectOptions,\n localeOrder: string[],\n sourceObjectPointer: string,\n defaultLocale: string\n): JSONValue[] {\n const sortMode = sourceObjectOptions.experimentalSort;\n if (!sortMode || !sourceObjectOptions.key) {\n return items;\n }\n\n const itemsWithLocale = items.map((item) => {\n let localeValue: string | undefined;\n try {\n const values = getJSONPathValues(item, sourceObjectOptions.key as string);\n const value = values?.[0];\n if (typeof value === 'string') {\n localeValue = value;\n }\n } catch {\n /* empty */\n }\n return { item, localeValue };\n });\n\n if (sortMode === 'locales') {\n if (!localeOrder.length) {\n return items;\n }\n\n const orderedLocaleList = [\n defaultLocale,\n ...localeOrder.filter((locale) => locale !== defaultLocale),\n ];\n const localeOrderValues = orderedLocaleList.map((locale) =>\n getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n )\n );\n\n const orderedItems: JSONValue[] = [];\n const remainingItems = [...itemsWithLocale];\n\n for (const localeValue of localeOrderValues) {\n for (let i = 0; i < remainingItems.length; ) {\n const entry = remainingItems[i];\n if (entry.localeValue === localeValue) {\n orderedItems.push(entry.item);\n remainingItems.splice(i, 1);\n continue;\n }\n i += 1;\n }\n }\n\n remainingItems.forEach((entry) => orderedItems.push(entry.item));\n\n return orderedItems;\n }\n\n if (sortMode === 'localesAlphabetical') {\n const defaultLocaleValue = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n const defaultItems: typeof itemsWithLocale = [];\n const sortableItems: typeof itemsWithLocale = [];\n const remainingItems: typeof itemsWithLocale = [];\n\n for (const entry of itemsWithLocale) {\n if (entry.localeValue === defaultLocaleValue) {\n defaultItems.push(entry);\n continue;\n }\n if (entry.localeValue) {\n sortableItems.push(entry);\n continue;\n }\n remainingItems.push(entry);\n }\n\n sortableItems.sort((a, b) => {\n if (!a.localeValue || !b.localeValue) {\n return 0;\n }\n return a.localeValue.localeCompare(b.localeValue);\n });\n\n return [...defaultItems, ...sortableItems, ...remainingItems].map(\n (entry) => entry.item\n );\n }\n\n return items;\n}\n\n/**\n * Remove top-level properties from a generated non-default-locale entry\n * (e.g. Mintlify's `default: true` flag, which is only valid on one entry)\n */\nfunction omitProperties(\n item: JSONValue,\n properties: string[] | undefined\n): void {\n if (!properties?.length) return;\n if (!item || typeof item !== 'object' || Array.isArray(item)) return;\n for (const property of properties) {\n delete (item as JSONObject)[property];\n }\n}\n\n/**\n * Apply transformations to the sourceItem in-place\n * @param sourceItem - The source item to apply transformations to\n * @param transform - The transformations to apply\n * @param targetLocale - The target locale\n * @param defaultLocale - The default locale\n */\nexport function applyTransformations(\n sourceItem: JSONValue,\n transform: SourceObjectOptions['transform'],\n targetLocale: string,\n defaultLocale: string\n): void {\n if (!transform) return;\n\n const targetLocaleProperties = getConfiguredLocaleProperties(targetLocale);\n const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);\n\n for (const [transformPath, transformOptions] of Object.entries(transform)) {\n if (\n !transformOptions.replace ||\n typeof transformOptions.replace !== 'string'\n ) {\n continue;\n }\n const results = getJSONPathMatches(sourceItem, transformPath);\n if (!results || results.length === 0) {\n continue;\n }\n results.forEach((result) => {\n if (typeof result.value !== 'string') {\n return;\n }\n // Replace locale placeholders in the replace string\n let replaceString = transformOptions.replace;\n\n // Replace all locale property placeholders\n replaceString = replaceLocalePlaceholders(\n replaceString,\n targetLocaleProperties\n );\n\n if (\n transformOptions.match &&\n typeof transformOptions.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformOptions.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n result.value = result.value.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n result.value = replaceString;\n }\n\n // Update the actual sourceItem using JSONPointer\n setJSONPointerValue(sourceItem, result.pointer, result.value);\n });\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { logger } from "../../console/logger.js";
2
2
  import { exitSync } from "../../console/logging.js";
3
+ import { getConfiguredLocaleProperties } from "../utils.js";
3
4
  import { getJSONPathMatches } from "./jsonPath.js";
4
5
  import { flattenJson } from "./flattenJson.js";
5
6
  import chalk from "chalk";
6
7
  import path from "node:path";
7
8
  import micromatch from "micromatch";
8
- import { getLocaleProperties } from "@generaltranslation/format";
9
9
  //#region src/formats/json/utils.ts
10
10
  const { isMatch } = micromatch;
11
11
  function findMatchingItemArray(locale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue) {
@@ -55,7 +55,7 @@ function findMatchingItemObject(locale, sourceObjectPointer, sourceObjectOptions
55
55
  */
56
56
  function getIdentifyingLocaleProperty(locale, sourceObjectPointer, sourceObjectOptions) {
57
57
  const localeProperty = sourceObjectOptions.localeProperty || "code";
58
- const identifyingLocaleProperty = getLocaleProperties(locale)[localeProperty];
58
+ const identifyingLocaleProperty = getConfiguredLocaleProperties(locale)[localeProperty];
59
59
  if (!identifyingLocaleProperty) {
60
60
  logger.error(`Source object options localeProperty is not a valid locale property at path: ${sourceObjectPointer}`);
61
61
  return exitSync(1);
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../../src/formats/json/utils.ts"],"sourcesContent":["import { getLocaleProperties } from '@generaltranslation/format';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport type { LocaleProperties } from '@generaltranslation/format/types';\nimport {\n AdditionalOptions,\n JsonSchema,\n SourceObjectOptions,\n} from '../../types/index.js';\nimport { flattenJson } from './flattenJson.js';\nimport chalk from 'chalk';\nimport path from 'node:path';\nimport micromatch from 'micromatch';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches } from './jsonPath.js';\nconst { isMatch } = micromatch;\n\ntype MatchingArrayItem = {\n sourceItem: JSONValue;\n keyParentProperty: string | number;\n keyPointer: string;\n index: number;\n};\n\ntype SourceObjectPointerMap = Record<\n string,\n { sourceObjectValue: JSONValue; sourceObjectOptions: SourceObjectOptions }\n>;\n\n// Find the matching source item in an array\n// where the key matches the identifying locale property\n// If no matching item is found, exit with an error\nexport function findMatchingItemArray(\n locale: string,\n sourceObjectOptions: SourceObjectOptions,\n sourceObjectPointer: string,\n sourceObjectValue: JSONValue[]\n): Record<string, MatchingArrayItem> {\n const { identifyingLocaleProperty, localeKeyJsonPath } =\n getSourceObjectOptionsArray(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n // Use the json pointer key to locate the source item\n const matchingItems: Record<string, MatchingArrayItem> = {};\n for (const [index, item] of sourceObjectValue.entries()) {\n // Get the key candidates\n const keyCandidates = getJSONPathMatches(item, localeKeyJsonPath);\n if (!keyCandidates) {\n logger.error(\n `Source item at path: ${sourceObjectPointer} does not have a key value at path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n } else if (keyCandidates.length === 0) {\n // If no key candidates, skip the item\n continue;\n } else if (keyCandidates.length > 1) {\n // If multiple key candidates, exit with an error\n logger.error(\n `Source item at path: ${sourceObjectPointer} has multiple matching keys with path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n } else if (identifyingLocaleProperty !== keyCandidates[0].value) {\n // Validate the key is the identifying locale property\n continue;\n }\n const keyParentProperty = keyCandidates[0].parentProperty;\n if (keyParentProperty === null) {\n logger.error(\n `Source item at path: ${sourceObjectPointer} has a root-level key match with path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n }\n // Map the index to the source item\n matchingItems[`/${index}`] = {\n sourceItem: item,\n keyParentProperty,\n keyPointer: keyCandidates[0].pointer,\n index,\n };\n }\n return matchingItems;\n}\n\nexport function findMatchingItemObject(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions,\n sourceObjectValue: JSONObject\n): { sourceItem: JSONValue | undefined; keyParentProperty: string } {\n const { identifyingLocaleProperty } = getSourceObjectOptionsObject(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n // Locate the source item\n if (sourceObjectValue[identifyingLocaleProperty]) {\n return {\n sourceItem: sourceObjectValue[identifyingLocaleProperty],\n keyParentProperty: identifyingLocaleProperty,\n };\n }\n return {\n sourceItem: undefined,\n keyParentProperty: identifyingLocaleProperty,\n };\n}\n\n/**\n * Get the identifying locale property for an object\n * @param locale - The locale to get the identifying locale property for\n * @param sourceObjectPointer - The path to the source object\n * @param sourceObjectOptions - The source object options\n * @returns The identifying locale property\n */\nexport function getIdentifyingLocaleProperty(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): string {\n // Validate localeProperty\n const localeProperty = sourceObjectOptions.localeProperty || 'code';\n const identifyingLocaleProperty =\n getLocaleProperties(locale)[localeProperty as keyof LocaleProperties];\n if (!identifyingLocaleProperty) {\n logger.error(\n `Source object options localeProperty is not a valid locale property at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return identifyingLocaleProperty;\n}\n\n/**\n * Get the identifying locale property and the json path to the key for an array\n * @param locale - The locale to get the identifying locale property for\n * @param sourceObjectPointer - The path to the source object\n * @param sourceObjectOptions - The source object options\n * @returns The identifying locale property and the json path to the key\n */\nexport function getSourceObjectOptionsArray(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): { identifyingLocaleProperty: string; localeKeyJsonPath: string } {\n const identifyingLocaleProperty = getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n const localeKeyJsonPath = sourceObjectOptions.key;\n if (!localeKeyJsonPath) {\n logger.error(\n `Source object options key is required for array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return { identifyingLocaleProperty, localeKeyJsonPath };\n}\n\nexport function getSourceObjectOptionsObject(\n defaultLocale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): { identifyingLocaleProperty: string } {\n const identifyingLocaleProperty = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n const jsonPathKey = sourceObjectOptions.key;\n if (jsonPathKey) {\n logger.error(\n `Source object options key is not allowed for object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return { identifyingLocaleProperty };\n}\n\n/**\n * Generate a mapping of sourceObjectPointer to SourceObjectOptions\n * where the sourceObjectPointer is a jsonpointer to the array or object containing\n * @param jsonSchema - The json schema to generate the mapping from\n * @param originalJson - The original json to generate the mapping from\n * @returns A mapping of sourceObjectPointer to SourceObjectOptions\n */\nexport function generateSourceObjectPointers(\n jsonSchema: {\n [sourceObjectPath: string]: SourceObjectOptions;\n },\n originalJson: JSONValue\n): SourceObjectPointerMap {\n const sourceObjectPointers = Object.entries(jsonSchema).reduce(\n (acc: SourceObjectPointerMap, [sourceObjectPath, sourceObjectOptions]) => {\n const sourceObjects = flattenJson(originalJson, [sourceObjectPath]);\n Object.entries(sourceObjects).forEach(([pointer, value]) => {\n acc[pointer as string] = {\n sourceObjectValue: value,\n sourceObjectOptions,\n };\n });\n return acc;\n },\n {}\n );\n return sourceObjectPointers;\n}\n\n/**\n * Validate the json schema for composite or include schemas\n * @param options - Additional options containing jsonSchema config\n * @param filePath - The path to the file (used for matching jsonSchema)\n * @returns The json schema, or null if no schema is found\n * @returns exitSync(1) if the json schema is invalid\n */\nexport function validateJsonSchema(\n options: AdditionalOptions,\n filePath: string\n): JsonSchema | null {\n if (!options.jsonSchema) {\n return null;\n }\n\n const fileGlobs = Object.keys(options.jsonSchema);\n const matchingGlob = fileGlobs.find((fileGlob) =>\n isMatch(path.relative(process.cwd(), filePath), fileGlob)\n );\n if (!matchingGlob || !options.jsonSchema[matchingGlob]) {\n return null;\n }\n // Validate includes or composite\n const jsonSchema = options.jsonSchema[matchingGlob];\n if (jsonSchema.include && jsonSchema.composite) {\n logger.error(\n 'include and composite cannot be used together in the same JSON schema'\n );\n return exitSync(1);\n }\n\n if (!jsonSchema.include && !jsonSchema.composite) {\n logger.error('No include or composite property found in JSON schema');\n return exitSync(1);\n }\n\n if (jsonSchema.structuralTransform && !jsonSchema.composite) {\n logger.error(\n 'structuralTransform requires composite to be defined in the JSON schema'\n );\n return exitSync(1);\n }\n return jsonSchema;\n}\n\nconst UNSUPPORTED_MINTLIFY_FIELDS = ['$ref'];\n\n/**\n * Recursively traverse a JSON value and collect all objects whose key\n * matches one of the unsupported field names.\n */\nfunction findMintlifyUnsupportedFields(\n value: JSONValue,\n fieldNames: string[],\n pointer: string = ''\n): { pointer: string; field: string; fieldValue: string }[] {\n if (value === null || typeof value !== 'object') return [];\n if (Array.isArray(value)) {\n const results: { pointer: string; field: string; fieldValue: string }[] =\n [];\n for (let i = 0; i < value.length; i++) {\n results.push(\n ...findMintlifyUnsupportedFields(\n value[i],\n fieldNames,\n `${pointer}/${i}`\n )\n );\n }\n return results;\n }\n // Check if this object contains an unsupported field\n const objectValue = value as JSONObject;\n for (const field of fieldNames) {\n if (typeof objectValue[field] === 'string') {\n return [{ pointer, field, fieldValue: objectValue[field] }];\n }\n }\n // Recurse into child properties\n const results: { pointer: string; field: string; fieldValue: string }[] = [];\n for (const key of Object.keys(objectValue)) {\n results.push(\n ...findMintlifyUnsupportedFields(\n objectValue[key],\n fieldNames,\n `${pointer}/${key}`\n )\n );\n }\n return results;\n}\n\n/**\n * Detect unsupported fields (e.g. $ref) in Mintlify docs.json files.\n * Logs a warning listing the fields found.\n */\nexport function detectMintlifyUnsupportedFields(\n json: JSONValue,\n filePath: string\n): void {\n const unsupported = findMintlifyUnsupportedFields(\n json,\n UNSUPPORTED_MINTLIFY_FIELDS\n );\n\n if (unsupported.length > 0) {\n const fileName = path.basename(filePath);\n const lines = unsupported\n .map(\n (u) =>\n chalk.yellow('• ') +\n chalk.white(\n `${u.pointer.replace(/\\//g, '.').replace(/^\\./, '')}.${u.field}`\n )\n )\n .join('\\n');\n logger.warn(\n chalk.yellow(\n `Mintlify config splitting is not yet supported. The following \\`$ref\\` fields were detected in \\`${fileName}\\` and will not be resolved:\\n`\n ) + lines\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../src/formats/json/utils.ts"],"sourcesContent":["import { getConfiguredLocaleProperties } from '../utils.js';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport type { LocaleProperties } from '@generaltranslation/format/types';\nimport {\n AdditionalOptions,\n JsonSchema,\n SourceObjectOptions,\n} from '../../types/index.js';\nimport { flattenJson } from './flattenJson.js';\nimport chalk from 'chalk';\nimport path from 'node:path';\nimport micromatch from 'micromatch';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches } from './jsonPath.js';\nconst { isMatch } = micromatch;\n\ntype MatchingArrayItem = {\n sourceItem: JSONValue;\n keyParentProperty: string | number;\n keyPointer: string;\n index: number;\n};\n\ntype SourceObjectPointerMap = Record<\n string,\n { sourceObjectValue: JSONValue; sourceObjectOptions: SourceObjectOptions }\n>;\n\n// Find the matching source item in an array\n// where the key matches the identifying locale property\n// If no matching item is found, exit with an error\nexport function findMatchingItemArray(\n locale: string,\n sourceObjectOptions: SourceObjectOptions,\n sourceObjectPointer: string,\n sourceObjectValue: JSONValue[]\n): Record<string, MatchingArrayItem> {\n const { identifyingLocaleProperty, localeKeyJsonPath } =\n getSourceObjectOptionsArray(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n // Use the json pointer key to locate the source item\n const matchingItems: Record<string, MatchingArrayItem> = {};\n for (const [index, item] of sourceObjectValue.entries()) {\n // Get the key candidates\n const keyCandidates = getJSONPathMatches(item, localeKeyJsonPath);\n if (!keyCandidates) {\n logger.error(\n `Source item at path: ${sourceObjectPointer} does not have a key value at path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n } else if (keyCandidates.length === 0) {\n // If no key candidates, skip the item\n continue;\n } else if (keyCandidates.length > 1) {\n // If multiple key candidates, exit with an error\n logger.error(\n `Source item at path: ${sourceObjectPointer} has multiple matching keys with path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n } else if (identifyingLocaleProperty !== keyCandidates[0].value) {\n // Validate the key is the identifying locale property\n continue;\n }\n const keyParentProperty = keyCandidates[0].parentProperty;\n if (keyParentProperty === null) {\n logger.error(\n `Source item at path: ${sourceObjectPointer} has a root-level key match with path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n }\n // Map the index to the source item\n matchingItems[`/${index}`] = {\n sourceItem: item,\n keyParentProperty,\n keyPointer: keyCandidates[0].pointer,\n index,\n };\n }\n return matchingItems;\n}\n\nexport function findMatchingItemObject(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions,\n sourceObjectValue: JSONObject\n): { sourceItem: JSONValue | undefined; keyParentProperty: string } {\n const { identifyingLocaleProperty } = getSourceObjectOptionsObject(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n // Locate the source item\n if (sourceObjectValue[identifyingLocaleProperty]) {\n return {\n sourceItem: sourceObjectValue[identifyingLocaleProperty],\n keyParentProperty: identifyingLocaleProperty,\n };\n }\n return {\n sourceItem: undefined,\n keyParentProperty: identifyingLocaleProperty,\n };\n}\n\n/**\n * Get the identifying locale property for an object\n * @param locale - The locale to get the identifying locale property for\n * @param sourceObjectPointer - The path to the source object\n * @param sourceObjectOptions - The source object options\n * @returns The identifying locale property\n */\nexport function getIdentifyingLocaleProperty(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): string {\n // Validate localeProperty\n const localeProperty = sourceObjectOptions.localeProperty || 'code';\n const identifyingLocaleProperty =\n getConfiguredLocaleProperties(locale)[\n localeProperty as keyof LocaleProperties\n ];\n if (!identifyingLocaleProperty) {\n logger.error(\n `Source object options localeProperty is not a valid locale property at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return identifyingLocaleProperty;\n}\n\n/**\n * Get the identifying locale property and the json path to the key for an array\n * @param locale - The locale to get the identifying locale property for\n * @param sourceObjectPointer - The path to the source object\n * @param sourceObjectOptions - The source object options\n * @returns The identifying locale property and the json path to the key\n */\nexport function getSourceObjectOptionsArray(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): { identifyingLocaleProperty: string; localeKeyJsonPath: string } {\n const identifyingLocaleProperty = getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n const localeKeyJsonPath = sourceObjectOptions.key;\n if (!localeKeyJsonPath) {\n logger.error(\n `Source object options key is required for array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return { identifyingLocaleProperty, localeKeyJsonPath };\n}\n\nexport function getSourceObjectOptionsObject(\n defaultLocale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): { identifyingLocaleProperty: string } {\n const identifyingLocaleProperty = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n const jsonPathKey = sourceObjectOptions.key;\n if (jsonPathKey) {\n logger.error(\n `Source object options key is not allowed for object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return { identifyingLocaleProperty };\n}\n\n/**\n * Generate a mapping of sourceObjectPointer to SourceObjectOptions\n * where the sourceObjectPointer is a jsonpointer to the array or object containing\n * @param jsonSchema - The json schema to generate the mapping from\n * @param originalJson - The original json to generate the mapping from\n * @returns A mapping of sourceObjectPointer to SourceObjectOptions\n */\nexport function generateSourceObjectPointers(\n jsonSchema: {\n [sourceObjectPath: string]: SourceObjectOptions;\n },\n originalJson: JSONValue\n): SourceObjectPointerMap {\n const sourceObjectPointers = Object.entries(jsonSchema).reduce(\n (acc: SourceObjectPointerMap, [sourceObjectPath, sourceObjectOptions]) => {\n const sourceObjects = flattenJson(originalJson, [sourceObjectPath]);\n Object.entries(sourceObjects).forEach(([pointer, value]) => {\n acc[pointer as string] = {\n sourceObjectValue: value,\n sourceObjectOptions,\n };\n });\n return acc;\n },\n {}\n );\n return sourceObjectPointers;\n}\n\n/**\n * Validate the json schema for composite or include schemas\n * @param options - Additional options containing jsonSchema config\n * @param filePath - The path to the file (used for matching jsonSchema)\n * @returns The json schema, or null if no schema is found\n * @returns exitSync(1) if the json schema is invalid\n */\nexport function validateJsonSchema(\n options: AdditionalOptions,\n filePath: string\n): JsonSchema | null {\n if (!options.jsonSchema) {\n return null;\n }\n\n const fileGlobs = Object.keys(options.jsonSchema);\n const matchingGlob = fileGlobs.find((fileGlob) =>\n isMatch(path.relative(process.cwd(), filePath), fileGlob)\n );\n if (!matchingGlob || !options.jsonSchema[matchingGlob]) {\n return null;\n }\n // Validate includes or composite\n const jsonSchema = options.jsonSchema[matchingGlob];\n if (jsonSchema.include && jsonSchema.composite) {\n logger.error(\n 'include and composite cannot be used together in the same JSON schema'\n );\n return exitSync(1);\n }\n\n if (!jsonSchema.include && !jsonSchema.composite) {\n logger.error('No include or composite property found in JSON schema');\n return exitSync(1);\n }\n\n if (jsonSchema.structuralTransform && !jsonSchema.composite) {\n logger.error(\n 'structuralTransform requires composite to be defined in the JSON schema'\n );\n return exitSync(1);\n }\n return jsonSchema;\n}\n\nconst UNSUPPORTED_MINTLIFY_FIELDS = ['$ref'];\n\n/**\n * Recursively traverse a JSON value and collect all objects whose key\n * matches one of the unsupported field names.\n */\nfunction findMintlifyUnsupportedFields(\n value: JSONValue,\n fieldNames: string[],\n pointer: string = ''\n): { pointer: string; field: string; fieldValue: string }[] {\n if (value === null || typeof value !== 'object') return [];\n if (Array.isArray(value)) {\n const results: { pointer: string; field: string; fieldValue: string }[] =\n [];\n for (let i = 0; i < value.length; i++) {\n results.push(\n ...findMintlifyUnsupportedFields(\n value[i],\n fieldNames,\n `${pointer}/${i}`\n )\n );\n }\n return results;\n }\n // Check if this object contains an unsupported field\n const objectValue = value as JSONObject;\n for (const field of fieldNames) {\n if (typeof objectValue[field] === 'string') {\n return [{ pointer, field, fieldValue: objectValue[field] }];\n }\n }\n // Recurse into child properties\n const results: { pointer: string; field: string; fieldValue: string }[] = [];\n for (const key of Object.keys(objectValue)) {\n results.push(\n ...findMintlifyUnsupportedFields(\n objectValue[key],\n fieldNames,\n `${pointer}/${key}`\n )\n );\n }\n return results;\n}\n\n/**\n * Detect unsupported fields (e.g. $ref) in Mintlify docs.json files.\n * Logs a warning listing the fields found.\n */\nexport function detectMintlifyUnsupportedFields(\n json: JSONValue,\n filePath: string\n): void {\n const unsupported = findMintlifyUnsupportedFields(\n json,\n UNSUPPORTED_MINTLIFY_FIELDS\n );\n\n if (unsupported.length > 0) {\n const fileName = path.basename(filePath);\n const lines = unsupported\n .map(\n (u) =>\n chalk.yellow('• ') +\n chalk.white(\n `${u.pointer.replace(/\\//g, '.').replace(/^\\./, '')}.${u.field}`\n )\n )\n .join('\\n');\n logger.warn(\n chalk.yellow(\n `Mintlify config splitting is not yet supported. The following \\`$ref\\` fields were detected in \\`${fileName}\\` and will not be resolved:\\n`\n ) + lines\n );\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,25 @@
1
1
  import type { LocaleProperties } from '@generaltranslation/format/types';
2
+ /**
3
+ * Locale properties for a locale as it is written in the user's `locales` config.
4
+ *
5
+ * `getLocaleProperties(locale).code` returns the *canonical* BCP-47 form of a
6
+ * tag, so "fr-ca" becomes "fr-CA" and "ja-jp" becomes "ja-JP". That is correct
7
+ * when talking to the API, but it is wrong for anything that names a file, a
8
+ * directory, a URL segment, or a locale key on disk: those must use the locale
9
+ * exactly as the user configured it, because that is the spelling every other
10
+ * part of the CLI uses. `resolveLocaleFiles` substitutes `[locale]` verbatim,
11
+ * the string form of `transform` substitutes `[locale]` verbatim, and
12
+ * `localizeStaticUrls` splices the raw locale into URL paths. If placeholder
13
+ * substitution canonicalizes while those do not, a project that configures a
14
+ * non-canonical tag gets content written to one directory and links pointing
15
+ * at another.
16
+ *
17
+ * Locales that are already canonical (the common case) are unaffected: for
18
+ * those, `code` and the configured string are identical.
19
+ *
20
+ * Callers that genuinely want the canonical tag should use
21
+ * `gt.resolveCanonicalLocale`, or one of the explicitly-named properties such
22
+ * as `{minimizedCode}` / `{maximizedCode}` / `{regionCode}`.
23
+ */
24
+ export declare function getConfiguredLocaleProperties(locale: string): LocaleProperties;
2
25
  export declare function replaceLocalePlaceholders(string: string, localeProperties: LocaleProperties): string;
@@ -1,4 +1,33 @@
1
+ import { getLocaleProperties } from "@generaltranslation/format";
1
2
  //#region src/formats/utils.ts
3
+ /**
4
+ * Locale properties for a locale as it is written in the user's `locales` config.
5
+ *
6
+ * `getLocaleProperties(locale).code` returns the *canonical* BCP-47 form of a
7
+ * tag, so "fr-ca" becomes "fr-CA" and "ja-jp" becomes "ja-JP". That is correct
8
+ * when talking to the API, but it is wrong for anything that names a file, a
9
+ * directory, a URL segment, or a locale key on disk: those must use the locale
10
+ * exactly as the user configured it, because that is the spelling every other
11
+ * part of the CLI uses. `resolveLocaleFiles` substitutes `[locale]` verbatim,
12
+ * the string form of `transform` substitutes `[locale]` verbatim, and
13
+ * `localizeStaticUrls` splices the raw locale into URL paths. If placeholder
14
+ * substitution canonicalizes while those do not, a project that configures a
15
+ * non-canonical tag gets content written to one directory and links pointing
16
+ * at another.
17
+ *
18
+ * Locales that are already canonical (the common case) are unaffected: for
19
+ * those, `code` and the configured string are identical.
20
+ *
21
+ * Callers that genuinely want the canonical tag should use
22
+ * `gt.resolveCanonicalLocale`, or one of the explicitly-named properties such
23
+ * as `{minimizedCode}` / `{maximizedCode}` / `{regionCode}`.
24
+ */
25
+ function getConfiguredLocaleProperties(locale) {
26
+ return {
27
+ ...getLocaleProperties(locale),
28
+ code: locale
29
+ };
30
+ }
2
31
  function replaceLocalePlaceholders(string, localeProperties) {
3
32
  return string.replace(/\{(\w+)\}/g, (match, property) => {
4
33
  if (property === "locale" || property === "localeCode") return localeProperties.code;
@@ -9,6 +38,6 @@ function replaceLocalePlaceholders(string, localeProperties) {
9
38
  });
10
39
  }
11
40
  //#endregion
12
- export { replaceLocalePlaceholders };
41
+ export { getConfiguredLocaleProperties, replaceLocalePlaceholders };
13
42
 
14
43
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../src/formats/utils.ts"],"sourcesContent":["import type { LocaleProperties } from '@generaltranslation/format/types';\n\n// helper function to replace locale placeholders in a string\n// with the corresponding locale properties\n// ex: {locale} -> will be replaced with the locale code\n// ex: {localeName} -> will be replaced with the locale name\nexport function replaceLocalePlaceholders(\n string: string,\n localeProperties: LocaleProperties\n): string {\n return string.replace(/\\{(\\w+)\\}/g, (match, property) => {\n // Handle common aliases\n if (property === 'locale' || property === 'localeCode') {\n return localeProperties.code;\n }\n if (property === 'localeName') {\n return localeProperties.name;\n }\n if (property === 'localeNativeName') {\n return localeProperties.nativeName;\n }\n // Check if the property exists in localeProperties\n if (property in localeProperties) {\n return localeProperties[property as keyof typeof localeProperties];\n }\n // Return the original placeholder if property not found\n return match;\n });\n}\n"],"mappings":";AAMA,SAAgB,0BACd,QACA,kBACQ;AACR,QAAO,OAAO,QAAQ,eAAe,OAAO,aAAa;AAEvD,MAAI,aAAa,YAAY,aAAa,aACxC,QAAO,iBAAiB;AAE1B,MAAI,aAAa,aACf,QAAO,iBAAiB;AAE1B,MAAI,aAAa,mBACf,QAAO,iBAAiB;AAG1B,MAAI,YAAY,iBACd,QAAO,iBAAiB;AAG1B,SAAO;GACP"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/formats/utils.ts"],"sourcesContent":["import { getLocaleProperties } from '@generaltranslation/format';\nimport type { LocaleProperties } from '@generaltranslation/format/types';\n\n/**\n * Locale properties for a locale as it is written in the user's `locales` config.\n *\n * `getLocaleProperties(locale).code` returns the *canonical* BCP-47 form of a\n * tag, so \"fr-ca\" becomes \"fr-CA\" and \"ja-jp\" becomes \"ja-JP\". That is correct\n * when talking to the API, but it is wrong for anything that names a file, a\n * directory, a URL segment, or a locale key on disk: those must use the locale\n * exactly as the user configured it, because that is the spelling every other\n * part of the CLI uses. `resolveLocaleFiles` substitutes `[locale]` verbatim,\n * the string form of `transform` substitutes `[locale]` verbatim, and\n * `localizeStaticUrls` splices the raw locale into URL paths. If placeholder\n * substitution canonicalizes while those do not, a project that configures a\n * non-canonical tag gets content written to one directory and links pointing\n * at another.\n *\n * Locales that are already canonical (the common case) are unaffected: for\n * those, `code` and the configured string are identical.\n *\n * Callers that genuinely want the canonical tag should use\n * `gt.resolveCanonicalLocale`, or one of the explicitly-named properties such\n * as `{minimizedCode}` / `{maximizedCode}` / `{regionCode}`.\n */\nexport function getConfiguredLocaleProperties(\n locale: string\n): LocaleProperties {\n return { ...getLocaleProperties(locale), code: locale };\n}\n\n// helper function to replace locale placeholders in a string\n// with the corresponding locale properties\n// ex: {locale} -> will be replaced with the locale code\n// ex: {localeName} -> will be replaced with the locale name\nexport function replaceLocalePlaceholders(\n string: string,\n localeProperties: LocaleProperties\n): string {\n return string.replace(/\\{(\\w+)\\}/g, (match, property) => {\n // Handle common aliases\n if (property === 'locale' || property === 'localeCode') {\n return localeProperties.code;\n }\n if (property === 'localeName') {\n return localeProperties.name;\n }\n if (property === 'localeNativeName') {\n return localeProperties.nativeName;\n }\n // Check if the property exists in localeProperties\n if (property in localeProperties) {\n return localeProperties[property as keyof typeof localeProperties];\n }\n // Return the original placeholder if property not found\n return match;\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,8BACd,QACkB;AAClB,QAAO;EAAE,GAAG,oBAAoB,OAAO;EAAE,MAAM;EAAQ;;AAOzD,SAAgB,0BACd,QACA,kBACQ;AACR,QAAO,OAAO,QAAQ,eAAe,OAAO,aAAa;AAEvD,MAAI,aAAa,YAAY,aAAa,aACxC,QAAO,iBAAiB;AAE1B,MAAI,aAAa,aACf,QAAO,iBAAiB;AAE1B,MAAI,aAAa,mBACf,QAAO,iBAAiB;AAG1B,MAAI,YAAY,iBACd,QAAO,iBAAiB;AAG1B,SAAO;GACP"}
@@ -30,6 +30,18 @@ async function createOrUpdateConfig(configFilepath, options) {
30
30
  ...oldContent,
31
31
  ...newContent
32
32
  };
33
+ if (options.files) {
34
+ const oldFiles = oldContent.files && typeof oldContent.files === "object" ? oldContent.files : {};
35
+ const oldGt = oldFiles.gt && typeof oldFiles.gt === "object" ? oldFiles.gt : {};
36
+ mergedContent.files = {
37
+ ...oldFiles,
38
+ ...options.files,
39
+ ...options.files.gt && { gt: {
40
+ ...oldGt,
41
+ ...options.files.gt
42
+ } }
43
+ };
44
+ }
33
45
  if (options.locales) mergedContent.locales = mergedContent.locales ? [...new Set([...mergedContent.locales, ...options.locales])] : options.locales;
34
46
  const mergedJsonContent = JSON.stringify(mergedContent, null, 2);
35
47
  await fs.promises.writeFile(configFilepath, mergedJsonContent, "utf-8");
@@ -1 +1 @@
1
- {"version":3,"file":"setupConfig.js","names":[],"sources":["../../../src/fs/config/setupConfig.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { displayCreatedConfigFile } from '../../console/logging.js';\nimport { FilesOptions, SupportedFrameworks } from '../../types/index.js';\nimport { logger } from '../../console/logger.js';\nimport { GT_CONFIG_SCHEMA_URL } from '../../utils/constants.js';\n\n/**\n * Checks if the config file exists.\n * If yes, make sure make sure projectId is correct\n * If not, creates a new JSON file at the given filepath and writes the provided config object to it.\n * @param {string} configFilepath - The path to the config file.\n * @param {Record<string, any>} configObject - The config object to write if the file does not exist.\n */\nexport async function createOrUpdateConfig(\n configFilepath: string,\n options: {\n projectId?: string;\n defaultLocale?: string;\n locales?: string[];\n files?: FilesOptions;\n framework?: SupportedFrameworks;\n baseUrl?: string;\n publish?: boolean;\n }\n): Promise<string> {\n // Filter out empty string values from the config object\n const newContent = {\n ...(options.projectId && { projectId: options.projectId }),\n ...(options.defaultLocale && { defaultLocale: options.defaultLocale }),\n ...(options.files && { files: options.files }),\n ...(options.framework && { framework: options.framework }),\n ...(options.baseUrl && { baseUrl: options.baseUrl }),\n ...(options.publish && { publish: options.publish }),\n };\n try {\n // if file exists\n let oldContent: Record<string, unknown> = {};\n if (fs.existsSync(configFilepath)) {\n const parsed = JSON.parse(\n await fs.promises.readFile(configFilepath, 'utf-8')\n );\n oldContent =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n }\n\n // merge old and new content\n const mergedContent = {\n $schema: GT_CONFIG_SCHEMA_URL,\n ...oldContent,\n ...newContent,\n } as Record<string, unknown> & { locales?: string[] };\n\n // Add locales to mergedContent if they exist\n if (options.locales) {\n mergedContent.locales = mergedContent.locales\n ? [...new Set([...mergedContent.locales, ...options.locales])]\n : options.locales;\n }\n\n // write to file\n const mergedJsonContent = JSON.stringify(mergedContent, null, 2);\n await fs.promises.writeFile(configFilepath, mergedJsonContent, 'utf-8');\n\n // show update in console\n displayCreatedConfigFile(configFilepath);\n } catch (error) {\n logger.error(\n `An error occurred while updating ${configFilepath}: ${error}`\n );\n }\n return configFilepath;\n}\n"],"mappings":";;;;;;;;;;;;AAaA,eAAsB,qBACpB,gBACA,SASiB;CAEjB,MAAM,aAAa;EACjB,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,WAAW;EACzD,GAAI,QAAQ,iBAAiB,EAAE,eAAe,QAAQ,eAAe;EACrE,GAAI,QAAQ,SAAS,EAAE,OAAO,QAAQ,OAAO;EAC7C,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,WAAW;EACzD,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS;EACnD,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS;EACpD;AACD,KAAI;EAEF,IAAI,aAAsC,EAAE;AAC5C,MAAI,GAAG,WAAW,eAAe,EAAE;GACjC,MAAM,SAAS,KAAK,MAClB,MAAM,GAAG,SAAS,SAAS,gBAAgB,QAAQ,CACpD;AACD,gBACE,OAAO,WAAW,YAAY,WAAW,OACpC,SACD,EAAE;;EAIV,MAAM,gBAAgB;GACpB,SAAS;GACT,GAAG;GACH,GAAG;GACJ;AAGD,MAAI,QAAQ,QACV,eAAc,UAAU,cAAc,UAClC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,cAAc,SAAS,GAAG,QAAQ,QAAQ,CAAC,CAAC,GAC5D,QAAQ;EAId,MAAM,oBAAoB,KAAK,UAAU,eAAe,MAAM,EAAE;AAChE,QAAM,GAAG,SAAS,UAAU,gBAAgB,mBAAmB,QAAQ;AAGvE,2BAAyB,eAAe;UACjC,OAAO;AACd,SAAO,MACL,oCAAoC,eAAe,IAAI,QACxD;;AAEH,QAAO"}
1
+ {"version":3,"file":"setupConfig.js","names":[],"sources":["../../../src/fs/config/setupConfig.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { displayCreatedConfigFile } from '../../console/logging.js';\nimport { FilesOptions, SupportedFrameworks } from '../../types/index.js';\nimport { logger } from '../../console/logger.js';\nimport { GT_CONFIG_SCHEMA_URL } from '../../utils/constants.js';\n\n/**\n * Checks if the config file exists.\n * If yes, make sure make sure projectId is correct\n * If not, creates a new JSON file at the given filepath and writes the provided config object to it.\n * @param {string} configFilepath - The path to the config file.\n * @param {Record<string, any>} configObject - The config object to write if the file does not exist.\n */\nexport async function createOrUpdateConfig(\n configFilepath: string,\n options: {\n projectId?: string;\n defaultLocale?: string;\n locales?: string[];\n files?: FilesOptions;\n framework?: SupportedFrameworks;\n baseUrl?: string;\n publish?: boolean;\n }\n): Promise<string> {\n // Filter out empty string values from the config object\n const newContent = {\n ...(options.projectId && { projectId: options.projectId }),\n ...(options.defaultLocale && { defaultLocale: options.defaultLocale }),\n ...(options.files && { files: options.files }),\n ...(options.framework && { framework: options.framework }),\n ...(options.baseUrl && { baseUrl: options.baseUrl }),\n ...(options.publish && { publish: options.publish }),\n };\n try {\n // if file exists\n let oldContent: Record<string, unknown> = {};\n if (fs.existsSync(configFilepath)) {\n const parsed = JSON.parse(\n await fs.promises.readFile(configFilepath, 'utf-8')\n );\n oldContent =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n }\n\n // merge old and new content\n const mergedContent = {\n $schema: GT_CONFIG_SCHEMA_URL,\n ...oldContent,\n ...newContent,\n } as Record<string, unknown> & { locales?: string[] };\n\n // Preserve unrelated file configuration and nested GT options when setup\n // only needs to add or update a translation output path.\n if (options.files) {\n const oldFiles =\n oldContent.files && typeof oldContent.files === 'object'\n ? (oldContent.files as Record<string, unknown>)\n : {};\n const oldGt =\n oldFiles.gt && typeof oldFiles.gt === 'object'\n ? (oldFiles.gt as Record<string, unknown>)\n : {};\n mergedContent.files = {\n ...oldFiles,\n ...options.files,\n ...(options.files.gt && {\n gt: { ...oldGt, ...options.files.gt },\n }),\n };\n }\n\n // Add locales to mergedContent if they exist\n if (options.locales) {\n mergedContent.locales = mergedContent.locales\n ? [...new Set([...mergedContent.locales, ...options.locales])]\n : options.locales;\n }\n\n // write to file\n const mergedJsonContent = JSON.stringify(mergedContent, null, 2);\n await fs.promises.writeFile(configFilepath, mergedJsonContent, 'utf-8');\n\n // show update in console\n displayCreatedConfigFile(configFilepath);\n } catch (error) {\n logger.error(\n `An error occurred while updating ${configFilepath}: ${error}`\n );\n }\n return configFilepath;\n}\n"],"mappings":";;;;;;;;;;;;AAaA,eAAsB,qBACpB,gBACA,SASiB;CAEjB,MAAM,aAAa;EACjB,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,WAAW;EACzD,GAAI,QAAQ,iBAAiB,EAAE,eAAe,QAAQ,eAAe;EACrE,GAAI,QAAQ,SAAS,EAAE,OAAO,QAAQ,OAAO;EAC7C,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,WAAW;EACzD,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS;EACnD,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS;EACpD;AACD,KAAI;EAEF,IAAI,aAAsC,EAAE;AAC5C,MAAI,GAAG,WAAW,eAAe,EAAE;GACjC,MAAM,SAAS,KAAK,MAClB,MAAM,GAAG,SAAS,SAAS,gBAAgB,QAAQ,CACpD;AACD,gBACE,OAAO,WAAW,YAAY,WAAW,OACpC,SACD,EAAE;;EAIV,MAAM,gBAAgB;GACpB,SAAS;GACT,GAAG;GACH,GAAG;GACJ;AAID,MAAI,QAAQ,OAAO;GACjB,MAAM,WACJ,WAAW,SAAS,OAAO,WAAW,UAAU,WAC3C,WAAW,QACZ,EAAE;GACR,MAAM,QACJ,SAAS,MAAM,OAAO,SAAS,OAAO,WACjC,SAAS,KACV,EAAE;AACR,iBAAc,QAAQ;IACpB,GAAG;IACH,GAAG,QAAQ;IACX,GAAI,QAAQ,MAAM,MAAM,EACtB,IAAI;KAAE,GAAG;KAAO,GAAG,QAAQ,MAAM;KAAI,EACtC;IACF;;AAIH,MAAI,QAAQ,QACV,eAAc,UAAU,cAAc,UAClC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,cAAc,SAAS,GAAG,QAAQ,QAAQ,CAAC,CAAC,GAC5D,QAAQ;EAId,MAAM,oBAAoB,KAAK,UAAU,eAAe,MAAM,EAAE;AAChE,QAAM,GAAG,SAAS,UAAU,gBAAgB,mBAAmB,QAAQ;AAGvE,2BAAyB,eAAe;UACjC,OAAO;AACd,SAAO,MACL,oCAAoC,eAAe,IAAI,QACxD;;AAEH,QAAO"}
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.16.1";
1
+ export declare const PACKAGE_VERSION = "2.16.3";
@@ -1,5 +1,5 @@
1
1
  //#region src/generated/version.ts
2
- const PACKAGE_VERSION = "2.16.1";
2
+ const PACKAGE_VERSION = "2.16.3";
3
3
  //#endregion
4
4
  export { PACKAGE_VERSION };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.16.1';\n"],"mappings":";AACA,MAAa,kBAAkB"}
1
+ {"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.16.3';\n"],"mappings":";AACA,MAAa,kBAAkB"}
@@ -0,0 +1,9 @@
1
+ type SetupViteSPAOptions = {
2
+ appDirectory: string;
3
+ configFilepath: string;
4
+ defaultLocale: string;
5
+ locales: string[];
6
+ translationsDir?: string;
7
+ };
8
+ export declare function setupViteSPA({ appDirectory, configFilepath, defaultLocale, locales, translationsDir, }: SetupViteSPAOptions): Promise<void>;
9
+ export {};
@@ -0,0 +1,111 @@
1
+ import { logger } from "../console/logger.js";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import { createDiagnosticMessage } from "generaltranslation/internal";
5
+ //#region src/setup/setupViteSPA.ts
6
+ const defaultBootstrapFilename = "gt-entry.ts";
7
+ const alternateBootstrapFilename = "gt-bootstrap.ts";
8
+ function getBootstrapConflictError(filename) {
9
+ return createDiagnosticMessage({
10
+ source: "gt",
11
+ severity: "Error",
12
+ whatHappened: "The Vite bootstrap file already exists",
13
+ why: `GT will not overwrite an existing src/${filename} file`,
14
+ fix: "Move or rename that file and rerun `npx gt@latest`"
15
+ });
16
+ }
17
+ function getLoaderContent(translationsImport) {
18
+ return `export default async function loadTranslations(locale: string) {
19
+ const translations = await import(\`${translationsImport}/\${locale}.json\`);
20
+ return translations.default;
21
+ }
22
+ `;
23
+ }
24
+ function isGeneratedLoader(content) {
25
+ return /^export default async function loadTranslations\(locale: string\) \{\r?\n const translations = await import\(`[^`]+\/\$\{locale\}\.json`\);\r?\n return translations\.default;\r?\n\}\r?\n?$/.test(content);
26
+ }
27
+ function toRelativeImport(fromDirectory, toPath) {
28
+ const relativePath = path.relative(fromDirectory, toPath).split(path.sep).join(path.posix.sep);
29
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
30
+ }
31
+ function getEntryImport(appDirectory, sourceDirectory, source) {
32
+ const sourcePath = source.replace(/[?#].*$/, "");
33
+ return toRelativeImport(sourceDirectory, sourcePath.startsWith("/") ? path.resolve(appDirectory, `.${sourcePath}`) : path.resolve(appDirectory, sourcePath)).replace(/\.(?:[cm]?[jt]sx?)$/i, "");
34
+ }
35
+ function getBootstrapEntry(bootstrap) {
36
+ return bootstrap.match(/await\s+import\(\s*(['"])([^'"]+)\1\s*\)/)?.[2];
37
+ }
38
+ function getModuleEntry(indexHtml) {
39
+ const moduleScripts = indexHtml.match(/<script\b[^>]*\btype=(['"])module\1[^>]*>\s*<\/script>/gi);
40
+ for (const script of moduleScripts ?? []) {
41
+ const source = script.match(/\bsrc=(['"])([^'"]+)\1/i)?.[2];
42
+ if (source) return {
43
+ script,
44
+ source
45
+ };
46
+ }
47
+ throw new Error(createDiagnosticMessage({
48
+ source: "gt",
49
+ severity: "Error",
50
+ whatHappened: "No Vite module entry was found in index.html",
51
+ fix: "Add a module script for the app entry and rerun `npx gt@latest`"
52
+ }));
53
+ }
54
+ async function setupViteSPA({ appDirectory, configFilepath, defaultLocale, locales, translationsDir }) {
55
+ const indexHtmlPath = path.join(appDirectory, "index.html");
56
+ const sourceDirectory = path.join(appDirectory, "src");
57
+ const indexHtml = await fs.promises.readFile(indexHtmlPath, "utf8");
58
+ const { script, source } = getModuleEntry(indexHtml);
59
+ const declaredEntryImport = getEntryImport(appDirectory, sourceDirectory, source);
60
+ const configuredBootstrap = source.match(/^\/?src\/(gt-entry\.ts|gt-bootstrap\.ts)(?:[?#].*)?$/)?.[1];
61
+ const isAlreadyConfigured = configuredBootstrap !== void 0;
62
+ const bootstrapFilename = configuredBootstrap ?? (declaredEntryImport === "./gt-entry" ? alternateBootstrapFilename : defaultBootstrapFilename);
63
+ const bootstrapPath = path.join(sourceDirectory, bootstrapFilename);
64
+ let existingBootstrap;
65
+ if (fs.existsSync(bootstrapPath)) {
66
+ existingBootstrap = await fs.promises.readFile(bootstrapPath, "utf8");
67
+ if (!existingBootstrap.includes("initializeGTSPA")) throw new Error(getBootstrapConflictError(bootstrapFilename));
68
+ }
69
+ const entryImport = isAlreadyConfigured ? existingBootstrap && getBootstrapEntry(existingBootstrap) : declaredEntryImport;
70
+ if (!entryImport) throw new Error(createDiagnosticMessage({
71
+ source: "gt",
72
+ severity: "Error",
73
+ whatHappened: "The existing Vite bootstrap has no app entry import",
74
+ fix: "Restore the app entry import and rerun `npx gt@latest`"
75
+ }));
76
+ await fs.promises.mkdir(sourceDirectory, { recursive: true });
77
+ let loadTranslationsImport = "";
78
+ let loadTranslationsOption = "gtConfig";
79
+ if (translationsDir) {
80
+ const translationsPath = path.resolve(appDirectory, translationsDir);
81
+ const loaderPath = path.join(sourceDirectory, "loadTranslations.ts");
82
+ const translationsImport = toRelativeImport(sourceDirectory, translationsPath);
83
+ const existingLoader = fs.existsSync(loaderPath) ? await fs.promises.readFile(loaderPath, "utf8") : void 0;
84
+ if (!existingLoader || isGeneratedLoader(existingLoader)) await fs.promises.writeFile(loaderPath, getLoaderContent(translationsImport));
85
+ await fs.promises.mkdir(translationsPath, { recursive: true });
86
+ for (const locale of new Set(locales)) {
87
+ if (locale === defaultLocale) continue;
88
+ const stubPath = path.join(translationsPath, `${locale}.json`);
89
+ if (!fs.existsSync(stubPath)) await fs.promises.writeFile(stubPath, "{}\n");
90
+ }
91
+ loadTranslationsImport = "import loadTranslations from './loadTranslations';\n";
92
+ loadTranslationsOption = "{ ...gtConfig, loadTranslations }";
93
+ }
94
+ const configImport = toRelativeImport(sourceDirectory, path.resolve(appDirectory, configFilepath));
95
+ await fs.promises.writeFile(bootstrapPath, `import { initializeGTSPA } from 'gt-react';
96
+ import gtConfig from '${configImport}';
97
+ ${loadTranslationsImport}
98
+ await initializeGTSPA(${loadTranslationsOption});
99
+
100
+ await import('${entryImport}');
101
+ `);
102
+ if (!isAlreadyConfigured) {
103
+ const updatedScript = script.replace(source, `/src/${bootstrapFilename}`);
104
+ await fs.promises.writeFile(indexHtmlPath, indexHtml.replace(script, updatedScript));
105
+ }
106
+ logger.success("Configured initializeGTSPA for this Vite application.");
107
+ }
108
+ //#endregion
109
+ export { setupViteSPA };
110
+
111
+ //# sourceMappingURL=setupViteSPA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupViteSPA.js","names":[],"sources":["../../src/setup/setupViteSPA.ts"],"sourcesContent":["import { createDiagnosticMessage } from 'generaltranslation/internal';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { logger } from '../console/logger.js';\n\ntype SetupViteSPAOptions = {\n appDirectory: string;\n configFilepath: string;\n defaultLocale: string;\n locales: string[];\n translationsDir?: string;\n};\n\nconst defaultBootstrapFilename = 'gt-entry.ts';\nconst alternateBootstrapFilename = 'gt-bootstrap.ts';\n\nfunction getBootstrapConflictError(filename: string): string {\n return createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The Vite bootstrap file already exists',\n why: `GT will not overwrite an existing src/${filename} file`,\n fix: 'Move or rename that file and rerun `npx gt@latest`',\n });\n}\n\nfunction getLoaderContent(translationsImport: string): string {\n return `export default async function loadTranslations(locale: string) {\n const translations = await import(\\`${translationsImport}/\\${locale}.json\\`);\n return translations.default;\n}\n`;\n}\n\nfunction isGeneratedLoader(content: string): boolean {\n return /^export default async function loadTranslations\\(locale: string\\) \\{\\r?\\n const translations = await import\\(`[^`]+\\/\\$\\{locale\\}\\.json`\\);\\r?\\n return translations\\.default;\\r?\\n\\}\\r?\\n?$/.test(\n content\n );\n}\n\nfunction toRelativeImport(fromDirectory: string, toPath: string): string {\n const relativePath = path\n .relative(fromDirectory, toPath)\n .split(path.sep)\n .join(path.posix.sep);\n return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;\n}\n\nfunction getEntryImport(\n appDirectory: string,\n sourceDirectory: string,\n source: string\n): string {\n const sourcePath = source.replace(/[?#].*$/, '');\n const entryPath = sourcePath.startsWith('/')\n ? path.resolve(appDirectory, `.${sourcePath}`)\n : path.resolve(appDirectory, sourcePath);\n return toRelativeImport(sourceDirectory, entryPath).replace(\n /\\.(?:[cm]?[jt]sx?)$/i,\n ''\n );\n}\n\nfunction getBootstrapEntry(bootstrap: string): string | undefined {\n return bootstrap.match(/await\\s+import\\(\\s*(['\"])([^'\"]+)\\1\\s*\\)/)?.[2];\n}\n\nfunction getModuleEntry(indexHtml: string): {\n script: string;\n source: string;\n} {\n const moduleScripts = indexHtml.match(\n /<script\\b[^>]*\\btype=(['\"])module\\1[^>]*>\\s*<\\/script>/gi\n );\n for (const script of moduleScripts ?? []) {\n const source = script.match(/\\bsrc=(['\"])([^'\"]+)\\1/i)?.[2];\n if (source) return { script, source };\n }\n\n throw new Error(\n createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'No Vite module entry was found in index.html',\n fix: 'Add a module script for the app entry and rerun `npx gt@latest`',\n })\n );\n}\n\nexport async function setupViteSPA({\n appDirectory,\n configFilepath,\n defaultLocale,\n locales,\n translationsDir,\n}: SetupViteSPAOptions): Promise<void> {\n const indexHtmlPath = path.join(appDirectory, 'index.html');\n const sourceDirectory = path.join(appDirectory, 'src');\n const indexHtml = await fs.promises.readFile(indexHtmlPath, 'utf8');\n const { script, source } = getModuleEntry(indexHtml);\n const declaredEntryImport = getEntryImport(\n appDirectory,\n sourceDirectory,\n source\n );\n const configuredBootstrap = source.match(\n /^\\/?src\\/(gt-entry\\.ts|gt-bootstrap\\.ts)(?:[?#].*)?$/\n )?.[1];\n const isAlreadyConfigured = configuredBootstrap !== undefined;\n const bootstrapFilename =\n configuredBootstrap ??\n (declaredEntryImport === './gt-entry'\n ? alternateBootstrapFilename\n : defaultBootstrapFilename);\n const bootstrapPath = path.join(sourceDirectory, bootstrapFilename);\n let existingBootstrap: string | undefined;\n\n if (fs.existsSync(bootstrapPath)) {\n existingBootstrap = await fs.promises.readFile(bootstrapPath, 'utf8');\n if (!existingBootstrap.includes('initializeGTSPA')) {\n throw new Error(getBootstrapConflictError(bootstrapFilename));\n }\n }\n\n const entryImport = isAlreadyConfigured\n ? existingBootstrap && getBootstrapEntry(existingBootstrap)\n : declaredEntryImport;\n if (!entryImport) {\n throw new Error(\n createDiagnosticMessage({\n source: 'gt',\n severity: 'Error',\n whatHappened: 'The existing Vite bootstrap has no app entry import',\n fix: 'Restore the app entry import and rerun `npx gt@latest`',\n })\n );\n }\n\n await fs.promises.mkdir(sourceDirectory, { recursive: true });\n\n let loadTranslationsImport = '';\n let loadTranslationsOption = 'gtConfig';\n if (translationsDir) {\n const translationsPath = path.resolve(appDirectory, translationsDir);\n const loaderPath = path.join(sourceDirectory, 'loadTranslations.ts');\n const translationsImport = toRelativeImport(\n sourceDirectory,\n translationsPath\n );\n const existingLoader = fs.existsSync(loaderPath)\n ? await fs.promises.readFile(loaderPath, 'utf8')\n : undefined;\n if (!existingLoader || isGeneratedLoader(existingLoader)) {\n await fs.promises.writeFile(\n loaderPath,\n getLoaderContent(translationsImport)\n );\n }\n\n await fs.promises.mkdir(translationsPath, { recursive: true });\n for (const locale of new Set(locales)) {\n if (locale === defaultLocale) continue;\n const stubPath = path.join(translationsPath, `${locale}.json`);\n if (!fs.existsSync(stubPath)) {\n await fs.promises.writeFile(stubPath, '{}\\n');\n }\n }\n\n loadTranslationsImport =\n \"import loadTranslations from './loadTranslations';\\n\";\n loadTranslationsOption = '{ ...gtConfig, loadTranslations }';\n }\n\n const configImport = toRelativeImport(\n sourceDirectory,\n path.resolve(appDirectory, configFilepath)\n );\n await fs.promises.writeFile(\n bootstrapPath,\n `import { initializeGTSPA } from 'gt-react';\nimport gtConfig from '${configImport}';\n${loadTranslationsImport}\nawait initializeGTSPA(${loadTranslationsOption});\n\nawait import('${entryImport}');\n`\n );\n\n if (!isAlreadyConfigured) {\n const updatedScript = script.replace(source, `/src/${bootstrapFilename}`);\n await fs.promises.writeFile(\n indexHtmlPath,\n indexHtml.replace(script, updatedScript)\n );\n }\n\n logger.success('Configured initializeGTSPA for this Vite application.');\n}\n"],"mappings":"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"}
@@ -1,4 +1,7 @@
1
- export declare function getDesiredLocales(): Promise<{
1
+ export declare function getDesiredLocales(existingConfig?: {
2
+ defaultLocale?: unknown;
3
+ locales?: unknown;
4
+ }): Promise<{
2
5
  defaultLocale: string;
3
6
  locales: string[];
4
7
  }>;
@@ -1,13 +1,15 @@
1
1
  import { promptLocale, promptLocaleList } from "../console/logging.js";
2
2
  import { libraryDefaultLocale } from "generaltranslation/internal";
3
3
  //#region src/setup/userInput.ts
4
- async function getDesiredLocales() {
4
+ async function getDesiredLocales(existingConfig) {
5
+ const configuredDefaultLocale = typeof existingConfig?.defaultLocale === "string" && existingConfig.defaultLocale ? existingConfig.defaultLocale : void 0;
6
+ const configuredLocales = Array.isArray(existingConfig?.locales) && existingConfig.locales.every((locale) => typeof locale === "string") ? existingConfig.locales : void 0;
5
7
  return {
6
- defaultLocale: await promptLocale({
8
+ defaultLocale: configuredDefaultLocale ?? await promptLocale({
7
9
  message: "What is the default locale for your project?",
8
10
  defaultValue: libraryDefaultLocale
9
11
  }),
10
- locales: await promptLocaleList({ message: "Which languages would you like to translate your project into?" })
12
+ locales: configuredLocales ?? await promptLocaleList({ message: "Which languages would you like to translate your project into?" })
11
13
  };
12
14
  }
13
15
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"userInput.js","names":[],"sources":["../../src/setup/userInput.ts"],"sourcesContent":["import { libraryDefaultLocale } from 'generaltranslation/internal';\nimport { promptLocale, promptLocaleList } from '../console/logging.js';\n\nexport async function getDesiredLocales(): Promise<{\n defaultLocale: string;\n locales: string[];\n}> {\n // Ask for the default locale\n const defaultLocale = await promptLocale({\n message: 'What is the default locale for your project?',\n defaultValue: libraryDefaultLocale,\n });\n\n // Ask for the locales\n const locales = await promptLocaleList({\n message: 'Which languages would you like to translate your project into?',\n });\n return { defaultLocale, locales };\n}\n"],"mappings":";;;AAGA,eAAsB,oBAGnB;AAWD,QAAO;EAAE,eAAA,MATmB,aAAa;GACvC,SAAS;GACT,cAAc;GACf,CAAC;EAMsB,SAAA,MAHF,iBAAiB,EACrC,SAAS,kEACV,CAAC;EAC+B"}
1
+ {"version":3,"file":"userInput.js","names":[],"sources":["../../src/setup/userInput.ts"],"sourcesContent":["import { libraryDefaultLocale } from 'generaltranslation/internal';\nimport { promptLocale, promptLocaleList } from '../console/logging.js';\n\nexport async function getDesiredLocales(existingConfig?: {\n defaultLocale?: unknown;\n locales?: unknown;\n}): Promise<{\n defaultLocale: string;\n locales: string[];\n}> {\n const configuredDefaultLocale =\n typeof existingConfig?.defaultLocale === 'string' &&\n existingConfig.defaultLocale\n ? existingConfig.defaultLocale\n : undefined;\n const configuredLocales =\n Array.isArray(existingConfig?.locales) &&\n existingConfig.locales.every((locale) => typeof locale === 'string')\n ? (existingConfig.locales as string[])\n : undefined;\n\n // Ask for the default locale\n const defaultLocale =\n configuredDefaultLocale ??\n (await promptLocale({\n message: 'What is the default locale for your project?',\n defaultValue: libraryDefaultLocale,\n }));\n\n // Ask for the locales\n const locales =\n configuredLocales ??\n (await promptLocaleList({\n message: 'Which languages would you like to translate your project into?',\n }));\n return { defaultLocale, locales };\n}\n"],"mappings":";;;AAGA,eAAsB,kBAAkB,gBAMrC;CACD,MAAM,0BACJ,OAAO,gBAAgB,kBAAkB,YACzC,eAAe,gBACX,eAAe,gBACf,KAAA;CACN,MAAM,oBACJ,MAAM,QAAQ,gBAAgB,QAAQ,IACtC,eAAe,QAAQ,OAAO,WAAW,OAAO,WAAW,SAAS,GAC/D,eAAe,UAChB,KAAA;AAgBN,QAAO;EAAE,eAZP,2BACC,MAAM,aAAa;GAClB,SAAS;GACT,cAAc;GACf,CAAC;EAQoB,SAJtB,qBACC,MAAM,iBAAiB,EACtB,SAAS,kEACV,CAAC;EAC6B"}
@@ -10,7 +10,6 @@ import { handleInitGT } from "../next/parse/handleInitGT.js";
10
10
  import { wrapContentNext } from "../next/parse/wrapContent.js";
11
11
  import { getPackageManager } from "../utils/packageManager.js";
12
12
  import { installPackage } from "../utils/installPackage.js";
13
- import { addVitePlugin } from "../react/parse/addVitePlugin/index.js";
14
13
  import { getFrameworkDisplayName } from "./frameworkUtils.js";
15
14
  import chalk from "chalk";
16
15
  //#region src/setup/wizard.ts
@@ -67,7 +66,7 @@ Make sure you have committed or stashed any changes. Do you want to continue?`),
67
66
  Please let us know what you would like to see added at https://github.com/generaltranslation/gt/issues`);
68
67
  exitSync(0);
69
68
  }
70
- await createOrUpdateConfig(options.config || "gt.config.json", { framework: frameworkType });
69
+ if (frameworkType !== "vite") await createOrUpdateConfig(options.config || "gt.config.json", { framework: frameworkType });
71
70
  const packageJson = await getPackageJson();
72
71
  if (!packageJson) {
73
72
  logger.error(chalk.red("No package.json found in the current directory. Run this command from the root of your project."));
@@ -123,13 +122,6 @@ Please let us know what you would like to see added at https://github.com/genera
123
122
  await handleInitGT(nextConfigPath, errors, warnings, filesUpdated, packageJson, tsconfigJson);
124
123
  logger.step(chalk.green(`Added withGTConfig() to your ${nextConfigPath} file.`));
125
124
  }
126
- if (frameworkType === "vite") await addVitePlugin({
127
- errors,
128
- warnings,
129
- filesUpdated,
130
- packageJson,
131
- tsconfigJson
132
- });
133
125
  if (errors.length > 0) logger.error(chalk.red("Failed to write files:\n") + errors.join("\n"));
134
126
  if (warnings.length > 0) logger.warn(chalk.yellow("Warnings encountered:") + "\n" + warnings.map((warning) => `${chalk.yellow("-")} ${warning}`).join("\n"));
135
127
  const formatter = await detectFormatter();
@@ -1 +1 @@
1
- {"version":3,"file":"wizard.js","names":[],"sources":["../../src/setup/wizard.ts"],"sourcesContent":["import { detectFormatter } from '../hooks/postProcess.js';\nimport { promptSelect } from '../console/logging.js';\nimport { logger } from '../console/logger.js';\nimport chalk from 'chalk';\nimport { promptConfirm } from '../console/logging.js';\nimport { SetupOptions, SupportedReactFrameworks } from '../types/index.js';\nimport findFilepath from '../fs/findFilepath.js';\nimport { formatFiles } from '../hooks/postProcess.js';\nimport { handleInitGT } from '../next/parse/handleInitGT.js';\nimport { getPackageJson, isPackageInstalled } from '../utils/packageJson.js';\nimport { wrapContentNext } from '../next/parse/wrapContent.js';\nimport { getPackageManager } from '../utils/packageManager.js';\nimport { installPackage } from '../utils/installPackage.js';\nimport { createOrUpdateConfig } from '../fs/config/setupConfig.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { addVitePlugin } from '../react/parse/addVitePlugin/index.js';\nimport { exitSync } from '../console/logging.js';\nimport { ReactFrameworkObject } from '../types/index.js';\nimport { getFrameworkDisplayName } from './frameworkUtils.js';\nimport { Libraries } from '../types/libraries.js';\n\nexport async function handleSetupReactCommand(\n options: SetupOptions,\n frameworkObject: ReactFrameworkObject,\n useDefaults: boolean = false\n): Promise<void> {\n const frameworkDisplayName = getFrameworkDisplayName(frameworkObject);\n\n // Ask user for confirmation using inquirer\n if (!useDefaults) {\n const answer = await promptConfirm({\n message: chalk.yellow(\n `This wizard will configure your ${frameworkDisplayName} project for internationalization with GT. If your project is already using a different i18n library, this wizard may cause issues.\n\nMake sure you have committed or stashed any changes. Do you want to continue?`\n ),\n defaultValue: true,\n cancelMessage:\n 'Operation cancelled. You can re-run this wizard with: npx gt setup',\n });\n if (!answer) {\n logger.info(\n 'Operation cancelled. You can re-run this wizard with: npx gt setup'\n );\n exitSync(0);\n }\n }\n\n const frameworkType =\n useDefaults && frameworkObject?.name\n ? frameworkObject.name\n : await promptSelect<SupportedReactFrameworks | 'other'>({\n message: 'Which framework are you using?',\n options: [\n { value: 'next-app', label: chalk.blue('Next.js App Router') },\n { value: 'next-pages', label: chalk.green('Next.js Pages Router') },\n { value: 'vite', label: chalk.cyan('Vite + React') },\n { value: 'gatsby', label: chalk.magenta('Gatsby') },\n { value: 'react', label: chalk.yellow('React') },\n { value: 'redwood', label: chalk.red('RedwoodJS') },\n { value: 'other', label: chalk.dim('Other') },\n ],\n defaultValue: frameworkObject?.name || 'other',\n });\n if (frameworkType === 'other') {\n logger.error(\n `Sorry, the wizard doesn't currently support other React frameworks.\nPlease let us know what you would like to see added at https://github.com/generaltranslation/gt/issues`\n );\n exitSync(0);\n }\n\n // ----- Create a starter gt.config.json file -----\n await createOrUpdateConfig(options.config || 'gt.config.json', {\n framework: frameworkType as SupportedReactFrameworks,\n });\n\n const packageJson = await getPackageJson();\n if (!packageJson) {\n logger.error(\n chalk.red(\n 'No package.json found in the current directory. Run this command from the root of your project.'\n )\n );\n exitSync(1);\n }\n // Check if gt-next or gt-react is installed\n if (\n frameworkType === 'next-app' &&\n !isPackageInstalled(Libraries.GT_NEXT, packageJson)\n ) {\n const packageManager = await getPackageManager();\n const spinner = logger.createSpinner('timer');\n spinner.start(\n `Installing ${Libraries.GT_NEXT} with ${packageManager.name}...`\n );\n await installPackage(Libraries.GT_NEXT, packageManager);\n spinner.stop(chalk.green(`Automatically installed ${Libraries.GT_NEXT}.`));\n } else if (\n ['next-pages', 'react', 'redwood', 'vite', 'gatsby'].includes(\n frameworkType\n ) &&\n !isPackageInstalled(Libraries.GT_REACT, packageJson)\n ) {\n const packageManager = await getPackageManager();\n const spinner = logger.createSpinner('timer');\n spinner.start(\n `Installing ${Libraries.GT_REACT} with ${packageManager.name}...`\n );\n await installPackage(Libraries.GT_REACT, packageManager);\n spinner.stop(chalk.green(`Automatically installed ${Libraries.GT_REACT}.`));\n }\n\n const errors: string[] = [];\n const warnings: string[] = [];\n let filesUpdated: string[] = [];\n\n // Read tsconfig.json if it exists\n const tsconfigPath = findFilepath(['tsconfig.json']);\n const tsconfigJson = tsconfigPath ? loadConfig(tsconfigPath) : undefined;\n\n if (frameworkType === 'next-app') {\n // Check if they have a next.config.js file\n const nextConfigPath = findFilepath([\n './next.config.js',\n './next.config.ts',\n './next.config.mjs',\n './next.config.mts',\n ]);\n if (!nextConfigPath) {\n logger.error('No next.config.[js|ts|mjs|mts] file found.');\n exitSync(1);\n }\n\n const mergeOptions = {\n ...options,\n disableIds: true,\n disableFormatting: true,\n skipTs: true,\n addGTProvider: true,\n };\n const spinner = logger.createSpinner();\n spinner.start('Wrapping JSX content with <T> tags...');\n // Wrap all JSX elements in the src directory with a <T> tag, with unique ids\n const { filesUpdated: filesUpdatedNext } = await wrapContentNext(\n mergeOptions,\n Libraries.GT_NEXT,\n errors,\n warnings\n );\n filesUpdated = [...filesUpdated, ...filesUpdatedNext];\n\n spinner.stop(\n chalk.green(\n `Success! Updated ${chalk.bold.cyan(filesUpdated.length)} files:\\n`\n ) + filesUpdated.map((file) => `${chalk.green('-')} ${file}`).join('\\n')\n );\n\n // Add the withGTConfig() function to the next.config.js file\n await handleInitGT(\n nextConfigPath,\n errors,\n warnings,\n filesUpdated,\n packageJson,\n tsconfigJson\n );\n logger.step(\n chalk.green(`Added withGTConfig() to your ${nextConfigPath} file.`)\n );\n }\n\n // Add gt compiler plugin\n if (frameworkType === 'vite') {\n await addVitePlugin({\n errors,\n warnings,\n filesUpdated,\n packageJson,\n tsconfigJson,\n });\n }\n\n if (errors.length > 0) {\n logger.error(chalk.red('Failed to write files:\\n') + errors.join('\\n'));\n }\n\n if (warnings.length > 0) {\n logger.warn(\n chalk.yellow('Warnings encountered:') +\n '\\n' +\n warnings.map((warning) => `${chalk.yellow('-')} ${warning}`).join('\\n')\n );\n }\n\n const formatter = await detectFormatter();\n\n if (!formatter || filesUpdated.length === 0) {\n return;\n }\n\n const applyFormatting = useDefaults\n ? true\n : await promptConfirm({\n message: `Would you like the wizard to auto-format the modified files? ${chalk.dim(\n `(${formatter})`\n )}`,\n defaultValue: true,\n });\n // Format updated files if formatters are available\n if (applyFormatting) await formatFiles(filesUpdated, formatter);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"wizard.js","names":[],"sources":["../../src/setup/wizard.ts"],"sourcesContent":["import { detectFormatter } from '../hooks/postProcess.js';\nimport { promptSelect } from '../console/logging.js';\nimport { logger } from '../console/logger.js';\nimport chalk from 'chalk';\nimport { promptConfirm } from '../console/logging.js';\nimport { SetupOptions, SupportedReactFrameworks } from '../types/index.js';\nimport findFilepath from '../fs/findFilepath.js';\nimport { formatFiles } from '../hooks/postProcess.js';\nimport { handleInitGT } from '../next/parse/handleInitGT.js';\nimport { getPackageJson, isPackageInstalled } from '../utils/packageJson.js';\nimport { wrapContentNext } from '../next/parse/wrapContent.js';\nimport { getPackageManager } from '../utils/packageManager.js';\nimport { installPackage } from '../utils/installPackage.js';\nimport { createOrUpdateConfig } from '../fs/config/setupConfig.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { exitSync } from '../console/logging.js';\nimport { ReactFrameworkObject } from '../types/index.js';\nimport { getFrameworkDisplayName } from './frameworkUtils.js';\nimport { Libraries } from '../types/libraries.js';\n\nexport async function handleSetupReactCommand(\n options: SetupOptions,\n frameworkObject: ReactFrameworkObject,\n useDefaults: boolean = false\n): Promise<void> {\n const frameworkDisplayName = getFrameworkDisplayName(frameworkObject);\n\n // Ask user for confirmation using inquirer\n if (!useDefaults) {\n const answer = await promptConfirm({\n message: chalk.yellow(\n `This wizard will configure your ${frameworkDisplayName} project for internationalization with GT. If your project is already using a different i18n library, this wizard may cause issues.\n\nMake sure you have committed or stashed any changes. Do you want to continue?`\n ),\n defaultValue: true,\n cancelMessage:\n 'Operation cancelled. You can re-run this wizard with: npx gt setup',\n });\n if (!answer) {\n logger.info(\n 'Operation cancelled. You can re-run this wizard with: npx gt setup'\n );\n exitSync(0);\n }\n }\n\n const frameworkType =\n useDefaults && frameworkObject?.name\n ? frameworkObject.name\n : await promptSelect<SupportedReactFrameworks | 'other'>({\n message: 'Which framework are you using?',\n options: [\n { value: 'next-app', label: chalk.blue('Next.js App Router') },\n { value: 'next-pages', label: chalk.green('Next.js Pages Router') },\n { value: 'vite', label: chalk.cyan('Vite + React') },\n { value: 'gatsby', label: chalk.magenta('Gatsby') },\n { value: 'react', label: chalk.yellow('React') },\n { value: 'redwood', label: chalk.red('RedwoodJS') },\n { value: 'other', label: chalk.dim('Other') },\n ],\n defaultValue: frameworkObject?.name || 'other',\n });\n if (frameworkType === 'other') {\n logger.error(\n `Sorry, the wizard doesn't currently support other React frameworks.\nPlease let us know what you would like to see added at https://github.com/generaltranslation/gt/issues`\n );\n exitSync(0);\n }\n\n // Vite setup writes its complete config after locales are collected.\n if (frameworkType !== 'vite') {\n await createOrUpdateConfig(options.config || 'gt.config.json', {\n framework: frameworkType as SupportedReactFrameworks,\n });\n }\n\n const packageJson = await getPackageJson();\n if (!packageJson) {\n logger.error(\n chalk.red(\n 'No package.json found in the current directory. Run this command from the root of your project.'\n )\n );\n exitSync(1);\n }\n // Check if gt-next or gt-react is installed\n if (\n frameworkType === 'next-app' &&\n !isPackageInstalled(Libraries.GT_NEXT, packageJson)\n ) {\n const packageManager = await getPackageManager();\n const spinner = logger.createSpinner('timer');\n spinner.start(\n `Installing ${Libraries.GT_NEXT} with ${packageManager.name}...`\n );\n await installPackage(Libraries.GT_NEXT, packageManager);\n spinner.stop(chalk.green(`Automatically installed ${Libraries.GT_NEXT}.`));\n } else if (\n ['next-pages', 'react', 'redwood', 'vite', 'gatsby'].includes(\n frameworkType\n ) &&\n !isPackageInstalled(Libraries.GT_REACT, packageJson)\n ) {\n const packageManager = await getPackageManager();\n const spinner = logger.createSpinner('timer');\n spinner.start(\n `Installing ${Libraries.GT_REACT} with ${packageManager.name}...`\n );\n await installPackage(Libraries.GT_REACT, packageManager);\n spinner.stop(chalk.green(`Automatically installed ${Libraries.GT_REACT}.`));\n }\n\n const errors: string[] = [];\n const warnings: string[] = [];\n let filesUpdated: string[] = [];\n\n // Read tsconfig.json if it exists\n const tsconfigPath = findFilepath(['tsconfig.json']);\n const tsconfigJson = tsconfigPath ? loadConfig(tsconfigPath) : undefined;\n\n if (frameworkType === 'next-app') {\n // Check if they have a next.config.js file\n const nextConfigPath = findFilepath([\n './next.config.js',\n './next.config.ts',\n './next.config.mjs',\n './next.config.mts',\n ]);\n if (!nextConfigPath) {\n logger.error('No next.config.[js|ts|mjs|mts] file found.');\n exitSync(1);\n }\n\n const mergeOptions = {\n ...options,\n disableIds: true,\n disableFormatting: true,\n skipTs: true,\n addGTProvider: true,\n };\n const spinner = logger.createSpinner();\n spinner.start('Wrapping JSX content with <T> tags...');\n // Wrap all JSX elements in the src directory with a <T> tag, with unique ids\n const { filesUpdated: filesUpdatedNext } = await wrapContentNext(\n mergeOptions,\n Libraries.GT_NEXT,\n errors,\n warnings\n );\n filesUpdated = [...filesUpdated, ...filesUpdatedNext];\n\n spinner.stop(\n chalk.green(\n `Success! Updated ${chalk.bold.cyan(filesUpdated.length)} files:\\n`\n ) + filesUpdated.map((file) => `${chalk.green('-')} ${file}`).join('\\n')\n );\n\n // Add the withGTConfig() function to the next.config.js file\n await handleInitGT(\n nextConfigPath,\n errors,\n warnings,\n filesUpdated,\n packageJson,\n tsconfigJson\n );\n logger.step(\n chalk.green(`Added withGTConfig() to your ${nextConfigPath} file.`)\n );\n }\n\n if (errors.length > 0) {\n logger.error(chalk.red('Failed to write files:\\n') + errors.join('\\n'));\n }\n\n if (warnings.length > 0) {\n logger.warn(\n chalk.yellow('Warnings encountered:') +\n '\\n' +\n warnings.map((warning) => `${chalk.yellow('-')} ${warning}`).join('\\n')\n );\n }\n\n const formatter = await detectFormatter();\n\n if (!formatter || filesUpdated.length === 0) {\n return;\n }\n\n const applyFormatting = useDefaults\n ? true\n : await promptConfirm({\n message: `Would you like the wizard to auto-format the modified files? ${chalk.dim(\n `(${formatter})`\n )}`,\n defaultValue: true,\n });\n // Format updated files if formatters are available\n if (applyFormatting) await formatFiles(filesUpdated, formatter);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gt",
3
- "version": "2.16.1",
3
+ "version": "2.16.3",
4
4
  "main": "dist/index.js",
5
5
  "bin": "bin/main.js",
6
6
  "files": [
@@ -117,8 +117,8 @@
117
117
  "yaml": "^2.8.0",
118
118
  "@generaltranslation/icu": "0.1.1",
119
119
  "@generaltranslation/format": "0.1.4",
120
- "@generaltranslation/python-extractor": "0.2.34",
121
120
  "@generaltranslation/supported-locales": "2.1.14",
121
+ "@generaltranslation/python-extractor": "0.2.34",
122
122
  "generaltranslation": "9.1.1",
123
123
  "gt-remark": "1.0.11"
124
124
  },