Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

docs: autogenerate rules table on website#5116

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 ourterms of service andprivacy 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 fromall commits
Commits
Show all changes
20 commits
Select commitHold shift + click to select a range
d9c310e
docs: autogenerate rules table on website
Josh-CenaMay 31, 2022
5bb667c
migrate rule attributes to global data
Josh-CenaMay 31, 2022
47bbca7
add mdlint ignore
Josh-CenaMay 31, 2022
ea763f9
add filter
Josh-CenaMay 31, 2022
0694aee
avoid redirecting to main site
Josh-CenaMay 31, 2022
4e81555
merge two columns
Josh-CenaMay 31, 2022
6749355
this is hard
Josh-CenaMay 31, 2022
b85e154
refactor
Josh-CenaJun 1, 2022
c6ef6b9
tweak colors
Josh-CenaJun 1, 2022
29bea86
ok - memoize this
Josh-CenaJun 1, 2022
a27757e
refactors more
Josh-CenaJun 1, 2022
cc3075f
Apply suggestions from code review
Josh-CenaJun 1, 2022
d9900fb
ok, use classes
Josh-CenaJun 1, 2022
5839647
vertially arrange icons
Josh-CenaJun 1, 2022
b4446ae
Merge branch 'master' into filterable-table
Josh-CenaJun 9, 2022
10d5ea0
Remove rules table from README
Josh-CenaJun 9, 2022
1363c4e
minor refactors
Josh-CenaJun 9, 2022
004f3e9
Merge branch 'main' into filterable-table
Josh-CenaJun 11, 2022
01017fe
Merge branch 'main' into filterable-table
JoshuaKGoldbergJun 25, 2022
fc3eadf
Accessibility labels
JoshuaKGoldbergJun 25, 2022
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
3 changes: 2 additions & 1 deletion.markdownlint.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,7 +69,8 @@
"details",
"summary",
"Tabs",
"TabItem"
"TabItem",
"RulesTable"
]
},
// MD034/no-bare-urls - Bare URL used
Expand Down
147 changes: 1 addition & 146 deletionspackages/eslint-plugin/README.md
View file
Open in desktop

Large diffs are not rendered by default.

142 changes: 3 additions & 139 deletionspackages/eslint-plugin/docs/rules/README.md
View file
Open in desktop

Large diffs are not rendered by default.

111 changes: 0 additions & 111 deletionspackages/eslint-plugin/tests/docs.test.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,6 @@ import { titleCase } from 'title-case';
const docsRoot = path.resolve(__dirname, '../docs/rules');
const rulesData = Object.entries(rules);

function createRuleLink(ruleName: string, readmePath: string): string {
return `[\`@typescript-eslint/${ruleName}\`](${
readmePath.includes('docs/rules') ? '.' : './docs/rules'
}/${ruleName}.md)`;
}

function parseMarkdownFile(filePath: string): marked.TokensList {
const file = fs.readFileSync(filePath, 'utf-8');

Expand All@@ -24,27 +18,6 @@ function parseMarkdownFile(filePath: string): marked.TokensList {
});
}

function parseReadme(readmePath: string): {
base: marked.Tokens.Table;
extension: marked.Tokens.Table;
} {
const readme = parseMarkdownFile(readmePath);

// find the table
const rulesTables = readme.filter(
(token): token is marked.Tokens.Table =>
'type' in token && token.type === 'table',
);
if (rulesTables.length !== 2) {
throw Error('Could not find both rules tables in README.md');
}

return {
base: rulesTables[0],
extension: rulesTables[1],
};
}

function isEmptySchema(schema: JSONSchema4 | JSONSchema4[]): boolean {
return Array.isArray(schema)
? schema.length === 0
Expand DownExpand Up@@ -207,87 +180,3 @@ describe('Validating rule metadata', () => {
});
}
});

describe.each([
path.join(__dirname, '../README.md'),
path.join(__dirname, '../docs/rules/README.md'),
])('%s', readmePath => {
const rulesTables = parseReadme(readmePath);
const notDeprecated = rulesData.filter(([, rule]) => !rule.meta.deprecated);
const baseRules = notDeprecated.filter(
([, rule]) => !rule.meta.docs?.extendsBaseRule,
);
const extensionRules = notDeprecated.filter(
([, rule]) => rule.meta.docs?.extendsBaseRule,
);

it('All non-deprecated base rules should have a row in the base rules table, and the table should be ordered alphabetically', () => {
const baseRuleNames = baseRules
.map(([ruleName]) => ruleName)
.sort()
.map(ruleName => createRuleLink(ruleName, readmePath));

expect(rulesTables.base.rows.map(row => row[0].text)).toStrictEqual(
baseRuleNames,
);
});
it('All non-deprecated extension rules should have a row in the base rules table, and the table should be ordered alphabetically', () => {
const extensionRuleNames = extensionRules
.map(([ruleName]) => ruleName)
.sort()
.map(ruleName => createRuleLink(ruleName, readmePath));

expect(rulesTables.extension.rows.map(row => row[0].text)).toStrictEqual(
extensionRuleNames,
);
});

for (const [ruleName, rule] of notDeprecated) {
describe(`Checking rule ${ruleName}`, () => {
const ruleRow: string[] | undefined = (
rule.meta.docs?.extendsBaseRule
? rulesTables.extension.rows
: rulesTables.base.rows
)
.find(row => row[0].text.includes(`/${ruleName}.md`))
?.map(cell => cell.text);
if (!ruleRow) {
// rule is in the wrong table, the first two tests will catch this, so no point in creating noise;
// these tests will ofc fail in that case
return;
}

it('Link column should be correct', () => {
expect(ruleRow[0]).toBe(createRuleLink(ruleName, readmePath));
});

it('Description column should be correct', () => {
expect(ruleRow[1]).toBe(rule.meta.docs?.description);
});

it('Recommended column should be correct', () => {
expect(ruleRow[2]).toBe(
rule.meta.docs?.recommended === 'strict'
? ':lock:'
: rule.meta.docs?.recommended
? ':white_check_mark:'
: '',
);
});

it('Fixable column should be correct', () => {
expect(ruleRow[3]).toBe(
rule.meta.fixable !== undefined ? ':wrench:' : '',
);
});

it('Requiring type information column should be correct', () => {
expect(ruleRow[4]).toBe(
rule.meta.docs?.requiresTypeChecking === true
? ':thought_balloon:'
: '',
);
});
});
}
});
4 changes: 4 additions & 0 deletionspackages/website/docusaurusConfig.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ import type { UserThemeConfig as ThemeCommonConfig } from '@docusaurus/theme-com
import type { UserThemeConfig as AlgoliaThemeConfig } from '@docusaurus/theme-search-algolia';
import type { Config } from '@docusaurus/types';

import { rulesMeta } from './rulesMeta';
import npm2yarnPlugin from '@docusaurus/remark-plugin-npm2yarn';
import tabsPlugin from 'remark-docusaurus-tabs';
import { addRuleAttributesList } from './plugins/add-rule-attributes-list';
Expand DownExpand Up@@ -175,6 +176,9 @@ const config: Config = {
projectName: 'typescript-eslint',
clientModules: [require.resolve('./src/clientModules.js')],
presets: [['classic', presetClassicOptions]],
customFields: {
rules: rulesMeta,
},
plugins: [
require.resolve('./webpack.plugin'),
['@docusaurus/plugin-content-docs', pluginContentDocsOptions],
Expand Down
185 changes: 10 additions & 175 deletionspackages/website/plugins/add-rule-attributes-list.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
import type * as unist from 'unist';
import type * as mdast from 'mdast';
import type { Plugin } from 'unified';

Expand All@@ -13,191 +14,25 @@ const addRuleAttributesList: Plugin = () => {
if (rule == null) {
return;
}
const config = ((): 'recommended' | 'strict' | null => {
switch (rule.meta.docs?.recommended) {
case 'error':
case 'warn':
return 'recommended';

case 'strict':
return 'strict';

default:
return null;
}
})();
const autoFixable = rule.meta.fixable != null;
const suggestionFixable = rule.meta.hasSuggestions === true;
const requiresTypeInfo = rule.meta.docs?.requiresTypeChecking === true;

const parent = root as mdast.Parent;
/*
This just outputs a list with a heading like:

## Attributes

- [ ] Config
- [ ] ✅ Recommended
- [ ] 🔒 Strict
- [ ] Fixable
- [ ] 🔧 Automated Fixer (`--fix`)
- [ ] 🛠 Suggestion Fixer
- [ ] 💭 Requires type information
*/
const heading = Heading({
depth: 2,
text: 'Attributes',
});
const ruleAttributes = List({
children: [
NestedList({
checked: config != null,
children: [
ListItem({
checked: config === 'recommended',
text: '✅ Recommended',
}),
ListItem({
checked: config === 'strict' || config === 'recommended',
text: '🔒 Strict',
}),
],
text: 'Included in configs',
}),
NestedList({
checked: autoFixable || suggestionFixable,
children: [
ListItem({
checked: autoFixable,
text: '🔧 Automated Fixer',
}),
ListItem({
checked: suggestionFixable,
text: '🛠 Suggestion Fixer',
}),
],
text: 'Fixable',
}),
ListItem({
checked: requiresTypeInfo,
text: '💭 Requires type information',
}),
],
});

const parent = root as unist.Parent;
const h2Idx = parent.children.findIndex(
child => child.type === 'heading' && child.depth === 2,
child => child.type === 'heading' &&(child as mdast.Heading).depth === 2,
);
// The actual content will be injected on client side.
const attrNode = {
type: 'jsx',
value: `<rule-attributes name="${file.stem}" />`,
};
if (h2Idx != null) {
// insert it just before the first h2 in the doc
// this should be just after the rule's description
parent.children.splice(h2Idx, 0,heading, ruleAttributes);
parent.children.splice(h2Idx, 0,attrNode);
} else {
// failing that, add it to the end
parent.children.push(heading, ruleAttributes);
parent.children.push(attrNode);
}
};
};

function Heading({
depth,
text,
id = text.toLowerCase(),
}: {
depth: mdast.Heading['depth'];
id?: string;
text: string;
}): mdast.Heading {
return {
type: 'heading',
depth,
children: [
{
type: 'text',
value: text,
},
],
data: {
hProperties: {
id,
},
id,
},
};
}

function Paragraph({ text }: { text: string }): mdast.Paragraph {
return {
type: 'paragraph',
children: [
{
type: 'text',
value: text,
},
],
};
}

function ListItem({
checked,
text,
}: {
checked: boolean;
text: string;
}): mdast.ListItem {
return {
type: 'listItem',
spread: false,
checked: checked,
children: [
{
type: 'paragraph',
children: [
{
type: 'text',
value: text,
},
],
},
],
};
}

function NestedList({
children,
checked,
text,
}: {
children: mdast.ListItem[];
checked: boolean;
text: string;
}): mdast.ListItem {
return {
type: 'listItem',
spread: false,
checked: checked,
children: [
Paragraph({
text,
}),
List({
children,
}),
],
data: {
className: 'test',
},
};
}

function List({ children }: { children: mdast.ListItem[] }): mdast.List {
return {
type: 'list',
ordered: false,
start: null,
spread: false,
children,
};
}

export { addRuleAttributesList };
15 changes: 15 additions & 0 deletionspackages/website/rulesMeta.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
import * as eslintPlugin from '@typescript-eslint/eslint-plugin';

export const rulesMeta = Object.entries(eslintPlugin.rules).map(
([name, content]) => ({
name,
type: content.meta.type,
docs: content.meta.docs,
fixable: content.meta.fixable,
hasSuggestions: content.meta.hasSuggestions,
deprecated: content.meta.deprecated,
replacedBy: content.meta.replacedBy,
}),
);

export type RulesMeta = typeof rulesMeta;
Loading

[8]ページ先頭

©2009-2025 Movatter.jp