From 7397cd795aeebc6d9ae8e747222109e57fcf3886 Mon Sep 17 00:00:00 2001 From: Nikhil Rao Date: Mon, 17 Jul 2023 11:42:07 -0700 Subject: [PATCH] Optimize dev mode compile (#1348) --- reflex/app.py | 86 +++++++++++++++++++++++-------------- reflex/compiler/compiler.py | 42 ++---------------- reflex/compiler/utils.py | 4 +- reflex/constants.py | 2 +- reflex/reflex.py | 3 +- reflex/utils/exec.py | 17 ++------ 6 files changed, 64 insertions(+), 90 deletions(-) diff --git a/reflex/app.py b/reflex/app.py index 7ffb48a97..b980d9c7b 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -18,6 +18,7 @@ from typing import ( from fastapi import FastAPI, UploadFile from fastapi.middleware import cors +from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn from socketio import ASGIApp, AsyncNamespace, AsyncServer from starlette_admin.contrib.sqla.admin import Admin from starlette_admin.contrib.sqla.view import ModelView @@ -437,6 +438,14 @@ class App(Base): def compile(self): """Compile the app and output it to the pages folder.""" + # Create a progress bar. + progress = Progress( + *Progress.get_default_columns()[:-1], + MofNCompleteColumn(), + TimeElapsedColumn(), + ) + task = progress.add_task("Compiling: ", total=len(self.pages)) + for render, kwargs in DECORATED_ROUTES: self.add_page(render, **kwargs) @@ -450,47 +459,60 @@ class App(Base): # Empty the .web pages directory compiler.purge_web_pages_dir() - # Compile the root document with base styles and fonts. - compiler.compile_document_root(self.stylesheets) + # Store the compile results. + compile_results = [] - # Compile the theme. - compiler.compile_theme(self.style) - - # Compile the Tailwind config. - compiler.compile_tailwind( - dict(**config.tailwind, content=constants.TAILWIND_CONTENT) - if config.tailwind is not None - else {} - ) - - # Compile the pages. + # Compile the pages in parallel. custom_components = set() thread_pool = ThreadPool() - compile_results = [] - for route, component in self.pages.items(): - component.add_style(self.style) - compile_results.append( - thread_pool.apply_async( - compiler.compile_page, - args=( - route, - component, - self.state, - self.connect_error_component, - ), + with progress: + for route, component in self.pages.items(): + progress.advance(task) + component.add_style(self.style) + compile_results.append( + thread_pool.apply_async( + compiler.compile_page, + args=( + route, + component, + self.state, + self.connect_error_component, + ), + ) ) - ) - # Add the custom components from the page to the set. - custom_components |= component.get_custom_components() + # Add the custom components from the page to the set. + custom_components |= component.get_custom_components() thread_pool.close() thread_pool.join() - # check the results of all the threads in case an exception was raised. - for r in compile_results: - r.get() + # Get the results. + compile_results = [result.get() for result in compile_results] # Compile the custom components. - compiler.compile_components(custom_components) + compile_results.append(compiler.compile_components(custom_components)) + + # Compile the root document with base styles and fonts. + compile_results.append(compiler.compile_document_root(self.stylesheets)) + + # Compile the theme. + compile_results.append(compiler.compile_theme(self.style)) + + # Compile the Tailwind config. + compile_results.append( + compiler.compile_tailwind( + dict(**config.tailwind, content=constants.TAILWIND_CONTENT) + if config.tailwind is not None + else {} + ) + ) + + # Write the pages at the end to trigger the NextJS hot reload only once. + thread_pool = ThreadPool() + for output_path, code in compile_results: + compiler_utils.write_page(output_path, code) + thread_pool.apply_async(compiler_utils.write_page, args=(output_path, code)) + thread_pool.close() + thread_pool.join() async def process( diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index bd3c23444..04a4ef3a5 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -1,14 +1,12 @@ """Compiler for the reflex apps.""" from __future__ import annotations -from functools import wraps -from typing import Callable, List, Set, Tuple, Type +from typing import List, Set, Tuple, Type from reflex import constants from reflex.compiler import templates, utils -from reflex.components.component import Component, CustomComponent +from reflex.components.component import Component, ComponentStyle, CustomComponent from reflex.state import State -from reflex.style import Style from reflex.utils import imports from reflex.vars import ImportVar @@ -144,36 +142,6 @@ def _compile_tailwind( ) -def write_output(fn: Callable[..., Tuple[str, str]]): - """Write the output of the function to a file. - - Args: - fn: The function to decorate. - - Returns: - The decorated function. - """ - - @wraps(fn) - def wrapper(*args, write: bool = True) -> Tuple[str, str]: - """Write the output of the function to a file. - - Args: - *args: The arguments to pass to the function. - write: Whether to write the output to a file. - - Returns: - The path and code of the output. - """ - path, code = fn(*args) - if write: - utils.write_page(path, code) - return path, code - - return wrapper - - -@write_output def compile_document_root(stylesheets: List[str]) -> Tuple[str, str]: """Compile the document root. @@ -194,8 +162,7 @@ def compile_document_root(stylesheets: List[str]) -> Tuple[str, str]: return output_path, code -@write_output -def compile_theme(style: Style) -> Tuple[str, str]: +def compile_theme(style: ComponentStyle) -> Tuple[str, str]: """Compile the theme. Args: @@ -214,7 +181,6 @@ def compile_theme(style: Style) -> Tuple[str, str]: return output_path, code -@write_output def compile_page( path: str, component: Component, @@ -240,7 +206,6 @@ def compile_page( return output_path, code -@write_output def compile_components(components: Set[CustomComponent]): """Compile the custom components. @@ -258,7 +223,6 @@ def compile_components(components: Set[CustomComponent]): return output_path, code -@write_output def compile_tailwind( config: dict, ): diff --git a/reflex/compiler/utils.py b/reflex/compiler/utils.py index 0edba6f78..c195f053d 100644 --- a/reflex/compiler/utils.py +++ b/reflex/compiler/utils.py @@ -18,7 +18,7 @@ from reflex.components.base import ( Script, Title, ) -from reflex.components.component import Component, CustomComponent +from reflex.components.component import Component, ComponentStyle, CustomComponent from reflex.event import get_hydrate_event from reflex.state import State from reflex.style import Style @@ -188,7 +188,7 @@ def create_document_root(stylesheets: List[str]) -> Component: ) -def create_theme(style: Style) -> Dict: +def create_theme(style: ComponentStyle) -> Dict: """Create the base style for the app. Args: diff --git a/reflex/constants.py b/reflex/constants.py index 3a39ff3f0..1255e9674 100644 --- a/reflex/constants.py +++ b/reflex/constants.py @@ -190,7 +190,7 @@ 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.reflex.app" +PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app" # Token expiration time in seconds. TOKEN_EXPIRATION = 60 * 60 diff --git a/reflex/reflex.py b/reflex/reflex.py index 7a0e233f6..c8a09c4a1 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -178,8 +178,7 @@ def deploy(dry_run: bool = typer.Option(False, help="Whether to run a dry run.") # Compile the app in production mode. typer.echo("Compiling production app") - app = prerequisites.get_app().app - build.export_app(app, zip=True, deploy_url=config.deploy_url) + export(for_reflex_deploy=True) # Exit early if this is a dry run. if dry_run: diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index c3f1e6dca..e054abba6 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -5,7 +5,6 @@ from __future__ import annotations import os import platform import subprocess -from datetime import datetime from pathlib import Path from rich import print @@ -42,22 +41,12 @@ def run_process_and_launch_url( cwd=constants.WEB_DIR, ) - current_time = datetime.now() if process.stdout: for line in process.stdout: if "ready started server on" in line: url = line.split("url: ")[-1].strip() print(f"App running at: [bold green]{url}") - if ( - "Fast Refresh" in line - or "compiling..." in line - and (datetime.now() - current_time).total_seconds() > 1 - ): - current_time = datetime.now() - print( - f"[yellow][Updating App][/yellow] Applying changes and refreshing. Time: {current_time}" - ) - elif loglevel == constants.LogLevel.DEBUG: + if loglevel == constants.LogLevel.DEBUG: print(line, end="") @@ -130,8 +119,8 @@ def run_backend( "--log-level", loglevel, "--reload", - "--reload-exclude", - f"'{constants.WEB_DIR}/*'", + "--reload-dir", + app_name.split(".")[0], ] process = subprocess.Popen(cmd)