From a1fa1a5873bc382547bcd34334cdbd6f29e35844 Mon Sep 17 00:00:00 2001
From: Sanskar Jethi <29942790+sansyrox@users.noreply.github.com>
Date: Tue, 16 Jan 2024 20:35:22 +0000
Subject: [PATCH] Render the tree on the client (#126)
* Add serialization and deserialization on the client
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* remove extra comments
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
docs/component-philospy.md | 9 ++
starfyre/__init__.py | 1 -
starfyre/component.py | 21 +++-
starfyre/dist_builder.py | 29 ++---
starfyre/dom_methods.py | 86 ++++++++-----
starfyre/js/dom_helpers.py | 121 ++++++++++++++++++
starfyre/js/dom_methods.py | 74 -----------
starfyre/js/store.js | 55 --------
starfyre/js/store.py | 159 +++++++++---------------
starfyre/parser.py | 40 +++---
test_application/components/parent.fyre | 2 +-
test_application/pages/__init__.fyre | 7 +-
12 files changed, 302 insertions(+), 302 deletions(-)
create mode 100644 docs/component-philospy.md
create mode 100644 starfyre/js/dom_helpers.py
delete mode 100644 starfyre/js/dom_methods.py
delete mode 100644 starfyre/js/store.js
diff --git a/docs/component-philospy.md b/docs/component-philospy.md
new file mode 100644
index 0000000..8bb408e
--- /dev/null
+++ b/docs/component-philospy.md
@@ -0,0 +1,9 @@
+## Component Philosophy
+
+Every component in Starfyre will be a stateless component. This means that the component will not have any state of its own.
+
+Should there be a need for a stateful component, the state will be treated like a unique entity.
+
+Just by including a state in the component, the component will be subscribed to the state. This means that the component will be re-rendered every time the state changes.
+
+The state doesn't have to be a global state. It can be a local state too. We don't need to do prop drilling to pass the state to the component. The component will be subscribed to the state, and it will be re-rendered every time the state changes.
diff --git a/starfyre/__init__.py b/starfyre/__init__.py
index 1f297bf..d532198 100644
--- a/starfyre/__init__.py
+++ b/starfyre/__init__.py
@@ -31,7 +31,6 @@ def create_component(
props={},
children=[],
event_listeners={},
- state={},
uuid="store",
js=js,
original_name="div",
diff --git a/starfyre/component.py b/starfyre/component.py
index 7ea912b..3311971 100644
--- a/starfyre/component.py
+++ b/starfyre/component.py
@@ -8,14 +8,16 @@ class Component:
props: dict
children: list
event_listeners: dict
- state: dict
uuid: Any
signal: str = ""
original_data: str = ""
data: str = ""
parentComponent: Optional[Any] = None
+ # html,css, and js are debug properties. Not needed for rendering
html: str = ""
+ # this should not be a part of the rendering
css: str = ""
+ # this should not be a part of the rendering
js: str = ""
client_side_python: str = ""
original_name: str = ""
@@ -31,3 +33,20 @@ def is_slot_component(self):
def __repr__(self):
return f"<{self.tag}> {self.data} {self.children} {self.tag}>"
+
+ def to_json(self):
+ return {
+ "tag": self.tag,
+ "props": self.props,
+ "children": self.children,
+ "event_listeners": self.event_listeners,
+ "uuid": self.uuid,
+ "signal": self.signal,
+ "original_data": self.original_data,
+ "data": self.data,
+ "parentComponent": self.parentComponent,
+ "html": self.html,
+ "original_name": self.original_name,
+ }
+
+ # not including the css, js and client_side_python as they are not needed for re rendering
diff --git a/starfyre/dist_builder.py b/starfyre/dist_builder.py
index 8a3bc7a..cbabdc7 100644
--- a/starfyre/dist_builder.py
+++ b/starfyre/dist_builder.py
@@ -12,28 +12,16 @@
"""
-def write_js_file(path: Path):
- dist_path = Path(path) / "dist"
- print("This is the dist path", dist_path)
- dist_path.mkdir(exist_ok=True)
-
- with pkg_resources.path("starfyre.js", "store.js") as js_store:
- store_path = dist_path / "store.js"
- print("This is the store path", store_path)
- print("This is the js store path", js_store)
- shutil.copy(str(js_store), str(store_path))
-
-
def write_python_client_file(path: Path):
dist_path = Path(path) / "dist"
dist_path.mkdir(exist_ok=True)
- with pkg_resources.path(
- "starfyre.js", "dom_methods.py"
- ) as dom_methods, pkg_resources.path("starfyre.js", "store.py") as store_py:
- dom_methods_path = dist_path / "dom_methods.py"
- shutil.copy(str(dom_methods), str(dom_methods_path))
+ with pkg_resources.path("starfyre.js", "store.py") as store_py, pkg_resources.path(
+ "starfyre.js", "dom_helpers.py"
+ ) as dom_helpers:
store_path = dist_path / "store.py"
shutil.copy(str(store_py), str(store_path))
+ dom_helpers_path = dist_path / "dom_helpers.py"
+ shutil.copy(str(dom_helpers), str(dom_helpers_path))
def generate_html_pages(file_routes, project_dir: Path):
@@ -98,8 +86,11 @@ def generate_html_pages(file_routes, project_dir: Path):
html_file.write(
""
)
+ html_file.write(
+ ""
+ )
+ html_file.write("")
html_file.write("")
- html_file.write("")
html_file.write(rendered_page)
# Change back to the original directory
@@ -167,8 +158,6 @@ def create_dist(file_routes, project_dir_path):
"""
print("This is the project dir path", project_dir_path)
print("These are the file routes", file_routes)
- write_js_file(project_dir_path)
- print("JS file written")
write_python_client_file(project_dir_path)
print("Python files written")
diff --git a/starfyre/dom_methods.py b/starfyre/dom_methods.py
index 27c514e..fc1dc92 100644
--- a/starfyre/dom_methods.py
+++ b/starfyre/dom_methods.py
@@ -1,6 +1,5 @@
-import re
-from functools import partial
-from uuid import uuid4
+import json
+from uuid import UUID, uuid4
from .component import Component
@@ -34,7 +33,30 @@ def is_attribute(name):
return not is_listener(name) and name != "children"
-def render_helper(component: Component) -> tuple[str, str, str, str]:
+def assign_initial_signal_population(component: Component):
+ return f"""
+component = js.document.querySelector("[data-pyxide-id='{component.uuid}']");
+if (component):
+ component.innerText = {component.data}
+ """
+
+
+def hydration_helper(component: Component) -> tuple[str, str, str, str]:
+ """
+ Args:
+ component (Component): The root component
+
+ We are just hyrdating the html here, and then the client side python should populate the html
+ with the new values and attach the event listeners, signal, etc.
+
+ TODO:
+ I want to find a way for signals to be populated with a default value on the server side or
+ something similar for more complex components.
+
+ The inital value should be filled in the html, which we are already doing and then everything
+ else should be attached on the client side.
+ """
+
parentElement = component.parentComponent
html = "\n"
css = ""
@@ -47,7 +69,6 @@ def render_helper(component: Component) -> tuple[str, str, str, str]:
props={"id": "root"},
children=[],
event_listeners={},
- state={},
uuid=uuid4(),
original_name="div",
)
@@ -55,36 +76,21 @@ def render_helper(component: Component) -> tuple[str, str, str, str]:
tag = component.tag
props = component.props
- state = component.state
data = component.data
event_listeners = component.event_listeners
# Create DOM element
if component.is_text_component:
- # find all the names in "{}" and print them
- matches = re.findall(r"{(.*?)}", data)
- for match in matches:
- if match in state:
- function = state[match]
- function = partial(function, component)
- data = component.data.replace(f"{{{ match }}}", str(function()))
- else:
- print(
- "No match found for", match, component, "This is the state", state
- )
-
component.parentComponent.uuid = component.uuid
html += f"{data}\n"
component.html = html
+ # matches = re.findall(r"{(.*?)}", data)
+ # print("This is the matches", matches)
+ # we need to do a better way of managing the signals
if component.signal:
- client_side_python += f"""
-component = js.document.querySelector("[data-pyxide-id='{component.uuid}']");
-js.addDomIdToMap('{component.uuid}', "{component.signal}");
-if (component):
- component.innerText = {component.signal}
- """
-
+ # TODO: this part should be moved to the client
+ client_side_python += assign_initial_signal_population(component)
return html, css, js, client_side_python
if component.css:
@@ -121,7 +127,9 @@ def render_helper(component: Component) -> tuple[str, str, str, str]:
for childElement in children:
childElement.parentElement = component
- new_html, new_css, new_js, new_client_side_python = render_helper(childElement)
+ new_html, new_css, new_js, new_client_side_python = hydration_helper(
+ childElement
+ )
html += new_html
css += new_css
js += new_js
@@ -134,13 +142,27 @@ def render_helper(component: Component) -> tuple[str, str, str, str]:
return html, css, js, client_side_python
+class ComponentEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, Component):
+ return obj.to_json()
+
+ if isinstance(obj, UUID):
+ return str(obj)
+ # Let the base class default method raise the TypeError
+ return json.JSONEncoder.default(self, obj)
+
+
def hydrate(component: Component) -> str:
- html, css, js, client_side_python = render_helper(component)
+ html, css, js, client_side_python = hydration_helper(component)
+ tree = json.dumps(component, cls=ComponentEncoder)
final_html = f"""
-
-
-
-
{html}
-"""
+
+
+
+
+
+ {html}
+ """
return final_html
diff --git a/starfyre/js/dom_helpers.py b/starfyre/js/dom_helpers.py
new file mode 100644
index 0000000..bce83dc
--- /dev/null
+++ b/starfyre/js/dom_helpers.py
@@ -0,0 +1,121 @@
+import ujson as json
+import js
+
+
+def is_listener(name):
+ return name.startswith("on")
+
+
+def is_attribute(name):
+ return not is_listener(name) and name != "children"
+
+
+class Component:
+ def __init__(
+ self,
+ tag: str,
+ props: dict,
+ children: list,
+ event_listeners: dict,
+ uuid: str,
+ signal: str = "",
+ original_data: str = "",
+ data: str = "",
+ parentComponent=None,
+ html: str = "",
+ css: str = "",
+ js: str = "",
+ client_side_python: str = "",
+ original_name: str = "",
+ ):
+ self.tag = tag
+ self.props = props
+ self.children = children
+ self.event_listeners = event_listeners
+ self.uuid = uuid
+ self.signal = signal
+ self.original_data = data
+ self.data = data
+ self.parentComponent = parentComponent
+ self.html = html
+ self.css = css
+ self.js = js
+ self.client_side_python = client_side_python
+ self.original_name = original_name
+
+ @property
+ def is_text_component(self):
+ return self.tag == "TEXT_NODE"
+
+ @property
+ def is_slot_component(self):
+ return self.tag == "slot"
+
+ def __repr__(self):
+ return f"<{self.tag}> {self.data} {self.children} {self.tag}>"
+
+ def re_render_helper(self, component):
+ # this will rebuild the tree
+ ...
+
+ if self == component:
+ print("This is the true component", component)
+
+ for child in component.children:
+ if isinstance(child, Component):
+ self.re_render_helper(child)
+
+ def re_render(self):
+ print("This is the re render function")
+ return self.re_render_helper(self)
+
+
+def parse_component_data(data):
+ if isinstance(data, dict):
+ # Check if this dictionary represents a Component
+ if "tag" in data and "props" in data:
+ # Parse children if present
+ children = data.get("children", [])
+ parsed_children = [parse_component_data(child) for child in children]
+
+ # Handle special cases like parentComponent
+ if "parentComponent" in data and data["parentComponent"] is not None:
+ data["parentComponent"] = parse_component_data(data["parentComponent"])
+
+ # Create a Component instance, passing the parsed children
+ data["children"] = parsed_children
+ component = Component(**data)
+ dom_id = component.uuid
+
+ # need to find a way to add this component to a global dom store
+ # and make it accessible to the render function
+ print("This is the dom id", clientDomIdMap)
+ clientDomIdMap[dom_id] = component
+ return component
+ else:
+ # Handle other dict structures (e.g., props)
+ return {k: parse_component_data(v) for k, v in data.items()}
+ elif isinstance(data, list):
+ # Handle lists (e.g., a list of children)
+ return [parse_component_data(item) for item in data]
+ else:
+ # Return the item as is for basic types (int, str, etc.)
+ return data
+
+
+def rebuild_tree():
+ print("This is the rebuild tree function")
+ # this is present globally
+ # TODO: need to work on this
+ # js.window maybe
+ STARFYRE_ROOT_NODE = getattr(js.window, "STARFYRE_ROOT_NODE")
+ tree_node = STARFYRE_ROOT_NODE
+ # print("This is the tree node", tree_node)
+ print("This is the tree node", type(tree_node))
+ json_data = json.loads(tree_node)
+ tree = parse_component_data(json_data)
+ print("Successfully loaded json data", json_data)
+ print(tree)
+
+
+rebuild_tree()
diff --git a/starfyre/js/dom_methods.py b/starfyre/js/dom_methods.py
deleted file mode 100644
index 50f2b95..0000000
--- a/starfyre/js/dom_methods.py
+++ /dev/null
@@ -1,74 +0,0 @@
-def assign_event_listeners(component, event_listeners):
- for event_listener_name, event_listener in event_listeners.items():
- event_type = event_listener_name.lower()[2:]
- print(
- "Assigning event listeners to the component",
- component,
- event_type,
- event_listener,
- )
- # component.dom.addEventListener(event_type, create_proxy(event_listener))
-
-
-# def render(component: Component):
-# parentElement = component.parentComponent
-# if parentElement is None:
-# parentElement = js.document.createElement("div")
-# parentElement.id = "root"
-# js.document.body.appendChild(parentElement)
-
-# component.parentComponent = parentElement
-# we will add rust later
-# dom_node = DomNode(element.tag, element.props, element.children, element.event_listeners, element.state or {})
-# dom_node.set_parent_element(parentComponent)
-# print(dom_node)
-
-# tag = component.tag
-# props = component.props
-# state = component.state
-# data = component.data
-# print(data)
-
-# Create DOM element
-# if component.is_text_component:
-# find all the names in "{}" and print them
-# matches = re.findall(r"{(.*?)}", data)
-# for match in matches:
-# if match in state:
-# function = state[match]
-# function = partial(function, component)
-# data = component.data.replace(f"{{{ match }}}", str(function()))
-# dom = js.document.createTextNode(data)
-# print("Text Node", component)
-# else:
-# dom = js.document.createElement(tag)
-
-# component.dom = dom
-
-# Add event listeners
-# def isListener(name):
-# return name.startswith("on")
-# def isAttribute(name):
-# return not isListener(name) and name != "children"
-
-# assign_event_listeners(component, component.event_listeners)
-# set attributes
-
-# for name in props:
-# if isAttribute(name):
-# dom.setAttribute(name, props[name])
-
-# Render children
-# children = component.children
-# childElements.forEach(childElement => render(childElement, dom));
-# for childElement in children:
-# childElement.parentComponent = dom
-# render(childElement)
-
-# // Append to parent
-# need to apply batch updates here
-# also create a tree of dom nodes in the first place
-# if parentElement.contains(dom):
-# parentElement.replaceChild(dom, parentElement.childNodes[0])
-# else:
-# parentElement.appendChild(dom)
diff --git a/starfyre/js/store.js b/starfyre/js/store.js
deleted file mode 100644
index 6b046a5..0000000
--- a/starfyre/js/store.js
+++ /dev/null
@@ -1,55 +0,0 @@
-observers = {}; // uuid , list[observers]
-
-const domIdMap = {};
-
-function addDomIdToMap(domId, pyxide) {
- domIdMap[domId] = pyxide;
-}
-
-function getPyxideFromDomId(domId) {
- return domIdMap[domId];
-}
-
-function render(domId) {
- const element = document.querySelector(`[data-pyxide-id="${domId}"]`);
- const pyxide = getPyxideFromDomId(domId);
- element.innerHTML = `${eval(pyxide)}`;
-}
-
-// function create_signal(initial_state) {
-// let id = Math.random() * 1000000000000000; // simulating uuid
-// let state = initial_state;
-
-// function use_signal(domId) {
-// if (!domId) {
-// // when the domId is not provided, it means that the signal is used in a function component
-// return store[id] || initial_state;
-// }
-
-// if (observers[id]) {
-// observers[id].push(domId);
-// } else {
-// observers[id] = [domId];
-// }
-
-// return state;
-// }
-
-// function set_signal(newState) {
-// state = newState;
-
-// if (!observers[id]) {
-// return;
-// }
-
-// observers[id].forEach((element) => {
-// render(element);
-// });
-// }
-
-// function get_signal() {
-// return state;
-// }
-
-// return [use_signal, set_signal, get_signal];
-// }
diff --git a/starfyre/js/store.py b/starfyre/js/store.py
index 7aaf7d3..94a408f 100644
--- a/starfyre/js/store.py
+++ b/starfyre/js/store.py
@@ -4,133 +4,96 @@
import js
-# from .dom_methods import render
-store = {}
-observers = {}
-
-reverse_store = {} # maps dom id: uuid
-# dict[uuid.UUID, list[Component]] =
-#
-
-
-def render(component, parentElement=None): # this is domElement
- # the best fix will be to serialize the dom tree and then de serialize it
- # on the client
- # and then find from a dict to re render
+store = GLOBAL_STORE
+observers = GLOBAL_OBSERVERS
+
+reverse_store = GLOBAL_OBSERVERS # maps dom id: uuid
+clientDomIdMap = GLOBAL_CLIENT_DOM_ID_MAP
+
+
+def evaluate_data(component, dom_id):
+ # we will need to extend this
+ data = component.data
+ new_data = data
+ print("Original data", data)
+ if "signal" in data:
+ store_id = reverse_store.get(dom_id)
+ signal_value = store.get(store_id)
+ print("This is the component signal", component.signal)
+ temp_data = data.replace(component.signal, str(signal_value))
+ print("This is the temp data", temp_data)
+ new_data = eval(f"{temp_data.strip().strip(';')}")
+ print("This is the new data", new_data)
+
+ return new_data
+
+
+def hydrate(component):
+ # this will take the component and then find the dom element
+ # and then convert the tree to the html
+ # component is then tree
+ # and pareneComponent is the parent node
+ # we need to remove the html of the parent component
+ # and then add the new html based on the component tree
#
+ for child in component.children:
+ id = child.uuid
+ domElement = js.document.querySelector(f"[data-pyxide-id='{id}']")
+ if domElement is None:
+ continue
- # but for now, we will just find the id of the component by
- # document.getElementById
- # and then get the current state
- # put it as a inner text and then re render the children
- #
-
- if parentElement is None:
- parentElement = js.document.querySelector("[data-pyxide-id=root]")
- if parentElement is None:
- parentElement = js.document.createElement("div")
- parentElement.id = "root"
- js.document.body.appendChild(parentElement)
-
- if component:
- # find all the names in "{}" and print them
- # need to sort this again
- # matches = re.findall(r"{(.*?)}", data)
- # for match in matches:
- # if match in state:
- # function = state[match]
- # function = partial(function, component)
- # data = component.data.replace(f"{{{ match }}}", str(function()))
- # dom = js.document.createTextNode(data)
-
- dom_id = component.id
-
- parentElement.appendChild(component)
- # print("Text Node", component)
- js.console.log(component, component.children)
- if component.children:
- for child in component.children:
- render(child, component.element)
-
- if dom_id in reverse_store:
- id = reverse_store[dom_id]
- state = store.get(id)
- if state:
- component.innerText = state
- # Add event listeners
- # def isListener(name):
- # return name.startswith("on")
- # def isAttribute(name):
- # return not isListener(name) and name != "children"
-
- # set attributes
-
- # for name in props:
- # if isAttribute(name):
- # dom.setAttribute(name, props[name])
-
- # Render children
- # children = component.children
- # childElements.forEach(childElement => render(childElement, dom));
- # for childElement in children:
- # childElement.parentDom = dom
- # render(childElement)
-
- # // Append to parent
- # need to apply batch updates here
- # also create a tree of dom nodes in the first place
- # if parentElement.contains(dom):
- # parentElement.replaceChild(dom, parentElement.childNodes[0])
- # else:
- # parentElement.appendChild(dom)
-
-
-# the render is the wrong implementation
+ data = evaluate_data(child, id)
+ domElement.innerText = data
+ hydrate(child)
def create_signal(initial_state=None):
"""Create a signal to be used in a component."""
global store, reverse_store
- id = random.randint(0, 100000)
+ signal_id = random.randint(0, 100000)
- def use_signal(element=None): # this will be the dom id
+ def use_signal(dom_id=None): # this will be the dom id
"""Get the state and manage observers."""
- nonlocal id
- if element:
- observers.setdefault(id, []).append(element)
- reverse_store[element] = id
+ nonlocal signal_id
+ if dom_id:
+ observers.setdefault(signal_id, []).append(dom_id)
+ reverse_store[dom_id] = signal_id
- return store.get(id, initial_state)
+ return store.get(signal_id, initial_state)
- def set_signal(state):
- """Set a new state and trigger re-render for observers."""
- """The render process works in a wrong way at this point but works for now."""
- """We need to update the value in the state"""
+ def set_signal(initial_state=None):
# need to fix this
# we need to maintain a global tree indexed by uuids after the hydration process
# after the hydration process, we need to update the tree with the new state
# then we need to re-render the tree
- nonlocal id
- store[id] = state
- for component_id in observers.get(id, []):
+ nonlocal signal_id
+ store[signal_id] = initial_state
+ for component_id in observers.get(signal_id, []):
component = js.document.querySelector(f"[data-pyxide-id='{component_id}']")
+ component = clientDomIdMap.get(component_id)
+ if component is None:
+ continue
+ print("component", component, "new state", initial_state)
+ print("clientDomIdMap", clientDomIdMap)
+
js.console.log(
"This is the component",
component,
str(dir(component)),
component.children,
)
-
- render(component, component.parentElement)
+ component.re_render()
+ hydrate(component)
+ print("hydration ended")
def get_signal(*args, **kwargs):
# args and kwargs are not used but a hack as the ids are being
# passed as arguments currently
# will be fixed once the serialization and deserialization is done
"""Get the current state without affecting the observer list."""
- nonlocal id
- return store.get(id, initial_state)
+ nonlocal signal_id
+ return store.get(signal_id, initial_state)
return [use_signal, set_signal, get_signal]
diff --git a/starfyre/parser.py b/starfyre/parser.py
index 72c0584..29ec659 100644
--- a/starfyre/parser.py
+++ b/starfyre/parser.py
@@ -184,7 +184,6 @@ def handle_starttag(self, tag, attrs):
# logic should be to just create an empty component on start
# and fill the contents on the end tag
props = {}
- state = {}
event_listeners = {}
self.current_depth += 1
@@ -210,10 +209,9 @@ def handle_starttag(self, tag, attrs):
else:
# here we need to check if these are functions
# or state objects or just regular text
- if attr_value in self.local_variables and self.is_state(
+ if attr_value in self.local_variables(
self.local_variables[attr_value]
):
- state[attr[0]] = self.local_variables[attr_value]
props[attr[0]] = self.local_variables[attr_value]()
else:
props[attr[0]] = attr[1]
@@ -223,7 +221,6 @@ def handle_starttag(self, tag, attrs):
component = self.components[tag]
component.original_name = tag
component.props = {**component.props, **props}
- component.state = {**component.state, **state}
component.event_listeners = {
**component.event_listeners,
@@ -247,7 +244,6 @@ def handle_starttag(self, tag, attrs):
props=props,
children=[],
event_listeners=event_listeners,
- state=state,
js=self.js,
css=self.css,
uuid=uuid4(),
@@ -260,7 +256,6 @@ def handle_starttag(self, tag, attrs):
props=props,
children=[],
event_listeners=event_listeners,
- state=state,
js="",
css="",
uuid=uuid4(),
@@ -333,6 +328,15 @@ def is_signal(self, str):
def inject_uuid(self, signal, uuid):
return signal.replace("()", f"('{uuid}')")
+ def extract_signal(self, str, id):
+ if not str:
+ return False
+ matches = re.match(rf".+\(.+{id}.+\)", str)
+ if not matches:
+ raise Exception("Signal not found. Something went wrong.")
+
+ return matches.group(0)
+
def handle_data(self, data):
# this is doing too much
# lexing
@@ -346,14 +350,13 @@ def handle_data(self, data):
matches = re.findall(r"{(.*?)}", data)
# parsing starts here
- state = {}
parent_node = self.stack[-1]
uuid = uuid4()
component_signal = ""
for match in matches:
- # match can be a sentece so we will split it
+ # match can be a sentence so we will split it
current_data = None
if match in self.local_variables:
current_data = self.local_variables[match]
@@ -364,10 +367,12 @@ def handle_data(self, data):
if self.is_signal(match):
new_js = transpile(match)
new_js = self.inject_uuid(new_js, uuid)
- component_signal = new_js.strip("{").strip("}").strip(";")
- print("new js", new_js)
- # inject uuid in the signal function call
+ component_signal = self.extract_signal(new_js, uuid)
+ # TODO: we need to account for multiple signals e.g. signal1 + signal2 + 1
+ print("This is the new js", new_js)
+ print("This is the component signal", component_signal)
+ # inject uuid in the signal function call
current_data = new_js
else:
@@ -394,6 +399,11 @@ def handle_data(self, data):
if matches:
data = data.replace("{", "").replace("}", "")
data = data.replace(match, str(current_data))
+ elif self.is_signal(current_data):
+ # we should find the initial state of the signal here
+ # TODO for the future
+ data = current_data
+ print("This is the current data", current_data)
if data == "":
return
@@ -407,16 +417,14 @@ def handle_data(self, data):
# this should never be in the parent stack
# a text node is a child node as soon as it is created
- # add a parent component
- # on the wrapper div component
+ # we are wrapping the text node in a div
wrapper_div_component = Component(
tag="div",
props={},
children=[],
event_listeners={},
- state=state,
- data=data,
+ data="",
css="",
js="",
signal="",
@@ -430,7 +438,6 @@ def handle_data(self, data):
props={},
children=[],
event_listeners={},
- state=state,
data=data,
css="",
js="",
@@ -463,7 +470,6 @@ def get_root(self):
props={},
children=[],
event_listeners={},
- state={},
data="",
css=self.css,
js=self.js,
diff --git a/test_application/components/parent.fyre b/test_application/components/parent.fyre
index f822ebe..ff3d777 100644
--- a/test_application/components/parent.fyre
+++ b/test_application/components/parent.fyre
@@ -21,7 +21,7 @@ def ssr_request():
{ssr_request()}
- {use_parent_signal()}
+ {use_parent_signal() + 1}
{get_parent_signal()}
diff --git a/test_application/pages/__init__.fyre b/test_application/pages/__init__.fyre
index 579d258..b912b2f 100644
--- a/test_application/pages/__init__.fyre
+++ b/test_application/pages/__init__.fyre
@@ -10,6 +10,7 @@ def mocked_request():
[use_parent_signal, set_parent_signal, get_parent_signal] = create_signal(1)
def handle_on_click(e):
+ print("Hello world")
signal_value = get_parent_signal()
set_parent_signal(signal_value+1)
@@ -17,9 +18,9 @@ def handle_on_click(e):
from pyscript.js_modules import Fireworks
from pyscript import document
-container = document.querySelector("[data-pyxide-id='root']")
-f = Fireworks.Fireworks.new(container)
-f.start()
+#container = document.querySelector("[data-pyxide-id='root']")
+#f = Fireworks.Fireworks.new(container)
+#f.start()
---