diff --git a/packages/preset-umi/src/features/tmpFiles/tmpFiles.ts b/packages/preset-umi/src/features/tmpFiles/tmpFiles.ts index 305727e5458c..8d3c54d96a7f 100644 --- a/packages/preset-umi/src/features/tmpFiles/tmpFiles.ts +++ b/packages/preset-umi/src/features/tmpFiles/tmpFiles.ts @@ -582,11 +582,16 @@ if (process.env.NODE_ENV === 'development') { reactRouter5Compat, }, }); + + const routesPath = getAllRoutesPath(routes); + const routesPathStr = + routesPath.map((path) => `'${path}'`).join(' | ') || 'never'; api.writeTmpFile({ noPluginDir: true, path: 'core/historyIntelli.ts', tplPath: join(TEMPLATES_DIR, 'historyIntelli.tpl'), context: { + routes: routesPathStr, historyPath, reactRouter5Compat, }, @@ -594,6 +599,67 @@ if (process.env.NODE_ENV === 'development') { } }); + function joinRoutePath(left: string, right: string) { + const joined = `${left}/${right}`; + const segments = joined.split('/').filter(Boolean); + const newPath = `/${segments.join('/')}`; + return newPath; + } + + function getAllRoutesPath(routes: any) { + const paths: Set = new Set(); + + const isLayoutOrWrapper = (route: any) => { + return route.isLayout || route.isWrapper; + }; + + const getPath = (id: string) => { + const route = routes[id]; + // ignore + const isNotRoute = isLayoutOrWrapper(route); + if (isNotRoute) { + return ''; + } + let path = (route.path || '') as string; + // stop + const isAbsolutePath = path.startsWith('/'); + if (isAbsolutePath) { + return path; + } + let parentId = route.parentId; + while (parentId) { + const parentRoute = routes[parentId]; + const parentPath = (parentRoute.path || '') as string; + // ignore + const isParentNotRoute = isLayoutOrWrapper(parentRoute); + if (isParentNotRoute) { + parentId = parentRoute.parentId; + continue; + } + // stop + // e.g. `/root` - `/root/child` - `nested` => `/root/child/nested` + const isParentAbsolutePath = parentPath.startsWith('/'); + if (isParentAbsolutePath) { + path = joinRoutePath(parentPath, path); + break; + } + // e.g. `/root` - `child` - `nested` => `/root/child/nested` + path = joinRoutePath(parentPath, path); + parentId = parentRoute.parentId; + } + return path; + }; + + Object.keys(routes).forEach((id: any) => { + const path = getPath(id); + if (path.length && !path.includes('*')) { + paths.add(path); + } + }); + + return Array.from(paths); + } + function checkMembers(opts: { path: string; members: string[]; diff --git a/packages/preset-umi/templates/historyIntelli.tpl b/packages/preset-umi/templates/historyIntelli.tpl index eb30e397975f..441c5bdae32a 100644 --- a/packages/preset-umi/templates/historyIntelli.tpl +++ b/packages/preset-umi/templates/historyIntelli.tpl @@ -1,69 +1,14 @@ import { getRoutes } from './route' import type { History } from '{{{ historyPath }}}' -type Routes = Awaited>['routes'] -type AllRoute = Routes[keyof Routes] -type IsRoot = 'parentId' extends keyof T ? false : true - -// show `/` in not `layout / wrapper` only -type GetAllRouteWithoutLayout = Item extends any - ? 'isWrapper' extends keyof Item - ? never - : 'isLayout' extends keyof Item - ? never - : Item - : never -type AllRouteWithoutLayout = GetAllRouteWithoutLayout -type IndexRoutePathname = '/' extends AllRouteWithoutLayout['path'] - ? '/' - : never - -type GetChildrens = T extends any - ? IsRoot extends true - ? never - : T - : never -type Childrens = GetChildrens -type Root = Exclude -type AllIds = AllRoute['id'] - -type GetChildrensByParentId< - Id extends AllIds, - Item = AllRoute -> = Item extends any - ? 'parentId' extends keyof Item - ? Item['parentId'] extends Id - ? Item - : never - : never - : never - -type RouteObject< - Id extends AllIds, - Item = GetChildrensByParentId -> = IsNever extends true - ? '' - : Item extends AllRoute - ? { - [Key in Item['path'] as TrimSlash]: UnionMerge< - RouteObject - > - } - : never - -type GetRootRouteObject = Item extends Root - ? { - [K in Item['path'] as TrimSlash]: UnionMerge> - } - : never -type MergedResult = UnionMerge> +type Routes = {{{ routes }}} // --- patch history types --- type HistoryTo = Parameters['0'] type HistoryPath = Exclude -type UmiPathname = Path | (string & {}) +type UmiPathname = Routes | (string & {}) interface UmiPath extends HistoryPath { pathname: UmiPathname } @@ -83,53 +28,3 @@ export interface UmiHistory extends History { goBack: UmiGoBack {{/reactRouter5Compat}} } - -// --- type utils --- -type TrimLeftSlash = T extends `/${infer R}` - ? TrimLeftSlash - : T -type TrimRightSlash = T extends `${infer R}/` - ? TrimRightSlash - : T -type TrimSlash = TrimLeftSlash> - -type IsNever = [T] extends [never] ? true : false -type IsEqual = (() => G extends A ? 1 : 2) extends () => G extends B - ? 1 - : 2 - ? true - : false - -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( - k: infer I -) => void - ? I - : never -type UnionMerge = UnionToIntersection extends infer O - ? { [K in keyof O]: O[K] } - : never - -type ExcludeEmptyKey = IsEqual extends true ? never : T - -type PathConcat< - TKey extends string, - TValue, - N = TrimSlash -> = TValue extends string - ? ExcludeEmptyKey - : - | ExcludeEmptyKey - | `${N & string}${IsNever> extends true - ? '' - : '/'}${UnionPath}` - -type UnionPath = { - [K in keyof T]-?: PathConcat -}[keyof T] - -type MakeSureLeftSlash = T extends any - ? `/${TrimRightSlash}` - : never - -// exclude `/*`, because it always at the top of the IDE tip list -type Path> = Exclude, '/*'> | IndexRoutePathname