Add state name into compiler

This commit is contained in:
Nikhil Rao 2022-11-18 17:48:06 -08:00
parent 03d0aca366
commit 705557b484
4 changed files with 93 additions and 88 deletions

View File

@ -56,9 +56,7 @@ def format_import(lib: str, default: str = "", rest: Optional[Set[str]] = None)
DOCUMENT_ROOT = join(
[
"{imports}",
"",
"export default function Document() {{",
"",
"return (",
"{document}",
")",
@ -74,17 +72,11 @@ COMPONENT = join(
[
"{imports}",
"{custom_code}",
"",
"{constants}",
"",
"export default function Component() {{",
"",
"{state}",
"",
"{events}",
"",
"{effects}",
"",
"return (",
"{render}",
")",
@ -155,22 +147,28 @@ def format_event_declaration(fn: Callable) -> str:
# Effects.
ROUTER = constants.ROUTER
RESULT = constants.RESULT
PROCESSING = constants.PROCESSING
STATE = constants.STATE
EVENTS = constants.EVENTS
SET_RESULT = format_state_setter(RESULT)
USE_EFFECT = join(
[
"useEffect(() => {{",
" const update = async () => {{",
" if (result.state != null) {{",
" setState({{",
" ...result.state,",
" events: [...state.events, ...result.events],",
f" if ({RESULT}.{STATE} != null) {{{{",
f" {{set_state}}({{{{",
f" ...{RESULT}.{STATE},",
f" events: [...{{state}}.{EVENTS}, ...{RESULT}.{EVENTS}],",
" }})",
" setResult({{",
" ...result,",
" state: null,",
" processing: false,",
f" {SET_RESULT}({{{{",
f" ...{RESULT},",
f" {{state}}: null,",
f" {PROCESSING}: false,",
" }})",
" }}",
f" await updateState({{state}}, {{result}}, {{set_result}}, {EVENT_ENDPOINT}, {constants.ROUTER})",
f" await updateState({{state}}, {RESULT}, {SET_RESULT}, {EVENT_ENDPOINT}, {ROUTER})",
" }}",
" update()",
"}})",

View File

@ -115,9 +115,9 @@ def compile_state(state: Type[State]) -> str:
state=state.get_name(), initial_state=json.dumps(initial_state)
)
initial_result = {
"state": None,
"events": [],
"processing": False,
constants.STATE: None,
constants.EVENTS: [],
constants.PROCESSING: False,
}
result = templates.format_state(
state="result",
@ -151,11 +151,8 @@ def compile_effects(state: Type[State]) -> str:
A string of the compiled effects for the component.
"""
state_name = state.get_name()
result_name = "result"
set_result = templates.format_state_setter(result_name)
return templates.USE_EFFECT(
state=state_name, result=result_name, set_result=set_result
)
set_state = templates.format_state_setter(state_name)
return templates.USE_EFFECT(state=state_name, set_state=set_state)
def compile_render(component: Component) -> str:

View File

@ -5,7 +5,7 @@ from __future__ import annotations
import json
import os
import re
from typing import Any, Dict, Optional, Union
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
from plotly.graph_objects import Figure
from plotly.io import to_json
@ -15,6 +15,9 @@ from pynecone.base import Base
from pynecone.event import EventChain
from pynecone.var import Var
if TYPE_CHECKING:
from pynecone.components.component import ComponentStyle
class Tag(Base):
"""A React tag."""
@ -43,87 +46,86 @@ class Tag(Base):
super().__init__(*args, **kwargs)
@staticmethod
def format_attr_value(
value: Union[Var, EventChain, Dict[str, Var], str],
def format_prop(
prop: Union[Var, EventChain, ComponentStyle, str],
) -> Union[int, float, str]:
"""Format an attribute value.
"""Format a prop.
Args:
value: The value of the attribute
prop: The prop to format.
Returns:
The formatted value to display within the tag.
The formatted prop to display within a tag.
"""
def format_fn(value):
args = ",".join([":".join((name, val)) for name, val in value.args])
return f"E(\"{utils.to_snake_case(value.handler.fn.__qualname__)}\", {utils.wrap(args, '{')})"
# Handle var attributes.
if isinstance(value, Var):
if not value.is_local or value.is_string:
return str(value)
if issubclass(value.type_, str):
value = json.dumps(value.full_name)
value = re.sub('"{', "", value)
value = re.sub('}"', "", value)
value = re.sub('"`', '{"', value)
value = re.sub('`"', '"}', value)
return value
value = value.full_name
# Handle var props.
if isinstance(prop, Var):
if not prop.is_local or prop.is_string:
return str(prop)
if issubclass(prop.type_, str):
prop = json.dumps(prop.full_name)
prop = re.sub('"{', "", prop)
prop = re.sub('}"', "", prop)
prop = re.sub('"`', '{"', prop)
prop = re.sub('`"', '"}', prop)
return prop
prop = prop.full_name
# Handle events.
elif isinstance(value, EventChain):
local_args = ",".join(value.events[0].local_args)
fns = ",".join([format_fn(event) for event in value.events])
value = f"({local_args}) => Event([{fns}])"
elif isinstance(prop, EventChain):
local_args = ",".join(prop.events[0].local_args)
fns = ",".join([format_fn(event) for event in prop.events])
prop = f"({local_args}) => Event([{fns}])"
# Handle other types.
elif isinstance(value, str):
if utils.is_wrapped(value, "{"):
return value
return json.dumps(value)
elif isinstance(prop, str):
if utils.is_wrapped(prop, "{"):
return prop
return json.dumps(prop)
elif isinstance(value, Figure):
value = json.loads(to_json(value))["data"]
elif isinstance(prop, Figure):
prop = json.loads(to_json(prop))["data"]
# For dictionaries, convert any properties to strings.
else:
if isinstance(value, dict):
value = json.dumps(
if isinstance(prop, dict):
prop = json.dumps(
{
key: str(val) if isinstance(val, Var) else val
for key, val in value.items()
for key, val in prop.items()
}
)
else:
value = json.dumps(value)
prop = json.dumps(prop)
value = re.sub('"{', "", value)
value = re.sub('}"', "", value)
value = re.sub('"`', '{"', value)
value = re.sub('`"', '"}', value)
prop = re.sub('"{', "", prop)
prop = re.sub('}"', "", prop)
prop = re.sub('"`', '{"', prop)
prop = re.sub('`"', '"}', prop)
# Wrap the variable in braces.
assert isinstance(value, str), "The value must be a string."
return utils.wrap(value, "{", check_first=False)
assert isinstance(prop, str), "The prop must be a string."
return utils.wrap(prop, "{", check_first=False)
def format_props(self) -> str:
"""Format a dictionary of attributes.
"""Format the tag's props.
Returns:
The formatted attributes.
The formatted props.
"""
# If there are no attributes, return an empty string.
# If there are no props, return an empty string.
if len(self.props) == 0:
return ""
# Get the string representation of all the attributes joined.
# We need a space at the beginning for formatting.
# Format all the props.
return os.linesep.join(
f"{name}={self.format_attr_value(value)}"
for name, value in self.props.items()
if value is not None
f"{name}={self.format_prop(prop)}"
for name, prop in self.props.items()
if prop is not None
)
def __str__(self) -> str:
@ -132,7 +134,7 @@ class Tag(Base):
Returns:
The React code to render the tag.
"""
# Get the tag attributes.
# Get the tag props.
props_str = self.format_props()
if len(props_str) > 0:
props_str = " " + props_str
@ -149,33 +151,33 @@ class Tag(Base):
return tag_str
def add_props(self, **kwargs: Optional[Any]) -> Tag:
"""Add attributes to the tag.
"""Add props to the tag.
Args:
**kwargs: The attributes to add.
**kwargs: The props to add.
Returns:
The tag with the attributes added.
The tag with the props added.
"""
self.props.update(
{
utils.to_camel_case(name): attr
if utils._isinstance(attr, Union[EventChain, dict])
else Var.create(attr)
for name, attr in kwargs.items()
if self.is_valid_attr(attr)
utils.to_camel_case(name): prop
if utils._isinstance(prop, Union[EventChain, dict])
else Var.create(prop)
for name, prop in kwargs.items()
if self.is_valid_prop(prop)
}
)
return self
def remove_props(self, *args: str) -> Tag:
"""Remove attributes from the tag.
"""Remove props from the tag.
Args:
*args: The attributes to remove.
*args: The props to remove.
Returns:
The tag with the attributes removed.
The tag with the props removed.
"""
for name in args:
if name in self.props:
@ -183,13 +185,13 @@ class Tag(Base):
return self
@staticmethod
def is_valid_attr(attr: Optional[Var]) -> bool:
"""Check if the attr is valid.
def is_valid_prop(prop: Optional[Var]) -> bool:
"""Check if the prop is valid.
Args:
attr: The value to check.
prop: The prop to check.
Returns:
Whether the value is valid.
Whether the prop is valid.
"""
return attr is not None and not (isinstance(attr, dict) and len(attr) == 0)
return prop is not None and not (isinstance(prop, dict) and len(prop) == 0)

View File

@ -70,6 +70,14 @@ APP_VAR = "app"
API_VAR = "api"
# The name of the router variable.
ROUTER = "router"
# The name of the variable to hold API results.
RESULT = "result"
# The name of the process variable.
PROCESSING = "processing"
# The name of the state variable.
STATE = "state"
# The name of the events variable.
EVENTS = "events"
# The name of the initial hydrate event.
HYDRATE = "hydrate"
# The name of the index page.