templates.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. """Templates to use in the reflex compiler."""
  2. from __future__ import annotations
  3. from jinja2 import Environment, FileSystemLoader, Template
  4. from reflex import constants
  5. from reflex.constants import Hooks
  6. from reflex.utils.format import format_state_name, json_dumps
  7. from reflex.vars.base import VarData
  8. def _sort_hooks(hooks: dict[str, VarData | None]):
  9. """Sort the hooks by their position.
  10. Args:
  11. hooks: The hooks to sort.
  12. Returns:
  13. The sorted hooks.
  14. """
  15. sorted_hooks = {
  16. Hooks.HookPosition.INTERNAL: [],
  17. Hooks.HookPosition.PRE_TRIGGER: [],
  18. Hooks.HookPosition.POST_TRIGGER: [],
  19. }
  20. for hook, data in hooks.items():
  21. if data and data.position and data.position == Hooks.HookPosition.INTERNAL:
  22. sorted_hooks[Hooks.HookPosition.INTERNAL].append((hook, data))
  23. elif not data or (
  24. not data.position
  25. or data.position == constants.Hooks.HookPosition.PRE_TRIGGER
  26. ):
  27. sorted_hooks[Hooks.HookPosition.PRE_TRIGGER].append((hook, data))
  28. elif (
  29. data
  30. and data.position
  31. and data.position == constants.Hooks.HookPosition.POST_TRIGGER
  32. ):
  33. sorted_hooks[Hooks.HookPosition.POST_TRIGGER].append((hook, data))
  34. return sorted_hooks
  35. class ReflexJinjaEnvironment(Environment):
  36. """The template class for jinja environment."""
  37. def __init__(self) -> None:
  38. """Set default environment."""
  39. extensions = ["jinja2.ext.debug"]
  40. super().__init__(
  41. extensions=extensions,
  42. trim_blocks=True,
  43. lstrip_blocks=True,
  44. )
  45. self.filters["json_dumps"] = json_dumps
  46. self.filters["react_setter"] = lambda state: f"set{state.capitalize()}"
  47. self.filters["var_name"] = format_state_name
  48. self.loader = FileSystemLoader(constants.Templates.Dirs.JINJA_TEMPLATE)
  49. self.globals["const"] = {
  50. "socket": constants.CompileVars.SOCKET,
  51. "result": constants.CompileVars.RESULT,
  52. "router": constants.CompileVars.ROUTER,
  53. "event_endpoint": constants.Endpoint.EVENT.name,
  54. "events": constants.CompileVars.EVENTS,
  55. "state": constants.CompileVars.STATE,
  56. "final": constants.CompileVars.FINAL,
  57. "processing": constants.CompileVars.PROCESSING,
  58. "initial_result": {
  59. constants.CompileVars.STATE: None,
  60. constants.CompileVars.EVENTS: [],
  61. constants.CompileVars.FINAL: True,
  62. constants.CompileVars.PROCESSING: False,
  63. },
  64. "color_mode": constants.ColorMode.NAME,
  65. "resolved_color_mode": constants.ColorMode.RESOLVED_NAME,
  66. "toggle_color_mode": constants.ColorMode.TOGGLE,
  67. "set_color_mode": constants.ColorMode.SET,
  68. "use_color_mode": constants.ColorMode.USE,
  69. "hydrate": constants.CompileVars.HYDRATE,
  70. "on_load_internal": constants.CompileVars.ON_LOAD_INTERNAL,
  71. "update_vars_internal": constants.CompileVars.UPDATE_VARS_INTERNAL,
  72. "frontend_exception_state": constants.CompileVars.FRONTEND_EXCEPTION_STATE_FULL,
  73. "hook_position": constants.Hooks.HookPosition,
  74. }
  75. self.globals["sort_hooks"] = _sort_hooks
  76. def get_template(name: str) -> Template:
  77. """Get render function that work with a template.
  78. Args:
  79. name: The template name. "/" is used as the path separator.
  80. Returns:
  81. A render function.
  82. """
  83. return ReflexJinjaEnvironment().get_template(name=name)
  84. # Template for the Reflex config file.
  85. RXCONFIG = get_template("app/rxconfig.py.jinja2")
  86. # Code to render a NextJS Document root.
  87. DOCUMENT_ROOT = get_template("web/pages/_document.js.jinja2")
  88. # Code to render NextJS App root.
  89. APP_ROOT = get_template("web/pages/_app.js.jinja2")
  90. # Template for the theme file.
  91. THEME = get_template("web/utils/theme.js.jinja2")
  92. # Template for the context file.
  93. CONTEXT = get_template("web/utils/context.js.jinja2")
  94. # Template for Tailwind config.
  95. TAILWIND_CONFIG = get_template("web/tailwind.config.js.jinja2")
  96. # Template to render a component tag.
  97. COMPONENT = get_template("web/pages/component.js.jinja2")
  98. # Code to render a single NextJS page.
  99. PAGE = get_template("web/pages/index.js.jinja2")
  100. # Code to render the custom components page.
  101. COMPONENTS = get_template("web/pages/custom_component.js.jinja2")
  102. # Code to render Component instances as part of StatefulComponent
  103. STATEFUL_COMPONENT = get_template("web/pages/stateful_component.js.jinja2")
  104. # Code to render StatefulComponent to an external file to be shared
  105. STATEFUL_COMPONENTS = get_template("web/pages/stateful_components.js.jinja2")
  106. # Sitemap config file.
  107. SITEMAP_CONFIG = "module.exports = {config}".format
  108. # Code to render the root stylesheet.
  109. STYLE = get_template("web/styles/styles.css.jinja2")
  110. # Code that generate the package json file
  111. PACKAGE_JSON = get_template("web/package.json.jinja2")
  112. # Template containing some macros used in the web pages.
  113. MACROS = get_template("web/pages/macros.js.jinja2")
  114. # Code that generate the pyproject.toml file for custom components.
  115. CUSTOM_COMPONENTS_PYPROJECT_TOML = get_template(
  116. "custom_components/pyproject.toml.jinja2"
  117. )
  118. # Code that generates the README file for custom components.
  119. CUSTOM_COMPONENTS_README = get_template("custom_components/README.md.jinja2")
  120. # Code that generates the source file for custom components.
  121. CUSTOM_COMPONENTS_SOURCE = get_template("custom_components/src.py.jinja2")
  122. # Code that generates the init file for custom components.
  123. CUSTOM_COMPONENTS_INIT_FILE = get_template("custom_components/__init__.py.jinja2")
  124. # Code that generates the demo app main py file for testing custom components.
  125. CUSTOM_COMPONENTS_DEMO_APP = get_template("custom_components/demo_app.py.jinja2")