@kopynator/cli 1.2.0 → 1.3.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/PUBLISH.md +59 -0
- package/README.md +15 -2
- package/dist/index.js +121 -3
- package/package.json +2 -1
- package/src/commands/index.ts +1 -0
- package/src/commands/upload.ts +145 -0
- package/src/index.ts +9 -2
package/PUBLISH.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Publicar @kopynator/cli en npm
|
|
2
|
+
|
|
3
|
+
## Requisitos
|
|
4
|
+
|
|
5
|
+
- Cuenta en [npmjs.com](https://www.npmjs.com) con acceso al scope `@kopynator` (o ser el propietario de la organización `kopynator`).
|
|
6
|
+
- `npm` instalado y sesión iniciada.
|
|
7
|
+
|
|
8
|
+
## Pasos
|
|
9
|
+
|
|
10
|
+
### 1. Iniciar sesión en npm (si no lo has hecho)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm login
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Te pedirá usuario, contraseña y email (o 2FA si lo tienes activado).
|
|
17
|
+
|
|
18
|
+
### 2. Compilar y publicar desde el paquete CLI
|
|
19
|
+
|
|
20
|
+
Desde la **raíz del monorepo** (para que las workspaces estén bien) o desde `packages/cli`:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd packages/cli
|
|
24
|
+
npm run build
|
|
25
|
+
npm publish
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- El script `prepublishOnly` ejecuta `npm run build` antes de publicar, así que `npm publish` ya deja el `dist/` actualizado.
|
|
29
|
+
- `publishConfig.access: "public"` en `package.json` hace que el paquete scoped `@kopynator/cli` sea público.
|
|
30
|
+
|
|
31
|
+
### 3. Comprobar la versión publicada
|
|
32
|
+
|
|
33
|
+
- En npm: https://www.npmjs.com/package/@kopynator/cli
|
|
34
|
+
- Desde terminal: `npm view @kopynator/cli version`
|
|
35
|
+
|
|
36
|
+
## Subir una nueva versión (después de cambios)
|
|
37
|
+
|
|
38
|
+
1. **Subir versión** (elegir una):
|
|
39
|
+
- Parche (1.3.0 → 1.3.1): `npm run release:patch`
|
|
40
|
+
- Minor (1.3.0 → 1.4.0): `npm run release:minor`
|
|
41
|
+
- Major (1.4.0 → 2.0.0): `npm run release:major`
|
|
42
|
+
|
|
43
|
+
O editar a mano `version` en `package.json` y en `src/index.ts` (`.version('X.Y.Z')`).
|
|
44
|
+
|
|
45
|
+
2. **Compilar y publicar**:
|
|
46
|
+
```bash
|
|
47
|
+
npm run build
|
|
48
|
+
npm publish
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
3. (Opcional) **Crear tag en Git**:
|
|
52
|
+
```bash
|
|
53
|
+
git tag @kopynator/cli@1.3.0
|
|
54
|
+
git push origin @kopynator/cli@1.3.0
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Versión actual
|
|
58
|
+
|
|
59
|
+
Tras añadir el comando `upload`, la versión del CLI es **1.3.0**.
|
package/README.md
CHANGED
|
@@ -33,13 +33,26 @@ Validates your local JSON translation files for syntax errors. Useful for CI/CD
|
|
|
33
33
|
npx -y @kopynator/cli check
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
### 3. Sync
|
|
37
|
-
|
|
36
|
+
### 3. Sync
|
|
37
|
+
Downloads translations from the Kopynator Cloud and saves them as local JSON files (e.g. `src/assets/i18n/en.json`).
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
npx -y @kopynator/cli sync
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
### 4. Upload
|
|
44
|
+
Uploads a JSON translation file to the Kopynator Cloud. Keys are merged/updated for the given language. Use the same API key (token) as in your app or `kopynator.config.json`.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Language inferred from filename (es.json → es)
|
|
48
|
+
npx -y @kopynator/cli upload --file=src/assets/i18n/es.json
|
|
49
|
+
|
|
50
|
+
# Explicit language
|
|
51
|
+
npx -y @kopynator/cli upload --file=locales/en.json --lang=en
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
After uploading, run `sync` to download the latest state from the cloud if needed.
|
|
55
|
+
|
|
43
56
|
## Configuration
|
|
44
57
|
The `init` command creates a `kopynator.config.json` file in your root:
|
|
45
58
|
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
27
|
var import_commander = require("commander");
|
|
28
|
-
var
|
|
28
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
29
29
|
|
|
30
30
|
// src/commands/init.ts
|
|
31
31
|
var import_inquirer = __toESM(require("inquirer"));
|
|
@@ -494,14 +494,132 @@ async function syncCommand() {
|
|
|
494
494
|
}
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
+
// src/commands/upload.ts
|
|
498
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
499
|
+
var import_fs4 = __toESM(require("fs"));
|
|
500
|
+
var import_path4 = __toESM(require("path"));
|
|
501
|
+
var import_ora2 = __toESM(require("ora"));
|
|
502
|
+
var BATCH_SIZE = 500;
|
|
503
|
+
function flatten(data, prefix = "") {
|
|
504
|
+
const result = {};
|
|
505
|
+
for (const key in data) {
|
|
506
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
507
|
+
const value = data[key];
|
|
508
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
509
|
+
Object.assign(result, flatten(value, fullKey));
|
|
510
|
+
} else {
|
|
511
|
+
result[fullKey] = String(value);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
function extractApiKey2() {
|
|
517
|
+
const appConfigPath = import_path4.default.join(process.cwd(), "src/app/app.config.ts");
|
|
518
|
+
const appModulePath = import_path4.default.join(process.cwd(), "src/app/app.module.ts");
|
|
519
|
+
const jsonConfigPath = import_path4.default.join(process.cwd(), "kopynator.config.json");
|
|
520
|
+
if (import_fs4.default.existsSync(appConfigPath)) {
|
|
521
|
+
const content = import_fs4.default.readFileSync(appConfigPath, "utf-8");
|
|
522
|
+
const apiKeyMatch = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
|
|
523
|
+
if (apiKeyMatch) return { apiKey: apiKeyMatch[1] };
|
|
524
|
+
}
|
|
525
|
+
if (import_fs4.default.existsSync(appModulePath)) {
|
|
526
|
+
const content = import_fs4.default.readFileSync(appModulePath, "utf-8");
|
|
527
|
+
const apiKeyMatch = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
|
|
528
|
+
if (apiKeyMatch) return { apiKey: apiKeyMatch[1] };
|
|
529
|
+
}
|
|
530
|
+
if (import_fs4.default.existsSync(jsonConfigPath)) {
|
|
531
|
+
try {
|
|
532
|
+
const config = JSON.parse(import_fs4.default.readFileSync(jsonConfigPath, "utf-8"));
|
|
533
|
+
if (config.apiKey) return { apiKey: config.apiKey, baseUrl: config.baseUrl };
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
function inferLangFromFile(filePath) {
|
|
540
|
+
const base = import_path4.default.basename(filePath, import_path4.default.extname(filePath));
|
|
541
|
+
return base;
|
|
542
|
+
}
|
|
543
|
+
async function uploadCommand(options) {
|
|
544
|
+
console.log(import_chalk4.default.bold.blue("\n\u{1F4E4} Upload translations to Kopynator Cloud...\n"));
|
|
545
|
+
const config = extractApiKey2();
|
|
546
|
+
if (!config) {
|
|
547
|
+
console.log(import_chalk4.default.red("\u274C Could not find API key. Run `npx kopynator init` or set apiKey in kopynator.config.json / app.config.ts"));
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const fileOption = options.file;
|
|
551
|
+
if (!fileOption) {
|
|
552
|
+
console.log(import_chalk4.default.red("\u274C Missing --file. Example: npx kopynator upload --file=es.json [--lang=es]"));
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const resolvedPath = import_path4.default.isAbsolute(fileOption) ? fileOption : import_path4.default.join(process.cwd(), fileOption);
|
|
556
|
+
if (!import_fs4.default.existsSync(resolvedPath)) {
|
|
557
|
+
console.log(import_chalk4.default.red(`\u274C File not found: ${resolvedPath}`));
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const lang = options.lang || inferLangFromFile(resolvedPath);
|
|
561
|
+
const baseUrl = config.baseUrl || "https://api.kopynator.com/api/tokens";
|
|
562
|
+
const token = config.apiKey;
|
|
563
|
+
let raw;
|
|
564
|
+
try {
|
|
565
|
+
raw = JSON.parse(import_fs4.default.readFileSync(resolvedPath, "utf-8"));
|
|
566
|
+
} catch (e) {
|
|
567
|
+
console.log(import_chalk4.default.red(`\u274C Invalid JSON: ${resolvedPath}`));
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
571
|
+
console.log(import_chalk4.default.red("\u274C JSON root must be an object (key-value)."));
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const flat = flatten(raw);
|
|
575
|
+
const entries = Object.entries(flat);
|
|
576
|
+
const total = entries.length;
|
|
577
|
+
if (total === 0) {
|
|
578
|
+
console.log(import_chalk4.default.yellow("\u26A0\uFE0F No keys to upload."));
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
const batches = [];
|
|
582
|
+
for (let i = 0; i < total; i += BATCH_SIZE) {
|
|
583
|
+
batches.push(Object.fromEntries(entries.slice(i, i + BATCH_SIZE)));
|
|
584
|
+
}
|
|
585
|
+
const spinner = (0, import_ora2.default)(`Uploading ${total} keys (${lang})...`).start();
|
|
586
|
+
try {
|
|
587
|
+
let imported = 0;
|
|
588
|
+
for (let i = 0; i < batches.length; i++) {
|
|
589
|
+
spinner.text = `Uploading batch ${i + 1}/${batches.length}...`;
|
|
590
|
+
const res = await fetch(`${baseUrl}/import`, {
|
|
591
|
+
method: "POST",
|
|
592
|
+
headers: {
|
|
593
|
+
"Content-Type": "application/json",
|
|
594
|
+
"x-api-token": token,
|
|
595
|
+
"x-kopynator-version": "1.2.0"
|
|
596
|
+
},
|
|
597
|
+
body: JSON.stringify({ lang, data: batches[i] })
|
|
598
|
+
});
|
|
599
|
+
if (!res.ok) {
|
|
600
|
+
const errText = await res.text();
|
|
601
|
+
spinner.fail(`Upload failed: ${res.status} ${errText}`);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
const result = await res.json();
|
|
605
|
+
imported += result.importedCount ?? batches[i].length;
|
|
606
|
+
}
|
|
607
|
+
spinner.succeed(import_chalk4.default.green(`Uploaded ${imported} keys for language "${lang}".`));
|
|
608
|
+
console.log(import_chalk4.default.bold.green("\n\u{1F389} Upload completed. Run `npx kopynator sync` to pull latest from cloud.\n"));
|
|
609
|
+
} catch (error) {
|
|
610
|
+
spinner.fail(`Upload failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
497
614
|
// src/index.ts
|
|
498
615
|
var program = new import_commander.Command();
|
|
499
|
-
program.name("kopynator").description("Kopynator CLI - Manage your i18n workflow").version("1.
|
|
616
|
+
program.name("kopynator").description("Kopynator CLI - Manage your i18n workflow").version("1.3.0");
|
|
500
617
|
program.command("init").description("Initialize Kopynator in your project").action(initCommand);
|
|
501
618
|
program.command("check").description("Validate your local JSON translation files").action(checkCommand);
|
|
502
619
|
program.command("sync").description("Sync your translations with the Kopynator Cloud").action(syncCommand);
|
|
620
|
+
program.command("upload").description("Upload a JSON translation file to Kopynator Cloud").option("-f, --file <path>", "Path to the JSON file (e.g. es.json)").option("-l, --lang <code>", "Language code (default: inferred from filename)").action((opts) => uploadCommand({ file: opts.file, lang: opts.lang }));
|
|
503
621
|
program.parse(process.argv);
|
|
504
622
|
if (!process.argv.slice(2).length) {
|
|
505
|
-
console.log(
|
|
623
|
+
console.log(import_chalk5.default.blue("\u{1F44B} Welcome to Kopynator CLI!"));
|
|
506
624
|
program.outputHelp();
|
|
507
625
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kopynator/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "CLI tool for Kopynator - The i18n management solution",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kopynator": "dist/index.js"
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsup src/index.ts --format cjs --dts --clean",
|
|
11
|
+
"prepublishOnly": "npm run build",
|
|
11
12
|
"dev": "tsup src/index.ts --format cjs --dts --watch",
|
|
12
13
|
"release:patch": "npm version patch",
|
|
13
14
|
"release:minor": "npm version minor",
|
package/src/commands/index.ts
CHANGED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
|
|
6
|
+
const BATCH_SIZE = 500;
|
|
7
|
+
|
|
8
|
+
interface KopyConfig {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Flatten nested JSON to dot-notation keys (same as backend/frontend).
|
|
15
|
+
*/
|
|
16
|
+
function flatten(data: Record<string, unknown>, prefix = ''): Record<string, string> {
|
|
17
|
+
const result: Record<string, string> = {};
|
|
18
|
+
for (const key in data) {
|
|
19
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
20
|
+
const value = data[key];
|
|
21
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
22
|
+
Object.assign(result, flatten(value as Record<string, unknown>, fullKey));
|
|
23
|
+
} else {
|
|
24
|
+
result[fullKey] = String(value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function extractApiKey(): KopyConfig | null {
|
|
31
|
+
const appConfigPath = path.join(process.cwd(), 'src/app/app.config.ts');
|
|
32
|
+
const appModulePath = path.join(process.cwd(), 'src/app/app.module.ts');
|
|
33
|
+
const jsonConfigPath = path.join(process.cwd(), 'kopynator.config.json');
|
|
34
|
+
|
|
35
|
+
if (fs.existsSync(appConfigPath)) {
|
|
36
|
+
const content = fs.readFileSync(appConfigPath, 'utf-8');
|
|
37
|
+
const apiKeyMatch = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
|
|
38
|
+
if (apiKeyMatch) return { apiKey: apiKeyMatch[1] };
|
|
39
|
+
}
|
|
40
|
+
if (fs.existsSync(appModulePath)) {
|
|
41
|
+
const content = fs.readFileSync(appModulePath, 'utf-8');
|
|
42
|
+
const apiKeyMatch = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
|
|
43
|
+
if (apiKeyMatch) return { apiKey: apiKeyMatch[1] };
|
|
44
|
+
}
|
|
45
|
+
if (fs.existsSync(jsonConfigPath)) {
|
|
46
|
+
try {
|
|
47
|
+
const config = JSON.parse(fs.readFileSync(jsonConfigPath, 'utf-8'));
|
|
48
|
+
if (config.apiKey) return { apiKey: config.apiKey, baseUrl: config.baseUrl };
|
|
49
|
+
} catch {
|
|
50
|
+
// ignore
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Infer language code from filename (e.g. es.json -> es, en-US.json -> en-US).
|
|
58
|
+
*/
|
|
59
|
+
function inferLangFromFile(filePath: string): string {
|
|
60
|
+
const base = path.basename(filePath, path.extname(filePath));
|
|
61
|
+
return base;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function uploadCommand(options: { file?: string; lang?: string }) {
|
|
65
|
+
console.log(chalk.bold.blue('\n📤 Upload translations to Kopynator Cloud...\n'));
|
|
66
|
+
|
|
67
|
+
const config = extractApiKey();
|
|
68
|
+
if (!config) {
|
|
69
|
+
console.log(chalk.red('❌ Could not find API key. Run `npx kopynator init` or set apiKey in kopynator.config.json / app.config.ts'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const fileOption = options.file;
|
|
74
|
+
if (!fileOption) {
|
|
75
|
+
console.log(chalk.red('❌ Missing --file. Example: npx kopynator upload --file=es.json [--lang=es]'));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const resolvedPath = path.isAbsolute(fileOption) ? fileOption : path.join(process.cwd(), fileOption);
|
|
80
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
81
|
+
console.log(chalk.red(`❌ File not found: ${resolvedPath}`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const lang = options.lang || inferLangFromFile(resolvedPath);
|
|
86
|
+
const baseUrl = config.baseUrl || 'https://api.kopynator.com/api/tokens';
|
|
87
|
+
const token = config.apiKey;
|
|
88
|
+
|
|
89
|
+
let raw: unknown;
|
|
90
|
+
try {
|
|
91
|
+
raw = JSON.parse(fs.readFileSync(resolvedPath, 'utf-8'));
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.log(chalk.red(`❌ Invalid JSON: ${resolvedPath}`));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
98
|
+
console.log(chalk.red('❌ JSON root must be an object (key-value).'));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const flat = flatten(raw as Record<string, unknown>);
|
|
103
|
+
const entries = Object.entries(flat);
|
|
104
|
+
const total = entries.length;
|
|
105
|
+
if (total === 0) {
|
|
106
|
+
console.log(chalk.yellow('⚠️ No keys to upload.'));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const batches: Record<string, string>[] = [];
|
|
111
|
+
for (let i = 0; i < total; i += BATCH_SIZE) {
|
|
112
|
+
batches.push(Object.fromEntries(entries.slice(i, i + BATCH_SIZE)));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const spinner = ora(`Uploading ${total} keys (${lang})...`).start();
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
let imported = 0;
|
|
119
|
+
for (let i = 0; i < batches.length; i++) {
|
|
120
|
+
spinner.text = `Uploading batch ${i + 1}/${batches.length}...`;
|
|
121
|
+
const res = await fetch(`${baseUrl}/import`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: {
|
|
124
|
+
'Content-Type': 'application/json',
|
|
125
|
+
'x-api-token': token,
|
|
126
|
+
'x-kopynator-version': '1.2.0',
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify({ lang, data: batches[i] }),
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
const errText = await res.text();
|
|
133
|
+
spinner.fail(`Upload failed: ${res.status} ${errText}`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const result = (await res.json()) as { importedCount?: number };
|
|
137
|
+
imported += result.importedCount ?? batches[i].length;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
spinner.succeed(chalk.green(`Uploaded ${imported} keys for language "${lang}".`));
|
|
141
|
+
console.log(chalk.bold.green('\n🎉 Upload completed. Run `npx kopynator sync` to pull latest from cloud.\n'));
|
|
142
|
+
} catch (error) {
|
|
143
|
+
spinner.fail(`Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { initCommand, checkCommand, syncCommand } from './commands';
|
|
4
|
+
import { initCommand, checkCommand, syncCommand, uploadCommand } from './commands';
|
|
5
5
|
|
|
6
6
|
const program = new Command();
|
|
7
7
|
|
|
8
8
|
program
|
|
9
9
|
.name('kopynator')
|
|
10
10
|
.description('Kopynator CLI - Manage your i18n workflow')
|
|
11
|
-
.version('1.
|
|
11
|
+
.version('1.3.0');
|
|
12
12
|
|
|
13
13
|
program
|
|
14
14
|
.command('init')
|
|
@@ -25,6 +25,13 @@ program
|
|
|
25
25
|
.description('Sync your translations with the Kopynator Cloud')
|
|
26
26
|
.action(syncCommand);
|
|
27
27
|
|
|
28
|
+
program
|
|
29
|
+
.command('upload')
|
|
30
|
+
.description('Upload a JSON translation file to Kopynator Cloud')
|
|
31
|
+
.option('-f, --file <path>', 'Path to the JSON file (e.g. es.json)')
|
|
32
|
+
.option('-l, --lang <code>', 'Language code (default: inferred from filename)')
|
|
33
|
+
.action((opts) => uploadCommand({ file: opts.file, lang: opts.lang }));
|
|
34
|
+
|
|
28
35
|
program.parse(process.argv);
|
|
29
36
|
|
|
30
37
|
if (!process.argv.slice(2).length) {
|