Optimize dev mode compile (#1348)

This commit is contained in:
Nikhil Rao 2023-07-17 11:42:07 -07:00 committed by GitHub
parent c589af84c1
commit 7397cd795a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 90 deletions

View File

@ -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(

View File

@ -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,
):

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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)