Skip to content

Commit 11bc096

Browse files
committed
feat: support auto-generated config lists
1 parent d27c509 commit 11bc096

File tree

7 files changed

+473
-12
lines changed

7 files changed

+473
-12
lines changed

README.md

+13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Automatic documentation generator for [ESLint](https://eslint.org/) plugins and
77
Generates the following documentation covering a [wide variety](#column-and-notice-types) of rule metadata:
88

99
- `README.md` rules table
10+
- `README.md` configs table
1011
- Rule doc titles and notices
1112

1213
Also performs [configurable](#configuration-options) section consistency checks on rule docs:
@@ -20,6 +21,7 @@ Also performs [configurable](#configuration-options) section consistency checks
2021
- [Usage](#usage)
2122
- [Examples](#examples)
2223
- [Rules list table](#rules-list-table)
24+
- [Configs list table](#configs-list-table)
2325
- [Rule doc notices](#rule-doc-notices)
2426
- [Users](#users)
2527
- [Configuration options](#configuration-options)
@@ -75,6 +77,13 @@ Delete any old rules list from your `README.md`. A new one will be automatically
7577
<!-- end auto-generated rules list -->
7678
```
7779

80+
Optionally, add these marker comments to your `README.md` where you would like the configs list to go:
81+
82+
```md
83+
<!-- begin auto-generated configs list -->
84+
<!-- end auto-generated configs list -->
85+
```
86+
7887
Delete any old recommended/fixable/etc. notices from your rule docs. A new title and notices will be automatically added to the top of each rule doc (along with a marker comment if it doesn't already exist).
7988

8089
```md
@@ -102,6 +111,10 @@ For examples, see our [users](#users) or the in-house examples below. Note that
102111

103112
See the generated rules table and legend in our example [`README.md`](./docs/examples/eslint-plugin-test/README.md#rules).
104113

114+
### Configs list table
115+
116+
See the generated configs table in our example [`README.md`](./docs/examples/eslint-plugin-test/README.md#configs).
117+
105118
### Rule doc notices
106119

107120
See the generated rule doc title and notices in our example rule docs [`no-foo.md`](./docs/examples/eslint-plugin-test/docs/rules/no-foo.md), [`prefer-bar.md`](./docs/examples/eslint-plugin-test/docs/rules/prefer-bar.md), [`require-baz.md`](./docs/examples/eslint-plugin-test/docs/rules/require-baz.md).

docs/examples/eslint-plugin-test/README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ This plugin is for x purpose.
44

55
## Configs
66

7-
Configs section would normally go here.
7+
<!-- begin auto-generated configs list -->
8+
9+
| | Name | Description |
10+
| :- | :------------ | :----------------------------------------------- |
11+
|| `recommended` | These rules are recommended for everyone. |
12+
| 🎨 | `stylistic` | These rules are more about code style than bugs. |
13+
| ⌨️ | `typescript` | These are good rules to use with TypeScript. |
14+
15+
<!-- end auto-generated configs list -->
816

917
## Rules
1018

lib/comment-markers.ts

+6
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ export const END_RULE_LIST_MARKER = '<!-- end auto-generated rules list -->';
55

66
// Marker so that rule doc header (title/notices) can be automatically updated.
77
export const END_RULE_HEADER_MARKER = '<!-- end auto-generated rule header -->';
8+
9+
// Markers so that the configs table list can be automatically updated.
10+
export const BEGIN_CONFIG_LIST_MARKER =
11+
'<!-- begin auto-generated configs list -->';
12+
export const END_CONFIG_LIST_MARKER =
13+
'<!-- end auto-generated configs list -->';

lib/config-list.ts

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {
2+
BEGIN_CONFIG_LIST_MARKER,
3+
END_CONFIG_LIST_MARKER,
4+
} from './comment-markers.js';
5+
import { markdownTable } from 'markdown-table';
6+
import type { ConfigsToRules, ConfigEmojis, Plugin } from './types.js';
7+
import { ConfigFormat, configNameToDisplay } from './config-format.js';
8+
9+
function generateConfigListMarkdown(
10+
plugin: Plugin,
11+
configsToRules: ConfigsToRules,
12+
pluginPrefix: string,
13+
configEmojis: ConfigEmojis,
14+
configFormat: ConfigFormat,
15+
ignoreConfig: readonly string[]
16+
): string {
17+
const hasDescription = Object.values(plugin.configs || {}).some(
18+
// @ts-expect-error -- description is not an official config property.
19+
(config) => config.description
20+
);
21+
const listHeaderRow = ['', 'Name'];
22+
if (hasDescription) {
23+
listHeaderRow.push('Description');
24+
}
25+
26+
return markdownTable(
27+
[
28+
listHeaderRow,
29+
...Object.keys(configsToRules)
30+
.filter((configName) => !ignoreConfig.includes(configName))
31+
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
32+
.map((configName) => {
33+
return [
34+
configEmojis.find((obj) => obj.config === configName)?.emoji || '',
35+
`\`${configNameToDisplay(
36+
configName,
37+
configFormat,
38+
pluginPrefix
39+
)}\``,
40+
hasDescription
41+
? // @ts-expect-error -- description is not an official config property.
42+
(plugin.configs?.[configName]?.description as
43+
| string
44+
| undefined) || ''
45+
: undefined,
46+
].filter((col) => col !== undefined);
47+
}),
48+
],
49+
{ align: 'l' } // Left-align headers.
50+
);
51+
}
52+
53+
export function updateConfigsList(
54+
markdown: string,
55+
plugin: Plugin,
56+
configsToRules: ConfigsToRules,
57+
pluginPrefix: string,
58+
configEmojis: ConfigEmojis,
59+
configFormat: ConfigFormat,
60+
ignoreConfig: readonly string[]
61+
): string {
62+
const listStartIndex = markdown.indexOf(BEGIN_CONFIG_LIST_MARKER);
63+
let listEndIndex = markdown.indexOf(END_CONFIG_LIST_MARKER);
64+
65+
if (listStartIndex === -1 || listEndIndex === -1) {
66+
// No config list found.
67+
return markdown;
68+
}
69+
70+
// Account for length of pre-existing marker.
71+
listEndIndex += END_CONFIG_LIST_MARKER.length;
72+
73+
const preList = markdown.slice(0, Math.max(0, listStartIndex));
74+
const postList = markdown.slice(Math.max(0, listEndIndex));
75+
76+
// New config list.
77+
const list = generateConfigListMarkdown(
78+
plugin,
79+
configsToRules,
80+
pluginPrefix,
81+
configEmojis,
82+
configFormat,
83+
ignoreConfig
84+
);
85+
86+
return `${preList}${BEGIN_CONFIG_LIST_MARKER}\n\n${list}\n\n${END_CONFIG_LIST_MARKER}${postList}`;
87+
}

lib/generator.ts

+20-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
getPathWithExactFileNameCasing,
99
} from './package-json.js';
1010
import { updateRulesList } from './rule-list.js';
11+
import { updateConfigsList } from './config-list.js';
1112
import { generateRuleHeaderLines } from './rule-doc-notices.js';
1213
import {
1314
parseRuleDocNoticesOption,
@@ -260,22 +261,30 @@ export async function generate(path: string, options?: GenerateOptions) {
260261
// Update the rules list in this file.
261262
const fileContents = readFileSync(pathToFile, 'utf8');
262263
const fileContentsNew = await postprocess(
263-
updateRulesList(
264-
ruleNamesAndRules,
265-
fileContents,
264+
updateConfigsList(
265+
updateRulesList(
266+
ruleNamesAndRules,
267+
fileContents,
268+
plugin,
269+
configsToRules,
270+
pluginPrefix,
271+
pathRuleDoc,
272+
pathToFile,
273+
path,
274+
configEmojis,
275+
configFormat,
276+
ignoreConfig,
277+
ruleListColumns,
278+
ruleListSplit,
279+
urlConfigs,
280+
urlRuleDoc
281+
),
266282
plugin,
267283
configsToRules,
268284
pluginPrefix,
269-
pathRuleDoc,
270-
pathToFile,
271-
path,
272285
configEmojis,
273286
configFormat,
274-
ignoreConfig,
275-
ruleListColumns,
276-
ruleListSplit,
277-
urlConfigs,
278-
urlRuleDoc
287+
ignoreConfig
279288
),
280289
resolve(pathToFile)
281290
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`generate (configs list) basic generates the documentation 1`] = `
4+
"## Rules
5+
<!-- begin auto-generated rules list -->
6+
7+
| Name | Description |
8+
| :----------------------------- | :--------------------- |
9+
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. |
10+
11+
<!-- end auto-generated rules list -->
12+
## Configs
13+
<!-- begin auto-generated configs list -->
14+
15+
| | Name |
16+
| :- | :------------ |
17+
| ✅ | \`recommended\` |
18+
19+
<!-- end auto-generated configs list -->"
20+
`;
21+
22+
exports[`generate (configs list) when a config exports a description generates the documentation 1`] = `
23+
"## Rules
24+
<!-- begin auto-generated rules list -->
25+
26+
| Name | Description |
27+
| :----------------------------- | :--------------------- |
28+
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. |
29+
30+
<!-- end auto-generated rules list -->
31+
## Configs
32+
<!-- begin auto-generated configs list -->
33+
34+
| | Name | Description |
35+
| :- | :------------ | :--------------------------------------- |
36+
| | \`foo\` | |
37+
| ✅ | \`recommended\` | This config has the recommended rules... |
38+
39+
<!-- end auto-generated configs list -->"
40+
`;
41+
42+
exports[`generate (configs list) with --config-format generates the documentation 1`] = `
43+
"## Rules
44+
<!-- begin auto-generated rules list -->
45+
46+
| Name | Description |
47+
| :----------------------------- | :--------------------- |
48+
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. |
49+
50+
<!-- end auto-generated rules list -->
51+
## Configs
52+
<!-- begin auto-generated configs list -->
53+
54+
| | Name |
55+
| :- | :----------------- |
56+
| ✅ | \`test/recommended\` |
57+
58+
<!-- end auto-generated configs list -->"
59+
`;
60+
61+
exports[`generate (configs list) with --ignore-config generates the documentation 1`] = `
62+
"## Rules
63+
<!-- begin auto-generated rules list -->
64+
65+
| Name | Description |
66+
| :----------------------------- | :--------------------- |
67+
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. |
68+
69+
<!-- end auto-generated rules list -->
70+
## Configs
71+
<!-- begin auto-generated configs list -->
72+
73+
| | Name |
74+
| :- | :------------ |
75+
| ✅ | \`recommended\` |
76+
77+
<!-- end auto-generated configs list -->"
78+
`;
79+
80+
exports[`generate (configs list) with configs not defined in alphabetical order generates the documentation 1`] = `
81+
"## Rules
82+
<!-- begin auto-generated rules list -->
83+
84+
| Name | Description |
85+
| :----------------------------- | :--------------------- |
86+
| [no-foo](docs/rules/no-foo.md) | Description of no-foo. |
87+
88+
<!-- end auto-generated rules list -->
89+
## Configs
90+
<!-- begin auto-generated configs list -->
91+
92+
| | Name |
93+
| :- | :------------ |
94+
| | \`foo\` |
95+
| ✅ | \`recommended\` |
96+
97+
<!-- end auto-generated configs list -->"
98+
`;

0 commit comments

Comments
 (0)