i18ntk 2.1.0 → 2.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.
Files changed (73) hide show
  1. package/README.md +87 -50
  2. package/main/i18ntk-analyze.js +63 -63
  3. package/main/i18ntk-backup-class.js +37 -41
  4. package/main/i18ntk-backup.js +28 -30
  5. package/main/i18ntk-complete.js +75 -74
  6. package/main/i18ntk-doctor.js +7 -6
  7. package/main/i18ntk-fixer.js +3 -3
  8. package/main/i18ntk-init.js +49 -13
  9. package/main/i18ntk-scanner.js +2 -2
  10. package/main/i18ntk-sizing.js +36 -37
  11. package/main/i18ntk-summary.js +4 -4
  12. package/main/i18ntk-ui.js +95 -96
  13. package/main/i18ntk-usage.js +31 -19
  14. package/main/i18ntk-validate.js +78 -27
  15. package/main/manage/commands/AnalyzeCommand.js +71 -73
  16. package/main/manage/commands/CommandRouter.js +15 -12
  17. package/main/manage/commands/FixerCommand.js +94 -38
  18. package/main/manage/commands/ScannerCommand.js +2 -2
  19. package/main/manage/commands/ValidateCommand.js +87 -36
  20. package/main/manage/index.js +165 -152
  21. package/main/manage/managers/DebugMenu.js +6 -6
  22. package/main/manage/managers/InteractiveMenu.js +6 -6
  23. package/main/manage/managers/LanguageMenu.js +12 -6
  24. package/main/manage/managers/SettingsMenu.js +6 -6
  25. package/main/manage/services/AuthenticationService.js +5 -6
  26. package/main/manage/services/ConfigurationService.js +22 -34
  27. package/main/manage/services/FileManagementService.js +6 -6
  28. package/main/manage/services/InitService.js +44 -8
  29. package/main/manage/services/UsageService.js +24 -12
  30. package/package.json +21 -42
  31. package/settings/settings-cli.js +5 -5
  32. package/settings/settings-manager.js +984 -968
  33. package/ui-locales/de.json +12 -11
  34. package/ui-locales/en.json +12 -11
  35. package/ui-locales/es.json +12 -11
  36. package/ui-locales/fr.json +12 -11
  37. package/ui-locales/ja.json +12 -11
  38. package/ui-locales/ru.json +12 -11
  39. package/ui-locales/zh.json +12 -11
  40. package/utils/config-helper.js +27 -16
  41. package/utils/config-manager.js +8 -7
  42. package/utils/i18n-helper.js +161 -166
  43. package/utils/init-helper.js +3 -2
  44. package/utils/json-output.js +11 -10
  45. package/{scripts → utils}/locale-optimizer.js +61 -60
  46. package/utils/logger.js +4 -4
  47. package/utils/safe-json.js +3 -3
  48. package/utils/secure-backup.js +8 -7
  49. package/utils/setup-enforcer.js +63 -98
  50. package/main/i18ntk-go.js +0 -283
  51. package/main/i18ntk-java.js +0 -380
  52. package/main/i18ntk-js.js +0 -512
  53. package/main/i18ntk-manage.js +0 -1694
  54. package/main/i18ntk-php.js +0 -462
  55. package/main/i18ntk-py.js +0 -379
  56. package/main/i18ntk-settings.js +0 -23
  57. package/main/manage/index-fixed.js +0 -1447
  58. package/scripts/build-lite.js +0 -279
  59. package/scripts/deprecate-versions.js +0 -317
  60. package/scripts/export-translations.js +0 -84
  61. package/scripts/fix-all-i18n.js +0 -236
  62. package/scripts/fix-and-purify-i18n.js +0 -233
  63. package/scripts/fix-locale-control-chars.js +0 -110
  64. package/scripts/lint-locales.js +0 -80
  65. package/scripts/prepublish-dev.js +0 -221
  66. package/scripts/prepublish.js +0 -362
  67. package/scripts/security-check.js +0 -117
  68. package/scripts/sync-translations.js +0 -151
  69. package/scripts/sync-ui-locales.js +0 -20
  70. package/scripts/validate-all-translations.js +0 -195
  71. package/scripts/verify-deprecations.js +0 -157
  72. package/scripts/verify-translations.js +0 -63
  73. package/utils/security-fixed.js +0 -609
@@ -7,17 +7,18 @@
7
7
  * during package initialization. Integrates with i18ntk init process.
8
8
  *
9
9
  * Usage:
10
- * node scripts/locale-optimizer.js --interactive
11
- * node scripts/locale-optimizer.js --list
12
- * node scripts/locale-optimizer.js --keep en,es,de
13
- * node scripts/locale-optimizer.js --restore
10
+ * node utils/locale-optimizer.js --interactive
11
+ * node utils/locale-optimizer.js --list
12
+ * node utils/locale-optimizer.js --keep en,es,de
13
+ * node utils/locale-optimizer.js --restore
14
14
  */
15
15
 
16
- const fs = require('fs');
17
- const path = require('path');
18
- const cliHelper = require('../utils/cli-helper');
19
- const JsonOutput = require('../utils/json-output');
20
- const { getGlobalReadline } = require('../utils/cli');
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const SecurityUtils = require('./security');
19
+ const cliHelper = require('./cli-helper');
20
+ const JsonOutput = require('./json-output');
21
+ const { getGlobalReadline } = require('./cli');
21
22
 
22
23
  class LocaleOptimizer {
23
24
  constructor() {
@@ -77,19 +78,19 @@ class LocaleOptimizer {
77
78
  return;
78
79
  }
79
80
 
80
- console.log('\n📊 UI Locale Package Analysis');
81
- console.log(''.repeat(40));
81
+ console.log('\n📊 UI Locale Package Analysis');
82
+ console.log('═'.repeat(40));
82
83
 
83
84
  const available = this.getAvailableLocales();
84
85
  let totalSize = 0;
85
86
 
86
87
  if (available.length === 0) {
87
- console.log(' No locale files found');
88
+ console.log(' ❌ No locale files found');
88
89
  return;
89
90
  }
90
91
 
91
92
  console.log(' Language Size Status');
92
- console.log(' ─────────────────────────');
93
+ console.log(' ─────────────────────────');
93
94
 
94
95
  available.forEach(locale => {
95
96
  const filePath = path.join(this.uiLocalesDir, `${locale}.json`);
@@ -105,11 +106,11 @@ class LocaleOptimizer {
105
106
 
106
107
  const missing = this.allLocales.filter(l => !available.includes(l));
107
108
  if (missing.length > 0) {
108
- console.log(`\n ⚠️ Missing: ${missing.join(', ').toUpperCase()}`);
109
+ console.log(`\n ⚠️ Missing: ${missing.join(', ').toUpperCase()}`);
109
110
  }
110
111
 
111
- console.log(`\n📦 Total package size: ${(totalSize / 1024).toFixed(1)}KB`);
112
- console.log(`💡 Potential savings: ${(totalSize/1024 - this.getLocaleSize('en')).toFixed(1)}KB (English only)`);
112
+ console.log(`\n📦 Total package size: ${(totalSize / 1024).toFixed(1)}KB`);
113
+ console.log(`💡 Potential savings: ${(totalSize/1024 - this.getLocaleSize('en')).toFixed(1)}KB (English only)`);
113
114
  console.log(` Run --interactive to optimize now`);
114
115
  }
115
116
 
@@ -135,7 +136,7 @@ class LocaleOptimizer {
135
136
  keepLocales(localesToKeep) {
136
137
  const keepList = Array.isArray(localesToKeep) ? localesToKeep : localesToKeep.split(',').map(l => l.trim().toLowerCase());
137
138
 
138
- console.log('🎯 Optimizing package size...');
139
+ console.log('🎯 Optimizing package size...');
139
140
  console.log(` Keeping: ${keepList.join(', ').toUpperCase()}`);
140
141
 
141
142
  // Create backup directory
@@ -162,22 +163,22 @@ class LocaleOptimizer {
162
163
  removedCount++;
163
164
  removedLocales.push(locale);
164
165
 
165
- console.log(` Removed ${locale.toUpperCase()} (backed up)`);
166
+ console.log(` ✅ Removed ${locale.toUpperCase()} (backed up)`);
166
167
  }
167
168
  });
168
169
 
169
170
  if (removedCount > 0) {
170
- console.log(`\n⚠️ WARNING: Removing locales may break UI functionality`);
171
- console.log(` If issues occur, restore with: node scripts/locale-optimizer.js --restore`);
171
+ console.log(`\n⚠️ WARNING: Removing locales may break UI functionality`);
172
+ console.log(` If issues occur, restore with: node utils/locale-optimizer.js --restore`);
172
173
  console.log(` Or reinstall the package: npm install -g i18ntk`);
173
174
 
174
175
  // Create warning file
175
176
  const warningPath = path.join(this.backupDir, 'REMOVED_LOCALES.txt');
176
- SecurityUtils.safeWriteFileSync(warningPath, `Removed locales: ${removedLocales.join(',')}\nRestore with: node scripts/locale-optimizer.js --restore`);
177
+ SecurityUtils.safeWriteFileSync(warningPath, `Removed locales: ${removedLocales.join(',')}\nRestore with: node utils/locale-optimizer.js --restore`);
177
178
  }
178
179
 
179
180
  const savedKB = (savedSpace / 1024).toFixed(1);
180
- console.log(`\n🎉 Optimization complete!`);
181
+ console.log(`\n🎉 Optimization complete!`);
181
182
  console.log(` Removed: ${removedCount} locales`);
182
183
  console.log(` Saved: ${savedKB}KB`);
183
184
  console.log(` Backup location: ${this.backupDir}`);
@@ -187,17 +188,17 @@ class LocaleOptimizer {
187
188
  * Restore locales from backup
188
189
  */
189
190
  restoreLocales() {
190
- console.log('🔄 Restoring locales from backup...');
191
+ console.log('🔄 Restoring locales from backup...');
191
192
 
192
193
  if (!SecurityUtils.safeExistsSync(this.backupDir)) {
193
- console.log(' No backup directory found');
194
+ console.log(' ❌ No backup directory found');
194
195
  return;
195
196
  }
196
197
 
197
198
  const backupFiles = fs.readdirSync(this.backupDir).filter(f => f.endsWith('.json'));
198
199
 
199
200
  if (backupFiles.length === 0) {
200
- console.log(' No backup files found');
201
+ console.log(' ❌ No backup files found');
201
202
  return;
202
203
  }
203
204
 
@@ -209,10 +210,10 @@ class LocaleOptimizer {
209
210
 
210
211
  fs.copyFileSync(backupPath, restorePath);
211
212
  restoredCount++;
212
- console.log(` Restored ${file.replace('.json', '').toUpperCase()}`);
213
+ console.log(` ✅ Restored ${file.replace('.json', '').toUpperCase()}`);
213
214
  });
214
215
 
215
- console.log(`\n🎉 Restoration complete!`);
216
+ console.log(`\n🎉 Restoration complete!`);
216
217
  console.log(` Restored: ${restoredCount} locales`);
217
218
  }
218
219
 
@@ -220,13 +221,13 @@ class LocaleOptimizer {
220
221
  * Dry run mode - show optimization scenarios without making changes
221
222
  */
222
223
  dryRun() {
223
- console.log('🔍 LOCALE OPTIMIZER - DRY RUN MODE');
224
- console.log(''.repeat(50));
224
+ console.log('🔍 LOCALE OPTIMIZER - DRY RUN MODE');
225
+ console.log('═'.repeat(50));
225
226
 
226
227
  const available = this.getAvailableLocales();
227
228
  const totalSize = this.getTotalSize();
228
229
 
229
- console.log(`📊 Current state:`);
230
+ console.log(`📊 Current state:`);
230
231
  console.log(` Total locales: ${available.length}`);
231
232
  console.log(` Total package size: ${totalSize.toFixed(1)}KB`);
232
233
  console.log(` Individual locale sizes:`);
@@ -237,7 +238,7 @@ class LocaleOptimizer {
237
238
  console.log(` - ${locale.toUpperCase()}: ${size.toFixed(1)}KB (${status})`);
238
239
  });
239
240
 
240
- console.log('\n🎯 Optimization scenarios:');
241
+ console.log('\n🎯 Optimization scenarios:');
241
242
 
242
243
  // Scenario 1: English only
243
244
  const enSize = this.getLocaleSize('en');
@@ -259,42 +260,42 @@ class LocaleOptimizer {
259
260
  console.log(` - Size: ${enEsFrSize.toFixed(1)}KB`);
260
261
  console.log(` - Savings: ${(totalSize - enEsFrSize).toFixed(1)}KB (${(((totalSize - enEsFrSize) / totalSize) * 100).toFixed(1)}%)`);
261
262
 
262
- console.log('\n💡 To run actual optimization:');
263
- console.log(' node scripts/locale-optimizer.js --interactive');
264
- console.log('\n Dry run complete - no files were modified');
263
+ console.log('\n💡 To run actual optimization:');
264
+ console.log(' node utils/locale-optimizer.js --interactive');
265
+ console.log('\n✅ Dry run complete - no files were modified');
265
266
  }
266
267
 
267
268
  /**
268
269
  * Interactive locale selection with enhanced UI
269
270
  */
270
271
  async interactiveSelect() {
271
- console.log('\n🌍 i18ntk Locale Optimizer');
272
- console.log(''.repeat(50));
272
+ console.log('\n🌍 i18ntk Locale Optimizer');
273
+ console.log('═'.repeat(50));
273
274
  console.log('Select which UI locales to keep (English is always kept)');
274
275
  console.log('This will reduce package size by removing unused language files\n');
275
276
 
276
277
  const available = this.getAvailableLocales();
277
278
  const selections = new Set(['en']); // Always keep English
278
279
 
279
- console.log('📊 Available locales and sizes:');
280
+ console.log('📊 Available locales and sizes:');
280
281
  available.forEach(locale => {
281
282
  const size = this.getLocaleSize(locale);
282
- const status = locale === 'en' ? ' (Required)' : '';
283
+ const status = locale === 'en' ? '✅ (Required)' : '○';
283
284
  console.log(` ${status} ${locale.toUpperCase()}: ${size}KB`);
284
285
  });
285
286
 
286
- console.log('\n💡 Type locale codes separated by commas (e.g., en,es,de)');
287
+ console.log('\n💡 Type locale codes separated by commas (e.g., en,es,de)');
287
288
  console.log(' Press Enter to keep all, or type "cancel" to abort\n');
288
289
 
289
290
  const answer = await cliHelper.prompt('Select locales to keep: ');
290
291
 
291
292
  if (answer.toLowerCase() === 'cancel') {
292
- console.log(' Operation cancelled');
293
+ console.log('❌ Operation cancelled');
293
294
  return false;
294
295
  }
295
296
 
296
297
  if (answer.trim() === '') {
297
- console.log(' Keeping all locales');
298
+ console.log('✅ Keeping all locales');
298
299
  return true;
299
300
  }
300
301
 
@@ -302,7 +303,7 @@ class LocaleOptimizer {
302
303
  const valid = selected.filter(l => available.includes(l));
303
304
 
304
305
  if (valid.length === 0) {
305
- console.log('⚠️ No valid locales selected, keeping all');
306
+ console.log('⚠️ No valid locales selected, keeping all');
306
307
  return true;
307
308
  }
308
309
 
@@ -310,16 +311,16 @@ class LocaleOptimizer {
310
311
  valid.push('en');
311
312
  const unique = [...new Set(valid)];
312
313
 
313
- console.log(`\n🎯 Selected: ${unique.join(', ').toUpperCase()}`);
314
+ console.log(`\n🎯 Selected: ${unique.join(', ').toUpperCase()}`);
314
315
  this.showImpact(unique);
315
316
 
316
317
  const confirm = await cliHelper.prompt('\nProceed? (y/N): ');
317
318
  if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
318
319
  this.keepLocales(unique.join(','));
319
- console.log('\n🎉 Package optimized successfully!');
320
- console.log('💡 Use --restore to bring back removed locales');
320
+ console.log('\n🎉 Package optimized successfully!');
321
+ console.log('💡 Use --restore to bring back removed locales');
321
322
  } else {
322
- console.log(' Operation cancelled');
323
+ console.log('❌ Operation cancelled');
323
324
  }
324
325
  return true;
325
326
  }
@@ -336,7 +337,7 @@ class LocaleOptimizer {
336
337
  const saved = allSize - selectedSize;
337
338
  const percentage = ((saved / allSize) * 100).toFixed(1);
338
339
 
339
- console.log(`\n📈 Impact Analysis:`);
340
+ console.log(`\n📈 Impact Analysis:`);
340
341
  console.log(` Current: ${allSize.toFixed(1)}KB`);
341
342
  console.log(` New: ${selectedSize.toFixed(1)}KB`);
342
343
  console.log(` Saved: ${saved.toFixed(1)}KB (${percentage}%)`);
@@ -536,8 +537,8 @@ async function main() {
536
537
 
537
538
  // Handle --init
538
539
  if (args.includes('--init')) {
539
- console.log('\n📦 Package Size Optimization');
540
- console.log(''.repeat(30));
540
+ console.log('\n📦 Package Size Optimization');
541
+ console.log('═'.repeat(30));
541
542
  optimizer.listLocales();
542
543
 
543
544
  const answer = await cliHelper.prompt('\nOptimize package size now? (Y/n): ');
@@ -552,20 +553,20 @@ async function main() {
552
553
  }
553
554
 
554
555
  if (!options.interactive && !options.list && !options.keep && !options.restore && !options.dryRun && !args.includes('--init')) {
555
- console.log('🌍 i18ntk Locale Optimizer v2.0');
556
- console.log(''.repeat(35));
556
+ console.log('🌍 i18ntk Locale Optimizer v2.0');
557
+ console.log('═'.repeat(35));
557
558
  console.log('Interactive package size optimization for UI locales');
558
559
  console.log('');
559
560
  console.log('Usage:');
560
- console.log(' node scripts/locale-optimizer.js --interactive 🎯 Interactive selection');
561
- console.log(' node scripts/locale-optimizer.js --list 📊 List all locales');
562
- console.log(' node scripts/locale-optimizer.js --keep en,es,de Quick keep');
563
- console.log(' node scripts/locale-optimizer.js --restore 🔄 Restore all locales');
564
- console.log(' node scripts/locale-optimizer.js --init 🚀 Called during init');
565
- console.log(' node scripts/locale-optimizer.js --dry-run 🔍 Simulation mode');
561
+ console.log(' node utils/locale-optimizer.js --interactive 🎯 Interactive selection');
562
+ console.log(' node utils/locale-optimizer.js --list 📊 List all locales');
563
+ console.log(' node utils/locale-optimizer.js --keep en,es,de âš¡ Quick keep');
564
+ console.log(' node utils/locale-optimizer.js --restore 🔄 Restore all locales');
565
+ console.log(' node utils/locale-optimizer.js --init 🚀 Called during init');
566
+ console.log(' node utils/locale-optimizer.js --dry-run 🔍 Simulation mode');
566
567
  console.log('');
567
- console.log('💡 Example: Keep only English and Spanish');
568
- console.log(' node scripts/locale-optimizer.js --keep en,es');
568
+ console.log('💡 Example: Keep only English and Spanish');
569
+ console.log(' node utils/locale-optimizer.js --keep en,es');
569
570
  return;
570
571
  }
571
572
 
@@ -581,4 +582,4 @@ if (require.main === module) {
581
582
  main();
582
583
  }
583
584
 
584
- module.exports = LocaleOptimizer;
585
+ module.exports = LocaleOptimizer;
package/utils/logger.js CHANGED
@@ -45,10 +45,10 @@ const logger = {
45
45
  logger.log(output, colors.blue);
46
46
  },
47
47
 
48
- // Debug logging (only when DEBUG env var is set or log level is debug)
49
- debug: (message) => {
50
- const logLevel = envManager.get('I18NTK_LOG_LEVEL');
51
- const debugEnabled = process.env.DEBUG || logLevel === 'debug';
48
+ // Debug logging (only when DEBUG env var is set or log level is debug)
49
+ debug: (message) => {
50
+ const logLevel = envManager.get('I18NTK_LOG_LEVEL');
51
+ const debugEnabled = logLevel === 'debug';
52
52
 
53
53
  if (debugEnabled) {
54
54
  const output = `[DEBUG] ${message}`;
@@ -1,5 +1,5 @@
1
1
  // utils/safe-json.js
2
- const { readFile } = require('fs/promises');
2
+ const fs = require('fs');
3
3
 
4
4
  function stripBOM(s) {
5
5
  if (typeof s === 'string' && s.charCodeAt(0) === 0xFEFF) return s.slice(1);
@@ -13,7 +13,7 @@ function stripBOM(s) {
13
13
  * - Single, typed error (no loops)
14
14
  */
15
15
  async function readJsonSafe(filePath, { maxBytes = 1_000_000 } = {}) {
16
- const buf = await readFile(filePath);
16
+ const buf = await fs.promises.readFile(filePath);
17
17
  if (buf.length === 0) {
18
18
  const err = new Error('Empty JSON file');
19
19
  err.code = 'EJSONEMPTY';
@@ -37,4 +37,4 @@ async function readJsonSafe(filePath, { maxBytes = 1_000_000 } = {}) {
37
37
  }
38
38
  }
39
39
 
40
- module.exports = { readJsonSafe };
40
+ module.exports = { readJsonSafe };
@@ -1,5 +1,6 @@
1
1
  const crypto = require('crypto');
2
- const fs = require('fs/promises');
2
+ const fs = require('fs');
3
+ const fsp = fs.promises;
3
4
  const path = require('path');
4
5
  const zlib = require('zlib');
5
6
  const { promisify } = require('util');
@@ -180,7 +181,7 @@ class SecureBackupManager {
180
181
  const backupPath = path.join(this.config.backupDir, backupName);
181
182
 
182
183
  // Write the backup file
183
- await fs.writeFile(backupPath, JSON.stringify(backupData, null, 2), 'utf8');
184
+ await fsp.writeFile(backupPath, JSON.stringify(backupData, null, 2), 'utf8');
184
185
 
185
186
  // Clean up old backups if we've exceeded the limit
186
187
  await this.cleanupOldBackups();
@@ -203,7 +204,7 @@ class SecureBackupManager {
203
204
  async restoreBackup(backupPath, password) {
204
205
  try {
205
206
  // Read the backup file
206
- const backupData = JSON.parse(await fs.readFile(backupPath, 'utf8'));
207
+ const backupData = JSON.parse(await fsp.readFile(backupPath, 'utf8'));
207
208
 
208
209
  // Validate the backup
209
210
  if (backupData.header !== BACKUP_HEADER) {
@@ -236,7 +237,7 @@ class SecureBackupManager {
236
237
  async listBackups() {
237
238
  try {
238
239
  // Read the backup directory
239
- const files = (await fs.readdir(this.config.backupDir, { withFileTypes: true }))
240
+ const files = (await fsp.readdir(this.config.backupDir, { withFileTypes: true }))
240
241
  .filter(dirent => dirent.isFile())
241
242
  .map(dirent => dirent.name);
242
243
 
@@ -247,7 +248,7 @@ class SecureBackupManager {
247
248
  const backups = await Promise.all(
248
249
  backupFiles.map(async (file) => {
249
250
  const filePath = path.join(this.config.backupDir, file);
250
- const stat = await fs.stat(filePath);
251
+ const stat = await fsp.stat(filePath);
251
252
  if (stat.isDirectory()) {
252
253
  throw new Error('Backup path is a directory');
253
254
  }
@@ -290,7 +291,7 @@ class SecureBackupManager {
290
291
  const deleted = [];
291
292
  for (const backup of toDelete) {
292
293
  try {
293
- await fs.unlink(backup.path);
294
+ await fsp.unlink(backup.path);
294
295
  deleted.push(backup.name);
295
296
  } catch (error) {
296
297
  console.error(`Failed to delete backup ${backup.name}:`, error);
@@ -310,7 +311,7 @@ class SecureBackupManager {
310
311
  async verifyBackup(backupPath, password) {
311
312
  try {
312
313
  // Read the backup file
313
- const backupData = JSON.parse(await fs.readFile(backupPath, 'utf8'));
314
+ const backupData = JSON.parse(await fsp.readFile(backupPath, 'utf8'));
314
315
 
315
316
  // Try to decrypt a small part to verify the password
316
317
  const testData = await this.decryptData(backupData, password);
@@ -9,9 +9,25 @@
9
9
 
10
10
  const { getIcon } = require('./terminal-icons');
11
11
  const fs = require('fs');
12
- const path = require('path');
13
- const { blue, yellow, gray, cyan, green, red } = require('./colors-new');
14
- const SecurityUtils = require('./security');
12
+ const path = require('path');
13
+ const { blue, yellow, gray, cyan, green, red } = require('./colors-new');
14
+ const SecurityUtils = require('./security');
15
+ const I18nSetupModule = require('../main/i18ntk-setup');
16
+
17
+ async function runSetupModule() {
18
+ if (I18nSetupModule && typeof I18nSetupModule.run === 'function') {
19
+ return await I18nSetupModule.run();
20
+ }
21
+
22
+ if (typeof I18nSetupModule === 'function') {
23
+ const setupManager = new I18nSetupModule();
24
+ if (typeof setupManager.setup === 'function') {
25
+ return await setupManager.setup();
26
+ }
27
+ }
28
+
29
+ throw new Error('Setup module does not expose a runnable entrypoint');
30
+ }
15
31
 
16
32
  class SetupEnforcer {
17
33
  static _setupCheckInProgress = false;
@@ -107,39 +123,22 @@ class SetupEnforcer {
107
123
  process.exit(0);
108
124
  }
109
125
 
110
- console.log(green(`${getIcon('rocket')} Running setup...`));
111
-
112
- try {
113
- // Import and run setup directly
114
- const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
115
- if (SecurityUtils.safeExistsSync(setupPath)) {
116
- try {
117
- const setup = require(setupPath);
118
- // Use the run function which properly instantiates the class
119
- if (setup && typeof setup.run === 'function') {
120
- await setup.run();
121
- } else {
122
- // Fallback: instantiate the class directly
123
- const I18nSetupManager = require(setupPath);
124
- const setupManager = new I18nSetupManager();
125
- await setupManager.setup();
126
- }
127
- console.log(gray('You can now run your original command.'));
128
- resolve(true);
129
- } catch (error) {
130
- console.error(red(`${getIcon('cross')} Setup failed:`), error.message);
131
- console.error(cyan(' Please try running setup manually:'));
132
- console.error(cyan(' npm run i18ntk-setup'));
133
- process.exit(1);
134
- }
135
- } else {
136
- console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
137
- console.error(cyan(' npm run i18ntk-setup'));
138
- process.exit(1);
139
- }
140
- } catch (error) {
141
- console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
142
- console.error(cyan(' npm run i18ntk-setup'));
126
+ console.log(green(`${getIcon('rocket')} Running setup...`));
127
+
128
+ try {
129
+ // Import once at module scope and execute the stable entrypoint.
130
+ const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
131
+ if (!SecurityUtils.safeExistsSync(setupPath)) {
132
+ console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
133
+ console.error(cyan(' npm run i18ntk-setup'));
134
+ process.exit(1);
135
+ }
136
+ await runSetupModule();
137
+ console.log(gray('You can now run your original command.'));
138
+ resolve(true);
139
+ } catch (error) {
140
+ console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
141
+ console.error(cyan(' npm run i18ntk-setup'));
143
142
  process.exit(1);
144
143
  }
145
144
  });
@@ -185,37 +184,20 @@ static async handleIncompleteSetup() {
185
184
  process.exit(0);
186
185
  }
187
186
 
188
- console.log(green(`${getIcon('rocket')} Running setup...`));
189
-
190
- try {
191
- const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
192
- if (SecurityUtils.safeExistsSync(setupPath)) {
193
- try {
194
- const setup = require(setupPath);
195
- // Use the run function which properly instantiates the class
196
- if (setup && typeof setup.run === 'function') {
197
- await setup.run();
198
- } else {
199
- // Fallback: instantiate the class directly
200
- const I18nSetupManager = require(setupPath);
201
- const setupManager = new I18nSetupManager();
202
- await setupManager.setup();
203
- }
204
- resolve(true);
205
- } catch (error) {
206
- console.error(red(`${getIcon('cross')} Setup failed:`), error.message);
207
- console.error(cyan(' Please try running setup manually:'));
208
- console.error(cyan(' npm run i18ntk-setup'));
209
- process.exit(1);
210
- }
211
- } else {
212
- console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
213
- console.error(cyan(' npm run i18ntk-setup'));
214
- process.exit(1);
215
- }
216
- } catch (error) {
217
- console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
218
- process.exit(1);
187
+ console.log(green(`${getIcon('rocket')} Running setup...`));
188
+
189
+ try {
190
+ const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
191
+ if (!SecurityUtils.safeExistsSync(setupPath)) {
192
+ console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
193
+ console.error(cyan(' npm run i18ntk-setup'));
194
+ process.exit(1);
195
+ }
196
+ await runSetupModule();
197
+ resolve(true);
198
+ } catch (error) {
199
+ console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
200
+ process.exit(1);
219
201
  }
220
202
  });
221
203
  });
@@ -260,37 +242,20 @@ static async handleInvalidConfig() {
260
242
  process.exit(0);
261
243
  }
262
244
 
263
- console.log(green(`${getIcon('rocket')} Running setup...`));
264
-
265
- try {
266
- const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
267
- if (SecurityUtils.safeExistsSync(setupPath)) {
268
- try {
269
- const setup = require(setupPath);
270
- // Use the run function which properly instantiates the class
271
- if (setup && typeof setup.run === 'function') {
272
- await setup.run();
273
- } else {
274
- // Fallback: instantiate the class directly
275
- const I18nSetupManager = require(setupPath);
276
- const setupManager = new I18nSetupManager();
277
- await setupManager.setup();
278
- }
279
- resolve(true);
280
- } catch (error) {
281
- console.error(red(`${getIcon('cross')} Setup failed:`), error.message);
282
- console.error(cyan(' Please try running setup manually:'));
283
- console.error(cyan(' npm run i18ntk-setup'));
284
- process.exit(1);
285
- }
286
- } else {
287
- console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
288
- console.error(cyan(' npm run i18ntk-setup'));
289
- process.exit(1);
290
- }
291
- } catch (error) {
292
- console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
293
- process.exit(1);
245
+ console.log(green(`${getIcon('rocket')} Running setup...`));
246
+
247
+ try {
248
+ const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
249
+ if (!SecurityUtils.safeExistsSync(setupPath)) {
250
+ console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
251
+ console.error(cyan(' npm run i18ntk-setup'));
252
+ process.exit(1);
253
+ }
254
+ await runSetupModule();
255
+ resolve(true);
256
+ } catch (error) {
257
+ console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
258
+ process.exit(1);
294
259
  }
295
260
  });
296
261
  });