diff --git a/reflex/.templates/web/package.json b/reflex/.templates/web/package.json index 191e7a4e5..09c44220f 100644 --- a/reflex/.templates/web/package.json +++ b/reflex/.templates/web/package.json @@ -24,7 +24,6 @@ }, "devDependencies": { "autoprefixer": "^10.4.14", - "postcss": "^8.4.24", - "tailwindcss": "^3.3.2" + "postcss": "^8.4.24" } } \ No newline at end of file diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 16df3c81a..d3e8f208c 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -8,6 +8,7 @@ from typing import List, Set, Tuple, Type from reflex import constants from reflex.compiler import templates, utils from reflex.components.component import Component, ComponentStyle, CustomComponent +from reflex.config import get_config from reflex.state import State from reflex.utils import imports from reflex.vars import ImportVar @@ -148,7 +149,12 @@ def _compile_root_stylesheet(stylesheets: List[str]) -> str: Raises: FileNotFoundError: If a specified stylesheet in assets directory does not exist. """ - sheets = [constants.TAILWIND_ROOT_STYLE_PATH] + # Add tailwind css if enabled. + sheets = ( + [constants.TAILWIND_ROOT_STYLE_PATH] + if get_config().tailwind is not None + else [] + ) for stylesheet in stylesheets: if not utils.is_valid_url(stylesheet): # check if stylesheet provided exists. diff --git a/reflex/config.py b/reflex/config.py index 397dea107..232406c6c 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -169,7 +169,7 @@ class Config(Base): cors_allowed_origins: List[str] = ["*"] # Tailwind config. - tailwind: Optional[Dict[str, Any]] = None + tailwind: Optional[Dict[str, Any]] = {} # Timeout when launching the gunicorn server. TODO(rename this to backend_timeout?) timeout: int = 120 diff --git a/reflex/constants.py b/reflex/constants.py index c2d2fc95a..e3e9e7a9c 100644 --- a/reflex/constants.py +++ b/reflex/constants.py @@ -134,6 +134,8 @@ TAILWIND_CONFIG = os.path.join(WEB_DIR, "tailwind.config.js") TAILWIND_CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"] # Relative tailwind style path to root stylesheet in STYLES_DIR. TAILWIND_ROOT_STYLE_PATH = "./tailwind.css" +# The Tailwindcss version +TAILWIND_VERSION = "tailwindcss@^3.3.2" # The NextJS config file NEXT_CONFIG_FILE = "next.config.js" # The sitemap config file. diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index 28c6d268e..60d716096 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -432,13 +432,20 @@ def install_frontend_packages(packages: set[str]): processes.show_status("Installing base frontend packages", process) config = get_config() - if config.tailwind is not None and "plugins" in config.tailwind: + if config.tailwind is not None: + # install tailwind and tailwind plugins as dev dependencies. process = processes.new_process( - [get_install_package_manager(), "add", *config.tailwind["plugins"]], + [ + get_install_package_manager(), + "add", + "-d", + constants.TAILWIND_VERSION, + *((config.tailwind or {}).get("plugins", [])), + ], cwd=constants.WEB_DIR, shell=constants.IS_WINDOWS, ) - processes.show_status("Installing tailwind packages", process) + processes.show_status("Installing tailwind", process) # Install custom packages defined in frontend_packages if len(packages) > 0: diff --git a/tests/compiler/test_compiler.py b/tests/compiler/test_compiler.py index 0e51fd67d..0b6cd6625 100644 --- a/tests/compiler/test_compiler.py +++ b/tests/compiler/test_compiler.py @@ -121,7 +121,6 @@ def test_compile_stylesheets(tmp_path, mocker): assets_dir.mkdir() (assets_dir / "styles.css").touch() - mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project) stylesheets = [ @@ -141,6 +140,36 @@ def test_compile_stylesheets(tmp_path, mocker): ) +def test_compile_stylesheets_exclude_tailwind(tmp_path, mocker): + """Test that Tailwind is excluded if tailwind config is explicitly set to None. + + Args: + tmp_path: The test directory. + mocker: Pytest mocker object. + """ + project = tmp_path / "test_project" + project.mkdir() + + assets_dir = project / "assets" + assets_dir.mkdir() + mock = mocker.Mock() + + mocker.patch.object(mock, "tailwind", None) + mocker.patch("reflex.compiler.compiler.get_config", return_value=mock) + + (assets_dir / "styles.css").touch() + mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project) + + stylesheets = [ + "/styles.css", + ] + + assert compiler.compile_root_stylesheet(stylesheets) == ( + os.path.join(".web", "styles", "styles.css"), + "@import url('@/styles.css'); \n", + ) + + def test_compile_nonexistent_stylesheet(tmp_path, mocker): """Test that an error is thrown for non-existent stylesheets.