| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 | 
							- /**
 
-  * @fileoverview enforce usage of `exact` modifier on `v-on`.
 
-  * @author Armano
 
-  */
 
- 'use strict'
 
- // ------------------------------------------------------------------------------
 
- // Requirements
 
- // ------------------------------------------------------------------------------
 
- /**
 
-  * @typedef { {name: string, node: VDirectiveKey, modifiers: string[] } } EventDirective
 
-  */
 
- const utils = require('../utils')
 
- const SYSTEM_MODIFIERS = new Set(['ctrl', 'shift', 'alt', 'meta'])
 
- const GLOBAL_MODIFIERS = new Set([
 
-   'stop',
 
-   'prevent',
 
-   'capture',
 
-   'self',
 
-   'once',
 
-   'passive',
 
-   'native'
 
- ])
 
- // ------------------------------------------------------------------------------
 
- // Helpers
 
- // ------------------------------------------------------------------------------
 
- /**
 
-  * Finds and returns all keys for event directives
 
-  *
 
-  * @param {VStartTag} startTag Element startTag
 
-  * @param {SourceCode} sourceCode The source code object.
 
-  * @returns {EventDirective[]} [{ name, node, modifiers }]
 
-  */
 
- function getEventDirectives(startTag, sourceCode) {
 
-   return utils.getDirectives(startTag, 'on').map((attribute) => ({
 
-     name: attribute.key.argument
 
-       ? sourceCode.getText(attribute.key.argument)
 
-       : '',
 
-     node: attribute.key,
 
-     modifiers: attribute.key.modifiers.map((modifier) => modifier.name)
 
-   }))
 
- }
 
- /**
 
-  * Checks whether given modifier is key modifier
 
-  *
 
-  * @param {string} modifier
 
-  * @returns {boolean}
 
-  */
 
- function isKeyModifier(modifier) {
 
-   return !GLOBAL_MODIFIERS.has(modifier) && !SYSTEM_MODIFIERS.has(modifier)
 
- }
 
- /**
 
-  * Checks whether given modifier is system one
 
-  *
 
-  * @param {string} modifier
 
-  * @returns {boolean}
 
-  */
 
- function isSystemModifier(modifier) {
 
-   return SYSTEM_MODIFIERS.has(modifier)
 
- }
 
- /**
 
-  * Checks whether given any of provided modifiers
 
-  * has system modifier
 
-  *
 
-  * @param {string[]} modifiers
 
-  * @returns {boolean}
 
-  */
 
- function hasSystemModifier(modifiers) {
 
-   return modifiers.some(isSystemModifier)
 
- }
 
- /**
 
-  * Groups all events in object,
 
-  * with keys represinting each event name
 
-  *
 
-  * @param {EventDirective[]} events
 
-  * @returns { { [key: string]: EventDirective[]  } } { click: [], keypress: [] }
 
-  */
 
- function groupEvents(events) {
 
-   return events.reduce((acc, event) => {
 
-     if (acc[event.name]) {
 
-       acc[event.name].push(event)
 
-     } else {
 
-       acc[event.name] = [event]
 
-     }
 
-     return acc
 
-   }, /** @type { { [key: string]: EventDirective[] } }*/ ({}))
 
- }
 
- /**
 
-  * Creates alphabetically sorted string with system modifiers
 
-  *
 
-  * @param {string[]} modifiers
 
-  * @returns {string} e.g. "alt,ctrl,del,shift"
 
-  */
 
- function getSystemModifiersString(modifiers) {
 
-   return modifiers.filter(isSystemModifier).sort().join(',')
 
- }
 
- /**
 
-  * Creates alphabetically sorted string with key modifiers
 
-  *
 
-  * @param {string[]} modifiers
 
-  * @returns {string} e.g. "enter,tab"
 
-  */
 
- function getKeyModifiersString(modifiers) {
 
-   return modifiers.filter(isKeyModifier).sort().join(',')
 
- }
 
- /**
 
-  * Compares two events based on their modifiers
 
-  * to detect possible event leakeage
 
-  *
 
-  * @param {EventDirective} baseEvent
 
-  * @param {EventDirective} event
 
-  * @returns {boolean}
 
-  */
 
- function hasConflictedModifiers(baseEvent, event) {
 
-   if (event.node === baseEvent.node || event.modifiers.includes('exact'))
 
-     return false
 
-   const eventKeyModifiers = getKeyModifiersString(event.modifiers)
 
-   const baseEventKeyModifiers = getKeyModifiersString(baseEvent.modifiers)
 
-   if (
 
-     eventKeyModifiers &&
 
-     baseEventKeyModifiers &&
 
-     eventKeyModifiers !== baseEventKeyModifiers
 
-   )
 
-     return false
 
-   const eventSystemModifiers = getSystemModifiersString(event.modifiers)
 
-   const baseEventSystemModifiers = getSystemModifiersString(baseEvent.modifiers)
 
-   return (
 
-     baseEvent.modifiers.length >= 1 &&
 
-     baseEventSystemModifiers !== eventSystemModifiers &&
 
-     baseEventSystemModifiers.indexOf(eventSystemModifiers) > -1
 
-   )
 
- }
 
- /**
 
-  * Searches for events that might conflict with each other
 
-  *
 
-  * @param {EventDirective[]} events
 
-  * @returns {EventDirective[]} conflicted events, without duplicates
 
-  */
 
- function findConflictedEvents(events) {
 
-   return events.reduce((acc, event) => {
 
-     return [
 
-       ...acc,
 
-       ...events
 
-         .filter((evt) => !acc.find((e) => evt === e)) // No duplicates
 
-         .filter(hasConflictedModifiers.bind(null, event))
 
-     ]
 
-   }, /** @type {EventDirective[]} */ ([]))
 
- }
 
- // ------------------------------------------------------------------------------
 
- // Rule details
 
- // ------------------------------------------------------------------------------
 
- module.exports = {
 
-   meta: {
 
-     type: 'suggestion',
 
-     docs: {
 
-       description: 'enforce usage of `exact` modifier on `v-on`',
 
-       categories: ['vue3-essential', 'essential'],
 
-       url: 'https://eslint.vuejs.org/rules/use-v-on-exact.html'
 
-     },
 
-     fixable: null,
 
-     schema: []
 
-   },
 
-   /**
 
-    * Creates AST event handlers for use-v-on-exact.
 
-    *
 
-    * @param {RuleContext} context - The rule context.
 
-    * @returns {Object} AST event handlers.
 
-    */
 
-   create(context) {
 
-     const sourceCode = context.getSourceCode()
 
-     return utils.defineTemplateBodyVisitor(context, {
 
-       /** @param {VStartTag} node */
 
-       VStartTag(node) {
 
-         if (node.attributes.length === 0) return
 
-         const isCustomComponent = utils.isCustomComponent(node.parent)
 
-         let events = getEventDirectives(node, sourceCode)
 
-         if (isCustomComponent) {
 
-           // For components consider only events with `native` modifier
 
-           events = events.filter((event) => event.modifiers.includes('native'))
 
-         }
 
-         const grouppedEvents = groupEvents(events)
 
-         Object.keys(grouppedEvents).forEach((eventName) => {
 
-           const eventsInGroup = grouppedEvents[eventName]
 
-           const hasEventWithKeyModifier = eventsInGroup.some((event) =>
 
-             hasSystemModifier(event.modifiers)
 
-           )
 
-           if (!hasEventWithKeyModifier) return
 
-           const conflictedEvents = findConflictedEvents(eventsInGroup)
 
-           conflictedEvents.forEach((e) => {
 
-             context.report({
 
-               node: e.node,
 
-               loc: e.node.loc,
 
-               message: "Consider to use '.exact' modifier."
 
-             })
 
-           })
 
-         })
 
-       }
 
-     })
 
-   }
 
- }
 
 
  |