@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.
Files changed (65) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +137 -1410
  4. package/dist/cli.mjs +145 -432
  5. package/dist/commands/check-translations.d.ts +3 -0
  6. package/dist/commands/check-translations.d.ts.map +1 -0
  7. package/dist/commands/check-translations.js +132 -0
  8. package/dist/commands/check-translations.mjs +130 -0
  9. package/dist/commands/clean-translations.d.ts +3 -0
  10. package/dist/commands/clean-translations.d.ts.map +1 -0
  11. package/dist/commands/clean-translations.js +148 -0
  12. package/dist/commands/clean-translations.mjs +146 -0
  13. package/dist/commands/create-diaomao-app.d.ts +2 -0
  14. package/dist/commands/create-diaomao-app.d.ts.map +1 -0
  15. package/dist/commands/create-diaomao-app.js +151 -0
  16. package/dist/commands/create-diaomao-app.mjs +149 -0
  17. package/dist/commands/deep-clean.d.ts +3 -0
  18. package/dist/commands/deep-clean.d.ts.map +1 -0
  19. package/dist/commands/deep-clean.js +119 -0
  20. package/dist/commands/deep-clean.mjs +117 -0
  21. package/dist/commands/easy-changeset.d.ts +2 -0
  22. package/dist/commands/easy-changeset.d.ts.map +1 -0
  23. package/dist/commands/easy-changeset.js +39 -0
  24. package/dist/commands/easy-changeset.mjs +37 -0
  25. package/dist/commands/generate-blog-index.d.ts +3 -0
  26. package/dist/commands/generate-blog-index.d.ts.map +1 -0
  27. package/dist/commands/generate-blog-index.js +302 -0
  28. package/dist/commands/generate-blog-index.mjs +300 -0
  29. package/dist/commands/generate-nextjs-architecture.d.ts +3 -0
  30. package/dist/commands/generate-nextjs-architecture.d.ts.map +1 -0
  31. package/dist/commands/generate-nextjs-architecture.js +84 -0
  32. package/dist/commands/generate-nextjs-architecture.mjs +82 -0
  33. package/dist/config/index.d.ts +10 -0
  34. package/dist/config/index.d.ts.map +1 -0
  35. package/dist/config/index.js +173 -0
  36. package/dist/config/index.mjs +170 -0
  37. package/dist/config/schema.d.ts +34 -0
  38. package/dist/config/schema.d.ts.map +1 -0
  39. package/dist/config/schema.js +80 -0
  40. package/dist/config/schema.mjs +78 -0
  41. package/dist/index.d.ts +6 -49
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +9 -996
  44. package/dist/index.mjs +4 -3
  45. package/dist/utils/file-scanner.d.ts +22 -0
  46. package/dist/utils/file-scanner.d.ts.map +1 -0
  47. package/dist/utils/file-scanner.js +70 -0
  48. package/dist/utils/file-scanner.mjs +65 -0
  49. package/dist/utils/logger.d.ts +24 -0
  50. package/dist/utils/logger.d.ts.map +1 -0
  51. package/dist/utils/logger.js +63 -0
  52. package/dist/utils/logger.mjs +61 -0
  53. package/dist/utils/translation-parser.d.ts +29 -0
  54. package/dist/utils/translation-parser.d.ts.map +1 -0
  55. package/dist/utils/translation-parser.js +225 -0
  56. package/dist/utils/translation-parser.mjs +218 -0
  57. package/package.json +5 -5
  58. package/dist/chunk-GVR6HFHM.mjs +0 -989
  59. package/dist/chunk-GVR6HFHM.mjs.map +0 -1
  60. package/dist/cli.d.mts +0 -1
  61. package/dist/cli.js.map +0 -1
  62. package/dist/cli.mjs.map +0 -1
  63. package/dist/index.d.mts +0 -49
  64. package/dist/index.js.map +0 -1
  65. 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,3 @@
1
+ import { DevScriptsConfig } from '@dev-scripts/config/schema';
2
+ export declare function cleanTranslations(config: DevScriptsConfig, shouldRemove?: boolean, cwd?: string): Promise<number>;
3
+ //# sourceMappingURL=clean-translations.d.ts.map
@@ -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,2 @@
1
+ export declare function createDiaomaoApp(targetDir: string): Promise<void>;
2
+ //# sourceMappingURL=create-diaomao-app.d.ts.map
@@ -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"}