def _compile(self, export: bool = False, dry_run: bool = False):
        """Compile the app and output it to the pages folder.

        Args:
            export: Whether to compile the app for export.
            dry_run: Whether to compile the app without saving it.

        Raises:
            ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined.
            FileNotFoundError: When a plugin requires a file that does not exist.
        """
        from reflex.utils.exceptions import ReflexRuntimeError

        self._pages = {}

        def get_compilation_time() -> str:
            return str(datetime.now().time()).split(".")[0]

        should_compile = self._should_compile()
        backend_dir = prerequisites.get_backend_dir()
        if not dry_run and not should_compile and backend_dir.exists():
            stateful_pages_marker = backend_dir / constants.Dirs.STATEFUL_PAGES
            if stateful_pages_marker.exists():
                with stateful_pages_marker.open("r") as f:
                    stateful_pages = json.load(f)
                for route in stateful_pages:
                    console.debug(f"BE Evaluating stateful page: {route}")
                    self._compile_page(route, save_page=False)
                self._enable_state()
            self._add_optional_endpoints()
            return

        # Render a default 404 page if the user didn't supply one
        if constants.Page404.SLUG not in self._unevaluated_pages:
            self.add_page(route=constants.Page404.SLUG)

        # Fix up the style.
        self.style = evaluate_style_namespaces(self.style)

        # Add the app wrappers.
        app_wrappers: dict[tuple[int, str], Component] = {
            # Default app wrap component renders {children}
            (0, "AppWrap"): AppWrap.create()
        }

        if self.theme is not None:
            # If a theme component was provided, wrap the app with it
            app_wrappers[(20, "Theme")] = self.theme

        # Get the env mode.
        config = get_config()

        if config.react_strict_mode:
            app_wrappers[(200, "StrictMode")] = StrictMode.create()

        if not should_compile and not dry_run:
            with console.timing("Evaluate Pages (Backend)"):
                for route in self._unevaluated_pages:
                    console.debug(f"Evaluating page: {route}")
                    self._compile_page(route, save_page=should_compile)

            # Save the pages which created new states at eval time.
            self._write_stateful_pages_marker()

            # Add the optional endpoints (_upload)
            self._add_optional_endpoints()

            return

        # Create a progress bar.
        progress = Progress(
            *Progress.get_default_columns()[:-1],
            MofNCompleteColumn(),
            TimeElapsedColumn(),
        )

        # try to be somewhat accurate - but still not 100%
        adhoc_steps_without_executor = 7
        fixed_pages_within_executor = 4
        plugin_count = len(config.plugins)
        progress.start()
        task = progress.add_task(
            f"[{get_compilation_time()}] Compiling:",
            total=len(self._pages)
            + (len(self._unevaluated_pages) * 2)
            + fixed_pages_within_executor
            + adhoc_steps_without_executor
            + plugin_count,
        )

        with console.timing("Evaluate Pages (Frontend)"):
            performance_metrics: list[tuple[str, float]] = []
            for route in self._unevaluated_pages:
                console.debug(f"Evaluating page: {route}")
                start = timer()
                self._compile_page(route, save_page=should_compile)
                end = timer()
                performance_metrics.append((route, end - start))
                progress.advance(task)
            console.debug(
                "Slowest pages:\n"
                + "\n".join(
                    f"{route}: {time * 1000:.1f}ms"
                    for route, time in sorted(
                        performance_metrics, key=lambda x: x[1], reverse=True
                    )[:10]
                )
            )
            # Save the pages which created new states at eval time.
            self._write_stateful_pages_marker()

        # Add the optional endpoints (_upload)
        self._add_optional_endpoints()

        self._validate_var_dependencies()
        self._setup_overlay_component()

        if config.show_built_with_reflex is None:
            if (
                get_compile_context() == constants.CompileContext.DEPLOY
                and prerequisites.get_user_tier() in ["pro", "team", "enterprise"]
            ):
                config.show_built_with_reflex = False
            else:
                config.show_built_with_reflex = True

        if is_prod_mode() and config.show_built_with_reflex:
            self._setup_sticky_badge()

        progress.advance(task)

        # Store the compile results.
        compile_results: list[tuple[str, str]] = []

        progress.advance(task)

        # Track imports found.
        all_imports = {}

        if (toaster := self.toaster) is not None:
            from reflex.components.component import memo

            @memo
            def memoized_toast_provider():
                return toaster

            toast_provider = Fragment.create(memoized_toast_provider())

            app_wrappers[(1, "ToasterProvider")] = toast_provider

        # Add the app wraps to the app.
        for key, app_wrap in self.app_wraps.items():
            component = app_wrap(self._state is not None)
            if component is not None:
                app_wrappers[key] = component

        # Compile custom components.
        (
            custom_components_output,
            custom_components_result,
            custom_components_imports,
        ) = compiler.compile_components(dict.fromkeys(CUSTOM_COMPONENTS.values()))
        compile_results.append((custom_components_output, custom_components_result))
        all_imports.update(custom_components_imports)
        progress.advance(task)

        # This has to happen before compiling stateful components as that
        # prevents recursive functions from reaching all components.
        for component in self._pages.values():
            # Add component._get_all_imports() to all_imports.
            all_imports.update(component._get_all_imports())

            # Add the app wrappers from this component.
            app_wrappers.update(component._get_all_app_wrap_components())

        if self.error_boundary:
            from reflex.compiler.compiler import into_component

            console.deprecate(
                feature_name="App.error_boundary",
                reason="Use app_wraps instead.",
                deprecation_version="0.7.1",
                removal_version="0.8.0",
            )
            app_wrappers[(55, "ErrorBoundary")] = into_component(self.error_boundary)

        # Perform auto-memoization of stateful components.
        with console.timing("Auto-memoize StatefulComponents"):
            (
                stateful_components_path,
                stateful_components_code,
                page_components,
            ) = compiler.compile_stateful_components(self._pages.values())
            progress.advance(task)

        # Catch "static" apps (that do not define a rx.State subclass) which are trying to access rx.State.
        if code_uses_state_contexts(stateful_components_code) and self._state is None:
            msg = (
                "To access rx.State in frontend components, at least one "
                "subclass of rx.State must be defined in the app."
            )
            raise ReflexRuntimeError(msg)
        compile_results.append((stateful_components_path, stateful_components_code))

        progress.advance(task)

        # Compile the root document before fork.
        compile_results.append(
            compiler.compile_document_root(
                self.head_components,
                html_lang=self.html_lang,
                html_custom_attrs=(
                    {**self.html_custom_attrs} if self.html_custom_attrs else {}
                ),
            )
        )

        progress.advance(task)

        # Copy the assets.
        assets_src = Path.cwd() / constants.Dirs.APP_ASSETS
        if assets_src.is_dir() and not dry_run:
            with console.timing("Copy assets"):
                path_ops.update_directory_tree(
                    src=assets_src,
                    dest=(
                        Path.cwd() / prerequisites.get_web_dir() / constants.Dirs.PUBLIC
                    ),
                )

        executor = ExecutorType.get_executor_from_environment()

        for route, component in zip(self._pages, page_components, strict=True):
            ExecutorSafeFunctions.COMPONENTS[route] = component

        ExecutorSafeFunctions.STATE = self._state

        modify_files_tasks: list[tuple[str, str, Callable[[str], str]]] = []

        with console.timing("Compile to Javascript"), executor as executor:
            result_futures: list[
                concurrent.futures.Future[
                    list[tuple[str, str]] | tuple[str, str] | None
                ]
            ] = []

            def _submit_work(
                fn: Callable[P, list[tuple[str, str]] | tuple[str, str] | None],
                *args: P.args,
                **kwargs: P.kwargs,
            ):
                f = executor.submit(fn, *args, **kwargs)
                f.add_done_callback(lambda _: progress.advance(task))
                result_futures.append(f)

            # Compile the pre-compiled pages.
            for route in self._pages:
                _submit_work(
                    ExecutorSafeFunctions.compile_page,
                    route,
                )

            # Compile the root stylesheet with base styles.
            _submit_work(compiler.compile_root_stylesheet, self.stylesheets)

            # Compile the theme.
            _submit_work(compile_theme, self.style)

            def _submit_work_without_advancing(
                fn: Callable[P, list[tuple[str, str]] | tuple[str, str] | None],
                *args: P.args,
                **kwargs: P.kwargs,
            ):
                f = executor.submit(fn, *args, **kwargs)
                result_futures.append(f)

            for plugin in config.plugins:
                plugin.pre_compile(
                    add_save_task=_submit_work_without_advancing,
                    add_modify_task=(
                        lambda *args, plugin=plugin: modify_files_tasks.append(
                            (
                                plugin.__class__.__module__ + plugin.__class__.__name__,
                                *args,
                            )
                        )
                    ),
                )

            # Wait for all compilation tasks to complete.
            for future in concurrent.futures.as_completed(result_futures):
                if (result := future.result()) is not None:
                    if isinstance(result, list):
                        compile_results.extend(result)
                    else:
                        compile_results.append(result)

        progress.advance(task, advance=len(config.plugins))

        app_root = self._app_root(app_wrappers=app_wrappers)

        # Get imports from AppWrap components.
        all_imports.update(app_root._get_all_imports())

        progress.advance(task)

        # Compile the contexts.
        compile_results.append(
            compiler.compile_contexts(self._state, self.theme),
        )
        if self.theme is not None:
            # Fix #2992 by removing the top-level appearance prop
            self.theme.appearance = None  # pyright: ignore[reportAttributeAccessIssue]
        progress.advance(task)

        # Compile the app root.
        compile_results.append(
            compiler.compile_app(app_root),
        )
        progress.advance(task)

        progress.stop()

        if dry_run:
            return

        # Install frontend packages.
        with console.timing("Install Frontend Packages"):
            self._get_frontend_packages(all_imports)

        # Setup the next.config.js
        transpile_packages = [
            package
            for package, import_vars in all_imports.items()
            if any(import_var.transpile for import_var in import_vars)
        ]
        prerequisites.update_next_config(
            export=export,
            transpile_packages=transpile_packages,
        )

        if is_prod_mode():
            # Empty the .web pages directory.
            compiler.purge_web_pages_dir()
        else:
            # In dev mode, delete removed pages and update existing pages.
            keep_files = [Path(output_path) for output_path, _ in compile_results]
            for p in Path(prerequisites.get_web_dir() / constants.Dirs.PAGES).rglob(
                "*"
            ):
                if p.is_file() and p not in keep_files:
                    # Remove pages that are no longer in the app.
                    p.unlink()

        output_mapping: dict[Path, str] = {}
        for output_path, code in compile_results:
            path = compiler_utils.resolve_path_of_web_dir(output_path)
            if path in output_mapping:
                console.warn(
                    f"Path {path} has two different outputs. The first one will be used."
                )
            else:
                output_mapping[path] = code

        for plugin in config.plugins:
            for static_file_path, content in plugin.get_static_assets():
                path = compiler_utils.resolve_path_of_web_dir(static_file_path)
                if path in output_mapping:
                    console.warn(
                        f"Plugin {plugin.__class__.__name__} is trying to write to {path} but it already exists. The plugin file will be ignored."
                    )
                else:
                    output_mapping[path] = (
                        content.decode("utf-8")
                        if isinstance(content, bytes)
                        else content
                    )

        for plugin_name, file_path, modify_fn in modify_files_tasks:
            path = compiler_utils.resolve_path_of_web_dir(file_path)
            file_content = output_mapping.get(path)
            if file_content is None:
                if path.exists():
                    file_content = path.read_text()
                else:
                    msg = f"Plugin {plugin_name} is trying to modify {path} but it does not exist."
                    raise FileNotFoundError(msg)
            output_mapping[path] = modify_fn(file_content)

        with console.timing("Write to Disk"):
            for output_path, code in output_mapping.items():
                compiler_utils.write_file(output_path, code)
Compile the app and output it to the pages folder.

        Args:
            export: Whether to compile the app for export.
            dry_run: Whether to compile the app without saving it.

        Raises:
            ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined.
            FileNotFoundError: When a plugin requires a file that does not exist.
        
reflex/app.py