ソースを参照

Optimize dev mode compile (#1348)

Nikhil Rao 1 年間 前
コミット
7397cd795a
6 ファイル変更63 行追加89 行削除
  1. 53 31
      reflex/app.py
  2. 3 39
      reflex/compiler/compiler.py
  3. 2 2
      reflex/compiler/utils.py
  4. 1 1
      reflex/constants.py
  5. 1 2
      reflex/reflex.py
  6. 3 14
      reflex/utils/exec.py

+ 53 - 31
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,48 +459,61 @@ class App(Base):
         # Empty the .web pages directory
         compiler.purge_web_pages_dir()
 
+        # Store the compile results.
+        compile_results = []
+
+        # Compile the pages in parallel.
+        custom_components = set()
+        thread_pool = ThreadPool()
+        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()
+        thread_pool.close()
+        thread_pool.join()
+
+        # Get the results.
+        compile_results = [result.get() for result in compile_results]
+
+        # Compile the custom components.
+        compile_results.append(compiler.compile_components(custom_components))
+
         # Compile the root document with base styles and fonts.
-        compiler.compile_document_root(self.stylesheets)
+        compile_results.append(compiler.compile_document_root(self.stylesheets))
 
         # Compile the theme.
-        compiler.compile_theme(self.style)
+        compile_results.append(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_results.append(
+            compiler.compile_tailwind(
+                dict(**config.tailwind, content=constants.TAILWIND_CONTENT)
+                if config.tailwind is not None
+                else {}
+            )
         )
 
-        # Compile the pages.
-        custom_components = set()
+        # Write the pages at the end to trigger the NextJS hot reload only once.
         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,
-                    ),
-                )
-            )
-            # Add the custom components from the page to the set.
-            custom_components |= component.get_custom_components()
+        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()
 
-        # check the results of all the threads in case an exception was raised.
-        for r in compile_results:
-            r.get()
-
-        # Compile the custom components.
-        compiler.compile_components(custom_components)
-
 
 async def process(
     app: App, event: Event, sid: str, headers: Dict, client_ip: str

+ 3 - 39
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,
 ):

+ 2 - 2
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:

+ 1 - 1
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
 

+ 1 - 2
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:

+ 3 - 14
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)