From fd80ba4a2686570620d09af3e06d7f5fdb5184e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sajn=C3=B3g?= Date: Sun, 30 Dec 2018 23:16:57 +0100 Subject: [PATCH 0001/1121] Chore: Add "no-layout-rules" config (#698) * Add 'no-layout-rules' config * Enable eslint-plugin/require-meta-type rule --- .eslintrc.js | 1 + lib/configs/no-layout-rules.js | 21 +++++++++++++ lib/index.js | 5 +-- lib/rules/v-bind-style.js | 2 +- lib/rules/v-on-style.js | 2 +- package.json | 4 +-- tools/lib/configs.js | 15 +++++++++ tools/update-lib-index.js | 4 +-- tools/update-no-layout-rules-config.js | 42 ++++++++++++++++++++++++++ tools/update.js | 1 + 10 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 lib/configs/no-layout-rules.js create mode 100644 tools/lib/configs.js create mode 100644 tools/update-no-layout-rules-config.js diff --git a/.eslintrc.js b/.eslintrc.js index 1f7f299aa..2475d88d6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,6 +28,7 @@ module.exports = { rules: { "consistent-docs-description": "error", "no-invalid-meta": "error", + 'eslint-plugin/require-meta-type': 'error', "require-meta-docs-url": ["error", { "pattern": `https://vuejs.github.io/eslint-plugin-vue/rules/{{name}}.html` }] diff --git a/lib/configs/no-layout-rules.js b/lib/configs/no-layout-rules.js new file mode 100644 index 000000000..052301e5f --- /dev/null +++ b/lib/configs/no-layout-rules.js @@ -0,0 +1,21 @@ +/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update it's content execute "npm run update" + */ +module.exports = { + rules: { + 'vue/html-closing-bracket-newline': 'off', + 'vue/html-closing-bracket-spacing': 'off', + 'vue/html-indent': 'off', + 'vue/html-quotes': 'off', + 'vue/html-self-closing': 'off', + 'vue/max-attributes-per-line': 'off', + 'vue/multiline-html-element-content-newline': 'off', + 'vue/mustache-interpolation-spacing': 'off', + 'vue/no-multi-spaces': 'off', + 'vue/no-spaces-around-equal-signs-in-attribute': 'off', + 'vue/script-indent': 'off', + 'vue/singleline-html-element-content-newline': 'off' + } +} diff --git a/lib/index.js b/lib/index.js index 5952af45a..e6086ac98 100644 --- a/lib/index.js +++ b/lib/index.js @@ -73,8 +73,9 @@ module.exports = { configs: { 'base': require('./configs/base'), 'essential': require('./configs/essential'), - 'strongly-recommended': require('./configs/strongly-recommended'), - 'recommended': require('./configs/recommended') + 'no-layout-rules': require('./configs/no-layout-rules'), + 'recommended': require('./configs/recommended'), + 'strongly-recommended': require('./configs/strongly-recommended') }, processors: { '.vue': require('./processor') diff --git a/lib/rules/v-bind-style.js b/lib/rules/v-bind-style.js index beba436c5..9312a54a2 100644 --- a/lib/rules/v-bind-style.js +++ b/lib/rules/v-bind-style.js @@ -17,7 +17,7 @@ const utils = require('../utils') module.exports = { meta: { - type: 'layout', + type: 'suggestion', docs: { description: 'enforce `v-bind` directive style', category: 'strongly-recommended', diff --git a/lib/rules/v-on-style.js b/lib/rules/v-on-style.js index 7618ec6ea..a7cc3324b 100644 --- a/lib/rules/v-on-style.js +++ b/lib/rules/v-on-style.js @@ -17,7 +17,7 @@ const utils = require('../utils') module.exports = { meta: { - type: 'layout', + type: 'suggestion', docs: { description: 'enforce `v-on` directive style', category: 'strongly-recommended', diff --git a/package.json b/package.json index 09bbdf61e..74d32989e 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,8 @@ "@types/node": "^4.2.16", "babel-eslint": "^8.2.2", "chai": "^4.1.0", - "eslint": "^5.2.0", - "eslint-plugin-eslint-plugin": "^1.4.0", + "eslint": "^5.11.1", + "eslint-plugin-eslint-plugin": "^2.0.1", "eslint-plugin-vue-libs": "^3.0.0", "eslint4b": "^5.1.0", "lodash": "^4.17.4", diff --git a/tools/lib/configs.js b/tools/lib/configs.js new file mode 100644 index 000000000..3eedc5422 --- /dev/null +++ b/tools/lib/configs.js @@ -0,0 +1,15 @@ +/** + * @author Michał Sajnóg + * See LICENSE file in root directory for full license. + */ + +'use strict' + +const fs = require('fs') +const path = require('path') +const ROOT = path.resolve(__dirname, '../../lib/configs') + +module.exports = + fs.readdirSync(ROOT) + .filter(file => path.extname(file) === '.js') + .map(file => path.basename(file, '.js')) diff --git a/tools/update-lib-index.js b/tools/update-lib-index.js index faeb32f33..742b10a02 100644 --- a/tools/update-lib-index.js +++ b/tools/update-lib-index.js @@ -13,7 +13,7 @@ const fs = require('fs') const path = require('path') const eslint = require('eslint') const rules = require('./lib/rules') -const categories = require('./lib/categories') +const configs = require('./lib/configs') // Update files. const filePath = path.resolve(__dirname, '../lib/index.js') @@ -29,7 +29,7 @@ module.exports = { ${rules.map(rule => `'${rule.name}': require('./rules/${rule.name}')`).join(',\n')} }, configs: { - ${categories.map(category => `'${category.categoryId}': require('./configs/${category.categoryId}')`).join(',\n')} + ${configs.map(config => `'${config}': require('./configs/${config}')`).join(',\n')} }, processors: { '.vue': require('./processor') diff --git a/tools/update-no-layout-rules-config.js b/tools/update-no-layout-rules-config.js new file mode 100644 index 000000000..f7b13e3ec --- /dev/null +++ b/tools/update-no-layout-rules-config.js @@ -0,0 +1,42 @@ +/** + * @author Michał Sajnóg + * @copyright 2018 Michał Sajnóg. All rights reserved. + * See LICENSE file in root directory for full license. + */ +'use strict' + +/* + * This script updates `lib/configs/prettier.js`, + * and disables all layout rules + */ + +const fs = require('fs') +const path = require('path') +const rules = require('./lib/rules') + +const rulesToDisable = rules.filter(({ meta }) => meta.type === 'layout') + +function formatRules (rules) { + const obj = rules.reduce((setting, rule) => { + setting[rule.ruleId] = 'off' + return setting + }, {}) + return JSON.stringify(obj, null, 2) +} + +function generateConfig (rules) { + return `/* + * IMPORTANT! + * This file has been automatically generated, + * in order to update it's content execute "npm run update" + */ +module.exports = { + rules: ${formatRules(rules)} +} +` +} + +// Update files. +const filePath = path.resolve(__dirname, '../lib/configs/no-layout-rules.js') +const content = generateConfig(rulesToDisable) +fs.writeFileSync(filePath, content) diff --git a/tools/update.js b/tools/update.js index f6ec8751f..fc1e2e6bd 100644 --- a/tools/update.js +++ b/tools/update.js @@ -5,6 +5,7 @@ */ 'use strict' +require('./update-no-layout-rules-config') require('./update-lib-configs') require('./update-lib-index') require('./update-docs') From 25a878f053fb048f1ed76d319e0099119c027b62 Mon Sep 17 00:00:00 2001 From: michalsnik Date: Wed, 2 Jan 2019 14:51:25 +0100 Subject: [PATCH 0002/1121] Update docs --- docs/rules/README.md | 4 ++-- docs/rules/component-name-in-template-casing.md | 1 - lib/configs/strongly-recommended.js | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/rules/README.md b/docs/rules/README.md index dcc4d9b2a..2c2e43e8c 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -80,7 +80,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | Rule ID | Description | | |:--------|:------------|:---| | [vue/attribute-hyphenation](./attribute-hyphenation.md) | enforce attribute naming style on custom components in template | :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/html-closing-bracket-newline](./html-closing-bracket-newline.md) | require or disallow a line break before tag's closing brackets | :wrench: | | [vue/html-closing-bracket-spacing](./html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets | :wrench: | | [vue/html-end-tags](./html-end-tags.md) | enforce end tag style | :wrench: | @@ -128,13 +127,14 @@ For example: ```json { "rules": { - "vue/script-indent": "error" + "vue/component-name-in-template-casing": "error" } } ``` | Rule ID | Description | | |:--------|:------------|:---| +| [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: | | [vue/script-indent](./script-indent.md) | enforce consistent indentation in ` ``` @@ -54,28 +73,64 @@ This rule aims to warn the tag names other than the configured casing in Vue.js ```vue + ``` +### `"PascalCase", { registeredComponentsOnly: false }` + + + +```vue + + +``` + + -### `"PascalCase", { ignores: ["custom-element"] }` +### `"PascalCase", { ignores: ["/^custom-/"], registeredComponentsOnly: false }` - + ```vue + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "the-component" is not PascalCase.'] }, @@ -109,13 +275,25 @@ tester.run('component-name-in-template-casing', rule, { content + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "the-component" is not PascalCase.'] }, @@ -124,11 +302,23 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "the-component" is not PascalCase.'] }, @@ -139,7 +329,13 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', options: ['kebab-case'], output: ` + `, errors: ['Component name "TheComponent" is not kebab-case.'] }, @@ -156,7 +357,7 @@ tester.run('component-name-in-template-casing', rule, { `, - options: ['kebab-case'], + options: ['kebab-case', { registeredComponentsOnly: false }], output: ` + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "the-component" is not PascalCase.'] }, @@ -184,11 +397,23 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "the-component" is not PascalCase.'] }, @@ -197,11 +422,23 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "the-component" is not PascalCase.'] }, @@ -210,11 +447,23 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "theComponent" is not PascalCase.'] }, @@ -223,12 +472,23 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', options: ['kebab-case'], output: ` + `, errors: ['Component name "theComponent" is not kebab-case.'] }, @@ -237,11 +497,23 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "The-component" is not PascalCase.'] }, @@ -250,12 +522,23 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', options: ['kebab-case'], output: ` + `, errors: ['Component name "The-component" is not kebab-case.'] }, @@ -265,7 +548,7 @@ tester.run('component-name-in-template-casing', rule, { `, - options: ['kebab-case'], + options: ['kebab-case', { registeredComponentsOnly: false }], output: ` + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "the-component" is not PascalCase.'] }, @@ -306,11 +613,23 @@ tester.run('component-name-in-template-casing', rule, { + `, + filename: 'test.vue', + options: ['PascalCase'], output: ` + `, errors: ['Component name "the-component" is not PascalCase.'] }, @@ -319,18 +638,28 @@ tester.run('component-name-in-template-casing', rule, { { code: ` `, output: ` `, - options: ['PascalCase', { ignores: ['custom-element'] }], - errors: ['Component name "the-component" is not PascalCase.'] + options: ['PascalCase', { + ignores: ['custom-element1', 'custom-element2'], + registeredComponentsOnly: false + }], + errors: [ + 'Component name "the-component" is not PascalCase.', + 'Component name "the-component" is not PascalCase.' + ] }, { code: ` @@ -349,7 +678,10 @@ tester.run('component-name-in-template-casing', rule, { `, - options: ['PascalCase', { ignores: ['custom-element1', 'custom-element2'] }], + options: ['PascalCase', { + ignores: ['/^custom-element/'], + registeredComponentsOnly: false + }], errors: [ 'Component name "the-component" is not PascalCase.', 'Component name "the-component" is not PascalCase.' diff --git a/tests/lib/utils/regexp.js b/tests/lib/utils/regexp.js new file mode 100644 index 000000000..8e38a9687 --- /dev/null +++ b/tests/lib/utils/regexp.js @@ -0,0 +1,40 @@ +'use strict' + +const { escape, toRegExp } = require('../../../lib/utils/regexp') +const chai = require('chai') + +const assert = chai.assert + +const ESCAPED = '\\^\\$\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\\\' +const UNESCAPED = '^$.*+?()[]{}|\\' + +describe('escape()', () => { + it('should escape values', () => { + assert.strictEqual(escape(UNESCAPED), ESCAPED) + assert.strictEqual(escape(UNESCAPED + UNESCAPED), ESCAPED + ESCAPED) + }) + + it('should handle strings with nothing to escape', () => { + assert.strictEqual(escape('abc'), 'abc') + }) + + it('should return an empty string for empty values', () => { + assert.strictEqual(escape(null), null) + assert.strictEqual(escape(undefined), undefined) + assert.strictEqual(escape(''), '') + }) +}) + +describe('toRegExp()', () => { + it('should be convert to RegExp', () => { + assert.deepEqual(toRegExp('foo'), /^foo$/) + assert.deepEqual(toRegExp(UNESCAPED), new RegExp(`^${ESCAPED}$`)) + }) + + it('RegExp like string should be convert to RegExp', () => { + assert.deepEqual(toRegExp('/^foo/i'), /^foo/i) + assert.deepEqual(toRegExp('/.*/iu'), /.*/iu) + assert.deepEqual(toRegExp(`${/^bar/i}`), /^bar/i) + assert.deepEqual(toRegExp(`${/[\sA-Z]+/u}`), /[\sA-Z]+/u) + }) +}) From 9edbf2f395b19e4431dd169573be1c5b91c260b7 Mon Sep 17 00:00:00 2001 From: michalsnik Date: Fri, 4 Jan 2019 16:20:05 +0100 Subject: [PATCH 0009/1121] =?UTF-8?q?Revert=20"Revert=20"=E2=AD=90?= =?UTF-8?q?=EF=B8=8FNew:=20Add=20`vue/match-component-file-name`=20rule=20?= =?UTF-8?q?(#668)""?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f4eef7801e70b457c9b8d661328018eb4f909427. --- docs/rules/README.md | 1 + docs/rules/match-component-file-name.md | 215 ++++++ lib/index.js | 1 + lib/rules/match-component-file-name.js | 141 ++++ tests/lib/rules/match-component-file-name.js | 729 +++++++++++++++++++ 5 files changed, 1087 insertions(+) create mode 100644 docs/rules/match-component-file-name.md create mode 100644 lib/rules/match-component-file-name.js create mode 100644 tests/lib/rules/match-component-file-name.js diff --git a/docs/rules/README.md b/docs/rules/README.md index 2c2e43e8c..0afebeedf 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -135,6 +135,7 @@ For example: | Rule ID | Description | | |:--------|:------------|:---| | [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: | +| [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | | | [vue/script-indent](./script-indent.md) | enforce consistent indentation in ` +``` + +```js +// file name: src/MyComponent.js +// options: {extensions: ["js"]} +new Vue({ + name: 'MComponent', + template: '
' +}) +``` + +```js +// file name: src/MyComponent.js +// options: {extensions: ["js"]} +Vue.component('MComponent', { + template: '
' +}) +``` + +```jsx +// file name: src/MyComponent.jsx +// options: {shouldMatchCase: true} +export default { + name: 'my-component', + render() { return
} +} +``` + +```jsx +// file name: src/my-component.jsx +// options: {shouldMatchCase: true} +export default { + name: 'MyComponent', + render() { return
} +} +``` + +:+1: Examples of **correct** code for this rule: + +```jsx +// file name: src/MyComponent.jsx +export default { + name: 'MyComponent', + render: () { + return

Hello world

+ } +} +``` + +```jsx +// file name: src/MyComponent.jsx +// no name property defined +export default { + render: () { + return

Hello world

+ } +} +``` + +```vue +// file name: src/MyComponent.vue + +``` + +```vue +// file name: src/MyComponent.vue + +``` + +```js +// file name: src/MyComponent.js +new Vue({ + name: 'MyComponent', + template: '
' +}) +``` + +```js +// file name: src/MyComponent.js +new Vue({ + template: '
' +}) +``` + +```js +// file name: src/MyComponent.js +Vue.component('MyComponent', { + template: '
' +}) +``` + +```js +// file name: src/components.js +// defines multiple components, so this rule is ignored +Vue.component('MyComponent', { + template: '
' +}) + +Vue.component('OtherComponent', { + template: '
' +}) + +new Vue({ + name: 'ThirdComponent', + template: '
' +}) +``` + +```jsx +// file name: src/MyComponent.jsx +// options: {shouldMatchCase: true} +export default { + name: 'MyComponent', + render() { return
} +} +``` + +```jsx +// file name: src/my-component.jsx +// options: {shouldMatchCase: true} +export default { + name: 'my-component', + render() { return
} +} +``` + +## :wrench: Options + +```json +{ + "vue/match-component-file-name": ["error", { + "extensions": ["jsx"], + "shouldMatchCase": false + }] +} +``` + +- `"extensions": []` ... array of file extensions to be verified. Default is set to `["jsx"]`. +- `"shouldMatchCase": false` ... boolean indicating if component's name + should also match its file name case. Default is set to `false`. + +## :books: Further reading + + - [Style guide - Single-file component filename casing](https://vuejs.org/v2/style-guide/#Single-file-component-filename-casing-strongly-recommended) + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/match-component-file-name.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/match-component-file-name.js) diff --git a/lib/index.js b/lib/index.js index e6086ac98..b74b3dc8f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,6 +18,7 @@ module.exports = { 'html-quotes': require('./rules/html-quotes'), 'html-self-closing': require('./rules/html-self-closing'), 'jsx-uses-vars': require('./rules/jsx-uses-vars'), + 'match-component-file-name': require('./rules/match-component-file-name'), 'max-attributes-per-line': require('./rules/max-attributes-per-line'), 'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'), 'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'), diff --git a/lib/rules/match-component-file-name.js b/lib/rules/match-component-file-name.js new file mode 100644 index 000000000..7eac356a3 --- /dev/null +++ b/lib/rules/match-component-file-name.js @@ -0,0 +1,141 @@ +/** + * @fileoverview Require component name property to match its file name + * @author Rodrigo Pedra Brum + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const utils = require('../utils') +const casing = require('../utils/casing') +const path = require('path') + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'require component name property to match its file name', + category: undefined, + url: 'https://eslint.vuejs.org/rules/match-component-file-name.html' + }, + fixable: null, + schema: [ + { + type: 'object', + properties: { + extensions: { + type: 'array', + items: { + type: 'string' + }, + uniqueItems: true, + additionalItems: false + }, + shouldMatchCase: { + type: 'boolean' + } + }, + additionalProperties: false + } + ] + }, + + create (context) { + const options = context.options[0] + const shouldMatchCase = (options && options.shouldMatchCase) || false + const extensionsArray = options && options.extensions + const allowedExtensions = Array.isArray(extensionsArray) ? extensionsArray : ['jsx'] + + const extension = path.extname(context.getFilename()) + const filename = path.basename(context.getFilename(), extension) + + const errors = [] + let componentCount = 0 + + if (!allowedExtensions.includes(extension.replace(/^\./, ''))) { + return {} + } + + // ---------------------------------------------------------------------- + // Private + // ---------------------------------------------------------------------- + + function compareNames (name, filename) { + if (shouldMatchCase) { + return name === filename + } + + return casing.pascalCase(name) === filename || casing.kebabCase(name) === filename + } + + function verifyName (node) { + let name + if (node.type === 'TemplateLiteral') { + const quasis = node.quasis[0] + name = quasis.value.cooked + } else { + name = node.value + } + + if (!compareNames(name, filename)) { + errors.push({ + node: node, + message: 'Component name `{{name}}` should match file name `{{filename}}`.', + data: { filename, name } + }) + } + } + + function canVerify (node) { + return node.type === 'Literal' || ( + node.type === 'TemplateLiteral' && + node.expressions.length === 0 && + node.quasis.length === 1 + ) + } + + return Object.assign({}, + { + "CallExpression > MemberExpression > Identifier[name='component']" (node) { + const parent = node.parent.parent + const calleeObject = utils.unwrapTypes(parent.callee.object) + + if (calleeObject.type === 'Identifier' && calleeObject.name === 'Vue') { + if (parent.arguments && parent.arguments.length === 2) { + const argument = parent.arguments[0] + if (canVerify(argument)) { + verifyName(argument) + } + } + } + } + }, + utils.executeOnVue(context, (object) => { + const node = object.properties + .find(item => ( + item.type === 'Property' && + item.key.name === 'name' && + canVerify(item.value) + )) + + componentCount++ + + if (!node) return + verifyName(node.value) + }), + { + 'Program:exit' () { + if (componentCount > 1) return + + errors.forEach((error) => context.report(error)) + } + } + ) + } +} diff --git a/tests/lib/rules/match-component-file-name.js b/tests/lib/rules/match-component-file-name.js new file mode 100644 index 000000000..1a56e45ee --- /dev/null +++ b/tests/lib/rules/match-component-file-name.js @@ -0,0 +1,729 @@ +/** + * @fileoverview Require component name property to match its file name + * @author Rodrigo Pedra Brum + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/match-component-file-name') +const RuleTester = require('eslint').RuleTester + +const jsxParserOptions = { + ecmaVersion: 2018, + sourceType: 'module', + ecmaFeatures: { jsx: true } +} + +const parserOptions = { + ecmaVersion: 2018, + sourceType: 'module' +} + +const ruleTester = new RuleTester() + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +ruleTester.run('match-component-file-name', rule, { + valid: [ + // .jsx + { + filename: 'MyComponent.jsx', + code: ` + export default { + render() { return
} + } + `, + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: 'MyComponent', + render() { return
} + } + `, + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: 'MyComponent', + render() { return
} + } + `, + options: [{ extensions: ['jsx'] }], + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: myComponent, + render() { return
} + } + `, + options: [{ extensions: ['jsx'] }], + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name, + render() { return
} + } + `, + options: [{ extensions: ['jsx'] }], + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: \`MyComponent\`, + render() { return
} + } + `, + options: [{ extensions: ['jsx'] }], + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: \`My\${foo}\`, + render() { return
} + } + `, + options: [{ extensions: ['jsx'] }], + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: 'MComponent', + render() { return
} + } + `, + options: [{ extensions: ['vue'] }], // missing jsx in options + parserOptions: jsxParserOptions + }, + + // .vue + { + filename: 'MyComponent.vue', + code: ` + + `, + parser: 'vue-eslint-parser', + parserOptions // options default to [['jsx']] + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['jsx'] }], // missing jsx in options + parser: 'vue-eslint-parser', + parserOptions + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions + }, + + // .js + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name: 'MComponent', + template: '
' + }) + `, + parserOptions // options default to [['jsx']] + }, + { + filename: 'MyComponent.js', + code: ` + Vue.mixin({}) + `, + parserOptions // options default to [['jsx']] + }, + { + filename: 'MyComponent.js', + code: ` + Vue.component('MComponent', { + template: '
' + }) + `, + parserOptions // options default to [['jsx']] + }, + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name: 'MComponent', + template: '
' + }) + `, + options: [{ extensions: ['vue'] }], // missing 'js' in options + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + Vue.mixin({}) + `, + options: [{ extensions: ['vue'] }], // missing 'js' in options + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + Vue.component('MComponent', { + template: '
' + }) + `, + options: [{ extensions: ['vue'] }], // missing 'js' in options + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue({ + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name: 'MyComponent', + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name: myComponent, + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name, + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name: \`MyComponent\`, + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name: \`My\${foo}\`, + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + Vue.mixin({}) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue.mixin({ + name: 'MyComponent', + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue.mixin({ + name: myComponent, + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue.mixin({ + name + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue.mixin({ + name: \`MyComponent\`, + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + new Vue.mixin({ + name: \`My\${foo}\`, + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + Vue.component('MyComponent', { + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + Vue.component(myComponent, { + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + Vue.component(\`MyComponent\`, { + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'MyComponent.js', + code: ` + Vue.component(\`My\${foo}\`, { + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + { + filename: 'index.js', + code: ` + Vue.component('MyComponent', { + template: '
' + }) + + Vue.component('OtherComponent', { + template: '
' + }) + + new Vue('OtherComponent', { + name: 'ThirdComponent', + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions + }, + + // casing + { + filename: 'my-component.jsx', + code: ` + export default { + name: 'my-component', + render() { return
} + } + `, + parserOptions: jsxParserOptions + }, + { + filename: 'my-component.jsx', + code: ` + export default { + name: 'MyComponent', + render() { return
} + } + `, + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: 'my-component', + render() { return
} + } + `, + parserOptions: jsxParserOptions + }, + { + filename: 'my-component.jsx', + code: ` + export default { + name: 'my-component', + render() { return
} + } + `, + options: [{ shouldMatchCase: true }], + parserOptions: jsxParserOptions + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: 'MyComponent', + render() { return
} + } + `, + options: [{ shouldMatchCase: true }], + parserOptions: jsxParserOptions + } + ], + + invalid: [ + // .jsx + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: 'MComponent', + render() { return
} + } + `, + parserOptions: jsxParserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: 'MComponent', + render() { return
} + } + `, + options: [{ extensions: ['jsx'] }], + parserOptions: jsxParserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: \`MComponent\`, + render() { return
} + } + `, + options: [{ extensions: ['jsx'] }], + parserOptions: jsxParserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + + // .vue + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + { + filename: 'MyComponent.vue', + code: ` + + `, + options: [{ extensions: ['vue'] }], + parser: 'vue-eslint-parser', + parserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + + // .js + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name: 'MComponent', + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + { + filename: 'MyComponent.js', + code: ` + new Vue({ + name: \`MComponent\`, + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + { + filename: 'MyComponent.js', + code: ` + Vue.mixin({ + name: 'MComponent', + }) + `, + options: [{ extensions: ['js'] }], + parserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + { + filename: 'MyComponent.js', + code: ` + Vue.mixin({ + name: \`MComponent\`, + }) + `, + options: [{ extensions: ['js'] }], + parserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + { + filename: 'MyComponent.js', + code: ` + Vue.component('MComponent', { + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + { + filename: 'MyComponent.js', + code: ` + Vue.component(\`MComponent\`, { + template: '
' + }) + `, + options: [{ extensions: ['js'] }], + parserOptions, + errors: [{ + message: 'Component name `MComponent` should match file name `MyComponent`.' + }] + }, + + // casing + { + filename: 'MyComponent.jsx', + code: ` + export default { + name: 'my-component', + render() { return
} + } + `, + options: [{ shouldMatchCase: true }], + parserOptions: jsxParserOptions, + errors: [{ + message: 'Component name `my-component` should match file name `MyComponent`.' + }] + }, + { + filename: 'my-component.jsx', + code: ` + export default { + name: 'MyComponent', + render() { return
} + } + `, + options: [{ shouldMatchCase: true }], + parserOptions: jsxParserOptions, + errors: [{ + message: 'Component name `MyComponent` should match file name `my-component`.' + }] + } + ] +}) From 9bf6098ec120e08cb6d71d184d24cc5c7755d293 Mon Sep 17 00:00:00 2001 From: michalsnik Date: Sat, 5 Jan 2019 00:19:53 +0100 Subject: [PATCH 0010/1121] chore(docs): Add legend on rules listing --- docs/rules/README.md | 5 +++++ tools/update-docs-rules-index.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/docs/rules/README.md b/docs/rules/README.md index 0afebeedf..c548e5163 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -6,6 +6,11 @@ sidebarDepth: 0 # Available rules +::: tip Legend + :wrench: Indicates that the rule is fixable, and using `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the reported problems. +::: + + ## Base Rules (Enabling Correct ESLint Parsing) Enforce all the rules in this category, as well as all higher priority rules, with: diff --git a/tools/update-docs-rules-index.js b/tools/update-docs-rules-index.js index 7d0799cd1..db8d6f487 100644 --- a/tools/update-docs-rules-index.js +++ b/tools/update-docs-rules-index.js @@ -97,5 +97,10 @@ sidebarDepth: 0 # Available rules + +::: tip Legend + :wrench: Indicates that the rule is fixable, and using \`--fix\` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the reported problems. +::: + ${rulesTableContent}` ) From 5dd07bf396d8ef2192074fef92a7bd3075e8ee1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sajn=C3=B3g?= Date: Sat, 5 Jan 2019 15:14:33 +0100 Subject: [PATCH 0011/1121] fix(use-v-on-exact): Reimplement algorithm to catch cases more properly (#750) * Reimplement use-v-on-exact rule * Document methods in use-v-on-exact * Check only system modifiers --- lib/rules/use-v-on-exact.js | 171 +++++++++++++++++++---- lib/rules/valid-v-on.js | 70 +--------- lib/utils/key-aliases.json | 68 ++++++++++ tests/lib/rules/use-v-on-exact.js | 218 ++++++++++++++++++++++++++++-- 4 files changed, 426 insertions(+), 101 deletions(-) create mode 100644 lib/utils/key-aliases.json diff --git a/lib/rules/use-v-on-exact.js b/lib/rules/use-v-on-exact.js index 0dafad708..3e110ec2a 100644 --- a/lib/rules/use-v-on-exact.js +++ b/lib/rules/use-v-on-exact.js @@ -10,8 +10,124 @@ const utils = require('../utils') +const SYSTEM_MODIFIERS = new Set(['ctrl', 'shift', 'alt', 'meta']) + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +/** + * Finds and returns all keys for event directives + * + * @param {array} attributes Element attributes + * @returns {array[object]} [{ name, node, modifiers }] + */ +function getEventDirectives (attributes) { + return attributes + .filter(attribute => + attribute.directive && + attribute.key.name === 'on' + ) + .map(attribute => ({ + name: attribute.key.argument, + node: attribute.key, + modifiers: attribute.key.modifiers + })) +} + +/** + * Checks whether given modifier is system one + * + * @param {string} modifier + * @returns {boolean} + */ +function isSystemModifier (modifier) { + return SYSTEM_MODIFIERS.has(modifier) +} + +/** + * Checks whether given any of provided modifiers + * has system modifier + * + * @param {array} modifiers + * @returns {boolean} + */ +function hasSystemModifier (modifiers) { + return modifiers.some(isSystemModifier) +} + +/** + * Groups all events in object, + * with keys represinting each event name + * + * @param {array} events + * @returns {object} { click: [], keypress: [] } + */ +function groupEvents (events) { + return events.reduce((acc, event) => { + if (acc[event.name]) { + acc[event.name].push(event) + } else { + acc[event.name] = [event] + } + + return acc + }, {}) +} + +/** + * Creates alphabetically sorted string with system modifiers + * + * @param {array[string]} modifiers + * @returns {string} e.g. "alt,ctrl,del,shift" + */ +function getSystemModifiersString (modifiers) { + return modifiers.filter(isSystemModifier).sort().join(',') +} + +/** + * Compares two events based on their modifiers + * to detect possible event leakeage + * + * @param {object} baseEvent + * @param {object} event + * @returns {boolean} + */ +function hasConflictedModifiers (baseEvent, event) { + if ( + event.node === baseEvent.node || + event.modifiers.includes('exact') + ) return false + + const eventModifiers = getSystemModifiersString(event.modifiers) + const baseEventModifiers = getSystemModifiersString(baseEvent.modifiers) + + return ( + baseEvent.modifiers.length >= 1 && + baseEventModifiers !== eventModifiers && + baseEventModifiers.indexOf(eventModifiers) > -1 + ) +} + +/** + * Searches for events that might conflict with each other + * + * @param {array} events + * @returns {array} conflicted events, without duplicates + */ +function findConflictedEvents (events) { + return events.reduce((acc, event) => { + return [ + ...acc, + ...events + .filter(evt => !acc.find(e => evt === e)) // No duplicates + .filter(hasConflictedModifiers.bind(null, event)) + ] + }, []) +} + // ------------------------------------------------------------------------------ -// Rule Definition +// Rule details // ------------------------------------------------------------------------------ module.exports = { @@ -35,31 +151,36 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { VStartTag (node) { - if (node.attributes.length > 1) { - const groups = node.attributes - .map(item => item.key) - .filter(item => item && item.type === 'VDirectiveKey' && item.name === 'on') - .reduce((rv, item) => { - (rv[item.argument] = rv[item.argument] || []).push(item) - return rv - }, {}) - - const directives = Object.keys(groups).map(key => groups[key]) - // const directives = Object.values(groups) // Uncomment after node 6 is dropped - .filter(item => item.length > 1) - for (const group of directives) { - if (group.some(item => item.modifiers.length > 0)) { // check if there are directives with modifiers - const invalid = group.filter(item => item.modifiers.length === 0) - for (const node of invalid) { - context.report({ - node, - loc: node.loc, - message: "Consider to use '.exact' modifier." - }) - } - } - } + if (node.attributes.length === 0) return + + const isCustomComponent = utils.isCustomComponent(node.parent) + let events = getEventDirectives(node.attributes) + + if (isCustomComponent) { + // For components consider only events with `native` modifier + events = events.filter(event => event.modifiers.includes('native')) } + + const grouppedEvents = groupEvents(events) + + Object.keys(grouppedEvents).forEach(eventName => { + const eventsInGroup = grouppedEvents[eventName] + const hasEventWithKeyModifier = eventsInGroup.some(event => + hasSystemModifier(event.modifiers) + ) + + if (!hasEventWithKeyModifier) return + + const conflictedEvents = findConflictedEvents(eventsInGroup) + + conflictedEvents.forEach(e => { + context.report({ + node: e.node, + loc: e.node.loc, + message: "Consider to use '.exact' modifier." + }) + }) + }) } }) } diff --git a/lib/rules/valid-v-on.js b/lib/rules/valid-v-on.js index 333739053..76034844d 100644 --- a/lib/rules/valid-v-on.js +++ b/lib/rules/valid-v-on.js @@ -10,6 +10,7 @@ // ------------------------------------------------------------------------------ const utils = require('../utils') +const keyAliases = require('../utils/key-aliases.json') // ------------------------------------------------------------------------------ // Helpers @@ -24,74 +25,7 @@ const VERB_MODIFIERS = new Set([ 'stop', 'prevent' ]) // https://www.w3.org/TR/uievents-key/ -const KEY_ALIASES = new Set([ - 'unidentified', 'alt', 'alt-graph', 'caps-lock', 'control', 'fn', 'fn-lock', - 'meta', 'num-lock', 'scroll-lock', 'shift', 'symbol', 'symbol-lock', 'hyper', - 'super', 'enter', 'tab', 'arrow-down', 'arrow-left', 'arrow-right', - 'arrow-up', 'end', 'home', 'page-down', 'page-up', 'backspace', 'clear', - 'copy', 'cr-sel', 'cut', 'delete', 'erase-eof', 'ex-sel', 'insert', 'paste', - 'redo', 'undo', 'accept', 'again', 'attn', 'cancel', 'context-menu', 'escape', - 'execute', 'find', 'help', 'pause', 'select', 'zoom-in', 'zoom-out', - 'brightness-down', 'brightness-up', 'eject', 'log-off', 'power', - 'print-screen', 'hibernate', 'standby', 'wake-up', 'all-candidates', - 'alphanumeric', 'code-input', 'compose', 'convert', 'dead', 'final-mode', - 'group-first', 'group-last', 'group-next', 'group-previous', 'mode-change', - 'next-candidate', 'non-convert', 'previous-candidate', 'process', - 'single-candidate', 'hangul-mode', 'hanja-mode', 'junja-mode', 'eisu', - 'hankaku', 'hiragana', 'hiragana-katakana', 'kana-mode', 'kanji-mode', - 'katakana', 'romaji', 'zenkaku', 'zenkaku-hankaku', 'f1', 'f2', 'f3', 'f4', - 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12', 'soft1', 'soft2', 'soft3', - 'soft4', 'channel-down', 'channel-up', 'close', 'mail-forward', 'mail-reply', - 'mail-send', 'media-close', 'media-fast-forward', 'media-pause', - 'media-play-pause', 'media-record', 'media-rewind', 'media-stop', - 'media-track-next', 'media-track-previous', 'new', 'open', 'print', 'save', - 'spell-check', 'key11', 'key12', 'audio-balance-left', 'audio-balance-right', - 'audio-bass-boost-down', 'audio-bass-boost-toggle', 'audio-bass-boost-up', - 'audio-fader-front', 'audio-fader-rear', 'audio-surround-mode-next', - 'audio-treble-down', 'audio-treble-up', 'audio-volume-down', - 'audio-volume-up', 'audio-volume-mute', 'microphone-toggle', - 'microphone-volume-down', 'microphone-volume-up', 'microphone-volume-mute', - 'speech-correction-list', 'speech-input-toggle', 'launch-application1', - 'launch-application2', 'launch-calendar', 'launch-contacts', 'launch-mail', - 'launch-media-player', 'launch-music-player', 'launch-phone', - 'launch-screen-saver', 'launch-spreadsheet', 'launch-web-browser', - 'launch-web-cam', 'launch-word-processor', 'browser-back', - 'browser-favorites', 'browser-forward', 'browser-home', 'browser-refresh', - 'browser-search', 'browser-stop', 'app-switch', 'call', 'camera', - 'camera-focus', 'end-call', 'go-back', 'go-home', 'headset-hook', - 'last-number-redial', 'notification', 'manner-mode', 'voice-dial', 't-v', - 't-v3-d-mode', 't-v-antenna-cable', 't-v-audio-description', - 't-v-audio-description-mix-down', 't-v-audio-description-mix-up', - 't-v-contents-menu', 't-v-data-service', 't-v-input', 't-v-input-component1', - 't-v-input-component2', 't-v-input-composite1', 't-v-input-composite2', - 't-v-input-h-d-m-i1', 't-v-input-h-d-m-i2', 't-v-input-h-d-m-i3', - 't-v-input-h-d-m-i4', 't-v-input-v-g-a1', 't-v-media-context', 't-v-network', - 't-v-number-entry', 't-v-power', 't-v-radio-service', 't-v-satellite', - 't-v-satellite-b-s', 't-v-satellite-c-s', 't-v-satellite-toggle', - 't-v-terrestrial-analog', 't-v-terrestrial-digital', 't-v-timer', - 'a-v-r-input', 'a-v-r-power', 'color-f0-red', 'color-f1-green', - 'color-f2-yellow', 'color-f3-blue', 'color-f4-grey', 'color-f5-brown', - 'closed-caption-toggle', 'dimmer', 'display-swap', 'd-v-r', 'exit', - 'favorite-clear0', 'favorite-clear1', 'favorite-clear2', 'favorite-clear3', - 'favorite-recall0', 'favorite-recall1', 'favorite-recall2', - 'favorite-recall3', 'favorite-store0', 'favorite-store1', 'favorite-store2', - 'favorite-store3', 'guide', 'guide-next-day', 'guide-previous-day', 'info', - 'instant-replay', 'link', 'list-program', 'live-content', 'lock', - 'media-apps', 'media-last', 'media-skip-backward', 'media-skip-forward', - 'media-step-backward', 'media-step-forward', 'media-top-menu', 'navigate-in', - 'navigate-next', 'navigate-out', 'navigate-previous', 'next-favorite-channel', - 'next-user-profile', 'on-demand', 'pairing', 'pin-p-down', 'pin-p-move', - 'pin-p-toggle', 'pin-p-up', 'play-speed-down', 'play-speed-reset', - 'play-speed-up', 'random-toggle', 'rc-low-battery', 'record-speed-next', - 'rf-bypass', 'scan-channels-toggle', 'screen-mode-next', 'settings', - 'split-screen-toggle', 's-t-b-input', 's-t-b-power', 'subtitle', 'teletext', - 'video-mode-next', 'wink', 'zoom-toggle', 'audio-volume-down', - 'audio-volume-up', 'audio-volume-mute', 'browser-back', 'browser-forward', - 'channel-down', 'channel-up', 'context-menu', 'eject', 'end', 'enter', 'home', - 'media-fast-forward', 'media-play', 'media-play-pause', 'media-record', - 'media-rewind', 'media-stop', 'media-next-track', 'media-pause', - 'media-previous-track', 'power', 'unidentified' -]) +const KEY_ALIASES = new Set(keyAliases) function isValidModifier (modifier, customModifiers) { return ( diff --git a/lib/utils/key-aliases.json b/lib/utils/key-aliases.json new file mode 100644 index 000000000..49de3b3d8 --- /dev/null +++ b/lib/utils/key-aliases.json @@ -0,0 +1,68 @@ +[ + "unidentified", "alt", "alt-graph", "caps-lock", "control", "fn", "fn-lock", + "meta", "num-lock", "scroll-lock", "shift", "symbol", "symbol-lock", "hyper", + "super", "enter", "tab", "arrow-down", "arrow-left", "arrow-right", + "arrow-up", "end", "home", "page-down", "page-up", "backspace", "clear", + "copy", "cr-sel", "cut", "delete", "erase-eof", "ex-sel", "insert", "paste", + "redo", "undo", "accept", "again", "attn", "cancel", "context-menu", "escape", + "execute", "find", "help", "pause", "select", "zoom-in", "zoom-out", + "brightness-down", "brightness-up", "eject", "log-off", "power", + "print-screen", "hibernate", "standby", "wake-up", "all-candidates", + "alphanumeric", "code-input", "compose", "convert", "dead", "final-mode", + "group-first", "group-last", "group-next", "group-previous", "mode-change", + "next-candidate", "non-convert", "previous-candidate", "process", + "single-candidate", "hangul-mode", "hanja-mode", "junja-mode", "eisu", + "hankaku", "hiragana", "hiragana-katakana", "kana-mode", "kanji-mode", + "katakana", "romaji", "zenkaku", "zenkaku-hankaku", "f1", "f2", "f3", "f4", + "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "soft1", "soft2", "soft3", + "soft4", "channel-down", "channel-up", "close", "mail-forward", "mail-reply", + "mail-send", "media-close", "media-fast-forward", "media-pause", + "media-play-pause", "media-record", "media-rewind", "media-stop", + "media-track-next", "media-track-previous", "new", "open", "print", "save", + "spell-check", "key11", "key12", "audio-balance-left", "audio-balance-right", + "audio-bass-boost-down", "audio-bass-boost-toggle", "audio-bass-boost-up", + "audio-fader-front", "audio-fader-rear", "audio-surround-mode-next", + "audio-treble-down", "audio-treble-up", "audio-volume-down", + "audio-volume-up", "audio-volume-mute", "microphone-toggle", + "microphone-volume-down", "microphone-volume-up", "microphone-volume-mute", + "speech-correction-list", "speech-input-toggle", "launch-application1", + "launch-application2", "launch-calendar", "launch-contacts", "launch-mail", + "launch-media-player", "launch-music-player", "launch-phone", + "launch-screen-saver", "launch-spreadsheet", "launch-web-browser", + "launch-web-cam", "launch-word-processor", "browser-back", + "browser-favorites", "browser-forward", "browser-home", "browser-refresh", + "browser-search", "browser-stop", "app-switch", "call", "camera", + "camera-focus", "end-call", "go-back", "go-home", "headset-hook", + "last-number-redial", "notification", "manner-mode", "voice-dial", "t-v", + "t-v3-d-mode", "t-v-antenna-cable", "t-v-audio-description", + "t-v-audio-description-mix-down", "t-v-audio-description-mix-up", + "t-v-contents-menu", "t-v-data-service", "t-v-input", "t-v-input-component1", + "t-v-input-component2", "t-v-input-composite1", "t-v-input-composite2", + "t-v-input-h-d-m-i1", "t-v-input-h-d-m-i2", "t-v-input-h-d-m-i3", + "t-v-input-h-d-m-i4", "t-v-input-v-g-a1", "t-v-media-context", "t-v-network", + "t-v-number-entry", "t-v-power", "t-v-radio-service", "t-v-satellite", + "t-v-satellite-b-s", "t-v-satellite-c-s", "t-v-satellite-toggle", + "t-v-terrestrial-analog", "t-v-terrestrial-digital", "t-v-timer", + "a-v-r-input", "a-v-r-power", "color-f0-red", "color-f1-green", + "color-f2-yellow", "color-f3-blue", "color-f4-grey", "color-f5-brown", + "closed-caption-toggle", "dimmer", "display-swap", "d-v-r", "exit", + "favorite-clear0", "favorite-clear1", "favorite-clear2", "favorite-clear3", + "favorite-recall0", "favorite-recall1", "favorite-recall2", + "favorite-recall3", "favorite-store0", "favorite-store1", "favorite-store2", + "favorite-store3", "guide", "guide-next-day", "guide-previous-day", "info", + "instant-replay", "link", "list-program", "live-content", "lock", + "media-apps", "media-last", "media-skip-backward", "media-skip-forward", + "media-step-backward", "media-step-forward", "media-top-menu", "navigate-in", + "navigate-next", "navigate-out", "navigate-previous", "next-favorite-channel", + "next-user-profile", "on-demand", "pairing", "pin-p-down", "pin-p-move", + "pin-p-toggle", "pin-p-up", "play-speed-down", "play-speed-reset", + "play-speed-up", "random-toggle", "rc-low-battery", "record-speed-next", + "rf-bypass", "scan-channels-toggle", "screen-mode-next", "settings", + "split-screen-toggle", "s-t-b-input", "s-t-b-power", "subtitle", "teletext", + "video-mode-next", "wink", "zoom-toggle", "audio-volume-down", + "audio-volume-up", "audio-volume-mute", "browser-back", "browser-forward", + "channel-down", "channel-up", "context-menu", "eject", "end", "enter", "home", + "media-fast-forward", "media-play", "media-play-pause", "media-record", + "media-rewind", "media-stop", "media-next-track", "media-pause", + "media-previous-track", "power", "unidentified" +] diff --git a/tests/lib/rules/use-v-on-exact.js b/tests/lib/rules/use-v-on-exact.js index e93a46601..be90196b1 100644 --- a/tests/lib/rules/use-v-on-exact.js +++ b/tests/lib/rules/use-v-on-exact.js @@ -36,9 +36,6 @@ ruleTester.run('use-v-on-exact', rule, { { code: `` }, - { - code: `` - }, { code: `` }, @@ -49,7 +46,25 @@ ruleTester.run('use-v-on-exact', rule, { code: `` }, { - code: `` + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` }, { code: `` @@ -59,17 +74,204 @@ ruleTester.run('use-v-on-exact', rule, { }, { code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` + }, + { + code: `` } ], invalid: [ { - code: ``, - errors: ["Consider to use '.exact' modifier."] + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 } + ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 } + ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 } + ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 4 } + ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 }, + { message: "Consider to use '.exact' modifier.", line: 4 } + ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 } + ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 4 } + ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 4 } + ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 } + ] }, { - code: ``, - errors: ["Consider to use '.exact' modifier."] + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 }, + { message: "Consider to use '.exact' modifier.", line: 4 } + ] } ] }) From ebd07e6bd9be54cf9370f08296541017bad41386 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 6 Jan 2019 00:29:33 +0900 Subject: [PATCH 0012/1121] New: some core rules for `, + + `, ` `, + + `, + ` + + `, + ` + + `, + ` + + `, + ` + + `, + ` + + `, + ` + + `, ` `, - `
multiline end tag
`, + + `, + ` +
multiline end tag
+ `, + // empty ``, ` @@ -71,7 +140,9 @@ tester.run('multiline-html-element-content-newline', rule, {
- `, + + `, + // allowEmptyLines { code: ` @@ -109,11 +180,14 @@ tester.run('multiline-html-element-content-newline', rule, { `, options: [{ allowEmptyLines: true }] }, + // self closing ` `, + + `, + // ignores ` + `, ` `, + + `, ` `, + + `, ` `, + + `, ` `, + + `, ` `, + + `, + ` + + `, + ` + + `, + // ignoreWhenNoAttributes: true ` `, output: ``, errors: [{ - message: 'Attribute "age" should be on a new line.', + message: '\'age\' should be on a new line.', type: 'VAttribute', line: 1 }, { - message: 'Attribute "job" should be on a new line.', + message: '\'job\' should be on a new line.', type: 'VAttribute', line: 1 }] @@ -187,7 +187,7 @@ name="John Doe" `, errors: [{ - message: 'Attribute "name" should be on a new line.', + message: '\'name\' should be on a new line.', type: 'VAttribute', line: 1 }] @@ -206,7 +206,7 @@ age="30" `, errors: [{ - message: 'Attribute "age" should be on a new line.', + message: '\'age\' should be on a new line.', type: 'VAttribute', line: 2 }] @@ -225,7 +225,7 @@ age="30" `, errors: [{ - message: 'Attribute "age" should be on a new line.', + message: '\'age\' should be on a new line.', type: 'VAttribute', line: 2 }] @@ -244,7 +244,7 @@ petname="Snoopy"> `, errors: [{ - message: 'Attribute "petname" should be on a new line.', + message: '\'petname\' should be on a new line.', type: 'VAttribute', line: 3 }] @@ -263,11 +263,11 @@ petname="Snoopy" extra="foo"> `, errors: [{ - message: 'Attribute "petname" should be on a new line.', + message: '\'petname\' should be on a new line.', type: 'VAttribute', line: 3 }, { - message: 'Attribute "extra" should be on a new line.', + message: '\'extra\' should be on a new line.', type: 'VAttribute', line: 3 }] diff --git a/tests/lib/rules/no-duplicate-attributes.js b/tests/lib/rules/no-duplicate-attributes.js index 03cd71c74..aeef7fa8a 100644 --- a/tests/lib/rules/no-duplicate-attributes.js +++ b/tests/lib/rules/no-duplicate-attributes.js @@ -52,6 +52,14 @@ tester.run('no-duplicate-attributes', rule, { filename: 'test.vue', code: '', options: [{ allowCoexistStyle: true }] + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' } ], invalid: [ diff --git a/tests/lib/rules/no-multi-spaces.js b/tests/lib/rules/no-multi-spaces.js index 68433af44..225f81c08 100644 --- a/tests/lib/rules/no-multi-spaces.js +++ b/tests/lib/rules/no-multi-spaces.js @@ -118,8 +118,8 @@ ruleTester.run('no-multi-spaces', rule, { output: '', errors: [ { - message: "Multiple spaces found before ':class'.", - type: 'HTMLIdentifier' + message: "Multiple spaces found before ':'.", + type: 'Punctuator' }, { message: "Multiple spaces found before '/>'.", diff --git a/tests/lib/rules/no-restricted-syntax.js b/tests/lib/rules/no-restricted-syntax.js index 3fcdbc24a..36d7b9a02 100644 --- a/tests/lib/rules/no-restricted-syntax.js +++ b/tests/lib/rules/no-restricted-syntax.js @@ -24,6 +24,18 @@ tester.run('no-restricted-syntax', rule, { 'message': 'Call expressions are not allowed.' } ] + }, + { + code: ` + `, + options: [ + { + 'selector': 'CallExpression', + 'message': 'Call expressions are not allowed.' + } + ] } ], invalid: [ @@ -113,6 +125,20 @@ tester.run('no-restricted-syntax', rule, { endColumn: 53 } ] + }, + + { + code: ` + `, + options: [ + { + 'selector': 'CallExpression', + 'message': 'Call expressions are not allowed.' + } + ], + errors: ['Call expressions are not allowed.', 'Call expressions are not allowed.'] } ] }) diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js index f717e7af9..663740e92 100644 --- a/tests/lib/rules/no-unused-vars.js +++ b/tests/lib/rules/no-unused-vars.js @@ -49,6 +49,9 @@ tester.run('no-unused-vars', rule, { }, { code: '' + }, + { + code: '' } ], invalid: [ diff --git a/tests/lib/rules/object-curly-spacing.js b/tests/lib/rules/object-curly-spacing.js index bf6cbc8a8..6471f3afc 100644 --- a/tests/lib/rules/object-curly-spacing.js +++ b/tests/lib/rules/object-curly-spacing.js @@ -21,19 +21,27 @@ tester.run('object-curly-spacing', rule, { { code: '', options: ['always'] + }, + '', + { + code: '', + options: ['always'] } ], invalid: [ { code: '', + output: '', errors: ["There should be no space after '{'."] }, { code: '', + output: '', errors: ["There should be no space before '}'."] }, { code: '', + output: '', errors: [ "There should be no space after '{'.", "There should be no space before '}'." @@ -42,16 +50,19 @@ tester.run('object-curly-spacing', rule, { { code: '', options: ['never'], + output: '', errors: ["There should be no space after '{'."] }, { code: '', options: ['never'], + output: '', errors: ["There should be no space before '}'."] }, { code: '', options: ['never'], + output: '', errors: [ "There should be no space after '{'.", "There should be no space before '}'." @@ -60,16 +71,28 @@ tester.run('object-curly-spacing', rule, { { code: '', options: ['always'], + output: '', errors: ["A space is required before '}'."] }, { code: '', options: ['always'], + output: '', errors: ["A space is required after '{'."] }, { code: '', options: ['always'], + output: '', + errors: [ + "A space is required after '{'.", + "A space is required before '}'." + ] + }, + { + code: '', + options: ['always'], + output: '', errors: [ "A space is required after '{'.", "A space is required before '}'." diff --git a/tests/lib/rules/space-infix-ops.js b/tests/lib/rules/space-infix-ops.js index ba67575ff..2c3da144a 100644 --- a/tests/lib/rules/space-infix-ops.js +++ b/tests/lib/rules/space-infix-ops.js @@ -19,20 +19,29 @@ const message = semver.lt(CLIEngine.version, '5.10.0') tester.run('space-infix-ops', rule, { valid: [ '', - '' + '', + '' ], invalid: [ { code: '', + output: '', errors: [message('+')] }, { code: '', + output: '', errors: [message('?')] }, { code: '', + output: '', errors: [message(':')] + }, + { + code: '', + output: '', + errors: [message('+')] } ] }) diff --git a/tests/lib/rules/space-unary-ops.js b/tests/lib/rules/space-unary-ops.js index 2f36302bb..f6accb202 100644 --- a/tests/lib/rules/space-unary-ops.js +++ b/tests/lib/rules/space-unary-ops.js @@ -14,16 +14,38 @@ const tester = new RuleTester({ tester.run('space-unary-ops', rule, { valid: [ '', - '' + '', + '', + { + code: '', + options: [{ nonwords: true }] + }, + { + code: '', + options: [{ nonwords: true }] + } ], invalid: [ { code: '', + output: '', errors: ['Unexpected space after unary operator \'-\'.'] }, { code: '', + output: '', errors: ['Unary word operator \'typeof\' must be followed by whitespace.'] + }, + { + code: '', + output: '', + errors: ['Unary word operator \'typeof\' must be followed by whitespace.'] + }, + { + code: '', + options: [{ nonwords: true }], + output: '', + errors: ['Unary operator \'!\' must be followed by whitespace.'] } ] }) diff --git a/tests/lib/rules/this-in-template.js b/tests/lib/rules/this-in-template.js index 1f7c7c69b..294882941 100644 --- a/tests/lib/rules/this-in-template.js +++ b/tests/lib/rules/this-in-template.js @@ -106,6 +106,12 @@ function createValidTests (prefix, options) {
`, options + }, + + // We cannot use `.` in dynamic arguments because the right of the `.` becomes a modifier. + { + code: ``, + options } ] } @@ -153,6 +159,13 @@ function createInvalidTests (prefix, options, message, type) { errors: [{ message, type }], options } + + // We cannot use `.` in dynamic arguments because the right of the `.` becomes a modifier. + // { + // code: ``, + // errors: [{ message, type }], + // options + // } ].concat(options[0] === 'always' ? [] : [ diff --git a/tests/lib/rules/use-v-on-exact.js b/tests/lib/rules/use-v-on-exact.js index be90196b1..2bb990403 100644 --- a/tests/lib/rules/use-v-on-exact.js +++ b/tests/lib/rules/use-v-on-exact.js @@ -152,6 +152,12 @@ ruleTester.run('use-v-on-exact', rule, { }, { code: `` + }, + { + code: `` + }, + { + code: `` } ], @@ -272,6 +278,17 @@ ruleTester.run('use-v-on-exact', rule, { { message: "Consider to use '.exact' modifier.", line: 3 }, { message: "Consider to use '.exact' modifier.", line: 4 } ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 } + ] } ] }) diff --git a/tests/lib/rules/v-bind-style.js b/tests/lib/rules/v-bind-style.js index eeab4b9ab..bc9afb971 100644 --- a/tests/lib/rules/v-bind-style.js +++ b/tests/lib/rules/v-bind-style.js @@ -44,6 +44,26 @@ tester.run('v-bind-style', rule, { filename: 'test.vue', code: '', options: ['longform'] + }, + + // Don't enforce `.prop` shorthand because of experimental. + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '', + options: ['shorthand'] + }, + { + filename: 'test.vue', + code: '', + options: ['shorthand'] } ], invalid: [ @@ -66,6 +86,34 @@ tester.run('v-bind-style', rule, { code: '', output: '', errors: ["Expected 'v-bind' before ':'."] + }, + { + filename: 'test.vue', + options: ['longform'], + code: '', + output: '', + errors: ["Expected 'v-bind:' instead of '.'."] + }, + { + filename: 'test.vue', + options: ['longform'], + code: '', + output: '', + errors: ["Expected 'v-bind:' instead of '.'."] + }, + { + filename: 'test.vue', + options: ['longform'], + code: '', + output: '', + errors: ["Expected 'v-bind:' instead of '.'."] + }, + { + filename: 'test.vue', + options: ['longform'], + code: '', + output: '', + errors: ["Expected 'v-bind:' instead of '.'."] } ] }) From 4d0158918cf3149d5f749b70bab592d0e3c6dc18 Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Thu, 28 Feb 2019 03:17:31 +0900 Subject: [PATCH 0043/1121] New: add `vue/no-empty-pattern` rule (#798) --- docs/rules/README.md | 1 + docs/rules/no-empty-pattern.md | 21 +++ lib/index.js | 1 + lib/rules/no-empty-pattern.js | 9 + tests/lib/rules/no-empty-pattern.js | 252 ++++++++++++++++++++++++++++ 5 files changed, 284 insertions(+) create mode 100644 docs/rules/no-empty-pattern.md create mode 100644 lib/rules/no-empty-pattern.js create mode 100644 tests/lib/rules/no-empty-pattern.js diff --git a/docs/rules/README.md b/docs/rules/README.md index 6b8b2df5c..96d0a96c1 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -150,6 +150,7 @@ For example: | [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 | | | [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | +| [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | | | [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax | | | [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: | | [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | | diff --git a/docs/rules/no-empty-pattern.md b/docs/rules/no-empty-pattern.md new file mode 100644 index 000000000..dd23c9f5b --- /dev/null +++ b/docs/rules/no-empty-pattern.md @@ -0,0 +1,21 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-empty-pattern +description: disallow empty destructuring patterns +--- +# vue/no-empty-pattern +> disallow empty destructuring patterns + +This rule is the same rule as core [no-empty-pattern] rule but it applies to the expressions in ` `, - errors: [{ - message: "'.sync' modifiers aren't supported on non Vue-components.", - line: 3, - column: 18, - endColumn: 39 - }] + errors: [ + { + message: + "'.sync' modifiers aren't supported on non Vue-components.", + line: 3, + column: 18, + endColumn: 39 + } + ] }, { filename: 'test.vue', @@ -214,12 +244,15 @@ tester.run('valid-v-bind-sync', rule, {
`, - errors: [{ - message: "'.sync' modifiers cannot update the iteration variable 'x' itself.", - line: 4, - column: 26, - endColumn: 39 - }] + errors: [ + { + message: + "'.sync' modifiers cannot update the iteration variable 'x' itself.", + line: 4, + column: 26, + endColumn: 39 + } + ] }, { filename: 'test.vue', @@ -230,12 +263,15 @@ tester.run('valid-v-bind-sync', rule, {
`, - errors: [{ - message: "'.sync' modifiers cannot update the iteration variable 'e' itself.", - line: 4, - column: 26, - endColumn: 45 - }] + errors: [ + { + message: + "'.sync' modifiers cannot update the iteration variable 'e' itself.", + line: 4, + column: 26, + endColumn: 45 + } + ] }, { filename: 'test.vue', @@ -250,10 +286,13 @@ tester.run('valid-v-bind-sync', rule, {
`, - errors: [{ - message: "'.sync' modifiers cannot update the iteration variable 'e1' itself.", - line: 6 - }] + errors: [ + { + message: + "'.sync' modifiers cannot update the iteration variable 'e1' itself.", + line: 6 + } + ] }, { filename: 'test.vue', @@ -264,10 +303,13 @@ tester.run('valid-v-bind-sync', rule, {
`, - errors: [{ - message: "'.sync' modifiers cannot update the iteration variable 'index' itself.", - line: 4 - }] + errors: [ + { + message: + "'.sync' modifiers cannot update the iteration variable 'index' itself.", + line: 4 + } + ] }, { filename: 'test.vue', @@ -276,17 +318,23 @@ tester.run('valid-v-bind-sync', rule, {
`, - errors: ["'.sync' modifiers aren't supported on
non Vue-components."] + errors: [ + "'.sync' modifiers aren't supported on
non Vue-components." + ] }, { filename: 'test.vue', code: '', - errors: ["'.sync' modifiers require the attribute value which is valid as LHS."] + errors: [ + "'.sync' modifiers require the attribute value which is valid as LHS." + ] }, { filename: 'test.vue', code: '', - errors: ["'.sync' modifiers require the attribute value which is valid as LHS."] + errors: [ + "'.sync' modifiers require the attribute value which is valid as LHS." + ] } ] }) diff --git a/tests/lib/rules/valid-v-else-if.js b/tests/lib/rules/valid-v-else-if.js index b0c18093f..778d07339 100644 --- a/tests/lib/rules/valid-v-else-if.js +++ b/tests/lib/rules/valid-v-else-if.js @@ -29,11 +29,13 @@ tester.run('valid-v-else-if', rule, { }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', @@ -43,57 +45,82 @@ tester.run('valid-v-else-if', rule, { invalid: [ { filename: 'test.vue', - code: '', - errors: ["'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + code: + '', + errors: [ + "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', code: '', - errors: ["'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + errors: [ + "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', code: '', - errors: ["'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + errors: [ + "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + code: + '', + errors: [ + "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + code: + '', + errors: [ + "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + code: + '', + errors: [ + "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else-if' and 'v-if' directives can't exist on the same element."] + code: + '', + errors: [ + "'v-else-if' and 'v-if' directives can't exist on the same element." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else-if' and 'v-else' directives can't exist on the same element."] + code: + '', + errors: [ + "'v-else-if' and 'v-else' directives can't exist on the same element." + ] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["'v-else-if' directives require no argument."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["'v-else-if' directives require no modifier."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["'v-else-if' directives require that attribute value."] } ] diff --git a/tests/lib/rules/valid-v-else.js b/tests/lib/rules/valid-v-else.js index 7825e4dfb..1e235f9b5 100644 --- a/tests/lib/rules/valid-v-else.js +++ b/tests/lib/rules/valid-v-else.js @@ -29,11 +29,13 @@ tester.run('valid-v-else', rule, { }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', @@ -44,56 +46,79 @@ tester.run('valid-v-else', rule, { { filename: 'test.vue', code: '', - errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + errors: [ + "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', code: '', - errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + errors: [ + "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', code: '', - errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + errors: [ + "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', code: '', - errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + errors: [ + "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + code: + '', + errors: [ + "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."] + code: + '', + errors: [ + "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else' and 'v-if' directives can't exist on the same element. You may want 'v-else-if' directives."] + code: + '', + errors: [ + "'v-else' and 'v-if' directives can't exist on the same element. You may want 'v-else-if' directives." + ] }, { filename: 'test.vue', - code: '', - errors: ["'v-else' and 'v-else-if' directives can't exist on the same element."] + code: + '', + errors: [ + "'v-else' and 'v-else-if' directives can't exist on the same element." + ] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["'v-else' directives require no argument."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["'v-else' directives require no modifier."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["'v-else' directives require no attribute value."] } ] diff --git a/tests/lib/rules/valid-v-for.js b/tests/lib/rules/valid-v-for.js index d83b634a3..74a1ea614 100644 --- a/tests/lib/rules/valid-v-for.js +++ b/tests/lib/rules/valid-v-for.js @@ -37,55 +37,68 @@ tester.run('valid-v-for', rule, { }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', - code: '' + code: + '' }, { filename: 'test.vue', @@ -134,92 +147,123 @@ tester.run('valid-v-for', rule, { }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Invalid alias ''."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Invalid alias ''."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Invalid alias ''."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Invalid alias '{b,c}'."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Invalid alias '{c,d}'."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', - code: '', - errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."] + code: + '', + errors: [ + "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."] + code: + '', + errors: [ + "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."] + code: + '', + errors: [ + "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."] + code: + '', + errors: [ + "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." + ] }, { filename: 'test.vue', - code: '', - errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."] + code: + '', + errors: [ + "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." + ] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["Custom elements in iteration require 'v-bind:key' directives."] }, { filename: 'test.vue', - code: '', + code: + '', errors: ["'v-for' directives require that attribute value."] }, { filename: 'test.vue', - code: '', - errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."] + code: + '', + errors: [ + "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." + ] }, { filename: 'test.vue', - errors: ["Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive."], + errors: [ + "Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive." + ], code: ` ` + }, + // empty value + { + filename: 'empty-value.vue', + code: '', + errors: ["'v-for' directives require that attribute value."] } ] }) diff --git a/tests/lib/rules/valid-v-html.js b/tests/lib/rules/valid-v-html.js index 9a30bcf48..afb312f74 100644 --- a/tests/lib/rules/valid-v-html.js +++ b/tests/lib/rules/valid-v-html.js @@ -30,6 +30,16 @@ tester.run('valid-v-html', rule, { { filename: 'test.vue', code: '' + }, + // parsing error + { + filename: 'parsing-error.vue', + code: '' + }, + // comment value (parsing error) + { + filename: 'comment-value.vue', + code: '' } ], invalid: [ @@ -47,6 +57,12 @@ tester.run('valid-v-html', rule, { filename: 'test.vue', code: '', errors: ["'v-html' directives require that attribute value."] + }, + // empty value + { + filename: 'empty-value.vue', + code: '', + errors: ["'v-html' directives require that attribute value."] } ] }) diff --git a/tests/lib/rules/valid-v-if.js b/tests/lib/rules/valid-v-if.js index effe04836..d3afe5780 100644 --- a/tests/lib/rules/valid-v-if.js +++ b/tests/lib/rules/valid-v-if.js @@ -30,6 +30,16 @@ tester.run('valid-v-if', rule, { { filename: 'test.vue', code: '' + }, + // parsing error + { + filename: 'parsing-error.vue', + code: '' + }, + // comment value (parsing error) + { + filename: 'comment-value.vue', + code: '' } ], invalid: [ @@ -62,6 +72,12 @@ tester.run('valid-v-if', rule, { filename: 'test.vue', code: '', errors: ["'v-if' directives require that attribute value."] + }, + // empty value + { + filename: 'empty-value.vue', + code: '', + errors: ["'v-if' directives require that attribute value."] } ] }) diff --git a/tests/lib/rules/valid-v-model.js b/tests/lib/rules/valid-v-model.js index c9c28585a..9e78ef47a 100644 --- a/tests/lib/rules/valid-v-model.js +++ b/tests/lib/rules/valid-v-model.js @@ -150,6 +150,16 @@ tester.run('valid-v-model', rule, { filename: 'test.vue', code: '' + }, + // parsing error + { + filename: 'parsing-error.vue', + code: '' + }, + // comment value (parsing error) + { + filename: 'comment-value.vue', + code: '' } ], invalid: [ @@ -216,6 +226,12 @@ tester.run('valid-v-model', rule, { errors: [ "'v-model' directives cannot update the iteration variable 'e' itself." ] + }, + // empty value + { + filename: 'empty-value.vue', + code: '', + errors: ["'v-model' directives require that attribute value."] } ] }) diff --git a/tests/lib/rules/valid-v-on.js b/tests/lib/rules/valid-v-on.js index 911039ea5..6f162e1df 100644 --- a/tests/lib/rules/valid-v-on.js +++ b/tests/lib/rules/valid-v-on.js @@ -96,6 +96,33 @@ tester.run('valid-v-on', rule, { filename: 'test.vue', code: '', options: [{ modifiers: ['bar', 'aaa'] }] + }, + // parsing error + { + filename: 'parsing-error.vue', + code: '' + }, + // comment value (valid) + { + filename: 'comment-value.vue', + code: '' + }, + { + filename: 'comment-value.vue', + code: '' + }, + { + filename: 'comment-value.vue', + code: '' + }, + { + filename: 'comment-value.vue', + code: '' + }, + // empty value + { + filename: 'empty-value.vue', + code: '' } ], invalid: [ @@ -139,6 +166,14 @@ tester.run('valid-v-on', rule, { filename: 'test.vue', code: '', errors: ['Avoid using JavaScript keyword as "v-on" value: "delete".'] + }, + // empty value + { + filename: 'empty-value.vue', + code: '', + errors: [ + "'v-on' directives require a value or verb modifier (like 'stop' or 'prevent')." + ] } ] }) diff --git a/tests/lib/rules/valid-v-once.js b/tests/lib/rules/valid-v-once.js index 0c6a71a20..c9f3750c3 100644 --- a/tests/lib/rules/valid-v-once.js +++ b/tests/lib/rules/valid-v-once.js @@ -47,6 +47,24 @@ tester.run('valid-v-once', rule, { filename: 'test.vue', code: '', errors: ["'v-once' directives require no attribute value."] + }, + // parsing error + { + filename: 'parsing-error.vue', + code: '', + errors: ["'v-once' directives require no attribute value."] + }, + // comment value + { + filename: 'comment-value.vue', + code: '', + errors: ["'v-once' directives require no attribute value."] + }, + // empty value + { + filename: 'comment-value.vue', + code: '', + errors: ["'v-once' directives require no attribute value."] } ] }) diff --git a/tests/lib/rules/valid-v-pre.js b/tests/lib/rules/valid-v-pre.js index ae9d38e6f..3d9c9d883 100644 --- a/tests/lib/rules/valid-v-pre.js +++ b/tests/lib/rules/valid-v-pre.js @@ -47,6 +47,24 @@ tester.run('valid-v-pre', rule, { filename: 'test.vue', code: '', errors: ["'v-pre' directives require no attribute value."] + }, + // parsing error + { + filename: 'parsing-error.vue', + code: '', + errors: ["'v-pre' directives require no attribute value."] + }, + // comment value + { + filename: 'comment-value.vue', + code: '', + errors: ["'v-pre' directives require no attribute value."] + }, + // empty value + { + filename: 'empty-value.vue', + code: '', + errors: ["'v-pre' directives require no attribute value."] } ] }) diff --git a/tests/lib/rules/valid-v-show.js b/tests/lib/rules/valid-v-show.js index 0471ccd9f..4220e8749 100644 --- a/tests/lib/rules/valid-v-show.js +++ b/tests/lib/rules/valid-v-show.js @@ -30,6 +30,16 @@ tester.run('valid-v-show', rule, { { filename: 'test.vue', code: '' + }, + // parsing error + { + filename: 'parsing-error.vue', + code: '' + }, + // comment value (parsing error) + { + filename: 'comment-value.vue', + code: '' } ], invalid: [ @@ -48,8 +58,9 @@ tester.run('valid-v-show', rule, { code: '', errors: ["'v-show' directives require that attribute value."] }, + // empty value { - filename: 'test.vue', + filename: 'empty-value.vue', code: '', errors: ["'v-show' directives require that attribute value."] } diff --git a/tests/lib/rules/valid-v-slot.js b/tests/lib/rules/valid-v-slot.js index ab8307fed..0043756f1 100644 --- a/tests/lib/rules/valid-v-slot.js +++ b/tests/lib/rules/valid-v-slot.js @@ -83,7 +83,13 @@ tester.run('valid-v-slot', rule, { - ` + `, + // parsing error + { + filename: 'parsing-error.vue', + code: + '' + } ], invalid: [ // Verify location. @@ -294,6 +300,26 @@ tester.run('valid-v-slot', rule, { `, errors: [{ messageId: 'requireAttributeValue' }] + }, + // comment value + { + filename: 'comment-value1.vue', + code: + '', + errors: [{ messageId: 'requireAttributeValue' }] + }, + { + filename: 'comment-value2.vue', + code: + '', + errors: [{ messageId: 'requireAttributeValue' }] + }, + // empty value + { + filename: 'empty-value.vue', + code: + '', + errors: [{ messageId: 'requireAttributeValue' }] } ] }) diff --git a/tests/lib/rules/valid-v-text.js b/tests/lib/rules/valid-v-text.js index 3956462ce..611b1a31a 100644 --- a/tests/lib/rules/valid-v-text.js +++ b/tests/lib/rules/valid-v-text.js @@ -34,6 +34,16 @@ tester.run('valid-v-text', rule, { { filename: 'test.vue', code: '' + }, + // parsing error + { + filename: 'parsing-error.vue', + code: '' + }, + // comment value (parsing error) + { + filename: 'parsing-error.vue', + code: '' } ], invalid: [ @@ -51,6 +61,12 @@ tester.run('valid-v-text', rule, { filename: 'test.vue', code: '', errors: ["'v-text' directives require that attribute value."] + }, + // empty value + { + filename: 'empty-value.vue', + code: '', + errors: ["'v-text' directives require that attribute value."] } ] }) From e5c835eb957340b153ea2047e3ba274b88ba01bb Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Fri, 5 Jun 2020 14:29:35 +0900 Subject: [PATCH 0218/1121] Add `vue/no-bare-strings-in-template` rule (#1185) --- docs/rules/README.md | 1 + docs/rules/no-bare-strings-in-template.md | 88 ++++++ lib/index.js | 1 + lib/rules/no-bare-strings-in-template.js | 279 +++++++++++++++++ lib/utils/regexp.js | 14 +- package.json | 2 + .../lib/rules/no-bare-strings-in-template.js | 290 ++++++++++++++++++ 7 files changed, 673 insertions(+), 2 deletions(-) create mode 100644 docs/rules/no-bare-strings-in-template.md create mode 100644 lib/rules/no-bare-strings-in-template.js create mode 100644 tests/lib/rules/no-bare-strings-in-template.js diff --git a/docs/rules/README.md b/docs/rules/README.md index f3e2bfdf6..55b3c4e0d 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -282,6 +282,7 @@ For example: | [vue/html-comment-content-spacing](./html-comment-content-spacing.md) | enforce unified spacing in HTML comments | :wrench: | | [vue/html-comment-indent](./html-comment-indent.md) | enforce consistent indentation in HTML comments | :wrench: | | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | | +| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in ` `, - options: [{ ignoreWhenEmpty: false, ignoreWhenNoAttributes: false }], output: ` `, + options: [{ ignoreWhenEmpty: false, ignoreWhenNoAttributes: false }], errors: [ 'Expected 1 line break after opening tag (`
`), but no line breaks found.' ] @@ -473,13 +473,13 @@ singleline element
`, - options: [{ ignoreWhenEmpty: false, ignoreWhenNoAttributes: false }], output: ` `, + options: [{ ignoreWhenEmpty: false, ignoreWhenNoAttributes: false }], errors: [ 'Expected 1 line break after opening tag (`
`), but no line breaks found.' ] diff --git a/tests/lib/rules/space-in-parens.js b/tests/lib/rules/space-in-parens.js index 8791652ed..ef6fe628e 100644 --- a/tests/lib/rules/space-in-parens.js +++ b/tests/lib/rules/space-in-parens.js @@ -94,13 +94,13 @@ tester.run('space-in-parens', rule, { @click="foo(arg)" /> `, - options: ['always'], output: ` `, + options: ['always'], errors: [ errorMessage({ messageId: 'missingOpeningSpace', @@ -143,13 +143,13 @@ tester.run('space-in-parens', rule, { :value="(1 + 2) + 3" > `, - options: ['always'], output: ` `, + options: ['always'], errors: [ errorMessage({ messageId: 'missingOpeningSpace', @@ -192,13 +192,13 @@ tester.run('space-in-parens', rule, { :[(1+2)]="(1 + 2) + 3" > `, - options: ['always'], output: ` `, + options: ['always'], errors: [ errorMessage({ messageId: 'missingOpeningSpace', diff --git a/tests/lib/rules/space-unary-ops.js b/tests/lib/rules/space-unary-ops.js index 6b3e7c25f..63539a5a7 100644 --- a/tests/lib/rules/space-unary-ops.js +++ b/tests/lib/rules/space-unary-ops.js @@ -50,8 +50,8 @@ tester.run('space-unary-ops', rule, { }, { code: '', - options: [{ nonwords: true }], output: '', + options: [{ nonwords: true }], errors: ["Unary operator '!' must be followed by whitespace."] }, diff --git a/tests/lib/rules/template-curly-spacing.js b/tests/lib/rules/template-curly-spacing.js index 6c4376969..0fe530511 100644 --- a/tests/lib/rules/template-curly-spacing.js +++ b/tests/lib/rules/template-curly-spacing.js @@ -41,14 +41,13 @@ tester.run('template-curly-spacing', rule, { }, // CSS vars injection - { - code: ` + ` ` - } + + ` ], invalid: [ { @@ -79,12 +78,12 @@ tester.run('template-curly-spacing', rule, {
`, - options: ['always'], output: ` `, + options: ['always'], errors: [ { message: "Expected space(s) after '${'.", diff --git a/tests/lib/rules/this-in-template.js b/tests/lib/rules/this-in-template.js index 904aa00c4..5686bf137 100644 --- a/tests/lib/rules/this-in-template.js +++ b/tests/lib/rules/this-in-template.js @@ -248,14 +248,14 @@ ruleTester.run('this-in-template', rule, { { code: ``, output: ``, - errors: ["Unexpected usage of 'this'."], - options: ['never'] + options: ['never'], + errors: ["Unexpected usage of 'this'."] }, { code: ``, output: ``, - errors: ["Unexpected usage of 'this'."], - options: ['never'] + options: ['never'], + errors: ["Unexpected usage of 'this'."] } ] }) diff --git a/tests/lib/rules/use-v-on-exact.js b/tests/lib/rules/use-v-on-exact.js index 73ac2408b..929fd746a 100644 --- a/tests/lib/rules/use-v-on-exact.js +++ b/tests/lib/rules/use-v-on-exact.js @@ -15,92 +15,48 @@ const ruleTester = new RuleTester({ ruleTester.run('use-v-on-exact', rule, { valid: [ - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: `` - }, - { - code: ` + ` ], invalid: [ diff --git a/tests/lib/rules/v-bind-style.js b/tests/lib/rules/v-bind-style.js index 2fd1ce7c2..0cb67f62f 100644 --- a/tests/lib/rules/v-bind-style.js +++ b/tests/lib/rules/v-bind-style.js @@ -67,44 +67,44 @@ tester.run('v-bind-style', rule, { }, { filename: 'test.vue', - options: ['shorthand'], code: '', output: '', + options: ['shorthand'], errors: ["Unexpected 'v-bind' before ':'."] }, { filename: 'test.vue', - options: ['longform'], code: '', output: '', + options: ['longform'], errors: ["Expected 'v-bind' before ':'."] }, { filename: 'test.vue', - options: ['longform'], code: '', output: '', + options: ['longform'], errors: ["Expected 'v-bind:' instead of '.'."] }, { filename: 'test.vue', - options: ['longform'], code: '', output: '', + options: ['longform'], errors: ["Expected 'v-bind:' instead of '.'."] }, { filename: 'test.vue', - options: ['longform'], code: '', output: '', + options: ['longform'], errors: ["Expected 'v-bind:' instead of '.'."] }, { filename: 'test.vue', - options: ['longform'], code: '', output: '', + options: ['longform'], errors: ["Expected 'v-bind:' instead of '.'."] } ] diff --git a/tests/lib/rules/v-for-delimiter-style.js b/tests/lib/rules/v-for-delimiter-style.js index b1500af50..23665a32c 100644 --- a/tests/lib/rules/v-for-delimiter-style.js +++ b/tests/lib/rules/v-for-delimiter-style.js @@ -94,9 +94,9 @@ tester.run('v-for-delimiter-style', rule, { }, { filename: 'test.vue', - options: ['in'], code: '', output: '', + options: ['in'], errors: [ { message: "Expected 'in' instead of 'of' in 'v-for'.", @@ -106,9 +106,9 @@ tester.run('v-for-delimiter-style', rule, { }, { filename: 'test.vue', - options: ['of'], code: '', output: '', + options: ['of'], errors: [ { message: "Expected 'of' instead of 'in' in 'v-for'.", diff --git a/tests/lib/rules/v-on-event-hyphenation.js b/tests/lib/rules/v-on-event-hyphenation.js index 087e5d00a..beaa5e3fa 100644 --- a/tests/lib/rules/v-on-event-hyphenation.js +++ b/tests/lib/rules/v-on-event-hyphenation.js @@ -73,12 +73,12 @@ tester.run('v-on-event-hyphenation', rule, { `, - options: ['always', { autofix: true }], output: ` `, + options: ['always', { autofix: true }], errors: [ { message: "v-on event '@customEvent' must be hyphenated.", @@ -95,12 +95,12 @@ tester.run('v-on-event-hyphenation', rule, { `, - options: ['never', { autofix: true }], output: ` `, + options: ['never', { autofix: true }], errors: ["v-on event 'v-on:custom-event' can't be hyphenated."] }, { @@ -110,13 +110,13 @@ tester.run('v-on-event-hyphenation', rule, { `, - options: ['always', { autofix: true }], output: ` `, + options: ['always', { autofix: true }], errors: ["v-on event '@update:modelValue' must be hyphenated."] }, { @@ -126,13 +126,13 @@ tester.run('v-on-event-hyphenation', rule, { `, - options: ['never', { autofix: true }], output: ` `, + options: ['never', { autofix: true }], errors: ["v-on event '@update:model-value' can't be hyphenated."] }, { @@ -144,7 +144,6 @@ tester.run('v-on-event-hyphenation', rule, { `, - options: ['always', { autofix: true }], output: ` `, + options: ['always', { autofix: true }], errors: [ "v-on event '@upDate:modelValue' must be hyphenated.", "v-on event '@up-date:modelValue' must be hyphenated.", @@ -168,7 +168,6 @@ tester.run('v-on-event-hyphenation', rule, { `, - options: ['never', { autofix: true }], output: ` `, + options: ['never', { autofix: true }], errors: [ "v-on event '@up-date:modelValue' can't be hyphenated.", "v-on event '@upDate:model-value' can't be hyphenated.", diff --git a/tests/lib/rules/v-on-function-call.js b/tests/lib/rules/v-on-function-call.js index d8486a448..d06bbe231 100644 --- a/tests/lib/rules/v-on-function-call.js +++ b/tests/lib/rules/v-on-function-call.js @@ -161,37 +161,37 @@ tester.run('v-on-function-call', rule, { filename: 'test.vue', code: '', output: null, + options: ['always'], errors: [ "Method calls inside of 'v-on' directives must have parentheses." - ], - options: ['always'] + ] }, { filename: 'test.vue', code: '', output: ``, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', code: '', output: ``, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', code: '', output: null, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', @@ -204,13 +204,13 @@ tester.run('v-on-function-call', rule, { ">
`, output: null, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses.", "Method calls without arguments inside of 'v-on' directives must not have parentheses.", "Method calls without arguments inside of 'v-on' directives must not have parentheses.", "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', @@ -222,10 +222,10 @@ tester.run('v-on-function-call', rule, { `, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', @@ -237,10 +237,10 @@ tester.run('v-on-function-call', rule, { `, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', @@ -254,11 +254,11 @@ tester.run('v-on-function-call', rule, {
`, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses.", "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', @@ -270,10 +270,10 @@ tester.run('v-on-function-call', rule, { `, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', @@ -285,19 +285,19 @@ tester.run('v-on-function-call', rule, { `, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', code: '', output: '', + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', @@ -319,10 +319,10 @@ tester.run('v-on-function-call', rule, { } } `, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] }, { filename: 'test.vue', @@ -344,10 +344,10 @@ tester.run('v-on-function-call', rule, { } } `, + options: ['never'], errors: [ "Method calls without arguments inside of 'v-on' directives must not have parentheses." - ], - options: ['never'] + ] } ] }) diff --git a/tests/lib/rules/v-on-handler-style.js b/tests/lib/rules/v-on-handler-style.js index e3afd05d5..314bbf8c8 100644 --- a/tests/lib/rules/v-on-handler-style.js +++ b/tests/lib/rules/v-on-handler-style.js @@ -78,12 +78,12 @@ tester.run('v-on-handler-style', rule, {