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.
- package/LICENSE +1 -1
- package/README.md +141 -1185
- package/main/i18ntk-analyze.js +149 -133
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +4 -4
- package/main/i18ntk-complete.js +90 -65
- package/main/i18ntk-doctor.js +123 -103
- package/main/i18ntk-fixer.js +61 -725
- package/main/i18ntk-go.js +14 -15
- package/main/i18ntk-init.js +76 -25
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +128 -29
- package/main/i18ntk-php.js +75 -75
- package/main/i18ntk-py.js +55 -56
- package/main/i18ntk-scanner.js +59 -57
- package/main/i18ntk-setup.js +10 -396
- package/main/i18ntk-sizing.js +46 -40
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +55 -19
- package/main/i18ntk-validate.js +13 -13
- package/main/manage/commands/AnalyzeCommand.js +1124 -0
- package/main/manage/commands/BackupCommand.js +62 -0
- package/main/manage/commands/CommandRouter.js +295 -0
- package/main/manage/commands/CompleteCommand.js +61 -0
- package/main/manage/commands/DoctorCommand.js +60 -0
- package/main/manage/commands/FixerCommand.js +624 -0
- package/main/manage/commands/InitCommand.js +62 -0
- package/main/manage/commands/ScannerCommand.js +654 -0
- package/main/manage/commands/SizingCommand.js +60 -0
- package/main/manage/commands/SummaryCommand.js +61 -0
- package/main/manage/commands/UsageCommand.js +60 -0
- package/main/manage/commands/ValidateCommand.js +978 -0
- package/main/manage/index-fixed.js +1447 -0
- package/main/manage/index.js +1462 -0
- package/main/manage/managers/DebugMenu.js +140 -0
- package/main/manage/managers/InteractiveMenu.js +177 -0
- package/main/manage/managers/LanguageMenu.js +62 -0
- package/main/manage/managers/SettingsMenu.js +53 -0
- package/main/manage/services/AuthenticationService.js +263 -0
- package/main/manage/services/ConfigurationService-fixed.js +449 -0
- package/main/manage/services/ConfigurationService.js +449 -0
- package/main/manage/services/FileManagementService.js +368 -0
- package/main/manage/services/FrameworkDetectionService.js +458 -0
- package/main/manage/services/InitService.js +1051 -0
- package/main/manage/services/SetupService.js +462 -0
- package/main/manage/services/SummaryService.js +450 -0
- package/main/manage/services/UsageService.js +1502 -0
- package/package.json +32 -30
- package/runtime/enhanced.d.ts +221 -221
- package/runtime/index.d.ts +29 -29
- package/runtime/index.full.d.ts +331 -331
- package/runtime/index.js +7 -6
- package/scripts/build-lite.js +17 -17
- package/scripts/deprecate-versions.js +23 -6
- package/scripts/export-translations.js +5 -5
- package/scripts/fix-all-i18n.js +3 -3
- package/scripts/fix-and-purify-i18n.js +3 -2
- package/scripts/fix-locale-control-chars.js +110 -0
- package/scripts/lint-locales.js +80 -0
- package/scripts/locale-optimizer.js +8 -8
- package/scripts/prepublish.js +21 -21
- package/scripts/security-check.js +13 -5
- package/scripts/sync-translations.js +4 -4
- package/scripts/sync-ui-locales.js +9 -8
- package/scripts/validate-all-translations.js +8 -7
- package/scripts/verify-deprecations.js +23 -15
- package/scripts/verify-translations.js +6 -5
- package/settings/i18ntk-config.json +282 -282
- package/settings/language-config.json +5 -5
- package/settings/settings-cli.js +9 -9
- package/settings/settings-manager.js +23 -20
- package/ui-locales/de.json +2417 -2348
- package/ui-locales/en.json +2415 -2352
- package/ui-locales/es.json +2425 -2353
- package/ui-locales/fr.json +2418 -2348
- package/ui-locales/ja.json +2463 -2361
- package/ui-locales/ru.json +2463 -2359
- package/ui-locales/zh.json +2418 -2351
- package/utils/admin-auth.js +2 -2
- package/utils/admin-cli.js +297 -297
- package/utils/admin-pin.js +9 -9
- package/utils/cli-helper.js +9 -9
- package/utils/config-helper.js +152 -103
- package/utils/config-manager.js +204 -164
- package/utils/config.js +5 -4
- package/utils/env-manager.js +256 -0
- package/utils/framework-detector.js +27 -24
- package/utils/i18n-helper.js +85 -41
- package/utils/init-helper.js +152 -94
- package/utils/json-output.js +98 -98
- package/utils/logger.js +6 -2
- package/utils/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +29 -11
- package/utils/prompt.js +14 -44
- package/utils/safe-json.js +40 -0
- package/utils/secure-errors.js +3 -3
- package/utils/security-check-improved.js +390 -0
- package/utils/security-config.js +5 -5
- package/utils/security-fixed.js +607 -0
- package/utils/security.js +462 -248
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- package/utils/terminal-icons.js +1 -1
- package/utils/ultra-performance-optimizer.js +11 -9
- package/utils/watch-locales.js +2 -1
- package/utils/prompt-fixed.js +0 -55
- package/utils/security-check.js +0 -450
package/utils/config-manager.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const SecurityUtils = require('./security');
|
|
5
5
|
|
|
6
6
|
// Determine package directory and user project root
|
|
7
7
|
const packageDir = path.resolve(__dirname, '..');
|
|
8
8
|
const userProjectRoot = process.cwd();
|
|
9
9
|
|
|
10
|
-
// Always use
|
|
11
|
-
//
|
|
12
|
-
const
|
|
13
|
-
const
|
|
10
|
+
// Always use current working directory for settings to support test environments
|
|
11
|
+
// This ensures config works correctly when tests change the working directory
|
|
12
|
+
const PROJECT_CONFIG_PATH = path.join(process.cwd(), '.i18ntk-config');
|
|
13
|
+
const PROJECT_SETTINGS_DIR = path.dirname(PROJECT_CONFIG_PATH);
|
|
14
14
|
|
|
15
15
|
// Setup tracking file
|
|
16
16
|
const SETUP_COMPLETED_FILE = path.join(PROJECT_SETTINGS_DIR, 'setup.json');
|
|
@@ -241,21 +241,26 @@ const DEFAULT_CONFIG = {
|
|
|
241
241
|
"timezone": "auto"
|
|
242
242
|
};
|
|
243
243
|
|
|
244
|
-
//
|
|
245
|
-
const ENV_VAR_MAP = {
|
|
246
|
-
I18NTK_PROJECT_ROOT: 'projectRoot',
|
|
247
|
-
I18NTK_SOURCE_DIR: 'sourceDir',
|
|
248
|
-
I18NTK_I18N_DIR: 'i18nDir',
|
|
249
|
-
I18NTK_OUTPUT_DIR: 'outputDir',
|
|
250
|
-
I18NTK_FRAMEWORK_PREFERENCE: 'framework.preference',
|
|
251
|
-
I18NTK_FRAMEWORK_FALLBACK: 'framework.fallback',
|
|
252
|
-
I18NTK_FRAMEWORK_DETECT: 'framework.detect',
|
|
253
|
-
};
|
|
244
|
+
// Environment variable support has been removed in favor of exclusive .i18ntk-config configuration
|
|
254
245
|
|
|
255
246
|
let currentConfig = null;
|
|
247
|
+
let configLoadInProgress = false;
|
|
248
|
+
let recursionDepth = 0;
|
|
249
|
+
const MAX_RECURSION_DEPTH = 15; // Increased to handle legitimate sequential calls
|
|
256
250
|
|
|
257
251
|
function clone(obj) {
|
|
258
|
-
|
|
252
|
+
return JSON.parse(JSON.stringify(obj));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function checkRecursionGuard() {
|
|
256
|
+
// Disabled recursion detection to prevent false positives on legitimate sequential calls
|
|
257
|
+
// TODO: Implement more sophisticated recursion detection in future versions
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function resetRecursionGuard() {
|
|
262
|
+
recursionDepth = 0;
|
|
263
|
+
configLoadInProgress = false;
|
|
259
264
|
}
|
|
260
265
|
|
|
261
266
|
function ensureProjectSettingsDir() {
|
|
@@ -294,52 +299,38 @@ function deepMerge(target, source, basePath = '') {
|
|
|
294
299
|
}
|
|
295
300
|
|
|
296
301
|
function applyEnvOverrides(cfg) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (value === undefined) continue;
|
|
300
|
-
const keys = keyPath.split('.');
|
|
301
|
-
let current = cfg;
|
|
302
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
303
|
-
const k = keys[i];
|
|
304
|
-
if (!current[k] || typeof current[k] !== 'object') current[k] = {};
|
|
305
|
-
current = current[k];
|
|
306
|
-
}
|
|
307
|
-
const leaf = keys[keys.length - 1];
|
|
308
|
-
if (keyPath === 'framework.detect') {
|
|
309
|
-
current[leaf] = String(value).toLowerCase() !== 'false';
|
|
310
|
-
} else {
|
|
311
|
-
current[leaf] = normalizePathValue(keyPath, value);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
302
|
+
// Environment variable support has been removed in favor of exclusive .i18ntk-config configuration
|
|
303
|
+
// This function is kept for backward compatibility but does nothing
|
|
314
304
|
return cfg;
|
|
315
305
|
}
|
|
316
306
|
|
|
317
307
|
function tryReadJson(filePath) {
|
|
318
308
|
try {
|
|
319
|
-
if (!
|
|
309
|
+
if (!SecurityUtils.safeExistsSync(filePath)) {
|
|
320
310
|
return null;
|
|
321
311
|
}
|
|
322
|
-
|
|
323
|
-
const data =
|
|
312
|
+
|
|
313
|
+
const data = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
324
314
|
if (!data || data.trim() === '') {
|
|
325
315
|
console.warn(`[i18ntk] Warning: Empty or invalid JSON file at ${filePath}`);
|
|
326
316
|
return null;
|
|
327
317
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
318
|
+
|
|
319
|
+
const parsed = SecurityUtils.safeParseJSON(data);
|
|
320
|
+
if (parsed && typeof parsed === 'object') {
|
|
321
|
+
return parsed;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.error(`[i18ntk] Error parsing JSON from ${filePath}: Invalid JSON content`);
|
|
325
|
+
// Create a backup of the corrupted file
|
|
326
|
+
const backupPath = `${filePath}.corrupted-${Date.now()}.bak`;
|
|
327
|
+
try {
|
|
328
|
+
SecurityUtils.safeWriteFileSync(backupPath, data, path.dirname(backupPath), 'utf8');
|
|
329
|
+
console.warn(`[i18ntk] Created backup of corrupted config at ${backupPath}`);
|
|
330
|
+
} catch (backupError) {
|
|
331
|
+
console.error(`[i18ntk] Failed to create backup of corrupted config: ${backupError.message}`);
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
343
334
|
} catch (error) {
|
|
344
335
|
console.error(`[i18ntk] Error reading config file at ${filePath}: ${error.message}`);
|
|
345
336
|
return null;
|
|
@@ -348,7 +339,7 @@ function tryReadJson(filePath) {
|
|
|
348
339
|
|
|
349
340
|
async function migrateLegacyIfNeeded(baseCfg) {
|
|
350
341
|
// If project config does not exist but legacy exists, migrate once
|
|
351
|
-
if (!
|
|
342
|
+
if (!SecurityUtils.safeExistsSync(PROJECT_CONFIG_PATH) && SecurityUtils.safeExistsSync(LEGACY_CONFIG_PATH)) {
|
|
352
343
|
const legacy = tryReadJson(LEGACY_CONFIG_PATH);
|
|
353
344
|
if (legacy && typeof legacy === 'object') {
|
|
354
345
|
const merged = deepMerge(clone(baseCfg), legacy);
|
|
@@ -360,61 +351,95 @@ async function migrateLegacyIfNeeded(baseCfg) {
|
|
|
360
351
|
// Best-effort removal of legacy file to prevent future use
|
|
361
352
|
try { fs.unlinkSync(LEGACY_CONFIG_PATH); } catch (_) {}
|
|
362
353
|
// Deprecation notice
|
|
363
|
-
console.warn('[i18ntk] Deprecated config location detected (~/.i18ntk).
|
|
364
|
-
return merged;
|
|
365
|
-
} catch (_) {
|
|
366
|
-
// If write fails, fall back to in-memory config without deleting legacy
|
|
367
|
-
console.warn('[i18ntk] Deprecated config location detected (~/.i18ntk). Using migrated settings in memory; failed to persist to
|
|
368
|
-
return merged;
|
|
369
|
-
}
|
|
354
|
+
console.warn('[i18ntk] Deprecated config location detected (~/.i18ntk). Configuration was migrated to project .i18ntk-config.');
|
|
355
|
+
return merged;
|
|
356
|
+
} catch (_) {
|
|
357
|
+
// If write fails, fall back to in-memory config without deleting legacy
|
|
358
|
+
console.warn('[i18ntk] Deprecated config location detected (~/.i18ntk). Using migrated settings in memory; failed to persist to .i18ntk-config.');
|
|
359
|
+
return merged;
|
|
360
|
+
}
|
|
370
361
|
}
|
|
371
362
|
}
|
|
372
363
|
return null;
|
|
373
364
|
}
|
|
374
365
|
|
|
375
366
|
function loadConfig() {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const pkgCfg = tryReadJson(PACKAGE_CONFIG_PATH);
|
|
385
|
-
if (pkgCfg) {
|
|
386
|
-
cfg = deepMerge(clone(DEFAULT_CONFIG), pkgCfg);
|
|
367
|
+
// Check for recursion
|
|
368
|
+
const recursionFallback = checkRecursionGuard();
|
|
369
|
+
if (recursionFallback) return recursionFallback;
|
|
370
|
+
|
|
371
|
+
// Return cached config if available
|
|
372
|
+
if (currentConfig) {
|
|
373
|
+
resetRecursionGuard();
|
|
374
|
+
return currentConfig;
|
|
387
375
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
// Ignore migration errors; we still return merged cfg in memory
|
|
395
|
-
// eslint-disable-next-line no-unused-vars
|
|
396
|
-
console.warn('[i18ntk] Detected legacy config at ~/.i18ntk. Migrating to project settings directory...');
|
|
397
|
-
const _ = (async () => { await migrateLegacyIfNeeded(DEFAULT_CONFIG); })();
|
|
398
|
-
}
|
|
376
|
+
|
|
377
|
+
// Prevent concurrent loading
|
|
378
|
+
if (configLoadInProgress) {
|
|
379
|
+
console.warn('[i18ntk] Configuration loading already in progress, returning defaults');
|
|
380
|
+
resetRecursionGuard();
|
|
381
|
+
return clone(DEFAULT_CONFIG);
|
|
399
382
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
383
|
+
|
|
384
|
+
configLoadInProgress = true;
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
let cfg = clone(DEFAULT_CONFIG);
|
|
388
|
+
// 1) Project config (primary)
|
|
389
|
+
const projectCfg = tryReadJson(PROJECT_CONFIG_PATH);
|
|
390
|
+
if (projectCfg) {
|
|
391
|
+
cfg = deepMerge(clone(DEFAULT_CONFIG), projectCfg);
|
|
392
|
+
} else {
|
|
393
|
+
// 2) Package default (read-only)
|
|
394
|
+
const pkgCfg = tryReadJson(PACKAGE_CONFIG_PATH);
|
|
395
|
+
if (pkgCfg) {
|
|
396
|
+
cfg = deepMerge(clone(DEFAULT_CONFIG), pkgCfg);
|
|
397
|
+
}
|
|
398
|
+
// 3) Legacy migration (read-only source)
|
|
399
|
+
if (!projectCfg) {
|
|
400
|
+
const fromLegacy = tryReadJson(LEGACY_CONFIG_PATH);
|
|
401
|
+
if (fromLegacy) {
|
|
402
|
+
cfg = deepMerge(clone(DEFAULT_CONFIG), fromLegacy);
|
|
403
|
+
// Attempt to migrate to project settings
|
|
404
|
+
// Ignore migration errors; we still return merged cfg in memory
|
|
405
|
+
// eslint-disable-next-line no-unused-vars
|
|
406
|
+
console.warn('[i18ntk] Detected legacy config at ~/.i18ntk. Migrating to project settings directory...');
|
|
407
|
+
const _ = (async () => { await migrateLegacyIfNeeded(DEFAULT_CONFIG); })();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
applyEnvOverrides(cfg);
|
|
412
|
+
currentConfig = cfg;
|
|
413
|
+
return currentConfig;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.error('[i18ntk] Error in loadConfig:', error.message);
|
|
416
|
+
currentConfig = clone(DEFAULT_CONFIG);
|
|
417
|
+
return currentConfig;
|
|
418
|
+
} finally {
|
|
419
|
+
configLoadInProgress = false;
|
|
420
|
+
recursionDepth = Math.max(0, recursionDepth - 1);
|
|
421
|
+
}
|
|
404
422
|
}
|
|
405
423
|
|
|
406
|
-
async function saveConfig(cfg = currentConfig) {
|
|
407
|
-
if (!cfg) return;
|
|
408
|
-
|
|
424
|
+
async function saveConfig(cfg = currentConfig) {
|
|
425
|
+
if (!cfg || typeof cfg !== 'object') return;
|
|
426
|
+
|
|
409
427
|
try {
|
|
410
428
|
// Ensure settings directory exists
|
|
411
|
-
if (!
|
|
429
|
+
if (!SecurityUtils.safeExistsSync(PROJECT_SETTINGS_DIR)) {
|
|
412
430
|
fs.mkdirSync(PROJECT_SETTINGS_DIR, { recursive: true });
|
|
413
431
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
432
|
+
|
|
433
|
+
const serialized = JSON.stringify(cfg, null, 2);
|
|
434
|
+
if (typeof serialized !== 'string' || serialized.length === 0) {
|
|
435
|
+
throw new Error('Cannot save empty configuration payload');
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Atomic write prevents partially-written/empty config files.
|
|
439
|
+
const tempPath = `${PROJECT_CONFIG_PATH}.tmp`;
|
|
440
|
+
await fs.promises.writeFile(tempPath, serialized, 'utf8');
|
|
441
|
+
await fs.promises.rename(tempPath, PROJECT_CONFIG_PATH);
|
|
442
|
+
currentConfig = cfg;
|
|
418
443
|
} catch (error) {
|
|
419
444
|
console.error('[i18ntk] Error saving configuration:', error.message);
|
|
420
445
|
throw error;
|
|
@@ -422,58 +447,71 @@ async function saveConfig(cfg = currentConfig) {
|
|
|
422
447
|
}
|
|
423
448
|
|
|
424
449
|
function getConfig() {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
try {
|
|
430
|
-
// Ensure settings directory exists
|
|
431
|
-
if (!fs.existsSync(PROJECT_SETTINGS_DIR)) {
|
|
432
|
-
fs.mkdirSync(PROJECT_SETTINGS_DIR, { recursive: true });
|
|
433
|
-
}
|
|
450
|
+
// Check for recursion
|
|
451
|
+
const recursionFallback = checkRecursionGuard();
|
|
452
|
+
if (recursionFallback) return resolvePaths(recursionFallback);
|
|
434
453
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
// Check if config file exists
|
|
439
|
-
if (fs.existsSync(PROJECT_CONFIG_PATH)) {
|
|
440
|
-
const config = JSON.parse(fs.readFileSync(PROJECT_CONFIG_PATH, 'utf8'));
|
|
441
|
-
currentConfig = config;
|
|
442
|
-
return resolvePaths(config);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Check for legacy config for migration
|
|
446
|
-
if (fs.existsSync(LEGACY_CONFIG_PATH)) {
|
|
447
|
-
console.log('📦 Migrating legacy configuration...');
|
|
448
|
-
const legacyConfig = JSON.parse(fs.readFileSync(LEGACY_CONFIG_PATH, 'utf8'));
|
|
449
|
-
const migratedConfig = { ...DEFAULT_CONFIG, ...legacyConfig };
|
|
450
|
-
saveConfig(migratedConfig);
|
|
451
|
-
currentConfig = migratedConfig;
|
|
452
|
-
|
|
453
|
-
// Clean up legacy config
|
|
454
|
-
try {
|
|
455
|
-
fs.unlinkSync(LEGACY_CONFIG_PATH);
|
|
456
|
-
if (fs.readdirSync(LEGACY_CONFIG_DIR).length === 0) {
|
|
457
|
-
fs.rmdirSync(LEGACY_CONFIG_DIR);
|
|
458
|
-
}
|
|
459
|
-
} catch (cleanupError) {
|
|
460
|
-
// Ignore cleanup errors
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
return resolvePaths(migratedConfig);
|
|
454
|
+
if (currentConfig) {
|
|
455
|
+
resetRecursionGuard();
|
|
456
|
+
return resolvePaths(currentConfig);
|
|
464
457
|
}
|
|
465
458
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
459
|
+
try {
|
|
460
|
+
// Ensure settings directory exists
|
|
461
|
+
if (!SecurityUtils.safeExistsSync(PROJECT_SETTINGS_DIR)) {
|
|
462
|
+
fs.mkdirSync(PROJECT_SETTINGS_DIR, { recursive: true });
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Setup is now handled automatically by the unified config system
|
|
466
|
+
// No need to check here - handled by getUnifiedConfig
|
|
467
|
+
|
|
468
|
+
// Check if config file exists
|
|
469
|
+
if (SecurityUtils.safeExistsSync(PROJECT_CONFIG_PATH)) {
|
|
470
|
+
const rawConfig = SecurityUtils.safeReadFileSync(PROJECT_CONFIG_PATH, path.dirname(PROJECT_CONFIG_PATH), 'utf8');
|
|
471
|
+
const config = SecurityUtils.safeParseJSON(rawConfig);
|
|
472
|
+
if (config && typeof config === 'object') {
|
|
473
|
+
currentConfig = config;
|
|
474
|
+
return resolvePaths(config);
|
|
475
|
+
}
|
|
476
|
+
throw new Error('Invalid project configuration JSON');
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Check for legacy config for migration
|
|
480
|
+
if (SecurityUtils.safeExistsSync(LEGACY_CONFIG_PATH)) {
|
|
481
|
+
console.log('📦 Migrating legacy configuration...');
|
|
482
|
+
const legacyRaw = SecurityUtils.safeReadFileSync(LEGACY_CONFIG_PATH, path.dirname(LEGACY_CONFIG_PATH), 'utf8');
|
|
483
|
+
const legacyConfig = SecurityUtils.safeParseJSON(legacyRaw);
|
|
484
|
+
if (!legacyConfig || typeof legacyConfig !== 'object') {
|
|
485
|
+
throw new Error('Invalid legacy configuration JSON');
|
|
486
|
+
}
|
|
487
|
+
const migratedConfig = { ...DEFAULT_CONFIG, ...legacyConfig };
|
|
488
|
+
saveConfig(migratedConfig);
|
|
489
|
+
currentConfig = migratedConfig;
|
|
490
|
+
|
|
491
|
+
// Clean up legacy config
|
|
492
|
+
try {
|
|
493
|
+
fs.unlinkSync(LEGACY_CONFIG_PATH);
|
|
494
|
+
if (fs.readdirSync(LEGACY_CONFIG_DIR).length === 0) {
|
|
495
|
+
fs.rmdirSync(LEGACY_CONFIG_DIR);
|
|
496
|
+
}
|
|
497
|
+
} catch (cleanupError) {
|
|
498
|
+
// Ignore cleanup errors
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return resolvePaths(migratedConfig);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Use package defaults for new installation
|
|
505
|
+
console.log('📦 Initializing with default configuration...');
|
|
506
|
+
saveConfig(DEFAULT_CONFIG);
|
|
507
|
+
currentConfig = DEFAULT_CONFIG;
|
|
508
|
+
return resolvePaths(DEFAULT_CONFIG);
|
|
509
|
+
|
|
510
|
+
} catch (error) {
|
|
511
|
+
console.warn('⚠️ Error loading configuration, using defaults:', error.message);
|
|
512
|
+
currentConfig = DEFAULT_CONFIG;
|
|
513
|
+
return resolvePaths(DEFAULT_CONFIG);
|
|
514
|
+
}
|
|
477
515
|
}
|
|
478
516
|
|
|
479
517
|
async function setConfig(cfg) {
|
|
@@ -495,27 +533,30 @@ async function resetToDefaults() {
|
|
|
495
533
|
return currentConfig;
|
|
496
534
|
}
|
|
497
535
|
|
|
498
|
-
function resolvePaths(cfg
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
resolved.projectRoot = root;
|
|
502
|
-
['sourceDir', 'i18nDir', 'outputDir'].forEach(key => {
|
|
503
|
-
if (resolved[key]) resolved[key] = path.resolve(root, resolved[key]);
|
|
504
|
-
});
|
|
505
|
-
if (resolved.scriptDirectories) {
|
|
506
|
-
resolved.scriptDirectories = { ...resolved.scriptDirectories };
|
|
507
|
-
for (const [k, v] of Object.entries(resolved.scriptDirectories)) {
|
|
508
|
-
if (v) resolved.scriptDirectories[k] = path.resolve(root, v);
|
|
536
|
+
function resolvePaths(cfg) {
|
|
537
|
+
if (!cfg) {
|
|
538
|
+
cfg = clone(DEFAULT_CONFIG);
|
|
509
539
|
}
|
|
510
|
-
|
|
511
|
-
|
|
540
|
+
const root = path.resolve(projectRoot, cfg.projectRoot || '.');
|
|
541
|
+
const resolved = clone(cfg);
|
|
542
|
+
resolved.projectRoot = root;
|
|
543
|
+
['sourceDir', 'i18nDir', 'outputDir'].forEach(key => {
|
|
544
|
+
if (resolved[key]) resolved[key] = path.resolve(root, resolved[key]);
|
|
545
|
+
});
|
|
546
|
+
if (resolved.scriptDirectories) {
|
|
547
|
+
resolved.scriptDirectories = { ...resolved.scriptDirectories };
|
|
548
|
+
for (const [k, v] of Object.entries(resolved.scriptDirectories)) {
|
|
549
|
+
if (v) resolved.scriptDirectories[k] = path.resolve(root, v);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return resolved;
|
|
512
553
|
}
|
|
513
554
|
|
|
514
555
|
function toRelative(absPath) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
556
|
+
if (!absPath) return absPath;
|
|
557
|
+
const rel = path.relative(projectRoot, absPath);
|
|
558
|
+
const normalized = rel ? `./${rel.replace(/\\/g, '/')}` : '.';
|
|
559
|
+
return normalized;
|
|
519
560
|
}
|
|
520
561
|
|
|
521
562
|
|
|
@@ -532,5 +573,4 @@ module.exports = {
|
|
|
532
573
|
resolvePaths,
|
|
533
574
|
toRelative,
|
|
534
575
|
normalizePathValue,
|
|
535
|
-
}
|
|
536
|
-
|
|
576
|
+
}
|
package/utils/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const SecurityUtils = require('./security');
|
|
3
4
|
|
|
4
5
|
const settingsManager = require('../settings/settings-manager');
|
|
5
6
|
const CONFIG_FILE = 'i18ntk-config.json';
|
|
@@ -46,12 +47,12 @@ function loadConfig(cwd = settingsManager.configDir) {
|
|
|
46
47
|
const configPath = getConfigPath(cwd);
|
|
47
48
|
|
|
48
49
|
// Check if file exists and is accessible
|
|
49
|
-
if (!
|
|
50
|
+
if (!SecurityUtils.safeExistsSync(configPath)) {
|
|
50
51
|
return null;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
// Read file with explicit encoding
|
|
54
|
-
const raw =
|
|
55
|
+
const raw = SecurityUtils.safeReadFileSync(configPath, settingsManager.configDir, 'utf8');
|
|
55
56
|
|
|
56
57
|
// Basic validation of file content
|
|
57
58
|
if (!raw || typeof raw !== 'string') {
|
|
@@ -83,12 +84,12 @@ function saveConfig(config, cwd = settingsManager.configDir) {
|
|
|
83
84
|
const dir = path.dirname(configPath);
|
|
84
85
|
|
|
85
86
|
// Ensure directory exists
|
|
86
|
-
if (!
|
|
87
|
+
if (!SecurityUtils.safeExistsSync(dir)) {
|
|
87
88
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
// Write file with secure permissions (read/write for owner only)
|
|
91
|
-
|
|
92
|
+
SecurityUtils.safeWriteFileSync(
|
|
92
93
|
configPath,
|
|
93
94
|
JSON.stringify(config, null, 2),
|
|
94
95
|
{ mode: 0o600, encoding: 'utf8' }
|