Parcourir la source

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

Thomas Brandého il y a 1 an
Parent
commit
dcb17103bb

+ 24 - 22
reflex/app.py

@@ -183,8 +183,8 @@ class App(Base):
             else config.cors_allowed_origins,
             cors_credentials=True,
             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.
@@ -340,14 +340,14 @@ class App(Base):
         self,
         component: Component | ComponentCallable,
         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
         | EventSpec
         | list[EventHandler | EventSpec]
         | 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,
     ):
         """Add a page to the app.
@@ -433,7 +433,7 @@ class App(Base):
         """
         route = route.lstrip("/")
         if route == "":
-            route = constants.INDEX_ROUTE
+            route = constants.PageNames.INDEX_ROUTE
         return self.load_events.get(route, [])
 
     def _check_routes_conflict(self, new_route: str):
@@ -472,14 +472,14 @@ class App(Base):
     def add_custom_404_page(
         self,
         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
         | EventSpec
         | list[EventHandler | EventSpec]
         | 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.
 
@@ -498,10 +498,10 @@ class App(Base):
             component = Default404Page.create()
         self.add_page(
             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,
             meta=meta,
         )
@@ -541,11 +541,13 @@ class App(Base):
         page_imports = {
             i
             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 any(tag.install for tag in tags)
         }
@@ -581,7 +583,7 @@ class App(Base):
             self.add_page(render, **kwargs)
 
         # 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()
 
         task = progress.add_task("Compiling: ", total=len(self.pages))
@@ -649,7 +651,7 @@ class App(Base):
         # Compile the Tailwind config.
         if config.tailwind is not None:
             config.tailwind["content"] = config.tailwind.get(
-                "content", constants.TAILWIND_CONTENT
+                "content", constants.Tailwind.CONTENT
             )
             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"),
     },
     "next/router": {ImportVar(tag="useRouter")},
-    f"/{constants.STATE_PATH}": {
+    f"/{constants.Dirs.STATE_PATH}": {
         ImportVar(tag="uploadFiles"),
         ImportVar(tag="Event"),
         ImportVar(tag="isTrue"),
@@ -40,9 +40,9 @@ DEFAULT_IMPORTS: imports.ImportDict = {
         ImportVar(tag="initialEvents"),
         ImportVar(tag="StateContext"),
     },
-    "": {ImportVar(tag="focus-visible/dist/focus-visible")},
+    "": {ImportVar(tag="focus-visible/dist/focus-visible", install=False)},
     "@chakra-ui/react": {
-        ImportVar(tag=constants.USE_COLOR_MODE),
+        ImportVar(tag=constants.ColorMode.USE),
         ImportVar(tag="Box"),
         ImportVar(tag="Text"),
     },
@@ -151,7 +151,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
     """
     # Add tailwind css if enabled.
     sheets = (
-        [constants.TAILWIND_ROOT_STYLE_PATH]
+        [constants.Tailwind.ROOT_STYLE_PATH]
         if get_config().tailwind is not None
         else []
     )
@@ -159,7 +159,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
         if not utils.is_valid_url(stylesheet):
             # check if stylesheet provided exists.
             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):
                 raise FileNotFoundError(
@@ -193,7 +193,7 @@ def _compile_components(components: set[CustomComponent]) -> str:
     """
     imports = {
         "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 = []
 
@@ -236,7 +236,7 @@ def compile_document_root(head_components: list[Component]) -> tuple[str, str]:
         The path and code of the compiled document root.
     """
     # 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.
     document_root = utils.create_document_root(head_components)
@@ -330,7 +330,7 @@ def compile_tailwind(
         The compiled Tailwind config.
     """
     # Get the path for the output file.
-    output_path = constants.TAILWIND_CONFIG
+    output_path = constants.Tailwind.CONFIG
 
     # Compile the config.
     code = _compile_tailwind(config)
@@ -339,4 +339,4 @@ def compile_tailwind(
 
 def purge_web_pages_dir():
     """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["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"] = {
-            "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,
-            "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": {
-                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)
 
         # 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
 
         if not lib:
@@ -104,9 +104,7 @@ def compile_imports(imports: imports.ImportDict) -> list[dict]:
             continue
 
         # 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))
     return import_dicts
@@ -313,7 +311,7 @@ def get_page_path(path: str) -> str:
     Returns:
         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:
@@ -322,7 +320,9 @@ def get_theme_path() -> str:
     Returns:
         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:
@@ -332,7 +332,7 @@ def get_root_stylesheet_path() -> str:
         The path of the app root file.
     """
     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:
         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:
@@ -351,7 +351,7 @@ def get_components_path() -> str:
     Returns:
         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:
@@ -364,9 +364,9 @@ def get_asset_path(filename: str | None = None) -> str:
         The path of the asset.
     """
     if filename is None:
-        return constants.WEB_ASSETS_DIR
+        return constants.Dirs.WEB_ASSETS
     else:
-        return os.path.join(constants.WEB_ASSETS_DIR, filename)
+        return os.path.join(constants.Dirs.WEB_ASSETS, filename)
 
 
 def add_meta(

+ 2 - 3
reflex/components/component.py

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

+ 2 - 4
reflex/config.py

@@ -163,7 +163,7 @@ class Config(Base):
     telemetry_enabled: bool = True
 
     # 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.
     cors_allowed_origins: List[str] = ["*"]
@@ -284,11 +284,9 @@ def get_config(reload: bool = False) -> Config:
     Returns:
         The app config.
     """
-    from reflex.config import Config
-
     sys.path.insert(0, os.getcwd())
     try:
-        rxconfig = __import__(constants.CONFIG_MODULE)
+        rxconfig = __import__(constants.Config.MODULE)
         if reload:
             importlib.reload(rxconfig)
         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.
-PYNECONE_PROVIDED_ATTRS = {"class", "id", "style"}
+REFLEX_PROVIDED_ATTRS = {"class", "id", "style"}
 
 # ATTR_TO_ELEMENTS contains HTML attribute names, which might be invalid as
 # Reflex prop names. PROP_TO_ELEMENTS contains the corresponding Reflex
@@ -41,7 +41,7 @@ PYNECONE_PROVIDED_ATTRS = {"class", "id", "style"}
 PROP_TO_ELEMENTS = {
     attr_to_prop(attr_name): elements
     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.

+ 11 - 11
reflex/event.py

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

+ 2 - 2
reflex/middleware/hydrate_middleware.py

@@ -13,7 +13,7 @@ if TYPE_CHECKING:
     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):
@@ -40,7 +40,7 @@ class HydrateMiddleware(Middleware):
         state._reset_client_storage()
 
         # 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
         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.
     """
     if value:
-        console.print(constants.VERSION)
+        console.print(constants.Reflex.VERSION)
         raise typer.Exit()
 
 
@@ -53,8 +53,9 @@ def init(
     name: str = typer.Option(
         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(
         config.loglevel, help="The log level to use."
@@ -78,7 +79,7 @@ def init(
     prerequisites.migrate_to_reflex()
 
     # 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.initialize_app_directory(app_name, template)
         telemetry.send("init")
@@ -193,7 +194,7 @@ def run(
 def deploy(
     dry_run: bool = typer.Option(False, help="Whether to run a dry run."),
     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."""
@@ -223,10 +224,10 @@ def deploy(
     backend = response["backend_resources_url"]
 
     # 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
 
-    with open(constants.BACKEND_ZIP, "rb") as f:
+    with open(constants.ComponentName.BACKEND.zip(), "rb") as f:
         httpx.put(backend, data=f)  # type: ignore
 
 
@@ -242,7 +243,7 @@ def export(
         True, "--frontend-only", help="Export only frontend.", show_default=False
     ),
     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."""

+ 3 - 3
reflex/state.py

@@ -1102,7 +1102,7 @@ class StateProxy(wrapt.ObjectProxy):
             state_instance: The state instance to proxy.
         """
         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_actx = None
         self._self_mutable = False
@@ -1355,10 +1355,10 @@ class StateManagerRedis(StateManager):
     redis: Redis
 
     # 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).
-    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
     _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.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):

+ 3 - 3
reflex/testing.py

@@ -154,7 +154,7 @@ class AppHarness:
             with chdir(self.app_path):
                 reflex.reflex.init(
                     name=self.app_name,
-                    template=reflex.constants.Template.DEFAULT,
+                    template=reflex.constants.Templates.Kind.DEFAULT,
                     loglevel=reflex.constants.LogLevel.INFO,
                 )
                 self.app_module_path.write_text(source_code)
@@ -211,7 +211,7 @@ class AppHarness:
         # Start the frontend.
         self.frontend_process = reflex.utils.processes.new_process(
             [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"},
             **FRONTEND_POPEN_ARGS,
         )
@@ -647,7 +647,7 @@ class AppHarnessProd(AppHarness):
     frontend_server: Optional[Subdir404TCPServer] = None
 
     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 = {
             404: web_root / "404.html",
         }

+ 16 - 19
reflex/utils/build.py

@@ -6,7 +6,6 @@ import json
 import os
 import subprocess
 import zipfile
-from enum import Enum
 from pathlib import Path
 
 from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
@@ -19,7 +18,7 @@ from reflex.utils import console, path_ops, prerequisites, processes
 def set_env_json():
     """Write the upload url to a REFLEX_JSON."""
     path_ops.update_json_file(
-        constants.ENV_JSON,
+        constants.Dirs.ENV_JSON,
         {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))
 
 
-class _ComponentName(Enum):
-    BACKEND = "Backend"
-    FRONTEND = "Frontend"
-
-
 def _zip(
-    component_name: _ComponentName,
+    component_name: constants.ComponentName,
     target: str,
     root_dir: str,
     dirs_to_exclude: set[str] | None = None,
@@ -130,7 +124,7 @@ def export(
         deploy_url: The URL of the deployed app.
     """
     # Remove the static folder.
-    path_ops.rm(constants.WEB_STATIC_DIR)
+    path_ops.rm(constants.Dirs.WEB_STATIC)
 
     # The export command to run.
     command = "export"
@@ -155,25 +149,28 @@ def export(
         # Start the subprocess with the progress bar.
         process = processes.new_process(
             [prerequisites.get_package_manager(), "run", command],
-            cwd=constants.WEB_DIR,
+            cwd=constants.Dirs.WEB,
             shell=constants.IS_WINDOWS,
         )
         processes.show_progress("Creating Production Build", process, checkpoints)
 
     # Zip up the app.
     if zip:
-        files_to_exclude = {constants.FRONTEND_ZIP, constants.BACKEND_ZIP}
+        files_to_exclude = {
+            constants.ComponentName.FRONTEND.zip(),
+            constants.ComponentName.BACKEND.zip(),
+        }
         if frontend:
             _zip(
-                component_name=_ComponentName.FRONTEND,
-                target=constants.FRONTEND_ZIP,
+                component_name=constants.ComponentName.FRONTEND,
+                target=constants.ComponentName.FRONTEND.zip(),
                 root_dir=".web/_static",
                 files_to_exclude=files_to_exclude,
             )
         if backend:
             _zip(
-                component_name=_ComponentName.BACKEND,
-                target=constants.BACKEND_ZIP,
+                component_name=constants.ComponentName.BACKEND,
+                target=constants.ComponentName.BACKEND.zip(),
                 root_dir=".",
                 dirs_to_exclude={"assets", "__pycache__"},
                 files_to_exclude=files_to_exclude,
@@ -192,8 +189,8 @@ def setup_frontend(
     """
     # Copy asset files to public folder.
     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).
@@ -209,7 +206,7 @@ def setup_frontend(
                 "telemetry",
                 "disable",
             ],
-            cwd=constants.WEB_DIR,
+            cwd=constants.Dirs.WEB,
             stdout=subprocess.DEVNULL,
             shell=constants.IS_WINDOWS,
         )

+ 10 - 10
reflex/utils/console.py

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

+ 11 - 11
reflex/utils/exec.py

@@ -73,7 +73,7 @@ def run_process_and_launch_url(run_command: list[str]):
     Args:
         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)
     process = None
     first_run = True
@@ -81,7 +81,7 @@ def run_process_and_launch_url(run_command: list[str]):
     while True:
         if process is None:
             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:
             for line in processes.stream_logs("Starting frontend", process):
@@ -153,9 +153,9 @@ def run_backend(
         loglevel: The log level.
     """
     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(
-        app=f"{app_module}.{constants.API_VAR}",
+        app=f"{app_module}.{constants.CompileVars.API}",
         host=host,
         port=port,
         log_level=loglevel.value,
@@ -180,7 +180,7 @@ def run_backend_prod(
     config = get_config()
     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()
-    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 = (
         [
             *RUN_BACKEND_PROD_WINDOWS,
@@ -217,7 +217,7 @@ def run_backend_prod(
 
 def output_system_info():
     """Show system information if the loglevel is in DEBUG."""
-    if console.LOG_LEVEL > constants.LogLevel.DEBUG:
+    if console._LOG_LEVEL > constants.LogLevel.DEBUG:
         return
 
     config = get_config()
@@ -231,8 +231,8 @@ def output_system_info():
     console.debug(f"Config: {config}")
 
     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()
@@ -240,13 +240,13 @@ def output_system_info():
     if system != "Windows":
         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:
         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":

+ 18 - 3
reflex/utils/format.py

@@ -5,7 +5,6 @@ from __future__ import annotations
 import inspect
 import json
 import os
-import os.path as op
 import re
 import sys
 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 route == "":
-        return constants.INDEX_ROUTE
+        return constants.PageNames.INDEX_ROUTE
 
     return route
 
@@ -559,11 +558,27 @@ def format_breadcrumbs(route: str) -> list[tuple[str, str]]:
 
     # create and return breadcrumbs
     return [
-        (part, op.join("/", *route_parts[: i + 1]))
+        (part, "/".join(["", *route_parts[: i + 1]]))
         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:
     """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:
         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")
         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:
@@ -130,9 +130,9 @@ def get_node_path() -> str | None:
     Returns:
         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 constants.NODE_PATH
+    return constants.Node.PATH
 
 
 def get_npm_path() -> str | None:
@@ -141,9 +141,9 @@ def get_npm_path() -> str | None:
     Returns:
         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 constants.NPM_PATH
+    return constants.Node.NPM_PATH
 
 
 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:
         # Compare the version numbers
         return (
-            current_version >= version.parse(constants.NODE_VERSION_MIN)
+            current_version >= version.parse(constants.Node.MIN_VERSION)
             if constants.IS_WINDOWS
-            else current_version == version.parse(constants.NODE_VERSION)
+            else current_version == version.parse(constants.Node.VERSION)
         )
     return False
 
@@ -111,7 +111,7 @@ def get_app(reload: bool = False) -> ModuleType:
     config = get_config()
     module = ".".join([config.app_name, config.app_name])
     sys.path.insert(0, os.getcwd())
-    app = __import__(module, fromlist=(constants.APP_VAR,))
+    app = __import__(module, fromlist=(constants.CompileVars.APP,))
     if reload:
         importlib.reload(app)
     return app
@@ -160,9 +160,9 @@ def get_default_app_name() -> str:
     app_name = os.getcwd().split(os.path.sep)[-1].replace("-", "_")
 
     # Make sure the app is not named "reflex".
-    if app_name == constants.MODULE_NAME:
+    if app_name == constants.Reflex.MODULE_NAME:
         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)
 
@@ -179,28 +179,28 @@ def create_config(app_name: str):
     from reflex.compiler import templates
 
     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))
 
 
 def initialize_gitignore():
     """Initialize the template .gitignore file."""
     # The files to add to the .gitignore file.
-    files = constants.DEFAULT_GITIGNORE
+    files = constants.GitIgnore.DEFAULTS
 
     # 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()])
 
     # 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()}")
 
 
-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.
 
     Args:
@@ -208,26 +208,28 @@ def initialize_app_directory(app_name: str, template: constants.Template):
         template: The template to use.
     """
     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(
         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():
     """Initialize the web directory on reflex init."""
     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()
 
-    path_ops.mkdir(constants.WEB_ASSETS_DIR)
+    path_ops.mkdir(constants.Dirs.WEB_ASSETS)
 
     # 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:
         next_config = file.read()
@@ -243,19 +245,19 @@ def initialize_web_directory():
 def _compile_package_json():
     return templates.PACKAGE_JSON.render(
         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():
     """Render and write in .web the package.json file."""
-    output_path = constants.PACKAGE_JSON_PATH
+    output_path = constants.PackageJson.PATH
     code = _compile_package_json()
     with open(output_path, "w") as file:
         file.write(code)
@@ -269,10 +271,10 @@ def init_reflex_json():
 
     # Write the hash and version to the reflex json file.
     reflex_json = {
-        "version": constants.VERSION,
+        "version": constants.Reflex.VERSION,
         "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:
@@ -302,7 +304,7 @@ def remove_existing_bun_installation():
     """Remove existing bun installation."""
     console.debug("Removing existing bun installation.")
     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):
@@ -339,9 +341,9 @@ def download_and_extract_fnm_zip():
         Exit: If an error occurs while downloading or extracting the FNM zip.
     """
     # Download the zip file
-    url = constants.FNM_INSTALL_URL
+    url = constants.Fnm.INSTALL_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.
     try:
         # Download the FNM zip release.
@@ -354,7 +356,7 @@ def download_and_extract_fnm_zip():
 
         # Extract the downloaded zip file.
         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.")
     except Exception as e:
@@ -369,13 +371,13 @@ def install_node():
     """Install fnm and nodejs for use by Reflex.
     Independent of any existing system installations.
     """
-    if not constants.FNM_FILENAME:
+    if not constants.Fnm.FILENAME:
         # fnm only support Linux, macOS and Windows distros.
         console.debug("")
         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()
 
     if constants.IS_WINDOWS:
@@ -384,13 +386,13 @@ def install_node():
             [
                 "powershell",
                 "-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).
         # TODO we can skip installation if check_node_version() checks out
         # Add execute permissions to fnm executable.
-        os.chmod(constants.FNM_EXE, stat.S_IXUSR)
+        os.chmod(constants.Fnm.EXE, stat.S_IXUSR)
         # Install node.
         # Specify arm64 arch explicitly for M1s and M2s.
         architecture_arg = (
@@ -401,12 +403,12 @@ def install_node():
 
         process = processes.new_process(
             [
-                constants.FNM_EXE,
+                constants.Fnm.EXE,
                 "install",
                 *architecture_arg,
-                constants.NODE_VERSION,
+                constants.Node.VERSION,
                 "--fnm-dir",
-                constants.FNM_DIR,
+                constants.Fnm.DIR,
             ],
         )
     processes.show_status("Installing node", process)
@@ -435,9 +437,9 @@ def install_bun():
 
     # Run the bun install script.
     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.
     process = processes.new_process(
         [get_install_package_manager(), "install", "--loglevel", "silly"],
-        cwd=constants.WEB_DIR,
+        cwd=constants.Dirs.WEB,
         shell=constants.IS_WINDOWS,
     )
 
@@ -467,10 +469,10 @@ def install_frontend_packages(packages: set[str]):
                 get_install_package_manager(),
                 "add",
                 "-d",
-                constants.TAILWIND_VERSION,
+                constants.Tailwind.VERSION,
                 *((config.tailwind or {}).get("plugins", [])),
             ],
-            cwd=constants.WEB_DIR,
+            cwd=constants.Dirs.WEB,
             shell=constants.IS_WINDOWS,
         )
         processes.show_status("Installing tailwind", process)
@@ -479,7 +481,7 @@ def install_frontend_packages(packages: set[str]):
     if len(packages) > 0:
         process = processes.new_process(
             [get_install_package_manager(), "add", *packages],
-            cwd=constants.WEB_DIR,
+            cwd=constants.Dirs.WEB,
             shell=constants.IS_WINDOWS,
         )
         processes.show_status(
@@ -496,14 +498,14 @@ def check_initialized(frontend: bool = True):
     Raises:
         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.
     if not (has_config and has_reflex_dir and has_web_dir):
         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)
 
@@ -527,11 +529,11 @@ def is_latest_template() -> bool:
     Returns:
         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
-    with open(constants.REFLEX_JSON) as f:  # type: ignore
+    with open(constants.Reflex.JSON) as f:  # type: ignore
         app_version = json.load(f)["version"]
-    return app_version == constants.VERSION
+    return app_version == constants.Reflex.VERSION
 
 
 def validate_bun():
@@ -543,16 +545,16 @@ def validate_bun():
     # if a custom bun path is provided, make sure its valid
     # This is specific to non-FHS OS
     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()
         if not bun_version:
             console.error(
                 "Failed to obtain bun version. Make sure the specified bun path in your config is correct."
             )
             raise typer.Exit(1)
-        elif bun_version < version.parse(constants.MIN_BUN_VERSION):
+        elif bun_version < version.parse(constants.Bun.MIN_VERSION):
             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"that satisfies the minimum version requirement."
             )
@@ -582,7 +584,7 @@ def validate_frontend_dependencies(init=True):
         if not check_node_version():
             node_version = get_node_version()
             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)
 
@@ -597,7 +599,7 @@ def validate_frontend_dependencies(init=True):
 def initialize_frontend_dependencies():
     """Initialize all the frontend dependencies."""
     # Create the reflex directory.
-    path_ops.mkdir(constants.REFLEX_DIR)
+    path_ops.mkdir(constants.Reflex.DIR)
     # validate dependencies before install
     validate_frontend_dependencies()
     # Install the frontend dependencies.
@@ -644,7 +646,7 @@ def check_schema_up_to_date():
 def migrate_to_reflex():
     """Migration from Pynecone to Reflex."""
     # 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
 
     # Ask the user if they want to migrate.
@@ -657,16 +659,16 @@ def migrate_to_reflex():
 
     # Rename pcconfig to rxconfig.
     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.
     file_pattern = os.path.join(get_config().app_name, "**/*.py")
     file_list = glob.glob(file_pattern, recursive=True)
 
     # 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.
     updates = {

+ 1 - 1
reflex/utils/telemetry.py

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

+ 2 - 2
reflex/utils/types.py

@@ -4,7 +4,6 @@ from __future__ import annotations
 
 import contextlib
 import typing
-from types import LambdaType
 from typing import Any, Callable, Type, Union, _GenericAlias  # type: ignore
 
 from reflex.base import Base
@@ -18,7 +17,8 @@ PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
 StateVar = Union[PrimitiveType, Base, None]
 StateIterVar = Union[list, set, tuple]
 
-ArgsSpec = LambdaType
+# ArgsSpec = Callable[[Var], list[Var]]
+ArgsSpec = Callable
 
 
 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.observers import Observer
 
-from reflex.constants import APP_ASSETS_DIR, WEB_ASSETS_DIR
+from reflex.constants import Dirs
 
 
 class AssetFolderWatch:
@@ -20,7 +20,7 @@ class AssetFolderWatch:
         Args:
             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)
 
     def start(self):
@@ -90,5 +90,5 @@ class AssetFolderHandler(FileSystemEventHandler):
             The public file path.
         """
         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.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:
     from reflex.state import State
@@ -124,7 +123,7 @@ class Var(ABC):
 
         # Try to serialize the value.
         type_ = type(value)
-        name = serialize(value)
+        name = serializers.serialize(value)
         if name is None:
             raise TypeError(
                 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
 
 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.state import State, StateUpdate
 
@@ -17,7 +17,7 @@ def exp_is_hydrated(state: State) -> Dict[str, Any]:
     Returns:
         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):

+ 1 - 1
tests/test_app.py

@@ -923,7 +923,7 @@ async def test_dynamic_route_var_route_change_completed_on_load(
                 state.get_name(): {
                     arg_name: exp_val,
                     f"comp_{arg_name}": exp_val,
-                    constants.IS_HYDRATED: False,
+                    constants.CompileVars.IS_HYDRATED: False,
                     "loaded": exp_index,
                     "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
 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.state import (
     ImmutableStateError,
@@ -28,7 +28,7 @@ from reflex.state import (
     StateProxy,
     StateUpdate,
 )
-from reflex.utils import format, prerequisites
+from reflex.utils import prerequisites
 from reflex.vars import BaseVar, ComputedVar
 
 from .states import GenState
@@ -118,6 +118,15 @@ class GrandchildState(ChildState):
         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
 def test_state() -> TestState:
     """A state.
@@ -214,7 +223,7 @@ def test_class_vars(test_state):
     """
     cls = type(test_state)
     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",
         "num2",
         "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):
     """Test that we can set default values.
 
@@ -750,21 +707,6 @@ async def test_process_event_generator():
     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):
     """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() == {
         cs.get_name(): {"dep_v": 2},
         "no_cache_v": 1,
-        IS_HYDRATED: False,
+        CompileVars.IS_HYDRATED: False,
     }
     assert ps.dict() == {
         cs.get_name(): {"dep_v": 4},
         "no_cache_v": 3,
-        IS_HYDRATED: False,
+        CompileVars.IS_HYDRATED: False,
     }
     assert ps.dict() == {
         cs.get_name(): {"dep_v": 6},
         "no_cache_v": 5,
-        IS_HYDRATED: False,
+        CompileVars.IS_HYDRATED: False,
     }
     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()
     """
     app_module = Mock()
-    setattr(app_module, APP_VAR, app)
+    setattr(app_module, CompileVars.APP, app)
     app.state = TestState
     app.state_manager = state_manager
     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.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.style import Style
 from reflex.utils import (
     build,
-    format,
     imports,
     prerequisites,
     types,
 )
 from reflex.utils import exec as utils_exec
 from reflex.utils.serializers import serialize
-from reflex.vars import BaseVar, Var
+from reflex.vars import Var
 
 
 def mock_event(arg):
@@ -36,7 +33,7 @@ def get_above_max_version():
         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
     return ".".join(semantic_version_list)
 
@@ -59,179 +56,6 @@ def test_func():
     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():
     """Test that imports are merged correctly."""
     d1 = {"react": {"Component"}}
@@ -263,110 +87,6 @@ def test_is_generic_alias(cls: type, expected: bool):
     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):
     """Test that an error is thrown when a custom specified bun path is not valid
     or does not exist.
@@ -523,31 +243,11 @@ def test_create_config_e2e(tmp_working_dir):
     app_name = "e2e"
     prerequisites.create_config(app_name)
     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"]
     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:
     """A Fake pandas DataFrame class."""
 
@@ -585,8 +285,8 @@ def test_initialize_non_existent_gitignore(tmp_path, mocker, gitignore_exists):
         mocker: The mock object.
         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"
 
@@ -633,8 +333,8 @@ def test_node_install_windows(tmp_path, mocker):
     fnm_root_path = tmp_path / "reflex" / "fnm"
     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.processes.new_process")
     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_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.platform.machine", return_value=machine)
     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,
                 "install",
                 "--arch=arm64",
-                constants.NODE_VERSION,
+                constants.Node.VERSION,
                 "--fnm-dir",
                 fnm_root_path,
             ]
         )
     else:
         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()
 
@@ -759,7 +459,7 @@ def test_output_system_info(mocker):
     This test makes no assertions about the output, other than it executes
     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()