reflex/pynecone/compiler/templates.py
2022-11-18 17:48:51 -08:00

180 lines
4.6 KiB
Python

"""Templates to use in the pynecone compiler."""
from typing import Callable, Optional, Set
from pynecone import constants, utils
from pynecone.utils import join
# Template for the Pynecone config file.
PCCONFIG = f"""# The Pynecone configuration file.
APP_NAME = "{{app_name}}"
API_HOST = "http://localhost:8000"
BUN_PATH = "$HOME/.bun/bin/bun"
ENV = "{constants.Env.DEV.value}"
DB_URI = "sqlite:///{constants.DB_NAME}"
"""
# Javascript formatting.
CONST = "const {name} = {value}".format
PROP = "{object}.{property}".format
IMPORT_LIB = 'import "{lib}"'.format
IMPORT_FIELDS = 'import {default}{others} from "{lib}"'.format
def format_import(lib: str, default: str = "", rest: Optional[Set[str]] = None) -> str:
"""Format an import statement.
Args:
lib: The library to import from.
default: The default field to import.
rest: The set of fields to import from the library.
Returns:
The compiled import statement.
"""
# Handle the case of direct imports with no libraries.
if lib == "":
assert default == "", "No default field allowed for empty library."
assert rest is not None and len(rest) > 0, "No fields to import."
return join([IMPORT_LIB(lib=lib) for lib in sorted(rest)])
# Handle importing from a library.
rest = rest or set()
if len(default) == 0 and len(rest) == 0:
# Handle the case of importing a library with no fields.
return IMPORT_LIB(lib=lib)
else:
# Handle importing specific fields from a library.
others = f'{{{", ".join(sorted(rest))}}}' if len(rest) > 0 else ""
if len(default) > 0 and len(rest) > 0:
default += ", "
return IMPORT_FIELDS(default=default, others=others, lib=lib)
# Code to render a NextJS Document root.
DOCUMENT_ROOT = join(
[
"{imports}",
"export default function Document() {{",
"return (",
"{document}",
")",
"}}",
]
).format
# Template for the theme file.
THEME = "export default {theme}".format
# Code to render a single NextJS component.
COMPONENT = join(
[
"{imports}",
"{custom_code}",
"{constants}",
"export default function Component() {{",
"{state}",
"{events}",
"{effects}",
"return (",
"{render}",
")",
"}}",
]
).format
# React state declarations.
USE_STATE = CONST(
name="[{state}, {set_state}]", value="useState({initial_state})"
).format
def format_state_setter(state: str) -> str:
"""Format a state setter.
Args:
state: The name of the state variable.
Returns:
The compiled state setter.
"""
return f"set{state[0].upper() + state[1:]}"
def format_state(
state: str,
initial_state: str,
) -> str:
"""Format a state declaration.
Args:
state: The name of the state variable.
initial_state: The initial state of the state variable.
Returns:
The compiled state declaration.
"""
set_state = format_state_setter(state)
return USE_STATE(state=state, set_state=set_state, initial_state=initial_state)
# Events.
EVENT_ENDPOINT = constants.Endpoint.EVENT.name
EVENT_FN = join(
[
"const E = (name, payload) => {{ return {{name, payload}} }}",
"const Event = events => {set_state}({{",
" ...{state},",
" events: [...{state}.events, ...events],",
"}})",
]
).format
def format_event_declaration(fn: Callable) -> str:
"""Format an event declaration.
Args:
fn: The function to declare.
Returns:
The compiled event declaration.
"""
name = utils.format_event_fn(fn=fn)
event = utils.to_snake_case(fn.__qualname__)
return f"const {name} = Event('{event}')"
# 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 () => {{",
f" if ({RESULT}.{STATE} != null) {{{{",
f" {{set_state}}({{{{",
f" ...{RESULT}.{STATE},",
f" events: [...{{state}}.{EVENTS}, ...{RESULT}.{EVENTS}],",
" }})",
f" {SET_RESULT}({{{{",
f" ...{RESULT},",
f" {{state}}: null,",
f" {PROCESSING}: false,",
" }})",
" }}",
f" await updateState({{state}}, {RESULT}, {SET_RESULT}, {EVENT_ENDPOINT}, {ROUTER})",
" }}",
" update()",
"}})",
]
).format
# Routing
ROUTER = f"const {constants.ROUTER} = useRouter()"