first commit
This commit is contained in:
294
app_vue/node_modules/eslint-plugin-vue/lib/rules/no-restricted-custom-event.js
generated
vendored
Normal file
294
app_vue/node_modules/eslint-plugin-vue/lib/rules/no-restricted-custom-event.js
generated
vendored
Normal file
@ -0,0 +1,294 @@
|
||||
/**
|
||||
* @author Yosuke Ota
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
'use strict'
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const { findVariable } = require('eslint-utils')
|
||||
const utils = require('../utils')
|
||||
const regexp = require('../utils/regexp')
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @typedef {object} ParsedOption
|
||||
* @property { (name: string) => boolean } test
|
||||
* @property {string|undefined} [message]
|
||||
* @property {string|undefined} [suggest]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {(str: string) => boolean}
|
||||
*/
|
||||
function buildMatcher(str) {
|
||||
if (regexp.isRegExp(str)) {
|
||||
const re = regexp.toRegExp(str)
|
||||
return (s) => {
|
||||
re.lastIndex = 0
|
||||
return re.test(s)
|
||||
}
|
||||
}
|
||||
return (s) => s === str
|
||||
}
|
||||
/**
|
||||
* @param {string|{event: string, message?: string, suggest?: string}} option
|
||||
* @returns {ParsedOption}
|
||||
*/
|
||||
function parseOption(option) {
|
||||
if (typeof option === 'string') {
|
||||
const matcher = buildMatcher(option)
|
||||
return {
|
||||
test(name) {
|
||||
return matcher(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
const parsed = parseOption(option.event)
|
||||
parsed.message = option.message
|
||||
parsed.suggest = option.suggest
|
||||
return parsed
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name param node from the given CallExpression
|
||||
* @param {CallExpression} node CallExpression
|
||||
* @returns { Literal & { value: string } | null }
|
||||
*/
|
||||
function getNameParamNode(node) {
|
||||
const nameLiteralNode = node.arguments[0]
|
||||
if (
|
||||
!nameLiteralNode ||
|
||||
nameLiteralNode.type !== 'Literal' ||
|
||||
typeof nameLiteralNode.value !== 'string'
|
||||
) {
|
||||
// cannot check
|
||||
return null
|
||||
}
|
||||
|
||||
return /** @type {Literal & { value: string }} */ (nameLiteralNode)
|
||||
}
|
||||
/**
|
||||
* Get the callee member node from the given CallExpression
|
||||
* @param {CallExpression} node CallExpression
|
||||
*/
|
||||
function getCalleeMemberNode(node) {
|
||||
const callee = utils.skipChainExpression(node.callee)
|
||||
|
||||
if (callee.type === 'MemberExpression') {
|
||||
const name = utils.getStaticPropertyName(callee)
|
||||
if (name) {
|
||||
return { name, member: callee }
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
module.exports = {
|
||||
meta: {
|
||||
hasSuggestions: true,
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'disallow specific custom event',
|
||||
categories: undefined,
|
||||
url: 'https://eslint.vuejs.org/rules/no-restricted-custom-event.html'
|
||||
},
|
||||
fixable: null,
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
oneOf: [
|
||||
{ type: ['string'] },
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
event: { type: 'string' },
|
||||
message: { type: 'string', minLength: 1 },
|
||||
suggest: { type: 'string' }
|
||||
},
|
||||
required: ['event'],
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
},
|
||||
uniqueItems: true,
|
||||
minItems: 0
|
||||
},
|
||||
|
||||
messages: {
|
||||
// eslint-disable-next-line eslint-plugin/report-message-format
|
||||
restrictedEvent: '{{message}}',
|
||||
instead: 'Instead, change to `{{suggest}}`.'
|
||||
}
|
||||
},
|
||||
/** @param {RuleContext} context */
|
||||
create(context) {
|
||||
/** @type {Map<ObjectExpression, {contextReferenceIds:Set<Identifier>,emitReferenceIds:Set<Identifier>}>} */
|
||||
const setupContexts = new Map()
|
||||
/** @type {ParsedOption[]} */
|
||||
const options = context.options.map(parseOption)
|
||||
|
||||
/**
|
||||
* @param { Literal & { value: string } } nameLiteralNode
|
||||
*/
|
||||
function verify(nameLiteralNode) {
|
||||
const name = nameLiteralNode.value
|
||||
|
||||
for (const option of options) {
|
||||
if (option.test(name)) {
|
||||
const message =
|
||||
option.message || `Using \`${name}\` event is not allowed.`
|
||||
context.report({
|
||||
node: nameLiteralNode,
|
||||
messageId: 'restrictedEvent',
|
||||
data: { message },
|
||||
suggest: option.suggest
|
||||
? [
|
||||
{
|
||||
fix(fixer) {
|
||||
const sourceCode = context.getSourceCode()
|
||||
return fixer.replaceText(
|
||||
nameLiteralNode,
|
||||
`${
|
||||
sourceCode.text[nameLiteralNode.range[0]]
|
||||
}${JSON.stringify(option.suggest)
|
||||
.slice(1, -1)
|
||||
.replace(/'/gu, "\\'")}${
|
||||
sourceCode.text[nameLiteralNode.range[1] - 1]
|
||||
}`
|
||||
)
|
||||
},
|
||||
messageId: 'instead',
|
||||
data: { suggest: option.suggest }
|
||||
}
|
||||
]
|
||||
: []
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return utils.defineTemplateBodyVisitor(
|
||||
context,
|
||||
{
|
||||
CallExpression(node) {
|
||||
const callee = node.callee
|
||||
const nameLiteralNode = getNameParamNode(node)
|
||||
if (!nameLiteralNode) {
|
||||
// cannot check
|
||||
return
|
||||
}
|
||||
if (callee.type === 'Identifier' && callee.name === '$emit') {
|
||||
verify(nameLiteralNode)
|
||||
}
|
||||
}
|
||||
},
|
||||
utils.compositingVisitors(
|
||||
utils.defineVueVisitor(context, {
|
||||
onSetupFunctionEnter(node, { node: vueNode }) {
|
||||
const contextParam = utils.skipDefaultParamValue(node.params[1])
|
||||
if (!contextParam) {
|
||||
// no arguments
|
||||
return
|
||||
}
|
||||
if (
|
||||
contextParam.type === 'RestElement' ||
|
||||
contextParam.type === 'ArrayPattern'
|
||||
) {
|
||||
// cannot check
|
||||
return
|
||||
}
|
||||
const contextReferenceIds = new Set()
|
||||
const emitReferenceIds = new Set()
|
||||
if (contextParam.type === 'ObjectPattern') {
|
||||
const emitProperty = utils.findAssignmentProperty(
|
||||
contextParam,
|
||||
'emit'
|
||||
)
|
||||
if (!emitProperty || emitProperty.value.type !== 'Identifier') {
|
||||
return
|
||||
}
|
||||
const emitParam = emitProperty.value
|
||||
// `setup(props, {emit})`
|
||||
const variable = findVariable(context.getScope(), emitParam)
|
||||
if (!variable) {
|
||||
return
|
||||
}
|
||||
for (const reference of variable.references) {
|
||||
emitReferenceIds.add(reference.identifier)
|
||||
}
|
||||
} else {
|
||||
// `setup(props, context)`
|
||||
const variable = findVariable(context.getScope(), contextParam)
|
||||
if (!variable) {
|
||||
return
|
||||
}
|
||||
for (const reference of variable.references) {
|
||||
contextReferenceIds.add(reference.identifier)
|
||||
}
|
||||
}
|
||||
setupContexts.set(vueNode, {
|
||||
contextReferenceIds,
|
||||
emitReferenceIds
|
||||
})
|
||||
},
|
||||
CallExpression(node, { node: vueNode }) {
|
||||
const nameLiteralNode = getNameParamNode(node)
|
||||
if (!nameLiteralNode) {
|
||||
// cannot check
|
||||
return
|
||||
}
|
||||
|
||||
// verify setup context
|
||||
const setupContext = setupContexts.get(vueNode)
|
||||
if (setupContext) {
|
||||
const { contextReferenceIds, emitReferenceIds } = setupContext
|
||||
if (
|
||||
node.callee.type === 'Identifier' &&
|
||||
emitReferenceIds.has(node.callee)
|
||||
) {
|
||||
// verify setup(props,{emit}) {emit()}
|
||||
verify(nameLiteralNode)
|
||||
} else {
|
||||
const emit = getCalleeMemberNode(node)
|
||||
if (
|
||||
emit &&
|
||||
emit.name === 'emit' &&
|
||||
emit.member.object.type === 'Identifier' &&
|
||||
contextReferenceIds.has(emit.member.object)
|
||||
) {
|
||||
// verify setup(props,context) {context.emit()}
|
||||
verify(nameLiteralNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onVueObjectExit(node) {
|
||||
setupContexts.delete(node)
|
||||
}
|
||||
}),
|
||||
{
|
||||
CallExpression(node) {
|
||||
const nameLiteralNode = getNameParamNode(node)
|
||||
if (!nameLiteralNode) {
|
||||
// cannot check
|
||||
return
|
||||
}
|
||||
const emit = getCalleeMemberNode(node)
|
||||
// verify $emit
|
||||
if (emit && emit.name === '$emit') {
|
||||
// verify this.$emit()
|
||||
verify(nameLiteralNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user