Skip to content

Commit 0bf74fc

Browse files
committed
Generate peer dependencies docs
1 parent 83e2045 commit 0bf74fc

File tree

9 files changed

+201
-52
lines changed

9 files changed

+201
-52
lines changed

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,33 @@ Configurations are available for different tech stacks.
2424
| ![cypress](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/cypress.png) | [@code-pushup/eslint-config/cypress](https://github.com/code-pushup/eslint-config/blob/main/docs/cypress.md) | Config for projects using **Cypress** for testing. |
2525
| ![storybook](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/storybook.png) | [@code-pushup/eslint-config/storybook](https://github.com/code-pushup/eslint-config/blob/main/docs/storybook.md) | Config for projects using **Storybook** for UI components. |
2626

27+
### 📦 Peer dependencies
28+
29+
| | NPM package | Version | Required |
30+
| :-: | :-- | :-: | :-: |
31+
| ![eslint](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/eslint.png) | [eslint](https://www.npmjs.com/package/eslint) | ```^8.0.0``` ||
32+
| ![typescript](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/typescript.png) | [@typescript-eslint/eslint-plugin](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) | ```^6.0.0``` ||
33+
| ![typescript](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/typescript.png) | [@typescript-eslint/parser](https://www.npmjs.com/package/@typescript-eslint/parser) | ```^6.0.0``` ||
34+
| ![lambda](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/icons8/lambda.png) | [eslint-plugin-functional](https://www.npmjs.com/package/eslint-plugin-functional) | ```^6.0.0``` ||
35+
| ![import](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/icons8/import.png) | [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import) | ```^2.25.0``` ||
36+
| ![import](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/icons8/import.png) | [eslint-import-resolver-typescript](https://www.npmjs.com/package/eslint-import-resolver-typescript) | ```^3.0.0``` | |
37+
| ![secure](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/icons8/secure.png) | [eslint-plugin-no-secrets](https://www.npmjs.com/package/eslint-plugin-no-secrets) | ```^0.8.0``` ||
38+
| ![promise](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/icons8/promise.png) | [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise) | ```^6.0.0``` ||
39+
| ![sonar](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/other/sonar.png) | [eslint-plugin-sonarjs](https://www.npmjs.com/package/eslint-plugin-sonarjs) | ```>=0.22.0``` ||
40+
| ![unicorn](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/icons8/unicorn.png) | [eslint-plugin-unicorn](https://www.npmjs.com/package/eslint-plugin-unicorn) | ```>=48.0.0``` ||
41+
| ![angular](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/angular.png) | [@angular-eslint/eslint-plugin](https://www.npmjs.com/package/@angular-eslint/eslint-plugin) | ```^17.0.0``` | |
42+
| ![angular_component](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/angular_component.png) | [@angular-eslint/eslint-plugin-template](https://www.npmjs.com/package/@angular-eslint/eslint-plugin-template) | ```^17.0.0``` | |
43+
| ![angular_component](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/angular_component.png) | [@angular-eslint/template-parser](https://www.npmjs.com/package/@angular-eslint/template-parser) | ```^17.0.0``` | |
44+
| ![graphql](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/graphql.png) | [@graphql-eslint/eslint-plugin](https://www.npmjs.com/package/@graphql-eslint/eslint-plugin) | ```^3.0.0``` | |
45+
| ![ngrx](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/other/ngrx.png) | [@ngrx/eslint-plugin](https://www.npmjs.com/package/@ngrx/eslint-plugin) | ```^17.0.0``` | |
46+
| ![cypress](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/cypress.png) | [eslint-plugin-cypress](https://www.npmjs.com/package/eslint-plugin-cypress) | ```^2.0.0``` | |
47+
| ![expired](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/icons8/expired.png) | [eslint-plugin-deprecation](https://www.npmjs.com/package/eslint-plugin-deprecation) | ```^2.0.0``` | |
48+
| ![jest](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/jest.png) | [eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) | ```^27.0.0``` | |
49+
| ![nodejs](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/nodejs.png) | [eslint-plugin-n](https://www.npmjs.com/package/eslint-plugin-n) | ```^16.0.0``` | |
50+
| ![rxjs](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/other/rxjs.png) | [eslint-plugin-rxjs](https://www.npmjs.com/package/eslint-plugin-rxjs) | ```^5.0.0``` | |
51+
| ![storybook](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/storybook.png) | [eslint-plugin-storybook](https://www.npmjs.com/package/eslint-plugin-storybook) | ```^0.6.0``` | |
52+
| ![vitest](https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/material/vitest.png) | [eslint-plugin-vitest](https://www.npmjs.com/package/eslint-plugin-vitest) | ```^0.3.0``` | |
53+
2754
### 🧪 Test overrides
2855

2956
For non-production code, some rules are disabled (or downgraded from errors to warnings).

docs/icons/material/eslint.png

880 Bytes
Loading

docs/storybook.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Add to `extends` in your .eslintrc file:
1212
}
1313
```
1414

15-
## 📏 Rules (11)
15+
## 📏 Rules (12)
1616

1717
> 🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).<br>💡 Manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).<br>🧪🚫 Disabled for [test files](../README.md#🧪-test-overrides).<br>🧪⚠️ Severity lessened to warning for [test files](../README.md#🧪-test-overrides).
1818
@@ -28,10 +28,11 @@ Add to `extends` in your .eslintrc file:
2828
| [![storybook](./icons/material/storybook.png)](https://github.com/storybookjs/eslint-plugin-storybook#readme) | [use-storybook-expect](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/docs/rules/use-storybook-expect.md)<br>Use expect from `@storybook/jest` | | 🔧, 💡 | |
2929
| [![storybook](./icons/material/storybook.png)](https://github.com/storybookjs/eslint-plugin-storybook#readme) | [use-storybook-testing-library](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/docs/rules/use-storybook-testing-library.md)<br>Do not use testing-library directly on stories | | 🔧, 💡 | |
3030

31-
### ⚠️ Warnings (4)
31+
### ⚠️ Warnings (5)
3232

3333
| Plugin | Rule | Options | Autofix | Overrides |
3434
| :-: | :-- | :-- | :-: | :-: |
35+
| [![@typescript-eslint](./icons/material/typescript.png)](https://typescript-eslint.io/) | naming-convention<br> | <details><summary>selector: variableLike, met...</summary><pre>[<br> {<br> "selector": [<br> "variableLike",<br> "method",<br> "typeProperty",<br> "parameterProperty",<br> "classProperty"<br> ],<br> "format": [<br> "camelCase"<br> ]<br> },<br> {<br> "selector": "variable",<br> "format": [<br> "camelCase",<br> "UPPER_CASE",<br> "PascalCase"<br> ]<br> },<br> {<br> "selector": "typeLike",<br> "format": [<br> "PascalCase"<br> ]<br> },<br> {<br> "selector": "enumMember",<br> "format": [<br> "PascalCase"<br> ]<br> },<br> {<br> "selector": "parameter",<br> "modifiers": [<br> "unused"<br> ],<br> "format": null,<br> "custom": {<br> "regex": "^(_+\|[a-z][a-zA-Z0-9]\*)$",<br> "match": true<br> }<br> },<br> {<br> "selector": "objectLiteralProperty",<br> "modifiers": [<br> "requiresQuotes"<br> ],<br> "format": null<br> },<br> {<br> "selector": [<br> "variable",<br> "parameter"<br> ],<br> "modifiers": [<br> "destructured"<br> ],<br> "format": null<br> }<br>]</pre></details> | | |
3536
| [![storybook](./icons/material/storybook.png)](https://github.com/storybookjs/eslint-plugin-storybook#readme) | [csf-component](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/docs/rules/csf-component.md)<br>The component property should be set | | | |
3637
| [![storybook](./icons/material/storybook.png)](https://github.com/storybookjs/eslint-plugin-storybook#readme) | [hierarchy-separator](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/docs/rules/hierarchy-separator.md)<br>Deprecated hierarchy separator in title property | | 🔧, 💡 | |
3738
| [![storybook](./icons/material/storybook.png)](https://github.com/storybookjs/eslint-plugin-storybook#readme) | [no-redundant-story-name](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/docs/rules/no-redundant-story-name.md)<br>A story should not have a redundant name property | | 🔧, 💡 | |

scripts/docs.js

+23-9
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ const { ESLint } = require('eslint');
22
const fs = require('node:fs/promises');
33
const path = require('node:path');
44
const { execSync } = require('node:child_process');
5-
const {
6-
configRulesToMarkdown,
7-
configsToMarkdown,
8-
} = require('./helpers/format');
5+
const { configRulesToMarkdown } = require('./helpers/format-config');
6+
const { configsToMarkdown } = require('./helpers/format-readme');
97
const {
108
configs,
119
configPattern,
@@ -23,19 +21,34 @@ async function generateDocs() {
2321
execSync('npm link @code-pushup/eslint-config');
2422

2523
try {
24+
const peerDeps = findPeerDependencies();
25+
2626
await fs.mkdir(docsDir, { recursive: true });
2727

2828
for (const config of configs) {
29-
await generateConfigDocs(config);
29+
await generateConfigDocs(config, peerDeps);
3030
}
3131

32-
await generateReadmeDocs();
32+
await generateReadmeDocs(peerDeps);
3333
} finally {
3434
execSync('npm unlink @code-pushup/eslint-config');
3535
}
3636
}
3737

38-
async function generateReadmeDocs() {
38+
function findPeerDependencies() {
39+
const packageJson = require('../package.json');
40+
return Object.entries(packageJson.peerDependencies).map(([pkg, version]) => ({
41+
pkg,
42+
version,
43+
optional: packageJson.peerDependenciesMeta[pkg]?.optional ?? false,
44+
}));
45+
}
46+
47+
/**
48+
* Update auto-generated part of README.md
49+
* @param {import('./helpers/types').PeerDep[]} peerDeps Peer depdendencies
50+
*/
51+
async function generateReadmeDocs(peerDeps) {
3952
const buffer = await fs.readFile(readmePath);
4053
const mdPrevious = buffer.toString('utf8');
4154

@@ -48,7 +61,7 @@ async function generateReadmeDocs() {
4861
startIndex + startComment.length,
4962
);
5063

51-
const mdGenerated = configsToMarkdown(configs);
64+
const mdGenerated = configsToMarkdown(configs, peerDeps);
5265
const mdGeneratedBlock = [
5366
startComment,
5467
mdGenerated.replace(/\n$/, ''),
@@ -72,8 +85,9 @@ async function generateReadmeDocs() {
7285
/**
7386
* Generate Markdown file for specified ESLint config.
7487
* @param {string} name Config file name without extension
88+
* @param {import('./helpers/types').PeerDep[]} peerDeps Peer depdendencies
7589
*/
76-
async function generateConfigDocs(name) {
90+
async function generateConfigDocs(name, peerDeps) {
7791
const eslint = new ESLint({
7892
baseConfig: { extends: `./${name}.js` },
7993
useEslintrc: false,

scripts/helpers/format.js scripts/helpers/format-config.js

+6-41
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const {
22
configDescription,
33
configAlias,
4-
configIcon,
54
configFromAlias,
65
} = require('./configs');
76
const { pluginIcon, pluginDocs } = require('./plugins');
@@ -16,45 +15,9 @@ const {
1615
mdQuote,
1716
mdTableCellSanitize,
1817
} = require('./markdown');
19-
const { TEST_FILE_PATTERNS } = require('../../lib/patterns');
2018

2119
const testGlobsLink = '../README.md#🧪-test-overrides';
2220

23-
/**
24-
* Format Markdown documentation for README
25-
* @param {string[]} configs Config names
26-
*/
27-
function configsToMarkdown(configs) {
28-
const blocks = [
29-
'## ⚙️ Configs',
30-
'Configurations are available for different tech stacks.',
31-
mdTable(
32-
['Stack', 'Config', 'Description'],
33-
configs.map(config => {
34-
const icon = configIcon(config);
35-
return [
36-
mdImage(
37-
`https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/${icon}.png`,
38-
icon.replace(/^\w+\//, ''),
39-
),
40-
mdLink(
41-
`https://github.com/code-pushup/eslint-config/blob/main/docs/${config}.md`,
42-
configAlias(config),
43-
),
44-
configDescription(config),
45-
];
46-
}),
47-
['c', 'l', 'l'],
48-
),
49-
'### 🧪 Test overrides',
50-
'For non-production code, some rules are disabled (or downgraded from errors to warnings).',
51-
'This applies to file paths matching any of the following globs:',
52-
mdList(TEST_FILE_PATTERNS.map(pattern => '`' + pattern + '`')),
53-
];
54-
55-
return blocks.join('\n\n');
56-
}
57-
5821
/**
5922
* Format Markdown documentation for given config.
6023
* @param {string} config Config name
@@ -172,9 +135,9 @@ function rulesTable(rules, hideOverrides) {
172135
)
173136
: '',
174137

175-
mdLink(rule.meta.docs?.url, name) +
138+
mdLink(rule.meta?.docs?.url, name) +
176139
'<br>' +
177-
(rule.meta.docs.description ?? '').replace(/\n/g, ''),
140+
(rule.meta?.docs.description ?? '').replace(/\n/g, ''),
178141

179142
options
180143
? htmlDetails(
@@ -188,7 +151,10 @@ function rulesTable(rules, hideOverrides) {
188151
)
189152
: '',
190153

191-
[rule.meta.fixable ? '🔧' : '', rule.meta.hasSuggestions ? '💡' : '']
154+
[
155+
rule.meta?.fixable ? '🔧' : '',
156+
rule.meta?.hasSuggestions ? '💡' : '',
157+
]
192158
.filter(Boolean)
193159
.join(', ') || '',
194160

@@ -253,6 +219,5 @@ function truncate(text, max) {
253219
}
254220

255221
module.exports = {
256-
configsToMarkdown,
257222
configRulesToMarkdown,
258223
};

scripts/helpers/format-readme.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const { configDescription, configAlias, configIcon } = require('./configs');
2+
const {
3+
mdLink,
4+
mdImage,
5+
mdTable,
6+
mdList,
7+
mdCodeInline,
8+
} = require('./markdown');
9+
const { TEST_FILE_PATTERNS } = require('../../lib/patterns');
10+
const { packageDocs, packageIcon, sortPeerDeps } = require('./packages');
11+
12+
/**
13+
* Format Markdown documentation for README
14+
* @param {string[]} configs Config names
15+
* @param {import('./types').PeerDep[]} peerDeps Peer depdendencies
16+
*/
17+
function configsToMarkdown(configs, peerDeps) {
18+
const blocks = [
19+
'## ⚙️ Configs',
20+
'Configurations are available for different tech stacks.',
21+
mdTable(
22+
['Stack', 'Config', 'Description'],
23+
configs.map(config => [
24+
iconToImage(configIcon(config)),
25+
mdLink(
26+
`https://github.com/code-pushup/eslint-config/blob/main/docs/${config}.md`,
27+
configAlias(config),
28+
),
29+
configDescription(config),
30+
]),
31+
['c', 'l', 'l'],
32+
),
33+
'### 📦 Peer dependencies',
34+
mdTable(
35+
['', 'NPM package', 'Version', 'Required'],
36+
sortPeerDeps(peerDeps).map(({ pkg, version, optional }) => [
37+
iconToImage(packageIcon(pkg)),
38+
mdLink(packageDocs(pkg), pkg),
39+
mdCodeInline(version),
40+
optional ? '' : '✅',
41+
]),
42+
['c', 'l', 'c', 'c'],
43+
),
44+
'### 🧪 Test overrides',
45+
'For non-production code, some rules are disabled (or downgraded from errors to warnings).',
46+
'This applies to file paths matching any of the following globs:',
47+
mdList(TEST_FILE_PATTERNS.map(pattern => '`' + pattern + '`')),
48+
];
49+
50+
return blocks.join('\n\n');
51+
}
52+
53+
/** @param {import('./types').Icon} icon */
54+
function iconToImage(icon) {
55+
return mdImage(
56+
`https://raw.githubusercontent.com/code-pushup/eslint-config/main/docs/icons/${icon}.png`,
57+
icon.replace(/^\w+\//, ''),
58+
);
59+
}
60+
61+
module.exports = {
62+
configsToMarkdown,
63+
};

scripts/helpers/markdown.js

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ function mdList(items) {
2424
return items.map(item => `- ${item}\n`).join('');
2525
}
2626

27+
/**
28+
* @param {string} content
29+
*/
30+
function mdCodeInline(content) {
31+
return '```' + content + '```';
32+
}
33+
2734
/**
2835
* @param {string} content
2936
* @param {'ts' | 'js' | 'json'} [lang='ts']
@@ -97,6 +104,7 @@ module.exports = {
97104
mdImage,
98105
mdList,
99106
mdTable,
107+
mdCodeInline,
100108
mdCodeBlock,
101109
mdQuote,
102110
htmlDetails,

scripts/helpers/packages.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const { pluginIcon } = require('./plugins');
2+
3+
/**
4+
* @param {string} pkg
5+
* @returns {import('./types').Icon}
6+
*/
7+
function packageIcon(pkg) {
8+
if (pkg === 'eslint') {
9+
return 'material/eslint';
10+
}
11+
if (pkg.includes('eslint-plugin')) {
12+
const alias = pkg.replace(/eslint-plugin-?/, '').replace(/\/$/, '');
13+
return pluginIcon(alias);
14+
}
15+
if (pkg.includes('parser')) {
16+
const pluginAlias = pkg.replace(/-?parser/, '').replace(/\/$/, '');
17+
return pluginIcon(pluginAlias);
18+
}
19+
if (pkg.startsWith('eslint-import-resolver')) {
20+
return pluginIcon('import');
21+
}
22+
throw new Error(`No icon found for package ${pkg}`);
23+
}
24+
25+
/**
26+
* @param {string} pkg
27+
*/
28+
function packageDocs(pkg) {
29+
return `https://www.npmjs.com/package/${pkg}`;
30+
}
31+
32+
/**
33+
* @param {import('./types').PeerDep[]} peerDeps
34+
*/
35+
function sortPeerDeps(peerDeps) {
36+
return Object.values(
37+
[...peerDeps]
38+
.sort((a, b) => {
39+
if (a.pkg === 'eslint') {
40+
return -1;
41+
}
42+
if (b.pkg === 'eslint') {
43+
return 1;
44+
}
45+
return Number(a.optional) - Number(b.optional);
46+
})
47+
.reduce(
48+
(acc, peerDep) => ({
49+
...acc,
50+
[packageIcon(peerDep.pkg)]: [
51+
...(acc[packageIcon(peerDep.pkg)] ?? []),
52+
peerDep,
53+
],
54+
}),
55+
{},
56+
),
57+
).flat();
58+
}
59+
60+
module.exports = {
61+
packageIcon,
62+
packageDocs,
63+
sortPeerDeps,
64+
};

scripts/helpers/types.d.ts

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ export type ExtendedConfig = {
1515
rulesCount: number;
1616
};
1717

18+
export type PeerDep = {
19+
pkg: string;
20+
version: string;
21+
optional: boolean;
22+
};
23+
1824
// corresponds to PNG file names in docs/icons
1925
export type Icon =
2026
| 'icons8/expired'
@@ -26,6 +32,7 @@ export type Icon =
2632
| 'material/angular_component'
2733
| 'material/angular'
2834
| 'material/cypress'
35+
| 'material/eslint'
2936
| 'material/graphql'
3037
| 'material/javascript'
3138
| 'material/jest'

0 commit comments

Comments
 (0)