Skip to content

Commit 9b43c9f

Browse files
authored
Update vue/custom-event-name-casing rule to support <script setup> (#1549)
1 parent 7a03a40 commit 9b43c9f

File tree

2 files changed

+92
-32
lines changed

2 files changed

+92
-32
lines changed

Diff for: lib/rules/custom-event-name-casing.js

+76-32
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ const utils = require('../utils')
1313
const casing = require('../utils/casing')
1414
const { toRegExp } = require('../utils/regexp')
1515

16+
/**
17+
* @typedef {import('../utils').VueObjectData} VueObjectData
18+
*/
19+
1620
// ------------------------------------------------------------------------------
1721
// Helpers
1822
// ------------------------------------------------------------------------------
@@ -103,7 +107,7 @@ module.exports = {
103107
},
104108
/** @param {RuleContext} context */
105109
create(context) {
106-
/** @type {Map<ObjectExpression, {contextReferenceIds:Set<Identifier>,emitReferenceIds:Set<Identifier>}>} */
110+
/** @type {Map<ObjectExpression|Program, {contextReferenceIds:Set<Identifier>,emitReferenceIds:Set<Identifier>}>} */
107111
const setupContexts = new Map()
108112
const options =
109113
context.options.length === 1 && typeof context.options[0] !== 'string'
@@ -143,6 +147,46 @@ module.exports = {
143147
})
144148
}
145149

150+
const programNode = context.getSourceCode().ast
151+
152+
const callVisitor = {
153+
/**
154+
* @param {CallExpression} node
155+
* @param {VueObjectData} [info]
156+
*/
157+
CallExpression(node, info) {
158+
const nameLiteralNode = getNameParamNode(node)
159+
if (!nameLiteralNode) {
160+
// cannot check
161+
return
162+
}
163+
164+
// verify setup context
165+
const setupContext = setupContexts.get(info ? info.node : programNode)
166+
if (setupContext) {
167+
const { contextReferenceIds, emitReferenceIds } = setupContext
168+
if (
169+
node.callee.type === 'Identifier' &&
170+
emitReferenceIds.has(node.callee)
171+
) {
172+
// verify setup(props,{emit}) {emit()}
173+
verify(nameLiteralNode)
174+
} else {
175+
const emit = getCalleeMemberNode(node)
176+
if (
177+
emit &&
178+
emit.name === 'emit' &&
179+
emit.member.object.type === 'Identifier' &&
180+
contextReferenceIds.has(emit.member.object)
181+
) {
182+
// verify setup(props,context) {context.emit()}
183+
verify(nameLiteralNode)
184+
}
185+
}
186+
}
187+
}
188+
}
189+
146190
return utils.defineTemplateBodyVisitor(
147191
context,
148192
{
@@ -159,6 +203,36 @@ module.exports = {
159203
}
160204
},
161205
utils.compositingVisitors(
206+
utils.defineScriptSetupVisitor(context, {
207+
onDefineEmitsEnter(node) {
208+
if (
209+
!node.parent ||
210+
node.parent.type !== 'VariableDeclarator' ||
211+
node.parent.init !== node
212+
) {
213+
return
214+
}
215+
216+
const emitParam = node.parent.id
217+
if (emitParam.type !== 'Identifier') {
218+
return
219+
}
220+
// const emit = defineEmits()
221+
const variable = findVariable(context.getScope(), emitParam)
222+
if (!variable) {
223+
return
224+
}
225+
const emitReferenceIds = new Set()
226+
for (const reference of variable.references) {
227+
emitReferenceIds.add(reference.identifier)
228+
}
229+
setupContexts.set(programNode, {
230+
contextReferenceIds: new Set(),
231+
emitReferenceIds
232+
})
233+
},
234+
...callVisitor
235+
}),
162236
utils.defineVueVisitor(context, {
163237
onSetupFunctionEnter(node, { node: vueNode }) {
164238
const contextParam = utils.skipDefaultParamValue(node.params[1])
@@ -207,37 +281,7 @@ module.exports = {
207281
emitReferenceIds
208282
})
209283
},
210-
CallExpression(node, { node: vueNode }) {
211-
const nameLiteralNode = getNameParamNode(node)
212-
if (!nameLiteralNode) {
213-
// cannot check
214-
return
215-
}
216-
217-
// verify setup context
218-
const setupContext = setupContexts.get(vueNode)
219-
if (setupContext) {
220-
const { contextReferenceIds, emitReferenceIds } = setupContext
221-
if (
222-
node.callee.type === 'Identifier' &&
223-
emitReferenceIds.has(node.callee)
224-
) {
225-
// verify setup(props,{emit}) {emit()}
226-
verify(nameLiteralNode)
227-
} else {
228-
const emit = getCalleeMemberNode(node)
229-
if (
230-
emit &&
231-
emit.name === 'emit' &&
232-
emit.member.object.type === 'Identifier' &&
233-
contextReferenceIds.has(emit.member.object)
234-
) {
235-
// verify setup(props,context) {context.emit()}
236-
verify(nameLiteralNode)
237-
}
238-
}
239-
}
240-
},
284+
...callVisitor,
241285
onVueObjectExit(node) {
242286
setupContexts.delete(node)
243287
}

Diff for: tests/lib/rules/custom-event-name-casing.js

+16
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,22 @@ tester.run('custom-event-name-casing', rule, {
478478
"Custom event name 'bar-baz' must be camelCase.",
479479
"Custom event name 'baz-qux' must be camelCase."
480480
]
481+
},
482+
{
483+
filename: 'test.vue',
484+
code: `
485+
<script setup>
486+
const emit = defineEmits({})
487+
emit('fooBar')
488+
emit('foo-bar')
489+
</script>
490+
`,
491+
errors: [
492+
{
493+
message: "Custom event name 'fooBar' must be kebab-case.",
494+
line: 4
495+
}
496+
]
481497
}
482498
]
483499
})

0 commit comments

Comments
 (0)