Skip to content

Commit 5d4734d

Browse files
committed
refactor: apply suggest
1 parent b5fe004 commit 5d4734d

File tree

3 files changed

+132
-133
lines changed

3 files changed

+132
-133
lines changed

docs/rules/no-undef-directives.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,16 @@ export default {
6464
```json
6565
{
6666
"vue/no-undef-directives": ["error", {
67-
"ignorePatterns": ["foo"]
67+
"ignore": ["foo"]
6868
}]
6969
}
7070
```
7171

72-
- `ignorePatterns` (`string[]`) ... An array of regex pattern strings to ignore.
72+
- `ignore` (`string[]`) An array of tags or regular expression patterns (e.g. `"/^custom-/"`) that ignore these rules. This option will check both kebab-case and PascalCase versions of the given tag names. Default is empty.
7373

74-
### `"ignorePatterns": ["foo"]`
74+
### `"ignore": ["foo"]`
7575

76-
<eslint-code-block :rules="{'vue/no-undef-directives': ['error', {ignorePatterns: ['foo']}]}">
76+
<eslint-code-block :rules="{'vue/no-undef-directives': ['error', {ignore: ['foo']}]}">
7777

7878
```vue
7979
<template>

lib/rules/no-undef-directives.js

Lines changed: 87 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
const utils = require('../utils')
88
const casing = require('../utils/casing')
9+
const regexp = require('../utils/regexp')
910

1011
/**
1112
* @param {string} str
@@ -44,69 +45,37 @@ function getRegisteredDirectives(componentObject) {
4445
.filter((res) => !!res)
4546
}
4647

47-
class DefinedInSetupDirectives {
48-
constructor() {
49-
/**
50-
* Directive names
51-
* @type {Set<string>}
52-
*/
53-
this.names = new Set()
54-
}
55-
56-
/**
57-
* @param {string} name
58-
*/
59-
addName(name) {
60-
this.names.add(name)
61-
}
62-
63-
/**
64-
* @param {string} rawName
65-
*/
66-
isDefinedDirective(rawName) {
67-
const camelName = camelize(rawName)
68-
const variableName = `v${casing.capitalize(camelName)}`
69-
return this.names.has(variableName)
70-
}
48+
/**
49+
* @param {string} rawName
50+
* @param {Set<string>} definedNames
51+
*/
52+
function isDefinedInSetup(rawName, definedNames) {
53+
const camelName = camelize(rawName)
54+
const variableName = `v${casing.capitalize(camelName)}`
55+
return definedNames.has(variableName)
7156
}
7257

73-
class DefinedInOptionDirectives {
74-
constructor() {
75-
/**
76-
* Directive names
77-
* @type {Set<string>}
78-
*/
79-
this.names = new Set()
80-
}
81-
82-
/**
83-
* @param {string} name
84-
*/
85-
addName(name) {
86-
this.names.add(name)
58+
/**
59+
* @param {string} rawName
60+
* @param {Set<string>} definedNames
61+
*/
62+
function isDefinedInOptions(rawName, definedNames) {
63+
const camelName = camelize(rawName)
64+
if (definedNames.has(rawName) || definedNames.has(camelName)) {
65+
return true
8766
}
8867

89-
/**
90-
* @param {string} rawName
91-
*/
92-
isDefinedDirective(rawName) {
93-
const camelName = camelize(rawName)
94-
if (this.names.has(rawName) || this.names.has(camelName)) {
68+
// allow case-insensitive only when the directive name itself contains capitalized letters
69+
for (const name of definedNames) {
70+
if (
71+
name.toLowerCase() === camelName.toLowerCase() &&
72+
name !== name.toLowerCase()
73+
) {
9574
return true
9675
}
97-
98-
// allow case-insensitive ONLY when the directive name itself contains capitalized letters
99-
for (const name of this.names) {
100-
if (
101-
name.toLowerCase() === camelName.toLowerCase() &&
102-
name !== name.toLowerCase()
103-
) {
104-
return true
105-
}
106-
}
107-
108-
return false
10976
}
77+
78+
return false
11079
}
11180

11281
module.exports = {
@@ -122,11 +91,9 @@ module.exports = {
12291
{
12392
type: 'object',
12493
properties: {
125-
ignorePatterns: {
94+
ignore: {
12695
type: 'array',
127-
items: {
128-
type: 'string'
129-
},
96+
items: { type: 'string' },
13097
uniqueItems: true
13198
}
13299
},
@@ -140,8 +107,8 @@ module.exports = {
140107
/** @param {RuleContext} context */
141108
create(context) {
142109
const options = context.options[0] || {}
143-
/** @type {string[]} */
144-
const ignorePatterns = options.ignorePatterns || []
110+
const { ignore = [] } = options
111+
const isAnyIgnored = regexp.toRegExpGroupMatcher(ignore)
145112

146113
/**
147114
* Check whether the given directive name is a verify target or not.
@@ -150,107 +117,101 @@ module.exports = {
150117
* @returns {boolean}
151118
*/
152119
function isVerifyTargetDirective(rawName) {
153-
if (utils.isBuiltInDirectiveName(rawName)) {
120+
const kebabName = casing.kebabCase(rawName)
121+
if (
122+
utils.isBuiltInDirectiveName(rawName) ||
123+
isAnyIgnored(rawName, kebabName)
124+
) {
154125
return false
155126
}
156-
157-
const ignored = ignorePatterns.some((pattern) =>
158-
new RegExp(pattern).test(rawName)
159-
)
160-
return !ignored
127+
return true
161128
}
162129

163-
/** @type { (rawName:string, reportNode: ASTNode) => void } */
164-
let verifyName
165-
/** @type {RuleListener} */
166-
let scriptVisitor = {}
167-
/** @type {TemplateListener} */
168-
const templateBodyVisitor = {
169-
/** @param {VDirective} node */
170-
'VAttribute[directive=true]'(node) {
171-
const name = node.key.name.name
172-
if (utils.isBuiltInDirectiveName(name)) {
173-
return
130+
/**
131+
* @param {(rawName: string) => boolean} isDefined
132+
* @returns {TemplateListener}
133+
*/
134+
function createTemplateBodyVisitor(isDefined) {
135+
return {
136+
/** @param {VDirective} node */
137+
'VAttribute[directive=true]'(node) {
138+
const name = node.key.name.name
139+
if (utils.isBuiltInDirectiveName(name)) {
140+
return
141+
}
142+
const rawName = node.key.name.rawName || name
143+
if (isVerifyTargetDirective(rawName) && !isDefined(rawName)) {
144+
context.report({
145+
node: node.key,
146+
messageId: 'undef',
147+
data: {
148+
name: rawName
149+
}
150+
})
151+
}
174152
}
175-
verifyName(node.key.name.rawName || name, node.key)
176153
}
177154
}
178155

179156
if (utils.isScriptSetup(context)) {
180157
// For <script setup>
181-
const definedInSetupDirectives = new DefinedInSetupDirectives()
182-
const definedInOptionDirectives = new DefinedInOptionDirectives()
158+
/** @type {Set<string>} */
159+
const definedInSetupDirectives = new Set()
160+
/** @type {Set<string>} */
161+
const definedInOptionDirectives = new Set()
183162

184163
const globalScope = context.sourceCode.scopeManager.globalScope
185164
if (globalScope) {
186165
for (const variable of globalScope.variables) {
187-
definedInSetupDirectives.addName(variable.name)
166+
definedInSetupDirectives.add(variable.name)
188167
}
189168
const moduleScope = globalScope.childScopes.find(
190169
(scope) => scope.type === 'module'
191170
)
192171
for (const variable of (moduleScope && moduleScope.variables) || []) {
193-
definedInSetupDirectives.addName(variable.name)
172+
definedInSetupDirectives.add(variable.name)
194173
}
195174
}
196175

197-
scriptVisitor = utils.defineVueVisitor(context, {
176+
const scriptVisitor = utils.defineVueVisitor(context, {
198177
onVueObjectEnter(node) {
199178
for (const directive of getRegisteredDirectives(node)) {
200-
definedInOptionDirectives.addName(directive.name)
179+
definedInOptionDirectives.add(directive.name)
201180
}
202181
}
203182
})
204183

205-
verifyName = (rawName, reportNode) => {
206-
if (
207-
!isVerifyTargetDirective(rawName) ||
208-
definedInSetupDirectives.isDefinedDirective(rawName) ||
209-
definedInOptionDirectives.isDefinedDirective(rawName)
210-
) {
211-
return
212-
}
184+
const templateBodyVisitor = createTemplateBodyVisitor(
185+
(rawName) =>
186+
isDefinedInSetup(rawName, definedInSetupDirectives) ||
187+
isDefinedInOptions(rawName, definedInOptionDirectives)
188+
)
213189

214-
context.report({
215-
node: reportNode,
216-
messageId: 'undef',
217-
data: {
218-
name: rawName
219-
}
220-
})
221-
}
190+
return utils.defineTemplateBodyVisitor(
191+
context,
192+
templateBodyVisitor,
193+
scriptVisitor
194+
)
222195
} else {
223196
// For Options API
224-
const definedInOptionDirectives = new DefinedInOptionDirectives()
197+
/** @type {Set<string>} */
198+
const definedInOptionDirectives = new Set()
225199

226-
scriptVisitor = utils.executeOnVue(context, (obj) => {
200+
const scriptVisitor = utils.executeOnVue(context, (obj) => {
227201
for (const directive of getRegisteredDirectives(obj)) {
228-
definedInOptionDirectives.addName(directive.name)
202+
definedInOptionDirectives.add(directive.name)
229203
}
230204
})
231205

232-
verifyName = (rawName, reportNode) => {
233-
if (
234-
!isVerifyTargetDirective(rawName) ||
235-
definedInOptionDirectives.isDefinedDirective(rawName)
236-
) {
237-
return
238-
}
206+
const templateBodyVisitor = createTemplateBodyVisitor((rawName) =>
207+
isDefinedInOptions(rawName, definedInOptionDirectives)
208+
)
239209

240-
context.report({
241-
node: reportNode,
242-
messageId: 'undef',
243-
data: {
244-
name: rawName
245-
}
246-
})
247-
}
210+
return utils.defineTemplateBodyVisitor(
211+
context,
212+
templateBodyVisitor,
213+
scriptVisitor
214+
)
248215
}
249-
250-
return utils.defineTemplateBodyVisitor(
251-
context,
252-
templateBodyVisitor,
253-
scriptVisitor
254-
)
255216
}
256217
}

tests/lib/rules/no-undef-directives.js

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ tester.run('no-undef-directives', rule, {
228228
<div v-barfoobaz></div>
229229
</template>
230230
`,
231-
options: [{ ignorePatterns: ['foo'] }]
231+
options: [{ ignore: ['/foo/'] }]
232232
},
233233
{
234234
filename: 'test.vue',
@@ -238,7 +238,7 @@ tester.run('no-undef-directives', rule, {
238238
<div v-foo-bar></div>
239239
</template>
240240
`,
241-
options: [{ ignorePatterns: ['foo-bar'] }]
241+
options: [{ ignore: ['foo-bar'] }]
242242
}
243243
],
244244
invalid: [
@@ -467,7 +467,7 @@ tester.run('no-undef-directives', rule, {
467467
<div v-foo-bar></div>
468468
</template>
469469
`,
470-
options: [{ ignorePatterns: ['baz'] }],
470+
options: [{ ignore: ['baz'] }],
471471
errors: [
472472
{
473473
message: "The 'v-foo-bar' directive has been used, but not defined.",
@@ -477,6 +477,44 @@ tester.run('no-undef-directives', rule, {
477477
endColumn: 23
478478
}
479479
]
480+
},
481+
{
482+
filename: 'test.vue',
483+
code: `
484+
<template>
485+
<div v-foo-bar></div>
486+
</template>
487+
`,
488+
options: [{ ignore: ['/^bar/'] }],
489+
errors: [
490+
{
491+
message: "The 'v-foo-bar' directive has been used, but not defined.",
492+
line: 3,
493+
column: 14,
494+
endLine: 3,
495+
endColumn: 23
496+
}
497+
]
498+
},
499+
{
500+
filename: 'test.vue',
501+
code: `
502+
<template>
503+
<div v-foo></div>
504+
<div v-barfoobaz></div>
505+
</template>
506+
`,
507+
options: [{ ignore: ['foo'] }],
508+
errors: [
509+
{
510+
message:
511+
"The 'v-barfoobaz' directive has been used, but not defined.",
512+
line: 4,
513+
column: 14,
514+
endLine: 4,
515+
endColumn: 25
516+
}
517+
]
480518
}
481519
]
482520
})

0 commit comments

Comments
 (0)