compiler.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. """Compiler for the pynecone apps."""
  2. from __future__ import annotations
  3. import json
  4. from functools import wraps
  5. from typing import Callable, List, Set, Tuple, Type
  6. from pynecone import constants
  7. from pynecone.compiler import templates, utils
  8. from pynecone.components.component import Component, CustomComponent
  9. from pynecone.state import State
  10. from pynecone.style import Style
  11. from pynecone.utils import imports, path_ops
  12. # Imports to be included in every Pynecone app.
  13. DEFAULT_IMPORTS: imports.ImportDict = {
  14. "react": {"useEffect", "useRef", "useState"},
  15. "next/router": {"useRouter"},
  16. f"/{constants.STATE_PATH}": {"connect", "updateState", "uploadFiles", "E"},
  17. "": {"focus-visible/dist/focus-visible"},
  18. "@chakra-ui/react": {constants.USE_COLOR_MODE},
  19. }
  20. def _compile_document_root(root: Component) -> str:
  21. """Compile the document root.
  22. Args:
  23. root: The document root to compile.
  24. Returns:
  25. The compiled document root.
  26. """
  27. return templates.DOCUMENT_ROOT(
  28. imports=utils.compile_imports(root.get_imports()),
  29. document=root.render(),
  30. )
  31. def _compile_theme(theme: dict) -> str:
  32. """Compile the theme.
  33. Args:
  34. theme: The theme to compile.
  35. Returns:
  36. The compiled theme.
  37. """
  38. return templates.THEME(theme=json.dumps(theme))
  39. def _compile_page(component: Component, state: Type[State]) -> str:
  40. """Compile the component given the app state.
  41. Args:
  42. component: The component to compile.
  43. state: The app state.
  44. Returns:
  45. The compiled component.
  46. """
  47. # Merge the default imports with the app-specific imports.
  48. imports = utils.merge_imports(DEFAULT_IMPORTS, component.get_imports())
  49. # Compile the code to render the component.
  50. return templates.PAGE(
  51. imports=utils.compile_imports(imports),
  52. custom_code=path_ops.join(component.get_custom_code()),
  53. constants=utils.compile_constants(),
  54. state=utils.compile_state(state),
  55. events=utils.compile_events(state),
  56. effects=utils.compile_effects(state),
  57. hooks=path_ops.join(component.get_hooks()),
  58. render=component.render(),
  59. )
  60. def _compile_components(components: Set[CustomComponent]) -> str:
  61. """Compile the components.
  62. Args:
  63. components: The components to compile.
  64. Returns:
  65. The compiled components.
  66. """
  67. imports = {
  68. "react": {"memo"},
  69. f"/{constants.STATE_PATH}": {"E"},
  70. }
  71. component_defs = []
  72. # Compile each component.
  73. for component in components:
  74. component_def, component_imports = utils.compile_custom_component(component)
  75. component_defs.append(component_def)
  76. imports = utils.merge_imports(imports, component_imports)
  77. # Compile the components page.
  78. return templates.COMPONENTS(
  79. imports=utils.compile_imports(imports),
  80. components=path_ops.join(component_defs),
  81. )
  82. def write_output(fn: Callable[..., Tuple[str, str]]):
  83. """Write the output of the function to a file.
  84. Args:
  85. fn: The function to decorate.
  86. Returns:
  87. The decorated function.
  88. """
  89. @wraps(fn)
  90. def wrapper(*args, write: bool = True) -> Tuple[str, str]:
  91. """Write the output of the function to a file.
  92. Args:
  93. *args: The arguments to pass to the function.
  94. write: Whether to write the output to a file.
  95. Returns:
  96. The path and code of the output.
  97. """
  98. path, code = fn(*args)
  99. if write:
  100. utils.write_page(path, code)
  101. return path, code
  102. return wrapper
  103. @write_output
  104. def compile_document_root(stylesheets: List[str]) -> Tuple[str, str]:
  105. """Compile the document root.
  106. Args:
  107. stylesheets: The stylesheets to include in the document root.
  108. Returns:
  109. The path and code of the compiled document root.
  110. """
  111. # Get the path for the output file.
  112. output_path = utils.get_page_path(constants.DOCUMENT_ROOT)
  113. # Create the document root.
  114. document_root = utils.create_document_root(stylesheets)
  115. # Compile the document root.
  116. code = _compile_document_root(document_root)
  117. return output_path, code
  118. @write_output
  119. def compile_theme(style: Style) -> Tuple[str, str]:
  120. """Compile the theme.
  121. Args:
  122. style: The style to compile.
  123. Returns:
  124. The path and code of the compiled theme.
  125. """
  126. output_path = utils.get_theme_path()
  127. # Create the theme.
  128. theme = utils.create_theme(style)
  129. # Compile the theme.
  130. code = _compile_theme(theme)
  131. return output_path, code
  132. @write_output
  133. def compile_page(
  134. path: str, component: Component, state: Type[State]
  135. ) -> Tuple[str, str]:
  136. """Compile a single page.
  137. Args:
  138. path: The path to compile the page to.
  139. component: The component to compile.
  140. state: The app state.
  141. Returns:
  142. The path and code of the compiled page.
  143. """
  144. # Get the path for the output file.
  145. output_path = utils.get_page_path(path)
  146. # Add the style to the component.
  147. code = _compile_page(component, state)
  148. return output_path, code
  149. @write_output
  150. def compile_components(components: Set[CustomComponent]):
  151. """Compile the custom components.
  152. Args:
  153. components: The custom components to compile.
  154. Returns:
  155. The path and code of the compiled components.
  156. """
  157. # Get the path for the output file.
  158. output_path = utils.get_components_path()
  159. # Compile the components.
  160. code = _compile_components(components)
  161. return output_path, code
  162. def purge_web_pages_dir():
  163. """Empty out .web directory."""
  164. template_files = ["_app.js", "404.js"]
  165. utils.empty_dir(constants.WEB_PAGES_DIR, keep_files=template_files)