From 6a147ec57ebfd73d1bb0550ec9aba667de8a963a Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Wed, 11 Mar 2020 13:23:24 +0800 Subject: [PATCH 01/12] feat: init rule --- docs/rules/no-computed-in-data.md | 36 +++++++++++++++++++++ lib/rules/no-computed-in-data.js | 45 ++++++++++++++++++++++++++ tests/lib/rules/no-computed-in-data.js | 29 +++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 docs/rules/no-computed-in-data.md create mode 100644 lib/rules/no-computed-in-data.js create mode 100644 tests/lib/rules/no-computed-in-data.js diff --git a/docs/rules/no-computed-in-data.md b/docs/rules/no-computed-in-data.md new file mode 100644 index 000000000..e1e9dbed7 --- /dev/null +++ b/docs/rules/no-computed-in-data.md @@ -0,0 +1,36 @@ +# Ensure computed properties are not used in the data() (no-computed-in-data) + +Please describe the origin of the rule here. + + +## Rule Details + +This rule aims to... + +Examples of **incorrect** code for this rule: + +```js + +// fill me in + +``` + +Examples of **correct** code for this rule: + +```js + +// fill me in + +``` + +### Options + +If there are any options, describe them here. Otherwise, delete this section. + +## When Not To Use It + +Give a short description of when it would be appropriate to turn off this rule. + +## Further Reading + +If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js new file mode 100644 index 000000000..784fadbf8 --- /dev/null +++ b/lib/rules/no-computed-in-data.js @@ -0,0 +1,45 @@ +/** + * @fileoverview Ensure computed properties are not used in the data() + * @author IWANABETHATGUY + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'require computed properties are not used in the data()', + category: '', + recommended: false, + url: 'https://eslint.vuejs.org/rules/no-computed-in-data.html' + }, + fixable: null, // or "code" or "whitespace" + schema: [ + // fill in your schema + ] + }, + + create: function (context) { + // variables should be defined here + + // ---------------------------------------------------------------------- + // Helpers + // ---------------------------------------------------------------------- + + // any helper functions should go here or else delete this section + + // ---------------------------------------------------------------------- + // Public + // ---------------------------------------------------------------------- + + return { + + // give me methods + + } + } +} diff --git a/tests/lib/rules/no-computed-in-data.js b/tests/lib/rules/no-computed-in-data.js new file mode 100644 index 000000000..af3dfa1f3 --- /dev/null +++ b/tests/lib/rules/no-computed-in-data.js @@ -0,0 +1,29 @@ +/** + * @fileoverview Ensure computed properties are not used in the data() + * @author IWANABETHATGUY + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +var rule = require('../../../lib/rules/no-computed-in-data') + +var RuleTester = require('eslint').RuleTester + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +var ruleTester = new RuleTester() +ruleTester.run('no-computed-in-data', rule, { + + valid: [ + + // give me some code that won't trigger a warning + ], + + invalid: [ + ] +}) From 6d7d8508a52eefc597c681f3ca5167512faf70e2 Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Thu, 12 Mar 2020 00:14:47 +0800 Subject: [PATCH 02/12] test: add test --- lib/rules/no-computed-in-data.js | 71 ++++++++++++++++------ lib/utils/index.js | 27 ++++++++- tests/lib/rules/no-computed-in-data.js | 84 +++++++++++++++++++++++++- 3 files changed, 163 insertions(+), 19 deletions(-) diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js index 784fadbf8..d51263996 100644 --- a/lib/rules/no-computed-in-data.js +++ b/lib/rules/no-computed-in-data.js @@ -7,7 +7,8 @@ // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ - +const utils = require('../utils') +const visitor = require('eslint-visitor-keys') module.exports = { meta: { type: 'problem', @@ -17,29 +18,65 @@ module.exports = { recommended: false, url: 'https://eslint.vuejs.org/rules/no-computed-in-data.html' }, - fixable: null, // or "code" or "whitespace" + fixable: null, // or "code" or "whitespace" schema: [ // fill in your schema ] }, create: function (context) { - // variables should be defined here - - // ---------------------------------------------------------------------- - // Helpers - // ---------------------------------------------------------------------- - - // any helper functions should go here or else delete this section - - // ---------------------------------------------------------------------- - // Public - // ---------------------------------------------------------------------- - + let dataAstNode + const memberExpressionMap = Object.create(null) return { - - // give me methods - + ObjectExpression (node) { + if (!dataAstNode) { + dataAstNode = node.properties.find( + p => + p.type === 'Property' && + p.key.type === 'Identifier' && + p.key.name === 'data' + ) + } + }, + 'Property:exit' (node) { + if (node === dataAstNode) { + dataAstNode = undefined + } + }, + MemberExpression (node) { + if (dataAstNode) { + // a memberExpression `like this.a.c.d` -> when traverse to c.d we can got the the full name -> this.a.c.d + const fullName = utils.parseMemberExpression(node).join('.') + if (memberExpressionMap[fullName]) { + memberExpressionMap[fullName] = [...memberExpressionMap[fullName], node] + } else { + memberExpressionMap[fullName] = [node] + } + } + }, + ...utils.executeOnVue(context, obj => { + const computedPropertyNameList = utils.getComputedProperties(obj).map(item => `this.${item.key}`) + Object.keys(memberExpressionMap).forEach(fullName => { + let index = -1 + for (let i = 0, len = computedPropertyNameList.length; i < len; i++) { + if (fullName.startsWith(computedPropertyNameList[i])) { + index = i + break + } + } + if (index !== -1) { + memberExpressionMap[fullName].forEach(memberExpressionNode => { + context.report({ + node: memberExpressionNode, + message: `The "{{name}}" is an computed data can't use in data property.`, + data: { + name: computedPropertyNameList[index] + } + }) + }) + } + }) + }) } } } diff --git a/lib/utils/index.js b/lib/utils/index.js index 011ec6571..deeca5fb2 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -499,7 +499,7 @@ module.exports = { return computedPropertiesNode.value.properties .filter(cp => cp.type === 'Property') .map(cp => { - const key = cp.key.name + const key = cp.key.type === 'Identifier' ? cp.key.name : cp.key.type === 'Literal' ? cp.key.value : undefined let value if (cp.value.type === 'FunctionExpression') { @@ -519,6 +519,31 @@ module.exports = { }) }, + /** + * Get all computed properties by looking at all component's properties + * @param {ObjectExpression} componentObject Object with component definition + * @return {Array} Array of node: could be all kinds of node + */ + getDataInnerNode (componentObject) { + const dataPropertiesNode = componentObject.properties + .find(p => + p.type === 'Property' && + p.key.type === 'Identifier' && + p.key.name === 'data' && + (p.value.type === 'ObjectExpression' || p.value.type === 'FunctionExpression') + ) + + if (!dataPropertiesNode) { return [] } + let nodeList + if (dataPropertiesNode.value.type === 'FunctionExpression') { + const nodeListGenerator = this.iterateFunctionExpression(dataPropertiesNode.value) + nodeList = [...nodeListGenerator] + } else { + nodeList = this.iterateObjectExpression(dataPropertiesNode.value) + } + return nodeList + }, + isVueFile (path) { return path.endsWith('.vue') || path.endsWith('.jsx') }, diff --git a/tests/lib/rules/no-computed-in-data.js b/tests/lib/rules/no-computed-in-data.js index af3dfa1f3..b3d5dbfba 100644 --- a/tests/lib/rules/no-computed-in-data.js +++ b/tests/lib/rules/no-computed-in-data.js @@ -16,14 +16,96 @@ var RuleTester = require('eslint').RuleTester // Tests // ------------------------------------------------------------------------------ -var ruleTester = new RuleTester() +var ruleTester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 2019, + sourceType: 'module' + } +}) ruleTester.run('no-computed-in-data', rule, { valid: [ + { + filename: 'test.vue', + code: ` + + ` + } // give me some code that won't trigger a warning ], invalid: [ + { + filename: 'test.vue', + code: ` + + `, + errors: [ + `The "this.test" is an computed data can't use in data property.` + ] + }, + // same computed data reference by multi data property + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: `The "this.test" is an computed data can't use in data property.`, + line: 6, + column: 16 + }, + { + message: `The "this.test" is an computed data can't use in data property.`, + line: 7, + column: 16 + } + ] + } ] }) From 21a58e8acb9504e9479da4627767eae301d4a979 Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Thu, 12 Mar 2020 00:20:19 +0800 Subject: [PATCH 03/12] feat: upate doc , add test --- docs/rules/README.md | 1 + docs/rules/no-computed-in-data.md | 14 +++++++++++++- lib/index.js | 1 + lib/rules/no-computed-in-data.js | 1 - tests/lib/rules/no-computed-in-data.js | 24 +++++++++++++++++++++++- 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/rules/README.md b/docs/rules/README.md index ab1f5b220..889452c75 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -155,6 +155,7 @@ For example: | [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-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | +| [vue/no-computed-in-data](./no-computed-in-data.md) | require computed properties are not used in the data() | | | [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: | | [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: | | [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: | diff --git a/docs/rules/no-computed-in-data.md b/docs/rules/no-computed-in-data.md index e1e9dbed7..a19c5f9e8 100644 --- a/docs/rules/no-computed-in-data.md +++ b/docs/rules/no-computed-in-data.md @@ -1,4 +1,11 @@ -# Ensure computed properties are not used in the data() (no-computed-in-data) +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-computed-in-data +description: require computed properties are not used in the data() +--- +# vue/no-computed-in-data +> require computed properties are not used in the data() Please describe the origin of the rule here. @@ -34,3 +41,8 @@ Give a short description of when it would be appropriate to turn off this rule. ## Further Reading If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-computed-in-data.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-computed-in-data.js) diff --git a/lib/index.js b/lib/index.js index 02e65ba96..217e8ddc9 100644 --- a/lib/index.js +++ b/lib/index.js @@ -38,6 +38,7 @@ module.exports = { 'name-property-casing': require('./rules/name-property-casing'), 'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'), 'no-boolean-default': require('./rules/no-boolean-default'), + 'no-computed-in-data': require('./rules/no-computed-in-data'), 'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'), 'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'), 'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'), diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js index d51263996..54e176926 100644 --- a/lib/rules/no-computed-in-data.js +++ b/lib/rules/no-computed-in-data.js @@ -8,7 +8,6 @@ // Rule Definition // ------------------------------------------------------------------------------ const utils = require('../utils') -const visitor = require('eslint-visitor-keys') module.exports = { meta: { type: 'problem', diff --git a/tests/lib/rules/no-computed-in-data.js b/tests/lib/rules/no-computed-in-data.js index b3d5dbfba..6f901534c 100644 --- a/tests/lib/rules/no-computed-in-data.js +++ b/tests/lib/rules/no-computed-in-data.js @@ -45,8 +45,30 @@ ruleTester.run('no-computed-in-data', rule, { } ` + }, + // should not warn when use computed in methods + { + filename: 'test.vue', + code: ` + + ` } - // give me some code that won't trigger a warning ], invalid: [ From ce6cc4c891f7cd5b34fac7533f947cd4eaf70c42 Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Thu, 12 Mar 2020 14:59:07 +0800 Subject: [PATCH 04/12] test: update test --- tests/lib/rules/no-computed-in-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/rules/no-computed-in-data.js b/tests/lib/rules/no-computed-in-data.js index 6f901534c..e1841a721 100644 --- a/tests/lib/rules/no-computed-in-data.js +++ b/tests/lib/rules/no-computed-in-data.js @@ -62,7 +62,7 @@ ruleTester.run('no-computed-in-data', rule, { }, methods: { foo() { - this.test() + this.test } } } From 98eb052d7ac388dd2631bdca37d1531a9d840c76 Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Sat, 14 Mar 2020 17:39:00 +0800 Subject: [PATCH 05/12] feat: resolve conflict --- docs/rules/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/rules/README.md b/docs/rules/README.md index 8e434483e..8caac7066 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -267,10 +267,6 @@ For example: | [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-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | -<<<<<<< HEAD -======= -| [vue/no-deprecated-filter](./no-deprecated-filter.md) | disallow using deprecated filters syntax | | ->>>>>>> f84fb9b4088d139109c799792fbacc069cb84dcc | [vue/no-computed-in-data](./no-computed-in-data.md) | require computed properties are not used in the data() | | | [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: | | [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: | From b5f9de203a4e96408e1483d46ede54d380d08aee Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Sat, 14 Mar 2020 20:30:08 +0800 Subject: [PATCH 06/12] feat: udpate version --- docs/rules/README.md | 5 +---- docs/rules/no-computed-in-data.md | 2 ++ lib/configs/strongly-recommended.js | 1 + lib/rules/no-computed-in-data.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/rules/README.md b/docs/rules/README.md index 8caac7066..ad1c8f282 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -207,6 +207,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | [vue/max-attributes-per-line](./max-attributes-per-line.md) | enforce the maximum number of attributes per line | :wrench: | | [vue/multiline-html-element-content-newline](./multiline-html-element-content-newline.md) | require a line break before and after the contents of a multiline element | :wrench: | | [vue/mustache-interpolation-spacing](./mustache-interpolation-spacing.md) | enforce unified spacing in mustache interpolations | :wrench: | +| [vue/no-computed-in-data](./no-computed-in-data.md) | require computed properties are not used in the data() | | | [vue/no-multi-spaces](./no-multi-spaces.md) | disallow multiple spaces | :wrench: | | [vue/no-spaces-around-equal-signs-in-attribute](./no-spaces-around-equal-signs-in-attribute.md) | disallow spaces around equal signs in attribute | :wrench: | | [vue/no-template-shadow](./no-template-shadow.md) | disallow variable declarations from shadowing variables declared in the outer scope | | @@ -267,10 +268,6 @@ For example: | [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-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | -| [vue/no-computed-in-data](./no-computed-in-data.md) | require computed properties are not used in the data() | | -| [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: | -| [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: | -| [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: | | [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | | | [vue/no-irregular-whitespace](./no-irregular-whitespace.md) | disallow irregular whitespace | | | [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | | diff --git a/docs/rules/no-computed-in-data.md b/docs/rules/no-computed-in-data.md index a19c5f9e8..0810801aa 100644 --- a/docs/rules/no-computed-in-data.md +++ b/docs/rules/no-computed-in-data.md @@ -7,6 +7,8 @@ description: require computed properties are not used in the data() # vue/no-computed-in-data > require computed properties are not used in the data() +- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`. + Please describe the origin of the rule here. diff --git a/lib/configs/strongly-recommended.js b/lib/configs/strongly-recommended.js index 22018866c..ce7077a33 100644 --- a/lib/configs/strongly-recommended.js +++ b/lib/configs/strongly-recommended.js @@ -17,6 +17,7 @@ module.exports = { 'vue/max-attributes-per-line': 'warn', 'vue/multiline-html-element-content-newline': 'warn', 'vue/mustache-interpolation-spacing': 'warn', + 'vue/no-computed-in-data': 'warn', 'vue/no-multi-spaces': 'warn', 'vue/no-spaces-around-equal-signs-in-attribute': 'warn', 'vue/no-template-shadow': 'warn', diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js index 54e176926..c83d4f3aa 100644 --- a/lib/rules/no-computed-in-data.js +++ b/lib/rules/no-computed-in-data.js @@ -13,7 +13,7 @@ module.exports = { type: 'problem', docs: { description: 'require computed properties are not used in the data()', - category: '', + categories: ['strongly-recommended'], recommended: false, url: 'https://eslint.vuejs.org/rules/no-computed-in-data.html' }, From d3383d1a09dd30d8d106872a3c199f7284bdfd6e Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Sun, 15 Mar 2020 21:10:59 +0800 Subject: [PATCH 07/12] feat: update readme, remove unnecessary util function --- docs/rules/no-computed-in-data.md | 51 +++++++++++++++++--------- lib/rules/no-computed-in-data.js | 2 +- lib/utils/index.js | 25 ------------- tests/lib/rules/no-computed-in-data.js | 18 ++++----- 4 files changed, 42 insertions(+), 54 deletions(-) diff --git a/docs/rules/no-computed-in-data.md b/docs/rules/no-computed-in-data.md index 0810801aa..0065d8b60 100644 --- a/docs/rules/no-computed-in-data.md +++ b/docs/rules/no-computed-in-data.md @@ -12,38 +12,53 @@ description: require computed properties are not used in the data() Please describe the origin of the rule here. -## Rule Details +## :book: Rule Details This rule aims to... Examples of **incorrect** code for this rule: ```js - -// fill me in + ``` Examples of **correct** code for this rule: -```js - -// fill me in - +```javascript + ``` - -### Options - -If there are any options, describe them here. Otherwise, delete this section. - -## When Not To Use It - -Give a short description of when it would be appropriate to turn off this rule. +## :wrench: Options +nothing ## Further Reading - -If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. - +[Computed Properties](https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods) ## :mag: Implementation - [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-computed-in-data.js) diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js index c83d4f3aa..79c106339 100644 --- a/lib/rules/no-computed-in-data.js +++ b/lib/rules/no-computed-in-data.js @@ -67,7 +67,7 @@ module.exports = { memberExpressionMap[fullName].forEach(memberExpressionNode => { context.report({ node: memberExpressionNode, - message: `The "{{name}}" is an computed data can't use in data property.`, + message: `The "{{name}}" is a computed data can't use in data property.`, data: { name: computedPropertyNameList[index] } diff --git a/lib/utils/index.js b/lib/utils/index.js index 0f06c4132..a5073c957 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -484,31 +484,6 @@ module.exports = { }) }, - /** - * Get all computed properties by looking at all component's properties - * @param {ObjectExpression} componentObject Object with component definition - * @return {Array} Array of node: could be all kinds of node - */ - getDataInnerNode (componentObject) { - const dataPropertiesNode = componentObject.properties - .find(p => - p.type === 'Property' && - p.key.type === 'Identifier' && - p.key.name === 'data' && - (p.value.type === 'ObjectExpression' || p.value.type === 'FunctionExpression') - ) - - if (!dataPropertiesNode) { return [] } - let nodeList - if (dataPropertiesNode.value.type === 'FunctionExpression') { - const nodeListGenerator = this.iterateFunctionExpression(dataPropertiesNode.value) - nodeList = [...nodeListGenerator] - } else { - nodeList = this.iterateObjectExpression(dataPropertiesNode.value) - } - return nodeList - }, - isVueFile (path) { return path.endsWith('.vue') || path.endsWith('.jsx') }, diff --git a/tests/lib/rules/no-computed-in-data.js b/tests/lib/rules/no-computed-in-data.js index e1841a721..f959bf9e5 100644 --- a/tests/lib/rules/no-computed-in-data.js +++ b/tests/lib/rules/no-computed-in-data.js @@ -70,8 +70,8 @@ ruleTester.run('no-computed-in-data', rule, { ` } ], - invalid: [ + // should warn when prop key is an String literal { filename: 'test.vue', code: ` @@ -79,12 +79,12 @@ ruleTester.run('no-computed-in-data', rule, { export default { data() { return { - a: this.test + a: this.test + this.foo } }, computed: { test() {}, - 'foo': { + 'foo'() { } } @@ -92,10 +92,11 @@ ruleTester.run('no-computed-in-data', rule, { `, errors: [ - `The "this.test" is an computed data can't use in data property.` + `The "this.test" is a computed data can't use in data property.`, + `The "this.foo" is a computed data can't use in data property.` ] }, - // same computed data reference by multi data property + // same computed data referenced by multi data property { filename: 'test.vue', code: ` @@ -109,21 +110,18 @@ ruleTester.run('no-computed-in-data', rule, { }, computed: { test() {}, - 'foo': { - - } } } `, errors: [ { - message: `The "this.test" is an computed data can't use in data property.`, + message: `The "this.test" is a computed data can't use in data property.`, line: 6, column: 16 }, { - message: `The "this.test" is an computed data can't use in data property.`, + message: `The "this.test" is a computed data can't use in data property.`, line: 7, column: 16 } From ee5bc02e0a0a77e72df3d23a76591f8b610426fe Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Tue, 17 Mar 2020 00:22:06 +0800 Subject: [PATCH 08/12] feat: udpate readme, update report message --- docs/rules/no-computed-in-data.md | 66 ++++++++++++++++---------- lib/rules/no-computed-in-data.js | 2 +- tests/lib/rules/no-computed-in-data.js | 8 ++-- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/docs/rules/no-computed-in-data.md b/docs/rules/no-computed-in-data.md index 0065d8b60..6b360d0fb 100644 --- a/docs/rules/no-computed-in-data.md +++ b/docs/rules/no-computed-in-data.md @@ -9,56 +9,72 @@ description: require computed properties are not used in the data() - :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`. -Please describe the origin of the rule here. +> require computed properties are not used in the data() +- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`. + +Please describe the origin of the rule here. ## :book: Rule Details -This rule aims to... +This rule report computed properties are used in data property Examples of **incorrect** code for this rule: -```js + + +```vue - ``` + + Examples of **correct** code for this rule: -```javascript + + +```vue ``` + + + ## :wrench: Options + nothing ## Further Reading + [Computed Properties](https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods) + ## :mag: Implementation - [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-computed-in-data.js) diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js index 79c106339..13c37b4d0 100644 --- a/lib/rules/no-computed-in-data.js +++ b/lib/rules/no-computed-in-data.js @@ -67,7 +67,7 @@ module.exports = { memberExpressionMap[fullName].forEach(memberExpressionNode => { context.report({ node: memberExpressionNode, - message: `The "{{name}}" is a computed data can't use in data property.`, + message: `Computed property '{{name}}' can't use in data property.`, data: { name: computedPropertyNameList[index] } diff --git a/tests/lib/rules/no-computed-in-data.js b/tests/lib/rules/no-computed-in-data.js index f959bf9e5..2daa77f96 100644 --- a/tests/lib/rules/no-computed-in-data.js +++ b/tests/lib/rules/no-computed-in-data.js @@ -92,8 +92,8 @@ ruleTester.run('no-computed-in-data', rule, { `, errors: [ - `The "this.test" is a computed data can't use in data property.`, - `The "this.foo" is a computed data can't use in data property.` + `Computed property 'this.test' can't use in data property.`, + `Computed property 'this.foo' can't use in data property.` ] }, // same computed data referenced by multi data property @@ -116,12 +116,12 @@ ruleTester.run('no-computed-in-data', rule, { `, errors: [ { - message: `The "this.test" is a computed data can't use in data property.`, + message: `Computed property 'this.test' can't use in data property.`, line: 6, column: 16 }, { - message: `The "this.test" is a computed data can't use in data property.`, + message: `Computed property 'this.test' can't use in data property.`, line: 7, column: 16 } From 7db773fbcacba85b389391b411097e28dfaf608c Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Sat, 4 Apr 2020 20:02:56 +0800 Subject: [PATCH 09/12] feat: fix request from maintainer --- lib/rules/no-computed-in-data.js | 44 ++++++-- lib/utils/index.js | 2 +- tests/lib/rules/no-computed-in-data.js | 140 +++++++++++++++++++++++++ 3 files changed, 175 insertions(+), 11 deletions(-) diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js index 13c37b4d0..c29c1f493 100644 --- a/lib/rules/no-computed-in-data.js +++ b/lib/rules/no-computed-in-data.js @@ -8,6 +8,10 @@ // Rule Definition // ------------------------------------------------------------------------------ const utils = require('../utils') +function topOfStack (stack) { + console.assert(Array.isArray(stack)) + return stack[stack.length - 1] +} module.exports = { meta: { type: 'problem', @@ -25,8 +29,13 @@ module.exports = { create: function (context) { let dataAstNode + let targetObjectExpression const memberExpressionMap = Object.create(null) + const scopeStack = [] + // if data is a function, should reference its scope, other wise should be undefined + let dataPropertyScope return { + ObjectExpression (node) { if (!dataAstNode) { dataAstNode = node.properties.find( @@ -35,34 +44,49 @@ module.exports = { p.key.type === 'Identifier' && p.key.name === 'data' ) + if (dataAstNode) { + targetObjectExpression = node + if (dataAstNode.value.type === 'FunctionExpression') { + dataPropertyScope = dataAstNode.value + scopeStack.push(dataPropertyScope) + } + } } }, + ':function' (node) { + scopeStack.push(node) + }, + ':function:exit' (node) { + scopeStack.pop() + }, 'Property:exit' (node) { if (node === dataAstNode) { dataAstNode = undefined } }, MemberExpression (node) { - if (dataAstNode) { + if (dataAstNode && dataPropertyScope === topOfStack(scopeStack)) { // a memberExpression `like this.a.c.d` -> when traverse to c.d we can got the the full name -> this.a.c.d - const fullName = utils.parseMemberExpression(node).join('.') + const fullName = utils.parseMemberExpression(node).slice(0, 2).join('.') if (memberExpressionMap[fullName]) { - memberExpressionMap[fullName] = [...memberExpressionMap[fullName], node] + // check if parent node in this array, if true ignore this node, such as `{a: this.a.c.d}`, this traverse function visit order is `this.a.c.d` -> `a.c.d` -> `c.d` + const hasParentNodeInArray = memberExpressionMap[fullName].some(nodeInMap => node.parent === nodeInMap) + if (!hasParentNodeInArray) { + memberExpressionMap[fullName] = [...memberExpressionMap[fullName], node] + } } else { memberExpressionMap[fullName] = [node] } } }, ...utils.executeOnVue(context, obj => { + // check if targetObjectExpression is Vue component + if (targetObjectExpression !== obj) { + return + } const computedPropertyNameList = utils.getComputedProperties(obj).map(item => `this.${item.key}`) Object.keys(memberExpressionMap).forEach(fullName => { - let index = -1 - for (let i = 0, len = computedPropertyNameList.length; i < len; i++) { - if (fullName.startsWith(computedPropertyNameList[i])) { - index = i - break - } - } + const index = computedPropertyNameList.findIndex(name => fullName.startsWith(name)) if (index !== -1) { memberExpressionMap[fullName].forEach(memberExpressionNode => { context.report({ diff --git a/lib/utils/index.js b/lib/utils/index.js index a5073c957..17016bdbf 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -464,7 +464,7 @@ module.exports = { return computedPropertiesNode.value.properties .filter(cp => cp.type === 'Property') .map(cp => { - const key = cp.key.type === 'Identifier' ? cp.key.name : cp.key.type === 'Literal' ? cp.key.value : undefined + const key = getStaticPropertyName(cp) let value if (cp.value.type === 'FunctionExpression') { diff --git a/tests/lib/rules/no-computed-in-data.js b/tests/lib/rules/no-computed-in-data.js index 2daa77f96..a555cd525 100644 --- a/tests/lib/rules/no-computed-in-data.js +++ b/tests/lib/rules/no-computed-in-data.js @@ -68,6 +68,62 @@ ruleTester.run('no-computed-in-data', rule, { } ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + // should not warn when objectExpression is not a vue component + { + filename: 'test.vue', + code: ` + + ` } ], invalid: [ @@ -96,6 +152,25 @@ ruleTester.run('no-computed-in-data', rule, { `Computed property 'this.foo' can't use in data property.` ] }, + // should report when data is objectExpression, when this is a root component + { + filename: 'test.vue', + code: ` + + `, + errors: [ + `Computed property 'this.test' can't use in data property.` + ] + }, // same computed data referenced by multi data property { filename: 'test.vue', @@ -126,6 +201,71 @@ ruleTester.run('no-computed-in-data', rule, { column: 16 } ] + }, + // should also report when computed data return an object + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: `Computed property 'this.test' can't use in data property.`, + line: 6, + column: 16, + endColumn: 29, + endLine: 6 + }, + { + message: `Computed property 'this.test' can't use in data property.`, + line: 7, + column: 16, + endLine: 7, + endColumn: 29 + } + ] + }, + // should only report the data function scope computed property + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: `Computed property 'this.foo' can't use in data property.`, + line: 6, + column: 18, + endColumn: 26, + endLine: 6 + } + ] } ] }) From 852ae6f6c7c1b0f9bdcbb7ccb73dcf311f957f0d Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Sun, 5 Apr 2020 12:16:24 +0800 Subject: [PATCH 10/12] feat: udpate readme --- docs/rules/README.md | 3 +- docs/rules/no-computed-in-data.md | 50 ++++++++--------------------- lib/configs/essential.js | 1 + lib/configs/strongly-recommended.js | 1 - lib/configs/vue3-essential.js | 1 + lib/rules/no-computed-in-data.js | 4 +-- 6 files changed, 20 insertions(+), 40 deletions(-) diff --git a/docs/rules/README.md b/docs/rules/README.md index ad1c8f282..e7648636b 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -39,6 +39,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | Rule ID | Description | | |:--------|:------------|:---| | [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | | +| [vue/no-computed-in-data](./no-computed-in-data.md) | disallow computed properties used in the data property | | | [vue/no-deprecated-filter](./no-deprecated-filter.md) | disallow using deprecated filters syntax | | | [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: | | [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: | @@ -146,6 +147,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | Rule ID | Description | | |:--------|:------------|:---| | [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | | +| [vue/no-computed-in-data](./no-computed-in-data.md) | disallow computed properties used in the data property | | | [vue/no-custom-modifiers-on-v-model](./no-custom-modifiers-on-v-model.md) | disallow custom modifiers on v-model used on the component | | | [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names | | | [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes | | @@ -207,7 +209,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | [vue/max-attributes-per-line](./max-attributes-per-line.md) | enforce the maximum number of attributes per line | :wrench: | | [vue/multiline-html-element-content-newline](./multiline-html-element-content-newline.md) | require a line break before and after the contents of a multiline element | :wrench: | | [vue/mustache-interpolation-spacing](./mustache-interpolation-spacing.md) | enforce unified spacing in mustache interpolations | :wrench: | -| [vue/no-computed-in-data](./no-computed-in-data.md) | require computed properties are not used in the data() | | | [vue/no-multi-spaces](./no-multi-spaces.md) | disallow multiple spaces | :wrench: | | [vue/no-spaces-around-equal-signs-in-attribute](./no-spaces-around-equal-signs-in-attribute.md) | disallow spaces around equal signs in attribute | :wrench: | | [vue/no-template-shadow](./no-template-shadow.md) | disallow variable declarations from shadowing variables declared in the outer scope | | diff --git a/docs/rules/no-computed-in-data.md b/docs/rules/no-computed-in-data.md index 6b360d0fb..965a78cd5 100644 --- a/docs/rules/no-computed-in-data.md +++ b/docs/rules/no-computed-in-data.md @@ -2,22 +2,16 @@ pageClass: rule-details sidebarDepth: 0 title: vue/no-computed-in-data -description: require computed properties are not used in the data() +description: disallow computed properties used in the data property --- # vue/no-computed-in-data -> require computed properties are not used in the data() +> disallow computed properties used in the data property -- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`. - -> require computed properties are not used in the data() - -- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`. - -Please describe the origin of the rule here. +- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`. ## :book: Rule Details -This rule report computed properties are used in data property +This rule report computed properties that used in data property Examples of **incorrect** code for this rule: @@ -28,38 +22,22 @@ Examples of **incorrect** code for this rule: export default { data() { return { - value: 'hello ' + this.world, + /* ✓ GOOD */ + value: 'hello ' + 'world', + + /* ✗ BAD */ a: this.world, b: this.world, + c: [this.world, this.number] }; }, computed: { world() { return 'world'; }, - }, -}; - -``` - - - -Examples of **correct** code for this rule: - - - -```vue - @@ -69,9 +47,9 @@ export default { ## :wrench: Options -nothing +Nothing. -## Further Reading +## :books: Further reading [Computed Properties](https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods) diff --git a/lib/configs/essential.js b/lib/configs/essential.js index 21335f197..3d3c93a4f 100644 --- a/lib/configs/essential.js +++ b/lib/configs/essential.js @@ -7,6 +7,7 @@ module.exports = { extends: require.resolve('./base'), rules: { 'vue/no-async-in-computed-properties': 'error', + 'vue/no-computed-in-data': 'error', 'vue/no-custom-modifiers-on-v-model': 'error', 'vue/no-dupe-keys': 'error', 'vue/no-duplicate-attributes': 'error', diff --git a/lib/configs/strongly-recommended.js b/lib/configs/strongly-recommended.js index ce7077a33..22018866c 100644 --- a/lib/configs/strongly-recommended.js +++ b/lib/configs/strongly-recommended.js @@ -17,7 +17,6 @@ module.exports = { 'vue/max-attributes-per-line': 'warn', 'vue/multiline-html-element-content-newline': 'warn', 'vue/mustache-interpolation-spacing': 'warn', - 'vue/no-computed-in-data': 'warn', 'vue/no-multi-spaces': 'warn', 'vue/no-spaces-around-equal-signs-in-attribute': 'warn', 'vue/no-template-shadow': 'warn', diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js index 3e45ddd2c..9510d3755 100644 --- a/lib/configs/vue3-essential.js +++ b/lib/configs/vue3-essential.js @@ -7,6 +7,7 @@ module.exports = { extends: require.resolve('./base'), rules: { 'vue/no-async-in-computed-properties': 'error', + 'vue/no-computed-in-data': 'error', 'vue/no-deprecated-filter': 'error', 'vue/no-deprecated-scope-attribute': 'error', 'vue/no-deprecated-slot-attribute': 'error', diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js index c29c1f493..5f870bd6b 100644 --- a/lib/rules/no-computed-in-data.js +++ b/lib/rules/no-computed-in-data.js @@ -16,8 +16,8 @@ module.exports = { meta: { type: 'problem', docs: { - description: 'require computed properties are not used in the data()', - categories: ['strongly-recommended'], + description: 'disallow computed properties used in the data property', + categories: ['vue3-essential', 'essential'], recommended: false, url: 'https://eslint.vuejs.org/rules/no-computed-in-data.html' }, From 51905de8b75a71b8bb6c12c9da3628f9e9e49430 Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Sun, 5 Apr 2020 12:18:34 +0800 Subject: [PATCH 11/12] feat: udpate readme --- docs/rules/no-computed-in-data.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/rules/no-computed-in-data.md b/docs/rules/no-computed-in-data.md index 965a78cd5..7c1e76418 100644 --- a/docs/rules/no-computed-in-data.md +++ b/docs/rules/no-computed-in-data.md @@ -13,8 +13,6 @@ description: disallow computed properties used in the data property This rule report computed properties that used in data property -Examples of **incorrect** code for this rule: - ```vue From ff6b877c31094545c38c829c93c5fe1b1a2e9920 Mon Sep 17 00:00:00 2001 From: IWANABETHATGUY <974153916@qq.com> Date: Tue, 7 Apr 2020 18:01:33 +0800 Subject: [PATCH 12/12] feat: set categories to undefined --- docs/rules/README.md | 3 +-- docs/rules/no-computed-in-data.md | 2 -- lib/configs/essential.js | 1 - lib/configs/vue3-essential.js | 1 - lib/rules/no-computed-in-data.js | 2 +- 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/rules/README.md b/docs/rules/README.md index e7648636b..2aa203662 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -39,7 +39,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | Rule ID | Description | | |:--------|:------------|:---| | [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | | -| [vue/no-computed-in-data](./no-computed-in-data.md) | disallow computed properties used in the data property | | | [vue/no-deprecated-filter](./no-deprecated-filter.md) | disallow using deprecated filters syntax | | | [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: | | [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: | @@ -147,7 +146,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | Rule ID | Description | | |:--------|:------------|:---| | [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | | -| [vue/no-computed-in-data](./no-computed-in-data.md) | disallow computed properties used in the data property | | | [vue/no-custom-modifiers-on-v-model](./no-custom-modifiers-on-v-model.md) | disallow custom modifiers on v-model used on the component | | | [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names | | | [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes | | @@ -269,6 +267,7 @@ For example: | [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-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: | +| [vue/no-computed-in-data](./no-computed-in-data.md) | disallow computed properties used in the data property | | | [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | | | [vue/no-irregular-whitespace](./no-irregular-whitespace.md) | disallow irregular whitespace | | | [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | | diff --git a/docs/rules/no-computed-in-data.md b/docs/rules/no-computed-in-data.md index 7c1e76418..2965d2e8f 100644 --- a/docs/rules/no-computed-in-data.md +++ b/docs/rules/no-computed-in-data.md @@ -7,8 +7,6 @@ description: disallow computed properties used in the data property # vue/no-computed-in-data > disallow computed properties used in the data property -- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`. - ## :book: Rule Details This rule report computed properties that used in data property diff --git a/lib/configs/essential.js b/lib/configs/essential.js index 3d3c93a4f..21335f197 100644 --- a/lib/configs/essential.js +++ b/lib/configs/essential.js @@ -7,7 +7,6 @@ module.exports = { extends: require.resolve('./base'), rules: { 'vue/no-async-in-computed-properties': 'error', - 'vue/no-computed-in-data': 'error', 'vue/no-custom-modifiers-on-v-model': 'error', 'vue/no-dupe-keys': 'error', 'vue/no-duplicate-attributes': 'error', diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js index 9510d3755..3e45ddd2c 100644 --- a/lib/configs/vue3-essential.js +++ b/lib/configs/vue3-essential.js @@ -7,7 +7,6 @@ module.exports = { extends: require.resolve('./base'), rules: { 'vue/no-async-in-computed-properties': 'error', - 'vue/no-computed-in-data': 'error', 'vue/no-deprecated-filter': 'error', 'vue/no-deprecated-scope-attribute': 'error', 'vue/no-deprecated-slot-attribute': 'error', diff --git a/lib/rules/no-computed-in-data.js b/lib/rules/no-computed-in-data.js index 5f870bd6b..ae9b76efd 100644 --- a/lib/rules/no-computed-in-data.js +++ b/lib/rules/no-computed-in-data.js @@ -17,7 +17,7 @@ module.exports = { type: 'problem', docs: { description: 'disallow computed properties used in the data property', - categories: ['vue3-essential', 'essential'], + categories: undefined, recommended: false, url: 'https://eslint.vuejs.org/rules/no-computed-in-data.html' },