@kopynator/cli 1.0.11 → 1.0.13
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/index.js +82 -14
- package/package.json +3 -2
- package/src/commands/sync.ts +110 -15
package/dist/index.js
CHANGED
|
@@ -204,6 +204,8 @@ async function checkCommand() {
|
|
|
204
204
|
var import_chalk3 = __toESM(require("chalk"));
|
|
205
205
|
var import_fs3 = __toESM(require("fs"));
|
|
206
206
|
var import_path3 = __toESM(require("path"));
|
|
207
|
+
var import_inquirer2 = __toESM(require("inquirer"));
|
|
208
|
+
var import_ora = __toESM(require("ora"));
|
|
207
209
|
function detectFramework() {
|
|
208
210
|
const angularJson = import_path3.default.join(process.cwd(), "angular.json");
|
|
209
211
|
const packageJson = import_path3.default.join(process.cwd(), "package.json");
|
|
@@ -267,6 +269,59 @@ function extractApiKey() {
|
|
|
267
269
|
}
|
|
268
270
|
return null;
|
|
269
271
|
}
|
|
272
|
+
async function getSyncConfig() {
|
|
273
|
+
const configPath = import_path3.default.join(process.cwd(), "kopynator.sync.config.json");
|
|
274
|
+
if (import_fs3.default.existsSync(configPath)) {
|
|
275
|
+
try {
|
|
276
|
+
const config2 = JSON.parse(import_fs3.default.readFileSync(configPath, "utf-8"));
|
|
277
|
+
console.log(import_chalk3.default.blue("\u2139\uFE0F Using existing sync configuration"));
|
|
278
|
+
return config2;
|
|
279
|
+
} catch (e) {
|
|
280
|
+
console.log(import_chalk3.default.yellow("\u26A0\uFE0F Error reading sync config, creating new one"));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
console.log(import_chalk3.default.blue("\n\u{1F4DD} First time sync! Let's configure your preferences:\n"));
|
|
284
|
+
const answers = await import_inquirer2.default.prompt([
|
|
285
|
+
{
|
|
286
|
+
type: "confirm",
|
|
287
|
+
name: "nested",
|
|
288
|
+
message: 'Use nested JSON structure? (e.g., {"nav": {"home": "Home"}})',
|
|
289
|
+
default: false
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
type: "confirm",
|
|
293
|
+
name: "includeLangKey",
|
|
294
|
+
message: 'Include language key in output? (e.g., {"en": {...}, "es": {...}})',
|
|
295
|
+
default: false
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
type: "confirm",
|
|
299
|
+
name: "pretty",
|
|
300
|
+
message: "Pretty print JSON?",
|
|
301
|
+
default: true
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
type: "list",
|
|
305
|
+
name: "indent",
|
|
306
|
+
message: "Indentation style:",
|
|
307
|
+
choices: [
|
|
308
|
+
{ name: "2 spaces", value: "2" },
|
|
309
|
+
{ name: "4 spaces", value: "4" },
|
|
310
|
+
{ name: "Tabs", value: "tab" }
|
|
311
|
+
],
|
|
312
|
+
default: "2"
|
|
313
|
+
}
|
|
314
|
+
]);
|
|
315
|
+
const config = {
|
|
316
|
+
nested: answers.nested,
|
|
317
|
+
includeLangKey: answers.includeLangKey,
|
|
318
|
+
pretty: answers.pretty,
|
|
319
|
+
indent: answers.indent
|
|
320
|
+
};
|
|
321
|
+
import_fs3.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
322
|
+
console.log(import_chalk3.default.green("\u2705 Configuration saved to kopynator.sync.config.json\n"));
|
|
323
|
+
return config;
|
|
324
|
+
}
|
|
270
325
|
async function syncCommand() {
|
|
271
326
|
console.log(import_chalk3.default.bold.blue("\n\u2601\uFE0F Syncing with Kopynator Cloud...\n"));
|
|
272
327
|
const framework = detectFramework();
|
|
@@ -277,39 +332,52 @@ async function syncCommand() {
|
|
|
277
332
|
console.log(import_chalk3.default.yellow("Run `npx kopynator init` first or configure your API key manually."));
|
|
278
333
|
return;
|
|
279
334
|
}
|
|
335
|
+
const syncConfig = await getSyncConfig();
|
|
280
336
|
const baseUrl = config.baseUrl || "http://localhost:7300/api/tokens";
|
|
281
337
|
const token = config.apiKey;
|
|
338
|
+
const spinner = (0, import_ora.default)("\u{1F4E1} Connecting to Kopynator Cloud...").start();
|
|
282
339
|
try {
|
|
283
|
-
|
|
340
|
+
spinner.text = "\u{1F4E1} Fetching available languages...";
|
|
284
341
|
const languagesUrl = `${baseUrl}/languages?token=${token}`;
|
|
285
342
|
const languagesResponse = await fetch(languagesUrl);
|
|
286
343
|
if (!languagesResponse.ok) {
|
|
287
|
-
|
|
344
|
+
spinner.fail(`Failed to fetch languages: ${languagesResponse.status} ${languagesResponse.statusText}`);
|
|
288
345
|
return;
|
|
289
346
|
}
|
|
290
347
|
const languages = await languagesResponse.json();
|
|
291
|
-
|
|
348
|
+
spinner.succeed(`Found ${languages.length} language(s): ${languages.join(", ")}`);
|
|
292
349
|
const i18nDir = getTranslationDir(framework);
|
|
293
350
|
if (!import_fs3.default.existsSync(i18nDir)) {
|
|
294
351
|
import_fs3.default.mkdirSync(i18nDir, { recursive: true });
|
|
295
352
|
console.log(import_chalk3.default.blue(`\u{1F4C1} Created directory: ${i18nDir}`));
|
|
296
353
|
}
|
|
297
354
|
for (const locale of languages) {
|
|
298
|
-
|
|
299
|
-
const fetchUrl = `${baseUrl}/fetch?token=${token}&langs=${locale}&nested
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
355
|
+
const downloadSpinner = (0, import_ora.default)(`\u2B07\uFE0F Downloading ${locale}...`).start();
|
|
356
|
+
const fetchUrl = `${baseUrl}/fetch?token=${token}&langs=${locale}&nested=${syncConfig.nested}&includeLangKey=${syncConfig.includeLangKey}&pretty=${syncConfig.pretty}&indent=${syncConfig.indent}`;
|
|
357
|
+
try {
|
|
358
|
+
const translationResponse = await fetch(fetchUrl);
|
|
359
|
+
if (!translationResponse.ok) {
|
|
360
|
+
downloadSpinner.warn(`Failed to fetch ${locale}: ${translationResponse.status}`);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const translationData = await translationResponse.json();
|
|
364
|
+
const outputPath = import_path3.default.join(i18nDir, `${locale}.json`);
|
|
365
|
+
let jsonString;
|
|
366
|
+
if (syncConfig.pretty) {
|
|
367
|
+
const indentValue = syncConfig.indent === "tab" ? " " : Number(syncConfig.indent);
|
|
368
|
+
jsonString = JSON.stringify(translationData, null, indentValue);
|
|
369
|
+
} else {
|
|
370
|
+
jsonString = JSON.stringify(translationData);
|
|
371
|
+
}
|
|
372
|
+
import_fs3.default.writeFileSync(outputPath, jsonString);
|
|
373
|
+
downloadSpinner.succeed(`Saved ${locale}.json`);
|
|
374
|
+
} catch (err) {
|
|
375
|
+
downloadSpinner.fail(`Error downloading ${locale}: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
304
376
|
}
|
|
305
|
-
const translationData = await translationResponse.json();
|
|
306
|
-
const outputPath = import_path3.default.join(i18nDir, `${locale}.json`);
|
|
307
|
-
import_fs3.default.writeFileSync(outputPath, JSON.stringify(translationData, null, 2));
|
|
308
|
-
console.log(import_chalk3.default.green(`\u2705 Saved ${locale}.json`));
|
|
309
377
|
}
|
|
310
378
|
console.log(import_chalk3.default.bold.green("\n\u{1F389} Sync completed successfully!\n"));
|
|
311
379
|
} catch (error) {
|
|
312
|
-
|
|
380
|
+
spinner.fail(`Sync failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
313
381
|
}
|
|
314
382
|
}
|
|
315
383
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kopynator/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "CLI tool for Kopynator - The i18n management solution",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kopynator": "dist/index.js"
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"chalk": "^4.1.2",
|
|
26
26
|
"commander": "^11.1.0",
|
|
27
|
-
"inquirer": "^8.2.6"
|
|
27
|
+
"inquirer": "^8.2.6",
|
|
28
|
+
"ora": "^5.4.1"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@types/inquirer": "^9.0.7",
|
package/src/commands/sync.ts
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import ora from 'ora';
|
|
4
6
|
|
|
5
7
|
interface KopyConfig {
|
|
6
8
|
apiKey: string;
|
|
7
9
|
baseUrl?: string;
|
|
8
10
|
}
|
|
9
11
|
|
|
12
|
+
interface SyncConfig {
|
|
13
|
+
nested: boolean;
|
|
14
|
+
includeLangKey: boolean;
|
|
15
|
+
pretty: boolean;
|
|
16
|
+
indent: '2' | '4' | 'tab';
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
/**
|
|
11
20
|
* Detect the framework being used in the current project
|
|
12
21
|
*/
|
|
@@ -100,6 +109,72 @@ function extractApiKey(): KopyConfig | null {
|
|
|
100
109
|
return null;
|
|
101
110
|
}
|
|
102
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Load or create sync configuration
|
|
114
|
+
*/
|
|
115
|
+
async function getSyncConfig(): Promise<SyncConfig> {
|
|
116
|
+
const configPath = path.join(process.cwd(), 'kopynator.sync.config.json');
|
|
117
|
+
|
|
118
|
+
// If config exists, load it
|
|
119
|
+
if (fs.existsSync(configPath)) {
|
|
120
|
+
try {
|
|
121
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
122
|
+
console.log(chalk.blue('ℹ️ Using existing sync configuration'));
|
|
123
|
+
return config;
|
|
124
|
+
} catch (e) {
|
|
125
|
+
console.log(chalk.yellow('⚠️ Error reading sync config, creating new one'));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Ask user for preferences
|
|
130
|
+
console.log(chalk.blue('\n📝 First time sync! Let\'s configure your preferences:\n'));
|
|
131
|
+
|
|
132
|
+
const answers = await inquirer.prompt([
|
|
133
|
+
{
|
|
134
|
+
type: 'confirm',
|
|
135
|
+
name: 'nested',
|
|
136
|
+
message: 'Use nested JSON structure? (e.g., {"nav": {"home": "Home"}})',
|
|
137
|
+
default: false,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
type: 'confirm',
|
|
141
|
+
name: 'includeLangKey',
|
|
142
|
+
message: 'Include language key in output? (e.g., {"en": {...}, "es": {...}})',
|
|
143
|
+
default: false,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
type: 'confirm',
|
|
147
|
+
name: 'pretty',
|
|
148
|
+
message: 'Pretty print JSON?',
|
|
149
|
+
default: true,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'list',
|
|
153
|
+
name: 'indent',
|
|
154
|
+
message: 'Indentation style:',
|
|
155
|
+
choices: [
|
|
156
|
+
{ name: '2 spaces', value: '2' },
|
|
157
|
+
{ name: '4 spaces', value: '4' },
|
|
158
|
+
{ name: 'Tabs', value: 'tab' },
|
|
159
|
+
],
|
|
160
|
+
default: '2',
|
|
161
|
+
},
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
const config: SyncConfig = {
|
|
165
|
+
nested: answers.nested,
|
|
166
|
+
includeLangKey: answers.includeLangKey,
|
|
167
|
+
pretty: answers.pretty,
|
|
168
|
+
indent: answers.indent,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Save config
|
|
172
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
173
|
+
console.log(chalk.green('✅ Configuration saved to kopynator.sync.config.json\n'));
|
|
174
|
+
|
|
175
|
+
return config;
|
|
176
|
+
}
|
|
177
|
+
|
|
103
178
|
export async function syncCommand() {
|
|
104
179
|
console.log(chalk.bold.blue('\n☁️ Syncing with Kopynator Cloud...\n'));
|
|
105
180
|
|
|
@@ -115,22 +190,27 @@ export async function syncCommand() {
|
|
|
115
190
|
return;
|
|
116
191
|
}
|
|
117
192
|
|
|
193
|
+
// Get sync configuration
|
|
194
|
+
const syncConfig = await getSyncConfig();
|
|
195
|
+
|
|
118
196
|
const baseUrl = config.baseUrl || 'http://localhost:7300/api/tokens';
|
|
119
197
|
const token = config.apiKey;
|
|
120
198
|
|
|
199
|
+
const spinner = ora('📡 Connecting to Kopynator Cloud...').start();
|
|
200
|
+
|
|
121
201
|
try {
|
|
122
202
|
// 1. Fetch available languages
|
|
123
|
-
|
|
203
|
+
spinner.text = '📡 Fetching available languages...';
|
|
124
204
|
const languagesUrl = `${baseUrl}/languages?token=${token}`;
|
|
125
205
|
const languagesResponse = await fetch(languagesUrl);
|
|
126
206
|
|
|
127
207
|
if (!languagesResponse.ok) {
|
|
128
|
-
|
|
208
|
+
spinner.fail(`Failed to fetch languages: ${languagesResponse.status} ${languagesResponse.statusText}`);
|
|
129
209
|
return;
|
|
130
210
|
}
|
|
131
211
|
|
|
132
212
|
const languages = await languagesResponse.json() as string[];
|
|
133
|
-
|
|
213
|
+
spinner.succeed(`Found ${languages.length} language(s): ${languages.join(', ')}`);
|
|
134
214
|
|
|
135
215
|
// 2. Get translation directory based on framework
|
|
136
216
|
const i18nDir = getTranslationDir(framework);
|
|
@@ -141,23 +221,38 @@ export async function syncCommand() {
|
|
|
141
221
|
|
|
142
222
|
// 3. Download translations for each language
|
|
143
223
|
for (const locale of languages) {
|
|
144
|
-
|
|
145
|
-
const fetchUrl = `${baseUrl}/fetch?token=${token}&langs=${locale}&nested
|
|
146
|
-
|
|
224
|
+
const downloadSpinner = ora(`⬇️ Downloading ${locale}...`).start();
|
|
225
|
+
const fetchUrl = `${baseUrl}/fetch?token=${token}&langs=${locale}&nested=${syncConfig.nested}&includeLangKey=${syncConfig.includeLangKey}&pretty=${syncConfig.pretty}&indent=${syncConfig.indent}`;
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const translationResponse = await fetch(fetchUrl);
|
|
147
229
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
230
|
+
if (!translationResponse.ok) {
|
|
231
|
+
downloadSpinner.warn(`Failed to fetch ${locale}: ${translationResponse.status}`);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
152
234
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
235
|
+
const translationData = await translationResponse.json();
|
|
236
|
+
const outputPath = path.join(i18nDir, `${locale}.json`);
|
|
237
|
+
|
|
238
|
+
// Format based on config
|
|
239
|
+
let jsonString: string;
|
|
240
|
+
if (syncConfig.pretty) {
|
|
241
|
+
const indentValue = syncConfig.indent === 'tab' ? '\t' : Number(syncConfig.indent);
|
|
242
|
+
jsonString = JSON.stringify(translationData, null, indentValue);
|
|
243
|
+
} else {
|
|
244
|
+
jsonString = JSON.stringify(translationData);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
fs.writeFileSync(outputPath, jsonString);
|
|
248
|
+
downloadSpinner.succeed(`Saved ${locale}.json`);
|
|
249
|
+
} catch (err) {
|
|
250
|
+
downloadSpinner.fail(`Error downloading ${locale}: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
251
|
+
}
|
|
157
252
|
}
|
|
158
253
|
|
|
159
254
|
console.log(chalk.bold.green('\n🎉 Sync completed successfully!\n'));
|
|
160
255
|
} catch (error) {
|
|
161
|
-
|
|
256
|
+
spinner.fail(`Sync failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
162
257
|
}
|
|
163
258
|
}
|