compiler.py 5.3 KB

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