Skip to content

Add ignores option to no-unused-keys rule #288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/rules/no-unused-keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ i18n.t('hi')
{
"src": "./src",
"extensions": [".js", ".vue"],
"ignores": [],
"enableFix": false
}
]
Expand All @@ -167,6 +168,7 @@ i18n.t('hi')

- `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.
- `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.
- `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/`.
- `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.)

## :couple: Related Rules
Expand Down
24 changes: 1 addition & 23 deletions lib/rules/no-raw-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
} from '../types'
import { isKebabCase, pascalCase } from '../utils/casing'
import { createRule } from '../utils/rule'
import { toRegExp } from '../utils/regexp'

type LiteralValue = VAST.ESLintLiteral['value']
type StaticTemplateLiteral = VAST.ESLintTemplateLiteral & {
Expand Down Expand Up @@ -54,32 +55,9 @@ function getFixQuote(quotes: Quotes, code: string) {
}
return null
}
const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu
const RE_HAS_REGEXP_CHAR = new RegExp(RE_REGEXP_CHAR.source)
const RE_REGEXP_STR = /^\/(.+)\/(.*)$/u
function toRegExp(str: string): RegExp {
const parts = RE_REGEXP_STR.exec(str)
if (parts) {
return new RegExp(parts[1], parts[2])
}
return new RegExp(`^${escape(str)}$`)
}
const hasOnlyWhitespace = (value: string) => /^[\r\n\s\t\f\v]+$/.test(value)
const INNER_START_OFFSET = '<template>'.length

/**
* Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
* "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
*
* @param {string} string The string to escape.
* @returns {string} Returns the escaped string.
*/
function escape(str: string) {
return str && RE_HAS_REGEXP_CHAR.test(str)
? str.replace(RE_REGEXP_CHAR, '\\$&')
: str
}

/**
* Get the attribute to be verified from the element name.
*/
Expand Down
9 changes: 9 additions & 0 deletions lib/rules/no-unused-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
import { joinPath, parsePath } from '../utils/key-path'
import { getCwd } from '../utils/get-cwd'
import { createRule } from '../utils/rule'
import { toRegExp } from '../utils/regexp'
const debug = debugBuilder('eslint-plugin-vue-i18n:no-unused-keys')

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

function createVerifyContext<N extends JSONAST.JSONNode | YAMLAST.YAMLNode>(
usedKeys: UsedKeys,
Expand Down Expand Up @@ -129,6 +131,9 @@ function create(context: RuleContext): RuleListener {
reports() {
for (const { node, keyPath } of reports) {
const keyPathStr = joinPath(...keyPath)
if (ignores.some(reg => reg.test(keyPathStr))) {
continue
}
const fix = buildFixer(node)
context.report({
message: `unused '${keyPathStr}' key`,
Expand Down Expand Up @@ -578,6 +583,10 @@ export = createRule({
items: { type: 'string' },
default: ['.js', '.vue']
},
ignores: {
type: 'array',
items: { type: 'string' }
},
enableFix: {
type: 'boolean'
}
Expand Down
20 changes: 20 additions & 0 deletions lib/utils/regexp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu
const RE_REGEXP_STR = /^\/(.+)\/(.*)$/u

export function toRegExp(str: string): RegExp {
const parts = RE_REGEXP_STR.exec(str)
if (parts) {
return new RegExp(parts[1], parts[2])
}
return new RegExp(`^${escape(str)}$`)
}
/**
* Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
* "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
*
* @param {string} string The string to escape.
* @returns {string} Returns the escaped string.
*/
function escape(str: string) {
return str && str.replace(RE_REGEXP_CHAR, '\\$&')
}
120 changes: 120 additions & 0 deletions tests/lib/rules/no-unused-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,52 @@ new RuleTester({
}
}
</script>`
},
{
filename: 'test.vue',
code: `
<i18n locale="en">
{
"foo": "foo",
"bar": {
"nest": "nest",
"ignore": "ignore",
},
"ignore": "ignore",
}
</i18n>
<script>
export default {
created () {
this.$t('foo')
this.$t('bar.nest')
}
}
</script>`,
options: [{ ignores: ['ignore', 'bar.ignore'] }]
},
{
filename: 'test.vue',
code: `
<i18n locale="en">
{
"foo": "foo",
"bar": {
"nest": "nest",
"ptn_foo": "ignore",
},
"ptn_bar": "ignore"
}
</i18n>
<script>
export default {
created () {
this.$t('foo')
this.$t('bar.nest')
}
}
</script>`,
options: [{ ignores: ['/ptn/'] }]
}
],
invalid: [
Expand Down Expand Up @@ -1026,6 +1072,80 @@ ${' '.repeat(6)}
<script></script>`,
errors: [`unused '["[{foo: bar}]"].foo' key`]
},
{
filename: 'test.vue',
code: `
<i18n locale="en">
{
"foo": "foo",
"bar": {
"nest": "nest",
"ignore": "ignore",
"not_ignore": "not_ignore",
},
"ignore": "ignore",
"not_ignore": "not_ignore",
}
</i18n>
<script>
export default {
created () {
this.$t('foo')
this.$t('bar.nest')
}
}
</script>`,
options: [{ ignores: ['ignore', 'bar.ignore'] }],
errors: [
{
message: "unused 'bar.not_ignore' key",
line: 8,
column: 9
},
{
message: "unused 'not_ignore' key",
line: 11,
column: 7
}
]
},
{
filename: 'test.vue',
code: `
<i18n locale="en">
{
"foo": "foo",
"bar": {
"nest": "nest",
"ptn_foo": "ignore",
"no_hit_pattern_foo": "not_ignore"
},
"ptn_bar": "ignore",
"no_hit_pattern_bar": "not_ignore"
}
</i18n>
<script>
export default {
created () {
this.$t('foo')
this.$t('bar.nest')
}
}
</script>`,
options: [{ ignores: ['/ptn/'] }],
errors: [
{
message: "unused 'bar.no_hit_pattern_foo' key",
line: 8,
column: 9
},
{
message: "unused 'no_hit_pattern_bar' key",
line: 11,
column: 7
}
]
},
...getTestCasesFromFixtures(
{
cwd: join(cwdRoot, './valid')
Expand Down