Browse Source

code cleanup (split constants into a folder) (#1866)

Thomas Brandého 1 year ago
parent
commit
dcb17103bb

+ 24 - 22
reflex/app.py

@@ -183,8 +183,8 @@ class App(Base):
             else config.cors_allowed_origins,
             else config.cors_allowed_origins,
             cors_credentials=True,
             cors_credentials=True,
             max_http_buffer_size=constants.POLLING_MAX_HTTP_BUFFER_SIZE,
             max_http_buffer_size=constants.POLLING_MAX_HTTP_BUFFER_SIZE,
-            ping_interval=constants.PING_INTERVAL,
-            ping_timeout=constants.PING_TIMEOUT,
+            ping_interval=constants.Ping.INTERVAL,
+            ping_timeout=constants.Ping.TIMEOUT,
         )
         )
 
 
         # Create the socket app. Note event endpoint constant replaces the default 'socket.io' path.
         # Create the socket app. Note event endpoint constant replaces the default 'socket.io' path.
@@ -340,14 +340,14 @@ class App(Base):
         self,
         self,
         component: Component | ComponentCallable,
         component: Component | ComponentCallable,
         route: str | None = None,
         route: str | None = None,
-        title: str = constants.DEFAULT_TITLE,
-        description: str = constants.DEFAULT_DESCRIPTION,
-        image=constants.DEFAULT_IMAGE,
+        title: str = constants.DefaultPage.TITLE,
+        description: str = constants.DefaultPage.DESCRIPTION,
+        image: str = constants.DefaultPage.IMAGE,
         on_load: EventHandler
         on_load: EventHandler
         | EventSpec
         | EventSpec
         | list[EventHandler | EventSpec]
         | list[EventHandler | EventSpec]
         | None = None,
         | None = None,
-        meta: list[dict[str, str]] = constants.DEFAULT_META_LIST,
+        meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
         script_tags: list[Component] | None = None,
         script_tags: list[Component] | None = None,
     ):
     ):
         """Add a page to the app.
         """Add a page to the app.
@@ -433,7 +433,7 @@ class App(Base):
         """
         """
         route = route.lstrip("/")
         route = route.lstrip("/")
         if route == "":
         if route == "":
-            route = constants.INDEX_ROUTE
+            route = constants.PageNames.INDEX_ROUTE
         return self.load_events.get(route, [])
         return self.load_events.get(route, [])
 
 
     def _check_routes_conflict(self, new_route: str):
     def _check_routes_conflict(self, new_route: str):
@@ -472,14 +472,14 @@ class App(Base):
     def add_custom_404_page(
     def add_custom_404_page(
         self,
         self,
         component: Component | ComponentCallable | None = None,
         component: Component | ComponentCallable | None = None,
-        title: str = constants.TITLE_404,
-        image: str = constants.FAVICON_404,
-        description: str = constants.DESCRIPTION_404,
+        title: str = constants.Page404.TITLE,
+        image: str = constants.Page404.IMAGE,
+        description: str = constants.Page404.DESCRIPTION,
         on_load: EventHandler
         on_load: EventHandler
         | EventSpec
         | EventSpec
         | list[EventHandler | EventSpec]
         | list[EventHandler | EventSpec]
         | None = None,
         | None = None,
-        meta: list[dict[str, str]] = constants.DEFAULT_META_LIST,
+        meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
     ):
     ):
         """Define a custom 404 page for any url having no match.
         """Define a custom 404 page for any url having no match.
 
 
@@ -498,10 +498,10 @@ class App(Base):
             component = Default404Page.create()
             component = Default404Page.create()
         self.add_page(
         self.add_page(
             component=wait_for_client_redirect(self._generate_component(component)),
             component=wait_for_client_redirect(self._generate_component(component)),
-            route=constants.SLUG_404,
-            title=title or constants.TITLE_404,
-            image=image or constants.FAVICON_404,
-            description=description or constants.DESCRIPTION_404,
+            route=constants.Page404.SLUG,
+            title=title or constants.Page404.TITLE,
+            image=image or constants.Page404.IMAGE,
+            description=description or constants.Page404.DESCRIPTION,
             on_load=on_load,
             on_load=on_load,
             meta=meta,
             meta=meta,
         )
         )
@@ -541,11 +541,13 @@ class App(Base):
         page_imports = {
         page_imports = {
             i
             i
             for i, tags in imports.items()
             for i, tags in imports.items()
-            if i not in compiler.DEFAULT_IMPORTS.keys()
-            and i != "focus-visible/dist/focus-visible"
-            and "next" not in i
-            and not i.startswith("/")
-            and not i.startswith(".")
+            if i
+            not in [
+                *compiler.DEFAULT_IMPORTS.keys(),
+                *constants.PackageJson.DEPENDENCIES.keys(),
+                *constants.PackageJson.DEV_DEPENDENCIES.keys(),
+            ]
+            and not any(i.startswith(prefix) for prefix in ["/", ".", "next/"])
             and i != ""
             and i != ""
             and any(tag.install for tag in tags)
             and any(tag.install for tag in tags)
         }
         }
@@ -581,7 +583,7 @@ class App(Base):
             self.add_page(render, **kwargs)
             self.add_page(render, **kwargs)
 
 
         # Render a default 404 page if the user didn't supply one
         # Render a default 404 page if the user didn't supply one
-        if constants.SLUG_404 not in self.pages:
+        if constants.Page404.SLUG not in self.pages:
             self.add_custom_404_page()
             self.add_custom_404_page()
 
 
         task = progress.add_task("Compiling: ", total=len(self.pages))
         task = progress.add_task("Compiling: ", total=len(self.pages))
@@ -649,7 +651,7 @@ class App(Base):
         # Compile the Tailwind config.
         # Compile the Tailwind config.
         if config.tailwind is not None:
         if config.tailwind is not None:
             config.tailwind["content"] = config.tailwind.get(
             config.tailwind["content"] = config.tailwind.get(
-                "content", constants.TAILWIND_CONTENT
+                "content", constants.Tailwind.CONTENT
             )
             )
             compile_results.append(compiler.compile_tailwind(config.tailwind))
             compile_results.append(compiler.compile_tailwind(config.tailwind))
 
 

+ 9 - 9
reflex/compiler/compiler.py

@@ -23,7 +23,7 @@ DEFAULT_IMPORTS: imports.ImportDict = {
         ImportVar(tag="useContext"),
         ImportVar(tag="useContext"),
     },
     },
     "next/router": {ImportVar(tag="useRouter")},
     "next/router": {ImportVar(tag="useRouter")},
-    f"/{constants.STATE_PATH}": {
+    f"/{constants.Dirs.STATE_PATH}": {
         ImportVar(tag="uploadFiles"),
         ImportVar(tag="uploadFiles"),
         ImportVar(tag="Event"),
         ImportVar(tag="Event"),
         ImportVar(tag="isTrue"),
         ImportVar(tag="isTrue"),
@@ -40,9 +40,9 @@ DEFAULT_IMPORTS: imports.ImportDict = {
         ImportVar(tag="initialEvents"),
         ImportVar(tag="initialEvents"),
         ImportVar(tag="StateContext"),
         ImportVar(tag="StateContext"),
     },
     },
-    "": {ImportVar(tag="focus-visible/dist/focus-visible")},
+    "": {ImportVar(tag="focus-visible/dist/focus-visible", install=False)},
     "@chakra-ui/react": {
     "@chakra-ui/react": {
-        ImportVar(tag=constants.USE_COLOR_MODE),
+        ImportVar(tag=constants.ColorMode.USE),
         ImportVar(tag="Box"),
         ImportVar(tag="Box"),
         ImportVar(tag="Text"),
         ImportVar(tag="Text"),
     },
     },
@@ -151,7 +151,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
     """
     """
     # Add tailwind css if enabled.
     # Add tailwind css if enabled.
     sheets = (
     sheets = (
-        [constants.TAILWIND_ROOT_STYLE_PATH]
+        [constants.Tailwind.ROOT_STYLE_PATH]
         if get_config().tailwind is not None
         if get_config().tailwind is not None
         else []
         else []
     )
     )
@@ -159,7 +159,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
         if not utils.is_valid_url(stylesheet):
         if not utils.is_valid_url(stylesheet):
             # check if stylesheet provided exists.
             # check if stylesheet provided exists.
             stylesheet_full_path = (
             stylesheet_full_path = (
-                Path.cwd() / constants.APP_ASSETS_DIR / stylesheet.strip("/")
+                Path.cwd() / constants.Dirs.APP_ASSETS / stylesheet.strip("/")
             )
             )
             if not os.path.exists(stylesheet_full_path):
             if not os.path.exists(stylesheet_full_path):
                 raise FileNotFoundError(
                 raise FileNotFoundError(
@@ -193,7 +193,7 @@ def _compile_components(components: set[CustomComponent]) -> str:
     """
     """
     imports = {
     imports = {
         "react": {ImportVar(tag="memo")},
         "react": {ImportVar(tag="memo")},
-        f"/{constants.STATE_PATH}": {ImportVar(tag="E"), ImportVar(tag="isTrue")},
+        f"/{constants.Dirs.STATE_PATH}": {ImportVar(tag="E"), ImportVar(tag="isTrue")},
     }
     }
     component_renders = []
     component_renders = []
 
 
@@ -236,7 +236,7 @@ def compile_document_root(head_components: list[Component]) -> tuple[str, str]:
         The path and code of the compiled document root.
         The path and code of the compiled document root.
     """
     """
     # Get the path for the output file.
     # Get the path for the output file.
-    output_path = utils.get_page_path(constants.DOCUMENT_ROOT)
+    output_path = utils.get_page_path(constants.PageNames.DOCUMENT_ROOT)
 
 
     # Create the document root.
     # Create the document root.
     document_root = utils.create_document_root(head_components)
     document_root = utils.create_document_root(head_components)
@@ -330,7 +330,7 @@ def compile_tailwind(
         The compiled Tailwind config.
         The compiled Tailwind config.
     """
     """
     # Get the path for the output file.
     # Get the path for the output file.
-    output_path = constants.TAILWIND_CONFIG
+    output_path = constants.Tailwind.CONFIG
 
 
     # Compile the config.
     # Compile the config.
     code = _compile_tailwind(config)
     code = _compile_tailwind(config)
@@ -339,4 +339,4 @@ def compile_tailwind(
 
 
 def purge_web_pages_dir():
 def purge_web_pages_dir():
     """Empty out .web directory."""
     """Empty out .web directory."""
-    utils.empty_dir(constants.WEB_PAGES_DIR, keep_files=["_app.js"])
+    utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])

+ 16 - 16
reflex/compiler/templates.py

@@ -19,26 +19,26 @@ class ReflexJinjaEnvironment(Environment):
         )
         )
         self.filters["json_dumps"] = json_dumps
         self.filters["json_dumps"] = json_dumps
         self.filters["react_setter"] = lambda state: f"set{state.capitalize()}"
         self.filters["react_setter"] = lambda state: f"set{state.capitalize()}"
-        self.loader = FileSystemLoader(constants.JINJA_TEMPLATE_DIR)
+        self.loader = FileSystemLoader(constants.Templates.Dirs.JINJA_TEMPLATE)
         self.globals["const"] = {
         self.globals["const"] = {
-            "socket": constants.SOCKET,
-            "result": constants.RESULT,
-            "router": constants.ROUTER,
+            "socket": constants.CompileVars.SOCKET,
+            "result": constants.CompileVars.RESULT,
+            "router": constants.CompileVars.ROUTER,
             "event_endpoint": constants.Endpoint.EVENT.name,
             "event_endpoint": constants.Endpoint.EVENT.name,
-            "events": constants.EVENTS,
-            "state": constants.STATE,
-            "final": constants.FINAL,
-            "processing": constants.PROCESSING,
+            "events": constants.CompileVars.EVENTS,
+            "state": constants.CompileVars.STATE,
+            "final": constants.CompileVars.FINAL,
+            "processing": constants.CompileVars.PROCESSING,
             "initial_result": {
             "initial_result": {
-                constants.STATE: None,
-                constants.EVENTS: [],
-                constants.FINAL: True,
-                constants.PROCESSING: False,
+                constants.CompileVars.STATE: None,
+                constants.CompileVars.EVENTS: [],
+                constants.CompileVars.FINAL: True,
+                constants.CompileVars.PROCESSING: False,
             },
             },
-            "color_mode": constants.COLOR_MODE,
-            "toggle_color_mode": constants.TOGGLE_COLOR_MODE,
-            "use_color_mode": constants.USE_COLOR_MODE,
-            "hydrate": constants.HYDRATE,
+            "color_mode": constants.ColorMode.NAME,
+            "toggle_color_mode": constants.ColorMode.TOGGLE,
+            "use_color_mode": constants.ColorMode.USE,
+            "hydrate": constants.CompileVars.HYDRATE,
         }
         }
 
 
 
 

+ 11 - 11
reflex/compiler/utils.py

@@ -93,7 +93,7 @@ def compile_imports(imports: imports.ImportDict) -> list[dict]:
         default, rest = compile_import_statement(fields)
         default, rest = compile_import_statement(fields)
 
 
         # prevent lib from being rendered on the page if all imports are non rendered kind
         # prevent lib from being rendered on the page if all imports are non rendered kind
-        if all({not f.render for f in fields}):  # type: ignore
+        if not any({f.render for f in fields}):  # type: ignore
             continue
             continue
 
 
         if not lib:
         if not lib:
@@ -104,9 +104,7 @@ def compile_imports(imports: imports.ImportDict) -> list[dict]:
             continue
             continue
 
 
         # remove the version before rendering the package imports
         # remove the version before rendering the package imports
-        lib, at, version = lib.rpartition("@")
-        if not lib:
-            lib = at + version
+        lib = format.format_library_name(lib)
 
 
         import_dicts.append(get_import_dict(lib, default, rest))
         import_dicts.append(get_import_dict(lib, default, rest))
     return import_dicts
     return import_dicts
@@ -313,7 +311,7 @@ def get_page_path(path: str) -> str:
     Returns:
     Returns:
         The path of the compiled JS file.
         The path of the compiled JS file.
     """
     """
-    return os.path.join(constants.WEB_PAGES_DIR, path + constants.JS_EXT)
+    return os.path.join(constants.Dirs.WEB_PAGES, path + constants.Ext.JS)
 
 
 
 
 def get_theme_path() -> str:
 def get_theme_path() -> str:
@@ -322,7 +320,9 @@ def get_theme_path() -> str:
     Returns:
     Returns:
         The path of the theme style.
         The path of the theme style.
     """
     """
-    return os.path.join(constants.WEB_UTILS_DIR, constants.THEME + constants.JS_EXT)
+    return os.path.join(
+        constants.Dirs.WEB_UTILS, constants.PageNames.THEME + constants.Ext.JS
+    )
 
 
 
 
 def get_root_stylesheet_path() -> str:
 def get_root_stylesheet_path() -> str:
@@ -332,7 +332,7 @@ def get_root_stylesheet_path() -> str:
         The path of the app root file.
         The path of the app root file.
     """
     """
     return os.path.join(
     return os.path.join(
-        constants.STYLES_DIR, constants.STYLESHEET_ROOT + constants.CSS_EXT
+        constants.STYLES_DIR, constants.PageNames.STYLESHEET_ROOT + constants.Ext.CSS
     )
     )
 
 
 
 
@@ -342,7 +342,7 @@ def get_context_path() -> str:
     Returns:
     Returns:
         The path of the context module.
         The path of the context module.
     """
     """
-    return os.path.join(constants.WEB_UTILS_DIR, "context" + constants.JS_EXT)
+    return os.path.join(constants.Dirs.WEB_UTILS, "context" + constants.Ext.JS)
 
 
 
 
 def get_components_path() -> str:
 def get_components_path() -> str:
@@ -351,7 +351,7 @@ def get_components_path() -> str:
     Returns:
     Returns:
         The path of the compiled components.
         The path of the compiled components.
     """
     """
-    return os.path.join(constants.WEB_UTILS_DIR, "components" + constants.JS_EXT)
+    return os.path.join(constants.Dirs.WEB_UTILS, "components" + constants.Ext.JS)
 
 
 
 
 def get_asset_path(filename: str | None = None) -> str:
 def get_asset_path(filename: str | None = None) -> str:
@@ -364,9 +364,9 @@ def get_asset_path(filename: str | None = None) -> str:
         The path of the asset.
         The path of the asset.
     """
     """
     if filename is None:
     if filename is None:
-        return constants.WEB_ASSETS_DIR
+        return constants.Dirs.WEB_ASSETS
     else:
     else:
-        return os.path.join(constants.WEB_ASSETS_DIR, filename)
+        return os.path.join(constants.Dirs.WEB_ASSETS, filename)
 
 
 
 
 def add_meta(
 def add_meta(

+ 2 - 3
reflex/components/component.py

@@ -7,10 +7,9 @@ from abc import ABC
 from functools import wraps
 from functools import wraps
 from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
 from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
 
 
-from reflex import constants
 from reflex.base import Base
 from reflex.base import Base
 from reflex.components.tags import Tag
 from reflex.components.tags import Tag
-from reflex.constants import EventTriggers
+from reflex.constants import Dirs, EventTriggers
 from reflex.event import (
 from reflex.event import (
     EventChain,
     EventChain,
     EventHandler,
     EventHandler,
@@ -762,7 +761,7 @@ class CustomComponent(Component):
     """A custom user-defined component."""
     """A custom user-defined component."""
 
 
     # Use the components library.
     # Use the components library.
-    library = f"/{constants.COMPONENTS_PATH}"
+    library = f"/{Dirs.COMPONENTS_PATH}"
 
 
     # The function that creates the component.
     # The function that creates the component.
     component_fn: Callable[..., Component] = Component.create
     component_fn: Callable[..., Component] = Component.create

+ 2 - 4
reflex/config.py

@@ -163,7 +163,7 @@ class Config(Base):
     telemetry_enabled: bool = True
     telemetry_enabled: bool = True
 
 
     # The bun path
     # The bun path
-    bun_path: str = constants.DEFAULT_BUN_PATH
+    bun_path: str = constants.Bun.DEFAULT_PATH
 
 
     # List of origins that are allowed to connect to the backend API.
     # List of origins that are allowed to connect to the backend API.
     cors_allowed_origins: List[str] = ["*"]
     cors_allowed_origins: List[str] = ["*"]
@@ -284,11 +284,9 @@ def get_config(reload: bool = False) -> Config:
     Returns:
     Returns:
         The app config.
         The app config.
     """
     """
-    from reflex.config import Config
-
     sys.path.insert(0, os.getcwd())
     sys.path.insert(0, os.getcwd())
     try:
     try:
-        rxconfig = __import__(constants.CONFIG_MODULE)
+        rxconfig = __import__(constants.Config.MODULE)
         if reload:
         if reload:
             importlib.reload(rxconfig)
             importlib.reload(rxconfig)
         return rxconfig.config
         return rxconfig.config

+ 0 - 444
reflex/constants.py

@@ -1,444 +0,0 @@
-"""Constants used throughout the package."""
-from __future__ import annotations
-
-import os
-import platform
-import re
-from enum import Enum
-from types import SimpleNamespace
-
-from platformdirs import PlatformDirs
-
-# importlib is only available for Python 3.8+ so we need the backport for Python 3.7
-try:
-    from importlib import metadata
-except ImportError:
-    import importlib_metadata as metadata  # pyright: ignore[reportMissingImports]
-
-IS_WINDOWS = platform.system() == "Windows"
-
-
-def get_fnm_name() -> str | None:
-    """Get the appropriate fnm executable name based on the current platform.
-
-    Returns:
-            The fnm executable name for the current platform.
-    """
-    platform_os = platform.system()
-
-    if platform_os == "Windows":
-        return "fnm-windows"
-    elif platform_os == "Darwin":
-        return "fnm-macos"
-    elif platform_os == "Linux":
-        machine = platform.machine()
-        if machine == "arm" or machine.startswith("armv7"):
-            return "fnm-arm32"
-        elif machine.startswith("aarch") or machine.startswith("armv8"):
-            return "fnm-arm64"
-        return "fnm-linux"
-    return None
-
-
-# App names and versions.
-# The name of the Reflex package.
-MODULE_NAME = "reflex"
-# The current version of Reflex.
-VERSION = metadata.version(MODULE_NAME)
-
-# Files and directories used to init a new project.
-# The directory to store reflex dependencies.
-REFLEX_DIR = (
-    # on windows, we use C:/Users/<username>/AppData/Local/reflex.
-    # on macOS, we use ~/Library/Application Support/reflex.
-    # on linux, we use ~/.local/share/reflex.
-    PlatformDirs(MODULE_NAME, False).user_data_dir
-)
-# The root directory of the reflex library.
-ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-# The name of the assets directory.
-APP_ASSETS_DIR = "assets"
-# The template directory used during reflex init.
-TEMPLATE_DIR = os.path.join(ROOT_DIR, MODULE_NAME, ".templates")
-# The web subdirectory of the template directory.
-WEB_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "web")
-# The assets subdirectory of the template directory.
-ASSETS_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, APP_ASSETS_DIR)
-# The jinja template directory.
-JINJA_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "jinja")
-
-# Bun config.
-# The Bun version.
-BUN_VERSION = "0.7.3"
-# Min Bun Version
-MIN_BUN_VERSION = "0.7.0"
-# The directory to store the bun.
-BUN_ROOT_PATH = os.path.join(REFLEX_DIR, "bun")
-# Default bun path.
-DEFAULT_BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
-# URL to bun install script.
-BUN_INSTALL_URL = "https://bun.sh/install"
-
-# FNM / Node config.
-# The FNM version.
-FNM_VERSION = "1.35.1"
-# The Node version.
-NODE_VERSION = "18.17.0"
-# The minimum required node version.
-NODE_VERSION_MIN = "16.8.0"
-# The directory to store fnm.
-FNM_DIR = os.path.join(REFLEX_DIR, "fnm")
-FNM_FILENAME = get_fnm_name()
-# The fnm executable binary.
-FNM_EXE = os.path.join(FNM_DIR, "fnm.exe" if IS_WINDOWS else "fnm")
-# The node bin path.
-NODE_BIN_PATH = os.path.join(
-    FNM_DIR,
-    "node-versions",
-    f"v{NODE_VERSION}",
-    "installation",
-    "bin" if not IS_WINDOWS else "",
-)
-# The default path where node is installed.
-NODE_PATH = os.path.join(NODE_BIN_PATH, "node.exe" if IS_WINDOWS else "node")
-# The default path where npm is installed.
-NPM_PATH = os.path.join(NODE_BIN_PATH, "npm")
-# The URL to the fnm release binary
-FNM_INSTALL_URL = (
-    f"https://github.com/Schniz/fnm/releases/download/v{FNM_VERSION}/{FNM_FILENAME}.zip"
-)
-# The frontend directories in a project.
-# The web folder where the NextJS app is compiled to.
-WEB_DIR = ".web"
-# The name of the utils file.
-UTILS_DIR = "utils"
-# The name of the output static directory.
-STATIC_DIR = "_static"
-# The name of the state file.
-STATE_PATH = "/".join([UTILS_DIR, "state"])
-# The name of the components file.
-COMPONENTS_PATH = "/".join([UTILS_DIR, "components"])
-# The directory where the app pages are compiled to.
-WEB_PAGES_DIR = os.path.join(WEB_DIR, "pages")
-# The directory where the static build is located.
-WEB_STATIC_DIR = os.path.join(WEB_DIR, STATIC_DIR)
-# The directory where the utils file is located.
-WEB_UTILS_DIR = os.path.join(WEB_DIR, UTILS_DIR)
-# The directory where the assets are located.
-WEB_ASSETS_DIR = os.path.join(WEB_DIR, "public")
-# The directory where styles are located.
-STYLES_DIR = os.path.join(WEB_DIR, "styles")
-# The Tailwind config.
-TAILWIND_CONFIG = os.path.join(WEB_DIR, "tailwind.config.js")
-# Default Tailwind content paths
-TAILWIND_CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"]
-# Relative tailwind style path to root stylesheet in STYLES_DIR.
-TAILWIND_ROOT_STYLE_PATH = "./tailwind.css"
-# The Tailwindcss version
-TAILWIND_VERSION = "tailwindcss@^3.3.2"
-# The package json file
-PACKAGE_JSON_PATH = os.path.join(WEB_DIR, "package.json")
-# The NextJS config file
-NEXT_CONFIG_FILE = "next.config.js"
-# The sitemap config file.
-SITEMAP_CONFIG_FILE = os.path.join(WEB_DIR, "next-sitemap.config.js")
-# The node modules directory.
-NODE_MODULES = "node_modules"
-# The package lock file.
-PACKAGE_LOCK = "package-lock.json"
-# The reflex json file.
-REFLEX_JSON = os.path.join(WEB_DIR, "reflex.json")
-# The env json file.
-ENV_JSON = os.path.join(WEB_DIR, "env.json")
-
-# Compiler variables.
-# The extension for compiled Javascript files.
-JS_EXT = ".js"
-# The extension for python files.
-PY_EXT = ".py"
-# The extension for css files.
-CSS_EXT = ".css"
-# The expected variable name where the rx.App is stored.
-APP_VAR = "app"
-# The expected variable name where the API object is stored for deployment.
-API_VAR = "api"
-# The name of the router variable.
-ROUTER = "router"
-# The name of the socket variable.
-SOCKET = "socket"
-# The name of the variable to hold API results.
-RESULT = "result"
-# The name of the final variable.
-FINAL = "final"
-# The name of the process variable.
-PROCESSING = "processing"
-# The name of the state variable.
-STATE = "state"
-# The name of the events variable.
-EVENTS = "events"
-# The name of the initial hydrate event.
-HYDRATE = "hydrate"
-# The name of the is_hydrated variable.
-IS_HYDRATED = "is_hydrated"
-# The name of the index page.
-INDEX_ROUTE = "index"
-# The name of the app root page.
-APP_ROOT = "_app"
-# The root stylesheet filename.
-STYLESHEET_ROOT = "styles"
-# The name of the document root page.
-DOCUMENT_ROOT = "_document"
-# The name of the theme page.
-THEME = "theme"
-# The prefix used to create setters for state vars.
-SETTER_PREFIX = "set_"
-# The name of the frontend zip during deployment.
-FRONTEND_ZIP = "frontend.zip"
-# The name of the backend zip during deployment.
-BACKEND_ZIP = "backend.zip"
-# The default title to show for Reflex apps.
-DEFAULT_TITLE = "Reflex App"
-# The default description to show for Reflex apps.
-DEFAULT_DESCRIPTION = "A Reflex app."
-# The default image to show for Reflex apps.
-DEFAULT_IMAGE = "favicon.ico"
-# The default meta list to show for Reflex apps.
-DEFAULT_META_LIST = []
-
-# The gitignore file.
-GITIGNORE_FILE = ".gitignore"
-# Files to gitignore.
-DEFAULT_GITIGNORE = {WEB_DIR, "*.db", "__pycache__/", "*.py[cod]"}
-# The name of the reflex config module.
-CONFIG_MODULE = "rxconfig"
-# The python config file.
-CONFIG_FILE = f"{CONFIG_MODULE}{PY_EXT}"
-# The previous config file.
-OLD_CONFIG_FILE = f"pcconfig{PY_EXT}"
-# The deployment URL.
-PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
-# Token expiration time in seconds.
-TOKEN_EXPIRATION = 60 * 60
-# Maximum time in milliseconds that a state can be locked for exclusive access.
-LOCK_EXPIRATION = 10000
-
-# Testing variables.
-# Testing os env set by pytest when running a test case.
-PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
-
-
-# Env modes
-class Env(str, Enum):
-    """The environment modes."""
-
-    DEV = "dev"
-    PROD = "prod"
-
-
-# Log levels
-class LogLevel(str, Enum):
-    """The log levels."""
-
-    DEBUG = "debug"
-    INFO = "info"
-    WARNING = "warning"
-    ERROR = "error"
-    CRITICAL = "critical"
-
-    def __le__(self, other: LogLevel) -> bool:
-        """Compare log levels.
-
-        Args:
-            other: The other log level.
-
-        Returns:
-            True if the log level is less than or equal to the other log level.
-        """
-        levels = list(LogLevel)
-        return levels.index(self) <= levels.index(other)
-
-
-# Templates
-class Template(str, Enum):
-    """The templates to use for the app."""
-
-    DEFAULT = "default"
-    COUNTER = "counter"
-
-
-class Endpoint(Enum):
-    """Endpoints for the reflex backend API."""
-
-    PING = "ping"
-    EVENT = "_event"
-    UPLOAD = "_upload"
-
-    def __str__(self) -> str:
-        """Get the string representation of the endpoint.
-
-        Returns:
-            The path for the endpoint.
-        """
-        return f"/{self.value}"
-
-    def get_url(self) -> str:
-        """Get the URL for the endpoint.
-
-        Returns:
-            The full URL for the endpoint.
-        """
-        # Import here to avoid circular imports.
-        from reflex.config import get_config
-
-        # Get the API URL from the config.
-        config = get_config()
-        url = "".join([config.api_url, str(self)])
-
-        # The event endpoint is a websocket.
-        if self == Endpoint.EVENT:
-            # Replace the protocol with ws.
-            url = url.replace("https://", "wss://").replace("http://", "ws://")
-
-        # Return the url.
-        return url
-
-
-class SocketEvent(Enum):
-    """Socket events sent by the reflex backend API."""
-
-    PING = "ping"
-    EVENT = "event"
-
-    def __str__(self) -> str:
-        """Get the string representation of the event name.
-
-        Returns:
-            The event name string.
-        """
-        return str(self.value)
-
-
-class RouteArgType(SimpleNamespace):
-    """Type of dynamic route arg extracted from URI route."""
-
-    # Typecast to str is needed for Enum to work.
-    SINGLE = str("arg_single")
-    LIST = str("arg_list")
-
-
-# the name of the backend var containing path and client information
-ROUTER_DATA = "router_data"
-
-
-class RouteVar(SimpleNamespace):
-    """Names of variables used in the router_data dict stored in State."""
-
-    CLIENT_IP = "ip"
-    CLIENT_TOKEN = "token"
-    HEADERS = "headers"
-    PATH = "pathname"
-    ORIGIN = "asPath"
-    SESSION_ID = "sid"
-    QUERY = "query"
-    COOKIE = "cookie"
-
-
-class RouteRegex(SimpleNamespace):
-    """Regex used for extracting route args in route."""
-
-    ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
-    # group return the catchall pattern (i.e. "[[..slug]]")
-    CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
-    # group return the arg name (i.e. "slug")
-    STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
-    # group return the arg name (i.e. "slug")
-    OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
-
-
-class PackageJsonCommands(SimpleNamespace):
-    """Commands used in package.json file."""
-
-    DEV = "next dev"
-    EXPORT = "next build && next export -o _static"
-    EXPORT_SITEMAP = "next build && next-sitemap && next export -o _static"
-    PROD = "next start"
-
-
-PACKAGE_DEPENDENCIES = {
-    "@chakra-ui/react": "^2.6.0",
-    "@chakra-ui/system": "^2.5.6",
-    "@emotion/react": "^11.10.6",
-    "@emotion/styled": "^11.10.6",
-    "axios": "^1.4.0",
-    "chakra-react-select": "^4.6.0",
-    "focus-visible": "^5.2.0",
-    "json5": "^2.2.3",
-    "next": "^13.3.1",
-    "next-sitemap": "^4.1.8",
-    "react": "^18.2.0",
-    "react-dom": "^18.2.0",
-    "socket.io-client": "^4.6.1",
-    "universal-cookie": "^4.0.4",
-}
-PACKAGE_DEV_DEPENDENCIES = {
-    "autoprefixer": "^10.4.14",
-    "postcss": "^8.4.24",
-}
-
-# 404 variables
-SLUG_404 = "404"
-TITLE_404 = "404 - Not Found"
-FAVICON_404 = "favicon.ico"
-DESCRIPTION_404 = "The page was not found"
-ROUTE_NOT_FOUND = "routeNotFound"
-
-# Color mode variables
-USE_COLOR_MODE = "useColorMode"
-COLOR_MODE = "colorMode"
-TOGGLE_COLOR_MODE = "toggleColorMode"
-
-# Server socket configuration variables
-POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
-PING_INTERVAL = 25
-PING_TIMEOUT = 120
-
-# Alembic migrations
-ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")
-
-# Keys in the client_side_storage dict
-COOKIES = "cookies"
-LOCAL_STORAGE = "local_storage"
-
-
-class EventTriggers(SimpleNamespace):
-    """All trigger names used in Reflex."""
-
-    ON_FOCUS = "on_focus"
-    ON_BLUR = "on_blur"
-    ON_CANCEL = "on_cancel"
-    ON_CLICK = "on_click"
-    ON_CHANGE = "on_change"
-    ON_CHANGE_END = "on_change_end"
-    ON_CHANGE_START = "on_change_start"
-    ON_COMPLETE = "on_complete"
-    ON_CONTEXT_MENU = "on_context_menu"
-    ON_DOUBLE_CLICK = "on_double_click"
-    ON_DROP = "on_drop"
-    ON_EDIT = "on_edit"
-    ON_KEY_DOWN = "on_key_down"
-    ON_KEY_UP = "on_key_up"
-    ON_MOUSE_DOWN = "on_mouse_down"
-    ON_MOUSE_ENTER = "on_mouse_enter"
-    ON_MOUSE_LEAVE = "on_mouse_leave"
-    ON_MOUSE_MOVE = "on_mouse_move"
-    ON_MOUSE_OUT = "on_mouse_out"
-    ON_MOUSE_OVER = "on_mouse_over"
-    ON_MOUSE_UP = "on_mouse_up"
-    ON_SCROLL = "on_scroll"
-    ON_SUBMIT = "on_submit"
-    ON_MOUNT = "on_mount"
-    ON_UNMOUNT = "on_unmount"
-
-
-# If this env var is set to "yes", App.compile will be a no-op
-SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"

+ 92 - 0
reflex/constants/__init__.py

@@ -0,0 +1,92 @@
+"""The constants package."""
+
+from .base import (
+    COOKIES,
+    IS_WINDOWS,
+    LOCAL_STORAGE,
+    POLLING_MAX_HTTP_BUFFER_SIZE,
+    PYTEST_CURRENT_TEST,
+    SKIP_COMPILE_ENV_VAR,
+    ColorMode,
+    Dirs,
+    Env,
+    LogLevel,
+    Next,
+    Ping,
+    Reflex,
+    Templates,
+)
+from .compiler import (
+    SETTER_PREFIX,
+    CompileVars,
+    ComponentName,
+    Ext,
+    PageNames,
+)
+from .config import (
+    ALEMBIC_CONFIG,
+    PRODUCTION_BACKEND_URL,
+    Config,
+    Expiration,
+    GitIgnore,
+)
+from .event import Endpoint, EventTriggers, SocketEvent
+from .installer import (
+    Bun,
+    Fnm,
+    Node,
+    PackageJson,
+)
+from .route import (
+    ROUTE_NOT_FOUND,
+    ROUTER_DATA,
+    DefaultPage,
+    Page404,
+    RouteArgType,
+    RouteRegex,
+    RouteVar,
+)
+from .style import STYLES_DIR, Tailwind
+
+__ALL__ = [
+    ALEMBIC_CONFIG,
+    Bun,
+    ColorMode,
+    Config,
+    COOKIES,
+    ComponentName,
+    DefaultPage,
+    Dirs,
+    Endpoint,
+    Env,
+    EventTriggers,
+    Expiration,
+    Ext,
+    Fnm,
+    GitIgnore,
+    IS_WINDOWS,
+    LOCAL_STORAGE,
+    LogLevel,
+    Next,
+    Node,
+    PackageJson,
+    PageNames,
+    Page404,
+    Ping,
+    POLLING_MAX_HTTP_BUFFER_SIZE,
+    PYTEST_CURRENT_TEST,
+    PRODUCTION_BACKEND_URL,
+    Reflex,
+    RouteVar,
+    RouteRegex,
+    RouteArgType,
+    ROUTER_DATA,
+    ROUTE_NOT_FOUND,
+    SETTER_PREFIX,
+    SKIP_COMPILE_ENV_VAR,
+    SocketEvent,
+    STYLES_DIR,
+    Tailwind,
+    Templates,
+    CompileVars,
+]

+ 174 - 0
reflex/constants/base.py

@@ -0,0 +1,174 @@
+"""Base file for constants that don't fit any other categories."""
+
+from __future__ import annotations
+
+import os
+import platform
+from enum import Enum
+from types import SimpleNamespace
+
+from platformdirs import PlatformDirs
+
+# importlib is only available for Python 3.8+ so we need the backport for Python 3.7
+try:
+    from importlib import metadata
+except ImportError:
+    import importlib_metadata as metadata  # pyright: ignore[reportMissingImports]
+
+
+IS_WINDOWS = platform.system() == "Windows"
+
+
+class Dirs(SimpleNamespace):
+    """Various directories/paths used by Reflex."""
+
+    # The frontend directories in a project.
+    # The web folder where the NextJS app is compiled to.
+    WEB = ".web"
+    # The name of the assets directory.
+    APP_ASSETS = "assets"
+    # The name of the utils file.
+    UTILS = "utils"
+    # The name of the output static directory.
+    STATIC = "_static"
+    # The name of the state file.
+    STATE_PATH = "/".join([UTILS, "state"])
+    # The name of the components file.
+    COMPONENTS_PATH = "/".join([UTILS, "components"])
+    # The directory where the app pages are compiled to.
+    WEB_PAGES = os.path.join(WEB, "pages")
+    # The directory where the static build is located.
+    WEB_STATIC = os.path.join(WEB, STATIC)
+    # The directory where the utils file is located.
+    WEB_UTILS = os.path.join(WEB, UTILS)
+    # The directory where the assets are located.
+    WEB_ASSETS = os.path.join(WEB, "public")
+    # The env json file.
+    ENV_JSON = os.path.join(WEB, "env.json")
+
+
+class Reflex(SimpleNamespace):
+    """Base constants concerning Reflex."""
+
+    # App names and versions.
+    # The name of the Reflex package.
+    MODULE_NAME = "reflex"
+    # The current version of Reflex.
+    VERSION = metadata.version(MODULE_NAME)
+
+    # The reflex json file.
+    JSON = os.path.join(Dirs.WEB, "reflex.json")
+
+    # Files and directories used to init a new project.
+    # The directory to store reflex dependencies.
+    DIR = (
+        # on windows, we use C:/Users/<username>/AppData/Local/reflex.
+        # on macOS, we use ~/Library/Application Support/reflex.
+        # on linux, we use ~/.local/share/reflex.
+        PlatformDirs(MODULE_NAME, False).user_data_dir
+    )
+    # The root directory of the reflex library.
+
+    ROOT_DIR = os.path.dirname(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+    )
+
+
+class Templates(SimpleNamespace):
+    """Constants related to Templates."""
+
+    class Kind(str, Enum):
+        """The templates to use for the app."""
+
+        DEFAULT = "default"
+        COUNTER = "counter"
+
+    class Dirs(SimpleNamespace):
+        """Folders used by the template system of Reflex."""
+
+        # The template directory used during reflex init.
+        BASE = os.path.join(Reflex.ROOT_DIR, Reflex.MODULE_NAME, ".templates")
+        # The web subdirectory of the template directory.
+        WEB_TEMPLATE = os.path.join(BASE, "web")
+        # The assets subdirectory of the template directory.
+        ASSETS_TEMPLATE = os.path.join(BASE, Dirs.APP_ASSETS)
+        # The jinja template directory.
+        JINJA_TEMPLATE = os.path.join(BASE, "jinja")
+
+
+class Next(SimpleNamespace):
+    """Constants related to NextJS."""
+
+    # The NextJS config file
+    CONFIG_FILE = "next.config.js"
+    # The sitemap config file.
+    SITEMAP_CONFIG_FILE = os.path.join(Dirs.WEB, "next-sitemap.config.js")
+    # The node modules directory.
+    NODE_MODULES = "node_modules"
+    # The package lock file.
+    PACKAGE_LOCK = "package-lock.json"
+
+
+# Color mode variables
+class ColorMode(SimpleNamespace):
+    """Constants related to ColorMode."""
+
+    NAME = "colorMode"
+    USE = "useColorMode"
+    TOGGLE = "toggleColorMode"
+
+
+# Env modes
+class Env(str, Enum):
+    """The environment modes."""
+
+    DEV = "dev"
+    PROD = "prod"
+
+
+# Log levels
+class LogLevel(str, Enum):
+    """The log levels."""
+
+    DEBUG = "debug"
+    INFO = "info"
+    WARNING = "warning"
+    ERROR = "error"
+    CRITICAL = "critical"
+
+    def __le__(self, other: LogLevel) -> bool:
+        """Compare log levels.
+
+        Args:
+            other: The other log level.
+
+        Returns:
+            True if the log level is less than or equal to the other log level.
+        """
+        levels = list(LogLevel)
+        return levels.index(self) <= levels.index(other)
+
+
+# Server socket configuration variables
+POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
+
+
+class Ping(SimpleNamespace):
+    """PING constants."""
+
+    # The 'ping' interval
+    INTERVAL = 25
+    # The 'ping' timeout
+    TIMEOUT = 120
+
+
+# Keys in the client_side_storage dict
+COOKIES = "cookies"
+LOCAL_STORAGE = "local_storage"
+
+# If this env var is set to "yes", App.compile will be a no-op
+SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
+
+# Testing variables.
+# Testing os env set by pytest when running a test case.
+PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"

+ 76 - 0
reflex/constants/compiler.py

@@ -0,0 +1,76 @@
+"""Compiler variables."""
+from enum import Enum
+from types import SimpleNamespace
+
+# The prefix used to create setters for state vars.
+SETTER_PREFIX = "set_"
+
+
+class Ext(SimpleNamespace):
+    """Extension used in Reflex."""
+
+    # The extension for JS files.
+    JS = ".js"
+    # The extension for python files.
+    PY = ".py"
+    # The extension for css files.
+    CSS = ".css"
+    # The extension for zip files.
+    ZIP = ".zip"
+
+
+class CompileVars(SimpleNamespace):
+    """The variables used during compilation."""
+
+    # The expected variable name where the rx.App is stored.
+    APP = "app"
+    # The expected variable name where the API object is stored for deployment.
+    API = "api"
+    # The name of the router variable.
+    ROUTER = "router"
+    # The name of the socket variable.
+    SOCKET = "socket"
+    # The name of the variable to hold API results.
+    RESULT = "result"
+    # The name of the final variable.
+    FINAL = "final"
+    # The name of the process variable.
+    PROCESSING = "processing"
+    # The name of the state variable.
+    STATE = "state"
+    # The name of the events variable.
+    EVENTS = "events"
+    # The name of the initial hydrate event.
+    HYDRATE = "hydrate"
+    # The name of the is_hydrated variable.
+    IS_HYDRATED = "is_hydrated"
+
+
+class PageNames(SimpleNamespace):
+    """The name of basic pages deployed in NextJS."""
+
+    # The name of the index page.
+    INDEX_ROUTE = "index"
+    # The name of the app root page.
+    APP_ROOT = "_app"
+    # The root stylesheet filename.
+    STYLESHEET_ROOT = "styles"
+    # The name of the document root page.
+    DOCUMENT_ROOT = "_document"
+    # The name of the theme page.
+    THEME = "theme"
+
+
+class ComponentName(Enum):
+    """Component names."""
+
+    BACKEND = "Backend"
+    FRONTEND = "Frontend"
+
+    def zip(self):
+        """Give the zip filename for the component.
+
+        Returns:
+            The lower-case filename with zip extension.
+        """
+        return self.value.lower() + Ext.ZIP

+ 45 - 0
reflex/constants/config.py

@@ -0,0 +1,45 @@
+"""Config constants."""
+import os
+from types import SimpleNamespace
+
+from reflex.constants.base import Dirs
+
+from .compiler import Ext
+
+# Alembic migrations
+ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")
+
+
+class Config(SimpleNamespace):
+    """Config constants."""
+
+    # The name of the reflex config module.
+    MODULE = "rxconfig"
+    # The python config file.
+    FILE = f"{MODULE}{Ext.PY}"
+    # The previous config file.
+    PREVIOUS_FILE = f"pcconfig{Ext.PY}"
+
+
+class Expiration(SimpleNamespace):
+    """Expiration constants."""
+
+    # Token expiration time in seconds
+    TOKEN = 60 * 60
+    # Maximum time in milliseconds that a state can be locked for exclusive access.
+    LOCK = 10000
+    # The PING timeout
+    PING = 120
+
+
+class GitIgnore(SimpleNamespace):
+    """Gitignore constants."""
+
+    # The gitignore file.
+    FILE = ".gitignore"
+    # Files to gitignore.
+    DEFAULTS = {Dirs.WEB, "*.db", "__pycache__/", "*.py[cod]"}
+
+
+# The deployment URL.
+PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"

+ 85 - 0
reflex/constants/event.py

@@ -0,0 +1,85 @@
+"""Event-related constants."""
+from enum import Enum
+from types import SimpleNamespace
+
+
+class Endpoint(Enum):
+    """Endpoints for the reflex backend API."""
+
+    PING = "ping"
+    EVENT = "_event"
+    UPLOAD = "_upload"
+
+    def __str__(self) -> str:
+        """Get the string representation of the endpoint.
+
+        Returns:
+            The path for the endpoint.
+        """
+        return f"/{self.value}"
+
+    def get_url(self) -> str:
+        """Get the URL for the endpoint.
+
+        Returns:
+            The full URL for the endpoint.
+        """
+        # Import here to avoid circular imports.
+        from reflex.config import get_config
+
+        # Get the API URL from the config.
+        config = get_config()
+        url = "".join([config.api_url, str(self)])
+
+        # The event endpoint is a websocket.
+        if self == Endpoint.EVENT:
+            # Replace the protocol with ws.
+            url = url.replace("https://", "wss://").replace("http://", "ws://")
+
+        # Return the url.
+        return url
+
+
+class SocketEvent(SimpleNamespace):
+    """Socket events sent by the reflex backend API."""
+
+    PING = "ping"
+    EVENT = "event"
+
+    def __str__(self) -> str:
+        """Get the string representation of the event name.
+
+        Returns:
+            The event name string.
+        """
+        return str(self.value)
+
+
+class EventTriggers(SimpleNamespace):
+    """All trigger names used in Reflex."""
+
+    ON_FOCUS = "on_focus"
+    ON_BLUR = "on_blur"
+    ON_CANCEL = "on_cancel"
+    ON_CLICK = "on_click"
+    ON_CHANGE = "on_change"
+    ON_CHANGE_END = "on_change_end"
+    ON_CHANGE_START = "on_change_start"
+    ON_COMPLETE = "on_complete"
+    ON_CONTEXT_MENU = "on_context_menu"
+    ON_DOUBLE_CLICK = "on_double_click"
+    ON_DROP = "on_drop"
+    ON_EDIT = "on_edit"
+    ON_KEY_DOWN = "on_key_down"
+    ON_KEY_UP = "on_key_up"
+    ON_MOUSE_DOWN = "on_mouse_down"
+    ON_MOUSE_ENTER = "on_mouse_enter"
+    ON_MOUSE_LEAVE = "on_mouse_leave"
+    ON_MOUSE_MOVE = "on_mouse_move"
+    ON_MOUSE_OUT = "on_mouse_out"
+    ON_MOUSE_OVER = "on_mouse_over"
+    ON_MOUSE_UP = "on_mouse_up"
+    ON_SCROLL = "on_scroll"
+    ON_SUBMIT = "on_submit"
+    ON_MOUNT = "on_mount"
+    ON_UNMOUNT = "on_unmount"

+ 123 - 0
reflex/constants/installer.py

@@ -0,0 +1,123 @@
+"""File for constants related to the installation process. (Bun/FNM/Node)."""
+from __future__ import annotations
+
+import os
+import platform
+from types import SimpleNamespace
+
+from .base import IS_WINDOWS, Dirs, Reflex
+
+
+def get_fnm_name() -> str | None:
+    """Get the appropriate fnm executable name based on the current platform.
+
+    Returns:
+            The fnm executable name for the current platform.
+    """
+    platform_os = platform.system()
+
+    if platform_os == "Windows":
+        return "fnm-windows"
+    elif platform_os == "Darwin":
+        return "fnm-macos"
+    elif platform_os == "Linux":
+        machine = platform.machine()
+        if machine == "arm" or machine.startswith("armv7"):
+            return "fnm-arm32"
+        elif machine.startswith("aarch") or machine.startswith("armv8"):
+            return "fnm-arm64"
+        return "fnm-linux"
+    return None
+
+
+# Bun config.
+class Bun(SimpleNamespace):
+    """Bun constants."""
+
+    # The Bun version.
+    VERSION = "0.7.3"
+    # Min Bun Version
+    MIN_VERSION = "0.7.0"
+    # The directory to store the bun.
+    ROOT_PATH = os.path.join(Reflex.DIR, "bun")
+    # Default bun path.
+    DEFAULT_PATH = os.path.join(ROOT_PATH, "bin", "bun")
+    # URL to bun install script.
+    INSTALL_URL = "https://bun.sh/install"
+
+
+# FNM config.
+class Fnm(SimpleNamespace):
+    """FNM constants."""
+
+    # The FNM version.
+    VERSION = "1.35.1"
+    # The directory to store fnm.
+    DIR = os.path.join(Reflex.DIR, "fnm")
+    FILENAME = get_fnm_name()
+    # The fnm executable binary.
+    EXE = os.path.join(DIR, "fnm.exe" if IS_WINDOWS else "fnm")
+
+    # The URL to the fnm release binary
+    INSTALL_URL = (
+        f"https://github.com/Schniz/fnm/releases/download/v{VERSION}/{FILENAME}.zip"
+    )
+
+
+# Node / NPM config
+class Node(SimpleNamespace):
+    """Node/ NPM constants."""
+
+    # The Node version.
+    VERSION = "18.17.0"
+    # The minimum required node version.
+    MIN_VERSION = "16.8.0"
+
+    # The node bin path.
+    BIN_PATH = os.path.join(
+        Fnm.DIR,
+        "node-versions",
+        f"v{VERSION}",
+        "installation",
+        "bin" if not IS_WINDOWS else "",
+    )
+    # The default path where node is installed.
+    PATH = os.path.join(BIN_PATH, "node.exe" if IS_WINDOWS else "node")
+
+    # The default path where npm is installed.
+    NPM_PATH = os.path.join(BIN_PATH, "npm")
+
+
+class PackageJson(SimpleNamespace):
+    """Constants used to build the package.json file."""
+
+    class Commands(SimpleNamespace):
+        """The commands to define in package.json."""
+
+        DEV = "next dev"
+        EXPORT = "next build && next export -o _static"
+        EXPORT_SITEMAP = "next build && next-sitemap && next export -o _static"
+        PROD = "next start"
+
+    PATH = os.path.join(Dirs.WEB, "package.json")
+
+    DEPENDENCIES = {
+        "@chakra-ui/react": "^2.6.0",
+        "@chakra-ui/system": "^2.5.6",
+        "@emotion/react": "^11.10.6",
+        "@emotion/styled": "^11.10.6",
+        "axios": "^1.4.0",
+        "chakra-react-select": "^4.6.0",
+        "focus-visible": "^5.2.0",
+        "json5": "^2.2.3",
+        "next": "^13.3.1",
+        "next-sitemap": "^4.1.8",
+        "react": "^18.2.0",
+        "react-dom": "^18.2.0",
+        "socket.io-client": "^4.6.1",
+        "universal-cookie": "^4.0.4",
+    }
+    DEV_DEPENDENCIES = {
+        "autoprefixer": "^10.4.14",
+        "postcss": "^8.4.24",
+    }

+ 67 - 0
reflex/constants/route.py

@@ -0,0 +1,67 @@
+"""Route constants."""
+
+import re
+from types import SimpleNamespace
+
+
+class RouteArgType(SimpleNamespace):
+    """Type of dynamic route arg extracted from URI route."""
+
+    # Typecast to str is needed for Enum to work.
+    SINGLE = str("arg_single")
+    LIST = str("arg_list")
+
+
+# the name of the backend var containing path and client information
+ROUTER_DATA = "router_data"
+
+
+class RouteVar(SimpleNamespace):
+    """Names of variables used in the router_data dict stored in State."""
+
+    CLIENT_IP = "ip"
+    CLIENT_TOKEN = "token"
+    HEADERS = "headers"
+    PATH = "pathname"
+    ORIGIN = "asPath"
+    SESSION_ID = "sid"
+    QUERY = "query"
+    COOKIE = "cookie"
+
+
+class RouteRegex(SimpleNamespace):
+    """Regex used for extracting route args in route."""
+
+    ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
+    # group return the catchall pattern (i.e. "[[..slug]]")
+    CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
+    # group return the arg name (i.e. "slug")
+    STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
+    # group return the arg name (i.e. "slug") (optional arg can be empty)
+    OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
+
+
+class DefaultPage(SimpleNamespace):
+    """Default page constants."""
+
+    # The default title to show for Reflex apps.
+    TITLE = "Reflex App"
+    # The default description to show for Reflex apps.
+    DESCRIPTION = "A Reflex app."
+    # The default image to show for Reflex apps.
+    IMAGE = "favicon.ico"
+    # The default meta list to show for Reflex apps.
+    META_LIST = []
+
+
+# 404 variables
+class Page404(SimpleNamespace):
+    """Page 404 constants."""
+
+    SLUG = "404"
+    TITLE = "404 - Not Found"
+    IMAGE = "favicon.ico"
+    DESCRIPTION = "The page was not found"
+
+
+ROUTE_NOT_FOUND = "routeNotFound"

+ 22 - 0
reflex/constants/style.py

@@ -0,0 +1,22 @@
+"""Style constants."""
+
+import os
+from types import SimpleNamespace
+
+from reflex.constants.base import Dirs
+
+# The directory where styles are located.
+STYLES_DIR = os.path.join(Dirs.WEB, "styles")
+
+
+class Tailwind(SimpleNamespace):
+    """Tailwind constants."""
+
+    # The Tailwindcss version
+    VERSION = "tailwindcss@^3.3.2"
+    # The Tailwind config.
+    CONFIG = os.path.join(Dirs.WEB, "tailwind.config.js")
+    # Default Tailwind content paths
+    CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"]
+    # Relative tailwind style path to root stylesheet in STYLES_DIR.
+    ROOT_STYLE_PATH = "./tailwind.css"

+ 2 - 2
reflex/el/constants/reflex.py

@@ -33,7 +33,7 @@ def attr_to_prop(attr_name: str) -> str:
 
 
 
 
 # Names of HTML attributes that are provided by Reflex out of the box.
 # Names of HTML attributes that are provided by Reflex out of the box.
-PYNECONE_PROVIDED_ATTRS = {"class", "id", "style"}
+REFLEX_PROVIDED_ATTRS = {"class", "id", "style"}
 
 
 # ATTR_TO_ELEMENTS contains HTML attribute names, which might be invalid as
 # ATTR_TO_ELEMENTS contains HTML attribute names, which might be invalid as
 # Reflex prop names. PROP_TO_ELEMENTS contains the corresponding Reflex
 # Reflex prop names. PROP_TO_ELEMENTS contains the corresponding Reflex
@@ -41,7 +41,7 @@ PYNECONE_PROVIDED_ATTRS = {"class", "id", "style"}
 PROP_TO_ELEMENTS = {
 PROP_TO_ELEMENTS = {
     attr_to_prop(attr_name): elements
     attr_to_prop(attr_name): elements
     for attr_name, elements in ATTR_TO_ELEMENTS.items()
     for attr_name, elements in ATTR_TO_ELEMENTS.items()
-    if attr_name not in PYNECONE_PROVIDED_ATTRS
+    if attr_name not in REFLEX_PROVIDED_ATTRS
 }
 }
 
 
 # Invert PROP_TO_ELEMENTS to enable easier lookup.
 # Invert PROP_TO_ELEMENTS to enable easier lookup.

+ 11 - 11
reflex/event.py

@@ -192,7 +192,7 @@ class EventChain(Base):
 
 
     events: List[EventSpec]
     events: List[EventSpec]
 
 
-    args_spec: Optional[ArgsSpec]
+    args_spec: Optional[Callable]
 
 
 
 
 class Target(Base):
 class Target(Base):
@@ -481,7 +481,7 @@ def get_hydrate_event(state) -> str:
     Returns:
     Returns:
         The name of the hydrate event.
         The name of the hydrate event.
     """
     """
-    return get_event(state, constants.HYDRATE)
+    return get_event(state, constants.CompileVars.HYDRATE)
 
 
 
 
 def call_event_handler(
 def call_event_handler(
@@ -507,12 +507,12 @@ def call_event_handler(
 
 
     # handle new API using lambda to define triggers
     # handle new API using lambda to define triggers
     if isinstance(arg_spec, ArgsSpec):
     if isinstance(arg_spec, ArgsSpec):
-        parsed_args = parse_args_spec(arg_spec)
+        parsed_args = parse_args_spec(arg_spec)  # type: ignore
 
 
         if len(args) == len(["self", *parsed_args]):
         if len(args) == len(["self", *parsed_args]):
             return event_handler(*parsed_args)  # type: ignore
             return event_handler(*parsed_args)  # type: ignore
         else:
         else:
-            source = inspect.getsource(arg_spec)
+            source = inspect.getsource(arg_spec)  # type: ignore
             raise ValueError(
             raise ValueError(
                 f"number of arguments in {event_handler.fn.__name__} "
                 f"number of arguments in {event_handler.fn.__name__} "
                 f"doesn't match the definition '{source.strip().strip(',')}'"
                 f"doesn't match the definition '{source.strip().strip(',')}'"
@@ -524,12 +524,12 @@ def call_event_handler(
             deprecation_version="0.2.8",
             deprecation_version="0.2.8",
             removal_version="0.2.9",
             removal_version="0.2.9",
         )
         )
-    if len(args) == 1:
-        return event_handler()
-    assert (
-        len(args) == 2
-    ), f"Event handler {event_handler.fn} must have 1 or 2 arguments."
-    return event_handler(arg_spec)
+        if len(args) == 1:
+            return event_handler()
+        assert (
+            len(args) == 2
+        ), f"Event handler {event_handler.fn} must have 1 or 2 arguments."
+        return event_handler(arg_spec)  # type: ignore
 
 
 
 
 def parse_args_spec(arg_spec: ArgsSpec):
 def parse_args_spec(arg_spec: ArgsSpec):
@@ -578,7 +578,7 @@ def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec]:
     args = inspect.getfullargspec(fn).args
     args = inspect.getfullargspec(fn).args
 
 
     if isinstance(arg, ArgsSpec):
     if isinstance(arg, ArgsSpec):
-        out = fn(*parse_args_spec(arg))
+        out = fn(*parse_args_spec(arg))  # type: ignore
     else:
     else:
         # Call the lambda.
         # Call the lambda.
         if len(args) == 0:
         if len(args) == 0:

+ 2 - 2
reflex/middleware/hydrate_middleware.py

@@ -13,7 +13,7 @@ if TYPE_CHECKING:
     from reflex.app import App
     from reflex.app import App
 
 
 
 
-State.add_var(constants.IS_HYDRATED, type_=bool, default_value=False)
+State.add_var(constants.CompileVars.IS_HYDRATED, type_=bool, default_value=False)
 
 
 
 
 class HydrateMiddleware(Middleware):
 class HydrateMiddleware(Middleware):
@@ -40,7 +40,7 @@ class HydrateMiddleware(Middleware):
         state._reset_client_storage()
         state._reset_client_storage()
 
 
         # Mark state as not hydrated (until on_loads are complete)
         # Mark state as not hydrated (until on_loads are complete)
-        setattr(state, constants.IS_HYDRATED, False)
+        setattr(state, constants.CompileVars.IS_HYDRATED, False)
 
 
         # Apply client side storage values to state
         # Apply client side storage values to state
         for storage_type in (constants.COOKIES, constants.LOCAL_STORAGE):
         for storage_type in (constants.COOKIES, constants.LOCAL_STORAGE):

+ 9 - 8
reflex/reflex.py

@@ -29,7 +29,7 @@ def version(value: bool):
         typer.Exit: If the version flag was passed.
         typer.Exit: If the version flag was passed.
     """
     """
     if value:
     if value:
-        console.print(constants.VERSION)
+        console.print(constants.Reflex.VERSION)
         raise typer.Exit()
         raise typer.Exit()
 
 
 
 
@@ -53,8 +53,9 @@ def init(
     name: str = typer.Option(
     name: str = typer.Option(
         None, metavar="APP_NAME", help="The name of the app to initialize."
         None, metavar="APP_NAME", help="The name of the app to initialize."
     ),
     ),
-    template: constants.Template = typer.Option(
-        constants.Template.DEFAULT, help="The template to initialize the app with."
+    template: constants.Templates.Kind = typer.Option(
+        constants.Templates.Kind.DEFAULT,
+        help="The template to initialize the app with.",
     ),
     ),
     loglevel: constants.LogLevel = typer.Option(
     loglevel: constants.LogLevel = typer.Option(
         config.loglevel, help="The log level to use."
         config.loglevel, help="The log level to use."
@@ -78,7 +79,7 @@ def init(
     prerequisites.migrate_to_reflex()
     prerequisites.migrate_to_reflex()
 
 
     # Set up the app directory, only if the config doesn't exist.
     # Set up the app directory, only if the config doesn't exist.
-    if not os.path.exists(constants.CONFIG_FILE):
+    if not os.path.exists(constants.Config.FILE):
         prerequisites.create_config(app_name)
         prerequisites.create_config(app_name)
         prerequisites.initialize_app_directory(app_name, template)
         prerequisites.initialize_app_directory(app_name, template)
         telemetry.send("init")
         telemetry.send("init")
@@ -193,7 +194,7 @@ def run(
 def deploy(
 def deploy(
     dry_run: bool = typer.Option(False, help="Whether to run a dry run."),
     dry_run: bool = typer.Option(False, help="Whether to run a dry run."),
     loglevel: constants.LogLevel = typer.Option(
     loglevel: constants.LogLevel = typer.Option(
-        console.LOG_LEVEL, help="The log level to use."
+        console._LOG_LEVEL, help="The log level to use."
     ),
     ),
 ):
 ):
     """Deploy the app to the Reflex hosting service."""
     """Deploy the app to the Reflex hosting service."""
@@ -223,10 +224,10 @@ def deploy(
     backend = response["backend_resources_url"]
     backend = response["backend_resources_url"]
 
 
     # Upload the frontend and backend.
     # Upload the frontend and backend.
-    with open(constants.FRONTEND_ZIP, "rb") as f:
+    with open(constants.ComponentName.FRONTEND.zip(), "rb") as f:
         httpx.put(frontend, data=f)  # type: ignore
         httpx.put(frontend, data=f)  # type: ignore
 
 
-    with open(constants.BACKEND_ZIP, "rb") as f:
+    with open(constants.ComponentName.BACKEND.zip(), "rb") as f:
         httpx.put(backend, data=f)  # type: ignore
         httpx.put(backend, data=f)  # type: ignore
 
 
 
 
@@ -242,7 +243,7 @@ def export(
         True, "--frontend-only", help="Export only frontend.", show_default=False
         True, "--frontend-only", help="Export only frontend.", show_default=False
     ),
     ),
     loglevel: constants.LogLevel = typer.Option(
     loglevel: constants.LogLevel = typer.Option(
-        console.LOG_LEVEL, help="The log level to use."
+        console._LOG_LEVEL, help="The log level to use."
     ),
     ),
 ):
 ):
     """Export the app to a zip file."""
     """Export the app to a zip file."""

+ 3 - 3
reflex/state.py

@@ -1102,7 +1102,7 @@ class StateProxy(wrapt.ObjectProxy):
             state_instance: The state instance to proxy.
             state_instance: The state instance to proxy.
         """
         """
         super().__init__(state_instance)
         super().__init__(state_instance)
-        self._self_app = getattr(prerequisites.get_app(), constants.APP_VAR)
+        self._self_app = getattr(prerequisites.get_app(), constants.CompileVars.APP)
         self._self_substate_path = state_instance.get_full_name().split(".")
         self._self_substate_path = state_instance.get_full_name().split(".")
         self._self_actx = None
         self._self_actx = None
         self._self_mutable = False
         self._self_mutable = False
@@ -1355,10 +1355,10 @@ class StateManagerRedis(StateManager):
     redis: Redis
     redis: Redis
 
 
     # The token expiration time (s).
     # The token expiration time (s).
-    token_expiration: int = constants.TOKEN_EXPIRATION
+    token_expiration: int = constants.Expiration.TOKEN
 
 
     # The maximum time to hold a lock (ms).
     # The maximum time to hold a lock (ms).
-    lock_expiration: int = constants.LOCK_EXPIRATION
+    lock_expiration: int = constants.Expiration.LOCK
 
 
     # The keyspace subscription string when redis is waiting for lock to be released
     # The keyspace subscription string when redis is waiting for lock to be released
     _redis_notify_keyspace_events: str = (
     _redis_notify_keyspace_events: str = (

+ 2 - 2
reflex/style.py

@@ -7,8 +7,8 @@ from reflex.event import EventChain
 from reflex.utils import format
 from reflex.utils import format
 from reflex.vars import BaseVar, Var
 from reflex.vars import BaseVar, Var
 
 
-color_mode = BaseVar(name=constants.COLOR_MODE, type_="str")
-toggle_color_mode = BaseVar(name=constants.TOGGLE_COLOR_MODE, type_=EventChain)
+color_mode = BaseVar(name=constants.ColorMode.NAME, type_="str")
+toggle_color_mode = BaseVar(name=constants.ColorMode.TOGGLE, type_=EventChain)
 
 
 
 
 def convert(style_dict):
 def convert(style_dict):

+ 3 - 3
reflex/testing.py

@@ -154,7 +154,7 @@ class AppHarness:
             with chdir(self.app_path):
             with chdir(self.app_path):
                 reflex.reflex.init(
                 reflex.reflex.init(
                     name=self.app_name,
                     name=self.app_name,
-                    template=reflex.constants.Template.DEFAULT,
+                    template=reflex.constants.Templates.Kind.DEFAULT,
                     loglevel=reflex.constants.LogLevel.INFO,
                     loglevel=reflex.constants.LogLevel.INFO,
                 )
                 )
                 self.app_module_path.write_text(source_code)
                 self.app_module_path.write_text(source_code)
@@ -211,7 +211,7 @@ class AppHarness:
         # Start the frontend.
         # Start the frontend.
         self.frontend_process = reflex.utils.processes.new_process(
         self.frontend_process = reflex.utils.processes.new_process(
             [reflex.utils.prerequisites.get_package_manager(), "run", "dev"],
             [reflex.utils.prerequisites.get_package_manager(), "run", "dev"],
-            cwd=self.app_path / reflex.constants.WEB_DIR,
+            cwd=self.app_path / reflex.constants.Dirs.WEB,
             env={"PORT": "0"},
             env={"PORT": "0"},
             **FRONTEND_POPEN_ARGS,
             **FRONTEND_POPEN_ARGS,
         )
         )
@@ -647,7 +647,7 @@ class AppHarnessProd(AppHarness):
     frontend_server: Optional[Subdir404TCPServer] = None
     frontend_server: Optional[Subdir404TCPServer] = None
 
 
     def _run_frontend(self):
     def _run_frontend(self):
-        web_root = self.app_path / reflex.constants.WEB_DIR / "_static"
+        web_root = self.app_path / reflex.constants.Dirs.WEB / "_static"
         error_page_map = {
         error_page_map = {
             404: web_root / "404.html",
             404: web_root / "404.html",
         }
         }

+ 16 - 19
reflex/utils/build.py

@@ -6,7 +6,6 @@ import json
 import os
 import os
 import subprocess
 import subprocess
 import zipfile
 import zipfile
-from enum import Enum
 from pathlib import Path
 from pathlib import Path
 
 
 from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
 from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
@@ -19,7 +18,7 @@ from reflex.utils import console, path_ops, prerequisites, processes
 def set_env_json():
 def set_env_json():
     """Write the upload url to a REFLEX_JSON."""
     """Write the upload url to a REFLEX_JSON."""
     path_ops.update_json_file(
     path_ops.update_json_file(
-        constants.ENV_JSON,
+        constants.Dirs.ENV_JSON,
         {endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
         {endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
     )
     )
 
 
@@ -52,17 +51,12 @@ def generate_sitemap_config(deploy_url: str):
         }
         }
     )
     )
 
 
-    with open(constants.SITEMAP_CONFIG_FILE, "w") as f:
+    with open(constants.Next.SITEMAP_CONFIG_FILE, "w") as f:
         f.write(templates.SITEMAP_CONFIG(config=config))
         f.write(templates.SITEMAP_CONFIG(config=config))
 
 
 
 
-class _ComponentName(Enum):
-    BACKEND = "Backend"
-    FRONTEND = "Frontend"
-
-
 def _zip(
 def _zip(
-    component_name: _ComponentName,
+    component_name: constants.ComponentName,
     target: str,
     target: str,
     root_dir: str,
     root_dir: str,
     dirs_to_exclude: set[str] | None = None,
     dirs_to_exclude: set[str] | None = None,
@@ -130,7 +124,7 @@ def export(
         deploy_url: The URL of the deployed app.
         deploy_url: The URL of the deployed app.
     """
     """
     # Remove the static folder.
     # Remove the static folder.
-    path_ops.rm(constants.WEB_STATIC_DIR)
+    path_ops.rm(constants.Dirs.WEB_STATIC)
 
 
     # The export command to run.
     # The export command to run.
     command = "export"
     command = "export"
@@ -155,25 +149,28 @@ def export(
         # Start the subprocess with the progress bar.
         # Start the subprocess with the progress bar.
         process = processes.new_process(
         process = processes.new_process(
             [prerequisites.get_package_manager(), "run", command],
             [prerequisites.get_package_manager(), "run", command],
-            cwd=constants.WEB_DIR,
+            cwd=constants.Dirs.WEB,
             shell=constants.IS_WINDOWS,
             shell=constants.IS_WINDOWS,
         )
         )
         processes.show_progress("Creating Production Build", process, checkpoints)
         processes.show_progress("Creating Production Build", process, checkpoints)
 
 
     # Zip up the app.
     # Zip up the app.
     if zip:
     if zip:
-        files_to_exclude = {constants.FRONTEND_ZIP, constants.BACKEND_ZIP}
+        files_to_exclude = {
+            constants.ComponentName.FRONTEND.zip(),
+            constants.ComponentName.BACKEND.zip(),
+        }
         if frontend:
         if frontend:
             _zip(
             _zip(
-                component_name=_ComponentName.FRONTEND,
-                target=constants.FRONTEND_ZIP,
+                component_name=constants.ComponentName.FRONTEND,
+                target=constants.ComponentName.FRONTEND.zip(),
                 root_dir=".web/_static",
                 root_dir=".web/_static",
                 files_to_exclude=files_to_exclude,
                 files_to_exclude=files_to_exclude,
             )
             )
         if backend:
         if backend:
             _zip(
             _zip(
-                component_name=_ComponentName.BACKEND,
-                target=constants.BACKEND_ZIP,
+                component_name=constants.ComponentName.BACKEND,
+                target=constants.ComponentName.BACKEND.zip(),
                 root_dir=".",
                 root_dir=".",
                 dirs_to_exclude={"assets", "__pycache__"},
                 dirs_to_exclude={"assets", "__pycache__"},
                 files_to_exclude=files_to_exclude,
                 files_to_exclude=files_to_exclude,
@@ -192,8 +189,8 @@ def setup_frontend(
     """
     """
     # Copy asset files to public folder.
     # Copy asset files to public folder.
     path_ops.cp(
     path_ops.cp(
-        src=str(root / constants.APP_ASSETS_DIR),
-        dest=str(root / constants.WEB_ASSETS_DIR),
+        src=str(root / constants.Dirs.APP_ASSETS),
+        dest=str(root / constants.Dirs.WEB_ASSETS),
     )
     )
 
 
     # Set the environment variables in client (env.json).
     # Set the environment variables in client (env.json).
@@ -209,7 +206,7 @@ def setup_frontend(
                 "telemetry",
                 "telemetry",
                 "disable",
                 "disable",
             ],
             ],
-            cwd=constants.WEB_DIR,
+            cwd=constants.Dirs.WEB,
             stdout=subprocess.DEVNULL,
             stdout=subprocess.DEVNULL,
             shell=constants.IS_WINDOWS,
             shell=constants.IS_WINDOWS,
         )
         )

+ 10 - 10
reflex/utils/console.py

@@ -14,7 +14,7 @@ from reflex.constants import LogLevel
 _console = Console()
 _console = Console()
 
 
 # The current log level.
 # The current log level.
-LOG_LEVEL = LogLevel.INFO
+_LOG_LEVEL = LogLevel.INFO
 
 
 
 
 def set_log_level(log_level: LogLevel):
 def set_log_level(log_level: LogLevel):
@@ -23,8 +23,8 @@ def set_log_level(log_level: LogLevel):
     Args:
     Args:
         log_level: The log level to set.
         log_level: The log level to set.
     """
     """
-    global LOG_LEVEL
-    LOG_LEVEL = log_level
+    global _LOG_LEVEL
+    _LOG_LEVEL = log_level
 
 
 
 
 def print(msg: str, **kwargs):
 def print(msg: str, **kwargs):
@@ -44,7 +44,7 @@ def debug(msg: str, **kwargs):
         msg: The debug message.
         msg: The debug message.
         kwargs: Keyword arguments to pass to the print function.
         kwargs: Keyword arguments to pass to the print function.
     """
     """
-    if LOG_LEVEL <= LogLevel.DEBUG:
+    if _LOG_LEVEL <= LogLevel.DEBUG:
         print(f"[blue]Debug: {msg}[/blue]", **kwargs)
         print(f"[blue]Debug: {msg}[/blue]", **kwargs)
 
 
 
 
@@ -55,7 +55,7 @@ def info(msg: str, **kwargs):
         msg: The info message.
         msg: The info message.
         kwargs: Keyword arguments to pass to the print function.
         kwargs: Keyword arguments to pass to the print function.
     """
     """
-    if LOG_LEVEL <= LogLevel.INFO:
+    if _LOG_LEVEL <= LogLevel.INFO:
         print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
         print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
 
 
 
 
@@ -66,7 +66,7 @@ def success(msg: str, **kwargs):
         msg: The success message.
         msg: The success message.
         kwargs: Keyword arguments to pass to the print function.
         kwargs: Keyword arguments to pass to the print function.
     """
     """
-    if LOG_LEVEL <= LogLevel.INFO:
+    if _LOG_LEVEL <= LogLevel.INFO:
         print(f"[green]Success: {msg}[/green]", **kwargs)
         print(f"[green]Success: {msg}[/green]", **kwargs)
 
 
 
 
@@ -77,7 +77,7 @@ def log(msg: str, **kwargs):
         msg: The message to log.
         msg: The message to log.
         kwargs: Keyword arguments to pass to the print function.
         kwargs: Keyword arguments to pass to the print function.
     """
     """
-    if LOG_LEVEL <= LogLevel.INFO:
+    if _LOG_LEVEL <= LogLevel.INFO:
         _console.log(msg, **kwargs)
         _console.log(msg, **kwargs)
 
 
 
 
@@ -98,7 +98,7 @@ def warn(msg: str, **kwargs):
         msg: The warning message.
         msg: The warning message.
         kwargs: Keyword arguments to pass to the print function.
         kwargs: Keyword arguments to pass to the print function.
     """
     """
-    if LOG_LEVEL <= LogLevel.WARNING:
+    if _LOG_LEVEL <= LogLevel.WARNING:
         print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
         print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
 
 
 
 
@@ -123,7 +123,7 @@ def deprecate(
         f"{feature_name} has been deprecated in version {deprecation_version} {reason}. It will be completely "
         f"{feature_name} has been deprecated in version {deprecation_version} {reason}. It will be completely "
         f"removed in {removal_version}"
         f"removed in {removal_version}"
     )
     )
-    if LOG_LEVEL <= LogLevel.WARNING:
+    if _LOG_LEVEL <= LogLevel.WARNING:
         print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
         print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
 
 
 
 
@@ -134,7 +134,7 @@ def error(msg: str, **kwargs):
         msg: The error message.
         msg: The error message.
         kwargs: Keyword arguments to pass to the print function.
         kwargs: Keyword arguments to pass to the print function.
     """
     """
-    if LOG_LEVEL <= LogLevel.ERROR:
+    if _LOG_LEVEL <= LogLevel.ERROR:
         print(f"[red]{msg}[/red]", **kwargs)
         print(f"[red]{msg}[/red]", **kwargs)
 
 
 
 

+ 11 - 11
reflex/utils/exec.py

@@ -73,7 +73,7 @@ def run_process_and_launch_url(run_command: list[str]):
     Args:
     Args:
         run_command: The command to run.
         run_command: The command to run.
     """
     """
-    json_file_path = os.path.join(constants.WEB_DIR, "package.json")
+    json_file_path = os.path.join(constants.Dirs.WEB, "package.json")
     last_hash = detect_package_change(json_file_path)
     last_hash = detect_package_change(json_file_path)
     process = None
     process = None
     first_run = True
     first_run = True
@@ -81,7 +81,7 @@ def run_process_and_launch_url(run_command: list[str]):
     while True:
     while True:
         if process is None:
         if process is None:
             process = processes.new_process(
             process = processes.new_process(
-                run_command, cwd=constants.WEB_DIR, shell=constants.IS_WINDOWS
+                run_command, cwd=constants.Dirs.WEB, shell=constants.IS_WINDOWS
             )
             )
         if process.stdout:
         if process.stdout:
             for line in processes.stream_logs("Starting frontend", process):
             for line in processes.stream_logs("Starting frontend", process):
@@ -153,9 +153,9 @@ def run_backend(
         loglevel: The log level.
         loglevel: The log level.
     """
     """
     config = get_config()
     config = get_config()
-    app_module = f"{config.app_name}.{config.app_name}:{constants.APP_VAR}"
+    app_module = f"{config.app_name}.{config.app_name}:{constants.CompileVars.APP}"
     uvicorn.run(
     uvicorn.run(
-        app=f"{app_module}.{constants.API_VAR}",
+        app=f"{app_module}.{constants.CompileVars.API}",
         host=host,
         host=host,
         port=port,
         port=port,
         log_level=loglevel.value,
         log_level=loglevel.value,
@@ -180,7 +180,7 @@ def run_backend_prod(
     config = get_config()
     config = get_config()
     RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {config.timeout} --log-level critical".split()
     RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {config.timeout} --log-level critical".split()
     RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
     RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
-    app_module = f"{config.app_name}.{config.app_name}:{constants.APP_VAR}"
+    app_module = f"{config.app_name}.{config.app_name}:{constants.CompileVars.APP}"
     command = (
     command = (
         [
         [
             *RUN_BACKEND_PROD_WINDOWS,
             *RUN_BACKEND_PROD_WINDOWS,
@@ -217,7 +217,7 @@ def run_backend_prod(
 
 
 def output_system_info():
 def output_system_info():
     """Show system information if the loglevel is in DEBUG."""
     """Show system information if the loglevel is in DEBUG."""
-    if console.LOG_LEVEL > constants.LogLevel.DEBUG:
+    if console._LOG_LEVEL > constants.LogLevel.DEBUG:
         return
         return
 
 
     config = get_config()
     config = get_config()
@@ -231,8 +231,8 @@ def output_system_info():
     console.debug(f"Config: {config}")
     console.debug(f"Config: {config}")
 
 
     dependencies = [
     dependencies = [
-        f"[Reflex {constants.VERSION} with Python {platform.python_version()} (PATH: {sys.executable})]",
-        f"[Node {prerequisites.get_node_version()} (Expected: {constants.NODE_VERSION}) (PATH:{path_ops.get_node_path()})]",
+        f"[Reflex {constants.Reflex.VERSION} with Python {platform.python_version()} (PATH: {sys.executable})]",
+        f"[Node {prerequisites.get_node_version()} (Expected: {constants.Node.VERSION}) (PATH:{path_ops.get_node_path()})]",
     ]
     ]
 
 
     system = platform.system()
     system = platform.system()
@@ -240,13 +240,13 @@ def output_system_info():
     if system != "Windows":
     if system != "Windows":
         dependencies.extend(
         dependencies.extend(
             [
             [
-                f"[FNM {constants.FNM_VERSION} (Expected: {constants.FNM_VERSION}) (PATH: {constants.FNM_EXE})]",
-                f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.BUN_VERSION}) (PATH: {config.bun_path})]",
+                f"[FNM {constants.Fnm.VERSION} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]",
+                f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.Bun.VERSION}) (PATH: {config.bun_path})]",
             ],
             ],
         )
         )
     else:
     else:
         dependencies.append(
         dependencies.append(
-            f"[FNM {constants.FNM_VERSION} (Expected: {constants.FNM_VERSION}) (PATH: {constants.FNM_EXE})]",
+            f"[FNM {constants.Fnm.VERSION} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]",
         )
         )
 
 
     if system == "Linux":
     if system == "Linux":

+ 18 - 3
reflex/utils/format.py

@@ -5,7 +5,6 @@ from __future__ import annotations
 import inspect
 import inspect
 import json
 import json
 import os
 import os
-import os.path as op
 import re
 import re
 import sys
 import sys
 from typing import TYPE_CHECKING, Any, Union
 from typing import TYPE_CHECKING, Any, Union
@@ -230,7 +229,7 @@ def format_route(route: str, format_case=True) -> str:
 
 
     # If the route is empty, return the index route.
     # If the route is empty, return the index route.
     if route == "":
     if route == "":
-        return constants.INDEX_ROUTE
+        return constants.PageNames.INDEX_ROUTE
 
 
     return route
     return route
 
 
@@ -559,11 +558,27 @@ def format_breadcrumbs(route: str) -> list[tuple[str, str]]:
 
 
     # create and return breadcrumbs
     # create and return breadcrumbs
     return [
     return [
-        (part, op.join("/", *route_parts[: i + 1]))
+        (part, "/".join(["", *route_parts[: i + 1]]))
         for i, part in enumerate(route_parts)
         for i, part in enumerate(route_parts)
     ]
     ]
 
 
 
 
+def format_library_name(library_fullname: str):
+    """Format the name of a library.
+
+    Args:
+        library_fullname: The fullname of the library.
+
+    Returns:
+        The name without the @version if it was part of the name
+    """
+    lib, at, version = library_fullname.rpartition("@")
+    if not lib:
+        lib = at + version
+
+    return lib
+
+
 def json_dumps(obj: Any) -> str:
 def json_dumps(obj: Any) -> str:
     """Takes an object and returns a jsonified string.
     """Takes an object and returns a jsonified string.
 
 

+ 6 - 6
reflex/utils/path_ops.py

@@ -118,10 +118,10 @@ def get_node_bin_path() -> str | None:
     Returns:
     Returns:
         The path to the node bin folder.
         The path to the node bin folder.
     """
     """
-    if not os.path.exists(constants.NODE_BIN_PATH):
+    if not os.path.exists(constants.Node.BIN_PATH):
         str_path = which("node")
         str_path = which("node")
         return str(Path(str_path).parent) if str_path else str_path
         return str(Path(str_path).parent) if str_path else str_path
-    return constants.NODE_BIN_PATH
+    return constants.Node.BIN_PATH
 
 
 
 
 def get_node_path() -> str | None:
 def get_node_path() -> str | None:
@@ -130,9 +130,9 @@ def get_node_path() -> str | None:
     Returns:
     Returns:
         The path to the node binary file.
         The path to the node binary file.
     """
     """
-    if not os.path.exists(constants.NODE_PATH):
+    if not os.path.exists(constants.Node.PATH):
         return which("node")
         return which("node")
-    return constants.NODE_PATH
+    return constants.Node.PATH
 
 
 
 
 def get_npm_path() -> str | None:
 def get_npm_path() -> str | None:
@@ -141,9 +141,9 @@ def get_npm_path() -> str | None:
     Returns:
     Returns:
         The path to the npm binary file.
         The path to the npm binary file.
     """
     """
-    if not os.path.exists(constants.NODE_PATH):
+    if not os.path.exists(constants.Node.PATH):
         return which("npm")
         return which("npm")
-    return constants.NPM_PATH
+    return constants.Node.NPM_PATH
 
 
 
 
 def update_json_file(file_path: str, update_dict: dict[str, int | str]):
 def update_json_file(file_path: str, update_dict: dict[str, int | str]):

+ 65 - 63
reflex/utils/prerequisites.py

@@ -39,9 +39,9 @@ def check_node_version() -> bool:
     if current_version:
     if current_version:
         # Compare the version numbers
         # Compare the version numbers
         return (
         return (
-            current_version >= version.parse(constants.NODE_VERSION_MIN)
+            current_version >= version.parse(constants.Node.MIN_VERSION)
             if constants.IS_WINDOWS
             if constants.IS_WINDOWS
-            else current_version == version.parse(constants.NODE_VERSION)
+            else current_version == version.parse(constants.Node.VERSION)
         )
         )
     return False
     return False
 
 
@@ -111,7 +111,7 @@ def get_app(reload: bool = False) -> ModuleType:
     config = get_config()
     config = get_config()
     module = ".".join([config.app_name, config.app_name])
     module = ".".join([config.app_name, config.app_name])
     sys.path.insert(0, os.getcwd())
     sys.path.insert(0, os.getcwd())
-    app = __import__(module, fromlist=(constants.APP_VAR,))
+    app = __import__(module, fromlist=(constants.CompileVars.APP,))
     if reload:
     if reload:
         importlib.reload(app)
         importlib.reload(app)
     return app
     return app
@@ -160,9 +160,9 @@ def get_default_app_name() -> str:
     app_name = os.getcwd().split(os.path.sep)[-1].replace("-", "_")
     app_name = os.getcwd().split(os.path.sep)[-1].replace("-", "_")
 
 
     # Make sure the app is not named "reflex".
     # Make sure the app is not named "reflex".
-    if app_name == constants.MODULE_NAME:
+    if app_name == constants.Reflex.MODULE_NAME:
         console.error(
         console.error(
-            f"The app directory cannot be named [bold]{constants.MODULE_NAME}[/bold]."
+            f"The app directory cannot be named [bold]{constants.Reflex.MODULE_NAME}[/bold]."
         )
         )
         raise typer.Exit(1)
         raise typer.Exit(1)
 
 
@@ -179,28 +179,28 @@ def create_config(app_name: str):
     from reflex.compiler import templates
     from reflex.compiler import templates
 
 
     config_name = f"{re.sub(r'[^a-zA-Z]', '', app_name).capitalize()}Config"
     config_name = f"{re.sub(r'[^a-zA-Z]', '', app_name).capitalize()}Config"
-    with open(constants.CONFIG_FILE, "w") as f:
-        console.debug(f"Creating {constants.CONFIG_FILE}")
+    with open(constants.Config.FILE, "w") as f:
+        console.debug(f"Creating {constants.Config.FILE}")
         f.write(templates.RXCONFIG.render(app_name=app_name, config_name=config_name))
         f.write(templates.RXCONFIG.render(app_name=app_name, config_name=config_name))
 
 
 
 
 def initialize_gitignore():
 def initialize_gitignore():
     """Initialize the template .gitignore file."""
     """Initialize the template .gitignore file."""
     # The files to add to the .gitignore file.
     # The files to add to the .gitignore file.
-    files = constants.DEFAULT_GITIGNORE
+    files = constants.GitIgnore.DEFAULTS
 
 
     # Subtract current ignored files.
     # Subtract current ignored files.
-    if os.path.exists(constants.GITIGNORE_FILE):
-        with open(constants.GITIGNORE_FILE, "r") as f:
+    if os.path.exists(constants.GitIgnore.FILE):
+        with open(constants.GitIgnore.FILE, "r") as f:
             files |= set([line.strip() for line in f.readlines()])
             files |= set([line.strip() for line in f.readlines()])
 
 
     # Write files to the .gitignore file.
     # Write files to the .gitignore file.
-    with open(constants.GITIGNORE_FILE, "w") as f:
-        console.debug(f"Creating {constants.GITIGNORE_FILE}")
+    with open(constants.GitIgnore.FILE, "w") as f:
+        console.debug(f"Creating {constants.GitIgnore.FILE}")
         f.write(f"{(path_ops.join(sorted(files))).lstrip()}")
         f.write(f"{(path_ops.join(sorted(files))).lstrip()}")
 
 
 
 
-def initialize_app_directory(app_name: str, template: constants.Template):
+def initialize_app_directory(app_name: str, template: constants.Templates.Kind):
     """Initialize the app directory on reflex init.
     """Initialize the app directory on reflex init.
 
 
     Args:
     Args:
@@ -208,26 +208,28 @@ def initialize_app_directory(app_name: str, template: constants.Template):
         template: The template to use.
         template: The template to use.
     """
     """
     console.log("Initializing the app directory.")
     console.log("Initializing the app directory.")
-    path_ops.cp(os.path.join(constants.TEMPLATE_DIR, "apps", template.value), app_name)
+    path_ops.cp(
+        os.path.join(constants.Templates.Dirs.BASE, "apps", template.value), app_name
+    )
     path_ops.mv(
     path_ops.mv(
         os.path.join(app_name, template.value + ".py"),
         os.path.join(app_name, template.value + ".py"),
-        os.path.join(app_name, app_name + constants.PY_EXT),
+        os.path.join(app_name, app_name + constants.Ext.PY),
     )
     )
-    path_ops.cp(constants.ASSETS_TEMPLATE_DIR, constants.APP_ASSETS_DIR)
+    path_ops.cp(constants.Templates.Dirs.ASSETS_TEMPLATE, constants.Dirs.APP_ASSETS)
 
 
 
 
 def initialize_web_directory():
 def initialize_web_directory():
     """Initialize the web directory on reflex init."""
     """Initialize the web directory on reflex init."""
     console.log("Initializing the web directory.")
     console.log("Initializing the web directory.")
 
 
-    path_ops.cp(constants.WEB_TEMPLATE_DIR, constants.WEB_DIR)
+    path_ops.cp(constants.Templates.Dirs.WEB_TEMPLATE, constants.Dirs.WEB)
 
 
     initialize_package_json()
     initialize_package_json()
 
 
-    path_ops.mkdir(constants.WEB_ASSETS_DIR)
+    path_ops.mkdir(constants.Dirs.WEB_ASSETS)
 
 
     # update nextJS config based on rxConfig
     # update nextJS config based on rxConfig
-    next_config_file = os.path.join(constants.WEB_DIR, constants.NEXT_CONFIG_FILE)
+    next_config_file = os.path.join(constants.Dirs.WEB, constants.Next.CONFIG_FILE)
 
 
     with open(next_config_file, "r") as file:
     with open(next_config_file, "r") as file:
         next_config = file.read()
         next_config = file.read()
@@ -243,19 +245,19 @@ def initialize_web_directory():
 def _compile_package_json():
 def _compile_package_json():
     return templates.PACKAGE_JSON.render(
     return templates.PACKAGE_JSON.render(
         scripts={
         scripts={
-            "dev": constants.PackageJsonCommands.DEV,
-            "export": constants.PackageJsonCommands.EXPORT,
-            "export_sitemap": constants.PackageJsonCommands.EXPORT_SITEMAP,
-            "prod": constants.PackageJsonCommands.PROD,
+            "dev": constants.PackageJson.Commands.DEV,
+            "export": constants.PackageJson.Commands.EXPORT,
+            "export_sitemap": constants.PackageJson.Commands.EXPORT_SITEMAP,
+            "prod": constants.PackageJson.Commands.PROD,
         },
         },
-        dependencies=constants.PACKAGE_DEPENDENCIES,
-        dev_dependencies=constants.PACKAGE_DEV_DEPENDENCIES,
+        dependencies=constants.PackageJson.DEPENDENCIES,
+        dev_dependencies=constants.PackageJson.DEV_DEPENDENCIES,
     )
     )
 
 
 
 
 def initialize_package_json():
 def initialize_package_json():
     """Render and write in .web the package.json file."""
     """Render and write in .web the package.json file."""
-    output_path = constants.PACKAGE_JSON_PATH
+    output_path = constants.PackageJson.PATH
     code = _compile_package_json()
     code = _compile_package_json()
     with open(output_path, "w") as file:
     with open(output_path, "w") as file:
         file.write(code)
         file.write(code)
@@ -269,10 +271,10 @@ def init_reflex_json():
 
 
     # Write the hash and version to the reflex json file.
     # Write the hash and version to the reflex json file.
     reflex_json = {
     reflex_json = {
-        "version": constants.VERSION,
+        "version": constants.Reflex.VERSION,
         "project_hash": project_hash,
         "project_hash": project_hash,
     }
     }
-    path_ops.update_json_file(constants.REFLEX_JSON, reflex_json)
+    path_ops.update_json_file(constants.Reflex.JSON, reflex_json)
 
 
 
 
 def update_next_config(next_config: str, config: Config) -> str:
 def update_next_config(next_config: str, config: Config) -> str:
@@ -302,7 +304,7 @@ def remove_existing_bun_installation():
     """Remove existing bun installation."""
     """Remove existing bun installation."""
     console.debug("Removing existing bun installation.")
     console.debug("Removing existing bun installation.")
     if os.path.exists(get_config().bun_path):
     if os.path.exists(get_config().bun_path):
-        path_ops.rm(constants.BUN_ROOT_PATH)
+        path_ops.rm(constants.Bun.ROOT_PATH)
 
 
 
 
 def download_and_run(url: str, *args, show_status: bool = False, **env):
 def download_and_run(url: str, *args, show_status: bool = False, **env):
@@ -339,9 +341,9 @@ def download_and_extract_fnm_zip():
         Exit: If an error occurs while downloading or extracting the FNM zip.
         Exit: If an error occurs while downloading or extracting the FNM zip.
     """
     """
     # Download the zip file
     # Download the zip file
-    url = constants.FNM_INSTALL_URL
+    url = constants.Fnm.INSTALL_URL
     console.debug(f"Downloading {url}")
     console.debug(f"Downloading {url}")
-    fnm_zip_file = os.path.join(constants.FNM_DIR, f"{constants.FNM_FILENAME}.zip")
+    fnm_zip_file = os.path.join(constants.Fnm.DIR, f"{constants.Fnm.FILENAME}.zip")
     # Function to download and extract the FNM zip release.
     # Function to download and extract the FNM zip release.
     try:
     try:
         # Download the FNM zip release.
         # Download the FNM zip release.
@@ -354,7 +356,7 @@ def download_and_extract_fnm_zip():
 
 
         # Extract the downloaded zip file.
         # Extract the downloaded zip file.
         with zipfile.ZipFile(fnm_zip_file, "r") as zip_ref:
         with zipfile.ZipFile(fnm_zip_file, "r") as zip_ref:
-            zip_ref.extractall(constants.FNM_DIR)
+            zip_ref.extractall(constants.Fnm.DIR)
 
 
         console.debug("FNM package downloaded and extracted successfully.")
         console.debug("FNM package downloaded and extracted successfully.")
     except Exception as e:
     except Exception as e:
@@ -369,13 +371,13 @@ def install_node():
     """Install fnm and nodejs for use by Reflex.
     """Install fnm and nodejs for use by Reflex.
     Independent of any existing system installations.
     Independent of any existing system installations.
     """
     """
-    if not constants.FNM_FILENAME:
+    if not constants.Fnm.FILENAME:
         # fnm only support Linux, macOS and Windows distros.
         # fnm only support Linux, macOS and Windows distros.
         console.debug("")
         console.debug("")
         return
         return
 
 
-    path_ops.mkdir(constants.FNM_DIR)
-    if not os.path.exists(constants.FNM_EXE):
+    path_ops.mkdir(constants.Fnm.DIR)
+    if not os.path.exists(constants.Fnm.EXE):
         download_and_extract_fnm_zip()
         download_and_extract_fnm_zip()
 
 
     if constants.IS_WINDOWS:
     if constants.IS_WINDOWS:
@@ -384,13 +386,13 @@ def install_node():
             [
             [
                 "powershell",
                 "powershell",
                 "-Command",
                 "-Command",
-                f'& "{constants.FNM_EXE}" install {constants.NODE_VERSION} --fnm-dir "{constants.FNM_DIR}"',
+                f'& "{constants.Fnm.EXE}" install {constants.Node.VERSION} --fnm-dir "{constants.Fnm.DIR}"',
             ],
             ],
         )
         )
     else:  # All other platforms (Linux, MacOS).
     else:  # All other platforms (Linux, MacOS).
         # TODO we can skip installation if check_node_version() checks out
         # TODO we can skip installation if check_node_version() checks out
         # Add execute permissions to fnm executable.
         # Add execute permissions to fnm executable.
-        os.chmod(constants.FNM_EXE, stat.S_IXUSR)
+        os.chmod(constants.Fnm.EXE, stat.S_IXUSR)
         # Install node.
         # Install node.
         # Specify arm64 arch explicitly for M1s and M2s.
         # Specify arm64 arch explicitly for M1s and M2s.
         architecture_arg = (
         architecture_arg = (
@@ -401,12 +403,12 @@ def install_node():
 
 
         process = processes.new_process(
         process = processes.new_process(
             [
             [
-                constants.FNM_EXE,
+                constants.Fnm.EXE,
                 "install",
                 "install",
                 *architecture_arg,
                 *architecture_arg,
-                constants.NODE_VERSION,
+                constants.Node.VERSION,
                 "--fnm-dir",
                 "--fnm-dir",
-                constants.FNM_DIR,
+                constants.Fnm.DIR,
             ],
             ],
         )
         )
     processes.show_status("Installing node", process)
     processes.show_status("Installing node", process)
@@ -435,9 +437,9 @@ def install_bun():
 
 
     # Run the bun install script.
     # Run the bun install script.
     download_and_run(
     download_and_run(
-        constants.BUN_INSTALL_URL,
-        f"bun-v{constants.BUN_VERSION}",
-        BUN_INSTALL=constants.BUN_ROOT_PATH,
+        constants.Bun.INSTALL_URL,
+        f"bun-v{constants.Bun.VERSION}",
+        BUN_INSTALL=constants.Bun.ROOT_PATH,
     )
     )
 
 
 
 
@@ -453,7 +455,7 @@ def install_frontend_packages(packages: set[str]):
     # Install the base packages.
     # Install the base packages.
     process = processes.new_process(
     process = processes.new_process(
         [get_install_package_manager(), "install", "--loglevel", "silly"],
         [get_install_package_manager(), "install", "--loglevel", "silly"],
-        cwd=constants.WEB_DIR,
+        cwd=constants.Dirs.WEB,
         shell=constants.IS_WINDOWS,
         shell=constants.IS_WINDOWS,
     )
     )
 
 
@@ -467,10 +469,10 @@ def install_frontend_packages(packages: set[str]):
                 get_install_package_manager(),
                 get_install_package_manager(),
                 "add",
                 "add",
                 "-d",
                 "-d",
-                constants.TAILWIND_VERSION,
+                constants.Tailwind.VERSION,
                 *((config.tailwind or {}).get("plugins", [])),
                 *((config.tailwind or {}).get("plugins", [])),
             ],
             ],
-            cwd=constants.WEB_DIR,
+            cwd=constants.Dirs.WEB,
             shell=constants.IS_WINDOWS,
             shell=constants.IS_WINDOWS,
         )
         )
         processes.show_status("Installing tailwind", process)
         processes.show_status("Installing tailwind", process)
@@ -479,7 +481,7 @@ def install_frontend_packages(packages: set[str]):
     if len(packages) > 0:
     if len(packages) > 0:
         process = processes.new_process(
         process = processes.new_process(
             [get_install_package_manager(), "add", *packages],
             [get_install_package_manager(), "add", *packages],
-            cwd=constants.WEB_DIR,
+            cwd=constants.Dirs.WEB,
             shell=constants.IS_WINDOWS,
             shell=constants.IS_WINDOWS,
         )
         )
         processes.show_status(
         processes.show_status(
@@ -496,14 +498,14 @@ def check_initialized(frontend: bool = True):
     Raises:
     Raises:
         Exit: If the app is not initialized.
         Exit: If the app is not initialized.
     """
     """
-    has_config = os.path.exists(constants.CONFIG_FILE)
-    has_reflex_dir = not frontend or os.path.exists(constants.REFLEX_DIR)
-    has_web_dir = not frontend or os.path.exists(constants.WEB_DIR)
+    has_config = os.path.exists(constants.Config.FILE)
+    has_reflex_dir = not frontend or os.path.exists(constants.Reflex.DIR)
+    has_web_dir = not frontend or os.path.exists(constants.Dirs.WEB)
 
 
     # Check if the app is initialized.
     # Check if the app is initialized.
     if not (has_config and has_reflex_dir and has_web_dir):
     if not (has_config and has_reflex_dir and has_web_dir):
         console.error(
         console.error(
-            f"The app is not initialized. Run [bold]{constants.MODULE_NAME} init[/bold] first."
+            f"The app is not initialized. Run [bold]{constants.Reflex.MODULE_NAME} init[/bold] first."
         )
         )
         raise typer.Exit(1)
         raise typer.Exit(1)
 
 
@@ -527,11 +529,11 @@ def is_latest_template() -> bool:
     Returns:
     Returns:
         Whether the app is using the latest template.
         Whether the app is using the latest template.
     """
     """
-    if not os.path.exists(constants.REFLEX_JSON):
+    if not os.path.exists(constants.Reflex.JSON):
         return False
         return False
-    with open(constants.REFLEX_JSON) as f:  # type: ignore
+    with open(constants.Reflex.JSON) as f:  # type: ignore
         app_version = json.load(f)["version"]
         app_version = json.load(f)["version"]
-    return app_version == constants.VERSION
+    return app_version == constants.Reflex.VERSION
 
 
 
 
 def validate_bun():
 def validate_bun():
@@ -543,16 +545,16 @@ def validate_bun():
     # if a custom bun path is provided, make sure its valid
     # if a custom bun path is provided, make sure its valid
     # This is specific to non-FHS OS
     # This is specific to non-FHS OS
     bun_path = get_config().bun_path
     bun_path = get_config().bun_path
-    if bun_path != constants.DEFAULT_BUN_PATH:
+    if bun_path != constants.Bun.DEFAULT_PATH:
         bun_version = get_bun_version()
         bun_version = get_bun_version()
         if not bun_version:
         if not bun_version:
             console.error(
             console.error(
                 "Failed to obtain bun version. Make sure the specified bun path in your config is correct."
                 "Failed to obtain bun version. Make sure the specified bun path in your config is correct."
             )
             )
             raise typer.Exit(1)
             raise typer.Exit(1)
-        elif bun_version < version.parse(constants.MIN_BUN_VERSION):
+        elif bun_version < version.parse(constants.Bun.MIN_VERSION):
             console.error(
             console.error(
-                f"Reflex requires bun version {constants.BUN_VERSION} or higher to run, but the detected version is "
+                f"Reflex requires bun version {constants.Bun.VERSION} or higher to run, but the detected version is "
                 f"{bun_version}. If you have specified a custom bun path in your config, make sure to provide one "
                 f"{bun_version}. If you have specified a custom bun path in your config, make sure to provide one "
                 f"that satisfies the minimum version requirement."
                 f"that satisfies the minimum version requirement."
             )
             )
@@ -582,7 +584,7 @@ def validate_frontend_dependencies(init=True):
         if not check_node_version():
         if not check_node_version():
             node_version = get_node_version()
             node_version = get_node_version()
             console.error(
             console.error(
-                f"Reflex requires node version {constants.NODE_VERSION_MIN} or higher to run, but the detected version is {node_version}",
+                f"Reflex requires node version {constants.Node.MIN_VERSION} or higher to run, but the detected version is {node_version}",
             )
             )
             raise typer.Exit(1)
             raise typer.Exit(1)
 
 
@@ -597,7 +599,7 @@ def validate_frontend_dependencies(init=True):
 def initialize_frontend_dependencies():
 def initialize_frontend_dependencies():
     """Initialize all the frontend dependencies."""
     """Initialize all the frontend dependencies."""
     # Create the reflex directory.
     # Create the reflex directory.
-    path_ops.mkdir(constants.REFLEX_DIR)
+    path_ops.mkdir(constants.Reflex.DIR)
     # validate dependencies before install
     # validate dependencies before install
     validate_frontend_dependencies()
     validate_frontend_dependencies()
     # Install the frontend dependencies.
     # Install the frontend dependencies.
@@ -644,7 +646,7 @@ def check_schema_up_to_date():
 def migrate_to_reflex():
 def migrate_to_reflex():
     """Migration from Pynecone to Reflex."""
     """Migration from Pynecone to Reflex."""
     # Check if the old config file exists.
     # Check if the old config file exists.
-    if not os.path.exists(constants.OLD_CONFIG_FILE):
+    if not os.path.exists(constants.Config.PREVIOUS_FILE):
         return
         return
 
 
     # Ask the user if they want to migrate.
     # Ask the user if they want to migrate.
@@ -657,16 +659,16 @@ def migrate_to_reflex():
 
 
     # Rename pcconfig to rxconfig.
     # Rename pcconfig to rxconfig.
     console.log(
     console.log(
-        f"[bold]Renaming {constants.OLD_CONFIG_FILE} to {constants.CONFIG_FILE}"
+        f"[bold]Renaming {constants.Config.PREVIOUS_FILE} to {constants.Config.FILE}"
     )
     )
-    os.rename(constants.OLD_CONFIG_FILE, constants.CONFIG_FILE)
+    os.rename(constants.Config.PREVIOUS_FILE, constants.Config.FILE)
 
 
     # Find all python files in the app directory.
     # Find all python files in the app directory.
     file_pattern = os.path.join(get_config().app_name, "**/*.py")
     file_pattern = os.path.join(get_config().app_name, "**/*.py")
     file_list = glob.glob(file_pattern, recursive=True)
     file_list = glob.glob(file_pattern, recursive=True)
 
 
     # Add the config file to the list of files to be migrated.
     # Add the config file to the list of files to be migrated.
-    file_list.append(constants.CONFIG_FILE)
+    file_list.append(constants.Config.FILE)
 
 
     # Migrate all files.
     # Migrate all files.
     updates = {
     updates = {

+ 1 - 1
reflex/utils/telemetry.py

@@ -39,7 +39,7 @@ def get_reflex_version() -> str:
     Returns:
     Returns:
         The Reflex version.
         The Reflex version.
     """
     """
-    return constants.VERSION
+    return constants.Reflex.VERSION
 
 
 
 
 def get_cpu_count() -> int:
 def get_cpu_count() -> int:

+ 2 - 2
reflex/utils/types.py

@@ -4,7 +4,6 @@ from __future__ import annotations
 
 
 import contextlib
 import contextlib
 import typing
 import typing
-from types import LambdaType
 from typing import Any, Callable, Type, Union, _GenericAlias  # type: ignore
 from typing import Any, Callable, Type, Union, _GenericAlias  # type: ignore
 
 
 from reflex.base import Base
 from reflex.base import Base
@@ -18,7 +17,8 @@ PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
 StateVar = Union[PrimitiveType, Base, None]
 StateVar = Union[PrimitiveType, Base, None]
 StateIterVar = Union[list, set, tuple]
 StateIterVar = Union[list, set, tuple]
 
 
-ArgsSpec = LambdaType
+# ArgsSpec = Callable[[Var], list[Var]]
+ArgsSpec = Callable
 
 
 
 
 def get_args(alias: _GenericAlias) -> tuple[Type, ...]:
 def get_args(alias: _GenericAlias) -> tuple[Type, ...]:

+ 3 - 3
reflex/utils/watch.py

@@ -8,7 +8,7 @@ import time
 from watchdog.events import FileSystemEvent, FileSystemEventHandler
 from watchdog.events import FileSystemEvent, FileSystemEventHandler
 from watchdog.observers import Observer
 from watchdog.observers import Observer
 
 
-from reflex.constants import APP_ASSETS_DIR, WEB_ASSETS_DIR
+from reflex.constants import Dirs
 
 
 
 
 class AssetFolderWatch:
 class AssetFolderWatch:
@@ -20,7 +20,7 @@ class AssetFolderWatch:
         Args:
         Args:
             root: root path of the public.
             root: root path of the public.
         """
         """
-        self.path = str(root / APP_ASSETS_DIR)
+        self.path = str(root / Dirs.APP_ASSETS)
         self.event_handler = AssetFolderHandler(root)
         self.event_handler = AssetFolderHandler(root)
 
 
     def start(self):
     def start(self):
@@ -90,5 +90,5 @@ class AssetFolderHandler(FileSystemEventHandler):
             The public file path.
             The public file path.
         """
         """
         return src_path.replace(
         return src_path.replace(
-            str(self.root / APP_ASSETS_DIR), str(self.root / WEB_ASSETS_DIR)
+            str(self.root / Dirs.APP_ASSETS), str(self.root / Dirs.WEB_ASSETS)
         )
         )

+ 2 - 3
reflex/vars.py

@@ -27,8 +27,7 @@ from pydantic.fields import ModelField
 
 
 from reflex import constants
 from reflex import constants
 from reflex.base import Base
 from reflex.base import Base
-from reflex.utils import console, format, types
-from reflex.utils.serializers import serialize
+from reflex.utils import console, format, serializers, types
 
 
 if TYPE_CHECKING:
 if TYPE_CHECKING:
     from reflex.state import State
     from reflex.state import State
@@ -124,7 +123,7 @@ class Var(ABC):
 
 
         # Try to serialize the value.
         # Try to serialize the value.
         type_ = type(value)
         type_ = type(value)
-        name = serialize(value)
+        name = serializers.serialize(value)
         if name is None:
         if name is None:
             raise TypeError(
             raise TypeError(
                 f"No JSON serializer found for var {value} of type {type_}."
                 f"No JSON serializer found for var {value} of type {type_}."

+ 2 - 2
tests/middleware/test_hydrate_middleware.py

@@ -3,7 +3,7 @@ from typing import Any, Dict
 import pytest
 import pytest
 
 
 from reflex.app import App
 from reflex.app import App
-from reflex.constants import IS_HYDRATED
+from reflex.constants import CompileVars
 from reflex.middleware.hydrate_middleware import HydrateMiddleware
 from reflex.middleware.hydrate_middleware import HydrateMiddleware
 from reflex.state import State, StateUpdate
 from reflex.state import State, StateUpdate
 
 
@@ -17,7 +17,7 @@ def exp_is_hydrated(state: State) -> Dict[str, Any]:
     Returns:
     Returns:
         dict similar to that returned by `State.get_delta` with IS_HYDRATED: True
         dict similar to that returned by `State.get_delta` with IS_HYDRATED: True
     """
     """
-    return {state.get_name(): {IS_HYDRATED: True}}
+    return {state.get_name(): {CompileVars.IS_HYDRATED: True}}
 
 
 
 
 class TestState(State):
 class TestState(State):

+ 1 - 1
tests/test_app.py

@@ -923,7 +923,7 @@ async def test_dynamic_route_var_route_change_completed_on_load(
                 state.get_name(): {
                 state.get_name(): {
                     arg_name: exp_val,
                     arg_name: exp_val,
                     f"comp_{arg_name}": exp_val,
                     f"comp_{arg_name}": exp_val,
-                    constants.IS_HYDRATED: False,
+                    constants.CompileVars.IS_HYDRATED: False,
                     "loaded": exp_index,
                     "loaded": exp_index,
                     "counter": exp_index,
                     "counter": exp_index,
                     # "side_effect_counter": exp_index,
                     # "side_effect_counter": exp_index,

+ 16 - 74
tests/test_state.py

@@ -15,7 +15,7 @@ from plotly.graph_objects import Figure
 
 
 import reflex as rx
 import reflex as rx
 from reflex.base import Base
 from reflex.base import Base
-from reflex.constants import APP_VAR, IS_HYDRATED, RouteVar, SocketEvent
+from reflex.constants import CompileVars, RouteVar, SocketEvent
 from reflex.event import Event, EventHandler
 from reflex.event import Event, EventHandler
 from reflex.state import (
 from reflex.state import (
     ImmutableStateError,
     ImmutableStateError,
@@ -28,7 +28,7 @@ from reflex.state import (
     StateProxy,
     StateProxy,
     StateUpdate,
     StateUpdate,
 )
 )
-from reflex.utils import format, prerequisites
+from reflex.utils import prerequisites
 from reflex.vars import BaseVar, ComputedVar
 from reflex.vars import BaseVar, ComputedVar
 
 
 from .states import GenState
 from .states import GenState
@@ -118,6 +118,15 @@ class GrandchildState(ChildState):
         pass
         pass
 
 
 
 
+class DateTimeState(State):
+    """A State with some datetime fields."""
+
+    d: datetime.date = datetime.date.fromisoformat("1989-11-09")
+    dt: datetime.datetime = datetime.datetime.fromisoformat("1989-11-09T18:53:00+01:00")
+    t: datetime.time = datetime.time.fromisoformat("18:53:00+01:00")
+    td: datetime.timedelta = datetime.timedelta(days=11, minutes=11)
+
+
 @pytest.fixture
 @pytest.fixture
 def test_state() -> TestState:
 def test_state() -> TestState:
     """A state.
     """A state.
@@ -214,7 +223,7 @@ def test_class_vars(test_state):
     """
     """
     cls = type(test_state)
     cls = type(test_state)
     assert set(cls.vars.keys()) == {
     assert set(cls.vars.keys()) == {
-        IS_HYDRATED,  # added by hydrate_middleware to all State
+        CompileVars.IS_HYDRATED,  # added by hydrate_middleware to all State
         "num1",
         "num1",
         "num2",
         "num2",
         "key",
         "key",
@@ -292,58 +301,6 @@ def test_dict(test_state):
     )
     )
 
 
 
 
-def test_format_state(test_state):
-    """Test that the format state is correct.
-
-    Args:
-        test_state: A state.
-    """
-    formatted_state = format.format_state(test_state.dict())
-    exp_formatted_state = {
-        "array": [1, 2, 3.14],
-        "child_state": {"count": 23, "grandchild_state": {"value2": ""}, "value": ""},
-        "child_state2": {"value": ""},
-        "complex": {
-            1: {"prop1": 42, "prop2": "hello"},
-            2: {"prop1": 42, "prop2": "hello"},
-        },
-        "dt": "1989-11-09 18:53:00+01:00",
-        "fig": [],
-        "is_hydrated": False,
-        "key": "",
-        "map_key": "a",
-        "mapping": {"a": [1, 2, 3], "b": [4, 5, 6]},
-        "num1": 0,
-        "num2": 3.14,
-        "obj": {"prop1": 42, "prop2": "hello"},
-        "sum": 3.14,
-        "upper": "",
-    }
-    assert formatted_state == exp_formatted_state
-
-
-def test_format_state_datetime():
-    """Test that the format state is correct for datetime classes."""
-
-    class DateTimeState(State):
-        d: datetime.date = datetime.date.fromisoformat("1989-11-09")
-        dt: datetime.datetime = datetime.datetime.fromisoformat(
-            "1989-11-09T18:53:00+01:00"
-        )
-        t: datetime.time = datetime.time.fromisoformat("18:53:00+01:00")
-        td: datetime.timedelta = datetime.timedelta(days=11, minutes=11)
-
-    formatted_state = format.format_state(DateTimeState().dict())
-    exp_formatted_state = {
-        "d": "1989-11-09",
-        "dt": "1989-11-09 18:53:00+01:00",
-        "is_hydrated": False,
-        "t": "18:53:00+01:00",
-        "td": "11 days, 0:11:00",
-    }
-    assert formatted_state == exp_formatted_state
-
-
 def test_default_setters(test_state):
 def test_default_setters(test_state):
     """Test that we can set default values.
     """Test that we can set default values.
 
 
@@ -750,21 +707,6 @@ async def test_process_event_generator():
     assert count == 6
     assert count == 6
 
 
 
 
-def test_format_event_handler():
-    """Test formatting an event handler."""
-    assert (
-        format.format_event_handler(TestState.do_something) == "test_state.do_something"  # type: ignore
-    )
-    assert (
-        format.format_event_handler(ChildState.change_both)  # type: ignore
-        == "test_state.child_state.change_both"
-    )
-    assert (
-        format.format_event_handler(GrandchildState.do_nothing)  # type: ignore
-        == "test_state.child_state.grandchild_state.do_nothing"
-    )
-
-
 def test_get_token(test_state, mocker, router_data):
 def test_get_token(test_state, mocker, router_data):
     """Test that the token obtained from the router_data is correct.
     """Test that the token obtained from the router_data is correct.
 
 
@@ -1184,17 +1126,17 @@ def test_computed_var_depends_on_parent_non_cached():
     assert ps.dict() == {
     assert ps.dict() == {
         cs.get_name(): {"dep_v": 2},
         cs.get_name(): {"dep_v": 2},
         "no_cache_v": 1,
         "no_cache_v": 1,
-        IS_HYDRATED: False,
+        CompileVars.IS_HYDRATED: False,
     }
     }
     assert ps.dict() == {
     assert ps.dict() == {
         cs.get_name(): {"dep_v": 4},
         cs.get_name(): {"dep_v": 4},
         "no_cache_v": 3,
         "no_cache_v": 3,
-        IS_HYDRATED: False,
+        CompileVars.IS_HYDRATED: False,
     }
     }
     assert ps.dict() == {
     assert ps.dict() == {
         cs.get_name(): {"dep_v": 6},
         cs.get_name(): {"dep_v": 6},
         "no_cache_v": 5,
         "no_cache_v": 5,
-        IS_HYDRATED: False,
+        CompileVars.IS_HYDRATED: False,
     }
     }
     assert counter == 6
     assert counter == 6
 
 
@@ -1595,7 +1537,7 @@ def mock_app(monkeypatch, app: rx.App, state_manager: StateManager) -> rx.App:
         The app, after mocking out prerequisites.get_app()
         The app, after mocking out prerequisites.get_app()
     """
     """
     app_module = Mock()
     app_module = Mock()
-    setattr(app_module, APP_VAR, app)
+    setattr(app_module, CompileVars.APP, app)
     app.state = TestState
     app.state = TestState
     app.state_manager = state_manager
     app.state_manager = state_manager
     assert app.event_namespace is not None
     assert app.event_namespace is not None

+ 578 - 0
tests/utils/test_format.py

@@ -0,0 +1,578 @@
+from typing import Any
+
+import pytest
+
+from reflex.components.tags.tag import Tag
+from reflex.event import EVENT_ARG, EventChain, EventHandler, EventSpec
+from reflex.style import Style
+from reflex.utils import format
+from reflex.vars import BaseVar, Var
+from tests.test_state import ChildState, DateTimeState, GrandchildState, TestState
+
+
+def mock_event(arg):
+    pass
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ("{", "}"),
+        ("(", ")"),
+        ("[", "]"),
+        ("<", ">"),
+        ('"', '"'),
+        ("'", "'"),
+    ],
+)
+def test_get_close_char(input: str, output: str):
+    """Test getting the close character for a given open character.
+
+    Args:
+        input: The open character.
+        output: The expected close character.
+    """
+    assert format.get_close_char(input) == output
+
+
+@pytest.mark.parametrize(
+    "text,open,expected",
+    [
+        ("", "{", False),
+        ("{wrap}", "{", True),
+        ("{wrap", "{", False),
+        ("{wrap}", "(", False),
+        ("(wrap)", "(", True),
+    ],
+)
+def test_is_wrapped(text: str, open: str, expected: bool):
+    """Test checking if a string is wrapped in the given open and close characters.
+
+    Args:
+        text: The text to check.
+        open: The open character.
+        expected: Whether the text is wrapped.
+    """
+    assert format.is_wrapped(text, open) == expected
+
+
+@pytest.mark.parametrize(
+    "text,open,check_first,num,expected",
+    [
+        ("", "{", True, 1, "{}"),
+        ("wrap", "{", True, 1, "{wrap}"),
+        ("wrap", "(", True, 1, "(wrap)"),
+        ("wrap", "(", True, 2, "((wrap))"),
+        ("(wrap)", "(", True, 1, "(wrap)"),
+        ("{wrap}", "{", True, 2, "{wrap}"),
+        ("(wrap)", "{", True, 1, "{(wrap)}"),
+        ("(wrap)", "(", False, 1, "((wrap))"),
+    ],
+)
+def test_wrap(text: str, open: str, expected: str, check_first: bool, num: int):
+    """Test wrapping a string.
+
+    Args:
+        text: The text to wrap.
+        open: The open character.
+        expected: The expected output string.
+        check_first: Whether to check if the text is already wrapped.
+        num: The number of times to wrap the text.
+    """
+    assert format.wrap(text, open, check_first=check_first, num=num) == expected
+
+
+@pytest.mark.parametrize(
+    "text,indent_level,expected",
+    [
+        ("", 2, ""),
+        ("hello", 2, "hello"),
+        ("hello\nworld", 2, "  hello\n  world\n"),
+        ("hello\nworld", 4, "    hello\n    world\n"),
+        ("  hello\n  world", 2, "    hello\n    world\n"),
+    ],
+)
+def test_indent(text: str, indent_level: int, expected: str, windows_platform: bool):
+    """Test indenting a string.
+
+    Args:
+        text: The text to indent.
+        indent_level: The number of spaces to indent by.
+        expected: The expected output string.
+        windows_platform: Whether the system is windows.
+    """
+    assert format.indent(text, indent_level) == (
+        expected.replace("\n", "\r\n") if windows_platform else expected
+    )
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ("", ""),
+        ("hello", "hello"),
+        ("Hello", "hello"),
+        ("camelCase", "camel_case"),
+        ("camelTwoHumps", "camel_two_humps"),
+        ("_start_with_underscore", "_start_with_underscore"),
+        ("__start_with_double_underscore", "__start_with_double_underscore"),
+    ],
+)
+def test_to_snake_case(input: str, output: str):
+    """Test converting strings to snake case.
+
+    Args:
+        input: The input string.
+        output: The expected output string.
+    """
+    assert format.to_snake_case(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ("", ""),
+        ("hello", "hello"),
+        ("Hello", "Hello"),
+        ("snake_case", "snakeCase"),
+        ("snake_case_two", "snakeCaseTwo"),
+    ],
+)
+def test_to_camel_case(input: str, output: str):
+    """Test converting strings to camel case.
+
+    Args:
+        input: The input string.
+        output: The expected output string.
+    """
+    assert format.to_camel_case(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ("", ""),
+        ("hello", "Hello"),
+        ("Hello", "Hello"),
+        ("snake_case", "SnakeCase"),
+        ("snake_case_two", "SnakeCaseTwo"),
+    ],
+)
+def test_to_title_case(input: str, output: str):
+    """Test converting strings to title case.
+
+    Args:
+        input: The input string.
+        output: The expected output string.
+    """
+    assert format.to_title_case(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ("", ""),
+        ("hello", "hello"),
+        ("Hello", "hello"),
+        ("snake_case", "snake-case"),
+        ("snake_case_two", "snake-case-two"),
+    ],
+)
+def test_to_kebab_case(input: str, output: str):
+    """Test converting strings to kebab case.
+
+    Args:
+        input: the input string.
+        output: the output string.
+    """
+    assert format.to_kebab_case(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ("", "{``}"),
+        ("hello", "{`hello`}"),
+        ("hello world", "{`hello world`}"),
+        ("hello=`world`", "{`hello=\\`world\\``}"),
+    ],
+)
+def test_format_string(input: str, output: str):
+    """Test formating the input as JS string literal.
+
+    Args:
+        input: the input string.
+        output: the output string.
+    """
+    assert format.format_string(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        (Var.create(value="test"), "{`test`}"),
+        (Var.create(value="test", is_local=True), "{`test`}"),
+        (Var.create(value="test", is_local=False), "{test}"),
+        (Var.create(value="test", is_string=True), "{`test`}"),
+        (Var.create(value="test", is_string=False), "{`test`}"),
+        (Var.create(value="test", is_local=False, is_string=False), "{test}"),
+    ],
+)
+def test_format_var(input: Var, output: str):
+    assert format.format_var(input) == output
+
+
+@pytest.mark.parametrize(
+    "route,format_case,expected",
+    [
+        ("", True, "index"),
+        ("/", True, "index"),
+        ("custom-route", True, "custom-route"),
+        ("custom-route", False, "custom-route"),
+        ("custom-route/", True, "custom-route"),
+        ("custom-route/", False, "custom-route"),
+        ("/custom-route", True, "custom-route"),
+        ("/custom-route", False, "custom-route"),
+        ("/custom_route", True, "custom-route"),
+        ("/custom_route", False, "custom_route"),
+        ("/CUSTOM_route", True, "custom-route"),
+        ("/CUSTOM_route", False, "CUSTOM_route"),
+    ],
+)
+def test_format_route(route: str, format_case: bool, expected: bool):
+    """Test formatting a route.
+
+    Args:
+        route: The route to format.
+        format_case: Whether to change casing to snake_case.
+        expected: The expected formatted route.
+    """
+    assert format.format_route(route, format_case=format_case) == expected
+
+
+@pytest.mark.parametrize(
+    "condition,true_value,false_value,expected",
+    [
+        ("cond", "<C1>", '""', '{isTrue(cond) ? <C1> : ""}'),
+        ("cond", "<C1>", "<C2>", "{isTrue(cond) ? <C1> : <C2>}"),
+    ],
+)
+def test_format_cond(condition: str, true_value: str, false_value: str, expected: str):
+    """Test formatting a cond.
+
+    Args:
+        condition: The condition to check.
+        true_value: The value to return if the condition is true.
+        false_value: The value to return if the condition is false.
+        expected: The expected output string.
+    """
+    assert format.format_cond(condition, true_value, false_value) == expected
+
+
+@pytest.mark.parametrize(
+    "prop,formatted",
+    [
+        ("string", '"string"'),
+        ("{wrapped_string}", "{wrapped_string}"),
+        (True, "{true}"),
+        (False, "{false}"),
+        (123, "{123}"),
+        (3.14, "{3.14}"),
+        ([1, 2, 3], "{[1, 2, 3]}"),
+        (["a", "b", "c"], '{["a", "b", "c"]}'),
+        ({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'),
+        ({"a": 'foo "bar" baz'}, r'{{"a": "foo \"bar\" baz"}}'),
+        (
+            {
+                "a": 'foo "{ "bar" }" baz',
+                "b": BaseVar(name="val", type_="str"),
+            },
+            r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
+        ),
+        (
+            EventChain(
+                events=[EventSpec(handler=EventHandler(fn=mock_event))], args_spec=None
+            ),
+            '{_e => addEvents([Event("mock_event", {})], _e)}',
+        ),
+        (
+            EventChain(
+                events=[
+                    EventSpec(
+                        handler=EventHandler(fn=mock_event),
+                        args=((Var.create_safe("arg"), EVENT_ARG.target.value),),
+                    )
+                ],
+                args_spec=None,
+            ),
+            '{_e => addEvents([Event("mock_event", {arg:_e.target.value})], _e)}',
+        ),
+        ({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'),
+        (BaseVar(name="var", type_="int"), "{var}"),
+        (
+            BaseVar(
+                name="_",
+                type_=Any,
+                state="",
+                is_local=True,
+                is_string=False,
+            ),
+            "{_}",
+        ),
+        (BaseVar(name='state.colors["a"]', type_="str"), '{state.colors["a"]}'),
+        ({"a": BaseVar(name="val", type_="str")}, '{{"a": val}}'),
+        ({"a": BaseVar(name='"val"', type_="str")}, '{{"a": "val"}}'),
+        (
+            {"a": BaseVar(name='state.colors["val"]', type_="str")},
+            '{{"a": state.colors["val"]}}',
+        ),
+        # tricky real-world case from markdown component
+        (
+            {
+                "h1": f"{{({{node, ...props}}) => <Heading {{...props}} {''.join(Tag(name='', props=Style({'as_': 'h1'})).format_props())} />}}"
+            },
+            '{{"h1": ({node, ...props}) => <Heading {...props} as={`h1`} />}}',
+        ),
+    ],
+)
+def test_format_prop(prop: Var, formatted: str):
+    """Test that the formatted value of an prop is correct.
+
+    Args:
+        prop: The prop to test.
+        formatted: The expected formatted value.
+    """
+    assert format.format_prop(prop) == formatted
+
+
+@pytest.mark.parametrize(
+    "single_props,key_value_props,output",
+    [
+        (["string"], {"key": 42}, ["key={42}", "string"]),
+    ],
+)
+def test_format_props(single_props, key_value_props, output):
+    """Test the result of formatting a set of props (both single and keyvalue).
+
+    Args:
+        single_props: the list of single props
+        key_value_props: the dict of key value props
+        output: the expected output
+    """
+    assert format.format_props(*single_props, **key_value_props) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        (EventHandler(fn=mock_event), ("", "mock_event")),
+    ],
+)
+def test_get_handler_parts(input, output):
+    assert format.get_event_handler_parts(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        (TestState.do_something, "test_state.do_something"),
+        (ChildState.change_both, "test_state.child_state.change_both"),
+        (
+            GrandchildState.do_nothing,
+            "test_state.child_state.grandchild_state.do_nothing",
+        ),
+    ],
+)
+def test_format_event_handler(input, output):
+    """Test formatting an event handler.
+
+    Args:
+        input: The event handler input.
+        output: The expected output.
+    """
+    assert format.format_event_handler(input) == output  # type: ignore
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        (EventSpec(handler=EventHandler(fn=mock_event)), 'Event("mock_event", {})'),
+    ],
+)
+def test_format_event(input, output):
+    assert format.format_event(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        (
+            EventChain(
+                events=[
+                    EventSpec(handler=EventHandler(fn=mock_event)),
+                    EventSpec(handler=EventHandler(fn=mock_event)),
+                ],
+                args_spec=None,
+            ),
+            'addEvents([Event("mock_event", {}),Event("mock_event", {})])',
+        ),
+        (
+            EventChain(
+                events=[
+                    EventSpec(handler=EventHandler(fn=mock_event)),
+                    EventSpec(handler=EventHandler(fn=mock_event)),
+                ],
+                args_spec=lambda e0: [e0],
+            ),
+            'addEvents([Event("mock_event", {}),Event("mock_event", {})])',
+        ),
+    ],
+)
+def test_format_event_chain(input, output):
+    assert format.format_event_chain(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ({"query": {"k1": 1, "k2": 2}}, {"k1": 1, "k2": 2}),
+        ({"query": {"k1": 1, "k-2": 2}}, {"k1": 1, "k_2": 2}),
+    ],
+)
+def test_format_query_params(input, output):
+    assert format.format_query_params(input) == output
+
+
+@pytest.mark.parametrize(
+    "input, output",
+    [
+        (
+            TestState().dict(),  # type: ignore
+            {
+                "array": [1, 2, 3.14],
+                "child_state": {
+                    "count": 23,
+                    "grandchild_state": {"value2": ""},
+                    "value": "",
+                },
+                "child_state2": {"value": ""},
+                "complex": {
+                    1: {"prop1": 42, "prop2": "hello"},
+                    2: {"prop1": 42, "prop2": "hello"},
+                },
+                "dt": "1989-11-09 18:53:00+01:00",
+                "fig": [],
+                "is_hydrated": False,
+                "key": "",
+                "map_key": "a",
+                "mapping": {"a": [1, 2, 3], "b": [4, 5, 6]},
+                "num1": 0,
+                "num2": 3.14,
+                "obj": {"prop1": 42, "prop2": "hello"},
+                "sum": 3.14,
+                "upper": "",
+            },
+        ),
+        (
+            DateTimeState().dict(),
+            {
+                "d": "1989-11-09",
+                "dt": "1989-11-09 18:53:00+01:00",
+                "is_hydrated": False,
+                "t": "18:53:00+01:00",
+                "td": "11 days, 0:11:00",
+            },
+        ),
+    ],
+)
+def test_format_state(input, output):
+    """Test that the format state is correct.
+
+    Args:
+        input: The state to format.
+        output: The expected formatted state.
+    """
+    assert format.format_state(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ("input1", "ref_input1"),
+        ("input 1", "ref_input_1"),
+        ("input-1", "ref_input_1"),
+        ("input_1", "ref_input_1"),
+        ("a long test?1! name", "ref_a_long_test_1_name"),
+    ],
+)
+def test_format_ref(input, output):
+    """Test formatting a ref.
+
+    Args:
+        input: The name to format.
+        output: The expected formatted name.
+    """
+    assert format.format_ref(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        (("my_array", None), "refs_my_array"),
+        (("my_array", Var.create(0)), "refs_my_array[0]"),
+        (("my_array", Var.create(1)), "refs_my_array[1]"),
+    ],
+)
+def test_format_array_ref(input, output):
+    assert format.format_array_ref(input[0], input[1]) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        ("/foo", [("foo", "/foo")]),
+        ("/foo/bar", [("foo", "/foo"), ("bar", "/foo/bar")]),
+        (
+            "/foo/bar/baz",
+            [("foo", "/foo"), ("bar", "/foo/bar"), ("baz", "/foo/bar/baz")],
+        ),
+    ],
+)
+def test_format_breadcrumbs(input, output):
+    assert format.format_breadcrumbs(input) == output
+
+
+@pytest.mark.parametrize(
+    "input, output",
+    [
+        ("library@^0.1.2", "library"),
+        ("library", "library"),
+        ("@library@^0.1.2", "@library"),
+        ("@library", "@library"),
+    ],
+)
+def test_format_library_name(input: str, output: str):
+    """Test formating a library name to remove the @version part.
+
+    Args:
+        input: the input string.
+        output: the output string.
+    """
+    assert format.format_library_name(input) == output
+
+
+@pytest.mark.parametrize(
+    "input,output",
+    [
+        (None, "null"),
+        (True, "true"),
+        (1, "1"),
+        (1.0, "1.0"),
+        ([], "[]"),
+        ([1, 2, 3], "[1, 2, 3]"),
+        ({}, "{}"),
+        ({"k1": False, "k2": True}, '{"k1": false, "k2": true}'),
+    ],
+)
+def test_json_dumps(input, output):
+    assert format.json_dumps(input) == output

+ 13 - 313
tests/utils/test_utils.py

@@ -9,20 +9,17 @@ from packaging import version
 
 
 from reflex import constants
 from reflex import constants
 from reflex.base import Base
 from reflex.base import Base
-from reflex.components.tags import Tag
-from reflex.event import EVENT_ARG, EventChain, EventHandler, EventSpec
+from reflex.event import EventHandler
 from reflex.state import State
 from reflex.state import State
-from reflex.style import Style
 from reflex.utils import (
 from reflex.utils import (
     build,
     build,
-    format,
     imports,
     imports,
     prerequisites,
     prerequisites,
     types,
     types,
 )
 )
 from reflex.utils import exec as utils_exec
 from reflex.utils import exec as utils_exec
 from reflex.utils.serializers import serialize
 from reflex.utils.serializers import serialize
-from reflex.vars import BaseVar, Var
+from reflex.vars import Var
 
 
 
 
 def mock_event(arg):
 def mock_event(arg):
@@ -36,7 +33,7 @@ def get_above_max_version():
         max bun version plus one.
         max bun version plus one.
 
 
     """
     """
-    semantic_version_list = constants.BUN_VERSION.split(".")
+    semantic_version_list = constants.Bun.VERSION.split(".")
     semantic_version_list[-1] = str(int(semantic_version_list[-1]) + 1)  # type: ignore
     semantic_version_list[-1] = str(int(semantic_version_list[-1]) + 1)  # type: ignore
     return ".".join(semantic_version_list)
     return ".".join(semantic_version_list)
 
 
@@ -59,179 +56,6 @@ def test_func():
     pass
     pass
 
 
 
 
-@pytest.mark.parametrize(
-    "input,output",
-    [
-        ("", ""),
-        ("hello", "hello"),
-        ("Hello", "hello"),
-        ("camelCase", "camel_case"),
-        ("camelTwoHumps", "camel_two_humps"),
-        ("_start_with_underscore", "_start_with_underscore"),
-        ("__start_with_double_underscore", "__start_with_double_underscore"),
-    ],
-)
-def test_to_snake_case(input: str, output: str):
-    """Test converting strings to snake case.
-
-    Args:
-        input: The input string.
-        output: The expected output string.
-    """
-    assert format.to_snake_case(input) == output
-
-
-@pytest.mark.parametrize(
-    "input,output",
-    [
-        ("", ""),
-        ("hello", "hello"),
-        ("Hello", "Hello"),
-        ("snake_case", "snakeCase"),
-        ("snake_case_two", "snakeCaseTwo"),
-    ],
-)
-def test_to_camel_case(input: str, output: str):
-    """Test converting strings to camel case.
-
-    Args:
-        input: The input string.
-        output: The expected output string.
-    """
-    assert format.to_camel_case(input) == output
-
-
-@pytest.mark.parametrize(
-    "input,output",
-    [
-        ("", ""),
-        ("hello", "Hello"),
-        ("Hello", "Hello"),
-        ("snake_case", "SnakeCase"),
-        ("snake_case_two", "SnakeCaseTwo"),
-    ],
-)
-def test_to_title_case(input: str, output: str):
-    """Test converting strings to title case.
-
-    Args:
-        input: The input string.
-        output: The expected output string.
-    """
-    assert format.to_title_case(input) == output
-
-
-@pytest.mark.parametrize(
-    "input,output",
-    [
-        ("{", "}"),
-        ("(", ")"),
-        ("[", "]"),
-        ("<", ">"),
-        ('"', '"'),
-        ("'", "'"),
-    ],
-)
-def test_get_close_char(input: str, output: str):
-    """Test getting the close character for a given open character.
-
-    Args:
-        input: The open character.
-        output: The expected close character.
-    """
-    assert format.get_close_char(input) == output
-
-
-@pytest.mark.parametrize(
-    "text,open,expected",
-    [
-        ("", "{", False),
-        ("{wrap}", "{", True),
-        ("{wrap", "{", False),
-        ("{wrap}", "(", False),
-        ("(wrap)", "(", True),
-    ],
-)
-def test_is_wrapped(text: str, open: str, expected: bool):
-    """Test checking if a string is wrapped in the given open and close characters.
-
-    Args:
-        text: The text to check.
-        open: The open character.
-        expected: Whether the text is wrapped.
-    """
-    assert format.is_wrapped(text, open) == expected
-
-
-@pytest.mark.parametrize(
-    "text,open,check_first,num,expected",
-    [
-        ("", "{", True, 1, "{}"),
-        ("wrap", "{", True, 1, "{wrap}"),
-        ("wrap", "(", True, 1, "(wrap)"),
-        ("wrap", "(", True, 2, "((wrap))"),
-        ("(wrap)", "(", True, 1, "(wrap)"),
-        ("{wrap}", "{", True, 2, "{wrap}"),
-        ("(wrap)", "{", True, 1, "{(wrap)}"),
-        ("(wrap)", "(", False, 1, "((wrap))"),
-    ],
-)
-def test_wrap(text: str, open: str, expected: str, check_first: bool, num: int):
-    """Test wrapping a string.
-
-    Args:
-        text: The text to wrap.
-        open: The open character.
-        expected: The expected output string.
-        check_first: Whether to check if the text is already wrapped.
-        num: The number of times to wrap the text.
-    """
-    assert format.wrap(text, open, check_first=check_first, num=num) == expected
-
-
-@pytest.mark.parametrize(
-    "text,indent_level,expected",
-    [
-        ("", 2, ""),
-        ("hello", 2, "hello"),
-        ("hello\nworld", 2, "  hello\n  world\n"),
-        ("hello\nworld", 4, "    hello\n    world\n"),
-        ("  hello\n  world", 2, "    hello\n    world\n"),
-    ],
-)
-def test_indent(text: str, indent_level: int, expected: str, windows_platform: bool):
-    """Test indenting a string.
-
-    Args:
-        text: The text to indent.
-        indent_level: The number of spaces to indent by.
-        expected: The expected output string.
-        windows_platform: Whether the system is windows.
-    """
-    assert format.indent(text, indent_level) == (
-        expected.replace("\n", "\r\n") if windows_platform else expected
-    )
-
-
-@pytest.mark.parametrize(
-    "condition,true_value,false_value,expected",
-    [
-        ("cond", "<C1>", '""', '{isTrue(cond) ? <C1> : ""}'),
-        ("cond", "<C1>", "<C2>", "{isTrue(cond) ? <C1> : <C2>}"),
-    ],
-)
-def test_format_cond(condition: str, true_value: str, false_value: str, expected: str):
-    """Test formatting a cond.
-
-    Args:
-        condition: The condition to check.
-        true_value: The value to return if the condition is true.
-        false_value: The value to return if the condition is false.
-        expected: The expected output string.
-    """
-    assert format.format_cond(condition, true_value, false_value) == expected
-
-
 def test_merge_imports():
 def test_merge_imports():
     """Test that imports are merged correctly."""
     """Test that imports are merged correctly."""
     d1 = {"react": {"Component"}}
     d1 = {"react": {"Component"}}
@@ -263,110 +87,6 @@ def test_is_generic_alias(cls: type, expected: bool):
     assert types.is_generic_alias(cls) == expected
     assert types.is_generic_alias(cls) == expected
 
 
 
 
-@pytest.mark.parametrize(
-    "route,format_case,expected",
-    [
-        ("", True, "index"),
-        ("/", True, "index"),
-        ("custom-route", True, "custom-route"),
-        ("custom-route", False, "custom-route"),
-        ("custom-route/", True, "custom-route"),
-        ("custom-route/", False, "custom-route"),
-        ("/custom-route", True, "custom-route"),
-        ("/custom-route", False, "custom-route"),
-        ("/custom_route", True, "custom-route"),
-        ("/custom_route", False, "custom_route"),
-        ("/CUSTOM_route", True, "custom-route"),
-        ("/CUSTOM_route", False, "CUSTOM_route"),
-    ],
-)
-def test_format_route(route: str, format_case: bool, expected: bool):
-    """Test formatting a route.
-
-    Args:
-        route: The route to format.
-        format_case: Whether to change casing to snake_case.
-        expected: The expected formatted route.
-    """
-    assert format.format_route(route, format_case=format_case) == expected
-
-
-@pytest.mark.parametrize(
-    "prop,formatted",
-    [
-        ("string", '"string"'),
-        ("{wrapped_string}", "{wrapped_string}"),
-        (True, "{true}"),
-        (False, "{false}"),
-        (123, "{123}"),
-        (3.14, "{3.14}"),
-        ([1, 2, 3], "{[1, 2, 3]}"),
-        (["a", "b", "c"], '{["a", "b", "c"]}'),
-        ({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'),
-        ({"a": 'foo "bar" baz'}, r'{{"a": "foo \"bar\" baz"}}'),
-        (
-            {
-                "a": 'foo "{ "bar" }" baz',
-                "b": BaseVar(name="val", type_="str"),
-            },
-            r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
-        ),
-        (
-            EventChain(
-                events=[EventSpec(handler=EventHandler(fn=mock_event))], args_spec=None
-            ),
-            '{_e => addEvents([Event("mock_event", {})], _e)}',
-        ),
-        (
-            EventChain(
-                events=[
-                    EventSpec(
-                        handler=EventHandler(fn=mock_event),
-                        args=((Var.create_safe("arg"), EVENT_ARG.target.value),),
-                    )
-                ],
-                args_spec=None,
-            ),
-            '{_e => addEvents([Event("mock_event", {arg:_e.target.value})], _e)}',
-        ),
-        ({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'),
-        (BaseVar(name="var", type_="int"), "{var}"),
-        (
-            BaseVar(
-                name="_",
-                type_=Any,
-                state="",
-                is_local=True,
-                is_string=False,
-            ),
-            "{_}",
-        ),
-        (BaseVar(name='state.colors["a"]', type_="str"), '{state.colors["a"]}'),
-        ({"a": BaseVar(name="val", type_="str")}, '{{"a": val}}'),
-        ({"a": BaseVar(name='"val"', type_="str")}, '{{"a": "val"}}'),
-        (
-            {"a": BaseVar(name='state.colors["val"]', type_="str")},
-            '{{"a": state.colors["val"]}}',
-        ),
-        # tricky real-world case from markdown component
-        (
-            {
-                "h1": f"{{({{node, ...props}}) => <Heading {{...props}} {''.join(Tag(name='', props=Style({'as_': 'h1'})).format_props())} />}}"
-            },
-            '{{"h1": ({node, ...props}) => <Heading {...props} as={`h1`} />}}',
-        ),
-    ],
-)
-def test_format_prop(prop: Var, formatted: str):
-    """Test that the formatted value of an prop is correct.
-
-    Args:
-        prop: The prop to test.
-        formatted: The expected formatted value.
-    """
-    assert format.format_prop(prop) == formatted
-
-
 def test_validate_invalid_bun_path(mocker):
 def test_validate_invalid_bun_path(mocker):
     """Test that an error is thrown when a custom specified bun path is not valid
     """Test that an error is thrown when a custom specified bun path is not valid
     or does not exist.
     or does not exist.
@@ -523,31 +243,11 @@ def test_create_config_e2e(tmp_working_dir):
     app_name = "e2e"
     app_name = "e2e"
     prerequisites.create_config(app_name)
     prerequisites.create_config(app_name)
     eval_globals = {}
     eval_globals = {}
-    exec((tmp_working_dir / constants.CONFIG_FILE).read_text(), eval_globals)
+    exec((tmp_working_dir / constants.Config.FILE).read_text(), eval_globals)
     config = eval_globals["config"]
     config = eval_globals["config"]
     assert config.app_name == app_name
     assert config.app_name == app_name
 
 
 
 
-@pytest.mark.parametrize(
-    "name,expected",
-    [
-        ("input1", "ref_input1"),
-        ("input 1", "ref_input_1"),
-        ("input-1", "ref_input_1"),
-        ("input_1", "ref_input_1"),
-        ("a long test?1! name", "ref_a_long_test_1_name"),
-    ],
-)
-def test_format_ref(name, expected):
-    """Test formatting a ref.
-
-    Args:
-        name: The name to format.
-        expected: The expected formatted name.
-    """
-    assert format.format_ref(name) == expected
-
-
 class DataFrame:
 class DataFrame:
     """A Fake pandas DataFrame class."""
     """A Fake pandas DataFrame class."""
 
 
@@ -585,8 +285,8 @@ def test_initialize_non_existent_gitignore(tmp_path, mocker, gitignore_exists):
         mocker: The mock object.
         mocker: The mock object.
         gitignore_exists: Whether a gitignore file exists in the root dir.
         gitignore_exists: Whether a gitignore file exists in the root dir.
     """
     """
-    expected = constants.DEFAULT_GITIGNORE.copy()
-    mocker.patch("reflex.constants.GITIGNORE_FILE", tmp_path / ".gitignore")
+    expected = constants.GitIgnore.DEFAULTS.copy()
+    mocker.patch("reflex.constants.GitIgnore.FILE", tmp_path / ".gitignore")
 
 
     gitignore_file = tmp_path / ".gitignore"
     gitignore_file = tmp_path / ".gitignore"
 
 
@@ -633,8 +333,8 @@ def test_node_install_windows(tmp_path, mocker):
     fnm_root_path = tmp_path / "reflex" / "fnm"
     fnm_root_path = tmp_path / "reflex" / "fnm"
     fnm_exe = fnm_root_path / "fnm.exe"
     fnm_exe = fnm_root_path / "fnm.exe"
 
 
-    mocker.patch("reflex.utils.prerequisites.constants.FNM_DIR", fnm_root_path)
-    mocker.patch("reflex.utils.prerequisites.constants.FNM_EXE", fnm_exe)
+    mocker.patch("reflex.utils.prerequisites.constants.Fnm.DIR", fnm_root_path)
+    mocker.patch("reflex.utils.prerequisites.constants.Fnm.EXE", fnm_exe)
     mocker.patch("reflex.utils.prerequisites.constants.IS_WINDOWS", True)
     mocker.patch("reflex.utils.prerequisites.constants.IS_WINDOWS", True)
     mocker.patch("reflex.utils.processes.new_process")
     mocker.patch("reflex.utils.processes.new_process")
     mocker.patch("reflex.utils.processes.stream_logs")
     mocker.patch("reflex.utils.processes.stream_logs")
@@ -675,8 +375,8 @@ def test_node_install_unix(tmp_path, mocker, machine, system):
     fnm_root_path = tmp_path / "reflex" / "fnm"
     fnm_root_path = tmp_path / "reflex" / "fnm"
     fnm_exe = fnm_root_path / "fnm"
     fnm_exe = fnm_root_path / "fnm"
 
 
-    mocker.patch("reflex.utils.prerequisites.constants.FNM_DIR", fnm_root_path)
-    mocker.patch("reflex.utils.prerequisites.constants.FNM_EXE", fnm_exe)
+    mocker.patch("reflex.utils.prerequisites.constants.Fnm.DIR", fnm_root_path)
+    mocker.patch("reflex.utils.prerequisites.constants.Fnm.EXE", fnm_exe)
     mocker.patch("reflex.utils.prerequisites.constants.IS_WINDOWS", False)
     mocker.patch("reflex.utils.prerequisites.constants.IS_WINDOWS", False)
     mocker.patch("reflex.utils.prerequisites.platform.machine", return_value=machine)
     mocker.patch("reflex.utils.prerequisites.platform.machine", return_value=machine)
     mocker.patch("reflex.utils.prerequisites.platform.system", return_value=system)
     mocker.patch("reflex.utils.prerequisites.platform.system", return_value=system)
@@ -701,14 +401,14 @@ def test_node_install_unix(tmp_path, mocker, machine, system):
                 fnm_exe,
                 fnm_exe,
                 "install",
                 "install",
                 "--arch=arm64",
                 "--arch=arm64",
-                constants.NODE_VERSION,
+                constants.Node.VERSION,
                 "--fnm-dir",
                 "--fnm-dir",
                 fnm_root_path,
                 fnm_root_path,
             ]
             ]
         )
         )
     else:
     else:
         process.assert_called_with(
         process.assert_called_with(
-            [fnm_exe, "install", constants.NODE_VERSION, "--fnm-dir", fnm_root_path]
+            [fnm_exe, "install", constants.Node.VERSION, "--fnm-dir", fnm_root_path]
         )
         )
     chmod.assert_called_once()
     chmod.assert_called_once()
 
 
@@ -759,7 +459,7 @@ def test_output_system_info(mocker):
     This test makes no assertions about the output, other than it executes
     This test makes no assertions about the output, other than it executes
     without crashing.
     without crashing.
     """
     """
-    mocker.patch("reflex.utils.console.LOG_LEVEL", constants.LogLevel.DEBUG)
+    mocker.patch("reflex.utils.console._LOG_LEVEL", constants.LogLevel.DEBUG)
     utils_exec.output_system_info()
     utils_exec.output_system_info()