Skip to content

Commit

Permalink
Update .build route helper
Browse files Browse the repository at this point in the history
  • Loading branch information
infomiho committed Sep 9, 2024
1 parent fc541ab commit 389713f
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 34 deletions.
17 changes: 7 additions & 10 deletions waspc/data/Generator/templates/sdk/wasp/client/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,22 @@ import type {
RouteDefinitionsToRoutes,
OptionalRouteOptions,
ParamValue,
ExpandRouteOnOptionalStaticSegments,
} from './types'

// PUBLIC API
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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>>>>}`

Expand Down
15 changes: 9 additions & 6 deletions waspc/src/Wasp/Generator/SdkGenerator/Client/RouterGenerator.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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]
49 changes: 33 additions & 16 deletions waspc/src/Wasp/Util/WebRouterPath.hs
Original file line number Diff line number Diff line change
@@ -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 "?"

0 comments on commit 389713f

Please sign in to comment.