1111from starlette .requests import Request
1212from starlette .responses import RedirectResponse
1313from starlette .staticfiles import StaticFiles
14- from starlette .types import Scope
14+ from starlette .types import Receive , Scope , Send
1515from starlette .websockets import WebSocket , WebSocketDisconnect
1616
1717from idom .config import IDOM_WEB_MODULES_DIR
2626from idom .core .types import RootComponentConstructor
2727
2828from ._asgi import serve_development_asgi
29- from .utils import CLIENT_BUILD_DIR
29+ from .utils import CLIENT_BUILD_DIR , client_build_dir_path
3030
3131
3232logger = logging .getLogger (__name__ )
@@ -51,9 +51,12 @@ def configure(
5151 options: Options for configuring server behavior
5252 """
5353 options = options or Options ()
54- _setup_common_routes (options , app )
54+
55+ # this route should take priority so set up it up first
5556 _setup_single_view_dispatcher_route (options .url_prefix , app , constructor )
5657
58+ _setup_common_routes (options , app )
59+
5760
5861def create_development_app () -> Starlette :
5962 """Return a :class:`Starlette` app instance in debug mode"""
@@ -116,39 +119,53 @@ def _setup_common_routes(options: Options, app: Starlette) -> None:
116119 # This really should be added to the APIRouter, but there's a bug in Starlette
117120 # BUG: https://github.com/tiangolo/fastapi/issues/1469
118121 url_prefix = options .url_prefix
122+
119123 if options .serve_static_files :
120- app .mount (
121- f"{ url_prefix } /client" ,
122- StaticFiles (
123- directory = str (CLIENT_BUILD_DIR ),
124- html = True ,
125- check_dir = True ,
126- ),
127- name = "idom_client_files" ,
128- )
124+
125+ mount_serve_single_page_app_files (app , url_prefix )
126+
129127 app .mount (
130128 f"{ url_prefix } /modules" ,
131129 StaticFiles (
132130 directory = str (IDOM_WEB_MODULES_DIR .current ),
133131 html = True ,
134132 check_dir = False ,
135133 ),
136- name = "idom_web_module_files" ,
137134 )
138135
139136 if options .redirect_root :
140137
141- @app .route (f" { url_prefix } /" )
138+ @app .route (url_prefix or " /" )
142139 def redirect_to_index (request : Request ) -> RedirectResponse :
143- return RedirectResponse (
144- f"{ url_prefix } /client/index.html?{ request .query_params } "
145- )
140+ redirect_url = url_prefix + "/app"
141+ if request .query_params :
142+ redirect_url = f"{ url_prefix } /app?{ request .query_params } "
143+ else :
144+ redirect_url = f"{ url_prefix } /app"
145+ return RedirectResponse (redirect_url )
146+
147+
148+ def mount_serve_single_page_app_files (app : Starlette , url_prefix : str ) -> None :
149+ asgi_app = StaticFiles (
150+ directory = CLIENT_BUILD_DIR ,
151+ html = True ,
152+ check_dir = False ,
153+ )
154+
155+ async def spa_app (scope : Scope , receive : Receive , send : Send ) -> None :
156+ return await asgi_app (
157+ {** scope , "path" : client_build_dir_path (scope ["path" ])},
158+ receive ,
159+ send ,
160+ )
161+
162+ app .mount (url_prefix + "/app" , spa_app )
146163
147164
148165def _setup_single_view_dispatcher_route (
149166 url_prefix : str , app : Starlette , constructor : RootComponentConstructor
150167) -> None :
151- @app .websocket_route (f" { url_prefix } /stream " )
168+ @app .websocket_route (url_prefix + "/app{path:path}/_stream " )
152169 async def model_stream (socket : WebSocket ) -> None :
153170 await socket .accept ()
154171 send , recv = _make_send_recv_callbacks (socket )
0 commit comments