From 5397a3d4196b650074f4092a87cf09a73c5afa45 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 31 Dec 2017 18:53:00 +0900 Subject: [PATCH 1/4] New: html-closing-bracket-spacing rule (fixes #229) --- README.md | 7 ++ docs/rules/html-closing-bracket-spacing.md | 83 +++++++++++++ lib/rules/html-closing-bracket-spacing.js | 112 ++++++++++++++++++ .../lib/rules/html-closing-bracket-spacing.js | 92 ++++++++++++++ 4 files changed, 294 insertions(+) create mode 100644 docs/rules/html-closing-bracket-spacing.md create mode 100644 lib/rules/html-closing-bracket-spacing.js create mode 100644 tests/lib/rules/html-closing-bracket-spacing.js diff --git a/README.md b/README.md index 871384de1..11ced0abc 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,13 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | | [order-in-components](./docs/rules/order-in-components.md) | enforce order of properties in components | | | [this-in-template](./docs/rules/this-in-template.md) | enforce usage of `this` in template | + +### Uncategorized + +| | Rule ID | Description | +|:---|:--------|:------------| +| :wrench: | [html-closing-bracket-spacing](./docs/rules/html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets | + ## :couple: FAQ diff --git a/docs/rules/html-closing-bracket-spacing.md b/docs/rules/html-closing-bracket-spacing.md new file mode 100644 index 000000000..61dfa4581 --- /dev/null +++ b/docs/rules/html-closing-bracket-spacing.md @@ -0,0 +1,83 @@ +# require or disallow a space before tag's closing brackets (html-closing-bracket-spacing) + +- :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 enforces space style before `>` of tags. + +```html +
or
+
or
+``` + +## Rule Details + +This rule has options. + +```json +{ + "html-closing-bracket-spacing": ["error", { + "startTag": "always" | "never", + "endTag": "always" | "never", + "selfClosingTag": "always" | "never" + }] +} +``` + +- `startTag` (`"always" | "never"`) ... Setting for the `>` of start tags (e.g. `
`). Default is `"never"`. + - `"always"` ... requires one or more spaces. + - `"never"` ... disallows spaces. +- `endTag` (`"always" | "never"`) ... Setting for the `>` of end tags (e.g. `
`). Default is `"never"`. + - `"always"` ... requires one or more spaces. + - `"never"` ... disallows spaces. +- `selfClosingTag` (`"always" | "never"`) ... Setting for the `/>` of self-closing tags (e.g. `
`). Default is `"always"`. + - `"always"` ... requires one or more spaces. + - `"never"` ... disallows spaces. + +Examples of **incorrect** code for this rule: + +```html + + +
+
+
+
+
+
+
+``` + +Examples of **correct** code for this rule: + +```html + + +
+
+
+
+
+
+
+``` + +```html + + +
+
+
+
+
+
+
+``` + +# Related rules + +- [vue/no-multi-spaces](./no-multi-spaces.md) +- [vue/html-closing-bracket-newline](./html-closing-bracket-newline.md) diff --git a/lib/rules/html-closing-bracket-spacing.js b/lib/rules/html-closing-bracket-spacing.js new file mode 100644 index 000000000..919a1b12c --- /dev/null +++ b/lib/rules/html-closing-bracket-spacing.js @@ -0,0 +1,112 @@ +/** + * @author Toru Nagashima + */ + +'use strict' + +// ----------------------------------------------------------------------------- +// Requirements +// ----------------------------------------------------------------------------- + +const utils = require('../utils') + +// ----------------------------------------------------------------------------- +// Helpers +// ----------------------------------------------------------------------------- + +/** + * Normalize options. + * @param {{startTag?:"always"|"never",endTag?:"always"|"never",selfClosingTag?:"always"|"never"}} options The options user configured. + * @param {TokenStore} tokens The token store of template body. + * @returns {{startTag:"always"|"never",endTag:"always"|"never",selfClosingTag:"always"|"never"}} The normalized options. + */ +function parseOptions (options, tokens) { + return Object.assign({ + startTag: 'never', + endTag: 'never', + selfClosingTag: 'always', + + detectType (node) { + const openType = tokens.getFirstToken(node).type + const closeType = tokens.getLastToken(node).type + + if (openType === 'HTMLEndTagOpen' && closeType === 'HTMLTagClose') { + return this.endTag + } + if (openType === 'HTMLTagOpen' && closeType === 'HTMLTagClose') { + return this.startTag + } + if (openType === 'HTMLTagOpen' && closeType === 'HTMLSelfClosingTagClose') { + return this.selfClosingTag + } + return null + } + }, options) +} + +// ----------------------------------------------------------------------------- +// Rule Definition +// ----------------------------------------------------------------------------- + +module.exports = { + meta: { + docs: { + description: 'require or disallow a space before tag\'s closing brackets', + category: undefined + }, + schema: [{ + type: 'object', + properties: { + startTag: { enum: ['always', 'never'] }, + endTag: { enum: ['always', 'never'] }, + selfClosingTag: { enum: ['always', 'never'] } + }, + additionalProperties: false + }], + fixable: 'whitespace' + }, + + create (context) { + const sourceCode = context.getSourceCode() + const tokens = + context.parserServices.getTemplateBodyTokenStore && + context.parserServices.getTemplateBodyTokenStore() + const options = parseOptions(context.options[0], tokens) + + return utils.defineTemplateBodyVisitor(context, { + 'VStartTag, VEndTag' (node) { + const type = options.detectType(node) + const lastToken = tokens.getLastToken(node) + const prevToken = tokens.getLastToken(node, 1) + + // Skip if EOF exists in the tag or linebreak exists before `>`. + if (type == null || prevToken == null || prevToken.loc.end.line !== lastToken.loc.start.line) { + return + } + + // Check and report. + const hasSpace = (prevToken.range[1] !== lastToken.range[0]) + if (type === 'always' && !hasSpace) { + context.report({ + node, + loc: lastToken.loc, + message: "Expected a space before '{{bracket}}', but not found.", + data: { bracket: sourceCode.getText(lastToken) }, + fix: (fixer) => fixer.insertTextBefore(lastToken, ' ') + }) + } else if (type === 'never' && hasSpace) { + context.report({ + node, + loc: { + start: prevToken.loc.end, + end: lastToken.loc.end + }, + message: "Expected no space before '{{bracket}}', but found.", + data: { bracket: sourceCode.getText(lastToken) }, + fix: (fixer) => fixer.removeRange([prevToken.range[1], lastToken.range[0]]) + }) + } + } + }) + } +} diff --git a/tests/lib/rules/html-closing-bracket-spacing.js b/tests/lib/rules/html-closing-bracket-spacing.js new file mode 100644 index 000000000..7663aeaa5 --- /dev/null +++ b/tests/lib/rules/html-closing-bracket-spacing.js @@ -0,0 +1,92 @@ +/** + * @author Toru Nagashima + */ + +'use strict' + +// ----------------------------------------------------------------------------- +// Requirements +// ----------------------------------------------------------------------------- + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../lib/rules/html-closing-bracket-spacing') + +// ----------------------------------------------------------------------------- +// Tests +// ----------------------------------------------------------------------------- + +var ruleTester = new RuleTester({ + parser: 'vue-eslint-parser', + parserOptions: { + ecmaVersion: 2015 + } +}) + +ruleTester.run('html-closing-bracket-spacing', rule, { + valid: [ + '', + '', + '', + '', + '', + { + code: '', + options: [{ startTag: 'always' }] + }, + { + code: '', + options: [{ endTag: 'always' }] + }, + { + code: '', + options: [{ selfClosingTag: 'never' }] + }, + '', + errors: [ + { message: "Expected no space before '>', but found.", line: 2 }, + { message: "Expected no space before '>', but found.", line: 3 }, + { message: "Expected a space before '/>', but not found.", line: 4 } + ] + }, + { + code: '', + errors: [ + { message: "Expected no space before '>', but found.", line: 2 }, + { message: "Expected a space before '/>', but not found.", line: 3 } + ] + }, + { + code: '', + errors: [ + { message: "Expected no space before '>', but found.", line: 2 }, + { message: "Expected a space before '/>', but not found.", line: 3 } + ] + }, + { + code: '', + options: [{ + startTag: 'always', + endTag: 'always', + selfClosingTag: 'never' + }], + errors: [ + { message: "Expected a space before '>', but not found.", line: 2 }, + { message: "Expected a space before '>', but not found.", line: 3 }, + { message: "Expected no space before '/>', but found.", line: 4 } + ] + } + ] +}) From d09d53d2c65f3fbc80bc4e2243bbd50fd6b323f8 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 2 Jan 2018 16:54:58 +0900 Subject: [PATCH 2/4] fix description --- docs/rules/html-closing-bracket-spacing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/html-closing-bracket-spacing.md b/docs/rules/html-closing-bracket-spacing.md index 61dfa4581..52799dd0f 100644 --- a/docs/rules/html-closing-bracket-spacing.md +++ b/docs/rules/html-closing-bracket-spacing.md @@ -2,7 +2,7 @@ - :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 enforces space style before `>` of tags. +This rule enforces consistent spacing style before closing brackets `>` of tags. ```html
or
From 6b5be1221db37494a8839186fb4b306985dcc97c Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 2 Jan 2018 17:00:31 +0900 Subject: [PATCH 3/4] add the check of columns --- .../lib/rules/html-closing-bracket-spacing.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/lib/rules/html-closing-bracket-spacing.js b/tests/lib/rules/html-closing-bracket-spacing.js index 7663aeaa5..3280611d9 100644 --- a/tests/lib/rules/html-closing-bracket-spacing.js +++ b/tests/lib/rules/html-closing-bracket-spacing.js @@ -56,23 +56,23 @@ ruleTester.run('html-closing-bracket-spacing', rule, { { code: '', errors: [ - { message: "Expected no space before '>', but found.", line: 2 }, - { message: "Expected no space before '>', but found.", line: 3 }, - { message: "Expected a space before '/>', but not found.", line: 4 } + { message: "Expected no space before '>', but found.", line: 2, column: 7, endColumn: 9 }, + { message: "Expected no space before '>', but found.", line: 3, column: 8, endColumn: 10 }, + { message: "Expected a space before '/>', but not found.", line: 4, column: 7, endColumn: 9 } ] }, { code: '', errors: [ - { message: "Expected no space before '>', but found.", line: 2 }, - { message: "Expected a space before '/>', but not found.", line: 3 } + { message: "Expected no space before '>', but found.", line: 2, column: 11, endColumn: 13 }, + { message: "Expected a space before '/>', but not found.", line: 3, column: 11, endColumn: 13 } ] }, { code: '', errors: [ - { message: "Expected no space before '>', but found.", line: 2 }, - { message: "Expected a space before '/>', but not found.", line: 3 } + { message: "Expected no space before '>', but found.", line: 2, column: 15, endColumn: 17 }, + { message: "Expected a space before '/>', but not found.", line: 3, column: 15, endColumn: 17 } ] }, { @@ -83,9 +83,9 @@ ruleTester.run('html-closing-bracket-spacing', rule, { selfClosingTag: 'never' }], errors: [ - { message: "Expected a space before '>', but not found.", line: 2 }, - { message: "Expected a space before '>', but not found.", line: 3 }, - { message: "Expected no space before '/>', but found.", line: 4 } + { message: "Expected a space before '>', but not found.", line: 2, column: 7, endColumn: 8 }, + { message: "Expected a space before '>', but not found.", line: 3, column: 8, endColumn: 9 }, + { message: "Expected no space before '/>', but found.", line: 4, column: 7, endColumn: 10 } ] } ] From b3f0d50a397a7e356c4336cc9577ee020af5dda1 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 2 Jan 2018 17:02:13 +0900 Subject: [PATCH 4/4] add autofix check --- tests/lib/rules/html-closing-bracket-spacing.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/lib/rules/html-closing-bracket-spacing.js b/tests/lib/rules/html-closing-bracket-spacing.js index 3280611d9..1609d1cd0 100644 --- a/tests/lib/rules/html-closing-bracket-spacing.js +++ b/tests/lib/rules/html-closing-bracket-spacing.js @@ -55,6 +55,7 @@ ruleTester.run('html-closing-bracket-spacing', rule, { invalid: [ { code: '', + output: '', errors: [ { message: "Expected no space before '>', but found.", line: 2, column: 7, endColumn: 9 }, { message: "Expected no space before '>', but found.", line: 3, column: 8, endColumn: 10 }, @@ -63,6 +64,7 @@ ruleTester.run('html-closing-bracket-spacing', rule, { }, { code: '', + output: '', errors: [ { message: "Expected no space before '>', but found.", line: 2, column: 11, endColumn: 13 }, { message: "Expected a space before '/>', but not found.", line: 3, column: 11, endColumn: 13 } @@ -70,6 +72,7 @@ ruleTester.run('html-closing-bracket-spacing', rule, { }, { code: '', + output: '', errors: [ { message: "Expected no space before '>', but found.", line: 2, column: 15, endColumn: 17 }, { message: "Expected a space before '/>', but not found.", line: 3, column: 15, endColumn: 17 } @@ -77,6 +80,7 @@ ruleTester.run('html-closing-bracket-spacing', rule, { }, { code: '', + output: '', options: [{ startTag: 'always', endTag: 'always',