From f7d9fd0ed057e5ea5dea74cfeb88e128238c3ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= <66130243+taobojlen@users.noreply.github.com> Date: Mon, 27 Sep 2021 19:01:00 +0100 Subject: [PATCH 1/7] Add vue/no-restricted-class rule --- docs/rules/no-restricted-class.md | 104 +++++++++ lib/rules/no-restricted-class.js | 221 ++++++++++++++++++ .../no-restricted-class/forbidden.json | 1 + tests/lib/rules/no-restricted-class.js | 102 ++++++++ 4 files changed, 428 insertions(+) create mode 100644 docs/rules/no-restricted-class.md create mode 100644 lib/rules/no-restricted-class.js create mode 100644 tests/fixtures/no-restricted-class/forbidden.json create mode 100644 tests/lib/rules/no-restricted-class.js diff --git a/docs/rules/no-restricted-class.md b/docs/rules/no-restricted-class.md new file mode 100644 index 000000000..d722a5618 --- /dev/null +++ b/docs/rules/no-restricted-class.md @@ -0,0 +1,104 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-restricted-class +description: disallow specific classes +since: v7.19.0 +--- +# vue/no-restricted-classes + +> disallow specific classes + +## :book: Rule Details + +This rule lets you specify a list of classes that you don't want to allow in your templates. + +## :wrench: Options + +The simplest way to specify a list of forbidden classes is to pass it directly +in the rule configuration. + +```json +{ + "vue/no-restricted-props": ["error", { classes: ["forbidden"] }] +} +``` + + + +```vue + + + +``` + + + + +Alternatively, you can also specify a list of files that contain forbidden classes. Each file +must contain a JSON-formatted array of strings. + +```json +{ + "vue/no-restricted-props": ["error", + { + files: [".eslint/forbidden-classes.json"] + }, + ] +} +``` + +`.eslint/forbidden-classes.json`: + +``` +[ + "forbidden-class", + "another-forbidden-class" +] +``` + +::: warning Note +This rule will only detect classes that are used as strings in your templates. Passing classes via +variables, like below, will not be detected by this rule. + +```vue + + + +``` +::: + +## :rocket: Version + +This rule was introduced in eslint-plugin-vue v7.19.0. + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-restricted-class.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-restricted-class.js) diff --git a/lib/rules/no-restricted-class.js b/lib/rules/no-restricted-class.js new file mode 100644 index 000000000..426a236cd --- /dev/null +++ b/lib/rules/no-restricted-class.js @@ -0,0 +1,221 @@ +/** + * @fileoverview Forbid certain classes from being used + * @author Tao Bojlen + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ +const fs = require('fs') +const utils = require('../utils') + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ +/** + * Report a forbidden class + * @param {Set} classes + * @param {*} node + * @param {RuleContext} context + * @param {Set} forbiddenClasses + */ +const reportForbiddenClasses = (classes, node, context, forbiddenClasses) => { + classes.forEach((className) => { + if (forbiddenClasses.has(className)) { + context.report({ + node, + loc: node.value.loc, + messageId: 'forbiddenClass', + data: { + class: className + } + }) + } + }) +} + +/** + * Recursively flatten a binary Expression into a string + * @param {BinaryExpression | Expression} left + * @param {BinaryExpression | Expression} right + * @returns {string} + */ +const flattenBinaryExpression = (left, right) => { + const result = [] + if (left) { + if (left.type === 'Literal') { + result.push(left.value) + } else if ( + left.type === 'BinaryExpression' && + left.left.type !== 'PrivateIdentifier' + ) { + result.push(flattenBinaryExpression(left.left, left.right)) + } + } + + if (right) { + if (right.type === 'Literal') { + result.push(right.value) + } else if ( + right.type === 'BinaryExpression' && + right.left.type !== 'PrivateIdentifier' + ) { + result.push(flattenBinaryExpression(right.left, right.right)) + } + } + + return result.join(' ') +} + +// ------------------------------------------------------------------------------ +// 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: 'object', + properties: { + classes: { + type: 'array', + items: { + type: 'string' + } + }, + files: { + type: 'array', + items: { type: 'string' } + } + }, + additionalProperties: false + } + ] + }, + + /** @param {RuleContext} context */ + create(context) { + const config = context.options[0] || {} + const forbiddenClasses = new Set(config.classes || []) + const forbiddenClassFiles = config.files || [] + + forbiddenClassFiles.forEach((/** @type {string} */ filePath) => { + if (!fs.existsSync(filePath)) { + throw new Error(`File "${filePath}" does not exist.`) + } + const rawData = fs.readFileSync(filePath, 'utf8') + JSON.parse(rawData).forEach((/** @type {string} */ forbiddenClass) => { + forbiddenClasses.add(forbiddenClass) + }) + }) + + return utils.defineTemplateBodyVisitor(context, { + // + /** + * @param {VAttribute & { value: VLiteral } } node + */ + 'VAttribute[directive=false][key.name="class"]'(node) { + const classes = new Set(node.value.value.split(/\s+/)) + reportForbiddenClasses(classes, node, context, forbiddenClasses) + }, + + // + /** + * @param {VAttribute & { value: { expression: ObjectExpression } } } node + */ + 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="ObjectExpression"]'( + node + ) { + const classes = node.value.expression.properties + .filter( + (property) => + property.type === 'Property' && + (property.key.type === 'Literal' || + property.key.type === 'Identifier') + ) + .reduce((acc, property) => { + // Ugly early return to make TS happy + if ( + property.type !== 'Property' || + (property.key.type !== 'Literal' && + property.key.type !== 'Identifier') + ) + return acc + let value = '' + if (property.key.type === 'Literal') { + value = (property.key.value || '').toString() + } else { + value = property.key.name + } + const values = value.split(/\s+/) + values.forEach((/** @type {string} */ className) => { + acc.add(className) + }) + return acc + }, new Set()) + reportForbiddenClasses(classes, node, context, forbiddenClasses) + }, + + // + /** + * @param {VAttribute & { value: { expression: TemplateLiteral } } } node + */ + 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="TemplateLiteral"]'( + node + ) { + const strings = node.value.expression.quasis.reduce( + (acc, templateElement) => { + const classNames = templateElement.value.raw.split(/\s+/) + classNames.forEach((className) => { + acc.add(className) + }) + return acc + }, + new Set() + ) + reportForbiddenClasses(strings, node, context, forbiddenClasses) + }, + + // + /** + * @param {VAttribute & { value: { expression: Literal } } } node + */ + 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="Literal"]'( + node + ) { + if (!node.value.expression.value) return + const classNames = new Set( + node.value.expression.value.toString().split(/\s+/) + ) + reportForbiddenClasses(classNames, node, context, forbiddenClasses) + }, + + // + /** + * @param {VAttribute & { value: { expression: BinaryExpression } } } node + */ + 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="BinaryExpression"]'( + node + ) { + if (node.value.expression.left.type === 'PrivateIdentifier') return + const classNames = new Set( + flattenBinaryExpression( + node.value.expression.left, + node.value.expression.right + ).split(/\s+/) + ) + reportForbiddenClasses(classNames, node, context, forbiddenClasses) + } + }) + } +} diff --git a/tests/fixtures/no-restricted-class/forbidden.json b/tests/fixtures/no-restricted-class/forbidden.json new file mode 100644 index 000000000..b2c833993 --- /dev/null +++ b/tests/fixtures/no-restricted-class/forbidden.json @@ -0,0 +1 @@ +["forbidden"] diff --git a/tests/lib/rules/no-restricted-class.js b/tests/lib/rules/no-restricted-class.js new file mode 100644 index 000000000..adf46874b --- /dev/null +++ b/tests/lib/rules/no-restricted-class.js @@ -0,0 +1,102 @@ +/** + * @author Tao Bojlen + */ + +'use strict' + +const rule = require('../../../lib/rules/no-restricted-class') +const RuleTester = require('eslint').RuleTester + +const ruleTester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { ecmaVersion: 2020, sourceType: 'module' } +}) + +ruleTester.run('no-restricted-class', rule, { + valid: [ + { code: `` }, + { + code: ``, + options: [{ classes: ['forbidden'] }] + }, + { + code: ``, + options: [{ classes: ['forbidden'] }] + } + ], + + invalid: [ + { + code: ``, + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: [{ classes: ['forbidden'] }] + }, + { + code: ``, + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: [{ classes: ['forbidden'] }] + }, + { + code: ``, + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: [{ classes: ['forbidden'] }] + }, + { + code: ``, + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: [{ classes: ['forbidden'] }] + }, + { + code: '', + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: [{ classes: ['forbidden'] }] + }, + { + code: ``, + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: [{ classes: ['forbidden'] }] + }, + { + code: ``, + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: [ + { files: ['tests/fixtures/no-restricted-class/forbidden.json'] } + ] + } + ] +}) From c1b550291e3edfac820fd2e3947061fdc9a7f82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= <66130243+taobojlen@users.noreply.github.com> Date: Tue, 28 Sep 2021 14:18:51 +0100 Subject: [PATCH 2/7] don't match '@class' --- lib/rules/no-restricted-class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-restricted-class.js b/lib/rules/no-restricted-class.js index 426a236cd..c87ebe326 100644 --- a/lib/rules/no-restricted-class.js +++ b/lib/rules/no-restricted-class.js @@ -133,7 +133,7 @@ module.exports = { /** * @param {VAttribute & { value: { expression: ObjectExpression } } } node */ - 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="ObjectExpression"]'( + 'VAttribute[directive=true][key.name.name="bind"][key.argument.name="class"][value.expression.type="ObjectExpression"]'( node ) { const classes = node.value.expression.properties From 7fe694d90fe1712ab248bafce51741bdaafb8abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= <66130243+taobojlen@users.noreply.github.com> Date: Tue, 28 Sep 2021 14:19:07 +0100 Subject: [PATCH 3/7] accept options in an array --- docs/rules/no-restricted-class.md | 28 +------------- lib/rules/no-restricted-class.js | 37 +++---------------- .../no-restricted-class/forbidden.json | 1 - tests/lib/rules/no-restricted-class.js | 28 ++++---------- 4 files changed, 16 insertions(+), 78 deletions(-) delete mode 100644 tests/fixtures/no-restricted-class/forbidden.json diff --git a/docs/rules/no-restricted-class.md b/docs/rules/no-restricted-class.md index d722a5618..b21f697ee 100644 --- a/docs/rules/no-restricted-class.md +++ b/docs/rules/no-restricted-class.md @@ -3,7 +3,6 @@ pageClass: rule-details sidebarDepth: 0 title: vue/no-restricted-class description: disallow specific classes -since: v7.19.0 --- # vue/no-restricted-classes @@ -20,11 +19,11 @@ in the rule configuration. ```json { - "vue/no-restricted-props": ["error", { classes: ["forbidden"] }] + "vue/no-restricted-props": ["error", "forbidden", "forbidden-two", "forbidden-three"] } ``` - + ```vue diff --git a/lib/rules/no-restricted-class.js b/lib/rules/no-restricted-class.js index 7ac0d37f2..eb72e250c 100644 --- a/lib/rules/no-restricted-class.js +++ b/lib/rules/no-restricted-class.js @@ -190,6 +190,23 @@ module.exports = { ).split(/\s+/) ) reportForbiddenClasses(classNames, node, context, forbiddenClasses) + }, + + // + /** + * @param {VAttribute & { value: { expression: ArrayExpression} } } node + */ + 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="ArrayExpression"]'( + node + ) { + const classNames = new Set( + node.value.expression.elements.flatMap((item) => { + // It's not possible to use a filter here because Typescript won't understand the types. + if (!item || item.type !== 'Literal') return [] + return (item.value || '').toString().split(/\s+/) + }) + ) + reportForbiddenClasses(classNames, node, context, forbiddenClasses) } }) } diff --git a/tests/lib/rules/no-restricted-class.js b/tests/lib/rules/no-restricted-class.js index 80e227a0f..23e28049a 100644 --- a/tests/lib/rules/no-restricted-class.js +++ b/tests/lib/rules/no-restricted-class.js @@ -85,6 +85,26 @@ ruleTester.run('no-restricted-class', rule, { } ], options: ['forbidden'] + }, + { + code: ``, + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: ['forbidden'] + }, + { + code: ``, + errors: [ + { + message: "'forbidden' class is not allowed.", + type: 'VAttribute' + } + ], + options: ['forbidden'] } ] }) From 330c3141486f979015a96b0d85156788c552b856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= <66130243+taobojlen@users.noreply.github.com> Date: Tue, 28 Sep 2021 15:17:38 +0100 Subject: [PATCH 5/7] refactor with @ota-meshi's suggestions --- lib/rules/no-restricted-class.js | 223 +++++++++---------------- tests/lib/rules/no-restricted-class.js | 18 +- 2 files changed, 93 insertions(+), 148 deletions(-) diff --git a/lib/rules/no-restricted-class.js b/lib/rules/no-restricted-class.js index eb72e250c..a4fef0da5 100644 --- a/lib/rules/no-restricted-class.js +++ b/lib/rules/no-restricted-class.js @@ -14,57 +14,86 @@ const utils = require('../utils') // ------------------------------------------------------------------------------ /** * Report a forbidden class - * @param {Set} classes + * @param {string} className * @param {*} node * @param {RuleContext} context * @param {Set} forbiddenClasses */ -const reportForbiddenClasses = (classes, node, context, forbiddenClasses) => { - classes.forEach((className) => { - if (forbiddenClasses.has(className)) { - context.report({ - node, - loc: node.value.loc, - messageId: 'forbiddenClass', - data: { - class: className - } - }) - } - }) +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 + } + }) + } } /** - * Recursively flatten a binary Expression into a string - * @param {BinaryExpression | Expression} left - * @param {BinaryExpression | Expression} right - * @returns {string} + * @param {Expression} node + * @param {boolean} [textOnly] + * @returns {IterableIterator<{ className:string, reportNode: ESNode }>} */ -const flattenBinaryExpression = (left, right) => { - const result = [] - if (left) { - if (left.type === 'Literal') { - result.push(left.value) - } else if ( - left.type === 'BinaryExpression' && - left.left.type !== 'PrivateIdentifier' - ) { - result.push(flattenBinaryExpression(left.left, left.right)) +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 (right) { - if (right.type === 'Literal') { - result.push(right.value) - } else if ( - right.type === 'BinaryExpression' && - right.left.type !== 'PrivateIdentifier' - ) { - result.push(flattenBinaryExpression(right.left, right.right)) + if (node.type === 'BinaryExpression') { + if (node.operator !== '+') { + return } + yield* extractClassNames(node.left, true) + yield* extractClassNames(node.right, true) + 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 + } + if (!textOnly) { + return } - - return result.join(' ') } // ------------------------------------------------------------------------------ @@ -95,118 +124,30 @@ module.exports = { const forbiddenClasses = new Set(context.options || []) return utils.defineTemplateBodyVisitor(context, { - // /** * @param {VAttribute & { value: VLiteral } } node */ 'VAttribute[directive=false][key.name="class"]'(node) { - const classes = new Set(node.value.value.split(/\s+/)) - reportForbiddenClasses(classes, node, context, forbiddenClasses) - }, - - // - /** - * @param {VAttribute & { value: { expression: ObjectExpression } } } node - */ - 'VAttribute[directive=true][key.name.name="bind"][key.argument.name="class"][value.expression.type="ObjectExpression"]'( - node - ) { - const classes = node.value.expression.properties - .filter( - (property) => - property.type === 'Property' && - (property.key.type === 'Literal' || - property.key.type === 'Identifier') + node.value.value + .split(/\s+/) + .forEach((className) => + reportForbiddenClass(className, node, context, forbiddenClasses) ) - .reduce((acc, property) => { - // Ugly early return to make TS happy - if ( - property.type !== 'Property' || - (property.key.type !== 'Literal' && - property.key.type !== 'Identifier') - ) - return acc - let value = '' - if (property.key.type === 'Literal') { - value = (property.key.value || '').toString() - } else { - value = property.key.name - } - const values = value.split(/\s+/) - values.forEach((/** @type {string} */ className) => { - acc.add(className) - }) - return acc - }, new Set()) - reportForbiddenClasses(classes, node, context, forbiddenClasses) - }, - - // - /** - * @param {VAttribute & { value: { expression: TemplateLiteral } } } node - */ - 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="TemplateLiteral"]'( - node - ) { - const strings = node.value.expression.quasis.reduce( - (acc, templateElement) => { - const classNames = templateElement.value.raw.split(/\s+/) - classNames.forEach((className) => { - acc.add(className) - }) - return acc - }, - new Set() - ) - reportForbiddenClasses(strings, node, context, forbiddenClasses) - }, - - // - /** - * @param {VAttribute & { value: { expression: Literal } } } node - */ - 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="Literal"]'( - node - ) { - if (!node.value.expression.value) return - const classNames = new Set( - node.value.expression.value.toString().split(/\s+/) - ) - reportForbiddenClasses(classNames, node, context, forbiddenClasses) }, - // - /** - * @param {VAttribute & { value: { expression: BinaryExpression } } } node - */ - 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="BinaryExpression"]'( + /** @param {VExpressionContainer} node */ + "VAttribute[directive=true][key.name.name='bind'][key.argument.name='class'] > VExpressionContainer.value"( node ) { - if (node.value.expression.left.type === 'PrivateIdentifier') return - const classNames = new Set( - flattenBinaryExpression( - node.value.expression.left, - node.value.expression.right - ).split(/\s+/) - ) - reportForbiddenClasses(classNames, node, context, forbiddenClasses) - }, + if (!node.expression) { + return + } - // - /** - * @param {VAttribute & { value: { expression: ArrayExpression} } } node - */ - 'VAttribute[directive=true][key.argument.name="class"][value.expression.type="ArrayExpression"]'( - node - ) { - const classNames = new Set( - node.value.expression.elements.flatMap((item) => { - // It's not possible to use a filter here because Typescript won't understand the types. - if (!item || item.type !== 'Literal') return [] - return (item.value || '').toString().split(/\s+/) - }) - ) - reportForbiddenClasses(classNames, node, context, forbiddenClasses) + for (const { className, reportNode } of extractClassNames( + /** @type {Expression} */ (node.expression) + )) { + reportForbiddenClass(className, reportNode, context, forbiddenClasses) + } } }) } diff --git a/tests/lib/rules/no-restricted-class.js b/tests/lib/rules/no-restricted-class.js index 23e28049a..a69081c43 100644 --- a/tests/lib/rules/no-restricted-class.js +++ b/tests/lib/rules/no-restricted-class.js @@ -22,6 +22,10 @@ ruleTester.run('no-restricted-class', rule, { { code: ``, options: ['forbidden'] + }, + { + code: ``, + options: ['forbidden'] } ], @@ -41,7 +45,7 @@ ruleTester.run('no-restricted-class', rule, { errors: [ { message: "'forbidden' class is not allowed.", - type: 'VAttribute' + type: 'Literal' } ], options: ['forbidden'] @@ -51,7 +55,7 @@ ruleTester.run('no-restricted-class', rule, { errors: [ { message: "'forbidden' class is not allowed.", - type: 'VAttribute' + type: 'Literal' } ], options: ['forbidden'] @@ -61,7 +65,7 @@ ruleTester.run('no-restricted-class', rule, { errors: [ { message: "'forbidden' class is not allowed.", - type: 'VAttribute' + type: 'Identifier' } ], options: ['forbidden'] @@ -71,7 +75,7 @@ ruleTester.run('no-restricted-class', rule, { errors: [ { message: "'forbidden' class is not allowed.", - type: 'VAttribute' + type: 'TemplateElement' } ], options: ['forbidden'] @@ -81,7 +85,7 @@ ruleTester.run('no-restricted-class', rule, { errors: [ { message: "'forbidden' class is not allowed.", - type: 'VAttribute' + type: 'Literal' } ], options: ['forbidden'] @@ -91,7 +95,7 @@ ruleTester.run('no-restricted-class', rule, { errors: [ { message: "'forbidden' class is not allowed.", - type: 'VAttribute' + type: 'Literal' } ], options: ['forbidden'] @@ -101,7 +105,7 @@ ruleTester.run('no-restricted-class', rule, { errors: [ { message: "'forbidden' class is not allowed.", - type: 'VAttribute' + type: 'Literal' } ], options: ['forbidden'] From b5fb51014f2c97e78a125d1850519bebd8a4e7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= <66130243+taobojlen@users.noreply.github.com> Date: Wed, 29 Sep 2021 13:10:42 +0100 Subject: [PATCH 6/7] handle objects converted to strings --- lib/rules/no-restricted-class.js | 6 +++--- tests/lib/rules/no-restricted-class.js | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/rules/no-restricted-class.js b/lib/rules/no-restricted-class.js index a4fef0da5..b1dcccb07 100644 --- a/lib/rules/no-restricted-class.js +++ b/lib/rules/no-restricted-class.js @@ -64,6 +64,9 @@ function* extractClassNames(node, textOnly) { yield* extractClassNames(node.right, true) return } + if (textOnly) { + return + } if (node.type === 'ObjectExpression') { for (const prop of node.properties) { if (prop.type !== 'Property') { @@ -91,9 +94,6 @@ function* extractClassNames(node, textOnly) { } return } - if (!textOnly) { - return - } } // ------------------------------------------------------------------------------ diff --git a/tests/lib/rules/no-restricted-class.js b/tests/lib/rules/no-restricted-class.js index a69081c43..baf39778f 100644 --- a/tests/lib/rules/no-restricted-class.js +++ b/tests/lib/rules/no-restricted-class.js @@ -26,6 +26,10 @@ ruleTester.run('no-restricted-class', rule, { { code: ``, options: ['forbidden'] + }, + { + code: ``, + options: ['forbidden'] } ], From 3235306563d17dc9cc40e5f3161047eacc21dd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= <66130243+taobojlen@users.noreply.github.com> Date: Wed, 29 Sep 2021 13:11:03 +0100 Subject: [PATCH 7/7] run update script --- docs/rules/README.md | 1 + docs/rules/no-restricted-class.md | 12 +++++------- lib/index.js | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/rules/README.md b/docs/rules/README.md index 85f6e661b..2f89e3a53 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -310,6 +310,7 @@ For example: | [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | | | [vue/no-restricted-block](./no-restricted-block.md) | disallow specific block | | | [vue/no-restricted-call-after-await](./no-restricted-call-after-await.md) | disallow asynchronously called restricted methods | | +| [vue/no-restricted-class](./no-restricted-class.md) | disallow specific classes in Vue components | | | [vue/no-restricted-component-options](./no-restricted-component-options.md) | disallow specific component option | | | [vue/no-restricted-custom-event](./no-restricted-custom-event.md) | disallow specific custom event | | | [vue/no-restricted-props](./no-restricted-props.md) | disallow specific props | | diff --git a/docs/rules/no-restricted-class.md b/docs/rules/no-restricted-class.md index 027d86abf..c7aebce6b 100644 --- a/docs/rules/no-restricted-class.md +++ b/docs/rules/no-restricted-class.md @@ -2,11 +2,13 @@ pageClass: rule-details sidebarDepth: 0 title: vue/no-restricted-class -description: disallow specific classes +description: disallow specific classes in Vue components --- -# vue/no-restricted-classes +# vue/no-restricted-class -> disallow specific classes +> disallow specific classes in Vue components + +- :exclamation: ***This rule has not been released yet.*** ## :book: Rule Details @@ -71,10 +73,6 @@ export default { ``` ::: -## :rocket: Version - -This rule was introduced in eslint-plugin-vue v7.19.0. - ## :mag: Implementation - [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-restricted-class.js) diff --git a/lib/index.js b/lib/index.js index 0015fb579..ef111732d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -101,6 +101,7 @@ module.exports = { 'no-reserved-keys': require('./rules/no-reserved-keys'), 'no-restricted-block': require('./rules/no-restricted-block'), 'no-restricted-call-after-await': require('./rules/no-restricted-call-after-await'), + 'no-restricted-class': require('./rules/no-restricted-class'), 'no-restricted-component-options': require('./rules/no-restricted-component-options'), 'no-restricted-custom-event': require('./rules/no-restricted-custom-event'), 'no-restricted-props': require('./rules/no-restricted-props'),