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/init-helper.js
CHANGED
|
@@ -1,81 +1,121 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const configManager = require('./config-
|
|
4
|
-
const
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const configManager = require('./config-manager');
|
|
4
|
+
const SecurityUtils = require('./security');
|
|
5
|
+
|
|
6
|
+
function ensureDirectory(dirPath) {
|
|
7
|
+
if (!dirPath || typeof dirPath !== 'string') return;
|
|
8
|
+
if (!SecurityUtils.safeExistsSync(dirPath)) {
|
|
9
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function readJsonSafe(filePath) {
|
|
14
|
+
try {
|
|
15
|
+
if (!SecurityUtils.safeExistsSync(filePath)) return null;
|
|
16
|
+
const raw = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
17
|
+
if (!raw) return null;
|
|
18
|
+
return JSON.parse(raw);
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hasSourceLanguageFiles(sourceDir, sourceLanguage) {
|
|
25
|
+
const baseSourceDir = path.resolve(sourceDir);
|
|
26
|
+
const modularLanguageDir = path.join(baseSourceDir, sourceLanguage);
|
|
27
|
+
const singleLanguageFile = path.join(baseSourceDir, `${sourceLanguage}.json`);
|
|
28
|
+
|
|
29
|
+
if (SecurityUtils.safeExistsSync(modularLanguageDir)) {
|
|
30
|
+
try {
|
|
31
|
+
if (fs.statSync(modularLanguageDir).isDirectory()) {
|
|
32
|
+
return fs.readdirSync(modularLanguageDir).some(file => file.endsWith('.json'));
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// Continue with single-file fallback.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return SecurityUtils.safeExistsSync(singleLanguageFile);
|
|
40
|
+
}
|
|
5
41
|
|
|
6
42
|
/**
|
|
7
43
|
* Check if the project is properly initialized
|
|
8
44
|
* @param {Object} options - Options for initialization check
|
|
9
45
|
* @returns {Promise<Object>} Object containing initialization status and config
|
|
10
46
|
*/
|
|
11
|
-
async function checkInitialized(options = {}) {
|
|
12
|
-
const settings = configManager.
|
|
13
|
-
const currentVersion = require('../package.json').version;
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
47
|
+
async function checkInitialized(options = {}) {
|
|
48
|
+
const settings = configManager.getConfig ? configManager.getConfig() : {};
|
|
49
|
+
const currentVersion = require('../package.json').version;
|
|
50
|
+
const projectConfigPath = configManager.CONFIG_PATH || path.join(process.cwd(), '.i18ntk-config');
|
|
51
|
+
const configDir = path.dirname(projectConfigPath);
|
|
52
|
+
|
|
53
|
+
// Ensure config directory exists
|
|
54
|
+
ensureDirectory(configDir);
|
|
55
|
+
|
|
19
56
|
const defaultConfig = {
|
|
20
57
|
sourceDir: settings.sourceDir || './locales',
|
|
21
58
|
sourceLanguage: settings.sourceLanguage || 'en',
|
|
22
59
|
projectRoot: path.resolve('.'),
|
|
23
60
|
framework: settings.framework || { detected: false, prompt: 'always' },
|
|
24
|
-
configDir: configDir
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
configDir: configDir
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Primary source of truth in v2: project-level .i18ntk-config
|
|
65
|
+
const projectConfig = readJsonSafe(projectConfigPath);
|
|
66
|
+
if (projectConfig?.setup?.completed === true) {
|
|
67
|
+
return {
|
|
68
|
+
initialized: true,
|
|
69
|
+
config: {
|
|
70
|
+
...defaultConfig,
|
|
71
|
+
...projectConfig,
|
|
72
|
+
framework: projectConfig.framework || defaultConfig.framework
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Backward compatibility: legacy initialization marker file.
|
|
78
|
+
const initFilePath = path.join(configDir, 'initialization.json');
|
|
79
|
+
const initStatus = readJsonSafe(initFilePath);
|
|
80
|
+
const isLegacyInitialized = Boolean(initStatus?.initialized) && (
|
|
81
|
+
!initStatus.version ||
|
|
82
|
+
initStatus.version.split('.')[0] === currentVersion.split('.')[0]
|
|
83
|
+
);
|
|
84
|
+
if (isLegacyInitialized) {
|
|
85
|
+
return {
|
|
86
|
+
initialized: true,
|
|
87
|
+
config: {
|
|
88
|
+
...defaultConfig,
|
|
89
|
+
...initStatus,
|
|
90
|
+
framework: initStatus.framework || defaultConfig.framework
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Final fallback: detect existing source language files and mark initialized.
|
|
96
|
+
const sourceDir = options.sourceDir || defaultConfig.sourceDir;
|
|
97
|
+
const sourceLanguage = options.sourceLanguage || defaultConfig.sourceLanguage;
|
|
98
|
+
const hasLanguageFiles = hasSourceLanguageFiles(sourceDir, sourceLanguage);
|
|
99
|
+
|
|
100
|
+
// If language files exist but no init file, create one
|
|
101
|
+
if (hasLanguageFiles) {
|
|
102
|
+
const initData = {
|
|
103
|
+
initialized: true,
|
|
104
|
+
version: currentVersion,
|
|
105
|
+
timestamp: new Date().toISOString(),
|
|
106
|
+
sourceDir,
|
|
107
|
+
sourceLanguage,
|
|
108
|
+
detectedLanguage: defaultConfig.detectedLanguage,
|
|
109
|
+
detectedFramework: defaultConfig.detectedFramework,
|
|
110
|
+
lastUpdated: new Date().toISOString()
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
ensureDirectory(path.dirname(initFilePath));
|
|
114
|
+
SecurityUtils.safeWriteFileSync(initFilePath, JSON.stringify(initData, null, 2), path.dirname(initFilePath), 'utf8');
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
initialized: true,
|
|
118
|
+
config: { ...defaultConfig, ...initData }
|
|
79
119
|
};
|
|
80
120
|
}
|
|
81
121
|
|
|
@@ -90,33 +130,51 @@ async function checkInitialized(options = {}) {
|
|
|
90
130
|
* @param {Object} config - Configuration to save
|
|
91
131
|
* @returns {Promise<void>}
|
|
92
132
|
*/
|
|
93
|
-
async function markAsInitialized(config) {
|
|
94
|
-
const settings = configManager.
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
133
|
+
async function markAsInitialized(config) {
|
|
134
|
+
const settings = configManager.getConfig ? configManager.getConfig() : {};
|
|
135
|
+
const projectConfigPath = configManager.CONFIG_PATH || path.join(process.cwd(), '.i18ntk-config');
|
|
136
|
+
const configDir = path.dirname(projectConfigPath);
|
|
137
|
+
const initFilePath = path.join(configDir, 'initialization.json');
|
|
138
|
+
const currentVersion = require('../package.json').version;
|
|
139
|
+
const now = new Date().toISOString();
|
|
140
|
+
const sourceDir = config?.sourceDir || settings.sourceDir || './locales';
|
|
141
|
+
const sourceLanguage = config?.sourceLanguage || settings.sourceLanguage || 'en';
|
|
142
|
+
|
|
143
|
+
const initData = {
|
|
144
|
+
initialized: true,
|
|
145
|
+
version: currentVersion,
|
|
146
|
+
timestamp: now,
|
|
147
|
+
sourceDir,
|
|
148
|
+
sourceLanguage,
|
|
149
|
+
detectedLanguage: config?.detectedLanguage || settings.detectedLanguage,
|
|
150
|
+
detectedFramework: config?.detectedFramework || settings.detectedFramework,
|
|
151
|
+
lastUpdated: now
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
ensureDirectory(path.dirname(initFilePath));
|
|
155
|
+
SecurityUtils.safeWriteFileSync(initFilePath, JSON.stringify(initData, null, 2), path.dirname(initFilePath), 'utf8');
|
|
156
|
+
|
|
157
|
+
const mergedConfig = {
|
|
158
|
+
...settings,
|
|
159
|
+
...(config || {}),
|
|
160
|
+
sourceDir,
|
|
161
|
+
sourceLanguage,
|
|
162
|
+
version: currentVersion,
|
|
163
|
+
setup: {
|
|
164
|
+
...(settings.setup || {}),
|
|
165
|
+
completed: true,
|
|
166
|
+
completedAt: now,
|
|
167
|
+
version: currentVersion,
|
|
168
|
+
setupId: settings.setup?.setupId || `setup_${Date.now()}`
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
if (configManager.saveConfig) {
|
|
173
|
+
await configManager.saveConfig(mergedConfig);
|
|
174
|
+
} else {
|
|
175
|
+
SecurityUtils.safeWriteFileSync(projectConfigPath, JSON.stringify(mergedConfig, null, 2), path.dirname(projectConfigPath), 'utf8');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
120
178
|
|
|
121
179
|
module.exports = {
|
|
122
180
|
checkInitialized,
|
package/utils/json-output.js
CHANGED
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON Output Utility for i18ntk commands
|
|
3
|
-
* Provides consistent machine-readable output format for CI/CD integration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
class JsonOutput {
|
|
7
|
-
constructor(command) {
|
|
8
|
-
this.command = command;
|
|
9
|
-
this.version = this.getPackageVersion();
|
|
10
|
-
this.startTime = Date.now();
|
|
11
|
-
this.data = {
|
|
12
|
-
command: this.command,
|
|
13
|
-
version: this.version,
|
|
14
|
-
status: 'ok',
|
|
15
|
-
stats: {},
|
|
16
|
-
issues: [],
|
|
17
|
-
metadata: {
|
|
18
|
-
timestamp: new Date().toISOString(),
|
|
19
|
-
duration: 0
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
getPackageVersion() {
|
|
25
|
-
try {
|
|
26
|
-
const packageJson = require('../package.json');
|
|
27
|
-
return packageJson.version;
|
|
28
|
-
} catch (error) {
|
|
29
|
-
return '1.8.3';
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Set the overall status
|
|
35
|
-
* @param {'ok'|'warn'|'error'} status
|
|
36
|
-
*/
|
|
37
|
-
setStatus(status) {
|
|
38
|
-
this.data.status = status;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Add statistics to the output
|
|
43
|
-
* @param {Object} stats
|
|
44
|
-
*/
|
|
45
|
-
setStats(stats) {
|
|
46
|
-
this.data.stats = { ...this.data.stats, ...stats };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Add an issue to the output
|
|
51
|
-
* @param {Object} issue
|
|
52
|
-
*/
|
|
53
|
-
addIssue(issue) {
|
|
54
|
-
this.data.issues.push({
|
|
55
|
-
file: issue.file || '',
|
|
56
|
-
key: issue.key || '',
|
|
57
|
-
type: issue.type || 'unknown',
|
|
58
|
-
message: issue.message || '',
|
|
59
|
-
severity: issue.severity || 'info'
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Add metadata information
|
|
65
|
-
* @param {Object} metadata
|
|
66
|
-
*/
|
|
67
|
-
addMetadata(metadata) {
|
|
68
|
-
this.data.metadata = { ...this.data.metadata, ...metadata };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Finalize and output the JSON
|
|
73
|
-
*/
|
|
74
|
-
output() {
|
|
75
|
-
this.data.metadata.duration = Date.now() - this.startTime;
|
|
76
|
-
|
|
77
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
78
|
-
console.log(JSON.stringify(this.data, null, 2));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return this.data;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Output error in JSON format
|
|
86
|
-
* @param {Error} error
|
|
87
|
-
*/
|
|
88
|
-
outputError(error) {
|
|
89
|
-
this.setStatus('error');
|
|
90
|
-
this.addIssue({
|
|
91
|
-
type: 'error',
|
|
92
|
-
message: error.message,
|
|
93
|
-
severity: 'error'
|
|
94
|
-
});
|
|
95
|
-
this.output();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
1
|
+
/**
|
|
2
|
+
* JSON Output Utility for i18ntk commands
|
|
3
|
+
* Provides consistent machine-readable output format for CI/CD integration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class JsonOutput {
|
|
7
|
+
constructor(command) {
|
|
8
|
+
this.command = command;
|
|
9
|
+
this.version = this.getPackageVersion();
|
|
10
|
+
this.startTime = Date.now();
|
|
11
|
+
this.data = {
|
|
12
|
+
command: this.command,
|
|
13
|
+
version: this.version,
|
|
14
|
+
status: 'ok',
|
|
15
|
+
stats: {},
|
|
16
|
+
issues: [],
|
|
17
|
+
metadata: {
|
|
18
|
+
timestamp: new Date().toISOString(),
|
|
19
|
+
duration: 0
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getPackageVersion() {
|
|
25
|
+
try {
|
|
26
|
+
const packageJson = require('../package.json');
|
|
27
|
+
return packageJson.version;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return '1.8.3';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set the overall status
|
|
35
|
+
* @param {'ok'|'warn'|'error'} status
|
|
36
|
+
*/
|
|
37
|
+
setStatus(status) {
|
|
38
|
+
this.data.status = status;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Add statistics to the output
|
|
43
|
+
* @param {Object} stats
|
|
44
|
+
*/
|
|
45
|
+
setStats(stats) {
|
|
46
|
+
this.data.stats = { ...this.data.stats, ...stats };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Add an issue to the output
|
|
51
|
+
* @param {Object} issue
|
|
52
|
+
*/
|
|
53
|
+
addIssue(issue) {
|
|
54
|
+
this.data.issues.push({
|
|
55
|
+
file: issue.file || '',
|
|
56
|
+
key: issue.key || '',
|
|
57
|
+
type: issue.type || 'unknown',
|
|
58
|
+
message: issue.message || '',
|
|
59
|
+
severity: issue.severity || 'info'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Add metadata information
|
|
65
|
+
* @param {Object} metadata
|
|
66
|
+
*/
|
|
67
|
+
addMetadata(metadata) {
|
|
68
|
+
this.data.metadata = { ...this.data.metadata, ...metadata };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Finalize and output the JSON
|
|
73
|
+
*/
|
|
74
|
+
output() {
|
|
75
|
+
this.data.metadata.duration = Date.now() - this.startTime;
|
|
76
|
+
|
|
77
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
78
|
+
console.log(JSON.stringify(this.data, null, 2));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return this.data;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Output error in JSON format
|
|
86
|
+
* @param {Error} error
|
|
87
|
+
*/
|
|
88
|
+
outputError(error) {
|
|
89
|
+
this.setStatus('error');
|
|
90
|
+
this.addIssue({
|
|
91
|
+
type: 'error',
|
|
92
|
+
message: error.message,
|
|
93
|
+
severity: 'error'
|
|
94
|
+
});
|
|
95
|
+
this.output();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
99
|
module.exports = JsonOutput;
|
package/utils/logger.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const colors = require('./colors-new');
|
|
2
|
+
const { envManager } = require('./env-manager');
|
|
2
3
|
|
|
3
4
|
// Enhanced logger with TTY detection and proper stream handling
|
|
4
5
|
const logger = {
|
|
@@ -44,9 +45,12 @@ const logger = {
|
|
|
44
45
|
logger.log(output, colors.blue);
|
|
45
46
|
},
|
|
46
47
|
|
|
47
|
-
// Debug logging (only when DEBUG env var is set)
|
|
48
|
+
// Debug logging (only when DEBUG env var is set or log level is debug)
|
|
48
49
|
debug: (message) => {
|
|
49
|
-
|
|
50
|
+
const logLevel = envManager.get('I18NTK_LOG_LEVEL');
|
|
51
|
+
const debugEnabled = process.env.DEBUG || logLevel === 'debug';
|
|
52
|
+
|
|
53
|
+
if (debugEnabled) {
|
|
50
54
|
const output = `[DEBUG] ${message}`;
|
|
51
55
|
if (process.stderr.isTTY) {
|
|
52
56
|
process.stderr.write(colors.gray(output) + '\n');
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal zero-dependency subset of commander used by i18ntk language CLIs.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
function toCamelCase(input) {
|
|
6
|
+
return String(input || '').replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function parseOptionDefinition(flags, defaultValue) {
|
|
10
|
+
const longMatch = flags.match(/--([a-zA-Z0-9-]+)/);
|
|
11
|
+
const shortMatch = flags.match(/(^|\s)-([a-zA-Z])(\s|,|$)/);
|
|
12
|
+
const hasValue = /<[^>]+>/.test(flags);
|
|
13
|
+
const longName = longMatch ? longMatch[1] : null;
|
|
14
|
+
const shortName = shortMatch ? shortMatch[2] : null;
|
|
15
|
+
const name = toCamelCase(longName || shortName || '');
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
flags,
|
|
19
|
+
hasValue,
|
|
20
|
+
defaultValue,
|
|
21
|
+
longFlag: longName ? `--${longName}` : null,
|
|
22
|
+
shortFlag: shortName ? `-${shortName}` : null,
|
|
23
|
+
name
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseOptions(args, optionDefs) {
|
|
28
|
+
const options = {};
|
|
29
|
+
|
|
30
|
+
for (const def of optionDefs) {
|
|
31
|
+
if (def.defaultValue !== undefined) {
|
|
32
|
+
options[def.name] = def.defaultValue;
|
|
33
|
+
} else if (!def.hasValue) {
|
|
34
|
+
options[def.name] = false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
const arg = args[i];
|
|
40
|
+
if (!arg || !arg.startsWith('-')) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let matchedDef = null;
|
|
45
|
+
let inlineValue;
|
|
46
|
+
|
|
47
|
+
if (arg.startsWith('--')) {
|
|
48
|
+
const [flag, value] = arg.split('=', 2);
|
|
49
|
+
matchedDef = optionDefs.find(def => def.longFlag === flag);
|
|
50
|
+
inlineValue = value;
|
|
51
|
+
} else {
|
|
52
|
+
matchedDef = optionDefs.find(def => def.shortFlag === arg);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!matchedDef) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!matchedDef.hasValue) {
|
|
60
|
+
options[matchedDef.name] = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let value = inlineValue;
|
|
65
|
+
if (value === undefined) {
|
|
66
|
+
const next = args[i + 1];
|
|
67
|
+
if (next !== undefined && !String(next).startsWith('-')) {
|
|
68
|
+
value = next;
|
|
69
|
+
i++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
options[matchedDef.name] = value !== undefined ? value : '';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return options;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
class MiniCommand {
|
|
80
|
+
constructor(name) {
|
|
81
|
+
this._name = name;
|
|
82
|
+
this._description = '';
|
|
83
|
+
this._options = [];
|
|
84
|
+
this._action = null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
description(text) {
|
|
88
|
+
this._description = text;
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
option(flags, _description, defaultValue) {
|
|
93
|
+
this._options.push(parseOptionDefinition(flags, defaultValue));
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
action(handler) {
|
|
98
|
+
this._action = handler;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
execute(args) {
|
|
103
|
+
const parsed = parseOptions(args, this._options);
|
|
104
|
+
if (typeof this._action === 'function') {
|
|
105
|
+
const result = this._action(parsed);
|
|
106
|
+
if (result && typeof result.then === 'function') {
|
|
107
|
+
result.catch(error => {
|
|
108
|
+
console.error(error && error.message ? error.message : String(error));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
class MiniProgram {
|
|
117
|
+
constructor() {
|
|
118
|
+
this._name = '';
|
|
119
|
+
this._description = '';
|
|
120
|
+
this._version = '';
|
|
121
|
+
this._options = [];
|
|
122
|
+
this._commands = [];
|
|
123
|
+
this._opts = {};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
name(value) {
|
|
127
|
+
this._name = value;
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
description(value) {
|
|
132
|
+
this._description = value;
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
version(value) {
|
|
137
|
+
this._version = value;
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
option(flags, _description, defaultValue) {
|
|
142
|
+
this._options.push(parseOptionDefinition(flags, defaultValue));
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
command(name) {
|
|
147
|
+
const command = new MiniCommand(name);
|
|
148
|
+
this._commands.push(command);
|
|
149
|
+
return command;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
opts() {
|
|
153
|
+
return { ...this._opts };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
parse(argv = process.argv) {
|
|
157
|
+
const args = argv.slice(2);
|
|
158
|
+
|
|
159
|
+
if (this._commands.length > 0 && args.length > 0 && !args[0].startsWith('-')) {
|
|
160
|
+
const command = this._commands.find(item => item._name === args[0]);
|
|
161
|
+
if (command) {
|
|
162
|
+
command.execute(args.slice(1));
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this._opts = parseOptions(args, this._options);
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function createProgram() {
|
|
173
|
+
return new MiniProgram();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = {
|
|
177
|
+
createProgram,
|
|
178
|
+
program: createProgram()
|
|
179
|
+
};
|