feima-shortcuts 0.3.0-beta.10 → 0.3.0-beta.11
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/package.json
CHANGED
|
@@ -5,16 +5,12 @@ const { parse } = require('@vue/compiler-sfc')
|
|
|
5
5
|
const babelParser = require('@babel/parser')
|
|
6
6
|
const traverse = require('@babel/traverse').default
|
|
7
7
|
|
|
8
|
-
/* ---------------- defaults ---------------- */
|
|
9
|
-
|
|
10
8
|
const DEFAULT_OPTIONS = {
|
|
11
9
|
cwd: process.cwd(),
|
|
12
10
|
scanDirs: ['src/views', 'src/components'],
|
|
13
11
|
silent: false,
|
|
14
12
|
}
|
|
15
13
|
|
|
16
|
-
/* ---------------- utils ---------------- */
|
|
17
|
-
|
|
18
14
|
function flattenLocale(obj, prefix = '') {
|
|
19
15
|
let res = []
|
|
20
16
|
for (const k in obj) {
|
|
@@ -43,12 +39,12 @@ function parseCode(code) {
|
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
42
|
+
* components 目录专用:
|
|
43
|
+
* 向上查找最近含 locale.ts 的目录(包含自身目录)
|
|
44
|
+
* 到达 componentsRoot 停止
|
|
48
45
|
*/
|
|
49
46
|
function findNearestLocale(fileDir, componentsRoot) {
|
|
50
47
|
let current = fileDir
|
|
51
|
-
|
|
52
48
|
while (current.startsWith(componentsRoot)) {
|
|
53
49
|
const localePath = path.join(current, 'locale.ts')
|
|
54
50
|
if (fs.existsSync(localePath)) {
|
|
@@ -58,11 +54,18 @@ function findNearestLocale(fileDir, componentsRoot) {
|
|
|
58
54
|
if (parent === current) break
|
|
59
55
|
current = parent
|
|
60
56
|
}
|
|
61
|
-
|
|
62
57
|
return null
|
|
63
58
|
}
|
|
64
59
|
|
|
65
|
-
|
|
60
|
+
/**
|
|
61
|
+
* views 目录专用:
|
|
62
|
+
* 只查找当前文件同目录的 locale.ts
|
|
63
|
+
*/
|
|
64
|
+
function findViewsLocale(fileDir) {
|
|
65
|
+
const localePath = path.join(fileDir, 'locale.ts')
|
|
66
|
+
if (fs.existsSync(localePath)) return localePath
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
66
69
|
|
|
67
70
|
function run(userOptions = {}) {
|
|
68
71
|
const options = { ...DEFAULT_OPTIONS, ...userOptions }
|
|
@@ -73,12 +76,9 @@ function run(userOptions = {}) {
|
|
|
73
76
|
for (const dir of scanDirs) {
|
|
74
77
|
const absDir = path.join(cwd, dir)
|
|
75
78
|
if (!fs.existsSync(absDir)) {
|
|
76
|
-
if (!silent) {
|
|
77
|
-
console.warn(`⚠️ 目录不存在:${dir}`)
|
|
78
|
-
}
|
|
79
|
+
if (!silent) console.warn(`⚠️ 目录不存在:${dir}`)
|
|
79
80
|
continue
|
|
80
81
|
}
|
|
81
|
-
|
|
82
82
|
files.push(
|
|
83
83
|
...glob.sync('**/*.{vue,ts,tsx}', {
|
|
84
84
|
cwd: absDir,
|
|
@@ -116,7 +116,7 @@ function run(userOptions = {}) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
const usedKeys = []
|
|
119
|
-
let importedLocale = false
|
|
119
|
+
let importedLocale = false // 只对 views 生效
|
|
120
120
|
|
|
121
121
|
traverse(ast, {
|
|
122
122
|
ImportDeclaration(p) {
|
|
@@ -124,22 +124,44 @@ function run(userOptions = {}) {
|
|
|
124
124
|
importedLocale = true
|
|
125
125
|
}
|
|
126
126
|
},
|
|
127
|
-
|
|
128
127
|
CallExpression(p) {
|
|
129
128
|
const callee = p.node.callee
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
129
|
+
|
|
130
|
+
if (file.startsWith(viewsRoot)) {
|
|
131
|
+
// src/views 目录:只检查 t()
|
|
132
|
+
if (
|
|
133
|
+
callee.type === 'Identifier' &&
|
|
134
|
+
callee.name === 't'
|
|
135
|
+
) {
|
|
136
|
+
const keyArg = p.node.arguments[0]
|
|
137
|
+
const defaultArg = p.node.arguments[1]
|
|
138
|
+
if (keyArg && keyArg.type === 'StringLiteral') {
|
|
139
|
+
usedKeys.push({
|
|
140
|
+
key: keyArg.value,
|
|
141
|
+
defaultValue:
|
|
142
|
+
defaultArg && defaultArg.type === 'StringLiteral'
|
|
143
|
+
? defaultArg.value
|
|
144
|
+
: null,
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else if (file.startsWith(componentsRoot)) {
|
|
149
|
+
// src/components 目录:只检查 $t()
|
|
150
|
+
if (
|
|
151
|
+
callee.type === 'Identifier' &&
|
|
152
|
+
callee.name === '$t'
|
|
153
|
+
) {
|
|
154
|
+
const keyArg = p.node.arguments[0]
|
|
155
|
+
const defaultArg = p.node.arguments[1]
|
|
156
|
+
if (keyArg && keyArg.type === 'StringLiteral') {
|
|
157
|
+
usedKeys.push({
|
|
158
|
+
key: keyArg.value,
|
|
159
|
+
defaultValue:
|
|
160
|
+
defaultArg && defaultArg.type === 'StringLiteral'
|
|
161
|
+
? defaultArg.value
|
|
162
|
+
: null,
|
|
163
|
+
})
|
|
164
|
+
}
|
|
143
165
|
}
|
|
144
166
|
}
|
|
145
167
|
},
|
|
@@ -149,15 +171,25 @@ function run(userOptions = {}) {
|
|
|
149
171
|
|
|
150
172
|
const fileDir = path.dirname(file)
|
|
151
173
|
|
|
152
|
-
/* ---------- views 逻辑 ---------- */
|
|
153
174
|
if (file.startsWith(viewsRoot)) {
|
|
154
|
-
|
|
155
|
-
const
|
|
175
|
+
// views 逻辑:只查同目录 locale.ts,且 vue 文件必须导入 locale
|
|
176
|
+
const localePath = findViewsLocale(fileDir)
|
|
177
|
+
const localeKeys = localePath && loadLocaleKeys(localePath)
|
|
156
178
|
|
|
157
|
-
if (
|
|
179
|
+
if (file.endsWith('.vue') && !importedLocale) {
|
|
158
180
|
noLocaleUsage.push({
|
|
159
181
|
file: path.relative(cwd, file),
|
|
160
182
|
keys: usedKeys.map(i => i.key),
|
|
183
|
+
reason: 'vue 文件未 import locale',
|
|
184
|
+
})
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!localeKeys) {
|
|
189
|
+
noLocaleUsage.push({
|
|
190
|
+
file: path.relative(cwd, file),
|
|
191
|
+
keys: usedKeys.map(i => i.key),
|
|
192
|
+
reason: '同目录无 locale.ts',
|
|
161
193
|
})
|
|
162
194
|
continue
|
|
163
195
|
}
|
|
@@ -171,12 +203,11 @@ function run(userOptions = {}) {
|
|
|
171
203
|
})
|
|
172
204
|
}
|
|
173
205
|
}
|
|
174
|
-
|
|
175
206
|
continue
|
|
176
207
|
}
|
|
177
208
|
|
|
178
|
-
/* ---------- components 逻辑 ---------- */
|
|
179
209
|
if (file.startsWith(componentsRoot)) {
|
|
210
|
+
// components 逻辑:向上找最近 locale.ts
|
|
180
211
|
const localePath = findNearestLocale(fileDir, componentsRoot)
|
|
181
212
|
const localeKeys = localePath && loadLocaleKeys(localePath)
|
|
182
213
|
|
|
@@ -184,6 +215,7 @@ function run(userOptions = {}) {
|
|
|
184
215
|
noLocaleUsage.push({
|
|
185
216
|
file: path.relative(cwd, file),
|
|
186
217
|
keys: usedKeys.map(i => i.key),
|
|
218
|
+
reason: '未找到可用 locale.ts',
|
|
187
219
|
})
|
|
188
220
|
continue
|
|
189
221
|
}
|
|
@@ -197,25 +229,28 @@ function run(userOptions = {}) {
|
|
|
197
229
|
})
|
|
198
230
|
}
|
|
199
231
|
}
|
|
232
|
+
continue
|
|
200
233
|
}
|
|
201
234
|
}
|
|
202
235
|
|
|
203
|
-
/* ---------------- output ---------------- */
|
|
204
|
-
|
|
205
236
|
if (!silent) {
|
|
206
237
|
if (missingKeys.length) {
|
|
207
238
|
console.error('\n❌ 缺失的国际化 key:')
|
|
208
239
|
missingKeys.forEach(i =>
|
|
209
240
|
console.error(
|
|
210
|
-
` ${i.file} → ${i.key}${
|
|
241
|
+
` ${i.file} → ${i.key}${
|
|
242
|
+
i.defaultValue ? ` (默认值: ${i.defaultValue})` : ''
|
|
243
|
+
}`
|
|
211
244
|
)
|
|
212
245
|
)
|
|
213
246
|
}
|
|
214
247
|
|
|
215
248
|
if (noLocaleUsage.length) {
|
|
216
|
-
console.warn('\n⚠️
|
|
249
|
+
console.warn('\n⚠️ 以下文件使用了 $t / t,但未正确使用 locale.ts:')
|
|
217
250
|
noLocaleUsage.forEach(i =>
|
|
218
|
-
console.warn(
|
|
251
|
+
console.warn(
|
|
252
|
+
` ${i.file} → 原因: ${i.reason},涉及 keys: ${i.keys.join(', ')}`
|
|
253
|
+
)
|
|
219
254
|
)
|
|
220
255
|
}
|
|
221
256
|
|
|
@@ -233,6 +268,4 @@ function run(userOptions = {}) {
|
|
|
233
268
|
}
|
|
234
269
|
}
|
|
235
270
|
|
|
236
|
-
/* ---------------- exports ---------------- */
|
|
237
|
-
|
|
238
271
|
exports.run = run
|