diff --git a/docs/rules/README.md b/docs/rules/README.md index e184873e5..f70f0f414 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-deprecated-data-object-declaration](./no-deprecated-data-object-declaration.md) | disallow using deprecated object declaration on data | :wrench: | | [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: | diff --git a/docs/rules/no-deprecated-data-object-declaration.md b/docs/rules/no-deprecated-data-object-declaration.md new file mode 100644 index 000000000..c095fadb3 --- /dev/null +++ b/docs/rules/no-deprecated-data-object-declaration.md @@ -0,0 +1,65 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-deprecated-data-object-declaration +description: disallow using deprecated object declaration on data +--- +# vue/no-deprecated-data-object-declaration +> disallow using deprecated object declaration on data + +- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`. +- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule. + +## :book: Rule Details + +This rule reports use of deprecated object declaration on `data` property (in Vue.js 3.0.0+). +The different from `vue/no-shared-component-data` is the root instance being also disallowed. + + + +```vue + +``` + + + +## :wrench: Options + +Nothing. + +## :books: Further reading + +- [RFC: remove data object declaration](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0019-remove-data-object-declaration.md) + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-data-object-declaration.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-data-object-declaration.js) diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js index 3e45ddd2c..3121fa6ce 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-deprecated-data-object-declaration': 'error', 'vue/no-deprecated-filter': 'error', 'vue/no-deprecated-scope-attribute': 'error', 'vue/no-deprecated-slot-attribute': 'error', diff --git a/lib/index.js b/lib/index.js index 10e2e8766..53e264215 100644 --- a/lib/index.js +++ b/lib/index.js @@ -40,6 +40,7 @@ module.exports = { 'no-boolean-default': require('./rules/no-boolean-default'), 'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'), 'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'), + 'no-deprecated-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'), 'no-deprecated-filter': require('./rules/no-deprecated-filter'), '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-deprecated-data-object-declaration.js b/lib/rules/no-deprecated-data-object-declaration.js new file mode 100644 index 000000000..4c8399d76 --- /dev/null +++ b/lib/rules/no-deprecated-data-object-declaration.js @@ -0,0 +1,86 @@ +/** + * @fileoverview disallow using deprecated object declaration on data + * @author yoyo930021 + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const utils = require('../utils') + +function isOpenParen (token) { + return token.type === 'Punctuator' && token.value === '(' +} + +function isCloseParen (token) { + return token.type === 'Punctuator' && token.value === ')' +} + +function getFirstAndLastTokens (node, sourceCode) { + let first = sourceCode.getFirstToken(node) + let last = sourceCode.getLastToken(node) + + // If the value enclosed by parentheses, update the 'first' and 'last' by the parentheses. + while (true) { + const prev = sourceCode.getTokenBefore(first) + const next = sourceCode.getTokenAfter(last) + if (isOpenParen(prev) && isCloseParen(next)) { + first = prev + last = next + } else { + return { first, last } + } + } +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'disallow using deprecated object declaration on data', + categories: ['vue3-essential'], + url: 'https://eslint.vuejs.org/rules/no-deprecated-data-object-declaration.html' + }, + fixable: 'code', + schema: [], + messages: { + objectDeclarationIsDeprecated: "Object declaration on \'data\' property is deprecated. Using function declaration instead." + } + }, + + create (context) { + const sourceCode = context.getSourceCode() + + return utils.executeOnVue(context, (obj) => { + obj.properties + .filter(p => + p.type === 'Property' && + p.key.type === 'Identifier' && + p.key.name === 'data' && + p.value.type !== 'FunctionExpression' && + p.value.type !== 'ArrowFunctionExpression' && + p.value.type !== 'Identifier' + ) + .forEach(p => { + context.report({ + node: p, + messageId: 'objectDeclarationIsDeprecated', + fix (fixer) { + const tokens = getFirstAndLastTokens(p.value, sourceCode) + + return [ + fixer.insertTextBefore(tokens.first, 'function() {\nreturn '), + fixer.insertTextAfter(tokens.last, ';\n}') + ] + } + }) + }) + }) + } +} diff --git a/tests/lib/rules/no-deprecated-data-object-declaration.js b/tests/lib/rules/no-deprecated-data-object-declaration.js new file mode 100644 index 000000000..1ced275c0 --- /dev/null +++ b/tests/lib/rules/no-deprecated-data-object-declaration.js @@ -0,0 +1,289 @@ +/** + * @fileoverview disallow using deprecated object declaration on data + * @author yoyo930021 + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/no-deprecated-data-object-declaration') + +const RuleTester = require('eslint').RuleTester + +const parserOptions = { + ecmaVersion: 2018, + sourceType: 'module' +} + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +const ruleTester = new RuleTester() +ruleTester.run('no-deprecated-data-object-declaration', rule, { + + valid: [ + { + filename: 'test.js', + code: ` + createApp({ + data: function () { + return { + foo: 'bar' + } + } + }).mount('#app') + `, + parserOptions + }, + { + filename: 'test.js', + code: ` + createApp({ + ...data, + data () { + return { + foo: 'bar' + } + } + }).mount('#app') + `, + parserOptions + }, + { + filename: 'test.js', + code: ` + const app = createApp(App) + app.component('some-comp', { + data: function () { + return { + foo: 'bar' + } + } + }) + `, + parserOptions + }, + { + filename: 'test.vue', + code: ` + export default { + data: function () { + return { + foo: 'bar' + } + } + } + `, + parserOptions + }, + { + filename: 'test.vue', + code: ` + export default { + ...foo + } + `, + parserOptions + }, + { + filename: 'test.vue', + code: ` + export default { + data + } + `, + parserOptions + }, + { + filename: 'test.vue', + code: ` + export default { + data: () => { + + } + } + `, + parserOptions + }, + { + filename: 'test.vue', + code: ` + export default { + data () { + + }, + methods: { + + } + } + `, + parserOptions + }, + { + filename: 'test.vue', + code: ` + export default { + data () { + + }, + computed: { + + } + } + `, + parserOptions + } + ], + + invalid: [ + { + filename: 'test.js', + code: ` + const app = createApp(App) + app.component('some-comp', { + data: { + foo: 'bar' + } + }) + `, + output: ` + const app = createApp(App) + app.component('some-comp', { + data: function() { +return { + foo: 'bar' + }; +} + }) + `, + parserOptions, + errors: [{ + message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.", + line: 4 + }] + }, + { + filename: 'test.js', + code: ` + app.component('some-comp', { + data: { + foo: 'bar' + } + }) + `, + output: ` + app.component('some-comp', { + data: function() { +return { + foo: 'bar' + }; +} + }) + `, + parserOptions, + errors: [{ + message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.", + line: 3 + }] + }, + { + filename: 'test.vue', + code: ` + export default { + data: { + foo: 'bar' + } + } + `, + output: ` + export default { + data: function() { +return { + foo: 'bar' + }; +} + } + `, + parserOptions, + errors: [{ + message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.", + line: 3 + }] + }, + { + filename: 'test.vue', + code: ` + export default { + data: /*a*/ (/*b*/{ + foo: 'bar' + }) + } + `, + output: ` + export default { + data: /*a*/ function() { +return (/*b*/{ + foo: 'bar' + }); +} + } + `, + parserOptions, + errors: [{ + message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.", + line: 3 + }] + }, + { + filename: 'test.js', + code: ` + createApp({ + data: { + foo: 'bar' + } + }) + `, + output: ` + createApp({ + data: function() { +return { + foo: 'bar' + }; +} + }) + `, + parserOptions, + errors: [{ + message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.", + line: 3 + }] + }, + { + filename: 'test.js', + code: ` + createApp({ + data: { + foo: 'bar' + } + }).mount('#app') + `, + output: ` + createApp({ + data: function() { +return { + foo: 'bar' + }; +} + }).mount('#app') + `, + parserOptions, + errors: [{ + message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.", + line: 3 + }] + } + ] +})