Skip to content

Commit 1789aca

Browse files
committed
🆙 update(rule) add option to no-unused-keys rule
1 parent e5cb3d0 commit 1789aca

File tree

2 files changed

+92
-40
lines changed

2 files changed

+92
-40
lines changed

docs/rules/no-unused-keys.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ i18n.t('hi')
7474

7575
## Options
7676

77-
You can specify allowed directive-comments.
78-
7977
```json
8078
{
8179
"vue-i18n/no-unused-keys": ["error", {
80+
"src": ["./src"],
8281
"extensions": [".js", ".vue"]
8382
}]
8483
}
8584
```
8685

86+
- `src`: specify the source codes directory to be able to lint. If you don't set any options, it set to `process.cwd()` as default.
8787
- `extenstions`: an array to allow specified lintable target file extention. If you don't set any options, it set to `.js` and` .vue` as default.

lib/rules/no-unused-keys.js

Lines changed: 90 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,73 @@ const jsonAstParse = require('json-to-ast')
88
const jsonDiffPatch = require('jsondiffpatch').create({})
99
const flatten = require('flat')
1010
const collectKeys = require('../utils/collect-keys')
11-
const { loadLocaleMessages } = require('../utils/index')
11+
const { UNEXPETECD_ERROR_LOCATION, loadLocaleMessages } = require('../utils/index')
1212
const debug = require('debug')('eslint-plugin-vue-i18n:no-unused-keys')
1313

1414
let usedLocaleMessageKeys = null // used locale message keys
1515
let localeMessages = null // used locale messages
16+
let localeDir = null
1617

1718
function findExistLocaleMessage (fullpath, localeMessages) {
1819
return localeMessages.find(message => message.fullpath === fullpath)
1920
}
2021

21-
function getUnusedKeys (diffLocaleMessage) {
22-
const unusedKeys = []
23-
Object.keys(diffLocaleMessage).forEach(key => {
24-
const value = diffLocaleMessage[key]
25-
if (value && Array.isArray(value) && value.length === 1) {
26-
unusedKeys.push(key)
27-
}
28-
})
22+
function extractJsonInfo (context, node) {
23+
try {
24+
const [str, filename] = node.comments
25+
return [
26+
Buffer.from(str.value, 'base64').toString(),
27+
Buffer.from(filename.value, 'base64').toString()
28+
]
29+
} catch (e) {
30+
context.report({
31+
loc: UNEXPETECD_ERROR_LOCATION,
32+
message: e.message
33+
})
34+
return []
35+
}
36+
}
37+
38+
function generateJsonAst (context, json, filename) {
39+
let ast = null
40+
41+
try {
42+
ast = jsonAstParse(json, { loc: true, source: filename })
43+
} catch (e) {
44+
const { message, line, column } = e
45+
context.report({
46+
message,
47+
loc: { line, column }
48+
})
49+
}
50+
51+
return ast
52+
}
53+
54+
function getUnusedKeys (context, json, usedkeys) {
55+
let unusedKeys = []
56+
57+
try {
58+
const jsonValue = JSON.parse(json)
59+
const diffValue = jsonDiffPatch.diff(
60+
flatten(usedkeys, { safe: true }),
61+
flatten(jsonValue, { safe: true })
62+
)
63+
const diffLocaleMessage = flatten(diffValue, { safe: true })
64+
Object.keys(diffLocaleMessage).forEach(key => {
65+
const value = diffLocaleMessage[key]
66+
if (value && Array.isArray(value) && value.length === 1) {
67+
unusedKeys.push(key)
68+
}
69+
})
70+
} catch (e) {
71+
context.report({
72+
loc: UNEXPETECD_ERROR_LOCATION,
73+
message: e.message
74+
})
75+
unusedKeys = null
76+
}
77+
2978
return unusedKeys
3079
}
3180

@@ -65,53 +114,53 @@ function create (context) {
65114

66115
const { settings } = context
67116
if (!settings['vue-i18n'] || !settings['vue-i18n'].localeDir) {
68-
// TODO: should be error
117+
context.report({
118+
loc: UNEXPETECD_ERROR_LOCATION,
119+
message: `You need to 'localeDir' at 'settings. See the 'eslint-plugin-vue-i18n documentation`
120+
})
69121
return {}
70122
}
71-
localeMessages = localeMessages || loadLocaleMessages(settings['vue-i18n'].localeDir)
123+
124+
if (localeDir !== settings['vue-i18n'].localeDir) {
125+
debug(`change localeDir: ${localeDir} -> ${settings['vue-i18n'].localeDir}`)
126+
localeDir = settings['vue-i18n'].localeDir
127+
localeMessages = loadLocaleMessages(localeDir)
128+
} else {
129+
localeMessages = localeMessages || loadLocaleMessages(settings['vue-i18n'].localeDir)
130+
}
72131

73132
const targetLocaleMessage = findExistLocaleMessage(filename, localeMessages)
74133
if (!targetLocaleMessage) {
75134
debug(`ignore ${filename} in no-unused-keys`)
76135
return {}
77136
}
78137

79-
const { extensions } = (context.options && context.options[0]) || { extensions: ['.js', '.vue'] }
80-
const src = [process.cwd()] || ['.']
138+
const options = (context.options && context.options[0]) || {}
139+
const src = options.src || process.cwd()
140+
const extensions = options.extensions || ['.js', '.vue']
81141

82142
if (!usedLocaleMessageKeys) {
83-
usedLocaleMessageKeys = collectKeys(src, extensions)
143+
usedLocaleMessageKeys = collectKeys([src], extensions)
84144
}
85145

86146
return {
87147
Program (node) {
88-
const [stringNode, filenameNode] = node.comments
89-
const jsonString = Buffer.from(stringNode.value, 'base64').toString()
90-
const jsonFilename = Buffer.from(filenameNode.value, 'base64').toString()
91-
const jsonValue = JSON.parse(jsonString)
92-
try {
93-
const diffValue = jsonDiffPatch.diff(
94-
flatten(usedLocaleMessageKeys, { safe: true }),
95-
flatten(jsonValue, { safe: true })
96-
)
97-
const diffLocaleMessage = flatten(diffValue, { safe: true })
98-
const unusedKeys = getUnusedKeys(diffLocaleMessage)
99-
100-
const jsonAstSettings = { loc: true, source: jsonFilename }
101-
const ast = jsonAstParse(jsonString, jsonAstSettings)
102-
traverseJsonAstWithUnusedKeys(unusedKeys, ast, (fullpath, node) => {
103-
const { line, column } = node.loc.start
104-
context.report({
105-
message: `unused '${fullpath}' key in '${targetLocaleMessage.path}'`,
106-
loc: { line, column }
107-
})
108-
})
109-
} catch ({ message, line, column }) {
148+
const [jsonString, jsonFilename] = extractJsonInfo(context, node)
149+
if (!jsonString || !jsonFilename) { return }
150+
151+
const ast = generateJsonAst(context, jsonString, jsonFilename)
152+
if (!ast) { return }
153+
154+
const unusedKeys = getUnusedKeys(context, jsonString, usedLocaleMessageKeys)
155+
if (!unusedKeys) { return }
156+
157+
traverseJsonAstWithUnusedKeys(unusedKeys, ast, (fullpath, node) => {
158+
const { line, column } = node.loc.start
110159
context.report({
111-
message,
160+
message: `unused '${fullpath}' key in '${targetLocaleMessage.path}'`,
112161
loc: { line, column }
113162
})
114-
}
163+
})
115164
}
116165
}
117166
}
@@ -128,6 +177,9 @@ module.exports = {
128177
schema: [{
129178
type: 'object',
130179
properties: {
180+
src: {
181+
type: 'string'
182+
},
131183
extensions: {
132184
type: 'array',
133185
items: { type: 'string' },

0 commit comments

Comments
 (0)