templates.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. """Templates to use in the pynecone compiler."""
  2. from typing import Optional, Set
  3. from pynecone import constants
  4. from pynecone.utils import path_ops
  5. # Template for the Pynecone config file.
  6. PCCONFIG = f"""import pynecone as pc
  7. class {{config_name}}(pc.Config):
  8. pass
  9. config = {{config_name}}(
  10. app_name="{{app_name}}",
  11. db_url="{constants.DB_URL}",
  12. env=pc.Env.DEV,
  13. )
  14. """
  15. # Javascript formatting.
  16. CONST = "const {name} = {value}".format
  17. PROP = "{object}.{property}".format
  18. IMPORT_LIB = 'import "{lib}"'.format
  19. IMPORT_FIELDS = 'import {default}{others} from "{lib}"'.format
  20. def format_import(lib: str, default: str = "", rest: Optional[Set[str]] = None) -> str:
  21. """Format an import statement.
  22. Args:
  23. lib: The library to import from.
  24. default: The default field to import.
  25. rest: The set of fields to import from the library.
  26. Returns:
  27. The compiled import statement.
  28. """
  29. # Handle the case of direct imports with no libraries.
  30. if not lib:
  31. assert not default, "No default field allowed for empty library."
  32. assert rest is not None and len(rest) > 0, "No fields to import."
  33. return path_ops.join([IMPORT_LIB(lib=lib) for lib in sorted(rest)])
  34. # Handle importing from a library.
  35. rest = rest or set()
  36. if len(default) == 0 and len(rest) == 0:
  37. # Handle the case of importing a library with no fields.
  38. return IMPORT_LIB(lib=lib)
  39. # Handle importing specific fields from a library.
  40. others = f'{{{", ".join(sorted(rest))}}}' if len(rest) > 0 else ""
  41. if default != "" and len(rest) > 0:
  42. default += ", "
  43. return IMPORT_FIELDS(default=default, others=others, lib=lib)
  44. # Code to render a NextJS Document root.
  45. DOCUMENT_ROOT = path_ops.join(
  46. [
  47. "{imports}",
  48. "export default function Document() {{",
  49. "return (",
  50. "{document}",
  51. ")",
  52. "}}",
  53. ]
  54. ).format
  55. # Template for the theme file.
  56. THEME = "export default {theme}".format
  57. # Code to render a single NextJS page.
  58. PAGE = path_ops.join(
  59. [
  60. "{imports}",
  61. "{custom_code}",
  62. "{constants}",
  63. "export default function Component() {{",
  64. "{state}",
  65. "{events}",
  66. "{effects}",
  67. "{hooks}",
  68. "return (",
  69. "{render}",
  70. ")",
  71. "}}",
  72. ]
  73. ).format
  74. # Code to render a single exported custom component.
  75. COMPONENT = path_ops.join(
  76. [
  77. "export const {name} = memo(({{{props}}}) => (",
  78. "{render}",
  79. "))",
  80. ]
  81. ).format
  82. # Code to render the custom components page.
  83. COMPONENTS = path_ops.join(
  84. [
  85. "{imports}",
  86. "{components}",
  87. ]
  88. ).format
  89. # React state declarations.
  90. USE_STATE = CONST(
  91. name="[{state}, {set_state}]", value="useState({initial_state})"
  92. ).format
  93. def format_state_setter(state: str) -> str:
  94. """Format a state setter.
  95. Args:
  96. state: The name of the state variable.
  97. Returns:
  98. The compiled state setter.
  99. """
  100. return f"set{state[0].upper() + state[1:]}"
  101. def format_state(
  102. state: str,
  103. initial_state: str,
  104. ) -> str:
  105. """Format a state declaration.
  106. Args:
  107. state: The name of the state variable.
  108. initial_state: The initial state of the state variable.
  109. Returns:
  110. The compiled state declaration.
  111. """
  112. set_state = format_state_setter(state)
  113. return USE_STATE(state=state, set_state=set_state, initial_state=initial_state)
  114. # Events.
  115. EVENT_ENDPOINT = constants.Endpoint.EVENT.name
  116. EVENT_FN = path_ops.join(
  117. [
  118. "const Event = events => {set_state}({{",
  119. " ...{state},",
  120. " events: [...{state}.events, ...events],",
  121. "}})",
  122. ]
  123. ).format
  124. UPLOAD_FN = path_ops.join(
  125. [
  126. "const File = files => {set_state}({{",
  127. " ...{state},",
  128. " files,",
  129. "}})",
  130. ]
  131. ).format
  132. FULL_CONTROL = path_ops.join(
  133. [
  134. "{{setState(prev => ({{",
  135. "...prev,{state_name}: {arg}",
  136. "}}), ",
  137. "()=>Event([{chain}])",
  138. ")}}",
  139. ]
  140. ).format
  141. # Effects.
  142. ROUTER = constants.ROUTER
  143. RESULT = constants.RESULT
  144. PROCESSING = constants.PROCESSING
  145. SOCKET = constants.SOCKET
  146. STATE = constants.STATE
  147. EVENTS = constants.EVENTS
  148. SET_RESULT = format_state_setter(RESULT)
  149. READY = f"const {{ isReady }} = {ROUTER};"
  150. USE_EFFECT = path_ops.join(
  151. [
  152. "useEffect(() => {{",
  153. " if(!isReady) {{",
  154. " return;",
  155. " }}",
  156. f" if (!{SOCKET}.current) {{{{",
  157. f" connect({SOCKET}, {{state}}, {{set_state}}, {RESULT}, {SET_RESULT}, {ROUTER}, {EVENT_ENDPOINT}, {{transports}})",
  158. " }}",
  159. " const update = async () => {{",
  160. f" if ({RESULT}.{STATE} != null) {{{{",
  161. f" {{set_state}}({{{{",
  162. f" ...{RESULT}.{STATE},",
  163. f" events: [...{{state}}.{EVENTS}, ...{RESULT}.{EVENTS}],",
  164. " }})",
  165. f" {SET_RESULT}({{{{",
  166. f" {STATE}: null,",
  167. f" {EVENTS}: [],",
  168. f" {PROCESSING}: false,",
  169. " }})",
  170. " }}",
  171. f" await updateState({{state}}, {{set_state}}, {RESULT}, {SET_RESULT}, {ROUTER}, {SOCKET}.current)",
  172. " }}",
  173. " update()",
  174. "}})",
  175. ]
  176. ).format
  177. # Routing
  178. ROUTER = f"const {constants.ROUTER} = useRouter()"
  179. # Sockets.
  180. SOCKET = "const socket = useRef(null)"
  181. # Color toggle
  182. COLORTOGGLE = f"const {{ {constants.COLOR_MODE}, {constants.TOGGLE_COLOR_MODE} }} = {constants.USE_COLOR_MODE}()"
  183. # Sitemap config file.
  184. SITEMAP_CONFIG = "module.exports = {config}".format