Skip to content

Commit

Permalink
Lazy load routes
Browse files Browse the repository at this point in the history
  • Loading branch information
infomiho committed Sep 11, 2024
1 parent d2b9176 commit f65f929
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 48 deletions.
12 changes: 6 additions & 6 deletions waspc/data/Generator/templates/react-app/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import createAuthRequiredPage from "./auth/pages/createAuthRequiredPage"
{=/ isAuthEnabled =}

{=# pagesToImport =}
{=& importStatement =}
{=/ pagesToImport =}

{=# isExternalAuthEnabled =}
import { OAuthCallbackPage } from "./auth/pages/OAuthCallback"
{=/ isExternalAuthEnabled =}
Expand All @@ -21,7 +17,11 @@ import { routes } from 'wasp/client/router'

export const routeNameToRouteComponent = {
{=# routes =}
{= name =}: {= targetComponent =},
{= name =}: async () => {
{=& importExpr =}
{=# isAuthRequired =}return { Component: createAuthRequiredPage({= targetComponent =}) }{=/ isAuthRequired =}
{=^ isAuthRequired =}return { Component: {= targetComponent =} }{=/ isAuthRequired =}
},
{=/ routes =}
} as const;

Expand All @@ -36,7 +36,7 @@ const waspDefinedRoutes = [
const userDefinedRoutes = Object.entries(routes).map(([routeKey, route]) => {
return {
path: route.to,
Component: routeNameToRouteComponent[routeKey],
lazy: routeNameToRouteComponent[routeKey],
}
})

Expand Down
55 changes: 18 additions & 37 deletions waspc/src/Wasp/Generator/WebAppGenerator/RouterGenerator.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import qualified Wasp.AppSpec as AS
import qualified Wasp.AppSpec.App as AS.App
import qualified Wasp.AppSpec.App.Auth as AS.App.Auth
import qualified Wasp.AppSpec.App.Client as AS.App.Client
import qualified Wasp.AppSpec.ExtImport as AS.ExtImport
import qualified Wasp.AppSpec.Page as AS.Page
import qualified Wasp.AppSpec.Route as AS.Route
import Wasp.AppSpec.Valid (getApp, isAuthEnabled)
Expand All @@ -27,11 +26,10 @@ import Wasp.Generator.Monad (Generator)
import Wasp.Generator.WebAppGenerator.Common (asTmplFile, asWebAppSrcFile)
import qualified Wasp.Generator.WebAppGenerator.Common as C
import Wasp.Generator.WebAppGenerator.JsImport (extImportToImportJson, extImportToJsImport)
import Wasp.JsImport (applyJsImportAlias, getJsImportStmtAndIdentifier)
import Wasp.JsImport (applyJsImportAlias, getDynamicJsImportExprAndIdentifier)

data RouterTemplateData = RouterTemplateData
{ _routes :: ![RouteTemplateData],
_pagesToImport :: ![PageTemplateData],
_isAuthEnabled :: Bool,
_isExternalAuthEnabled :: Bool,
_rootComponent :: Aeson.Value,
Expand All @@ -42,7 +40,6 @@ instance ToJSON RouterTemplateData where
toJSON routerTD =
object
[ "routes" .= _routes routerTD,
"pagesToImport" .= _pagesToImport routerTD,
"isAuthEnabled" .= _isAuthEnabled routerTD,
"isExternalAuthEnabled" .= _isExternalAuthEnabled routerTD,
"rootComponent" .= _rootComponent routerTD,
Expand All @@ -52,14 +49,18 @@ instance ToJSON RouterTemplateData where

data RouteTemplateData = RouteTemplateData
{ _routeName :: !String,
_targetComponent :: !String
_importExpr :: !String,
_targetComponent :: !String,
_isAuthRequired :: !Bool
}

instance ToJSON RouteTemplateData where
toJSON routeTD =
object
[ "name" .= _routeName routeTD,
"targetComponent" .= _targetComponent routeTD
"importExpr" .= _importExpr routeTD,
"targetComponent" .= _targetComponent routeTD,
"isAuthRequired" .= _isAuthRequired routeTD
]

data PageTemplateData = PageTemplateData
Expand Down Expand Up @@ -95,36 +96,31 @@ createRouterTemplateData :: AppSpec -> RouterTemplateData
createRouterTemplateData spec =
RouterTemplateData
{ _routes = routes,
_pagesToImport = pages,
-- _pagesToImport = pages,
_isAuthEnabled = isAuthEnabled spec,
_isExternalAuthEnabled = (AS.App.Auth.isExternalAuthEnabled <$> maybeAuth) == Just True,
_rootComponent = extImportToImportJson relPathToWebAppSrcDir maybeRootComponent,
_baseDir = SP.fromAbsDirP $ C.getBaseDir spec
}
where
routes = map (createRouteTemplateData spec) $ AS.getRoutes spec
pages = map createPageTemplateData $ AS.getPages spec
-- pages = map createPageTemplateData $ AS.getPages spec
maybeAuth = AS.App.auth $ snd $ getApp spec
maybeRootComponent = AS.App.Client.rootComponent =<< AS.App.client (snd $ getApp spec)

createRouteTemplateData :: AppSpec -> (String, AS.Route.Route) -> RouteTemplateData
createRouteTemplateData spec namedRoute@(name, _) =
createRouteTemplateData spec (name, route) =
RouteTemplateData
{ _routeName = name,
_targetComponent = determineRouteTargetComponent spec namedRoute
_importExpr = importExpr,
_targetComponent = importIdentifier,
_isAuthRequired = isAuthRequired
}

-- NOTE: This should be prevented by Analyzer, so use error since it should not be possible
determineRouteTargetComponent :: AppSpec -> (String, AS.Route.Route) -> String
determineRouteTargetComponent spec (_, route) =
maybe
targetPageName
determineRouteTargetComponent'
(AS.Page.authRequired $ snd targetPage)
where
targetPageName = AS.refName (AS.Route.to route :: AS.Ref AS.Page.Page)
targetPage =
fromMaybe
-- NOTE: This should be prevented by Analyzer, so use error since it should not be possible
( error $
"Can't find page with name '"
++ targetPageName
Expand All @@ -133,28 +129,13 @@ determineRouteTargetComponent spec (_, route) =
++ "'"
)
(find ((==) targetPageName . fst) (AS.getPages spec))
isAuthRequired = AS.Page.authRequired (snd targetPage) == Just True

determineRouteTargetComponent' :: Bool -> String
determineRouteTargetComponent' authRequired =
if authRequired
then -- TODO(matija): would be nicer if this function name wasn't hardcoded here.
"createAuthRequiredPage(" ++ targetPageName ++ ")"
else targetPageName

createPageTemplateData :: (String, AS.Page.Page) -> PageTemplateData
createPageTemplateData page =
PageTemplateData
{ _importStmt = importStmt
}
where
importStmt :: String
(importStmt, _) = getJsImportStmtAndIdentifier $ applyJsImportAlias (Just importAlias) $ extImportToJsImport relPathToWebAppSrcDir pageComponent
(importExpr, importIdentifier) = getDynamicJsImportExprAndIdentifier $ applyJsImportAlias (Just importAlias) $ extImportToJsImport relPathToWebAppSrcDir pageComponent

pageComponent :: AS.ExtImport.ExtImport
pageComponent = AS.Page.component $ snd page
pageComponent = AS.Page.component $ snd targetPage

importAlias :: String
importAlias = fst page
importAlias = fst targetPage

relPathToWebAppSrcDir :: Path Posix (Rel importLocation) (Dir C.WebAppSrcDir)
relPathToWebAppSrcDir = [reldirP|./|]
47 changes: 42 additions & 5 deletions waspc/src/Wasp/JsImport.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Wasp.JsImport
applyJsImportAlias,
getImportIdentifier,
getJsImportStmtAndIdentifier,
getJsImportStmtAndIdentifierRaw,
getDynamicJsImportExprAndIdentifier,
)
where

Expand Down Expand Up @@ -58,6 +58,9 @@ type JsImportClause = String
-- | Represents the full import statement e.g. @import { Name } from "file.js"@
type JsImportStatement = String

-- | Represents the object destructuring part of a dynamic import statement e.g. @const { Name } = await import("file.js")@
type JsImportObject = String

getImportIdentifier :: JsImport -> JsImportIdentifier
getImportIdentifier JsImport {_name = name} = case name of
JsImportModule identifier -> identifier
Expand All @@ -71,12 +74,13 @@ applyJsImportAlias importAlias jsImport = jsImport {_importAlias = importAlias}

getJsImportStmtAndIdentifier :: JsImport -> (JsImportStatement, JsImportIdentifier)
getJsImportStmtAndIdentifier (JsImport importPath importName maybeImportAlias) =
getJsImportStmtAndIdentifierRaw filePath importName maybeImportAlias
getJsImportStmtAndIdentifierRaw (getImportFilePath importPath) importName maybeImportAlias

getImportFilePath :: JsImportPath -> FilePath
getImportFilePath (RelativeImportPath relPath) = normalizePath $ SP.fromRelFileP relPath
where
filePath = case importPath of
RelativeImportPath relPath -> normalizePath $ SP.fromRelFileP relPath
ModuleImportPath pathString -> SP.fromRelFileP pathString
normalizePath path = if ".." `isPrefixOf` path then path else "./" ++ path
getImportFilePath (ModuleImportPath pathString) = SP.fromRelFileP pathString

-- todo(filip): attempt to simplify how we generate imports. I wanted to generate a
-- module import (e.g., '@ext-src/something') and couldn't do it. This is one of
Expand Down Expand Up @@ -110,3 +114,36 @@ getJsImportStmtAndIdentifierRaw importPath importName maybeImportAlias =
where
resolvedIdentifier =
if identifier == importAlias then identifier else identifier ++ " as " ++ importAlias

getDynamicJsImportExprAndIdentifier :: JsImport -> (JsImportStatement, JsImportIdentifier)
getDynamicJsImportExprAndIdentifier (JsImport importPath importName maybeImportAlias) =
getDynamicJsImportExprAndIdentifierRaw (getImportFilePath importPath) importName maybeImportAlias

getDynamicJsImportExprAndIdentifierRaw ::
FilePath ->
JsImportName ->
Maybe JsImportAlias ->
(JsImportStatement, JsImportIdentifier)
getDynamicJsImportExprAndIdentifierRaw importPath importName maybeImportAlias =
(importExpr, importIdentifier)
where
(importIdentifier, importObject) = jsImportIdentifierAndObject
importExpr = "const " ++ importObject ++ " = await import('" ++ importPath ++ "')"

-- First part of a dynamic import statement e.g. @{ default: Name }@
-- in @const { default: Name } = await import("file.js")@
jsImportIdentifierAndObject :: (JsImportIdentifier, JsImportObject)
jsImportIdentifierAndObject = case importName of
JsImportModule defaultImport -> getForDefault defaultImport maybeImportAlias
JsImportField namedImport -> getForNamed namedImport maybeImportAlias
where
getForDefault :: JsImportIdentifier -> Maybe JsImportAlias -> (JsImportIdentifier, JsImportObject)
getForDefault identifier Nothing = (identifier, "{ default: " ++ identifier ++ " }")
getForDefault _ (Just importAlias) = (importAlias, "{ default: " ++ importAlias ++ " }")

getForNamed :: JsImportIdentifier -> Maybe JsImportAlias -> (JsImportIdentifier, JsImportObject)
getForNamed identifier Nothing = (identifier, "{ " ++ identifier ++ " }")
getForNamed identifier (Just importAlias) = (importAlias, "{ " ++ resolvedIdentifier ++ " }")
where
resolvedIdentifier =
if identifier == importAlias then identifier else identifier ++ ": " ++ importAlias

0 comments on commit f65f929

Please sign in to comment.