Skip to content

Commit 091c90e

Browse files
adhami3310masenf
andauthored
move tailwind to its own module (#5169)
* move tailwind to its own module * mkdir if it's missing * fix tests * add comment for config * woops * fix the test * refactor a bit of code * typos! Co-authored-by: Masen Furer <[email protected]> * i have so much typos * deprecate inferring tailwind * update base pyi * fix check for once plugin is there * empty * do things in a slightly more solid way * 0.7.13 * add v4 plugin * fix imports * fix tests * fix the tests * we don't need to repeat this guy --------- Co-authored-by: Masen Furer <[email protected]>
1 parent c3ccdf5 commit 091c90e

File tree

22 files changed

+831
-304
lines changed

22 files changed

+831
-304
lines changed

pyi_hashes.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"reflex/__init__.pyi": "8a6d2350e96659846436792a5c7b772b",
2+
"reflex/__init__.pyi": "d7767c4fe815246a4409359da60aac25",
33
"reflex/components/__init__.pyi": "76ba0a12cd3a7ba5ab6341a3ae81551f",
44
"reflex/components/base/__init__.pyi": "e9aaf47be1e1977eacee97b880c8f7de",
55
"reflex/components/base/app_wrap.pyi": "1d0e224e2d4b0538b19c0a038284e9b2",
@@ -56,7 +56,7 @@
5656
"reflex/components/radix/primitives/progress.pyi": "98b4add410a80a353ab503ad577169c2",
5757
"reflex/components/radix/primitives/slider.pyi": "573837a7d8d90deaf57c911faffed254",
5858
"reflex/components/radix/themes/__init__.pyi": "a15f9464ad99f248249ffa8e6deea4cf",
59-
"reflex/components/radix/themes/base.pyi": "1f0740d3165100c24e6bb4792aa81571",
59+
"reflex/components/radix/themes/base.pyi": "526db93a3f52bb00ad220f8744eba797",
6060
"reflex/components/radix/themes/color_mode.pyi": "f7515dccd1e315dc28a3cbbe2eabe7ff",
6161
"reflex/components/radix/themes/components/__init__.pyi": "87bb9ffff641928562da1622d2ca5993",
6262
"reflex/components/radix/themes/components/alert_dialog.pyi": "9f19bcdb4588a7f76596d142a0ac0950",

reflex/.templates/jinja/app/rxconfig.py.jinja2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ import reflex as rx
22

33
config = rx.Config(
44
app_name="{{ app_name }}",
5+
plugins=[rx.plugins.TailwindV3Plugin()],
56
)

reflex/.templates/jinja/web/tailwind.config.js.jinja2

Lines changed: 0 additions & 66 deletions
This file was deleted.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
module.exports = {
22
plugins: {
33
"postcss-import": {},
4-
tailwindcss: {},
54
autoprefixer: {},
65
},
76
};

reflex/.templates/web/styles/tailwind.css

Lines changed: 0 additions & 6 deletions
This file was deleted.

reflex/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@
361361
"vars",
362362
"config",
363363
"compiler",
364+
"plugins",
364365
}
365366
_SUBMOD_ATTRS: dict = _MAPPING
366367
getattr, __dir__, __all__ = lazy_loader.attach(

reflex/app.py

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from pathlib import Path
1919
from timeit import default_timer as timer
2020
from types import SimpleNamespace
21-
from typing import TYPE_CHECKING, Any, BinaryIO, get_args, get_type_hints
21+
from typing import TYPE_CHECKING, Any, BinaryIO, ParamSpec, get_args, get_type_hints
2222

2323
from fastapi import FastAPI
2424
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
@@ -314,6 +314,9 @@ def merged_with(self, other: UnevaluatedPage) -> UnevaluatedPage:
314314
)
315315

316316

317+
P = ParamSpec("P")
318+
319+
317320
@dataclasses.dataclass()
318321
class App(MiddlewareMixin, LifespanMixin):
319322
"""The main Reflex app that encapsulates the backend and frontend.
@@ -620,10 +623,12 @@ def __call__(self) -> ASGIApp:
620623
compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit(
621624
self._compile
622625
)
623-
compile_future.add_done_callback(
626+
627+
def callback(f: concurrent.futures.Future):
624628
# Force background compile errors to print eagerly
625-
lambda f: f.result()
626-
)
629+
return f.result()
630+
631+
compile_future.add_done_callback(callback)
627632
# Wait for the compile to finish to ensure all optional endpoints are mounted.
628633
compile_future.result()
629634

@@ -1029,11 +1034,6 @@ def _get_frontend_packages(self, imports: dict[str, set[ImportVar]]):
10291034
frontend_packages = get_config().frontend_packages
10301035
_frontend_packages = []
10311036
for package in frontend_packages:
1032-
if package in (get_config().tailwind or {}).get("plugins", []):
1033-
console.warn(
1034-
f"Tailwind packages are inferred from 'plugins', remove `{package}` from `frontend_packages`"
1035-
)
1036-
continue
10371037
if package in page_imports:
10381038
console.warn(
10391039
f"React packages and their dependencies are inferred from Component.library and Component.lib_dependencies, remove `{package}` from `frontend_packages`"
@@ -1166,6 +1166,7 @@ def _compile(self, export: bool = False, dry_run: bool = False):
11661166
11671167
Raises:
11681168
ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined.
1169+
FileNotFoundError: When a plugin requires a file that does not exist.
11691170
"""
11701171
from reflex.utils.exceptions import ReflexRuntimeError
11711172

@@ -1380,10 +1381,20 @@ def memoized_toast_provider():
13801381

13811382
ExecutorSafeFunctions.STATE = self._state
13821383

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

1386-
def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
1386+
with console.timing("Compile to Javascript"), executor as executor:
1387+
result_futures: list[
1388+
concurrent.futures.Future[
1389+
list[tuple[str, str]] | tuple[str, str] | None
1390+
]
1391+
] = []
1392+
1393+
def _submit_work(
1394+
fn: Callable[P, list[tuple[str, str]] | tuple[str, str] | None],
1395+
*args: P.args,
1396+
**kwargs: P.kwargs,
1397+
):
13871398
f = executor.submit(fn, *args, **kwargs)
13881399
f.add_done_callback(lambda _: progress.advance(task))
13891400
result_futures.append(f)
@@ -1401,20 +1412,26 @@ def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
14011412
# Compile the theme.
14021413
_submit_work(compile_theme, self.style)
14031414

1404-
# Compile the Tailwind config.
1405-
if config.tailwind is not None:
1406-
config.tailwind["content"] = config.tailwind.get(
1407-
"content", constants.Tailwind.CONTENT
1415+
for plugin in config.plugins:
1416+
plugin.pre_compile(
1417+
add_save_task=_submit_work,
1418+
add_modify_task=(
1419+
lambda *args, plugin=plugin: modify_files_tasks.append(
1420+
(
1421+
plugin.__class__.__module__ + plugin.__class__.__name__,
1422+
*args,
1423+
)
1424+
)
1425+
),
14081426
)
1409-
_submit_work(compiler.compile_tailwind, config.tailwind)
1410-
else:
1411-
_submit_work(compiler.remove_tailwind_from_postcss)
14121427

14131428
# Wait for all compilation tasks to complete.
1414-
compile_results.extend(
1415-
future.result()
1416-
for future in concurrent.futures.as_completed(result_futures)
1417-
)
1429+
for future in concurrent.futures.as_completed(result_futures):
1430+
if (result := future.result()) is not None:
1431+
if isinstance(result, list):
1432+
compile_results.extend(result)
1433+
else:
1434+
compile_results.append(result)
14181435

14191436
app_root = self._app_root(app_wrappers=app_wrappers)
14201437

@@ -1481,9 +1498,45 @@ def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
14811498
# Remove pages that are no longer in the app.
14821499
p.unlink()
14831500

1501+
output_mapping: dict[Path, str] = {}
1502+
for output_path, code in compile_results:
1503+
path = compiler_utils.resolve_path_of_web_dir(output_path)
1504+
if path in output_mapping:
1505+
console.warn(
1506+
f"Path {path} has two different outputs. The first one will be used."
1507+
)
1508+
else:
1509+
output_mapping[path] = code
1510+
1511+
for plugin in config.plugins:
1512+
for static_file_path, content in plugin.get_static_assets():
1513+
path = compiler_utils.resolve_path_of_web_dir(static_file_path)
1514+
if path in output_mapping:
1515+
console.warn(
1516+
f"Plugin {plugin.__class__.__name__} is trying to write to {path} but it already exists. The plugin file will be ignored."
1517+
)
1518+
else:
1519+
output_mapping[path] = (
1520+
content.decode("utf-8")
1521+
if isinstance(content, bytes)
1522+
else content
1523+
)
1524+
1525+
for plugin_name, file_path, modify_fn in modify_files_tasks:
1526+
path = compiler_utils.resolve_path_of_web_dir(file_path)
1527+
file_content = output_mapping.get(path)
1528+
if file_content is None:
1529+
if path.exists():
1530+
file_content = path.read_text()
1531+
else:
1532+
raise FileNotFoundError(
1533+
f"Plugin {plugin_name} is trying to modify {path} but it does not exist."
1534+
)
1535+
output_mapping[path] = modify_fn(file_content)
1536+
14841537
with console.timing("Write to Disk"):
1485-
for output_path, code in compile_results:
1486-
compiler_utils.write_page(output_path, code)
1538+
for output_path, code in output_mapping.items():
1539+
compiler_utils.write_file(output_path, code)
14871540

14881541
def _write_stateful_pages_marker(self):
14891542
"""Write list of routes that create dynamic states for the backend to use later."""

reflex/compiler/compiler.py

Lines changed: 9 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ def _validate_stylesheet(stylesheet_full_path: Path, assets_app_path: Path) -> N
223223
)
224224

225225

226+
RADIX_THEMES_STYLESHEET = "@radix-ui/themes/styles.css"
227+
228+
226229
def _compile_root_stylesheet(stylesheets: list[str]) -> str:
227230
"""Compile the root stylesheet.
228231
@@ -235,12 +238,12 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
235238
Raises:
236239
FileNotFoundError: If a specified stylesheet in assets directory does not exist.
237240
"""
238-
# Add tailwind css if enabled.
239-
sheets = (
240-
[constants.Tailwind.ROOT_STYLE_PATH]
241-
if get_config().tailwind is not None
242-
else []
243-
)
241+
# Add stylesheets from plugins.
242+
sheets = [RADIX_THEMES_STYLESHEET] + [
243+
sheet
244+
for plugin in get_config().plugins
245+
for sheet in plugin.get_stylesheet_paths()
246+
]
244247

245248
failed_to_import_sass = False
246249
assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS
@@ -451,22 +454,6 @@ def get_shared_components_recursive(component: BaseComponent):
451454
)
452455

453456

454-
def _compile_tailwind(
455-
config: dict,
456-
) -> str:
457-
"""Compile the Tailwind config.
458-
459-
Args:
460-
config: The Tailwind config.
461-
462-
Returns:
463-
The compiled Tailwind config.
464-
"""
465-
return templates.TAILWIND_CONFIG.render(
466-
**config,
467-
)
468-
469-
470457
def compile_document_root(
471458
head_components: list[Component],
472459
html_lang: str | None = None,
@@ -613,44 +600,6 @@ def compile_stateful_components(
613600
return output_path, code, page_components
614601

615602

616-
def compile_tailwind(
617-
config: dict,
618-
):
619-
"""Compile the Tailwind config.
620-
621-
Args:
622-
config: The Tailwind config.
623-
624-
Returns:
625-
The compiled Tailwind config.
626-
"""
627-
# Get the path for the output file.
628-
output_path = str((get_web_dir() / constants.Tailwind.CONFIG).absolute())
629-
630-
# Compile the config.
631-
code = _compile_tailwind(config)
632-
return output_path, code
633-
634-
635-
def remove_tailwind_from_postcss() -> tuple[str, str]:
636-
"""If tailwind is not to be used, remove it from postcss.config.js.
637-
638-
Returns:
639-
The path and code of the compiled postcss.config.js.
640-
"""
641-
# Get the path for the output file.
642-
output_path = str(get_web_dir() / constants.Dirs.POSTCSS_JS)
643-
644-
code = [
645-
line
646-
for line in Path(output_path).read_text().splitlines(keepends=True)
647-
if "tailwindcss: " not in line
648-
]
649-
650-
# Compile the config.
651-
return output_path, "".join(code)
652-
653-
654603
def purge_web_pages_dir():
655604
"""Empty out .web/pages directory."""
656605
if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR.get():

0 commit comments

Comments
 (0)