Skip to content

Commit b7d71be

Browse files
feat(markdown): support relative image links without ./ (#1103)
BREAKING CHANGE: for markdown image syntax `![alt](path)`, paths without `./` or `/` or protocol will be treated as relative links --------- Co-authored-by: meteorlxy <meteor.lxy@foxmail.com>
1 parent 031563e commit b7d71be

File tree

5 files changed

+68
-50
lines changed

5 files changed

+68
-50
lines changed

docs/guide/assets.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ You can reference any assets using relative URLs in your Markdown content:
88
![An image](./image.png)
99
```
1010

11+
or
12+
13+
```md
14+
![An image](image.png)
15+
```
16+
1117
This is generally the suggested way to import images, as users usually place images near the Markdown file that references them.
1218

1319
## Public Files
@@ -86,8 +92,10 @@ Although it is not a common usage, you can reference images from dependent packa
8692
npm install -D package-name
8793
```
8894

95+
Since markdown image syntax regards image links as relative paths by default, you need to use `<img>` tag:
96+
8997
```md
90-
![Image from dependency](package-name/image.png)
98+
<img src="package-name/image.png" alt="Image from dependency">
9199
```
92100

93101
The path aliases that set in config file are also supported:
@@ -105,7 +113,7 @@ export default {
105113
```
106114

107115
```md
108-
![Image from path alias](@alias/image.png)
116+
<img src="@alias/image.png" alt="Image from path alias">
109117
```
110118

111119
::: tip

docs/zh/guide/assets.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
![图片](./image.png)
99
```
1010

11+
12+
13+
```md
14+
![图片](image.png)
15+
```
16+
1117
一般情况下,我们推荐你使用这种方式来引用图片,因为人们通常会把图片放在引用它的 Markdown 文件附近。
1218

1319
## Public 文件
@@ -86,8 +92,10 @@ const logoPath = ref('/images/hero.png')
8692
npm install -D package-name
8793
```
8894

95+
由于 Markdown 会默认将图片链接视为相对链接,你需要使用 `<img>` 标签:
96+
8997
```md
90-
![来自依赖包的图片](package-name/image.png)
98+
<img src="package-name/image.png" alt="来自依赖包的图片">
9199
```
92100

93101
在配置文件中设置的路径别名也同样支持:
@@ -105,7 +113,7 @@ export default {
105113
```
106114

107115
```md
108-
![来自路径别名的图片](@alias/image.png)
116+
<img src="@alias/image.png" alt="来自路径别名的图片">
109117
```
110118

111119
::: tip

packages/markdown/src/plugins/assetsPlugin/assetsPlugin.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export const assetsPlugin: PluginWithOptions<AssetsPluginOptions> = (
4646
`${prefix}${quote}${resolveLink(
4747
src.trim(),
4848
relativePathPrefix,
49-
env
49+
env,
50+
true
5051
)}${quote}`
5152
)
5253
// handle srcset
@@ -64,7 +65,8 @@ export const assetsPlugin: PluginWithOptions<AssetsPluginOptions> = (
6465
`${resolveLink(
6566
url.trim(),
6667
relativePathPrefix,
67-
env
68+
env,
69+
true
6870
)}${descriptor.replace(/[ \n]+/g, ' ').trimEnd()}`
6971
)
7072
)

packages/markdown/src/plugins/assetsPlugin/resolveLink.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@ import type { MarkdownEnv } from '../../types.js'
55
export const resolveLink = (
66
link: string,
77
relativePathPrefix: string,
8-
env: MarkdownEnv
8+
env: MarkdownEnv,
9+
strict = false
910
): string => {
1011
// decode link to ensure bundler can find the file correctly
1112
let resolvedLink = decode(link)
1213

14+
// check if the link is relative path
15+
const isRelativePath = strict
16+
? // in strict mode, only link that starts with `./` or `../` is considered as relative path
17+
/^\.{1,2}\//.test(link)
18+
: // in non-strict mode, link that does not start with `/` and does not have protocol is considered as relative path
19+
!link.startsWith('/') && !/[A-z]+:\/\//.test(link)
20+
1321
// if the link is relative path, and the `env.filePathRelative` exists
1422
// add `@source` alias to the link
15-
if (/^\.{1,2}\//.test(link) && env.filePathRelative) {
23+
if (isRelativePath && env.filePathRelative) {
1624
resolvedLink = `${relativePathPrefix}/${path.join(
1725
path.dirname(env.filePathRelative),
1826
resolvedLink

packages/markdown/tests/plugins/assetsPlugin.spec.ts

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,18 @@ describe('@vuepress/markdown > plugins > assetsPlugin', () => {
1515
'![out](../../out.png)',
1616
'![汉字](./汉字.png)',
1717
'![100%](./100%.png)',
18-
// aliases
19-
'![alias](@alias/foo.png)',
20-
'![汉字](@alias/汉字.png)',
21-
'![100%](@alias/100%.png)',
22-
// webpack legacy aliases
23-
'![~alias](~@alias/foo.png)',
24-
'![~汉字](~@alias/汉字.png)',
25-
'![~100%](~@alias/100%.png)',
2618
// absolute paths
2719
'![absolute](/absolute.png)',
2820
'![absolute-foo](/foo/absolute.png)',
2921
// no-prefix paths
3022
'![no-prefix](no-prefix.png)',
3123
'![no-prefix-foo](foo/no-prefix.png)',
24+
'![alias](@alias/foo.png)',
25+
'![汉字](@alias/汉字.png)',
26+
'![100%](@alias/100%.png)',
27+
'![~alias](~@alias/foo.png)',
28+
'![~汉字](~@alias/汉字.png)',
29+
'![~100%](~@alias/100%.png)',
3230
// keep as is
3331
'![url](http://foobar.com/icon.png)',
3432
'![empty]()',
@@ -60,27 +58,25 @@ describe('@vuepress/markdown > plugins > assetsPlugin', () => {
6058
'<img src="@source/../out.png" alt="out">',
6159
'<img src="@source/sub/汉字.png" alt="汉字">',
6260
'<img src="@source/sub/100%.png" alt="100%">',
63-
// aliases
64-
'<img src="@alias/foo.png" alt="alias">',
65-
'<img src="@alias/汉字.png" alt="汉字">',
66-
'<img src="@alias/100%.png" alt="100%">',
67-
// webpack legacy aliases
68-
'<img src="~@alias/foo.png" alt="~alias">',
69-
'<img src="~@alias/汉字.png" alt="~汉字">',
70-
'<img src="~@alias/100%.png" alt="~100%">',
7161
// absolute paths
7262
'<img src="/absolute.png" alt="absolute">',
7363
'<img src="/foo/absolute.png" alt="absolute-foo">',
7464
// no-prefix paths
75-
'<img src="no-prefix.png" alt="no-prefix">',
76-
'<img src="foo/no-prefix.png" alt="no-prefix-foo">',
65+
'<img src="@source/sub/no-prefix.png" alt="no-prefix">',
66+
'<img src="@source/sub/foo/no-prefix.png" alt="no-prefix-foo">',
67+
'<img src="@source/sub/@alias/foo.png" alt="alias">',
68+
'<img src="@source/sub/@alias/汉字.png" alt="汉字">',
69+
'<img src="@source/sub/@alias/100%.png" alt="100%">',
70+
'<img src="@source/sub/~@alias/foo.png" alt="~alias">',
71+
'<img src="@source/sub/~@alias/汉字.png" alt="~汉字">',
72+
'<img src="@source/sub/~@alias/100%.png" alt="~100%">',
7773
// keep as is
7874
'<img src="http://foobar.com/icon.png" alt="url">',
7975
'<img src="" alt="empty">',
8076
// invalid paths
81-
'<img src=".../invalid.png" alt="invalid">',
82-
'<img src=".../汉字.png" alt="汉字">',
83-
'<img src=".../100%.png" alt="100%">',
77+
'<img src="@source/sub/.../invalid.png" alt="invalid">',
78+
'<img src="@source/sub/.../汉字.png" alt="汉字">',
79+
'<img src="@source/sub/.../100%.png" alt="100%">',
8480
],
8581
},
8682
{
@@ -101,27 +97,25 @@ describe('@vuepress/markdown > plugins > assetsPlugin', () => {
10197
'<img src="@foo/../out.png" alt="out">',
10298
'<img src="@foo/sub/汉字.png" alt="汉字">',
10399
'<img src="@foo/sub/100%.png" alt="100%">',
104-
// aliases
105-
'<img src="@alias/foo.png" alt="alias">',
106-
'<img src="@alias/汉字.png" alt="汉字">',
107-
'<img src="@alias/100%.png" alt="100%">',
108-
// webpack legacy aliases
109-
'<img src="~@alias/foo.png" alt="~alias">',
110-
'<img src="~@alias/汉字.png" alt="~汉字">',
111-
'<img src="~@alias/100%.png" alt="~100%">',
112100
// absolute paths
113101
'<img src="/absolute.png" alt="absolute">',
114102
'<img src="/foo/absolute.png" alt="absolute-foo">',
115103
// no-prefix paths
116-
'<img src="no-prefix.png" alt="no-prefix">',
117-
'<img src="foo/no-prefix.png" alt="no-prefix-foo">',
104+
'<img src="@foo/sub/no-prefix.png" alt="no-prefix">',
105+
'<img src="@foo/sub/foo/no-prefix.png" alt="no-prefix-foo">',
106+
'<img src="@foo/sub/@alias/foo.png" alt="alias">',
107+
'<img src="@foo/sub/@alias/汉字.png" alt="汉字">',
108+
'<img src="@foo/sub/@alias/100%.png" alt="100%">',
109+
'<img src="@foo/sub/~@alias/foo.png" alt="~alias">',
110+
'<img src="@foo/sub/~@alias/汉字.png" alt="~汉字">',
111+
'<img src="@foo/sub/~@alias/100%.png" alt="~100%">',
118112
// keep as is
119113
'<img src="http://foobar.com/icon.png" alt="url">',
120114
'<img src="" alt="empty">',
121115
// invalid paths
122-
'<img src=".../invalid.png" alt="invalid">',
123-
'<img src=".../汉字.png" alt="汉字">',
124-
'<img src=".../100%.png" alt="100%">',
116+
'<img src="@foo/sub/.../invalid.png" alt="invalid">',
117+
'<img src="@foo/sub/.../汉字.png" alt="汉字">',
118+
'<img src="@foo/sub/.../100%.png" alt="100%">',
125119
],
126120
},
127121
{
@@ -139,20 +133,18 @@ describe('@vuepress/markdown > plugins > assetsPlugin', () => {
139133
'<img src="../../out.png" alt="out">',
140134
'<img src="./汉字.png" alt="汉字">',
141135
'<img src="./100%.png" alt="100%">',
142-
// aliases
143-
'<img src="@alias/foo.png" alt="alias">',
144-
'<img src="@alias/汉字.png" alt="汉字">',
145-
'<img src="@alias/100%.png" alt="100%">',
146-
// webpack legacy aliases
147-
'<img src="~@alias/foo.png" alt="~alias">',
148-
'<img src="~@alias/汉字.png" alt="~汉字">',
149-
'<img src="~@alias/100%.png" alt="~100%">',
150136
// absolute paths
151137
'<img src="/absolute.png" alt="absolute">',
152138
'<img src="/foo/absolute.png" alt="absolute-foo">',
153139
// no-prefix paths
154140
'<img src="no-prefix.png" alt="no-prefix">',
155141
'<img src="foo/no-prefix.png" alt="no-prefix-foo">',
142+
'<img src="@alias/foo.png" alt="alias">',
143+
'<img src="@alias/汉字.png" alt="汉字">',
144+
'<img src="@alias/100%.png" alt="100%">',
145+
'<img src="~@alias/foo.png" alt="~alias">',
146+
'<img src="~@alias/汉字.png" alt="~汉字">',
147+
'<img src="~@alias/100%.png" alt="~100%">',
156148
// keep as is
157149
'<img src="http://foobar.com/icon.png" alt="url">',
158150
'<img src="" alt="empty">',

0 commit comments

Comments
 (0)