@vocoder/cli 0.2.4 → 0.8.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/LICENSE +21 -0
- package/dist/bin.mjs +68 -106
- package/dist/bin.mjs.map +1 -1
- package/dist/chunk-7LFRSUPU.mjs +50638 -0
- package/dist/chunk-7LFRSUPU.mjs.map +1 -0
- package/dist/lib.d.mts +50 -3
- package/dist/lib.mjs +8 -5
- package/dist/lib.mjs.map +1 -1
- package/package.json +15 -15
- package/dist/chunk-TFAPB25S.mjs +0 -277
- package/dist/chunk-TFAPB25S.mjs.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vocoder
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/bin.mjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __createRequire } from 'module'; const require = __createRequire(import.meta.url);
|
|
2
3
|
import {
|
|
3
4
|
StringExtractor,
|
|
4
5
|
buildInstallCommand,
|
|
5
6
|
detectLocalEcosystem,
|
|
6
7
|
getPackagesToInstall,
|
|
7
|
-
getSetupSnippets
|
|
8
|
-
|
|
8
|
+
getSetupSnippets,
|
|
9
|
+
loadVocoderConfig
|
|
10
|
+
} from "./chunk-7LFRSUPU.mjs";
|
|
9
11
|
|
|
10
12
|
// src/bin.ts
|
|
11
13
|
import { Command } from "commander";
|
|
@@ -122,6 +124,7 @@ var VocoderAPI = class {
|
|
|
122
124
|
return {
|
|
123
125
|
projectName: data.projectName,
|
|
124
126
|
organizationName: data.organizationName,
|
|
127
|
+
shortCode: data.shortCode,
|
|
125
128
|
sourceLocale: data.sourceLocale,
|
|
126
129
|
targetLocales: data.targetLocales,
|
|
127
130
|
targetBranches: data.targetBranches ?? ["main"],
|
|
@@ -184,7 +187,7 @@ var VocoderAPI = class {
|
|
|
184
187
|
branch,
|
|
185
188
|
stringEntries,
|
|
186
189
|
targetLocales,
|
|
187
|
-
stringsHash,
|
|
190
|
+
...options?.force ? {} : { stringsHash },
|
|
188
191
|
...options?.requestedMode ? { requestedMode: options.requestedMode } : {},
|
|
189
192
|
...typeof options?.requestedMaxWaitMs === "number" ? { requestedMaxWaitMs: options.requestedMaxWaitMs } : {},
|
|
190
193
|
...options?.clientRunId ? { clientRunId: options.clientRunId } : {},
|
|
@@ -2932,7 +2935,6 @@ function matchBranchPattern(branch, pattern) {
|
|
|
2932
2935
|
import * as p7 from "@clack/prompts";
|
|
2933
2936
|
import chalk7 from "chalk";
|
|
2934
2937
|
import { config as loadEnv2 } from "dotenv";
|
|
2935
|
-
import { loadVocoderConfig } from "@vocoder/extractor";
|
|
2936
2938
|
loadEnv2();
|
|
2937
2939
|
function validateLocalConfig(config) {
|
|
2938
2940
|
if (!config.apiKey || config.apiKey.length === 0) {
|
|
@@ -3092,20 +3094,9 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
|
|
|
3092
3094
|
}
|
|
3093
3095
|
|
|
3094
3096
|
// src/commands/sync.ts
|
|
3095
|
-
function
|
|
3097
|
+
function computeFingerprint(shortCode, texts) {
|
|
3096
3098
|
const sorted = [...texts].sort();
|
|
3097
|
-
return createHash("sha256").update(sorted.join("\0")).digest("hex").slice(0,
|
|
3098
|
-
}
|
|
3099
|
-
function readCachedStringsHash(projectRoot, branch) {
|
|
3100
|
-
const filePath = getCacheFilePath(projectRoot, branch);
|
|
3101
|
-
if (!existsSync3(filePath)) return null;
|
|
3102
|
-
try {
|
|
3103
|
-
const raw = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
3104
|
-
if (isRecord(raw) && typeof raw.stringsHash === "string")
|
|
3105
|
-
return raw.stringsHash;
|
|
3106
|
-
} catch {
|
|
3107
|
-
}
|
|
3108
|
-
return null;
|
|
3099
|
+
return createHash("sha256").update(`${shortCode}:${sorted.join("\0")}`).digest("hex").slice(0, 12);
|
|
3109
3100
|
}
|
|
3110
3101
|
function isRecord(value) {
|
|
3111
3102
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -3150,69 +3141,52 @@ function parseTranslations(value) {
|
|
|
3150
3141
|
}
|
|
3151
3142
|
return Object.keys(translations).length > 0 ? translations : null;
|
|
3152
3143
|
}
|
|
3153
|
-
function getCacheFilePath(projectRoot,
|
|
3154
|
-
|
|
3155
|
-
return join4(
|
|
3156
|
-
projectRoot,
|
|
3157
|
-
"node_modules",
|
|
3158
|
-
".vocoder",
|
|
3159
|
-
"cache",
|
|
3160
|
-
"sync",
|
|
3161
|
-
`${branchHash}.json`
|
|
3162
|
-
);
|
|
3144
|
+
function getCacheFilePath(projectRoot, fingerprint) {
|
|
3145
|
+
return join4(projectRoot, "node_modules", ".vocoder", "cache", `${fingerprint}.json`);
|
|
3163
3146
|
}
|
|
3164
|
-
function
|
|
3165
|
-
const
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
const raw = readFileSync3(cacheFilePath, "utf-8");
|
|
3173
|
-
const parsed = JSON.parse(raw);
|
|
3174
|
-
if (!isRecord(parsed)) {
|
|
3175
|
-
continue;
|
|
3176
|
-
}
|
|
3177
|
-
const translations = parseTranslations(parsed.translations);
|
|
3178
|
-
if (!translations) {
|
|
3179
|
-
continue;
|
|
3180
|
-
}
|
|
3181
|
-
const localeMetadata = parseLocaleMetadata(parsed.localeMetadata);
|
|
3182
|
-
return {
|
|
3183
|
-
source: "local-cache",
|
|
3184
|
-
translations,
|
|
3185
|
-
localeMetadata,
|
|
3186
|
-
snapshotBatchId: typeof parsed.snapshotBatchId === "string" ? parsed.snapshotBatchId : void 0,
|
|
3187
|
-
completedAt: typeof parsed.completedAt === "string" ? parsed.completedAt : null,
|
|
3188
|
-
cacheBranch: candidateBranch
|
|
3189
|
-
};
|
|
3190
|
-
} catch {
|
|
3147
|
+
function buildTranslationData(params) {
|
|
3148
|
+
const textToHash = new Map(params.stringEntries.map((e) => [e.text, e.key]));
|
|
3149
|
+
const hashKeyed = {};
|
|
3150
|
+
for (const [locale, localeMap] of Object.entries(params.translations)) {
|
|
3151
|
+
hashKeyed[locale] = {};
|
|
3152
|
+
for (const [text2, translation] of Object.entries(localeMap)) {
|
|
3153
|
+
const hash = textToHash.get(text2);
|
|
3154
|
+
if (hash) hashKeyed[locale][hash] = translation;
|
|
3191
3155
|
}
|
|
3192
3156
|
}
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
{
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
);
|
|
3203
|
-
const payload = {
|
|
3204
|
-
version: 1,
|
|
3205
|
-
branch: params.branch,
|
|
3206
|
-
sourceLocale: params.sourceLocale,
|
|
3207
|
-
targetLocales: params.targetLocales,
|
|
3208
|
-
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3209
|
-
...params.stringsHash ? { stringsHash: params.stringsHash } : {},
|
|
3210
|
-
...params.snapshotBatchId ? { snapshotBatchId: params.snapshotBatchId } : {},
|
|
3211
|
-
...params.completedAt ? { completedAt: params.completedAt } : {},
|
|
3212
|
-
...params.localeMetadata ? { localeMetadata: params.localeMetadata } : {},
|
|
3213
|
-
translations: params.translations
|
|
3157
|
+
const locales = {};
|
|
3158
|
+
for (const code of [params.sourceLocale, ...params.targetLocales]) {
|
|
3159
|
+
const meta = params.localeMetadata?.[code];
|
|
3160
|
+
if (meta) locales[code] = { nativeName: meta.nativeName, ...meta.dir ? { dir: meta.dir } : {} };
|
|
3161
|
+
}
|
|
3162
|
+
return {
|
|
3163
|
+
config: { sourceLocale: params.sourceLocale, targetLocales: params.targetLocales, locales },
|
|
3164
|
+
translations: hashKeyed,
|
|
3165
|
+
updatedAt: params.updatedAt
|
|
3214
3166
|
};
|
|
3215
|
-
|
|
3167
|
+
}
|
|
3168
|
+
function readLocalCache(params) {
|
|
3169
|
+
const cacheFilePath = getCacheFilePath(params.projectRoot, params.fingerprint);
|
|
3170
|
+
if (!existsSync3(cacheFilePath)) return null;
|
|
3171
|
+
try {
|
|
3172
|
+
const raw = readFileSync3(cacheFilePath, "utf-8");
|
|
3173
|
+
const parsed = JSON.parse(raw);
|
|
3174
|
+
if (!isRecord(parsed)) return null;
|
|
3175
|
+
const inner = isRecord(parsed.config) ? parsed : null;
|
|
3176
|
+
if (!inner) return null;
|
|
3177
|
+
const translations = parseTranslations(inner.translations);
|
|
3178
|
+
if (!translations) return null;
|
|
3179
|
+
const localeMetadata = isRecord(inner.config) ? parseLocaleMetadata(inner.config.locales) : void 0;
|
|
3180
|
+
return { source: "local-cache", translations, localeMetadata };
|
|
3181
|
+
} catch {
|
|
3182
|
+
return null;
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
function writeCache(params) {
|
|
3186
|
+
const cacheDir = join4(params.projectRoot, "node_modules", ".vocoder", "cache");
|
|
3187
|
+
mkdirSync2(cacheDir, { recursive: true });
|
|
3188
|
+
const cacheFilePath = getCacheFilePath(params.projectRoot, params.fingerprint);
|
|
3189
|
+
writeFileSync4(cacheFilePath, JSON.stringify(params.data), "utf-8");
|
|
3216
3190
|
return cacheFilePath;
|
|
3217
3191
|
}
|
|
3218
3192
|
function resolveEffectiveModeFromPolicy(params) {
|
|
@@ -3469,30 +3443,20 @@ async function sync(options = {}) {
|
|
|
3469
3443
|
`Deduped ${extractedStrings.length} extracted entries into ${stringEntries.length} unique source strings`
|
|
3470
3444
|
);
|
|
3471
3445
|
}
|
|
3472
|
-
const
|
|
3446
|
+
const fingerprint = computeFingerprint(config.shortCode, sourceStrings);
|
|
3473
3447
|
if (!options.force) {
|
|
3474
|
-
const
|
|
3475
|
-
if (
|
|
3476
|
-
const cacheFile = getCacheFilePath(projectRoot, branch);
|
|
3477
|
-
if (cachedHash) {
|
|
3478
|
-
p8.log.info(
|
|
3479
|
-
`Local cache: ${chalk8.dim(cacheFile)}
|
|
3480
|
-
cached hash ${chalk8.cyan(cachedHash.slice(0, 8))}\u2026 vs current ${chalk8.cyan(currentHash.slice(0, 8))}\u2026 \u2014 ${cachedHash === currentHash ? chalk8.green("match") : chalk8.yellow("changed")}`
|
|
3481
|
-
);
|
|
3482
|
-
} else {
|
|
3483
|
-
p8.log.info(`No local cache found at ${chalk8.dim(cacheFile)} \u2014 will submit to API`);
|
|
3484
|
-
}
|
|
3485
|
-
}
|
|
3486
|
-
if (cachedHash && cachedHash === currentHash) {
|
|
3448
|
+
const cacheFile = getCacheFilePath(projectRoot, fingerprint);
|
|
3449
|
+
if (existsSync3(cacheFile)) {
|
|
3487
3450
|
if (options.verbose) {
|
|
3488
|
-
p8.log.info(
|
|
3489
|
-
"Skipping API submission \u2014 delete node_modules/.vocoder to force a fresh sync"
|
|
3490
|
-
);
|
|
3451
|
+
p8.log.info(`Cache hit: ${chalk8.dim(cacheFile)} (fingerprint ${chalk8.cyan(fingerprint)})`);
|
|
3491
3452
|
}
|
|
3492
3453
|
const duration2 = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
3493
3454
|
p8.outro(`Up to date (${duration2}s)`);
|
|
3494
3455
|
return 0;
|
|
3495
3456
|
}
|
|
3457
|
+
if (options.verbose) {
|
|
3458
|
+
p8.log.info(`No cache for fingerprint ${chalk8.cyan(fingerprint)} \u2014 will submit to API`);
|
|
3459
|
+
}
|
|
3496
3460
|
}
|
|
3497
3461
|
spinner4.start("Submitting strings to Vocoder API");
|
|
3498
3462
|
const batchResponse = await api.submitTranslation(
|
|
@@ -3502,7 +3466,8 @@ async function sync(options = {}) {
|
|
|
3502
3466
|
{
|
|
3503
3467
|
requestedMode,
|
|
3504
3468
|
requestedMaxWaitMs: waitTimeoutMs,
|
|
3505
|
-
clientRunId: randomUUID()
|
|
3469
|
+
clientRunId: randomUUID(),
|
|
3470
|
+
force: options.force
|
|
3506
3471
|
},
|
|
3507
3472
|
repoIdentity ? { ...repoIdentity, commitSha } : { commitSha }
|
|
3508
3473
|
);
|
|
@@ -3587,14 +3552,13 @@ async function sync(options = {}) {
|
|
|
3587
3552
|
);
|
|
3588
3553
|
}
|
|
3589
3554
|
spinner4.start("Loading fallback translations");
|
|
3590
|
-
const localFallback =
|
|
3555
|
+
const localFallback = readLocalCache({
|
|
3591
3556
|
projectRoot,
|
|
3592
|
-
|
|
3557
|
+
fingerprint
|
|
3593
3558
|
});
|
|
3594
3559
|
if (localFallback) {
|
|
3595
3560
|
artifacts = localFallback;
|
|
3596
|
-
|
|
3597
|
-
spinner4.stop(`Using local cached snapshot (${cacheBranchLabel})`);
|
|
3561
|
+
spinner4.stop(`Using local cached snapshot (${fingerprint})`);
|
|
3598
3562
|
} else {
|
|
3599
3563
|
try {
|
|
3600
3564
|
const apiSnapshot = await fetchApiSnapshot(api, {
|
|
@@ -3633,24 +3597,22 @@ async function sync(options = {}) {
|
|
|
3633
3597
|
translations: artifacts.translations
|
|
3634
3598
|
});
|
|
3635
3599
|
try {
|
|
3636
|
-
const
|
|
3637
|
-
projectRoot,
|
|
3638
|
-
branch,
|
|
3600
|
+
const data = buildTranslationData({
|
|
3639
3601
|
sourceLocale: config.sourceLocale,
|
|
3640
3602
|
targetLocales: config.targetLocales,
|
|
3603
|
+
stringEntries,
|
|
3641
3604
|
translations: finalTranslations,
|
|
3642
3605
|
localeMetadata: artifacts.localeMetadata,
|
|
3643
|
-
|
|
3644
|
-
snapshotBatchId: artifacts.snapshotBatchId ?? (artifacts.source === "fresh" ? batchResponse.batchId : batchResponse.latestCompletedBatchId),
|
|
3645
|
-
completedAt: artifacts.completedAt ?? (artifacts.source === "fresh" ? (/* @__PURE__ */ new Date()).toISOString() : null)
|
|
3606
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3646
3607
|
});
|
|
3608
|
+
const cachePath = writeCache({ projectRoot, fingerprint, data });
|
|
3647
3609
|
if (options.verbose) {
|
|
3648
|
-
p8.log.info(`
|
|
3610
|
+
p8.log.info(`Cache written: ${cachePath}`);
|
|
3649
3611
|
}
|
|
3650
3612
|
} catch (error) {
|
|
3651
3613
|
if (options.verbose) {
|
|
3652
3614
|
const message = error instanceof Error ? error.message : "Unknown cache write error";
|
|
3653
|
-
p8.log.warn(`Failed to write
|
|
3615
|
+
p8.log.warn(`Failed to write cache: ${message}`);
|
|
3654
3616
|
}
|
|
3655
3617
|
}
|
|
3656
3618
|
if (artifacts.source !== "fresh") {
|