From 42b8586ab9f1509f310d6a251b297fc2931b0204 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 22 Oct 2024 18:58:33 -0700 Subject: [PATCH 01/20] component as literal vars --- .../jinja/web/pages/utils.js.jinja2 | 12 +- reflex/components/base/bare.py | 4 +- reflex/components/component.py | 126 +++++++++++++++++- reflex/components/core/upload.py | 102 ++++++++++---- reflex/components/tags/tag.py | 5 +- 5 files changed, 210 insertions(+), 39 deletions(-) diff --git a/reflex/.templates/jinja/web/pages/utils.js.jinja2 b/reflex/.templates/jinja/web/pages/utils.js.jinja2 index 908482d2409..624e3bee862 100644 --- a/reflex/.templates/jinja/web/pages/utils.js.jinja2 +++ b/reflex/.templates/jinja/web/pages/utils.js.jinja2 @@ -36,14 +36,10 @@ {# component: component dictionary #} {% macro render_tag(component) %} <{{component.name}} {{- render_props(component.props) }}> -{%- if component.args is not none -%} - {{- render_arg_content(component) }} -{%- else -%} - {{ component.contents }} - {% for child in component.children %} - {{ render(child) }} - {% endfor %} -{%- endif -%} +{{ component.contents }} +{% for child in component.children %} +{{ render(child) }} +{% endfor %} {%- endmacro %} diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index ada511ef2bd..8b4d7b2169c 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -7,7 +7,7 @@ from reflex.components.component import Component from reflex.components.tags import Tag from reflex.components.tags.tagless import Tagless -from reflex.vars import ArrayVar, BooleanVar, ObjectVar, Var +from reflex.vars import BooleanVar, ObjectVar, Var class Bare(Component): @@ -33,7 +33,7 @@ def create(cls, contents: Any) -> Component: def _render(self) -> Tag: if isinstance(self.contents, Var): - if isinstance(self.contents, (BooleanVar, ObjectVar, ArrayVar)): + if isinstance(self.contents, (BooleanVar, ObjectVar)): return Tagless(contents=f"{{{str(self.contents.to_string())}}}") return Tagless(contents=f"{{{str(self.contents)}}}") return Tagless(contents=str(self.contents)) diff --git a/reflex/components/component.py b/reflex/components/component.py index a0d9c93b09e..8f15e1ba816 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -3,6 +3,7 @@ from __future__ import annotations import copy +import dataclasses import typing from abc import ABC, abstractmethod from functools import lru_cache, wraps @@ -58,7 +59,14 @@ parse_imports, ) from reflex.vars import VarData -from reflex.vars.base import LiteralVar, Var +from reflex.vars.base import ( + CachedVarOperation, + LiteralVar, + Var, + cached_property_no_lock, +) +from reflex.vars.function import FunctionStringVar +from reflex.vars.object import ObjectVar from reflex.vars.sequence import LiteralArrayVar @@ -2340,3 +2348,119 @@ def create(cls, *children, **props) -> Component: load_dynamic_serializer() + + +class ComponentVar(Var[Component], python_types=Component): + """A Var that represents a Component.""" + + +def empty_component() -> Component: + """Create an empty component. + + Returns: + An empty component. + """ + from reflex.components.base.bare import Bare + + return Bare.create("") + + +@dataclasses.dataclass( + eq=False, + frozen=True, +) +class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar): + """A Var that represents a Component.""" + + _var_value: Component = dataclasses.field(default_factory=empty_component) + + @cached_property_no_lock + def _cached_var_name(self) -> str: + """Get the name of the var. + + Returns: + The name of the var. + """ + tag = self._var_value._render() + + props = Var.create(tag.props).to(ObjectVar) + for prop in tag.special_props: + props = props.merge(prop) + + contents = getattr(self._var_value, "contents", None) + + tag_name = Var(tag.name) if tag.name else Var("Fragment") + + return str( + FunctionStringVar.create( + "jsx", + ).call( + tag_name, + props, + *([Var.create(contents)] if contents is not None else []), + *[Var.create(child) for child in self._var_value.children], + ) + ) + + @cached_property_no_lock + def _cached_get_all_var_data(self) -> VarData | None: + """Get the VarData for the var. + + Returns: + The VarData for the var. + """ + return VarData.merge( + VarData( + imports={ + "@emotion/react": [ + ImportVar(tag="jsx"), + ], + } + ), + VarData( + imports=self._var_value._get_all_imports(collapse=True), + ), + *( + [ + VarData( + imports={ + "react": [ + ImportVar(tag="Fragment"), + ], + } + ) + ] + if not self._var_value.tag + else [] + ), + ) + + def __hash__(self) -> int: + """Get the hash of the var. + + Returns: + The hash of the var. + """ + return hash((self.__class__.__name__,)) + + @classmethod + def create( + cls, + value: Component, + _var_data: VarData | None = None, + ): + """Create a var from a value. + + Args: + value: The value of the var. + _var_data: Additional hooks and imports associated with the Var. + + Returns: + The var. + """ + return LiteralComponentVar( + _js_expr="", + _var_type=type(value), + _var_data=_var_data, + _var_value=value, + ) diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index 89665bb3199..278d9e0d5fb 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -5,11 +5,17 @@ from pathlib import Path from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple -from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf +from reflex.components.component import ( + Component, + ComponentNamespace, + MemoizationLeaf, + StatefulComponent, +) from reflex.components.el.elements.forms import Input from reflex.components.radix.themes.layout.box import Box from reflex.config import environment from reflex.constants import Dirs +from reflex.constants.compiler import Imports from reflex.event import ( CallableEventSpec, EventChain, @@ -19,9 +25,10 @@ call_script, parse_args_spec, ) +from reflex.utils import format from reflex.utils.imports import ImportVar from reflex.vars import VarData -from reflex.vars.base import CallableVar, LiteralVar, Var +from reflex.vars.base import CallableVar, LiteralVar, Var, get_unique_variable_name from reflex.vars.sequence import LiteralStringVar DEFAULT_UPLOAD_ID: str = "default" @@ -179,9 +186,7 @@ class Upload(MemoizationLeaf): library = "react-dropzone@14.2.10" - tag = "ReactDropzone" - - is_default = True + tag = "" # The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as # values. @@ -201,7 +206,7 @@ class Upload(MemoizationLeaf): min_size: Var[int] # Whether to allow multiple files to be uploaded. - multiple: Var[bool] = True # type: ignore + multiple: Var[bool] # Whether to disable click to upload. no_click: Var[bool] @@ -219,11 +224,12 @@ class Upload(MemoizationLeaf): on_drop: EventHandler[_on_drop_spec] @classmethod - def create(cls, *children, **props) -> Component: + def create(cls, *children, multiple=True, **props) -> Component: """Create an upload component. Args: *children: The children of the component. + multiple: Whether to allow multiple files to be uploaded. **props: The properties of the component. Returns: @@ -232,6 +238,8 @@ def create(cls, *children, **props) -> Component: # Mark the Upload component as used in the app. cls.is_used = True + props["multiple"] = multiple + # Apply the default classname given_class_name = props.pop("class_name", []) if isinstance(given_class_name, str): @@ -243,17 +251,6 @@ def create(cls, *children, **props) -> Component: upload_props = { key: value for key, value in props.items() if key in supported_props } - # The file input to use. - upload = Input.create(type="file") - upload.special_props = [Var(_js_expr="{...getInputProps()}", _var_type=None)] - - # The dropzone to use. - zone = Box.create( - upload, - *children, - **{k: v for k, v in props.items() if k not in supported_props}, - ) - zone.special_props = [Var(_js_expr="{...getRootProps()}", _var_type=None)] # Create the component. upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID) @@ -275,9 +272,71 @@ def create(cls, *children, **props) -> Component: ), ) upload_props["on_drop"] = on_drop + + input_props_unique_name = get_unique_variable_name() + root_props_unique_name = get_unique_variable_name() + + event_var, callback_str = StatefulComponent._get_memoized_event_triggers( + Box.create(on_click=upload_props["on_drop"]) + )["on_click"] + + upload_props["on_drop"] = event_var + + upload_props = { + format.to_camel_case(key): value for key, value in upload_props.items() + } + + var_data = VarData.merge( + VarData( + imports=Imports.EVENTS, + hooks={ + "const [addEvents, connectError] = useContext(EventLoopContext);": None + }, + ), + event_var._get_all_var_data(), + VarData( + hooks={ + callback_str: None, + f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} = useDropzone({ + str(Var.create({ + 'onDrop': event_var, + **upload_props, + })) + });": None, + }, + imports={ + "react-dropzone": "useDropzone", + **Imports.EVENTS, + }, + ), + ) + + # The file input to use. + upload = Input.create(type="file") + upload.special_props = [ + Var( + _js_expr=f"{{...{input_props_unique_name}()}}", + _var_type=None, + _var_data=var_data, + ) + ] + + # The dropzone to use. + zone = Box.create( + upload, + *children, + **{k: v for k, v in props.items() if k not in supported_props}, + ) + zone.special_props = [ + Var( + _js_expr=f"{{...{root_props_unique_name}()}}", + _var_type=None, + _var_data=var_data, + ) + ] + return super().create( zone, - **upload_props, ) @classmethod @@ -295,11 +354,6 @@ def _update_arg_tuple_for_on_drop(cls, arg_value: tuple[Var, Var]): return (arg_value[0], placeholder) return arg_value - def _render(self): - out = super()._render() - out.args = ("getRootProps", "getInputProps") - return out - @staticmethod def _get_app_wrap_components() -> dict[tuple[int, str], Component]: return { diff --git a/reflex/components/tags/tag.py b/reflex/components/tags/tag.py index d577abc6e1c..0587c61eda8 100644 --- a/reflex/components/tags/tag.py +++ b/reflex/components/tags/tag.py @@ -3,7 +3,7 @@ from __future__ import annotations import dataclasses -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Union from reflex.event import EventChain from reflex.utils import format, types @@ -23,9 +23,6 @@ class Tag: # The inner contents of the tag. contents: str = "" - # Args to pass to the tag. - args: Optional[Tuple[str, ...]] = None - # Special props that aren't key value pairs. special_props: List[Var] = dataclasses.field(default_factory=list) From d8962dbc086fcdc3711bdd73273f05df6b4dc855 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 22 Oct 2024 19:00:15 -0700 Subject: [PATCH 02/20] fix pyi --- reflex/components/core/upload.py | 4 ++-- reflex/components/core/upload.pyi | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index 278d9e0d5fb..a95bede10d8 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -224,7 +224,7 @@ class Upload(MemoizationLeaf): on_drop: EventHandler[_on_drop_spec] @classmethod - def create(cls, *children, multiple=True, **props) -> Component: + def create(cls, *children, **props) -> Component: """Create an upload component. Args: @@ -238,7 +238,7 @@ def create(cls, *children, multiple=True, **props) -> Component: # Mark the Upload component as used in the app. cls.is_used = True - props["multiple"] = multiple + props["multiple"].set_default(True) # Apply the default classname given_class_name = props.pop("class_name", []) diff --git a/reflex/components/core/upload.pyi b/reflex/components/core/upload.pyi index 5bbae892f04..dfe0b7e5e5a 100644 --- a/reflex/components/core/upload.pyi +++ b/reflex/components/core/upload.pyi @@ -6,7 +6,11 @@ from pathlib import Path from typing import Any, ClassVar, Dict, List, Optional, Union, overload -from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf +from reflex.components.component import ( + Component, + ComponentNamespace, + MemoizationLeaf, +) from reflex.constants import Dirs from reflex.event import ( CallableEventSpec, @@ -133,6 +137,7 @@ class Upload(MemoizationLeaf): Args: *children: The children of the component. + multiple: Whether to allow multiple files to be uploaded. accept: The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as values. supported MIME types: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types disabled: Whether the dropzone is disabled. max_files: The maximum number of files that can be uploaded. From f754b85c8d0788f812c89c0b4e005b4fde3b6df8 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 22 Oct 2024 19:22:26 -0700 Subject: [PATCH 03/20] use render --- reflex/components/component.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/reflex/components/component.py b/reflex/components/component.py index 8f15e1ba816..dcbfc43598c 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -2381,15 +2381,29 @@ def _cached_var_name(self) -> str: Returns: The name of the var. """ - tag = self._var_value._render() + tag = self._var_value.render() - props = Var.create(tag.props).to(ObjectVar) - for prop in tag.special_props: + props = {} + + special_props = [] + + for prop_str in tag["props"]: + if "=" not in prop_str: + special_props.append(Var(prop_str).to(ObjectVar)) + continue + prop = prop_str.index("=") + key = prop_str[:prop] + value = prop_str[prop + 2 : -1] + props[key] = value + + props = Var.create({Var.create(k): Var(v) for k, v in props.items()}) + + for prop in special_props: props = props.merge(prop) - contents = getattr(self._var_value, "contents", None) + contents = tag["contents"][1:-1] if tag["contents"] else None - tag_name = Var(tag.name) if tag.name else Var("Fragment") + tag_name = Var(tag.get("name", None) or "Fragment") return str( FunctionStringVar.create( @@ -2397,7 +2411,7 @@ def _cached_var_name(self) -> str: ).call( tag_name, props, - *([Var.create(contents)] if contents is not None else []), + *([Var(contents)] if contents is not None else []), *[Var.create(child) for child in self._var_value.children], ) ) From 67b8eb9b15bf520cb43db53b2df243168db1814b Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 22 Oct 2024 19:26:25 -0700 Subject: [PATCH 04/20] fix pyi --- reflex/components/core/upload.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index a95bede10d8..2de37fc0e4c 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -286,6 +286,14 @@ def create(cls, *children, **props) -> Component: format.to_camel_case(key): value for key, value in upload_props.items() } + use_dropzone_arguements = { + "onDrop": event_var, + **upload_props, + } + + left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} " + right_side = f"useDropzone({str(Var.create(use_dropzone_arguements))})" + var_data = VarData.merge( VarData( imports=Imports.EVENTS, @@ -297,12 +305,7 @@ def create(cls, *children, **props) -> Component: VarData( hooks={ callback_str: None, - f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} = useDropzone({ - str(Var.create({ - 'onDrop': event_var, - **upload_props, - })) - });": None, + f"{left_side} = {right_side};": None, }, imports={ "react-dropzone": "useDropzone", From c7a234b94de6d47e75771a2ab4b86d46c0387b21 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 22 Oct 2024 19:31:38 -0700 Subject: [PATCH 05/20] only render once --- reflex/components/component.py | 76 +++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/reflex/components/component.py b/reflex/components/component.py index dcbfc43598c..607598ddf93 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -2365,6 +2365,47 @@ def empty_component() -> Component: return Bare.create("") +def render_dict_to_var(tag: dict) -> Var: + """Convert a render dict to a Var. + + Args: + tag: The render dict. + + Returns: + The Var. + """ + props = {} + + special_props = [] + + for prop_str in tag["props"]: + if "=" not in prop_str: + special_props.append(Var(prop_str).to(ObjectVar)) + continue + prop = prop_str.index("=") + key = prop_str[:prop] + value = prop_str[prop + 2 : -1] + props[key] = value + + props = Var.create({Var.create(k): Var(v) for k, v in props.items()}) + + for prop in special_props: + props = props.merge(prop) + + contents = tag["contents"][1:-1] if tag["contents"] else None + + tag_name = Var(tag.get("name") or "Fragment") + + return FunctionStringVar.create( + "jsx", + ).call( + tag_name, + props, + *([Var(contents)] if contents is not None else []), + *[render_dict_to_var(child) for child in tag["children"]], + ) + + @dataclasses.dataclass( eq=False, frozen=True, @@ -2381,40 +2422,7 @@ def _cached_var_name(self) -> str: Returns: The name of the var. """ - tag = self._var_value.render() - - props = {} - - special_props = [] - - for prop_str in tag["props"]: - if "=" not in prop_str: - special_props.append(Var(prop_str).to(ObjectVar)) - continue - prop = prop_str.index("=") - key = prop_str[:prop] - value = prop_str[prop + 2 : -1] - props[key] = value - - props = Var.create({Var.create(k): Var(v) for k, v in props.items()}) - - for prop in special_props: - props = props.merge(prop) - - contents = tag["contents"][1:-1] if tag["contents"] else None - - tag_name = Var(tag.get("name", None) or "Fragment") - - return str( - FunctionStringVar.create( - "jsx", - ).call( - tag_name, - props, - *([Var(contents)] if contents is not None else []), - *[Var.create(child) for child in self._var_value.children], - ) - ) + return str(render_dict_to_var(self._var_value.render())) @cached_property_no_lock def _cached_get_all_var_data(self) -> VarData | None: From 2786bd5bff95bd2aaa4bc668747476b5293e197d Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 22 Oct 2024 19:32:49 -0700 Subject: [PATCH 06/20] add type ignore --- reflex/components/core/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index 2de37fc0e4c..791c0352ae0 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -277,7 +277,7 @@ def create(cls, *children, **props) -> Component: root_props_unique_name = get_unique_variable_name() event_var, callback_str = StatefulComponent._get_memoized_event_triggers( - Box.create(on_click=upload_props["on_drop"]) + Box.create(on_click=upload_props["on_drop"]) # type: ignore )["on_click"] upload_props["on_drop"] = event_var From 669894500b2eb48ad3cc98141bd063dcd6ee656f Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 22 Oct 2024 20:07:37 -0700 Subject: [PATCH 07/20] fix upload default value --- reflex/components/core/upload.py | 2 +- tests/units/components/test_component.py | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index 791c0352ae0..ad5bdc46b55 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -238,7 +238,7 @@ def create(cls, *children, **props) -> Component: # Mark the Upload component as used in the app. cls.is_used = True - props["multiple"].set_default(True) + props.setdefault("multiple", True) # Apply the default classname given_class_name = props.pop("class_name", []) diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py index b7b721a9243..c2d73aca526 100644 --- a/tests/units/components/test_component.py +++ b/tests/units/components/test_component.py @@ -642,21 +642,18 @@ def test_component_create_unallowed_types(children, test_component): "name": "Fragment", "props": [], "contents": "", - "args": None, "special_props": [], "children": [ { "name": "RadixThemesText", "props": ['as={"p"}'], "contents": "", - "args": None, "special_props": [], "children": [ { "name": "", "props": [], "contents": '{"first_text"}', - "args": None, "special_props": [], "children": [], "autofocus": False, @@ -671,15 +668,12 @@ def test_component_create_unallowed_types(children, test_component): ( (rx.text("first_text"), rx.text("second_text")), { - "args": None, "autofocus": False, "children": [ { - "args": None, "autofocus": False, "children": [ { - "args": None, "autofocus": False, "children": [], "contents": '{"first_text"}', @@ -694,11 +688,9 @@ def test_component_create_unallowed_types(children, test_component): "special_props": [], }, { - "args": None, "autofocus": False, "children": [ { - "args": None, "autofocus": False, "children": [], "contents": '{"second_text"}', @@ -722,15 +714,12 @@ def test_component_create_unallowed_types(children, test_component): ( (rx.text("first_text"), rx.box((rx.text("second_text"),))), { - "args": None, "autofocus": False, "children": [ { - "args": None, "autofocus": False, "children": [ { - "args": None, "autofocus": False, "children": [], "contents": '{"first_text"}', @@ -745,19 +734,15 @@ def test_component_create_unallowed_types(children, test_component): "special_props": [], }, { - "args": None, "autofocus": False, "children": [ { - "args": None, "autofocus": False, "children": [ { - "args": None, "autofocus": False, "children": [ { - "args": None, "autofocus": False, "children": [], "contents": '{"second_text"}', @@ -1117,10 +1102,10 @@ def test_component_with_only_valid_children(fixture, request): @pytest.mark.parametrize( "component,rendered", [ - (rx.text("hi"), '\n {"hi"}\n'), + (rx.text("hi"), '\n\n{"hi"}\n'), ( rx.box(rx.heading("test", size="3")), - '\n \n {"test"}\n\n', + '\n\n\n\n{"test"}\n\n', ), ], ) From 49ab17d6d609b8c2316e27522fbd0142a1187d35 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 23 Oct 2024 11:13:55 -0700 Subject: [PATCH 08/20] remove testcases if you don't pass them --- reflex/components/core/upload.py | 1 - reflex/components/core/upload.pyi | 1 - tests/integration/test_var_operations.py | 12 +- tests/units/components/forms/test_uploads.py | 205 ------------------- 4 files changed, 6 insertions(+), 213 deletions(-) delete mode 100644 tests/units/components/forms/test_uploads.py diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index ad5bdc46b55..718e266b8fd 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -229,7 +229,6 @@ def create(cls, *children, **props) -> Component: Args: *children: The children of the component. - multiple: Whether to allow multiple files to be uploaded. **props: The properties of the component. Returns: diff --git a/reflex/components/core/upload.pyi b/reflex/components/core/upload.pyi index dfe0b7e5e5a..ded465e71c1 100644 --- a/reflex/components/core/upload.pyi +++ b/reflex/components/core/upload.pyi @@ -137,7 +137,6 @@ class Upload(MemoizationLeaf): Args: *children: The children of the component. - multiple: Whether to allow multiple files to be uploaded. accept: The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as values. supported MIME types: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types disabled: Whether the dropzone is disabled. max_files: The maximum number of files that can be uploaded. diff --git a/tests/integration/test_var_operations.py b/tests/integration/test_var_operations.py index 919a39f3b31..30ebad96d8a 100644 --- a/tests/integration/test_var_operations.py +++ b/tests/integration/test_var_operations.py @@ -710,7 +710,7 @@ def test_var_operations(driver, var_operations: AppHarness): ("float_neq_str", "true"), # float, list ("float_or_list", "10.5"), - ("float_and_list", "[1,2]"), + ("float_and_list", "12"), ("float_eq_list", "false"), ("float_neq_list", "true"), # float, dict @@ -739,7 +739,7 @@ def test_var_operations(driver, var_operations: AppHarness): ("str_eq_int", "false"), ("str_neq_int", "true"), # str, list - ("str_and_list", "[1,2]"), + ("str_and_list", "12"), ("str_or_list", "first"), ("str_eq_list", "false"), ("str_neq_list", "true"), @@ -757,7 +757,7 @@ def test_var_operations(driver, var_operations: AppHarness): ("list_eq_list", "false"), ("list_neq_list", "true"), ("list_and_list", "[3,4]"), - ("list_or_list", "[1,2]"), + ("list_or_list", "12"), ("list_contains", "true"), ("list_pluck", '["obj_1","obj_2"]'), ("list_reverse", "[2,1]"), @@ -769,13 +769,13 @@ def test_var_operations(driver, var_operations: AppHarness): ("list_join_range4", "0,1,2"), # list, int ("list_mult_int", "[1,2,1,2,1,2,1,2,1,2]"), - ("list_or_int", "[1,2]"), + ("list_or_int", "12"), ("list_and_int", "10"), ("list_eq_int", "false"), ("list_neq_int", "true"), # list, dict ("list_and_dict", '{"1":2}'), - ("list_or_dict", "[1,2]"), + ("list_or_dict", "12"), ("list_eq_dict", "false"), ("list_neq_dict", "true"), # dict, dict @@ -793,7 +793,7 @@ def test_var_operations(driver, var_operations: AppHarness): ("foreach_list_ix", "1\n2"), ("foreach_list_nested", "1\n1\n2"), # rx.memo component with state - ("memo_comp", "[1,2]10"), + ("memo_comp", "1210"), ("memo_comp_nested", "[3,4]5"), # foreach in a match ("foreach_in_match", "first\nsecond\nthird"), diff --git a/tests/units/components/forms/test_uploads.py b/tests/units/components/forms/test_uploads.py deleted file mode 100644 index 3b2ee014f28..00000000000 --- a/tests/units/components/forms/test_uploads.py +++ /dev/null @@ -1,205 +0,0 @@ -import pytest - -import reflex as rx - - -@pytest.fixture -def upload_root_component(): - """A test upload component function. - - Returns: - A test upload component function. - """ - - def upload_root_component(): - return rx.upload.root( - rx.button("select file"), - rx.text("Drag and drop files here or click to select files"), - border="1px dotted black", - ) - - return upload_root_component() - - -@pytest.fixture -def upload_component(): - """A test upload component function. - - Returns: - A test upload component function. - """ - - def upload_component(): - return rx.upload( - rx.button("select file"), - rx.text("Drag and drop files here or click to select files"), - border="1px dotted black", - ) - - return upload_component() - - -@pytest.fixture -def upload_component_id_special(): - def upload_component(): - return rx.upload( - rx.button("select file"), - rx.text("Drag and drop files here or click to select files"), - border="1px dotted black", - id="#spec!`al-_98ID", - ) - - return upload_component() - - -@pytest.fixture -def upload_component_with_props(): - """A test upload component with props function. - - Returns: - A test upload component with props function. - """ - - def upload_component_with_props(): - return rx.upload( - rx.button("select file"), - rx.text("Drag and drop files here or click to select files"), - border="1px dotted black", - no_drag=True, - max_files=2, - ) - - return upload_component_with_props() - - -def test_upload_root_component_render(upload_root_component): - """Test that the render function is set correctly. - - Args: - upload_root_component: component fixture - """ - upload = upload_root_component.render() - - # upload - assert upload["name"] == "ReactDropzone" - assert upload["props"] == [ - 'id={"default"}', - "multiple={true}", - "onDrop={e => setFilesById(filesById => {\n" - " const updatedFilesById = Object.assign({}, filesById);\n" - ' updatedFilesById["default"] = e;\n' - " return updatedFilesById;\n" - " })\n" - " }", - "ref={ref_default}", - ] - assert upload["args"] == ("getRootProps", "getInputProps") - - # box inside of upload - [box] = upload["children"] - assert box["name"] == "RadixThemesBox" - assert box["props"] == [ - 'className={"rx-Upload"}', - 'css={({ ["border"] : "1px dotted black" })}', - "{...getRootProps()}", - ] - - # input, button and text inside of box - [input, button, text] = box["children"] - assert input["name"] == "input" - assert input["props"] == ['type={"file"}', "{...getInputProps()}"] - - assert button["name"] == "RadixThemesButton" - assert button["children"][0]["contents"] == '{"select file"}' - - assert text["name"] == "RadixThemesText" - assert ( - text["children"][0]["contents"] - == '{"Drag and drop files here or click to select files"}' - ) - - -def test_upload_component_render(upload_component): - """Test that the render function is set correctly. - - Args: - upload_component: component fixture - """ - upload = upload_component.render() - - # upload - assert upload["name"] == "ReactDropzone" - assert upload["props"] == [ - 'id={"default"}', - "multiple={true}", - "onDrop={e => setFilesById(filesById => {\n" - " const updatedFilesById = Object.assign({}, filesById);\n" - ' updatedFilesById["default"] = e;\n' - " return updatedFilesById;\n" - " })\n" - " }", - "ref={ref_default}", - ] - assert upload["args"] == ("getRootProps", "getInputProps") - - # box inside of upload - [box] = upload["children"] - assert box["name"] == "RadixThemesBox" - assert box["props"] == [ - 'className={"rx-Upload"}', - 'css={({ ["border"] : "1px dotted black", ["padding"] : "5em", ["textAlign"] : "center" })}', - "{...getRootProps()}", - ] - - # input, button and text inside of box - [input, button, text] = box["children"] - assert input["name"] == "input" - assert input["props"] == ['type={"file"}', "{...getInputProps()}"] - - assert button["name"] == "RadixThemesButton" - assert button["children"][0]["contents"] == '{"select file"}' - - assert text["name"] == "RadixThemesText" - assert ( - text["children"][0]["contents"] - == '{"Drag and drop files here or click to select files"}' - ) - - -def test_upload_component_with_props_render(upload_component_with_props): - """Test that the render function is set correctly. - - Args: - upload_component_with_props: component fixture - """ - upload = upload_component_with_props.render() - - assert upload["props"] == [ - 'id={"default"}', - "maxFiles={2}", - "multiple={true}", - "noDrag={true}", - "onDrop={e => setFilesById(filesById => {\n" - " const updatedFilesById = Object.assign({}, filesById);\n" - ' updatedFilesById["default"] = e;\n' - " return updatedFilesById;\n" - " })\n" - " }", - "ref={ref_default}", - ] - - -def test_upload_component_id_with_special_chars(upload_component_id_special): - upload = upload_component_id_special.render() - - assert upload["props"] == [ - r'id={"#spec!`al-_98ID"}', - "multiple={true}", - "onDrop={e => setFilesById(filesById => {\n" - " const updatedFilesById = Object.assign({}, filesById);\n" - ' updatedFilesById["#spec!`al-_98ID"] = e;\n' - " return updatedFilesById;\n" - " })\n" - " }", - "ref={ref__spec_al__98ID}", - ] From 56b703d2dedccbb28355df6f1ef23def4ae379fa Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 23 Oct 2024 14:57:33 -0700 Subject: [PATCH 09/20] improve behavior --- reflex/components/base/bare.py | 69 ++++++++++++++++++++++++++- reflex/components/component.py | 87 ++++++++++++++++++++++++++++++---- 2 files changed, 146 insertions(+), 10 deletions(-) diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index 8b4d7b2169c..d2c5a1f6ce2 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -4,9 +4,10 @@ from typing import Any, Iterator -from reflex.components.component import Component +from reflex.components.component import Component, LiteralComponentVar from reflex.components.tags import Tag from reflex.components.tags.tagless import Tagless +from reflex.utils.imports import ParsedImportDict from reflex.vars import BooleanVar, ObjectVar, Var @@ -31,6 +32,72 @@ def create(cls, contents: Any) -> Component: contents = str(contents) if contents is not None else "" return cls(contents=contents) # type: ignore + def _get_all_hooks_internal(self) -> dict[str, None]: + """Include the hooks for the component. + + Returns: + The hooks for the component. + """ + hooks = super()._get_all_hooks_internal() + if isinstance(self.contents, LiteralComponentVar): + hooks |= self.contents._var_value._get_all_hooks_internal() + return hooks + + def _get_all_hooks(self) -> dict[str, None]: + """Include the hooks for the component. + + Returns: + The hooks for the component. + """ + hooks = super()._get_all_hooks() + if isinstance(self.contents, LiteralComponentVar): + hooks |= self.contents._var_value._get_all_hooks() + return hooks + + def _get_all_imports(self) -> ParsedImportDict: + """Include the imports for the component. + + Returns: + The imports for the component. + """ + imports = super()._get_all_imports() + if isinstance(self.contents, LiteralComponentVar): + imports |= self.contents._var_value._get_all_imports() + return imports + + def _get_all_dynamic_imports(self) -> set[str]: + """Get dynamic imports for the component. + + Returns: + The dynamic imports. + """ + dynamic_imports = super()._get_all_dynamic_imports() + if isinstance(self.contents, LiteralComponentVar): + dynamic_imports |= self.contents._var_value._get_all_dynamic_imports() + return dynamic_imports + + def _get_all_custom_code(self) -> set[str]: + """Get custom code for the component. + + Returns: + The custom code. + """ + custom_code = super()._get_all_custom_code() + if isinstance(self.contents, LiteralComponentVar): + custom_code |= self.contents._var_value._get_all_custom_code() + return custom_code + + def _get_all_refs(self) -> set[str]: + """Get the refs for the children of the component. + + Returns: + The refs for the children. + """ + refs = super()._get_all_refs() + if isinstance(self.contents, LiteralComponentVar): + refs |= self.contents._var_value._get_all_refs() + return refs + def _render(self) -> Tag: if isinstance(self.contents, Var): if isinstance(self.contents, (BooleanVar, ObjectVar)): diff --git a/reflex/components/component.py b/reflex/components/component.py index 607598ddf93..f85fb8ee4e8 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -65,7 +65,8 @@ Var, cached_property_no_lock, ) -from reflex.vars.function import FunctionStringVar +from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar +from reflex.vars.number import ternary_operation from reflex.vars.object import ObjectVar from reflex.vars.sequence import LiteralArrayVar @@ -2350,7 +2351,7 @@ def create(cls, *children, **props) -> Component: load_dynamic_serializer() -class ComponentVar(Var[Component], python_types=Component): +class ComponentVar(Var[Component], python_types=BaseComponent): """A Var that represents a Component.""" @@ -2365,15 +2366,68 @@ def empty_component() -> Component: return Bare.create("") -def render_dict_to_var(tag: dict) -> Var: +def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) -> Var: """Convert a render dict to a Var. Args: tag: The render dict. + imported_names: The names of the imported components. Returns: The Var. """ + if not isinstance(tag, dict): + if isinstance(tag, Component): + return render_dict_to_var(tag.render(), imported_names) + return Var.create(tag) + + if "iterable" in tag: + function_return = Var.create( + [ + render_dict_to_var(child.render(), imported_names) + for child in tag["children"] + ] + ) + + func = ArgsFunctionOperation.create( + (tag["arg_var_name"], tag["index_var_name"]), + function_return, + ) + + return FunctionStringVar.create("Array.prototype.map.call").call( + tag["iterable"] + if not isinstance(tag["iterable"], ObjectVar) + else tag["iterable"].items(), + func, + ) + + if tag["name"] == "match": + element = tag["cond"] + + conditionals = tag["default"] + + for case in tag["match_cases"][::-1]: + condition = case[0].to_string() == element.to_string() + for pattern in case[1:-1]: + condition = condition | (pattern.to_string() == element.to_string()) + + conditionals = ternary_operation( + condition, + case[-1], + conditionals, + ) + + return conditionals + + if "cond" in tag: + return ternary_operation( + tag["cond"], + render_dict_to_var(tag["true_value"], imported_names), + render_dict_to_var(tag["false_value"], imported_names) + if tag["false_value"] is not None + else Var.create(None), + ) + props = {} special_props = [] @@ -2394,7 +2448,16 @@ def render_dict_to_var(tag: dict) -> Var: contents = tag["contents"][1:-1] if tag["contents"] else None - tag_name = Var(tag.get("name") or "Fragment") + raw_tag_name = tag.get("name") + tag_name = Var(raw_tag_name or "Fragment") + + tag_name = ( + Var.create(raw_tag_name) + if raw_tag_name + and raw_tag_name.split(".")[0] not in imported_names + and raw_tag_name.lower() == raw_tag_name + else tag_name + ) return FunctionStringVar.create( "jsx", @@ -2402,7 +2465,7 @@ def render_dict_to_var(tag: dict) -> Var: tag_name, props, *([Var(contents)] if contents is not None else []), - *[render_dict_to_var(child) for child in tag["children"]], + *[render_dict_to_var(child, imported_names) for child in tag["children"]], ) @@ -2413,7 +2476,7 @@ def render_dict_to_var(tag: dict) -> Var: class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar): """A Var that represents a Component.""" - _var_value: Component = dataclasses.field(default_factory=empty_component) + _var_value: BaseComponent = dataclasses.field(default_factory=empty_component) @cached_property_no_lock def _cached_var_name(self) -> str: @@ -2422,7 +2485,13 @@ def _cached_var_name(self) -> str: Returns: The name of the var. """ - return str(render_dict_to_var(self._var_value.render())) + var_data = self._get_all_var_data() + if var_data is not None: + # flatten imports + imported_names = {j.alias or j.name for i in var_data.imports for j in i[1]} + else: + imported_names = set() + return str(render_dict_to_var(self._var_value.render(), imported_names)) @cached_property_no_lock def _cached_get_all_var_data(self) -> VarData | None: @@ -2440,7 +2509,7 @@ def _cached_get_all_var_data(self) -> VarData | None: } ), VarData( - imports=self._var_value._get_all_imports(collapse=True), + imports=self._var_value._get_all_imports(), ), *( [ @@ -2463,7 +2532,7 @@ def __hash__(self) -> int: Returns: The hash of the var. """ - return hash((self.__class__.__name__,)) + return hash((self.__class__.__name__, self._js_expr)) @classmethod def create( From 3dacafe60c687e398a95c6e9b68c27cb396331da Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 23 Oct 2024 15:04:02 -0700 Subject: [PATCH 10/20] fix render --- tests/integration/test_var_operations.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/test_var_operations.py b/tests/integration/test_var_operations.py index 30ebad96d8a..cae56e1a8d7 100644 --- a/tests/integration/test_var_operations.py +++ b/tests/integration/test_var_operations.py @@ -710,7 +710,7 @@ def test_var_operations(driver, var_operations: AppHarness): ("float_neq_str", "true"), # float, list ("float_or_list", "10.5"), - ("float_and_list", "12"), + ("float_and_list", "[1,2]"), ("float_eq_list", "false"), ("float_neq_list", "true"), # float, dict @@ -739,7 +739,7 @@ def test_var_operations(driver, var_operations: AppHarness): ("str_eq_int", "false"), ("str_neq_int", "true"), # str, list - ("str_and_list", "12"), + ("str_and_list", "[1,2]"), ("str_or_list", "first"), ("str_eq_list", "false"), ("str_neq_list", "true"), @@ -757,7 +757,7 @@ def test_var_operations(driver, var_operations: AppHarness): ("list_eq_list", "false"), ("list_neq_list", "true"), ("list_and_list", "[3,4]"), - ("list_or_list", "12"), + ("list_or_list", "[1,2]"), ("list_contains", "true"), ("list_pluck", '["obj_1","obj_2"]'), ("list_reverse", "[2,1]"), @@ -769,13 +769,13 @@ def test_var_operations(driver, var_operations: AppHarness): ("list_join_range4", "0,1,2"), # list, int ("list_mult_int", "[1,2,1,2,1,2,1,2,1,2]"), - ("list_or_int", "12"), + ("list_or_int", "[1,2]"), ("list_and_int", "10"), ("list_eq_int", "false"), ("list_neq_int", "true"), # list, dict ("list_and_dict", '{"1":2}'), - ("list_or_dict", "12"), + ("list_or_dict", "[1,2]"), ("list_eq_dict", "false"), ("list_neq_dict", "true"), # dict, dict @@ -794,7 +794,7 @@ def test_var_operations(driver, var_operations: AppHarness): ("foreach_list_nested", "1\n1\n2"), # rx.memo component with state ("memo_comp", "1210"), - ("memo_comp_nested", "[3,4]5"), + ("memo_comp_nested", "345"), # foreach in a match ("foreach_in_match", "first\nsecond\nthird"), ] From 668e4022a42bfbdb5e58557cacb067ffd8072e81 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 23 Oct 2024 15:36:45 -0700 Subject: [PATCH 11/20] that's not how icon buttons work --- tests/integration/test_form_submit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_form_submit.py b/tests/integration/test_form_submit.py index a020a7e15e7..3bfcf6e8c07 100644 --- a/tests/integration/test_form_submit.py +++ b/tests/integration/test_form_submit.py @@ -121,7 +121,7 @@ def index(): on_change=rx.console_log, ), rx.button("Submit", type_="submit"), - rx.icon_button(FormState.val, icon=rx.icon(tag="plus")), + rx.icon_button(rx.icon(tag="plus")), ), on_submit=FormState.form_submit, custom_attrs={"action": "/invalid"}, From 5c99656ca94e036ea93867b6111a33a14b651e44 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 23 Oct 2024 18:12:22 -0700 Subject: [PATCH 12/20] upgrade to next js 15 and remove babel and enable turbo --- .../.templates/jinja/web/pages/_app.js.jinja2 | 4 ++-- .../jinja/web/utils/context.js.jinja2 | 2 +- .../radix_themes_color_mode_provider.js | 2 +- reflex/.templates/web/jsconfig.json | 3 ++- reflex/.templates/web/utils/state.js | 10 ++++---- reflex/app.py | 2 +- reflex/compiler/compiler.py | 6 ++--- reflex/components/base/bare.py | 4 +++- reflex/components/component.py | 24 ++++++++----------- reflex/components/core/banner.py | 4 ++-- reflex/components/core/client_side_routing.py | 2 +- reflex/components/core/clipboard.py | 2 +- reflex/components/core/cond.py | 2 +- reflex/components/core/upload.py | 8 +++---- reflex/components/datadisplay/dataeditor.py | 2 +- reflex/components/dynamic.py | 10 +++++--- reflex/components/el/elements/forms.py | 2 +- reflex/components/radix/themes/base.py | 4 ++-- reflex/components/sonner/toast.py | 2 +- reflex/constants/compiler.py | 4 ++-- reflex/constants/installer.py | 5 ++-- reflex/experimental/client_state.py | 2 +- reflex/style.py | 2 +- reflex/vars/base.py | 6 ++--- reflex/vars/number.py | 2 +- tests/units/components/core/test_banner.py | 14 +++++------ 26 files changed, 65 insertions(+), 65 deletions(-) diff --git a/reflex/.templates/jinja/web/pages/_app.js.jinja2 b/reflex/.templates/jinja/web/pages/_app.js.jinja2 index c893e19e2f0..21cfd921a14 100644 --- a/reflex/.templates/jinja/web/pages/_app.js.jinja2 +++ b/reflex/.templates/jinja/web/pages/_app.js.jinja2 @@ -1,11 +1,11 @@ {% extends "web/pages/base_page.js.jinja2" %} {% block early_imports %} -import '/styles/styles.css' +import '$/styles/styles.css' {% endblock %} {% block declaration %} -import { EventLoopProvider, StateProvider, defaultColorMode } from "/utils/context.js"; +import { EventLoopProvider, StateProvider, defaultColorMode } from "$/utils/context.js"; import { ThemeProvider } from 'next-themes' {% for library_alias, library_path in window_libraries %} import * as {{library_alias}} from "{{library_path}}"; diff --git a/reflex/.templates/jinja/web/utils/context.js.jinja2 b/reflex/.templates/jinja/web/utils/context.js.jinja2 index 8faecd9d2e9..29328cca5b4 100644 --- a/reflex/.templates/jinja/web/utils/context.js.jinja2 +++ b/reflex/.templates/jinja/web/utils/context.js.jinja2 @@ -1,5 +1,5 @@ import { createContext, useContext, useMemo, useReducer, useState } from "react" -import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "/utils/state.js" +import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "$/utils/state.js" {% if initial_state %} export const initialState = {{ initial_state|json_dumps }} diff --git a/reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js b/reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js index 04df060593d..1e6ea1a5635 100644 --- a/reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +++ b/reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js @@ -5,7 +5,7 @@ import { defaultColorMode, isDevMode, lastCompiledTimeStamp -} from "/utils/context.js"; +} from "$/utils/context.js"; export default function RadixThemesColorModeProvider({ children }) { const { theme, resolvedTheme, setTheme } = useTheme(); diff --git a/reflex/.templates/web/jsconfig.json b/reflex/.templates/web/jsconfig.json index 3c8a3257fbc..3fcb35ba604 100644 --- a/reflex/.templates/web/jsconfig.json +++ b/reflex/.templates/web/jsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "baseUrl": ".", "paths": { + "$/*": ["*"], "@/*": ["public/*"] } } -} \ No newline at end of file +} diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 24092f23560..5d62aab149c 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -2,7 +2,7 @@ import axios from "axios"; import io from "socket.io-client"; import JSON5 from "json5"; -import env from "/env.json"; +import env from "$/env.json"; import Cookies from "universal-cookie"; import { useEffect, useRef, useState } from "react"; import Router, { useRouter } from "next/router"; @@ -13,9 +13,8 @@ import { state_name, exception_state_name, } from "utils/context.js"; -import debounce from "/utils/helpers/debounce"; -import throttle from "/utils/helpers/throttle"; -import * as Babel from "@babel/standalone"; +import debounce from "$/utils/helpers/debounce"; +import throttle from "$/utils/helpers/throttle"; // Endpoint URLs. const EVENTURL = env.EVENT; @@ -139,8 +138,7 @@ export const evalReactComponent = async (component) => { if (!window.React && window.__reflex) { window.React = window.__reflex.react; } - const output = Babel.transform(component, { presets: ["react"] }).code; - const encodedJs = encodeURIComponent(output); + const encodedJs = encodeURIComponent(component); const dataUri = "data:text/javascript;charset=utf-8," + encodedJs; const module = await eval(`import(dataUri)`); return module.default; diff --git a/reflex/app.py b/reflex/app.py index abf0b5d4110..e524d40ad25 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -679,7 +679,7 @@ def _get_frontend_packages(self, imports: Dict[str, set[ImportVar]]): for i, tags in imports.items() if i not in constants.PackageJson.DEPENDENCIES and i not in constants.PackageJson.DEV_DEPENDENCIES - and not any(i.startswith(prefix) for prefix in ["/", ".", "next/"]) + and not any(i.startswith(prefix) for prefix in ["/", "$/", ".", "next/"]) and i != "" and any(tag.install for tag in tags) } diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 909299635c5..21b83af9f71 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -67,8 +67,8 @@ def _compile_app(app_root: Component) -> str: window_libraries = [ (_normalize_library_name(name), name) for name in bundled_libraries ] + [ - ("utils_context", f"/{constants.Dirs.UTILS}/context"), - ("utils_state", f"/{constants.Dirs.UTILS}/state"), + ("utils_context", f"$/{constants.Dirs.UTILS}/context"), + ("utils_state", f"$/{constants.Dirs.UTILS}/state"), ] return templates.APP_ROOT.render( @@ -228,7 +228,7 @@ def _compile_components( """ imports = { "react": [ImportVar(tag="memo")], - f"/{constants.Dirs.STATE_PATH}": [ImportVar(tag="E"), ImportVar(tag="isTrue")], + f"$/{constants.Dirs.STATE_PATH}": [ImportVar(tag="E"), ImportVar(tag="isTrue")], } component_renders = [] diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index d2c5a1f6ce2..c70b4c844c1 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -62,7 +62,9 @@ def _get_all_imports(self) -> ParsedImportDict: """ imports = super()._get_all_imports() if isinstance(self.contents, LiteralComponentVar): - imports |= self.contents._var_value._get_all_imports() + var_data = self.contents._get_all_var_data() + if var_data: + imports |= {k: list(v) for k, v in var_data.imports} return imports def _get_all_dynamic_imports(self) -> set[str]: diff --git a/reflex/components/component.py b/reflex/components/component.py index f85fb8ee4e8..7407080efbc 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -1314,7 +1314,9 @@ def _get_hooks_imports(self) -> ParsedImportDict: if self._get_ref_hook(): # Handle hooks needed for attaching react refs to DOM nodes. _imports.setdefault("react", set()).add(ImportVar(tag="useRef")) - _imports.setdefault(f"/{Dirs.STATE_PATH}", set()).add(ImportVar(tag="refs")) + _imports.setdefault(f"$/{Dirs.STATE_PATH}", set()).add( + ImportVar(tag="refs") + ) if self._get_mount_lifecycle_hook(): # Handle hooks for `on_mount` / `on_unmount`. @@ -1671,7 +1673,7 @@ class CustomComponent(Component): """A custom user-defined component.""" # Use the components library. - library = f"/{Dirs.COMPONENTS_PATH}" + library = f"$/{Dirs.COMPONENTS_PATH}" # The function that creates the component. component_fn: Callable[..., Component] = Component.create @@ -2511,18 +2513,12 @@ def _cached_get_all_var_data(self) -> VarData | None: VarData( imports=self._var_value._get_all_imports(), ), - *( - [ - VarData( - imports={ - "react": [ - ImportVar(tag="Fragment"), - ], - } - ) - ] - if not self._var_value.tag - else [] + VarData( + imports={ + "react": [ + ImportVar(tag="Fragment"), + ], + } ), ) diff --git a/reflex/components/core/banner.py b/reflex/components/core/banner.py index 29897cffa70..8a37b0bf7f2 100644 --- a/reflex/components/core/banner.py +++ b/reflex/components/core/banner.py @@ -66,8 +66,8 @@ def create(cls) -> Var: _js_expr="getBackendURL(env.EVENT).href", _var_data=VarData( imports={ - "/env.json": [ImportVar(tag="env", is_default=True)], - f"/{Dirs.STATE_PATH}": [ImportVar(tag="getBackendURL")], + "$/env.json": [ImportVar(tag="env", is_default=True)], + f"$/{Dirs.STATE_PATH}": [ImportVar(tag="getBackendURL")], }, ), _var_type=WebsocketTargetURL, diff --git a/reflex/components/core/client_side_routing.py b/reflex/components/core/client_side_routing.py index efa622f8196..342c69632e0 100644 --- a/reflex/components/core/client_side_routing.py +++ b/reflex/components/core/client_side_routing.py @@ -21,7 +21,7 @@ class ClientSideRouting(Component): """The client-side routing component.""" - library = "/utils/client_side_routing" + library = "$/utils/client_side_routing" tag = "useClientSideRouting" def add_hooks(self) -> list[str]: diff --git a/reflex/components/core/clipboard.py b/reflex/components/core/clipboard.py index 88aa2d145ba..cce0af4a72a 100644 --- a/reflex/components/core/clipboard.py +++ b/reflex/components/core/clipboard.py @@ -67,7 +67,7 @@ def add_imports(self) -> dict[str, ImportVar]: The import dict for the component. """ return { - "/utils/helpers/paste.js": ImportVar( + "$/utils/helpers/paste.js": ImportVar( tag="usePasteHandler", is_default=True ), } diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index 1590875d3f9..e0c47f0fe03 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -15,7 +15,7 @@ from reflex.vars.number import ternary_operation _IS_TRUE_IMPORT: ImportDict = { - f"/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], + f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], } diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index 718e266b8fd..787cca9d08c 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -36,7 +36,7 @@ upload_files_context_var_data: VarData = VarData( imports={ "react": "useContext", - f"/{Dirs.CONTEXTS_PATH}": "UploadFilesContext", + f"$/{Dirs.CONTEXTS_PATH}": "UploadFilesContext", }, hooks={ "const [filesById, setFilesById] = useContext(UploadFilesContext);": None, @@ -141,8 +141,8 @@ def get_upload_dir() -> Path: _js_expr="getBackendURL(env.UPLOAD)", _var_data=VarData( imports={ - f"/{Dirs.STATE_PATH}": "getBackendURL", - "/env.json": ImportVar(tag="env", is_default=True), + f"$/{Dirs.STATE_PATH}": "getBackendURL", + "$/env.json": ImportVar(tag="env", is_default=True), } ), ).to(str) @@ -177,7 +177,7 @@ def _on_drop_spec(files: Var) -> Tuple[Var[Any]]: class UploadFilesProvider(Component): """AppWrap component that provides a dict of selected files by ID via useContext.""" - library = f"/{Dirs.CONTEXTS_PATH}" + library = f"$/{Dirs.CONTEXTS_PATH}" tag = "UploadFilesProvider" diff --git a/reflex/components/datadisplay/dataeditor.py b/reflex/components/datadisplay/dataeditor.py index b9bd4cebf43..1caf053b5f6 100644 --- a/reflex/components/datadisplay/dataeditor.py +++ b/reflex/components/datadisplay/dataeditor.py @@ -344,7 +344,7 @@ def add_imports(self) -> ImportDict: return { "": f"{format.format_library_name(self.library)}/dist/index.css", self.library: "GridCellKind", - "/utils/helpers/dataeditor.js": ImportVar( + "$/utils/helpers/dataeditor.js": ImportVar( tag="formatDataEditorCells", is_default=False, install=False ), } diff --git a/reflex/components/dynamic.py b/reflex/components/dynamic.py index 9c498fb6193..a133db5a108 100644 --- a/reflex/components/dynamic.py +++ b/reflex/components/dynamic.py @@ -63,6 +63,9 @@ def make_component(component: Component) -> str: """ # Causes a circular import, so we import here. from reflex.compiler import templates, utils + from reflex.components.base.bare import Bare + + component = Bare.create(Var.create(component)) rendered_components = {} # Include dynamic imports in the shared component. @@ -127,14 +130,15 @@ def make_component(component: Component) -> str: module_code_lines[ix] = line.replace( "export function", "export default function", 1 ) + line_stripped = line.strip() + if line_stripped.startswith("{") and line_stripped.endswith("}"): + module_code_lines[ix] = line_stripped[1:-1] module_code_lines.insert(0, "const React = window.__reflex.react;") return "\n".join( [ "//__reflex_evaluate", - "/** @jsx jsx */", - "const { jsx } = window.__reflex['@emotion/react']", *module_code_lines, ] ) @@ -157,7 +161,7 @@ def evaluate_component(js_string: Var[str]) -> Var[Component]: merge_var_data=VarData.merge( VarData( imports={ - f"/{constants.Dirs.STATE_PATH}": [ + f"$/{constants.Dirs.STATE_PATH}": [ imports.ImportVar(tag="evalReactComponent"), ], "react": [ diff --git a/reflex/components/el/elements/forms.py b/reflex/components/el/elements/forms.py index a343991d538..084aa8f8e96 100644 --- a/reflex/components/el/elements/forms.py +++ b/reflex/components/el/elements/forms.py @@ -187,7 +187,7 @@ def add_imports(self) -> ImportDict: """ return { "react": "useCallback", - f"/{Dirs.STATE_PATH}": ["getRefValue", "getRefValues"], + f"$/{Dirs.STATE_PATH}": ["getRefValue", "getRefValues"], } def add_hooks(self) -> list[str]: diff --git a/reflex/components/radix/themes/base.py b/reflex/components/radix/themes/base.py index e41c5e7b0fc..acca1dce8eb 100644 --- a/reflex/components/radix/themes/base.py +++ b/reflex/components/radix/themes/base.py @@ -221,7 +221,7 @@ def add_imports(self) -> ImportDict | list[ImportDict]: The import dict. """ _imports: ImportDict = { - "/utils/theme.js": [ImportVar(tag="theme", is_default=True)], + "$/utils/theme.js": [ImportVar(tag="theme", is_default=True)], } if get_config().tailwind is None: # When tailwind is disabled, import the radix-ui styles directly because they will @@ -265,7 +265,7 @@ def add_imports(self) -> dict[str, str]: class RadixThemesColorModeProvider(Component): """Next-themes integration for radix themes components.""" - library = "/components/reflex/radix_themes_color_mode_provider.js" + library = "$/components/reflex/radix_themes_color_mode_provider.js" tag = "RadixThemesColorModeProvider" is_default = True diff --git a/reflex/components/sonner/toast.py b/reflex/components/sonner/toast.py index 02c320ac6b6..65f1157d37d 100644 --- a/reflex/components/sonner/toast.py +++ b/reflex/components/sonner/toast.py @@ -251,7 +251,7 @@ def add_hooks(self) -> list[Var | str]: _js_expr=f"{toast_ref} = toast", _var_data=VarData( imports={ - "/utils/state": [ImportVar(tag="refs")], + "$/utils/state": [ImportVar(tag="refs")], self.library: [ImportVar(tag="toast", install=False)], } ), diff --git a/reflex/constants/compiler.py b/reflex/constants/compiler.py index 318a93783f2..b7ffef1613f 100644 --- a/reflex/constants/compiler.py +++ b/reflex/constants/compiler.py @@ -114,8 +114,8 @@ class Imports(SimpleNamespace): EVENTS = { "react": [ImportVar(tag="useContext")], - f"/{Dirs.CONTEXTS_PATH}": [ImportVar(tag="EventLoopContext")], - f"/{Dirs.STATE_PATH}": [ImportVar(tag=CompileVars.TO_EVENT)], + f"$/{Dirs.CONTEXTS_PATH}": [ImportVar(tag="EventLoopContext")], + f"$/{Dirs.STATE_PATH}": [ImportVar(tag=CompileVars.TO_EVENT)], } diff --git a/reflex/constants/installer.py b/reflex/constants/installer.py index 766dcf5beea..ae71055066f 100644 --- a/reflex/constants/installer.py +++ b/reflex/constants/installer.py @@ -165,7 +165,7 @@ class PackageJson(SimpleNamespace): class Commands(SimpleNamespace): """The commands to define in package.json.""" - DEV = "next dev" + DEV = "next dev --turbo" EXPORT = "next build" EXPORT_SITEMAP = "next build && next-sitemap" PROD = "next start" @@ -173,11 +173,10 @@ class Commands(SimpleNamespace): PATH = "package.json" DEPENDENCIES = { - "@babel/standalone": "7.25.8", "@emotion/react": "11.13.3", "axios": "1.7.7", "json5": "2.2.3", - "next": "14.2.15", + "next": "15.0.1", "next-sitemap": "4.2.3", "next-themes": "0.3.0", "react": "18.3.1", diff --git a/reflex/experimental/client_state.py b/reflex/experimental/client_state.py index c7b2260a1be..698829d3381 100644 --- a/reflex/experimental/client_state.py +++ b/reflex/experimental/client_state.py @@ -21,7 +21,7 @@ _refs_import = { - f"/{constants.Dirs.STATE_PATH}": [ImportVar(tag="refs")], + f"$/{constants.Dirs.STATE_PATH}": [ImportVar(tag="refs")], } diff --git a/reflex/style.py b/reflex/style.py index 8e24e9b6b2b..f0ee8c6a707 100644 --- a/reflex/style.py +++ b/reflex/style.py @@ -23,7 +23,7 @@ # Reference the global ColorModeContext color_mode_imports = { - f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="ColorModeContext")], + f"$/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="ColorModeContext")], "react": [ImportVar(tag="useContext")], } diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 6df8eec6fbf..2007bc09166 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -217,7 +217,7 @@ def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarDa ): None }, imports={ - f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")], + f"$/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")], "react": [ImportVar(tag="useContext")], }, ) @@ -956,7 +956,7 @@ def as_ref(self) -> Var: _js_expr="refs", _var_data=VarData( imports={ - f"/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")] + f"$/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")] } ), ).to(ObjectVar, Dict[str, str]) @@ -2530,7 +2530,7 @@ def get_uuid_string_var() -> Var: unique_uuid_var = get_unique_variable_name() unique_uuid_var_data = VarData( imports={ - f"/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore + f"$/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore "react": "useMemo", }, hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None}, diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 77c728d1373..e403e63e470 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -1090,7 +1090,7 @@ def create(cls, value: bool, _var_data: VarData | None = None): _IS_TRUE_IMPORT: ImportDict = { - f"/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], + f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], } diff --git a/tests/units/components/core/test_banner.py b/tests/units/components/core/test_banner.py index 02b03090299..7add913eabb 100644 --- a/tests/units/components/core/test_banner.py +++ b/tests/units/components/core/test_banner.py @@ -12,7 +12,7 @@ def test_websocket_target_url(): var_data = url._get_all_var_data() assert var_data is not None assert sorted(tuple((key for key, _ in var_data.imports))) == sorted( - ("/utils/state", "/env.json") + ("$/utils/state", "$/env.json") ) @@ -22,10 +22,10 @@ def test_connection_banner(): assert sorted(tuple(_imports)) == sorted( ( "react", - "/utils/context", - "/utils/state", + "$/utils/context", + "$/utils/state", "@radix-ui/themes@^3.0.0", - "/env.json", + "$/env.json", ) ) @@ -40,10 +40,10 @@ def test_connection_modal(): assert sorted(tuple(_imports)) == sorted( ( "react", - "/utils/context", - "/utils/state", + "$/utils/context", + "$/utils/state", "@radix-ui/themes@^3.0.0", - "/env.json", + "$/env.json", ) ) From 39bbbd94c9905d6a6afc43b40e2fe6fb31bcf838 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 23 Oct 2024 18:13:40 -0700 Subject: [PATCH 13/20] upload is a silly guy --- reflex/components/core/upload.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/core/upload.pyi b/reflex/components/core/upload.pyi index ded465e71c1..e5c73bd147f 100644 --- a/reflex/components/core/upload.pyi +++ b/reflex/components/core/upload.pyi @@ -38,8 +38,8 @@ uploaded_files_url_prefix = Var( _js_expr="getBackendURL(env.UPLOAD)", _var_data=VarData( imports={ - f"/{Dirs.STATE_PATH}": "getBackendURL", - "/env.json": ImportVar(tag="env", is_default=True), + f"$/{Dirs.STATE_PATH}": "getBackendURL", + "$/env.json": ImportVar(tag="env", is_default=True), } ), ).to(str) From 2fb0b449fda3583e460406827f572544ce048dda Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 23 Oct 2024 18:29:52 -0700 Subject: [PATCH 14/20] woops --- reflex/components/dynamic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/dynamic.py b/reflex/components/dynamic.py index a133db5a108..4df89330a52 100644 --- a/reflex/components/dynamic.py +++ b/reflex/components/dynamic.py @@ -93,7 +93,7 @@ def make_component(component: Component) -> str: for lib, names in component._get_all_imports().items(): formatted_lib_name = format_library_name(lib) if ( - not lib.startswith((".", "/")) + not lib.startswith((".", "$/")) and not lib.startswith("http") and formatted_lib_name not in libs_in_window ): @@ -109,7 +109,7 @@ def make_component(component: Component) -> str: # Rewrite imports from `/` to destructure from window for ix, line in enumerate(module_code_lines[:]): if line.startswith("import "): - if 'from "/' in line: + if 'from "$/' in line: module_code_lines[ix] = ( line.replace("import ", "const ", 1).replace( " from ", " = window['__reflex'][", 1 From ecaea4c38d31eeedcff87516e25b79fe6394eca3 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 23 Oct 2024 19:44:12 -0700 Subject: [PATCH 15/20] how did this work before --- reflex/.templates/jinja/web/utils/context.js.jinja2 | 2 ++ reflex/.templates/web/utils/state.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/reflex/.templates/jinja/web/utils/context.js.jinja2 b/reflex/.templates/jinja/web/utils/context.js.jinja2 index 29328cca5b4..2428cfa9d2e 100644 --- a/reflex/.templates/jinja/web/utils/context.js.jinja2 +++ b/reflex/.templates/jinja/web/utils/context.js.jinja2 @@ -59,6 +59,8 @@ export const initialEvents = () => [ {% else %} export const state_name = undefined +export const exception_state_name = undefined + export const onLoadInternalEvent = () => [] export const initialEvents = () => [] diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 5d62aab149c..7d76b080ac0 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -12,7 +12,7 @@ import { onLoadInternalEvent, state_name, exception_state_name, -} from "utils/context.js"; +} from "$/utils/context.js"; import debounce from "$/utils/helpers/debounce"; import throttle from "$/utils/helpers/throttle"; From 35008af125e717fd5a6fcfa8fa933766ccefb281 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 24 Oct 2024 15:33:55 -0700 Subject: [PATCH 16/20] set env variable --- .github/workflows/integration_tests.yml | 34 ++++++++++++------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 106ac138398..e1c31946db4 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -2,13 +2,13 @@ name: integration-tests on: push: - branches: ['main'] + branches: ["main"] paths-ignore: - - '**/*.md' + - "**/*.md" pull_request: - branches: ['main'] + branches: ["main"] paths-ignore: - - '**/*.md' + - "**/*.md" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.id }} @@ -27,9 +27,9 @@ env: # TODO: can we fix windows encoding natively within reflex? Bug above can hit real users too (less common, but possible) # - Catch encoding errors when printing logs # - Best effort print lines that contain illegal chars (map to some default char, etc.) - PYTHONIOENCODING: 'utf8' + PYTHONIOENCODING: "utf8" TELEMETRY_ENABLED: false - NODE_OPTIONS: '--max_old_space_size=8192' + NODE_OPTIONS: "--max_old_space_size=8192" PR_TITLE: ${{ github.event.pull_request.title }} jobs: @@ -43,17 +43,17 @@ jobs: matrix: # Show OS combos first in GUI os: [ubuntu-latest, windows-latest] - python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0'] + python-version: ["3.9.18", "3.10.13", "3.11.5", "3.12.0"] exclude: - os: windows-latest - python-version: '3.10.13' + python-version: "3.10.13" - os: windows-latest - python-version: '3.9.18' + python-version: "3.9.18" include: - os: windows-latest - python-version: '3.10.11' + python-version: "3.10.11" - os: windows-latest - python-version: '3.9.13' + python-version: "3.9.13" runs-on: ${{ matrix.os }} steps: @@ -115,18 +115,17 @@ jobs: --branch-name "${{ github.head_ref || github.ref_name }}" --pr-id "${{ github.event.pull_request.id }}" --app-name "counter" - - reflex-web: strategy: fail-fast: false matrix: # Show OS combos first in GUI os: [ubuntu-latest, windows-latest] - python-version: ['3.10.11', '3.11.4'] + python-version: ["3.10.11", "3.11.4"] env: - REFLEX_WEB_WINDOWS_OVERRIDE: '1' + REFLEX_WEB_WINDOWS_OVERRIDE: "1" + REFLEX_WEB_WINDOWS_MAX_ROUTES: "100" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -162,13 +161,13 @@ jobs: --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}" --app-name "reflex-web" --path ./reflex-web/.web - + reflex-web-macos: if: github.event_name == 'push' && github.ref == 'refs/heads/main' strategy: fail-fast: false matrix: - python-version: ['3.11.5', '3.12.0'] + python-version: ["3.11.5", "3.12.0"] runs-on: macos-12 steps: - uses: actions/checkout@v4 @@ -202,4 +201,3 @@ jobs: --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}" --app-name "reflex-web" --path ./reflex-web/.web - \ No newline at end of file From d3ec565c58eb12380bc74725da7ae9312a927e8e Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 24 Oct 2024 15:50:07 -0700 Subject: [PATCH 17/20] lower it even more --- .github/workflows/integration_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index e1c31946db4..55f1b1e8ff5 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -125,7 +125,7 @@ jobs: env: REFLEX_WEB_WINDOWS_OVERRIDE: "1" - REFLEX_WEB_WINDOWS_MAX_ROUTES: "100" + REFLEX_WEB_WINDOWS_MAX_ROUTES: "75" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From 5f6e5380e439b43dc98c063929ea9f0454889f42 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 24 Oct 2024 20:56:48 -0700 Subject: [PATCH 18/20] lower it even more --- .github/workflows/integration_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 55f1b1e8ff5..ccc39fc9f3b 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -125,7 +125,7 @@ jobs: env: REFLEX_WEB_WINDOWS_OVERRIDE: "1" - REFLEX_WEB_WINDOWS_MAX_ROUTES: "75" + REFLEX_WEB_WINDOWS_MAX_ROUTES: "50" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From 867190cde693ca5fa120e850bf1ab4e49c9b7695 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 25 Oct 2024 09:55:36 -0700 Subject: [PATCH 19/20] lower it even more --- .github/workflows/integration_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index ccc39fc9f3b..00e5c1c6721 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -125,7 +125,7 @@ jobs: env: REFLEX_WEB_WINDOWS_OVERRIDE: "1" - REFLEX_WEB_WINDOWS_MAX_ROUTES: "50" + REFLEX_WEB_WINDOWS_MAX_ROUTES: "25" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From 923e60f1590418d797423a7cac344ef18e4883c8 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 25 Oct 2024 13:43:53 -0700 Subject: [PATCH 20/20] only do literals as component vars --- .github/workflows/integration_tests.yml | 34 +++++++++++++------------ reflex/constants/installer.py | 5 ++-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 00e5c1c6721..106ac138398 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -2,13 +2,13 @@ name: integration-tests on: push: - branches: ["main"] + branches: ['main'] paths-ignore: - - "**/*.md" + - '**/*.md' pull_request: - branches: ["main"] + branches: ['main'] paths-ignore: - - "**/*.md" + - '**/*.md' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.id }} @@ -27,9 +27,9 @@ env: # TODO: can we fix windows encoding natively within reflex? Bug above can hit real users too (less common, but possible) # - Catch encoding errors when printing logs # - Best effort print lines that contain illegal chars (map to some default char, etc.) - PYTHONIOENCODING: "utf8" + PYTHONIOENCODING: 'utf8' TELEMETRY_ENABLED: false - NODE_OPTIONS: "--max_old_space_size=8192" + NODE_OPTIONS: '--max_old_space_size=8192' PR_TITLE: ${{ github.event.pull_request.title }} jobs: @@ -43,17 +43,17 @@ jobs: matrix: # Show OS combos first in GUI os: [ubuntu-latest, windows-latest] - python-version: ["3.9.18", "3.10.13", "3.11.5", "3.12.0"] + python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0'] exclude: - os: windows-latest - python-version: "3.10.13" + python-version: '3.10.13' - os: windows-latest - python-version: "3.9.18" + python-version: '3.9.18' include: - os: windows-latest - python-version: "3.10.11" + python-version: '3.10.11' - os: windows-latest - python-version: "3.9.13" + python-version: '3.9.13' runs-on: ${{ matrix.os }} steps: @@ -115,17 +115,18 @@ jobs: --branch-name "${{ github.head_ref || github.ref_name }}" --pr-id "${{ github.event.pull_request.id }}" --app-name "counter" + + reflex-web: strategy: fail-fast: false matrix: # Show OS combos first in GUI os: [ubuntu-latest, windows-latest] - python-version: ["3.10.11", "3.11.4"] + python-version: ['3.10.11', '3.11.4'] env: - REFLEX_WEB_WINDOWS_OVERRIDE: "1" - REFLEX_WEB_WINDOWS_MAX_ROUTES: "25" + REFLEX_WEB_WINDOWS_OVERRIDE: '1' runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -161,13 +162,13 @@ jobs: --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}" --app-name "reflex-web" --path ./reflex-web/.web - + reflex-web-macos: if: github.event_name == 'push' && github.ref == 'refs/heads/main' strategy: fail-fast: false matrix: - python-version: ["3.11.5", "3.12.0"] + python-version: ['3.11.5', '3.12.0'] runs-on: macos-12 steps: - uses: actions/checkout@v4 @@ -201,3 +202,4 @@ jobs: --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}" --app-name "reflex-web" --path ./reflex-web/.web + \ No newline at end of file diff --git a/reflex/constants/installer.py b/reflex/constants/installer.py index ae71055066f..766dcf5beea 100644 --- a/reflex/constants/installer.py +++ b/reflex/constants/installer.py @@ -165,7 +165,7 @@ class PackageJson(SimpleNamespace): class Commands(SimpleNamespace): """The commands to define in package.json.""" - DEV = "next dev --turbo" + DEV = "next dev" EXPORT = "next build" EXPORT_SITEMAP = "next build && next-sitemap" PROD = "next start" @@ -173,10 +173,11 @@ class Commands(SimpleNamespace): PATH = "package.json" DEPENDENCIES = { + "@babel/standalone": "7.25.8", "@emotion/react": "11.13.3", "axios": "1.7.7", "json5": "2.2.3", - "next": "15.0.1", + "next": "14.2.15", "next-sitemap": "4.2.3", "next-themes": "0.3.0", "react": "18.3.1",