i18ntk 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -32
- package/main/i18ntk-analyze.js +59 -54
- package/main/i18ntk-ui.js +96 -49
- package/main/manage/commands/AnalyzeCommand.js +59 -54
- package/main/manage/index.js +140 -71
- package/package.json +290 -290
- package/scripts/fix-all-i18n.js +41 -20
- package/scripts/fix-and-purify-i18n.js +43 -23
- package/scripts/prepublish-dev.js +221 -0
- package/scripts/prepublish.js +155 -141
- package/scripts/validate-all-translations.js +190 -134
- package/ui-locales/de.json +149 -155
- package/ui-locales/en.json +1 -7
- package/ui-locales/es.json +159 -173
- package/ui-locales/fr.json +143 -150
- package/ui-locales/ja.json +181 -233
- package/ui-locales/ru.json +133 -185
- package/ui-locales/zh.json +168 -175
- package/utils/cli-helper.js +26 -98
- package/utils/extractors/regex.js +39 -12
- package/utils/i18n-helper.js +181 -128
- package/utils/security-check-improved.js +16 -13
- package/utils/security-fixed.js +6 -4
- package/utils/security.js +6 -4
- package/main/manage/services/ConfigurationService-fixed.js +0 -449
package/scripts/prepublish.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Prepublish Script
|
|
5
5
|
* Cleans up development artifacts before npm publish
|
|
6
6
|
* Ensures fresh config and settings for public package
|
|
7
|
+
* version 2.0.5
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
const fs = require('fs');
|
|
10
11
|
const path = require('path');
|
|
12
|
+
const SecurityUtils = require('../utils/security');
|
|
11
13
|
|
|
12
14
|
class PrepublishCleaner {
|
|
13
15
|
constructor() {
|
|
@@ -95,83 +97,102 @@ class PrepublishCleaner {
|
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
async cleanDirectory(dirPath) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
100
|
+
const validatedPath = SecurityUtils.validatePath(dirPath, this.projectRoot);
|
|
101
|
+
if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const files = SecurityUtils.safeReaddirSync(validatedPath, this.projectRoot);
|
|
107
|
+
let deletedCount = 0;
|
|
108
|
+
|
|
109
|
+
for (const file of files) {
|
|
110
|
+
const filePath = path.join(validatedPath, file);
|
|
111
|
+
const validatedFilePath = SecurityUtils.validatePath(filePath, this.projectRoot);
|
|
112
|
+
if (!validatedFilePath) continue;
|
|
113
|
+
|
|
114
|
+
const stat = SecurityUtils.safeStatSync(validatedFilePath, this.projectRoot);
|
|
115
|
+
if (!stat) continue;
|
|
116
|
+
|
|
117
|
+
if (stat.isFile()) {
|
|
118
|
+
if (SecurityUtils.safeExistsSync(validatedFilePath)) {
|
|
119
|
+
fs.unlinkSync(validatedFilePath);
|
|
120
|
+
deletedCount++;
|
|
121
|
+
}
|
|
122
|
+
} else if (stat.isDirectory()) {
|
|
123
|
+
// Recursively clean subdirectories
|
|
124
|
+
await this.cleanDirectory(validatedFilePath);
|
|
125
|
+
// Remove empty directories
|
|
126
|
+
try {
|
|
127
|
+
fs.rmdirSync(validatedFilePath);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
// Directory not empty, skip
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (deletedCount > 0) {
|
|
135
|
+
this.log(`Cleaned ${deletedCount} files from ${path.relative(this.projectRoot, validatedPath)}`);
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
this.log(`Warning: Could not clean ${dirPath}: ${error.message}`);
|
|
139
|
+
}
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
async cleanFile(pattern) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
143
|
+
const searchPath = path.join(this.projectRoot, pattern);
|
|
144
|
+
const validatedSearchPath = SecurityUtils.validatePath(searchPath, this.projectRoot);
|
|
145
|
+
if (!validatedSearchPath) return;
|
|
146
|
+
|
|
147
|
+
if (pattern.includes('*')) {
|
|
148
|
+
// Handle glob patterns
|
|
149
|
+
const dir = path.dirname(validatedSearchPath);
|
|
150
|
+
const filenamePattern = path.basename(validatedSearchPath);
|
|
151
|
+
const validatedDir = SecurityUtils.validatePath(dir, this.projectRoot);
|
|
152
|
+
if (!validatedDir) return;
|
|
153
|
+
|
|
154
|
+
if (SecurityUtils.safeExistsSync(validatedDir)) {
|
|
155
|
+
const files = SecurityUtils.safeReaddirSync(validatedDir, this.projectRoot);
|
|
156
|
+
const regex = new RegExp(filenamePattern.replace('*', '.*'));
|
|
157
|
+
|
|
158
|
+
for (const file of files) {
|
|
159
|
+
if (regex.test(file)) {
|
|
160
|
+
const filePath = path.join(validatedDir, file);
|
|
161
|
+
const validatedFilePath = SecurityUtils.validatePath(filePath, this.projectRoot);
|
|
162
|
+
if (validatedFilePath && SecurityUtils.safeExistsSync(validatedFilePath)) {
|
|
163
|
+
fs.unlinkSync(validatedFilePath);
|
|
164
|
+
this.log(`Deleted ${path.relative(this.projectRoot, validatedFilePath)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
// Handle exact files
|
|
171
|
+
const validatedFilePath = SecurityUtils.validatePath(searchPath, this.projectRoot);
|
|
172
|
+
if (validatedFilePath && SecurityUtils.safeExistsSync(validatedFilePath)) {
|
|
173
|
+
fs.unlinkSync(validatedFilePath);
|
|
174
|
+
this.log(`Deleted ${path.relative(this.projectRoot, validatedFilePath)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
160
177
|
}
|
|
161
178
|
|
|
162
179
|
async validateEssentialFiles() {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
180
|
+
this.log('Validating essential files...');
|
|
181
|
+
|
|
182
|
+
let missingFiles = [];
|
|
183
|
+
for (const file of this.essentialFiles) {
|
|
184
|
+
const filePath = path.join(this.projectRoot, file);
|
|
185
|
+
const validatedPath = SecurityUtils.validatePath(filePath, this.projectRoot);
|
|
186
|
+
if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
|
|
187
|
+
missingFiles.push(file);
|
|
188
|
+
} else {
|
|
189
|
+
const stat = SecurityUtils.safeStatSync(validatedPath, this.projectRoot);
|
|
190
|
+
if (!stat || !stat.isFile()) {
|
|
191
|
+
this.log(`❌ ${file} is not a file`);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
175
196
|
|
|
176
197
|
if (missingFiles.length > 0) {
|
|
177
198
|
this.log(`❌ Missing essential files: ${missingFiles.join(', ')}`);
|
|
@@ -182,19 +203,19 @@ class PrepublishCleaner {
|
|
|
182
203
|
}
|
|
183
204
|
|
|
184
205
|
async validateLocaleFiles() {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
this.log('Validating locale files...');
|
|
207
|
+
|
|
208
|
+
let invalidFiles = [];
|
|
209
|
+
for (const localeFile of this.essentialLocales) {
|
|
210
|
+
const filePath = path.join(this.projectRoot, localeFile);
|
|
211
|
+
const validatedPath = SecurityUtils.validatePath(filePath, this.projectRoot);
|
|
212
|
+
if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
|
|
213
|
+
invalidFiles.push(localeFile);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const content = SecurityUtils.safeReadFileSync(validatedPath, this.projectRoot, 'utf8');
|
|
198
219
|
|
|
199
220
|
// Validate structure
|
|
200
221
|
if (typeof parsed !== 'object' || parsed === null) {
|
|
@@ -220,11 +241,16 @@ class PrepublishCleaner {
|
|
|
220
241
|
}
|
|
221
242
|
|
|
222
243
|
async validatePackageJson() {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
244
|
+
this.log('Validating package.json...');
|
|
245
|
+
|
|
246
|
+
const packagePath = path.join(this.projectRoot, 'package.json');
|
|
247
|
+
const validatedPath = SecurityUtils.validatePath(packagePath, this.projectRoot);
|
|
248
|
+
if (!validatedPath) {
|
|
249
|
+
this.log('❌ package.json not found or invalid path');
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
const pkg = JSON.parse(SecurityUtils.safeReadFileSync(validatedPath, this.projectRoot, 'utf8'));
|
|
228
254
|
|
|
229
255
|
// Validate required fields
|
|
230
256
|
const requiredFields = ['name', 'version', 'description', 'main', 'bin', 'files'];
|
|
@@ -269,70 +295,58 @@ class PrepublishCleaner {
|
|
|
269
295
|
}
|
|
270
296
|
|
|
271
297
|
async finalValidation() {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
298
|
+
this.log('Running final validation checks...');
|
|
299
|
+
|
|
300
|
+
// Check for development artifacts
|
|
301
|
+
const devArtifacts = [
|
|
302
|
+
'dev/debug',
|
|
303
|
+
'benchmarks',
|
|
304
|
+
'.github',
|
|
305
|
+
'test-usage-fix.html',
|
|
306
|
+
'.i18ntk'
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
for (const artifact of devArtifacts) {
|
|
310
|
+
const artifactPath = path.join(this.projectRoot, artifact);
|
|
311
|
+
const validatedPath = SecurityUtils.validatePath(artifactPath, this.projectRoot);
|
|
312
|
+
if (validatedPath && SecurityUtils.safeExistsSync(validatedPath)) {
|
|
313
|
+
this.log(`⚠️ Development artifact found: ${artifact}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
289
316
|
|
|
290
317
|
// Validate file permissions for executable scripts
|
|
291
318
|
const scripts = [
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
319
|
+
'main/manage/index.js',
|
|
320
|
+
'main/i18ntk-init.js',
|
|
321
|
+
'main/i18ntk-analyze.js',
|
|
322
|
+
'main/i18ntk-validate.js',
|
|
323
|
+
'main/i18ntk-usage.js',
|
|
324
|
+
'main/i18ntk-summary.js',
|
|
325
|
+
'main/i18ntk-sizing.js',
|
|
326
|
+
'main/i18ntk-complete.js',
|
|
327
|
+
'main/i18ntk-ui.js',
|
|
328
|
+
'main/i18ntk-autorun.js'
|
|
302
329
|
];
|
|
303
330
|
|
|
304
331
|
for (const script of scripts) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
332
|
+
const scriptPath = path.join(this.projectRoot, script);
|
|
333
|
+
const validatedPath = SecurityUtils.validatePath(scriptPath, this.projectRoot);
|
|
334
|
+
if (validatedPath && SecurityUtils.safeExistsSync(validatedPath)) {
|
|
335
|
+
try {
|
|
336
|
+
fs.accessSync(validatedPath, fs.constants.X_OK);
|
|
337
|
+
} catch (e) {
|
|
338
|
+
this.log(`⚠️ Script not executable: ${script}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
313
341
|
}
|
|
314
342
|
|
|
315
343
|
this.log('✅ Final validation complete');
|
|
316
344
|
}
|
|
317
345
|
|
|
318
346
|
async resetSecuritySettings() {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const defaultConfig = {
|
|
323
|
-
enabled: false,
|
|
324
|
-
pinHash: null,
|
|
325
|
-
sessionTimeout: 30,
|
|
326
|
-
maxFailedAttempts: 3,
|
|
327
|
-
lockoutDuration: 15,
|
|
328
|
-
lastActivity: null,
|
|
329
|
-
failedAttempts: 0,
|
|
330
|
-
lockedUntil: null
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
SecurityUtils.safeWriteFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
334
|
-
this.log('Reset security settings to defaults');
|
|
335
|
-
}
|
|
347
|
+
// Remove security settings reset to prevent disabling security during publish
|
|
348
|
+
// This script should not modify security configurations
|
|
349
|
+
this.log('Skipping security settings reset to maintain security posture');
|
|
336
350
|
}
|
|
337
351
|
}
|
|
338
352
|
|