diff --git a/docs/rules/README.md b/docs/rules/README.md index ecea70df7..4327caf50 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -286,6 +286,7 @@ For example: | [vue/keyword-spacing](./keyword-spacing.md) | enforce consistent spacing before and after keywords | :wrench: | | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | | | [vue/max-len](./max-len.md) | enforce a maximum line length | | +| [vue/no-arrow-functions-in-watch](./no-arrow-functions-in-watch.md)| disallows using arrow functions to define wathcer | | | [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | | [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | | | [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | | diff --git a/docs/rules/no-arrow-functions-in-watch.md b/docs/rules/no-arrow-functions-in-watch.md new file mode 100644 index 000000000..7da767330 --- /dev/null +++ b/docs/rules/no-arrow-functions-in-watch.md @@ -0,0 +1,61 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-arrow-functions-in-watch +description: disallow arrow functions to define watcher +--- +# vue/no-arrow-functions-in-watch +> disallow using arrow functions to define watcher + +## :book: Rule Details + +This rules disallows using arrow functions to defined watcher.The reason is arrow functions bind the parent context, so `this` will not be the Vue instance as you expect.([see here for more details](https://vuejs.org/v2/api/#watch)) + + + +```vue + +``` + + + +## :wrench: Options + +Nothing. + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-arrow-functions-in-watch.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-arrow-functions-in-watch.js) diff --git a/lib/index.js b/lib/index.js index 58642e5b6..66c381b39 100644 --- a/lib/index.js +++ b/lib/index.js @@ -40,6 +40,7 @@ module.exports = { 'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'), 'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'), 'name-property-casing': require('./rules/name-property-casing'), + 'no-arrow-functions-in-watch': require('./rules/no-arrow-functions-in-watch'), 'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'), 'no-boolean-default': require('./rules/no-boolean-default'), 'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'), diff --git a/lib/rules/no-arrow-functions-in-watch.js b/lib/rules/no-arrow-functions-in-watch.js new file mode 100644 index 000000000..ab8a608da --- /dev/null +++ b/lib/rules/no-arrow-functions-in-watch.js @@ -0,0 +1,39 @@ +/** + * @author Sosuke Suzuki + */ +'use strict' + +const utils = require('../utils') + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'disallow using arrow functions to define watcher', + categories: undefined, + url: 'https://eslint.vuejs.org/rules/no-arrow-functions-in-watch.html' + }, + fixable: null, + schema: [] + }, + create (context) { + return utils.executeOnVue(context, (obj) => { + const watchNode = obj.properties.find((property) => utils.getStaticPropertyName(property) === 'watch') + if (watchNode == null) { + return + } + const watchValue = watchNode.value + if (watchValue.type !== 'ObjectExpression') { + return + } + for (const property of watchValue.properties) { + if (property.type === 'Property' && property.value.type === 'ArrowFunctionExpression') { + context.report({ + node: property, + message: 'You should not use an arrow function to define a watcher.' + }) + } + } + }) + } +} diff --git a/tests/lib/rules/no-arrow-functions-in-watch.js b/tests/lib/rules/no-arrow-functions-in-watch.js new file mode 100644 index 000000000..5d5b5cd87 --- /dev/null +++ b/tests/lib/rules/no-arrow-functions-in-watch.js @@ -0,0 +1,217 @@ +/** + * @author Sosuke Suzuki + */ + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/no-arrow-functions-in-watch') +const RuleTester = require('eslint').RuleTester + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module' + } +}) +ruleTester.run('no-arrow-functions-in-watch', rule, { + valid: [ + { + filename: 'test.vue', + code: ` + export default {} + ` + }, + { + filename: 'test.vue', + code: ` + export default { + watch: {} + } + ` + }, + { + filename: 'test.vue', + code: ` + export default { + watch: { + foo() {} + }, + } + ` + }, + { + filename: 'test.vue', + code: ` + export default { + watch: { + foo: function() {} + }, + } + ` + }, + { + filename: 'test.vue', + code: ` + export default { + watch: { + foo() {}, + bar() {} + }, + } + ` + }, + { + filename: 'test.vue', + code: ` + export default { + watch: { + foo: function() {}, + bar: function() {} + }, + } + ` + }, + { + filename: 'test.vue', + code: ` + export default { + watch: { + ...obj, + foo: function() {}, + bar: function() {} + }, + } + ` + }, + { + filename: 'test.vue', + code: ` + export default { + data: { + a: 1, + b: 2, + c: 3, + d: 4, + e: { + f: { + g: 5 + } + } + }, + watch: { + a: function (val, oldVal) { + console.log('new: %s, old: %s', val, oldVal) + }, + b: 'someMethod', + c: { + handler: function (val, oldVal) {}, + deep: true + }, + d: { + handler: 'someMethod', + immediate: true + }, + e: [ + 'handle1', + function handle2 (val, oldVal) {}, + { + handler: function handle3 (val, oldVal) {}, + /* ... */ + } + ], + 'e.f': function (val, oldVal) { /* ... */ } + } + }` + } + ], + invalid: [ + { + filename: 'test.vue', + code: ` + export default { + watch: { + foo: () => {} + }, + }`, + errors: ['You should not use an arrow function to define a watcher.'] + }, + { + filename: 'test.vue', + code: ` + export default { + watch: { + foo() {}, + bar: () => {} + } + }`, + errors: [{ + message: 'You should not use an arrow function to define a watcher.', + line: 5 + }] + }, + { + filename: 'test.vue', + code: ` + export default { + watch: { + foo: function() {}, + bar: () => {} + } + }`, + errors: [{ + message: 'You should not use an arrow function to define a watcher.', + line: 5 + }] + }, + { + filename: 'test.vue', + code: ` + export default { + data: { + a: 1, + b: 2, + c: 3, + d: 4, + e: { + f: { + g: 5 + } + } + }, + watch: { + a: (val, oldVal) => { + console.log('new: %s, old: %s', val, oldVal) + }, + b: 'someMethod', + c: { + handler: function (val, oldVal) {}, + deep: true + }, + d: { + handler: 'someMethod', + immediate: true + }, + e: [ + 'handle1', + function handle2 (val, oldVal) {}, + { + handler: function handle3 (val, oldVal) {}, + /* ... */ + } + ], + 'e.f': function (val, oldVal) { /* ... */ } + } + }`, + errors: [{ + message: 'You should not use an arrow function to define a watcher.', + line: 15 + }] + } + ] +})