content: import collections
  import json
  import string
  import textwrap
  import importlib
  
  from .._utils import pascal_case
  
  
  shapes = {}
  shape_template = """{name} = TypedDict(
      "{name}",
      {values}
  )
  """
  custom_imports = collections.defaultdict(lambda: collections.defaultdict(list))
  
  
  def _get_custom(module_name, prop, default):
      if not module_name:
          return default
      try:
          module = importlib.import_module(module_name)
          return getattr(module, prop, default)
      except ImportError:
          return default
  
  
  def get_custom_imports(module_name):
      return _get_custom(module_name, "custom_imports", {})
  
  
  def get_custom_props(module_name):
      return _get_custom(module_name, "custom_props", {})
  
  
  def get_custom_ignore(module_name):
      return _get_custom(module_name, "ignore_props", ["style"])
  
  
  def _clean_key(key):
      k = ""
      for ch in key:
          if ch not in string.ascii_letters + "_":
              k += "_"
          else:
              k += ch
      return k
  
  
  def generate_any(*_):
      return "typing.Any"
  
  
  def generate_shape(type_info, component_name: str, prop_name: str):
      props = []
      name = pascal_case(prop_name)
  
      for prop_key, prop_type in type_info["value"].items():
          typed = get_prop_typing(
              prop_type["name"], component_name, f"{prop_name}_{prop_key}", prop_type
          )
          if not prop_type.get("required"):
              props.append(f'        "{prop_key}": NotRequired[{typed}]')
          else:
              props.append(f'        "{prop_key}": {typed}')
  
      shapes.setdefault(component_name, {})
      shapes[component_name][name] = textwrap.indent(
          shape_template.format(
              name=name, values="    {\n" + ",\n".join(props) + "\n    }"
          ),
          "    ",
      )
  
      return f'"{name}"'
  
  
  def generate_union(type_info, component_name: str, prop_name: str):
      types = []
      for union in type_info["value"]:
          u_type = get_prop_typing(union["name"], component_name, prop_name, union)
          if u_type not in types:
              types.append(u_type)
      return f"typing.Union[{', '.join(types)}]"
  
  
  def generate_tuple(
      type_info,
      component_name: str,
      prop_name: str,
  ):
      els = type_info.get("elements")
      elements = ", ".join(
          get_prop_typing(x.get("name"), component_name, prop_name, x) for x in els
      )
      return f"typing.Tuple[{elements}]"
  
  
  def generate_array_of(
      type_info,
      component_name: str,
      prop_name: str,
  ):
      typed = get_prop_typing(
          type_info["value"]["name"], component_name, prop_name, type_info["value"]
      )
      return f"typing.Sequence[{typed}]"
  
  
  def generate_object_of(type_info, component_name: str, prop_name: str):
      typed = get_prop_typing(
          type_info["value"]["name"], component_name, prop_name, type_info["value"]
      )
      return f"typing.Dict[typing.Union[str, float, int], {typed}]"
  
  
  def generate_type(typename):
      def type_handler(*_):
          return typename
  
      return type_handler
  
  
  def _get_literal_value(value):
      if isinstance(value, str):
          value = json.loads(value.replace("'", '"'))
  
      if value is None:
          return "None"
  
      if isinstance(value, bool):
          return str(value)
  
      return json.dumps(value)
  
  
  def generate_enum(type_info, *_):
      values = [_get_literal_value(v["value"]) for v in type_info["value"] if v]
      return f"Literal[{', '.join(values)}]"
  
  
  def generate_literal(type_info, *_):
      return f"Literal[{json.dumps(type_info['value'])}]"
  
  
  def _get_custom_prop(custom_props, component_name, prop_name):
      customs = custom_props.get(component_name) or custom_props.get("*", {})
      return customs.get(prop_name)
  
  
  def get_prop_typing(
      type_name: str,
      component_name: str,
      prop_name: str,
      type_info,
      custom_props=None,
      custom_ignore=None,
  ):
      if prop_name == "id":
          # Id is always the same either a string or a dict for pattern matching.
          return "typing.Union[str, dict]"
  
      if custom_props:
          special = _get_custom_prop(custom_props, component_name, prop_name)
          if special:
              return special(type_info, component_name, prop_name)
  
      if custom_ignore and prop_name in custom_ignore:
          return "typing.Any"
  
      prop_type = PROP_TYPING.get(type_name, generate_any)(
          type_info, component_name, prop_name
      )
      return prop_type
  
  
  PROP_TYPING = {
      "array": generate_type("typing.Sequence"),
      "arrayOf": generate_array_of,
      "object": generate_type("dict"),
      "shape": generate_shape,
      "exact": generate_shape,
      "string": generate_type("str"),
      "bool": generate_type("bool"),
      "number": generate_type("NumberType"),
      "node": generate_type("ComponentType"),
      "func": generate_any,
      "element": generate_type("Component"),
      "union": generate_union,
      "any": generate_any,
      "custom": generate_any,
      "enum": generate_enum,
      "objectOf": generate_object_of,
      "tuple": generate_tuple,
      "literal": generate_literal,
  }
  