Skip to content

Commit bba44fb

Browse files
authored
Merge pull request #714 from ENOVACOM/relative-path
feat: allows relative path, fixed #590
2 parents 270fa6c + ca8ae50 commit bba44fb

File tree

6 files changed

+155
-3
lines changed

6 files changed

+155
-3
lines changed

docs/configuration.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,46 @@ window.$docsify = {
150150
};
151151
```
152152

153+
## relativePath
154+
155+
- Type: `Boolean`
156+
- Default: `false`
157+
158+
If **true** links are relative to the current context.
159+
160+
For example, the directory structure is as follows:
161+
162+
```text
163+
.
164+
└── docs
165+
├── README.md
166+
├── guide.md
167+
└── zh-cn
168+
├── README.md
169+
├── guide.md
170+
└── config
171+
└── example.md
172+
```
173+
174+
With relative path **enabled** and current URL `http://domain.com/zh-cn/README`, given links will resolve to:
175+
176+
```text
177+
guide.md => http://domain.com/zh-cn/guide
178+
config/example.md => http://domain.com/zh-cn/config/example
179+
../README.md => http://domain.com/README
180+
/README.md => http://domain.com/README
181+
```
182+
183+
```js
184+
window.$docsify = {
185+
// Relative path enabled
186+
relativePath: true,
187+
188+
// Relative path disabled (default value)
189+
relativePath: false
190+
};
191+
```
192+
153193
## coverpage
154194

155195
- Type: `Boolean|String|String[]|Object`

src/core/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export default function () {
2525
formatUpdated: '',
2626
externalLinkTarget: '_blank',
2727
routerMode: 'hash',
28-
noCompileLinks: []
28+
noCompileLinks: [],
29+
relativePath: false
2930
},
3031
window.$docsify
3132
)

src/core/router/history/base.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {
33
isAbsolutePath,
44
stringifyQuery,
55
cleanPath,
6-
replaceSlug
6+
replaceSlug,
7+
resolvePath
78
} from '../util'
89
import {noop, merge} from '../../util/core'
910

@@ -73,9 +74,13 @@ export class History {
7374
if (local) {
7475
const idIndex = currentRoute.indexOf('?')
7576
path =
76-
(idIndex > 0 ? currentRoute.substr(0, idIndex) : currentRoute) + path
77+
(idIndex > 0 ? currentRoute.substring(0, idIndex) : currentRoute) + path
7778
}
7879

80+
if (this.config.relativePath && path.indexOf('/') !== 0) {
81+
const currentDir = currentRoute.substring(0, currentRoute.lastIndexOf('/') + 1)
82+
return cleanPath(resolvePath(currentDir + path))
83+
}
7984
return cleanPath('/' + path)
8085
}
8186
}

src/core/router/util.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ export const cleanPath = cached(path => {
5353
return path.replace(/^\/+/, '/').replace(/([^:])\/{2,}/g, '$1/')
5454
})
5555

56+
export const resolvePath = cached(path => {
57+
const segments = path.replace(/^\//, '').split('/')
58+
let resolved = []
59+
for (let i = 0, len = segments.length; i < len; i++) {
60+
const segment = segments[i]
61+
if (segment === '..') {
62+
resolved.pop()
63+
} else if (segment !== '.') {
64+
resolved.push(segment)
65+
}
66+
}
67+
return '/' + resolved.join('/')
68+
})
69+
5670
export function getPath(...args) {
5771
return cleanPath(args.join('/'))
5872
}

test/unit/base.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* eslint-env node, chai, mocha */
2+
require = require('esm')(module/*, options*/)
3+
const {expect} = require('chai')
4+
const {History} = require('../../src/core/router/history/base')
5+
6+
class MockHistory extends History {
7+
parse(path) {
8+
return {path}
9+
}
10+
}
11+
12+
describe('router/history/base', function () {
13+
describe('relativePath true', function () {
14+
var history
15+
16+
beforeEach(function () {
17+
history = new MockHistory({relativePath: true})
18+
})
19+
20+
it('toURL', function () {
21+
// WHEN
22+
const url = history.toURL('guide.md', {}, '/zh-ch/')
23+
24+
// THEN
25+
expect(url).equal('/zh-ch/guide')
26+
})
27+
28+
it('toURL with double dot', function () {
29+
// WHEN
30+
const url = history.toURL('../README.md', {}, '/zh-ch/')
31+
32+
// THEN
33+
expect(url).equal('/README')
34+
})
35+
36+
it('toURL child path', function () {
37+
// WHEN
38+
const url = history.toURL('config/example.md', {}, '/zh-ch/')
39+
40+
// THEN
41+
expect(url).equal('/zh-ch/config/example')
42+
})
43+
44+
it('toURL absolute path', function () {
45+
// WHEN
46+
const url = history.toURL('/README', {}, '/zh-ch/')
47+
48+
// THEN
49+
expect(url).equal('/README')
50+
})
51+
})
52+
53+
it('toURL without relative path', function () {
54+
const history = new MockHistory({relativePath: false})
55+
56+
// WHEN
57+
const url = history.toURL('README', {}, '/zh-ch/')
58+
59+
// THEN
60+
expect(url).equal('/README')
61+
})
62+
})

test/unit/util.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* eslint-env node, chai, mocha */
2+
require = require('esm')(module/*, options*/)
3+
const {expect} = require('chai')
4+
const {resolvePath} = require('../../src/core/router/util')
5+
6+
describe('router/util', function () {
7+
it('resolvePath', async function () {
8+
// WHEN
9+
const result = resolvePath('hello.md')
10+
11+
// THEN
12+
expect(result).equal('/hello.md')
13+
})
14+
15+
it('resolvePath with dot', async function () {
16+
// WHEN
17+
const result = resolvePath('./hello.md')
18+
19+
// THEN
20+
expect(result).equal('/hello.md')
21+
})
22+
23+
it('resolvePath with two dots', async function () {
24+
// WHEN
25+
const result = resolvePath('test/../hello.md')
26+
27+
// THEN
28+
expect(result).equal('/hello.md')
29+
})
30+
})

0 commit comments

Comments
 (0)