Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions __tests__/unit/node/markdown/plugins/link.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ describe('node/markdown/plugins/link', () => {
const env: {
cleanUrls: boolean
links?: string[]
linkLines?: number[]
linkMetadatas?: { rawLink: string; line?: number }[]
} = { cleanUrls: false }

await md.renderAsync('Intro\n\n[Missing](./missing.md)\n', env)

expect(env.links).toEqual(['./missing'])
expect(env.linkLines).toEqual([3])
expect(env.links).toEqual(['./missing.html'])
expect(env.linkMetadatas).toEqual([{ rawLink: './missing.md', line: 3 }])
})
})
36 changes: 34 additions & 2 deletions __tests__/unit/node/markdownToVue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,38 @@ describe('node/markdownToVue', () => {
}
})

test('records link path as written for dead links', async () => {
root = await mkdtemp(path.join(tmpdir(), 'vitepress-link-path-'))

const file = path.join(root, 'index.md')
const src = [
'[a](./a.md)',
'[b](./b#hash)',
'[c](./中文.md)',
'[d](/d)'
].join('\n\n')
await writeFile(file, src)

const siteConfig = await resolveConfig(root, 'build', 'production')
const render = await createMarkdownToVueRenderFn(
siteConfig.srcDir,
{ cache: false },
'/',
false,
false,
siteConfig
)

const result = await render(src, file)

expect(result.deadLinks).toEqual([
{ url: './a.md', file, line: 1 },
{ url: './b#hash', file, line: 3 },
{ url: './中文.md', file, line: 5 },
{ url: '/d', file, line: 7 }
])
})

test('records source line numbers for dead links', async () => {
root = await mkdtemp(path.join(tmpdir(), 'vitepress-dead-link-'))

Expand All @@ -34,7 +66,7 @@ describe('node/markdownToVue', () => {
const result = await render(src, file)

expect(result.deadLinks).toContainEqual({
url: './missing',
url: './missing.md',
file,
line: 5
})
Expand All @@ -61,7 +93,7 @@ describe('node/markdownToVue', () => {
const result = await render(src, file)

expect(result.deadLinks).toContainEqual({
url: './missing',
url: './missing.md',
file,
line: 8
})
Expand Down
23 changes: 14 additions & 9 deletions src/node/markdown/plugins/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const linkPlugin = (
token.attrGet('class') !== 'header-anchor' // header anchors are already normalized
) {
const hrefAttr = token.attrs![hrefIndex]
const rawUrl = decodeURI(hrefAttr[1])
let [url, frag] = hrefAttr[1].split(':~:', 2)
hrefAttr[1] = url
if (isExternal(url)) {
Expand All @@ -55,7 +56,7 @@ export const linkPlugin = (
})
// catch localhost links as dead link
if (url.replace(EXTERNAL_URL_RE, '').startsWith('//localhost:')) {
pushLink(url, env, token.meta?.vpLine)
pushLink(url, rawUrl, env, token.meta?.vpLine)
}
hrefAttr[1] = url
} else {
Expand Down Expand Up @@ -95,6 +96,7 @@ export const linkPlugin = (
line?: number
) {
let url = hrefAttr[1]
const rawUrl = decodeURI(url)

const indexMatch = url.match(indexRE)
if (indexMatch) {
Expand Down Expand Up @@ -124,7 +126,7 @@ export const linkPlugin = (
}

// export it for existence check
pushLink(url.replace(/\.html$/, ''), env, line)
pushLink(url, rawUrl, env, line)
Comment thread
brc-dd marked this conversation as resolved.

// markdown-it encodes the uri
hrefAttr[1] = decodeURI(url)
Expand All @@ -134,12 +136,15 @@ export const linkPlugin = (
return str ? encodeURI('#' + slugify(decodeURI(str).slice(1))) : ''
}

function pushLink(link: string, env: MarkdownEnv, line?: number) {
const links = env.links || (env.links = [])
links.push(link)
if (line != null) {
const linkLines = env.linkLines || (env.linkLines = [])
linkLines[links.length - 1] = line
}
function pushLink(
link: string,
rawLink: string,
env: MarkdownEnv,
line?: number
) {
env.links ??= []
env.links.push(link)
env.linkMetadatas ??= []
env.linkMetadatas.push({ rawLink, line })
}
}
9 changes: 4 additions & 5 deletions src/node/markdownToVue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ export async function createMarkdownToVueRenderFn(
frontmatter = {},
headers = [],
includes = [],
linkLines = [],
links = [],
linkMetadatas = [],
sfcBlocks,
title = ''
} = env
Expand Down Expand Up @@ -215,10 +215,9 @@ export async function createMarkdownToVueRenderFn(
const dir = path.dirname(file)
for (const [index, rawUrl] of links.entries()) {
let url = rawUrl
const metadata = linkMetadatas[index]
const line =
linkLines[index] == null
? undefined
: linkLines[index] + contentLineOffset
metadata?.line != null ? metadata.line + contentLineOffset : undefined
const { pathname } = new URL(url, 'http://a.com')
if (!treatAsHtml(pathname)) continue

Expand Down Expand Up @@ -250,7 +249,7 @@ export async function createMarkdownToVueRenderFn(
) &&
!shouldIgnoreDeadLink(url)
) {
recordDeadLink(url, line)
recordDeadLink(metadata.rawLink, line)
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions types/shared.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,11 @@ export interface MarkdownEnv {
*/
links?: string[]
/**
* The line numbers at which each of `links` appears in the source.
* The metadata of the links collected from the page.
* - `rawLink`: The url as written
* - `line`: The line number at which the link appears in the source.
*/
linkLines?: number[]
linkMetadatas?: { rawLink: string; line?: number }[]
/**
* The absolute paths of the files inlined via `<!--@include-->` and
* imported via `<<<` code snippets, used for watch invalidation.
Expand Down