reflex 0.7.14a6__py3-none-any.whl → 0.8.0__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 (237) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +21 -11
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
  7. reflex/.templates/jinja/web/styles/styles.css.jinja2 +1 -0
  8. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -8
  9. reflex/.templates/web/app/entry.client.js +8 -0
  10. reflex/.templates/web/app/routes.js +10 -0
  11. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -37
  12. reflex/.templates/web/postcss.config.js +1 -1
  13. reflex/.templates/web/react-router.config.js +6 -0
  14. reflex/.templates/web/styles/__reflex_style_reset.css +399 -0
  15. reflex/.templates/web/utils/client_side_routing.js +21 -19
  16. reflex/.templates/web/utils/react-theme.js +92 -0
  17. reflex/.templates/web/utils/state.js +251 -100
  18. reflex/.templates/web/vite-plugin-safari-cachebust.js +160 -0
  19. reflex/.templates/web/vite.config.js +39 -0
  20. reflex/__init__.py +1 -6
  21. reflex/__init__.pyi +327 -192
  22. reflex/app.py +86 -135
  23. reflex/base.py +1 -87
  24. reflex/compiler/compiler.py +70 -19
  25. reflex/compiler/templates.py +3 -3
  26. reflex/compiler/utils.py +91 -33
  27. reflex/components/__init__.py +0 -2
  28. reflex/components/__init__.pyi +34 -18
  29. reflex/components/base/__init__.py +1 -5
  30. reflex/components/base/__init__.pyi +30 -21
  31. reflex/components/base/app_wrap.pyi +7 -7
  32. reflex/components/base/body.pyi +7 -7
  33. reflex/components/base/document.py +18 -14
  34. reflex/components/base/document.pyi +88 -38
  35. reflex/components/base/error_boundary.pyi +7 -7
  36. reflex/components/base/fragment.pyi +7 -7
  37. reflex/components/base/link.pyi +12 -12
  38. reflex/components/base/meta.py +4 -15
  39. reflex/components/base/meta.pyi +31 -31
  40. reflex/components/base/script.py +60 -58
  41. reflex/components/base/script.pyi +248 -34
  42. reflex/components/base/strict_mode.pyi +7 -7
  43. reflex/components/component.py +146 -217
  44. reflex/components/core/__init__.py +1 -0
  45. reflex/components/core/__init__.pyi +77 -37
  46. reflex/components/core/auto_scroll.pyi +7 -7
  47. reflex/components/core/banner.pyi +33 -33
  48. reflex/components/core/client_side_routing.py +7 -6
  49. reflex/components/core/client_side_routing.pyi +8 -59
  50. reflex/components/core/clipboard.pyi +7 -7
  51. reflex/components/core/debounce.py +1 -0
  52. reflex/components/core/debounce.pyi +7 -7
  53. reflex/components/core/foreach.py +5 -4
  54. reflex/components/core/helmet.py +14 -0
  55. reflex/components/{next/base.pyi → core/helmet.pyi} +12 -10
  56. reflex/components/core/html.pyi +7 -7
  57. reflex/components/core/match.py +3 -3
  58. reflex/components/core/sticky.pyi +21 -20
  59. reflex/components/core/upload.py +4 -2
  60. reflex/components/core/upload.pyi +26 -25
  61. reflex/components/datadisplay/__init__.pyi +13 -7
  62. reflex/components/datadisplay/code.py +14 -79
  63. reflex/components/datadisplay/code.pyi +11 -13
  64. reflex/components/datadisplay/dataeditor.pyi +38 -15
  65. reflex/components/datadisplay/shiki_code_block.py +5 -3
  66. reflex/components/datadisplay/shiki_code_block.pyi +16 -15
  67. reflex/components/dynamic.py +5 -5
  68. reflex/components/el/__init__.pyi +506 -246
  69. reflex/components/el/element.pyi +7 -7
  70. reflex/components/el/elements/__init__.pyi +504 -245
  71. reflex/components/el/elements/base.pyi +7 -7
  72. reflex/components/el/elements/forms.pyi +146 -101
  73. reflex/components/el/elements/inline.pyi +142 -142
  74. reflex/components/el/elements/media.pyi +131 -130
  75. reflex/components/el/elements/metadata.pyi +32 -32
  76. reflex/components/el/elements/other.pyi +37 -37
  77. reflex/components/el/elements/scripts.pyi +17 -17
  78. reflex/components/el/elements/sectioning.pyi +77 -77
  79. reflex/components/el/elements/tables.pyi +52 -52
  80. reflex/components/el/elements/typography.pyi +77 -77
  81. reflex/components/field.py +175 -0
  82. reflex/components/gridjs/datatable.py +2 -2
  83. reflex/components/gridjs/datatable.pyi +14 -14
  84. reflex/components/lucide/icon.py +6 -2
  85. reflex/components/lucide/icon.pyi +19 -17
  86. reflex/components/markdown/markdown.py +5 -3
  87. reflex/components/markdown/markdown.pyi +7 -7
  88. reflex/components/moment/moment.py +1 -1
  89. reflex/components/moment/moment.pyi +7 -7
  90. reflex/components/plotly/plotly.py +12 -6
  91. reflex/components/plotly/plotly.pyi +50 -49
  92. reflex/components/props.py +376 -27
  93. reflex/components/radix/__init__.pyi +123 -65
  94. reflex/components/radix/primitives/__init__.pyi +6 -4
  95. reflex/components/radix/primitives/accordion.py +8 -1
  96. reflex/components/radix/primitives/accordion.pyi +37 -37
  97. reflex/components/radix/primitives/base.pyi +12 -12
  98. reflex/components/radix/primitives/drawer.pyi +56 -55
  99. reflex/components/radix/primitives/form.pyi +63 -53
  100. reflex/components/radix/primitives/progress.pyi +26 -25
  101. reflex/components/radix/primitives/slider.pyi +27 -27
  102. reflex/components/radix/themes/__init__.pyi +5 -6
  103. reflex/components/radix/themes/base.py +3 -3
  104. reflex/components/radix/themes/base.pyi +42 -42
  105. reflex/components/radix/themes/color_mode.py +5 -6
  106. reflex/components/radix/themes/color_mode.pyi +17 -17
  107. reflex/components/radix/themes/components/__init__.pyi +75 -38
  108. reflex/components/radix/themes/components/alert_dialog.pyi +37 -37
  109. reflex/components/radix/themes/components/aspect_ratio.pyi +7 -7
  110. reflex/components/radix/themes/components/avatar.pyi +7 -7
  111. reflex/components/radix/themes/components/badge.pyi +7 -7
  112. reflex/components/radix/themes/components/button.pyi +7 -7
  113. reflex/components/radix/themes/components/callout.pyi +26 -25
  114. reflex/components/radix/themes/components/card.pyi +7 -7
  115. reflex/components/radix/themes/components/checkbox.pyi +16 -15
  116. reflex/components/radix/themes/components/checkbox_cards.pyi +12 -12
  117. reflex/components/radix/themes/components/checkbox_group.pyi +12 -12
  118. reflex/components/radix/themes/components/context_menu.pyi +67 -67
  119. reflex/components/radix/themes/components/data_list.pyi +22 -22
  120. reflex/components/radix/themes/components/dialog.pyi +36 -35
  121. reflex/components/radix/themes/components/dropdown_menu.pyi +42 -42
  122. reflex/components/radix/themes/components/hover_card.pyi +21 -20
  123. reflex/components/radix/themes/components/icon_button.pyi +7 -7
  124. reflex/components/radix/themes/components/inset.pyi +7 -7
  125. reflex/components/radix/themes/components/popover.pyi +22 -22
  126. reflex/components/radix/themes/components/progress.pyi +7 -7
  127. reflex/components/radix/themes/components/radio.pyi +7 -7
  128. reflex/components/radix/themes/components/radio_cards.pyi +12 -12
  129. reflex/components/radix/themes/components/radio_group.pyi +21 -20
  130. reflex/components/radix/themes/components/scroll_area.pyi +7 -7
  131. reflex/components/radix/themes/components/segmented_control.pyi +12 -12
  132. reflex/components/radix/themes/components/select.pyi +46 -45
  133. reflex/components/radix/themes/components/separator.pyi +7 -7
  134. reflex/components/radix/themes/components/skeleton.pyi +7 -7
  135. reflex/components/radix/themes/components/slider.pyi +17 -9
  136. reflex/components/radix/themes/components/spinner.pyi +7 -7
  137. reflex/components/radix/themes/components/switch.pyi +7 -7
  138. reflex/components/radix/themes/components/table.pyi +37 -37
  139. reflex/components/radix/themes/components/tabs.pyi +26 -25
  140. reflex/components/radix/themes/components/text_area.pyi +15 -9
  141. reflex/components/radix/themes/components/text_field.pyi +32 -19
  142. reflex/components/radix/themes/components/tooltip.pyi +7 -7
  143. reflex/components/radix/themes/layout/__init__.pyi +27 -14
  144. reflex/components/radix/themes/layout/base.pyi +7 -7
  145. reflex/components/radix/themes/layout/box.pyi +7 -7
  146. reflex/components/radix/themes/layout/center.pyi +7 -7
  147. reflex/components/radix/themes/layout/container.pyi +7 -7
  148. reflex/components/radix/themes/layout/flex.pyi +7 -7
  149. reflex/components/radix/themes/layout/grid.pyi +7 -7
  150. reflex/components/radix/themes/layout/list.pyi +26 -25
  151. reflex/components/radix/themes/layout/section.pyi +7 -7
  152. reflex/components/radix/themes/layout/spacer.pyi +7 -7
  153. reflex/components/radix/themes/layout/stack.pyi +17 -17
  154. reflex/components/radix/themes/typography/__init__.pyi +7 -5
  155. reflex/components/radix/themes/typography/blockquote.pyi +7 -7
  156. reflex/components/radix/themes/typography/code.pyi +7 -7
  157. reflex/components/radix/themes/typography/heading.pyi +7 -7
  158. reflex/components/radix/themes/typography/link.py +46 -11
  159. reflex/components/radix/themes/typography/link.pyi +312 -9
  160. reflex/components/radix/themes/typography/text.pyi +36 -35
  161. reflex/components/react_player/audio.pyi +10 -8
  162. reflex/components/react_player/react_player.pyi +7 -7
  163. reflex/components/react_player/video.pyi +10 -8
  164. reflex/components/recharts/__init__.pyi +208 -100
  165. reflex/components/recharts/cartesian.py +10 -8
  166. reflex/components/recharts/cartesian.pyi +90 -94
  167. reflex/components/recharts/charts.py +4 -2
  168. reflex/components/recharts/charts.pyi +49 -49
  169. reflex/components/recharts/general.pyi +31 -31
  170. reflex/components/recharts/polar.py +8 -4
  171. reflex/components/recharts/polar.pyi +23 -23
  172. reflex/components/recharts/recharts.py +2 -2
  173. reflex/components/recharts/recharts.pyi +12 -12
  174. reflex/components/sonner/toast.py +3 -3
  175. reflex/components/sonner/toast.pyi +9 -9
  176. reflex/config.py +10 -113
  177. reflex/constants/__init__.py +2 -2
  178. reflex/constants/base.py +28 -11
  179. reflex/constants/compiler.py +12 -3
  180. reflex/constants/event.py +1 -0
  181. reflex/constants/installer.py +26 -20
  182. reflex/constants/route.py +27 -8
  183. reflex/constants/state.py +2 -0
  184. reflex/custom_components/custom_components.py +0 -14
  185. reflex/environment.py +77 -5
  186. reflex/event.py +178 -81
  187. reflex/experimental/__init__.py +0 -30
  188. reflex/istate/__init__.py +69 -0
  189. reflex/istate/manager.py +1 -0
  190. reflex/istate/proxy.py +5 -3
  191. reflex/page.py +0 -27
  192. reflex/plugins/__init__.py +3 -2
  193. reflex/plugins/base.py +5 -1
  194. reflex/plugins/shared_tailwind.py +215 -0
  195. reflex/plugins/sitemap.py +206 -0
  196. reflex/plugins/tailwind_v3.py +15 -108
  197. reflex/plugins/tailwind_v4.py +18 -110
  198. reflex/reflex.py +1 -0
  199. reflex/route.py +157 -75
  200. reflex/state.py +171 -155
  201. reflex/testing.py +86 -16
  202. reflex/utils/build.py +38 -82
  203. reflex/utils/exec.py +83 -175
  204. reflex/utils/export.py +2 -2
  205. reflex/utils/format.py +1 -5
  206. reflex/utils/imports.py +5 -16
  207. reflex/utils/misc.py +67 -0
  208. reflex/utils/prerequisites.py +66 -68
  209. reflex/utils/processes.py +24 -47
  210. reflex/utils/pyi_generator.py +44 -49
  211. reflex/utils/serializers.py +14 -1
  212. reflex/utils/telemetry.py +0 -15
  213. reflex/utils/types.py +197 -62
  214. reflex/vars/__init__.py +2 -0
  215. reflex/vars/base.py +367 -134
  216. {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/METADATA +15 -8
  217. reflex-0.8.0.dist-info/RECORD +403 -0
  218. reflex/.templates/web/next.config.js +0 -7
  219. reflex/components/base/head.py +0 -20
  220. reflex/components/base/head.pyi +0 -116
  221. reflex/components/next/__init__.py +0 -10
  222. reflex/components/next/base.py +0 -7
  223. reflex/components/next/image.py +0 -117
  224. reflex/components/next/image.pyi +0 -94
  225. reflex/components/next/link.py +0 -20
  226. reflex/components/next/link.pyi +0 -67
  227. reflex/components/next/video.py +0 -38
  228. reflex/components/next/video.pyi +0 -68
  229. reflex/components/suneditor/__init__.py +0 -5
  230. reflex/components/suneditor/editor.py +0 -269
  231. reflex/components/suneditor/editor.pyi +0 -199
  232. reflex/experimental/layout.py +0 -254
  233. reflex/experimental/layout.pyi +0 -814
  234. reflex-0.7.14a6.dist-info/RECORD +0 -408
  235. {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/WHEEL +0 -0
  236. {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/entry_points.txt +0 -0
  237. {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/licenses/LICENSE +0 -0
reflex/testing.py CHANGED
@@ -22,9 +22,8 @@ import types
22
22
  from collections.abc import AsyncIterator, Callable, Coroutine, Sequence
23
23
  from http.server import SimpleHTTPRequestHandler
24
24
  from pathlib import Path
25
- from typing import TYPE_CHECKING, Any, TypeVar
25
+ from typing import TYPE_CHECKING, Any, Literal, TypeVar
26
26
 
27
- import psutil
28
27
  import uvicorn
29
28
 
30
29
  import reflex
@@ -241,7 +240,7 @@ class AppHarness:
241
240
  def _initialize_app(self):
242
241
  # disable telemetry reporting for tests
243
242
 
244
- os.environ["TELEMETRY_ENABLED"] = "false"
243
+ os.environ["REFLEX_TELEMETRY_ENABLED"] = "false"
245
244
  CustomComponent.create().get_component.cache_clear()
246
245
  self.app_path.mkdir(parents=True, exist_ok=True)
247
246
  if self.app_source is not None:
@@ -344,8 +343,12 @@ class AppHarness:
344
343
  )
345
344
  self.backend.shutdown = self._get_backend_shutdown_handler()
346
345
  with chdir(self.app_path):
346
+ print( # noqa: T201
347
+ "Creating backend in a new thread..."
348
+ ) # for pytest diagnosis
347
349
  self.backend_thread = threading.Thread(target=self.backend.run)
348
350
  self.backend_thread.start()
351
+ print("Backend started.") # for pytest diagnosis #noqa: T201
349
352
 
350
353
  async def _reset_backend_state_manager(self):
351
354
  """Reset the StateManagerRedis event loop affinity.
@@ -377,11 +380,15 @@ class AppHarness:
377
380
  # Set up the frontend.
378
381
  with chdir(self.app_path):
379
382
  config = reflex.config.get_config()
383
+ print("Polling for servers...") # for pytest diagnosis #noqa: T201
380
384
  config.api_url = "http://{}:{}".format(
381
- *self._poll_for_servers().getsockname(),
385
+ *self._poll_for_servers(timeout=30).getsockname(),
382
386
  )
387
+ print("Building frontend...") # for pytest diagnosis #noqa: T201
383
388
  reflex.utils.build.setup_frontend(self.app_path)
384
389
 
390
+ print("Frontend starting...") # for pytest diagnosis #noqa: T201
391
+
385
392
  # Start the frontend.
386
393
  self.frontend_process = reflex.utils.processes.new_process(
387
394
  [
@@ -392,19 +399,20 @@ class AppHarness:
392
399
  "dev",
393
400
  ],
394
401
  cwd=self.app_path / reflex.utils.prerequisites.get_web_dir(),
395
- env={"PORT": "0"},
402
+ env={"PORT": "0", "NO_COLOR": "1"},
396
403
  **FRONTEND_POPEN_ARGS,
397
404
  )
398
405
 
399
406
  def _wait_frontend(self):
407
+ if self.frontend_process is None or self.frontend_process.stdout is None:
408
+ msg = "Frontend process has no stdout."
409
+ raise RuntimeError(msg)
400
410
  while self.frontend_url is None:
401
- line = (
402
- self.frontend_process.stdout.readline() # pyright: ignore [reportOptionalMemberAccess]
403
- )
411
+ line = self.frontend_process.stdout.readline()
404
412
  if not line:
405
413
  break
406
414
  print(line) # for pytest diagnosis #noqa: T201
407
- m = re.search(reflex.constants.Next.FRONTEND_LISTENING_REGEX, line)
415
+ m = re.search(reflex.constants.ReactRouter.FRONTEND_LISTENING_REGEX, line)
408
416
  if m is not None:
409
417
  self.frontend_url = m.group(1)
410
418
  config = reflex.config.get_config()
@@ -469,6 +477,8 @@ class AppHarness:
469
477
 
470
478
  def stop(self) -> None:
471
479
  """Stop the frontend and backend servers."""
480
+ import psutil
481
+
472
482
  # Quit browsers first to avoid any lingering events being sent during shutdown.
473
483
  for driver in self._frontends:
474
484
  driver.quit()
@@ -517,7 +527,7 @@ class AppHarness:
517
527
  target: Callable[[], T],
518
528
  timeout: TimeoutType = None,
519
529
  step: TimeoutType = None,
520
- ) -> T | bool:
530
+ ) -> T | Literal[False]:
521
531
  """Generic polling logic.
522
532
 
523
533
  Args:
@@ -535,9 +545,10 @@ class AppHarness:
535
545
  step = POLL_INTERVAL
536
546
  deadline = time.time() + timeout
537
547
  while time.time() < deadline:
538
- success = target()
539
- if success:
540
- return success
548
+ with contextlib.suppress(Exception):
549
+ success = target()
550
+ if success:
551
+ return success
541
552
  time.sleep(step)
542
553
  return False
543
554
 
@@ -840,6 +851,57 @@ class AppHarness:
840
851
  raise TimeoutError(msg)
841
852
  return state_manager.states
842
853
 
854
+ @staticmethod
855
+ def poll_for_or_raise_timeout(
856
+ target: Callable[[], T],
857
+ timeout: TimeoutType = None,
858
+ step: TimeoutType = None,
859
+ ) -> T:
860
+ """Poll target callable for a truthy return value.
861
+
862
+ Like `_poll_for`, but raises a `TimeoutError` if the target does not
863
+ return a truthy value within the timeout.
864
+
865
+ Args:
866
+ target: callable that returns truthy if polling condition is met.
867
+ timeout: max polling time
868
+ step: interval between checking target()
869
+
870
+ Returns:
871
+ return value of target() if truthy within timeout
872
+
873
+ Raises:
874
+ TimeoutError: when target does not return a truthy value within timeout
875
+ """
876
+ result = AppHarness._poll_for(
877
+ target=target,
878
+ timeout=timeout,
879
+ step=step,
880
+ )
881
+ if result is False:
882
+ msg = "Target did not return a truthy value while polling."
883
+ raise TimeoutError(msg)
884
+ return result
885
+
886
+ @staticmethod
887
+ def expect(
888
+ target: Callable[[], T],
889
+ timeout: TimeoutType = None,
890
+ step: TimeoutType = None,
891
+ ):
892
+ """Expect a target callable to return a truthy value within the timeout.
893
+
894
+ Args:
895
+ target: callable that returns truthy if polling condition is met.
896
+ timeout: max polling time
897
+ step: interval between checking target()
898
+ """
899
+ AppHarness.poll_for_or_raise_timeout(
900
+ target=target,
901
+ timeout=timeout,
902
+ step=step,
903
+ )
904
+
843
905
 
844
906
  class SimpleHTTPRequestHandlerCustomErrors(SimpleHTTPRequestHandler):
845
907
  """SimpleHTTPRequestHandler with custom error page handling."""
@@ -922,7 +984,7 @@ class Subdir404TCPServer(socketserver.TCPServer):
922
984
  class AppHarnessProd(AppHarness):
923
985
  """AppHarnessProd executes a reflex app in-process for testing.
924
986
 
925
- In prod mode, instead of running `next dev` the app is exported as static
987
+ In prod mode, instead of running `react-router dev` the app is exported as static
926
988
  files and served via the builtin python http.server with custom 404 redirect
927
989
  handling. Additionally, the backend runs in multi-worker mode.
928
990
  """
@@ -937,7 +999,7 @@ class AppHarnessProd(AppHarness):
937
999
  / reflex.constants.Dirs.STATIC
938
1000
  )
939
1001
  error_page_map = {
940
- 404: web_root / "404.html",
1002
+ 404: web_root / "404" / "index.html",
941
1003
  }
942
1004
  with Subdir404TCPServer(
943
1005
  ("", 0),
@@ -954,9 +1016,11 @@ class AppHarnessProd(AppHarness):
954
1016
  # Set up the frontend.
955
1017
  with chdir(self.app_path):
956
1018
  config = reflex.config.get_config()
1019
+ print("Polling for servers...") # for pytest diagnosis #noqa: T201
957
1020
  config.api_url = "http://{}:{}".format(
958
- *self._poll_for_servers().getsockname(),
1021
+ *self._poll_for_servers(timeout=30).getsockname(),
959
1022
  )
1023
+ print("Building frontend...") # for pytest diagnosis #noqa: T201
960
1024
 
961
1025
  get_config().loglevel = reflex.constants.LogLevel.INFO
962
1026
 
@@ -973,6 +1037,8 @@ class AppHarnessProd(AppHarness):
973
1037
  env=reflex.constants.Env.PROD,
974
1038
  )
975
1039
 
1040
+ print("Frontend starting...") # for pytest diagnosis #noqa: T201
1041
+
976
1042
  self.frontend_thread = threading.Thread(target=self._run_frontend)
977
1043
  self.frontend_thread.start()
978
1044
 
@@ -996,8 +1062,12 @@ class AppHarnessProd(AppHarness):
996
1062
  ),
997
1063
  )
998
1064
  self.backend.shutdown = self._get_backend_shutdown_handler()
1065
+ print( # noqa: T201
1066
+ "Creating backend in a new thread..."
1067
+ )
999
1068
  self.backend_thread = threading.Thread(target=self.backend.run)
1000
1069
  self.backend_thread.start()
1070
+ print("Backend started.") # for pytest diagnosis #noqa: T201
1001
1071
 
1002
1072
  def _poll_for_servers(self, timeout: TimeoutType = None) -> socket.socket:
1003
1073
  try:
reflex/utils/build.py CHANGED
@@ -2,16 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import json
6
5
  import os
7
- import subprocess
8
6
  import zipfile
9
7
  from pathlib import Path
10
8
 
11
9
  from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
12
10
 
13
11
  from reflex import constants
14
- from reflex.config import get_config
15
12
  from reflex.utils import console, path_ops, prerequisites, processes
16
13
  from reflex.utils.exec import is_in_app_harness
17
14
 
@@ -27,30 +24,6 @@ def set_env_json():
27
24
  )
28
25
 
29
26
 
30
- def generate_sitemap_config(deploy_url: str, export: bool = False):
31
- """Generate the sitemap config file.
32
-
33
- Args:
34
- deploy_url: The URL of the deployed app.
35
- export: If the sitemap are generated for an export.
36
- """
37
- # Import here to avoid circular imports.
38
- from reflex.compiler import templates
39
-
40
- config = {
41
- "siteUrl": deploy_url,
42
- "generateRobotsTxt": True,
43
- }
44
-
45
- if export:
46
- config["outDir"] = constants.Dirs.STATIC
47
-
48
- config = json.dumps(config)
49
-
50
- sitemap = prerequisites.get_web_dir() / constants.Next.SITEMAP_CONFIG_FILE
51
- sitemap.write_text(templates.SITEMAP_CONFIG(config=config))
52
-
53
-
54
27
  def _zip(
55
28
  component_name: constants.ComponentName,
56
29
  target: str | Path,
@@ -175,102 +148,85 @@ def zip_app(
175
148
  )
176
149
 
177
150
 
178
- def build(
179
- deploy_url: str | None = None,
180
- for_export: bool = False,
181
- ):
182
- """Build the app for deployment.
151
+ def _duplicate_index_html_to_parent_dir(directory: Path):
152
+ """Duplicate index.html in the child directories to the given directory.
153
+
154
+ This makes accessing /route and /route/ work in production.
183
155
 
184
156
  Args:
185
- deploy_url: The deployment URL.
186
- for_export: Whether the build is for export.
157
+ directory: The directory to duplicate index.html to.
187
158
  """
159
+ for child in directory.iterdir():
160
+ if child.is_dir():
161
+ # If the child directory has an index.html, copy it to the parent directory.
162
+ index_html = child / "index.html"
163
+ if index_html.exists():
164
+ target = directory / (child.name + ".html")
165
+ if not target.exists():
166
+ console.debug(f"Copying {index_html} to {target}")
167
+ path_ops.cp(index_html, target)
168
+ else:
169
+ console.debug(f"Skipping {index_html}, already exists at {target}")
170
+ # Recursively call this function for the child directory.
171
+ _duplicate_index_html_to_parent_dir(child)
172
+
173
+
174
+ def build():
175
+ """Build the app for deployment."""
188
176
  wdir = prerequisites.get_web_dir()
189
177
 
190
178
  # Clean the static directory if it exists.
191
- path_ops.rm(str(wdir / constants.Dirs.STATIC))
192
-
193
- # The export command to run.
194
- command = "export"
179
+ path_ops.rm(str(wdir / constants.Dirs.BUILD_DIR))
195
180
 
196
181
  checkpoints = [
197
- "Linting and checking ",
198
- "Creating an optimized production build",
199
- "Route (pages)",
200
- "prerendered as static HTML",
201
- "Collecting page data",
202
- "Finalizing page optimization",
203
- "Collecting build traces",
182
+ "building for production",
183
+ "building SSR bundle for production",
184
+ "built in",
204
185
  ]
205
186
 
206
- # Generate a sitemap if a deploy URL is provided.
207
- if deploy_url is not None:
208
- generate_sitemap_config(deploy_url, export=for_export)
209
- command = "export-sitemap"
210
-
211
- checkpoints.extend(["Loading next-sitemap", "Generation completed"])
212
-
213
187
  # Start the subprocess with the progress bar.
214
188
  process = processes.new_process(
215
- [*prerequisites.get_js_package_executor(raise_on_none=True)[0], "run", command],
189
+ [
190
+ *prerequisites.get_js_package_executor(raise_on_none=True)[0],
191
+ "run",
192
+ "export",
193
+ ],
216
194
  cwd=wdir,
217
195
  shell=constants.IS_WINDOWS,
196
+ env={
197
+ **os.environ,
198
+ "NO_COLOR": "1",
199
+ },
218
200
  )
219
201
  processes.show_progress("Creating Production Build", process, checkpoints)
202
+ _duplicate_index_html_to_parent_dir(wdir / constants.Dirs.STATIC)
220
203
 
221
204
 
222
205
  def setup_frontend(
223
206
  root: Path,
224
- disable_telemetry: bool = True,
225
207
  ):
226
208
  """Set up the frontend to run the app.
227
209
 
228
210
  Args:
229
211
  root: The root path of the project.
230
- disable_telemetry: Whether to disable the Next telemetry.
231
212
  """
232
- # Create the assets dir if it doesn't exist.
233
- path_ops.mkdir(constants.Dirs.APP_ASSETS)
234
- path_ops.copy_tree(
235
- src=str(root / constants.Dirs.APP_ASSETS),
236
- dest=str(root / prerequisites.get_web_dir() / constants.Dirs.PUBLIC),
237
- ignore=tuple(f"*.{ext}" for ext in constants.Reflex.STYLESHEETS_SUPPORTED),
238
- )
239
-
240
213
  # Set the environment variables in client (env.json).
241
214
  set_env_json()
242
215
 
243
216
  # update the last reflex run time.
244
217
  prerequisites.set_last_reflex_run_time()
245
218
 
246
- # Disable the Next telemetry.
247
- if disable_telemetry:
248
- processes.new_process(
249
- [
250
- *prerequisites.get_js_package_executor(raise_on_none=True)[0],
251
- "run",
252
- "next",
253
- "telemetry",
254
- "disable",
255
- ],
256
- cwd=prerequisites.get_web_dir(),
257
- stdout=subprocess.DEVNULL,
258
- shell=constants.IS_WINDOWS,
259
- )
260
-
261
219
 
262
220
  def setup_frontend_prod(
263
221
  root: Path,
264
- disable_telemetry: bool = True,
265
222
  ):
266
223
  """Set up the frontend for prod mode.
267
224
 
268
225
  Args:
269
226
  root: The root path of the project.
270
- disable_telemetry: Whether to disable the Next telemetry.
271
227
  """
272
- setup_frontend(root, disable_telemetry)
273
- build(deploy_url=get_config().deploy_url)
228
+ setup_frontend(root)
229
+ build()
274
230
 
275
231
 
276
232
  def _looks_like_venv_dir(dir_to_check: str | Path) -> bool: