Skip to content
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

Add optional options column and notice to indicate whether a rule is configurable #232

Merged
merged 1 commit into from
Nov 17, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,12 @@ These can be provided as CLI options or as [config file](#configuration-file) op
| `--ignore-deprecated-rules` | Whether to ignore deprecated rules from being checked, displayed, or updated (default: `false`). |
| `--path-rule-doc` | Path to markdown file for each rule doc. Use `{name}` placeholder for the rule name (default: `docs/rules/{name}.md`). |
| `--path-rule-list` | Path to markdown file with a rules section where the rules table list should live (default: `README.md`). |
| `--rule-doc-notices` | Ordered, comma-separated list of notices to display in rule doc. Non-applicable notices will be hidden. Choices: `configs`, `deprecated`, `fixable` (off by default), `fixableAndHasSuggestions`, `hasSuggestions` (off by default), `requiresTypeChecking`, `type` (off by default). Default: `deprecated,configs,fixableAndHasSuggestions,requiresTypeChecking`. |
| `--rule-doc-notices` | Ordered, comma-separated list of notices to display in rule doc. Non-applicable notices will be hidden. Choices: `configs`, `deprecated`, `fixable` (off by default), `fixableAndHasSuggestions`, `hasSuggestions` (off by default), `options` (off by default), `requiresTypeChecking`, `type` (off by default). Default: `deprecated,configs,fixableAndHasSuggestions,requiresTypeChecking`. |
| `--rule-doc-section-exclude` | Disallowed section in each rule doc. Exit with failure if present. Option can be repeated. |
| `--rule-doc-section-include` | Required section in each rule doc. Exit with failure if missing. Option can be repeated. |
| `--rule-doc-section-options` | Whether to require an "Options" or "Config" rule doc section and mention of any named options for rules with options (default: `true`). |
| `--rule-doc-title-format` | The format to use for rule doc titles. Defaults to `desc-parens-prefix-name`. See choices in below [table](#--rule-doc-title-format). |
| `--rule-list-columns` | Ordered, comma-separated list of columns to display in rule list. Empty columns will be hidden. Choices: `configsError`, `configsOff`, `configsWarn`, `deprecated`, `description`, `fixable`, `fixableAndHasSuggestions` (off by default), `hasSuggestions`, `name`, `requiresTypeChecking`, `type` (off by default). Default: `name,description,configsError,configsWarn,configsOff,fixable,hasSuggestions,requiresTypeChecking,deprecated`. |
| `--rule-list-columns` | Ordered, comma-separated list of columns to display in rule list. Empty columns will be hidden. Choices: `configsError`, `configsOff`, `configsWarn`, `deprecated`, `description`, `fixable`, `fixableAndHasSuggestions` (off by default), `hasSuggestions`, `name`, `options` (off by default), `requiresTypeChecking`, `type` (off by default). Default: `name,description,configsError,configsWarn,configsOff,fixable,hasSuggestions,requiresTypeChecking,deprecated`. |
| `--split-by` | Rule property to split the rules list by. A separate list and header will be created for each value. Example: `meta.type`. |
| `--url-configs` | Link to documentation about the ESLint configurations exported by the plugin. |

Expand Down
11 changes: 6 additions & 5 deletions docs/examples/eslint-plugin-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ Configs section would normally go here.
⌨️ Set in the `typescript` configuration.\
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
💡 Manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).\
⚙️ Has configuration options.\
💭 Requires type information.\
🗂️ The type of rule.\
❗ Identifies problems that could cause errors or unexpected behavior.\
📖 Identifies potential improvements.\
📏 Focuses on code formatting.\
❌ Deprecated.

| Name | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | 💭 | 🗂️ ||
| :--------------------------------------- | :----------------- | :- | :- | :- | :- | :- | :- | :-- | :- |
| [no-foo](docs/rules/no-foo.md) | disallow using foo || | | 🔧 | | 💭 || |
| [prefer-bar](docs/rules/prefer-bar.md) | enforce using bar || 🎨 | | | 💡 | 💭 | 📖 | |
| [require-baz](docs/rules/require-baz.md) | require using baz | | | ⌨️ | 🔧 | | | 📏 ||
| Name | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ⚙️ | 💭 | 🗂️ ||
| :--------------------------------------- | :----------------- | :- | :- | :- | :- | :- | :- | :- | :-- | :- |
| [no-foo](docs/rules/no-foo.md) | disallow using foo || | | 🔧 | | ⚙️ | 💭 || |
| [prefer-bar](docs/rules/prefer-bar.md) | enforce using bar || 🎨 | | | 💡 | | 💭 | 📖 | |
| [require-baz](docs/rules/require-baz.md) | require using baz | | | ⌨️ | 🔧 | | | | 📏 ||

<!-- end auto-generated rules list -->
4 changes: 4 additions & 0 deletions docs/examples/eslint-plugin-test/docs/rules/no-foo.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

⚙️ This rule is configurable.

💭 This rule requires type information.

❗ This rule identifies problems that could cause errors or unexpected behavior.

<!-- end auto-generated rule header -->

## Rule details
Expand Down
2 changes: 2 additions & 0 deletions docs/examples/eslint-plugin-test/docs/rules/prefer-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

💭 This rule requires type information.

📖 This rule identifies potential improvements.

<!-- end auto-generated rule header -->

## Rule details
Expand Down
2 changes: 2 additions & 0 deletions docs/examples/eslint-plugin-test/docs/rules/require-baz.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

📏 This rule focuses on code formatting.

<!-- end auto-generated rule header -->

## Rule details
Expand Down
4 changes: 4 additions & 0 deletions lib/emojis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export const EMOJI_CONFIG_FROM_SEVERITY: {
export const EMOJI_FIXABLE = '🔧';
export const EMOJI_HAS_SUGGESTIONS = '💡';

// Options.
export const EMOJI_OPTIONS = '⚙️';

// TypeScript.
export const EMOJI_REQUIRES_TYPE_CHECKING = '💭';

Expand All @@ -58,6 +61,7 @@ export const RESERVED_EMOJIS = [

EMOJI_FIXABLE,
EMOJI_HAS_SUGGESTIONS,
EMOJI_OPTIONS,
EMOJI_REQUIRES_TYPE_CHECKING,
EMOJI_TYPE,
EMOJI_DEPRECATED,
Expand Down
2 changes: 2 additions & 0 deletions lib/legend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
EMOJI_DEPRECATED,
EMOJI_FIXABLE,
EMOJI_HAS_SUGGESTIONS,
EMOJI_OPTIONS,
EMOJI_REQUIRES_TYPE_CHECKING,
EMOJI_TYPE,
EMOJI_CONFIG_FROM_SEVERITY,
Expand Down Expand Up @@ -139,6 +140,7 @@ const LEGENDS: {
],
[COLUMN_TYPE.HAS_SUGGESTIONS]: [LEGEND_HAS_SUGGESTIONS],
[COLUMN_TYPE.NAME]: undefined,
[COLUMN_TYPE.OPTIONS]: [`${EMOJI_OPTIONS} Has configuration options.`],
[COLUMN_TYPE.REQUIRES_TYPE_CHECKING]: [
`${EMOJI_REQUIRES_TYPE_CHECKING} Requires type information.`,
],
Expand Down
2 changes: 2 additions & 0 deletions lib/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const COLUMN_TYPE_DEFAULT_PRESENCE_AND_ORDERING: {
[COLUMN_TYPE.FIXABLE]: true,
[COLUMN_TYPE.FIXABLE_AND_HAS_SUGGESTIONS]: false, // Optional, consolidated column.
[COLUMN_TYPE.HAS_SUGGESTIONS]: true,
[COLUMN_TYPE.OPTIONS]: false,
[COLUMN_TYPE.REQUIRES_TYPE_CHECKING]: true,
[COLUMN_TYPE.TYPE]: false,
[COLUMN_TYPE.DEPRECATED]: true,
Expand All @@ -29,6 +30,7 @@ export const NOTICE_TYPE_DEFAULT_PRESENCE_AND_ORDERING: {
[NOTICE_TYPE.FIXABLE]: false,
[NOTICE_TYPE.FIXABLE_AND_HAS_SUGGESTIONS]: true, // Default, consolidated notice.
[NOTICE_TYPE.HAS_SUGGESTIONS]: false,
[NOTICE_TYPE.OPTIONS]: false,
[NOTICE_TYPE.REQUIRES_TYPE_CHECKING]: true,
[NOTICE_TYPE.TYPE]: false,
};
Expand Down
4 changes: 4 additions & 0 deletions lib/rule-list-columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import {
EMOJI_REQUIRES_TYPE_CHECKING,
EMOJI_TYPE,
EMOJI_CONFIG_FROM_SEVERITY,
EMOJI_OPTIONS,
} from './emojis.js';
import { RULE_TYPES } from './rule-type.js';
import { COLUMN_TYPE, SEVERITY_TYPE } from './types.js';
import { getConfigsThatSetARule } from './configs.js';
import { hasOptions } from './rule-options.js';
import type { RuleDetails, ConfigsToRules, Plugin } from './types.js';

/**
Expand Down Expand Up @@ -52,6 +54,7 @@ export const COLUMN_HEADER: {
[COLUMN_TYPE.FIXABLE]: EMOJI_FIXABLE,
[COLUMN_TYPE.FIXABLE_AND_HAS_SUGGESTIONS]: `${EMOJI_FIXABLE}${EMOJI_HAS_SUGGESTIONS}`,
[COLUMN_TYPE.HAS_SUGGESTIONS]: EMOJI_HAS_SUGGESTIONS,
[COLUMN_TYPE.OPTIONS]: EMOJI_OPTIONS,
[COLUMN_TYPE.REQUIRES_TYPE_CHECKING]: EMOJI_REQUIRES_TYPE_CHECKING,
[COLUMN_TYPE.TYPE]: EMOJI_TYPE,
};
Expand Down Expand Up @@ -106,6 +109,7 @@ export function getColumns(
(detail) => detail.hasSuggestions
),
[COLUMN_TYPE.NAME]: true,
[COLUMN_TYPE.OPTIONS]: details.some((detail) => hasOptions(detail.schema)),
[COLUMN_TYPE.REQUIRES_TYPE_CHECKING]: details.some(
(detail) => detail.requiresTypeChecking
),
Expand Down
3 changes: 3 additions & 0 deletions lib/rule-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
EMOJI_DEPRECATED,
EMOJI_FIXABLE,
EMOJI_HAS_SUGGESTIONS,
EMOJI_OPTIONS,
EMOJI_REQUIRES_TYPE_CHECKING,
} from './emojis.js';
import { getConfigsForRule, findConfigEmoji } from './configs.js';
Expand All @@ -21,6 +22,7 @@ import type {
ConfigEmojis,
} from './types.js';
import { EMOJIS_TYPE, RULE_TYPE } from './rule-type.js';
import { hasOptions } from './rule-options.js';

// Example: theWeatherIsNice => The Weather Is Nice
function camelCaseStringToTitle(str: string) {
Expand Down Expand Up @@ -166,6 +168,7 @@ function buildRuleRow(
/{name}/g,
rule.name
)})`,
[COLUMN_TYPE.OPTIONS]: hasOptions(rule.schema) ? EMOJI_OPTIONS : '',
[COLUMN_TYPE.REQUIRES_TYPE_CHECKING]: rule.requiresTypeChecking
? EMOJI_REQUIRES_TYPE_CHECKING
: '',
Expand Down
5 changes: 5 additions & 0 deletions lib/rule-notices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
EMOJI_HAS_SUGGESTIONS,
EMOJI_REQUIRES_TYPE_CHECKING,
EMOJI_CONFIG_FROM_SEVERITY,
EMOJI_OPTIONS,
} from './emojis.js';
import { findConfigEmoji, getConfigsForRule } from './configs.js';
import {
Expand All @@ -17,6 +18,7 @@ import {
} from './types.js';
import { RULE_TYPE, RULE_TYPE_MESSAGES_NOTICES } from './rule-type.js';
import { RuleDocTitleFormat } from './rule-doc-title-format.js';
import { hasOptions } from './rule-options.js';

function severityToTerminology(severity: SEVERITY_TYPE) {
switch (severity) {
Expand Down Expand Up @@ -194,6 +196,7 @@ const RULE_NOTICES: {
},
[NOTICE_TYPE.HAS_SUGGESTIONS]: NOTICE_HAS_SUGGESTIONS,

[NOTICE_TYPE.OPTIONS]: `${EMOJI_OPTIONS} This rule is configurable.`,
[NOTICE_TYPE.REQUIRES_TYPE_CHECKING]: `${EMOJI_REQUIRES_TYPE_CHECKING} This rule requires type information.`,
};

Expand Down Expand Up @@ -241,11 +244,13 @@ function getNoticesForRule(
configsOff.length > 0,
[NOTICE_TYPE.DEPRECATED]: rule.meta?.deprecated || false,

// Fixable/suggestions.
[NOTICE_TYPE.FIXABLE]: Boolean(rule.meta?.fixable),
[NOTICE_TYPE.FIXABLE_AND_HAS_SUGGESTIONS]:
Boolean(rule.meta?.fixable) || Boolean(rule.meta?.hasSuggestions),
[NOTICE_TYPE.HAS_SUGGESTIONS]: Boolean(rule.meta?.hasSuggestions),

[NOTICE_TYPE.OPTIONS]: hasOptions(rule.meta?.schema),
[NOTICE_TYPE.REQUIRES_TYPE_CHECKING]:
rule.meta?.docs?.requiresTypeChecking || false,
[NOTICE_TYPE.TYPE]: Boolean(rule.meta?.type),
Expand Down
3 changes: 2 additions & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ export type ConfigEmojis = { config: string; emoji: string }[];

/**
* Rule doc notices.
* Same as COLUMN_TYPE but without NAME, DESCRIPTION (which are in the rule doc title).
*/
export enum NOTICE_TYPE {
CONFIGS = 'configs',
DEPRECATED = 'deprecated',
FIXABLE = 'fixable',
FIXABLE_AND_HAS_SUGGESTIONS = 'fixableAndHasSuggestions', // Consolidated notice for space-saving.
HAS_SUGGESTIONS = 'hasSuggestions',
OPTIONS = 'options',
REQUIRES_TYPE_CHECKING = 'requiresTypeChecking',
TYPE = 'type',
}
Expand All @@ -77,6 +77,7 @@ export enum COLUMN_TYPE {
FIXABLE_AND_HAS_SUGGESTIONS = 'fixableAndHasSuggestions', // Consolidated column for space-saving.
HAS_SUGGESTIONS = 'hasSuggestions',
NAME = 'name',
OPTIONS = 'options',
REQUIRES_TYPE_CHECKING = 'requiresTypeChecking',
TYPE = 'type',
}
51 changes: 51 additions & 0 deletions test/lib/__snapshots__/generator-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,57 @@ exports[`generator #generate rule with no meta object generates the documentatio
"
`;

exports[`generator #generate rule with options, options column/notice enabled displays the column and notice 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
⚙️ Has configuration options.
| Name | ⚙️ |
| :----------------------------- | :- |
| [no-bar](docs/rules/no-bar.md) | ⚙️ |
| [no-baz](docs/rules/no-baz.md) | |
| [no-biz](docs/rules/no-biz.md) | |
| [no-foo](docs/rules/no-foo.md) | ⚙️ |
<!-- end auto-generated rules list -->
"
`;

exports[`generator #generate rule with options, options column/notice enabled displays the column and notice 2`] = `
"# test/no-foo
⚙️ This rule is configurable.
<!-- end auto-generated rule header -->
## Options
"
`;

exports[`generator #generate rule with options, options column/notice enabled displays the column and notice 3`] = `
"# test/no-bar
⚙️ This rule is configurable.
<!-- end auto-generated rule header -->
## Options
"
`;

exports[`generator #generate rule with options, options column/notice enabled displays the column and notice 4`] = `
"# test/no-biz
<!-- end auto-generated rule header -->
"
`;

exports[`generator #generate rule with options, options column/notice enabled displays the column and notice 5`] = `
"# test/no-baz
<!-- end auto-generated rule header -->
"
`;

exports[`generator #generate rule with type, type column enabled displays the type 1`] = `
"## Rules
<!-- begin auto-generated rules list -->
Expand Down
51 changes: 51 additions & 0 deletions test/lib/generator-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4077,6 +4077,57 @@ describe('generator', function () {
});
});

describe('rule with options, options column/notice enabled', function () {
beforeEach(function () {
mockFs({
'package.json': JSON.stringify({
name: 'eslint-plugin-test',
main: 'index.js',
type: 'module',
}),

'index.js': `
export default {
rules: {
'no-foo': { meta: { schema: [{foo:true}] }, create(context) {} },
'no-bar': { meta: { schema: {foo:true} }, create(context) {} },
'no-biz': { meta: { schema: [] }, create(context) {} },
'no-baz': { meta: { }, create(context) {} },
},
};`,

'README.md': '## Rules\n',

'docs/rules/no-foo.md': '## Options\n',
'docs/rules/no-bar.md': '## Options\n',
'docs/rules/no-biz.md': '',
'docs/rules/no-baz.md': '',

// Needed for some of the test infrastructure to work.
node_modules: mockFs.load(
resolve(__dirname, '..', '..', 'node_modules')
),
});
});

afterEach(function () {
mockFs.restore();
jest.resetModules();
});

it('displays the column and notice', async function () {
await generate('.', {
ruleListColumns: 'name,options',
ruleDocNotices: 'options',
});
expect(readFileSync('README.md', 'utf8')).toMatchSnapshot();
expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot();
expect(readFileSync('docs/rules/no-bar.md', 'utf8')).toMatchSnapshot();
expect(readFileSync('docs/rules/no-biz.md', 'utf8')).toMatchSnapshot();
expect(readFileSync('docs/rules/no-baz.md', 'utf8')).toMatchSnapshot();
});
});

describe('rule with long-enough description to require name column wrapping avoidance', function () {
beforeEach(function () {
mockFs({
Expand Down