i18ntk 1.10.2 → 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 -1191
- package/main/i18ntk-analyze.js +65 -84
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +3 -3
- 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 +77 -26
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +129 -30
- 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 +9 -404
- package/main/i18ntk-sizing.js +6 -6
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +54 -18
- 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 -29
- 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 +117 -117
- 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 +157 -161
- 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 +18 -18
- 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 +73 -104
- package/utils/config-manager.js +204 -171
- package/utils/config.js +5 -4
- package/utils/env-manager.js +249 -263
- 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/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +40 -29
- 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 +652 -602
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- 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 -454
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Detection Service
|
|
3
|
+
* Handles framework detection, i18n directory detection, and framework suggestions
|
|
4
|
+
* @module services/FrameworkDetectionService
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const SecurityUtils = require('../../../utils/security');
|
|
10
|
+
|
|
11
|
+
module.exports = class FrameworkDetectionService {
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.settings = null;
|
|
15
|
+
this.configManager = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Initialize the service with required dependencies
|
|
20
|
+
* @param {Object} configManager - Configuration manager instance
|
|
21
|
+
*/
|
|
22
|
+
initialize(configManager) {
|
|
23
|
+
this.configManager = configManager;
|
|
24
|
+
this.settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Detect environment and framework from project structure
|
|
29
|
+
* @returns {Promise<Object>} Object with detectedLanguage and detectedFramework
|
|
30
|
+
*/
|
|
31
|
+
async detectEnvironmentAndFramework() {
|
|
32
|
+
// Defensive check to ensure SecurityUtils is available
|
|
33
|
+
if (!SecurityUtils) {
|
|
34
|
+
throw new Error('SecurityUtils is not available. This may indicate a module loading issue.');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
38
|
+
const pyprojectPath = path.join(process.cwd(), 'pyproject.toml');
|
|
39
|
+
const requirementsPath = path.join(process.cwd(), 'requirements.txt');
|
|
40
|
+
const goModPath = path.join(process.cwd(), 'go.mod');
|
|
41
|
+
const pomPath = path.join(process.cwd(), 'pom.xml');
|
|
42
|
+
const composerPath = path.join(process.cwd(), 'composer.json');
|
|
43
|
+
|
|
44
|
+
let detectedLanguage = 'generic';
|
|
45
|
+
let detectedFramework = 'generic';
|
|
46
|
+
|
|
47
|
+
if (SecurityUtils.safeExistsSync(packageJsonPath)) {
|
|
48
|
+
detectedLanguage = 'javascript';
|
|
49
|
+
try {
|
|
50
|
+
const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
|
|
51
|
+
const deps = {
|
|
52
|
+
...(packageJson.dependencies || {}),
|
|
53
|
+
...(packageJson.devDependencies || {}),
|
|
54
|
+
...(packageJson.peerDependencies || {})
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Check for i18ntk-runtime first (check both package names)
|
|
58
|
+
const hasI18nTkRuntime = deps['i18ntk-runtime'] || deps['i18ntk/runtime'];
|
|
59
|
+
|
|
60
|
+
// Check for common i18n patterns in source code if not found in package.json
|
|
61
|
+
if (!hasI18nTkRuntime) {
|
|
62
|
+
const i18nPatterns = [
|
|
63
|
+
/i18n\.t\(['\"`]/,
|
|
64
|
+
/useI18n\(/,
|
|
65
|
+
/from ['\"]i18ntk[\/\\]runtime['\"]/,
|
|
66
|
+
/require\(['\"]i18ntk[\/\\]runtime['\"]\)/
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const sourceFiles = await this.customGlob(['src/**/*.{js,jsx,ts,tsx}'], {
|
|
70
|
+
cwd: process.cwd(),
|
|
71
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**']
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
for (const file of sourceFiles) {
|
|
75
|
+
try {
|
|
76
|
+
const content = await fs.promises.readFile(path.join(process.cwd(), file), 'utf8');
|
|
77
|
+
if (i18nPatterns.some(pattern => pattern.test(content))) {
|
|
78
|
+
detectedFramework = 'i18ntk-runtime';
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {
|
|
82
|
+
// Skip files we can't read
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
detectedFramework = 'i18ntk-runtime';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Only check other frameworks if i18ntk-runtime wasn't detected
|
|
91
|
+
if (detectedFramework !== 'i18ntk-runtime') {
|
|
92
|
+
if (deps.react || deps['react-dom']) detectedFramework = 'react';
|
|
93
|
+
else if (deps.vue || deps['vue-router']) detectedFramework = 'vue';
|
|
94
|
+
else if (deps['@angular/core']) detectedFramework = 'angular';
|
|
95
|
+
else if (deps.next) detectedFramework = 'nextjs';
|
|
96
|
+
else if (deps.nuxt) detectedFramework = 'nuxt';
|
|
97
|
+
else if (deps.svelte) detectedFramework = 'svelte';
|
|
98
|
+
else detectedFramework = 'generic';
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
detectedFramework = 'generic';
|
|
102
|
+
}
|
|
103
|
+
} else if (SecurityUtils.safeExistsSync(pyprojectPath) || SecurityUtils.safeExistsSync(requirementsPath)) {
|
|
104
|
+
detectedLanguage = 'python';
|
|
105
|
+
try {
|
|
106
|
+
if (SecurityUtils.safeExistsSync(requirementsPath)) {
|
|
107
|
+
const requirements = SecurityUtils.safeReadFileSync(requirementsPath, path.dirname(requirementsPath), 'utf8');
|
|
108
|
+
if (requirements.includes('django')) detectedFramework = 'django';
|
|
109
|
+
else if (requirements.includes('flask')) detectedFramework = 'flask';
|
|
110
|
+
else if (requirements.includes('fastapi')) detectedFramework = 'fastapi';
|
|
111
|
+
else detectedFramework = 'generic';
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
detectedFramework = 'generic';
|
|
115
|
+
}
|
|
116
|
+
} else if (SecurityUtils.safeExistsSync(goModPath)) {
|
|
117
|
+
detectedLanguage = 'go';
|
|
118
|
+
detectedFramework = 'generic';
|
|
119
|
+
} else if (SecurityUtils.safeExistsSync(pomPath)) {
|
|
120
|
+
detectedLanguage = 'java';
|
|
121
|
+
try {
|
|
122
|
+
const pomContent = SecurityUtils.safeReadFileSync(pomPath, path.dirname(pomPath), 'utf8');
|
|
123
|
+
if (pomContent.includes('spring-boot')) detectedFramework = 'spring-boot';
|
|
124
|
+
else if (pomContent.includes('spring')) detectedFramework = 'spring';
|
|
125
|
+
else if (pomContent.includes('quarkus')) detectedFramework = 'quarkus';
|
|
126
|
+
else detectedFramework = 'generic';
|
|
127
|
+
} catch (error) {
|
|
128
|
+
detectedFramework = 'generic';
|
|
129
|
+
}
|
|
130
|
+
} else if (SecurityUtils.safeExistsSync(composerPath)) {
|
|
131
|
+
detectedLanguage = 'php';
|
|
132
|
+
try {
|
|
133
|
+
const composer = JSON.parse(SecurityUtils.safeReadFileSync(composerPath, path.dirname(composerPath), 'utf8'));
|
|
134
|
+
const deps = composer.require || {};
|
|
135
|
+
|
|
136
|
+
if (deps['laravel/framework']) detectedFramework = 'laravel';
|
|
137
|
+
else if (deps['symfony/framework-bundle']) detectedFramework = 'symfony';
|
|
138
|
+
else if (deps['wordpress']) detectedFramework = 'wordpress';
|
|
139
|
+
else detectedFramework = 'generic';
|
|
140
|
+
} catch (error) {
|
|
141
|
+
detectedFramework = 'generic';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { detectedLanguage, detectedFramework };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get framework suggestions for a given language
|
|
150
|
+
* @param {string} language - The programming language
|
|
151
|
+
* @returns {Array} Array of framework suggestions with name and description
|
|
152
|
+
*/
|
|
153
|
+
getFrameworkSuggestions(language) {
|
|
154
|
+
const suggestions = {
|
|
155
|
+
javascript: [
|
|
156
|
+
{ name: 'i18next', description: 'Feature-rich i18n framework for JavaScript' },
|
|
157
|
+
{ name: 'react-i18next', description: 'React integration for i18next' },
|
|
158
|
+
{ name: 'vue-i18n', description: 'Vue.js i18n plugin' },
|
|
159
|
+
{ name: 'Angular i18n', description: 'Built-in Angular i18n' }
|
|
160
|
+
],
|
|
161
|
+
typescript: [
|
|
162
|
+
{ name: 'i18next', description: 'TypeScript-first i18n framework' },
|
|
163
|
+
{ name: 'react-i18next', description: 'React + TypeScript integration' },
|
|
164
|
+
{ name: 'vue-i18n', description: 'Vue.js i18n with TypeScript support' }
|
|
165
|
+
],
|
|
166
|
+
python: [
|
|
167
|
+
{ name: 'Django i18n', description: 'Built-in Django internationalization' },
|
|
168
|
+
{ name: 'Flask-Babel', description: 'Babel integration for Flask' },
|
|
169
|
+
{ name: 'FastAPI i18n', description: 'i18n middleware for FastAPI' }
|
|
170
|
+
],
|
|
171
|
+
java: [
|
|
172
|
+
{ name: 'Spring i18n', description: 'Spring Framework internationalization' },
|
|
173
|
+
{ name: 'Spring Boot i18n', description: 'Spring Boot auto-configuration' },
|
|
174
|
+
{ name: 'Quarkus i18n', description: 'Quarkus internationalization support' }
|
|
175
|
+
],
|
|
176
|
+
go: [
|
|
177
|
+
{ name: 'go-i18n', description: 'Go i18n library with pluralization' },
|
|
178
|
+
{ name: 'nicksnyder/go-i18n', description: 'Feature-rich Go i18n' }
|
|
179
|
+
],
|
|
180
|
+
php: [
|
|
181
|
+
{ name: 'Laravel i18n', description: 'Built-in Laravel localization' },
|
|
182
|
+
{ name: 'Symfony Translation', description: 'Symfony translation component' },
|
|
183
|
+
{ name: 'WordPress i18n', description: 'WordPress localization functions' }
|
|
184
|
+
]
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return suggestions[language] || suggestions.javascript;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Handle framework detection and prompting logic
|
|
192
|
+
* @param {Object} prompt - Prompt interface for user interaction
|
|
193
|
+
* @param {Object} cfg - Configuration object
|
|
194
|
+
* @param {string} currentVersion - Current version of the tool
|
|
195
|
+
* @returns {Promise<Object>} Updated configuration
|
|
196
|
+
*/
|
|
197
|
+
async maybePromptFramework(prompt, cfg, currentVersion) {
|
|
198
|
+
// Load current settings to check framework configuration
|
|
199
|
+
let settings = this.settings || (this.configManager.loadSettings ? this.configManager.loadSettings() : (this.configManager.getConfig ? this.configManager.getConfig() : {}));
|
|
200
|
+
|
|
201
|
+
// Ensure framework configuration exists with all required fields
|
|
202
|
+
if (!settings.framework) {
|
|
203
|
+
settings.framework = {
|
|
204
|
+
detected: false,
|
|
205
|
+
preference: null,
|
|
206
|
+
prompt: 'always',
|
|
207
|
+
lastPromptedVersion: null,
|
|
208
|
+
installed: [],
|
|
209
|
+
version: '1.0' // Schema version for future compatibility
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Save the updated settings
|
|
213
|
+
if (this.configManager.saveSettings) {
|
|
214
|
+
await this.configManager.saveSettings(settings);
|
|
215
|
+
} else if (this.configManager.saveConfig) {
|
|
216
|
+
await this.configManager.saveConfig(settings);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Reload settings to ensure we have the latest framework detection results
|
|
221
|
+
const freshSettings = this.configManager.loadSettings ? this.configManager.loadSettings() : (this.configManager.getConfig ? this.configManager.getConfig() : {});
|
|
222
|
+
if (freshSettings.framework) {
|
|
223
|
+
settings.framework = { ...settings.framework, ...freshSettings.framework };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check if framework is already detected or preference is explicitly set to none
|
|
227
|
+
if (settings.framework.detected || settings.framework.preference === 'none') {
|
|
228
|
+
return cfg;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Check if DNR (Do Not Remind) is active for this version
|
|
232
|
+
if (settings.framework.prompt === 'suppress' && settings.framework.lastPromptedVersion === currentVersion) {
|
|
233
|
+
return cfg;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Reset DNR if version changed
|
|
237
|
+
if (settings.framework.prompt === 'suppress' && settings.framework.lastPromptedVersion !== currentVersion) {
|
|
238
|
+
settings.framework.prompt = 'always';
|
|
239
|
+
settings.framework.lastPromptedVersion = null;
|
|
240
|
+
|
|
241
|
+
// Save the updated settings
|
|
242
|
+
if (this.configManager.saveSettings) {
|
|
243
|
+
await this.configManager.saveSettings(settings);
|
|
244
|
+
} else if (this.configManager.saveConfig) {
|
|
245
|
+
await this.configManager.saveConfig(settings);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// This function is now handled by ensureInitializedOrExit for better flow control
|
|
250
|
+
return cfg;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Auto-detect i18n directory from common locations only if not configured in settings
|
|
255
|
+
* @returns {Object} Updated configuration with detected i18n directory
|
|
256
|
+
*/
|
|
257
|
+
detectI18nDirectory() {
|
|
258
|
+
const settings = this.settings || (this.configManager.loadSettings ? this.configManager.loadSettings() : (this.configManager.getConfig ? this.configManager.getConfig() : {}));
|
|
259
|
+
const projectRoot = path.resolve(settings.projectRoot || this.config.projectRoot || '.');
|
|
260
|
+
const fs = require('fs');
|
|
261
|
+
|
|
262
|
+
// Use per-script directory configuration if available, fallback to global sourceDir
|
|
263
|
+
const sourceDir = settings.scriptDirectories?.manage || settings.sourceDir;
|
|
264
|
+
|
|
265
|
+
if (sourceDir) {
|
|
266
|
+
this.config.sourceDir = path.resolve(projectRoot, sourceDir);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Define possible i18n paths for auto-detection
|
|
271
|
+
const possibleI18nPaths = [
|
|
272
|
+
'./locales',
|
|
273
|
+
'./src/locales',
|
|
274
|
+
'./src/i18n',
|
|
275
|
+
'./src/i18n/locales',
|
|
276
|
+
'./app/locales',
|
|
277
|
+
'./app/i18n',
|
|
278
|
+
'./public/locales',
|
|
279
|
+
'./assets/locales',
|
|
280
|
+
'./translations',
|
|
281
|
+
'./lang'
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
// Only auto-detect if no settings are configured
|
|
285
|
+
for (const possiblePath of possibleI18nPaths) {
|
|
286
|
+
const resolvedPath = path.resolve(projectRoot, possiblePath);
|
|
287
|
+
if (SecurityUtils.safeExistsSync(resolvedPath)) {
|
|
288
|
+
// Check if it contains language directories
|
|
289
|
+
try {
|
|
290
|
+
const items = fs.readdirSync(resolvedPath);
|
|
291
|
+
const hasLanguageDirs = items.some(item => {
|
|
292
|
+
const itemPath = path.join(resolvedPath, item);
|
|
293
|
+
return fs.statSync(itemPath).isDirectory() &&
|
|
294
|
+
['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'].includes(item);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
if (hasLanguageDirs) {
|
|
298
|
+
this.config.sourceDir = possiblePath;
|
|
299
|
+
// Note: Translation function would need to be injected
|
|
300
|
+
// t('init.autoDetectedI18nDirectory', { path: possiblePath });
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
// Continue checking other paths
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Check if i18n framework is installed - configuration-based check without prompts
|
|
312
|
+
* @param {Object} ui - UI instance for translations (optional)
|
|
313
|
+
* @returns {Promise<boolean>} True if frameworks detected, false otherwise
|
|
314
|
+
*/
|
|
315
|
+
async checkI18nDependencies(ui = null) {
|
|
316
|
+
const packageJsonPath = path.resolve('./package.json');
|
|
317
|
+
|
|
318
|
+
if (!SecurityUtils.safeExistsSync(packageJsonPath)) {
|
|
319
|
+
if (ui && ui.t) {
|
|
320
|
+
console.log(ui.t('errors.noPackageJson'));
|
|
321
|
+
} else {
|
|
322
|
+
console.log('No package.json found');
|
|
323
|
+
}
|
|
324
|
+
return false; // Treat as no framework detected
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
|
|
329
|
+
// Include peerDependencies in the check
|
|
330
|
+
const dependencies = {
|
|
331
|
+
...packageJson.dependencies,
|
|
332
|
+
...packageJson.devDependencies,
|
|
333
|
+
...packageJson.peerDependencies
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const i18nFrameworks = [
|
|
337
|
+
'react-i18next',
|
|
338
|
+
'vue-i18n',
|
|
339
|
+
'angular-i18n',
|
|
340
|
+
'i18next',
|
|
341
|
+
'next-i18next',
|
|
342
|
+
'svelte-i18n',
|
|
343
|
+
'@nuxtjs/i18n',
|
|
344
|
+
'i18ntk-runtime'
|
|
345
|
+
];
|
|
346
|
+
|
|
347
|
+
const installedFrameworks = i18nFrameworks.filter(framework => dependencies[framework]);
|
|
348
|
+
|
|
349
|
+
if (installedFrameworks.length > 0) {
|
|
350
|
+
if (ui && ui.t) {
|
|
351
|
+
console.log(ui.t('init.detectedFrameworks', { frameworks: installedFrameworks.join(', ') }));
|
|
352
|
+
} else {
|
|
353
|
+
console.log(`Detected frameworks: ${installedFrameworks.join(', ')}`);
|
|
354
|
+
}
|
|
355
|
+
const cfg = this.settings || (this.configManager.loadSettings ? this.configManager.loadSettings() : (this.configManager.getConfig ? this.configManager.getConfig() : {}));
|
|
356
|
+
cfg.framework = cfg.framework || {};
|
|
357
|
+
cfg.framework.detected = true;
|
|
358
|
+
cfg.framework.installed = installedFrameworks;
|
|
359
|
+
if (this.configManager.saveSettings) {
|
|
360
|
+
this.configManager.saveSettings(cfg);
|
|
361
|
+
} else if (this.configManager.saveConfig) {
|
|
362
|
+
this.configManager.saveConfig(cfg);
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
} else {
|
|
366
|
+
const cfg = this.settings || (this.configManager.loadSettings ? this.configManager.loadSettings() : (this.configManager.getConfig ? this.configManager.getConfig() : {}));
|
|
367
|
+
if (cfg.framework) {
|
|
368
|
+
cfg.framework.detected = false;
|
|
369
|
+
if (this.configManager.saveSettings) {
|
|
370
|
+
this.configManager.saveSettings(cfg);
|
|
371
|
+
} else if (this.configManager.saveConfig) {
|
|
372
|
+
this.configManager.saveConfig(cfg);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Always signal that frameworks were not detected
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
} catch (error) {
|
|
379
|
+
// Note: Translation function would need to be injected
|
|
380
|
+
// console.log(t('init.errors.packageJsonRead'));
|
|
381
|
+
console.log('Error reading package.json');
|
|
382
|
+
return false; // Treat as no framework detected on error
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Custom glob implementation using Node.js built-in modules (zero dependencies)
|
|
388
|
+
* @param {string[]} patterns - Array of glob patterns
|
|
389
|
+
* @param {Object} options - Options object with cwd and ignore properties
|
|
390
|
+
* @returns {Promise<string[]>} Array of matching file paths
|
|
391
|
+
*/
|
|
392
|
+
async customGlob(patterns, options = {}) {
|
|
393
|
+
const cwd = options.cwd || process.cwd();
|
|
394
|
+
const ignorePatterns = options.ignore || [];
|
|
395
|
+
|
|
396
|
+
function matchesPattern(filename, pattern) {
|
|
397
|
+
// Simple pattern matching for **/*.{js,jsx,ts,tsx} style patterns
|
|
398
|
+
if (pattern.includes('**/*')) {
|
|
399
|
+
const extensionPart = pattern.split('*.')[1];
|
|
400
|
+
if (extensionPart) {
|
|
401
|
+
const extensions = extensionPart.replace('{', '').replace('}', '').split(',');
|
|
402
|
+
return extensions.some(ext => filename.endsWith('.' + ext.trim()));
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return filename.includes(pattern.replace('**/', ''));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function shouldIgnore(filePath) {
|
|
409
|
+
return ignorePatterns.some(pattern => {
|
|
410
|
+
if (pattern.includes('**/')) {
|
|
411
|
+
const patternEnd = pattern.replace('**/', '');
|
|
412
|
+
return filePath.includes('/' + patternEnd) || filePath.includes('\\' + patternEnd);
|
|
413
|
+
}
|
|
414
|
+
return filePath.includes(pattern);
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function findFiles(dir, results = []) {
|
|
419
|
+
try {
|
|
420
|
+
const items = fs.readdirSync(dir);
|
|
421
|
+
|
|
422
|
+
for (const item of items) {
|
|
423
|
+
const fullPath = path.join(dir, item);
|
|
424
|
+
const relativePath = path.relative(cwd, fullPath);
|
|
425
|
+
|
|
426
|
+
if (shouldIgnore(relativePath)) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
try {
|
|
431
|
+
const stat = fs.statSync(fullPath);
|
|
432
|
+
|
|
433
|
+
if (stat.isDirectory()) {
|
|
434
|
+
findFiles(fullPath, results);
|
|
435
|
+
} else if (stat.isFile()) {
|
|
436
|
+
// Check if file matches any of our patterns
|
|
437
|
+
for (const pattern of patterns) {
|
|
438
|
+
if (matchesPattern(item, pattern)) {
|
|
439
|
+
results.push(relativePath);
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
} catch (error) {
|
|
445
|
+
// Skip files we can't access
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
} catch (error) {
|
|
450
|
+
// Skip directories we can't access
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return results;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return findFiles(cwd);
|
|
457
|
+
}
|
|
458
|
+
};
|