Преглед изворни кода

in theory, one can get rid of jsx, but that would be craaazy

Khaleel Al-Adhami пре 2 месеци
родитељ
комит
7d26ba751a

+ 10 - 16
reflex/.templates/jinja/web/pages/_app.js.jinja2

@@ -40,25 +40,19 @@ export function Layout({children}) {
     window["__reflex"] = windowImports;
     window["__reflex"] = windowImports;
   }, []);
   }, []);
 
 
-  return (
-    <AppLayout>
-      <ThemeProvider defaultTheme={ defaultColorMode } attribute="class">
-        <StateProvider>
-          <EventLoopProvider>
-            <AppWrap>
-              {children}
-            </AppWrap>
-          </EventLoopProvider>
-        </StateProvider>
-      </ThemeProvider>
-    </AppLayout>
-  )
+  return jsx(AppLayout, {},
+    jsx(ThemeProvider, {defaultTheme: defaultColorMode, attribute: "class"},
+      jsx(StateProvider, {},
+        jsx(EventLoopProvider, {},
+          jsx(AppWrap, {}, children)
+        )
+      )
+    )
+  );
 }
 }
 
 
 export default function App() {
 export default function App() {
-  return (
-    <Outlet />
-  );
+  return jsx(Outlet, {});
 }
 }
 
 
 {% endblock %}
 {% endblock %}

+ 33 - 29
reflex/.templates/jinja/web/pages/utils.js.jinja2

@@ -25,7 +25,11 @@
 {#     component: component dictionary #}
 {#     component: component dictionary #}
 {% macro render_self_close_tag(component) %}
 {% macro render_self_close_tag(component) %}
 {%- if component.name|length %}
 {%- if component.name|length %}
-<{{ component.name }} {{- render_props(component.props) }}{% if component.autofocus %} ref={focusRef} {% endif %}/>
+jsx(
+  {{ component.name }},
+  {{- render_props(component.props) }},
+  {{- component.contents }}
+)
 {%- else %}
 {%- else %}
   {{- component.contents }}
   {{- component.contents }}
 {%- endif %}
 {%- endif %}
@@ -35,12 +39,16 @@
 {# Args: #}
 {# Args: #}
 {#     component: component dictionary #}
 {#     component: component dictionary #}
 {% macro render_tag(component) %}
 {% macro render_tag(component) %}
-<{{component.name}} {{- render_props(component.props) }}>
-{{ component.contents }}
-{% for child in component.children %}
-{{ render(child) }}
-{% endfor %}
-</{{component.name}}>
+jsx({{ component.name }}, {{- render_props(component.props) -}},
+  {%- if component.contents|length -%}
+    {{- component.contents -}},
+  {%- endif -%}
+  {%- for child in component.children -%}
+    {%- if child is mapping or child|length %}
+      {{- render(child) -}},
+    {%- endif -%}
+  {%- endfor -%}
+)
 {%- endmacro %}
 {%- endmacro %}
 
 
 
 
@@ -48,11 +56,11 @@
 {# Args: #}
 {# Args: #}
 {#     component: component dictionary #}
 {#     component: component dictionary #}
 {% macro render_condition_tag(component) %}
 {% macro render_condition_tag(component) %}
-{ {{- component.cond_state }} ? (
+{{- component.cond_state }} ? (
   {{ render(component.true_value) }}
   {{ render(component.true_value) }}
 ) : (
 ) : (
   {{ render(component.false_value) }}
   {{ render(component.false_value) }}
-)}
+)
 {%- endmacro %}
 {%- endmacro %}
 
 
 
 
@@ -60,41 +68,37 @@
 {# Args: #}
 {# Args: #}
 {#     component: component dictionary #}
 {#     component: component dictionary #}
 {% macro render_iterable_tag(component) %}
 {% macro render_iterable_tag(component) %}
-<>{ {{ component.iterable_state }}.map(({{ component.arg_name }}, {{ component.arg_index }}) => (
+{{ component.iterable_state }}.map(({{ component.arg_name }}, {{ component.arg_index }}) => (
   {% for child in component.children %}
   {% for child in component.children %}
   {{ render(child) }}
   {{ render(child) }}
   {% endfor %}
   {% endfor %}
-))}</>
+))
 {%- endmacro %}
 {%- endmacro %}
 
 
 
 
 {# Rendering props of a component. #}
 {# Rendering props of a component. #}
 {# Args: #}
 {# Args: #}
 {#     component: component dictionary #}
 {#     component: component dictionary #}
-{% macro render_props(props) %}
-{% if props|length %} {{ props|join(" ") }}{% endif %}
-{% endmacro %}
+{% macro render_props(props) %}{ {%- if props|length -%} {{- props|join(", ") -}}{%- endif -%} }{% endmacro %}
 
 
 {# Rendering Match component. #}
 {# Rendering Match component. #}
 {# Args: #}
 {# Args: #}
 {#     component: component dictionary #}
 {#     component: component dictionary #}
 {% macro render_match_tag(component) %}
 {% macro render_match_tag(component) %}
-{
-    (() => {
-        switch (JSON.stringify({{ component.cond._js_expr }})) {
-        {% for case in component.match_cases %}
-            {% for condition in case[:-1] %}
-                case JSON.stringify({{ condition._js_expr }}):
-            {% endfor %}
-                return {{ render(case[-1]) }};
-                break;
-        {% endfor %}
-            default:
-                return {{ render(component.default) }};
-                break;
-        }
-    })()
+(() => {
+  switch (JSON.stringify({{ component.cond._js_expr }})) {
+  {% for case in component.match_cases %}
+    {% for condition in case[:-1] %}
+      case JSON.stringify({{ condition._js_expr }}):
+    {% endfor %}
+      return {{ render(case[-1]) }};
+      break;
+  {% endfor %}
+    default:
+      return {{ render(component.default) }};
+      break;
   }
   }
+})()
 {%- endmacro %}
 {%- endmacro %}
 
 
 
 

+ 2 - 2
reflex/.templates/web/app/routes.js

@@ -2,8 +2,8 @@ import { route } from "@react-router/dev/routes";
 import { flatRoutes } from "@react-router/fs-routes";
 import { flatRoutes } from "@react-router/fs-routes";
 
 
 export default [
 export default [
-  route("*", "routes/404.jsx"),
+  route("*", "routes/404.js"),
   ...(await flatRoutes({
   ...(await flatRoutes({
-    ignoredRouteFiles: ["routes/404.jsx"],
+    ignoredRouteFiles: ["routes/404.js"],
   })),
   })),
 ];
 ];

+ 18 - 18
reflex/.templates/web/utils/state.js

@@ -229,8 +229,8 @@ export const applyEvent = async (event, socket, navigate) => {
       a.href = eval?.(
       a.href = eval?.(
         event.payload.url.replace(
         event.payload.url.replace(
           "getBackendURL(env.UPLOAD)",
           "getBackendURL(env.UPLOAD)",
-          `"${getBackendURL(env.UPLOAD)}"`
-        )
+          `"${getBackendURL(env.UPLOAD)}"`,
+        ),
       );
       );
     }
     }
     a.download = event.payload.filename;
     a.download = event.payload.filename;
@@ -344,7 +344,7 @@ export const applyRestEvent = async (event, socket) => {
       event.payload.files,
       event.payload.files,
       event.payload.upload_id,
       event.payload.upload_id,
       event.payload.on_upload_progress,
       event.payload.on_upload_progress,
-      socket
+      socket,
     );
     );
     return false;
     return false;
   }
   }
@@ -363,7 +363,7 @@ export const queueEvents = async (events, socket, prepend) => {
     events = [
     events = [
       ...events,
       ...events,
       ...Array.from({ length: event_queue.length }).map(() =>
       ...Array.from({ length: event_queue.length }).map(() =>
-        event_queue.shift()
+        event_queue.shift(),
       ),
       ),
     ];
     ];
   }
   }
@@ -422,7 +422,7 @@ export const connect = async (
   dispatch,
   dispatch,
   transports,
   transports,
   setConnectErrors,
   setConnectErrors,
-  client_storage = {}
+  client_storage = {},
 ) => {
 ) => {
   // Get backend URL object from the endpoint.
   // Get backend URL object from the endpoint.
   const endpoint = getBackendURL(EVENTURL);
   const endpoint = getBackendURL(EVENTURL);
@@ -524,7 +524,7 @@ export const uploadFiles = async (
   files,
   files,
   upload_id,
   upload_id,
   on_upload_progress,
   on_upload_progress,
-  socket
+  socket,
 ) => {
 ) => {
   // return if there's no file to upload
   // return if there's no file to upload
   if (files === undefined || files.length === 0) {
   if (files === undefined || files.length === 0) {
@@ -629,7 +629,7 @@ export const Event = (
   name,
   name,
   payload = {},
   payload = {},
   event_actions = {},
   event_actions = {},
-  handler = null
+  handler = null,
 ) => {
 ) => {
   return { name, payload, handler, event_actions };
   return { name, payload, handler, event_actions };
 };
 };
@@ -656,7 +656,7 @@ export const hydrateClientStorage = (client_storage) => {
     for (const state_key in client_storage.local_storage) {
     for (const state_key in client_storage.local_storage) {
       const options = client_storage.local_storage[state_key];
       const options = client_storage.local_storage[state_key];
       const local_storage_value = localStorage.getItem(
       const local_storage_value = localStorage.getItem(
-        options.name || state_key
+        options.name || state_key,
       );
       );
       if (local_storage_value !== null) {
       if (local_storage_value !== null) {
         client_storage_values[state_key] = local_storage_value;
         client_storage_values[state_key] = local_storage_value;
@@ -667,7 +667,7 @@ export const hydrateClientStorage = (client_storage) => {
     for (const state_key in client_storage.session_storage) {
     for (const state_key in client_storage.session_storage) {
       const session_options = client_storage.session_storage[state_key];
       const session_options = client_storage.session_storage[state_key];
       const session_storage_value = sessionStorage.getItem(
       const session_storage_value = sessionStorage.getItem(
-        session_options.name || state_key
+        session_options.name || state_key,
       );
       );
       if (session_storage_value != null) {
       if (session_storage_value != null) {
         client_storage_values[state_key] = session_storage_value;
         client_storage_values[state_key] = session_storage_value;
@@ -692,7 +692,7 @@ export const hydrateClientStorage = (client_storage) => {
 const applyClientStorageDelta = (client_storage, delta) => {
 const applyClientStorageDelta = (client_storage, delta) => {
   // find the main state and check for is_hydrated
   // find the main state and check for is_hydrated
   const unqualified_states = Object.keys(delta).filter(
   const unqualified_states = Object.keys(delta).filter(
-    (key) => key.split(".").length === 1
+    (key) => key.split(".").length === 1,
   );
   );
   if (unqualified_states.length === 1) {
   if (unqualified_states.length === 1) {
     const main_state = delta[unqualified_states[0]];
     const main_state = delta[unqualified_states[0]];
@@ -726,7 +726,7 @@ const applyClientStorageDelta = (client_storage, delta) => {
         const session_options = client_storage.session_storage[state_key];
         const session_options = client_storage.session_storage[state_key];
         sessionStorage.setItem(
         sessionStorage.setItem(
           session_options.name || state_key,
           session_options.name || state_key,
-          delta[substate][key]
+          delta[substate][key],
         );
         );
       }
       }
     }
     }
@@ -746,7 +746,7 @@ const applyClientStorageDelta = (client_storage, delta) => {
 export const useEventLoop = (
 export const useEventLoop = (
   dispatch,
   dispatch,
   initial_events = () => [],
   initial_events = () => [],
-  client_storage = {}
+  client_storage = {},
 ) => {
 ) => {
   const socket = useRef(null);
   const socket = useRef(null);
   const location = useLocation();
   const location = useLocation();
@@ -763,7 +763,7 @@ export const useEventLoop = (
 
 
     event_actions = events.reduce(
     event_actions = events.reduce(
       (acc, e) => ({ ...acc, ...e.event_actions }),
       (acc, e) => ({ ...acc, ...e.event_actions }),
-      event_actions ?? {}
+      event_actions ?? {},
     );
     );
 
 
     const _e = args.filter((o) => o?.preventDefault !== undefined)[0];
     const _e = args.filter((o) => o?.preventDefault !== undefined)[0];
@@ -791,7 +791,7 @@ export const useEventLoop = (
       debounce(
       debounce(
         combined_name,
         combined_name,
         () => queueEvents(events, socket),
         () => queueEvents(events, socket),
-        event_actions.debounce
+        event_actions.debounce,
       );
       );
     } else {
     } else {
       queueEvents(events, socket);
       queueEvents(events, socket);
@@ -811,7 +811,7 @@ export const useEventLoop = (
           },
           },
         })),
         })),
         socket,
         socket,
-        true
+        true,
       );
       );
       sentHydrate.current = true;
       sentHydrate.current = true;
     }
     }
@@ -857,7 +857,7 @@ export const useEventLoop = (
           dispatch,
           dispatch,
           ["websocket"],
           ["websocket"],
           setConnectErrors,
           setConnectErrors,
-          client_storage
+          client_storage,
         );
         );
       }
       }
     }
     }
@@ -905,7 +905,7 @@ export const useEventLoop = (
         vars[storage_to_state_map[e.key]] = e.newValue;
         vars[storage_to_state_map[e.key]] = e.newValue;
         const event = Event(
         const event = Event(
           `${state_name}.reflex___state____update_vars_internal_state.update_vars_internal`,
           `${state_name}.reflex___state____update_vars_internal_state.update_vars_internal`,
-          { vars: vars }
+          { vars: vars },
         );
         );
         addEvents([event], e);
         addEvents([event], e);
       }
       }
@@ -994,7 +994,7 @@ export const getRefValues = (refs) => {
   return refs.map((ref) =>
   return refs.map((ref) =>
     ref.current
     ref.current
       ? ref.current.value || ref.current.getAttribute("aria-valuenow")
       ? ref.current.value || ref.current.getAttribute("aria-valuenow")
-      : null
+      : null,
   );
   );
 };
 };
 
 

+ 5 - 5
reflex/compiler/utils.py

@@ -13,11 +13,11 @@ from urllib.parse import urlparse
 from pydantic.v1.fields import ModelField
 from pydantic.v1.fields import ModelField
 
 
 from reflex import constants
 from reflex import constants
-from reflex.components.base import Description, Image, Meta, Scripts, Title
+from reflex.components.base import Description, Image, Scripts
 from reflex.components.base.document import Links, ScrollRestoration
 from reflex.components.base.document import Links, ScrollRestoration
 from reflex.components.base.document import Meta as ReactMeta
 from reflex.components.base.document import Meta as ReactMeta
 from reflex.components.component import Component, ComponentStyle, CustomComponent
 from reflex.components.component import Component, ComponentStyle, CustomComponent
-from reflex.components.el.elements.metadata import Head
+from reflex.components.el.elements.metadata import Head, Meta, Title
 from reflex.components.el.elements.other import Html
 from reflex.components.el.elements.other import Html
 from reflex.components.el.elements.sectioning import Body
 from reflex.components.el.elements.sectioning import Body
 from reflex.istate.storage import Cookie, LocalStorage, SessionStorage
 from reflex.istate.storage import Cookie, LocalStorage, SessionStorage
@@ -401,7 +401,7 @@ def get_page_path(path: str) -> str:
         get_web_dir()
         get_web_dir()
         / constants.Dirs.PAGES
         / constants.Dirs.PAGES
         / constants.Dirs.ROUTES
         / constants.Dirs.ROUTES
-        / (path + constants.Ext.JSX)
+        / (path + constants.Ext.JS)
     )
     )
 
 
 
 
@@ -449,7 +449,7 @@ def get_components_path() -> str:
     return str(
     return str(
         get_web_dir()
         get_web_dir()
         / constants.Dirs.UTILS
         / constants.Dirs.UTILS
-        / (constants.PageNames.COMPONENTS + constants.Ext.JSX),
+        / (constants.PageNames.COMPONENTS + constants.Ext.JS),
     )
     )
 
 
 
 
@@ -462,7 +462,7 @@ def get_stateful_components_path() -> str:
     return str(
     return str(
         get_web_dir()
         get_web_dir()
         / constants.Dirs.UTILS
         / constants.Dirs.UTILS
-        / (constants.PageNames.STATEFUL_COMPONENTS + constants.Ext.JSX)
+        / (constants.PageNames.STATEFUL_COMPONENTS + constants.Ext.JS)
     )
     )
 
 
 
 

+ 4 - 3
reflex/components/base/bare.py

@@ -2,6 +2,7 @@
 
 
 from __future__ import annotations
 from __future__ import annotations
 
 
+import json
 from typing import Any, Iterator, Sequence
 from typing import Any, Iterator, Sequence
 
 
 from reflex.components.component import BaseComponent, Component, ComponentStyle
 from reflex.components.component import BaseComponent, Component, ComponentStyle
@@ -170,9 +171,9 @@ class Bare(Component):
     def _render(self) -> Tag:
     def _render(self) -> Tag:
         if isinstance(self.contents, Var):
         if isinstance(self.contents, Var):
             if isinstance(self.contents, (BooleanVar, ObjectVar)):
             if isinstance(self.contents, (BooleanVar, ObjectVar)):
-                return Tagless(contents=f"{{{self.contents.to_string()!s}}}")
-            return Tagless(contents=f"{{{self.contents!s}}}")
-        return Tagless(contents=str(self.contents))
+                return Tagless(contents=f"{self.contents.to_string()!s}")
+            return Tagless(contents=f"{self.contents!s}")
+        return Tagless(contents=f'"{json.dumps(self.contents)}"')
 
 
     def _add_style_recursive(
     def _add_style_recursive(
         self, style: ComponentStyle, theme: Component | None = None
         self, style: ComponentStyle, theme: Component | None = None

+ 17 - 16
reflex/components/component.py

@@ -276,6 +276,9 @@ class Component(BaseComponent, ABC):
     # The alias for the tag.
     # The alias for the tag.
     alias: str | None = pydantic.v1.Field(default_factory=lambda: None)
     alias: str | None = pydantic.v1.Field(default_factory=lambda: None)
 
 
+    # Whether the component is a global scope tag. True for tags like `html`, `head`, `body`.
+    _is_tag_in_global_scope: bool = pydantic.PrivateAttr(default_factory=lambda: False)
+
     # Whether the import is default or named.
     # Whether the import is default or named.
     is_default: bool | None = pydantic.v1.Field(default_factory=lambda: False)
     is_default: bool | None = pydantic.v1.Field(default_factory=lambda: False)
 
 
@@ -668,9 +671,13 @@ class Component(BaseComponent, ABC):
         Returns:
         Returns:
             The tag to render.
             The tag to render.
         """
         """
+        name = (self.tag if not self.alias else self.alias) or ""
+        if self._is_tag_in_global_scope and self.library is None:
+            name = '"' + name + '"'
+
         # Create the base tag.
         # Create the base tag.
         tag = Tag(
         tag = Tag(
-            name=(self.tag if not self.alias else self.alias) or "",
+            name=name,
             special_props=self.special_props,
             special_props=self.special_props,
         )
         )
 
 
@@ -1390,11 +1397,13 @@ class Component(BaseComponent, ABC):
         Returns:
         Returns:
             The imports needed by the component.
             The imports needed by the component.
         """
         """
-        _imports = {}
+        _imports = {
+            "@emotion/react": ImportVar(tag="jsx"),
+        }
 
 
         # Import this component's tag from the main library.
         # Import this component's tag from the main library.
         if self.library is not None and self.tag is not None:
         if self.library is not None and self.tag is not None:
-            _imports[self.library] = {self.import_var}
+            _imports[self.library] = self.import_var
 
 
         # Get static imports required for event processing.
         # Get static imports required for event processing.
         event_imports = Imports.EVENTS if self.event_triggers else {}
         event_imports = Imports.EVENTS if self.event_triggers else {}
@@ -1420,7 +1429,7 @@ class Component(BaseComponent, ABC):
         return imports.merge_imports(
         return imports.merge_imports(
             self._get_dependencies_imports(),
             self._get_dependencies_imports(),
             self._get_hooks_imports(),
             self._get_hooks_imports(),
-            _imports,
+            {**_imports},
             event_imports,
             event_imports,
             *var_imports,
             *var_imports,
             *added_import_dicts,
             *added_import_dicts,
@@ -2534,12 +2543,12 @@ def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) ->
     special_props = []
     special_props = []
 
 
     for prop_str in tag["props"]:
     for prop_str in tag["props"]:
-        if "=" not in prop_str:
+        if ":" not in prop_str:
             special_props.append(Var(prop_str).to(ObjectVar))
             special_props.append(Var(prop_str).to(ObjectVar))
             continue
             continue
-        prop = prop_str.index("=")
+        prop = prop_str.index(":")
         key = prop_str[:prop]
         key = prop_str[:prop]
-        value = prop_str[prop + 2 : -1]
+        value = prop_str[prop + 1 :]
         props[key] = value
         props[key] = value
 
 
     props = Var.create({Var.create(k): Var(v) for k, v in props.items()})
     props = Var.create({Var.create(k): Var(v) for k, v in props.items()})
@@ -2547,19 +2556,11 @@ def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) ->
     for prop in special_props:
     for prop in special_props:
         props = props.merge(prop)
         props = props.merge(prop)
 
 
-    contents = tag["contents"][1:-1] if tag["contents"] else None
+    contents = tag["contents"] if tag["contents"] else None
 
 
     raw_tag_name = tag.get("name")
     raw_tag_name = tag.get("name")
     tag_name = Var(raw_tag_name or "Fragment")
     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(
     return FunctionStringVar.create(
         "jsx",
         "jsx",
     ).call(
     ).call(

+ 4 - 0
reflex/components/el/element.py

@@ -1,11 +1,15 @@
 """Base class definition for raw HTML elements."""
 """Base class definition for raw HTML elements."""
 
 
+import pydantic
+
 from reflex.components.component import Component
 from reflex.components.component import Component
 
 
 
 
 class Element(Component):
 class Element(Component):
     """The base class for all raw HTML elements."""
     """The base class for all raw HTML elements."""
 
 
+    _is_tag_in_global_scope = pydantic.PrivateAttr(default_factory=lambda: True)
+
     def __eq__(self, other: object):
     def __eq__(self, other: object):
         """Two elements are equal if they have the same tag.
         """Two elements are equal if they have the same tag.
 
 

+ 1 - 1
reflex/utils/format.py

@@ -437,7 +437,7 @@ def format_props(*single_props, **key_value_props) -> list[str]:
 
 
     return [
     return [
         (
         (
-            f"{name}={{{format_prop(prop if isinstance(prop, Var) else LiteralVar.create(prop))}}}"
+            f"{name}:{format_prop(prop if isinstance(prop, Var) else LiteralVar.create(prop))}"
         )
         )
         for name, prop in sorted(key_value_props.items())
         for name, prop in sorted(key_value_props.items())
         if prop is not None
         if prop is not None

+ 1 - 0
reflex/utils/pyi_generator.py

@@ -47,6 +47,7 @@ EXCLUDED_PROPS = [
     "tag",
     "tag",
     "is_default",
     "is_default",
     "special_props",
     "special_props",
+    "_is_tag_in_global_scope",
     "_invalid_children",
     "_invalid_children",
     "_memoization_mode",
     "_memoization_mode",
     "_rename_props",
     "_rename_props",