oedotme / generouted

Generated file-based routes for Vite
https://stackblitz.com/github.com/oedotme/generouted/tree/main/explorer
MIT License
1.02k stars 47 forks source link

Feature Request: Programmatically Handling Routes #138

Closed mooxl closed 9 months ago

mooxl commented 9 months ago

Current State:

Currently, generouted uses Path as a TypeScript type, which is a union of string literals. This setup inhibits the capacity to programmatically filter paths, as types cannot be iterated over.

export type Path =
  | `/`
  | `/login`
  | `/login/create`
  | `/tickets`

Feature Request:

The feature proposal is to refactor Path into a constant array. This allows developers to use array methods like filter and map for more flexibility.

export const Paths = [
  `/`,
  `/importerPipelines`,
  `/importerPipelines/create`,
  `/login`,
  `/login/create`,
  `/tickets`,
] as const;

export type Path = (typeof Paths)[number];

This alteration significantly simplifies the method of implementing the Redirect Pattern of Render . It eases managing 'protected' and 'public' routes since developers can now programmatically filter the public routes.

evanlong0803 commented 9 months ago

Yes, I added the 'components' directory in the same way, but it shouldn't have been scanned.

image

image

collins401 commented 9 months ago

Yes, I added the 'components' directory in the same way, but it shouldn't have been scanned.

image

image

try it

// vite.config.ts
generouted({
    source: {
      routes: ['./src/pages/**/[\\w[-]*.{jsx,tsx}', '!./src/pages/**/components/**'],
      modals: './src/pages/**/[+]*.{jsx,tsx}'
    }
  }),
oedotme commented 9 months ago

@mooxl unfortunately the generated file is intentionally meant only for types. Using runtime exports either for routes declaration and other exports were causing specific issue with Vite's HMR.

If you need to have a runtime list of the available routes matching generouted at the moment, you could use the same code used to generate the types:

import { patterns } from '@generouted/react-router/core'

const ROUTES = import.meta.glob(['/src/pages/**/[\\w[-]*.{jsx,tsx}', '!**/(_app|404).*'], { eager: true })

const filtered = Object.keys(ROUTES).filter((key) => !key.includes('/_') && !key.includes('/404'))
const paths = filtered
  .map((key) => {
    const path = key
      .replace(...patterns.route)
      .replace(...patterns.splat)
      .replace(...patterns.param)
      .replace(/\(\w+\)\/|\/?_layout/g, '')
      .replace(/\/?index|\./g, '/')
      .replace(/(\w)\/$/g, '$1')
      .split('/')
      .map((segment) => segment.replace(...patterns.optional))
      .join('/')

    return path.length > 1 ? `/${path}` : path
  })
  .filter(Boolean)

console.log(paths)
// [
//   "/login",
//   "/register",
//   "/about",
//   "/blog/w/o/layout",
//   "/blog/:slug",
//   "/blog",
//   "/blog/tags",
//   "/docs/:lang?",
//   "/docs/:lang?/resources",
//   "/"
// ]

This is unlikely to change, I'll let you know if a utility function is exported for the paths transformation. Or in case router.tsx started exporting runtime values.

Hope that helps.

oedotme commented 9 months ago

@evanlong0926 as @collins401 mentioned, if you want to exclude components directory on the plugin/types level:

// vite.config.ts

export default defineConfig({
  plugins: [
    react(),
    generouted({
      source: {
        routes: ['./src/pages/**/[\\w[-]*.{jsx,tsx}', '!**/components/**'],
        modals: './src/pages/**/[+]*.{jsx,tsx}'
      },
      output: './src/router.ts',
    }),
  ],
  resolve: { alias: { '@': '/client/src' } },
})

Additionally, you'll need to have a custom routes.tsx file, and exclude components from the glob pattern:

// src/routes.tsx

// ...
const ROUTES = import.meta.glob(
  ['/src/pages/**/[\\w[-]*.{jsx,tsx}', '!**/components/**', '!**/(_app|404).*'],
  { eager: true }
)
// ...