-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathpopulateLibraries.ts
More file actions
109 lines (91 loc) · 3 KB
/
populateLibraries.ts
File metadata and controls
109 lines (91 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { readdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
const UTF8: BufferEncoding = "utf8";
const MARKERS = {
start: "<!-- START: Auto-generated by ./populateLibraries.ts -->",
end: "<!-- END: Auto-generated by ./populateLibraries.ts -->",
} as const;
interface LibraryEntry {
name: string;
description: string;
}
interface FileUpdate {
path: string;
label: string;
formatLine: (entry: LibraryEntry) => string;
}
interface PackageMetadata {
description?: string;
}
const FILES: FileUpdate[] = [
{
path: join(__dirname, "README.md"),
label: "README.md '## Libraries'",
formatLine: (entry) =>
`- [${entry.name}](./packages/${entry.name}/README.md): ${entry.description}`,
},
{
path: join(__dirname, "packages", "ai-rules", "rules", "common", "coreLibraries.md"),
label: "ai-rules coreLibraries.md",
formatLine: (entry) => `- **${entry.name}**: ${entry.description}`,
},
];
async function getLibraryEntries(): Promise<LibraryEntry[]> {
const entries = await readdir(join(__dirname, "packages"), {
withFileTypes: true,
});
const directories = entries.filter((dirent) => dirent.isDirectory());
const results = await Promise.allSettled(
directories.map(async (dirent) => {
const path = join(__dirname, "packages", dirent.name, "package.json");
const parsed = parsePackageMetadata(await readFile(path, UTF8));
return { name: dirent.name, description: parsed.description ?? "" };
}),
);
const rejected = results.filter(
(result): result is PromiseRejectedResult => result.status === "rejected",
);
if (rejected.length > 0) {
console.warn(
`Skipped ${rejected.length} package.json file(s):`,
rejected.map((r) => String(r.reason)),
);
}
return results
.filter(
(result): result is PromiseFulfilledResult<LibraryEntry> => result.status === "fulfilled",
)
.map((result) => result.value);
}
function parsePackageMetadata(value: string): PackageMetadata {
const parsed: unknown = JSON.parse(value);
if (typeof parsed !== "object" || parsed === null) {
return {};
}
if (!("description" in parsed)) {
return {};
}
return typeof parsed.description === "string" ? { description: parsed.description } : {};
}
async function updateFile(file: FileUpdate, entries: readonly LibraryEntry[]): Promise<void> {
const content = await readFile(file.path, UTF8);
const lines = entries.map((entry) => file.formatLine(entry));
const updated = content.replace(
new RegExp(`${MARKERS.start}[\\s\\S]*${MARKERS.end}`),
`${MARKERS.start}\n\n${lines.join("\n")}\n\n${MARKERS.end}`,
);
if (content === updated) {
console.log(`No changes to ${file.label} section.`);
return;
}
await writeFile(file.path, updated, UTF8);
}
async function updateLibraries(): Promise<void> {
const entries = await getLibraryEntries();
await Promise.all(
FILES.map(async (file) => {
await updateFile(file, entries);
}),
);
}
void updateLibraries();