Skip to content

Commit d672991

Browse files
committed
add reflex compile cli
1 parent a24f08f commit d672991

5 files changed

Lines changed: 53 additions & 19 deletions

File tree

reflex/app.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,11 +1152,12 @@ def _validate_var_dependencies(self, state: type[BaseState] | None = None) -> No
11521152
for substate in state.class_subclasses:
11531153
self._validate_var_dependencies(substate)
11541154

1155-
def _compile(self, export: bool = False):
1155+
def _compile(self, export: bool = False, dry_run: bool = False):
11561156
"""Compile the app and output it to the pages folder.
11571157
11581158
Args:
11591159
export: Whether to compile the app for export.
1160+
dry_run: Whether to compile the app without saving it.
11601161
11611162
Raises:
11621163
ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined.
@@ -1170,7 +1171,7 @@ def get_compilation_time() -> str:
11701171

11711172
should_compile = self._should_compile()
11721173
backend_dir = prerequisites.get_backend_dir()
1173-
if not should_compile and backend_dir.exists():
1174+
if not dry_run and not should_compile and backend_dir.exists():
11741175
stateful_pages_marker = backend_dir / constants.Dirs.STATEFUL_PAGES
11751176
if stateful_pages_marker.exists():
11761177
with stateful_pages_marker.open("r") as f:
@@ -1205,7 +1206,7 @@ def get_compilation_time() -> str:
12051206
if config.react_strict_mode:
12061207
app_wrappers[(200, "StrictMode")] = StrictMode.create()
12071208

1208-
if not should_compile:
1209+
if not should_compile and not dry_run:
12091210
with console.timing("Evaluate Pages (Backend)"):
12101211
for route in self._unevaluated_pages:
12111212
console.debug(f"Evaluating page: {route}")
@@ -1358,7 +1359,7 @@ def memoized_toast_provider():
13581359

13591360
# Copy the assets.
13601361
assets_src = Path.cwd() / constants.Dirs.APP_ASSETS
1361-
if assets_src.is_dir():
1362+
if assets_src.is_dir() and not dry_run:
13621363
with console.timing("Copy assets"):
13631364
path_ops.update_directory_tree(
13641365
src=assets_src,
@@ -1444,6 +1445,9 @@ def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
14441445
progress.advance(task)
14451446
progress.stop()
14461447

1448+
if dry_run:
1449+
return
1450+
14471451
# Install frontend packages.
14481452
with console.timing("Install Frontend Packages"):
14491453
self._get_frontend_packages(all_imports)

reflex/reflex.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,30 @@ def run(
349349
)
350350

351351

352+
@cli.command()
353+
@loglevel_option
354+
@click.option(
355+
"--dry",
356+
is_flag=True,
357+
default=False,
358+
help="Run the command without making any changes.",
359+
)
360+
def compile(dry: bool):
361+
"""Compile the app in the current directory."""
362+
import time
363+
364+
from reflex.utils import prerequisites
365+
366+
# Check the app.
367+
if prerequisites.needs_reinit():
368+
_init(name=get_config().app_name)
369+
get_config(reload=True)
370+
starting_time = time.monotonic()
371+
prerequisites.compile_app(dry_run=dry)
372+
elapsed_time = time.monotonic() - starting_time
373+
console.success(f"App compiled successfully in {elapsed_time:.3f} seconds.")
374+
375+
352376
@cli.command()
353377
@loglevel_option
354378
@click.option(

reflex/utils/prerequisites.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,15 @@ def validate_app(reload: bool = False) -> None:
432432
get_and_validate_app(reload=reload)
433433

434434

435-
def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
435+
def get_compiled_app(
436+
reload: bool = False, export: bool = False, dry_run: bool = False
437+
) -> ModuleType:
436438
"""Get the app module based on the default config after first compiling it.
437439
438440
Args:
439441
reload: Re-import the app module from disk
440442
export: Compile the app for export
443+
dry_run: If True, do not write the compiled app to disk.
441444
442445
Returns:
443446
The compiled app based on the default config.
@@ -446,18 +449,21 @@ def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
446449
# For py3.9 compatibility when redis is used, we MUST add any decorator pages
447450
# before compiling the app in a thread to avoid event loop error (REF-2172).
448451
app._apply_decorated_pages()
449-
app._compile(export=export)
452+
app._compile(export=export, dry_run=dry_run)
450453
return app_module
451454

452455

453-
def compile_app(reload: bool = False, export: bool = False) -> None:
456+
def compile_app(
457+
reload: bool = False, export: bool = False, dry_run: bool = False
458+
) -> None:
454459
"""Compile the app module based on the default config.
455460
456461
Args:
457462
reload: Re-import the app module from disk
458463
export: Compile the app for export
464+
dry_run: If True, do not write the compiled app to disk.
459465
"""
460-
get_compiled_app(reload=reload, export=export)
466+
get_compiled_app(reload=reload, export=export, dry_run=dry_run)
461467

462468

463469
def _can_colorize() -> bool:

reflex/utils/types.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@
2828
from typing import get_origin as get_origin_og
2929
from typing import get_type_hints as get_type_hints_og
3030

31-
import sqlalchemy
3231
from pydantic.v1.fields import ModelField
33-
from sqlalchemy.ext.associationproxy import AssociationProxyInstance
34-
from sqlalchemy.ext.hybrid import hybrid_property
35-
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
3632
from typing_extensions import Self as Self
3733
from typing_extensions import is_typeddict
3834
from typing_extensions import override as override
@@ -322,6 +318,8 @@ def get_property_hint(attr: Any | None) -> GenericType | None:
322318
Returns:
323319
The type hint of the property, if it is a property, else None.
324320
"""
321+
from sqlalchemy.ext.hybrid import hybrid_property
322+
325323
if not isinstance(attr, (property, hybrid_property)):
326324
return None
327325
hints = get_type_hints(attr.fget)
@@ -340,6 +338,10 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
340338
Returns:
341339
The type of the attribute, if accessible, or None
342340
"""
341+
import sqlalchemy
342+
from sqlalchemy.ext.associationproxy import AssociationProxyInstance
343+
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
344+
343345
from reflex.model import Model
344346

345347
try:

reflex/vars/base.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
)
4040

4141
from rich.markup import escape
42-
from sqlalchemy.orm import DeclarativeBase
4342
from typing_extensions import deprecated, override
4443

4544
from reflex import constants
@@ -3318,18 +3317,17 @@ def dispatch(
33183317
).guess_type()
33193318

33203319

3321-
V = TypeVar("V")
3322-
3323-
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base | None)
3324-
SQLA_TYPE = TypeVar("SQLA_TYPE", bound=DeclarativeBase | None)
3325-
33263320
if TYPE_CHECKING:
33273321
from _typeshed import DataclassInstance
3322+
from sqlalchemy.orm import DeclarativeBase
33283323

3324+
SQLA_TYPE = TypeVar("SQLA_TYPE", bound=DeclarativeBase | None)
3325+
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base | None)
33293326
DATACLASS_TYPE = TypeVar("DATACLASS_TYPE", bound=DataclassInstance | None)
3327+
MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping | None)
3328+
V = TypeVar("V")
33303329

33313330
FIELD_TYPE = TypeVar("FIELD_TYPE")
3332-
MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping | None)
33333331

33343332

33353333
class Field(Generic[FIELD_TYPE]):

0 commit comments

Comments
 (0)