Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyi_hashes.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"reflex/__init__.pyi": "8a6d2350e96659846436792a5c7b772b",
"reflex/__init__.pyi": "d7767c4fe815246a4409359da60aac25",
"reflex/components/__init__.pyi": "76ba0a12cd3a7ba5ab6341a3ae81551f",
"reflex/components/base/__init__.pyi": "e9aaf47be1e1977eacee97b880c8f7de",
"reflex/components/base/app_wrap.pyi": "1d0e224e2d4b0538b19c0a038284e9b2",
Expand Down Expand Up @@ -56,7 +56,7 @@
"reflex/components/radix/primitives/progress.pyi": "98b4add410a80a353ab503ad577169c2",
"reflex/components/radix/primitives/slider.pyi": "573837a7d8d90deaf57c911faffed254",
"reflex/components/radix/themes/__init__.pyi": "a15f9464ad99f248249ffa8e6deea4cf",
"reflex/components/radix/themes/base.pyi": "1f0740d3165100c24e6bb4792aa81571",
"reflex/components/radix/themes/base.pyi": "526db93a3f52bb00ad220f8744eba797",
"reflex/components/radix/themes/color_mode.pyi": "f7515dccd1e315dc28a3cbbe2eabe7ff",
"reflex/components/radix/themes/components/__init__.pyi": "87bb9ffff641928562da1622d2ca5993",
"reflex/components/radix/themes/components/alert_dialog.pyi": "9f19bcdb4588a7f76596d142a0ac0950",
Expand Down
1 change: 1 addition & 0 deletions reflex/.templates/jinja/app/rxconfig.py.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import reflex as rx

config = rx.Config(
app_name="{{ app_name }}",
plugins=[rx.plugins.TailwindV3Plugin()],
)
66 changes: 0 additions & 66 deletions reflex/.templates/jinja/web/tailwind.config.js.jinja2

This file was deleted.

1 change: 0 additions & 1 deletion reflex/.templates/web/postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module.exports = {
plugins: {
"postcss-import": {},
tailwindcss: {},
autoprefixer: {},
},
};
6 changes: 0 additions & 6 deletions reflex/.templates/web/styles/tailwind.css

This file was deleted.

1 change: 1 addition & 0 deletions reflex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@
"vars",
"config",
"compiler",
"plugins",
}
_SUBMOD_ATTRS: dict = _MAPPING
getattr, __dir__, __all__ = lazy_loader.attach(
Expand Down
103 changes: 78 additions & 25 deletions reflex/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from pathlib import Path
from timeit import default_timer as timer
from types import SimpleNamespace
from typing import TYPE_CHECKING, Any, BinaryIO, get_args, get_type_hints
from typing import TYPE_CHECKING, Any, BinaryIO, ParamSpec, get_args, get_type_hints

from fastapi import FastAPI
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
Expand Down Expand Up @@ -314,6 +314,9 @@ def merged_with(self, other: UnevaluatedPage) -> UnevaluatedPage:
)


P = ParamSpec("P")


@dataclasses.dataclass()
class App(MiddlewareMixin, LifespanMixin):
"""The main Reflex app that encapsulates the backend and frontend.
Expand Down Expand Up @@ -620,10 +623,12 @@ def __call__(self) -> ASGIApp:
compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit(
self._compile
)
compile_future.add_done_callback(

def callback(f: concurrent.futures.Future):
# Force background compile errors to print eagerly
lambda f: f.result()
)
return f.result()

compile_future.add_done_callback(callback)
# Wait for the compile to finish to ensure all optional endpoints are mounted.
compile_future.result()

Expand Down Expand Up @@ -1029,11 +1034,6 @@ def _get_frontend_packages(self, imports: dict[str, set[ImportVar]]):
frontend_packages = get_config().frontend_packages
_frontend_packages = []
for package in frontend_packages:
if package in (get_config().tailwind or {}).get("plugins", []):
console.warn(
f"Tailwind packages are inferred from 'plugins', remove `{package}` from `frontend_packages`"
)
continue
if package in page_imports:
console.warn(
f"React packages and their dependencies are inferred from Component.library and Component.lib_dependencies, remove `{package}` from `frontend_packages`"
Expand Down Expand Up @@ -1166,6 +1166,7 @@ def _compile(self, export: bool = False, dry_run: bool = False):

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

Expand Down Expand Up @@ -1380,10 +1381,20 @@ def memoized_toast_provider():

ExecutorSafeFunctions.STATE = self._state

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

def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
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)
Expand All @@ -1401,20 +1412,26 @@ def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
# Compile the theme.
_submit_work(compile_theme, self.style)

# Compile the Tailwind config.
if config.tailwind is not None:
config.tailwind["content"] = config.tailwind.get(
"content", constants.Tailwind.CONTENT
for plugin in config.plugins:
plugin.pre_compile(
add_save_task=_submit_work,
add_modify_task=(
lambda *args, plugin=plugin: modify_files_tasks.append(
(
plugin.__class__.__module__ + plugin.__class__.__name__,
*args,
)
)
),
)
_submit_work(compiler.compile_tailwind, config.tailwind)
else:
_submit_work(compiler.remove_tailwind_from_postcss)

# Wait for all compilation tasks to complete.
compile_results.extend(
future.result()
for future in concurrent.futures.as_completed(result_futures)
)
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)

app_root = self._app_root(app_wrappers=app_wrappers)

Expand Down Expand Up @@ -1481,9 +1498,45 @@ def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
# 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:
raise FileNotFoundError(
f"Plugin {plugin_name} is trying to modify {path} but it does not exist."
)
output_mapping[path] = modify_fn(file_content)

with console.timing("Write to Disk"):
for output_path, code in compile_results:
compiler_utils.write_page(output_path, code)
for output_path, code in output_mapping.items():
compiler_utils.write_file(output_path, code)

def _write_stateful_pages_marker(self):
"""Write list of routes that create dynamic states for the backend to use later."""
Expand Down
69 changes: 9 additions & 60 deletions reflex/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ def _validate_stylesheet(stylesheet_full_path: Path, assets_app_path: Path) -> N
)


RADIX_THEMES_STYLESHEET = "@radix-ui/themes/styles.css"


def _compile_root_stylesheet(stylesheets: list[str]) -> str:
"""Compile the root stylesheet.

Expand All @@ -235,12 +238,12 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
Raises:
FileNotFoundError: If a specified stylesheet in assets directory does not exist.
"""
# Add tailwind css if enabled.
sheets = (
[constants.Tailwind.ROOT_STYLE_PATH]
if get_config().tailwind is not None
else []
)
# Add stylesheets from plugins.
sheets = [RADIX_THEMES_STYLESHEET] + [
sheet
for plugin in get_config().plugins
for sheet in plugin.get_stylesheet_paths()
]

failed_to_import_sass = False
assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS
Expand Down Expand Up @@ -451,22 +454,6 @@ def get_shared_components_recursive(component: BaseComponent):
)


def _compile_tailwind(
config: dict,
) -> str:
"""Compile the Tailwind config.

Args:
config: The Tailwind config.

Returns:
The compiled Tailwind config.
"""
return templates.TAILWIND_CONFIG.render(
**config,
)


def compile_document_root(
head_components: list[Component],
html_lang: str | None = None,
Expand Down Expand Up @@ -613,44 +600,6 @@ def compile_stateful_components(
return output_path, code, page_components


def compile_tailwind(
config: dict,
):
"""Compile the Tailwind config.

Args:
config: The Tailwind config.

Returns:
The compiled Tailwind config.
"""
# Get the path for the output file.
output_path = str((get_web_dir() / constants.Tailwind.CONFIG).absolute())

# Compile the config.
code = _compile_tailwind(config)
return output_path, code


def remove_tailwind_from_postcss() -> tuple[str, str]:
"""If tailwind is not to be used, remove it from postcss.config.js.

Returns:
The path and code of the compiled postcss.config.js.
"""
# Get the path for the output file.
output_path = str(get_web_dir() / constants.Dirs.POSTCSS_JS)

code = [
line
for line in Path(output_path).read_text().splitlines(keepends=True)
if "tailwindcss: " not in line
]

# Compile the config.
return output_path, "".join(code)


def purge_web_pages_dir():
"""Empty out .web/pages directory."""
if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR.get():
Expand Down
Loading