From 666f017e636ececd7cdc5f4bdff095ca9d11dfa4 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Thu, 5 Sep 2024 15:19:20 +0200 Subject: [PATCH] WIP optional static segments and splat paths --- .../sdk/wasp/client/router/linkHelpers.ts | 3 ++ .../templates/sdk/wasp/client/router/types.ts | 41 ++++++++++++++++++- waspc/examples/todoApp/main.wasp | 2 +- waspc/examples/todoApp/src/App.tsx | 6 ++- .../SdkGenerator/Client/RouterGenerator.hs | 6 +-- waspc/src/Wasp/Util/WebRouterPath.hs | 3 ++ 6 files changed, 55 insertions(+), 6 deletions(-) diff --git a/waspc/data/Generator/templates/sdk/wasp/client/router/linkHelpers.ts b/waspc/data/Generator/templates/sdk/wasp/client/router/linkHelpers.ts index e10cba9281..c12c36125e 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/router/linkHelpers.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/router/linkHelpers.ts @@ -17,6 +17,9 @@ export function interpolatePath( function interpolatePathParams(path: string, params: Params) { function mapPathPart(part: string) { + if (part === '*') { + return params.splat; + } if (part.startsWith(":")) { const paramName = extractParamNameFromPathPart(part); return params[paramName]; diff --git a/waspc/data/Generator/templates/sdk/wasp/client/router/types.ts b/waspc/data/Generator/templates/sdk/wasp/client/router/types.ts index 71c6a3812d..4de10febd6 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/router/types.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/router/types.ts @@ -21,7 +21,7 @@ export type Search = type RouteDefinitionsToRoutesObj = { [K in keyof Routes]: { - to: Routes[K]['to'] + to: GenerateRoute } & ParamsFromBuildFn } @@ -39,3 +39,42 @@ type ParamsFromBuildFn = Parameters[0] extends { } ? { params: Params } : { params?: never } + +// Optional static segments handling + + +type SplitPath = S extends `${infer T}/${infer U}` + ? [T, ...SplitPath] + : [S] + +type DropEmptyStrings = T extends [infer Head, ...infer Tail] + ? Head extends '' + ? [...DropEmptyStrings] + : [ExplodeOptionalStatic, ...DropEmptyStrings] + : T + +type ExplodeOptionalStatic = T extends `:${infer P}` + ? T + : T extends `${infer S}?` + ? [S, ''] + : T + +type Elem = string | [string, string] + +type JoinSegments = T extends [] + ? [] + : T extends [infer First extends Elem, ...infer Rest extends Elem[]] + ? First extends string + ? [First, ...JoinSegments] + : [First[0], ...JoinSegments] | JoinSegments + : [] + +type JoinPath = T extends [infer Only extends string] + ? Only + : T extends [infer First extends string, ...infer Rest extends string[]] + ? `${First}/${JoinPath}` + : never + +type GenerateRoute = S extends '/' + ? '/' + : `/${JoinPath>>>>}` diff --git a/waspc/examples/todoApp/main.wasp b/waspc/examples/todoApp/main.wasp index 0043e397b4..9fa9f246bf 100644 --- a/waspc/examples/todoApp/main.wasp +++ b/waspc/examples/todoApp/main.wasp @@ -109,7 +109,7 @@ page AboutPage { component: import About from "@src/pages/About" } -route ProfileRoute { path: "/profile", to: ProfilePage } +route ProfileRoute { path: "/profile/extra?", to: ProfilePage } page ProfilePage { authRequired: true, component: import { ProfilePage } from "@src/pages/ProfilePage" diff --git a/waspc/examples/todoApp/src/App.tsx b/waspc/examples/todoApp/src/App.tsx index 35b1b4442d..acec24e80e 100644 --- a/waspc/examples/todoApp/src/App.tsx +++ b/waspc/examples/todoApp/src/App.tsx @@ -24,7 +24,11 @@ export function App({ children }: any) {

- {appName} + + {appName} + + + {/* Filip */}

Your site was loaded at: {date?.toLocaleString()} {connectionIcon} diff --git a/waspc/src/Wasp/Generator/SdkGenerator/Client/RouterGenerator.hs b/waspc/src/Wasp/Generator/SdkGenerator/Client/RouterGenerator.hs index 57875a355f..e84384fd91 100644 --- a/waspc/src/Wasp/Generator/SdkGenerator/Client/RouterGenerator.hs +++ b/waspc/src/Wasp/Generator/SdkGenerator/Client/RouterGenerator.hs @@ -17,7 +17,7 @@ import Wasp.Util.WebRouterPath (Param (Optional, Required), extractPathParams) genNewClientRouterApi :: AppSpec -> Generator [FileDraft] genNewClientRouterApi spec = sequence - [ genRouterTsx spec, + [ genIndexTs spec, genFileCopy [relfile|client/router/types.ts|], genFileCopy [relfile|client/router/linkHelpers.ts|], genFileCopy [relfile|client/router/Link.tsx|] @@ -25,8 +25,8 @@ genNewClientRouterApi spec = where genFileCopy = return . C.mkTmplFd -genRouterTsx :: AppSpec -> Generator FileDraft -genRouterTsx spec = return $ C.mkTmplFdWithData [relfile|client/router/index.ts|] tmplData +genIndexTs :: AppSpec -> Generator FileDraft +genIndexTs spec = return $ C.mkTmplFdWithData [relfile|client/router/index.ts|] tmplData where tmplData = object ["routes" .= map createRouteTemplateData (AS.getRoutes spec)] diff --git a/waspc/src/Wasp/Util/WebRouterPath.hs b/waspc/src/Wasp/Util/WebRouterPath.hs index e1c5dd51fe..5f22943549 100644 --- a/waspc/src/Wasp/Util/WebRouterPath.hs +++ b/waspc/src/Wasp/Util/WebRouterPath.hs @@ -7,10 +7,13 @@ import Data.Maybe (mapMaybe) data Param = Optional String | Required String deriving (Show, Eq) -- TODO: upgrade to work with React Router v6: https://reactrouter.com/en/main/route/route#splats +-- Maybe explode all optional segments and then compute the routes for the Link component +-- This would mean we have two different lists: routes and links? extractPathParams :: String -> [Param] extractPathParams = mapMaybe parseParam . splitOn "/" where parseParam :: String -> Maybe Param + parseParam "*" = Just $ Required "splat" parseParam (':' : xs) = Just $ if "?" `isSuffixOf` xs