content: from collections import OrderedDict
  
  import json
  import sys
  import subprocess
  import shlex
  import os
  import argparse
  import shutil
  import functools
  import importlib.resources as importlib_resources
  
  import yaml
  
  from ._r_components_generation import write_class_file
  from ._r_components_generation import generate_exports
  from ._py_components_generation import generate_class_file
  from ._py_components_generation import generate_imports
  from ._py_components_generation import generate_classes_files
  from ._jl_components_generation import generate_struct_file
  from ._jl_components_generation import generate_module
  from ._generate_prop_types import generate_prop_types
  
  reserved_words = [
      "UNDEFINED",
      "REQUIRED",
      "to_plotly_json",
      "available_properties",
      "available_wildcard_properties",
      "_.*",
  ]
  
  
  class _CombinedFormatter(
      argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter
  ):
      pass
  
  
  # pylint: disable=too-many-locals, too-many-arguments, too-many-branches, too-many-statements
  def generate_components(
      components_source,
      project_shortname,
      package_info_filename="package.json",
      ignore="^_",
      rprefix=None,
      rdepends="",
      rimports="",
      rsuggests="",
      jlprefix=None,
      metadata=None,
      keep_prop_order=None,
      max_props=None,
      custom_typing_module=None,
  ):
  
      project_shortname = project_shortname.replace("-", "_").rstrip("/\\")
  
      is_windows = sys.platform == "win32"
  
      # Get path to extract-meta.js using importlib.resources
      try:
          # Python 3.9+
          extract_path = str(
              importlib_resources.files("dash").joinpath("extract-meta.js")
          )
      except AttributeError:
          # Python 3.8 fallback
          with importlib_resources.path("dash", "extract-meta.js") as p:
              extract_path = str(p)
  
      reserved_patterns = "|".join(f"^{p}$" for p in reserved_words)
  
      os.environ["NODE_PATH"] = "node_modules"
  
      shutil.copyfile(
          "package.json", os.path.join(project_shortname, package_info_filename)
      )
  
      if not metadata:
          env = os.environ.copy()
  
          # Ensure local node modules is used when the script is packaged.
          env["MODULES_PATH"] = os.path.abspath("./node_modules")
  
          cmd = shlex.split(
              f'node {extract_path} "{ignore}" "{reserved_patterns}" {components_source}',
              posix=not is_windows,
          )
  
          proc = subprocess.Popen(  # pylint: disable=consider-using-with
              cmd,
              stdout=subprocess.PIPE,
              stderr=subprocess.PIPE,
              shell=is_windows,
              env=env,
          )
          out, err = proc.communicate()
          status = proc.poll()
  
          if err:
              print(err.decode(), file=sys.stderr)
  
          if not out:
              print(
                  f"Error generating metadata in {project_shortname} (status={status})",
                  file=sys.stderr,
              )
              sys.exit(1)
  
          metadata = safe_json_loads(out.decode("utf-8"))
  
      py_generator_kwargs = {
          "custom_typing_module": custom_typing_module,
      }
      if keep_prop_order is not None:
          keep_prop_order = [
              component.strip(" ") for component in keep_prop_order.split(",")
          ]
          py_generator_kwargs["prop_reorder_exceptions"] = keep_prop_order
  
      if max_props:
          py_generator_kwargs["max_props"] = max_props
  
      generator_methods = [functools.partial(generate_class_file, **py_generator_kwargs)]
  
      pkg_data = None
      if rprefix is not None or jlprefix is not None:
          with open("package.json", "r", encoding="utf-8") as f:
              pkg_data = safe_json_loads(f.read())
  
      rpkg_data = None
      if rprefix is not None:
          if not os.path.exists("man"):
              os.makedirs("man")
          if not os.path.exists("R"):
              os.makedirs("R")
          if os.path.isfile("dash-info.yaml"):
              with open("dash-info.yaml", encoding="utf-8") as yamldata:
                  rpkg_data = yaml.safe_load(yamldata)
          generator_methods.append(
              functools.partial(write_class_file, prefix=rprefix, rpkg_data=rpkg_data)
          )
  
      if jlprefix is not None:
          generator_methods.append(
              functools.partial(generate_struct_file, prefix=jlprefix)
          )
  
      components = generate_classes_files(project_shortname, metadata, *generator_methods)
  
      generate_prop_types(
          metadata,
          project_shortname,
          custom_typing_module=custom_typing_module,
      )
  
      with open(
          os.path.join(project_shortname, "metadata.json"), "w", encoding="utf-8"
      ) as f:
          json.dump(metadata, f, separators=(",", ":"))
  
      generate_imports(project_shortname, components)
  
      if rprefix is not None:
          generate_exports(
              project_shortname,
              components,
              metadata,
              pkg_data,
              rpkg_data,
              rprefix,
              rdepends,
              rimports,
              rsuggests,
          )
  
      if jlprefix is not None:
          generate_module(project_shortname, components, metadata, pkg_data, jlprefix)
  
  
  def safe_json_loads(s):
      jsondata_unicode = json.loads(s, object_pairs_hook=OrderedDict)
      if sys.version_info[0] >= 3:
          return jsondata_unicode
      return byteify(jsondata_unicode)
  
  
  def component_build_arg_parser():
      parser = argparse.ArgumentParser(
          prog="dash-generate-components",
          formatter_class=_CombinedFormatter,
          description="Generate dash components by extracting the metadata "
          "using react-docgen. Then map the metadata to Python classes.",
      )
      parser.add_argument("components_source", help="React components source directory.")
      parser.add_argument(
          "project_shortname", help="Name of the project to export the classes files."
      )
      parser.add_argument(
          "-p",
          "--package-info-filename",
          default="package.json",
          help="The filename of the copied `package.json` to `project_shortname`",
      )
      parser.add_argument(
          "-i",
          "--ignore",
          default="^_",
          help="Files/directories matching the pattern will be ignored",
      )
      parser.add_argument(
          "--r-prefix",
          help="Specify a prefix for Dash for R component names, write "
          "components to R dir, create R package.",
      )
      parser.add_argument(
          "--r-depends",
          default="",
          help="Specify a comma-separated list of R packages to be "
          "inserted into the Depends field of the DESCRIPTION file.",
      )
      parser.add_argument(
          "--r-imports",
          default="",
          help="Specify a comma-separated list of R packages to be "
          "inserted into the Imports field of the DESCRIPTION file.",
      )
      parser.add_argument(
          "--r-suggests",
          default="",
          help="Specify a comma-separated list of R packages to be "
          "inserted into the Suggests field of the DESCRIPTION file.",
      )
      parser.add_argument(
          "--jl-prefix",
          help="Specify a prefix for Dash for R component names, write "
          "components to R dir, create R package.",
      )
      parser.add_argument(
          "-k",
          "--keep-prop-order",
          default=None,
          help="Specify a comma-separated list of components which will use the prop "
          "order described in the component proptypes instead of alphabetically reordered "
          "props. Pass the 'ALL' keyword to have every component retain "
          "its original prop order.",
      )
      parser.add_argument(
          "--max-props",
          type=int,
          default=250,
          help="Specify the max number of props to list in the component signature. "
          "More props will still be shown in the docstring, and will still work when "
          "provided as kwargs to the component. Python <3.7 only supports 255 args, "
          "but you may also want to reduce further for improved readability at the "
          "expense of auto-completion for the later props. Use 0 to include all props.",
      )
      parser.add_argument(
          "-t",
          "--custom-typing-module",
          type=str,
          default="dash_prop_typing",
          help=" Module containing custom typing definition for components."
          "Can contains two variables:\n"
          " - custom_imports: dict[ComponentName, list[str]].\n"
          " - custom_props: dict[ComponentName, dict[PropName, function]].\n",
      )
      return parser
  
  
  def cli():
      # Add current path for loading modules.
      sys.path.insert(0, ".")
      args = component_build_arg_parser().parse_args()
      generate_components(
          args.components_source,
          args.project_shortname,
          package_info_filename=args.package_info_filename,
          ignore=args.ignore,
          rprefix=args.r_prefix,
          rdepends=args.r_depends,
          rimports=args.r_imports,
          rsuggests=args.r_suggests,
          jlprefix=args.jl_prefix,
          keep_prop_order=args.keep_prop_order,
          max_props=args.max_props,
          custom_typing_module=args.custom_typing_module,
      )
  
  
  # pylint: disable=undefined-variable
  def byteify(input_object):
      if isinstance(input_object, dict):
          return OrderedDict(
              [(byteify(key), byteify(value)) for key, value in input_object.items()]
          )
      if isinstance(input_object, list):
          return [byteify(element) for element in input_object]
      if isinstance(input_object, str):  # noqa:F821
          return input_object.encode(encoding="utf-8")
      return input_object
  
  
  if __name__ == "__main__":
      cli()
  