@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 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
- console.log(import_chalk3.default.blue("\u{1F4E1} Fetching available languages..."));
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
- console.log(import_chalk3.default.red(`\u274C Failed to fetch languages: ${languagesResponse.status} ${languagesResponse.statusText}`));
344
+ spinner.fail(`Failed to fetch languages: ${languagesResponse.status} ${languagesResponse.statusText}`);
288
345
  return;
289
346
  }
290
347
  const languages = await languagesResponse.json();
291
- console.log(import_chalk3.default.green(`\u2705 Found ${languages.length} language(s): ${languages.join(", ")}`));
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
- console.log(import_chalk3.default.blue(`\u2B07\uFE0F Downloading ${locale}...`));
299
- const fetchUrl = `${baseUrl}/fetch?token=${token}&langs=${locale}&nested=false&includeLangKey=false&pretty=true&indent=2`;
300
- const translationResponse = await fetch(fetchUrl);
301
- if (!translationResponse.ok) {
302
- console.log(import_chalk3.default.yellow(`\u26A0\uFE0F Failed to fetch ${locale}: ${translationResponse.status}`));
303
- continue;
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
- console.log(import_chalk3.default.red(`\u274C Sync failed: ${error instanceof Error ? error.message : "Unknown error"}`));
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.11",
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",
@@ -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
- console.log(chalk.blue('📡 Fetching available languages...'));
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
- console.log(chalk.red(`❌ Failed to fetch languages: ${languagesResponse.status} ${languagesResponse.statusText}`));
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
- console.log(chalk.green(`✅ Found ${languages.length} language(s): ${languages.join(', ')}`));
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
- console.log(chalk.blue(`⬇️ Downloading ${locale}...`));
145
- const fetchUrl = `${baseUrl}/fetch?token=${token}&langs=${locale}&nested=false&includeLangKey=false&pretty=true&indent=2`;
146
- const translationResponse = await fetch(fetchUrl);
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
- if (!translationResponse.ok) {
149
- console.log(chalk.yellow(`⚠️ Failed to fetch ${locale}: ${translationResponse.status}`));
150
- continue;
151
- }
230
+ if (!translationResponse.ok) {
231
+ downloadSpinner.warn(`Failed to fetch ${locale}: ${translationResponse.status}`);
232
+ continue;
233
+ }
152
234
 
153
- const translationData = await translationResponse.json();
154
- const outputPath = path.join(i18nDir, `${locale}.json`);
155
- fs.writeFileSync(outputPath, JSON.stringify(translationData, null, 2));
156
- console.log(chalk.green(`✅ Saved ${locale}.json`));
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
- console.log(chalk.red(`❌ Sync failed: ${error instanceof Error ? error.message : 'Unknown error'}`));
256
+ spinner.fail(`Sync failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
162
257
  }
163
258
  }