[IMPL] - added support for sass and scss stylesheet languages
- better checking of stylesheets to be compiled - added support for sass and scss stylesheet languages - the stylesheets files are now copied to ".web/styles/" at compile time - relock poetry file for libsass deps - stylesheet compiler unit tests also check the contents of the file
This commit is contained in:
parent
b70f33d972
commit
0681cc271c
87
poetry.lock
generated
87
poetry.lock
generated
@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alembic"
|
name = "alembic"
|
||||||
@ -54,13 +54,13 @@ trio = ["trio (>=0.26.1)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-timeout"
|
name = "async-timeout"
|
||||||
version = "4.0.3"
|
version = "5.0.0"
|
||||||
description = "Timeout context manager for asyncio programs"
|
description = "Timeout context manager for asyncio programs"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
|
{file = "async_timeout-5.0.0-py3-none-any.whl", hash = "sha256:904719a4bd6e0520047d0ddae220aabee67b877f7ca17bf8cea20f67f6247ae0"},
|
||||||
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
|
{file = "async_timeout-5.0.0.tar.gz", hash = "sha256:49675ec889daacfe65ff66d2dde7dd1447a6f4b2f23721022e4ba121f8772a85"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -585,13 +585,13 @@ test = ["pytest (>=6)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastapi"
|
name = "fastapi"
|
||||||
version = "0.115.3"
|
version = "0.115.4"
|
||||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "fastapi-0.115.3-py3-none-any.whl", hash = "sha256:8035e8f9a2b0aa89cea03b6c77721178ed5358e1aea4cd8570d9466895c0638c"},
|
{file = "fastapi-0.115.4-py3-none-any.whl", hash = "sha256:0b504a063ffb3cf96a5e27dc1bc32c80ca743a2528574f9cdc77daa2d31b4742"},
|
||||||
{file = "fastapi-0.115.3.tar.gz", hash = "sha256:c091c6a35599c036d676fa24bd4a6e19fa30058d93d950216cdc672881f6f7db"},
|
{file = "fastapi-0.115.4.tar.gz", hash = "sha256:db653475586b091cb8b2fec2ac54a680ac6a158e07406e1abae31679e8826349"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -937,13 +937,13 @@ i18n = ["Babel (>=2.7)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyring"
|
name = "keyring"
|
||||||
version = "25.4.1"
|
version = "25.5.0"
|
||||||
description = "Store and access your passwords safely."
|
description = "Store and access your passwords safely."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "keyring-25.4.1-py3-none-any.whl", hash = "sha256:5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf"},
|
{file = "keyring-25.5.0-py3-none-any.whl", hash = "sha256:e67f8ac32b04be4714b42fe84ce7dad9c40985b9ca827c592cc303e7c26d9741"},
|
||||||
{file = "keyring-25.4.1.tar.gz", hash = "sha256:b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b"},
|
{file = "keyring-25.5.0.tar.gz", hash = "sha256:4c753b3ec91717fe713c4edd522d625889d8973a349b0e582622f49766de58e6"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -983,6 +983,21 @@ dev = ["changelist (==0.5)"]
|
|||||||
lint = ["pre-commit (==3.7.0)"]
|
lint = ["pre-commit (==3.7.0)"]
|
||||||
test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"]
|
test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsass"
|
||||||
|
version = "0.23.0"
|
||||||
|
description = "Sass for Python: A straightforward binding of libsass for Python."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "libsass-0.23.0-cp38-abi3-macosx_11_0_x86_64.whl", hash = "sha256:34cae047cbbfc4ffa832a61cbb110f3c95f5471c6170c842d3fed161e40814dc"},
|
||||||
|
{file = "libsass-0.23.0-cp38-abi3-macosx_14_0_arm64.whl", hash = "sha256:ea97d1b45cdc2fc3590cb9d7b60f1d8915d3ce17a98c1f2d4dd47ee0d9c68ce6"},
|
||||||
|
{file = "libsass-0.23.0-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4a218406d605f325d234e4678bd57126a66a88841cb95bee2caeafdc6f138306"},
|
||||||
|
{file = "libsass-0.23.0-cp38-abi3-win32.whl", hash = "sha256:31e86d92a5c7a551df844b72d83fc2b5e50abc6fbbb31e296f7bebd6489ed1b4"},
|
||||||
|
{file = "libsass-0.23.0-cp38-abi3-win_amd64.whl", hash = "sha256:a2ec85d819f353cbe807432d7275d653710d12b08ec7ef61c124a580a8352f3c"},
|
||||||
|
{file = "libsass-0.23.0.tar.gz", hash = "sha256:6f209955ede26684e76912caf329f4ccb57e4a043fd77fe0e7348dd9574f1880"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mako"
|
name = "mako"
|
||||||
version = "1.3.6"
|
version = "1.3.6"
|
||||||
@ -1475,13 +1490,13 @@ xmp = ["defusedxml"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pip"
|
name = "pip"
|
||||||
version = "24.2"
|
version = "24.3.1"
|
||||||
description = "The PyPA recommended tool for installing Python packages."
|
description = "The PyPA recommended tool for installing Python packages."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "pip-24.2-py3-none-any.whl", hash = "sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2"},
|
{file = "pip-24.3.1-py3-none-any.whl", hash = "sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed"},
|
||||||
{file = "pip-24.2.tar.gz", hash = "sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8"},
|
{file = "pip-24.3.1.tar.gz", hash = "sha256:ebcb60557f2aefabc2e0f918751cd24ea0d56d8ec5445fe1807f1d2109660b99"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2013,13 +2028,13 @@ docs = ["sphinx"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-multipart"
|
name = "python-multipart"
|
||||||
version = "0.0.12"
|
version = "0.0.17"
|
||||||
description = "A streaming multipart parser for Python"
|
description = "A streaming multipart parser for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "python_multipart-0.0.12-py3-none-any.whl", hash = "sha256:43dcf96cf65888a9cd3423544dd0d75ac10f7aa0c3c28a175bbcd00c9ce1aebf"},
|
{file = "python_multipart-0.0.17-py3-none-any.whl", hash = "sha256:15dc4f487e0a9476cc1201261188ee0940165cffc94429b6fc565c4d3045cb5d"},
|
||||||
{file = "python_multipart-0.0.12.tar.gz", hash = "sha256:045e1f98d719c1ce085ed7f7e1ef9d8ccc8c02ba02b5566d5f7521410ced58cb"},
|
{file = "python_multipart-0.0.17.tar.gz", hash = "sha256:41330d831cae6e2f22902704ead2826ea038d0419530eadff3ea80175aec5538"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2268,13 +2283,13 @@ idna2008 = ["idna"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rich"
|
name = "rich"
|
||||||
version = "13.9.3"
|
version = "13.9.4"
|
||||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.0"
|
python-versions = ">=3.8.0"
|
||||||
files = [
|
files = [
|
||||||
{file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"},
|
{file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"},
|
||||||
{file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"},
|
{file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -2329,13 +2344,13 @@ jeepney = ">=0.6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selenium"
|
name = "selenium"
|
||||||
version = "4.25.0"
|
version = "4.26.1"
|
||||||
description = "Official Python bindings for Selenium WebDriver"
|
description = "Official Python bindings for Selenium WebDriver"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "selenium-4.25.0-py3-none-any.whl", hash = "sha256:3798d2d12b4a570bc5790163ba57fef10b2afee958bf1d80f2a3cf07c4141f33"},
|
{file = "selenium-4.26.1-py3-none-any.whl", hash = "sha256:1db3f3a0cd5bb07624fa8a3905a6fdde1595a42185a0617077c361dc53d104fb"},
|
||||||
{file = "selenium-4.25.0.tar.gz", hash = "sha256:95d08d3b82fb353f3c474895154516604c7f0e6a9a565ae6498ef36c9bac6921"},
|
{file = "selenium-4.26.1.tar.gz", hash = "sha256:7640f3f08ae7f4e450f895678e8a10a55eb4e4ca18311ed675ecc4684b96b683"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -2348,23 +2363,23 @@ websocket-client = ">=1.8,<2.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "setuptools"
|
name = "setuptools"
|
||||||
version = "75.2.0"
|
version = "75.3.0"
|
||||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"},
|
{file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"},
|
||||||
{file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"},
|
{file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"]
|
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"]
|
||||||
core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
|
core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
|
||||||
cover = ["pytest-cov"]
|
cover = ["pytest-cov"]
|
||||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
|
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
|
||||||
enabler = ["pytest-enabler (>=2.2)"]
|
enabler = ["pytest-enabler (>=2.2)"]
|
||||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
|
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
|
||||||
type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"]
|
type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shellingham"
|
name = "shellingham"
|
||||||
@ -2540,13 +2555,13 @@ SQLAlchemy = ">=2.0.14,<2.1.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "starlette"
|
name = "starlette"
|
||||||
version = "0.41.0"
|
version = "0.41.2"
|
||||||
description = "The little ASGI library that shines."
|
description = "The little ASGI library that shines."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "starlette-0.41.0-py3-none-any.whl", hash = "sha256:a0193a3c413ebc9c78bff1c3546a45bb8c8bcb4a84cae8747d650a65bd37210a"},
|
{file = "starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d"},
|
||||||
{file = "starlette-0.41.0.tar.gz", hash = "sha256:39cbd8768b107d68bfe1ff1672b38a2c38b49777de46d2a592841d58e3bf7c2a"},
|
{file = "starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -2790,13 +2805,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtualenv"
|
name = "virtualenv"
|
||||||
version = "20.27.0"
|
version = "20.27.1"
|
||||||
description = "Virtual Python Environment builder"
|
description = "Virtual Python Environment builder"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"},
|
{file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"},
|
||||||
{file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"},
|
{file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -3048,4 +3063,4 @@ type = ["pytest-mypy"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "547fdabf7a030c2a7c8d63eb5b2a3c5e821afa86390f08b895db038d30013904"
|
content-hash = "8d9118a634276acd5cbbf8b8392693f8a28f261fabff701ff1901b942b1cdcbe"
|
||||||
|
@ -59,6 +59,7 @@ twine = ">=4.0.0,<6.0"
|
|||||||
tomlkit = ">=0.12.4,<1.0"
|
tomlkit = ">=0.12.4,<1.0"
|
||||||
lazy_loader = ">=0.4"
|
lazy_loader = ">=0.4"
|
||||||
reflex-chakra = ">=0.6.0"
|
reflex-chakra = ">=0.6.0"
|
||||||
|
libsass = "0.23.0"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pytest = ">=7.1.2,<9.0"
|
pytest = ">=7.1.2,<9.0"
|
||||||
|
@ -4,8 +4,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from re import IGNORECASE as RE_IGNORECASE
|
||||||
|
from re import compile as re_compile
|
||||||
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple, Type, Union
|
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple, Type, Union
|
||||||
|
|
||||||
|
from sass import compile as sass_compile
|
||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.compiler import templates, utils
|
from reflex.compiler import templates, utils
|
||||||
from reflex.components.base.fragment import Fragment
|
from reflex.components.base.fragment import Fragment
|
||||||
@ -24,6 +28,8 @@ from reflex.utils.imports import ImportVar
|
|||||||
from reflex.utils.prerequisites import get_web_dir
|
from reflex.utils.prerequisites import get_web_dir
|
||||||
from reflex.vars.base import LiteralVar, Var
|
from reflex.vars.base import LiteralVar, Var
|
||||||
|
|
||||||
|
RE_SASS_SCSS_EXT = re_compile(r"\.s(c|a)ss", flags=RE_IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
def _compile_document_root(root: Component) -> str:
|
def _compile_document_root(root: Component) -> str:
|
||||||
"""Compile the document root.
|
"""Compile the document root.
|
||||||
@ -189,18 +195,66 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
|
|||||||
if get_config().tailwind is not None
|
if get_config().tailwind is not None
|
||||||
else []
|
else []
|
||||||
)
|
)
|
||||||
for stylesheet in stylesheets:
|
|
||||||
|
while len(stylesheets):
|
||||||
|
stylesheet = stylesheets.pop(0)
|
||||||
if not utils.is_valid_url(stylesheet):
|
if not utils.is_valid_url(stylesheet):
|
||||||
# check if stylesheet provided exists.
|
# check if stylesheet provided exists.
|
||||||
stylesheet_full_path = (
|
assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS
|
||||||
Path.cwd() / constants.Dirs.APP_ASSETS / stylesheet.strip("/")
|
stylesheet_full_path = assets_app_path / stylesheet.strip("/")
|
||||||
)
|
|
||||||
if not stylesheet_full_path.exists():
|
if not stylesheet_full_path.exists():
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
f"The stylesheet file {stylesheet_full_path} does not exist."
|
f"The stylesheet file {stylesheet_full_path} does not exist."
|
||||||
)
|
)
|
||||||
stylesheet = f"../{constants.Dirs.PUBLIC}/{stylesheet.strip('/')}"
|
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()
|
||||||
|
]
|
||||||
|
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 (
|
||||||
|
stylesheet_full_path.suffix[1:]
|
||||||
|
in constants.Reflex.STYLESHEETS_SUPPORTED
|
||||||
|
):
|
||||||
|
target = (
|
||||||
|
Path.cwd()
|
||||||
|
/ f"{constants.Dirs.WEB}/{constants.Dirs.STYLES}/{RE_SASS_SCSS_EXT.sub(".css", str(stylesheet)).strip('/')}"
|
||||||
|
)
|
||||||
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
target.write_text(
|
||||||
|
data=sass_compile(
|
||||||
|
filename=str(stylesheet_full_path), output_style="compressed"
|
||||||
|
),
|
||||||
|
encoding="utf8",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
stylesheet = f"./{RE_SASS_SCSS_EXT.sub(".css", str(stylesheet)).strip('/')}"
|
||||||
|
|
||||||
sheets.append(stylesheet) if stylesheet not in sheets else None
|
sheets.append(stylesheet) if stylesheet not in sheets else None
|
||||||
|
|
||||||
return templates.STYLE.render(stylesheets=sheets)
|
return templates.STYLE.render(stylesheets=sheets)
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,6 +78,9 @@ class Reflex(SimpleNamespace):
|
|||||||
|
|
||||||
RELEASES_URL = f"https://api.github.com/repos/reflex-dev/templates/releases"
|
RELEASES_URL = f"https://api.github.com/repos/reflex-dev/templates/releases"
|
||||||
|
|
||||||
|
# The reflex stylesheet language supported
|
||||||
|
STYLESHEETS_SUPPORTED = ["css", "sass", "scss"]
|
||||||
|
|
||||||
|
|
||||||
class ReflexHostingCLI(SimpleNamespace):
|
class ReflexHostingCLI(SimpleNamespace):
|
||||||
"""Base constants concerning Reflex Hosting CLI."""
|
"""Base constants concerning Reflex Hosting CLI."""
|
||||||
|
@ -220,6 +220,9 @@ def setup_frontend(
|
|||||||
path_ops.cp(
|
path_ops.cp(
|
||||||
src=str(root / constants.Dirs.APP_ASSETS),
|
src=str(root / constants.Dirs.APP_ASSETS),
|
||||||
dest=str(root / prerequisites.get_web_dir() / constants.Dirs.PUBLIC),
|
dest=str(root / prerequisites.get_web_dir() / constants.Dirs.PUBLIC),
|
||||||
|
ignore=tuple(
|
||||||
|
f"*.{ext}" for ext in constants.Reflex.STYLESHEETS_SUPPORTED
|
||||||
|
), # ignore stylesheet files precompiled in the compiler
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the environment variables in client (env.json).
|
# Set the environment variables in client (env.json).
|
||||||
|
@ -28,13 +28,19 @@ def rm(path: str | Path):
|
|||||||
path.unlink()
|
path.unlink()
|
||||||
|
|
||||||
|
|
||||||
def cp(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
|
def cp(
|
||||||
|
src: str | Path,
|
||||||
|
dest: str | Path,
|
||||||
|
overwrite: bool = True,
|
||||||
|
ignore: tuple[str, ...] | None = None,
|
||||||
|
) -> bool:
|
||||||
"""Copy a file or directory.
|
"""Copy a file or directory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
src: The path to the file or directory.
|
src: The path to the file or directory.
|
||||||
dest: The path to the destination.
|
dest: The path to the destination.
|
||||||
overwrite: Whether to overwrite the destination.
|
overwrite: Whether to overwrite the destination.
|
||||||
|
ignore: Ignoring files and directories that match one of the glob-style patterns provided
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Whether the copy was successful.
|
Whether the copy was successful.
|
||||||
@ -46,7 +52,11 @@ def cp(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
|
|||||||
return False
|
return False
|
||||||
if src.is_dir():
|
if src.is_dir():
|
||||||
rm(dest)
|
rm(dest)
|
||||||
shutil.copytree(src, dest)
|
shutil.copytree(
|
||||||
|
src,
|
||||||
|
dest,
|
||||||
|
ignore=shutil.ignore_patterns(*ignore) if ignore is not None else ignore,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
shutil.copyfile(src, dest)
|
shutil.copyfile(src, dest)
|
||||||
return True
|
return True
|
||||||
|
@ -106,7 +106,7 @@ def test_compile_imports(import_dict: ParsedImportDict, test_dicts: List[dict]):
|
|||||||
assert sorted(import_dict["rest"]) == test_dict["rest"] # type: ignore
|
assert sorted(import_dict["rest"]) == test_dict["rest"] # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def test_compile_stylesheets(tmp_path, mocker):
|
def test_compile_stylesheets(tmp_path: Path, mocker):
|
||||||
"""Test that stylesheets compile correctly.
|
"""Test that stylesheets compile correctly.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -119,13 +119,26 @@ def test_compile_stylesheets(tmp_path, mocker):
|
|||||||
assets_dir = project / "assets"
|
assets_dir = project / "assets"
|
||||||
assets_dir.mkdir()
|
assets_dir.mkdir()
|
||||||
|
|
||||||
(assets_dir / "styles.css").touch()
|
assets_preprocess_dir = project / "assets" / "preprocess"
|
||||||
|
assets_preprocess_dir.mkdir()
|
||||||
|
|
||||||
|
(assets_dir / "styles.css").write_text(
|
||||||
|
"button.rt-Button {\n\tborder-radius:unset !important;\n}"
|
||||||
|
)
|
||||||
|
(assets_preprocess_dir / "styles_a.sass").write_text(
|
||||||
|
"button.rt-Button\n\tborder-radius:unset !important"
|
||||||
|
)
|
||||||
|
(assets_preprocess_dir / "styles_b.scss").write_text(
|
||||||
|
"button.rt-Button {\n\tborder-radius:unset !important;\n}"
|
||||||
|
)
|
||||||
mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project)
|
mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project)
|
||||||
|
|
||||||
stylesheets = [
|
stylesheets = [
|
||||||
"https://fonts.googleapis.com/css?family=Sofia&effect=neon|outline|emboss|shadow-multiple",
|
"https://fonts.googleapis.com/css?family=Sofia&effect=neon|outline|emboss|shadow-multiple",
|
||||||
"https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css",
|
"https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css",
|
||||||
"/styles.css",
|
"/styles.css",
|
||||||
|
"/preprocess/styles_a.sass",
|
||||||
|
"/preprocess/styles_b.scss",
|
||||||
"https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css",
|
"https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -134,10 +147,22 @@ def test_compile_stylesheets(tmp_path, mocker):
|
|||||||
f"@import url('./tailwind.css'); \n"
|
f"@import url('./tailwind.css'); \n"
|
||||||
f"@import url('https://fonts.googleapis.com/css?family=Sofia&effect=neon|outline|emboss|shadow-multiple'); \n"
|
f"@import url('https://fonts.googleapis.com/css?family=Sofia&effect=neon|outline|emboss|shadow-multiple'); \n"
|
||||||
f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css'); \n"
|
f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css'); \n"
|
||||||
f"@import url('../public/styles.css'); \n"
|
f"@import url('./styles.css'); \n"
|
||||||
|
f"@import url('./preprocess/styles_a.css'); \n"
|
||||||
|
f"@import url('./preprocess/styles_b.css'); \n"
|
||||||
f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css'); \n",
|
f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css'); \n",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# NOTE: the css file is also inserted into the s(a|c)ss preprocessor, which compressed the result, which means we don't have the tab, return,... characters.
|
||||||
|
expected_result = "button.rt-Button{border-radius:unset !important}\n"
|
||||||
|
assert (project / ".web" / "styles" / "styles.css").read_text() == expected_result
|
||||||
|
assert (
|
||||||
|
project / ".web" / "styles" / "preprocess" / "styles_a.css"
|
||||||
|
).read_text() == expected_result
|
||||||
|
assert (
|
||||||
|
project / ".web" / "styles" / "preprocess" / "styles_b.css"
|
||||||
|
).read_text() == expected_result
|
||||||
|
|
||||||
|
|
||||||
def test_compile_stylesheets_exclude_tailwind(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.
|
"""Test that Tailwind is excluded if tailwind config is explicitly set to None.
|
||||||
@ -165,7 +190,7 @@ def test_compile_stylesheets_exclude_tailwind(tmp_path, mocker):
|
|||||||
|
|
||||||
assert compiler.compile_root_stylesheet(stylesheets) == (
|
assert compiler.compile_root_stylesheet(stylesheets) == (
|
||||||
str(Path(".web") / "styles" / "styles.css"),
|
str(Path(".web") / "styles" / "styles.css"),
|
||||||
"@import url('../public/styles.css'); \n",
|
"@import url('./styles.css'); \n",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user