Update .build route helper

This commit is contained in:
Mihovil Ilakovac 2024-09-09 16:24:21 +02:00
parent fc541ab861
commit 389713fe2e
4 changed files with 52 additions and 34 deletions

View File

@ -4,6 +4,7 @@ import type {
RouteDefinitionsToRoutes,
OptionalRouteOptions,
ParamValue,
ExpandRouteOnOptionalStaticSegments,
} from './types'
// PUBLIC API
@ -11,18 +12,14 @@ export const routes = {
{=# routes =}
{= name =}: {
to: "{= urlPath =}",
{=# hasUrlParams =}
build: (
options: {
options{=^ hasUrlParams =}?{=/ hasUrlParams =}:
OptionalRouteOptions
{=# hasUrlParams =}& {
params: {{=# urlParams =}{= name =}{=# isOptional =}?{=/ isOptional =}: ParamValue;{=/ urlParams =}}
} & OptionalRouteOptions,
) => interpolatePath("{= urlPath =}", options.params, options?.search, options?.hash),
{=/ hasUrlParams =}
{=^ hasUrlParams =}
build: (
options?: OptionalRouteOptions,
) => interpolatePath("{= urlPath =}", undefined, options?.search, options?.hash),
{=/ hasUrlParams =}
}{=/ hasUrlParams =}
{=# hasOptionalStaticSegments =}& { path: ExpandRouteOnOptionalStaticSegments<"{= urlPath =}"> }{=/ hasOptionalStaticSegments =}
) => interpolatePath({=# hasOptionalStaticSegments =}options.path{=/ hasOptionalStaticSegments =}{=^ hasOptionalStaticSegments =}"{= urlPath =}"{=/ hasOptionalStaticSegments =}, {=^ hasUrlParams =}undefined{=/ hasUrlParams =}{=# hasUrlParams =}options.params{=/ hasUrlParams =}, options?.search, options?.hash),
},
{=/ routes =}
} as const;

View File

@ -2,7 +2,7 @@
export type RouteDefinitionsToRoutes<Routes extends RoutesDefinition> =
RouteDefinitionsToRoutesObj<Routes>[keyof RouteDefinitionsToRoutesObj<Routes>]
// PRIVATE API
// PRIVATE API
export type OptionalRouteOptions = {
search?: Search
hash?: string
@ -40,6 +40,7 @@ type ParamsFromBuildFn<BF extends BuildFn> = Parameters<BF>[0] extends {
? { params: Params }
: { params?: never }
// PRIVATE API (sdk)
/**
* Optional static segments handling: expands routes with optional segments
* into multiple routes, one for each possible combination of optional segments.
@ -48,7 +49,7 @@ type ParamsFromBuildFn<BF extends BuildFn> = Parameters<BF>[0] extends {
* - /users/:id
* - /users/tasks/:id
*/
type ExpandRouteOnOptionalStaticSegments<S extends string> = S extends '/'
export type ExpandRouteOnOptionalStaticSegments<S extends string> = S extends '/'
? '/'
: `/${JoinPath<JoinSegments<ExpandOptionalSegments<SplitPath<S>>>>}`

View File

@ -12,7 +12,7 @@ import qualified Wasp.AppSpec.Route as AS.Route
import Wasp.Generator.FileDraft (FileDraft)
import Wasp.Generator.Monad (Generator)
import qualified Wasp.Generator.SdkGenerator.Common as C
import Wasp.Util.WebRouterPath (Param (Optional, Required), extractPathParams)
import qualified Wasp.Util.WebRouterPath as WebRouterPath
genNewClientRouterApi :: AppSpec -> Generator [FileDraft]
genNewClientRouterApi spec =
@ -37,13 +37,16 @@ createRouteTemplateData (name, route) =
[ "name" .= name,
"urlPath" .= path,
"urlParams" .= map mapPathParamToJson urlParams,
"hasUrlParams" .= (not . null $ urlParams)
"hasUrlParams" .= (not . null $ urlParams),
"hasOptionalStaticSegments" .= (not . null $ optionalStaticSegments)
]
where
path = AS.Route.path route
urlParams = extractPathParams path
routeSegments = WebRouterPath.getRouteSegments path
urlParams = [param | WebRouterPath.ParamSegment param <- routeSegments]
optionalStaticSegments = [segment | (WebRouterPath.StaticSegment (WebRouterPath.OptionalStaticSegment segment)) <- routeSegments]
mapPathParamToJson :: Param -> Aeson.Value
mapPathParamToJson (Required paramName) = object ["name" .= paramName, "isOptional" .= False]
mapPathParamToJson (Optional paramName) = object ["name" .= paramName, "isOptional" .= True]
mapPathParamToJson :: WebRouterPath.ParamSegment -> Aeson.Value
mapPathParamToJson (WebRouterPath.RequiredParamSegment paramName) = object ["name" .= paramName, "isOptional" .= False]
mapPathParamToJson (WebRouterPath.OptionalParamSegment paramName) = object ["name" .= paramName, "isOptional" .= True]

View File

@ -1,22 +1,39 @@
module Wasp.Util.WebRouterPath where
module Wasp.Util.WebRouterPath
( Segment (StaticSegment, ParamSegment),
StaticSegment (RequiredStaticSegment, OptionalStaticSegment),
ParamSegment (RequiredParamSegment, OptionalParamSegment),
getRouteSegments,
)
where
import Data.List (isSuffixOf)
import Data.List.Split (splitOn)
import Data.Maybe (mapMaybe)
data Param = Optional String | Required String deriving (Show, Eq)
data Segment = StaticSegment StaticSegment | ParamSegment ParamSegment 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 "/"
data StaticSegment = RequiredStaticSegment StaticSegmentValue | OptionalStaticSegment StaticSegmentValue deriving (Show, Eq)
data ParamSegment = RequiredParamSegment ParamName | OptionalParamSegment ParamName deriving (Show, Eq)
type StaticSegmentValue = String
type ParamName = String
getRouteSegments :: String -> [Segment]
getRouteSegments = map parseSegment . splitOn "/"
where
parseParam :: String -> Maybe Param
parseParam "*" = Just $ Required "splat"
parseParam (':' : xs) =
Just $
if "?" `isSuffixOf` xs
then Optional (take (length xs - 1) xs)
else Required xs
parseParam _ = Nothing
parseSegment :: String -> Segment
parseSegment "*" = ParamSegment $ RequiredParamSegment "splat"
parseSegment (':' : xs) =
ParamSegment $
if isSegmentOptional xs
then OptionalParamSegment (take (length xs - 1) xs)
else RequiredParamSegment xs
parseSegment x =
StaticSegment $
if isSegmentOptional x
then OptionalStaticSegment x
else RequiredStaticSegment x
isSegmentOptional :: String -> Bool
isSegmentOptional = isSuffixOf "?"