diff --git a/docs/rules/html-element-name-kebab-casing.md b/docs/rules/html-element-name-kebab-casing.md new file mode 100644 index 000000000..536bf4c4b --- /dev/null +++ b/docs/rules/html-element-name-kebab-casing.md @@ -0,0 +1,34 @@ +# enforce the tag name of the Vue component and HTML element to be `kebab-case` (vue/html-element-name-kebab-casing) + +- :wrench: The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule. + +This rule enforce the tag name of the Vue component, HTML element, and custom element to be `kebab-case`. +It does not target elements of MathML or SVG. + + +Do not use this rule if you prefer `PascalCase` to the Vue component name. + +## :book: Rule Details + +:+1: Examples of **correct** code: + +```html + +``` + +:-1: Examples of **incorrect** code: + +```html + +``` diff --git a/lib/index.js b/lib/index.js index 79769f2a3..551136c97 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,6 +12,7 @@ module.exports = { 'comment-directive': require('./rules/comment-directive'), 'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'), 'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'), + 'html-element-name-kebab-casing': require('./rules/html-element-name-kebab-casing'), 'html-end-tags': require('./rules/html-end-tags'), 'html-indent': require('./rules/html-indent'), 'html-quotes': require('./rules/html-quotes'), diff --git a/lib/rules/html-element-name-kebab-casing.js b/lib/rules/html-element-name-kebab-casing.js new file mode 100644 index 000000000..df5ce5901 --- /dev/null +++ b/lib/rules/html-element-name-kebab-casing.js @@ -0,0 +1,83 @@ +/** + * @author Yosuke Ota + * issue https://github.com/vuejs/eslint-plugin-vue/issues/499 + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const utils = require('../utils') +const casing = require('../utils/casing') + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const kebabConverter = casing.getConverter('kebab-case') + +module.exports = { + meta: { + docs: { + description: 'enforce the tag name of the Vue component and HTML element to be `kebab-case`', + category: undefined, + url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.7.0/docs/rules/html-element-name-kebab-casing.md' + }, + fixable: 'code', + schema: [] + }, + + create (context) { + const tokens = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore() + const sourceCode = context.getSourceCode() + + let hasInvalidEOF = false + + return utils.defineTemplateBodyVisitor(context, { + 'VElement' (node) { + if (hasInvalidEOF) { + return + } + + if (utils.isSvgElementNode(node) || utils.isMathMLElementNode(node)) { + return + } + + const name = node.rawName + const casingName = kebabConverter(name) + if (casingName !== name) { + const startTag = node.startTag + const open = tokens.getFirstToken(startTag) + + context.report({ + node: open, + loc: open.loc, + message: 'Element name "{{name}}" is not kebab-case.', + data: { + name + }, + fix: fixer => { + const endTag = node.endTag + if (!endTag) { + return fixer.replaceText(open, `<${casingName}`) + } + const endTagOpen = tokens.getFirstToken(endTag) + // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by: + // return [ + // fixer.replaceText(open, `<${casingName}`), + // fixer.replaceText(endTagOpen, `
', + '', + '', + '', + '', + '', + '', + '', + // Invalid EOF + ' +`, + output: ` + +`, + errors: ['Element name "TheComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "TheComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "TheComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "TheComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "TheComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "theComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "The-component" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "Thecomponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "TheComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "TheComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: ['Element name "TheComponent" is not kebab-case.'] + }, + { + code: ` + +`, + output: ` + +`, + errors: [ + 'Element name "Div" is not kebab-case.', + 'Element name "INPUT" is not kebab-case.' + ] + } + ] +})