/
link.ts
94 lines (83 loc) · 2.77 KB
/
link.ts
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
// markdown-it plugin for:
// 1. adding target="_blank" to external links
// 2. normalize internal links to end with `.html`
import MarkdownIt from 'markdown-it'
import { MarkdownRenderer } from '../markdown'
import { URL } from 'url'
import { EXTERNAL_URL_RE } from '../../shared'
const indexRE = /(^|.*\/)index.md(#?.*)$/i
export const linkPlugin = (
md: MarkdownIt,
externalAttrs: Record<string, string>,
base: string
) => {
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const token = tokens[idx]
const hrefIndex = token.attrIndex('href')
if (hrefIndex >= 0) {
const hrefAttr = token.attrs![hrefIndex]
const url = hrefAttr[1]
const isExternal = EXTERNAL_URL_RE.test(url)
if (isExternal) {
Object.entries(externalAttrs).forEach(([key, val]) => {
token.attrSet(key, val)
})
// catch localhost links as dead link
if (url.replace(EXTERNAL_URL_RE, '').startsWith('//localhost:')) {
pushLink(url)
}
} else if (
// internal anchor links
!url.startsWith('#') &&
// mail links
!url.startsWith('mailto:')
) {
normalizeHref(hrefAttr)
}
// encode vite-specific replace strings in case they appear in URLs
// this also excludes them from build-time replacements (which injects
// <wbr/> and will break URLs)
hrefAttr[1] = hrefAttr[1]
.replace(/\bimport\.meta/g, 'import%2Emeta')
.replace(/\bprocess\.env/g, 'process%2Eenv')
}
return self.renderToken(tokens, idx, options)
}
function normalizeHref(hrefAttr: [string, string]) {
let url = hrefAttr[1]
const indexMatch = url.match(indexRE)
if (indexMatch) {
const [, path, hash] = indexMatch
url = path + hash
} else {
let cleanUrl = url.replace(/\#.*$/, '').replace(/\?.*$/, '')
// .md -> .html
if (cleanUrl.endsWith('.md')) {
cleanUrl = cleanUrl.replace(/\.md$/, '.html')
}
// ./foo -> ./foo.html
if (!cleanUrl.endsWith('.html') && !cleanUrl.endsWith('/')) {
cleanUrl += '.html'
}
const parsed = new URL(url, 'http://a.com')
url = cleanUrl + parsed.search + parsed.hash
}
// ensure leading . for relative paths
if (!url.startsWith('/') && !/^\.\//.test(url)) {
url = './' + url
}
// export it for existence check
pushLink(url.replace(/\.html$/, ''))
// append base to internal (non-relative) urls
if (url.startsWith('/')) {
url = `${base}${url}`.replace(/\/+/g, '/')
}
// markdown-it encodes the uri
hrefAttr[1] = decodeURI(url)
}
function pushLink(link: string) {
const data = (md as MarkdownRenderer).__data
const links = data.links || (data.links = [])
links.push(link)
}
}