Coder Social home page Coder Social logo

oedotme / generouted Goto Github PK

View Code? Open in Web Editor NEW
900.0 9.0 43.0 1.2 MB

Generated file-based routes for Vite

Home Page: https://stackblitz.com/github.com/oedotme/generouted/tree/main/explorer

License: MIT License

TypeScript 97.53% JavaScript 1.71% HTML 0.67% CSS 0.09%
file-based-routing generate react react-location router routes vite pages code-splitting pre-loading

generouted's Introduction


generouted


Generouted

Generated file-based routes for Vite

Motivation

I enjoyed using file-based routing since I tried Next.js (pages directory). After applying the same concept with Vite and client-side applications, I started writing blog posts covering the implementation of client-side file-based routing with React Router which was packaged later as generouted.

Today generouted brings many features, supports multiple frameworks and routers, and inspires ideas and conventions from Next.js, Remix, Expo, Docusaurus, SvelteKit and more.


How does it work?

generouted uses Vite's glob import API to list the routes within the src/pages directory and generates the routes tree and modals based on generouted's conventions.

There are also Vite plugins available for some integrations to provide type-safe components/hooks/utils through code-generation.



Features

  • ๐Ÿ“ Client-side file-based routing
  • โšก Powered by Vite
  • โœจ React support with react-router-dom or @tanstack/router ๐Ÿงช or @tanstack/react-location ๐Ÿšจ
  • โœจ Solid support with @solidjs/router
  • โœจ File-based MDX routes with React or Solid, requires @mdx-js/rollup installation and setup
  • ๐Ÿ” Type-safe navigation
  • ๐Ÿš€ Type-safe global modals
  • ๐Ÿ’ค Route-based code-splitting and lazy-loading
  • ๐Ÿ“„ Route-based data loaders and actions
  • ๐Ÿ’ฃ Route-based error boundary
  • ๐Ÿ“‚ Nested layouts
  • ๐Ÿซ™ Pathless layout groups
  • โ“ Optional static and dynamic routes
  • ๐Ÿ’ญ Ignored routes per file or directory

Online explorer

  • โšก Visit generouted's interactive playground via StackBlitz
  • ๐Ÿงฉ Explore file-based routing patterns and conventions
  • ๐Ÿ”Ž Visualize the routes layouts and the resolved routes paths
  • ๐Ÿ“ Update src/pages/ files and see your changes reflecting

Getting started

React Router

React Router

In case you don't have a Vite project with React and TypeScript, check Vite documentation to start a new project.

Installation

pnpm add @generouted/react-router react-router-dom

Setup

// vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import generouted from '@generouted/react-router/plugin'

export default defineConfig({ plugins: [react(), generouted()] })

Usage

// src/main.tsx

import { createRoot } from 'react-dom/client'
import { Routes } from '@generouted/react-router'

createRoot(document.getElementById('root')!).render(<Routes />)

Adding pages

Add the home page by creating a new file src/pages/index.tsx โ†’ /, then export a default component:

export default function Home() {
  return <h1>Home</h1>
}

Check the routing conventions section below.

Docs

You can find more details about type-safe navigation and global modals at @generouted/react-router docs.

Examples


Solid Router

Solid Router

In case you don't have a Vite project with Solid and TypeScript, check Vite documentation to start a new project.

Installation

pnpm add @generouted/solid-router @solidjs/router

Setup

// vite.config.ts

import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'
import generouted from '@generouted/solid-router/plugin'

export default defineConfig({ plugins: [solid(), generouted()] })

Usage

// src/main.tsx

import { render } from 'solid-js/web'
import { Routes } from '@generouted/solid-router'

render(Routes, document.getElementById('root')!)

Adding pages

Add the home page by creating a new file src/pages/index.tsx โ†’ /, then export a default component:

export default function Home() {
  return <h1>Home</h1>
}

See more about generouted routing conventions below.

Docs

You can find more details about type-safe navigation and global modals at @generouted/solid-router docs.

Examples


TanStack React Router โ€” In-progress experimental support ๐Ÿงช

TanStack React Router โ€” In-progress experimental support ๐Ÿงช

Check out the docs here

Examples


React Location โ€” Deprecated ๐Ÿšจ

React Location โ€” Deprecated ๐Ÿšจ

In case you don't have a Vite project with React and TypeScript, check Vite documentation to start a new project.

Installation

pnpm add generouted @tanstack/react-location

Usage

// src/main.tsx

import { createRoot } from 'react-dom/client'
import { Routes } from 'generouted/react-location'

createRoot(document.getElementById('root')!).render(<Routes />)

Adding pages

Add the home page by creating a new file src/pages/index.tsx โ†’ /, then export a default component:

export default function Home() {
  return <h1>Home</h1>
}

Examples



Conventions

File and directories naming and conventions

  • Routes declaration at src/pages
  • Supports .tsx, .jsx and .mdx file extensions
  • Optional src/pages/_app.tsx for an app level layout
  • Optional src/pages/404.tsx for a custom not-found page

Index routes

  • src/pages/index.tsx โ†’ /
  • src/pages/posts/index.tsx โ†’ /posts

Nested routes

  • src/pages/posts/2022/index.tsx โ†’ /posts/2022
  • src/pages/posts/2022/resolutions.tsx โ†’ /posts/2022/resolutions

Dynamic routes

  • src/pages/posts/[slug].tsx โ†’ /posts/:slug
  • src/pages/posts/[slug]/tags.tsx โ†’ /posts/:slug/tags
  • src/pages/posts/[...all].tsx โ†’ /posts/*

Nested layouts

  • By defining _layout.tsx in any nested directory โ†’ src/pages/posts/_layout.tsx
  • Requires using an <Outlet /> component to render layout children
  • All the files within the src/pages/posts/ directory in this case, will be wrapped with that layout

Nested URLs without nested layouts

  • Route file should be outside of the nested layout directory
  • Include dots . between the segments to be converted to forward slashes
  • src/pages/posts.nested.as.url.not.layout.tsx โ†’ /posts/nested/as/url/not/layout

Pathless layouts

  • Similar to nested layouts for layout definition
  • By wrapping the parent directory with parentheses ()
  • src/pages/(auth)/_layout.tsx
  • src/pages/(auth)/login.tsx โ†’ /login
  • Layout parent directory name is not included in the routes paths

Global modals

  • By prefixing the file name with a plus sign + (thinking the modal is an extra route overlaying the current route)
  • Modals navigation available via the useModals() hook
  • src/pages/+info.tsx โ†’ /info
  • src/pages/+checkout/+details.tsx โ†’ /checkout/details
  • src/pages/+checkout/+payment.tsx โ†’ /checkout/payment

Optional segments

  • By prefixing a route segment with a minus sign - (thinking the segment can be subtracted or removed from the route path)
  • src/pages/-en/about.tsx โ†’ /en?/about โ†’ /en/about, /about
  • src/pages/-[lang]/about.tsx โ†’ /:lang?/about โ†’ /en/about, /fr/about, /about

Ignored routes

  • Any directory or file starts with an underscore _ will be ignored
  • src/pages/_ignored.tsx
  • src/pages/posts/_components/button.tsx
  • src/pages/posts/_components/link.tsx

Page exports

  • Required page component export default Component() {...}
  • Optional page loader function export const Loader = () => {...}
  • Optional page action function export const Action = () => {...}
  • Optional suspense-based pending component export const Pending = () => {...}
  • Optional error boundary component export const Catch = () => {...}

Example

Directory structure
src/pages
โ”œโ”€โ”€ (auth)
โ”‚   โ”œโ”€โ”€ _layout.tsx
โ”‚   โ”œโ”€โ”€ login.tsx
โ”‚   โ””โ”€โ”€ register.tsx
โ”œโ”€โ”€ blog
โ”‚   โ”œโ”€โ”€ _components
โ”‚   โ”‚   โ”œโ”€โ”€ button.tsx
โ”‚   โ”‚   โ””โ”€โ”€ comments.tsx
โ”‚   โ”œโ”€โ”€ [...all].tsx
โ”‚   โ”œโ”€โ”€ [slug].tsx
โ”‚   โ”œโ”€โ”€ _layout.tsx
โ”‚   โ”œโ”€โ”€ index.tsx
โ”‚   โ””โ”€โ”€ tags.tsx
โ”œโ”€โ”€ docs
โ”‚   โ”œโ”€โ”€ -[lang]
โ”‚   โ”‚   โ”œโ”€โ”€ index.tsx
โ”‚   โ”‚   โ””โ”€โ”€ resources.tsx
โ”‚   โ””โ”€โ”€ -en
โ”‚       โ””โ”€โ”€ contributors.tsx
โ”œโ”€โ”€ +info.tsx
โ”œโ”€โ”€ 404.tsx
โ”œโ”€โ”€ _app.tsx
โ”œโ”€โ”€ _ignored.tsx
โ”œโ”€โ”€ about.tsx
โ”œโ”€โ”€ blog.w.o.layout.tsx
โ””โ”€โ”€ index.tsx
Overview
File Path Convention
(auth)/_layout.tsx Pathless Layout group
(auth)/login.tsx /login Regular route
(auth)/register.tsx /register Regular route
blog/_components/button.tsx Ignored route by an ignored directory
blog/_components/comments.tsx Ignored route by an ignored directory
blog/[...all].tsx /blog/* Dynamic catch-all route
blog/[slug].tsx /blog/:slug Dynamic route
blog/_layout.tsx Layout for /blog routes
blog/index.tsx /blog Index route
blog/tags.tsx /blog/tags Regular route
docs/-[lang]/index.tsx /docs/:lang?/index Optional dynamic route segment
docs/-[lang]/resources.tsx /docs/:lang?/resources Optional dynamic route segment
docs/-en/contributors.tsx /docs/en?/contributors Optional static route segment
+info.tsx /info Modal route
404.tsx * Custom 404 (optional)
_app.tsx Custom app layout (optional)
_ignored.tsx Ignored route
about.tsx /about Regular route
blog.w.o.layout.tsx /blog/w/o/layout Route without /blog layout
index.tsx / Index route

API

Routing

Via @generouted/react-router or @generouted/solid-router

  • <Routes /> โ€” file-based routing component to be render in the app entry
  • routes โ€” file-based routes tree/object used by default at <Routes /> component

Routing + code-splitting and lazy-loading

Via @generouted/react-router/lazy or @generouted/solid-router/lazy

  • Used instead of @generouted/react-router or @generouted/solid-router to enable lazy-loading
  • Make sure to replace all imports to lazy imports โ€” namely at app entry and src/pages/_app.tsx
  • Provides the same <Routes /> and routes exports

Plugins

Via @generouted/react-router/plugin or @generouted/solid-router/plugin

  • Vite plugin for type generation and initializing type-safe components/hooks/utils
  • Generates src/router.ts file
  • Exports type-safe <Link>, <Navigate>, useModals(), useNavigate(), useParams(), redirect(), etc.
  • Check out @generouted/react-router docs or @generouted/solid-router docs for more details

Core

Via @generouted/react-router/core or @generouted/solid-router/core

  • Available for customization, however it's recommended to use the available integrations directory via the <Routes/> component
  • Check out the custom integration example

FAQ

How to implement protected or guarded routes?

There are multiple approaches to achieve that. If you prefer handling the logic in one place, you can define the protected routes with redirection handling within a component:

// src/config/redirects.tsx

import { Navigate, useLocation } from 'react-router-dom'

import { useAuth } from '../context/auth'
import { Path } from '../router'

const PRIVATE: Path[] = ['/logout']
const PUBLIC: Path[] = ['/login']

export const Redirects = ({ children }: { children: React.ReactNode }) => {
  const auth = useAuth()
  const location = useLocation()

  const authenticatedOnPublicPath = auth.active && PUBLIC.includes(location.pathname as Path)
  const unAuthenticatedOnPrivatePath = !auth.active && PRIVATE.includes(location.pathname as Path)

  if (authenticatedOnPublicPath) return <Navigate to="/" replace />
  if (unAuthenticatedOnPrivatePath) return <Navigate to="/login" replace />

  return children
}

Then use that component (<Redirects> ) at the root-level layout src/pages/_app.tsx to wrap the <Outlet> component:

// src/pages/_app.tsx

import { Outlet } from 'react-router-dom'

import { Redirects } from '../config/redirects'

export default function App() {
  return (
    <section>
      <header>
        <nav>...</nav>
      </header>

      <main>
        <Redirects>
          <Outlet />
        </Redirects>
      </main>
    </section>
  )
}

You can find a full example of this approach at Render template


How to use with Hash or Memory Routers?

You can use the exported routes object to customize the router or to use hash/memory routers:

import { createRoot } from 'react-dom/client'
import { RouterProvider, createHashRouter } from 'react-router-dom'
import { routes } from '@generouted/react-router'

const router = createHashRouter(routes)
const Routes = () => <RouterProvider router={router} />

createRoot(document.getElementById('root')!).render(<Routes />)


License

MIT

generouted's People

Contributors

agustinbravop avatar akshaypathak avatar aplulu avatar az33zy avatar binghuis avatar bzbetty avatar d0whc3r avatar egreb avatar gregfenton avatar ka2n avatar krosf avatar lokhmakov avatar obedm503 avatar oedotme avatar spa5k avatar timothympace avatar vnikolov88 avatar vriskaserket51 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

generouted's Issues

Dynamic route at root level

Hello !

I'm wondering why it is not possible to create dynamic route at root level?

Considering this pages folder structure:

pages
โ”œโ”€โ”€ 404.tsx
โ”œโ”€โ”€ about.tsx
โ”œโ”€โ”€ _app.tsx
โ”œโ”€โ”€ index.tsx
โ”œโ”€โ”€ [slug]
โ”‚ย ย  โ”œโ”€โ”€ index.tsx
โ”‚ย ย  โ””โ”€โ”€ foo.tsx
โ””โ”€โ”€ [slug].tsx

I want to be abble to access a route with given path: /1234 or /1234/foo. But with the current implementation it's not possible.

I checked the core code and figure out that the code at https://github.com/oedotme/generouted/blob/main/src/core.ts#L43...L44 was responsible of the non creation of those routes paths. I have commented those lines and it looks like to work (tested with react-router).

Do you think it is possible to remove those lines safely?

Fails to build (TS)

Using Vite + ReactJS with typescript works on development but when attempting to build the following happens:

node_modules/generouted/src/react-location.tsx:9:31 - error TS2339: Property 'glob' does not exist on type 'ImportMeta'.

9 const PRESERVED = import.meta.glob<Module>('/src/pages/(_app|404).{jsx,tsx}', { eager: true })
                                ~~~~
                                
                                
node_modules/generouted/src/react-location.tsx:10:28 - error TS2339: Property 'glob' does not exist on type 'ImportMeta'.

10 const ROUTES = import.meta.glob<Module>(['/src/pages/**/[\\w[]*.{jsx,tsx}', '!**/(_app|404).*'])
                              ~~~~

Openning the file with vscode results in the same warnings on the file.
added //@ts-expect-error and i could build, adding glob to the global import.meta via typescript should work?

Vite plugin does not work in a monorepo context

Tried using the react-router vite plugin in a monorepo. The plugin does not use relative paths so it generates routes.gen.tsx in /src/routes.gen.tsx instead of /packages/[app]/src/routes.gen.tsx even when vite.config.ts lives in /packages/[app]/vite.config.ts. Happy to provide an example reproducing the issue.

Link issue

Hello. In the latest version 1.7.20 of generouted, using thetag of ant design, the route can be updated after clicking, but the page is not updated. Hope it can be solved.thanks

[Discussion] - Support Sentry wrapCreateBrowserRouter or possibly make the router variable a function

Hey me and my team really enjoy this plugin! Amazing work you have done here!

We are having a bit of an issue implementing Sentry we wondered if you could take a look at:

The problem

We are trying to implement Sentry performance tracing in our vite app. To do this we need to wrap a sentry function 'wrapCreateBrowserRouter' around 'createBrowserRouter'. Docs here

Our current fix:

We tried circumventing this issue by importing the generated routes variable and then making our own Routes component. This works, but this causes double fetching on the initial load since createBrowserRouter is still called in "routes.gen.tsx".

Possible solution 1 - Make router a function

We think the easiest solution could be making the router a function:

const router = () => createBrowserRouter([{ element: <App />, children: routes }])
// ...
// then update Routes to call it
export const Routes = () => <RouterProvider router={router()} />

This way createBrowserRouter is not called unless we use the Routes component.

Possible solution 2 (trickier) - Support function propagating

Alternatively if genroutes supported passing an optional function to wrap "createBrowserRouter" that would be great, but sounds like a lot more work for an issue not many people seem to have.

eg:

// vite.config.ts
 plugins: [
      genroutes({browserWrapper: wrapCreateBrowserRouter}),
      ...

We understand that this is a pretty specific problem for us so no worries if this is out of scope for request you are taking in atm. Please let me know if there is anymore info you need, and thanks again for this great plugin.

Invalid glob import syntax: Expect CallExpression, got BinaryExpression

Invalid glob import syntax: Expect CallExpression, got BinaryExpression
00:00:44 [vite] Internal server error: Invalid glob import syntax: Expect CallExpression, got BinaryExpression
Plugin: vite:import-glob
File: D:/test/node_modules/.pnpm/[email protected][email protected]/node_modules/generouted/src/react-router.tsx:10:19

  8  |  type Module = { default: Element; Loader: LoaderFunction; Action: ActionFunction; ErrorElement: Element }
  9  |  
  10 |  const PRESERVED = import.meta.glob<Module>('/src/pages/(_app|404).{jsx,tsx}', { eager: true })
     |                    ^
  11 |  const ROUTES = import.meta.glob<Module>(['/src/pages/**/[\\w[]*.{jsx,tsx}', '!**/(_app|404).*'])
  12 |

at err (file:///D:/test/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-9912c491.js:37834:23)
at file:///D:/test/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-9912c491.js:37873:19
at Array.map ()
at parseImportGlob (file:///D:/test/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-9912c491.js:37830:27)
at transformGlobImport (file:///D:/test/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-9912c491.js:37948:27)
at TransformContext.transform (file:///D:/test/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-9912c491.js:37755:34)
at Object.transform (file:///D:/test/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-9912c491.js:41662:44)
at async loadAndTransform (file:///D:/test/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-9912c491.js:39467:29)

React Location vs. Tanstack React Router?

Hi! Just wanted to drop how happy I am that someone has solved this! Magical indeed. Thanks!

That said... I was wondering whether React Location is kind of the old version on Tanstack Router. The first one is 8 months old while second one in in current development as it seems. Do you see how to migrate this solution to the "new router"?

How to configure route based code splitting?

Hi there, found your blog and this package from it. Really like idea of file based routing in vite for working on classic spa apps.
I want to try how to configure code splitting for routes. Is this possible, because description does not mention anything?

Routes not found when running vite plugin from Nx monorepo

I have setup a mono-repo with Nx and Vite as the bundler. I tried following the guide at https://github.com/oedotme/generouted/tree/main/plugins/tanstack-react-router and also https://github.com/oedotme/generouted/tree/main/examples/react-router/basic, but in both cases it doesn't work:

โžœ  workspace git:(master) โœ— npx nx serve demo

> nx run demo:serve:development

5:51:34 PM [generouted] 0 routes in 69 ms
node:fs:594
  handleErrorFromBinding(ctx);
  ^

Error: ENOENT: no such file or directory, open './src/routes.gen.tsx'
    at Object.openSync (node:fs:594:3)
    at writeFileSync (node:fs:2207:35)
    at T (/project/home/mcreenan/workspace/node_modules/@generouted/tanstack-react-router/dist/index.cjs:48:211) {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: './src/routes.gen.tsx'
}

 โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”

 >  NX   Running target serve for project demo failed

   Failed tasks:
   
   - demo:serve:development
   
   Hint: run the command with --verbose for more details.

Here's a codesandbox showing the issue -- https://codesandbox.io/p/sandbox/admiring-wescoff-hzfm0g?file=%2FREADME.md

feat: Pathless Layout Routes

Pathless Layouts are quite useful when implementing an Auth / Private Layer to specific routes without prefixing routes with, say, "/auth". This feature comes with Remix through prefixing directories with a double-underscore, although I'm aware that that may be confusing since single-underscoring ignores directories/files. Any thoughts about adding this?

Remix Documentation: https://remix.run/docs/en/v1/guides/routing#pathless-layout-routes

Thanks in advance! It's been a pleasure using your library :)

React Router with plugin not working

I just followed this guide and this example but I have a couple of problems in this setup.

  1. When I try to install the plugin I had the error below related to fast-glob package. I think installing this package is missing in the guide:
pnpm add -D @generouted/react-router
Packages: +1
+
Progress: resolved 166, reused 144, downloaded 0, added 0, done

devDependencies:
+ @generouted/react-router 1.7.5

โ€‰WARNโ€‰ Issues with peer dependencies found
.
โ””โ”€โ”ฌ @generouted/react-router 1.7.5
  โ””โ”€โ”€ โœ• missing peer fast-glob@>=3
Peer dependencies that should be installed:
  fast-glob@>=3  

Done in 2s

Then I tried to install fast-glob manually like this to solve this step and it seems it's working:

pnpm add -D fast-glob
  1. Then I got a type error on my vite.config.ts file as below:
    01
    But it seems the routes.gen.tsx file is created for me in the src folder so I think we can ignore this issue for now.

  2. I just added some pages exactly same as the example but I only can see blank pages as a result in the browser.
    After switch from plugin setup to the normal setup everything is working without any other changes anywhere. I just replaced this line:

import { Routes } from './routes.gen'

with this:

import { Routes } from 'generouted/react-router'

and installed generouted package rather than @generouted/react-router

'@vitejs/plugin-react-swc' doesn't work with Generouted.

Internal server error: Invalid glob import syntax: Expect CallExpression, got BinaryExpression
  Plugin: vite:import-glob
  File: F:/quran/node_modules/.pnpm/[email protected][email protected]/node_modules/generouted/src/react-location.tsx:9:19
  7  |  type Module = { default: Element; Loader: LoaderFn; PendingElement: Element; ErrorElement: Element }
  8  |  
  9  |  const PRESERVED = import.meta.glob<Module>('/src/pages/(_app|404).{jsx,tsx}', { eager: true })
     |                    ^
  10 |  const ROUTES = import.meta.glob<Module>(['/src/pages/**/[\\w[]*.{jsx,tsx}', '!**/(_app|404).*'])
  11 |  

Config -

import react from '@vitejs/plugin-react-swc';
import { defineConfig } from 'vite';

export default defineConfig({ plugins: [react()] });

No error if we were to use @vitejs/plugin-react

Layout for pages at the top level?

I missed something or this isn't documented. How can I create a layout for the top-level pages located right under the /src/pages/ folder? The example shows a posts.tsx layout file for files located under the posts/ directory, but what about pages that are located under the pages/ directory? I created a file called pages.tsx under /src but that didn't appear to work.

Changing the name of the generated file

Hi there,

I am wondering if it's possible to change the name of the routes file that gets generated. I would prefer something like _routes.tsx instead of routes.gen.tsx, if possible.

Thanks a lot.

Error boundary's not working in React Router because default errorElement resolves to null instead of component

If an error is thrown in a file which doesn't export an ErrorElement, the default React Router error boundary will be displayed with this error: Uncaught Error: Element type is invalid. Received a promise that resolves to: null. Lazy element type must resolve to a class or function. This happens even if an error boundary is used. If the file does export ErrorElement, then the element will be rendered with the Uncaught Error: ... mentioned above as the error.

Why

In https://github.com/oedotme/generouted/blob/main/packages/generouted/src/react-router.tsx#L17, if no errorElement is found for the route, we use a lazy component which resolves to null.

const ErrorElement = lazy(() => module().then((module) => ({ default: module.ErrorElement || null })))

When an error is thrown in the component, React Router tries to render the corresponding lazy ErrorElement. If no ErrorElement is exported from the module, it will try to use the null one, but React will throw an error because lazy component's must resolve to a class or function.

Fix

I think this problem is the same as the one from #32. The same solution there works, but I would throw only the error instead of adding text to it:

export function DefaultErrorElement() {
  throw ${useRouteError()}
}

That way, the other error boundaries we bubble up to will get the original error.

Authentication example

Thank you Omar! I love your work. Would it be possible to add an authentication example?

How can we use the data that's gotten from the Loader in @tanstack/react-router

export const Loader = async () => {
	const list = await getList();
	return { list };
};

export default function Index() {
	const { route } = useMatch({ from: '/list/' });
	console.log(route);

	const data = useLoaderClient();
	console.log(data);
	// const posts = useLoaderInstance({ key: 'listIndex' });
	// console.log(posts)

	return (
		<div>
			List Index
		</div>
	);
}

Tried checking the official docs but they seem to be having a step of loading the loader like on Load for example here - https://tanstack.com/router/v1/docs/guide/data-loading#the-onload-route-option

Compatibility with Electron?

I'm developing my app for the web first, with the future goal of adding Electron as a compilation target and keeping close to 100% of the code base shared between web and Electron.

With that in mind, I decided to skip Next.js and instead use plain React because it's more easily integrated in Electron. I like your idea of bringing Next.js-inspired routing to plain React!

Before I get too deep into the rabbit hole though, is generouted compatible with an Electron environment as-is? Meaning no code changes or adaptations are required.

New convention for layout groups, optional routes layouts?

There are two SvelteKit conventions that might be useful in some cases that I'm thinking to add:

Pathless layout groups https://kit.svelte.dev/docs/advanced-routing#advanced-layouts

  • By wrapping a directory name with (): src/pages/(app)/...
  • Related #19
Example
src/pages/
โ”œโ”€โ”€ (app)/
โ”‚   โ”œโ”€โ”€ _layout.tsx
โ”‚   โ”œโ”€โ”€ dashboard.tsx      โ†’  /dashboard      wrapped by (app)/_layout.tsx
โ”‚   โ””โ”€โ”€ item.tsx           โ†’  /item           wrapped by (app)/_layout.tsx
โ”œโ”€โ”€ (marketing)/
โ”‚   โ”œโ”€โ”€ _layout.tsx
โ”‚   โ”œโ”€โ”€ about.tsx          โ†’  /about          wrapped by (marketing)/_layout.tsx
โ”‚   โ””โ”€โ”€ testimonials.tsx   โ†’  /testimonials   wrapped by (marketing)/_layout.tsx
โ””โ”€โ”€ admin/
    โ”œโ”€โ”€ _layout.tsx
    โ””โ”€โ”€ index.tsx          โ†’  /admin          wrapped by admin/_layout.tsx

Optional route segment https://kit.svelte.dev/docs/advanced-routing#advanced-layouts

  • Support for React Router v6.5.0+
  • By wrapping the dynamic route with extra []: src/pages/[param]/about.tsx โ†’ src/pages/[[param]]/about.tsx
Example
  • src/pages/[[lang]]/about.tsx โ†’ /:lang?/about
  • /:lang?/about will match both /:lang/about โ†’ /en/about and /about

Bug/Question: Initial build/dev fails... while second execution works

Has anybody ever seen the following issue with Generouted and React Router?

When I am deleting node_modules/.vite to force refresh the build or when clean installing node_modules I get a somewhat confusing error where the client side application is reporting context errors like:

utils.ts:784 Uncaught Error: useHref() may be used only in the context of a <Router> component.

The stack trace shows all the routing specific components ... e.g. RouterProvider.

The funny thing is: when stopping the dev server and restarting it everything works fine. As if something get cached somehow during the first execution and fixes the second time execution.

Feature Request :: Nested URLs without Nesting Layouts

Having support for convention-based nested URLs while being able to escape from the default layout convention in a given folder would be awesome. ๐Ÿ˜„

As per the remix.run docs: https://remix.run/docs/en/v1/guides/routing#nested-urls-without-nesting-layouts

So, if we want a flat UI hierarchy, we create a flat filename--we use "." to create segments instead of folders. This defines URL nesting without creating component nesting.

โ””โ”€โ”€ app
    โ”œโ”€โ”€ root.jsx
    โ””โ”€โ”€ routes
        โ”œโ”€โ”€ sales
        โ”‚   โ”œโ”€โ”€ invoices
        โ”‚   โ”‚   โ””โ”€โ”€ $invoiceId.jsx
        โ”‚   โ””โ”€โ”€ invoices.jsx
        โ”œโ”€โ”€ sales.invoices.$invoiceId.edit.jsx ๐Ÿ‘ˆ not nested
        โ””โ”€โ”€ sales.jsx

Just for absolute clarity, if the url is "example.com/sales/invoices/2000/edit", we'll get this UI hierarchy that matches the file system hierarchy:

<Root>
  <EditInvoice />
</Root>

Circular dependency

routes.gen.tsx

import app from "./pages/_app";
const App = app || Outlet;

_app.tsx

import { Link, routes } from "@/routes.gen";

Two files referencing each other will result in circular dependency issue, and require modification to the file structure.

Warning: Can't perform a React state update on a component that hasn't mounted yet.

Hi there!

First of all, I am not quite sure if this is an issue with generouted or @tanstack/react-router, or even with Relay (the GraphQL client I use for data fetching) but I noticed that my page gives me the following error after a few refreshes:

Warning: Can't perform a React state update on a component that hasn't mounted yet. This indicates that you have a side-effect in your render function that asynchronously later calls tries to update the component.

I realised that the error disappears if I get rid of the lazy() import from the generated routes file and manually import the component in there.

Could this be an issue with TanStack router or perhaps with the way of how I fetch data in combination with React 18 / Suspense? I am not quite sure what's happening. Maybe any of you are able to help me out.

Thanks a lot, I appreciate it!

Question: fallback loading element for routes

How do i show a loading state when a route is being fetched?

eg, in nextjs 13, i can create a loading.tsx file and return some jsx, which will server as a suspense fallback element when child routes are being fetched.

Is there a built in way to achieve same with this package?

Using hash link with Transtack React Router

I tried to use Transtack React Router with generouted but when I tried to use the hash link with the bellow code I got a redundant # character in the beginning of has value, this is my link code:

        <Link to="/" hash="test" activeOptions={{ includeHash: true }}>
          Test
        </Link>

It creates something like this for me as result:

http://localhost:5173/##test

I tried to reproduce this with Transtack official stackblitz
But it was OK there. Is it somehow related to generouted? ๐Ÿค”

[Feature]Some expected features.

  1. <Suspense fallback={null} children={<New />} /> Expecting the ability to customize settings for fallback,The use case is to set a loading component when switching between routes.
  2. Routes supports configuring the handle property. The use case is for customizing page authentication (auth) and breadcrumb (crumb).
  3. Exporting a more user-friendly configuration that supports dynamically generating route menus.
  4. Creating separate files for Link, Navigate, Path, Params, useNavigate, etc., in order to avoid circular dependency caused by their use in _app.

The above are some expected features for current use. Since routing is often used in conjunction with sidebar menus, page authentication, and breadcrumbs, it is expected to configure related properties for better compatibility with these scenarios.

If there are better ways to integrate with these scenarios, it would be great if the author could share and communicate them.

Type safe redirect

The vite plugin is awesome, but when redirecting using import { redirect } from "react-router-dom"; the type-safety from routes.gen.tsx gets lost. It would be cool if it also exported a redirect function that knew about the available paths.

feat: react-router - add possibility to use HashRouter

First of all, thanks for your work! :-)

For now it's not possible to use the component with a HashRouter inseatd of a BrowserRouter. I know it's not recommended in the doc but for legacy reasons I need a hash in my urls.

Two ideas/proposals:

  1. export the Array used to build the BrowserRouter (https://github.com/oedotme/generouted/blob/main/src/react-router.tsx#L32) and then use this Array in the app that use the component. Something like :
// react-router.tsx
...
const routes = [
  { element: <App children={<Outlet />} />, children: [...regularRoutes, { path: '*', element: <NotFound /> }] },
]
const router = createBrowserRouter(routes)

export routes
export const Routes = () => {
  return <RouterProvider router={router} />
}

// my-app/index.tsx
import { HashRouter } from 'react-router-dom'
import { routes } from 'generouted/react-location'

const container = document.getElementById('app')!
createRoot(container).render(<HashRouter>{useRoutes(routes)}</HashRouter>)
  1. being able to import generouted/core in our app and let the possibility to use the functions generatePreservedRoutes and generateRegularRoutes for building our "custom" routes. I think this can be useful if the root configuration in the vite.config is different. Such as :
export default defineConfig({
  ...
  root: resolve(process.cwd(), 'client'),
  ...
});

In this case the glob patterns won't work and no pages will be found. Also page regex should also be configurable :-)

I don't know what's the best solution and what you think about the proposals or if you have other solutions :-)

Elegant exclude certain files in the pages directory

we are developing a large react app, and we put a few non-page files in the folder.

โ”œโ”€โ”€ src
|  โ”œโ”€โ”€ entry
|  โ”œโ”€โ”€ common
|  |  โ”œโ”€โ”€ components
|  |  โ”œโ”€โ”€ constants
|  |  โ”œโ”€โ”€ env
|  |  โ”œโ”€โ”€ hooks
|  |  โ”œโ”€โ”€ store.ts
|  |  โ”œโ”€โ”€ types
|  |  โ””โ”€โ”€ utils
|  โ””โ”€โ”€ pages
|     โ”œโ”€โ”€ choose-role
|     |  โ”œโ”€โ”€ api.ts
|     |  โ”œโ”€โ”€ components
|     |  โ”œโ”€โ”€ constants.ts
|     |  โ”œโ”€โ”€ utils.ts
|     |  โ””โ”€โ”€ index.tsx // the page file

the other files than index.tsx are not routes (e.g. /choose-role/utils
). So, we want to exclude these files when auto-generating the routes.

I can imagine three ways:

  1. adding a _ before all these filenames. e.g. utils.ts => _utils.ts. awful tho. ๐Ÿ˜…
  2. adding an excluded files list. (Thankfully, we have a good name convention for these files).
  3. renaming the index.tsx => page.tsx(similar to the app dir, other files should be excluded by default.)

my personal preference is 3 โ‰ˆ 2 > 1.

is generouted adaptable for our case? would like to see your insights! โค๏ธ

Cannot find module '@generouted/react-router/client' or its corresponding type declarations.

I am trying @generouted/react-router following steps mentioned in the plugin's readme.

The generated routes.gen.tsx contains an import

import { components, hooks } from '@generouted/react-router/client'

Looking into plugin's package.json I found these module exports

"exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "require": "./dist/index.cjs",
      "import": "./dist/index.js"
    },
    "./client": {
      "types": "./src/client/index.ts",
      "import": "./src/client/index.ts"
    }
  },

but there is no src directory in the installed plugin module

image

Apologies if I have missed something.

Routes of only numbers in filename do not work

src/pages/index.tsx - yes
src/pages/01.tsx - no
src/pages/three/index.tsx - yes
src/pages/three/01.tsx - no
src/pages/three/02/index.tsx - yes
src/pages/three/2022.tsx - no
src/pages/three/foo.tsx - yes

Doesn't work with swc

When using "@vitejs/plugin-react-swc": "^3.1.0" instead of "@vitejs/plugin-react": "^3.1.0", running the app fails with an exception 2:50:04โ€ฏPM [vite] Internal server error: Invalid glob import syntax: Expect CallExpression, got BinaryExpression

Changes to routes containing loaders in dev mode causes router to stop functioning

Thrilled to have found this library - pretty much exactly what I was looking for! One minor thing I've found thus far is it seems that changing a route that contains a loader such as posts/[slug].tsx in the data-loaders example while it's running in dev mode causes the router to stop functioning and a reload is necessary to get things working again. It happens with any route that contains a loader based on my experimentation, not just child routes. Any idea what that could be?

Colocating non page components

One of my gripes with file based routes is you usually cannot co-locate components that belong to a page in the same folder. Is this possible with this package?

Something like:

  • pages/contact/index.ts
  • pages/contact/_components/form.tsx

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.