reflex 0.8.6a1__py3-none-any.whl → 0.8.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/web/vite.config.js.jinja2 +4 -1
- reflex/.templates/web/app/routes.js +0 -1
- reflex/.templates/web/utils/state.js +1 -11
- reflex/app.py +15 -23
- reflex/components/component.py +6 -4
- reflex/components/lucide/icon.py +4 -1
- reflex/components/lucide/icon.pyi +4 -1
- reflex/components/plotly/plotly.py +9 -9
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/sonner/toast.py +7 -7
- reflex/components/sonner/toast.pyi +8 -8
- reflex/config.py +9 -2
- reflex/constants/base.py +2 -0
- reflex/constants/installer.py +6 -6
- reflex/constants/state.py +1 -0
- reflex/custom_components/custom_components.py +3 -3
- reflex/reflex.py +7 -6
- reflex/route.py +4 -0
- reflex/state.py +1 -1
- reflex/testing.py +3 -5
- reflex/utils/build.py +21 -3
- reflex/utils/exec.py +11 -11
- reflex/utils/frontend_skeleton.py +254 -0
- reflex/utils/js_runtimes.py +411 -0
- reflex/utils/prerequisites.py +17 -1383
- reflex/utils/rename.py +170 -0
- reflex/utils/telemetry.py +101 -10
- reflex/utils/templates.py +443 -0
- reflex/vars/base.py +3 -3
- {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/METADATA +2 -2
- {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/RECORD +34 -33
- reflex/.templates/web/utils/client_side_routing.js +0 -45
- reflex/components/core/client_side_routing.py +0 -70
- reflex/components/core/client_side_routing.pyi +0 -68
- {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/WHEEL +0 -0
- {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/licenses/LICENSE +0 -0
reflex/testing.py
CHANGED
|
@@ -45,7 +45,7 @@ from reflex.state import (
|
|
|
45
45
|
StateManagerRedis,
|
|
46
46
|
reload_state_module,
|
|
47
47
|
)
|
|
48
|
-
from reflex.utils import console
|
|
48
|
+
from reflex.utils import console, js_runtimes
|
|
49
49
|
from reflex.utils.export import export
|
|
50
50
|
from reflex.utils.types import ASGIApp
|
|
51
51
|
|
|
@@ -403,9 +403,7 @@ class AppHarness:
|
|
|
403
403
|
# Start the frontend.
|
|
404
404
|
self.frontend_process = reflex.utils.processes.new_process(
|
|
405
405
|
[
|
|
406
|
-
*
|
|
407
|
-
0
|
|
408
|
-
],
|
|
406
|
+
*js_runtimes.get_js_package_executor(raise_on_none=True)[0],
|
|
409
407
|
"run",
|
|
410
408
|
"dev",
|
|
411
409
|
],
|
|
@@ -1010,7 +1008,7 @@ class AppHarnessProd(AppHarness):
|
|
|
1010
1008
|
/ reflex.constants.Dirs.STATIC
|
|
1011
1009
|
)
|
|
1012
1010
|
error_page_map = {
|
|
1013
|
-
404: web_root / "404
|
|
1011
|
+
404: web_root / "404.html",
|
|
1014
1012
|
}
|
|
1015
1013
|
with Subdir404TCPServer(
|
|
1016
1014
|
("", 0),
|
reflex/utils/build.py
CHANGED
|
@@ -4,12 +4,13 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import zipfile
|
|
7
|
-
from pathlib import Path
|
|
7
|
+
from pathlib import Path, PosixPath
|
|
8
8
|
|
|
9
9
|
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
|
10
10
|
|
|
11
11
|
from reflex import constants
|
|
12
|
-
from reflex.
|
|
12
|
+
from reflex.config import get_config
|
|
13
|
+
from reflex.utils import console, js_runtimes, path_ops, prerequisites, processes
|
|
13
14
|
from reflex.utils.exec import is_in_app_harness
|
|
14
15
|
|
|
15
16
|
|
|
@@ -187,7 +188,7 @@ def build():
|
|
|
187
188
|
# Start the subprocess with the progress bar.
|
|
188
189
|
process = processes.new_process(
|
|
189
190
|
[
|
|
190
|
-
*
|
|
191
|
+
*js_runtimes.get_js_package_executor(raise_on_none=True)[0],
|
|
191
192
|
"run",
|
|
192
193
|
"export",
|
|
193
194
|
],
|
|
@@ -200,6 +201,23 @@ def build():
|
|
|
200
201
|
)
|
|
201
202
|
processes.show_progress("Creating Production Build", process, checkpoints)
|
|
202
203
|
_duplicate_index_html_to_parent_dir(wdir / constants.Dirs.STATIC)
|
|
204
|
+
path_ops.cp(
|
|
205
|
+
wdir / constants.Dirs.STATIC / constants.ReactRouter.SPA_FALLBACK,
|
|
206
|
+
wdir / constants.Dirs.STATIC / "404.html",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
config = get_config()
|
|
210
|
+
|
|
211
|
+
if frontend_path := config.frontend_path.strip("/"):
|
|
212
|
+
frontend_path = PosixPath(frontend_path)
|
|
213
|
+
first_part = frontend_path.parts[0]
|
|
214
|
+
for child in list((wdir / constants.Dirs.STATIC).iterdir()):
|
|
215
|
+
if child.is_dir() and child.name == first_part:
|
|
216
|
+
continue
|
|
217
|
+
path_ops.mv(
|
|
218
|
+
child,
|
|
219
|
+
wdir / constants.Dirs.STATIC / frontend_path / child.name,
|
|
220
|
+
)
|
|
203
221
|
|
|
204
222
|
|
|
205
223
|
def setup_frontend(
|
reflex/utils/exec.py
CHANGED
|
@@ -231,17 +231,17 @@ def run_frontend(root: Path, port: str, backend_present: bool = True):
|
|
|
231
231
|
port: The port to run the frontend on.
|
|
232
232
|
backend_present: Whether the backend is present.
|
|
233
233
|
"""
|
|
234
|
-
from reflex.utils import
|
|
234
|
+
from reflex.utils import js_runtimes
|
|
235
235
|
|
|
236
236
|
# validate dependencies before run
|
|
237
|
-
|
|
237
|
+
js_runtimes.validate_frontend_dependencies(init=False)
|
|
238
238
|
|
|
239
239
|
# Run the frontend in development mode.
|
|
240
240
|
console.rule("[bold green]App Running")
|
|
241
241
|
os.environ["PORT"] = str(get_config().frontend_port if port is None else port)
|
|
242
242
|
run_process_and_launch_url(
|
|
243
243
|
[
|
|
244
|
-
*
|
|
244
|
+
*js_runtimes.get_js_package_executor(raise_on_none=True)[0],
|
|
245
245
|
"run",
|
|
246
246
|
"dev",
|
|
247
247
|
],
|
|
@@ -257,16 +257,16 @@ def run_frontend_prod(root: Path, port: str, backend_present: bool = True):
|
|
|
257
257
|
port: The port to run the frontend on.
|
|
258
258
|
backend_present: Whether the backend is present.
|
|
259
259
|
"""
|
|
260
|
-
from reflex.utils import
|
|
260
|
+
from reflex.utils import js_runtimes
|
|
261
261
|
|
|
262
262
|
# Set the port.
|
|
263
263
|
os.environ["PORT"] = str(get_config().frontend_port if port is None else port)
|
|
264
264
|
# validate dependencies before run
|
|
265
|
-
|
|
265
|
+
js_runtimes.validate_frontend_dependencies(init=False)
|
|
266
266
|
# Run the frontend in production mode.
|
|
267
267
|
console.rule("[bold green]App Running")
|
|
268
268
|
run_process_and_launch_url(
|
|
269
|
-
[*
|
|
269
|
+
[*js_runtimes.get_js_package_executor(raise_on_none=True)[0], "run", "prod"],
|
|
270
270
|
backend_present,
|
|
271
271
|
)
|
|
272
272
|
|
|
@@ -670,7 +670,7 @@ def output_system_info():
|
|
|
670
670
|
if console._LOG_LEVEL > constants.LogLevel.DEBUG:
|
|
671
671
|
return
|
|
672
672
|
|
|
673
|
-
from reflex.utils import
|
|
673
|
+
from reflex.utils import js_runtimes
|
|
674
674
|
|
|
675
675
|
config = get_config()
|
|
676
676
|
try:
|
|
@@ -684,13 +684,13 @@ def output_system_info():
|
|
|
684
684
|
|
|
685
685
|
dependencies = [
|
|
686
686
|
f"[Reflex {constants.Reflex.VERSION} with Python {platform.python_version()} (PATH: {sys.executable})]",
|
|
687
|
-
f"[Node {
|
|
687
|
+
f"[Node {js_runtimes.get_node_version()} (Minimum: {constants.Node.MIN_VERSION}) (PATH:{path_ops.get_node_path()})]",
|
|
688
688
|
]
|
|
689
689
|
|
|
690
690
|
system = platform.system()
|
|
691
691
|
|
|
692
692
|
dependencies.append(
|
|
693
|
-
f"[Bun {
|
|
693
|
+
f"[Bun {js_runtimes.get_bun_version()} (Minimum: {constants.Bun.MIN_VERSION}) (PATH: {path_ops.get_bun_path()})]"
|
|
694
694
|
)
|
|
695
695
|
|
|
696
696
|
if system == "Linux":
|
|
@@ -704,10 +704,10 @@ def output_system_info():
|
|
|
704
704
|
console.debug(f"{dep}")
|
|
705
705
|
|
|
706
706
|
console.debug(
|
|
707
|
-
f"Using package installer at: {
|
|
707
|
+
f"Using package installer at: {js_runtimes.get_nodejs_compatible_package_managers(raise_on_none=False)}"
|
|
708
708
|
)
|
|
709
709
|
console.debug(
|
|
710
|
-
f"Using package executer at: {
|
|
710
|
+
f"Using package executer at: {js_runtimes.get_js_package_executor(raise_on_none=False)}"
|
|
711
711
|
)
|
|
712
712
|
if system != "Windows":
|
|
713
713
|
console.debug(f"Unzip path: {path_ops.which('unzip')}")
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""This module provides utility functions to initialize the frontend skeleton."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import random
|
|
5
|
+
import re
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from reflex import constants
|
|
11
|
+
from reflex.compiler import templates
|
|
12
|
+
from reflex.config import Config, get_config
|
|
13
|
+
from reflex.utils import console, path_ops
|
|
14
|
+
from reflex.utils.prerequisites import get_project_hash, get_web_dir
|
|
15
|
+
from reflex.utils.registry import get_npm_registry
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def initialize_gitignore(
|
|
19
|
+
gitignore_file: Path = constants.GitIgnore.FILE,
|
|
20
|
+
files_to_ignore: set[str] | list[str] = constants.GitIgnore.DEFAULTS,
|
|
21
|
+
):
|
|
22
|
+
"""Initialize the template .gitignore file.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
gitignore_file: The .gitignore file to create.
|
|
26
|
+
files_to_ignore: The files to add to the .gitignore file.
|
|
27
|
+
"""
|
|
28
|
+
# Combine with the current ignored files.
|
|
29
|
+
current_ignore: list[str] = []
|
|
30
|
+
if gitignore_file.exists():
|
|
31
|
+
current_ignore = [ln.strip() for ln in gitignore_file.read_text().splitlines()]
|
|
32
|
+
|
|
33
|
+
if files_to_ignore == current_ignore:
|
|
34
|
+
console.debug(f"{gitignore_file} already up to date.")
|
|
35
|
+
return
|
|
36
|
+
files_to_ignore = [ln for ln in files_to_ignore if ln not in current_ignore]
|
|
37
|
+
files_to_ignore += current_ignore
|
|
38
|
+
|
|
39
|
+
# Write files to the .gitignore file.
|
|
40
|
+
gitignore_file.touch(exist_ok=True)
|
|
41
|
+
console.debug(f"Creating {gitignore_file}")
|
|
42
|
+
gitignore_file.write_text("\n".join(files_to_ignore) + "\n")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def initialize_requirements_txt() -> bool:
|
|
46
|
+
"""Initialize the requirements.txt file.
|
|
47
|
+
If absent and no pyproject.toml file exists, generate one for the user.
|
|
48
|
+
If the requirements.txt does not have reflex as dependency,
|
|
49
|
+
generate a requirement pinning current version and append to
|
|
50
|
+
the requirements.txt file.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
True if the user has to update the requirements.txt file.
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
Exit: If the requirements.txt file cannot be read or written to.
|
|
57
|
+
"""
|
|
58
|
+
requirements_file_path = Path(constants.RequirementsTxt.FILE)
|
|
59
|
+
if (
|
|
60
|
+
not requirements_file_path.exists()
|
|
61
|
+
and Path(constants.PyprojectToml.FILE).exists()
|
|
62
|
+
):
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
requirements_file_path.touch(exist_ok=True)
|
|
66
|
+
|
|
67
|
+
for encoding in [None, "utf-8"]:
|
|
68
|
+
try:
|
|
69
|
+
content = requirements_file_path.read_text(encoding)
|
|
70
|
+
break
|
|
71
|
+
except UnicodeDecodeError:
|
|
72
|
+
continue
|
|
73
|
+
except Exception as e:
|
|
74
|
+
console.error(f"Failed to read {requirements_file_path}.")
|
|
75
|
+
raise click.exceptions.Exit(1) from e
|
|
76
|
+
else:
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
for line in content.splitlines():
|
|
80
|
+
if re.match(r"^reflex[^a-zA-Z0-9]", line):
|
|
81
|
+
console.debug(f"{requirements_file_path} already has reflex as dependency.")
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
console.debug(
|
|
85
|
+
f"Appending {constants.RequirementsTxt.DEFAULTS_STUB} to {requirements_file_path}"
|
|
86
|
+
)
|
|
87
|
+
with requirements_file_path.open("a", encoding=encoding) as f:
|
|
88
|
+
f.write(
|
|
89
|
+
"\n" + constants.RequirementsTxt.DEFAULTS_STUB + constants.Reflex.VERSION
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def initialize_web_directory():
|
|
96
|
+
"""Initialize the web directory on reflex init."""
|
|
97
|
+
console.log("Initializing the web directory.")
|
|
98
|
+
|
|
99
|
+
# Reuse the hash if one is already created, so we don't over-write it when running reflex init
|
|
100
|
+
project_hash = get_project_hash()
|
|
101
|
+
|
|
102
|
+
console.debug(f"Copying {constants.Templates.Dirs.WEB_TEMPLATE} to {get_web_dir()}")
|
|
103
|
+
path_ops.copy_tree(constants.Templates.Dirs.WEB_TEMPLATE, str(get_web_dir()))
|
|
104
|
+
|
|
105
|
+
console.debug("Initializing the web directory.")
|
|
106
|
+
initialize_package_json()
|
|
107
|
+
|
|
108
|
+
console.debug("Initializing the bun config file.")
|
|
109
|
+
initialize_bun_config()
|
|
110
|
+
|
|
111
|
+
console.debug("Initializing the .npmrc file.")
|
|
112
|
+
initialize_npmrc()
|
|
113
|
+
|
|
114
|
+
console.debug("Initializing the public directory.")
|
|
115
|
+
path_ops.mkdir(get_web_dir() / constants.Dirs.PUBLIC)
|
|
116
|
+
|
|
117
|
+
console.debug("Initializing the react-router.config.js file.")
|
|
118
|
+
update_react_router_config()
|
|
119
|
+
|
|
120
|
+
console.debug("Initializing the vite.config.js file.")
|
|
121
|
+
initialize_vite_config()
|
|
122
|
+
|
|
123
|
+
console.debug("Initializing the reflex.json file.")
|
|
124
|
+
# Initialize the reflex json file.
|
|
125
|
+
init_reflex_json(project_hash=project_hash)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def update_react_router_config(prerender_routes: bool = False):
|
|
129
|
+
"""Update react-router.config.js config from Reflex config.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
prerender_routes: Whether to enable prerendering of routes.
|
|
133
|
+
"""
|
|
134
|
+
react_router_config_file_path = get_web_dir() / constants.ReactRouter.CONFIG_FILE
|
|
135
|
+
|
|
136
|
+
new_react_router_config = _update_react_router_config(
|
|
137
|
+
get_config(), prerender_routes=prerender_routes
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Overwriting the config file triggers a full server reload, so make sure
|
|
141
|
+
# there is actually a diff.
|
|
142
|
+
old_react_router_config = (
|
|
143
|
+
react_router_config_file_path.read_text()
|
|
144
|
+
if react_router_config_file_path.exists()
|
|
145
|
+
else ""
|
|
146
|
+
)
|
|
147
|
+
if old_react_router_config != new_react_router_config:
|
|
148
|
+
react_router_config_file_path.write_text(new_react_router_config)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _update_react_router_config(config: Config, prerender_routes: bool = False):
|
|
152
|
+
basename = "/" + (config.frontend_path or "").strip("/")
|
|
153
|
+
if not basename.endswith("/"):
|
|
154
|
+
basename += "/"
|
|
155
|
+
|
|
156
|
+
react_router_config = {
|
|
157
|
+
"basename": basename,
|
|
158
|
+
"future": {
|
|
159
|
+
"unstable_optimizeDeps": True,
|
|
160
|
+
},
|
|
161
|
+
"ssr": False,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if prerender_routes:
|
|
165
|
+
react_router_config["prerender"] = True
|
|
166
|
+
react_router_config["build"] = constants.Dirs.BUILD_DIR
|
|
167
|
+
|
|
168
|
+
return f"export default {json.dumps(react_router_config)};"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _compile_package_json():
|
|
172
|
+
return templates.PACKAGE_JSON.render(
|
|
173
|
+
scripts={
|
|
174
|
+
"dev": constants.PackageJson.Commands.DEV,
|
|
175
|
+
"export": constants.PackageJson.Commands.EXPORT,
|
|
176
|
+
"prod": constants.PackageJson.Commands.PROD,
|
|
177
|
+
},
|
|
178
|
+
dependencies=constants.PackageJson.DEPENDENCIES,
|
|
179
|
+
dev_dependencies=constants.PackageJson.DEV_DEPENDENCIES,
|
|
180
|
+
overrides=constants.PackageJson.OVERRIDES,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def initialize_package_json():
|
|
185
|
+
"""Render and write in .web the package.json file."""
|
|
186
|
+
output_path = get_web_dir() / constants.PackageJson.PATH
|
|
187
|
+
output_path.write_text(_compile_package_json())
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _compile_vite_config(config: Config):
|
|
191
|
+
# base must have exactly one trailing slash
|
|
192
|
+
base = "/"
|
|
193
|
+
if frontend_path := config.frontend_path.strip("/"):
|
|
194
|
+
base += frontend_path + "/"
|
|
195
|
+
return templates.VITE_CONFIG.render(base=base)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def initialize_vite_config():
|
|
199
|
+
"""Render and write in .web the vite.config.js file using Reflex config."""
|
|
200
|
+
vite_config_file_path = get_web_dir() / constants.ReactRouter.VITE_CONFIG_FILE
|
|
201
|
+
vite_config_file_path.write_text(_compile_vite_config(get_config()))
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def initialize_bun_config():
|
|
205
|
+
"""Initialize the bun config file."""
|
|
206
|
+
bun_config_path = get_web_dir() / constants.Bun.CONFIG_PATH
|
|
207
|
+
|
|
208
|
+
if (custom_bunfig := Path(constants.Bun.CONFIG_PATH)).exists():
|
|
209
|
+
bunfig_content = custom_bunfig.read_text()
|
|
210
|
+
console.info(f"Copying custom bunfig.toml inside {get_web_dir()} folder")
|
|
211
|
+
else:
|
|
212
|
+
best_registry = get_npm_registry()
|
|
213
|
+
bunfig_content = constants.Bun.DEFAULT_CONFIG.format(registry=best_registry)
|
|
214
|
+
|
|
215
|
+
bun_config_path.write_text(bunfig_content)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def initialize_npmrc():
|
|
219
|
+
"""Initialize the .npmrc file."""
|
|
220
|
+
npmrc_path = get_web_dir() / constants.Node.CONFIG_PATH
|
|
221
|
+
|
|
222
|
+
if (custom_npmrc := Path(constants.Node.CONFIG_PATH)).exists():
|
|
223
|
+
npmrc_content = custom_npmrc.read_text()
|
|
224
|
+
console.info(f"Copying custom .npmrc inside {get_web_dir()} folder")
|
|
225
|
+
else:
|
|
226
|
+
best_registry = get_npm_registry()
|
|
227
|
+
npmrc_content = constants.Node.DEFAULT_CONFIG.format(registry=best_registry)
|
|
228
|
+
|
|
229
|
+
npmrc_path.write_text(npmrc_content)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def init_reflex_json(project_hash: int | None):
|
|
233
|
+
"""Write the hash of the Reflex project to a REFLEX_JSON.
|
|
234
|
+
|
|
235
|
+
Reuse the hash if one is already created, therefore do not
|
|
236
|
+
overwrite it every time we run the reflex init command
|
|
237
|
+
.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
project_hash: The app hash.
|
|
241
|
+
"""
|
|
242
|
+
if project_hash is not None:
|
|
243
|
+
console.debug(f"Project hash is already set to {project_hash}.")
|
|
244
|
+
else:
|
|
245
|
+
# Get a random project hash.
|
|
246
|
+
project_hash = random.getrandbits(128)
|
|
247
|
+
console.debug(f"Setting project hash to {project_hash}.")
|
|
248
|
+
|
|
249
|
+
# Write the hash and version to the reflex json file.
|
|
250
|
+
reflex_json = {
|
|
251
|
+
"version": constants.Reflex.VERSION,
|
|
252
|
+
"project_hash": project_hash,
|
|
253
|
+
}
|
|
254
|
+
path_ops.update_json_file(get_web_dir() / constants.Reflex.JSON, reflex_json)
|