Skip to content

Commit 0de56c1

Browse files
committed
add an internal link check for markdownlint-cli2
1 parent 459e145 commit 0de56c1

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

.markdownlint-cli2.jsonc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint-cli2/refs/heads/main/schema/markdownlint-cli2-config-schema.json",
3+
"customRules": ["scripts/checkInternalLink.js"],
4+
}

.markdownlint.jsonc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@
1818
],
1919
},
2020
"ul-style": false,
21+
"check-internal-link": {
22+
"warn_only": true,
23+
},
2124
}

scripts/checkInternalLink.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import path, { relative } from 'node:path'
2+
import { env } from 'node:process'
3+
4+
const is_in_github_action = env.GITHUB_ACTIONS == 'true'
5+
6+
// the slugify function is used in vitepress to generate title id
7+
const rControl = /[\u0000-\u001f]/g
8+
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'<>,.?/]+/g
9+
const rCombining = /[\u0300-\u036F]/g
10+
const slugify = str =>
11+
str
12+
.normalize('NFKD')
13+
.replace(rCombining, '')
14+
.replace(rControl, '')
15+
.replace(rSpecial, '-')
16+
.replace(/-{2,}/g, '-')
17+
.replace(/^-+|-+$/g, '')
18+
.replace(/^(\d)/, '_$1')
19+
.toLowerCase()
20+
21+
export default {
22+
names: ['check-internal-link'],
23+
description: 'check the internal title is referenced currectly in wiki',
24+
// information: undefined,
25+
tags: ['i18n'],
26+
function: function rule(params, onError) {
27+
// warn_only will just generate a `console.warn` message and will not block CI build
28+
// config it at `.markdownlint.jsonc`
29+
const warn_only = params.config && params.config.warn_only
30+
31+
let found_title = new Set()
32+
33+
let title_level = 0
34+
let pending_title = undefined
35+
36+
function find_titles(token) {
37+
if (token.children) {
38+
for (let t of token.children) {
39+
find_titles(t)
40+
}
41+
return
42+
}
43+
44+
switch (token.type) {
45+
case 'heading_open':
46+
pending_title = ''
47+
title_level++
48+
break
49+
case 'text':
50+
if (title_level > 0) pending_title += token.content
51+
break
52+
case 'heading_close':
53+
title_level--
54+
found_title.add(slugify(pending_title))
55+
break
56+
}
57+
}
58+
for (let token of params.parsers.markdownit.tokens) {
59+
find_titles(token)
60+
}
61+
if (title_level != 0) {
62+
console.warn(
63+
"warning: checkInternalLink.js can't find title currectly, it will not check the file " +
64+
params.name,
65+
)
66+
return
67+
}
68+
69+
//console.log("found title in file", found_title)
70+
71+
function handle(token) {
72+
if (token.type == 'link_open') {
73+
const href = token.attrs[0][1]
74+
if (href.startsWith('#')) {
75+
const title = href.substr(1)
76+
if (!found_title.has(decodeURI(title))) {
77+
if (warn_only) {
78+
let file_name = relative(
79+
path.join(import.meta.dirname, '..'),
80+
params.name,
81+
)
82+
let lineno = token.lineNumber + params.frontMatterLines.length
83+
84+
if (is_in_github_action) {
85+
// github has 10 limit of annotation, it's not visible in the logs
86+
// so output the file_name and lineno in the next line
87+
console.log(
88+
`::warning file=${file_name},line=${lineno},title=Title not found::${href}\nat ${file_name}:${lineno}`,
89+
)
90+
} else {
91+
console.warn(
92+
'warning: ' +
93+
file_name +
94+
':' +
95+
lineno +
96+
': title not found: ' +
97+
href,
98+
)
99+
}
100+
} else {
101+
onError({
102+
lineNumber: token.lineNumber,
103+
detail: 'title not found: ' + href,
104+
})
105+
}
106+
}
107+
}
108+
} else {
109+
if (token.children) {
110+
for (let t of token.children) {
111+
handle(t)
112+
}
113+
}
114+
}
115+
}
116+
117+
for (let token of params.parsers.markdownit.tokens) {
118+
handle(token)
119+
}
120+
},
121+
}

0 commit comments

Comments
 (0)