diff --git a/docs/rules/README.md b/docs/rules/README.md index 3d4a9b2e3..8a8b6b2d0 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -335,6 +335,7 @@ For example: | [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 | :bulb: | +| [vue/no-restricted-html-elements](./no-restricted-html-elements.md) | disallow specific HTML elements | | | [vue/no-restricted-props](./no-restricted-props.md) | disallow specific props | :bulb: | | [vue/no-restricted-static-attribute](./no-restricted-static-attribute.md) | disallow specific attribute | | | [vue/no-restricted-v-bind](./no-restricted-v-bind.md) | disallow specific argument in `v-bind` | | diff --git a/docs/rules/no-restricted-html-elements.md b/docs/rules/no-restricted-html-elements.md new file mode 100644 index 000000000..780708d6a --- /dev/null +++ b/docs/rules/no-restricted-html-elements.md @@ -0,0 +1,96 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-restricted-html-elements +description: disallow specific HTML elements +--- +# vue/no-restricted-html-elements + +> disallow specific HTML elements + +- :exclamation: ***This rule has not been released yet.*** + +## :book: Rule Details + +This rule allows you to specify HTML elements that you don't want to use in your application. + + + +```vue + +``` + + + +## :wrench: Options + +This rule takes a list of strings, where each string is an HTML element name to be restricted: + +```json +{ + "vue/no-restricted-html-elements": ["error", "button", "marquee"] +} +``` + + + +```vue + +``` + + + +Alternatively, the rule also accepts objects. + +```json +{ + "vue/no-restricted-html-elements": [ + "error", + { + "element": "button", + "message": "Prefer use of our custom component" + }, + { + "element": "marquee", + "message": "Do not use deprecated HTML tags" + } + ] +} +``` + +The following properties can be specified for the object. + +- `element` ... Specify the html element. +- `message` ... Specify an optional custom message. + +### `{ "element": "marquee" }, { "element": "button" }` + + + +```vue + +``` + + + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-restricted-html-elements.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-restricted-html-elements.js) diff --git a/lib/index.js b/lib/index.js index 97ff805d3..9f1e2bdde 100644 --- a/lib/index.js +++ b/lib/index.js @@ -113,6 +113,7 @@ module.exports = { '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-html-elements': require('./rules/no-restricted-html-elements'), 'no-restricted-props': require('./rules/no-restricted-props'), 'no-restricted-static-attribute': require('./rules/no-restricted-static-attribute'), 'no-restricted-syntax': require('./rules/no-restricted-syntax'), diff --git a/lib/rules/no-restricted-html-elements.js b/lib/rules/no-restricted-html-elements.js new file mode 100644 index 000000000..b0874f1c0 --- /dev/null +++ b/lib/rules/no-restricted-html-elements.js @@ -0,0 +1,68 @@ +/** + * @author Doug Wade + */ + +'use strict' + +const utils = require('../utils') + +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow specific HTML elements', + categories: undefined, + url: 'https://eslint.vuejs.org/rules/no-restricted-html-elements.html' + }, + fixable: null, + schema: { + type: 'array', + items: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + properties: { + element: { type: 'string' }, + message: { type: 'string', minLength: 1 } + }, + required: ['element'], + additionalProperties: false + } + ] + }, + uniqueItems: true, + minItems: 0 + } + }, + /** + * @param {RuleContext} context - The rule context. + * @returns {RuleListener} AST event handlers. + */ + create(context) { + return utils.defineTemplateBodyVisitor(context, { + /** + * @param {VElement} node + */ + VElement(node) { + if (!utils.isHtmlElementNode(node)) { + return + } + + context.options.forEach((option) => { + const message = + option.message || + `Unexpected use of forbidden HTML element ${node.rawName}.` + const element = option.element || option + + if (element === node.rawName) { + context.report({ + message, + node: node.startTag + }) + } + }) + } + }) + } +} diff --git a/tests/lib/rules/no-restricted-html-elements.js b/tests/lib/rules/no-restricted-html-elements.js new file mode 100644 index 000000000..775d94580 --- /dev/null +++ b/tests/lib/rules/no-restricted-html-elements.js @@ -0,0 +1,75 @@ +/** + * @author Doug Wade + */ +'use strict' + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../lib/rules/no-restricted-html-elements') + +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { ecmaVersion: 2015 } +}) + +tester.run('no-restricted-html-elements', rule, { + valid: [ + { + filename: 'test.vue', + code: '', + options: ['button'] + }, + { + filename: 'test.vue', + code: '', + options: ['button'] + }, + { + filename: 'test.vue', + code: '', + options: ['div'] + }, + { + filename: 'test.vue', + code: '', + options: ['button'] + } + ], + invalid: [ + { + filename: 'test.vue', + code: '', + errors: [ + { + message: 'Unexpected use of forbidden HTML element button.', + line: 1, + column: 16 + } + ], + options: ['button'] + }, + { + filename: 'test.vue', + code: '', + errors: [ + { + message: 'Unexpected use of forbidden HTML element div.', + line: 1, + column: 11 + } + ], + options: ['div'] + }, + { + filename: 'test.vue', + code: '', + errors: [ + { + message: 'Custom error', + line: 1, + column: 11 + } + ], + options: [{ element: 'marquee', message: 'Custom error' }] + } + ] +})