diff --git a/reflex/constants/compiler.py b/reflex/constants/compiler.py index b7ffef161..da6da3c4c 100644 --- a/reflex/constants/compiler.py +++ b/reflex/constants/compiler.py @@ -1,6 +1,7 @@ """Compiler variables.""" import enum +import os from enum import Enum from types import SimpleNamespace @@ -61,18 +62,35 @@ class CompileVars(SimpleNamespace): CONNECT_ERROR = "connectErrors" # The name of the function for converting a dict to an event. TO_EVENT = "Event" + # The env var to toggle minification of states. + ENV_MINIFY_STATES = "REFLEX_MINIFY_STATES" + # Whether to minify states. + MINIFY_STATES = os.environ.get(ENV_MINIFY_STATES, False) # The name of the internal on_load event. - ON_LOAD_INTERNAL = "reflex___state____on_load_internal_state.on_load_internal" + ON_LOAD_INTERNAL = ( + "l" + if MINIFY_STATES + else "reflex___state____on_load_internal_state.on_load_internal" + ) # The name of the internal event to update generic state vars. UPDATE_VARS_INTERNAL = ( - "reflex___state____update_vars_internal_state.update_vars_internal" + "u" + if MINIFY_STATES + else ("reflex___state____update_vars_internal_state.update_vars_internal") ) # The name of the frontend event exception state - FRONTEND_EXCEPTION_STATE = "reflex___state____frontend_event_exception_state" + FRONTEND_EXCEPTION_STATE = ( + "e" if MINIFY_STATES else "reflex___state____frontend_event_exception_state" + ) # The full name of the frontend exception state FRONTEND_EXCEPTION_STATE_FULL = ( f"reflex___state____state.{FRONTEND_EXCEPTION_STATE}" ) + INTERNAL_STATE_NAMES = { + ON_LOAD_INTERNAL, + UPDATE_VARS_INTERNAL, + FRONTEND_EXCEPTION_STATE_FULL, + } class PageNames(SimpleNamespace): diff --git a/reflex/state.py b/reflex/state.py index a53df7b6f..783d007f5 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -285,6 +285,61 @@ def get_var_for_field(cls: Type[BaseState], f: ModelField): ) +# Keep track of all state instances to calculate minified state names +state_count = 0 + +all_state_names: Set[str] = set() + + +def next_minified_state_name() -> str: + """Get the next minified state name. + + Returns: + The next minified state name. + + Raises: + RuntimeError: If the minified state name already exists. + """ + global state_count + global all_state_names + num = state_count + + # All possible chars for minified state name + chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_" + base = len(chars) + state_name = "" + + if num == 0: + state_name = chars[0] + + while num > 0: + state_name = chars[num % base] + state_name + num = num // base + + state_count += 1 + + if state_name in all_state_names: + raise RuntimeError(f"Minified state name {state_name} already exists") + all_state_names.add(state_name) + + return state_name + + +def generate_state_name() -> str: + """Generate a minified state name. + + Returns: + The minified state name. + + Raises: + ValueError: If no more minified state names are available + """ + while name := next_minified_state_name(): + if name not in constants.CompileVars.INTERNAL_STATE_NAMES: + return name + raise ValueError("No more minified state names available") + + class BaseState(Base, ABC, extra=pydantic.Extra.allow): """The state of the app.""" @@ -354,6 +409,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): # A special event handler for setting base vars. setvar: ClassVar[EventHandler] + # Minified state name + _state_name: ClassVar[Optional[str]] = None + def __init__( self, parent_state: BaseState | None = None, @@ -459,6 +517,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): if "" in cls.__qualname__: cls._handle_local_def() + # Generate a minified state name by converting state count to string + if not cls._state_name: + cls._state_name = generate_state_name() + # Validate the module name. cls._validate_module_name() @@ -874,7 +936,16 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): Returns: The name of the state. + + Raises: + RuntimeError: If the state name is not set. """ + if constants.CompileVars.MINIFY_STATES: + if not cls._state_name: + raise RuntimeError( + "State name minification is enabled, but state name is not set." + ) + return cls._state_name module = cls.__module__.replace(".", "___") return format.to_snake_case(f"{module}___{cls.__name__}") @@ -2218,6 +2289,8 @@ def dynamic(func: Callable[[T], Component]): class FrontendEventExceptionState(State): """Substate for handling frontend exceptions.""" + _state_name: Optional[str] = constants.CompileVars.FRONTEND_EXCEPTION_STATE + @event def handle_frontend_exception(self, stack: str, component_stack: str) -> None: """Handle frontend exceptions. @@ -2237,6 +2310,8 @@ class FrontendEventExceptionState(State): class UpdateVarsInternalState(State): """Substate for handling internal state var updates.""" + _state_name: Optional[str] = constants.CompileVars.UPDATE_VARS_INTERNAL + async def update_vars_internal(self, vars: dict[str, Any]) -> None: """Apply updates to fully qualified state vars. @@ -2262,6 +2337,8 @@ class OnLoadInternalState(State): This is a separate substate to avoid deserializing the entire state tree for every page navigation. """ + _state_name: Optional[str] = constants.CompileVars.ON_LOAD_INTERNAL + def on_load_internal(self) -> list[Event | EventSpec] | None: """Queue on_load handlers for the current page.