content: # pylint: disable=consider-using-f-string
  # type: ignore
  import copy
  import os
  import shutil
  import warnings
  import sys
  import importlib
  import uuid
  import hashlib
  
  from ._all_keywords import julia_keywords
  from ._py_components_generation import reorder_props
  
  # uuid of DashBase Julia package.
  jl_dash_base_uuid = "03207cf0-e2b3-4b91-9ca8-690cf0fb507e"
  
  # uuid of Dash Julia package. Used as base for component package uuid
  jl_dash_uuid = "1b08a953-4be3-4667-9a23-3db579824955"
  
  # Declaring longer string templates as globals to improve
  # readability, make method logic clearer to anyone inspecting
  # code below
  jl_component_string = '''
  export {funcname}
  
  """
      {funcname}(;kwargs...){children_signatures}
  
  {docstring}
  """
  function {funcname}(; kwargs...)
          available_props = Symbol[{component_props}]
          wild_props = Symbol[{wildcard_symbols}]
          return Component("{funcname}", "{element_name}", "{module_name}", available_props, wild_props; kwargs...)
  end
  {children_definitions}
  '''  # noqa:E501
  
  jl_children_signatures = """
      {funcname}(children::Any;kwargs...)
      {funcname}(children_maker::Function;kwargs...)
  """
  
  jl_children_definitions = """
  {funcname}(children::Any; kwargs...) = {funcname}(;kwargs..., children = children)
  {funcname}(children_maker::Function; kwargs...) = {funcname}(children_maker(); kwargs...)
  """
  
  jl_package_file_string = """
  module {package_name}
  using {base_package}
  
  const resources_path = realpath(joinpath( @__DIR__, "..", "deps"))
  const version = "{version}"
  
  {component_includes}
  
  function __init__()
      DashBase.register_package(
          DashBase.ResourcePkg(
              "{project_shortname}",
              resources_path,
              version = version,
              [
                  {resources_dist}
              ]
          )
  
      )
  end
  end
  """
  
  jl_projecttoml_string = """
  name = "{package_name}"
  uuid = "{package_uuid}"
  {authors}version = "{version}"
  
  [deps]
  {base_package} = "{dash_uuid}"
  
  [compat]
  julia = "1.2"
  {base_package} = "{base_version}"
  """
  
  jl_base_version = {
      "Dash": "0.1.3, 1.0",
      "DashBase": "0.1",
  }
  
  jl_component_include_string = 'include("jl/{name}.jl")'
  
  jl_resource_tuple_string = """DashBase.Resource(
      relative_package_path = {relative_package_path},
      external_url = {external_url},
      dynamic = {dynamic},
      async = {async_string},
      type = :{type}
  )"""
  
  core_packages = ["dash_html_components", "dash_core_components", "dash_table"]
  
  
  def jl_package_name(namestring):
      s = namestring.split("_")
      return "".join(w.capitalize() for w in s)
  
  
  def stringify_wildcards(wclist, no_symbol=False):
      if no_symbol:
          wcstring = "|".join("{}-".format(item) for item in wclist)
      else:
          wcstring = ", ".join('Symbol("{}-")'.format(item) for item in wclist)
      return wcstring
  
  
  def get_wildcards_jl(props):
      return [key.replace("-*", "") for key in props if key.endswith("-*")]
  
  
  def get_jl_prop_types(type_object):
      """Mapping from the PropTypes js type object to the Julia type."""
  
      def shape_or_exact():
          return "lists containing elements {}.\n{}".format(
              ", ".join("'{}'".format(t) for t in type_object["value"]),
              "Those elements have the following types:\n{}".format(
                  "\n".join(
                      create_prop_docstring_jl(
                          prop_name=prop_name,
                          type_object=prop,
                          required=prop["required"],
                          description=prop.get("description", ""),
                          indent_num=1,
                      )
                      for prop_name, prop in type_object["value"].items()
                  )
              ),
          )
  
      return dict(
          array=lambda: "Array",
          bool=lambda: "Bool",
          number=lambda: "Real",
          string=lambda: "String",
          object=lambda: "Dict",
          any=lambda: "Bool | Real | String | Dict | Array",
          element=lambda: "dash component",
          node=lambda: "a list of or a singular dash component, string or number",
          # React's PropTypes.oneOf
          enum=lambda: "a value equal to: {}".format(
              ", ".join("{}".format(str(t["value"])) for t in type_object["value"])
          ),
          # React's PropTypes.oneOfType
          union=lambda: "{}".format(
              " | ".join(
                  "{}".format(get_jl_type(subType))
                  for subType in type_object["value"]
                  if get_jl_type(subType) != ""
              )
          ),
          # React's PropTypes.arrayOf
          arrayOf=lambda: (
              "Array"
              + (
                  " of {}s".format(get_jl_type(type_object["value"]))
                  if get_jl_type(type_object["value"]) != ""
                  else ""
              )
          ),
          # React's PropTypes.objectOf
          objectOf=lambda: "Dict with Strings as keys and values of type {}".format(
              get_jl_type(type_object["value"])
          ),
          # React's PropTypes.shape
          shape=shape_or_exact,
          # React's PropTypes.exact
          exact=shape_or_exact,
      )
  
  
  def filter_props(props):
      """Filter props from the Component arguments to exclude:
          - Those without a "type" or a "flowType" field
          - Those with arg.type.name in {'func', 'symbol', 'instanceOf'}
      Parameters
      ----------
      props: dict
          Dictionary with {propName: propMetadata} structure
      Returns
      -------
      dict
          Filtered dictionary with {propName: propMetadata} structure
      """
      filtered_props = copy.deepcopy(props)
  
      for arg_name, arg in list(filtered_props.items()):
          if "type" not in arg and "flowType" not in arg:
              filtered_props.pop(arg_name)
              continue
  
          # Filter out functions and instances --
          if "type" in arg:  # These come from PropTypes
              arg_type = arg["type"]["name"]
              if arg_type in {"func", "symbol", "instanceOf"}:
                  filtered_props.pop(arg_name)
          elif "flowType" in arg:  # These come from Flow & handled differently
              arg_type_name = arg["flowType"]["name"]
              if arg_type_name == "signature":
                  # This does the same as the PropTypes filter above, but "func"
                  # is under "type" if "name" is "signature" vs just in "name"
                  if "type" not in arg["flowType"] or arg["flowType"]["type"] != "object":
                      filtered_props.pop(arg_name)
          else:
              raise ValueError
  
      return filtered_props
  
  
  def get_jl_type(type_object):
      """
      Convert JS types to Julia types for the component definition
      Parameters
      ----------
      type_object: dict
          react-docgen-generated prop type dictionary
      Returns
      -------
      str
          Julia type string
      """
      js_type_name = type_object["name"]
      js_to_jl_types = get_jl_prop_types(type_object=type_object)
      if js_type_name in js_to_jl_types:
          prop_type = js_to_jl_types[js_type_name]()
          return prop_type
      return ""
  
  
  def print_jl_type(typedata):
      typestring = get_jl_type(typedata).capitalize()
      if typestring:
          typestring += ". "
      return typestring
  
  
  def create_docstring_jl(component_name, props, description):
      """Create the Dash component docstring.
      Parameters
      ----------
      component_name: str
          Component name
      props: dict
          Dictionary with {propName: propMetadata} structure
      description: str
          Component description
      Returns
      -------
      str
          Dash component docstring
      """
      # Ensure props are ordered with children first
      props = reorder_props(props=props)
  
      return "A{n} {name} component.\n{description}\nKeyword arguments:\n{args}".format(
          n="n" if component_name[0].lower() in "aeiou" else "",
          name=component_name,
          description=description,
          args="\n".join(
              create_prop_docstring_jl(
                  prop_name=p,
                  type_object=prop["type"] if "type" in prop else prop["flowType"],
                  required=prop["required"],
                  description=prop["description"],
                  indent_num=0,
              )
              for p, prop in filter_props(props).items()
          ),
      )
  
  
  def create_prop_docstring_jl(
      prop_name,
      type_object,
      required,
      description,
      indent_num,
  ):
      """
      Create the Dash component prop docstring
      Parameters
      ----------
      prop_name: str
          Name of the Dash component prop
      type_object: dict
          react-docgen-generated prop type dictionary
      required: bool
          Component is required?
      description: str
          Dash component description
      indent_num: int
          Number of indents to use for the context block
          (creates 2 spaces for every indent)
      is_flow_type: bool
          Does the prop use Flow types? Otherwise, uses PropTypes
      Returns
      -------
      str
          Dash component prop docstring
      """
      jl_type_name = get_jl_type(type_object=type_object)
  
      indent_spacing = "  " * indent_num
      if "\n" in jl_type_name:
          return (
              "{indent_spacing}- `{name}` ({is_required}): {description}. "
              "{name} has the following type: {type}".format(
                  indent_spacing=indent_spacing,
                  name=prop_name,
                  type=jl_type_name,
                  description=description,
                  is_required="required" if required else "optional",
              )
          )
      return "{indent_spacing}- `{name}` ({type}{is_required}){description}".format(
          indent_spacing=indent_spacing,
          name=prop_name,
          type="{}; ".format(jl_type_name) if jl_type_name else "",
          description=(": {}".format(description) if description != "" else ""),
          is_required="required" if required else "optional",
      )
  
  
  # this logic will permit passing blank Julia prefixes to
  # dash-generate-components, while also enforcing
  # lower case names for the resulting functions; if a prefix
  # is supplied, leave it as-is
  def format_fn_name(prefix, name):
      if prefix:
          return "{}_{}".format(prefix, name.lower())
      return name.lower()
  
  
  def generate_metadata_strings(resources, metatype):
      def nothing_or_string(v):
          return '"{}"'.format(v) if v else "nothing"
  
      return [
          jl_resource_tuple_string.format(
              relative_package_path=nothing_or_string(
                  resource.get("relative_package_path", "")
              ),
              external_url=nothing_or_string(resource.get("external_url", "")),
              dynamic=str(resource.get("dynamic", "nothing")).lower(),
              type=metatype,
              async_string=":{}".format(str(resource.get("async")).lower())
              if "async" in resource.keys()
              else "nothing",
          )
          for resource in resources
      ]
  
  
  def is_core_package(project_shortname):
      return project_shortname in core_packages
  
  
  def base_package_name(project_shortname):
      return "DashBase" if is_core_package(project_shortname) else "Dash"
  
  
  def base_package_uid(project_shortname):
      return jl_dash_base_uuid if is_core_package(project_shortname) else jl_dash_uuid
  
  
  def generate_package_file(project_shortname, components, pkg_data, prefix):
      package_name = jl_package_name(project_shortname)
  
      sys.path.insert(0, os.getcwd())
      mod = importlib.import_module(project_shortname)
      js_dist = getattr(mod, "_js_dist", [])
      css_dist = getattr(mod, "_css_dist", [])
      project_ver = pkg_data.get("version")
  
      resources_dist = ",\n".join(
          generate_metadata_strings(js_dist, "js")
          + generate_metadata_strings(css_dist, "css")
      )
  
      package_string = jl_package_file_string.format(
          package_name=package_name,
          component_includes="\n".join(
              [
                  jl_component_include_string.format(
                      name=format_fn_name(prefix, comp_name)
                  )
                  for comp_name in components
              ]
          ),
          resources_dist=resources_dist,
          version=project_ver,
          project_shortname=project_shortname,
          base_package=base_package_name(project_shortname),
      )
      file_path = os.path.join("src", package_name + ".jl")
      with open(file_path, "w", encoding="utf-8") as f:
          f.write(package_string)
      print("Generated {}".format(file_path))
  
  
  def generate_toml_file(project_shortname, pkg_data):
      package_author = pkg_data.get("author", "")
      project_ver = pkg_data.get("version")
      package_name = jl_package_name(project_shortname)
      u = uuid.UUID(jl_dash_uuid)
  
      package_uuid = uuid.UUID(
          hex=u.hex[:-12] + hashlib.sha256(package_name.encode("utf-8")).hexdigest()[-12:]
      )
  
      authors_string = (
          'authors = ["{}"]\n'.format(package_author) if package_author else ""
      )
  
      base_package = base_package_name(project_shortname)
  
      toml_string = jl_projecttoml_string.format(
          package_name=package_name,
          package_uuid=package_uuid,
          version=project_ver,
          authors=authors_string,
          base_package=base_package,
          base_version=jl_base_version[base_package],
          dash_uuid=base_package_uid(project_shortname),
      )
      file_path = "Project.toml"
      with open(file_path, "w", encoding="utf-8") as f:
          f.write(toml_string)
      print("Generated {}".format(file_path))
  
  
  def generate_class_string(name, props, description, project_shortname, prefix):
      # Ensure props are ordered with children first
      filtered_props = reorder_props(filter_props(props))
  
      prop_keys = list(filtered_props.keys())
  
      docstring = (
          create_docstring_jl(
              component_name=name, props=filtered_props, description=description
          )
          .replace("\r\n", "\n")
          .replace("$", "\\$")
      )
  
      wclist = get_wildcards_jl(props)
      default_paramtext = ""
  
      # Filter props to remove those we don't want to expose
      for item in prop_keys[:]:
          if item.endswith("-*") or item == "setProps":
              prop_keys.remove(item)
          elif item in julia_keywords:
              prop_keys.remove(item)
              warnings.warn(
                  (
                      'WARNING: prop "{}" in component "{}" is a Julia keyword'
                      " - REMOVED FROM THE JULIA COMPONENT"
                  ).format(item, name)
              )
  
      default_paramtext += ", ".join(":{}".format(p) for p in prop_keys)
  
      has_children = "children" in prop_keys
      funcname = format_fn_name(prefix, name)
      children_signatures = (
          jl_children_signatures.format(funcname=funcname) if has_children else ""
      )
      children_definitions = (
          jl_children_definitions.format(funcname=funcname) if has_children else ""
      )
      return jl_component_string.format(
          funcname=format_fn_name(prefix, name),
          docstring=docstring,
          component_props=default_paramtext,
          wildcard_symbols=stringify_wildcards(wclist, no_symbol=False),
          wildcard_names=stringify_wildcards(wclist, no_symbol=True),
          element_name=name,
          module_name=project_shortname,
          children_signatures=children_signatures,
          children_definitions=children_definitions,
      )
  
  
  def generate_struct_file(name, props, description, project_shortname, prefix):
      props = reorder_props(props=props)
      import_string = "# AUTO GENERATED FILE - DO NOT EDIT\n"
      class_string = generate_class_string(
          name, props, description, project_shortname, prefix
      )
  
      file_name = format_fn_name(prefix, name) + ".jl"
  
      # put component files in src/jl subdir,
      # this also creates the Julia source directory for the package
      # if it is missing
      if not os.path.exists("src/jl"):
          os.makedirs("src/jl")
  
      file_path = os.path.join("src", "jl", file_name)
      with open(file_path, "w", encoding="utf-8") as f:
          f.write(import_string)
          f.write(class_string)
  
      print("Generated {}".format(file_name))
  
  
  # pylint: disable=unused-argument
  def generate_module(
      project_shortname, components, metadata, pkg_data, prefix, **kwargs
  ):
      # copy over all JS dependencies from the (Python) components dir
      # the inst/lib directory for the package won't exist on first call
      # create this directory if it is missing
      if os.path.exists("deps"):
          shutil.rmtree("deps")
  
      os.makedirs("deps")
  
      for rel_dirname, _, filenames in os.walk(project_shortname):
          for filename in filenames:
              extension = os.path.splitext(filename)[1]
  
              if extension in [".py", ".pyc", ".json"]:
                  continue
  
              target_dirname = os.path.join(
                  "deps/", os.path.relpath(rel_dirname, project_shortname)
              )
  
              if not os.path.exists(target_dirname):
                  os.makedirs(target_dirname)
  
              shutil.copy(os.path.join(rel_dirname, filename), target_dirname)
  
      generate_package_file(project_shortname, components, pkg_data, prefix)
      generate_toml_file(project_shortname, pkg_data)
  