Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: history typescript optimize #12632

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions packages/preset-umi/src/features/tmpFiles/tmpFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,18 +582,84 @@ if (process.env.NODE_ENV === 'development') {
reactRouter5Compat,
},
});

const routesPath = getAllRoutesPath(routes);
const routesPathStr =
routesPath.map((path) => `'${path}'`).join(' | ') || 'never';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

necer 有引号?那如果 routers 没有,就是字符串 never 了

api.writeTmpFile({
noPluginDir: true,
path: 'core/historyIntelli.ts',
tplPath: join(TEMPLATES_DIR, 'historyIntelli.tpl'),
context: {
routes: routesPathStr,
historyPath,
reactRouter5Compat,
},
});
}
});

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<string> = 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[];
Expand Down
109 changes: 2 additions & 107 deletions packages/preset-umi/templates/historyIntelli.tpl
Original file line number Diff line number Diff line change
@@ -1,69 +1,14 @@
import { getRoutes } from './route'
import type { History } from '{{{ historyPath }}}'

type Routes = Awaited<ReturnType<typeof getRoutes>>['routes']
type AllRoute = Routes[keyof Routes]
type IsRoot<T extends any> = 'parentId' extends keyof T ? false : true

// show `/` in not `layout / wrapper` only
type GetAllRouteWithoutLayout<Item extends AllRoute> = Item extends any
? 'isWrapper' extends keyof Item
? never
: 'isLayout' extends keyof Item
? never
: Item
: never
type AllRouteWithoutLayout = GetAllRouteWithoutLayout<AllRoute>
type IndexRoutePathname = '/' extends AllRouteWithoutLayout['path']
? '/'
: never

type GetChildrens<T extends any> = T extends any
? IsRoot<T> extends true
? never
: T
: never
type Childrens = GetChildrens<AllRoute>
type Root = Exclude<AllRoute, Childrens>
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<Id>
> = IsNever<Item> extends true
? ''
: Item extends AllRoute
? {
[Key in Item['path'] as TrimSlash<Key>]: UnionMerge<
RouteObject<Item['id']>
>
}
: never

type GetRootRouteObject<Item extends Root> = Item extends Root
? {
[K in Item['path'] as TrimSlash<K>]: UnionMerge<RouteObject<Item['id']>>
}
: never
type MergedResult = UnionMerge<GetRootRouteObject<Root>>
type Routes = {{{ routes }}}

// --- patch history types ---

type HistoryTo = Parameters<History['push']>['0']
type HistoryPath = Exclude<HistoryTo, string>

type UmiPathname = Path<MergedResult> | (string & {})
type UmiPathname = Routes | (string & {})
interface UmiPath extends HistoryPath {
pathname: UmiPathname
}
Expand All @@ -83,53 +28,3 @@ export interface UmiHistory extends History {
goBack: UmiGoBack
{{/reactRouter5Compat}}
}

// --- type utils ---
type TrimLeftSlash<T extends string> = T extends `/${infer R}`
? TrimLeftSlash<R>
: T
type TrimRightSlash<T extends string> = T extends `${infer R}/`
? TrimRightSlash<R>
: T
type TrimSlash<T extends string> = TrimLeftSlash<TrimRightSlash<T>>

type IsNever<T> = [T] extends [never] ? true : false
type IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <G>() => G extends B
? 1
: 2
? true
: false

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never
type UnionMerge<U> = UnionToIntersection<U> extends infer O
? { [K in keyof O]: O[K] }
: never

type ExcludeEmptyKey<T> = IsEqual<T, ''> extends true ? never : T

type PathConcat<
TKey extends string,
TValue,
N = TrimSlash<TKey>
> = TValue extends string
? ExcludeEmptyKey<N>
:
| ExcludeEmptyKey<N>
| `${N & string}${IsNever<ExcludeEmptyKey<N>> extends true
? ''
: '/'}${UnionPath<TValue>}`

type UnionPath<T> = {
[K in keyof T]-?: PathConcat<K & string, T[K]>
}[keyof T]

type MakeSureLeftSlash<T> = T extends any
? `/${TrimRightSlash<T & string>}`
: never

// exclude `/*`, because it always at the top of the IDE tip list
type Path<T, K = UnionPath<T>> = Exclude<MakeSureLeftSlash<K>, '/*'> | IndexRoutePathname
Loading