use dynamically generated libraries

This commit is contained in:
Khaleel Al-Adhami 2024-10-03 11:03:35 -07:00
parent 4a96c761a6
commit 29f864f248
3 changed files with 50 additions and 22 deletions

View File

@ -4,7 +4,6 @@ from __future__ import annotations
import os import os
from datetime import datetime from datetime import datetime
from importlib.util import find_spec
from pathlib import Path from pathlib import Path
from typing import Dict, Iterable, Optional, Type, Union from typing import Dict, Iterable, Optional, Type, Union
@ -41,6 +40,20 @@ def _compile_document_root(root: Component) -> str:
) )
def _normalize_library_name(lib: str) -> str:
"""Normalize the library name.
Args:
lib: The library name to normalize.
Returns:
The normalized library name.
"""
if lib == "react":
return "React"
return lib.replace("@", "").replace("/", "_").replace("-", "_")
def _compile_app(app_root: Component) -> str: def _compile_app(app_root: Component) -> str:
"""Compile the app template component. """Compile the app template component.
@ -50,25 +63,20 @@ def _compile_app(app_root: Component) -> str:
Returns: Returns:
The compiled app. The compiled app.
""" """
chakra_available = find_spec("reflex_chakra") is not None from reflex.components.dynamic import bundled_libraries
window_libraries = [
(_normalize_library_name(name), name) for name in bundled_libraries
] + [
("utils_context", f"/{constants.Dirs.UTILS}/context.js"),
("utils_state", f"/{constants.Dirs.UTILS}/state.js"),
]
return templates.APP_ROOT.render( return templates.APP_ROOT.render(
imports=utils.compile_imports(app_root._get_all_imports()), imports=utils.compile_imports(app_root._get_all_imports()),
custom_codes=app_root._get_all_custom_code(), custom_codes=app_root._get_all_custom_code(),
hooks={**app_root._get_all_hooks_internal(), **app_root._get_all_hooks()}, hooks={**app_root._get_all_hooks_internal(), **app_root._get_all_hooks()},
window_libraries=[ window_libraries=window_libraries,
("React", "react"),
("utils_context", f"/{constants.Dirs.UTILS}/context.js"),
("utils_state", f"/{constants.Dirs.UTILS}/state.js"),
("radix", "@radix-ui/themes"),
]
+ (
[
("chakra", "@chakra-ui/react"),
]
if chakra_available
else []
),
render=app_root.render(), render=app_root.render(),
) )

View File

@ -1,14 +1,18 @@
"""Components that are dynamically generated on the backend.""" """Components that are dynamically generated on the backend."""
from importlib.util import find_spec from typing import TYPE_CHECKING
from reflex import constants from reflex import constants
from reflex.utils import imports from reflex.utils import imports
from reflex.utils.exceptions import DynamicComponentMissingLibrary
from reflex.utils.format import format_library_name from reflex.utils.format import format_library_name
from reflex.utils.serializers import serializer from reflex.utils.serializers import serializer
from reflex.vars import Var, get_unique_variable_name from reflex.vars import Var, get_unique_variable_name
from reflex.vars.base import VarData, transform from reflex.vars.base import VarData, transform
if TYPE_CHECKING:
from reflex.components.component import Component
def get_cdn_url(lib: str) -> str: def get_cdn_url(lib: str) -> str:
"""Get the CDN URL for a library. """Get the CDN URL for a library.
@ -22,6 +26,23 @@ def get_cdn_url(lib: str) -> str:
return f"https://cdn.jsdelivr.net/npm/{lib}" + "/+esm" return f"https://cdn.jsdelivr.net/npm/{lib}" + "/+esm"
bundled_libraries = {"react", "@radix-ui/themes"}
def bundle_library(component: "Component"):
"""Bundle a library with the component.
Args:
component: The component to bundle the library with.
Raises:
DynamicComponentMissingLibrary: Raised when a dynamic component is missing a library.
"""
if component.library is None:
raise DynamicComponentMissingLibrary("Component must have a library to bundle.")
bundled_libraries.add(format_library_name(component.library))
def load_dynamic_serializer(): def load_dynamic_serializer():
"""Load the serializer for dynamic components.""" """Load the serializer for dynamic components."""
# Causes a circular import, so we import here. # Causes a circular import, so we import here.
@ -60,12 +81,7 @@ def load_dynamic_serializer():
) )
] = None ] = None
chakra_available = find_spec("reflex_chakra") is not None libs_in_window = bundled_libraries
libs_in_window = [
"react",
"@radix-ui/themes",
] + (["@chakra-ui/react"] if chakra_available else [])
imports = {} imports = {}
for lib, names in component._get_all_imports().items(): for lib, names in component._get_all_imports().items():

View File

@ -115,3 +115,7 @@ class PrimitiveUnserializableToJSON(ReflexError, ValueError):
class InvalidLifespanTaskType(ReflexError, TypeError): class InvalidLifespanTaskType(ReflexError, TypeError):
"""Raised when an invalid task type is registered as a lifespan task.""" """Raised when an invalid task type is registered as a lifespan task."""
class DynamicComponentMissingLibrary(ReflexError, ValueError):
"""Raised when a dynamic component is missing a library."""