-
Notifications
You must be signed in to change notification settings - Fork 315
/
check-link.js
176 lines (153 loc) · 4.45 KB
/
check-link.js
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
const fs = require("fs");
const path = require("path");
const { Remarkable } = require("remarkable");
const md = new Remarkable({ html: true });
const EN_MENU_PATH = path.join(__dirname, "site/en/menuStructure/en.json");
const EN_MDS_PATH = path.join(__dirname, "site/en/");
const errors = [];
const getMenuJson = (path) => {
const doc = fs.readFileSync(path);
return JSON.parse(doc.toString());
};
const getMenuIds = (data)=>{
if(data?.menuList?.length){
return data.menuList.map((v) => v.id);
}
return data.reduce((acc, cur) => {
const {children = []} = cur;
if(children.length){
return acc.concat(getMenuIds(children))
}
return acc.concat(cur.id)
},[])
}
// there are two version of menu structure now
const EN_MENU_CONTENT = getMenuJson(EN_MENU_PATH);
const enIds = getMenuIds(EN_MENU_CONTENT)
// check if id is duplicate in menustructor
const checkSameId = (ids, prefix = "") => {
const arr = [];
const sameArr = new Set();
ids.forEach((v) => {
arr.includes(v) ? sameArr.add(v) : arr.push(v);
});
if (sameArr.size > 0) {
let warning = "";
sameArr.forEach(
(v) =>
(warning += `Menustructure ${prefix}.json - id: ${
v || `\'\'`
} is duplicate. \n`)
);
errors.push(warning);
// throw new Error(warning);
}
return "";
};
checkSameId(enIds, "EN");
const metaRegx = /(\S+)\s*?:\s*([\s\S]*?)(?=$|\n)/g;
/**
*
* @param {*} dirPath
* @param {*} validMds
* @param {*} validPaths
* @returns {
* validMds: all valid markdown id
* validPaths: all valid markdown file path
* }
*/
const generateValidIds = (dirPath, validMds = [], validPaths = []) => {
let filesList = fs.readdirSync(dirPath);
for (let i = 0; i < filesList.length; i++) {
//拼接当前文件的路径(上一层路径+当前file的名字)
let filePath = path.join(dirPath, filesList[i]);
//根据文件路径获取文件信息,返回一个fs.Stats对象
let stats = fs.statSync(filePath);
if (stats.isDirectory()) {
//递归调用
generateValidIds(filePath, validMds, validPaths);
} else {
if (filesList[i].includes(".md") && !dirPath.includes("fragments")) {
const doc = fs.readFileSync(filePath);
const content = doc.toString();
const match = content.match(metaRegx);
if (match && match[0].includes("id")) {
const metaId = match[0].split(":")[1].trim();
if (metaId !== filesList[i]) {
errors.push(`Filename: ${filesList[i]} need same with its id`);
// throw `Filename: ${filesList[i]} need same with its id`;
}
validMds.push(metaId);
validPaths.push(filePath);
}
}
}
}
return { validMds, validPaths };
};
const { validMds: enValidMds, validPaths: enValidPaths } =
generateValidIds(EN_MDS_PATH);
const enMenuMdIds = enIds.filter((v) => v.includes(".md"));
const checkIsIdValid = (arr, validArr) => {
arr.forEach((v) => {
if (!validArr.includes(v)) {
errors.push(
`Id: ${v} in menustructor will be a broken link, because cant find markdown file by this id. `
);
// throw new Error(
// `Id: ${v} in menustructor will be a broken link, because cant find markdown file by this id. `
// );
}
});
};
// check menu id valid
checkIsIdValid(enMenuMdIds, enValidMds);
const getCheckLinks = (content) => {
const html = md.render(content);
const regex = /<a\s+(?:[^>]*?\s+)?href=(["'])(.*?)\1/g;
const checkedUrls = new Set();
let match;
while ((match = regex.exec(html)) !== null) {
let link = match[2].trim();
if (checkedUrls.has(link)) {
continue;
}
checkedUrls.add(link);
}
const checkLinks = Array.from(checkedUrls);
return checkLinks
? checkLinks.filter(
(v) =>
v && v.includes(".md") && !v.includes("http") && !v.includes("\n")
)
: [];
};
const checkInnerLink = (paths, validMds) => {
paths.forEach((v) => {
const doc = fs.readFileSync(v);
const content = doc.toString();
const innerLinks = getCheckLinks(content);
innerLinks.forEach((link) => {
let ignoreAnchorLink = link.split("#")[0];
// ignore api reference
// api reference links with () will beacome .md after match
if (
link.includes("/api-reference/") ||
link === ".md" ||
link.indexOf("**") !== -1
) {
return;
}
if (!validMds.includes(ignoreAnchorLink)) {
errors.push(`Check Link ${link} in ${v}`);
// throw new Error(`Check Link ${link} in ${v}`);
}
});
});
};
// check markdown file inner link
checkInnerLink(enValidPaths, enValidMds);
if (errors.length) {
errors.forEach((v) => console.log(v));
throw new Error("Fail");
}