@windrun-huaiin/dev-scripts 6.8.2 → 6.9.1
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/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +137 -1410
- package/dist/cli.mjs +145 -432
- package/dist/commands/check-translations.d.ts +3 -0
- package/dist/commands/check-translations.d.ts.map +1 -0
- package/dist/commands/check-translations.js +132 -0
- package/dist/commands/check-translations.mjs +130 -0
- package/dist/commands/clean-translations.d.ts +3 -0
- package/dist/commands/clean-translations.d.ts.map +1 -0
- package/dist/commands/clean-translations.js +148 -0
- package/dist/commands/clean-translations.mjs +146 -0
- package/dist/commands/create-diaomao-app.d.ts +2 -0
- package/dist/commands/create-diaomao-app.d.ts.map +1 -0
- package/dist/commands/create-diaomao-app.js +151 -0
- package/dist/commands/create-diaomao-app.mjs +149 -0
- package/dist/commands/deep-clean.d.ts +3 -0
- package/dist/commands/deep-clean.d.ts.map +1 -0
- package/dist/commands/deep-clean.js +119 -0
- package/dist/commands/deep-clean.mjs +117 -0
- package/dist/commands/easy-changeset.d.ts +2 -0
- package/dist/commands/easy-changeset.d.ts.map +1 -0
- package/dist/commands/easy-changeset.js +39 -0
- package/dist/commands/easy-changeset.mjs +37 -0
- package/dist/commands/generate-blog-index.d.ts +3 -0
- package/dist/commands/generate-blog-index.d.ts.map +1 -0
- package/dist/commands/generate-blog-index.js +302 -0
- package/dist/commands/generate-blog-index.mjs +300 -0
- package/dist/commands/generate-nextjs-architecture.d.ts +3 -0
- package/dist/commands/generate-nextjs-architecture.d.ts.map +1 -0
- package/dist/commands/generate-nextjs-architecture.js +84 -0
- package/dist/commands/generate-nextjs-architecture.mjs +82 -0
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +173 -0
- package/dist/config/index.mjs +170 -0
- package/dist/config/schema.d.ts +34 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +80 -0
- package/dist/config/schema.mjs +78 -0
- package/dist/index.d.ts +6 -49
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -996
- package/dist/index.mjs +4 -3
- package/dist/utils/file-scanner.d.ts +22 -0
- package/dist/utils/file-scanner.d.ts.map +1 -0
- package/dist/utils/file-scanner.js +70 -0
- package/dist/utils/file-scanner.mjs +65 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +63 -0
- package/dist/utils/logger.mjs +61 -0
- package/dist/utils/translation-parser.d.ts +29 -0
- package/dist/utils/translation-parser.d.ts.map +1 -0
- package/dist/utils/translation-parser.js +225 -0
- package/dist/utils/translation-parser.mjs +218 -0
- package/package.json +5 -5
- package/dist/chunk-GVR6HFHM.mjs +0 -989
- package/dist/chunk-GVR6HFHM.mjs.map +0 -1
- package/dist/cli.d.mts +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cli.mjs.map +0 -1
- package/dist/index.d.mts +0 -49
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var logger = require('../utils/logger.js');
|
|
4
|
+
var fileScanner = require('../utils/file-scanner.js');
|
|
5
|
+
var translationParser = require('../utils/translation-parser.js');
|
|
6
|
+
|
|
7
|
+
async function checkTranslations(config, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
|
|
8
|
+
const logger$1 = new logger.Logger(config);
|
|
9
|
+
logger$1.warn('==============================');
|
|
10
|
+
logger$1.warn(`‼️ Current working directory: ⭕ ${cwd} ⭕`);
|
|
11
|
+
logger$1.warn('==============================');
|
|
12
|
+
try {
|
|
13
|
+
logger$1.log('start checking translations...');
|
|
14
|
+
// scan all files
|
|
15
|
+
const scanResults = await fileScanner.scanFiles(config, cwd);
|
|
16
|
+
logger$1.log(`found ${scanResults.length} files to scan`);
|
|
17
|
+
// load translation files
|
|
18
|
+
const translations = fileScanner.loadTranslations(config, cwd);
|
|
19
|
+
// collect used translation keys and namespaces
|
|
20
|
+
const foundTranslationKeys = new Set();
|
|
21
|
+
const foundNamespaces = new Set();
|
|
22
|
+
// scan all files, extract translation information
|
|
23
|
+
for (const { filePath, content } of scanResults) {
|
|
24
|
+
try {
|
|
25
|
+
const { namespaces, keys } = translationParser.extractTranslationsInfo(content, filePath);
|
|
26
|
+
if (keys.length > 0 || namespaces.size > 0) {
|
|
27
|
+
logger$1.log(`found the following information in the file ${filePath}:`);
|
|
28
|
+
if (namespaces.size > 0) {
|
|
29
|
+
logger$1.log(` translation function mapping:`);
|
|
30
|
+
namespaces.forEach((namespace, varName) => {
|
|
31
|
+
logger$1.log(` - ${varName} => ${namespace}`);
|
|
32
|
+
foundNamespaces.add(namespace);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (keys.length > 0) {
|
|
36
|
+
logger$1.log(` translation keys:`);
|
|
37
|
+
keys.forEach(key => {
|
|
38
|
+
logger$1.log(` - ${key}`);
|
|
39
|
+
foundTranslationKeys.add(key);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof Error) {
|
|
46
|
+
logger$1.error(`error processing file ${filePath}: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
logger$1.error(`error processing file ${filePath}: unknown error`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
logger$1.log(`\nfound ${foundNamespaces.size} used namespaces in the code: ${Array.from(foundNamespaces).join(', ')}`);
|
|
54
|
+
// check results
|
|
55
|
+
const report = {};
|
|
56
|
+
// check if the namespace exists
|
|
57
|
+
foundNamespaces.forEach(namespace => {
|
|
58
|
+
config.i18n.locales.forEach(locale => {
|
|
59
|
+
const missingNamespaceKey = `missingNamespacesIn${locale.toUpperCase()}`;
|
|
60
|
+
if (!translationParser.checkNamespaceExists(namespace, translations[locale])) {
|
|
61
|
+
report[missingNamespaceKey] = report[missingNamespaceKey] || [];
|
|
62
|
+
report[missingNamespaceKey].push(namespace);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
// check if the translation key exists
|
|
67
|
+
foundTranslationKeys.forEach(key => {
|
|
68
|
+
config.i18n.locales.forEach(locale => {
|
|
69
|
+
const missingKey = `missingIn${locale.toUpperCase()}`;
|
|
70
|
+
if (!translationParser.checkKeyExists(key, translations[locale])) {
|
|
71
|
+
report[missingKey] = report[missingKey] || [];
|
|
72
|
+
report[missingKey].push(key);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
// check if the translation keys are consistent
|
|
77
|
+
config.i18n.locales.forEach(locale => {
|
|
78
|
+
const allKeys = translationParser.getAllKeys(translations[locale]);
|
|
79
|
+
config.i18n.locales.forEach(otherLocale => {
|
|
80
|
+
if (locale !== otherLocale) {
|
|
81
|
+
const otherKeys = translationParser.getAllKeys(translations[otherLocale]);
|
|
82
|
+
const onlyKeys = `${locale}OnlyKeys`;
|
|
83
|
+
report[onlyKeys] = allKeys.filter(key => !otherKeys.includes(key));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
// generate report
|
|
88
|
+
logger$1.log('\n=== translation check report ===\n');
|
|
89
|
+
// first report missing namespaces, which is usually the most serious problem
|
|
90
|
+
config.i18n.locales.forEach(locale => {
|
|
91
|
+
const missingNamespaceKey = `missingNamespacesIn${locale.toUpperCase()}`;
|
|
92
|
+
if (report[missingNamespaceKey]?.length > 0) {
|
|
93
|
+
logger$1.log(`🚨 missing namespaces in the ${locale} translation file:`);
|
|
94
|
+
report[missingNamespaceKey].forEach(namespace => logger$1.log(` - ${namespace}`));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
logger$1.success(`${locale} translation file has all used namespaces`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// then report missing translation keys
|
|
101
|
+
config.i18n.locales.forEach(locale => {
|
|
102
|
+
const missingKey = `missingIn${locale.toUpperCase()}`;
|
|
103
|
+
if (report[missingKey]?.length > 0) {
|
|
104
|
+
logger$1.log(`\n🔴 missing keys in the ${locale} translation file:`);
|
|
105
|
+
report[missingKey].forEach(key => logger$1.log(` - ${key}`));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
logger$1.success(`${locale} translation file has all used keys`);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// finally report inconsistent keys
|
|
112
|
+
config.i18n.locales.forEach(locale => {
|
|
113
|
+
const onlyKeys = `${locale}OnlyKeys`;
|
|
114
|
+
if (report[onlyKeys]?.length > 0) {
|
|
115
|
+
logger$1.log(`\n⚠️ keys only exist in the ${locale} translation file:`);
|
|
116
|
+
report[onlyKeys].forEach(key => logger$1.log(` - ${key}`));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
logger$1.log('\n=== report end ===\n');
|
|
120
|
+
logger$1.log("⚠️⚠️⚠️script depends on regular matching, for multiple translation namespaces in a single file, use naming to distinguish: t1 | t2 | t3 | ... ⚠️⚠️⚠️");
|
|
121
|
+
// save log file
|
|
122
|
+
logger$1.saveToFile('check.log', cwd);
|
|
123
|
+
// if there are any problems, return non-zero status code
|
|
124
|
+
return Object.values(report).some(keys => keys.length > 0) ? 1 : 0;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
logger$1.error(`error checking translations: ${error}`);
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
exports.checkTranslations = checkTranslations;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Logger } from '../utils/logger.mjs';
|
|
2
|
+
import { scanFiles, loadTranslations } from '../utils/file-scanner.mjs';
|
|
3
|
+
import { extractTranslationsInfo, checkNamespaceExists, checkKeyExists, getAllKeys } from '../utils/translation-parser.mjs';
|
|
4
|
+
|
|
5
|
+
async function checkTranslations(config, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
|
|
6
|
+
const logger = new Logger(config);
|
|
7
|
+
logger.warn('==============================');
|
|
8
|
+
logger.warn(`‼️ Current working directory: ⭕ ${cwd} ⭕`);
|
|
9
|
+
logger.warn('==============================');
|
|
10
|
+
try {
|
|
11
|
+
logger.log('start checking translations...');
|
|
12
|
+
// scan all files
|
|
13
|
+
const scanResults = await scanFiles(config, cwd);
|
|
14
|
+
logger.log(`found ${scanResults.length} files to scan`);
|
|
15
|
+
// load translation files
|
|
16
|
+
const translations = loadTranslations(config, cwd);
|
|
17
|
+
// collect used translation keys and namespaces
|
|
18
|
+
const foundTranslationKeys = new Set();
|
|
19
|
+
const foundNamespaces = new Set();
|
|
20
|
+
// scan all files, extract translation information
|
|
21
|
+
for (const { filePath, content } of scanResults) {
|
|
22
|
+
try {
|
|
23
|
+
const { namespaces, keys } = extractTranslationsInfo(content, filePath);
|
|
24
|
+
if (keys.length > 0 || namespaces.size > 0) {
|
|
25
|
+
logger.log(`found the following information in the file ${filePath}:`);
|
|
26
|
+
if (namespaces.size > 0) {
|
|
27
|
+
logger.log(` translation function mapping:`);
|
|
28
|
+
namespaces.forEach((namespace, varName) => {
|
|
29
|
+
logger.log(` - ${varName} => ${namespace}`);
|
|
30
|
+
foundNamespaces.add(namespace);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (keys.length > 0) {
|
|
34
|
+
logger.log(` translation keys:`);
|
|
35
|
+
keys.forEach(key => {
|
|
36
|
+
logger.log(` - ${key}`);
|
|
37
|
+
foundTranslationKeys.add(key);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error instanceof Error) {
|
|
44
|
+
logger.error(`error processing file ${filePath}: ${error.message}`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
logger.error(`error processing file ${filePath}: unknown error`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
logger.log(`\nfound ${foundNamespaces.size} used namespaces in the code: ${Array.from(foundNamespaces).join(', ')}`);
|
|
52
|
+
// check results
|
|
53
|
+
const report = {};
|
|
54
|
+
// check if the namespace exists
|
|
55
|
+
foundNamespaces.forEach(namespace => {
|
|
56
|
+
config.i18n.locales.forEach(locale => {
|
|
57
|
+
const missingNamespaceKey = `missingNamespacesIn${locale.toUpperCase()}`;
|
|
58
|
+
if (!checkNamespaceExists(namespace, translations[locale])) {
|
|
59
|
+
report[missingNamespaceKey] = report[missingNamespaceKey] || [];
|
|
60
|
+
report[missingNamespaceKey].push(namespace);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
// check if the translation key exists
|
|
65
|
+
foundTranslationKeys.forEach(key => {
|
|
66
|
+
config.i18n.locales.forEach(locale => {
|
|
67
|
+
const missingKey = `missingIn${locale.toUpperCase()}`;
|
|
68
|
+
if (!checkKeyExists(key, translations[locale])) {
|
|
69
|
+
report[missingKey] = report[missingKey] || [];
|
|
70
|
+
report[missingKey].push(key);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
// check if the translation keys are consistent
|
|
75
|
+
config.i18n.locales.forEach(locale => {
|
|
76
|
+
const allKeys = getAllKeys(translations[locale]);
|
|
77
|
+
config.i18n.locales.forEach(otherLocale => {
|
|
78
|
+
if (locale !== otherLocale) {
|
|
79
|
+
const otherKeys = getAllKeys(translations[otherLocale]);
|
|
80
|
+
const onlyKeys = `${locale}OnlyKeys`;
|
|
81
|
+
report[onlyKeys] = allKeys.filter(key => !otherKeys.includes(key));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
// generate report
|
|
86
|
+
logger.log('\n=== translation check report ===\n');
|
|
87
|
+
// first report missing namespaces, which is usually the most serious problem
|
|
88
|
+
config.i18n.locales.forEach(locale => {
|
|
89
|
+
const missingNamespaceKey = `missingNamespacesIn${locale.toUpperCase()}`;
|
|
90
|
+
if (report[missingNamespaceKey]?.length > 0) {
|
|
91
|
+
logger.log(`🚨 missing namespaces in the ${locale} translation file:`);
|
|
92
|
+
report[missingNamespaceKey].forEach(namespace => logger.log(` - ${namespace}`));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
logger.success(`${locale} translation file has all used namespaces`);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
// then report missing translation keys
|
|
99
|
+
config.i18n.locales.forEach(locale => {
|
|
100
|
+
const missingKey = `missingIn${locale.toUpperCase()}`;
|
|
101
|
+
if (report[missingKey]?.length > 0) {
|
|
102
|
+
logger.log(`\n🔴 missing keys in the ${locale} translation file:`);
|
|
103
|
+
report[missingKey].forEach(key => logger.log(` - ${key}`));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
logger.success(`${locale} translation file has all used keys`);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
// finally report inconsistent keys
|
|
110
|
+
config.i18n.locales.forEach(locale => {
|
|
111
|
+
const onlyKeys = `${locale}OnlyKeys`;
|
|
112
|
+
if (report[onlyKeys]?.length > 0) {
|
|
113
|
+
logger.log(`\n⚠️ keys only exist in the ${locale} translation file:`);
|
|
114
|
+
report[onlyKeys].forEach(key => logger.log(` - ${key}`));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
logger.log('\n=== report end ===\n');
|
|
118
|
+
logger.log("⚠️⚠️⚠️script depends on regular matching, for multiple translation namespaces in a single file, use naming to distinguish: t1 | t2 | t3 | ... ⚠️⚠️⚠️");
|
|
119
|
+
// save log file
|
|
120
|
+
logger.saveToFile('check.log', cwd);
|
|
121
|
+
// if there are any problems, return non-zero status code
|
|
122
|
+
return Object.values(report).some(keys => keys.length > 0) ? 1 : 0;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
logger.error(`error checking translations: ${error}`);
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { checkTranslations };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clean-translations.d.ts","sourceRoot":"","sources":["../../src/commands/clean-translations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAe7D,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,gBAAgB,EACxB,YAAY,GAAE,OAAe,EAC7B,GAAG,GAAE,MAA6D,GACjE,OAAO,CAAC,MAAM,CAAC,CAoKjB"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fileScanner = require('../utils/file-scanner.js');
|
|
4
|
+
var logger = require('../utils/logger.js');
|
|
5
|
+
var translationParser = require('../utils/translation-parser.js');
|
|
6
|
+
var fs = require('fs');
|
|
7
|
+
|
|
8
|
+
async function cleanTranslations(config, shouldRemove = false, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
|
|
9
|
+
const logger$1 = new logger.Logger(config);
|
|
10
|
+
const logFileName = shouldRemove ? 'remove.log' : 'clean.log';
|
|
11
|
+
logger$1.warn('==============================');
|
|
12
|
+
logger$1.warn(`‼️ Current working directory: ⭕ ${cwd} ⭕`);
|
|
13
|
+
logger$1.warn('==============================');
|
|
14
|
+
try {
|
|
15
|
+
logger$1.log('start checking unused translation keys...');
|
|
16
|
+
// scan all files
|
|
17
|
+
const scanResults = await fileScanner.scanFiles(config, cwd);
|
|
18
|
+
logger$1.log(`找到 ${scanResults.length} 个文件需要扫描`);
|
|
19
|
+
// load translation files
|
|
20
|
+
const translations = fileScanner.loadTranslations(config, cwd);
|
|
21
|
+
// collect used translation keys and namespaces
|
|
22
|
+
const foundTranslationKeys = new Set();
|
|
23
|
+
const foundNamespaces = new Set();
|
|
24
|
+
// scan all files, collect used translation keys and namespaces
|
|
25
|
+
for (const { filePath, content } of scanResults) {
|
|
26
|
+
try {
|
|
27
|
+
const { namespaces, keys } = translationParser.extractTranslationsInfo(content, filePath);
|
|
28
|
+
if (keys.length > 0 || namespaces.size > 0) {
|
|
29
|
+
logger$1.log(`found the following information in the file ${filePath}:`);
|
|
30
|
+
if (namespaces.size > 0) {
|
|
31
|
+
logger$1.log(` translation function mapping:`);
|
|
32
|
+
namespaces.forEach((namespace, varName) => {
|
|
33
|
+
logger$1.log(` - ${varName} => ${namespace}`);
|
|
34
|
+
foundNamespaces.add(namespace);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (keys.length > 0) {
|
|
38
|
+
logger$1.log(` translation keys:`);
|
|
39
|
+
keys.forEach(key => {
|
|
40
|
+
logger$1.log(` - ${key}`);
|
|
41
|
+
foundTranslationKeys.add(key);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof Error) {
|
|
48
|
+
logger$1.error(`error processing file ${filePath}: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
logger$1.error(`error processing file ${filePath}: unknown error`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
logger$1.log(`\nfound ${foundTranslationKeys.size} used translation keys in the code`);
|
|
56
|
+
logger$1.log(`found ${foundNamespaces.size} used namespaces in the code: ${Array.from(foundNamespaces).join(', ')}`);
|
|
57
|
+
// check unused keys in each language file
|
|
58
|
+
const unusedKeys = {};
|
|
59
|
+
const removedKeys = {};
|
|
60
|
+
const unusedNamespaces = {};
|
|
61
|
+
config.i18n.locales.forEach(locale => {
|
|
62
|
+
unusedKeys[locale] = [];
|
|
63
|
+
removedKeys[locale] = [];
|
|
64
|
+
unusedNamespaces[locale] = [];
|
|
65
|
+
// get all keys in the translation file
|
|
66
|
+
const allTranslationKeys = translationParser.getAllKeys(translations[locale]);
|
|
67
|
+
// get all namespaces (top-level keys) in the translation file
|
|
68
|
+
const allNamespaces = Object.keys(translations[locale] || {});
|
|
69
|
+
// find unused namespaces
|
|
70
|
+
allNamespaces.forEach(namespace => {
|
|
71
|
+
if (!foundNamespaces.has(namespace)) {
|
|
72
|
+
unusedNamespaces[locale].push(namespace);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// find unused keys
|
|
76
|
+
allTranslationKeys.forEach(key => {
|
|
77
|
+
if (!foundTranslationKeys.has(key)) {
|
|
78
|
+
unusedKeys[locale].push(key);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
logger$1.log(`\nfound ${unusedKeys[locale].length} unused keys in the ${locale} translation file`);
|
|
82
|
+
logger$1.log(`found ${unusedNamespaces[locale].length} unused namespaces in the ${locale} translation file`);
|
|
83
|
+
});
|
|
84
|
+
if (shouldRemove) {
|
|
85
|
+
logger$1.log('\nstart deleting unused translation keys...');
|
|
86
|
+
// delete unused keys in each language file
|
|
87
|
+
config.i18n.locales.forEach(locale => {
|
|
88
|
+
const translationsCopy = { ...translations[locale] };
|
|
89
|
+
unusedKeys[locale].forEach(key => {
|
|
90
|
+
if (translationParser.removeKeyFromTranslations(key, translationsCopy)) {
|
|
91
|
+
removedKeys[locale].push(key);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// delete unused namespaces
|
|
95
|
+
unusedNamespaces[locale].forEach(namespace => {
|
|
96
|
+
if (translationsCopy[namespace] !== undefined) {
|
|
97
|
+
delete translationsCopy[namespace];
|
|
98
|
+
logger$1.log(`deleted unused namespace ${namespace} from the ${locale} translation file`);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
// clean empty objects
|
|
102
|
+
const cleanedTranslations = translationParser.cleanEmptyObjects(translationsCopy);
|
|
103
|
+
// save updated translation file
|
|
104
|
+
const filePath = fileScanner.getTranslationFilePath(locale, config, cwd);
|
|
105
|
+
fs.writeFileSync(filePath, JSON.stringify(cleanedTranslations, null, 2), 'utf8');
|
|
106
|
+
logger$1.log(`deleted ${removedKeys[locale].length} unused keys from the ${locale} translation file`);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
logger$1.log('\nTo delete unused keys, please run the script with the --remove parameter');
|
|
111
|
+
}
|
|
112
|
+
// generate report
|
|
113
|
+
logger$1.log('\n=== unused translation keys report ===\n');
|
|
114
|
+
config.i18n.locales.forEach(locale => {
|
|
115
|
+
if (unusedNamespaces[locale].length > 0) {
|
|
116
|
+
logger$1.log(`🔍 unused namespaces in the ${locale} translation file:`);
|
|
117
|
+
unusedNamespaces[locale].forEach(namespace => logger$1.log(` - ${namespace}`));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
logger$1.success(`${locale} translation file has no unused namespaces`);
|
|
121
|
+
}
|
|
122
|
+
if (unusedKeys[locale].length > 0) {
|
|
123
|
+
logger$1.log(`\n🔍 unused keys in the ${locale} translation file:`);
|
|
124
|
+
unusedKeys[locale].forEach(key => logger$1.log(` - ${key}`));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
logger$1.success(`${locale} translation file has no unused keys`);
|
|
128
|
+
}
|
|
129
|
+
if (shouldRemove && removedKeys[locale].length > 0) {
|
|
130
|
+
logger$1.log(`\n🗑️ deleted keys from the ${locale} translation file:`);
|
|
131
|
+
removedKeys[locale].forEach(key => logger$1.log(` - ${key}`));
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
logger$1.log('\n=== report end ===\n');
|
|
135
|
+
logger$1.log("⚠️⚠️⚠️script depends on regular matching, for multiple translation namespaces in a single file, use naming to distinguish: t1 | t2 | t3 | ... ⚠️⚠️⚠️");
|
|
136
|
+
// save log file
|
|
137
|
+
logger$1.saveToFile(logFileName, cwd);
|
|
138
|
+
// if there are any unused keys or namespaces, return non-zero status code
|
|
139
|
+
return (Object.values(unusedKeys).some(keys => keys.length > 0) ||
|
|
140
|
+
Object.values(unusedNamespaces).some(namespaces => namespaces.length > 0)) ? 1 : 0;
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
logger$1.error(`error cleaning translations: ${error}`);
|
|
144
|
+
return 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
exports.cleanTranslations = cleanTranslations;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { scanFiles, loadTranslations, getTranslationFilePath } from '../utils/file-scanner.mjs';
|
|
2
|
+
import { Logger } from '../utils/logger.mjs';
|
|
3
|
+
import { extractTranslationsInfo, getAllKeys, removeKeyFromTranslations, cleanEmptyObjects } from '../utils/translation-parser.mjs';
|
|
4
|
+
import { writeFileSync } from 'fs';
|
|
5
|
+
|
|
6
|
+
async function cleanTranslations(config, shouldRemove = false, cwd = typeof process !== 'undefined' ? process.cwd() : '.') {
|
|
7
|
+
const logger = new Logger(config);
|
|
8
|
+
const logFileName = shouldRemove ? 'remove.log' : 'clean.log';
|
|
9
|
+
logger.warn('==============================');
|
|
10
|
+
logger.warn(`‼️ Current working directory: ⭕ ${cwd} ⭕`);
|
|
11
|
+
logger.warn('==============================');
|
|
12
|
+
try {
|
|
13
|
+
logger.log('start checking unused translation keys...');
|
|
14
|
+
// scan all files
|
|
15
|
+
const scanResults = await scanFiles(config, cwd);
|
|
16
|
+
logger.log(`找到 ${scanResults.length} 个文件需要扫描`);
|
|
17
|
+
// load translation files
|
|
18
|
+
const translations = loadTranslations(config, cwd);
|
|
19
|
+
// collect used translation keys and namespaces
|
|
20
|
+
const foundTranslationKeys = new Set();
|
|
21
|
+
const foundNamespaces = new Set();
|
|
22
|
+
// scan all files, collect used translation keys and namespaces
|
|
23
|
+
for (const { filePath, content } of scanResults) {
|
|
24
|
+
try {
|
|
25
|
+
const { namespaces, keys } = extractTranslationsInfo(content, filePath);
|
|
26
|
+
if (keys.length > 0 || namespaces.size > 0) {
|
|
27
|
+
logger.log(`found the following information in the file ${filePath}:`);
|
|
28
|
+
if (namespaces.size > 0) {
|
|
29
|
+
logger.log(` translation function mapping:`);
|
|
30
|
+
namespaces.forEach((namespace, varName) => {
|
|
31
|
+
logger.log(` - ${varName} => ${namespace}`);
|
|
32
|
+
foundNamespaces.add(namespace);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (keys.length > 0) {
|
|
36
|
+
logger.log(` translation keys:`);
|
|
37
|
+
keys.forEach(key => {
|
|
38
|
+
logger.log(` - ${key}`);
|
|
39
|
+
foundTranslationKeys.add(key);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof Error) {
|
|
46
|
+
logger.error(`error processing file ${filePath}: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
logger.error(`error processing file ${filePath}: unknown error`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
logger.log(`\nfound ${foundTranslationKeys.size} used translation keys in the code`);
|
|
54
|
+
logger.log(`found ${foundNamespaces.size} used namespaces in the code: ${Array.from(foundNamespaces).join(', ')}`);
|
|
55
|
+
// check unused keys in each language file
|
|
56
|
+
const unusedKeys = {};
|
|
57
|
+
const removedKeys = {};
|
|
58
|
+
const unusedNamespaces = {};
|
|
59
|
+
config.i18n.locales.forEach(locale => {
|
|
60
|
+
unusedKeys[locale] = [];
|
|
61
|
+
removedKeys[locale] = [];
|
|
62
|
+
unusedNamespaces[locale] = [];
|
|
63
|
+
// get all keys in the translation file
|
|
64
|
+
const allTranslationKeys = getAllKeys(translations[locale]);
|
|
65
|
+
// get all namespaces (top-level keys) in the translation file
|
|
66
|
+
const allNamespaces = Object.keys(translations[locale] || {});
|
|
67
|
+
// find unused namespaces
|
|
68
|
+
allNamespaces.forEach(namespace => {
|
|
69
|
+
if (!foundNamespaces.has(namespace)) {
|
|
70
|
+
unusedNamespaces[locale].push(namespace);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// find unused keys
|
|
74
|
+
allTranslationKeys.forEach(key => {
|
|
75
|
+
if (!foundTranslationKeys.has(key)) {
|
|
76
|
+
unusedKeys[locale].push(key);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
logger.log(`\nfound ${unusedKeys[locale].length} unused keys in the ${locale} translation file`);
|
|
80
|
+
logger.log(`found ${unusedNamespaces[locale].length} unused namespaces in the ${locale} translation file`);
|
|
81
|
+
});
|
|
82
|
+
if (shouldRemove) {
|
|
83
|
+
logger.log('\nstart deleting unused translation keys...');
|
|
84
|
+
// delete unused keys in each language file
|
|
85
|
+
config.i18n.locales.forEach(locale => {
|
|
86
|
+
const translationsCopy = { ...translations[locale] };
|
|
87
|
+
unusedKeys[locale].forEach(key => {
|
|
88
|
+
if (removeKeyFromTranslations(key, translationsCopy)) {
|
|
89
|
+
removedKeys[locale].push(key);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// delete unused namespaces
|
|
93
|
+
unusedNamespaces[locale].forEach(namespace => {
|
|
94
|
+
if (translationsCopy[namespace] !== undefined) {
|
|
95
|
+
delete translationsCopy[namespace];
|
|
96
|
+
logger.log(`deleted unused namespace ${namespace} from the ${locale} translation file`);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
// clean empty objects
|
|
100
|
+
const cleanedTranslations = cleanEmptyObjects(translationsCopy);
|
|
101
|
+
// save updated translation file
|
|
102
|
+
const filePath = getTranslationFilePath(locale, config, cwd);
|
|
103
|
+
writeFileSync(filePath, JSON.stringify(cleanedTranslations, null, 2), 'utf8');
|
|
104
|
+
logger.log(`deleted ${removedKeys[locale].length} unused keys from the ${locale} translation file`);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
logger.log('\nTo delete unused keys, please run the script with the --remove parameter');
|
|
109
|
+
}
|
|
110
|
+
// generate report
|
|
111
|
+
logger.log('\n=== unused translation keys report ===\n');
|
|
112
|
+
config.i18n.locales.forEach(locale => {
|
|
113
|
+
if (unusedNamespaces[locale].length > 0) {
|
|
114
|
+
logger.log(`🔍 unused namespaces in the ${locale} translation file:`);
|
|
115
|
+
unusedNamespaces[locale].forEach(namespace => logger.log(` - ${namespace}`));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
logger.success(`${locale} translation file has no unused namespaces`);
|
|
119
|
+
}
|
|
120
|
+
if (unusedKeys[locale].length > 0) {
|
|
121
|
+
logger.log(`\n🔍 unused keys in the ${locale} translation file:`);
|
|
122
|
+
unusedKeys[locale].forEach(key => logger.log(` - ${key}`));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
logger.success(`${locale} translation file has no unused keys`);
|
|
126
|
+
}
|
|
127
|
+
if (shouldRemove && removedKeys[locale].length > 0) {
|
|
128
|
+
logger.log(`\n🗑️ deleted keys from the ${locale} translation file:`);
|
|
129
|
+
removedKeys[locale].forEach(key => logger.log(` - ${key}`));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
logger.log('\n=== report end ===\n');
|
|
133
|
+
logger.log("⚠️⚠️⚠️script depends on regular matching, for multiple translation namespaces in a single file, use naming to distinguish: t1 | t2 | t3 | ... ⚠️⚠️⚠️");
|
|
134
|
+
// save log file
|
|
135
|
+
logger.saveToFile(logFileName, cwd);
|
|
136
|
+
// if there are any unused keys or namespaces, return non-zero status code
|
|
137
|
+
return (Object.values(unusedKeys).some(keys => keys.length > 0) ||
|
|
138
|
+
Object.values(unusedNamespaces).some(namespaces => namespaces.length > 0)) ? 1 : 0;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
logger.error(`error cleaning translations: ${error}`);
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { cleanTranslations };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-diaomao-app.d.ts","sourceRoot":"","sources":["../../src/commands/create-diaomao-app.ts"],"names":[],"mappings":"AAKA,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,iBA6JvD"}
|