diff --git a/README.md b/README.md index 866c7f08..a78fc8c2 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Name | ✔️ | 🛠 | 💡 | Description [require-meta-docs-description](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-docs-description.md) | | | | require rules to implement a `meta.docs.description` property with the correct format [require-meta-docs-url](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-docs-url.md) | | 🛠 | | require rules to implement a `meta.docs.url` property [require-meta-fixable](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-fixable.md) | ✔️ | | | require rules to implement a `meta.fixable` property -[require-meta-has-suggestions](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-has-suggestions.md) | | | | require suggestable rules to implement a `meta.hasSuggestions` property +[require-meta-has-suggestions](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-has-suggestions.md) | | 🛠 | | require suggestable rules to implement a `meta.hasSuggestions` property [require-meta-schema](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-schema.md) | | 🛠 | | require rules to implement a `meta.schema` property [require-meta-type](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-type.md) | | | | require rules to implement a `meta.type` property [test-case-property-ordering](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/test-case-property-ordering.md) | | 🛠 | | require the properties of a test case to be placed in a consistent order diff --git a/docs/rules/require-meta-has-suggestions.md b/docs/rules/require-meta-has-suggestions.md index 86568702..6d08487d 100644 --- a/docs/rules/require-meta-has-suggestions.md +++ b/docs/rules/require-meta-has-suggestions.md @@ -1,5 +1,7 @@ # Require suggestable rules to implement a `meta.hasSuggestions` property (require-meta-has-suggestions) +⚒️ The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#--fix) can automatically fix some of the problems reported by this rule. + A suggestable ESLint rule should specify the `meta.hasSuggestions` property with a value of `true`. This makes it easier for both humans and tooling to tell whether a rule provides suggestions. [As of ESLint 8](https://eslint.org/blog/2021/06/whats-coming-in-eslint-8.0.0#rules-with-suggestions-now-require-the-metahassuggestions-property), an exception will be thrown if a suggestable rule is missing this property. Likewise, rules that do not report suggestions should not enable the `meta.hasSuggestions` property. diff --git a/lib/rules/require-meta-has-suggestions.js b/lib/rules/require-meta-has-suggestions.js index e29a41ce..537cebd0 100644 --- a/lib/rules/require-meta-has-suggestions.js +++ b/lib/rules/require-meta-has-suggestions.js @@ -15,6 +15,7 @@ module.exports = { category: 'Rules', recommended: false, }, + fixable: 'code', schema: [], messages: { shouldBeSuggestable: 'Suggestable rules should specify a `meta.hasSuggestions` property with value `true`.', @@ -64,10 +65,31 @@ module.exports = { if (ruleReportsSuggestions) { if (!hasSuggestionsProperty) { // Rule reports suggestions but is missing the `meta.hasSuggestions` property altogether. - context.report({ node: metaNode ? metaNode : ruleInfo.create, messageId: 'shouldBeSuggestable' }); + context.report({ + node: metaNode ? metaNode : ruleInfo.create, + messageId: 'shouldBeSuggestable', + fix (fixer) { + if (metaNode && metaNode.type === 'ObjectExpression') { + if (metaNode.properties.length === 0) { + // If object is empty, just replace entire object. + return fixer.replaceText(metaNode, '{ hasSuggestions: true }'); + } + // Add new property to start of property list. + return fixer.insertTextBefore(metaNode.properties[0], 'hasSuggestions: true, '); + } + }, + }); } else if (hasSuggestionsStaticValue && hasSuggestionsStaticValue.value !== true) { // Rule reports suggestions but does not have `meta.hasSuggestions` property enabled. - context.report({ node: hasSuggestionsProperty.value, messageId: 'shouldBeSuggestable' }); + context.report({ + node: hasSuggestionsProperty.value, + messageId: 'shouldBeSuggestable', + fix (fixer) { + if (hasSuggestionsProperty.value.type === 'Literal' || (hasSuggestionsProperty.value.type === 'Identifier' && hasSuggestionsProperty.value.name === 'undefined')) { + return fixer.replaceText(hasSuggestionsProperty.value, 'true'); + } + }, + }); } } else if (!ruleReportsSuggestions && hasSuggestionsProperty && hasSuggestionsStaticValue && hasSuggestionsStaticValue.value === true) { // Rule does not report suggestions but has the `meta.hasSuggestions` property enabled. diff --git a/tests/lib/rules/require-meta-has-suggestions.js b/tests/lib/rules/require-meta-has-suggestions.js index 5043b5bf..38e6005c 100644 --- a/tests/lib/rules/require-meta-has-suggestions.js +++ b/tests/lib/rules/require-meta-has-suggestions.js @@ -164,18 +164,41 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: [{}]}); } }; `, + output: null, errors: [{ messageId: 'shouldBeSuggestable', type: 'FunctionExpression', line: 3, column: 17, endLine: 3, endColumn: 78 }], }, { - // Reports suggestions, no hasSuggestions property, violation should be on `meta` object. + // Reports suggestions, no hasSuggestions property, violation should be on `meta` object, empty meta object. code: ` module.exports = { meta: {}, create(context) { context.report({node, message, suggest: [{}]}); } }; `, + output: ` + module.exports = { + meta: { hasSuggestions: true }, + create(context) { context.report({node, message, suggest: [{}]}); } + }; + `, errors: [{ messageId: 'shouldBeSuggestable', type: 'ObjectExpression', line: 3, column: 17, endLine: 3, endColumn: 19 }], }, + { + // Reports suggestions, no hasSuggestions property, violation should be on `meta` object, non-empty meta object. + code: ` + module.exports = { + meta: { foo: 'bar' }, + create(context) { context.report({node, message, suggest: [{}]}); } + }; + `, + output: ` + module.exports = { + meta: { hasSuggestions: true, foo: 'bar' }, + create(context) { context.report({node, message, suggest: [{}]}); } + }; + `, + errors: [{ messageId: 'shouldBeSuggestable', type: 'ObjectExpression', line: 3, column: 17, endLine: 3, endColumn: 31 }], + }, { // Reports suggestions (in variable), no hasSuggestions property, violation should be on `meta` object. code: ` @@ -185,6 +208,13 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: SUGGESTIONS}); } }; `, + output: ` + const SUGGESTIONS = [{}]; + module.exports = { + meta: { hasSuggestions: true }, + create(context) { context.report({node, message, suggest: SUGGESTIONS}); } + }; + `, errors: [{ messageId: 'shouldBeSuggestable', type: 'ObjectExpression', line: 4, column: 17, endLine: 4, endColumn: 19 }], }, { @@ -195,8 +225,46 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: [{}]}); } }; `, + output: ` + module.exports = { + meta: { hasSuggestions: true }, + create(context) { context.report({node, message, suggest: [{}]}); } + }; + `, errors: [{ messageId: 'shouldBeSuggestable', type: 'Literal', line: 3, column: 35, endLine: 3, endColumn: 40 }], }, + { + // Reports suggestions, hasSuggestions property set to `null`, violation should be on `null` + code: ` + module.exports = { + meta: { hasSuggestions: null }, + create(context) { context.report({node, message, suggest: [{}]}); } + }; + `, + output: ` + module.exports = { + meta: { hasSuggestions: true }, + create(context) { context.report({node, message, suggest: [{}]}); } + }; + `, + errors: [{ messageId: 'shouldBeSuggestable', type: 'Literal', line: 3, column: 35, endLine: 3, endColumn: 39 }], + }, + { + // Reports suggestions, hasSuggestions property set to `undefined`, violation should be on `undefined` + code: ` + module.exports = { + meta: { hasSuggestions: undefined }, + create(context) { context.report({node, message, suggest: [{}]}); } + }; + `, + output: ` + module.exports = { + meta: { hasSuggestions: true }, + create(context) { context.report({node, message, suggest: [{}]}); } + }; + `, + errors: [{ messageId: 'shouldBeSuggestable', type: 'Identifier', line: 3, column: 35, endLine: 3, endColumn: 44 }], + }, { // Reports suggestions, hasSuggestions property set to false (as variable), violation should be on variable code: ` @@ -206,6 +274,7 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: [{}]}); } }; `, + output: null, errors: [{ messageId: 'shouldBeSuggestable', type: 'Identifier', line: 4, column: 19, endLine: 4, endColumn: 33 }], }, { @@ -216,6 +285,7 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message}); } }; `, + output: null, errors: [{ messageId: 'shouldNotBeSuggestable', type: 'Literal', line: 3, column: 35, endLine: 3, endColumn: 39 }], }, ],