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.

Files changed (37) hide show
  1. reflex/.templates/jinja/web/vite.config.js.jinja2 +4 -1
  2. reflex/.templates/web/app/routes.js +0 -1
  3. reflex/.templates/web/utils/state.js +1 -11
  4. reflex/app.py +15 -23
  5. reflex/components/component.py +6 -4
  6. reflex/components/lucide/icon.py +4 -1
  7. reflex/components/lucide/icon.pyi +4 -1
  8. reflex/components/plotly/plotly.py +9 -9
  9. reflex/components/recharts/recharts.py +2 -2
  10. reflex/components/sonner/toast.py +7 -7
  11. reflex/components/sonner/toast.pyi +8 -8
  12. reflex/config.py +9 -2
  13. reflex/constants/base.py +2 -0
  14. reflex/constants/installer.py +6 -6
  15. reflex/constants/state.py +1 -0
  16. reflex/custom_components/custom_components.py +3 -3
  17. reflex/reflex.py +7 -6
  18. reflex/route.py +4 -0
  19. reflex/state.py +1 -1
  20. reflex/testing.py +3 -5
  21. reflex/utils/build.py +21 -3
  22. reflex/utils/exec.py +11 -11
  23. reflex/utils/frontend_skeleton.py +254 -0
  24. reflex/utils/js_runtimes.py +411 -0
  25. reflex/utils/prerequisites.py +17 -1383
  26. reflex/utils/rename.py +170 -0
  27. reflex/utils/telemetry.py +101 -10
  28. reflex/utils/templates.py +443 -0
  29. reflex/vars/base.py +3 -3
  30. {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/METADATA +2 -2
  31. {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/RECORD +34 -33
  32. reflex/.templates/web/utils/client_side_routing.js +0 -45
  33. reflex/components/core/client_side_routing.py +0 -70
  34. reflex/components/core/client_side_routing.pyi +0 -68
  35. {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/WHEEL +0 -0
  36. {reflex-0.8.6a1.dist-info → reflex-0.8.7.dist-info}/entry_points.txt +0 -0
  37. {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
- *reflex.utils.prerequisites.get_js_package_executor(raise_on_none=True)[
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" / "index.html",
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.utils import console, path_ops, prerequisites, processes
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
- *prerequisites.get_js_package_executor(raise_on_none=True)[0],
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 prerequisites
234
+ from reflex.utils import js_runtimes
235
235
 
236
236
  # validate dependencies before run
237
- prerequisites.validate_frontend_dependencies(init=False)
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
- *prerequisites.get_js_package_executor(raise_on_none=True)[0],
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 prerequisites
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
- prerequisites.validate_frontend_dependencies(init=False)
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
- [*prerequisites.get_js_package_executor(raise_on_none=True)[0], "run", "prod"],
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 prerequisites
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 {prerequisites.get_node_version()} (Minimum: {constants.Node.MIN_VERSION}) (PATH:{path_ops.get_node_path()})]",
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 {prerequisites.get_bun_version()} (Minimum: {constants.Bun.MIN_VERSION}) (PATH: {path_ops.get_bun_path()})]"
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: {prerequisites.get_nodejs_compatible_package_managers(raise_on_none=False)}"
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: {prerequisites.get_js_package_executor(raise_on_none=False)}"
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)