@kopynator/cli 1.0.10 → 1.0.11

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
@@ -202,11 +202,115 @@ async function checkCommand() {
202
202
 
203
203
  // src/commands/sync.ts
204
204
  var import_chalk3 = __toESM(require("chalk"));
205
+ var import_fs3 = __toESM(require("fs"));
206
+ var import_path3 = __toESM(require("path"));
207
+ function detectFramework() {
208
+ const angularJson = import_path3.default.join(process.cwd(), "angular.json");
209
+ const packageJson = import_path3.default.join(process.cwd(), "package.json");
210
+ if (import_fs3.default.existsSync(angularJson)) {
211
+ return "Angular";
212
+ }
213
+ if (import_fs3.default.existsSync(packageJson)) {
214
+ try {
215
+ const pkg = JSON.parse(import_fs3.default.readFileSync(packageJson, "utf-8"));
216
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
217
+ if (deps["@angular/core"]) return "Angular";
218
+ if (deps["react"]) return "React";
219
+ if (deps["vue"]) return "Vue";
220
+ } catch (e) {
221
+ }
222
+ }
223
+ return "Other";
224
+ }
225
+ function getTranslationDir(framework) {
226
+ switch (framework) {
227
+ case "Angular":
228
+ return import_path3.default.join(process.cwd(), "src/assets/i18n");
229
+ case "React":
230
+ return import_path3.default.join(process.cwd(), "public/locales");
231
+ case "Vue":
232
+ return import_path3.default.join(process.cwd(), "public/locales");
233
+ default:
234
+ return import_path3.default.join(process.cwd(), "locales");
235
+ }
236
+ }
237
+ function extractApiKey() {
238
+ const appConfigPath = import_path3.default.join(process.cwd(), "src/app/app.config.ts");
239
+ const appModulePath = import_path3.default.join(process.cwd(), "src/app/app.module.ts");
240
+ const jsonConfigPath = import_path3.default.join(process.cwd(), "kopynator.config.json");
241
+ if (import_fs3.default.existsSync(appConfigPath)) {
242
+ const content = import_fs3.default.readFileSync(appConfigPath, "utf-8");
243
+ const apiKeyMatch = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
244
+ if (apiKeyMatch) {
245
+ console.log(import_chalk3.default.blue("\u2139\uFE0F Found API key in app.config.ts"));
246
+ return { apiKey: apiKeyMatch[1] };
247
+ }
248
+ }
249
+ if (import_fs3.default.existsSync(appModulePath)) {
250
+ const content = import_fs3.default.readFileSync(appModulePath, "utf-8");
251
+ const apiKeyMatch = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
252
+ if (apiKeyMatch) {
253
+ console.log(import_chalk3.default.blue("\u2139\uFE0F Found API key in app.module.ts"));
254
+ return { apiKey: apiKeyMatch[1] };
255
+ }
256
+ }
257
+ if (import_fs3.default.existsSync(jsonConfigPath)) {
258
+ try {
259
+ const config = JSON.parse(import_fs3.default.readFileSync(jsonConfigPath, "utf-8"));
260
+ if (config.apiKey) {
261
+ console.log(import_chalk3.default.blue("\u2139\uFE0F Found API key in kopynator.config.json"));
262
+ return { apiKey: config.apiKey, baseUrl: config.baseUrl };
263
+ }
264
+ } catch (e) {
265
+ console.log(import_chalk3.default.red("\u274C Error parsing kopynator.config.json"));
266
+ }
267
+ }
268
+ return null;
269
+ }
205
270
  async function syncCommand() {
206
271
  console.log(import_chalk3.default.bold.blue("\n\u2601\uFE0F Syncing with Kopynator Cloud...\n"));
207
- console.log(import_chalk3.default.yellow("\u{1F512} This feature is available in the Starter and Growth plans."));
208
- console.log(import_chalk3.default.white("Upgrade your plan at https://www.kopynator.com/pricing to enable cloud synchronization."));
209
- console.log(import_chalk3.default.gray("\n(In a real implementation, this would push local keys and pull remote translations)"));
272
+ const framework = detectFramework();
273
+ console.log(import_chalk3.default.blue(`\u2139\uFE0F Detected ${framework} project`));
274
+ const config = extractApiKey();
275
+ if (!config) {
276
+ console.log(import_chalk3.default.red("\u274C Could not find API key in any config file."));
277
+ console.log(import_chalk3.default.yellow("Run `npx kopynator init` first or configure your API key manually."));
278
+ return;
279
+ }
280
+ const baseUrl = config.baseUrl || "http://localhost:7300/api/tokens";
281
+ const token = config.apiKey;
282
+ try {
283
+ console.log(import_chalk3.default.blue("\u{1F4E1} Fetching available languages..."));
284
+ const languagesUrl = `${baseUrl}/languages?token=${token}`;
285
+ const languagesResponse = await fetch(languagesUrl);
286
+ if (!languagesResponse.ok) {
287
+ console.log(import_chalk3.default.red(`\u274C Failed to fetch languages: ${languagesResponse.status} ${languagesResponse.statusText}`));
288
+ return;
289
+ }
290
+ const languages = await languagesResponse.json();
291
+ console.log(import_chalk3.default.green(`\u2705 Found ${languages.length} language(s): ${languages.join(", ")}`));
292
+ const i18nDir = getTranslationDir(framework);
293
+ if (!import_fs3.default.existsSync(i18nDir)) {
294
+ import_fs3.default.mkdirSync(i18nDir, { recursive: true });
295
+ console.log(import_chalk3.default.blue(`\u{1F4C1} Created directory: ${i18nDir}`));
296
+ }
297
+ 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;
304
+ }
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
+ }
310
+ console.log(import_chalk3.default.bold.green("\n\u{1F389} Sync completed successfully!\n"));
311
+ } catch (error) {
312
+ console.log(import_chalk3.default.red(`\u274C Sync failed: ${error instanceof Error ? error.message : "Unknown error"}`));
313
+ }
210
314
  }
211
315
 
212
316
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kopynator/cli",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "CLI tool for Kopynator - The i18n management solution",
5
5
  "bin": {
6
6
  "kopynator": "dist/index.js"
@@ -1,10 +1,163 @@
1
1
  import chalk from 'chalk';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ interface KopyConfig {
6
+ apiKey: string;
7
+ baseUrl?: string;
8
+ }
9
+
10
+ /**
11
+ * Detect the framework being used in the current project
12
+ */
13
+ function detectFramework(): 'Angular' | 'React' | 'Vue' | 'Other' {
14
+ const angularJson = path.join(process.cwd(), 'angular.json');
15
+ const packageJson = path.join(process.cwd(), 'package.json');
16
+
17
+ // Check for Angular
18
+ if (fs.existsSync(angularJson)) {
19
+ return 'Angular';
20
+ }
21
+
22
+ // Check package.json for React/Vue
23
+ if (fs.existsSync(packageJson)) {
24
+ try {
25
+ const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf-8'));
26
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
27
+
28
+ if (deps['@angular/core']) return 'Angular';
29
+ if (deps['react']) return 'React';
30
+ if (deps['vue']) return 'Vue';
31
+ } catch (e) {
32
+ // Ignore parse errors
33
+ }
34
+ }
35
+
36
+ return 'Other';
37
+ }
38
+
39
+ /**
40
+ * Get the translation directory path based on framework
41
+ */
42
+ function getTranslationDir(framework: string): string {
43
+ switch (framework) {
44
+ case 'Angular':
45
+ return path.join(process.cwd(), 'src/assets/i18n');
46
+ case 'React':
47
+ // Common React patterns, can be customized later
48
+ return path.join(process.cwd(), 'public/locales');
49
+ case 'Vue':
50
+ // Common Vue patterns, can be customized later
51
+ return path.join(process.cwd(), 'public/locales');
52
+ default:
53
+ // Default fallback
54
+ return path.join(process.cwd(), 'locales');
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Extract API key from Angular config files or JSON config
60
+ * Tries: app.config.ts -> app.module.ts -> kopynator.config.json
61
+ */
62
+ function extractApiKey(): KopyConfig | null {
63
+ const appConfigPath = path.join(process.cwd(), 'src/app/app.config.ts');
64
+ const appModulePath = path.join(process.cwd(), 'src/app/app.module.ts');
65
+ const jsonConfigPath = path.join(process.cwd(), 'kopynator.config.json');
66
+
67
+ // Try app.config.ts
68
+ if (fs.existsSync(appConfigPath)) {
69
+ const content = fs.readFileSync(appConfigPath, 'utf-8');
70
+ const apiKeyMatch = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
71
+ if (apiKeyMatch) {
72
+ console.log(chalk.blue('ℹ️ Found API key in app.config.ts'));
73
+ return { apiKey: apiKeyMatch[1] };
74
+ }
75
+ }
76
+
77
+ // Try app.module.ts
78
+ if (fs.existsSync(appModulePath)) {
79
+ const content = fs.readFileSync(appModulePath, 'utf-8');
80
+ const apiKeyMatch = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
81
+ if (apiKeyMatch) {
82
+ console.log(chalk.blue('ℹ️ Found API key in app.module.ts'));
83
+ return { apiKey: apiKeyMatch[1] };
84
+ }
85
+ }
86
+
87
+ // Try kopynator.config.json
88
+ if (fs.existsSync(jsonConfigPath)) {
89
+ try {
90
+ const config = JSON.parse(fs.readFileSync(jsonConfigPath, 'utf-8'));
91
+ if (config.apiKey) {
92
+ console.log(chalk.blue('ℹ️ Found API key in kopynator.config.json'));
93
+ return { apiKey: config.apiKey, baseUrl: config.baseUrl };
94
+ }
95
+ } catch (e) {
96
+ console.log(chalk.red('❌ Error parsing kopynator.config.json'));
97
+ }
98
+ }
99
+
100
+ return null;
101
+ }
2
102
 
3
103
  export async function syncCommand() {
4
104
  console.log(chalk.bold.blue('\n☁️ Syncing with Kopynator Cloud...\n'));
5
105
 
6
- // Placeholder for paid/backend feature
7
- console.log(chalk.yellow('🔒 This feature is available in the Starter and Growth plans.'));
8
- console.log(chalk.white('Upgrade your plan at https://www.kopynator.com/pricing to enable cloud synchronization.'));
9
- console.log(chalk.gray('\n(In a real implementation, this would push local keys and pull remote translations)'));
106
+ // Detect framework
107
+ const framework = detectFramework();
108
+ console.log(chalk.blue(`ℹ️ Detected ${framework} project`));
109
+
110
+ // Extract API key
111
+ const config = extractApiKey();
112
+ if (!config) {
113
+ console.log(chalk.red('❌ Could not find API key in any config file.'));
114
+ console.log(chalk.yellow('Run `npx kopynator init` first or configure your API key manually.'));
115
+ return;
116
+ }
117
+
118
+ const baseUrl = config.baseUrl || 'http://localhost:7300/api/tokens';
119
+ const token = config.apiKey;
120
+
121
+ try {
122
+ // 1. Fetch available languages
123
+ console.log(chalk.blue('📡 Fetching available languages...'));
124
+ const languagesUrl = `${baseUrl}/languages?token=${token}`;
125
+ const languagesResponse = await fetch(languagesUrl);
126
+
127
+ if (!languagesResponse.ok) {
128
+ console.log(chalk.red(`❌ Failed to fetch languages: ${languagesResponse.status} ${languagesResponse.statusText}`));
129
+ return;
130
+ }
131
+
132
+ const languages = await languagesResponse.json() as string[];
133
+ console.log(chalk.green(`✅ Found ${languages.length} language(s): ${languages.join(', ')}`));
134
+
135
+ // 2. Get translation directory based on framework
136
+ const i18nDir = getTranslationDir(framework);
137
+ if (!fs.existsSync(i18nDir)) {
138
+ fs.mkdirSync(i18nDir, { recursive: true });
139
+ console.log(chalk.blue(`📁 Created directory: ${i18nDir}`));
140
+ }
141
+
142
+ // 3. Download translations for each language
143
+ 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);
147
+
148
+ if (!translationResponse.ok) {
149
+ console.log(chalk.yellow(`⚠️ Failed to fetch ${locale}: ${translationResponse.status}`));
150
+ continue;
151
+ }
152
+
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`));
157
+ }
158
+
159
+ console.log(chalk.bold.green('\n🎉 Sync completed successfully!\n'));
160
+ } catch (error) {
161
+ console.log(chalk.red(`❌ Sync failed: ${error instanceof Error ? error.message : 'Unknown error'}`));
162
+ }
10
163
  }