|
1 | 1 | import assert from "assert";
|
2 |
| -import {readdirSync, readFileSync, statSync} from "fs"; |
| 2 | +import {readdir, readFile, stat} from "fs/promises"; |
3 | 3 |
|
4 | 4 | it("documentation links point to existing internal anchors", async () => {
|
5 | 5 | const root = "docs";
|
6 | 6 |
|
7 | 7 | // Crawl all files, read their links and anchors.
|
8 | 8 | const anchors = new Map();
|
9 | 9 | const links = [];
|
10 |
| - for (const file of getMDFiles(root)) { |
11 |
| - const text = getSource(root + file); |
| 10 | + for await (const file of readMarkdownFiles(root)) { |
| 11 | + const text = await readMarkdownSource(root + file); |
12 | 12 | anchors.set(file, getAnchors(text));
|
13 |
| - for (const {pathname, hash} of getLinks(file, text)) links.push({source: file, target: pathname, hash}); |
| 13 | + for (const {pathname, hash} of getLinks(file, text)) { |
| 14 | + links.push({source: file, target: pathname, hash}); |
| 15 | + } |
14 | 16 | }
|
15 | 17 |
|
16 | 18 | // Check for broken links.
|
@@ -50,25 +52,23 @@ function getLinks(file, text) {
|
50 | 52 | const links = [];
|
51 | 53 | for (const match of text.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)) {
|
52 | 54 | const [, link] = match;
|
53 |
| - if (link.startsWith("http")) continue; |
54 |
| - const {pathname, hash} = new URL(link, new URL(file, "https://toplevel.tld/")); |
| 55 | + if (/^\w+:/.test(link)) continue; // absolute link with protocol |
| 56 | + const {pathname, hash} = new URL(link, new URL(file, "https://example.com/")); |
55 | 57 | links.push({pathname, hash});
|
56 | 58 | }
|
57 | 59 | return links;
|
58 | 60 | }
|
59 | 61 |
|
60 | 62 | // In source files, ignore comments.
|
61 |
| -function getSource(f) { |
62 |
| - return readFileSync(f, "utf8").replaceAll(/<!-- .*? -->/gs, ""); |
| 63 | +async function readMarkdownSource(f) { |
| 64 | + return (await readFile(f, "utf8")).replaceAll(/<!-- .*? -->/gs, ""); |
63 | 65 | }
|
64 | 66 |
|
65 | 67 | // Recursively find all md files in the directory.
|
66 |
| -function getMDFiles(root, subpath = "/") { |
67 |
| - const files = []; |
68 |
| - for (const fname of readdirSync(root + subpath)) { |
69 |
| - if (fname.startsWith(".") || fname.endsWith(".js")) continue; |
70 |
| - if (fname.endsWith(".md")) files.push(subpath + fname); |
71 |
| - else if (statSync(root + subpath + fname).isDirectory()) files.push(...getMDFiles(root, subpath + fname + "/")); |
| 68 | +async function* readMarkdownFiles(root, subpath = "/") { |
| 69 | + for (const fname of await readdir(root + subpath)) { |
| 70 | + if (fname.startsWith(".")) continue; // ignore .vitepress etc. |
| 71 | + if ((await stat(root + subpath + fname)).isDirectory()) yield* readMarkdownFiles(root, subpath + fname + "/"); |
| 72 | + else if (fname.endsWith(".md")) yield subpath + fname; |
72 | 73 | }
|
73 |
| - return files; |
74 | 74 | }
|
0 commit comments