hannoeru / vite-plugin-pages

File system based route generator for ⚡️Vite

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Feature] Typescript support for route path and route path params(Auto complete)

niku98 opened this issue · comments

Description

It's so good to have Typescript support for route path an path params, just like Tanstack Router 🔥

With this, everyone will have a better DX.

Suggested solution

  1. Instead of using a static declaration file, we should generate one.
  2. Wrapping react-router-dom/vue-router API to using types from generated declaration file. Then exports them.

Example:

// Generated declaration file

declare module '~react-pages' {
  import type { RouteObject } from 'react-router'
  import type { NavigateOptions, LinkProps } from 'react-router-dom'
 
  // Generated route paths
  export type RoutePaths = "/posts" | "/posts/:id";

  // Declare function generatePath
  type Split<T extends string> = T extends `${infer P}/`
    ? Split<P>
      : T extends `/${infer P}`
    ? Split<P>
      : T extends `${infer PL}/${infer PR}`
    ? Split<PL> | Split<PR>
      : T;

  type PathParamsKey<T extends string, K = Split<T>> = K extends `:${infer P}`
    ? P
      : K extends `...${infer P}`
    ? P
      : // eslint-disable-next-line @typescript-eslint/no-unused-vars
      K extends `${infer _P}*`
    ? "slug"
      : never;

  type PathParams<T extends string> = {
    [P in PathParamsKey<T>]: string | number;
  };

  export type GeneratePathOptions<
    T extends string,
    Params extends object = PathParams<T>
  > = {
    path: T;
  } & (keyof Params extends never ? { params?: Params } : { params: Params });

  export function generatePath<T extends string = RoutePaths>({
    path,
    params,
  }: GeneratePathOptions<T>): string;

  // Declare hook useNavigate
  export function useNavigate(): <T extends string = RoutePaths>(
    pathOptions: GeneratePathOptions<T>,
    options?: NavigateOptions
  ) => void

  // Declare component Link
  export function Link<T extends string = RoutePaths>(
    props: GeneratePathOptions<T> &
  Omit<LinkProps, "to">
  ): void
  
  // Export routes
  const routes: RouteObject[]
  export default routes
}

declare module 'virtual:generated-pages-react' {
  import type { RouteObject } from 'react-router'
  const routes: RouteObject[]
  export default routes
}
// Function generatePath

export default function generatePath<T extends string>({
  path,
  params,
}: GeneratePathOptions<T>): string {
  let result: string = path;

  if (params) {
    for (const key in params) {
      if (Object.prototype.hasOwnProperty.call(params, key)) {
        const value = params[key as keyof typeof params] as string;
        result = result.replace(`:${key}`, value);
      }
    }

    if ("slug" in params && result.endsWith("*")) {
      result = result.replace(/\*$/, params.slug as string);
    }
  }

  return result;
}

Then, we should exports generatePath, useNavigate and Link in virtual module, which currently only exports routes.

Alternative

No response

Additional context

No response

Validations

  • Follow the Code of Conduct
  • Read the docs.
  • Check that there isn't already an issue that request the same feature to avoid creating a duplicate.

Out of scope and not planning to do.

Maybe someone can make a react version of unplugin-vue-router.