Move custom styles to root App file(_app.js) (#1764)

This commit is contained in:
Elijah Ahianyo 2023-09-15 20:18:35 +00:00 committed by GitHub
parent 63ae96ede3
commit 74d227d2fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 159 additions and 39 deletions

View File

@ -0,0 +1,5 @@
{%- block imports_styles %}
{% for sheet_name in stylesheets %}
{{- "@import url('" + sheet_name + "'); " }}
{% endfor %}
{% endblock %}

View File

@ -1,5 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": "." "baseUrl": ".",
"paths": {
"@/*": ["public/*"]
}
} }
} }

View File

@ -4,7 +4,7 @@ import theme from "/utils/theme";
import { clientStorage, initialEvents, initialState, StateContext, EventLoopContext } from "/utils/context.js"; import { clientStorage, initialEvents, initialState, StateContext, EventLoopContext } from "/utils/context.js";
import { useEventLoop } from "utils/state"; import { useEventLoop } from "utils/state";
import '../styles/tailwind.css' import '/styles/styles.css'
const GlobalStyles = css` const GlobalStyles = css`
/* Hide the blue border around Chakra components. */ /* Hide the blue border around Chakra components. */

View File

@ -612,8 +612,11 @@ class App(Base):
for component in custom_components: for component in custom_components:
all_imports.update(component.get_imports()) all_imports.update(component.get_imports())
# Compile the root document with base styles and fonts # Compile the root stylesheet with base styles.
compile_results.append(compiler.compile_document_root(self.stylesheets)) compile_results.append(compiler.compile_root_stylesheet(self.stylesheets))
# Compile the root document.
compile_results.append(compiler.compile_document_root())
# Compile the theme. # Compile the theme.
compile_results.append(compiler.compile_theme(self.style)) compile_results.append(compiler.compile_theme(self.style))

View File

@ -1,6 +1,8 @@
"""Compiler for the reflex apps.""" """Compiler for the reflex apps."""
from __future__ import annotations from __future__ import annotations
import os
from pathlib import Path
from typing import List, Set, Tuple, Type from typing import List, Set, Tuple, Type
from reflex import constants from reflex import constants
@ -118,6 +120,50 @@ def _compile_page(
) )
def compile_root_stylesheet(stylesheets: List[str]) -> Tuple[str, str]:
"""Compile the root stylesheet.
Args:
stylesheets: The stylesheets to include in the root stylesheet.
Returns:
The path and code of the compiled root stylesheet.
"""
output_path = utils.get_root_stylesheet_path()
code = _compile_root_stylesheet(stylesheets)
return output_path, code
def _compile_root_stylesheet(stylesheets: List[str]) -> str:
"""Compile the root stylesheet.
Args:
stylesheets: The stylesheets to include in the root stylesheet.
Returns:
The compiled root stylesheet.
Raises:
FileNotFoundError: If a specified stylesheet in assets directory does not exist.
"""
sheets = [constants.TAILWIND_ROOT_STYLE_PATH]
for stylesheet in stylesheets:
if not utils.is_valid_url(stylesheet):
# check if stylesheet provided exists.
stylesheet_full_path = (
Path.cwd() / constants.APP_ASSETS_DIR / stylesheet.strip("/")
)
if not os.path.exists(stylesheet_full_path):
raise FileNotFoundError(
f"The stylesheet file {stylesheet_full_path} does not exist."
)
stylesheet = f"@/{stylesheet.strip('/')}"
sheets.append(stylesheet) if stylesheet not in sheets else None
return templates.STYLE.render(stylesheets=sheets)
def _compile_components(components: Set[CustomComponent]) -> str: def _compile_components(components: Set[CustomComponent]) -> str:
"""Compile the components. """Compile the components.
@ -162,12 +208,9 @@ def _compile_tailwind(
) )
def compile_document_root(stylesheets: List[str]) -> Tuple[str, str]: def compile_document_root() -> Tuple[str, str]:
"""Compile the document root. """Compile the document root.
Args:
stylesheets: The stylesheets to include in the document root.
Returns: Returns:
The path and code of the compiled document root. The path and code of the compiled document root.
""" """
@ -175,8 +218,7 @@ def compile_document_root(stylesheets: List[str]) -> Tuple[str, str]:
output_path = utils.get_page_path(constants.DOCUMENT_ROOT) output_path = utils.get_page_path(constants.DOCUMENT_ROOT)
# Create the document root. # Create the document root.
document_root = utils.create_document_root(stylesheets) document_root = utils.create_document_root()
# Compile the document root. # Compile the document root.
code = _compile_document_root(document_root) code = _compile_document_root(document_root)
return output_path, code return output_path, code
@ -279,5 +321,4 @@ def compile_tailwind(
def purge_web_pages_dir(): def purge_web_pages_dir():
"""Empty out .web directory.""" """Empty out .web directory."""
template_files = ["_app.js"] utils.empty_dir(constants.WEB_PAGES_DIR, keep_files=["_app.js"])
utils.empty_dir(constants.WEB_PAGES_DIR, keep_files=template_files)

View File

@ -77,3 +77,6 @@ COMPONENTS = get_template("web/pages/custom_component.js.jinja2")
# Sitemap config file. # Sitemap config file.
SITEMAP_CONFIG = "module.exports = {config}".format SITEMAP_CONFIG = "module.exports = {config}".format
# Code to render the root stylesheet.
STYLE = get_template("web/styles/styles.css.jinja2")

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import os import os
from typing import Any, Dict, List, Optional, Set, Tuple, Type from typing import Any, Dict, List, Optional, Set, Tuple, Type
from urllib.parse import urlparse
from pydantic.fields import ModelField from pydantic.fields import ModelField
@ -18,7 +19,6 @@ from reflex.components.base import (
Main, Main,
Meta, Meta,
NextScript, NextScript,
RawLink,
Title, Title,
) )
from reflex.components.component import Component, ComponentStyle, CustomComponent from reflex.components.component import Component, ComponentStyle, CustomComponent
@ -257,18 +257,14 @@ def compile_custom_component(
) )
def create_document_root(stylesheets: List[str]) -> Component: def create_document_root() -> Component:
"""Create the document root. """Create the document root.
Args:
stylesheets: The list of stylesheets to include in the document root.
Returns: Returns:
The document root. The document root.
""" """
sheets = [RawLink.create(rel="stylesheet", href=href) for href in stylesheets]
return Html.create( return Html.create(
DocumentHead.create(*sheets), DocumentHead.create(),
Body.create( Body.create(
ColorModeScript.create(), ColorModeScript.create(),
Main.create(), Main.create(),
@ -324,6 +320,17 @@ def get_theme_path() -> str:
return os.path.join(constants.WEB_UTILS_DIR, constants.THEME + constants.JS_EXT) return os.path.join(constants.WEB_UTILS_DIR, constants.THEME + constants.JS_EXT)
def get_root_stylesheet_path() -> str:
"""Get the path of the app root file.
Returns:
The path of the app root file.
"""
return os.path.join(
constants.STYLES_DIR, constants.STYLESHEET_ROOT + constants.CSS_EXT
)
def get_context_path() -> str: def get_context_path() -> str:
"""Get the path of the context / initial state file. """Get the path of the context / initial state file.
@ -415,3 +422,16 @@ def empty_dir(path: str, keep_files: Optional[List[str]] = None):
for element in directory_contents: for element in directory_contents:
if element not in keep_files: if element not in keep_files:
path_ops.rm(os.path.join(path, element)) path_ops.rm(os.path.join(path, element))
def is_valid_url(url) -> bool:
"""Check if a url is valid.
Args:
url: The Url to check.
Returns:
Whether url is valid.
"""
result = urlparse(url)
return all([result.scheme, result.netloc])

View File

@ -126,10 +126,14 @@ WEB_STATIC_DIR = os.path.join(WEB_DIR, STATIC_DIR)
WEB_UTILS_DIR = os.path.join(WEB_DIR, UTILS_DIR) WEB_UTILS_DIR = os.path.join(WEB_DIR, UTILS_DIR)
# The directory where the assets are located. # The directory where the assets are located.
WEB_ASSETS_DIR = os.path.join(WEB_DIR, "public") WEB_ASSETS_DIR = os.path.join(WEB_DIR, "public")
# The directory where styles are located.
STYLES_DIR = os.path.join(WEB_DIR, "styles")
# The Tailwind config. # The Tailwind config.
TAILWIND_CONFIG = os.path.join(WEB_DIR, "tailwind.config.js") TAILWIND_CONFIG = os.path.join(WEB_DIR, "tailwind.config.js")
# Default Tailwind content paths # Default Tailwind content paths
TAILWIND_CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"] TAILWIND_CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"]
# Relative tailwind style path to root stylesheet in STYLES_DIR.
TAILWIND_ROOT_STYLE_PATH = "./tailwind.css"
# The NextJS config file # The NextJS config file
NEXT_CONFIG_FILE = "next.config.js" NEXT_CONFIG_FILE = "next.config.js"
# The sitemap config file. # The sitemap config file.
@ -148,6 +152,8 @@ ENV_JSON = os.path.join(WEB_DIR, "env.json")
JS_EXT = ".js" JS_EXT = ".js"
# The extension for python files. # The extension for python files.
PY_EXT = ".py" PY_EXT = ".py"
# The extension for css files.
CSS_EXT = ".css"
# The expected variable name where the rx.App is stored. # The expected variable name where the rx.App is stored.
APP_VAR = "app" APP_VAR = "app"
# The expected variable name where the API object is stored for deployment. # The expected variable name where the API object is stored for deployment.
@ -172,6 +178,10 @@ HYDRATE = "hydrate"
IS_HYDRATED = "is_hydrated" IS_HYDRATED = "is_hydrated"
# The name of the index page. # The name of the index page.
INDEX_ROUTE = "index" INDEX_ROUTE = "index"
# The name of the app root page.
APP_ROOT = "_app"
# The root stylesheet filename.
STYLESHEET_ROOT = "styles"
# The name of the document root page. # The name of the document root page.
DOCUMENT_ROOT = "_document" DOCUMENT_ROOT = "_document"
# The name of the theme page. # The name of the theme page.

View File

@ -1,8 +1,9 @@
import os
from typing import List, Set from typing import List, Set
import pytest import pytest
from reflex.compiler import utils from reflex.compiler import compiler, utils
from reflex.utils import imports from reflex.utils import imports
from reflex.vars import ImportVar from reflex.vars import ImportVar
@ -106,22 +107,56 @@ def test_compile_imports(import_dict: imports.ImportDict, test_dicts: List[dict]
assert import_dict["rest"] == test_dict["rest"] assert import_dict["rest"] == test_dict["rest"]
# @pytest.mark.parametrize( def test_compile_stylesheets(tmp_path, mocker):
# "name,value,output", """Test that stylesheets compile correctly.
# [
# ("foo", "bar", 'const foo = "bar"'),
# ("num", 1, "const num = 1"),
# ("check", False, "const check = false"),
# ("arr", [1, 2, 3], "const arr = [1, 2, 3]"),
# ("obj", {"foo": "bar"}, 'const obj = {"foo": "bar"}'),
# ],
# )
# def test_compile_constant_declaration(name: str, value: str, output: str):
# """Test the compile_constant_declaration function.
# Args: Args:
# name: The name of the constant. tmp_path: The test directory.
# value: The value of the constant. mocker: Pytest mocker object.
# output: The expected output. """
# """ project = tmp_path / "test_project"
# assert utils.compile_constant_declaration(name, value) == output project.mkdir()
assets_dir = project / "assets"
assets_dir.mkdir()
(assets_dir / "styles.css").touch()
mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project)
stylesheets = [
"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",
"/styles.css",
"https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css",
]
assert compiler.compile_root_stylesheet(stylesheets) == (
os.path.join(".web", "styles", "styles.css"),
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://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css'); \n"
f"@import url('@/styles.css'); \n"
f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css'); \n",
)
def test_compile_nonexistent_stylesheet(tmp_path, mocker):
"""Test that an error is thrown for non-existent stylesheets.
Args:
tmp_path: The test directory.
mocker: Pytest mocker object.
"""
project = tmp_path / "test_project"
project.mkdir()
assets_dir = project / "assets"
assets_dir.mkdir()
mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project)
stylesheets = ["/styles.css"]
with pytest.raises(FileNotFoundError):
compiler.compile_root_stylesheet(stylesheets)