feima-shortcuts 0.2.3 → 0.3.0-beta.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/bin/feima.js +21 -1
- package/package.json +1 -1
- package/src/scripts/check-locale/index.js +217 -0
- package/src/scripts/restful-api/old.js +1 -6
package/bin/feima.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const inquirer = require("inquirer");
|
|
3
|
+
const { Command } = require('commander')
|
|
3
4
|
const feima = require("../src/generate");
|
|
4
5
|
const { version } = require("../package.json"); // 读取 package.json 中的版本号
|
|
5
6
|
|
|
7
|
+
const program = new Command()
|
|
8
|
+
|
|
9
|
+
program
|
|
10
|
+
.name('feima')
|
|
11
|
+
.version(version)
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.command('check-locale')
|
|
15
|
+
.description('检查未使用的 locale')
|
|
16
|
+
.action(() => {
|
|
17
|
+
require('../src/scripts/check-locale')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
|
|
6
21
|
const run = () => {
|
|
7
22
|
console.log(`🚀 当前版本:v${version}`);
|
|
8
23
|
|
|
@@ -81,4 +96,9 @@ const run = () => {
|
|
|
81
96
|
});
|
|
82
97
|
};
|
|
83
98
|
|
|
84
|
-
|
|
99
|
+
if (process.argv.length <= 2) {
|
|
100
|
+
// 等价于你之前 bin/feima.js 的全部内容
|
|
101
|
+
run();
|
|
102
|
+
} else {
|
|
103
|
+
program.parse(process.argv)
|
|
104
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const glob = require('glob')
|
|
5
|
+
const ts = require('typescript')
|
|
6
|
+
|
|
7
|
+
/* 工具函数,保持你原逻辑 */
|
|
8
|
+
function flattenLocale(obj, prefix = '') {
|
|
9
|
+
let res = []
|
|
10
|
+
for (const k in obj) {
|
|
11
|
+
const key = prefix ? `${prefix}.${k}` : k
|
|
12
|
+
if (typeof obj[k] === 'object' && obj[k] !== null) {
|
|
13
|
+
res = res.concat(flattenLocale(obj[k], key))
|
|
14
|
+
} else {
|
|
15
|
+
res.push(key)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return res
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadLocaleKeys(localePath) {
|
|
22
|
+
const code = fs.readFileSync(localePath, 'utf-8')
|
|
23
|
+
const sf = ts.createSourceFile(localePath, code, ts.ScriptTarget.Latest, true)
|
|
24
|
+
|
|
25
|
+
let obj = null
|
|
26
|
+
|
|
27
|
+
function visit(node) {
|
|
28
|
+
if (
|
|
29
|
+
ts.isExportAssignment(node) &&
|
|
30
|
+
ts.isObjectLiteralExpression(node.expression)
|
|
31
|
+
) {
|
|
32
|
+
obj = eval(`(${node.expression.getText()})`)
|
|
33
|
+
}
|
|
34
|
+
ts.forEachChild(node, visit)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
visit(sf)
|
|
38
|
+
return obj ? flattenLocale(obj) : []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function extractUsedKeys(code) {
|
|
42
|
+
const reg = /t\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g
|
|
43
|
+
const set = new Set()
|
|
44
|
+
let m
|
|
45
|
+
while ((m = reg.exec(code))) set.add(m[1])
|
|
46
|
+
return set
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function detectLocaleImport(code) {
|
|
50
|
+
const res = {
|
|
51
|
+
correct: false,
|
|
52
|
+
invalid: [],
|
|
53
|
+
external: [],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const reg = /import\s+locale\s+from\s+['"]([^'"]+)['"]/g
|
|
57
|
+
let m
|
|
58
|
+
|
|
59
|
+
while ((m = reg.exec(code))) {
|
|
60
|
+
const src = m[1]
|
|
61
|
+
if (src === './locale') {
|
|
62
|
+
res.correct = true
|
|
63
|
+
} else if (src.startsWith('../')) {
|
|
64
|
+
res.invalid.push(src)
|
|
65
|
+
} else {
|
|
66
|
+
res.external.push(src)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return res
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function detectUseLocale(code) {
|
|
74
|
+
return {
|
|
75
|
+
local: /useLocale\s*\(\s*\{\s*locale\s*\}\s*\)/.test(code),
|
|
76
|
+
global: /useLocale\s*\(\s*\)/.test(code),
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* 页面 locale 检测 */
|
|
81
|
+
function checkPageLocale(pageDir) {
|
|
82
|
+
const localePath = path.join(pageDir, 'locale.ts')
|
|
83
|
+
const indexVue = path.join(pageDir, 'index.vue')
|
|
84
|
+
|
|
85
|
+
if (!fs.existsSync(localePath) || !fs.existsSync(indexVue)) return false
|
|
86
|
+
|
|
87
|
+
const code = fs.readFileSync(indexVue, 'utf-8')
|
|
88
|
+
const localeImport = detectLocaleImport(code)
|
|
89
|
+
const useLocale = detectUseLocale(code)
|
|
90
|
+
|
|
91
|
+
const warnings = []
|
|
92
|
+
|
|
93
|
+
if (localeImport.invalid.length) {
|
|
94
|
+
warnings.push('使用了 ../locale')
|
|
95
|
+
}
|
|
96
|
+
if (!localeImport.correct) {
|
|
97
|
+
warnings.push('未使用 ./locale')
|
|
98
|
+
}
|
|
99
|
+
if (!useLocale.local) {
|
|
100
|
+
warnings.push('未使用 useLocale({ locale })')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (warnings.length) {
|
|
104
|
+
console.log(`\n⚠️ 页面存在不规范 locale 用法`)
|
|
105
|
+
console.log(` ${indexVue}`)
|
|
106
|
+
warnings.forEach((w) => console.log(` - ${w}`))
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const localeKeys = loadLocaleKeys(localePath)
|
|
111
|
+
const usedKeys = extractUsedKeys(code)
|
|
112
|
+
const unused = localeKeys.filter((k) => !usedKeys.has(k))
|
|
113
|
+
|
|
114
|
+
if (unused.length) {
|
|
115
|
+
console.log(`\n❌ 页面未使用 locale key`)
|
|
116
|
+
console.log(` ${localePath}`)
|
|
117
|
+
unused.forEach((k) => console.log(` - ${k}`))
|
|
118
|
+
return true
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* 组件 locale 检测 */
|
|
125
|
+
function checkComponentLocale(componentDir) {
|
|
126
|
+
const localePath = path.join(componentDir, 'locale.ts')
|
|
127
|
+
if (!fs.existsSync(localePath)) return false
|
|
128
|
+
|
|
129
|
+
const vueFiles = glob.sync('**/*.vue', {
|
|
130
|
+
cwd: componentDir,
|
|
131
|
+
absolute: true,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const localeKeys = loadLocaleKeys(localePath)
|
|
135
|
+
const usedKeys = new Set()
|
|
136
|
+
|
|
137
|
+
const invalidFiles = []
|
|
138
|
+
const skippedFiles = []
|
|
139
|
+
|
|
140
|
+
vueFiles.forEach((file) => {
|
|
141
|
+
const code = fs.readFileSync(file, 'utf-8')
|
|
142
|
+
const localeImport = detectLocaleImport(code)
|
|
143
|
+
const useLocale = detectUseLocale(code)
|
|
144
|
+
|
|
145
|
+
if (localeImport.invalid.length) {
|
|
146
|
+
invalidFiles.push({ file, src: localeImport.invalid })
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!localeImport.correct || !useLocale.local) {
|
|
151
|
+
skippedFiles.push(file)
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
extractUsedKeys(code).forEach((k) => usedKeys.add(k))
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
let hasError = false
|
|
159
|
+
|
|
160
|
+
const unused = localeKeys.filter((k) => !usedKeys.has(k))
|
|
161
|
+
if (unused.length) {
|
|
162
|
+
hasError = true
|
|
163
|
+
console.log(`\n❌ 组件未使用 locale key`)
|
|
164
|
+
console.log(` ${localePath}`)
|
|
165
|
+
unused.forEach((k) => console.log(` - ${k}`))
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (invalidFiles.length) {
|
|
169
|
+
hasError = true
|
|
170
|
+
console.log(`\n🚫 组件存在错误的 locale 引入方式`)
|
|
171
|
+
invalidFiles.forEach((f) => {
|
|
172
|
+
console.log(` ${path.relative(process.cwd(), f.file)}`)
|
|
173
|
+
f.src.forEach((s) => console.log(` import locale from '${s}'`))
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (skippedFiles.length) {
|
|
178
|
+
console.log(`\n⚠️ 组件中以下文件未绑定本地 locale(已跳过)`)
|
|
179
|
+
skippedFiles.forEach((f) =>
|
|
180
|
+
console.log(` ${path.relative(process.cwd(), f)}`)
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return hasError
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* 主入口 */
|
|
188
|
+
|
|
189
|
+
function run() {
|
|
190
|
+
const root = path.resolve(process.cwd(), 'src/views')
|
|
191
|
+
let hasError = false
|
|
192
|
+
|
|
193
|
+
// 页面
|
|
194
|
+
glob.sync('**/index.vue', { cwd: root }).forEach((p) => {
|
|
195
|
+
const dir = path.join(root, path.dirname(p))
|
|
196
|
+
if (checkPageLocale(dir)) hasError = true
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
// 组件
|
|
200
|
+
glob.sync('**/components/**/locale.ts', { cwd: root }).forEach((p) => {
|
|
201
|
+
const dir = path.join(root, path.dirname(p))
|
|
202
|
+
if (checkComponentLocale(dir)) hasError = true
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
if (hasError) {
|
|
206
|
+
console.log('\n❗ locale 治理未通过')
|
|
207
|
+
process.exit(1)
|
|
208
|
+
} else {
|
|
209
|
+
console.log('\n✅ locale 治理通过')
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (require.main === module) {
|
|
214
|
+
run()
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = run
|
|
@@ -34,15 +34,10 @@ function splitString(input) {
|
|
|
34
34
|
return { path, functionName };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const isFunctionExists = (data, functionName) => {
|
|
38
|
-
const regex = new RegExp(`export\\s+const\\s+${functionName}\\s*=`, 'g');
|
|
39
|
-
return regex.test(data);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
37
|
const fileData = (answers, functionName) => {
|
|
43
38
|
if (fs.existsSync(`./${fileName}`)) {
|
|
44
39
|
const data = fs.readFileSync(`./${fileName}`, "utf8");
|
|
45
|
-
if (
|
|
40
|
+
if (data.includes(`export const ${functionName}: any`)) {
|
|
46
41
|
return "";
|
|
47
42
|
}
|
|
48
43
|
return data;
|