-
Notifications
You must be signed in to change notification settings - Fork 283
/
Copy pathcheck-sidebar.mjs
executable file
Β·125 lines (92 loc) Β· 3.24 KB
/
check-sidebar.mjs
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
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env node
import fsPromises from 'fs/promises';
import fs from 'fs';
import path, { resolve } from 'path';
import readline from 'readline';
const ACCEPTED_FILE_EXTENSIONS = ['md', 'mdx']; // TODO: maybe regex later
const walkJson = async (jsonPath) => {
const json = await fsPromises.readFile(resolve(jsonPath));
const docs = JSON.parse(json)['docs'];
const paths = [];
while (docs.length) {
const stringOrObject = docs.pop();
if (typeof stringOrObject === 'string') {
paths.push(stringOrObject);
continue;
}
// TODO: cover case of complex config objects (https://docusaurus.io/docs/next/sidebar/items#sidebar-item-category)
if (Object.keys(stringOrObject).length > 1) continue;
const [vals] = Object.values(stringOrObject);
docs.push(...vals);
}
return paths;
};
const walkFiles = async (docsPath) => {
const resolvedPath = resolve(docsPath);
const files = await fsPromises.readdir(resolvedPath, { withFileTypes: true });
const extractIdPromises = [];
while (files.length) {
const fileOrDirectory = files.pop();
const completePath = resolve(fileOrDirectory.parentPath, fileOrDirectory.name);
if (fileOrDirectory.isDirectory()) {
const d = await fsPromises.readdir(completePath, { withFileTypes: true });
files.push(...d);
continue;
}
if (
fileOrDirectory.isFile() &&
ACCEPTED_FILE_EXTENSIONS.some((extension) =>
fileOrDirectory.name.endsWith(extension),
)
) {
extractIdPromises.push(extractIdFromFile(completePath));
}
}
const finalPaths = (await Promise.allSettled(extractIdPromises))
.filter((v) => v.status === 'fulfilled')
.map((v) => v.value.replace(resolvedPath + path.sep, ''));
return finalPaths;
};
const extractIdFromFile = async (resolvedPath) => {
const rs = fs.createReadStream(resolvedPath);
const rli = readline.createInterface({ input: rs });
let limit = 8;
const id = await new Promise((resolve, reject) => {
rli.on('line', (line) => {
if (!limit) return rli.pause();
if (!line.startsWith('id:')) return;
const id = line.replace(/^(id:)\s*/, '').trim();
resolve(id);
});
rli.on('pause', () => {
// extractIdFromFile executed "sync" unless this is here
setTimeout(() => {
reject('EOF');
}, 50);
});
});
rli.close();
rs.close();
const d = resolvedPath.split(path.sep);
d.pop();
d.push(id);
return d.join(path.sep);
};
(async () => {
const args = process.argv;
if (!args.includes('--folder-path')) throw new Error('Missing --folder-path');
if (!args.includes('--json-path')) throw new Error('Missing --json-path');
const folderPath = args.at(args.indexOf('--folder-path') + 1);
const jsonPath = args.at(args.indexOf('--json-path') + 1);
const [definedPaths, availablePaths] = await Promise.all([
walkJson(jsonPath),
walkFiles(folderPath),
]);
const unusedPaths = availablePaths.filter((p) => !definedPaths.includes(p));
const nonExistentPaths = definedPaths.filter((p) => !availablePaths.includes(p));
console.log('Paths missing from sidebars_[platform].json:', unusedPaths);
console.log(
'Paths defined in sidebars_[platform].json but missing from docs:',
nonExistentPaths,
);
})();