From d8686114790ab7dea9d1e05e873c2c27d6a63a4e Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Wed, 9 Jan 2019 21:52:11 +0900 Subject: [PATCH] =?UTF-8?q?=E2=AD=90=EF=B8=8FNew:=20Add=20`vue/component-t?= =?UTF-8?q?ags-order`=20rule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/rules/README.md | 1 + docs/rules/component-tags-order.md | 96 +++++++++++ lib/index.js | 1 + lib/rules/component-tags-order.js | 94 +++++++++++ tests/lib/rules/component-tags-order.js | 206 ++++++++++++++++++++++++ 5 files changed, 398 insertions(+) create mode 100644 docs/rules/component-tags-order.md create mode 100644 lib/rules/component-tags-order.js create mode 100644 tests/lib/rules/component-tags-order.js diff --git a/docs/rules/README.md b/docs/rules/README.md index b37c250a0..fe1636909 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -141,6 +141,7 @@ For example: |:--------|:------------|:---| | [vue/array-bracket-spacing](./array-bracket-spacing.md) | enforce consistent spacing inside array brackets | :wrench: | | [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: | +| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | | | [vue/eqeqeq](./eqeqeq.md) | require the use of `===` and `!==` | :wrench: | | [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: | | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | | diff --git a/docs/rules/component-tags-order.md b/docs/rules/component-tags-order.md new file mode 100644 index 000000000..d8c45447b --- /dev/null +++ b/docs/rules/component-tags-order.md @@ -0,0 +1,96 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/component-tags-order +description: enforce order of component top-level elements +--- +# vue/component-tags-order +> enforce order of component top-level elements + +## :book: Rule Details + +This rule warns about the order of the ` + + +``` + + + + + +```vue + + + + +``` + + + +### `{ "order": ["template", "script", "style"] }` + + + +```vue + + + + +``` + + + +### `{ "order": ["docs", "template", "script", "style"] }` + + + +```vue + + documents + + + +``` + + + + + +```vue + + + + documents + +``` + + + +## :books: Further reading + +- [Style guide - Single-file component top-level element order](https://vuejs.org/v2/style-guide/#Single-file-component-top-level-element-order-recommended) + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/component-tags-order.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/component-tags-order.js) diff --git a/lib/index.js b/lib/index.js index bb5b86031..8fefedeb4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,6 +12,7 @@ module.exports = { 'attributes-order': require('./rules/attributes-order'), 'comment-directive': require('./rules/comment-directive'), 'component-name-in-template-casing': require('./rules/component-name-in-template-casing'), + 'component-tags-order': require('./rules/component-tags-order'), 'eqeqeq': require('./rules/eqeqeq'), 'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'), 'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'), diff --git a/lib/rules/component-tags-order.js b/lib/rules/component-tags-order.js new file mode 100644 index 000000000..e7f8f39ac --- /dev/null +++ b/lib/rules/component-tags-order.js @@ -0,0 +1,94 @@ +/** + * @author Yosuke Ota + * issue https://github.com/vuejs/eslint-plugin-vue/issues/140 + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const utils = require('../utils') + +const DEFAULT_ORDER = Object.freeze(['script', 'template', 'style']) + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'enforce order of component top-level elements', + category: undefined, + url: 'https://eslint.vuejs.org/rules/component-tags-order.html' + }, + fixable: null, + schema: { + type: 'array', + properties: { + order: { + type: 'array' + } + } + }, + messages: { + unexpected: 'The <{{name}}> should be above the <{{firstUnorderedName}}> on line {{line}}.' + } + }, + create (context) { + const order = (context.options[0] && context.options[0].order) || DEFAULT_ORDER + + function getTopLevelHTMLElements (node) { + const templateBody = node.templateBody + if (templateBody) { + const document = templateBody.parent + return document.children + } + return [] + } + + function report (element, firstUnorderedElement) { + context.report({ + node: element, + loc: element.loc, + messageId: 'unexpected', + data: { + name: element.name, + firstUnorderedName: firstUnorderedElement.name, + line: firstUnorderedElement.loc.start.line + } + }) + } + + return utils.defineTemplateBodyVisitor( + context, + {}, + { + Program (node) { + if (utils.hasInvalidEOF(node)) { + return + } + const elements = getTopLevelHTMLElements(node) + + elements.forEach((element, index) => { + const expectedIndex = order.indexOf(element.name) + if (expectedIndex < 0) { + return + } + const firstUnordered = elements + .slice(0, index) + .filter(e => expectedIndex < order.indexOf(e.name)) + .sort( + (e1, e2) => order.indexOf(e1.name) - order.indexOf(e2.name) + )[0] + if (firstUnordered) { + report(element, firstUnordered) + } + }) + } + } + ) + } +} diff --git a/tests/lib/rules/component-tags-order.js b/tests/lib/rules/component-tags-order.js new file mode 100644 index 000000000..75702cdff --- /dev/null +++ b/tests/lib/rules/component-tags-order.js @@ -0,0 +1,206 @@ +/** + * @author Yosuke Ota + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/component-tags-order') +const RuleTester = require('eslint').RuleTester + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +const tester = new RuleTester({ + parser: 'vue-eslint-parser' +}) + +tester.run('component-tags-order', rule, { + valid: [ + // default + '', + '', + '', + '', + '', + ` + + + + + + `, + + // order + { + code: '', + output: null, + options: [{ order: ['template', 'script', 'style'] }] + }, + { + code: '', + output: null, + options: [{ order: ['style', 'template', 'script'] }] + }, + { + code: '', + output: null, + options: [{ order: ['template', 'docs', 'script', 'style'] }] + }, + { + code: '', + output: null, + options: [{ order: ['template', 'script', 'style'] }] + }, + { + code: '
text

', + output: null, + options: [{ order: ['docs', 'script', 'template', 'style'] }] + }, + + // No template (can not check) + ``, + ``, + + // Invalid EOF + '