diff --git a/reflex/.templates/jinja/web/vite.config.js.jinja2 b/reflex/.templates/jinja/web/vite.config.js.jinja2 index efd12c672b9..4dfc723d9e5 100644 --- a/reflex/.templates/jinja/web/vite.config.js.jinja2 +++ b/reflex/.templates/jinja/web/vite.config.js.jinja2 @@ -25,13 +25,13 @@ function alwaysUseReactDomServerNode() { } export default defineConfig((config) => ({ - base: "{{base}}", plugins: [ alwaysUseReactDomServerNode(), reactRouter(), safariCacheBustPlugin(), ], build: { + assetsDir: "{{base}}assets".slice(1), rollupOptions: { jsx: {}, output: { diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 9a317cf9ef4..38d33773291 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -969,17 +969,7 @@ export const useEventLoop = ( useEffect(() => { if (!sentHydrate.current) { queueEvents( - initial_events().map((e) => ({ - ...e, - router_data: { - pathname: location.pathname, - query: { - ...Object.fromEntries(searchParams.entries()), - ...params.current, - }, - asPath: location.pathname + location.search, - }, - })), + initial_events(), socket, true, navigate, diff --git a/reflex/app.py b/reflex/app.py index 7d24c451ba9..7aba623a4c6 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -862,7 +862,7 @@ def router(self) -> Callable[[str], str | None]: """ from reflex.route import get_router - return get_router(list(self._unevaluated_pages)) + return get_router(list(dict.fromkeys([*self._unevaluated_pages, *self._pages]))) def get_load_events(self, path: str) -> list[IndividualEventType[()]]: """Get the load events for a route. diff --git a/reflex/route.py b/reflex/route.py index 44252f63502..761ec8f974d 100644 --- a/reflex/route.py +++ b/reflex/route.py @@ -6,6 +6,7 @@ from collections.abc import Callable from reflex import constants +from reflex.config import get_config def verify_route_validity(route: str) -> None: @@ -211,6 +212,9 @@ def get_route(path: str) -> str | None: Returns: The first matching route, or None if no match is found. """ + config = get_config() + if config.frontend_path: + path = path.removeprefix(config.frontend_path) path = "/" + path.removeprefix("/").removesuffix("/") if path == "/index": path = "/" diff --git a/reflex/state.py b/reflex/state.py index 639fb3dbbef..4240ee010c2 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -2491,7 +2491,7 @@ def on_load_internal(self) -> list[Event | EventSpec | event.EventCallback] | No # Cache the app reference for subsequent calls. if type(self)._app_ref is None: type(self)._app_ref = app - load_events = app.get_load_events(self.router._page.path) + load_events = app.get_load_events(self.router.url.path) if not load_events: self.is_hydrated = True return None # Fast path for navigation with no on_load events defined. diff --git a/reflex/utils/build.py b/reflex/utils/build.py index fb77db39376..63d00d60815 100644 --- a/reflex/utils/build.py +++ b/reflex/utils/build.py @@ -4,11 +4,12 @@ import os import zipfile -from pathlib import Path +from pathlib import Path, PosixPath from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn from reflex import constants +from reflex.config import get_config from reflex.utils import console, js_runtimes, path_ops, prerequisites, processes from reflex.utils.exec import is_in_app_harness @@ -205,6 +206,19 @@ def build(): wdir / constants.Dirs.STATIC / "404.html", ) + config = get_config() + + if frontend_path := config.frontend_path.strip("/"): + frontend_path = PosixPath(frontend_path) + first_part = frontend_path.parts[0] + for child in list((wdir / constants.Dirs.STATIC).iterdir()): + if child.is_dir() and child.name == first_part: + continue + path_ops.mv( + child, + wdir / constants.Dirs.STATIC / frontend_path / child.name, + ) + def setup_frontend( root: Path, diff --git a/reflex/utils/frontend_skeleton.py b/reflex/utils/frontend_skeleton.py index 5891024a0a1..d3e9f69256b 100644 --- a/reflex/utils/frontend_skeleton.py +++ b/reflex/utils/frontend_skeleton.py @@ -149,8 +149,12 @@ def update_react_router_config(prerender_routes: bool = False): def _update_react_router_config(config: Config, prerender_routes: bool = False): + basename = "/" + (config.frontend_path or "").strip("/") + if not basename.endswith("/"): + basename += "/" + react_router_config = { - "basename": "/" + (config.frontend_path or "").removeprefix("/"), + "basename": basename, "future": { "unstable_optimizeDeps": True, }, diff --git a/tests/units/test_app.py b/tests/units/test_app.py index 7f955adb926..64fd149b123 100644 --- a/tests/units/test_app.py +++ b/tests/units/test_app.py @@ -1061,7 +1061,12 @@ def _event(name, val, **kwargs): token=kwargs.pop("token", token), name=name, router_data=kwargs.pop( - "router_data", {"pathname": "/" + route, "query": {arg_name: val}} + "router_data", + { + "pathname": "/" + route, + "query": {arg_name: val}, + "asPath": "/test/something", + }, ), payload=kwargs.pop("payload", {}), **kwargs, diff --git a/tests/units/test_prerequisites.py b/tests/units/test_prerequisites.py index 028afc7e6f5..131904e964c 100644 --- a/tests/units/test_prerequisites.py +++ b/tests/units/test_prerequisites.py @@ -43,7 +43,7 @@ frontend_path="/test", ), False, - 'export default {"basename": "/test", "future": {"unstable_optimizeDeps": true}, "ssr": false};', + 'export default {"basename": "/test/", "future": {"unstable_optimizeDeps": true}, "ssr": false};', ), ( Config( @@ -67,21 +67,21 @@ def test_update_react_router_config(config, export, expected_output): app_name="test", frontend_path="", ), - 'base: "/",', + 'assetsDir: "/assets".slice(1),', ), ( Config( app_name="test", frontend_path="/test", ), - 'base: "/test/",', + 'assetsDir: "/test/assets".slice(1),', ), ( Config( app_name="test", frontend_path="/test/", ), - 'base: "/test/",', + 'assetsDir: "/test/assets".slice(1),', ), ], )