Skip to content

Commit

Permalink
feat(markdown): prepend base to absolute links to markdown files (close
Browse files Browse the repository at this point in the history
#653)

BREAKING CHANGE: now absolute links to markdown files will be treated as internal links, and do not need to prepend `base` manually
  • Loading branch information
meteorlxy committed May 14, 2022
1 parent ad8b5a8 commit e4155a0
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 23 deletions.
3 changes: 1 addition & 2 deletions docs/guide/markdown.md
Expand Up @@ -88,11 +88,10 @@ Take our documentation source files as an example:

**Suggestion**

Try to use relative paths instead of absolute paths for internal links.
Try to use relative paths instead of absolute paths for internal links to markdown files.

- Relative paths are a valid links to the target files, and they can navigate correctly when browsing the source files in your editor or repository.
- Relative paths are consistent in different locales, so you don't need to change the locale path when translating your content.
- When using absolute paths, if the [base](../reference/config.md#base) of your site is not `"/"`, you will need to prepend the `base` manually or use [base helper](./assets.md#base-helper).

::: tip
This links extension is supported by our built-in plugin.
Expand Down
3 changes: 1 addition & 2 deletions docs/zh/guide/markdown.md
Expand Up @@ -89,11 +89,10 @@ VuePress 会使用 [markdown-it](https://github.com/markdown-it/markdown-it) 来

**建议**

对于内部链接,尽可能使用相对路径而不是绝对路径。
对于指向内部 Markdown 文件的链接,尽可能使用相对路径而不是绝对路径。

- 相对路径是指向目标文件的有效链接,在你的编辑器或者代码仓库中浏览源文件时也可以正确跳转。
- 相对路径在不同 locales 下都是一致的,这样在翻译你的内容时就不需要修改 locale 路径了。
- 在使用绝对路径时,如果你站点的 [base](../reference/config.md#base) 不是 `"/"`,你需要手动添加 `base` 或者使用 [base helper](./assets.md#base-helper)

::: tip
链接扩展是由我们的内置插件支持的。
Expand Down
37 changes: 33 additions & 4 deletions packages/@vuepress/markdown/__tests__/plugins/linksPlugin.spec.ts
Expand Up @@ -9,9 +9,14 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {
'[https-github](https://github.com)',
'[http-github](http://github.com)',
'[github](//github.com)',
'[https-github-md](https://github.com/foo/bar/blob/main/README.md)',
'[http-github-md](http://github.com/foo/bar/blob/main/README.md)',
'[github-md](//github.com/foo/bar/blob/main/README.md)',
// autolink
'<https://github.com>',
'<http://github.com>',
'<https://github.com/foo/bar/blob/main/README.md>',
'<http://github.com/foo/bar/blob/main/README.md>',
].join('\n\n')

it('should render default attrs', () => {
Expand All @@ -25,8 +30,13 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {
'<a href="https://github.com" target="_blank" rel="noopener noreferrer">https-github</a>',
'<a href="http://github.com" target="_blank" rel="noopener noreferrer">http-github</a>',
'<a href="//github.com" target="_blank" rel="noopener noreferrer">github</a>',
'<a href="https://github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer">https-github-md</a>',
'<a href="http://github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer">http-github-md</a>',
'<a href="//github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer">github-md</a>',
'<a href="https://github.com" target="_blank" rel="noopener noreferrer">https://github.com</a>',
'<a href="http://github.com" target="_blank" rel="noopener noreferrer">http://github.com</a>',
'<a href="https://github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer">https://github.com/foo/bar/blob/main/README.md</a>',
'<a href="http://github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer">http://github.com/foo/bar/blob/main/README.md</a>',
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n'
Expand All @@ -50,8 +60,13 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {
'<a href="https://github.com" target="_blank" rel="noopener noreferrer" foo="bar">https-github</a>',
'<a href="http://github.com" target="_blank" rel="noopener noreferrer" foo="bar">http-github</a>',
'<a href="//github.com" target="_blank" rel="noopener noreferrer" foo="bar">github</a>',
'<a href="https://github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer" foo="bar">https-github-md</a>',
'<a href="http://github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer" foo="bar">http-github-md</a>',
'<a href="//github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer" foo="bar">github-md</a>',
'<a href="https://github.com" target="_blank" rel="noopener noreferrer" foo="bar">https://github.com</a>',
'<a href="http://github.com" target="_blank" rel="noopener noreferrer" foo="bar">http://github.com</a>',
'<a href="https://github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer" foo="bar">https://github.com/foo/bar/blob/main/README.md</a>',
'<a href="http://github.com/foo/bar/blob/main/README.md" target="_blank" rel="noopener noreferrer" foo="bar">http://github.com/foo/bar/blob/main/README.md</a>',
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n'
Expand All @@ -74,8 +89,13 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {
'<a href="https://github.com" target="_blank" rel="foobar">https-github</a>',
'<a href="http://github.com" target="_blank" rel="foobar">http-github</a>',
'<a href="//github.com" target="_blank" rel="foobar">github</a>',
'<a href="https://github.com/foo/bar/blob/main/README.md" target="_blank" rel="foobar">https-github-md</a>',
'<a href="http://github.com/foo/bar/blob/main/README.md" target="_blank" rel="foobar">http-github-md</a>',
'<a href="//github.com/foo/bar/blob/main/README.md" target="_blank" rel="foobar">github-md</a>',
'<a href="https://github.com" target="_blank" rel="foobar">https://github.com</a>',
'<a href="http://github.com" target="_blank" rel="foobar">http://github.com</a>',
'<a href="https://github.com/foo/bar/blob/main/README.md" target="_blank" rel="foobar">https://github.com/foo/bar/blob/main/README.md</a>',
'<a href="http://github.com/foo/bar/blob/main/README.md" target="_blank" rel="foobar">http://github.com/foo/bar/blob/main/README.md</a>',
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n'
Expand All @@ -86,8 +106,9 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

describe('absolute links', () => {
const source = [
'[md](/path/to/index.md)',
'[html](/path/to/index.html)',
'[pdf](/path/to/index.pdf)',
'[png](/path/to/index.png)',
].join('\n\n')

it('should render default attrs', () => {
Expand All @@ -100,8 +121,9 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

expect(rendered).toEqual(
[
'<a href="/path/to/index.md" target="_blank" rel="noopener noreferrer">md</a>',
'<a href="/path/to/index.html" target="_blank" rel="noopener noreferrer">html</a>',
'<a href="/path/to/index.pdf" target="_blank" rel="noopener noreferrer">pdf</a>',
'<a href="/path/to/index.png" target="_blank" rel="noopener noreferrer">png</a>',
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n'
Expand Down Expand Up @@ -774,7 +796,8 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

describe('absolute links', () => {
const source = [
'[md](/base/path/to/index.md)',
'[md](/path/to/index.md)',
'[md-with-redundant-base](/base/path/to/index.md)',
'[html](/base/path/to/index.html)',
].join('\n\n')

Expand All @@ -789,6 +812,7 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {
expect(rendered).toEqual(
[
'<RouterLink to="/path/to/">md</RouterLink>',
'<RouterLink to="/base/path/to/">md-with-redundant-base</RouterLink>',
'<RouterLink to="/path/to/index.html">html</RouterLink>',
]
.map((a) => `<p>${a}</p>`)
Expand All @@ -797,10 +821,15 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

expect(env.links).toEqual([
{
raw: '/base/path/to/index.md',
raw: '/path/to/index.md',
relative: 'path/to/index.md',
absolute: '/base/path/to/index.md',
},
{
raw: '/base/path/to/index.md',
relative: 'base/path/to/index.md',
absolute: '/base/base/path/to/index.md',
},
{
raw: '/base/path/to/index.html',
relative: 'path/to/index.html',
Expand Down
@@ -1,3 +1,4 @@
import { removeLeadingSlash } from '@vuepress/shared'
import { path } from '@vuepress/utils'

/**
Expand All @@ -11,13 +12,25 @@ export const resolvePaths = (
absolutePath: string
relativePath: string
} => {
let relativePath: string
let absolutePath: string
let relativePath: string

if (rawPath.startsWith('/')) {
// if raw path is absolute
absolutePath = rawPath
relativePath = path.relative(base, absolutePath)

if (rawPath.endsWith('.md')) {
// if raw path is a link to markdown file

// prepend `base` to the link
absolutePath = path.join(base, rawPath)
relativePath = removeLeadingSlash(rawPath)
} else {
// if raw path is a link to other kind of file

// keep the link as is
absolutePath = rawPath
relativePath = path.relative(base, absolutePath)
}
} else {
// if raw path is relative
if (filePathRelative) {
Expand Down
43 changes: 33 additions & 10 deletions packages/@vuepress/shared/__tests__/utils/isLinkExternal.spec.ts
Expand Up @@ -4,22 +4,45 @@ const testCases: [
Parameters<typeof isLinkExternal>,
ReturnType<typeof isLinkExternal>
][] = [
// with default base `/`
// http & ftp links
[['https://foobar.com'], true],
[['https://foobar.com', '/base/'], true],
[['http://foobar.com'], true],
[['http://foobar.com', '/base/'], true],
[['//foobar.com'], true],
[['foobar.com'], false],
[['/foo/bar'], false],
[['foo/bar'], false],
[['../foo/bar'], false],
[['//foobar.com', '/base/'], true],
[['ftp://foobar.com'], true],
[['ftp://foobar.com', '/base/'], true],
[['https://foobar.com/base/README.md'], true],
[['https://foobar.com/base/README.md', '/base/'], true],
[['http://foobar.com/base/README.md'], true],
[['http://foobar.com/base/README.md', '/base/'], true],
[['//foobar.com/base/README.md'], true],
[['//foobar.com/base/README.md', '/base/'], true],
[['ftp://foobar.com/base/README.md'], true],
[['ftp://foobar.com/base/README.md', '/base/'], true],

// with base `/base/`
[['/foo/bar', '/base/'], true],
[['foo/bar', '/base/'], false],
[['../foo/bar', '/base/'], false],
[['/base/foo/bar', '/base/'], false],
// links with other protocols
[['mailto:foobar', '/base/'], false],
[['tel:foobar', '/base/'], false],

// absolute links
[['/foo/bar'], false],
[['/foo/bar', '/base/'], true],
[['/foo/bar', '/foo/'], false],
[['/foo/bar/baz.md'], false],
[['/foo/bar/baz.md', '/base/'], false],
[['/foo/bar/baz.md', '/foo/'], false],

// relative links
[['foobar.com'], false],
[['foobar.com', '/base/'], false],
[['foo/bar'], false],
[['foo/bar', '/base/'], false],
[['foo/bar', '/foo/'], false],
[['foo/bar/baz.md'], false],
[['foo/bar/baz.md', '/base/'], false],
[['foo/bar/baz.md', '/foo/'], false],
]

describe('shared > isLinkExternal', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/@vuepress/shared/src/utils/isLinkExternal.ts
Expand Up @@ -10,8 +10,8 @@ export const isLinkExternal = (link: string, base = '/'): boolean => {
return true
}

// absolute link that does not start with `base`
if (link.startsWith('/') && !link.startsWith(base)) {
// absolute link that does not start with `base` and does not end with `.md`
if (link.startsWith('/') && !link.startsWith(base) && !link.endsWith('.md')) {
return true
}

Expand Down

0 comments on commit e4155a0

Please sign in to comment.