vue hello world项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.0 KiB

3 years ago
/**
* @fileoverview Forbid certain classes from being used
* @author Tao Bojlen
*/
'use strict'
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const utils = require('../utils')
// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
/**
* Report a forbidden class
* @param {string} className
* @param {*} node
* @param {RuleContext} context
* @param {Set<string>} forbiddenClasses
*/
const reportForbiddenClass = (className, node, context, forbiddenClasses) => {
if (forbiddenClasses.has(className)) {
const loc = node.value ? node.value.loc : node.loc
context.report({
node,
loc,
messageId: 'forbiddenClass',
data: {
class: className
}
})
}
}
/**
* @param {Expression} node
* @param {boolean} [textOnly]
* @returns {IterableIterator<{ className:string, reportNode: ESNode }>}
*/
function* extractClassNames(node, textOnly) {
if (node.type === 'Literal') {
yield* `${node.value}`
.split(/\s+/)
.map((className) => ({ className, reportNode: node }))
return
}
if (node.type === 'TemplateLiteral') {
for (const templateElement of node.quasis) {
yield* templateElement.value.cooked
.split(/\s+/)
.map((className) => ({ className, reportNode: templateElement }))
}
for (const expr of node.expressions) {
yield* extractClassNames(expr, true)
}
return
}
if (node.type === 'BinaryExpression') {
if (node.operator !== '+') {
return
}
yield* extractClassNames(node.left, true)
yield* extractClassNames(node.right, true)
return
}
if (textOnly) {
return
}
if (node.type === 'ObjectExpression') {
for (const prop of node.properties) {
if (prop.type !== 'Property') {
continue
}
const classNames = utils.getStaticPropertyName(prop)
if (!classNames) {
continue
}
yield* classNames
.split(/\s+/)
.map((className) => ({ className, reportNode: prop.key }))
}
return
}
if (node.type === 'ArrayExpression') {
for (const element of node.elements) {
if (element == null) {
continue
}
if (element.type === 'SpreadElement') {
continue
}
yield* extractClassNames(element)
}
return
}
}
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow specific classes in Vue components',
url: 'https://eslint.vuejs.org/rules/no-restricted-class.html',
categories: undefined
},
fixable: null,
messages: {
forbiddenClass: "'{{class}}' class is not allowed."
},
schema: {
type: 'array',
items: {
type: 'string'
}
}
},
/** @param {RuleContext} context */
create(context) {
const forbiddenClasses = new Set(context.options || [])
return utils.defineTemplateBodyVisitor(context, {
/**
* @param {VAttribute & { value: VLiteral } } node
*/
'VAttribute[directive=false][key.name="class"]'(node) {
node.value.value
.split(/\s+/)
.forEach((className) =>
reportForbiddenClass(className, node, context, forbiddenClasses)
)
},
/** @param {VExpressionContainer} node */
"VAttribute[directive=true][key.name.name='bind'][key.argument.name='class'] > VExpressionContainer.value"(
node
) {
if (!node.expression) {
return
}
for (const { className, reportNode } of extractClassNames(
/** @type {Expression} */ (node.expression)
)) {
reportForbiddenClass(className, reportNode, context, forbiddenClasses)
}
}
})
}
}