Skip to content

Commit c60ab58

Browse files
authored
fix: ambiguous relative link problem in md (#1491)
1 parent 96040d6 commit c60ab58

File tree

4 files changed

+49
-14
lines changed

4 files changed

+49
-14
lines changed

src/features/compile/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default (api: IApi) => {
4242
resolve: api.config.resolve,
4343
extraRemarkPlugins: api.config.extraRemarkPlugins,
4444
extraRehypePlugins: api.config.extraRehypePlugins,
45-
routers: api.appData.routes,
45+
routes: api.appData.routes,
4646
};
4747

4848
memo.module

src/features/tabs.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export function getTabKeyFromFile(file: string) {
2121
return file.match(/\$tab-([^.]+)/)![1];
2222
}
2323

24+
export function getHostForTabRouteFile(file: string) {
25+
return file.replace(/\$tab-[^.]+\./, '');
26+
}
27+
2428
/**
2529
* plugin for add conventional tab and plugin tab into page content
2630
*/

src/loaders/markdown/transformer/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export interface IMdTransformerOptions {
6161
resolve: IDumiConfig['resolve'];
6262
extraRemarkPlugins?: IDumiConfig['extraRemarkPlugins'];
6363
extraRehypePlugins?: IDumiConfig['extraRehypePlugins'];
64-
routers: Record<string, IRoute>;
64+
routes: Record<string, IRoute>;
6565
}
6666

6767
export interface IMdTransformerResult {
@@ -147,7 +147,7 @@ export default async (raw: string, opts: IMdTransformerOptions) => {
147147
.use(rehypeSlug)
148148
.use(rehypeLink, {
149149
fileAbsPath: opts.fileAbsPath,
150-
routers: opts.routers,
150+
routes: opts.routes,
151151
})
152152
.use(rehypeAutolinkHeadings)
153153
.use(rehypeIsolation)

src/loaders/markdown/transformer/rehypeLink.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import { getHostForTabRouteFile } from '@/features/tabs';
12
import type { Root } from 'hast';
23
import path from 'path';
3-
import { lodash, winPath } from 'umi/plugin-utils';
4+
import { lodash, logger, winPath } from 'umi/plugin-utils';
45
import type { Transformer } from 'unified';
56
import url from 'url';
67
import type { IMdTransformerOptions } from '.';
@@ -13,10 +14,7 @@ let SKIP: typeof import('unist-util-visit').SKIP;
1314
({ visit, SKIP } = await import('unist-util-visit'));
1415
})();
1516

16-
type IRehypeLinkOptions = Pick<
17-
IMdTransformerOptions,
18-
'fileAbsPath' | 'routers'
19-
>;
17+
type IRehypeLinkOptions = Pick<IMdTransformerOptions, 'fileAbsPath' | 'routes'>;
2018

2119
export default function rehypeLink(
2220
opts: IRehypeLinkOptions,
@@ -26,22 +24,55 @@ export default function rehypeLink(
2624
if (node.tagName === 'a' && typeof node.properties?.href === 'string') {
2725
const href = node.properties.href;
2826
const parsedUrl = url.parse(href);
27+
const hostAbsPath = getHostForTabRouteFile(opts.fileAbsPath);
2928

3029
// handle internal link
3130
if (parsedUrl.hostname) return SKIP;
3231

33-
// handle markdown link
3432
if (/\.md$/i.test(parsedUrl.pathname!)) {
35-
const { routers } = opts;
33+
// handle markdown link
34+
const { routes } = opts;
3635
const absPath = winPath(
37-
path.resolve(opts.fileAbsPath, '..', parsedUrl.pathname!),
36+
path.resolve(hostAbsPath, '..', parsedUrl.pathname!),
3837
);
3938

40-
Object.keys(routers).forEach((key) => {
41-
if (routers[key].file === absPath) {
42-
parsedUrl.pathname = routers[key].absPath;
39+
Object.keys(routes).forEach((key) => {
40+
if (routes[key].file === absPath) {
41+
parsedUrl.pathname = routes[key].absPath;
4342
}
4443
});
44+
} else if (
45+
/^\.?\.\//.test(parsedUrl.pathname!) ||
46+
/^(\w+:)?\/\//.test(parsedUrl.pathname!)
47+
) {
48+
// handle relative link
49+
// transform relative link to absolute link
50+
// because react-router@6 and HTML href are different in processing relative link
51+
// e.g. in /a page, <Link to="./b">b</Link> will be resolved to /a/b in react-router@6
52+
// but will be resolved to /b in <a href="./b">b</a>
53+
const routes = Object.values(opts.routes);
54+
const basePath = routes.find(
55+
(route) => route.file === hostAbsPath,
56+
)!.absPath;
57+
const htmlTargetPath = url.resolve(basePath, parsedUrl.pathname!);
58+
const rr6TargetPath = winPath(
59+
path.resolve(basePath, parsedUrl.pathname!),
60+
);
61+
62+
// use html way first
63+
parsedUrl.pathname = htmlTargetPath;
64+
65+
// warn if user already use react-router@6 way
66+
if (
67+
routes.every((route) => route.absPath !== htmlTargetPath) &&
68+
routes.some((route) => route.absPath === rr6TargetPath)
69+
) {
70+
parsedUrl.pathname = rr6TargetPath;
71+
logger.warn(
72+
`Detected ambiguous link \`${href}\` in \`${opts.fileAbsPath}\`, please use \`./xxx.md\` file path instead of normal relative path, dumi will deprecate this behavior in the future.
73+
See more: https://github.com/umijs/dumi/pull/1491`,
74+
);
75+
}
4576
}
4677

4778
parent!.children.splice(i!, 1, {

0 commit comments

Comments
 (0)