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')
8252const vueEslintParser = require ( 'vue-eslint-parser' )
8353const traverseNodes = vueEslintParser . AST . traverseNodes
8454const { 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