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( DOCUMENT_ROOT = join(
[ [
"{imports}", "{imports}",
"",
"export default function Document() {{", "export default function Document() {{",
"",
"return (", "return (",
"{document}", "{document}",
")", ")",
@ -74,17 +72,11 @@ COMPONENT = join(
[ [
"{imports}", "{imports}",
"{custom_code}", "{custom_code}",
"",
"{constants}", "{constants}",
"",
"export default function Component() {{", "export default function Component() {{",
"",
"{state}", "{state}",
"",
"{events}", "{events}",
"",
"{effects}", "{effects}",
"",
"return (", "return (",
"{render}", "{render}",
")", ")",
@ -155,22 +147,28 @@ def format_event_declaration(fn: Callable) -> str:
# Effects. # 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( USE_EFFECT = join(
[ [
"useEffect(() => {{", "useEffect(() => {{",
" const update = async () => {{", " const update = async () => {{",
" if (result.state != null) {{", f" if ({RESULT}.{STATE} != null) {{{{",
" setState({{", f" {{set_state}}({{{{",
" ...result.state,", f" ...{RESULT}.{STATE},",
" events: [...state.events, ...result.events],", f" events: [...{{state}}.{EVENTS}, ...{RESULT}.{EVENTS}],",
" }})", " }})",
" setResult({{", f" {SET_RESULT}({{{{",
" ...result,", f" ...{RESULT},",
" state: null,", f" {{state}}: null,",
" processing: false,", 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()", " update()",
"}})", "}})",

View File

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

View File

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