Skip to content

Add new rule require-meta-docs-description #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,26 @@ Then configure the rules you want to use under the rules section.
<!-- __BEGIN AUTOGENERATED TABLE__ -->
Name | ✔️ | 🛠 | Description
----- | ----- | ----- | -----
[consistent-output](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/consistent-output.md) | | | Enforce consistent use of output assertions in rule tests
[fixer-return](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/fixer-return.md) | ✔️ | | Expected fixer function to always return a value.
[meta-property-ordering](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/meta-property-ordering.md) | | 🛠 | Enforces the order of meta properties
[no-deprecated-context-methods](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-deprecated-context-methods.md) | | 🛠 | Disallows usage of deprecated methods on rule context objects
[consistent-output](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/consistent-output.md) | | | enforce consistent use of output assertions in rule tests
[fixer-return](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/fixer-return.md) | ✔️ | | require fixer function to always return a value.
[meta-property-ordering](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/meta-property-ordering.md) | | 🛠 | enforce the order of meta properties
[no-deprecated-context-methods](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-deprecated-context-methods.md) | | 🛠 | disallow usage of deprecated methods on rule context objects
[no-deprecated-report-api](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-deprecated-report-api.md) | ✔️ | 🛠 | disallow use of the deprecated context.report() API
[no-identical-tests](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-identical-tests.md) | ✔️ | 🛠 | disallow identical tests
[no-missing-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-missing-placeholders.md) | ✔️ | | Disallow missing placeholders in rule report messages
[no-unused-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-unused-placeholders.md) | ✔️ | | Disallow unused placeholders in rule report messages
[no-useless-token-range](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-useless-token-range.md) | ✔️ | 🛠 | Disallow unnecessary calls to sourceCode.getFirstToken and sourceCode.getLastToken
[prefer-output-null](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-output-null.md) | | 🛠 | disallows invalid RuleTester test cases with the output the same as the code.
[no-missing-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-missing-placeholders.md) | ✔️ | | disallow missing placeholders in rule report messages
[no-unused-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-unused-placeholders.md) | ✔️ | | disallow unused placeholders in rule report messages
[no-useless-token-range](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-useless-token-range.md) | ✔️ | 🛠 | disallow unnecessary calls to sourceCode.getFirstToken and sourceCode.getLastToken
[prefer-output-null](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-output-null.md) | | 🛠 | disallow invalid RuleTester test cases with the output the same as the code.
[prefer-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-placeholders.md) | | | disallow template literals as report messages
[prefer-replace-text](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-replace-text.md) | | | prefer using replaceText instead of replaceTextRange.
[prefer-replace-text](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-replace-text.md) | | | require using replaceText instead of replaceTextRange.
[report-message-format](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/report-message-format.md) | | | enforce a consistent format for rule report messages
[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-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) | | 🛠 | Requires the properties of a test case to be placed in a consistent order
[test-case-shorthand-strings](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/test-case-shorthand-strings.md) | | 🛠 | Enforce consistent usage of shorthand strings for test cases with no options
[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
[test-case-shorthand-strings](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/test-case-shorthand-strings.md) | | 🛠 | enforce consistent usage of shorthand strings for test cases with no options
<!-- __END AUTOGENERATED TABLE__ -->

## Supported Presets
Expand Down
47 changes: 47 additions & 0 deletions docs/rules/require-meta-docs-description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# require rules to implement a meta.docs.description property (require-meta-docs-description)

Defining a clear and consistent description for each rule helps developers understand what they're used for.

In particular, each rule description should begin with an allowed prefix:
* `enforce`
* `require`
* `disallow`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one should probably be configurable with something like a regex (maybe with enforce/require/disallow as the default). As far as I know, enforce/require/disallow is a convention used by the ESLint team but it's not universally adopted.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll update shortly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an allowedPrefixes array option to the rule.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really rather this be a regex option -- choosing a set of specific prefixes feels like an oddly specific restriction to impose on users of the rule.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@not-an-aardvark sounds good, I switched to a pattern option.


## Rule Details

This rule requires ESLint rules to have a valid `meta.docs.description` property.

Examples of **incorrect** code for this rule:

```js
/* eslint eslint-plugin/require-meta-docs-description: error */
module.exports = {
meta: {},
create: function(context) { /* ... */}
};

module.exports = {
meta: { description: 'this rule does ...' }, // missing allowed prefix
create: function(context) { /* ... */}
};
```

Examples of **correct** code for this rule:

```js
/* eslint eslint-plugin/require-meta-docs-description: error */
module.exports = {
meta: { description: 'disallow unused variables' },
create: function(context) { /* ... */}
};
```

## Options

This rule takes an optional object containing:

- `String` — `pattern` — A regular expression that the description must match. Use `'.+'` to allow anything. Defaults to `^(enforce|require|disallow)`.

## Further Reading

* [working-with-rules#options-schemas](https://eslint.org/docs/developer-guide/working-with-rules#options-schemas)
2 changes: 1 addition & 1 deletion lib/rules/consistent-output.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'Enforce consistent use of output assertions in rule tests',
description: 'enforce consistent use of output assertions in rule tests',
category: 'Tests',
recommended: false,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/fixer-return.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'Expected fixer function to always return a value.',
description: 'require fixer function to always return a value.',
category: 'Possible Errors',
recommended: true,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/meta-property-ordering.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const { getKeyName, getRuleInfo } = require('../utils');
module.exports = {
meta: {
docs: {
description: 'Enforces the order of meta properties',
description: 'enforce the order of meta properties',
category: 'Rules',
recommended: false,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-deprecated-context-methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const DEPRECATED_PASSTHROUGHS = {
module.exports = {
meta: {
docs: {
description: 'Disallows usage of deprecated methods on rule context objects',
description: 'disallow usage of deprecated methods on rule context objects',
category: 'Rules',
recommended: false,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-missing-placeholders.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'Disallow missing placeholders in rule report messages',
description: 'disallow missing placeholders in rule report messages',
category: 'Rules',
recommended: true,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-unused-placeholders.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'Disallow unused placeholders in rule report messages',
description: 'disallow unused placeholders in rule report messages',
category: 'Rules',
recommended: true,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-useless-token-range.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'Disallow unnecessary calls to sourceCode.getFirstToken and sourceCode.getLastToken',
description: 'disallow unnecessary calls to sourceCode.getFirstToken and sourceCode.getLastToken',
category: 'Rules',
recommended: true,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/prefer-output-null.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'disallows invalid RuleTester test cases with the output the same as the code.',
description: 'disallow invalid RuleTester test cases with the output the same as the code.',
category: 'Tests',
recommended: false,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/prefer-replace-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'prefer using replaceText instead of replaceTextRange.',
description: 'require using replaceText instead of replaceTextRange.',
category: 'Rules',
recommended: false,
},
Expand Down
87 changes: 87 additions & 0 deletions lib/rules/require-meta-docs-description.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict';

const utils = require('../utils');

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

const DEFAULT_PATTERN = new RegExp('^(enforce|require|disallow)');

/**
* Whether or not the node is a string literal
*
* @param {object} node
* @returns {boolean} whether or not the node is a string literal
*/
function isStringLiteral (node) {
return node.type === 'Literal' && typeof node.value === 'string';
}

module.exports = {
meta: {
docs: {
description: 'require rules to implement a meta.docs.description property with the correct format',
category: 'Rules',
recommended: false, // TODO: enable it in a major release.
},
type: 'suggestion',
fixable: null,
schema: [
{
type: 'object',
properties: {
pattern: {
type: 'string',
},
},
additionalProperties: false,
},
],
messages: {
missing: '`meta.docs.description` is required.',
wrongType: '`meta.docs.description` must be a non-empty string.',
extraWhitespace: '`meta.docs.description` must not have leading nor trailing whitespace.',
},
},

create (context) {
const sourceCode = context.getSourceCode();
const info = utils.getRuleInfo(sourceCode.ast, sourceCode.scopeManager);

return {
Program () {
if (info === null || info.meta === null) {
return;
}

const pattern = context.options[0] && context.options[0].pattern ? new RegExp(context.options[0].pattern) : DEFAULT_PATTERN;

const metaNode = info.meta;
const docsNode =
metaNode &&
metaNode.properties &&
metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'docs');

const descriptionNode =
docsNode &&
docsNode.value.properties &&
docsNode.value.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'description');

if (!descriptionNode) {
context.report({ node: docsNode ? docsNode : metaNode, messageId: 'missing' });
} else if (!isStringLiteral(descriptionNode.value) || descriptionNode.value.value === '') {
context.report({ node: descriptionNode.value, messageId: 'wrongType' });
} else if (descriptionNode.value.value !== descriptionNode.value.value.trim()) {
context.report({ node: descriptionNode.value, messageId: 'extraWhitespace' });
} else if (!pattern.test(descriptionNode.value.value)) {
context.report({
node: descriptionNode.value,
message: '`meta.docs.description` must match the regexp {{pattern}}.',
data: { pattern },
});
}
},
};
},
};
2 changes: 1 addition & 1 deletion lib/rules/test-case-property-ordering.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'Requires the properties of a test case to be placed in a consistent order',
description: 'require the properties of a test case to be placed in a consistent order',
category: 'Tests',
recommended: false,
},
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/test-case-shorthand-strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const utils = require('../utils');
module.exports = {
meta: {
docs: {
description: 'Enforce consistent usage of shorthand strings for test cases with no options',
description: 'enforce consistent usage of shorthand strings for test cases with no options',
category: 'Tests',
recommended: false,
},
Expand Down
Loading