-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathicon-assets.test.ts
More file actions
114 lines (96 loc) · 3.22 KB
/
Copy pathicon-assets.test.ts
File metadata and controls
114 lines (96 loc) · 3.22 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
110
111
112
113
114
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
interface IconFinding {
readonly file: string;
readonly issue: string;
}
interface DocsIconConfig {
readonly favicon: string;
readonly logo: {
readonly dark: string;
readonly light: string;
};
}
const INVALID_ICON_REPLACEMENTS: ReadonlyMap<string, string> = new Map([
["bell-slash", "bell-off"],
["circle-exclamation", "circle-alert"],
["circle-info", "info"],
["clock-rotate-left", "history"],
["file-json", "braces"],
["file-warning", "triangle-alert"],
["filter", "funnel"],
["home", "house"],
["magnifying-glass", "search"],
["messages", "messages-square"],
["microsoft", "workflow"],
["pause-circle", "circle-pause"],
["play-circle", "circle-play"],
["retweet", "repeat-2"],
["user-group", "users"],
["user-shield", "shield-user"],
]);
const SKIPPED_DIRECTORIES = new Set<string>([".git", ".github", "node_modules"] as const);
function listMarkdownFiles(directory = "."): readonly string[] {
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
const path = join(directory, entry.name);
if (entry.isDirectory()) {
return SKIPPED_DIRECTORIES.has(entry.name) ? [] : listMarkdownFiles(path);
}
if (entry.name === "DOCS_QUALITY_POLL.md") {
return [];
}
return entry.isFile() && /\.mdx?$/u.test(entry.name) ? [path] : [];
});
}
function inspectIcon(file: string, icon: string | undefined, findings: IconFinding[]): void {
if (icon === undefined) {
findings.push({ file, issue: "Icon value missing. Set an icon." });
return;
}
if (icon.startsWith("/")) {
if (!existsSync(`.${icon}`)) {
findings.push({ file, issue: `Local icon ${icon} does not exist.` });
}
return;
}
const replacement = INVALID_ICON_REPLACEMENTS.get(icon);
if (replacement !== undefined) {
findings.push({
file,
issue: `Lucide icon ${icon} returns 403. Use ${replacement}.`,
});
}
}
function collectIconFindings(): readonly IconFinding[] {
const findings: IconFinding[] = [];
for (const file of listMarkdownFiles()) {
const source = readFileSync(file, "utf8");
for (const match of source.matchAll(/\bicon\s*=\s*["']([^"']+)["']/gu)) {
inspectIcon(file, match[1], findings);
}
}
const docsSource = readFileSync("docs.json", "utf8");
for (const match of docsSource.matchAll(/"icon"\s*:\s*"([^"]+)"/gu)) {
inspectIcon("docs.json", match[1], findings);
}
const docsConfig = JSON.parse(docsSource) as DocsIconConfig;
for (const asset of [docsConfig.favicon, docsConfig.logo.dark, docsConfig.logo.light]) {
inspectIcon("docs.json", asset, findings);
}
return findings;
}
describe("icon assets", (): void => {
it("reports missing icon values", (): void => {
expect.assertions(1);
const findings: IconFinding[] = [];
inspectIcon("page.mdx", undefined, findings);
expect(findings).toStrictEqual([
{ file: "page.mdx", issue: "Icon value missing. Set an icon." },
]);
});
it("keeps local and Lucide icon references loadable", (): void => {
expect.assertions(1);
expect(collectIconFindings()).toStrictEqual([]);
});
});