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,34 +1053,57 @@ module.exports = {
11201053 return null
11211054 }
11221055
1123- if ( visitor . onDefinePropsEnter || visitor . onDefinePropsExit ) {
1056+ const hasPropsEvent =
1057+ visitor . onDefinePropsEnter || visitor . onDefinePropsExit
1058+ const hasEmitsEvent =
1059+ visitor . onDefineEmitsEnter || visitor . onDefineEmitsExit
1060+ if ( hasPropsEvent || hasEmitsEvent ) {
11241061 const definePropsMap = new Map ( )
1062+ const defineEmitsMap = new Map ( )
11251063 /**
11261064 * @param {CallExpression } node
11271065 */
11281066 scriptSetupVisitor . CallExpression = ( node ) => {
1129- if (
1130- inScriptSetup ( node ) &&
1131- node . callee . type === 'Identifier' &&
1132- node . callee . name === 'defineProps'
1133- ) {
1134- /** @type {(ComponentArrayProp | ComponentObjectProp | ComponentTypeProp)[] } */
1135- let props = [ ]
1136- if ( node . arguments . length >= 1 ) {
1137- const defNode = getObjectOrArray ( node . arguments [ 0 ] )
1138- if ( defNode ) {
1139- props = getComponentPropsFromDefine ( defNode )
1067+ if ( inScriptSetup ( node ) && node . callee . type === 'Identifier' ) {
1068+ if ( hasPropsEvent && node . callee . name === 'defineProps' ) {
1069+ /** @type {(ComponentArrayProp | ComponentObjectProp | ComponentTypeProp)[] } */
1070+ let props = [ ]
1071+ if ( node . arguments . length >= 1 ) {
1072+ const defNode = getObjectOrArray ( node . arguments [ 0 ] )
1073+ if ( defNode ) {
1074+ props = getComponentPropsFromDefine ( defNode )
1075+ }
1076+ } else if (
1077+ node . typeParameters &&
1078+ node . typeParameters . params . length >= 1
1079+ ) {
1080+ props = getComponentPropsFromTypeDefine (
1081+ context ,
1082+ node . typeParameters . params [ 0 ]
1083+ )
11401084 }
1141- } else if (
1142- node . typeParameters &&
1143- node . typeParameters . params . length >= 1
1144- ) {
1145- props = getComponentPropsFromTypeDefine (
1146- context ,
1147- node . typeParameters . params [ 0 ]
1148- )
1085+ callVisitor ( 'onDefinePropsEnter' , node , props )
1086+ definePropsMap . set ( node , props )
1087+ } else if ( hasEmitsEvent ) {
1088+ /** @type {(ComponentArrayEmit | ComponentObjectEmit | ComponentTypeEmit)[] } */
1089+ let emits = [ ]
1090+ if ( node . arguments . length >= 1 ) {
1091+ const defNode = getObjectOrArray ( node . arguments [ 0 ] )
1092+ if ( defNode ) {
1093+ emits = getComponentEmitsFromDefine ( defNode )
1094+ }
1095+ } else if (
1096+ node . typeParameters &&
1097+ node . typeParameters . params . length >= 1
1098+ ) {
1099+ emits = getComponentEmitsFromTypeDefine (
1100+ context ,
1101+ node . typeParameters . params [ 0 ]
1102+ )
1103+ }
1104+ callVisitor ( 'onDefineEmitsEnter' , node , emits )
1105+ defineEmitsMap . set ( node , emits )
11491106 }
1150- callVisitor ( 'onDefinePropsEnter' , node , props )
11511107 }
11521108 callVisitor ( 'CallExpression' , node )
11531109 }
@@ -1157,6 +1113,10 @@ module.exports = {
11571113 callVisitor ( 'onDefinePropsExit' , node , definePropsMap . get ( node ) )
11581114 definePropsMap . delete ( node )
11591115 }
1116+ if ( defineEmitsMap . has ( node ) ) {
1117+ callVisitor ( 'onDefineEmitsExit' , node , defineEmitsMap . get ( node ) )
1118+ defineEmitsMap . delete ( node )
1119+ }
11601120 }
11611121 }
11621122
@@ -2422,3 +2382,54 @@ function getComponentPropsFromDefine(propsNode) {
24222382 } )
24232383 }
24242384}
2385+
2386+ /**
2387+ * Get all emits by looking at all component's properties
2388+ * @param {ObjectExpression|ArrayExpression } emitsNode Object with emits definition
2389+ * @return {(ComponentArrayEmit | ComponentObjectEmit)[] } Array of component emits
2390+ */
2391+ function getComponentEmitsFromDefine ( emitsNode ) {
2392+ if ( emitsNode . type === 'ObjectExpression' ) {
2393+ return emitsNode . properties . filter ( isProperty ) . map ( ( prop ) => {
2394+ const emitName = getStaticPropertyName ( prop )
2395+ if ( emitName != null ) {
2396+ return {
2397+ type : 'object' ,
2398+ key : prop . key ,
2399+ emitName,
2400+ value : skipTSAsExpression ( prop . value ) ,
2401+ node : prop
2402+ }
2403+ }
2404+ return {
2405+ type : 'object' ,
2406+ key : null ,
2407+ emitName : null ,
2408+ value : skipTSAsExpression ( prop . value ) ,
2409+ node : prop
2410+ }
2411+ } )
2412+ } else {
2413+ return emitsNode . elements . filter ( isDef ) . map ( ( emit ) => {
2414+ if ( emit . type === 'Literal' || emit . type === 'TemplateLiteral' ) {
2415+ const emitName = getStringLiteralValue ( emit )
2416+ if ( emitName != null ) {
2417+ return {
2418+ type : 'array' ,
2419+ key : emit ,
2420+ emitName,
2421+ value : null ,
2422+ node : emit
2423+ }
2424+ }
2425+ }
2426+ return {
2427+ type : 'array' ,
2428+ key : null ,
2429+ emitName : null ,
2430+ value : null ,
2431+ node : emit
2432+ }
2433+ } )
2434+ }
2435+ }
0 commit comments