148 lines
7.1 KiB
JavaScript
148 lines
7.1 KiB
JavaScript
|
/**
|
||
|
* @author Toru Nagashima
|
||
|
* @copyright 2017 Toru Nagashima. All rights reserved.
|
||
|
* See LICENSE file in root directory for full license.
|
||
|
*/
|
||
|
'use strict'
|
||
|
|
||
|
// ------------------------------------------------------------------------------
|
||
|
// Requirements
|
||
|
// ------------------------------------------------------------------------------
|
||
|
|
||
|
const utils = require('../utils')
|
||
|
|
||
|
// ------------------------------------------------------------------------------
|
||
|
// Helpers
|
||
|
// ------------------------------------------------------------------------------
|
||
|
|
||
|
const VALID_MODIFIERS = new Set([
|
||
|
'stop', 'prevent', 'capture', 'self', 'ctrl', 'shift', 'alt', 'meta',
|
||
|
'native', 'once', 'left', 'right', 'middle', 'passive', 'esc', 'tab',
|
||
|
'enter', 'space', 'up', 'left', 'right', 'down', 'delete', 'exact'
|
||
|
])
|
||
|
const VERB_MODIFIERS = new Set([
|
||
|
'stop', 'prevent'
|
||
|
])
|
||
|
// https://www.w3.org/TR/uievents-key/
|
||
|
const KEY_ALIASES = new Set([
|
||
|
'unidentified', 'alt', 'alt-graph', 'caps-lock', 'control', 'fn', 'fn-lock',
|
||
|
'meta', 'num-lock', 'scroll-lock', 'shift', 'symbol', 'symbol-lock', 'hyper',
|
||
|
'super', 'enter', 'tab', 'arrow-down', 'arrow-left', 'arrow-right',
|
||
|
'arrow-up', 'end', 'home', 'page-down', 'page-up', 'backspace', 'clear',
|
||
|
'copy', 'cr-sel', 'cut', 'delete', 'erase-eof', 'ex-sel', 'insert', 'paste',
|
||
|
'redo', 'undo', 'accept', 'again', 'attn', 'cancel', 'context-menu', 'escape',
|
||
|
'execute', 'find', 'help', 'pause', 'select', 'zoom-in', 'zoom-out',
|
||
|
'brightness-down', 'brightness-up', 'eject', 'log-off', 'power',
|
||
|
'print-screen', 'hibernate', 'standby', 'wake-up', 'all-candidates',
|
||
|
'alphanumeric', 'code-input', 'compose', 'convert', 'dead', 'final-mode',
|
||
|
'group-first', 'group-last', 'group-next', 'group-previous', 'mode-change',
|
||
|
'next-candidate', 'non-convert', 'previous-candidate', 'process',
|
||
|
'single-candidate', 'hangul-mode', 'hanja-mode', 'junja-mode', 'eisu',
|
||
|
'hankaku', 'hiragana', 'hiragana-katakana', 'kana-mode', 'kanji-mode',
|
||
|
'katakana', 'romaji', 'zenkaku', 'zenkaku-hankaku', 'f1', 'f2', 'f3', 'f4',
|
||
|
'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12', 'soft1', 'soft2', 'soft3',
|
||
|
'soft4', 'channel-down', 'channel-up', 'close', 'mail-forward', 'mail-reply',
|
||
|
'mail-send', 'media-close', 'media-fast-forward', 'media-pause',
|
||
|
'media-play-pause', 'media-record', 'media-rewind', 'media-stop',
|
||
|
'media-track-next', 'media-track-previous', 'new', 'open', 'print', 'save',
|
||
|
'spell-check', 'key11', 'key12', 'audio-balance-left', 'audio-balance-right',
|
||
|
'audio-bass-boost-down', 'audio-bass-boost-toggle', 'audio-bass-boost-up',
|
||
|
'audio-fader-front', 'audio-fader-rear', 'audio-surround-mode-next',
|
||
|
'audio-treble-down', 'audio-treble-up', 'audio-volume-down',
|
||
|
'audio-volume-up', 'audio-volume-mute', 'microphone-toggle',
|
||
|
'microphone-volume-down', 'microphone-volume-up', 'microphone-volume-mute',
|
||
|
'speech-correction-list', 'speech-input-toggle', 'launch-application1',
|
||
|
'launch-application2', 'launch-calendar', 'launch-contacts', 'launch-mail',
|
||
|
'launch-media-player', 'launch-music-player', 'launch-phone',
|
||
|
'launch-screen-saver', 'launch-spreadsheet', 'launch-web-browser',
|
||
|
'launch-web-cam', 'launch-word-processor', 'browser-back',
|
||
|
'browser-favorites', 'browser-forward', 'browser-home', 'browser-refresh',
|
||
|
'browser-search', 'browser-stop', 'app-switch', 'call', 'camera',
|
||
|
'camera-focus', 'end-call', 'go-back', 'go-home', 'headset-hook',
|
||
|
'last-number-redial', 'notification', 'manner-mode', 'voice-dial', 't-v',
|
||
|
't-v3-d-mode', 't-v-antenna-cable', 't-v-audio-description',
|
||
|
't-v-audio-description-mix-down', 't-v-audio-description-mix-up',
|
||
|
't-v-contents-menu', 't-v-data-service', 't-v-input', 't-v-input-component1',
|
||
|
't-v-input-component2', 't-v-input-composite1', 't-v-input-composite2',
|
||
|
't-v-input-h-d-m-i1', 't-v-input-h-d-m-i2', 't-v-input-h-d-m-i3',
|
||
|
't-v-input-h-d-m-i4', 't-v-input-v-g-a1', 't-v-media-context', 't-v-network',
|
||
|
't-v-number-entry', 't-v-power', 't-v-radio-service', 't-v-satellite',
|
||
|
't-v-satellite-b-s', 't-v-satellite-c-s', 't-v-satellite-toggle',
|
||
|
't-v-terrestrial-analog', 't-v-terrestrial-digital', 't-v-timer',
|
||
|
'a-v-r-input', 'a-v-r-power', 'color-f0-red', 'color-f1-green',
|
||
|
'color-f2-yellow', 'color-f3-blue', 'color-f4-grey', 'color-f5-brown',
|
||
|
'closed-caption-toggle', 'dimmer', 'display-swap', 'd-v-r', 'exit',
|
||
|
'favorite-clear0', 'favorite-clear1', 'favorite-clear2', 'favorite-clear3',
|
||
|
'favorite-recall0', 'favorite-recall1', 'favorite-recall2',
|
||
|
'favorite-recall3', 'favorite-store0', 'favorite-store1', 'favorite-store2',
|
||
|
'favorite-store3', 'guide', 'guide-next-day', 'guide-previous-day', 'info',
|
||
|
'instant-replay', 'link', 'list-program', 'live-content', 'lock',
|
||
|
'media-apps', 'media-last', 'media-skip-backward', 'media-skip-forward',
|
||
|
'media-step-backward', 'media-step-forward', 'media-top-menu', 'navigate-in',
|
||
|
'navigate-next', 'navigate-out', 'navigate-previous', 'next-favorite-channel',
|
||
|
'next-user-profile', 'on-demand', 'pairing', 'pin-p-down', 'pin-p-move',
|
||
|
'pin-p-toggle', 'pin-p-up', 'play-speed-down', 'play-speed-reset',
|
||
|
'play-speed-up', 'random-toggle', 'rc-low-battery', 'record-speed-next',
|
||
|
'rf-bypass', 'scan-channels-toggle', 'screen-mode-next', 'settings',
|
||
|
'split-screen-toggle', 's-t-b-input', 's-t-b-power', 'subtitle', 'teletext',
|
||
|
'video-mode-next', 'wink', 'zoom-toggle', 'audio-volume-down',
|
||
|
'audio-volume-up', 'audio-volume-mute', 'browser-back', 'browser-forward',
|
||
|
'channel-down', 'channel-up', 'context-menu', 'eject', 'end', 'enter', 'home',
|
||
|
'media-fast-forward', 'media-play', 'media-play-pause', 'media-record',
|
||
|
'media-rewind', 'media-stop', 'media-next-track', 'media-pause',
|
||
|
'media-previous-track', 'power', 'unidentified'
|
||
|
])
|
||
|
|
||
|
function isValidModifier (modifier) {
|
||
|
return (
|
||
|
// built-in aliases
|
||
|
VALID_MODIFIERS.has(modifier) ||
|
||
|
// keyCode
|
||
|
Number.isInteger(parseInt(modifier, 10)) ||
|
||
|
// keyAlias (an Unicode character)
|
||
|
Array.from(modifier).length === 1 ||
|
||
|
// keyAlias (special keys)
|
||
|
KEY_ALIASES.has(modifier)
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// ------------------------------------------------------------------------------
|
||
|
// Rule Definition
|
||
|
// ------------------------------------------------------------------------------
|
||
|
|
||
|
module.exports = {
|
||
|
meta: {
|
||
|
docs: {
|
||
|
description: 'enforce valid `v-on` directives',
|
||
|
category: 'essential',
|
||
|
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.7.1/docs/rules/valid-v-on.md'
|
||
|
},
|
||
|
fixable: null,
|
||
|
schema: []
|
||
|
},
|
||
|
|
||
|
create (context) {
|
||
|
return utils.defineTemplateBodyVisitor(context, {
|
||
|
"VAttribute[directive=true][key.name='on']" (node) {
|
||
|
for (const modifier of node.key.modifiers) {
|
||
|
if (!isValidModifier(modifier)) {
|
||
|
context.report({
|
||
|
node,
|
||
|
loc: node.loc,
|
||
|
message: "'v-on' directives don't support the modifier '{{modifier}}'.",
|
||
|
data: { modifier }
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
if (!utils.hasAttributeValue(node) && !node.key.modifiers.some(VERB_MODIFIERS.has, VERB_MODIFIERS)) {
|
||
|
context.report({
|
||
|
node,
|
||
|
loc: node.loc,
|
||
|
message: "'v-on' directives require that attribute value or verb modifiers."
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|