Skip to content

Commit 249852a

Browse files
committed
Add ignores option to no-unused-keys rule
1 parent 731fc03 commit 249852a

File tree

5 files changed

+152
-23
lines changed

5 files changed

+152
-23
lines changed

docs/rules/no-unused-keys.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ i18n.t('hi')
159159
{
160160
"src": "./src",
161161
"extensions": [".js", ".vue"],
162+
"ignores": [],
162163
"enableFix": false
163164
}
164165
]
@@ -167,6 +168,7 @@ i18n.t('hi')
167168

168169
- `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.
169170
- `extensions`: an array to allow specified lintable target file extension. If you don't set any options, it set to `.js` and `.vue` as default.
171+
- `ignores`: An array of key names and patterns to exclude from the check. If you want to specify a pattern, specify a string such as `/pattern/`.
170172
- `enableFix`: if `true`, enable automatically remove unused keys on `eslint --fix`. If you don't set any options, it set to `false` as default. (This is an experimental feature.)
171173

172174
## :couple: Related Rules

lib/rules/no-raw-text.ts

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type {
2525
} from '../types'
2626
import { isKebabCase, pascalCase } from '../utils/casing'
2727
import { createRule } from '../utils/rule'
28+
import { toRegExp } from '../utils/regexp'
2829

2930
type LiteralValue = VAST.ESLintLiteral['value']
3031
type StaticTemplateLiteral = VAST.ESLintTemplateLiteral & {
@@ -54,32 +55,9 @@ function getFixQuote(quotes: Quotes, code: string) {
5455
}
5556
return null
5657
}
57-
const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu
58-
const RE_HAS_REGEXP_CHAR = new RegExp(RE_REGEXP_CHAR.source)
59-
const RE_REGEXP_STR = /^\/(.+)\/(.*)$/u
60-
function toRegExp(str: string): RegExp {
61-
const parts = RE_REGEXP_STR.exec(str)
62-
if (parts) {
63-
return new RegExp(parts[1], parts[2])
64-
}
65-
return new RegExp(`^${escape(str)}$`)
66-
}
6758
const hasOnlyWhitespace = (value: string) => /^[\r\n\s\t\f\v]+$/.test(value)
6859
const INNER_START_OFFSET = '<template>'.length
6960

70-
/**
71-
* Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
72-
* "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
73-
*
74-
* @param {string} string The string to escape.
75-
* @returns {string} Returns the escaped string.
76-
*/
77-
function escape(str: string) {
78-
return str && RE_HAS_REGEXP_CHAR.test(str)
79-
? str.replace(RE_REGEXP_CHAR, '\\$&')
80-
: str
81-
}
82-
8361
/**
8462
* Get the attribute to be verified from the element name.
8563
*/

lib/rules/no-unused-keys.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import type {
2323
import { joinPath, parsePath } from '../utils/key-path'
2424
import { getCwd } from '../utils/get-cwd'
2525
import { createRule } from '../utils/rule'
26+
import { toRegExp } from '../utils/regexp'
2627
const debug = debugBuilder('eslint-plugin-vue-i18n:no-unused-keys')
2728

2829
type UsedKeys = {
@@ -78,6 +79,7 @@ function create(context: RuleContext): RuleListener {
7879
const filename = context.getFilename()
7980
const options = (context.options && context.options[0]) || {}
8081
const enableFix = options.enableFix
82+
const ignores = ((options.ignores || []) as string[]).map(toRegExp)
8183

8284
function createVerifyContext<N extends JSONAST.JSONNode | YAMLAST.YAMLNode>(
8385
usedKeys: UsedKeys,
@@ -129,6 +131,9 @@ function create(context: RuleContext): RuleListener {
129131
reports() {
130132
for (const { node, keyPath } of reports) {
131133
const keyPathStr = joinPath(...keyPath)
134+
if (ignores.some(reg => reg.test(keyPathStr))) {
135+
continue
136+
}
132137
const fix = buildFixer(node)
133138
context.report({
134139
message: `unused '${keyPathStr}' key`,
@@ -578,6 +583,10 @@ export = createRule({
578583
items: { type: 'string' },
579584
default: ['.js', '.vue']
580585
},
586+
ignores: {
587+
type: 'array',
588+
items: { type: 'string' }
589+
},
581590
enableFix: {
582591
type: 'boolean'
583592
}

lib/utils/regexp.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu
2+
const RE_REGEXP_STR = /^\/(.+)\/(.*)$/u
3+
4+
export function toRegExp(str: string): RegExp {
5+
const parts = RE_REGEXP_STR.exec(str)
6+
if (parts) {
7+
return new RegExp(parts[1], parts[2])
8+
}
9+
return new RegExp(`^${escape(str)}$`)
10+
}
11+
/**
12+
* Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
13+
* "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
14+
*
15+
* @param {string} string The string to escape.
16+
* @returns {string} Returns the escaped string.
17+
*/
18+
function escape(str: string) {
19+
return str && str.replace(RE_REGEXP_CHAR, '\\$&')
20+
}

tests/lib/rules/no-unused-keys.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,52 @@ new RuleTester({
205205
}
206206
}
207207
</script>`
208+
},
209+
{
210+
filename: 'test.vue',
211+
code: `
212+
<i18n locale="en">
213+
{
214+
"foo": "foo",
215+
"bar": {
216+
"nest": "nest",
217+
"ignore": "ignore",
218+
},
219+
"ignore": "ignore",
220+
}
221+
</i18n>
222+
<script>
223+
export default {
224+
created () {
225+
this.$t('foo')
226+
this.$t('bar.nest')
227+
}
228+
}
229+
</script>`,
230+
options: [{ ignores: ['ignore', 'bar.ignore'] }]
231+
},
232+
{
233+
filename: 'test.vue',
234+
code: `
235+
<i18n locale="en">
236+
{
237+
"foo": "foo",
238+
"bar": {
239+
"nest": "nest",
240+
"ptn_foo": "ignore",
241+
},
242+
"ptn_bar": "ignore"
243+
}
244+
</i18n>
245+
<script>
246+
export default {
247+
created () {
248+
this.$t('foo')
249+
this.$t('bar.nest')
250+
}
251+
}
252+
</script>`,
253+
options: [{ ignores: ['/ptn/'] }]
208254
}
209255
],
210256
invalid: [
@@ -1026,6 +1072,80 @@ ${' '.repeat(6)}
10261072
<script></script>`,
10271073
errors: [`unused '["[{foo: bar}]"].foo' key`]
10281074
},
1075+
{
1076+
filename: 'test.vue',
1077+
code: `
1078+
<i18n locale="en">
1079+
{
1080+
"foo": "foo",
1081+
"bar": {
1082+
"nest": "nest",
1083+
"ignore": "ignore",
1084+
"not_ignore": "not_ignore",
1085+
},
1086+
"ignore": "ignore",
1087+
"not_ignore": "not_ignore",
1088+
}
1089+
</i18n>
1090+
<script>
1091+
export default {
1092+
created () {
1093+
this.$t('foo')
1094+
this.$t('bar.nest')
1095+
}
1096+
}
1097+
</script>`,
1098+
options: [{ ignores: ['ignore', 'bar.ignore'] }],
1099+
errors: [
1100+
{
1101+
message: "unused 'bar.not_ignore' key",
1102+
line: 8,
1103+
column: 9
1104+
},
1105+
{
1106+
message: "unused 'not_ignore' key",
1107+
line: 11,
1108+
column: 7
1109+
}
1110+
]
1111+
},
1112+
{
1113+
filename: 'test.vue',
1114+
code: `
1115+
<i18n locale="en">
1116+
{
1117+
"foo": "foo",
1118+
"bar": {
1119+
"nest": "nest",
1120+
"ptn_foo": "ignore",
1121+
"no_hit_pattern_foo": "not_ignore"
1122+
},
1123+
"ptn_bar": "ignore",
1124+
"no_hit_pattern_bar": "not_ignore"
1125+
}
1126+
</i18n>
1127+
<script>
1128+
export default {
1129+
created () {
1130+
this.$t('foo')
1131+
this.$t('bar.nest')
1132+
}
1133+
}
1134+
</script>`,
1135+
options: [{ ignores: ['/ptn/'] }],
1136+
errors: [
1137+
{
1138+
message: "unused 'bar.no_hit_pattern_foo' key",
1139+
line: 8,
1140+
column: 9
1141+
},
1142+
{
1143+
message: "unused 'no_hit_pattern_bar' key",
1144+
line: 11,
1145+
column: 7
1146+
}
1147+
]
1148+
},
10291149
...getTestCasesFromFixtures(
10301150
{
10311151
cwd: join(cwdRoot, './valid')

0 commit comments

Comments
 (0)