templates.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. """Templates to use in the pynecone compiler."""
  2. from typing import Callable, Optional, Set
  3. from pynecone import constants, utils
  4. from pynecone.utils import join
  5. # Template for the Pynecone config file.
  6. PCCONFIG = f"""# The Pynecone configuration file.
  7. APP_NAME = "{{app_name}}"
  8. API_HOST = "http://localhost:8000"
  9. BUN_PATH = "$HOME/.bun/bin/bun"
  10. ENV = "{constants.Env.DEV.value}"
  11. DB_URI = "sqlite:///{constants.DB_NAME}"
  12. """
  13. # Javascript formatting.
  14. CONST = "const {name} = {value}".format
  15. PROP = "{object}.{property}".format
  16. IMPORT_LIB = 'import "{lib}"'.format
  17. IMPORT_FIELDS = 'import {default}{others} from "{lib}"'.format
  18. def format_import(lib: str, default: str = "", rest: Optional[Set[str]] = None) -> str:
  19. """Format an import statement.
  20. Args:
  21. lib: The library to import from.
  22. default: The default field to import.
  23. rest: The set of fields to import from the library.
  24. Returns:
  25. The compiled import statement.
  26. """
  27. # Handle the case of direct imports with no libraries.
  28. if lib == "":
  29. assert default == "", "No default field allowed for empty library."
  30. assert rest is not None and len(rest) > 0, "No fields to import."
  31. return join([IMPORT_LIB(lib=lib) for lib in sorted(rest)])
  32. # Handle importing from a library.
  33. rest = rest or set()
  34. if len(default) == 0 and len(rest) == 0:
  35. # Handle the case of importing a library with no fields.
  36. return IMPORT_LIB(lib=lib)
  37. else:
  38. # Handle importing specific fields from a library.
  39. others = f'{{{", ".join(sorted(rest))}}}' if len(rest) > 0 else ""
  40. if len(default) > 0 and len(rest) > 0:
  41. default += ", "
  42. return IMPORT_FIELDS(default=default, others=others, lib=lib)
  43. # Code to render a NextJS Document root.
  44. DOCUMENT_ROOT = join(
  45. [
  46. "{imports}",
  47. "",
  48. "export default function Document() {{",
  49. "",
  50. "return (",
  51. "{document}",
  52. ")",
  53. "}}",
  54. ]
  55. ).format
  56. # Template for the theme file.
  57. THEME = "export default {theme}".format
  58. # Code to render a single NextJS component.
  59. COMPONENT = join(
  60. [
  61. "{imports}",
  62. "{custom_code}",
  63. "",
  64. "{constants}",
  65. "",
  66. "export default function Component() {{",
  67. "",
  68. "{state}",
  69. "",
  70. "{events}",
  71. "",
  72. "{effects}",
  73. "",
  74. "return (",
  75. "{render}",
  76. ")",
  77. "}}",
  78. ]
  79. ).format
  80. # React state declarations.
  81. USE_STATE = CONST(
  82. name="[{state}, {set_state}]", value="useState({initial_state})"
  83. ).format
  84. def format_state_setter(state: str) -> str:
  85. """Format a state setter.
  86. Args:
  87. state: The name of the state variable.
  88. Returns:
  89. The compiled state setter.
  90. """
  91. return f"set{state[0].upper() + state[1:]}"
  92. def format_state(
  93. state: str,
  94. initial_state: str,
  95. ) -> str:
  96. """Format a state declaration.
  97. Args:
  98. state: The name of the state variable.
  99. initial_state: The initial state of the state variable.
  100. Returns:
  101. The compiled state declaration.
  102. """
  103. set_state = format_state_setter(state)
  104. return USE_STATE(state=state, set_state=set_state, initial_state=initial_state)
  105. # Events.
  106. EVENT_ENDPOINT = constants.Endpoint.EVENT.name
  107. EVENT_FN = join(
  108. [
  109. "const E = (name, payload) => {{ return {{name, payload}} }}",
  110. "const Event = events => {set_state}({{",
  111. " ...{state},",
  112. " events: [...{state}.events, ...events],",
  113. "}})",
  114. ]
  115. ).format
  116. def format_event_declaration(fn: Callable) -> str:
  117. """Format an event declaration.
  118. Args:
  119. fn: The function to declare.
  120. Returns:
  121. The compiled event declaration.
  122. """
  123. name = utils.format_event_fn(fn=fn)
  124. event = utils.to_snake_case(fn.__qualname__)
  125. return f"const {name} = Event('{event}')"
  126. # Effects.
  127. USE_EFFECT = join(
  128. [
  129. "useEffect(() => {{",
  130. " const update = async () => {{",
  131. " if (result.state != null) {{",
  132. " setState({{",
  133. " ...result.state,",
  134. " events: [...state.events, ...result.events],",
  135. " }})",
  136. " setResult({{",
  137. " ...result,",
  138. " state: null,",
  139. " processing: false,",
  140. " }})",
  141. " }}",
  142. f" await updateState({{state}}, {{result}}, {{set_result}}, {EVENT_ENDPOINT}, {constants.ROUTER})",
  143. " }}",
  144. " update()",
  145. "}})",
  146. ]
  147. ).format
  148. # Routing
  149. ROUTER = f"const {constants.ROUTER} = useRouter()"