diff --git a/lib/rules/no-shared-component-data.js b/lib/rules/no-shared-component-data.js index 85d97b9dd..69d58c970 100644 --- a/lib/rules/no-shared-component-data.js +++ b/lib/rules/no-shared-component-data.js @@ -6,10 +6,33 @@ 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 } + } + } +} + function create (context) { - // ---------------------------------------------------------------------- - // Public - // ---------------------------------------------------------------------- + const sourceCode = context.getSourceCode() return utils.executeOnVueComponent(context, (obj) => { obj.properties @@ -21,10 +44,24 @@ function create (context) { p.value.type !== 'ArrowFunctionExpression' && p.value.type !== 'Identifier' ) - .forEach(cp => { + .forEach(p => { context.report({ - node: cp.value, - message: '`data` property in component must be a function' + node: p, + message: '`data` property in component must be a function', + fix (fixer) { + const tokens = getFirstAndLastTokens(p.value, sourceCode) + + // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by: + // return [ + // fixer.insertTextBefore(tokens.first, 'function() {\nreturn '), + // fixer.insertTextAfter(tokens.last, ';\n}') + // ] + // See: https://eslint.org/blog/2017/06/eslint-v4.1.0-released#applying-multiple-autofixes-simultaneously + const range = [tokens.first.range[0], tokens.last.range[1]] + const valueText = sourceCode.text.slice(range[0], range[1]) + const replacement = `function() {\nreturn ${valueText};\n}` + return fixer.replaceTextRange(range, replacement) + } }) }) }) @@ -40,7 +77,7 @@ module.exports = { description: "enforce component's data property to be a function", category: 'essential' }, - fixable: null, // or "code" or "whitespace" + fixable: 'code', schema: [] }, diff --git a/tests/lib/rules/no-shared-component-data.js b/tests/lib/rules/no-shared-component-data.js index cc1380284..29d70c9ea 100644 --- a/tests/lib/rules/no-shared-component-data.js +++ b/tests/lib/rules/no-shared-component-data.js @@ -130,6 +130,15 @@ ruleTester.run('no-shared-component-data', rule, { } }) `, + output: ` + Vue.component('some-comp', { + data: function() { +return { + foo: 'bar' + }; +} + }) + `, parserOptions, errors: [{ message: '`data` property in component must be a function', @@ -145,6 +154,39 @@ ruleTester.run('no-shared-component-data', rule, { } } `, + output: ` + export default { + data: function() { +return { + foo: 'bar' + }; +} + } + `, + parserOptions, + errors: [{ + message: '`data` property in component must be a function', + 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: '`data` property in component must be a function',