瀏覽代碼

wip: more dynamic jinja contexts, tests for minification

Benedikt Bartscher 10 月之前
父節點
當前提交
dadfb5663a

+ 2 - 2
reflex/.templates/web/utils/state.js

@@ -117,7 +117,7 @@ export const isStateful = () => {
   if (event_queue.length === 0) {
     return false;
   }
-  return event_queue.some((event) => event.name.startsWith("reflex___state"));
+  return event_queue.some(event => event.name.includes("___"));
 };
 
 /**
@@ -810,7 +810,7 @@ export const useEventLoop = (
         const vars = {};
         vars[storage_to_state_map[e.key]] = e.newValue;
         const event = Event(
-          `${state_name}.reflex___state____update_vars_internal_state.update_vars_internal`,
+          `${state_name}.{{ update_vars_internal }}`,
           { vars: vars }
         );
         addEvents([event], e);

+ 11 - 11
reflex/compiler/compiler.py

@@ -34,7 +34,7 @@ def _compile_document_root(root: Component) -> str:
     Returns:
         The compiled document root.
     """
-    return templates.DOCUMENT_ROOT.render(
+    return templates.document_root().render(
         imports=utils.compile_imports(root._get_all_imports()),
         document=root.render(),
     )
@@ -72,7 +72,7 @@ def _compile_app(app_root: Component) -> str:
         ("utils_state", f"$/{constants.Dirs.UTILS}/state"),
     ]
 
-    return templates.APP_ROOT.render(
+    return templates.app_root().render(
         imports=utils.compile_imports(app_root._get_all_imports()),
         custom_codes=app_root._get_all_custom_code(),
         hooks={**app_root._get_all_hooks_internal(), **app_root._get_all_hooks()},
@@ -90,7 +90,7 @@ def _compile_theme(theme: str) -> str:
     Returns:
         The compiled theme.
     """
-    return templates.THEME.render(theme=theme)
+    return templates.theme().render(theme=theme)
 
 
 def _compile_contexts(state: Optional[Type[BaseState]], theme: Component | None) -> str:
@@ -109,7 +109,7 @@ def _compile_contexts(state: Optional[Type[BaseState]], theme: Component | None)
 
     last_compiled_time = str(datetime.now())
     return (
-        templates.CONTEXT.render(
+        templates.context().render(
             initial_state=utils.compile_state(state),
             state_name=state.get_name(),
             client_storage=utils.compile_client_storage(state),
@@ -118,7 +118,7 @@ def _compile_contexts(state: Optional[Type[BaseState]], theme: Component | None)
             default_color_mode=appearance,
         )
         if state
-        else templates.CONTEXT.render(
+        else templates.context().render(
             is_dev_mode=not is_prod_mode(),
             default_color_mode=appearance,
             last_compiled_time=last_compiled_time,
@@ -145,7 +145,7 @@ def _compile_page(
     # Compile the code to render the component.
     kwargs = {"state_name": state.get_name()} if state is not None else {}
 
-    return templates.PAGE.render(
+    return templates.page().render(
         imports=imports,
         dynamic_imports=component._get_all_dynamic_imports(),
         custom_codes=component._get_all_custom_code(),
@@ -201,7 +201,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
                 )
             stylesheet = f"../{constants.Dirs.PUBLIC}/{stylesheet.strip('/')}"
         sheets.append(stylesheet) if stylesheet not in sheets else None
-    return templates.STYLE.render(stylesheets=sheets)
+    return templates.style().render(stylesheets=sheets)
 
 
 def _compile_component(component: Component | StatefulComponent) -> str:
@@ -213,7 +213,7 @@ def _compile_component(component: Component | StatefulComponent) -> str:
     Returns:
         The compiled component.
     """
-    return templates.COMPONENT.render(component=component)
+    return templates.component().render(component=component)
 
 
 def _compile_components(
@@ -241,7 +241,7 @@ def _compile_components(
 
     # Compile the components page.
     return (
-        templates.COMPONENTS.render(
+        templates.components().render(
             imports=utils.compile_imports(imports),
             components=component_renders,
         ),
@@ -319,7 +319,7 @@ def _compile_stateful_components(
         f"$/{constants.Dirs.UTILS}/{constants.PageNames.STATEFUL_COMPONENTS}", None
     )
 
-    return templates.STATEFUL_COMPONENTS.render(
+    return templates.stateful_components().render(
         imports=utils.compile_imports(all_imports),
         memoized_code="\n".join(rendered_components),
     )
@@ -336,7 +336,7 @@ def _compile_tailwind(
     Returns:
         The compiled Tailwind config.
     """
-    return templates.TAILWIND_CONFIG.render(
+    return templates.tailwind_config().render(
         **config,
     )
 

+ 160 - 43
reflex/compiler/templates.py

@@ -11,6 +11,12 @@ class ReflexJinjaEnvironment(Environment):
 
     def __init__(self) -> None:
         """Set default environment."""
+        from reflex.state import (
+            FrontendEventExceptionState,
+            OnLoadInternalState,
+            UpdateVarsInternalState,
+        )
+
         extensions = ["jinja2.ext.debug"]
         super().__init__(
             extensions=extensions,
@@ -42,9 +48,9 @@ class ReflexJinjaEnvironment(Environment):
             "set_color_mode": constants.ColorMode.SET,
             "use_color_mode": constants.ColorMode.USE,
             "hydrate": constants.CompileVars.HYDRATE,
-            "on_load_internal": constants.CompileVars.ON_LOAD_INTERNAL,
-            "update_vars_internal": constants.CompileVars.UPDATE_VARS_INTERNAL,
-            "frontend_exception_state": constants.CompileVars.FRONTEND_EXCEPTION_STATE_FULL,
+            "on_load_internal": f"{OnLoadInternalState.get_name()}.on_load_internal",
+            "update_vars_internal": f"{UpdateVarsInternalState.get_name()}.update_vars_internal",
+            "frontend_exception_state": FrontendEventExceptionState.get_full_name(),
         }
 
 
@@ -60,61 +66,172 @@ def get_template(name: str) -> Template:
     return ReflexJinjaEnvironment().get_template(name=name)
 
 
-# Template for the Reflex config file.
-RXCONFIG = get_template("app/rxconfig.py.jinja2")
+def rxconfig():
+    """Template for the Reflex config file.
+
+    Returns:
+        Template: The template for the Reflex config file.
+    """
+    return get_template("app/rxconfig.py.jinja2")
+
+
+def document_root():
+    """Code to render a NextJS Document root.
+
+    Returns:
+        Template: The template for the NextJS Document root.
+    """
+    return get_template("web/pages/_document.js.jinja2")
 
-# Code to render a NextJS Document root.
-DOCUMENT_ROOT = get_template("web/pages/_document.js.jinja2")
 
-# Code to render NextJS App root.
-APP_ROOT = get_template("web/pages/_app.js.jinja2")
+def app_root():
+    """Code to render NextJS App root.
 
-# Template for the theme file.
-THEME = get_template("web/utils/theme.js.jinja2")
+    Returns:
+        Template: The template for the NextJS App root.
+    """
+    return get_template("web/pages/_app.js.jinja2")
 
-# Template for the context file.
-CONTEXT = get_template("web/utils/context.js.jinja2")
 
-# Template for Tailwind config.
-TAILWIND_CONFIG = get_template("web/tailwind.config.js.jinja2")
+def theme():
+    """Template for the theme file.
 
-# Template to render a component tag.
-COMPONENT = get_template("web/pages/component.js.jinja2")
+    Returns:
+        Template: The template for the theme file.
+    """
+    return get_template("web/utils/theme.js.jinja2")
 
-# Code to render a single NextJS page.
-PAGE = get_template("web/pages/index.js.jinja2")
 
-# Code to render the custom components page.
-COMPONENTS = get_template("web/pages/custom_component.js.jinja2")
+def context():
+    """Template for the context file.
 
-# Code to render Component instances as part of StatefulComponent
-STATEFUL_COMPONENT = get_template("web/pages/stateful_component.js.jinja2")
+    Returns:
+        Template: The template for the context file.
+    """
+    return get_template("web/utils/context.js.jinja2")
 
-# Code to render StatefulComponent to an external file to be shared
-STATEFUL_COMPONENTS = get_template("web/pages/stateful_components.js.jinja2")
 
-# Sitemap config file.
-SITEMAP_CONFIG = "module.exports = {config}".format
+def tailwind_config():
+    """Template for Tailwind config.
 
-# Code to render the root stylesheet.
-STYLE = get_template("web/styles/styles.css.jinja2")
+    Returns:
+        Template: The template for the Tailwind config
+    """
+    return get_template("web/tailwind.config.js.jinja2")
 
-# Code that generate the package json file
-PACKAGE_JSON = get_template("web/package.json.jinja2")
 
-# Code that generate the pyproject.toml file for custom components.
-CUSTOM_COMPONENTS_PYPROJECT_TOML = get_template(
-    "custom_components/pyproject.toml.jinja2"
-)
+def component():
+    """Template to render a component tag.
 
-# Code that generates the README file for custom components.
-CUSTOM_COMPONENTS_README = get_template("custom_components/README.md.jinja2")
+    Returns:
+        Template: The template for the component tag.
+    """
+    return get_template("web/pages/component.js.jinja2")
 
-# Code that generates the source file for custom components.
-CUSTOM_COMPONENTS_SOURCE = get_template("custom_components/src.py.jinja2")
 
-# Code that generates the init file for custom components.
-CUSTOM_COMPONENTS_INIT_FILE = get_template("custom_components/__init__.py.jinja2")
+def page():
+    """Code to render a single NextJS page.
+
+    Returns:
+        Template: The template for the NextJS page.
+    """
+    return get_template("web/pages/index.js.jinja2")
+
+
+def components():
+    """Code to render the custom components page.
+
+    Returns:
+        Template: The template for the custom components page.
+    """
+    return get_template("web/pages/custom_component.js.jinja2")
+
+
+def stateful_component():
+    """Code to render Component instances as part of StatefulComponent.
+
+    Returns:
+        Template: The template for the StatefulComponent.
+    """
+    return get_template("web/pages/stateful_component.js.jinja2")
+
+
+def stateful_components():
+    """Code to render StatefulComponent to an external file to be shared.
+
+    Returns:
+        Template: The template for the StatefulComponent.
+    """
+    return get_template("web/pages/stateful_components.js.jinja2")
+
 
-# Code that generates the demo app main py file for testing custom components.
-CUSTOM_COMPONENTS_DEMO_APP = get_template("custom_components/demo_app.py.jinja2")
+def sitemap_config():
+    """Sitemap config file.
+
+    Returns:
+        Template: The template for the sitemap config file.
+    """
+    return "module.exports = {config}".format
+
+
+def style():
+    """Code to render the root stylesheet.
+
+    Returns:
+        Template: The template for the root stylesheet
+    """
+    return get_template("web/styles/styles.css.jinja2")
+
+
+def package_json():
+    """Code that generate the package json file.
+
+    Returns:
+        Template: The template for the package json file
+    """
+    return get_template("web/package.json.jinja2")
+
+
+def custom_components_pyproject_toml():
+    """Code that generate the pyproject.toml file for custom components.
+
+    Returns:
+        Template: The template for the pyproject.toml file
+    """
+    return get_template("custom_components/pyproject.toml.jinja2")
+
+
+def custom_components_readme():
+    """Code that generates the README file for custom components.
+
+    Returns:
+        Template: The template for the README file
+    """
+    return get_template("custom_components/README.md.jinja2")
+
+
+def custom_components_source():
+    """Code that generates the source file for custom components.
+
+    Returns:
+        Template: The template for the source file
+    """
+    return get_template("custom_components/src.py.jinja2")
+
+
+def custom_components_init():
+    """Code that generates the init file for custom components.
+
+    Returns:
+        Template: The template for the init file
+    """
+    return get_template("custom_components/__init__.py.jinja2")
+
+
+def custom_components_demo_app():
+    """Code that generates the demo app main py file for testing custom components.
+
+    Returns:
+        Template: The template for the demo app main py file
+    """
+    return get_template("custom_components/demo_app.py.jinja2")

+ 2 - 2
reflex/components/component.py

@@ -24,7 +24,7 @@ from typing import (
 
 import reflex.state
 from reflex.base import Base
-from reflex.compiler.templates import STATEFUL_COMPONENT
+from reflex.compiler.templates import stateful_component
 from reflex.components.core.breakpoints import Breakpoints
 from reflex.components.dynamic import load_dynamic_serializer
 from reflex.components.tags import Tag
@@ -2134,7 +2134,7 @@ class StatefulComponent(BaseComponent):
                 component.event_triggers[event_trigger] = memo_trigger
 
         # Render the code for this component and hooks.
-        return STATEFUL_COMPONENT.render(
+        return stateful_component().render(
             tag_name=tag_name,
             memo_trigger_hooks=memo_trigger_hooks,
             component=component,

+ 9 - 5
reflex/components/dynamic.py

@@ -80,7 +80,7 @@ def load_dynamic_serializer():
         )
 
         rendered_components[
-            templates.STATEFUL_COMPONENT.render(
+            templates.stateful_component().render(
                 tag_name="MySSRComponent",
                 memo_trigger_hooks=[],
                 component=component,
@@ -101,10 +101,14 @@ def load_dynamic_serializer():
             else:
                 imports[lib] = names
 
-        module_code_lines = templates.STATEFUL_COMPONENTS.render(
-            imports=utils.compile_imports(imports),
-            memoized_code="\n".join(rendered_components),
-        ).splitlines()[1:]
+        module_code_lines = (
+            templates.stateful_components()
+            .render(
+                imports=utils.compile_imports(imports),
+                memoized_code="\n".join(rendered_components),
+            )
+            .splitlines()[1:]
+        )
 
         # Rewrite imports from `/` to destructure from window
         for ix, line in enumerate(module_code_lines[:]):

+ 3 - 0
reflex/config.py

@@ -545,6 +545,9 @@ class EnvironmentVariables:
     # Where to save screenshots when tests fail.
     SCREENSHOT_DIR: EnvVar[Optional[Path]] = env_var(None)
 
+    # Whether to minify state names.
+    REFLEX_MINIFY_STATES: EnvVar[Optional[bool]] = env_var(False)
+
 
 environment = EnvironmentVariables()
 

+ 14 - 32
reflex/constants/compiler.py

@@ -6,7 +6,7 @@ from enum import Enum
 from types import SimpleNamespace
 
 from reflex.base import Base
-from reflex.constants import ENV_MODE_ENV_VAR, Dirs, Env
+from reflex.constants import Dirs, Env
 from reflex.utils.imports import ImportVar
 
 # The prefix used to create setters for state vars.
@@ -40,12 +40,14 @@ def minify_states() -> bool:
     Returns:
         True if states should be minified.
     """
-    env = os.environ.get(ENV_MINIFY_STATES, None)
+    from reflex.config import environment
+
+    env = environment.REFLEX_MINIFY_STATES.get()
     if env is not None:
-        return env.lower() == "true"
+        return env
 
     # minify states in prod by default
-    return os.environ.get(ENV_MODE_ENV_VAR, "") == Env.PROD.value
+    return environment.REFLEX_ENV_MODE.get() == Env.PROD
 
 
 class CompileVars(SimpleNamespace):
@@ -80,34 +82,14 @@ class CompileVars(SimpleNamespace):
     # The name of the function for converting a dict to an event.
     TO_EVENT = "Event"
 
-    # Whether to minify states.
-    MINIFY_STATES = minify_states()
-
-    # The name of the OnLoadInternal state.
-    ON_LOAD_INTERNAL_STATE = (
-        "l" if MINIFY_STATES else "reflex___state____on_load_internal_state"
-    )
-    # The name of the internal on_load event.
-    ON_LOAD_INTERNAL = f"{ON_LOAD_INTERNAL_STATE}.on_load_internal"
-    # The name of the UpdateVarsInternal state.
-    UPDATE_VARS_INTERNAL_STATE = (
-        "u" if MINIFY_STATES else "reflex___state____update_vars_internal_state"
-    )
-    # The name of the internal event to update generic state vars.
-    UPDATE_VARS_INTERNAL = f"{UPDATE_VARS_INTERNAL_STATE}.update_vars_internal"
-    # The name of the 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_STATE,
-        UPDATE_VARS_INTERNAL_STATE,
-        FRONTEND_EXCEPTION_STATE,
-    }
+    @classmethod
+    def MINIFY_STATES(cls) -> bool:
+        """Whether to minify states.
+
+        Returns:
+            True if states should be minified.
+        """
+        return minify_states()
 
 
 class PageNames(SimpleNamespace):

+ 5 - 5
reflex/custom_components/custom_components.py

@@ -65,7 +65,7 @@ def _create_package_config(module_name: str, package_name: str):
 
     pyproject = Path(CustomComponents.PYPROJECT_TOML)
     pyproject.write_text(
-        templates.CUSTOM_COMPONENTS_PYPROJECT_TOML.render(
+        templates.custom_components_pyproject_toml().render(
             module_name=module_name,
             package_name=package_name,
             reflex_version=constants.Reflex.VERSION,
@@ -106,7 +106,7 @@ def _create_readme(module_name: str, package_name: str):
 
     readme = Path(CustomComponents.PACKAGE_README)
     readme.write_text(
-        templates.CUSTOM_COMPONENTS_README.render(
+        templates.custom_components_readme().render(
             module_name=module_name,
             package_name=package_name,
         )
@@ -129,14 +129,14 @@ def _write_source_and_init_py(
 
     module_path = custom_component_src_dir / f"{module_name}.py"
     module_path.write_text(
-        templates.CUSTOM_COMPONENTS_SOURCE.render(
+        templates.custom_components_source().render(
             component_class_name=component_class_name, module_name=module_name
         )
     )
 
     init_path = custom_component_src_dir / CustomComponents.INIT_FILE
     init_path.write_text(
-        templates.CUSTOM_COMPONENTS_INIT_FILE.render(module_name=module_name)
+        templates.custom_components_init.render(module_name=module_name)
     )
 
 
@@ -164,7 +164,7 @@ def _populate_demo_app(name_variants: NameVariants):
         # This source file is rendered using jinja template file.
         with open(f"{demo_app_name}/{demo_app_name}.py", "w") as f:
             f.write(
-                templates.CUSTOM_COMPONENTS_DEMO_APP.render(
+                templates.custom_components_demo_app().render(
                     custom_component_module_dir=name_variants.custom_component_module_dir,
                     module_name=name_variants.module_name,
                 )

+ 14 - 38
reflex/state.py

@@ -288,7 +288,7 @@ def get_var_for_field(cls: Type[BaseState], f: ModelField):
 # Keep track of all state instances to calculate minified state names
 state_count: int = 0
 
-all_state_names: Set[str] = set()
+minified_state_names: Dict[str, str] = {}
 
 
 def next_minified_state_name() -> str:
@@ -296,12 +296,8 @@ def next_minified_state_name() -> str:
 
     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
@@ -318,25 +314,28 @@ def next_minified_state_name() -> str:
 
     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:
+def get_minified_state_name(state_name: str) -> str:
     """Generate a minified state name.
 
+    Args:
+        state_name: The state name to minify.
+
     Returns:
         The minified state name.
 
     Raises:
         ValueError: If no more minified state names are available
     """
+    if state_name in minified_state_names:
+        return minified_state_names[state_name]
+
     while name := next_minified_state_name():
-        if name in constants.CompileVars.INTERNAL_STATE_NAMES:
+        if name in minified_state_names.values():
             continue
+        minified_state_names[state_name] = name
         return name
     raise ValueError("No more minified state names available")
 
@@ -410,9 +409,6 @@ 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,
@@ -518,10 +514,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
         if "<locals>" in cls.__qualname__:
             cls._handle_local_def()
 
-        # Generate a minified state name by converting state count to string
-        if not cls._state_name or cls._state_name in all_state_names:
-            cls._state_name = generate_state_name()
-
         # Validate the module name.
         cls._validate_module_name()
 
@@ -937,18 +929,12 @@ 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__}")
+        state_name = format.to_snake_case(f"{module}___{cls.__name__}")
+        if constants.compiler.CompileVars.MINIFY_STATES():
+            return get_minified_state_name(state_name)
+        return state_name
 
     @classmethod
     @functools.lru_cache()
@@ -2290,10 +2276,6 @@ def dynamic(func: Callable[[T], Component]):
 class FrontendEventExceptionState(State):
     """Substate for handling frontend exceptions."""
 
-    _state_name: ClassVar[Optional[str]] = (
-        constants.CompileVars.FRONTEND_EXCEPTION_STATE
-    )
-
     @event
     def handle_frontend_exception(self, stack: str, component_stack: str) -> None:
         """Handle frontend exceptions.
@@ -2313,10 +2295,6 @@ class FrontendEventExceptionState(State):
 class UpdateVarsInternalState(State):
     """Substate for handling internal state var updates."""
 
-    _state_name: ClassVar[Optional[str]] = (
-        constants.CompileVars.UPDATE_VARS_INTERNAL_STATE
-    )
-
     async def update_vars_internal(self, vars: dict[str, Any]) -> None:
         """Apply updates to fully qualified state vars.
 
@@ -2342,8 +2320,6 @@ class OnLoadInternalState(State):
     This is a separate substate to avoid deserializing the entire state tree for every page navigation.
     """
 
-    _state_name: ClassVar[Optional[str]] = constants.CompileVars.ON_LOAD_INTERNAL_STATE
-
     def on_load_internal(self) -> list[Event | EventSpec] | None:
         """Queue on_load handlers for the current page.
 

+ 32 - 6
reflex/testing.py

@@ -46,12 +46,14 @@ import reflex.utils.processes
 from reflex.config import environment
 from reflex.state import (
     BaseState,
+    State,
     StateManager,
     StateManagerDisk,
     StateManagerMemory,
     StateManagerRedis,
     reload_state_module,
 )
+from reflex.utils.types import override
 
 try:
     from selenium import webdriver  # pyright: ignore [reportMissingImports]
@@ -141,7 +143,7 @@ class AppHarness:
             types.FunctionType | types.ModuleType | str | functools.partial[Any]
         ] = None,
         app_name: Optional[str] = None,
-    ) -> "AppHarness":
+    ) -> AppHarness:
         """Create an AppHarness instance at root.
 
         Args:
@@ -191,7 +193,14 @@ class AppHarness:
 
         Returns:
             The state name
+
+        Raises:
+            NotImplementedError: when minified state names are enabled
         """
+        if reflex.constants.CompileVars.MINIFY_STATES():
+            raise NotImplementedError(
+                "This API is not available with minified state names."
+            )
         return reflex.utils.format.to_snake_case(
             f"{self.app_name}___{self.app_name}___" + state_cls_name
         )
@@ -207,7 +216,7 @@ class AppHarness:
         """
         # NOTE: using State.get_name() somehow causes trouble here
         # path = [State.get_name()] + [self.get_state_name(p) for p in path]
-        path = ["reflex___state____state"] + [self.get_state_name(p) for p in path]
+        path = [State.get_name()] + [self.get_state_name(p) for p in path]
         return ".".join(path)
 
     def _get_globals_from_signature(self, func: Any) -> dict[str, Any]:
@@ -412,7 +421,7 @@ class AppHarness:
         self.frontend_output_thread = threading.Thread(target=consume_frontend_output)
         self.frontend_output_thread.start()
 
-    def start(self) -> "AppHarness":
+    def start(self) -> AppHarness:
         """Start the backend in a new thread and dev frontend as a separate process.
 
         Returns:
@@ -442,7 +451,7 @@ class AppHarness:
             return f"{key} = {value!r}"
         return inspect.getsource(value)
 
-    def __enter__(self) -> "AppHarness":
+    def __enter__(self) -> AppHarness:
         """Contextmanager protocol for `start()`.
 
         Returns:
@@ -921,6 +930,7 @@ class AppHarnessProd(AppHarness):
             )
             self.frontend_server.serve_forever()
 
+    @override
     def _start_frontend(self):
         # Set up the frontend.
         with chdir(self.app_path):
@@ -932,17 +942,19 @@ class AppHarnessProd(AppHarness):
                 zipping=False,
                 frontend=True,
                 backend=False,
-                loglevel=reflex.constants.LogLevel.INFO,
+                loglevel=reflex.constants.base.LogLevel.INFO,
             )
 
         self.frontend_thread = threading.Thread(target=self._run_frontend)
         self.frontend_thread.start()
 
+    @override
     def _wait_frontend(self):
-        self._poll_for(lambda: self.frontend_server is not None)
+        _ = self._poll_for(lambda: self.frontend_server is not None)
         if self.frontend_server is None or not self.frontend_server.socket.fileno():
             raise RuntimeError("Frontend did not start")
 
+    @override
     def _start_backend(self):
         if self.app_instance is None:
             raise RuntimeError("App was not initialized.")
@@ -959,12 +971,25 @@ class AppHarnessProd(AppHarness):
         self.backend_thread = threading.Thread(target=self.backend.run)
         self.backend_thread.start()
 
+    @override
     def _poll_for_servers(self, timeout: TimeoutType = None) -> socket.socket:
         try:
             return super()._poll_for_servers(timeout)
         finally:
             environment.REFLEX_SKIP_COMPILE.set(None)
 
+    @override
+    def start(self) -> AppHarnessProd:
+        """Start AppHarnessProd instance.
+
+        Returns:
+            self
+        """
+        environment.REFLEX_ENV_MODE.set(reflex.constants.base.Env.PROD)
+        _ = super().start()
+        return self
+
+    @override
     def stop(self):
         """Stop the frontend python webserver."""
         super().stop()
@@ -972,3 +997,4 @@ class AppHarnessProd(AppHarness):
             self.frontend_server.shutdown()
         if self.frontend_thread is not None:
             self.frontend_thread.join()
+        environment.REFLEX_ENV_MODE.set(None)

+ 1 - 1
reflex/utils/build.py

@@ -44,7 +44,7 @@ def generate_sitemap_config(deploy_url: str, export=False):
     config = json.dumps(config)
 
     sitemap = prerequisites.get_web_dir() / constants.Next.SITEMAP_CONFIG_FILE
-    sitemap.write_text(templates.SITEMAP_CONFIG(config=config))
+    sitemap.write_text(templates.sitemap_config()(config=config))
 
 
 def _zip(

+ 2 - 2
reflex/utils/prerequisites.py

@@ -436,7 +436,7 @@ def create_config(app_name: str):
     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}")
-        f.write(templates.RXCONFIG.render(app_name=app_name, config_name=config_name))
+        f.write(templates.rxconfig().render(app_name=app_name, config_name=config_name))
 
 
 def initialize_gitignore(
@@ -604,7 +604,7 @@ def initialize_web_directory():
 
 
 def _compile_package_json():
-    return templates.PACKAGE_JSON.render(
+    return templates.package_json().render(
         scripts={
             "dev": constants.PackageJson.Commands.DEV,
             "export": constants.PackageJson.Commands.EXPORT,

+ 21 - 4
tests/integration/conftest.py

@@ -3,9 +3,11 @@
 import os
 import re
 from pathlib import Path
+from typing import Generator, Type
 
 import pytest
 
+import reflex.constants
 from reflex.config import environment
 from reflex.testing import AppHarness, AppHarnessProd
 
@@ -64,15 +66,30 @@ def pytest_exception_interact(node, call, report):
 
 
 @pytest.fixture(
-    scope="session", params=[AppHarness, AppHarnessProd], ids=["dev", "prod"]
+    scope="session",
+    params=[
+        AppHarness,
+        AppHarnessProd,
+    ],
+    ids=[
+        reflex.constants.Env.DEV.value,
+        reflex.constants.Env.PROD.value,
+    ],
 )
-def app_harness_env(request):
+def app_harness_env(
+    request: pytest.FixtureRequest,
+) -> Generator[Type[AppHarness], None, None]:
     """Parametrize the AppHarness class to use for the test, either dev or prod.
 
     Args:
         request: The pytest fixture request object.
 
-    Returns:
+    Yields:
         The AppHarness class to use for the test.
     """
-    return request.param
+    harness: Type[AppHarness] = request.param
+    if issubclass(harness, AppHarnessProd):
+        environment.REFLEX_ENV_MODE.set(reflex.constants.Env.PROD)
+    yield harness
+    if issubclass(harness, AppHarnessProd):
+        environment.REFLEX_ENV_MODE.set(None)

+ 0 - 1
tests/integration/test_computed_vars.py

@@ -106,7 +106,6 @@ def ComputedVars():
             ),
         )
 
-    # raise Exception(State.count3._deps(objclass=State))
     app = rx.App()
     app.add_page(index)
 

+ 87 - 13
tests/integration/test_minified_states.py

@@ -2,20 +2,24 @@
 
 from __future__ import annotations
 
-import time
-from typing import Generator, Type
+import os
+from functools import partial
+from typing import Generator, Optional, Type
 
 import pytest
 from selenium.webdriver.common.by import By
 from selenium.webdriver.remote.webdriver import WebDriver
-from selenium.webdriver.support import expected_conditions as EC
-from selenium.webdriver.support.ui import WebDriverWait
 
-from reflex.testing import AppHarness
+from reflex.constants.compiler import ENV_MINIFY_STATES
+from reflex.testing import AppHarness, AppHarnessProd
 
 
-def TestApp():
-    """A test app for minified state names."""
+def TestApp(minify: bool | None) -> None:
+    """A test app for minified state names.
+
+    Args:
+        minify: whether to minify state names
+    """
     import reflex as rx
 
     class TestAppState(rx.State):
@@ -25,7 +29,6 @@ def TestApp():
 
     app = rx.App()
 
-    @app.add_page
     def index():
         return rx.vstack(
             rx.input(
@@ -33,27 +36,64 @@ def TestApp():
                 is_read_only=True,
                 id="token",
             ),
+            rx.text(f"minify: {minify}", id="minify"),
+            rx.text(TestAppState.get_name(), id="state_name"),
+            rx.text(TestAppState.get_full_name(), id="state_full_name"),
         )
 
+    app.add_page(index)
+
+
+@pytest.fixture(
+    params=[
+        pytest.param(False),
+        pytest.param(True),
+        pytest.param(None),
+    ],
+)
+def minify_state_env(
+    request: pytest.FixtureRequest,
+) -> Generator[Optional[bool], None, None]:
+    """Set the environment variable to minify state names.
+
+    Args:
+        request: pytest fixture request
+
+    Yields:
+        minify_states: whether to minify state names
+    """
+    minify_states: Optional[bool] = request.param
+    if minify_states is None:
+        _ = os.environ.pop(ENV_MINIFY_STATES, None)
+    else:
+        os.environ[ENV_MINIFY_STATES] = str(minify_states).lower()
+    yield minify_states
+    if minify_states is not None:
+        os.environ.pop(ENV_MINIFY_STATES, None)
+
 
-@pytest.fixture(scope="module")
+@pytest.fixture
 def test_app(
-    app_harness_env: Type[AppHarness], tmp_path_factory: pytest.TempPathFactory
+    app_harness_env: Type[AppHarness],
+    tmp_path_factory: pytest.TempPathFactory,
+    minify_state_env: Optional[bool],
 ) -> Generator[AppHarness, None, None]:
     """Start TestApp app at tmp_path via AppHarness.
 
     Args:
         app_harness_env: either AppHarness (dev) or AppHarnessProd (prod)
         tmp_path_factory: pytest tmp_path_factory fixture
+        minify_state_env: need to request this fixture to set env before the app starts
 
     Yields:
         running AppHarness instance
 
     """
+    name = f"testapp_{app_harness_env.__name__.lower()}"
     with app_harness_env.create(
-        root=tmp_path_factory.mktemp("test_app"),
-        app_name=f"testapp_{app_harness_env.__name__.lower()}",
-        app_source=TestApp,  # type: ignore
+        root=tmp_path_factory.mktemp(name),
+        app_name=name,
+        app_source=partial(TestApp, minify=minify_state_env),  # pyright: ignore[reportArgumentType]
     ) as harness:
         yield harness
 
@@ -80,16 +120,33 @@ def driver(test_app: AppHarness) -> Generator[WebDriver, None, None]:
 def test_minified_states(
     test_app: AppHarness,
     driver: WebDriver,
+    minify_state_env: Optional[bool],
 ) -> None:
     """Test minified state names.
 
     Args:
         test_app: harness for TestApp
         driver: WebDriver instance.
+        minify_state_env: whether state minification is enabled by env var.
 
     """
     assert test_app.app_instance is not None, "app is not running"
 
+    is_prod = isinstance(test_app, AppHarnessProd)
+
+    # default to minifying in production
+    should_minify: bool = is_prod
+
+    # env overrides default
+    if minify_state_env is not None:
+        should_minify = minify_state_env
+
+    # TODO: reload internal states, or refactor VarData to reference state object instead of name
+    if should_minify:
+        pytest.skip(
+            "minify tests are currently not working, because _var_set_states writes the state names during import time"
+        )
+
     # get a reference to the connected client
     token_input = driver.find_element(By.ID, "token")
     assert token_input
@@ -97,3 +154,20 @@ def test_minified_states(
     # wait for the backend connection to send the token
     token = test_app.poll_for_value(token_input)
     assert token
+
+    state_name_text = driver.find_element(By.ID, "state_name")
+    assert state_name_text
+    state_name = state_name_text.text
+
+    state_full_name_text = driver.find_element(By.ID, "state_full_name")
+    assert state_full_name_text
+    _ = state_full_name_text.text
+
+    assert test_app.app_module
+    module_state_prefix = test_app.app_module.__name__.replace(".", "___")
+    # prod_module_suffix = "prod" if is_prod else ""
+
+    if should_minify:
+        assert len(state_name) == 1
+    else:
+        assert state_name == f"{module_state_prefix}____test_app_state"

+ 2 - 5
tests/test_minify_state.py

@@ -1,17 +1,14 @@
 from typing import Set
 
-from reflex.state import all_state_names, next_minified_state_name
+from reflex.state import next_minified_state_name
 
 
 def test_next_minified_state_name():
     """Test that the next_minified_state_name function returns unique state names."""
-    current_state_count = len(all_state_names)
     state_names: Set[str] = set()
-    gen: int = 10000
+    gen = 10000
     for _ in range(gen):
         state_name = next_minified_state_name()
         assert state_name not in state_names
         state_names.add(state_name)
     assert len(state_names) == gen
-
-    assert len(all_state_names) == current_state_count + gen

+ 1 - 1
tests/units/test_app.py

@@ -1032,7 +1032,7 @@ async def test_dynamic_route_var_route_change_completed_on_load(
     prev_exp_val = ""
     for exp_index, exp_val in enumerate(exp_vals):
         on_load_internal = _event(
-            name=f"{state.get_full_name()}.{constants.CompileVars.ON_LOAD_INTERNAL.rpartition('.')[2]}",
+            name=f"{state.get_full_name()}.on_load_internal",
             val=exp_val,
         )
         exp_router_data = {

+ 3 - 2
tests/units/test_state.py

@@ -54,6 +54,7 @@ CI = bool(os.environ.get("CI", False))
 LOCK_EXPIRATION = 2000 if CI else 300
 LOCK_EXPIRE_SLEEP = 2.5 if CI else 0.4
 
+ON_LOAD_INTERNAL = f"{OnLoadInternalState.get_name()}.on_load_internal"
 
 formatted_router = {
     "session": {"client_token": "", "client_ip": "", "session_id": ""},
@@ -2793,7 +2794,7 @@ async def test_preprocess(app_module_mock, token, test_state, expected, mocker):
         app=app,
         event=Event(
             token=token,
-            name=f"{state.get_name()}.{CompileVars.ON_LOAD_INTERNAL}",
+            name=f"{state.get_name()}.{ON_LOAD_INTERNAL}",
             router_data={RouteVar.PATH: "/", RouteVar.ORIGIN: "/", RouteVar.QUERY: {}},
         ),
         sid="sid",
@@ -2840,7 +2841,7 @@ async def test_preprocess_multiple_load_events(app_module_mock, token, mocker):
         app=app,
         event=Event(
             token=token,
-            name=f"{state.get_full_name()}.{CompileVars.ON_LOAD_INTERNAL}",
+            name=f"{state.get_full_name()}.{ON_LOAD_INTERNAL}",
             router_data={RouteVar.PATH: "/", RouteVar.ORIGIN: "/", RouteVar.QUERY: {}},
         ),
         sid="sid",

+ 3 - 3
tests/units/utils/test_utils.py

@@ -275,7 +275,7 @@ def test_unsupported_literals(cls: type):
     ],
 )
 def test_create_config(app_name, expected_config_name, mocker):
-    """Test templates.RXCONFIG is formatted with correct app name and config class name.
+    """Test templates.rxconfig is formatted with correct app name and config class name.
 
     Args:
         app_name: App name.
@@ -283,9 +283,9 @@ def test_create_config(app_name, expected_config_name, mocker):
         mocker: Mocker object.
     """
     mocker.patch("builtins.open")
-    tmpl_mock = mocker.patch("reflex.compiler.templates.RXCONFIG")
+    tmpl_mock = mocker.patch("reflex.compiler.templates.rxconfig")
     prerequisites.create_config(app_name)
-    tmpl_mock.render.assert_called_with(
+    tmpl_mock().render.assert_called_with(
         app_name=app_name, config_name=expected_config_name
     )