oedotme / generouted

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

[Feature] Move routes config generation from runtime to build time #135

Closed lauri865 closed 11 months ago

lauri865 commented 11 months ago

Currently routes config is generated on run-time, which means that on each page load, the routes config gets parsed with a bunch logic and regex run against it. Not an issue for a small site, but as you enter in the realm of hundreds of routes (or alternatively if the initial page load is already CPU-intensive), it can easily add up to a few ms of processing time before routes can be mounted / data loaded. An unnecessary processing time, since the routes config is static by nature, and not run-time dependent.

I suggest moving that logic to build-time processing similar to how router.ts types file is generated.

There are two major side-benefits from this:

  1. Easier inspectability, since you can see the end-result that gets passed into e.g. createBrowserRouter of react-router-dom, you can more easily understand the end-structure and spot any errors easily.
  2. Eject-ability. Want to move back to config-based routing? Just remove the plugin and you have a working config file to build on top of.
oedotme commented 11 months ago

Hey @lauri865, I implemented that at some points (not long time ago) for those benefits but there were couple of major issues directly related to the dev experience. That can be revisited in the future though.

lauri865 commented 11 months ago

What were the issues @oedotme? Happy to take a stab at it.

oedotme commented 11 months ago

@lauri865 that was initially released at v1.7.0 fb6496a and removed at v1.9.0 9df4161

lauri865 commented 11 months ago

Thanks, will try out out. But if you can share what the issues were with DX, it'd be very helpful 🙏

As a benchmark, I'll add the auto-generated output file of Tanstack router's CLI: https://github.com/TanStack/router/blob/4dbd334fdad68ff80f91824da6d32ffc1450fc4e/examples/react/file-based-routes/src/routeTree.gen.ts#L4

oedotme commented 11 months ago

Thanks! Yeah sure, the main issue was the routes and exports detection while adding/removing routes and the HMR while updating routes.

oedotme commented 11 months ago

Also I've had those issue mostly with vite@v4, I've updated the dependencies now to vite@5 which fixes some issues regrading certain files detection and also HMR in general in my experience.

lauri865 commented 8 months ago

Seems like Tanstack's vite plugin for file based routing works pretty well. Would benefit neat to have sth similar with react router as well.

We're considering three options right now:

  1. Create a custom vite plugin and/or contribute to generouted
  2. Move over to tanstack/router (difficult to prioritise)
  3. Move back to config based routing for the time being, until we can prioritise 1 or 2
lauri865 commented 8 months ago

@oedotme, can you reopen this?

Another reason to do it in the plugin is to remove a circular dependency in Vite due to importing Modals from @generouted/react-router: vite:hmr circular imports detected: /src/pages/(app)/_layout.tsx -> /node_modules/.vite/deps/@generouted_react-router.js?v=977c9260 -> /src/pages/(app)/_layout.tsx

This breaks HMR, and makes for a worse DX. Can't have them outside of pages either, because RouterProvider doesn't accept children, and then Modals would be outside of the router context, which doesn't work.

lauri865 commented 8 months ago

@oedotme, we finished a custom plugin for this now. Reimplemented everything from scratch, but pretty much same ideology with generouted, with some additional features, and support for multi page apps in the same src dir.

oedotme commented 8 months ago

@lauri865 What about the dev experience, HMR or the changes detection in general?

I'm interested to see how it differs from my previous implementation. Could you please share an example with this plugin?

Thanks!

lauri865 commented 8 months ago

Well, we had to hook up watch for add and unlink events to get the tree to regenerate when renaming, adding or deleting files.

There's another issues with your implementation, and that's around the use of prettier. You're spawning a child process with prettier CLI, which is slow. Much better to make it prettier a dependency, and invoke it directly. Much faster, but also just a single file change. Otherwise, you're pushing the same effective code twice with different formats and with a delay in between (can be a second). If a page reload is needed (e.g. when editing loaders), you get a double flash in your face, and significantly slower reloads.

When it comes to HMR – the only case it doesn't work in where it does currently for generouted is editing an inline component that is directly defined in the route file. And this only works in generouted if the route file only exports the said inline component. Should you export anything but a single component in Vite, HMR breaks.

And we're completely fine with that, since most of our routes export a loader or metadata anyways, so we'd want them to be reloaded. And we avoid defining components within route files, and use route files only as a route configs – keeps things easier to follow and more reusable (need to have the same page with a different path? Just duplicate the file with a different name, etc.).

So, HMR works perfectly fine in the happy path.