vike icon indicating copy to clipboard operation
vike copied to clipboard

Single Route File

Open brillout opened this issue 2 years ago • 56 comments

Description

Being able to define the entire routing of the app in a single file.

// vike.config.h.ts

import type { Config } from 'vike/types'
import LandingPage from './pages/LandingPage.js'
import ProductListPage from './pages/ProductListPage.js'
import ProductPage from './pages/ProductPage.js'

export default {
  pages: [
    '/': {
      Page: LandingPage
    },
    '/product/@id': {
      Page: ProductPage
    },
    '/products': {
      Page: ProductListPage
    },
    // ...
  ]
} satisfies Config

If you want this, feel free to write a comment down below explaining why. The more we know why users want this, the better we can prioritize accordingly.

brillout avatar Jun 17 '22 07:06 brillout

Edit: Redacted outdated design.

brillout avatar Sep 10 '22 10:09 brillout

One option could be to use a tagged template literal string. Which at first glance seems to have typescript support. It would also allow to eliminate the documentation/comments in the aforementioned example, since it would be more direct.

function Menu() {
  return <>
    <Link to={`/`}  />
    <Link to={`/about`} />
    <Link to={`/product/${42}`} />
    <Link to={`/products`} />
  </>
}

redbar0n avatar Sep 10 '22 15:09 redbar0n

I see there’s some precedence to the API that I suggested: https://solito.dev/usage/link#wrapper-components

In all, Solito does some really nifty things with routing, which is very desirable to replicate on a non-NextJS stack. To enable universal apps cross platform.

The downside with Fernando’ stack (NextJS+Expo+Solito+Dripsy) is that it actually disables SSR on web to be able to work (due to issues with responsivity, that Tamagui solves).

So there’s a double opportunity there: universal apps on Vite and with SSR on web. I know the very competent and prolific author of Tamagui is looking into that, and has been eyeing vite-plugin-ssr since I gave word to him about it.

redbar0n avatar Sep 10 '22 21:09 redbar0n

Open question: how to make it work with TypeScript? So that TypeScript complains upon <Link to="unknown-page" /> or <Link to="product-list" id={"42"} /> (because "42" is a string but should be a number).

Does Solito has such TypeScript support?

One option could be to use a tagged template literal string.

So far, I can't see how TypeScript's template literal strings would help.

One aspect of Single Route Files that is interesting is that the user can change the URL without having to modify each link. That's why I'm leaning towards <Link to="product" id={42} /> . But I can see that to be a matter of taste and use case. Small apps don't need that.

brillout avatar Sep 13 '22 10:09 brillout

Does Solito has such TypeScript support?

It doesn't seem like it. It simply replicates NextJS' API.

So far, I can't see how TypeScript's template literal strings would help.

On second glance, I think you're right. TypeScript Template Literal Types that I linked to are a different thing. What I had in mind - type checking inside tagged template literal strings - is still an open TypeScript issue (not very likely to get implemented, it seems): https://github.com/microsoft/TypeScript/issues/29432

change the URL without having to modify each link

I see. I think the general web native way of doing it - and what Rails did - is to simply provide a re-route to the new URL. That way, outdated links in ones app would work the same as the outdated links to one's page that had already been shared on the web: they would be re-routed to the new URL.

redbar0n avatar Sep 13 '22 10:09 redbar0n

Open question: how to make it work with TypeScript? So that TypeScript complains upon <Link to="unknown-page" /> or <Link to="product-list" id={"42"} /> (because "42" is a string but should be a number).

Did some more research into this, since you mentioned it is a blocker.

In addition to pathpida, check out next-type-safe-routes or nextjs-routes or next-typed-routes for inspiration.

The API of next-type-safe-routes seems the nicest (clearest, most straightforward/intuitive, least boilerplate). The automatic route listing (route autocomplete) is quite nice.

next-type-safe-routes parses the /pages folder in your Next.js app and generates types for all the pages and API routes in the application. These types can then be used to ensure that you only link to pages (and only use API routes) that actually exists in your application.

due to the strictness, we can also determine which parameters are needed for dynamic routes.

So, the only issue with using either of those directly is that they all presume filesystem routing (being for NextJS).

Maybe typesafe-routes is what you're looking for?

Actually, it turns out you can use TypeScript Template Literal Types to get type checking inside tagged template literal strings, like I had in mind. This library does it, see the GIF animation in that repo.

As long as it is possible to wrap the <Link> component that vite-plugin-ssr provides, so that a Solito.dev like library could be developed on top, for universal/crossplatform-app support. With the goal of having vite-plugin-ssr (Vite) instead of NextJS (Webpack) as the base for such crossplatform apps.

redbar0n avatar Sep 16 '22 11:09 redbar0n

Neat. Indeed, infer with template literal types is a solution.

We can even make the route types defined by the user in route.ts available globally by using declare module (e.g. https://telefunc.com/typescript#getcontext).

I think we can make it work.

Although Route Functions routes cannot be typed, but that's expected.

As long as it is possible to wrap the component that vite-plugin-ssr provides

Yes, that's the idea. So far, I don't see a problem with that.

brillout avatar Sep 20 '22 20:09 brillout

Came across this. Could be potential inspiration for typesafe routes.

solito’s useParam() hook takes in a parse() function to let you turn the string → number. i’ve found it to be nice for dealing with numbers and validating enums it comes with type safety + inference too: https://solito.dev/usage/params#parsing-values from Fernando's tweet

redbar0n avatar Sep 22 '22 13:09 redbar0n

react-router-url-params is another one, for typesafe query parameters. It was made this summer.

redbar0n avatar Sep 22 '22 15:09 redbar0n

My idea is that TypeScript for query params just works if we treat param serialization as implementation detail defined by the user.

// `order` is typesafe
<Link to="product-search" order={"desc"} />
// /routes.js

export default {
  routes: [
    {
      Page: '/pages/product-search.page.js',
      toUrl: ({ order }: { order: 'asc' | 'desc' }) => `/product/search?order={order}`,
      route: '/product/search'
    },
  ]
}

brillout avatar Sep 26 '22 15:09 brillout

I'm a big fan of Tanstack React Location and upcoming Tanstack Router. I think it's API is quite well thought. See this single route file example from Tanstack Router: https://github.com/TanStack/router/blob/572318b1b132307be02857b97ae05b2040363ccf/examples/kitchen-sink/src/index.tsx#L91

I really like how React components can be used instead of file path as proposed in this issue. Also, pending state feature sounds incredible.

patryk-smc avatar Sep 26 '22 15:09 patryk-smc

The API of React Location / TanStack Router looks a lot like the API of (Remix and) React Router v6 : https://gist.github.com/redbar0n/f6ec12264ff9d58e243cc516e4e3f41b?permalink_comment_id=4312122#gistcomment-4312122

TanStack Router has the same issue as React Router mentioned there:

the component tree custody is duplicated: once in React and once in React Router... Because React Router directly drives React.

redbar0n avatar Sep 27 '22 08:09 redbar0n

The overall design is starting to crystalize (it also includes better support for nested layouts).

One open question: https://stackoverflow.com/questions/73957822/get-import-path-of-dynamic-import-as-typescript-string-type, in case someone knows this. I don't think it's possible, but who knows with TypeScript 😅.

brillout avatar Oct 05 '22 11:10 brillout

blitz-js has another way to achieve typesafe routing which looks quite neat:

https://twitter.com/aleksandrasays/status/1559157742559825920?s=20&t=uaV2ruG_UcOlWxDwGhk9yQ

redbar0n avatar Oct 05 '22 23:10 redbar0n

blitz-js has another way to achieve typesafe routing which looks quite neat:

https://twitter.com/aleksandrasays/status/1559157742559825920?s=20&t=uaV2ruG_UcOlWxDwGhk9yQ

👍 Neat idea to make the link target a TS object, so you can definition-jump directly to the route file. Neat.

brillout avatar Oct 07 '22 14:10 brillout

@patryk-smc @brillout here's an idea for a simplified API which presents the routes in a flat manner so you can more easily visually pattern-match to the URL in the browser (compared to the nesting in React Router and TanStack Router):

https://gist.github.com/redbar0n/f6ec12264ff9d58e243cc516e4e3f41b?permalink_comment_id=4343793#gistcomment-4343793

redbar0n avatar Oct 21 '22 14:10 redbar0n

@redbar0n Yes I'm also not too fond of their verbosity. While I share the sentiment to keep things succinct, I'm thinking multi-dimensional arrays are a bit too extreme, i.e. not explicit enough. Let's see if the solution I come up with is succinct enough (so far I think so).

brillout avatar Oct 24 '22 09:10 brillout

Ok, excited to see how it will look. I wonder how much explicitness is needed if we can have some Convention over Configuration.

redbar0n avatar Oct 24 '22 13:10 redbar0n

Coming from React Router I use their Javascript Route Definitions which allows me to dynamically create and pre-filter routes, and render navigation from the definitions.

robinelvin avatar Oct 28 '22 14:10 robinelvin

For folks who need/want Single Route File, please PM on discord.

brillout avatar Oct 29 '22 17:10 brillout

FFR, here is a thread with people who don't like file based routing: https://twitter.com/t3dotgg/status/1583285667768827904

redbar0n avatar Feb 01 '23 15:02 redbar0n

I think SolidStart is taking a great approach with the dynamic <FileRoutes /> component https://start.solidjs.com/core-concepts/routing

CanRau avatar Feb 02 '23 03:02 CanRau

I think SolidStart is taking a great approach with the dynamic <FileRoutes /> component https://start.solidjs.com/core-concepts/routing

Yeah, I agree, with one exception. To quote the SolidStart approach:

Under the hood, SolidStart traverses your routes directory, collects all the routes, and makes them accessible using the <FileRoutes /> component. The <FileRoutes /> component only includes your UI routes, and not your API routes. You can use it instead of manually entering all your Routes inside the <Routes /> component in root.tsx. Let the compiler do the boring work!

I think it makes more sense to expose the file routes through some other means than as a UI component. Maybe as a function (e.g. import getFileSystemRoutes() like the getRoute that next-type-safe-routes does).

You then use it to import the default routes derived from the file system, which you are then able to overwrite in the routes file. Or you could choose to not import it and rather manually write out all the routes if you'd like. I think it could be the best of both worlds: convenience of filesystem routing (aka. file based routes) + the power and customizability of a single route file.

Avoiding having it as a UI component would also allow vps to remain render library agnostic.

redbar0n avatar Feb 03 '23 17:02 redbar0n

I'm actually thinking of increasingly focusing on single route files, and I'm increasingly thinking that it's superior than Next/Solid/Remix's approach. That said, so far, I think still supporting basic FS routing makes sense (like what VPS already does today), but I'll likely won't focus much on it.

brillout avatar Feb 03 '23 17:02 brillout

TanStack Router vs. React Router DOM vs. Next.js comparison page is out, as per demand. It has a good overview of potentially desirable features for a routing solution.

redbar0n avatar Mar 08 '23 13:03 redbar0n

TanStack Router vs. React Router DOM vs. Next.js comparison page is out, as per demand. It has a good overview of potentially desirable features for a routing solution.

Other than typesafe links (which is quite lovely), is there something you'd miss if using VPS's built-in router?

brillout avatar Mar 09 '23 20:03 brillout

Not that I can think of (the search params API seems neat, but not sure if it's in scope).

The ability to easily swap out the router and use any router would be nice. Even if it required a one-time mapping from the vps router to that router (say, for SSR).

redbar0n avatar Mar 10 '23 11:03 redbar0n

easily swap out the router

In principle, it should be possible to deeply integrate any router using the onBeforeRoute() hook.

But, tbh and AFAICT, I see VPS's router to be superior for its intended purpose than what I'm seeing so far.

The only thing missing is typesafe links, which require either code gen or opting-out of Filesystem Routing (i.e. what this ticket is about). (AFAICT TypeScript types can't be deduced from the mere presence of a file, so it's either or.) I think it's possible to do code gen in a seamless manner without the user even noticing.

brillout avatar Mar 10 '23 11:03 brillout

Ticket for typesafe links: https://github.com/brillout/vite-plugin-ssr/issues/698.

brillout avatar Mar 11 '23 12:03 brillout

Now that the V1 design is out, this ticket should be fairly easy to implement.

To increase the priority of this ticket, add a comment here with the reasons why you want this.

brillout avatar May 02 '23 14:05 brillout