From 64d2a59cb688e0ecf28873c402e0d1fa57b14202 Mon Sep 17 00:00:00 2001 From: waynzh Date: Mon, 11 Mar 2024 20:16:40 +0800 Subject: [PATCH 1/4] feat(order-in-components): add side effects suggestions --- lib/rules/order-in-components.js | 92 +++++---- tests/lib/rules/order-in-components.js | 248 +++++++++++++++++++++++-- 2 files changed, 288 insertions(+), 52 deletions(-) diff --git a/lib/rules/order-in-components.js b/lib/rules/order-in-components.js index 57247e0c2..72591117d 100644 --- a/lib/rules/order-in-components.js +++ b/lib/rules/order-in-components.js @@ -218,6 +218,7 @@ module.exports = { url: 'https://eslint.vuejs.org/rules/order-in-components.html' }, fixable: 'code', // null or "code" or "whitespace" + hasSuggestions: true, schema: [ { type: 'object', @@ -231,7 +232,9 @@ module.exports = { ], messages: { order: - 'The "{{name}}" property should be above the "{{firstUnorderedPropertyName}}" property on line {{line}}.' + 'The "{{name}}" property should be above the "{{firstUnorderedPropertyName}}" property on line {{line}}.', + hasSideEffect: + 'There may be some side effects, the "{{name}}" property should be manually moved above the "{{firstUnorderedPropertyName}}" property on line {{line}}.' } }, /** @param {RuleContext} context */ @@ -287,6 +290,41 @@ module.exports = { if (firstUnorderedProperty) { const line = firstUnorderedProperty.node.loc.start.line + const propertyNode = property.node + const firstUnorderedPropertyNode = firstUnorderedProperty.node + const hasSideEffectsPossibility = propertiesNodes + .slice( + propertiesNodes.indexOf(firstUnorderedPropertyNode), + propertiesNodes.indexOf(propertyNode) + 1 + ) + .some( + (property) => + !isNotSideEffectsNode(property, sourceCode.visitorKeys) + ) + + function* handleFix(fixer) { + const afterComma = sourceCode.getTokenAfter(propertyNode) + const hasAfterComma = isComma(afterComma) + + const beforeComma = sourceCode.getTokenBefore(propertyNode) + const codeStart = beforeComma.range[1] // to include comments + const codeEnd = hasAfterComma + ? afterComma.range[1] + : propertyNode.range[1] + + const removeStart = hasAfterComma ? codeStart : beforeComma.range[0] + yield fixer.removeRange([removeStart, codeEnd]) + + const propertyCode = + sourceCode.text.slice(codeStart, codeEnd) + + (hasAfterComma ? '' : ',') + const insertTarget = sourceCode.getTokenBefore( + firstUnorderedPropertyNode + ) + + yield fixer.insertTextAfter(insertTarget, propertyCode) + } + context.report({ node: property.node, messageId: 'order', @@ -295,44 +333,20 @@ module.exports = { firstUnorderedPropertyName: firstUnorderedProperty.name, line }, - *fix(fixer) { - const propertyNode = property.node - const firstUnorderedPropertyNode = firstUnorderedProperty.node - const hasSideEffectsPossibility = propertiesNodes - .slice( - propertiesNodes.indexOf(firstUnorderedPropertyNode), - propertiesNodes.indexOf(propertyNode) + 1 - ) - .some( - (property) => - !isNotSideEffectsNode(property, sourceCode.visitorKeys) - ) - if (hasSideEffectsPossibility) { - return - } - const afterComma = sourceCode.getTokenAfter(propertyNode) - const hasAfterComma = isComma(afterComma) - - const beforeComma = sourceCode.getTokenBefore(propertyNode) - const codeStart = beforeComma.range[1] // to include comments - const codeEnd = hasAfterComma - ? afterComma.range[1] - : propertyNode.range[1] - - const removeStart = hasAfterComma - ? codeStart - : beforeComma.range[0] - yield fixer.removeRange([removeStart, codeEnd]) - - const propertyCode = - sourceCode.text.slice(codeStart, codeEnd) + - (hasAfterComma ? '' : ',') - const insertTarget = sourceCode.getTokenBefore( - firstUnorderedPropertyNode - ) - - yield fixer.insertTextAfter(insertTarget, propertyCode) - } + fix: hasSideEffectsPossibility ? undefined : handleFix, + suggest: hasSideEffectsPossibility + ? [ + { + messageId: 'hasSideEffect', + data: { + name: property.name, + firstUnorderedPropertyName: firstUnorderedProperty.name, + line + }, + fix: handleFix + } + ] + : undefined }) } } diff --git a/tests/lib/rules/order-in-components.js b/tests/lib/rules/order-in-components.js index 25010f483..c396feacf 100644 --- a/tests/lib/rules/order-in-components.js +++ b/tests/lib/rules/order-in-components.js @@ -104,6 +104,23 @@ ruleTester.run('order-in-components', rule, { `, languageOptions }, + { + filename: 'test.vue', + code: ` + export default { + name: 'app', + data () { + return { + msg: 'Welcome to Your Vue.js App' + } + }, + computed: { + ...mapStates(['foo']) + }, + } + `, + languageOptions + }, { filename: 'test.js', code: ` @@ -591,7 +608,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: obj.fn(), + }; + ` + } + ] } ], languageOptions @@ -612,7 +642,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: new MyClass(), + }; + ` + } + ] } ], languageOptions @@ -633,7 +676,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: i++, + }; + ` + } + ] } ], languageOptions @@ -654,7 +710,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: i = 0, + }; + ` + } + ] } ], languageOptions @@ -675,7 +744,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: template\`\${foo}\`, + }; + ` + } + ] } ], languageOptions @@ -696,7 +778,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + [obj.fn()]: 'test', + }; + ` + } + ] } ], languageOptions @@ -717,7 +812,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: {test: obj.fn()}, + }; + ` + } + ] } ], languageOptions @@ -738,7 +846,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: [obj.fn(), 1], + }; + ` + } + ] } ], languageOptions @@ -759,7 +880,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: obj.fn().prop, + }; + ` + } + ] } ], languageOptions @@ -780,7 +914,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: delete obj.prop, + }; + ` + } + ] } ], languageOptions @@ -801,7 +948,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: fn() + a + b, + }; + ` + } + ] } ], languageOptions @@ -822,7 +982,20 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: a ? fn() : null, + }; + ` + } + ] } ], languageOptions @@ -843,7 +1016,56 @@ ruleTester.run('order-in-components', rule, { { message: 'The "name" property should be above the "data" property on line 3.', - line: 6 + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "name" property should be manually moved above the "data" property on line 3.', + output: ` + export default { + name: 'burger', + data() { + }, + test: \`test \${fn()} \${a}\`, + }; + ` + } + ] + } + ], + languageOptions + }, + { + // side-effects https://github.com/vuejs/eslint-plugin-vue/issues/2418 + filename: 'example.vue', + code: ` + export default { + computed: { + ...mapStates(['foo']) + }, + data() { + }, + }; + `, + output: null, + errors: [ + { + message: + 'The "data" property should be above the "computed" property on line 3.', + line: 6, + suggestions: [ + { + desc: 'There may be some side effects, the "data" property should be manually moved above the "computed" property on line 3.', + output: ` + export default { + data() { + }, + computed: { + ...mapStates(['foo']) + }, + }; + ` + } + ] } ], languageOptions From 2d8ff23d45cc2d4e115b00c7dc9df44318970d14 Mon Sep 17 00:00:00 2001 From: waynzh Date: Mon, 11 Mar 2024 20:16:47 +0800 Subject: [PATCH 2/4] docs: update --- docs/rules/index.md | 2 +- docs/rules/order-in-components.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/rules/index.md b/docs/rules/index.md index 8d1b7f366..ad3d06906 100644 --- a/docs/rules/index.md +++ b/docs/rules/index.md @@ -181,7 +181,7 @@ Rules in this category are enabled for all presets provided by eslint-plugin-vue | [vue/no-lone-template](./no-lone-template.md) | disallow unnecessary `