i18ntk 1.10.1 → 2.0.2

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 (110) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +141 -1185
  3. package/main/i18ntk-analyze.js +149 -133
  4. package/main/i18ntk-backup-class.js +420 -0
  5. package/main/i18ntk-backup.js +4 -4
  6. package/main/i18ntk-complete.js +90 -65
  7. package/main/i18ntk-doctor.js +123 -103
  8. package/main/i18ntk-fixer.js +61 -725
  9. package/main/i18ntk-go.js +14 -15
  10. package/main/i18ntk-init.js +76 -25
  11. package/main/i18ntk-java.js +27 -32
  12. package/main/i18ntk-js.js +70 -68
  13. package/main/i18ntk-manage.js +128 -29
  14. package/main/i18ntk-php.js +75 -75
  15. package/main/i18ntk-py.js +55 -56
  16. package/main/i18ntk-scanner.js +59 -57
  17. package/main/i18ntk-setup.js +10 -396
  18. package/main/i18ntk-sizing.js +46 -40
  19. package/main/i18ntk-summary.js +21 -18
  20. package/main/i18ntk-ui.js +11 -10
  21. package/main/i18ntk-usage.js +55 -19
  22. package/main/i18ntk-validate.js +13 -13
  23. package/main/manage/commands/AnalyzeCommand.js +1124 -0
  24. package/main/manage/commands/BackupCommand.js +62 -0
  25. package/main/manage/commands/CommandRouter.js +295 -0
  26. package/main/manage/commands/CompleteCommand.js +61 -0
  27. package/main/manage/commands/DoctorCommand.js +60 -0
  28. package/main/manage/commands/FixerCommand.js +624 -0
  29. package/main/manage/commands/InitCommand.js +62 -0
  30. package/main/manage/commands/ScannerCommand.js +654 -0
  31. package/main/manage/commands/SizingCommand.js +60 -0
  32. package/main/manage/commands/SummaryCommand.js +61 -0
  33. package/main/manage/commands/UsageCommand.js +60 -0
  34. package/main/manage/commands/ValidateCommand.js +978 -0
  35. package/main/manage/index-fixed.js +1447 -0
  36. package/main/manage/index.js +1462 -0
  37. package/main/manage/managers/DebugMenu.js +140 -0
  38. package/main/manage/managers/InteractiveMenu.js +177 -0
  39. package/main/manage/managers/LanguageMenu.js +62 -0
  40. package/main/manage/managers/SettingsMenu.js +53 -0
  41. package/main/manage/services/AuthenticationService.js +263 -0
  42. package/main/manage/services/ConfigurationService-fixed.js +449 -0
  43. package/main/manage/services/ConfigurationService.js +449 -0
  44. package/main/manage/services/FileManagementService.js +368 -0
  45. package/main/manage/services/FrameworkDetectionService.js +458 -0
  46. package/main/manage/services/InitService.js +1051 -0
  47. package/main/manage/services/SetupService.js +462 -0
  48. package/main/manage/services/SummaryService.js +450 -0
  49. package/main/manage/services/UsageService.js +1502 -0
  50. package/package.json +32 -30
  51. package/runtime/enhanced.d.ts +221 -221
  52. package/runtime/index.d.ts +29 -29
  53. package/runtime/index.full.d.ts +331 -331
  54. package/runtime/index.js +7 -6
  55. package/scripts/build-lite.js +17 -17
  56. package/scripts/deprecate-versions.js +23 -6
  57. package/scripts/export-translations.js +5 -5
  58. package/scripts/fix-all-i18n.js +3 -3
  59. package/scripts/fix-and-purify-i18n.js +3 -2
  60. package/scripts/fix-locale-control-chars.js +110 -0
  61. package/scripts/lint-locales.js +80 -0
  62. package/scripts/locale-optimizer.js +8 -8
  63. package/scripts/prepublish.js +21 -21
  64. package/scripts/security-check.js +13 -5
  65. package/scripts/sync-translations.js +4 -4
  66. package/scripts/sync-ui-locales.js +9 -8
  67. package/scripts/validate-all-translations.js +8 -7
  68. package/scripts/verify-deprecations.js +23 -15
  69. package/scripts/verify-translations.js +6 -5
  70. package/settings/i18ntk-config.json +282 -282
  71. package/settings/language-config.json +5 -5
  72. package/settings/settings-cli.js +9 -9
  73. package/settings/settings-manager.js +23 -20
  74. package/ui-locales/de.json +2417 -2348
  75. package/ui-locales/en.json +2415 -2352
  76. package/ui-locales/es.json +2425 -2353
  77. package/ui-locales/fr.json +2418 -2348
  78. package/ui-locales/ja.json +2463 -2361
  79. package/ui-locales/ru.json +2463 -2359
  80. package/ui-locales/zh.json +2418 -2351
  81. package/utils/admin-auth.js +2 -2
  82. package/utils/admin-cli.js +297 -297
  83. package/utils/admin-pin.js +9 -9
  84. package/utils/cli-helper.js +9 -9
  85. package/utils/config-helper.js +152 -103
  86. package/utils/config-manager.js +204 -164
  87. package/utils/config.js +5 -4
  88. package/utils/env-manager.js +256 -0
  89. package/utils/framework-detector.js +27 -24
  90. package/utils/i18n-helper.js +85 -41
  91. package/utils/init-helper.js +152 -94
  92. package/utils/json-output.js +98 -98
  93. package/utils/logger.js +6 -2
  94. package/utils/mini-commander.js +179 -0
  95. package/utils/missing-key-validator.js +5 -5
  96. package/utils/plugin-loader.js +29 -11
  97. package/utils/prompt.js +14 -44
  98. package/utils/safe-json.js +40 -0
  99. package/utils/secure-errors.js +3 -3
  100. package/utils/security-check-improved.js +390 -0
  101. package/utils/security-config.js +5 -5
  102. package/utils/security-fixed.js +607 -0
  103. package/utils/security.js +462 -248
  104. package/utils/setup-enforcer.js +136 -44
  105. package/utils/setup-validator.js +33 -32
  106. package/utils/terminal-icons.js +1 -1
  107. package/utils/ultra-performance-optimizer.js +11 -9
  108. package/utils/watch-locales.js +2 -1
  109. package/utils/prompt-fixed.js +0 -55
  110. package/utils/security-check.js +0 -450
package/main/i18ntk-js.js CHANGED
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * i18ntk JavaScript/TypeScript Command
5
5
  * Specialized command for JavaScript and TypeScript i18n management
6
- *
6
+ *
7
7
  * Usage: i18ntk-js [options]
8
8
  */
9
9
 
@@ -13,7 +13,7 @@ const SecurityUtils = require(path.join(__dirname, '../utils/security.js'));
13
13
  const { getConfig, saveConfig } = require(path.join(__dirname, '../utils/config-helper.js'));
14
14
  const I18nHelper = require(path.join(__dirname, '../utils/i18n-helper.js'));
15
15
  const SetupEnforcer = require(path.join(__dirname, '../utils/setup-enforcer'));
16
- const { program } = require('commander');
16
+ const { program } = require('../utils/mini-commander');
17
17
 
18
18
  (async () => {
19
19
  try {
@@ -37,29 +37,29 @@ class I18ntkJavaScriptCommand {
37
37
  /intl\.formatMessage\s*\(\s*{[^}]*id:\s*["'`]([^"'`]+)["'`]/g,
38
38
  /useTranslations?\s*\(\s*["'`]([^"'`]+)["'`]/g,
39
39
  /getTranslations?\s*\(\s*["'`]([^"'`]+)["'`]/g,
40
-
40
+
41
41
  // React patterns
42
42
  /FormattedMessage\s+id=["'`]([^"'`]+)["'`]/g,
43
43
  /<FormattedMessage[^>]*id={["'`]([^"'`]+)["'`]}/g,
44
44
  /useTranslation\(\)\s*\.t\s*\(\s*["'`]([^"'`]+)["'`]/g,
45
-
45
+
46
46
  // Angular patterns
47
47
  /translate\s*\|\s*translate/g,
48
48
  /\{\{\s*["'`]([^"'`]+)["'`]\s*\|\s*translate\s*}}/g,
49
-
49
+
50
50
  // Vue patterns
51
51
  /\$t\s*\(\s*["'`]([^"'`]+)["'`]/g,
52
52
  /this\.\$t\s*\(\s*["'`]([^"'`]+)["'`]/g,
53
-
53
+
54
54
  // Node.js patterns
55
55
  /__\s*\(\s*["'`]([^"'`]+)["'`]/g,
56
56
  /gettext\s*\(\s*["'`]([^"'`]+)["'`]/g,
57
-
57
+
58
58
  // Template literal patterns
59
59
  /i18n\.t\s*\(\s*`([^`]+)`/g,
60
60
  /t\s*\(\s*`([^`]+)`/g
61
61
  ];
62
-
62
+
63
63
  this.frameworks = {
64
64
  react: {
65
65
  indicators: ['package.json', 'react', 'react-dom'],
@@ -82,7 +82,7 @@ class I18ntkJavaScriptCommand {
82
82
 
83
83
  async init() {
84
84
  console.log('šŸ”§ Initializing i18ntk JavaScript/TypeScript command...');
85
-
85
+
86
86
  program
87
87
  .name('i18ntk-js')
88
88
  .description('i18ntk specialized for JavaScript and TypeScript applications')
@@ -104,13 +104,13 @@ class I18ntkJavaScriptCommand {
104
104
  this.options = program.opts();
105
105
  this.sourceDir = path.resolve(this.options.sourceDir);
106
106
  this.localesDir = path.resolve(this.options.localesDir);
107
-
107
+
108
108
  await this.validateSourceDir();
109
109
  await this.loadConfig();
110
110
  }
111
111
 
112
112
  async validateSourceDir() {
113
- if (!fs.existsSync(this.sourceDir)) {
113
+ if (!SecurityUtils.safeExistsSync(this.sourceDir, path.dirname(this.sourceDir))) {
114
114
  console.error(`āŒ Source directory not found: ${this.sourceDir}`);
115
115
  process.exit(1);
116
116
  }
@@ -137,16 +137,17 @@ class I18ntkJavaScriptCommand {
137
137
  if (this.options.vue) return 'vue';
138
138
  if (this.options.angular) return 'angular';
139
139
  if (this.options.node) return 'node';
140
-
140
+
141
141
  console.log('šŸ” Detecting JavaScript framework...');
142
-
142
+
143
143
  let framework = 'generic';
144
144
 
145
145
  try {
146
146
  const packageJsonPath = path.join(this.sourceDir, 'package.json');
147
- if (fs.existsSync(packageJsonPath)) {
148
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
149
-
147
+ if (SecurityUtils.safeExistsSync(packageJsonPath, this.sourceDir)) {
148
+ const packageJsonContent = SecurityUtils.safeReadFileSync(packageJsonPath, this.sourceDir, 'utf8');
149
+ const packageJson = SecurityUtils.safeParseJSON(packageJsonContent);
150
+
150
151
  const dependencies = {
151
152
  ...packageJson.dependencies || {},
152
153
  ...packageJson.devDependencies || {}
@@ -183,72 +184,72 @@ class I18ntkJavaScriptCommand {
183
184
  findFiles(pattern, extensions = []) {
184
185
  const results = [];
185
186
  const validExtensions = extensions.length > 0 ? extensions : ['.js', '.jsx', '.ts', '.tsx'];
186
-
187
+
187
188
  function scanDir(dir) {
188
- if (!fs.existsSync(dir)) return;
189
-
189
+ if (!SecurityUtils.safeExistsSync(dir, path.dirname(dir))) return;
190
+
190
191
  const items = fs.readdirSync(dir);
191
192
  for (const item of items) {
192
193
  const fullPath = path.join(dir, item);
193
194
  const stat = fs.statSync(fullPath);
194
-
195
+
195
196
  if (stat.isDirectory()) {
196
197
  // Skip node_modules and hidden directories
197
198
  if (item === 'node_modules' || item.startsWith('.')) continue;
198
-
199
+
199
200
  // Skip test directories unless explicitly included
200
- if (!this.options.includeTests &&
201
+ if (!this.options.includeTests &&
201
202
  (item === '__tests__' || item === 'test' || item === 'tests' || item.includes('.test.'))) {
202
203
  continue;
203
204
  }
204
-
205
+
205
206
  scanDir.call(this, fullPath);
206
207
  } else {
207
208
  // Check file extension
208
209
  const ext = path.extname(item);
209
210
  if (validExtensions.includes(ext)) {
210
211
  // Skip test files unless explicitly included
211
- if (!this.options.includeTests &&
212
+ if (!this.options.includeTests &&
212
213
  (item.includes('.test.') || item.includes('.spec.') || item.includes('__test__'))) {
213
214
  continue;
214
215
  }
215
-
216
+
216
217
  results.push(fullPath);
217
218
  }
218
219
  }
219
220
  }
220
221
  }
221
-
222
+
222
223
  scanDir.call(this, this.sourceDir);
223
224
  return results;
224
225
  }
225
226
 
226
227
  async extractTranslations() {
227
228
  console.log('šŸ“¦ Extracting JavaScript/TypeScript translations...');
228
-
229
- const extensions = this.options.typescript
230
- ? ['.js', '.jsx', '.ts', '.tsx']
229
+
230
+ const extensions = this.options.typescript
231
+ ? ['.js', '.jsx', '.ts', '.tsx']
231
232
  : ['.js', '.jsx'];
232
-
233
+
233
234
  const jsFiles = this.findFiles('', extensions);
234
235
  const translations = new Set();
235
-
236
+
236
237
  for (const file of jsFiles) {
237
238
  try {
238
- const content = fs.readFileSync(file, 'utf8');
239
-
239
+ const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
240
+
240
241
  for (const pattern of this.jsPatterns) {
241
242
  let match;
242
243
  while ((match = pattern.exec(content)) !== null) {
243
244
  translations.add(match[1]);
244
245
  }
245
246
  }
246
-
247
+
247
248
  // Reset regex state for next file
248
249
  for (const pattern of this.jsPatterns) {
249
250
  pattern.lastIndex = 0;
250
251
  }
251
-
252
+
252
253
  } catch (error) {
253
254
  if (this.options.debug) {
254
255
  console.error(`Error reading ${file}:`, error.message);
@@ -261,28 +262,28 @@ class I18ntkJavaScriptCommand {
261
262
  }
262
263
 
263
264
  async createLocaleStructure() {
264
- if (!fs.existsSync(this.localesDir)) {
265
+ if (!SecurityUtils.safeExistsSync(this.localesDir, path.dirname(this.localesDir))) {
265
266
  if (this.options.dryRun) {
266
267
  console.log(`šŸ“ Would create directory: ${this.localesDir}`);
267
268
  return;
268
269
  }
269
-
270
+
270
271
  fs.mkdirSync(this.localesDir, { recursive: true });
271
272
  console.log(`šŸ“ Created locales directory: ${this.localesDir}`);
272
273
  }
273
274
 
274
275
  const languages = ['en', 'es', 'fr', 'de', 'ja', 'ru', 'zh'];
275
-
276
+
276
277
  for (const lang of languages) {
277
278
  const langDir = path.join(this.localesDir, lang);
278
- if (!fs.existsSync(langDir)) {
279
+ if (!SecurityUtils.safeExistsSync(langDir, this.localesDir)) {
279
280
  if (this.options.dryRun) {
280
281
  console.log(`šŸ“ Would create directory: ${langDir}`);
281
282
  continue;
282
283
  }
283
-
284
+
284
285
  fs.mkdirSync(langDir, { recursive: true });
285
-
286
+
286
287
  // Create framework-specific translation files
287
288
  const commonFile = path.join(langDir, 'common.json');
288
289
  const initialContent = {
@@ -306,21 +307,21 @@ class I18ntkJavaScriptCommand {
306
307
  }
307
308
  }
308
309
  };
309
-
310
- fs.writeFileSync(commonFile, JSON.stringify(initialContent, null, 2));
310
+
311
+ SecurityUtils.safeWriteFileSync(commonFile, JSON.stringify(initialContent, null, 2), this.localesDir);
311
312
  }
312
313
  }
313
314
  }
314
315
 
315
316
  async analyzeFramework(framework) {
316
317
  console.log(`šŸ” Analyzing ${framework} project...`);
317
-
318
- const extensions = this.options.typescript
319
- ? ['.js', '.jsx', '.ts', '.tsx']
318
+
319
+ const extensions = this.options.typescript
320
+ ? ['.js', '.jsx', '.ts', '.tsx']
320
321
  : ['.js', '.jsx'];
321
-
322
+
322
323
  const jsFiles = this.findFiles('', extensions);
323
-
324
+
324
325
  const analysis = {
325
326
  framework,
326
327
  typescript: this.options.typescript || false,
@@ -358,14 +359,14 @@ class I18ntkJavaScriptCommand {
358
359
  else if (ext === '.ts') analysis.files.typescript++;
359
360
  else if (ext === '.jsx') analysis.files.jsx++;
360
361
  else if (ext === '.tsx') analysis.files.tsx++;
361
-
362
+
362
363
  if (file.includes('.test.') || file.includes('.spec.')) {
363
364
  analysis.files.test++;
364
365
  }
365
366
 
366
367
  try {
367
- const content = fs.readFileSync(file, 'utf8');
368
-
368
+ const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8');
369
+
369
370
  // Check patterns
370
371
  if (content.includes('i18n.t(')) analysis.patterns.i18n_t++;
371
372
  if (content.includes('t(')) analysis.patterns.t_function++;
@@ -374,7 +375,7 @@ class I18ntkJavaScriptCommand {
374
375
  if (content.includes('translate')) analysis.patterns.translate++;
375
376
  if (content.includes('$t(')) analysis.patterns.$t++;
376
377
  if (content.includes('gettext(')) analysis.patterns.gettext++;
377
-
378
+
378
379
  // Detect frameworks
379
380
  if (content.includes('import React') || content.includes('require("react")')) {
380
381
  analysis.frameworks.react = true;
@@ -388,7 +389,7 @@ class I18ntkJavaScriptCommand {
388
389
  if (content.includes('express') || content.includes('node')) {
389
390
  analysis.frameworks.node = true;
390
391
  }
391
-
392
+
392
393
  } catch (error) {
393
394
  // Skip unreadable files
394
395
  }
@@ -398,11 +399,11 @@ class I18ntkJavaScriptCommand {
398
399
  if (analysis.files.total > 0 && analysis.patterns.i18n_t === 0) {
399
400
  analysis.recommendations.push('Consider adding i18n support to your JavaScript/TypeScript files');
400
401
  }
401
-
402
+
402
403
  if (analysis.frameworks.react && analysis.patterns.FormattedMessage === 0) {
403
404
  analysis.recommendations.push('Consider using react-intl or react-i18next for React i18n');
404
405
  }
405
-
406
+
406
407
  if (analysis.frameworks.vue && analysis.patterns.$t === 0) {
407
408
  analysis.recommendations.push('Consider using vue-i18n for Vue.js i18n');
408
409
  }
@@ -410,10 +411,11 @@ class I18ntkJavaScriptCommand {
410
411
  // Check package.json for dependencies
411
412
  try {
412
413
  const packageJsonPath = path.join(this.sourceDir, 'package.json');
413
- if (fs.existsSync(packageJsonPath)) {
414
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
414
+ if (SecurityUtils.safeExistsSync(packageJsonPath, this.sourceDir)) {
415
+ const packageJsonContent = SecurityUtils.safeReadFileSync(packageJsonPath, this.sourceDir, 'utf8');
416
+ const packageJson = SecurityUtils.safeParseJSON(packageJsonContent);
415
417
  const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
416
-
418
+
417
419
  for (const [pkg, version] of Object.entries(deps)) {
418
420
  if (pkg.includes('i18n') || pkg.includes('intl') || pkg.includes('translate')) {
419
421
  analysis.dependencies.push(`${pkg}@${version}`);
@@ -455,12 +457,12 @@ class I18ntkJavaScriptCommand {
455
457
  };
456
458
 
457
459
  const reportPath = path.join(this.sourceDir, 'i18ntk-js-report.json');
458
-
460
+
459
461
  if (this.options.dryRun) {
460
462
  console.log(`šŸ“Š Would create report: ${reportPath}`);
461
463
  console.log('šŸ“‹ Report contents:', JSON.stringify(report, null, 2));
462
464
  } else {
463
- fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
465
+ SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2), this.sourceDir);
464
466
  console.log(`šŸ“Š Report saved: ${reportPath}`);
465
467
  }
466
468
 
@@ -469,19 +471,19 @@ class I18ntkJavaScriptCommand {
469
471
 
470
472
  async run() {
471
473
  try {
472
- console.log('šŸš€ i18ntk JavaScript/TypeScript Command v1.10.1');
473
- console.log('=' * 60);
474
-
474
+ console.log('šŸš€ i18ntk JavaScript/TypeScript Command v1.10.1');
475
+ console.log('='.repeat(60));
476
+
475
477
  await this.init();
476
-
478
+
477
479
  const framework = await this.detectFramework();
478
480
  const translations = await this.extractTranslations();
479
-
481
+
480
482
  if (!this.options.extractOnly) {
481
483
  await this.createLocaleStructure();
482
484
  const analysis = await this.analyzeFramework(framework);
483
485
  const report = await this.generateReport(analysis, translations);
484
-
486
+
485
487
  console.log('\nāœ… Analysis complete!');
486
488
  console.log(`šŸ“Š Framework: ${framework}`);
487
489
  console.log(`šŸ“„ Total files: ${analysis.files.total}`);
@@ -490,7 +492,7 @@ class I18ntkJavaScriptCommand {
490
492
  } else {
491
493
  console.log(`šŸ“¦ Extracted ${translations.length} translation keys`);
492
494
  }
493
-
495
+
494
496
  } catch (error) {
495
497
  console.error('āŒ Error:', error.message);
496
498
  if (this.options.debug) {
@@ -507,4 +509,4 @@ if (require.main === module) {
507
509
  cmd.run();
508
510
  }
509
511
 
510
- module.exports = I18ntkJavaScriptCommand;
512
+ module.exports = I18ntkJavaScriptCommand;
@@ -18,11 +18,12 @@
18
18
  * node i18ntk-manage.js --command=init
19
19
  */
20
20
 
21
- const fs = require('fs');
22
21
  const path = require('path');
23
22
  const UIi18n = require('./i18ntk-ui');
24
23
  const AdminAuth = require('../utils/admin-auth');
25
24
  const SecurityUtils = require('../utils/security');
25
+
26
+
26
27
  const AdminCLI = require('../utils/admin-cli');
27
28
  const configManager = require('../settings/settings-manager');
28
29
  const { showFrameworkWarningOnce } = require('../utils/cli-helper');
@@ -36,6 +37,8 @@ const SettingsCLI = require('../settings/settings-cli');
36
37
  // const I18nDebugger = require('../scripts/debug/debugger');
37
38
  const { createPrompt, isInteractive } = require('../utils/prompt-helper');
38
39
  const { loadTranslations, t, refreshLanguageFromSettings} = require('../utils/i18n-helper');
40
+ // Preload translations early to avoid missing key warnings
41
+ loadTranslations();
39
42
  const cliHelper = require('../utils/cli-helper');
40
43
  const { loadConfig, saveConfig, ensureConfigDefaults } = require('../utils/config');
41
44
  const pkg = require('../package.json');
@@ -50,10 +53,6 @@ async function runInitFlow() {
50
53
  return { i18nDir: settings.i18nDir, sourceDir: settings.sourceDir };
51
54
  }
52
55
 
53
-
54
-
55
-
56
-
57
56
  /**
58
57
  * Ensures the project is properly initialized or exits the process
59
58
  * @param {Object} prompt - Prompt interface for user interaction
@@ -129,7 +128,7 @@ async function ensureInitializedOrExit(prompt) {
129
128
  console.log('Framework detection prompt will be suppressed for this version.');
130
129
  } else if (action === 'detect') {
131
130
  // Run framework detection
132
- const { detectedLanguage, detectedFramework } = detectEnvironmentAndFramework();
131
+ const { detectedLanguage, detectedFramework } = await detectEnvironmentAndFramework();
133
132
 
134
133
  if (detectedFramework && detectedFramework !== 'generic') {
135
134
  console.log(`\nDetected framework: ${detectedFramework}`);
@@ -156,7 +155,90 @@ async function ensureInitializedOrExit(prompt) {
156
155
  return { ...config, ...settings };
157
156
  }
158
157
 
158
+ /**
159
+ * Custom glob implementation using Node.js built-in modules (zero dependencies)
160
+ * @param {string[]} patterns - Array of glob patterns
161
+ * @param {Object} options - Options object with cwd and ignore properties
162
+ * @returns {Promise<string[]>} Array of matching file paths
163
+ */
164
+ async function customGlob(patterns, options = {}) {
165
+ const fs = require('fs');
166
+ const path = require('path');
167
+ const cwd = options.cwd || process.cwd();
168
+ const ignorePatterns = options.ignore || [];
169
+
170
+ function matchesPattern(filename, pattern) {
171
+ // Simple pattern matching for **/*.{js,jsx,ts,tsx} style patterns
172
+ if (pattern.includes('**/*')) {
173
+ const extensionPart = pattern.split('*.')[1];
174
+ if (extensionPart) {
175
+ const extensions = extensionPart.replace('{', '').replace('}', '').split(',');
176
+ return extensions.some(ext => filename.endsWith('.' + ext.trim()));
177
+ }
178
+ }
179
+ return filename.includes(pattern.replace('**/', ''));
180
+ }
181
+
182
+ function shouldIgnore(filePath) {
183
+ return ignorePatterns.some(pattern => {
184
+ if (pattern.includes('**/')) {
185
+ const patternEnd = pattern.replace('**/', '');
186
+ return filePath.includes('/' + patternEnd) || filePath.includes('\\' + patternEnd);
187
+ }
188
+ return filePath.includes(pattern);
189
+ });
190
+ }
191
+
192
+ function findFiles(dir, results = []) {
193
+ try {
194
+ const items = fs.readdirSync(dir);
195
+
196
+ for (const item of items) {
197
+ const fullPath = path.join(dir, item);
198
+ const relativePath = path.relative(cwd, fullPath);
199
+
200
+ if (shouldIgnore(relativePath)) {
201
+ continue;
202
+ }
203
+
204
+ try {
205
+ const stat = fs.statSync(fullPath);
206
+
207
+ if (stat.isDirectory()) {
208
+ findFiles(fullPath, results);
209
+ } else if (stat.isFile()) {
210
+ // Check if file matches any of our patterns
211
+ for (const pattern of patterns) {
212
+ if (matchesPattern(item, pattern)) {
213
+ results.push(relativePath);
214
+ break;
215
+ }
216
+ }
217
+ }
218
+ } catch (error) {
219
+ // Skip files we can't access
220
+ continue;
221
+ }
222
+ }
223
+ } catch (error) {
224
+ // Skip directories we can't access
225
+ }
226
+
227
+ return results;
228
+ }
229
+
230
+ return findFiles(cwd);
231
+ }
232
+
159
233
  async function detectEnvironmentAndFramework() {
234
+ // Defensive check to ensure SecurityUtils is available
235
+ if (!SecurityUtils) {
236
+ throw new Error('SecurityUtils is not available. This may indicate a module loading issue.');
237
+ }
238
+ const fs = require('fs');
239
+ const path = require('path');
240
+
241
+
160
242
  const packageJsonPath = path.join(process.cwd(), 'package.json');
161
243
  const pyprojectPath = path.join(process.cwd(), 'pyproject.toml');
162
244
  const requirementsPath = path.join(process.cwd(), 'requirements.txt');
@@ -167,10 +249,10 @@ async function detectEnvironmentAndFramework() {
167
249
  let detectedLanguage = 'generic';
168
250
  let detectedFramework = 'generic';
169
251
 
170
- if (fs.existsSync(packageJsonPath)) {
252
+ if (SecurityUtils.safeExistsSync(packageJsonPath)) {
171
253
  detectedLanguage = 'javascript';
172
254
  try {
173
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
255
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
174
256
  const deps = {
175
257
  ...(packageJson.dependencies || {}),
176
258
  ...(packageJson.devDependencies || {}),
@@ -189,7 +271,7 @@ async function detectEnvironmentAndFramework() {
189
271
  /require\(['\"]i18ntk[\/\\]runtime['\"]\)/
190
272
  ];
191
273
 
192
- const sourceFiles = await glob(['src/**/*.{js,jsx,ts,tsx}'], {
274
+ const sourceFiles = await customGlob(['src/**/*.{js,jsx,ts,tsx}'], {
193
275
  cwd: process.cwd(),
194
276
  ignore: ['**/node_modules/**', '**/dist/**', '**/build/**']
195
277
  });
@@ -223,11 +305,11 @@ async function detectEnvironmentAndFramework() {
223
305
  } catch (error) {
224
306
  detectedFramework = 'generic';
225
307
  }
226
- } else if (fs.existsSync(pyprojectPath) || fs.existsSync(requirementsPath)) {
308
+ } else if (SecurityUtils.safeExistsSync(pyprojectPath) || SecurityUtils.safeExistsSync(requirementsPath)) {
227
309
  detectedLanguage = 'python';
228
310
  try {
229
- if (fs.existsSync(requirementsPath)) {
230
- const requirements = fs.readFileSync(requirementsPath, 'utf8');
311
+ if (SecurityUtils.safeExistsSync(requirementsPath)) {
312
+ const requirements = SecurityUtils.safeReadFileSync(requirementsPath, path.dirname(requirementsPath), 'utf8');
231
313
  if (requirements.includes('django')) detectedFramework = 'django';
232
314
  else if (requirements.includes('flask')) detectedFramework = 'flask';
233
315
  else if (requirements.includes('fastapi')) detectedFramework = 'fastapi';
@@ -236,13 +318,13 @@ async function detectEnvironmentAndFramework() {
236
318
  } catch (error) {
237
319
  detectedFramework = 'generic';
238
320
  }
239
- } else if (fs.existsSync(goModPath)) {
321
+ } else if (SecurityUtils.safeExistsSync(goModPath)) {
240
322
  detectedLanguage = 'go';
241
323
  detectedFramework = 'generic';
242
- } else if (fs.existsSync(pomPath)) {
324
+ } else if (SecurityUtils.safeExistsSync(pomPath)) {
243
325
  detectedLanguage = 'java';
244
326
  try {
245
- const pomContent = fs.readFileSync(pomPath, 'utf8');
327
+ const pomContent = SecurityUtils.safeReadFileSync(pomPath, path.dirname(pomPath), 'utf8');
246
328
  if (pomContent.includes('spring-boot')) detectedFramework = 'spring-boot';
247
329
  else if (pomContent.includes('spring')) detectedFramework = 'spring';
248
330
  else if (pomContent.includes('quarkus')) detectedFramework = 'quarkus';
@@ -250,10 +332,10 @@ async function detectEnvironmentAndFramework() {
250
332
  } catch (error) {
251
333
  detectedFramework = 'generic';
252
334
  }
253
- } else if (fs.existsSync(composerPath)) {
335
+ } else if (SecurityUtils.safeExistsSync(composerPath)) {
254
336
  detectedLanguage = 'php';
255
337
  try {
256
- const composer = JSON.parse(fs.readFileSync(composerPath, 'utf8'));
338
+ const composer = JSON.parse(SecurityUtils.safeReadFileSync(composerPath, path.dirname(composerPath), 'utf8'));
257
339
  const deps = composer.require || {};
258
340
 
259
341
  if (deps['laravel/framework']) detectedFramework = 'laravel';
@@ -461,7 +543,7 @@ class I18nManager {
461
543
  // Only auto-detect if no settings are configured
462
544
  for (const possiblePath of possibleI18nPaths) {
463
545
  const resolvedPath = path.resolve(projectRoot, possiblePath);
464
- if (fs.existsSync(resolvedPath)) {
546
+ if (SecurityUtils.safeExistsSync(resolvedPath)) {
465
547
  // Check if it contains language directories
466
548
  try {
467
549
  const items = fs.readdirSync(resolvedPath);
@@ -487,13 +569,13 @@ class I18nManager {
487
569
  async checkI18nDependencies() {
488
570
  const packageJsonPath = path.resolve('./package.json');
489
571
 
490
- if (!fs.existsSync(packageJsonPath)) {
572
+ if (!SecurityUtils.safeExistsSync(packageJsonPath)) {
491
573
  console.log(this.ui ? this.ui.t('errors.noPackageJson') : 'No package.json found');
492
574
  return false; // Treat as no framework detected
493
575
  }
494
576
 
495
577
  try {
496
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
578
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
497
579
  // Include peerDependencies in the check
498
580
  const dependencies = {
499
581
  ...packageJson.dependencies,
@@ -610,12 +692,29 @@ class I18nManager {
610
692
 
611
693
  // Add this run method after the checkI18nDependencies method
612
694
  async run() {
695
+ // Add timeout to prevent hanging
696
+ const args = this.parseArgs();
697
+
698
+ const timeout = setTimeout(() => {
699
+ console.error('āŒ CLI startup timeout - something is hanging');
700
+ if (args.debug) {
701
+ console.error('šŸ” DEBUG: Last known execution point reached');
702
+ }
703
+ process.exit(1);
704
+ }, 10000); // 10 second timeout
705
+
706
+ if (args.debug) {
707
+ console.log('šŸ” DEBUG: Starting i18ntk-manage.js...');
708
+ console.log('šŸ” DEBUG: Process.argv:', process.argv);
709
+ console.log('šŸ” DEBUG: Parsed args:', args);
710
+ console.log('šŸ” DEBUG: About to call SetupEnforcer.checkSetupCompleteAsync()');
711
+ }
712
+
613
713
  let prompt;
614
714
  try {
615
715
  // Ensure setup is complete before running any operations
616
716
  await SetupEnforcer.checkSetupCompleteAsync();
617
-
618
- const args = this.parseArgs();
717
+
619
718
  prompt = createPrompt({ noPrompt: args.noPrompt || Boolean(args.adminPin) });
620
719
  const interactive = isInteractive({ noPrompt: args.noPrompt || Boolean(args.adminPin) });
621
720
 
@@ -1186,7 +1285,7 @@ class I18nManager {
1186
1285
  console.log(t('debug.runningDebugTool', { displayName }));
1187
1286
  try {
1188
1287
  const toolPath = path.join(__dirname, '..', 'scripts', 'debug', toolName);
1189
- if (fs.existsSync(toolPath)) {
1288
+ if (SecurityUtils.safeExistsSync(toolPath)) {
1190
1289
  console.log(`Debug tool available: ${toolName}`);
1191
1290
  console.log(`To run this tool manually: node "${toolPath}"`);
1192
1291
  console.log(`Working directory: ${path.join(__dirname, '..')}`);
@@ -1209,7 +1308,7 @@ class I18nManager {
1209
1308
 
1210
1309
  try {
1211
1310
  const logsDir = path.join(__dirname, '..', 'scripts', 'debug', 'logs');
1212
- if (fs.existsSync(logsDir)) {
1311
+ if (SecurityUtils.safeExistsSync(logsDir)) {
1213
1312
  const files = fs.readdirSync(logsDir)
1214
1313
  .filter(file => file.endsWith('.log') || file.endsWith('.txt'))
1215
1314
  .sort((a, b) => {
@@ -1230,7 +1329,7 @@ class I18nManager {
1230
1329
  const fileIndex = parseInt(choice) - 1;
1231
1330
 
1232
1331
  if (fileIndex >= 0 && fileIndex < files.length) {
1233
- const logContent = fs.readFileSync(path.join(logsDir, files[fileIndex]), 'utf8');
1332
+ const logContent = SecurityUtils.safeReadFileSync(path.join(logsDir, files[fileIndex]), logsDir, 'utf8');
1234
1333
  console.log(`\n${t('debug.contentOf', { filename: files[fileIndex] })}:`);
1235
1334
  console.log('============================================================');
1236
1335
  console.log(logContent.slice(-2000)); // Show last 2000 characters
@@ -1290,7 +1389,7 @@ class I18nManager {
1290
1389
 
1291
1390
  // Check which directories exist and have files
1292
1391
  for (const dir of targetDirs) {
1293
- if (fs.existsSync(dir.path)) {
1392
+ if (SecurityUtils.safeExistsSync(dir.path)) {
1294
1393
  const files = this.getAllReportFiles(dir.path);
1295
1394
  if (files.length > 0) {
1296
1395
  availableDirs.push({
@@ -1429,7 +1528,7 @@ class I18nManager {
1429
1528
  let files = [];
1430
1529
 
1431
1530
  try {
1432
- if (!fs.existsSync(dir)) {
1531
+ if (!SecurityUtils.safeExistsSync(dir)) {
1433
1532
  return [];
1434
1533
  }
1435
1534
 
@@ -1561,7 +1660,7 @@ if (require.main === module) {
1561
1660
  if (args.includes('--version') || args.includes('-v')) {
1562
1661
  try {
1563
1662
  const packageJsonPath = path.resolve(__dirname, '../package.json');
1564
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
1663
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
1565
1664
  const versionInfo = packageJson.versionInfo || {};
1566
1665
 
1567
1666
  console.log(`\nšŸŒ i18n Toolkit (i18ntk)`);
@@ -1592,4 +1691,4 @@ if (require.main === module) {
1592
1691
  manager.run();
1593
1692
  }
1594
1693
 
1595
- module.exports = I18nManager;
1694
+ module.exports = I18nManager;