diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index f79309760..d5dcef0fe 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -21,7 +21,7 @@ from reflex.components.component import ( from reflex.config import environment, get_config from reflex.state import BaseState from reflex.style import SYSTEM_COLOR_MODE -from reflex.utils import console +from reflex.utils import console, path_ops from reflex.utils.exec import is_prod_mode from reflex.utils.imports import ImportVar from reflex.utils.prerequisites import get_web_dir @@ -195,67 +195,49 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str: else [] ) - try: - sass_compile = None - while len(stylesheets): - stylesheet = stylesheets.pop(0) - if not utils.is_valid_url(stylesheet): - # check if stylesheet provided exists. - assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS - stylesheet_full_path = assets_app_path / stylesheet.strip("/") + sass_compile = None + for stylesheet in stylesheets: + if not utils.is_valid_url(stylesheet): + # check if stylesheet provided exists. + assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS + stylesheet_full_path = assets_app_path / stylesheet.strip("/") - if not stylesheet_full_path.exists(): - raise FileNotFoundError( - f"The stylesheet file {stylesheet_full_path} does not exist." - ) - elif not stylesheet_full_path.is_file(): - if stylesheet_full_path.is_dir(): - # NOTE: this can create an infinite loop, for example: - # assets/ - # | dir_a/ - # | | dir_c/ (symlink to "assets/dir_a") - # | dir_b/ - # so to avoid the infinite loop, we don't include symbolic links - stylesheets += [ - str(p.relative_to(assets_app_path)) - for p in stylesheet_full_path.iterdir() - if not (p.is_symlink() and p.is_dir()) - ] - continue - else: - raise FileNotFoundError( - f'The stylesheet path "{stylesheet_full_path}" is not a valid path.' - ) - elif ( - stylesheet_full_path.suffix[1:] - not in constants.Reflex.STYLESHEETS_SUPPORTED - ): - raise FileNotFoundError( - f'The stylesheet file "{stylesheet_full_path}" is not a valid file.' - ) + if not stylesheet_full_path.exists(): + raise FileNotFoundError( + f"The stylesheet file {stylesheet_full_path} does not exist." + ) - if ( - stylesheet_full_path.suffix[1:] - in constants.Reflex.STYLESHEETS_SUPPORTED - ): - target = ( - Path.cwd() - / constants.Dirs.WEB - / constants.Dirs.STYLES - / RE_SASS_SCSS_EXT.sub(".css", str(stylesheet)).strip("/") - ) - target.parent.mkdir(parents=True, exist_ok=True) + if stylesheet_full_path.is_dir(): + # NOTE: this can create an infinite loop, for example: + # assets/ + # | dir_a/ + # | | dir_c/ (symlink to "assets/dir_a") + # | dir_b/ + # so to avoid the infinite loop, we don't include symbolic links + stylesheets += [ + str(p.relative_to(assets_app_path)) + for p in stylesheet_full_path.iterdir() + if not (p.is_symlink() and p.is_dir()) + ] + continue - if stylesheet_full_path.suffix == ".css": - target.write_text( - data=stylesheet_full_path.read_text(), - encoding="utf8", - ) - else: - if sass_compile is None: - from sass import compile as sass_compile - else: - pass + if ( + stylesheet_full_path.suffix[1:].lower() + in constants.Reflex.STYLESHEETS_SUPPORTED + ): + target = ( + Path.cwd() + / constants.Dirs.WEB + / constants.Dirs.STYLES + / RE_SASS_SCSS_EXT.sub(".css", str(stylesheet)).strip("/") + ) + target.parent.mkdir(parents=True, exist_ok=True) + + if stylesheet_full_path.suffix == ".css": + path_ops.cp(src=stylesheet_full_path, dest=target, overwrite=True) + else: + try: + from sass import compile as sass_compile target.write_text( data=sass_compile( @@ -264,15 +246,18 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str: ), encoding="utf8", ) - else: - pass - - stylesheet = ( - f"./{RE_SASS_SCSS_EXT.sub('.css', str(stylesheet)).strip('/')}" + except ImportError: + sass_compile = None + else: + raise FileNotFoundError( + f'The stylesheet file "{stylesheet_full_path}" is not a valid file.' ) - sheets.append(stylesheet) if stylesheet not in sheets else None - except ImportError: + stylesheet = f"./{str(stylesheet).replace(stylesheet_full_path.suffix, ".css").strip('/')}" + + sheets.append(stylesheet) if stylesheet not in sheets else None + + if sass_compile is None: console.error( """The `libsass` package is required to compile sass/scss stylesheet files. Run `pip install "libsass>=0.23.0"`.""" ) diff --git a/tests/units/compiler/test_compiler.py b/tests/units/compiler/test_compiler.py index 7049caf89..c7bdbf6e4 100644 --- a/tests/units/compiler/test_compiler.py +++ b/tests/units/compiler/test_compiler.py @@ -187,6 +187,19 @@ def test_compile_stylesheets_scss_sass(tmp_path: Path, mocker): f"@import url('./preprocess/styles_b.css'); \n", ) + stylesheets = [ + "/styles.css", + "/preprocess", # this is a folder containing "styles_a.sass" and "styles_b.scss" + ] + + assert compiler.compile_root_stylesheet(stylesheets) == ( + str(Path(".web") / "styles" / "styles.css"), + f"@import url('./tailwind.css'); \n" + f"@import url('./styles.css'); \n" + f"@import url('./preprocess/styles_b.css'); \n" + f"@import url('./preprocess/styles_a.css'); \n", + ) + assert (project / ".web" / "styles" / "styles.css").read_text() == ( assets_dir / "styles.css" ).read_text()