123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- """The route decorator and associated variables and functions."""
- from __future__ import annotations
- import re
- from reflex import constants
- def verify_route_validity(route: str) -> None:
- """Verify if the route is valid, and throw an error if not.
- Args:
- route: The route that need to be checked
- Raises:
- ValueError: If the route is invalid.
- """
- pattern = catchall_in_route(route)
- if pattern and not route.endswith(pattern):
- raise ValueError(f"Catch-all must be the last part of the URL: {route}")
- if route == "api" or route.startswith("api/"):
- raise ValueError(
- f"Cannot have a route prefixed with 'api/': {route} (conflicts with NextJS)"
- )
- def get_route_args(route: str) -> dict[str, str]:
- """Get the dynamic arguments for the given route.
- Args:
- route: The route to get the arguments for.
- Returns:
- The route arguments.
- """
- args = {}
- def add_route_arg(match: re.Match[str], type_: str):
- """Add arg from regex search result.
- Args:
- match: Result of a regex search
- type_: The assigned type for this arg
- Raises:
- ValueError: If the route is invalid.
- """
- arg_name = match.groups()[0]
- if arg_name in args:
- raise ValueError(
- f"Arg name [{arg_name}] is used more than once in this URL"
- )
- args[arg_name] = type_
- # Regex to check for route args.
- check = constants.RouteRegex.ARG
- check_strict_catchall = constants.RouteRegex.STRICT_CATCHALL
- check_opt_catchall = constants.RouteRegex.OPT_CATCHALL
- # Iterate over the route parts and check for route args.
- for part in route.split("/"):
- match_opt = check_opt_catchall.match(part)
- if match_opt:
- add_route_arg(match_opt, constants.RouteArgType.LIST)
- break
- match_strict = check_strict_catchall.match(part)
- if match_strict:
- add_route_arg(match_strict, constants.RouteArgType.LIST)
- break
- match = check.match(part)
- if match:
- # Add the route arg to the list.
- add_route_arg(match, constants.RouteArgType.SINGLE)
- return args
- def catchall_in_route(route: str) -> str:
- """Extract the catchall part from a route.
- Args:
- route: the route from which to extract
- Returns:
- str: the catchall part of the URI
- """
- match_ = constants.RouteRegex.CATCHALL.search(route)
- return match_.group() if match_ else ""
- def catchall_prefix(route: str) -> str:
- """Extract the prefix part from a route that contains a catchall.
- Args:
- route: the route from which to extract
- Returns:
- str: the prefix part of the URI
- """
- pattern = catchall_in_route(route)
- return route.replace(pattern, "") if pattern else ""
- def replace_brackets_with_keywords(input_string):
- """Replace brackets and everything inside it in a string with a keyword.
- Args:
- input_string: String to replace.
- Returns:
- new string containing keywords.
- """
- # /posts -> /post
- # /posts/[slug] -> /posts/__SINGLE_SEGMENT__
- # /posts/[slug]/comments -> /posts/__SINGLE_SEGMENT__/comments
- # /posts/[[slug]] -> /posts/__DOUBLE_SEGMENT__
- # / posts/[[...slug2]]-> /posts/__DOUBLE_CATCHALL_SEGMENT__
- # /posts/[...slug3]-> /posts/__SINGLE_CATCHALL_SEGMENT__
- # Replace [[...<slug>]] with __DOUBLE_CATCHALL_SEGMENT__
- output_string = re.sub(
- r"\[\[\.\.\..+?\]\]",
- constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT,
- input_string,
- )
- # Replace [...<slug>] with __SINGLE_CATCHALL_SEGMENT__
- output_string = re.sub(
- r"\[\.\.\..+?\]",
- constants.RouteRegex.SINGLE_CATCHALL_SEGMENT,
- output_string,
- )
- # Replace [[<slug>]] with __DOUBLE_SEGMENT__
- output_string = re.sub(
- r"\[\[.+?\]\]", constants.RouteRegex.DOUBLE_SEGMENT, output_string
- )
- # Replace [<slug>] with __SINGLE_SEGMENT__
- output_string = re.sub(
- r"\[.+?\]", constants.RouteRegex.SINGLE_SEGMENT, output_string
- )
- return output_string
|