From d6689bcf31dccb86b32360c9e30b9db6856597c7 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sat, 2 Dec 2017 20:07:57 +0900 Subject: [PATCH 1/2] Update: make vue/no-shared-component-data fixable --- lib/rules/no-shared-component-data.js | 43 +++++++++++++++++---- tests/lib/rules/no-shared-component-data.js | 42 ++++++++++++++++++++ 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/lib/rules/no-shared-component-data.js b/lib/rules/no-shared-component-data.js index 85d97b9dd..2ac16f300 100644 --- a/lib/rules/no-shared-component-data.js +++ b/lib/rules/no-shared-component-data.js @@ -6,10 +6,16 @@ const utils = require('../utils') +function isOpenParen (token) { + return token.type === 'Punctuator' && token.value === '(' +} + +function isCloseParen (token) { + return token.type === 'Punctuator' && token.value === ')' +} + function create (context) { - // ---------------------------------------------------------------------- - // Public - // ---------------------------------------------------------------------- + const sourceCode = context.getSourceCode() return utils.executeOnVueComponent(context, (obj) => { obj.properties @@ -21,10 +27,33 @@ 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) { + let first = sourceCode.getFirstToken(p.value) + let last = sourceCode.getLastToken(p.value) + + // If the value enclosed by parentheses, update the 'first' and 'last' by the parentheses. + let t = null + let u = null + while (isOpenParen(t = sourceCode.getTokenBefore(first)) && isCloseParen(u = sourceCode.getTokenAfter(last))) { + first = t + last = u + } + + // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by: + // return [ + // fixer.insertTextBefore(first, 'function() {\nreturn '), + // fixer.insertTextAfter(last, ';\n}') + // ] + // See: https://eslint.org/blog/2017/06/eslint-v4.1.0-released#applying-multiple-autofixes-simultaneously + const range = [first.range[0], 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 +69,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', From b5b8dc55b7c8da543f8b1c9ededf10b391809ba7 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 3 Dec 2017 21:08:19 +0900 Subject: [PATCH 2/2] refactoring --- lib/rules/no-shared-component-data.js | 34 +++++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/rules/no-shared-component-data.js b/lib/rules/no-shared-component-data.js index 2ac16f300..69d58c970 100644 --- a/lib/rules/no-shared-component-data.js +++ b/lib/rules/no-shared-component-data.js @@ -14,6 +14,23 @@ 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) { const sourceCode = context.getSourceCode() @@ -32,24 +49,15 @@ function create (context) { node: p, message: '`data` property in component must be a function', fix (fixer) { - let first = sourceCode.getFirstToken(p.value) - let last = sourceCode.getLastToken(p.value) - - // If the value enclosed by parentheses, update the 'first' and 'last' by the parentheses. - let t = null - let u = null - while (isOpenParen(t = sourceCode.getTokenBefore(first)) && isCloseParen(u = sourceCode.getTokenAfter(last))) { - first = t - last = u - } + 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(first, 'function() {\nreturn '), - // fixer.insertTextAfter(last, ';\n}') + // 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 = [first.range[0], last.range[1]] + 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)