Skip to content

Commit b94645f

Browse files
committed
Update vue/return-in-emits-validator rule to support <script setup>
1 parent edd2248 commit b94645f

File tree

7 files changed

+343
-116
lines changed

7 files changed

+343
-116
lines changed

Diff for: lib/rules/return-in-emits-validator.js

+29-17
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const utils = require('../utils')
99
/**
1010
* @typedef {import('../utils').ComponentArrayEmit} ComponentArrayEmit
1111
* @typedef {import('../utils').ComponentObjectEmit} ComponentObjectEmit
12+
* @typedef {import('../utils').ComponentTypeEmit} ComponentTypeEmit
1213
*/
1314

1415
/**
@@ -67,25 +68,36 @@ module.exports = {
6768
*/
6869
let scopeStack = null
6970

70-
return Object.assign(
71-
{},
71+
/**
72+
* @param {(ComponentArrayEmit | ComponentObjectEmit | ComponentTypeEmit)[]} emits
73+
*/
74+
function processEmits(emits) {
75+
for (const emit of emits) {
76+
if (!emit.value) {
77+
continue
78+
}
79+
if (
80+
emit.value.type !== 'FunctionExpression' &&
81+
emit.value.type !== 'ArrowFunctionExpression'
82+
) {
83+
continue
84+
}
85+
emitsValidators.push(emit)
86+
}
87+
}
88+
return utils.compositingVisitors(
89+
utils.defineScriptSetupVisitor(context, {
90+
onDefineEmitsEnter(_node, emits) {
91+
processEmits(emits)
92+
}
93+
}),
7294
utils.defineVueVisitor(context, {
7395
/** @param {ObjectExpression} obj */
7496
onVueObjectEnter(obj) {
75-
for (const emits of utils.getComponentEmits(obj)) {
76-
if (!emits.value) {
77-
continue
78-
}
79-
const emitsValue = emits.value
80-
if (
81-
emitsValue.type !== 'FunctionExpression' &&
82-
emitsValue.type !== 'ArrowFunctionExpression'
83-
) {
84-
continue
85-
}
86-
emitsValidators.push(emits)
87-
}
88-
},
97+
processEmits(utils.getComponentEmits(obj))
98+
}
99+
}),
100+
{
89101
/** @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node */
90102
':function'(node) {
91103
scopeStack = {
@@ -135,7 +147,7 @@ module.exports = {
135147

136148
scopeStack = scopeStack && scopeStack.upper
137149
}
138-
})
150+
}
139151
)
140152
}
141153
}

Diff for: lib/utils/index.js

+110-97
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,9 @@
1515
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentArrayProp} ComponentArrayProp
1616
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentObjectProp} ComponentObjectProp
1717
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeProp} ComponentTypeProp
18-
*/
19-
/**
20-
* @typedef {object} ComponentArrayEmitDetectName
21-
* @property {'array'} type
22-
* @property {Literal | TemplateLiteral} key
23-
* @property {string} emitName
24-
* @property {null} value
25-
* @property {Expression | SpreadElement} node
26-
*
27-
* @typedef {object} ComponentArrayEmitUnknownName
28-
* @property {'array'} type
29-
* @property {null} key
30-
* @property {null} emitName
31-
* @property {null} value
32-
* @property {Expression | SpreadElement} node
33-
*
34-
* @typedef {ComponentArrayEmitDetectName | ComponentArrayEmitUnknownName} ComponentArrayEmit
35-
*
36-
* @typedef {object} ComponentObjectEmitDetectName
37-
* @property {'object'} type
38-
* @property {Expression} key
39-
* @property {string} emitName
40-
* @property {Expression} value
41-
* @property {Property} node
42-
*
43-
* @typedef {object} ComponentObjectEmitUnknownName
44-
* @property {'object'} type
45-
* @property {null} key
46-
* @property {null} emitName
47-
* @property {Expression} value
48-
* @property {Property} node
49-
*
50-
* @typedef {ComponentObjectEmitDetectName | ComponentObjectEmitUnknownName} ComponentObjectEmit
18+
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentArrayEmit} ComponentArrayEmit
19+
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentObjectEmit} ComponentObjectEmit
20+
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeEmit} ComponentTypeEmit
5121
*/
5222
/**
5323
* @typedef { {key: string | null, value: BlockStatement | null} } ComponentComputedProperty
@@ -82,7 +52,10 @@ const path = require('path')
8252
const vueEslintParser = require('vue-eslint-parser')
8353
const traverseNodes = vueEslintParser.AST.traverseNodes
8454
const { findVariable } = require('eslint-utils')
85-
const { getComponentPropsFromTypeDefine } = require('./ts-ast-utils')
55+
const {
56+
getComponentPropsFromTypeDefine,
57+
getComponentEmitsFromTypeDefine
58+
} = require('./ts-ast-utils')
8659

8760
/**
8861
* @type { WeakMap<RuleContext, Token[]> }
@@ -769,49 +742,7 @@ module.exports = {
769742
return []
770743
}
771744

772-
if (emitsNode.value.type === 'ObjectExpression') {
773-
return emitsNode.value.properties.filter(isProperty).map((prop) => {
774-
const emitName = getStaticPropertyName(prop)
775-
if (emitName != null) {
776-
return {
777-
type: 'object',
778-
key: prop.key,
779-
emitName,
780-
value: skipTSAsExpression(prop.value),
781-
node: prop
782-
}
783-
}
784-
return {
785-
type: 'object',
786-
key: null,
787-
emitName: null,
788-
value: skipTSAsExpression(prop.value),
789-
node: prop
790-
}
791-
})
792-
} else {
793-
return emitsNode.value.elements.filter(isDef).map((prop) => {
794-
if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') {
795-
const emitName = getStringLiteralValue(prop)
796-
if (emitName != null) {
797-
return {
798-
type: 'array',
799-
key: prop,
800-
emitName,
801-
value: null,
802-
node: prop
803-
}
804-
}
805-
}
806-
return {
807-
type: 'array',
808-
key: null,
809-
emitName: null,
810-
value: null,
811-
node: prop
812-
}
813-
})
814-
}
745+
return getComponentEmitsFromDefine(emitsNode.value)
815746
},
816747

817748
/**
@@ -1051,6 +982,8 @@ module.exports = {
1051982
*
1052983
* - `onDefinePropsEnter` ... Event when defineProps is found.
1053984
* - `onDefinePropsExit` ... Event when defineProps visit ends.
985+
* - `onDefineEmitsEnter` ... Event when defineEmits is found.
986+
* - `onDefineEmitsExit` ... Event when defineEmits visit ends.
1054987
*
1055988
* @param {RuleContext} context The ESLint rule context object.
1056989
* @param {ScriptSetupVisitor} visitor The visitor to traverse the AST nodes.
@@ -1120,9 +1053,11 @@ module.exports = {
11201053
return null
11211054
}
11221055

1123-
if (visitor.onDefinePropsEnter || visitor.onDefinePropsExit) {
1124-
const definePropsMap = new Map()
1125-
1056+
const hasPropsEvent =
1057+
visitor.onDefinePropsEnter || visitor.onDefinePropsExit
1058+
const hasEmitsEvent =
1059+
visitor.onDefineEmitsEnter || visitor.onDefineEmitsExit
1060+
if (hasPropsEvent || hasEmitsEvent) {
11261061
/** @type {ESNode | null} */
11271062
let nested = null
11281063
scriptSetupVisitor[':function, BlockStatement'] = (node) => {
@@ -1135,33 +1070,56 @@ module.exports = {
11351070
nested = null
11361071
}
11371072
}
1073+
const definePropsMap = new Map()
1074+
const defineEmitsMap = new Map()
11381075
/**
11391076
* @param {CallExpression} node
11401077
*/
11411078
scriptSetupVisitor.CallExpression = (node) => {
11421079
if (
11431080
!nested &&
11441081
inScriptSetup(node) &&
1145-
node.callee.type === 'Identifier' &&
1146-
node.callee.name === 'defineProps'
1082+
node.callee.type === 'Identifier'
11471083
) {
1148-
/** @type {(ComponentArrayProp | ComponentObjectProp | ComponentTypeProp)[]} */
1149-
let props = []
1150-
if (node.arguments.length >= 1) {
1151-
const defNode = getObjectOrArray(node.arguments[0])
1152-
if (defNode) {
1153-
props = getComponentPropsFromDefine(defNode)
1084+
if (hasPropsEvent && node.callee.name === 'defineProps') {
1085+
/** @type {(ComponentArrayProp | ComponentObjectProp | ComponentTypeProp)[]} */
1086+
let props = []
1087+
if (node.arguments.length >= 1) {
1088+
const defNode = getObjectOrArray(node.arguments[0])
1089+
if (defNode) {
1090+
props = getComponentPropsFromDefine(defNode)
1091+
}
1092+
} else if (
1093+
node.typeParameters &&
1094+
node.typeParameters.params.length >= 1
1095+
) {
1096+
props = getComponentPropsFromTypeDefine(
1097+
context,
1098+
node.typeParameters.params[0]
1099+
)
11541100
}
1155-
} else if (
1156-
node.typeParameters &&
1157-
node.typeParameters.params.length >= 1
1158-
) {
1159-
props = getComponentPropsFromTypeDefine(
1160-
context,
1161-
node.typeParameters.params[0]
1162-
)
1101+
callVisitor('onDefinePropsEnter', node, props)
1102+
definePropsMap.set(node, props)
1103+
} else if (hasEmitsEvent && node.callee.name === 'defineEmits') {
1104+
/** @type {(ComponentArrayEmit | ComponentObjectEmit | ComponentTypeEmit)[]} */
1105+
let emits = []
1106+
if (node.arguments.length >= 1) {
1107+
const defNode = getObjectOrArray(node.arguments[0])
1108+
if (defNode) {
1109+
emits = getComponentEmitsFromDefine(defNode)
1110+
}
1111+
} else if (
1112+
node.typeParameters &&
1113+
node.typeParameters.params.length >= 1
1114+
) {
1115+
emits = getComponentEmitsFromTypeDefine(
1116+
context,
1117+
node.typeParameters.params[0]
1118+
)
1119+
}
1120+
callVisitor('onDefineEmitsEnter', node, emits)
1121+
defineEmitsMap.set(node, emits)
11631122
}
1164-
callVisitor('onDefinePropsEnter', node, props)
11651123
}
11661124
callVisitor('CallExpression', node)
11671125
}
@@ -1171,6 +1129,10 @@ module.exports = {
11711129
callVisitor('onDefinePropsExit', node, definePropsMap.get(node))
11721130
definePropsMap.delete(node)
11731131
}
1132+
if (defineEmitsMap.has(node)) {
1133+
callVisitor('onDefineEmitsExit', node, defineEmitsMap.get(node))
1134+
defineEmitsMap.delete(node)
1135+
}
11741136
}
11751137
}
11761138

@@ -2472,3 +2434,54 @@ function getComponentPropsFromDefine(propsNode) {
24722434
})
24732435
}
24742436
}
2437+
2438+
/**
2439+
* Get all emits by looking at all component's properties
2440+
* @param {ObjectExpression|ArrayExpression} emitsNode Object with emits definition
2441+
* @return {(ComponentArrayEmit | ComponentObjectEmit)[]} Array of component emits
2442+
*/
2443+
function getComponentEmitsFromDefine(emitsNode) {
2444+
if (emitsNode.type === 'ObjectExpression') {
2445+
return emitsNode.properties.filter(isProperty).map((prop) => {
2446+
const emitName = getStaticPropertyName(prop)
2447+
if (emitName != null) {
2448+
return {
2449+
type: 'object',
2450+
key: prop.key,
2451+
emitName,
2452+
value: skipTSAsExpression(prop.value),
2453+
node: prop
2454+
}
2455+
}
2456+
return {
2457+
type: 'object',
2458+
key: null,
2459+
emitName: null,
2460+
value: skipTSAsExpression(prop.value),
2461+
node: prop
2462+
}
2463+
})
2464+
} else {
2465+
return emitsNode.elements.filter(isDef).map((emit) => {
2466+
if (emit.type === 'Literal' || emit.type === 'TemplateLiteral') {
2467+
const emitName = getStringLiteralValue(emit)
2468+
if (emitName != null) {
2469+
return {
2470+
type: 'array',
2471+
key: emit,
2472+
emitName,
2473+
value: null,
2474+
node: emit
2475+
}
2476+
}
2477+
}
2478+
return {
2479+
type: 'array',
2480+
key: null,
2481+
emitName: null,
2482+
value: null,
2483+
node: emit
2484+
}
2485+
})
2486+
}
2487+
}

0 commit comments

Comments
 (0)