Skip to content

Commit 330cc4c

Browse files
committed
feat: update rule metadata generation and add missing rules to documentation
1 parent a3e5280 commit 330cc4c

File tree

3 files changed

+56
-25
lines changed

3 files changed

+56
-25
lines changed

apps/website/content/docs/rules/meta.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
"no-create-ref",
2929
"no-default-props",
3030
"no-direct-mutation-state",
31-
"no-direct-set-state-in-use-effect",
32-
"no-direct-set-state-in-use-layout-effect",
3331
"no-duplicate-key",
3432
"no-forbidden-props",
3533
"no-forward-ref",
@@ -60,6 +58,7 @@
6058
"no-unused-state",
6159
"no-use-context",
6260
"no-useless-forward-ref",
61+
"no-useless-fragment",
6362
"prefer-destructuring-assignment",
6463
"prefer-namespace-import",
6564
"prefer-read-only-props",
@@ -82,6 +81,7 @@
8281
"dom-no-unsafe-target-blank",
8382
"dom-no-use-form-state",
8483
"dom-no-void-elements-with-children",
84+
"dom-prefer-namespace-import",
8585
"---Web API Rules---",
8686
"web-api-no-leaked-event-listener",
8787
"web-api-no-leaked-interval",

apps/website/content/docs/rules/overview.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ full: true
110110
| [`no-unsafe-target-blank`](./dom-no-unsafe-target-blank) | 1️⃣ | `🔧` | Disallow `target="_blank"` without `rel="noreferrer noopener"` | |
111111
| [`no-use-form-state`](./dom-no-use-form-state) | 2️⃣ | `🔄` | Replaces usages of `useFormState` with `useActionState` | >=19.0.0 |
112112
| [`no-void-elements-with-children`](./dom-no-void-elements-with-children) | 2️⃣ | | Disallow `children` in void DOM elements | |
113+
| [`prefer-namespace-import`](./dom-prefer-namespace-import) | 0️⃣ | `🔧` | Enforces React DOM is imported via a namespace import | |
113114

114115
## Web API Rules
115116

scripts/update-website.ts

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,6 @@ import * as Effect from "effect/Effect";
77

88
import { glob } from "./utils/glob";
99

10-
/**
11-
* Build script for processing and copying documentation to the website
12-
*
13-
* This script (Effect version):
14-
* 1. Collects rule documentation from ESLint Plugins
15-
* 2. Copies them to the website with proper naming
16-
* 3. Processes the changelog
17-
* 4. (TODO) Generates meta.json / rules data
18-
*/
19-
2010
const DOCS_GLOB = ["packages/plugins/eslint-plugin-react-*/src/rules/*.mdx"];
2111

2212
interface RuleMeta {
@@ -54,7 +44,6 @@ function copyRuleDoc(meta: RuleMeta) {
5444
const fs = yield* FileSystem.FileSystem;
5545
const path = yield* Path.Path;
5646
const dir = path.dirname(meta.destination);
57-
// Ensure destination directory exists
5847
yield* fs.makeDirectory(dir, { recursive: true });
5948
const content = yield* fs.readFileString(meta.source, "utf8");
6049
yield* fs.writeFileString(meta.destination, content);
@@ -63,6 +52,58 @@ function copyRuleDoc(meta: RuleMeta) {
6352
});
6453
}
6554

55+
function generateRuleMetaJson(metas: RuleMeta[]) {
56+
return Effect.gen(function*() {
57+
const fs = yield* FileSystem.FileSystem;
58+
const path = yield* Path.Path;
59+
const targetPath = path.join("apps", "website", "content", "docs", "rules", "meta.json");
60+
interface Grouped {
61+
readonly [k: string]: readonly string[];
62+
}
63+
64+
const grouped = metas.reduce<Grouped>((acc, meta) => {
65+
const catename = meta.title.includes("/") ? meta.title.split("/", 1)[0] : "x";
66+
if (catename == null) return acc;
67+
const list = acc[catename] ?? [];
68+
return {
69+
...acc,
70+
[catename]: [...list, meta.name],
71+
};
72+
}, {});
73+
74+
const sortAsc = (arr: readonly string[]): string[] => [...arr].sort((a, b) => a.localeCompare(b, "en"));
75+
76+
const orderedCategories: Array<{
77+
key: string;
78+
heading: string;
79+
}> = [
80+
{ key: "x", heading: "---X Rules---" },
81+
{ key: "dom", heading: "---DOM Rules---" },
82+
{ key: "web-api", heading: "---Web API Rules---" },
83+
{ key: "naming-convention", heading: "---Naming Convention Rules---" },
84+
{ key: "debug", heading: "---Debug Rules---" },
85+
];
86+
87+
const pages = orderedCategories.reduce<string[]>((acc, cat) => {
88+
const rules = grouped[cat.key];
89+
if (rules && rules.length > 0) {
90+
acc.push(cat.heading);
91+
acc.push(...sortAsc(rules));
92+
}
93+
return acc;
94+
}, ["overview"]);
95+
96+
const jsonContent = JSON.stringify({ pages }, null, 2) + "\n";
97+
98+
const dir = path.dirname(targetPath);
99+
yield* fs.makeDirectory(dir, { recursive: true });
100+
yield* fs.writeFileString(targetPath, jsonContent);
101+
yield* Effect.log(ansis.magenta(`Generated rules meta -> ${targetPath}`));
102+
103+
return { pages, path: targetPath };
104+
});
105+
}
106+
66107
const processChangelog = Effect.gen(function*() {
67108
const fs = yield* FileSystem.FileSystem;
68109
const path = yield* Path.Path;
@@ -95,20 +136,9 @@ const program = Effect.gen(function*() {
95136
: `Found ${ansis.bold(metas.length.toString())} rule documentation file(s).`,
96137
);
97138

98-
// Copy in parallel with limited concurrency (adjust if needed)
99139
yield* Effect.forEach(metas, copyRuleDoc, { concurrency: 8 });
100140

101-
// (Optional) Generate rules metadata JSON (still TODO)
102-
// const rulesData = metas.map(({ name, title }) => ({ name, title }));
103-
// const rulesDataPath = path.join("apps", "website", "content", "docs", "rules", "data.json");
104-
// yield* FileSystem.FileSystem.flatMap(fs =>
105-
// fs.makeDirectory(path.dirname(rulesDataPath), { recursive: true }).pipe(
106-
// Effect.zipRight(
107-
// fs.writeFileString(rulesDataPath, JSON.stringify(rulesData, null, 2) + "\n")
108-
// ),
109-
// Effect.tap(() => Effect.log(ansis.magenta(`Generated ${rulesDataPath}`)))
110-
// )
111-
// );
141+
yield* generateRuleMetaJson(metas);
112142

113143
yield* processChangelog;
114144

0 commit comments

Comments
 (0)