1+ import { getHostForTabRouteFile } from '@/features/tabs' ;
12import type { Root } from 'hast' ;
23import path from 'path' ;
3- import { lodash , winPath } from 'umi/plugin-utils' ;
4+ import { lodash , logger , winPath } from 'umi/plugin-utils' ;
45import type { Transformer } from 'unified' ;
56import url from 'url' ;
67import 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
2119export 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 ( / \. m d $ / 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