constants.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. """Constants used throughout the package."""
  2. from __future__ import annotations
  3. import os
  4. import platform
  5. import re
  6. from enum import Enum
  7. from types import SimpleNamespace
  8. from platformdirs import PlatformDirs
  9. # importlib is only available for Python 3.8+ so we need the backport for Python 3.7
  10. try:
  11. from importlib import metadata
  12. except ImportError:
  13. import importlib_metadata as metadata # pyright: ignore[reportMissingImports]
  14. IS_WINDOWS = platform.system() == "Windows"
  15. def get_fnm_name() -> str | None:
  16. """Get the appropriate fnm executable name based on the current platform.
  17. Returns:
  18. The fnm executable name for the current platform.
  19. """
  20. platform_os = platform.system()
  21. if platform_os == "Windows":
  22. return "fnm-windows"
  23. elif platform_os == "Darwin":
  24. return "fnm-macos"
  25. elif platform_os == "Linux":
  26. machine = platform.machine()
  27. if machine == "arm" or machine.startswith("armv7"):
  28. return "fnm-arm32"
  29. elif machine.startswith("aarch") or machine.startswith("armv8"):
  30. return "fnm-arm64"
  31. return "fnm-linux"
  32. return None
  33. # App names and versions.
  34. # The name of the Reflex package.
  35. MODULE_NAME = "reflex"
  36. # The current version of Reflex.
  37. VERSION = metadata.version(MODULE_NAME)
  38. # Files and directories used to init a new project.
  39. # The directory to store reflex dependencies.
  40. REFLEX_DIR = (
  41. # on windows, we use C:/Users/<username>/AppData/Local/reflex.
  42. # on macOS, we use ~/Library/Application Support/reflex.
  43. # on linux, we use ~/.local/share/reflex.
  44. PlatformDirs(MODULE_NAME, False).user_data_dir
  45. )
  46. # The root directory of the reflex library.
  47. ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  48. # The name of the assets directory.
  49. APP_ASSETS_DIR = "assets"
  50. # The template directory used during reflex init.
  51. TEMPLATE_DIR = os.path.join(ROOT_DIR, MODULE_NAME, ".templates")
  52. # The web subdirectory of the template directory.
  53. WEB_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "web")
  54. # The assets subdirectory of the template directory.
  55. ASSETS_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, APP_ASSETS_DIR)
  56. # The jinja template directory.
  57. JINJA_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "jinja")
  58. # Bun config.
  59. # The Bun version.
  60. BUN_VERSION = "0.7.3"
  61. # Min Bun Version
  62. MIN_BUN_VERSION = "0.7.0"
  63. # The directory to store the bun.
  64. BUN_ROOT_PATH = os.path.join(REFLEX_DIR, "bun")
  65. # Default bun path.
  66. DEFAULT_BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
  67. # URL to bun install script.
  68. BUN_INSTALL_URL = "https://bun.sh/install"
  69. # FNM / Node config.
  70. # The FNM version.
  71. FNM_VERSION = "1.35.1"
  72. # The Node version.
  73. NODE_VERSION = "18.17.0"
  74. # The minimum required node version.
  75. NODE_VERSION_MIN = "16.8.0"
  76. # The directory to store fnm.
  77. FNM_DIR = os.path.join(REFLEX_DIR, "fnm")
  78. FNM_FILENAME = get_fnm_name()
  79. # The fnm executable binary.
  80. FNM_EXE = os.path.join(FNM_DIR, "fnm.exe" if IS_WINDOWS else "fnm")
  81. # The node bin path.
  82. NODE_BIN_PATH = os.path.join(
  83. FNM_DIR,
  84. "node-versions",
  85. f"v{NODE_VERSION}",
  86. "installation",
  87. "bin" if not IS_WINDOWS else "",
  88. )
  89. # The default path where node is installed.
  90. NODE_PATH = os.path.join(NODE_BIN_PATH, "node.exe" if IS_WINDOWS else "node")
  91. # The default path where npm is installed.
  92. NPM_PATH = os.path.join(NODE_BIN_PATH, "npm")
  93. # The URL to the fnm release binary
  94. FNM_INSTALL_URL = (
  95. f"https://github.com/Schniz/fnm/releases/download/v{FNM_VERSION}/{FNM_FILENAME}.zip"
  96. )
  97. # The frontend directories in a project.
  98. # The web folder where the NextJS app is compiled to.
  99. WEB_DIR = ".web"
  100. # The name of the utils file.
  101. UTILS_DIR = "utils"
  102. # The name of the output static directory.
  103. STATIC_DIR = "_static"
  104. # The name of the state file.
  105. STATE_PATH = "/".join([UTILS_DIR, "state"])
  106. # The name of the components file.
  107. COMPONENTS_PATH = "/".join([UTILS_DIR, "components"])
  108. # The directory where the app pages are compiled to.
  109. WEB_PAGES_DIR = os.path.join(WEB_DIR, "pages")
  110. # The directory where the static build is located.
  111. WEB_STATIC_DIR = os.path.join(WEB_DIR, STATIC_DIR)
  112. # The directory where the utils file is located.
  113. WEB_UTILS_DIR = os.path.join(WEB_DIR, UTILS_DIR)
  114. # The directory where the assets are located.
  115. WEB_ASSETS_DIR = os.path.join(WEB_DIR, "public")
  116. # The directory where styles are located.
  117. STYLES_DIR = os.path.join(WEB_DIR, "styles")
  118. # The Tailwind config.
  119. TAILWIND_CONFIG = os.path.join(WEB_DIR, "tailwind.config.js")
  120. # Default Tailwind content paths
  121. TAILWIND_CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"]
  122. # Relative tailwind style path to root stylesheet in STYLES_DIR.
  123. TAILWIND_ROOT_STYLE_PATH = "./tailwind.css"
  124. # The Tailwindcss version
  125. TAILWIND_VERSION = "tailwindcss@^3.3.2"
  126. # The package json file
  127. PACKAGE_JSON_PATH = os.path.join(WEB_DIR, "package.json")
  128. # The NextJS config file
  129. NEXT_CONFIG_FILE = "next.config.js"
  130. # The sitemap config file.
  131. SITEMAP_CONFIG_FILE = os.path.join(WEB_DIR, "next-sitemap.config.js")
  132. # The node modules directory.
  133. NODE_MODULES = "node_modules"
  134. # The package lock file.
  135. PACKAGE_LOCK = "package-lock.json"
  136. # The reflex json file.
  137. REFLEX_JSON = os.path.join(WEB_DIR, "reflex.json")
  138. # The env json file.
  139. ENV_JSON = os.path.join(WEB_DIR, "env.json")
  140. # Compiler variables.
  141. # The extension for compiled Javascript files.
  142. JS_EXT = ".js"
  143. # The extension for python files.
  144. PY_EXT = ".py"
  145. # The extension for css files.
  146. CSS_EXT = ".css"
  147. # The expected variable name where the rx.App is stored.
  148. APP_VAR = "app"
  149. # The expected variable name where the API object is stored for deployment.
  150. API_VAR = "api"
  151. # The name of the router variable.
  152. ROUTER = "router"
  153. # The name of the socket variable.
  154. SOCKET = "socket"
  155. # The name of the variable to hold API results.
  156. RESULT = "result"
  157. # The name of the final variable.
  158. FINAL = "final"
  159. # The name of the process variable.
  160. PROCESSING = "processing"
  161. # The name of the state variable.
  162. STATE = "state"
  163. # The name of the events variable.
  164. EVENTS = "events"
  165. # The name of the initial hydrate event.
  166. HYDRATE = "hydrate"
  167. # The name of the is_hydrated variable.
  168. IS_HYDRATED = "is_hydrated"
  169. # The name of the index page.
  170. INDEX_ROUTE = "index"
  171. # The name of the app root page.
  172. APP_ROOT = "_app"
  173. # The root stylesheet filename.
  174. STYLESHEET_ROOT = "styles"
  175. # The name of the document root page.
  176. DOCUMENT_ROOT = "_document"
  177. # The name of the theme page.
  178. THEME = "theme"
  179. # The prefix used to create setters for state vars.
  180. SETTER_PREFIX = "set_"
  181. # The name of the frontend zip during deployment.
  182. FRONTEND_ZIP = "frontend.zip"
  183. # The name of the backend zip during deployment.
  184. BACKEND_ZIP = "backend.zip"
  185. # The default title to show for Reflex apps.
  186. DEFAULT_TITLE = "Reflex App"
  187. # The default description to show for Reflex apps.
  188. DEFAULT_DESCRIPTION = "A Reflex app."
  189. # The default image to show for Reflex apps.
  190. DEFAULT_IMAGE = "favicon.ico"
  191. # The default meta list to show for Reflex apps.
  192. DEFAULT_META_LIST = []
  193. # The gitignore file.
  194. GITIGNORE_FILE = ".gitignore"
  195. # Files to gitignore.
  196. DEFAULT_GITIGNORE = {WEB_DIR, "*.db", "__pycache__/", "*.py[cod]"}
  197. # The name of the reflex config module.
  198. CONFIG_MODULE = "rxconfig"
  199. # The python config file.
  200. CONFIG_FILE = f"{CONFIG_MODULE}{PY_EXT}"
  201. # The previous config file.
  202. OLD_CONFIG_FILE = f"pcconfig{PY_EXT}"
  203. # The deployment URL.
  204. PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
  205. # Token expiration time in seconds.
  206. TOKEN_EXPIRATION = 60 * 60
  207. # Testing variables.
  208. # Testing os env set by pytest when running a test case.
  209. PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
  210. # Env modes
  211. class Env(str, Enum):
  212. """The environment modes."""
  213. DEV = "dev"
  214. PROD = "prod"
  215. # Log levels
  216. class LogLevel(str, Enum):
  217. """The log levels."""
  218. DEBUG = "debug"
  219. INFO = "info"
  220. WARNING = "warning"
  221. ERROR = "error"
  222. CRITICAL = "critical"
  223. def __le__(self, other: LogLevel) -> bool:
  224. """Compare log levels.
  225. Args:
  226. other: The other log level.
  227. Returns:
  228. True if the log level is less than or equal to the other log level.
  229. """
  230. levels = list(LogLevel)
  231. return levels.index(self) <= levels.index(other)
  232. # Templates
  233. class Template(str, Enum):
  234. """The templates to use for the app."""
  235. DEFAULT = "default"
  236. COUNTER = "counter"
  237. class Endpoint(Enum):
  238. """Endpoints for the reflex backend API."""
  239. PING = "ping"
  240. EVENT = "_event"
  241. UPLOAD = "_upload"
  242. def __str__(self) -> str:
  243. """Get the string representation of the endpoint.
  244. Returns:
  245. The path for the endpoint.
  246. """
  247. return f"/{self.value}"
  248. def get_url(self) -> str:
  249. """Get the URL for the endpoint.
  250. Returns:
  251. The full URL for the endpoint.
  252. """
  253. # Import here to avoid circular imports.
  254. from reflex.config import get_config
  255. # Get the API URL from the config.
  256. config = get_config()
  257. url = "".join([config.api_url, str(self)])
  258. # The event endpoint is a websocket.
  259. if self == Endpoint.EVENT:
  260. # Replace the protocol with ws.
  261. url = url.replace("https://", "wss://").replace("http://", "ws://")
  262. # Return the url.
  263. return url
  264. class SocketEvent(Enum):
  265. """Socket events sent by the reflex backend API."""
  266. PING = "ping"
  267. EVENT = "event"
  268. def __str__(self) -> str:
  269. """Get the string representation of the event name.
  270. Returns:
  271. The event name string.
  272. """
  273. return str(self.value)
  274. class RouteArgType(SimpleNamespace):
  275. """Type of dynamic route arg extracted from URI route."""
  276. # Typecast to str is needed for Enum to work.
  277. SINGLE = str("arg_single")
  278. LIST = str("arg_list")
  279. # the name of the backend var containing path and client information
  280. ROUTER_DATA = "router_data"
  281. class RouteVar(SimpleNamespace):
  282. """Names of variables used in the router_data dict stored in State."""
  283. CLIENT_IP = "ip"
  284. CLIENT_TOKEN = "token"
  285. HEADERS = "headers"
  286. PATH = "pathname"
  287. ORIGIN = "asPath"
  288. SESSION_ID = "sid"
  289. QUERY = "query"
  290. COOKIE = "cookie"
  291. class RouteRegex(SimpleNamespace):
  292. """Regex used for extracting route args in route."""
  293. ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
  294. # group return the catchall pattern (i.e. "[[..slug]]")
  295. CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
  296. # group return the arg name (i.e. "slug")
  297. STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
  298. # group return the arg name (i.e. "slug")
  299. OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
  300. class PackageJsonCommands(SimpleNamespace):
  301. """Commands used in package.json file."""
  302. DEV = "next dev"
  303. EXPORT = "next build && next export -o _static"
  304. EXPORT_SITEMAP = "next build && next-sitemap && next export -o _static"
  305. PROD = "next start"
  306. PACKAGE_DEPENDENCIES = {
  307. "@chakra-ui/react": "^2.6.0",
  308. "@chakra-ui/system": "^2.5.6",
  309. "@emotion/react": "^11.10.6",
  310. "@emotion/styled": "^11.10.6",
  311. "axios": "^1.4.0",
  312. "chakra-react-select": "^4.6.0",
  313. "focus-visible": "^5.2.0",
  314. "json5": "^2.2.3",
  315. "next": "^13.3.1",
  316. "next-sitemap": "^4.1.8",
  317. "react": "^18.2.0",
  318. "react-dom": "^18.2.0",
  319. "socket.io-client": "^4.6.1",
  320. "universal-cookie": "^4.0.4",
  321. }
  322. PACKAGE_DEV_DEPENDENCIES = {
  323. "autoprefixer": "^10.4.14",
  324. "postcss": "^8.4.24",
  325. }
  326. # 404 variables
  327. SLUG_404 = "404"
  328. TITLE_404 = "404 - Not Found"
  329. FAVICON_404 = "favicon.ico"
  330. DESCRIPTION_404 = "The page was not found"
  331. ROUTE_NOT_FOUND = "routeNotFound"
  332. # Color mode variables
  333. USE_COLOR_MODE = "useColorMode"
  334. COLOR_MODE = "colorMode"
  335. TOGGLE_COLOR_MODE = "toggleColorMode"
  336. # Server socket configuration variables
  337. POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
  338. PING_INTERVAL = 25
  339. PING_TIMEOUT = 120
  340. # Alembic migrations
  341. ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")
  342. # Keys in the client_side_storage dict
  343. COOKIES = "cookies"
  344. LOCAL_STORAGE = "local_storage"
  345. # Names of event handlers on all components mapped to useEffect
  346. ON_MOUNT = "on_mount"
  347. ON_UNMOUNT = "on_unmount"
  348. # If this env var is set to "yes", App.compile will be a no-op
  349. SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"