@vocoder/cli 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  getPackagesToInstall,
8
8
  getSetupSnippets,
9
9
  loadVocoderConfig
10
- } from "./chunk-7LFRSUPU.mjs";
10
+ } from "./chunk-73U4VZYP.mjs";
11
11
 
12
12
  // src/bin.ts
13
13
  import { Command } from "commander";
@@ -167,7 +167,9 @@ var VocoderAPI = class {
167
167
  key: entry.key || this.stableTextKey(`${entry.text}:${index}`),
168
168
  text: entry.text,
169
169
  ...entry.context ? { context: entry.context } : {},
170
- ...entry.formality ? { formality: entry.formality } : {}
170
+ ...entry.formality ? { formality: entry.formality } : {},
171
+ ...entry.uiRole ? { uiRole: entry.uiRole } : {},
172
+ ...entry.featureArea ? { featureArea: entry.featureArea } : {}
171
173
  }));
172
174
  }
173
175
  async submitTranslation(branch, entries, targetLocales, options, repoIdentity) {
@@ -193,7 +195,8 @@ var VocoderAPI = class {
193
195
  ...options?.clientRunId ? { clientRunId: options.clientRunId } : {},
194
196
  ...repoIdentity?.repoCanonical ? { repoCanonical: repoIdentity.repoCanonical } : {},
195
197
  ...repoIdentity?.repoAppDir !== void 0 ? { repoAppDir: repoIdentity.repoAppDir } : {},
196
- ...repoIdentity?.commitSha ? { commitSha: repoIdentity.commitSha } : {}
198
+ ...repoIdentity?.commitSha ? { commitSha: repoIdentity.commitSha } : {},
199
+ ...options?.appIndustry ? { appIndustry: options.appIndustry } : {}
197
200
  })
198
201
  },
199
202
  "Translation submission failed"
@@ -745,10 +748,26 @@ function clearAuthData() {
745
748
  // src/utils/write-config.ts
746
749
  import { existsSync, writeFileSync as writeFileSync2 } from "fs";
747
750
  import { join as join2 } from "path";
751
+ function findExistingConfig(cwd = process.cwd()) {
752
+ for (const name of [
753
+ "vocoder.config.ts",
754
+ "vocoder.config.js",
755
+ "vocoder.config.json"
756
+ ]) {
757
+ const candidate = join2(cwd, name);
758
+ if (existsSync(candidate)) return candidate;
759
+ }
760
+ return null;
761
+ }
748
762
  function writeVocoderConfig(options) {
749
- const { targetBranches = ["main"], cwd = process.cwd() } = options;
750
- const configPath = join2(cwd, "vocoder.config.ts");
751
- if (existsSync(configPath)) return false;
763
+ const {
764
+ targetBranches = ["main"],
765
+ useTypeScript = true,
766
+ cwd = process.cwd()
767
+ } = options;
768
+ if (findExistingConfig(cwd)) return null;
769
+ const ext = useTypeScript ? "ts" : "js";
770
+ const configPath = join2(cwd, `vocoder.config.${ext}`);
752
771
  const branchesStr = targetBranches.map((b) => `'${b}'`).join(", ");
753
772
  const content = `import { defineConfig } from '@vocoder/config'
754
773
 
@@ -767,9 +786,9 @@ export default defineConfig({
767
786
  `;
768
787
  try {
769
788
  writeFileSync2(configPath, content, "utf-8");
770
- return true;
789
+ return `vocoder.config.${ext}`;
771
790
  } catch {
772
- return false;
791
+ return null;
773
792
  }
774
793
  }
775
794
 
@@ -2002,6 +2021,7 @@ function printPlanLimitMessage(apiUrl, message) {
2002
2021
  function runScaffold(params) {
2003
2022
  const { sourceLocale, targetBranches } = params;
2004
2023
  const detection = detectLocalEcosystem();
2024
+ const useTypeScript = detection.isTypeScript;
2005
2025
  if (detection.ecosystem) {
2006
2026
  const frameworkLabel = detection.framework ?? detection.ecosystem;
2007
2027
  const pmLabel = detection.packageManager;
@@ -2075,12 +2095,13 @@ function runScaffold(params) {
2075
2095
  printCodeBlock(step.code);
2076
2096
  if (i < steps.length - 1) p5.log.message("");
2077
2097
  }
2078
- const written = writeVocoderConfig({ targetBranches });
2098
+ const written = writeVocoderConfig({ targetBranches, useTypeScript });
2079
2099
  if (written) {
2080
- p5.log.success(`Created ${chalk6.cyan("vocoder.config.ts")}`);
2081
- } else if (!existsSync2(join3(process.cwd(), "vocoder.config.ts"))) {
2100
+ p5.log.success(`Created ${chalk6.cyan(written)}`);
2101
+ } else if (!findExistingConfig(process.cwd())) {
2102
+ const ext = useTypeScript ? "ts" : "js";
2082
2103
  p5.log.warn(
2083
- "Could not write vocoder.config.ts \u2014 create it manually with your extraction patterns."
2104
+ `Could not write vocoder.config.${ext} \u2014 create it manually with your extraction patterns.`
2084
2105
  );
2085
2106
  }
2086
2107
  p5.log.message("");
@@ -2352,8 +2373,9 @@ async function init(options = {}) {
2352
2373
  return 1;
2353
2374
  }
2354
2375
  }
2355
- const written = writeVocoderConfig({ targetBranches: exactMatch.targetBranches ?? ["main"] });
2356
- if (written) p5.log.success(`Created ${chalk6.cyan("vocoder.config.ts")}`);
2376
+ const isTs = detectLocalEcosystem().isTypeScript;
2377
+ const written = writeVocoderConfig({ targetBranches: exactMatch.targetBranches ?? ["main"], useTypeScript: isTs });
2378
+ if (written) p5.log.success(`Created ${chalk6.cyan(written)}`);
2357
2379
  p5.outro("Vocoder is already set up for this repository.");
2358
2380
  return 0;
2359
2381
  }
@@ -2361,8 +2383,9 @@ async function init(options = {}) {
2361
2383
  const wholeRepo = lookup.existingApps.find((a) => a.appDir === "");
2362
2384
  if (wholeRepo) {
2363
2385
  p5.log.success(`Project: ${chalk6.bold(wholeRepo.projectName)}`);
2364
- const written = writeVocoderConfig({ targetBranches: ["main"] });
2365
- if (written) p5.log.success(`Created ${chalk6.cyan("vocoder.config.ts")}`);
2386
+ const isTs = detectLocalEcosystem().isTypeScript;
2387
+ const written = writeVocoderConfig({ targetBranches: ["main"], useTypeScript: isTs });
2388
+ if (written) p5.log.success(`Created ${chalk6.cyan(written)}`);
2366
2389
  p5.outro("Vocoder is already set up for this repository.");
2367
2390
  return 0;
2368
2391
  }
@@ -3305,7 +3328,9 @@ function buildStringEntries(extractedStrings) {
3305
3328
  key: str.key,
3306
3329
  text: str.text,
3307
3330
  ...str.context ? { context: str.context } : {},
3308
- ...str.formality ? { formality: str.formality } : {}
3331
+ ...str.formality ? { formality: str.formality } : {},
3332
+ ...str.uiRole ? { uiRole: str.uiRole } : {},
3333
+ ...str.featureArea ? { featureArea: str.featureArea } : {}
3309
3334
  });
3310
3335
  continue;
3311
3336
  }
@@ -3354,9 +3379,7 @@ async function sync(options = {}) {
3354
3379
  }
3355
3380
  const spinner4 = p8.spinner();
3356
3381
  try {
3357
- spinner4.start("Detecting branch");
3358
3382
  const branch = detectBranch(options.branch);
3359
- spinner4.stop(`Branch: ${chalk8.cyan(branch)}`);
3360
3383
  spinner4.start("Loading project configuration");
3361
3384
  const localConfig = {
3362
3385
  apiKey: mergedConfig.apiKey,
@@ -3371,14 +3394,17 @@ async function sync(options = {}) {
3371
3394
  policyDefaultMaxWaitMs: apiConfig.syncPolicy.defaultMaxWaitMs,
3372
3395
  fallbackTimeoutMs: 6e4
3373
3396
  });
3397
+ const fileConfig = loadVocoderConfig(process.cwd());
3374
3398
  const config = {
3375
3399
  ...localConfig,
3376
3400
  ...apiConfig,
3377
3401
  includePattern: mergedConfig.includePattern,
3378
3402
  excludePattern: mergedConfig.excludePattern,
3379
- timeout: waitTimeoutMs
3403
+ timeout: waitTimeoutMs,
3404
+ ...fileConfig?.appIndustry ? { appIndustry: fileConfig.appIndustry } : {},
3405
+ ...fileConfig?.formality ? { formality: fileConfig.formality } : {}
3380
3406
  };
3381
- spinner4.stop("Project configuration loaded");
3407
+ spinner4.stop(`Branch: ${chalk8.cyan(branch)}`);
3382
3408
  if (!options.force && !isTargetBranch(branch, config.targetBranches)) {
3383
3409
  p8.log.warn(
3384
3410
  `Skipping translations (${chalk8.cyan(branch)} is not a target branch)`
@@ -3467,19 +3493,20 @@ async function sync(options = {}) {
3467
3493
  requestedMode,
3468
3494
  requestedMaxWaitMs: waitTimeoutMs,
3469
3495
  clientRunId: randomUUID(),
3470
- force: options.force
3496
+ force: options.force,
3497
+ // Sync appIndustry from vocoder.config.ts to ProjectApp on every push
3498
+ ...config.appIndustry ? { appIndustry: config.appIndustry } : {}
3471
3499
  },
3472
3500
  repoIdentity ? { ...repoIdentity, commitSha } : { commitSha }
3473
3501
  );
3474
- spinner4.stop(
3475
- `Submitted to API - Batch ${chalk8.cyan(batchResponse.batchId)}`
3476
- );
3502
+ spinner4.stop("Strings submitted");
3477
3503
  const effectiveMode = batchResponse.effectiveMode ?? resolveEffectiveModeFromPolicy({
3478
3504
  branch,
3479
3505
  requestedMode,
3480
3506
  policy: config.syncPolicy
3481
3507
  });
3482
3508
  if (options.verbose) {
3509
+ p8.log.info(`Batch: ${chalk8.dim(batchResponse.batchId)}`);
3483
3510
  p8.log.info(`Requested mode: ${requestedMode}`);
3484
3511
  p8.log.info(`Effective mode: ${effectiveMode}`);
3485
3512
  p8.log.info(`Wait timeout: ${waitTimeoutMs}ms`);
@@ -3488,24 +3515,17 @@ async function sync(options = {}) {
3488
3515
  }
3489
3516
  }
3490
3517
  if (batchResponse.status === "UP_TO_DATE" && batchResponse.noChanges) {
3491
- p8.log.success("No changes detected - strings are up to date");
3492
- }
3493
- p8.log.info(`New strings: ${chalk8.cyan(batchResponse.newStrings)}`);
3494
- if (batchResponse.deletedStrings && batchResponse.deletedStrings > 0) {
3495
- p8.log.info(
3496
- `Deleted strings: ${chalk8.yellow(batchResponse.deletedStrings)} (archived)`
3497
- );
3498
- }
3499
- p8.log.info(`Total strings: ${chalk8.cyan(batchResponse.totalStrings)}`);
3500
- if (batchResponse.newStrings === 0) {
3501
- p8.log.success("No new strings - using existing translations");
3518
+ p8.log.success(`Up to date \u2014 ${chalk8.cyan(batchResponse.totalStrings)} strings, no changes`);
3519
+ } else if (batchResponse.newStrings === 0) {
3520
+ const archivedNote = batchResponse.deletedStrings && batchResponse.deletedStrings > 0 ? `, ${chalk8.yellow(batchResponse.deletedStrings)} archived` : "";
3521
+ p8.log.success(`No new strings \u2014 ${chalk8.cyan(batchResponse.totalStrings)} total${archivedNote}, using existing translations`);
3502
3522
  } else {
3503
- p8.log.info(
3504
- `Syncing to ${config.targetLocales.length} locales (${config.targetLocales.join(", ")})`
3505
- );
3506
- if (batchResponse.estimatedTime) {
3507
- p8.log.info(`Estimated time: ~${batchResponse.estimatedTime}s`);
3523
+ const statParts = [`${chalk8.cyan(batchResponse.newStrings)} new, ${chalk8.cyan(batchResponse.totalStrings)} total`];
3524
+ if (batchResponse.deletedStrings && batchResponse.deletedStrings > 0) {
3525
+ statParts.push(`${chalk8.yellow(batchResponse.deletedStrings)} archived`);
3508
3526
  }
3527
+ const estTime = batchResponse.estimatedTime ? ` (~${batchResponse.estimatedTime}s)` : "";
3528
+ p8.log.info(`${statParts.join(", ")} \u2192 syncing to ${config.targetLocales.join(", ")}${estTime}`);
3509
3529
  }
3510
3530
  let artifacts = null;
3511
3531
  if (batchResponse.translations) {
@@ -3516,7 +3536,8 @@ async function sync(options = {}) {
3516
3536
  }
3517
3537
  let waitError = null;
3518
3538
  if (!artifacts && (effectiveMode === "required" || effectiveMode === "best-effort")) {
3519
- spinner4.start(`Waiting for translations (max ${waitTimeoutMs}ms)`);
3539
+ const waitTimeoutSecs = Math.round(waitTimeoutMs / 1e3);
3540
+ spinner4.start(`Waiting for translations (max ${waitTimeoutSecs}s)`);
3520
3541
  let lastProgress = 0;
3521
3542
  try {
3522
3543
  const completion = await api.waitForCompletion(