Skip to content

Commit ab3d6e6

Browse files
committed
Merge branch 'normalizeRoutePath' into route-link
2 parents c12435a + b69d3b3 commit ab3d6e6

File tree

6 files changed

+99
-26
lines changed

6 files changed

+99
-26
lines changed

packages/shared/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './isLinkExternal.js'
66
export * from './isLinkHttp.js'
77
export * from './isLinkWithProtocol.js'
88
export * from './isPlainObject.js'
9+
export * from './inferRoutePath.js'
910
export * from './normalizeRoutePath.js'
1011
export * from './omit.js'
1112
export * from './removeEndingSlash.js'
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export const inferRoutePath = (path: string): string => {
2+
// if the pathname is empty or ends with `/`, return as is
3+
if (!path || path.endsWith('/')) return path
4+
5+
// convert README.md to index.html
6+
let routePath = path.replace(/(^|\/)README.md$/i, '$1index.html')
7+
8+
// convert /foo/bar.md to /foo/bar.html
9+
if (routePath.endsWith('.md')) {
10+
routePath = routePath.substring(0, routePath.length - 3) + '.html'
11+
}
12+
// convert /foo/bar to /foo/bar.html
13+
else if (!routePath.endsWith('.html')) {
14+
routePath = routePath + '.html'
15+
}
16+
17+
// convert /foo/index.html to /foo/
18+
if (routePath.endsWith('/index.html')) {
19+
routePath = routePath.substring(0, routePath.length - 10)
20+
}
21+
22+
return routePath
23+
}

packages/shared/src/utils/isLinkWithProtocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
* Determine a link has protocol or not
33
*/
44
export const isLinkWithProtocol = (link: string): boolean =>
5-
/^[a-z][a-z0-9+.-]*:/.test(link)
5+
/^[a-z][a-z0-9+.-]*:/.test(link) || link.startsWith('//')

packages/shared/src/utils/normalizeRoutePath.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,6 @@
1-
const FAKE_HOST = 'http://.'
2-
3-
export const inferRoutePath = (path: string): string => {
4-
// if the pathname is empty or ends with `/`, return as is
5-
if (!path || path.endsWith('/')) return path
6-
7-
// convert README.md to index.html
8-
let routePath = path.replace(/(^|\/)README.md$/i, '$1index.html')
9-
10-
// convert /foo/bar.md to /foo/bar.html
11-
if (routePath.endsWith('.md')) {
12-
routePath = routePath.substring(0, routePath.length - 3) + '.html'
13-
}
14-
// convert /foo/bar to /foo/bar.html
15-
else if (!routePath.endsWith('.html')) {
16-
routePath = routePath + '.html'
17-
}
18-
19-
// convert /foo/index.html to /foo/
20-
if (routePath.endsWith('/index.html')) {
21-
routePath = routePath.substring(0, routePath.length - 10)
22-
}
1+
import { inferRoutePath } from './inferRoutePath.js'
232

24-
return routePath
25-
}
3+
const FAKE_HOST = 'http://.'
264

275
/**
286
* Normalize the given path to the final route path
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { inferRoutePath } from '../src/index.js'
3+
4+
const testCases = [
5+
// absolute index
6+
['/', '/'],
7+
['/README.md', '/'],
8+
['/readme.md', '/'],
9+
['/index.md', '/'],
10+
['/index.html', '/'],
11+
['/index', '/'],
12+
['/foo/', '/foo/'],
13+
['/foo/README.md', '/foo/'],
14+
['/foo/readme.md', '/foo/'],
15+
['/foo/index.md', '/foo/'],
16+
['/foo/index.html', '/foo/'],
17+
['/foo/index', '/foo/'],
18+
['README.md', 'index.html'],
19+
['readme.md', 'index.html'],
20+
['index.md', 'index.html'],
21+
['index.html', 'index.html'],
22+
['index', 'index.html'],
23+
24+
// absolute non-index
25+
['/foo', '/foo.html'],
26+
['/foo.md', '/foo.html'],
27+
['/foo.html', '/foo.html'],
28+
['/foo/bar', '/foo/bar.html'],
29+
['/foo/bar.md', '/foo/bar.html'],
30+
['/foo/bar.html', '/foo/bar.html'],
31+
32+
// relative index without current
33+
['foo/', 'foo/'],
34+
['foo/README.md', 'foo/'],
35+
['foo/readme.md', 'foo/'],
36+
['foo/index.md', 'foo/'],
37+
['foo/index.html', 'foo/'],
38+
['foo/index', 'foo/'],
39+
40+
// relative non index without current
41+
['foo', 'foo.html'],
42+
['foo.md', 'foo.html'],
43+
['foo.html', 'foo.html'],
44+
['foo/bar', 'foo/bar.html'],
45+
['foo/bar.md', 'foo/bar.html'],
46+
['foo/bar.html', 'foo/bar.html'],
47+
48+
// unexpected corner cases
49+
['', ''],
50+
['.md', '.html'],
51+
['foo/.md', 'foo/.html'],
52+
['/.md', '/.html'],
53+
['/foo/.md', '/foo/.html'],
54+
]
55+
56+
describe('should normalize clean paths correctly', () => {
57+
testCases.forEach(([path, expected]) =>
58+
it(`"${path}" -> "${expected}"`, () => {
59+
expect(inferRoutePath(path)).toBe(expected)
60+
}),
61+
)
62+
})

packages/shared/tests/isLinkWithProtocol.spec.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,25 @@ import { expect, it } from 'vitest'
22
import { isLinkWithProtocol } from '../src/index.js'
33

44
const testCases: [string, ReturnType<typeof isLinkWithProtocol>][] = [
5+
// with protocol
56
['ftp://foobar.com', true],
67
['ms-windows-store://home', true],
78
['mailto:foobar', true],
89
['tel:foobar', true],
910
['https://foobar.com', true],
1011
['http://foobar.com', true],
12+
['//foobar.com', true],
13+
14+
// hostname
1115
['foobar.com', false],
16+
17+
// pathname
1218
['/foo/bar', false],
19+
20+
// relative path
1321
['../foo/bar', false],
14-
['//foobar.com', false],
22+
['./foo/bar', false],
23+
['foo/bar', false],
1524
]
1625

1726
testCases.forEach(([source, expected]) => {

0 commit comments

Comments
 (0)