Skip to content

Commit b9845ba

Browse files
masenfclaudegreptile-apps[bot]
authored
fix: include frontend_path in sirv prod command for correct 404.html fallback (#6154)
* fix: include frontend_path in sirv prod command for correct 404.html fallback When frontend_path is set, the build moves all static files (including 404.html) into a subdirectory, but the sirv --single flag was hardcoded to look for 404.html at the root. This broke dynamic routes in prod. Closes #5812 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update reflex/constants/installer.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent 0b848fa commit b9845ba

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

reflex/constants/installer.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,20 @@ class Commands(SimpleNamespace):
106106

107107
DEV = "react-router dev --host"
108108
EXPORT = "react-router build"
109-
PROD = "sirv ./build/client --single 404.html --host"
109+
110+
@staticmethod
111+
def get_prod_command(frontend_path: str = "") -> str:
112+
"""Get the prod command with the correct 404.html path for the given frontend_path.
113+
114+
Args:
115+
frontend_path: The frontend path prefix (e.g. "/app").
116+
117+
Returns:
118+
The sirv command with the correct --single fallback path.
119+
"""
120+
stripped = frontend_path.strip("/")
121+
fallback = f"{stripped}/404.html" if stripped else "404.html"
122+
return f"sirv ./build/client --single {fallback} --host"
110123

111124
PATH = "package.json"
112125

reflex/utils/frontend_skeleton.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,14 @@ def _update_react_router_config(config: Config, prerender_routes: bool = False):
168168

169169

170170
def _compile_package_json():
171+
config = get_config()
171172
return templates.package_json_template(
172173
scripts={
173174
"dev": constants.PackageJson.Commands.DEV,
174175
"export": constants.PackageJson.Commands.EXPORT,
175-
"prod": constants.PackageJson.Commands.PROD,
176+
"prod": constants.PackageJson.Commands.get_prod_command(
177+
config.frontend_path
178+
),
176179
},
177180
dependencies=constants.PackageJson.DEPENDENCIES,
178181
dev_dependencies=constants.PackageJson.DEV_DEPENDENCIES,

tests/units/test_prerequisites.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
from click.testing import CliRunner
77

88
from reflex.config import Config
9+
from reflex.constants.installer import PackageJson
910
from reflex.reflex import cli
1011
from reflex.testing import chdir
1112
from reflex.utils.decorator import cached_procedure
1213
from reflex.utils.frontend_skeleton import (
14+
_compile_package_json,
1315
_compile_vite_config,
1416
_update_react_router_config,
1517
)
@@ -90,6 +92,47 @@ def test_initialise_vite_config(config, expected_output):
9092
assert expected_output in output
9193

9294

95+
@pytest.mark.parametrize(
96+
("frontend_path", "expected_command"),
97+
[
98+
("", "sirv ./build/client --single 404.html --host"),
99+
("/", "sirv ./build/client --single 404.html --host"),
100+
("/app", "sirv ./build/client --single app/404.html --host"),
101+
("/app/", "sirv ./build/client --single app/404.html --host"),
102+
("app", "sirv ./build/client --single app/404.html --host"),
103+
(
104+
"/deep/nested/path",
105+
"sirv ./build/client --single deep/nested/path/404.html --host",
106+
),
107+
],
108+
)
109+
def test_get_prod_command(frontend_path, expected_command):
110+
assert PackageJson.Commands.get_prod_command(frontend_path) == expected_command
111+
112+
113+
@pytest.mark.parametrize(
114+
("config", "expected_prod_script"),
115+
[
116+
(
117+
Config(app_name="test"),
118+
"sirv ./build/client --single 404.html --host",
119+
),
120+
(
121+
Config(app_name="test", frontend_path="/app"),
122+
"sirv ./build/client --single app/404.html --host",
123+
),
124+
(
125+
Config(app_name="test", frontend_path="/deep/nested"),
126+
"sirv ./build/client --single deep/nested/404.html --host",
127+
),
128+
],
129+
)
130+
def test_compile_package_json_prod_command(config, expected_prod_script, monkeypatch):
131+
monkeypatch.setattr("reflex.utils.frontend_skeleton.get_config", lambda: config)
132+
output = _compile_package_json()
133+
assert f'"prod": "{expected_prod_script}"' in output
134+
135+
93136
def test_cached_procedure():
94137
call_count = 0
95138

0 commit comments

Comments
 (0)