# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


import ntpath
import os
import posixpath
import re
import subprocess
import sys

from collections import OrderedDict

import gyp.common
import gyp.easy_xml as easy_xml
import gyp.generator.ninja as ninja_generator
import gyp.MSVSNew as MSVSNew
import gyp.MSVSProject as MSVSProject
import gyp.MSVSSettings as MSVSSettings
import gyp.MSVSToolFile as MSVSToolFile
import gyp.MSVSUserFile as MSVSUserFile
import gyp.MSVSUtil as MSVSUtil
import gyp.MSVSVersion as MSVSVersion
from gyp.common import GypError
from gyp.common import OrderedSet


# Regular expression for validating Visual Studio GUIDs.  If the GUID
# contains lowercase hex letters, MSVS will be fine. However,
# IncrediBuild BuildConsole will parse the solution file, but then
# silently skip building the target causing hard to track down errors.
# Note that this only happens with the BuildConsole, and does not occur
# if IncrediBuild is executed from inside Visual Studio.  This regex
# validates that the string looks like a GUID with all uppercase hex
# letters.
VALID_MSVS_GUID_CHARS = re.compile(r"^[A-F0-9\-]+$")

generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()

generator_default_variables = {
    "DRIVER_PREFIX": "",
    "DRIVER_SUFFIX": ".sys",
    "EXECUTABLE_PREFIX": "",
    "EXECUTABLE_SUFFIX": ".exe",
    "STATIC_LIB_PREFIX": "",
    "SHARED_LIB_PREFIX": "",
    "STATIC_LIB_SUFFIX": ".lib",
    "SHARED_LIB_SUFFIX": ".dll",
    "INTERMEDIATE_DIR": "$(IntDir)",
    "SHARED_INTERMEDIATE_DIR": "$(OutDir)/obj/global_intermediate",
    "OS": "win",
    "PRODUCT_DIR": "$(OutDir)",
    "LIB_DIR": "$(OutDir)lib",
    "RULE_INPUT_ROOT": "$(InputName)",
    "RULE_INPUT_DIRNAME": "$(InputDir)",
    "RULE_INPUT_EXT": "$(InputExt)",
    "RULE_INPUT_NAME": "$(InputFileName)",
    "RULE_INPUT_PATH": "$(InputPath)",
    "CONFIGURATION_NAME": "$(ConfigurationName)",
}


# The msvs specific sections that hold paths
generator_additional_path_sections = [
    "msvs_cygwin_dirs",
    "msvs_props",
]


generator_additional_non_configuration_keys = [
    "msvs_cygwin_dirs",
    "msvs_cygwin_shell",
    "msvs_large_pdb",
    "msvs_shard",
    "msvs_external_builder",
    "msvs_external_builder_out_dir",
    "msvs_external_builder_build_cmd",
    "msvs_external_builder_clean_cmd",
    "msvs_external_builder_clcompile_cmd",
    "msvs_enable_winrt",
    "msvs_requires_importlibrary",
    "msvs_enable_winphone",
    "msvs_application_type_revision",
    "msvs_target_platform_version",
    "msvs_target_platform_minversion",
]

generator_filelist_paths = None

# List of precompiled header related keys.
precomp_keys = [
    "msvs_precompiled_header",
    "msvs_precompiled_source",
]


cached_username = None


cached_domain = None


# TODO(gspencer): Switch the os.environ calls to be
# win32api.GetDomainName() and win32api.GetUserName() once the
# python version in depot_tools has been updated to work on Vista
# 64-bit.
def _GetDomainAndUserName():
    if sys.platform not in ("win32", "cygwin"):
        return ("DOMAIN", "USERNAME")
    global cached_username
    global cached_domain
    if not cached_domain or not cached_username:
        domain = os.environ.get("USERDOMAIN")
        username = os.environ.get("USERNAME")
        if not domain or not username:
            call = subprocess.Popen(
                ["net", "config", "Workstation"], stdout=subprocess.PIPE
            )
            config = call.communicate()[0].decode("utf-8")
            username_re = re.compile(r"^User name\s+(\S+)", re.MULTILINE)
            username_match = username_re.search(config)
            if username_match:
                username = username_match.group(1)
            domain_re = re.compile(r"^Logon domain\s+(\S+)", re.MULTILINE)
            domain_match = domain_re.search(config)
            if domain_match:
                domain = domain_match.group(1)
        cached_domain = domain
        cached_username = username
    return (cached_domain, cached_username)


fixpath_prefix = None


def _NormalizedSource(source):
    """Normalize the path.

  But not if that gets rid of a variable, as this may expand to something
  larger than one directory.

  Arguments:
      source: The path to be normalize.d

  Returns:
      The normalized path.
  """
    normalized = os.path.normpath(source)
    if source.count("$") == normalized.count("$"):
        source = normalized
    return source


def _FixPath(path, separator="\\"):
    """Convert paths to a form that will make sense in a vcproj file.

  Arguments:
    path: The path to convert, may contain / etc.
  Returns:
    The path with all slashes made into backslashes.
  """
    if (
        fixpath_prefix
        and path
        and not os.path.isabs(path)
        and path[0] != "$"
        and not _IsWindowsAbsPath(path)
    ):
        path = os.path.join(fixpath_prefix, path)
    if separator == "\\":
        path = path.replace("/", "\\")
    path = _NormalizedSource(path)
    if separator == "/":
        path = path.replace("\\", "/")
    if path and path[-1] == separator:
        path = path[:-1]
    return path


def _IsWindowsAbsPath(path):
    """
  On Cygwin systems Python needs a little help determining if a path
  is an absolute Windows path or not, so that
  it does not treat those as relative, which results in bad paths like:
  '..\\C:\\<some path>\\some_source_code_file.cc'
  """
    return path.startswith("c:") or path.startswith("C:")


def _FixPaths(paths, separator="\\"):
    """Fix each of the paths of the list."""
    return [_FixPath(i, separator) for i in paths]


def _ConvertSourcesToFilterHierarchy(
    sources, prefix=None, excluded=None, list_excluded=True, msvs_version=None
):
    """Converts a list split source file paths into a vcproj folder hierarchy.

  Arguments:
    sources: A list of source file paths split.
    prefix: A list of source file path layers meant to apply to each of sources.
    excluded: A set of excluded files.
    msvs_version: A MSVSVersion object.

  Returns:
    A hierarchy of filenames and MSVSProject.Filter objects that matches the
    layout of the source tree.
    For example:
    _ConvertSourcesToFilterHierarchy([['a', 'bob1.c'], ['b', 'bob2.c']],
                                     prefix=['joe'])
    -->
    [MSVSProject.Filter('a', contents=['joe\\a\\bob1.c']),
     MSVSProject.Filter('b', contents=['joe\\b\\bob2.c'])]
  """
    if not prefix:
        prefix = []
    result = []
    excluded_result = []
    folders = OrderedDict()
    # Gather files into the final result, excluded, or folders.
    for s in sources:
        if len(s) == 1:
            filename = _NormalizedSource("\\".join(prefix + s))
            if filename in excluded:
                excluded_result.append(filename)
            else:
                result.append(filename)
        elif msvs_version and not msvs_version.UsesVcxproj():
            # For MSVS 2008 and earlier, we need to process all files before walking
            # the sub folders.
            if not folders.get(s[0]):
                folders[s[0]] = []
            folders[s[0]].append(s[1:])
        else:
            contents = _ConvertSourcesToFilterHierarchy(
                [s[1:]],
                prefix + [s[0]],
                excluded=excluded,
                list_excluded=list_excluded,
                msvs_version=msvs_version,
            )
            contents = MSVSProject.Filter(s[0], contents=contents)
            result.append(contents)
    # Add a folder for excluded files.
    if excluded_result and list_excluded:
        excluded_folder = MSVSProject.Filter(
            "_excluded_files", contents=excluded_result
        )
        result.append(excluded_folder)

    if msvs_version and msvs_version.UsesVcxproj():
        return result

    # Populate all the folders.
    for f in folders:
        contents = _ConvertSourcesToFilterHierarchy(
            folders[f],
            prefix=prefix + [f],
            excluded=excluded,
            list_excluded=list_excluded,
            msvs_version=msvs_version,
        )
        contents = MSVSProject.Filter(f, contents=contents)
        result.append(contents)
    return result


def _ToolAppend(tools, tool_name, setting, value, only_if_unset=False):
    if not value:
        return
    _ToolSetOrAppend(tools, tool_name, setting, value, only_if_unset)


def _ToolSetOrAppend(tools, tool_name, setting, value, only_if_unset=False):
    # TODO(bradnelson): ugly hack, fix this more generally!!!
    if "Directories" in setting or "Dependencies" in setting:
        if type(value) == str:
            value = value.replace("/", "\\")
        else:
            value = [i.replace("/", "\\") for i in value]
    if not tools.get(tool_name):
        tools[tool_name] = {}
    tool = tools[tool_name]
    if setting == "CompileAsWinRT":
        return
    if tool.get(setting):
        if only_if_unset:
            return
        if type(tool[setting]) == list and type(value) == list:
            tool[setting] += value
        else:
            raise TypeError(
                'Appending "%s" to a non-list setting "%s" for tool "%s" is '
                "not allowed, previous value: %s"
                % (value, setting, tool_name, str(tool[setting]))
            )
    else:
        tool[setting] = value


def _ConfigTargetVersion(config_data):
    return config_data.get("msvs_target_version", "Windows7")


def _ConfigPlatform(config_data):
    return config_data.get("msvs_configuration_platform", "Win32")


def _ConfigBaseName(config_name, platform_name):
    if config_name.endswith("_" + platform_name):
        return config_name[0 : -len(platform_name) - 1]
    else:
        return config_name


def _ConfigFullName(config_name, config_data):
    platform_name = _ConfigPlatform(config_data)
    return f"{_ConfigBaseName(config_name, platform_name)}|{platform_name}"


def _ConfigWindowsTargetPlatformVersion(config_data, version):
    target_ver = config_data.get("msvs_windows_target_platform_version")
    if target_ver and re.match(r"^\d+", target_ver):
        return target_ver
    config_ver = config_data.get("msvs_windows_sdk_version")
    vers = [config_ver] if config_ver else version.compatible_sdks
    for ver in vers:
        for key in [
            r"HKLM\Software\Microsoft\Microsoft SDKs\Windows\%s",
            r"HKLM\Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows\%s",
        ]:
            sdk_dir = MSVSVersion._RegistryGetValue(key % ver, "InstallationFolder")
            if not sdk_dir:
                continue
            version = MSVSVersion._RegistryGetValue(key % ver, "ProductVersion") or ""
            # Find a matching entry in sdk_dir\include.
            expected_sdk_dir = r"%s\include" % sdk_dir
            names = sorted(
                (
                    x
                    for x in (
                        os.listdir(expected_sdk_dir)
                        if os.path.isdir(expected_sdk_dir)
                        else []
                    )
                    if x.startswith(version)
                ),
                reverse=True,
            )
            if names:
                return names[0]
            else:
                print(
                    "Warning: No include files found for detected "
                    "Windows SDK version %s" % (version),
                    file=sys.stdout,
                )


def _BuildCommandLineForRuleRaw(
    spec, cmd, cygwin_shell, has_input_path, quote_cmd, do_setup_env
):

    if [x for x in cmd if "$(InputDir)" in x]:
        input_dir_preamble = (
            "set INPUTDIR=$(InputDir)\n"
            "if NOT DEFINED INPUTDIR set INPUTDIR=.\\\n"
            "set INPUTDIR=%INPUTDIR:~0,-1%\n"
        )
    else:
        input_dir_preamble = ""

    if cygwin_shell:
        # Find path to cygwin.
        cygwin_dir = _FixPath(spec.get("msvs_cygwin_dirs", ["."])[0])
        # Prepare command.
        direct_cmd = cmd
        direct_cmd = [
            i.replace("$(IntDir)", '`cygpath -m "${INTDIR}"`') for i in direct_cmd
        ]
        direct_cmd = [
            i.replace("$(OutDir)", '`cygpath -m "${OUTDIR}"`') for i in direct_cmd
        ]
        direct_cmd = [
            i.replace("$(InputDir)", '`cygpath -m "${INPUTDIR}"`') for i in direct_cmd
        ]
        if has_input_path:
            direct_cmd = [
                i.replace("$(InputPath)", '`cygpath -m "${INPUTPATH}"`')
                for i in direct_cmd
            ]
        direct_cmd = ['\\"%s\\"' % i.replace('"', '\\\\\\"') for i in direct_cmd]
        # direct_cmd = gyp.common.EncodePOSIXShellList(direct_cmd)
        direct_cmd = " ".join(direct_cmd)
        # TODO(quote):  regularize quoting path names throughout the module
        cmd = ""
        if do_setup_env:
            cmd += 'call "$(ProjectDir)%(cygwin_dir)s\\setup_env.bat" && '
        cmd += "set CYGWIN=nontsec&& "
        if direct_cmd.find("NUMBER_OF_PROCESSORS") >= 0:
            cmd += "set /a NUMBER_OF_PROCESSORS_PLUS_1=%%NUMBER_OF_PROCESSORS%%+1&& "
        if direct_cmd.find("INTDIR") >= 0:
            cmd += "set INTDIR=$(IntDir)&& "
        if direct_cmd.find("OUTDIR") >= 0:
            cmd += "set OUTDIR=$(OutDir)&& "
        if has_input_path and direct_cmd.find("INPUTPATH") >= 0:
            cmd += "set INPUTPATH=$(InputPath) && "
        cmd += 'bash -c "%(cmd)s"'
        cmd = cmd % {"cygwin_dir": cygwin_dir, "cmd": direct_cmd}
        return input_dir_preamble + cmd
    else:
        # Convert cat --> type to mimic unix.
        command = ["type"] if cmd[0] == "cat" else [cmd[0].replace("/", "\\")]
        # Add call before command to ensure that commands can be tied together one
        # after the other without aborting in Incredibuild, since IB makes a bat
        # file out of the raw command string, and some commands (like python) are
        # actually batch files themselves.
        command.insert(0, "call")
        # Fix the paths
        # TODO(quote): This is a really ugly heuristic, and will miss path fixing
        #              for arguments like "--arg=path", arg=path, or "/opt:path".
        # If the argument starts with a slash or dash, or contains an equal sign,
        # it's probably a command line switch.
        # Return the path with forward slashes because the command using it might
        # not support backslashes.
        arguments = [
            i if (i[:1] in "/-" or "=" in i) else _FixPath(i, "/")
            for i in cmd[1:]
        ]
        arguments = [i.replace("$(InputDir)", "%INPUTDIR%") for i in arguments]
        arguments = [MSVSSettings.FixVCMacroSlashes(i) for i in arguments]
        if quote_cmd:
            # Support a mode for using cmd directly.
            # Convert any paths to native form (first element is used directly).
            # TODO(quote):  regularize quoting path names throughout the module
            command[1] = '"%s"' % command[1]
            arguments = ['"%s"' % i for i in arguments]
        # Collapse into a single command.
        return input_dir_preamble + " ".join(command + arguments)


def _BuildCommandLineForRule(spec, rule, has_input_path, do_setup_env):
    # Currently this weird argument munging is used to duplicate the way a
    # python script would need to be run as part of the chrome tree.
    # Eventually we should add some sort of rule_default option to set this
    # per project. For now the behavior chrome needs is the default.
    mcs = rule.get("msvs_cygwin_shell")
    if mcs is None:
        mcs = int(spec.get("msvs_cygwin_shell", 1))
    elif isinstance(mcs, str):
        mcs = int(mcs)
    quote_cmd = int(rule.get("msvs_quote_cmd", 1))
    return _BuildCommandLineForRuleRaw(
        spec, rule["action"], mcs, has_input_path, quote_cmd, do_setup_env=do_setup_env
    )


def _AddActionStep(actions_dict, inputs, outputs, description, command):
    """Merge action into an existing list of actions.

  Care must be taken so that actions which have overlapping inputs either don't
  get assigned to the same input, or get collapsed into one.

  Arguments:
    actions_dict: dictionary keyed on input name, which maps to a list of
      dicts describing the actions attached to that input file.
    inputs: list of inputs
    outputs: list of outputs
    description: description of the action
    command: command line to execute
  """
    # Require there to be at least one input (call sites will ensure this).
    assert inputs

    action = {
        "inputs": inputs,
        "outputs": outputs,
        "description": description,
        "command": command,
    }

    # Pick where to stick this action.
    # While less than optimal in terms of build time, attach them to the first
    # input for now.
    chosen_input = inputs[0]

    # Add it there.
    if chosen_input not in actions_dict:
        actions_dict[chosen_input] = []
    actions_dict[chosen_input].append(action)


def _AddCustomBuildToolForMSVS(
    p, spec, primary_input, inputs, outputs, description, cmd
):
    """Add a custom build tool to execute something.

  Arguments:
    p: the target project
    spec: the target project dict
    primary_input: input file to attach the build tool to
    inputs: list of inputs
    outputs: list of outputs
    description: description of the action
    cmd: command line to execute
  """
    inputs = _FixPaths(inputs)
    outputs = _FixPaths(outputs)
    tool = MSVSProject.Tool(
        "VCCustomBuildTool",
        {
            "Description": description,
            "AdditionalDependencies": ";".join(inputs),
            "Outputs": ";".join(outputs),
            "CommandLine": cmd,
        },
    )
    # Add to the properties of primary input for each config.
    for config_name, c_data in spec["configurations"].items():
        p.AddFileConfig(
            _FixPath(primary_input), _ConfigFullName(config_name, c_data), tools=[tool]
        )


def _AddAccumulatedActionsToMSVS(p, spec, actions_dict):
    """Add actions accumulated into an actions_dict, merging as needed.

  Arguments:
    p: the target project
    spec: the target project dict
    actions_dict: dictionary keyed on input name, which maps to a list of
        dicts describing the actions attached to that input file.
  """
    for primary_input in actions_dict:
        inputs = OrderedSet()
        outputs = OrderedSet()
        descriptions = []
        commands = []
        for action in actions_dict[primary_input]:
            inputs.update(OrderedSet(action["inputs"]))
            outputs.update(OrderedSet(action["outputs"]))
            descriptions.append(action["description"])
            commands.append(action["command"])
        # Add the custom build step for one input file.
        description = ", and also ".join(descriptions)
        command = "\r\n".join(commands)
        _AddCustomBuildToolForMSVS(
            p,
            spec,
            primary_input=primary_input,
            inputs=inputs,
            outputs=outputs,
            description=description,
            cmd=command,
        )


def _RuleExpandPath(path, input_file):
    """Given the input file to which a rule applied, string substitute a path.

  Arguments:
    path: a path to string expand
    input_file: the file to which the rule applied.
  Returns:
    The string substituted path.
  """
    path = path.replace(
        "$(InputName)", os.path.splitext(os.path.split(input_file)[1])[0]
    )
    path = path.replace("$(InputDir)", os.path.dirname(input_file))
    path = path.replace(
        "$(InputExt)", os.path.splitext(os.path.split(input_file)[1])[1]
    )
    path = path.replace("$(InputFileName)", os.path.split(input_file)[1])
    path = path.replace("$(InputPath)", input_file)
    return path


def _FindRuleTriggerFiles(rule, sources):
    """Find the list of files which a particular rule applies to.

  Arguments:
    rule: the rule in question
    sources: the set of all known source files for this project
  Returns:
    The list of sources that trigger a particular rule.
  """
    return rule.get("rule_sources", [])


def _RuleInputsAndOutputs(rule, trigger_file):
    """Find the inputs and outputs generated by a rule.

  Arguments:
    rule: the rule in question.
    trigger_file: the main trigger for this rule.
  Returns:
    The pair of (inputs, outputs) involved in this rule.
  """
    raw_inputs = _FixPaths(rule.get("inputs", []))
    raw_outputs = _FixPaths(rule.get("outputs", []))
    inputs = OrderedSet()
    outputs = OrderedSet()
    inputs.add(trigger_file)
    for i in raw_inputs:
        inputs.add(_RuleExpandPath(i, trigger_file))
    for o in raw_outputs:
        outputs.add(_RuleExpandPath(o, trigger_file))
    return (inputs, outputs)


def _GenerateNativeRulesForMSVS(p, rules, output_dir, spec, options):
    """Generate a native rules file.

  Arguments:
    p: the target project
    rules: the set of rules to include
    output_dir: the directory in which the project/gyp resides
    spec: the project dict
    options: global generator options
  """
    rules_filename = "{}{}.rules".format(spec["target_name"], options.suffix)
    rules_file = MSVSToolFile.Writer(
        os.path.join(output_dir, rules_filename), spec["target_name"]
    )
    # Add each rule.
    for r in rules:
        rule_name = r["rule_name"]
        rule_ext = r["extension"]
        inputs = _FixPaths(r.get("inputs", []))
        outputs = _FixPaths(r.get("outputs", []))
        # Skip a rule with no action and no inputs.
        if "action" not in r and not r.get("rule_sources", []):
            continue
        cmd = _BuildCommandLineForRule(spec, r, has_input_path=True, do_setup_env=True)
        rules_file.AddCustomBuildRule(
            name=rule_name,
            description=r.get("message", rule_name),
            extensions=[rule_ext],
            additional_dependencies=inputs,
            outputs=outputs,
            cmd=cmd,
        )
    # Write out rules file.
    rules_file.WriteIfChanged()

    # Add rules file to project.
    p.AddToolFile(rules_filename)


def _Cygwinify(path):
    path = path.replace("$(OutDir)", "$(OutDirCygwin)")
    path = path.replace("$(IntDir)", "$(IntDirCygwin)")
    return path


def _GenerateExternalRules(rules, output_dir, spec, sources, options, actions_to_add):
    """Generate an external makefile to do a set of rules.

  Arguments:
    rules: the list of rules to include
    output_dir: path containing project and gyp files
    spec: project specification data
    sources: set of sources known
    options: global generator options
    actions_to_add: The list of actions we will add to.
  """
    filename = "{}_rules{}.mk".format(spec["target_name"], options.suffix)
    mk_file = gyp.common.WriteOnDiff(os.path.join(output_dir, filename))
    # Find cygwin style versions of some paths.
    mk_file.write('OutDirCygwin:=$(shell cygpath -u "$(OutDir)")\n')
    mk_file.write('IntDirCygwin:=$(shell cygpath -u "$(IntDir)")\n')
    # Gather stuff needed to emit all: target.
    all_inputs = OrderedSet()
    all_outputs = OrderedSet()
    all_output_dirs = OrderedSet()
    first_outputs = []
    for rule in rules:
        trigger_files = _FindRuleTriggerFiles(rule, sources)
        for tf in trigger_files:
            inputs, outputs = _RuleInputsAndOutputs(rule, tf)
            all_inputs.update(OrderedSet(inputs))
            all_outputs.update(OrderedSet(outputs))
            # Only use one target from each rule as the dependency for
            # 'all' so we don't try to build each rule multiple times.
            first_outputs.append(next(iter(outputs)))
            # Get the unique output directories for this rule.
            output_dirs = [os.path.split(i)[0] for i in outputs]
            for od in output_dirs:
                all_output_dirs.add(od)
    first_outputs_cyg = [_Cygwinify(i) for i in first_outputs]
    # Write out all: target, including mkdir for each output directory.
    mk_file.write("all: %s\n" % " ".join(first_outputs_cyg))
    for od in all_output_dirs:
        if od:
            mk_file.write('\tmkdir -p `cygpath -u "%s"`\n' % od)
    mk_file.write("\n")
    # Define how each output is generated.
    for rule in rules:
        trigger_files = _FindRuleTriggerFiles(rule, sources)
        for tf in trigger_files:
            # Get all the inputs and outputs for this rule for this trigger file.
            inputs, outputs = _RuleInputsAndOutputs(rule, tf)
            inputs = [_Cygwinify(i) for i in inputs]
            outputs = [_Cygwinify(i) for i in outputs]
            # Prepare the command line for this rule.
            cmd = [_RuleExpandPath(c, tf) for c in rule["action"]]
            cmd = ['"%s"' % i for i in cmd]
            cmd = " ".join(cmd)
            # Add it to the makefile.
            mk_file.write("{}: {}\n".format(" ".join(outputs), " ".join(inputs)))
            mk_file.write("\t%s\n\n" % cmd)
    # Close up the file.
    mk_file.close()

    # Add makefile to list of sources.
    sources.add(filename)
    # Add a build action to call makefile.
    cmd = [
        "make",
        "OutDir=$(OutDir)",
        "IntDir=$(IntDir)",
        "-j",
        "${NUMBER_OF_PROCESSORS_PLUS_1}",
        "-f",
        filename,
    ]
    cmd = _BuildCommandLineForRuleRaw(spec, cmd, True, False, True, True)
    # Insert makefile as 0'th input, so it gets the action attached there,
    # as this is easier to understand from in the IDE.
    all_inputs = list(all_inputs)
    all_inputs.insert(0, filename)
    _AddActionStep(
        actions_to_add,
        inputs=_FixPaths(all_inputs),
        outputs=_FixPaths(all_outputs),
        description="Running external rules for %s" % spec["target_name"],
        command=cmd,
    )


def _EscapeEnvironmentVariableExpansion(s):
    """Escapes % characters.

  Escapes any % characters so that Windows-style environment variable
  expansions will leave them alone.
  See http://connect.microsoft.com/VisualStudio/feedback/details/106127/cl-d-name-text-containing-percentage-characters-doesnt-compile
  to understand why we have to do this.

  Args:
      s: The string to be escaped.

  Returns:
      The escaped string.
  """
    s = s.replace("%", "%%")
    return s


quote_replacer_regex = re.compile(r'(\\*)"')


def _EscapeCommandLineArgumentForMSVS(s):
    """Escapes a Windows command-line argument.

  So that the Win32 CommandLineToArgv function will turn the escaped result back
  into the original string.
  See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
  ("Parsing C++ Command-Line Arguments") to understand why we have to do
  this.

  Args:
      s: the string to be escaped.
  Returns:
      the escaped string.
  """

    def _Replace(match):
        # For a literal quote, CommandLineToArgv requires an odd number of
        # backslashes preceding it, and it produces half as many literal backslashes
        # (rounded down). So we need to produce 2n+1 backslashes.
        return 2 * match.group(1) + '\\"'

    # Escape all quotes so that they are interpreted literally.
    s = quote_replacer_regex.sub(_Replace, s)
    # Now add unescaped quotes so that any whitespace is interpreted literally.
    s = '"' + s + '"'
    return s


delimiters_replacer_regex = re.compile(r"(\\*)([,;]+)")


def _EscapeVCProjCommandLineArgListItem(s):
    """Escapes command line arguments for MSVS.

  The VCProj format stores string lists in a single string using commas and
  semi-colons as separators, which must be quoted if they are to be
  interpreted literally. However, command-line arguments may already have
  quotes, and the VCProj parser is ignorant of the backslash escaping
  convention used by CommandLineToArgv, so the command-line quotes and the
  VCProj quotes may not be the same quotes. So to store a general
  command-line argument in a VCProj list, we need to parse the existing
  quoting according to VCProj's convention and quote any delimiters that are
  not already quoted by that convention. The quotes that we add will also be
  seen by CommandLineToArgv, so if backslashes precede them then we also have
  to escape those backslashes according to the CommandLineToArgv
  convention.

  Args:
      s: the string to be escaped.
  Returns:
      the escaped string.
  """

    def _Replace(match):
        # For a non-literal quote, CommandLineToArgv requires an even number of
        # backslashes preceding it, and it produces half as many literal
        # backslashes. So we need to produce 2n backslashes.
        return 2 * match.group(1) + '"' + match.group(2) + '"'

    segments = s.split('"')
    # The unquoted segments are at the even-numbered indices.
    for i in range(0, len(segments), 2):
        segments[i] = delimiters_replacer_regex.sub(_Replace, segments[i])
    # Concatenate back into a single string
    s = '"'.join(segments)
    if len(segments) % 2 == 0:
        # String ends while still quoted according to VCProj's convention. This
        # means the delimiter and the next list item that follow this one in the
        # .vcproj file will be misinterpreted as part of this item. There is nothing
        # we can do about this. Adding an extra quote would correct the problem in
        # the VCProj but cause the same problem on the final command-line. Moving
        # the item to the end of the list does works, but that's only possible if
        # there's only one such item. Let's just warn the user.
        print(
            "Warning: MSVS may misinterpret the odd number of " + "quotes in " + s,
            file=sys.stderr,
        )
    return s


def _EscapeCppDefineForMSVS(s):
    """Escapes a CPP define so that it will reach the compiler unaltered."""
    s = _EscapeEnvironmentVariableExpansion(s)
    s = _EscapeCommandLineArgumentForMSVS(s)
    s = _EscapeVCProjCommandLineArgListItem(s)
    # cl.exe replaces literal # characters with = in preprocessor definitions for
    # some reason. Octal-encode to work around that.
    s = s.replace("#", "\\%03o" % ord("#"))
    return s


quote_replacer_regex2 = re.compile(r'(\\+)"')


def _EscapeCommandLineArgumentForMSBuild(s):
    """Escapes a Windows command-line argument for use by MSBuild."""

    def _Replace(match):
        return (len(match.group(1)) / 2 * 4) * "\\" + '\\"'

    # Escape all quotes so that they are interpreted literally.
    s = quote_replacer_regex2.sub(_Replace, s)
    return s


def _EscapeMSBuildSpecialCharacters(s):
    escape_dictionary = {
        "%": "%25",
        "$": "%24",
        "@": "%40",
        "'": "%27",
        ";": "%3B",
        "?": "%3F",
        "*": "%2A",
    }
    result = "".join([escape_dictionary.get(c, c) for c in s])
    return result


def _EscapeCppDefineForMSBuild(s):
    """Escapes a CPP define so that it will reach the compiler unaltered."""
    s = _EscapeEnvironmentVariableExpansion(s)
    s = _EscapeCommandLineArgumentForMSBuild(s)
    s = _EscapeMSBuildSpecialCharacters(s)
    # cl.exe replaces literal # characters with = in preprocessor definitions for
    # some reason. Octal-encode to work around that.
    s = s.replace("#", "\\%03o" % ord("#"))
    return s


def _GenerateRulesForMSVS(
    p, output_dir, options, spec, sources, excluded_sources, actions_to_add
):
    """Generate all the rules for a particular project.

  Arguments:
    p: the project
    output_dir: directory to emit rules to
    options: global options passed to the generator
    spec: the specification for this project
    sources: the set of all known source files in this project
    excluded_sources: the set of sources excluded from normal processing
    actions_to_add: deferred list of actions to add in
  """
    rules = spec.get("rules", [])
    rules_native = [r for r in rules if not int(r.get("msvs_external_rule", 0))]
    rules_external = [r for r in rules if int(r.get("msvs_external_rule", 0))]

    # Handle rules that use a native rules file.
    if rules_native:
        _GenerateNativeRulesForMSVS(p, rules_native, output_dir, spec, options)

    # Handle external rules (non-native rules).
    if rules_external:
        _GenerateExternalRules(
            rules_external, output_dir, spec, sources, options, actions_to_add
        )
    _AdjustSourcesForRules(rules, sources, excluded_sources, False)


def _AdjustSourcesForRules(rules, sources, excluded_sources, is_msbuild):
    # Add outputs generated by each rule (if applicable).
    for rule in rules:
        # Add in the outputs from this rule.
        trigger_files = _FindRuleTriggerFiles(rule, sources)
        for trigger_file in trigger_files:
            # Remove trigger_file from excluded_sources to let the rule be triggered
            # (e.g. rule trigger ax_enums.idl is added to excluded_sources
            # because it's also in an action's inputs in the same project)
            excluded_sources.discard(_FixPath(trigger_file))
            # Done if not processing outputs as sources.
            if int(rule.get("process_outputs_as_sources", False)):
                inputs, outputs = _RuleInputsAndOutputs(rule, trigger_file)
                inputs = OrderedSet(_FixPaths(inputs))
                outputs = OrderedSet(_FixPaths(outputs))
                inputs.remove(_FixPath(trigger_file))
                sources.update(inputs)
                if not is_msbuild:
                    excluded_sources.update(inputs)
                sources.update(outputs)


def _FilterActionsFromExcluded(excluded_sources, actions_to_add):
    """Take inputs with actions attached out of the list of exclusions.

  Arguments:
    excluded_sources: list of source files not to be built.
    actions_to_add: dict of actions keyed on source file they're attached to.
  Returns:
    excluded_sources with files that have actions attached removed.
  """
    must_keep = OrderedSet(_FixPaths(actions_to_add.keys()))
    return [s for s in excluded_sources if s not in must_keep]


def _GetDefaultConfiguration(spec):
    return spec["configurations"][spec["default_configuration"]]


def _GetGuidOfProject(proj_path, spec):
    """Get the guid for the project.

  Arguments:
    proj_path: Path of the vcproj or vcxproj file to generate.
    spec: The target dictionary containing the properties of the target.
  Returns:
    the guid.
  Raises:
    ValueError: if the specified GUID is invalid.
  """
    # Pluck out the default configuration.
    default_config = _GetDefaultConfiguration(spec)
    # Decide the guid of the project.
    guid = default_config.get("msvs_guid")
    if guid:
        if VALID_MSVS_GUID_CHARS.match(guid) is None:
            raise ValueError(
                'Invalid MSVS guid: "%s".  Must match regex: "%s".'
                % (guid, VALID_MSVS_GUID_CHARS.pattern)
            )
        guid = "{%s}" % guid
    guid = guid or MSVSNew.MakeGuid(proj_path)
    return guid


def _GetMsbuildToolsetOfProject(proj_path, spec, version):
    """Get the platform toolset for the project.

  Arguments:
    proj_path: Path of the vcproj or vcxproj file to generate.
    spec: The target dictionary containing the properties of the target.
    version: The MSVSVersion object.
  Returns:
    the platform toolset string or None.
  """
    # Pluck out the default configuration.
    default_config = _GetDefaultConfiguration(spec)
    toolset = default_config.get("msbuild_toolset")
    if not toolset and version.DefaultToolset():
        toolset = version.DefaultToolset()
    if spec["type"] == "windows_driver":
        toolset = "WindowsKernelModeDriver10.0"
    return toolset


def _GenerateProject(project, options, version, generator_flags, spec):
    """Generates a vcproj file.

  Arguments:
    project: the MSVSProject object.
    options: global generator options.
    version: the MSVSVersion object.
    generator_flags: dict of generator-specific flags.
  Returns:
    A list of source files that cannot be found on disk.
  """
    default_config = _GetDefaultConfiguration(project.spec)

    # Skip emitting anything if told to with msvs_existing_vcproj option.
    if default_config.get("msvs_existing_vcproj"):
        return []

    if version.UsesVcxproj():
        return _GenerateMSBuildProject(project, options, version, generator_flags, spec)
    else:
        return _GenerateMSVSProject(project, options, version, generator_flags)


def _GenerateMSVSProject(project, options, version, generator_flags):
    """Generates a .vcproj file.  It may create .rules and .user files too.

  Arguments:
    project: The project object we will generate the file for.
    options: Global options passed to the generator.
    version: The VisualStudioVersion object.
    generator_flags: dict of generator-specific flags.
  """
    spec = project.spec
    gyp.common.EnsureDirExists(project.path)

    platforms = _GetUniquePlatforms(spec)
    p = MSVSProject.Writer(
        project.path, version, spec["target_name"], project.guid, platforms
    )

    # Get directory project file is in.
    project_dir = os.path.split(project.path)[0]
    gyp_path = _NormalizedSource(project.build_file)
    relative_path_of_gyp_file = gyp.common.RelativePath(gyp_path, project_dir)

    config_type = _GetMSVSConfigurationType(spec, project.build_file)
    for config_name, config in spec["configurations"].items():
        _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config)

    # Prepare list of sources and excluded sources.
    gyp_file = os.path.split(project.build_file)[1]
    sources, excluded_sources = _PrepareListOfSources(spec, generator_flags, gyp_file)

    # Add rules.
    actions_to_add = {}
    _GenerateRulesForMSVS(
        p, project_dir, options, spec, sources, excluded_sources, actions_to_add
    )
    list_excluded = generator_flags.get("msvs_list_excluded_files", True)
    sources, excluded_sources, excluded_idl = _AdjustSourcesAndConvertToFilterHierarchy(
        spec, options, project_dir, sources, excluded_sources, list_excluded, version
    )

    # Add in files.
    missing_sources = _VerifySourcesExist(sources, project_dir)
    p.AddFiles(sources)

    _AddToolFilesToMSVS(p, spec)
    _HandlePreCompiledHeaders(p, sources, spec)
    _AddActions(actions_to_add, spec, relative_path_of_gyp_file)
    _AddCopies(actions_to_add, spec)
    _WriteMSVSUserFile(project.path, version, spec)

    # NOTE: this stanza must appear after all actions have been decided.
    # Don't excluded sources with actions attached, or they won't run.
    excluded_sources = _FilterActionsFromExcluded(excluded_sources, actions_to_add)
    _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl, list_excluded)
    _AddAccumulatedActionsToMSVS(p, spec, actions_to_add)

    # Write it out.
    p.WriteIfChanged()

    return missing_sources


def _GetUniquePlatforms(spec):
    """Returns the list of unique platforms for this spec, e.g ['win32', ...].

  Arguments:
    spec: The target dictionary containing the properties of the target.
  Returns:
    The MSVSUserFile object created.
  """
    # Gather list of unique platforms.
    platforms = OrderedSet()
    for configuration in spec["configurations"]:
        platforms.add(_ConfigPlatform(spec["configurations"][configuration]))
    platforms = list(platforms)
    return platforms


def _CreateMSVSUserFile(proj_path, version, spec):
    """Generates a .user file for the user running this Gyp program.

  Arguments:
    proj_path: The path of the project file being created.  The .user file
               shares the same path (with an appropriate suffix).
    version: The VisualStudioVersion object.
    spec: The target dictionary containing the properties of the target.
  Returns:
    The MSVSUserFile object created.
  """
    (domain, username) = _GetDomainAndUserName()
    vcuser_filename = ".".join([proj_path, domain, username, "user"])
    user_file = MSVSUserFile.Writer(vcuser_filename, version, spec["target_name"])
    return user_file


def _GetMSVSConfigurationType(spec, build_file):
    """Returns the configuration type for this project.

  It's a number defined by Microsoft.  May raise an exception.

  Args:
      spec: The target dictionary containing the properties of the target.
      build_file: The path of the gyp file.
  Returns:
      An integer, the configuration type.
  """
    try:
        config_type = {
            "executable": "1",  # .exe
            "shared_library": "2",  # .dll
            "loadable_module": "2",  # .dll
            "static_library": "4",  # .lib
            "windows_driver": "5",  # .sys
            "none": "10",  # Utility type
        }[spec["type"]]
    except KeyError:
        if spec.get("type"):
            raise GypError(
                "Target type %s is not a valid target type for "
                "target %s in %s." % (spec["type"], spec["target_name"], build_file)
            )
        else:
            raise GypError(
                "Missing type field for target %s in %s."
                % (spec["target_name"], build_file)
            )
    return config_type


def _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config):
    """Adds a configuration to the MSVS project.

  Many settings in a vcproj file are specific to a configuration.  This
  function the main part of the vcproj file that's configuration specific.

  Arguments:
    p: The target project being generated.
    spec: The target dictionary containing the properties of the target.
    config_type: The configuration type, a number as defined by Microsoft.
    config_name: The name of the configuration.
    config: The dictionary that defines the special processing to be done
            for this configuration.
  """
    # Get the information for this configuration
    include_dirs, midl_include_dirs, resource_include_dirs = _GetIncludeDirs(config)
    libraries = _GetLibraries(spec)
    library_dirs = _GetLibraryDirs(config)
    out_file, vc_tool, _ = _GetOutputFilePathAndTool(spec, msbuild=False)
    defines = _GetDefines(config)
    defines = [_EscapeCppDefineForMSVS(d) for d in defines]
    disabled_warnings = _GetDisabledWarnings(config)
    prebuild = config.get("msvs_prebuild")
    postbuild = config.get("msvs_postbuild")
    def_file = _GetModuleDefinition(spec)
    precompiled_header = config.get("msvs_precompiled_header")

    # Prepare the list of tools as a dictionary.
    tools = {}
    # Add in user specified msvs_settings.
    msvs_settings = config.get("msvs_settings", {})
    MSVSSettings.ValidateMSVSSettings(msvs_settings)

    # Prevent default library inheritance from the environment.
    _ToolAppend(tools, "VCLinkerTool", "AdditionalDependencies", ["$(NOINHERIT)"])

    for tool in msvs_settings:
        settings = config["msvs_settings"][tool]
        for setting in settings:
            _ToolAppend(tools, tool, setting, settings[setting])
    # Add the information to the appropriate tool
    _ToolAppend(tools, "VCCLCompilerTool", "AdditionalIncludeDirectories", include_dirs)
    _ToolAppend(tools, "VCMIDLTool", "AdditionalIncludeDirectories", midl_include_dirs)
    _ToolAppend(
        tools,
        "VCResourceCompilerTool",
        "AdditionalIncludeDirectories",
        resource_include_dirs,
    )
    # Add in libraries.
    _ToolAppend(tools, "VCLinkerTool", "AdditionalDependencies", libraries)
    _ToolAppend(tools, "VCLinkerTool", "AdditionalLibraryDirectories", library_dirs)
    if out_file:
        _ToolAppend(tools, vc_tool, "OutputFile", out_file, only_if_unset=True)
    # Add defines.
    _ToolAppend(tools, "VCCLCompilerTool", "PreprocessorDefinitions", defines)
    _ToolAppend(tools, "VCResourceCompilerTool", "PreprocessorDefinitions", defines)
    # Change program database directory to prevent collisions.
    _ToolAppend(
        tools,
        "VCCLCompilerTool",
        "ProgramDataBaseFileName",
        "$(IntDir)$(ProjectName)\\vc80.pdb",
        only_if_unset=True,
    )
    # Add disabled warnings.
    _ToolAppend(tools, "VCCLCompilerTool", "DisableSpecificWarnings", disabled_warnings)
    # Add Pre-build.
    _ToolAppend(tools, "VCPreBuildEventTool", "CommandLine", prebuild)
    # Add Post-build.
    _ToolAppend(tools, "VCPostBuildEventTool", "CommandLine", postbuild)
    # Turn on precompiled headers if appropriate.
    if precompiled_header:
        precompiled_header = os.path.split(precompiled_header)[1]
        _ToolAppend(tools, "VCCLCompilerTool", "UsePrecompiledHeader", "2")
        _ToolAppend(
            tools, "VCCLCompilerTool", "PrecompiledHeaderThrough", precompiled_header
        )
        _ToolAppend(tools, "VCCLCompilerTool", "ForcedIncludeFiles", precompiled_header)
    # Loadable modules don't generate import libraries;
    # tell dependent projects to not expect one.
    if spec["type"] == "loadable_module":
        _ToolAppend(tools, "VCLinkerTool", "IgnoreImportLibrary", "true")
    # Set the module definition file if any.
    if def_file:
        _ToolAppend(tools, "VCLinkerTool", "ModuleDefinitionFile", def_file)

    _AddConfigurationToMSVS(p, spec, tools, config, config_type, config_name)


def _GetIncludeDirs(config):
    """Returns the list of directories to be used for #include directives.

  Arguments:
    config: The dictionary that defines the special processing to be done
            for this configuration.
  Returns:
    The list of directory paths.
  """
    # TODO(bradnelson): include_dirs should really be flexible enough not to
    #                   require this sort of thing.
    include_dirs = config.get("include_dirs", []) + config.get(
        "msvs_system_include_dirs", []
    )
    midl_include_dirs = config.get("midl_include_dirs", []) + config.get(
        "msvs_system_include_dirs", []
    )
    resource_include_dirs = config.get("resource_include_dirs", include_dirs)
    include_dirs = _FixPaths(include_dirs)
    midl_include_dirs = _FixPaths(midl_include_dirs)
    resource_include_dirs = _FixPaths(resource_include_dirs)
    return include_dirs, midl_include_dirs, resource_include_dirs


def _GetLibraryDirs(config):
    """Returns the list of directories to be used for library search paths.

  Arguments:
    config: The dictionary that defines the special processing to be done
            for this configuration.
  Returns:
    The list of directory paths.
  """

    library_dirs = config.get("library_dirs", [])
    library_dirs = _FixPaths(library_dirs)
    return library_dirs


def _GetLibraries(spec):
    """Returns the list of libraries for this configuration.

  Arguments:
    spec: The target dictionary containing the properties of the target.
  Returns:
    The list of directory paths.
  """
    libraries = spec.get("libraries", [])
    # Strip out -l, as it is not used on windows (but is needed so we can pass
    # in libraries that are assumed to be in the default library path).
    # Also remove duplicate entries, leaving only the last duplicate, while
    # preserving order.
    found = OrderedSet()
    unique_libraries_list = []
    for entry in reversed(libraries):
        library = re.sub(r"^\-l", "", entry)
        if not os.path.splitext(library)[1]:
            library += ".lib"
        if library not in found:
            found.add(library)
            unique_libraries_list.append(library)
    unique_libraries_list.reverse()
    return unique_libraries_list


def _GetOutputFilePathAndTool(spec, msbuild):
    """Returns the path and tool to use for this target.

  Figures out the path of the file this spec will create and the name of
  the VC tool that will create it.

  Arguments:
    spec: The target dictionary containing the properties of the target.
  Returns:
    A triple of (file path, name of the vc tool, name of the msbuild tool)
  """
    # Select a name for the output file.
    out_file = ""
    vc_tool = ""
    msbuild_tool = ""
    output_file_map = {
        "executable": ("VCLinkerTool", "Link", "$(OutDir)", ".exe"),
        "shared_library": ("VCLinkerTool", "Link", "$(OutDir)", ".dll"),
        "loadable_module": ("VCLinkerTool", "Link", "$(OutDir)", ".dll"),
        "windows_driver": ("VCLinkerTool", "Link", "$(OutDir)", ".sys"),
        "static_library": ("VCLibrarianTool", "Lib", "$(OutDir)lib\\", ".lib"),
    }
    output_file_props = output_file_map.get(spec["type"])
    if output_file_props and int(spec.get("msvs_auto_output_file", 1)):
        vc_tool, msbuild_tool, out_dir, suffix = output_file_props
        if spec.get("standalone_static_library", 0):
            out_dir = "$(OutDir)"
        out_dir = spec.get("product_dir", out_dir)
        product_extension = spec.get("product_extension")
        if product_extension:
            suffix = "." + product_extension
        elif msbuild:
            suffix = "$(TargetExt)"
        prefix = spec.get("product_prefix", "")
        product_name = spec.get("product_name", "$(ProjectName)")
        out_file = ntpath.join(out_dir, prefix + product_name + suffix)
    return out_file, vc_tool, msbuild_tool


def _GetOutputTargetExt(spec):
    """Returns the extension for this target, including the dot

  If product_extension is specified, set target_extension to this to avoid
  MSB8012, returns None otherwise. Ignores any target_extension settings in
  the input files.

  Arguments:
    spec: The target dictionary containing the properties of the target.
  Returns:
    A string with the extension, or None
  """
    target_extension = spec.get("product_extension")
    if target_extension:
        return "." + target_extension
    return None


def _GetDefines(config):
    """Returns the list of preprocessor definitions for this configuration.

  Arguments:
    config: The dictionary that defines the special processing to be done
            for this configuration.
  Returns:
    The list of preprocessor definitions.
  """
    defines = []
    for d in config.get("defines", []):
        fd = "=".join([str(dpart) for dpart in d]) if isinstance(d, list) else str(d)
        defines.append(fd)
    return defines


def _GetDisabledWarnings(config):
    return [str(i) for i in config.get("msvs_disabled_warnings", [])]


def _GetModuleDefinition(spec):
    def_file = ""
    if spec["type"] in [
        "shared_library",
        "loadable_module",
        "executable",
        "windows_driver",
    ]:
        def_files = [s for s in spec.get("sources", []) if s.endswith(".def")]
        if len(def_files) == 1:
            def_file = _FixPath(def_files[0])
        elif def_files:
            raise ValueError(
                "Multiple module definition files in one target, target %s lists "
                "multiple .def files: %s" % (spec["target_name"], " ".join(def_files))
            )
    return def_file


def _ConvertToolsToExpectedForm(tools):
    """Convert tools to a form expected by Visual Studio.

  Arguments:
    tools: A dictionary of settings; the tool name is the key.
  Returns:
    A list of Tool objects.
  """
    tool_list = []
    for tool, settings in tools.items():
        # Collapse settings with lists.
        settings_fixed = {}
        for setting, value in settings.items():
            if type(value) == list:
                if (
                    tool == "VCLinkerTool" and setting == "AdditionalDependencies"
                ) or setting == "AdditionalOptions":
                    settings_fixed[setting] = " ".join(value)
                else:
                    settings_fixed[setting] = ";".join(value)
            else:
                settings_fixed[setting] = value
        # Add in this tool.
        tool_list.append(MSVSProject.Tool(tool, settings_fixed))
    return tool_list


def _AddConfigurationToMSVS(p, spec, tools, config, config_type, config_name):
    """Add to the project file the configuration specified by config.

  Arguments:
    p: The target project being generated.
    spec: the target project dict.
    tools: A dictionary of settings; the tool name is the key.
    config: The dictionary that defines the special processing to be done
            for this configuration.
    config_type: The configuration type, a number as defined by Microsoft.
    config_name: The name of the configuration.
  """
    attributes = _GetMSVSAttributes(spec, config, config_type)
    # Add in this configuration.
    tool_list = _ConvertToolsToExpectedForm(tools)
    p.AddConfig(_ConfigFullName(config_name, config), attrs=attributes, tools=tool_list)


def _GetMSVSAttributes(spec, config, config_type):
    # Prepare configuration attributes.
    prepared_attrs = {}
    source_attrs = config.get("msvs_configuration_attributes", {})
    for a in source_attrs:
        prepared_attrs[a] = source_attrs[a]
    # Add props files.
    vsprops_dirs = config.get("msvs_props", [])
    vsprops_dirs = _FixPaths(vsprops_dirs)
    if vsprops_dirs:
        prepared_attrs["InheritedPropertySheets"] = ";".join(vsprops_dirs)
    # Set configuration type.
    prepared_attrs["ConfigurationType"] = config_type
    output_dir = prepared_attrs.get(
        "OutputDirectory", "$(SolutionDir)$(ConfigurationName)"
    )
    prepared_attrs["OutputDirectory"] = _FixPath(output_dir) + "\\"
    if "IntermediateDirectory" not in prepared_attrs:
        intermediate = "$(ConfigurationName)\\obj\\$(ProjectName)"
        prepared_attrs["IntermediateDirectory"] = _FixPath(intermediate) + "\\"
    else:
        intermediate = _FixPath(prepared_attrs["IntermediateDirectory"]) + "\\"
        intermediate = MSVSSettings.FixVCMacroSlashes(intermediate)
        prepared_attrs["IntermediateDirectory"] = intermediate
    return prepared_attrs


def _AddNormalizedSources(sources_set, sources_array):
    sources_set.update(_NormalizedSource(s) for s in sources_array)


def _PrepareListOfSources(spec, generator_flags, gyp_file):
    """Prepare list of sources and excluded sources.

  Besides the sources specified directly in the spec, adds the gyp file so
  that a change to it will cause a re-compile. Also adds appropriate sources
  for actions and copies. Assumes later stage will un-exclude files which
  have custom build steps attached.

  Arguments:
    spec: The target dictionary containing the properties of the target.
    gyp_file: The name of the gyp file.
  Returns:
    A pair of (list of sources, list of excluded sources).
    The sources will be relative to the gyp file.
  """
    sources = OrderedSet()
    _AddNormalizedSources(sources, spec.get("sources", []))
    excluded_sources = OrderedSet()
    # Add in the gyp file.
    if not generator_flags.get("standalone"):
        sources.add(gyp_file)

    # Add in 'action' inputs and outputs.
    for a in spec.get("actions", []):
        inputs = a["inputs"]
        inputs = [_NormalizedSource(i) for i in inputs]
        # Add all inputs to sources and excluded sources.
        inputs = OrderedSet(inputs)
        sources.update(inputs)
        if not spec.get("msvs_external_builder"):
            excluded_sources.update(inputs)
        if int(a.get("process_outputs_as_sources", False)):
            _AddNormalizedSources(sources, a.get("outputs", []))
    # Add in 'copies' inputs and outputs.
    for cpy in spec.get("copies", []):
        _AddNormalizedSources(sources, cpy.get("files", []))
    return (sources, excluded_sources)


def _AdjustSourcesAndConvertToFilterHierarchy(
    spec, options, gyp_dir, sources, excluded_sources, list_excluded, version
):
    """Adjusts the list of sources and excluded sources.

  Also converts the sets to lists.

  Arguments:
    spec: The target dictionary containing the properties of the target.
    options: Global generator options.
    gyp_dir: The path to the gyp file being processed.
    sources: A set of sources to be included for this project.
    excluded_sources: A set of sources to be excluded for this project.
    version: A MSVSVersion object.
  Returns:
    A trio of (list of sources, list of excluded sources,
               path of excluded IDL file)
  """
    # Exclude excluded sources coming into the generator.
    excluded_sources.update(OrderedSet(spec.get("sources_excluded", [])))
    # Add excluded sources into sources for good measure.
    sources.update(excluded_sources)
    # Convert to proper windows form.
    # NOTE: sources goes from being a set to a list here.
    # NOTE: excluded_sources goes from being a set to a list here.
    sources = _FixPaths(sources)
    # Convert to proper windows form.
    excluded_sources = _FixPaths(excluded_sources)

    excluded_idl = _IdlFilesHandledNonNatively(spec, sources)

    precompiled_related = _GetPrecompileRelatedFiles(spec)
    # Find the excluded ones, minus the precompiled header related ones.
    fully_excluded = [i for i in excluded_sources if i not in precompiled_related]

    # Convert to folders and the right slashes.
    sources = [i.split("\\") for i in sources]
    sources = _ConvertSourcesToFilterHierarchy(
        sources,
        excluded=fully_excluded,
        list_excluded=list_excluded,
        msvs_version=version,
    )

    # Prune filters with a single child to flatten ugly directory structures
    # such as ../../src/modules/module1 etc.
    if version.UsesVcxproj():
        while (
            all(isinstance(s, MSVSProject.Filter) for s in sources)
            and len({s.name for s in sources}) == 1
        ):
            assert all(len(s.contents) == 1 for s in sources)
            sources = [s.contents[0] for s in sources]
    else:
        while len(sources) == 1 and isinstance(sources[0], MSVSProject.Filter):
            sources = sources[0].contents

    return sources, excluded_sources, excluded_idl


def _IdlFilesHandledNonNatively(spec, sources):
    # If any non-native rules use 'idl' as an extension exclude idl files.
    # Gather a list here to use later.
    using_idl = False
    for rule in spec.get("rules", []):
        if rule["extension"] == "idl" and int(rule.get("msvs_external_rule", 0)):
            using_idl = True
            break
    excluded_idl = [i for i in sources if i.endswith(".idl")] if using_idl else []
    return excluded_idl


def _GetPrecompileRelatedFiles(spec):
    # Gather a list of precompiled header related sources.
    precompiled_related = []
    for _, config in spec["configurations"].items():
        for k in precomp_keys:
            f = config.get(k)
            if f:
                precompiled_related.append(_FixPath(f))
    return precompiled_related


def _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl, list_excluded):
    exclusions = _GetExcludedFilesFromBuild(spec, excluded_sources, excluded_idl)
    for file_name, excluded_configs in exclusions.items():
        if not list_excluded and len(excluded_configs) == len(spec["configurations"]):
            # If we're not listing excluded files, then they won't appear in the
            # project, so don't try to configure them to be excluded.
            pass
        else:
            for config_name, config in excluded_configs:
                p.AddFileConfig(
                    file_name,
                    _ConfigFullName(config_name, config),
                    {"ExcludedFromBuild": "true"},
                )


def _GetExcludedFilesFromBuild(spec, excluded_sources, excluded_idl):
    exclusions = {}
    # Exclude excluded sources from being built.
    for f in excluded_sources:
        excluded_configs = []
        for config_name, config in spec["configurations"].items():
            precomped = [_FixPath(config.get(i, "")) for i in precomp_keys]
            # Don't do this for ones that are precompiled header related.
            if f not in precomped:
                excluded_configs.append((config_name, config))
        exclusions[f] = excluded_configs
    # If any non-native rules use 'idl' as an extension exclude idl files.
    # Exclude them now.
    for f in excluded_idl:
        excluded_configs = []
        for config_name, config in spec["configurations"].items():
            excluded_configs.append((config_name, config))
        exclusions[f] = excluded_configs
    return exclusions


def _AddToolFilesToMSVS(p, spec):
    # Add in tool files (rules).
    tool_files = OrderedSet()
    for _, config in spec["configurations"].items():
        for f in config.get("msvs_tool_files", []):
            tool_files.add(f)
    for f in tool_files:
        p.AddToolFile(f)


def _HandlePreCompiledHeaders(p, sources, spec):
    # Pre-compiled header source stubs need a different compiler flag
    # (generate precompiled header) and any source file not of the same
    # kind (i.e. C vs. C++) as the precompiled header source stub needs
    # to have use of precompiled headers disabled.
    extensions_excluded_from_precompile = []
    for config_name, config in spec["configurations"].items():
        source = config.get("msvs_precompiled_source")
        if source:
            source = _FixPath(source)
            # UsePrecompiledHeader=1 for if using precompiled headers.
            tool = MSVSProject.Tool("VCCLCompilerTool", {"UsePrecompiledHeader": "1"})
            p.AddFileConfig(
                source, _ConfigFullName(config_name, config), {}, tools=[tool]
            )
            basename, extension = os.path.splitext(source)
            if extension == ".c":
                extensions_excluded_from_precompile = [".cc", ".cpp", ".cxx"]
            else:
                extensions_excluded_from_precompile = [".c"]

    def DisableForSourceTree(source_tree):
        for source in source_tree:
            if isinstance(source, MSVSProject.Filter):
                DisableForSourceTree(source.contents)
            else:
                basename, extension = os.path.splitext(source)
                if extension in extensions_excluded_from_precompile:
                    for config_name, config in spec["configurations"].items():
                        tool = MSVSProject.Tool(
                            "VCCLCompilerTool",
                            {
                                "UsePrecompiledHeader": "0",
                                "ForcedIncludeFiles": "$(NOINHERIT)",
                            },
                        )
                        p.AddFileConfig(
                            _FixPath(source),
                            _ConfigFullName(config_name, config),
                            {},
                            tools=[tool],
                        )

    # Do nothing if there was no precompiled source.
    if extensions_excluded_from_precompile:
        DisableForSourceTree(sources)


def _AddActions(actions_to_add, spec, relative_path_of_gyp_file):
    # Add actions.
    actions = spec.get("actions", [])
    # Don't setup_env every time. When all the actions are run together in one
    # batch file in VS, the PATH will grow too long.
    # Membership in this set means that the cygwin environment has been set up,
    # and does not need to be set up again.
    have_setup_env = set()
    for a in actions:
        # Attach actions to the gyp file if nothing else is there.
        inputs = a.get("inputs") or [relative_path_of_gyp_file]
        attached_to = inputs[0]
        need_setup_env = attached_to not in have_setup_env
        cmd = _BuildCommandLineForRule(
            spec, a, has_input_path=False, do_setup_env=need_setup_env
        )
        have_setup_env.add(attached_to)
        # Add the action.
        _AddActionStep(
            actions_to_add,
            inputs=inputs,
            outputs=a.get("outputs", []),
            description=a.get("message", a["action_name"]),
            command=cmd,
        )


def _WriteMSVSUserFile(project_path, version, spec):
    # Add run_as and test targets.
    if "run_as" in spec:
        run_as = spec["run_as"]
        action = run_as.get("action", [])
        environment = run_as.get("environment", [])
        working_directory = run_as.get("working_directory", ".")
    elif int(spec.get("test", 0)):
        action = ["$(TargetPath)", "--gtest_print_time"]
        environment = []
        working_directory = "."
    else:
        return  # Nothing to add
    # Write out the user file.
    user_file = _CreateMSVSUserFile(project_path, version, spec)
    for config_name, c_data in spec["configurations"].items():
        user_file.AddDebugSettings(
            _ConfigFullName(config_name, c_data), action, environment, working_directory
        )
    user_file.WriteIfChanged()


def _AddCopies(actions_to_add, spec):
    copies = _GetCopies(spec)
    for inputs, outputs, cmd, description in copies:
        _AddActionStep(
            actions_to_add,
            inputs=inputs,
            outputs=outputs,
            description=description,
            command=cmd,
        )


def _GetCopies(spec):
    copies = []
    # Add copies.
    for cpy in spec.get("copies", []):
        for src in cpy.get("files", []):
            dst = os.path.join(cpy["destination"], os.path.basename(src))
            # _AddCustomBuildToolForMSVS() will call _FixPath() on the inputs and
            # outputs, so do the same for our generated command line.
            if src.endswith("/"):
                src_bare = src[:-1]
                base_dir = posixpath.split(src_bare)[0]
                outer_dir = posixpath.split(src_bare)[1]
                fixed_dst = _FixPath(dst)
                full_dst = f'"{fixed_dst}\\{outer_dir}\\"'
                cmd = (
                    f'mkdir {full_dst} 2>nul & cd "{_FixPath(base_dir)}" && '
                    f'xcopy /e /f /y "{outer_dir}" {full_dst}'
                )
                copies.append(
                    (
                        [src],
                        ["dummy_copies", dst],
                        cmd,
                        f"Copying {src} to {fixed_dst}",
                    )
                )
            else:
                fix_dst = _FixPath(cpy["destination"])
                cmd = (
                    f'mkdir "{fix_dst}" 2>nul & set ERRORLEVEL=0 & '
                    f'copy /Y "{_FixPath(src)}" "{_FixPath(dst)}"'
                )
                copies.append(([src], [dst], cmd, f"Copying {src} to {fix_dst}"))
    return copies


def _GetPathDict(root, path):
    # |path| will eventually be empty (in the recursive calls) if it was initially
    # relative; otherwise it will eventually end up as '\', 'D:\', etc.
    if not path or path.endswith(os.sep):
        return root
    parent, folder = os.path.split(path)
    parent_dict = _GetPathDict(root, parent)
    if folder not in parent_dict:
        parent_dict[folder] = {}
    return parent_dict[folder]


def _DictsToFolders(base_path, bucket, flat):
    # Convert to folders recursively.
    children = []
    for folder, contents in bucket.items():
        if type(contents) == dict:
            folder_children = _DictsToFolders(
                os.path.join(base_path, folder), contents, flat
            )
            if flat:
                children += folder_children
            else:
                folder_children = MSVSNew.MSVSFolder(
                    os.path.join(base_path, folder),
                    name="(" + folder + ")",
                    entries=folder_children,
                )
                children.append(folder_children)
        else:
            children.append(contents)
    return children


def _CollapseSingles(parent, node):
    # Recursively explorer the tree of dicts looking for projects which are
    # the sole item in a folder which has the same name as the project. Bring
    # such projects up one level.
    if type(node) == dict and len(node) == 1 and next(iter(node)) == parent + ".vcproj":
        return node[next(iter(node))]
    if type(node) != dict:
        return node
    for child in node:
        node[child] = _CollapseSingles(child, node[child])
    return node


def _GatherSolutionFolders(sln_projects, project_objects, flat):
    root = {}
    # Convert into a tree of dicts on path.
    for p in sln_projects:
        gyp_file, target = gyp.common.ParseQualifiedTarget(p)[0:2]
        if p.endswith("#host"):
            target += "_host"
        gyp_dir = os.path.dirname(gyp_file)
        path_dict = _GetPathDict(root, gyp_dir)
        path_dict[target + ".vcproj"] = project_objects[p]
    # Walk down from the top until we hit a folder that has more than one entry.
    # In practice, this strips the top-level "src/" dir from the hierarchy in
    # the solution.
    while len(root) == 1 and type(root[next(iter(root))]) == dict:
        root = root[next(iter(root))]
    # Collapse singles.
    root = _CollapseSingles("", root)
    # Merge buckets until everything is a root entry.
    return _DictsToFolders("", root, flat)


def _GetPathOfProject(qualified_target, spec, options, msvs_version):
    default_config = _GetDefaultConfiguration(spec)
    proj_filename = default_config.get("msvs_existing_vcproj")
    if not proj_filename:
        proj_filename = spec["target_name"]
        if spec["toolset"] == "host":
            proj_filename += "_host"
        proj_filename = proj_filename + options.suffix + msvs_version.ProjectExtension()

    build_file = gyp.common.BuildFile(qualified_target)
    proj_path = os.path.join(os.path.dirname(build_file), proj_filename)
    fix_prefix = None
    if options.generator_output:
        project_dir_path = os.path.dirname(os.path.abspath(proj_path))
        proj_path = os.path.join(options.generator_output, proj_path)
        fix_prefix = gyp.common.RelativePath(
            project_dir_path, os.path.dirname(proj_path)
        )
    return proj_path, fix_prefix


def _GetPlatformOverridesOfProject(spec):
    # Prepare a dict indicating which project configurations are used for which
    # solution configurations for this target.
    config_platform_overrides = {}
    for config_name, c in spec["configurations"].items():
        config_fullname = _ConfigFullName(config_name, c)
        platform = c.get("msvs_target_platform", _ConfigPlatform(c))
        fixed_config_fullname = (
            f"{_ConfigBaseName(config_name, _ConfigPlatform(c))}|{platform}"
        )
        if spec["toolset"] == "host" and generator_supports_multiple_toolsets:
            fixed_config_fullname = f"{config_name}|x64"
        config_platform_overrides[config_fullname] = fixed_config_fullname
    return config_platform_overrides


def _CreateProjectObjects(target_list, target_dicts, options, msvs_version):
    """Create a MSVSProject object for the targets found in target list.

  Arguments:
    target_list: the list of targets to generate project objects for.
    target_dicts: the dictionary of specifications.
    options: global generator options.
    msvs_version: the MSVSVersion object.
  Returns:
    A set of created projects, keyed by target.
  """
    global fixpath_prefix
    # Generate each project.
    projects = {}
    for qualified_target in target_list:
        spec = target_dicts[qualified_target]
        proj_path, fixpath_prefix = _GetPathOfProject(
            qualified_target, spec, options, msvs_version
        )
        guid = _GetGuidOfProject(proj_path, spec)
        overrides = _GetPlatformOverridesOfProject(spec)
        build_file = gyp.common.BuildFile(qualified_target)
        # Create object for this project.
        target_name = spec["target_name"]
        if spec["toolset"] == "host":
            target_name += "_host"
        obj = MSVSNew.MSVSProject(
            proj_path,
            name=target_name,
            guid=guid,
            spec=spec,
            build_file=build_file,
            config_platform_overrides=overrides,
            fixpath_prefix=fixpath_prefix,
        )
        # Set project toolset if any (MS build only)
        if msvs_version.UsesVcxproj():
            obj.set_msbuild_toolset(
                _GetMsbuildToolsetOfProject(proj_path, spec, msvs_version)
            )
        projects[qualified_target] = obj
    # Set all the dependencies, but not if we are using an external builder like
    # ninja
    for project in projects.values():
        if not project.spec.get("msvs_external_builder"):
            deps = project.spec.get("dependencies", [])
            deps = [projects[d] for d in deps]
            project.set_dependencies(deps)
    return projects


def _InitNinjaFlavor(params, target_list, target_dicts):
    """Initialize targets for the ninja flavor.

  This sets up the necessary variables in the targets to generate msvs projects
  that use ninja as an external builder. The variables in the spec are only set
  if they have not been set. This allows individual specs to override the
  default values initialized here.
  Arguments:
    params: Params provided to the generator.
    target_list: List of target pairs: 'base/base.gyp:base'.
    target_dicts: Dict of target properties keyed on target pair.
  """
    for qualified_target in target_list:
        spec = target_dicts[qualified_target]
        if spec.get("msvs_external_builder"):
            # The spec explicitly defined an external builder, so don't change it.
            continue

        path_to_ninja = spec.get("msvs_path_to_ninja", "ninja.exe")

        spec["msvs_external_builder"] = "ninja"
        if not spec.get("msvs_external_builder_out_dir"):
            gyp_file, _, _ = gyp.common.ParseQualifiedTarget(qualified_target)
            gyp_dir = os.path.dirname(gyp_file)
            configuration = "$(Configuration)"
            if params.get("target_arch") == "x64":
                configuration += "_x64"
            if params.get("target_arch") == "arm64":
                configuration += "_arm64"
            spec["msvs_external_builder_out_dir"] = os.path.join(
                gyp.common.RelativePath(params["options"].toplevel_dir, gyp_dir),
                ninja_generator.ComputeOutputDir(params),
                configuration,
            )
        if not spec.get("msvs_external_builder_build_cmd"):
            spec["msvs_external_builder_build_cmd"] = [
                path_to_ninja,
                "-C",
                "$(OutDir)",
                "$(ProjectName)",
            ]
        if not spec.get("msvs_external_builder_clean_cmd"):
            spec["msvs_external_builder_clean_cmd"] = [
                path_to_ninja,
                "-C",
                "$(OutDir)",
                "-tclean",
                "$(ProjectName)",
            ]


def CalculateVariables(default_variables, params):
    """Generated variables that require params to be known."""

    generator_flags = params.get("generator_flags", {})

    # Select project file format version (if unset, default to auto detecting).
    msvs_version = MSVSVersion.SelectVisualStudioVersion(
        generator_flags.get("msvs_version", "auto")
    )
    # Stash msvs_version for later (so we don't have to probe the system twice).
    params["msvs_version"] = msvs_version

    # Set a variable so conditions can be based on msvs_version.
    default_variables["MSVS_VERSION"] = msvs_version.ShortName()

    # To determine processor word size on Windows, in addition to checking
    # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
    # process), it is also necessary to check PROCESSOR_ARCITEW6432 (which
    # contains the actual word size of the system when running thru WOW64).
    if (
        os.environ.get("PROCESSOR_ARCHITECTURE", "").find("64") >= 0
        or os.environ.get("PROCESSOR_ARCHITEW6432", "").find("64") >= 0
    ):
        default_variables["MSVS_OS_BITS"] = 64
    else:
        default_variables["MSVS_OS_BITS"] = 32

    if gyp.common.GetFlavor(params) == "ninja":
        default_variables["SHARED_INTERMEDIATE_DIR"] = "$(OutDir)gen"


def PerformBuild(data, configurations, params):
    options = params["options"]
    msvs_version = params["msvs_version"]
    devenv = os.path.join(msvs_version.path, "Common7", "IDE", "devenv.com")

    for build_file, build_file_dict in data.items():
        (build_file_root, build_file_ext) = os.path.splitext(build_file)
        if build_file_ext != ".gyp":
            continue
        sln_path = build_file_root + options.suffix + ".sln"
        if options.generator_output:
            sln_path = os.path.join(options.generator_output, sln_path)

    for config in configurations:
        arguments = [devenv, sln_path, "/Build", config]
        print(f"Building [{config}]: {arguments}")
        subprocess.check_call(arguments)


def CalculateGeneratorInputInfo(params):
    if params.get("flavor") == "ninja":
        toplevel = params["options"].toplevel_dir
        qualified_out_dir = os.path.normpath(
            os.path.join(
                toplevel,
                ninja_generator.ComputeOutputDir(params),
                "gypfiles-msvs-ninja",
            )
        )

        global generator_filelist_paths
        generator_filelist_paths = {
            "toplevel": toplevel,
            "qualified_out_dir": qualified_out_dir,
        }


def GenerateOutput(target_list, target_dicts, data, params):
    """Generate .sln and .vcproj files.

  This is the entry point for this generator.
  Arguments:
    target_list: List of target pairs: 'base/base.gyp:base'.
    target_dicts: Dict of target properties keyed on target pair.
    data: Dictionary containing per .gyp data.
  """
    global fixpath_prefix

    options = params["options"]

    # Get the project file format version back out of where we stashed it in
    # GeneratorCalculatedVariables.
    msvs_version = params["msvs_version"]

    generator_flags = params.get("generator_flags", {})

    # Optionally shard targets marked with 'msvs_shard': SHARD_COUNT.
    (target_list, target_dicts) = MSVSUtil.ShardTargets(target_list, target_dicts)

    # Optionally use the large PDB workaround for targets marked with
    # 'msvs_large_pdb': 1.
    (target_list, target_dicts) = MSVSUtil.InsertLargePdbShims(
        target_list, target_dicts, generator_default_variables
    )

    # Optionally configure each spec to use ninja as the external builder.
    if params.get("flavor") == "ninja":
        _InitNinjaFlavor(params, target_list, target_dicts)

    # Prepare the set of configurations.
    configs = set()
    for qualified_target in target_list:
        spec = target_dicts[qualified_target]
        for config_name, config in spec["configurations"].items():
            config_name = _ConfigFullName(config_name, config)
            configs.add(config_name)
            if config_name == "Release|arm64":
                configs.add("Release|x64")
    configs = list(configs)

    # Figure out all the projects that will be generated and their guids
    project_objects = _CreateProjectObjects(
        target_list, target_dicts, options, msvs_version
    )

    # Generate each project.
    missing_sources = []
    for project in project_objects.values():
        fixpath_prefix = project.fixpath_prefix
        missing_sources.extend(
            _GenerateProject(project, options, msvs_version, generator_flags, spec)
        )
    fixpath_prefix = None

    for build_file in data:
        # Validate build_file extension
        target_only_configs = configs
        if generator_supports_multiple_toolsets:
            target_only_configs = [i for i in configs if i.endswith("arm64")]
        if not build_file.endswith(".gyp"):
            continue
        sln_path = os.path.splitext(build_file)[0] + options.suffix + ".sln"
        if options.generator_output:
            sln_path = os.path.join(options.generator_output, sln_path)
        # Get projects in the solution, and their dependents.
        sln_projects = gyp.common.BuildFileTargets(target_list, build_file)
        sln_projects += gyp.common.DeepDependencyTargets(target_dicts, sln_projects)
        # Create folder hierarchy.
        root_entries = _GatherSolutionFolders(
            sln_projects, project_objects, flat=msvs_version.FlatSolution()
        )
        # Create solution.
        sln = MSVSNew.MSVSSolution(
            sln_path,
            entries=root_entries,
            variants=target_only_configs,
            websiteProperties=False,
            version=msvs_version,
        )
        sln.Write()

    if missing_sources:
        error_message = "Missing input files:\n" + "\n".join(set(missing_sources))
        if generator_flags.get("msvs_error_on_missing_sources", False):
            raise GypError(error_message)
        else:
            print("Warning: " + error_message, file=sys.stdout)


def _GenerateMSBuildFiltersFile(
    filters_path,
    source_files,
    rule_dependencies,
    extension_to_rule_name,
    platforms,
    toolset,
):
    """Generate the filters file.

  This file is used by Visual Studio to organize the presentation of source
  files into folders.

  Arguments:
      filters_path: The path of the file to be created.
      source_files: The hierarchical structure of all the sources.
      extension_to_rule_name: A dictionary mapping file extensions to rules.
  """
    filter_group = []
    source_group = []
    _AppendFiltersForMSBuild(
        "",
        source_files,
        rule_dependencies,
        extension_to_rule_name,
        platforms,
        toolset,
        filter_group,
        source_group,
    )
    if filter_group:
        content = [
            "Project",
            {
                "ToolsVersion": "4.0",
                "xmlns": "http://schemas.microsoft.com/developer/msbuild/2003",
            },
            ["ItemGroup"] + filter_group,
            ["ItemGroup"] + source_group,
        ]
        easy_xml.WriteXmlIfChanged(content, filters_path, pretty=True, win32=True)
    elif os.path.exists(filters_path):
        # We don't need this filter anymore.  Delete the old filter file.
        os.unlink(filters_path)


def _AppendFiltersForMSBuild(
    parent_filter_name,
    sources,
    rule_dependencies,
    extension_to_rule_name,
    platforms,
    toolset,
    filter_group,
    source_group,
):
    """Creates the list of filters and sources to be added in the filter file.

  Args:
      parent_filter_name: The name of the filter under which the sources are
          found.
      sources: The hierarchy of filters and sources to process.
      extension_to_rule_name: A dictionary mapping file extensions to rules.
      filter_group: The list to which filter entries will be appended.
      source_group: The list to which source entries will be appended.
  """
    for source in sources:
        if isinstance(source, MSVSProject.Filter):
            # We have a sub-filter.  Create the name of that sub-filter.
            if not parent_filter_name:
                filter_name = source.name
            else:
                filter_name = f"{parent_filter_name}\\{source.name}"
            # Add the filter to the group.
            filter_group.append(
                [
                    "Filter",
                    {"Include": filter_name},
                    ["UniqueIdentifier", MSVSNew.MakeGuid(source.name)],
                ]
            )
            # Recurse and add its dependents.
            _AppendFiltersForMSBuild(
                filter_name,
                source.contents,
                rule_dependencies,
                extension_to_rule_name,
                platforms,
                toolset,
                filter_group,
                source_group,
            )
        else:
            # It's a source.  Create a source entry.
            _, element = _MapFileToMsBuildSourceType(
                source, rule_dependencies, extension_to_rule_name, platforms, toolset
            )
            source_entry = [element, {"Include": source}]
            # Specify the filter it is part of, if any.
            if parent_filter_name:
                source_entry.append(["Filter", parent_filter_name])
            source_group.append(source_entry)


def _MapFileToMsBuildSourceType(
    source, rule_dependencies, extension_to_rule_name, platforms, toolset
):
    """Returns the group and element type of the source file.

  Arguments:
      source: The source file name.
      extension_to_rule_name: A dictionary mapping file extensions to rules.

  Returns:
      A pair of (group this file should be part of, the label of element)
  """
    _, ext = os.path.splitext(source)
    ext = ext.lower()
    if ext in extension_to_rule_name:
        group = "rule"
        element = extension_to_rule_name[ext]
    elif ext in [".cc", ".cpp", ".c", ".cxx", ".mm"]:
        group = "compile"
        element = "ClCompile"
    elif ext in [".h", ".hxx"]:
        group = "include"
        element = "ClInclude"
    elif ext == ".rc":
        group = "resource"
        element = "ResourceCompile"
    elif ext in [".s", ".asm"]:
        group = "masm"
        element = "MASM"
        if "arm64" in platforms and toolset == "target":
            element = "MARMASM"
    elif ext == ".idl":
        group = "midl"
        element = "Midl"
    elif source in rule_dependencies:
        group = "rule_dependency"
        element = "CustomBuild"
    else:
        group = "none"
        element = "None"
    return (group, element)


def _GenerateRulesForMSBuild(
    output_dir,
    options,
    spec,
    sources,
    excluded_sources,
    props_files_of_rules,
    targets_files_of_rules,
    actions_to_add,
    rule_dependencies,
    extension_to_rule_name,
):
    # MSBuild rules are implemented using three files: an XML file, a .targets
    # file and a .props file.
    # For more details see:
    # https://devblogs.microsoft.com/cppblog/quick-help-on-vs2010-custom-build-rule/
    rules = spec.get("rules", [])
    rules_native = [r for r in rules if not int(r.get("msvs_external_rule", 0))]
    rules_external = [r for r in rules if int(r.get("msvs_external_rule", 0))]

    msbuild_rules = []
    for rule in rules_native:
        # Skip a rule with no action and no inputs.
        if "action" not in rule and not rule.get("rule_sources", []):
            continue
        msbuild_rule = MSBuildRule(rule, spec)
        msbuild_rules.append(msbuild_rule)
        rule_dependencies.update(msbuild_rule.additional_dependencies.split(";"))
        extension_to_rule_name[msbuild_rule.extension] = msbuild_rule.rule_name
    if msbuild_rules:
        base = spec["target_name"] + options.suffix
        props_name = base + ".props"
        targets_name = base + ".targets"
        xml_name = base + ".xml"

        props_files_of_rules.add(props_name)
        targets_files_of_rules.add(targets_name)

        props_path = os.path.join(output_dir, props_name)
        targets_path = os.path.join(output_dir, targets_name)
        xml_path = os.path.join(output_dir, xml_name)

        _GenerateMSBuildRulePropsFile(props_path, msbuild_rules)
        _GenerateMSBuildRuleTargetsFile(targets_path, msbuild_rules)
        _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules)

    if rules_external:
        _GenerateExternalRules(
            rules_external, output_dir, spec, sources, options, actions_to_add
        )
    _AdjustSourcesForRules(rules, sources, excluded_sources, True)


class MSBuildRule:
    """Used to store information used to generate an MSBuild rule.

  Attributes:
    rule_name: The rule name, sanitized to use in XML.
    target_name: The name of the target.
    after_targets: The name of the AfterTargets element.
    before_targets: The name of the BeforeTargets element.
    depends_on: The name of the DependsOn element.
    compute_output: The name of the ComputeOutput element.
    dirs_to_make: The name of the DirsToMake element.
    inputs: The name of the _inputs element.
    tlog: The name of the _tlog element.
    extension: The extension this rule applies to.
    description: The message displayed when this rule is invoked.
    additional_dependencies: A string listing additional dependencies.
    outputs: The outputs of this rule.
    command: The command used to run the rule.
  """

    def __init__(self, rule, spec):
        self.display_name = rule["rule_name"]
        # Assure that the rule name is only characters and numbers
        self.rule_name = re.sub(r"\W", "_", self.display_name)
        # Create the various element names, following the example set by the
        # Visual Studio 2008 to 2010 conversion.  I don't know if VS2010
        # is sensitive to the exact names.
        self.target_name = "_" + self.rule_name
        self.after_targets = self.rule_name + "AfterTargets"
        self.before_targets = self.rule_name + "BeforeTargets"
        self.depends_on = self.rule_name + "DependsOn"
        self.compute_output = "Compute%sOutput" % self.rule_name
        self.dirs_to_make = self.rule_name + "DirsToMake"
        self.inputs = self.rule_name + "_inputs"
        self.tlog = self.rule_name + "_tlog"
        self.extension = rule["extension"]
        if not self.extension.startswith("."):
            self.extension = "." + self.extension

        self.description = MSVSSettings.ConvertVCMacrosToMSBuild(
            rule.get("message", self.rule_name)
        )
        old_additional_dependencies = _FixPaths(rule.get("inputs", []))
        self.additional_dependencies = ";".join(
            [
                MSVSSettings.ConvertVCMacrosToMSBuild(i)
                for i in old_additional_dependencies
            ]
        )
        old_outputs = _FixPaths(rule.get("outputs", []))
        self.outputs = ";".join(
            [MSVSSettings.ConvertVCMacrosToMSBuild(i) for i in old_outputs]
        )
        old_command = _BuildCommandLineForRule(
            spec, rule, has_input_path=True, do_setup_env=True
        )
        self.command = MSVSSettings.ConvertVCMacrosToMSBuild(old_command)


def _GenerateMSBuildRulePropsFile(props_path, msbuild_rules):
    """Generate the .props file."""
    content = [
        "Project",
        {"xmlns": "http://schemas.microsoft.com/developer/msbuild/2003"},
    ]
    for rule in msbuild_rules:
        content.extend(
            [
                [
                    "PropertyGroup",
                    {
                        "Condition": "'$(%s)' == '' and '$(%s)' == '' and "
                        "'$(ConfigurationType)' != 'Makefile'"
                        % (rule.before_targets, rule.after_targets)
                    },
                    [rule.before_targets, "Midl"],
                    [rule.after_targets, "CustomBuild"],
                ],
                [
                    "PropertyGroup",
                    [
                        rule.depends_on,
                        {"Condition": "'$(ConfigurationType)' != 'Makefile'"},
                        "_SelectedFiles;$(%s)" % rule.depends_on,
                    ],
                ],
                [
                    "ItemDefinitionGroup",
                    [
                        rule.rule_name,
                        ["CommandLineTemplate", rule.command],
                        ["Outputs", rule.outputs],
                        ["ExecutionDescription", rule.description],
                        ["AdditionalDependencies", rule.additional_dependencies],
                    ],
                ],
            ]
        )
    easy_xml.WriteXmlIfChanged(content, props_path, pretty=True, win32=True)


def _GenerateMSBuildRuleTargetsFile(targets_path, msbuild_rules):
    """Generate the .targets file."""
    content = [
        "Project",
        {"xmlns": "http://schemas.microsoft.com/developer/msbuild/2003"},
    ]
    item_group = [
        "ItemGroup",
        [
            "PropertyPageSchema",
            {"Include": "$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml"},
        ],
    ]
    for rule in msbuild_rules:
        item_group.append(
            [
                "AvailableItemName",
                {"Include": rule.rule_name},
                ["Targets", rule.target_name],
            ]
        )
    content.append(item_group)

    for rule in msbuild_rules:
        content.append(
            [
                "UsingTask",
                {
                    "TaskName": rule.rule_name,
                    "TaskFactory": "XamlTaskFactory",
                    "AssemblyName": "Microsoft.Build.Tasks.v4.0",
                },
                ["Task", "$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml"],
            ]
        )
    for rule in msbuild_rules:
        rule_name = rule.rule_name
        target_outputs = "%%(%s.Outputs)" % rule_name
        target_inputs = (
            "%%(%s.Identity);%%(%s.AdditionalDependencies);" "$(MSBuildProjectFile)"
        ) % (rule_name, rule_name)
        rule_inputs = "%%(%s.Identity)" % rule_name
        extension_condition = (
            "'%(Extension)'=='.obj' or "
            "'%(Extension)'=='.res' or "
            "'%(Extension)'=='.rsc' or "
            "'%(Extension)'=='.lib'"
        )
        remove_section = [
            "ItemGroup",
            {"Condition": "'@(SelectedFiles)' != ''"},
            [
                rule_name,
                {
                    "Remove": "@(%s)" % rule_name,
                    "Condition": "'%(Identity)' != '@(SelectedFiles)'",
                },
            ],
        ]
        inputs_section = [
            "ItemGroup",
            [rule.inputs, {"Include": "%%(%s.AdditionalDependencies)" % rule_name}],
        ]
        logging_section = [
            "ItemGroup",
            [
                rule.tlog,
                {
                    "Include": "%%(%s.Outputs)" % rule_name,
                    "Condition": (
                        "'%%(%s.Outputs)' != '' and "
                        "'%%(%s.ExcludedFromBuild)' != 'true'" % (rule_name, rule_name)
                    ),
                },
                ["Source", "@(%s, '|')" % rule_name],
                ["Inputs", "@(%s -> '%%(Fullpath)', ';')" % rule.inputs],
            ],
        ]
        message_section = [
            "Message",
            {"Importance": "High", "Text": "%%(%s.ExecutionDescription)" % rule_name},
        ]
        write_tlog_section = [
            "WriteLinesToFile",
            {
                "Condition": "'@(%s)' != '' and '%%(%s.ExcludedFromBuild)' != "
                "'true'" % (rule.tlog, rule.tlog),
                "File": "$(IntDir)$(ProjectName).write.1.tlog",
                "Lines": "^%%(%s.Source);@(%s->'%%(Fullpath)')"
                % (rule.tlog, rule.tlog),
            },
        ]
        read_tlog_section = [
            "WriteLinesToFile",
            {
                "Condition": "'@(%s)' != '' and '%%(%s.ExcludedFromBuild)' != "
                "'true'" % (rule.tlog, rule.tlog),
                "File": "$(IntDir)$(ProjectName).read.1.tlog",
                "Lines": f"^%({rule.tlog}.Source);%({rule.tlog}.Inputs)",
            },
        ]
        command_and_input_section = [
            rule_name,
            {
                "Condition": "'@(%s)' != '' and '%%(%s.ExcludedFromBuild)' != "
                "'true'" % (rule_name, rule_name),
                "EchoOff": "true",
                "StandardOutputImportance": "High",
                "StandardErrorImportance": "High",
                "CommandLineTemplate": "%%(%s.CommandLineTemplate)" % rule_name,
                "AdditionalOptions": "%%(%s.AdditionalOptions)" % rule_name,
                "Inputs": rule_inputs,
            },
        ]
        content.extend(
            [
                [
                    "Target",
                    {
                        "Name": rule.target_name,
                        "BeforeTargets": "$(%s)" % rule.before_targets,
                        "AfterTargets": "$(%s)" % rule.after_targets,
                        "Condition": "'@(%s)' != ''" % rule_name,
                        "DependsOnTargets": "$(%s);%s"
                        % (rule.depends_on, rule.compute_output),
                        "Outputs": target_outputs,
                        "Inputs": target_inputs,
                    },
                    remove_section,
                    inputs_section,
                    logging_section,
                    message_section,
                    write_tlog_section,
                    read_tlog_section,
                    command_and_input_section,
                ],
                [
                    "PropertyGroup",
                    [
                        "ComputeLinkInputsTargets",
                        "$(ComputeLinkInputsTargets);",
                        "%s;" % rule.compute_output,
                    ],
                    [
                        "ComputeLibInputsTargets",
                        "$(ComputeLibInputsTargets);",
                        "%s;" % rule.compute_output,
                    ],
                ],
                [
                    "Target",
                    {
                        "Name": rule.compute_output,
                        "Condition": "'@(%s)' != ''" % rule_name,
                    },
                    [
                        "ItemGroup",
                        [
                            rule.dirs_to_make,
                            {
                                "Condition": "'@(%s)' != '' and "
                                "'%%(%s.ExcludedFromBuild)' != 'true'"
                                % (rule_name, rule_name),
                                "Include": "%%(%s.Outputs)" % rule_name,
                            },
                        ],
                        [
                            "Link",
                            {
                                "Include": "%%(%s.Identity)" % rule.dirs_to_make,
                                "Condition": extension_condition,
                            },
                        ],
                        [
                            "Lib",
                            {
                                "Include": "%%(%s.Identity)" % rule.dirs_to_make,
                                "Condition": extension_condition,
                            },
                        ],
                        [
                            "ImpLib",
                            {
                                "Include": "%%(%s.Identity)" % rule.dirs_to_make,
                                "Condition": extension_condition,
                            },
                        ],
                    ],
                    [
                        "MakeDir",
                        {
                            "Directories": (
                                "@(%s->'%%(RootDir)%%(Directory)')" % rule.dirs_to_make
                            )
                        },
                    ],
                ],
            ]
        )
    easy_xml.WriteXmlIfChanged(content, targets_path, pretty=True, win32=True)


def _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules):
    # Generate the .xml file
    content = [
        "ProjectSchemaDefinitions",
        {
            "xmlns": (
                "clr-namespace:Microsoft.Build.Framework.XamlTypes;"
                "assembly=Microsoft.Build.Framework"
            ),
            "xmlns:x": "http://schemas.microsoft.com/winfx/2006/xaml",
            "xmlns:sys": "clr-namespace:System;assembly=mscorlib",
            "xmlns:transformCallback": "Microsoft.Cpp.Dev10.ConvertPropertyCallback",
        },
    ]
    for rule in msbuild_rules:
        content.extend(
            [
                [
                    "Rule",
                    {
                        "Name": rule.rule_name,
                        "PageTemplate": "tool",
                        "DisplayName": rule.display_name,
                        "Order": "200",
                    },
                    [
                        "Rule.DataSource",
                        [
                            "DataSource",
                            {"Persistence": "ProjectFile", "ItemType": rule.rule_name},
                        ],
                    ],
                    [
                        "Rule.Categories",
                        [
                            "Category",
                            {"Name": "General"},
                            ["Category.DisplayName", ["sys:String", "General"]],
                        ],
                        [
                            "Category",
                            {"Name": "Command Line", "Subtype": "CommandLine"},
                            ["Category.DisplayName", ["sys:String", "Command Line"]],
                        ],
                    ],
                    [
                        "StringListProperty",
                        {
                            "Name": "Inputs",
                            "Category": "Command Line",
                            "IsRequired": "true",
                            "Switch": " ",
                        },
                        [
                            "StringListProperty.DataSource",
                            [
                                "DataSource",
                                {
                                    "Persistence": "ProjectFile",
                                    "ItemType": rule.rule_name,
                                    "SourceType": "Item",
                                },
                            ],
                        ],
                    ],
                    [
                        "StringProperty",
                        {
                            "Name": "CommandLineTemplate",
                            "DisplayName": "Command Line",
                            "Visible": "False",
                            "IncludeInCommandLine": "False",
                        },
                    ],
                    [
                        "DynamicEnumProperty",
                        {
                            "Name": rule.before_targets,
                            "Category": "General",
                            "EnumProvider": "Targets",
                            "IncludeInCommandLine": "False",
                        },
                        [
                            "DynamicEnumProperty.DisplayName",
                            ["sys:String", "Execute Before"],
                        ],
                        [
                            "DynamicEnumProperty.Description",
                            [
                                "sys:String",
                                "Specifies the targets for the build customization"
                                " to run before.",
                            ],
                        ],
                        [
                            "DynamicEnumProperty.ProviderSettings",
                            [
                                "NameValuePair",
                                {
                                    "Name": "Exclude",
                                    "Value": "^%s|^Compute" % rule.before_targets,
                                },
                            ],
                        ],
                        [
                            "DynamicEnumProperty.DataSource",
                            [
                                "DataSource",
                                {
                                    "Persistence": "ProjectFile",
                                    "HasConfigurationCondition": "true",
                                },
                            ],
                        ],
                    ],
                    [
                        "DynamicEnumProperty",
                        {
                            "Name": rule.after_targets,
                            "Category": "General",
                            "EnumProvider": "Targets",
                            "IncludeInCommandLine": "False",
                        },
                        [
                            "DynamicEnumProperty.DisplayName",
                            ["sys:String", "Execute After"],
                        ],
                        [
                            "DynamicEnumProperty.Description",
                            [
                                "sys:String",
                                (
                                    "Specifies the targets for the build customization"
                                    " to run after."
                                ),
                            ],
                        ],
                        [
                            "DynamicEnumProperty.ProviderSettings",
                            [
                                "NameValuePair",
                                {
                                    "Name": "Exclude",
                                    "Value": "^%s|^Compute" % rule.after_targets,
                                },
                            ],
                        ],
                        [
                            "DynamicEnumProperty.DataSource",
                            [
                                "DataSource",
                                {
                                    "Persistence": "ProjectFile",
                                    "ItemType": "",
                                    "HasConfigurationCondition": "true",
                                },
                            ],
                        ],
                    ],
                    [
                        "StringListProperty",
                        {
                            "Name": "Outputs",
                            "DisplayName": "Outputs",
                            "Visible": "False",
                            "IncludeInCommandLine": "False",
                        },
                    ],
                    [
                        "StringProperty",
                        {
                            "Name": "ExecutionDescription",
                            "DisplayName": "Execution Description",
                            "Visible": "False",
                            "IncludeInCommandLine": "False",
                        },
                    ],
                    [
                        "StringListProperty",
                        {
                            "Name": "AdditionalDependencies",
                            "DisplayName": "Additional Dependencies",
                            "IncludeInCommandLine": "False",
                            "Visible": "false",
                        },
                    ],
                    [
                        "StringProperty",
                        {
                            "Subtype": "AdditionalOptions",
                            "Name": "AdditionalOptions",
                            "Category": "Command Line",
                        },
                        [
                            "StringProperty.DisplayName",
                            ["sys:String", "Additional Options"],
                        ],
                        [
                            "StringProperty.Description",
                            ["sys:String", "Additional Options"],
                        ],
                    ],
                ],
                [
                    "ItemType",
                    {"Name": rule.rule_name, "DisplayName": rule.display_name},
                ],
                [
                    "FileExtension",
                    {"Name": "*" + rule.extension, "ContentType": rule.rule_name},
                ],
                [
                    "ContentType",
                    {
                        "Name": rule.rule_name,
                        "DisplayName": "",
                        "ItemType": rule.rule_name,
                    },
                ],
            ]
        )
    easy_xml.WriteXmlIfChanged(content, xml_path, pretty=True, win32=True)


def _GetConfigurationAndPlatform(name, settings, spec):
    configuration = name.rsplit("_", 1)[0]
    platform = settings.get("msvs_configuration_platform", "Win32")
    if spec["toolset"] == "host" and platform == "arm64":
        platform = "x64"  # Host-only tools are always built for x64
    return (configuration, platform)


def _GetConfigurationCondition(name, settings, spec):
    return r"'$(Configuration)|$(Platform)'=='%s|%s'" % _GetConfigurationAndPlatform(
        name, settings, spec
    )


def _GetMSBuildProjectConfigurations(configurations, spec):
    group = ["ItemGroup", {"Label": "ProjectConfigurations"}]
    for (name, settings) in sorted(configurations.items()):
        configuration, platform = _GetConfigurationAndPlatform(name, settings, spec)
        designation = f"{configuration}|{platform}"
        group.append(
            [
                "ProjectConfiguration",
                {"Include": designation},
                ["Configuration", configuration],
                ["Platform", platform],
            ]
        )
    return [group]


def _GetMSBuildGlobalProperties(spec, version, guid, gyp_file_name):
    namespace = os.path.splitext(gyp_file_name)[0]
    properties = [
        [
            "PropertyGroup",
            {"Label": "Globals"},
            ["ProjectGuid", guid],
            ["Keyword", "Win32Proj"],
            ["RootNamespace", namespace],
            ["IgnoreWarnCompileDuplicatedFilename", "true"],
        ]
    ]

    if (
        os.environ.get("PROCESSOR_ARCHITECTURE") == "AMD64"
        or os.environ.get("PROCESSOR_ARCHITEW6432") == "AMD64"
    ):
        properties[0].append(["PreferredToolArchitecture", "x64"])

    if spec.get("msvs_target_platform_version"):
        target_platform_version = spec.get("msvs_target_platform_version")
        properties[0].append(["WindowsTargetPlatformVersion", target_platform_version])
        if spec.get("msvs_target_platform_minversion"):
            target_platform_minversion = spec.get("msvs_target_platform_minversion")
            properties[0].append(
                ["WindowsTargetPlatformMinVersion", target_platform_minversion]
            )
        else:
            properties[0].append(
                ["WindowsTargetPlatformMinVersion", target_platform_version]
            )

    if spec.get("msvs_enable_winrt"):
        properties[0].append(["DefaultLanguage", "en-US"])
        properties[0].append(["AppContainerApplication", "true"])
        if spec.get("msvs_application_type_revision"):
            app_type_revision = spec.get("msvs_application_type_revision")
            properties[0].append(["ApplicationTypeRevision", app_type_revision])
        else:
            properties[0].append(["ApplicationTypeRevision", "8.1"])
        if spec.get("msvs_enable_winphone"):
            properties[0].append(["ApplicationType", "Windows Phone"])
        else:
            properties[0].append(["ApplicationType", "Windows Store"])

    platform_name = None
    msvs_windows_sdk_version = None
    for configuration in spec["configurations"].values():
        platform_name = platform_name or _ConfigPlatform(configuration)
        msvs_windows_sdk_version = (
            msvs_windows_sdk_version
            or _ConfigWindowsTargetPlatformVersion(configuration, version)
        )
        if platform_name and msvs_windows_sdk_version:
            break
    if msvs_windows_sdk_version:
        properties[0].append(
            ["WindowsTargetPlatformVersion", str(msvs_windows_sdk_version)]
        )
    elif version.compatible_sdks:
        raise GypError(
            "%s requires any SDK of %s version, but none were found"
            % (version.description, version.compatible_sdks)
        )

    if platform_name == "ARM":
        properties[0].append(["WindowsSDKDesktopARMSupport", "true"])

    return properties


def _GetMSBuildConfigurationDetails(spec, build_file):
    properties = {}
    for name, settings in spec["configurations"].items():
        msbuild_attributes = _GetMSBuildAttributes(spec, settings, build_file)
        condition = _GetConfigurationCondition(name, settings, spec)
        character_set = msbuild_attributes.get("CharacterSet")
        vctools_version = msbuild_attributes.get("VCToolsVersion")
        config_type = msbuild_attributes.get("ConfigurationType")
        _AddConditionalProperty(properties, condition, "ConfigurationType", config_type)
        spectre_mitigation = msbuild_attributes.get('SpectreMitigation')
        if spectre_mitigation:
            _AddConditionalProperty(properties, condition, "SpectreMitigation",
                                    spectre_mitigation)
        if config_type == "Driver":
            _AddConditionalProperty(properties, condition, "DriverType", "WDM")
            _AddConditionalProperty(
                properties, condition, "TargetVersion", _ConfigTargetVersion(settings)
            )
        if character_set and "msvs_enable_winrt" not in spec:
            _AddConditionalProperty(
                properties, condition, "CharacterSet", character_set
            )
        if vctools_version and "msvs_enable_winrt" not in spec:
            _AddConditionalProperty(
                properties, condition, "VCToolsVersion", vctools_version
            )
    return _GetMSBuildPropertyGroup(spec, "Configuration", properties)


def _GetMSBuildLocalProperties(msbuild_toolset):
    # Currently the only local property we support is PlatformToolset
    properties = {}
    if msbuild_toolset:
        properties = [
            [
                "PropertyGroup",
                {"Label": "Locals"},
                ["PlatformToolset", msbuild_toolset],
            ]
        ]
    return properties


def _GetMSBuildPropertySheets(configurations, spec):
    user_props = r"$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
    additional_props = {}
    props_specified = False
    for name, settings in sorted(configurations.items()):
        configuration = _GetConfigurationCondition(name, settings, spec)
        if "msbuild_props" in settings:
            additional_props[configuration] = _FixPaths(settings["msbuild_props"])
            props_specified = True
        else:
            additional_props[configuration] = ""

    if not props_specified:
        return [
            [
                "ImportGroup",
                {"Label": "PropertySheets"},
                [
                    "Import",
                    {
                        "Project": user_props,
                        "Condition": "exists('%s')" % user_props,
                        "Label": "LocalAppDataPlatform",
                    },
                ],
            ]
        ]
    else:
        sheets = []
        for condition, props in additional_props.items():
            import_group = [
                "ImportGroup",
                {"Label": "PropertySheets", "Condition": condition},
                [
                    "Import",
                    {
                        "Project": user_props,
                        "Condition": "exists('%s')" % user_props,
                        "Label": "LocalAppDataPlatform",
                    },
                ],
            ]
            for props_file in props:
                import_group.append(["Import", {"Project": props_file}])
            sheets.append(import_group)
        return sheets


def _ConvertMSVSBuildAttributes(spec, config, build_file):
    config_type = _GetMSVSConfigurationType(spec, build_file)
    msvs_attributes = _GetMSVSAttributes(spec, config, config_type)
    msbuild_attributes = {}
    for a in msvs_attributes:
        if a in ["IntermediateDirectory", "OutputDirectory"]:
            directory = MSVSSettings.ConvertVCMacrosToMSBuild(msvs_attributes[a])
            if not directory.endswith("\\"):
                directory += "\\"
            msbuild_attributes[a] = directory
        elif a == "CharacterSet":
            msbuild_attributes[a] = _ConvertMSVSCharacterSet(msvs_attributes[a])
        elif a == "ConfigurationType":
            msbuild_attributes[a] = _ConvertMSVSConfigurationType(msvs_attributes[a])
        elif a == "SpectreMitigation":
            msbuild_attributes[a] = msvs_attributes[a]
        elif a == "VCToolsVersion":
            msbuild_attributes[a] = msvs_attributes[a]
        else:
            print("Warning: Do not know how to convert MSVS attribute " + a)
    return msbuild_attributes


def _ConvertMSVSCharacterSet(char_set):
    if char_set.isdigit():
        char_set = {"0": "MultiByte", "1": "Unicode", "2": "MultiByte"}[char_set]
    return char_set


def _ConvertMSVSConfigurationType(config_type):
    if config_type.isdigit():
        config_type = {
            "1": "Application",
            "2": "DynamicLibrary",
            "4": "StaticLibrary",
            "5": "Driver",
            "10": "Utility",
        }[config_type]
    return config_type


def _GetMSBuildAttributes(spec, config, build_file):
    if "msbuild_configuration_attributes" not in config:
        msbuild_attributes = _ConvertMSVSBuildAttributes(spec, config, build_file)

    else:
        config_type = _GetMSVSConfigurationType(spec, build_file)
        config_type = _ConvertMSVSConfigurationType(config_type)
        msbuild_attributes = config.get("msbuild_configuration_attributes", {})
        msbuild_attributes.setdefault("ConfigurationType", config_type)
        output_dir = msbuild_attributes.get(
            "OutputDirectory", "$(SolutionDir)$(Configuration)"
        )
        msbuild_attributes["OutputDirectory"] = _FixPath(output_dir) + "\\"
        if "IntermediateDirectory" not in msbuild_attributes:
            intermediate = _FixPath("$(Configuration)") + "\\"
            msbuild_attributes["IntermediateDirectory"] = intermediate
        if "CharacterSet" in msbuild_attributes:
            msbuild_attributes["CharacterSet"] = _ConvertMSVSCharacterSet(
                msbuild_attributes["CharacterSet"]
            )
    if "TargetName" not in msbuild_attributes:
        prefix = spec.get("product_prefix", "")
        product_name = spec.get("product_name", "$(ProjectName)")
        target_name = prefix + product_name
        msbuild_attributes["TargetName"] = target_name
    if "TargetExt" not in msbuild_attributes and "product_extension" in spec:
        ext = spec.get("product_extension")
        msbuild_attributes["TargetExt"] = "." + ext

    if spec.get("msvs_external_builder"):
        external_out_dir = spec.get("msvs_external_builder_out_dir", ".")
        msbuild_attributes["OutputDirectory"] = _FixPath(external_out_dir) + "\\"

    # Make sure that 'TargetPath' matches 'Lib.OutputFile' or 'Link.OutputFile'
    # (depending on the tool used) to avoid MSB8012 warning.
    msbuild_tool_map = {
        "executable": "Link",
        "shared_library": "Link",
        "loadable_module": "Link",
        "windows_driver": "Link",
        "static_library": "Lib",
    }
    msbuild_tool = msbuild_tool_map.get(spec["type"])
    if msbuild_tool:
        msbuild_settings = config["finalized_msbuild_settings"]
        out_file = msbuild_settings[msbuild_tool].get("OutputFile")
        if out_file:
            msbuild_attributes["TargetPath"] = _FixPath(out_file)
        target_ext = msbuild_settings[msbuild_tool].get("TargetExt")
        if target_ext:
            msbuild_attributes["TargetExt"] = target_ext

    return msbuild_attributes


def _GetMSBuildConfigurationGlobalProperties(spec, configurations, build_file):
    # TODO(jeanluc) We could optimize out the following and do it only if
    # there are actions.
    # TODO(jeanluc) Handle the equivalent of setting 'CYGWIN=nontsec'.
    new_paths = []
    cygwin_dirs = spec.get("msvs_cygwin_dirs", ["."])[0]
    if cygwin_dirs:
        cyg_path = "$(MSBuildProjectDirectory)\\%s\\bin\\" % _FixPath(cygwin_dirs)
        new_paths.append(cyg_path)
        # TODO(jeanluc) Change the convention to have both a cygwin_dir and a
        # python_dir.
        python_path = cyg_path.replace("cygwin\\bin", "python_26")
        new_paths.append(python_path)
        if new_paths:
            new_paths = "$(ExecutablePath);" + ";".join(new_paths)

    properties = {}
    for (name, configuration) in sorted(configurations.items()):
        condition = _GetConfigurationCondition(name, configuration, spec)
        attributes = _GetMSBuildAttributes(spec, configuration, build_file)
        msbuild_settings = configuration["finalized_msbuild_settings"]
        _AddConditionalProperty(
            properties, condition, "IntDir", attributes["IntermediateDirectory"]
        )
        _AddConditionalProperty(
            properties, condition, "OutDir", attributes["OutputDirectory"]
        )
        _AddConditionalProperty(
            properties, condition, "TargetName", attributes["TargetName"]
        )
        if "TargetExt" in attributes:
            _AddConditionalProperty(
                properties, condition, "TargetExt", attributes["TargetExt"]
            )

        if attributes.get("TargetPath"):
            _AddConditionalProperty(
                properties, condition, "TargetPath", attributes["TargetPath"]
            )
        if attributes.get("TargetExt"):
            _AddConditionalProperty(
                properties, condition, "TargetExt", attributes["TargetExt"]
            )

        if new_paths:
            _AddConditionalProperty(properties, condition, "ExecutablePath", new_paths)
        tool_settings = msbuild_settings.get("", {})
        for name, value in sorted(tool_settings.items()):
            formatted_value = _GetValueFormattedForMSBuild("", name, value)
            _AddConditionalProperty(properties, condition, name, formatted_value)
    return _GetMSBuildPropertyGroup(spec, None, properties)


def _AddConditionalProperty(properties, condition, name, value):
    """Adds a property / conditional value pair to a dictionary.

  Arguments:
    properties: The dictionary to be modified.  The key is the name of the
        property.  The value is itself a dictionary; its key is the value and
        the value a list of condition for which this value is true.
    condition: The condition under which the named property has the value.
    name: The name of the property.
    value: The value of the property.
  """
    if name not in properties:
        properties[name] = {}
    values = properties[name]
    if value not in values:
        values[value] = []
    conditions = values[value]
    conditions.append(condition)


# Regex for msvs variable references ( i.e. $(FOO) ).
MSVS_VARIABLE_REFERENCE = re.compile(r"\$\(([a-zA-Z_][a-zA-Z0-9_]*)\)")


def _GetMSBuildPropertyGroup(spec, label, properties):
    """Returns a PropertyGroup definition for the specified properties.

  Arguments:
    spec: The target project dict.
    label: An optional label for the PropertyGroup.
    properties: The dictionary to be converted.  The key is the name of the
        property.  The value is itself a dictionary; its key is the value and
        the value a list of condition for which this value is true.
  """
    group = ["PropertyGroup"]
    if label:
        group.append({"Label": label})
    num_configurations = len(spec["configurations"])

    def GetEdges(node):
        # Use a definition of edges such that user_of_variable -> used_varible.
        # This happens to be easier in this case, since a variable's
        # definition contains all variables it references in a single string.
        edges = set()
        for value in sorted(properties[node].keys()):
            # Add to edges all $(...) references to variables.
            #
            # Variable references that refer to names not in properties are excluded
            # These can exist for instance to refer built in definitions like
            # $(SolutionDir).
            #
            # Self references are ignored. Self reference is used in a few places to
            # append to the default value. I.e. PATH=$(PATH);other_path
            edges.update(
                {
                    v
                    for v in MSVS_VARIABLE_REFERENCE.findall(value)
                    if v in properties and v != node
                }
            )
        return edges

    properties_ordered = gyp.common.TopologicallySorted(properties.keys(), GetEdges)
    # Walk properties in the reverse of a topological sort on
    # user_of_variable -> used_variable as this ensures variables are
    # defined before they are used.
    # NOTE: reverse(topsort(DAG)) = topsort(reverse_edges(DAG))
    for name in reversed(properties_ordered):
        values = properties[name]
        for value, conditions in sorted(values.items()):
            if len(conditions) == num_configurations:
                # If the value is the same all configurations,
                # just add one unconditional entry.
                group.append([name, value])
            else:
                for condition in conditions:
                    group.append([name, {"Condition": condition}, value])
    return [group]


def _GetMSBuildToolSettingsSections(spec, configurations):
    groups = []
    for (name, configuration) in sorted(configurations.items()):
        msbuild_settings = configuration["finalized_msbuild_settings"]
        group = [
            "ItemDefinitionGroup",
            {"Condition": _GetConfigurationCondition(name, configuration, spec)},
        ]
        for tool_name, tool_settings in sorted(msbuild_settings.items()):
            # Skip the tool named '' which is a holder of global settings handled
            # by _GetMSBuildConfigurationGlobalProperties.
            if tool_name and tool_settings:
                tool = [tool_name]
                for name, value in sorted(tool_settings.items()):
                    formatted_value = _GetValueFormattedForMSBuild(
                        tool_name, name, value
                    )
                    tool.append([name, formatted_value])
                group.append(tool)
        groups.append(group)
    return groups


def _FinalizeMSBuildSettings(spec, configuration):
    if "msbuild_settings" in configuration:
        converted = False
        msbuild_settings = configuration["msbuild_settings"]
        MSVSSettings.ValidateMSBuildSettings(msbuild_settings)
    else:
        converted = True
        msvs_settings = configuration.get("msvs_settings", {})
        msbuild_settings = MSVSSettings.ConvertToMSBuildSettings(msvs_settings)
    include_dirs, midl_include_dirs, resource_include_dirs = _GetIncludeDirs(
        configuration
    )
    libraries = _GetLibraries(spec)
    library_dirs = _GetLibraryDirs(configuration)
    out_file, _, msbuild_tool = _GetOutputFilePathAndTool(spec, msbuild=True)
    target_ext = _GetOutputTargetExt(spec)
    defines = _GetDefines(configuration)
    if converted:
        # Visual Studio 2010 has TR1
        defines = [d for d in defines if d != "_HAS_TR1=0"]
        # Warn of ignored settings
        ignored_settings = ["msvs_tool_files"]
        for ignored_setting in ignored_settings:
            value = configuration.get(ignored_setting)
            if value:
                print(
                    "Warning: The automatic conversion to MSBuild does not handle "
                    "%s.  Ignoring setting of %s" % (ignored_setting, str(value))
                )

    defines = [_EscapeCppDefineForMSBuild(d) for d in defines]
    disabled_warnings = _GetDisabledWarnings(configuration)
    prebuild = configuration.get("msvs_prebuild")
    postbuild = configuration.get("msvs_postbuild")
    def_file = _GetModuleDefinition(spec)
    precompiled_header = configuration.get("msvs_precompiled_header")

    # Add the information to the appropriate tool
    # TODO(jeanluc) We could optimize and generate these settings only if
    # the corresponding files are found, e.g. don't generate ResourceCompile
    # if you don't have any resources.
    _ToolAppend(
        msbuild_settings, "ClCompile", "AdditionalIncludeDirectories", include_dirs
    )
    _ToolAppend(
        msbuild_settings, "Midl", "AdditionalIncludeDirectories", midl_include_dirs
    )
    _ToolAppend(
        msbuild_settings,
        "ResourceCompile",
        "AdditionalIncludeDirectories",
        resource_include_dirs,
    )
    # Add in libraries, note that even for empty libraries, we want this
    # set, to prevent inheriting default libraries from the environment.
    _ToolSetOrAppend(msbuild_settings, "Link", "AdditionalDependencies", libraries)
    _ToolAppend(msbuild_settings, "Link", "AdditionalLibraryDirectories", library_dirs)
    if out_file:
        _ToolAppend(
            msbuild_settings, msbuild_tool, "OutputFile", out_file, only_if_unset=True
        )
    if target_ext:
        _ToolAppend(
            msbuild_settings, msbuild_tool, "TargetExt", target_ext, only_if_unset=True
        )
    # Add defines.
    _ToolAppend(msbuild_settings, "ClCompile", "PreprocessorDefinitions", defines)
    _ToolAppend(msbuild_settings, "ResourceCompile", "PreprocessorDefinitions", defines)
    # Add disabled warnings.
    _ToolAppend(
        msbuild_settings, "ClCompile", "DisableSpecificWarnings", disabled_warnings
    )
    # Turn on precompiled headers if appropriate.
    if precompiled_header:
        precompiled_header = os.path.split(precompiled_header)[1]
        _ToolAppend(msbuild_settings, "ClCompile", "PrecompiledHeader", "Use")
        _ToolAppend(
            msbuild_settings, "ClCompile", "PrecompiledHeaderFile", precompiled_header
        )
        _ToolAppend(
            msbuild_settings, "ClCompile", "ForcedIncludeFiles", [precompiled_header]
        )
    else:
        _ToolAppend(msbuild_settings, "ClCompile", "PrecompiledHeader", "NotUsing")
    # Turn off WinRT compilation
    _ToolAppend(msbuild_settings, "ClCompile", "CompileAsWinRT", "false")
    # Turn on import libraries if appropriate
    if spec.get("msvs_requires_importlibrary"):
        _ToolAppend(msbuild_settings, "", "IgnoreImportLibrary", "false")
    # Loadable modules don't generate import libraries;
    # tell dependent projects to not expect one.
    if spec["type"] == "loadable_module":
        _ToolAppend(msbuild_settings, "", "IgnoreImportLibrary", "true")
    # Set the module definition file if any.
    if def_file:
        _ToolAppend(msbuild_settings, "Link", "ModuleDefinitionFile", def_file)
    configuration["finalized_msbuild_settings"] = msbuild_settings
    if prebuild:
        _ToolAppend(msbuild_settings, "PreBuildEvent", "Command", prebuild)
    if postbuild:
        _ToolAppend(msbuild_settings, "PostBuildEvent", "Command", postbuild)


def _GetValueFormattedForMSBuild(tool_name, name, value):
    if type(value) == list:
        # For some settings, VS2010 does not automatically extends the settings
        # TODO(jeanluc) Is this what we want?
        if name in [
            "AdditionalIncludeDirectories",
            "AdditionalLibraryDirectories",
            "AdditionalOptions",
            "DelayLoadDLLs",
            "DisableSpecificWarnings",
            "PreprocessorDefinitions",
        ]:
            value.append("%%(%s)" % name)
        # For most tools, entries in a list should be separated with ';' but some
        # settings use a space.  Check for those first.
        exceptions = {
            "ClCompile": ["AdditionalOptions"],
            "Link": ["AdditionalOptions"],
            "Lib": ["AdditionalOptions"],
        }
        char = " " if name in exceptions.get(tool_name, []) else ";"
        formatted_value = char.join(
            [MSVSSettings.ConvertVCMacrosToMSBuild(i) for i in value]
        )
    else:
        formatted_value = MSVSSettings.ConvertVCMacrosToMSBuild(value)
    return formatted_value


def _VerifySourcesExist(sources, root_dir):
    """Verifies that all source files exist on disk.

  Checks that all regular source files, i.e. not created at run time,
  exist on disk.  Missing files cause needless recompilation but no otherwise
  visible errors.

  Arguments:
    sources: A recursive list of Filter/file names.
    root_dir: The root directory for the relative path names.
  Returns:
    A list of source files that cannot be found on disk.
  """
    missing_sources = []
    for source in sources:
        if isinstance(source, MSVSProject.Filter):
            missing_sources.extend(_VerifySourcesExist(source.contents, root_dir))
        else:
            if "$" not in source:
                full_path = os.path.join(root_dir, source)
                if not os.path.exists(full_path):
                    missing_sources.append(full_path)
    return missing_sources


def _GetMSBuildSources(
    spec,
    sources,
    exclusions,
    rule_dependencies,
    extension_to_rule_name,
    actions_spec,
    sources_handled_by_action,
    list_excluded,
):
    groups = [
        "none",
        "masm",
        "midl",
        "include",
        "compile",
        "resource",
        "rule",
        "rule_dependency",
    ]
    grouped_sources = {}
    for g in groups:
        grouped_sources[g] = []

    _AddSources2(
        spec,
        sources,
        exclusions,
        grouped_sources,
        rule_dependencies,
        extension_to_rule_name,
        sources_handled_by_action,
        list_excluded,
    )
    sources = []
    for g in groups:
        if grouped_sources[g]:
            sources.append(["ItemGroup"] + grouped_sources[g])
    if actions_spec:
        sources.append(["ItemGroup"] + actions_spec)
    return sources


def _AddSources2(
    spec,
    sources,
    exclusions,
    grouped_sources,
    rule_dependencies,
    extension_to_rule_name,
    sources_handled_by_action,
    list_excluded,
):
    extensions_excluded_from_precompile = []
    for source in sources:
        if isinstance(source, MSVSProject.Filter):
            _AddSources2(
                spec,
                source.contents,
                exclusions,
                grouped_sources,
                rule_dependencies,
                extension_to_rule_name,
                sources_handled_by_action,
                list_excluded,
            )
        else:
            if source not in sources_handled_by_action:
                detail = []
                excluded_configurations = exclusions.get(source, [])
                if len(excluded_configurations) == len(spec["configurations"]):
                    detail.append(["ExcludedFromBuild", "true"])
                else:
                    for config_name, configuration in sorted(excluded_configurations):
                        condition = _GetConfigurationCondition(
                            config_name, configuration
                        )
                        detail.append(
                            ["ExcludedFromBuild", {"Condition": condition}, "true"]
                        )
                # Add precompile if needed
                for config_name, configuration in spec["configurations"].items():
                    precompiled_source = configuration.get(
                        "msvs_precompiled_source", ""
                    )
                    if precompiled_source != "":
                        precompiled_source = _FixPath(precompiled_source)
                        if not extensions_excluded_from_precompile:
                            # If the precompiled header is generated by a C source,
                            # we must not try to use it for C++ sources,
                            # and vice versa.
                            basename, extension = os.path.splitext(precompiled_source)
                            if extension == ".c":
                                extensions_excluded_from_precompile = [
                                    ".cc",
                                    ".cpp",
                                    ".cxx",
                                ]
                            else:
                                extensions_excluded_from_precompile = [".c"]

                    if precompiled_source == source:
                        condition = _GetConfigurationCondition(
                            config_name, configuration, spec
                        )
                        detail.append(
                            ["PrecompiledHeader", {"Condition": condition}, "Create"]
                        )
                    else:
                        # Turn off precompiled header usage for source files of a
                        # different type than the file that generated the
                        # precompiled header.
                        for extension in extensions_excluded_from_precompile:
                            if source.endswith(extension):
                                detail.append(["PrecompiledHeader", ""])
                                detail.append(["ForcedIncludeFiles", ""])

                group, element = _MapFileToMsBuildSourceType(
                    source,
                    rule_dependencies,
                    extension_to_rule_name,
                    _GetUniquePlatforms(spec),
                    spec["toolset"],
                )
                if group == "compile" and not os.path.isabs(source):
                    # Add an <ObjectFileName> value to support duplicate source
                    # file basenames, except for absolute paths to avoid paths
                    # with more than 260 characters.
                    file_name = os.path.splitext(source)[0] + ".obj"
                    if file_name.startswith("..\\"):
                        file_name = re.sub(r"^(\.\.\\)+", "", file_name)
                    elif file_name.startswith("$("):
                        file_name = re.sub(r"^\$\([^)]+\)\\", "", file_name)
                    detail.append(["ObjectFileName", "$(IntDir)\\" + file_name])
                grouped_sources[group].append([element, {"Include": source}] + detail)


def _GetMSBuildProjectReferences(project):
    references = []
    if project.dependencies:
        group = ["ItemGroup"]
        added_dependency_set = set()
        for dependency in project.dependencies:
            dependency_spec = dependency.spec
            should_skip_dep = False
            if project.spec["toolset"] == "target":
                if dependency_spec["toolset"] == "host":
                    if dependency_spec["type"] == "static_library":
                        should_skip_dep = True
            if dependency.name.startswith("run_"):
                should_skip_dep = False
            if should_skip_dep:
                continue

            canonical_name = dependency.name.replace("_host", "")
            added_dependency_set.add(canonical_name)
            guid = dependency.guid
            project_dir = os.path.split(project.path)[0]
            relative_path = gyp.common.RelativePath(dependency.path, project_dir)
            project_ref = [
                "ProjectReference",
                {"Include": relative_path},
                ["Project", guid],
                ["ReferenceOutputAssembly", "false"],
            ]
            for config in dependency.spec.get("configurations", {}).values():
                if config.get("msvs_use_library_dependency_inputs", 0):
                    project_ref.append(["UseLibraryDependencyInputs", "true"])
                    break
                # If it's disabled in any config, turn it off in the reference.
                if config.get("msvs_2010_disable_uldi_when_referenced", 0):
                    project_ref.append(["UseLibraryDependencyInputs", "false"])
                    break
            group.append(project_ref)
        references.append(group)
    return references


def _GenerateMSBuildProject(project, options, version, generator_flags, spec):
    spec = project.spec
    configurations = spec["configurations"]
    toolset = spec["toolset"]
    project_dir, project_file_name = os.path.split(project.path)
    gyp.common.EnsureDirExists(project.path)
    # Prepare list of sources and excluded sources.

    gyp_file = os.path.split(project.build_file)[1]
    sources, excluded_sources = _PrepareListOfSources(spec, generator_flags, gyp_file)
    # Add rules.
    actions_to_add = {}
    props_files_of_rules = set()
    targets_files_of_rules = set()
    rule_dependencies = set()
    extension_to_rule_name = {}
    list_excluded = generator_flags.get("msvs_list_excluded_files", True)
    platforms = _GetUniquePlatforms(spec)

    # Don't generate rules if we are using an external builder like ninja.
    if not spec.get("msvs_external_builder"):
        _GenerateRulesForMSBuild(
            project_dir,
            options,
            spec,
            sources,
            excluded_sources,
            props_files_of_rules,
            targets_files_of_rules,
            actions_to_add,
            rule_dependencies,
            extension_to_rule_name,
        )
    else:
        rules = spec.get("rules", [])
        _AdjustSourcesForRules(rules, sources, excluded_sources, True)

    sources, excluded_sources, excluded_idl = _AdjustSourcesAndConvertToFilterHierarchy(
        spec, options, project_dir, sources, excluded_sources, list_excluded, version
    )

    # Don't add actions if we are using an external builder like ninja.
    if not spec.get("msvs_external_builder"):
        _AddActions(actions_to_add, spec, project.build_file)
        _AddCopies(actions_to_add, spec)

        # NOTE: this stanza must appear after all actions have been decided.
        # Don't excluded sources with actions attached, or they won't run.
        excluded_sources = _FilterActionsFromExcluded(excluded_sources, actions_to_add)

    exclusions = _GetExcludedFilesFromBuild(spec, excluded_sources, excluded_idl)
    actions_spec, sources_handled_by_action = _GenerateActionsForMSBuild(
        spec, actions_to_add
    )

    _GenerateMSBuildFiltersFile(
        project.path + ".filters",
        sources,
        rule_dependencies,
        extension_to_rule_name,
        platforms,
        toolset,
    )
    missing_sources = _VerifySourcesExist(sources, project_dir)

    for configuration in configurations.values():
        _FinalizeMSBuildSettings(spec, configuration)

    # Add attributes to root element

    import_default_section = [
        ["Import", {"Project": r"$(VCTargetsPath)\Microsoft.Cpp.Default.props"}]
    ]
    import_cpp_props_section = [
        ["Import", {"Project": r"$(VCTargetsPath)\Microsoft.Cpp.props"}]
    ]
    import_cpp_targets_section = [
        ["Import", {"Project": r"$(VCTargetsPath)\Microsoft.Cpp.targets"}]
    ]
    import_masm_props_section = [
        ["Import", {"Project": r"$(VCTargetsPath)\BuildCustomizations\masm.props"}]
    ]
    import_masm_targets_section = [
        ["Import", {"Project": r"$(VCTargetsPath)\BuildCustomizations\masm.targets"}]
    ]
    import_marmasm_props_section = [
        ["Import", {"Project": r"$(VCTargetsPath)\BuildCustomizations\marmasm.props"}]
    ]
    import_marmasm_targets_section = [
        ["Import", {"Project": r"$(VCTargetsPath)\BuildCustomizations\marmasm.targets"}]
    ]
    macro_section = [["PropertyGroup", {"Label": "UserMacros"}]]

    content = [
        "Project",
        {
            "xmlns": "http://schemas.microsoft.com/developer/msbuild/2003",
            "ToolsVersion": version.ProjectVersion(),
            "DefaultTargets": "Build",
        },
    ]

    content += _GetMSBuildProjectConfigurations(configurations, spec)
    content += _GetMSBuildGlobalProperties(
        spec, version, project.guid, project_file_name
    )
    content += import_default_section
    content += _GetMSBuildConfigurationDetails(spec, project.build_file)
    if spec.get("msvs_enable_winphone"):
        content += _GetMSBuildLocalProperties("v120_wp81")
    else:
        content += _GetMSBuildLocalProperties(project.msbuild_toolset)
    content += import_cpp_props_section
    content += import_masm_props_section
    if "arm64" in platforms and toolset == "target":
        content += import_marmasm_props_section
    content += _GetMSBuildExtensions(props_files_of_rules)
    content += _GetMSBuildPropertySheets(configurations, spec)
    content += macro_section
    content += _GetMSBuildConfigurationGlobalProperties(
        spec, configurations, project.build_file
    )
    content += _GetMSBuildToolSettingsSections(spec, configurations)
    content += _GetMSBuildSources(
        spec,
        sources,
        exclusions,
        rule_dependencies,
        extension_to_rule_name,
        actions_spec,
        sources_handled_by_action,
        list_excluded,
    )
    content += _GetMSBuildProjectReferences(project)
    content += import_cpp_targets_section
    content += import_masm_targets_section
    if "arm64" in platforms and toolset == "target":
        content += import_marmasm_targets_section
    content += _GetMSBuildExtensionTargets(targets_files_of_rules)

    if spec.get("msvs_external_builder"):
        content += _GetMSBuildExternalBuilderTargets(spec)

    # TODO(jeanluc) File a bug to get rid of runas.  We had in MSVS:
    # has_run_as = _WriteMSVSUserFile(project.path, version, spec)

    easy_xml.WriteXmlIfChanged(content, project.path, pretty=True, win32=True)

    return missing_sources


def _GetMSBuildExternalBuilderTargets(spec):
    """Return a list of MSBuild targets for external builders.

  The "Build" and "Clean" targets are always generated.  If the spec contains
  'msvs_external_builder_clcompile_cmd', then the "ClCompile" target will also
  be generated, to support building selected C/C++ files.

  Arguments:
    spec: The gyp target spec.
  Returns:
    List of MSBuild 'Target' specs.
  """
    build_cmd = _BuildCommandLineForRuleRaw(
        spec, spec["msvs_external_builder_build_cmd"], False, False, False, False
    )
    build_target = ["Target", {"Name": "Build"}]
    build_target.append(["Exec", {"Command": build_cmd}])

    clean_cmd = _BuildCommandLineForRuleRaw(
        spec, spec["msvs_external_builder_clean_cmd"], False, False, False, False
    )
    clean_target = ["Target", {"Name": "Clean"}]
    clean_target.append(["Exec", {"Command": clean_cmd}])

    targets = [build_target, clean_target]

    if spec.get("msvs_external_builder_clcompile_cmd"):
        clcompile_cmd = _BuildCommandLineForRuleRaw(
            spec,
            spec["msvs_external_builder_clcompile_cmd"],
            False,
            False,
            False,
            False,
        )
        clcompile_target = ["Target", {"Name": "ClCompile"}]
        clcompile_target.append(["Exec", {"Command": clcompile_cmd}])
        targets.append(clcompile_target)

    return targets


def _GetMSBuildExtensions(props_files_of_rules):
    extensions = ["ImportGroup", {"Label": "ExtensionSettings"}]
    for props_file in props_files_of_rules:
        extensions.append(["Import", {"Project": props_file}])
    return [extensions]


def _GetMSBuildExtensionTargets(targets_files_of_rules):
    targets_node = ["ImportGroup", {"Label": "ExtensionTargets"}]
    for targets_file in sorted(targets_files_of_rules):
        targets_node.append(["Import", {"Project": targets_file}])
    return [targets_node]


def _GenerateActionsForMSBuild(spec, actions_to_add):
    """Add actions accumulated into an actions_to_add, merging as needed.

  Arguments:
    spec: the target project dict
    actions_to_add: dictionary keyed on input name, which maps to a list of
        dicts describing the actions attached to that input file.

  Returns:
    A pair of (action specification, the sources handled by this action).
  """
    sources_handled_by_action = OrderedSet()
    actions_spec = []
    for primary_input, actions in actions_to_add.items():
        if generator_supports_multiple_toolsets:
            primary_input = primary_input.replace(".exe", "_host.exe")
        inputs = OrderedSet()
        outputs = OrderedSet()
        descriptions = []
        commands = []
        for action in actions:

            def fixup_host_exe(i):
                if "$(OutDir)" in i:
                    i = i.replace(".exe", "_host.exe")
                return i

            if generator_supports_multiple_toolsets:
                action["inputs"] = [fixup_host_exe(i) for i in action["inputs"]]
            inputs.update(OrderedSet(action["inputs"]))
            outputs.update(OrderedSet(action["outputs"]))
            descriptions.append(action["description"])
            cmd = action["command"]
            if generator_supports_multiple_toolsets:
                cmd = cmd.replace(".exe", "_host.exe")
            # For most actions, add 'call' so that actions that invoke batch files
            # return and continue executing.  msbuild_use_call provides a way to
            # disable this but I have not seen any adverse effect from doing that
            # for everything.
            if action.get("msbuild_use_call", True):
                cmd = "call " + cmd
            commands.append(cmd)
        # Add the custom build action for one input file.
        description = ", and also ".join(descriptions)

        # We can't join the commands simply with && because the command line will
        # get too long. See also _AddActions: cygwin's setup_env mustn't be called
        # for every invocation or the command that sets the PATH will grow too
        # long.
        command = "\r\n".join(
            [c + "\r\nif %errorlevel% neq 0 exit /b %errorlevel%" for c in commands]
        )
        _AddMSBuildAction(
            spec,
            primary_input,
            inputs,
            outputs,
            command,
            description,
            sources_handled_by_action,
            actions_spec,
        )
    return actions_spec, sources_handled_by_action


def _AddMSBuildAction(
    spec,
    primary_input,
    inputs,
    outputs,
    cmd,
    description,
    sources_handled_by_action,
    actions_spec,
):
    command = MSVSSettings.ConvertVCMacrosToMSBuild(cmd)
    primary_input = _FixPath(primary_input)
    inputs_array = _FixPaths(inputs)
    outputs_array = _FixPaths(outputs)
    additional_inputs = ";".join([i for i in inputs_array if i != primary_input])
    outputs = ";".join(outputs_array)
    sources_handled_by_action.add(primary_input)
    action_spec = ["CustomBuild", {"Include": primary_input}]
    action_spec.extend(
        # TODO(jeanluc) 'Document' for all or just if as_sources?
        [
            ["FileType", "Document"],
            ["Command", command],
            ["Message", description],
            ["Outputs", outputs],
        ]
    )
    if additional_inputs:
        action_spec.append(["AdditionalInputs", additional_inputs])
    actions_spec.append(action_spec)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    # Copyright (c) 2013 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Notes:
#
# This is all roughly based on the Makefile system used by the Linux
# kernel, but is a non-recursive make -- we put the entire dependency
# graph in front of make and let it figure it out.
#
# The code below generates a separate .mk file for each target, but
# all are sourced by the top-level Makefile.  This means that all
# variables in .mk-files clobber one another.  Be careful to use :=
# where appropriate for immediate evaluation, and similarly to watch
# that you're not relying on a variable value to last between different
# .mk files.
#
# TODOs:
#
# Global settings and utility functions are currently stuffed in the
# toplevel Makefile.  It may make sense to generate some .mk files on
# the side to keep the files readable.


import os
import re
import subprocess
import gyp
import gyp.common
import gyp.xcode_emulation
from gyp.common import GetEnvironFallback

import hashlib

generator_default_variables = {
    "EXECUTABLE_PREFIX": "",
    "EXECUTABLE_SUFFIX": "",
    "STATIC_LIB_PREFIX": "lib",
    "SHARED_LIB_PREFIX": "lib",
    "STATIC_LIB_SUFFIX": ".a",
    "INTERMEDIATE_DIR": "$(obj).$(TOOLSET)/$(TARGET)/geni",
    "SHARED_INTERMEDIATE_DIR": "$(obj)/gen",
    "PRODUCT_DIR": "$(builddir)",
    "RULE_INPUT_ROOT": "%(INPUT_ROOT)s",  # This gets expanded by Python.
    "RULE_INPUT_DIRNAME": "%(INPUT_DIRNAME)s",  # This gets expanded by Python.
    "RULE_INPUT_PATH": "$(abspath $<)",
    "RULE_INPUT_EXT": "$(suffix $<)",
    "RULE_INPUT_NAME": "$(notdir $<)",
    "CONFIGURATION_NAME": "$(BUILDTYPE)",
}

# Make supports multiple toolsets
generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()

# Request sorted dependencies in the order from dependents to dependencies.
generator_wants_sorted_dependencies = False

# Placates pylint.
generator_additional_non_configuration_keys = []
generator_additional_path_sections = []
generator_extra_sources_for_rules = []
generator_filelist_paths = None


def CalculateVariables(default_variables, params):
    """Calculate additional variables for use in the build (called by gyp)."""
    flavor = gyp.common.GetFlavor(params)
    if flavor == "mac":
        default_variables.setdefault("OS", "mac")
        default_variables.setdefault("SHARED_LIB_SUFFIX", ".dylib")
        default_variables.setdefault(
            "SHARED_LIB_DIR", generator_default_variables["PRODUCT_DIR"]
        )
        default_variables.setdefault(
            "LIB_DIR", generator_default_variables["PRODUCT_DIR"]
        )

        # Copy additional generator configuration data from Xcode, which is shared
        # by the Mac Make generator.
        import gyp.generator.xcode as xcode_generator

        global generator_additional_non_configuration_keys
        generator_additional_non_configuration_keys = getattr(
            xcode_generator, "generator_additional_non_configuration_keys", []
        )
        global generator_additional_path_sections
        generator_additional_path_sections = getattr(
            xcode_generator, "generator_additional_path_sections", []
        )
        global generator_extra_sources_for_rules
        generator_extra_sources_for_rules = getattr(
            xcode_generator, "generator_extra_sources_for_rules", []
        )
        COMPILABLE_EXTENSIONS.update({".m": "objc", ".mm": "objcxx"})
    else:
        operating_system = flavor
        if flavor == "android":
            operating_system = "linux"  # Keep this legacy behavior for now.
        default_variables.setdefault("OS", operating_system)
        if flavor == "aix":
            default_variables.setdefault("SHARED_LIB_SUFFIX", ".a")
        elif flavor == "zos":
            default_variables.setdefault("SHARED_LIB_SUFFIX", ".x")
            COMPILABLE_EXTENSIONS.update({".pli": "pli"})
        else:
            default_variables.setdefault("SHARED_LIB_SUFFIX", ".so")
        default_variables.setdefault("SHARED_LIB_DIR", "$(builddir)/lib.$(TOOLSET)")
        default_variables.setdefault("LIB_DIR", "$(obj).$(TOOLSET)")


def CalculateGeneratorInputInfo(params):
    """Calculate the generator specific info that gets fed to input (called by
    gyp)."""
    generator_flags = params.get("generator_flags", {})
    android_ndk_version = generator_flags.get("android_ndk_version", None)
    # Android NDK requires a strict link order.
    if android_ndk_version:
        global generator_wants_sorted_dependencies
        generator_wants_sorted_dependencies = True

    output_dir = params["options"].generator_output or params["options"].toplevel_dir
    builddir_name = generator_flags.get("output_dir", "out")
    qualified_out_dir = os.path.normpath(
        os.path.join(output_dir, builddir_name, "gypfiles")
    )

    global generator_filelist_paths
    generator_filelist_paths = {
        "toplevel": params["options"].toplevel_dir,
        "qualified_out_dir": qualified_out_dir,
    }


# The .d checking code below uses these functions:
# wildcard, sort, foreach, shell, wordlist
# wildcard can handle spaces, the rest can't.
# Since I could find no way to make foreach work with spaces in filenames
# correctly, the .d files have spaces replaced with another character. The .d
# file for
#     Chromium\ Framework.framework/foo
# is for example
#     out/Release/.deps/out/Release/Chromium?Framework.framework/foo
# This is the replacement character.
SPACE_REPLACEMENT = "?"


LINK_COMMANDS_LINUX = """\
quiet_cmd_alink = AR($(TOOLSET)) $@
cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^)

quiet_cmd_alink_thin = AR($(TOOLSET)) $@
cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^)

# Due to circular dependencies between libraries :(, we wrap the
# special "figure out circular dependencies" flags around the entire
# input list during linking.
quiet_cmd_link = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) -o $@ $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,--start-group $(LD_INPUTS) $(LIBS) -Wl,--end-group

# Note: this does not handle spaces in paths
define xargs
  $(1) $(word 1,$(2))
$(if $(word 2,$(2)),$(call xargs,$(1),$(wordlist 2,$(words $(2)),$(2))))
endef

define write-to-file
  @: >$(1)
$(call xargs,@printf "%s\\n" >>$(1),$(2))
endef

OBJ_FILE_LIST := ar-file-list

define create_archive
        rm -f $(1) $(1).$(OBJ_FILE_LIST); mkdir -p `dirname $(1)`
        $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2)))
        $(AR.$(TOOLSET)) crs $(1) @$(1).$(OBJ_FILE_LIST)
endef

define create_thin_archive
        rm -f $(1) $(OBJ_FILE_LIST); mkdir -p `dirname $(1)`
        $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2)))
        $(AR.$(TOOLSET)) crsT $(1) @$(1).$(OBJ_FILE_LIST)
endef

# We support two kinds of shared objects (.so):
# 1) shared_library, which is just bundling together many dependent libraries
# into a link line.
# 2) loadable_module, which is generating a module intended for dlopen().
#
# They differ only slightly:
# In the former case, we want to package all dependent code into the .so.
# In the latter case, we want to package just the API exposed by the
# outermost module.
# This means shared_library uses --whole-archive, while loadable_module doesn't.
# (Note that --whole-archive is incompatible with the --start-group used in
# normal linking.)

# Other shared-object link notes:
# - Set SONAME to the library filename so our binaries don't reference
# the local, absolute paths used on the link command-line.
quiet_cmd_solink = SOLINK($(TOOLSET)) $@
cmd_solink = $(LINK.$(TOOLSET)) -o $@ -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)

quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) -o $@ -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS)
"""  # noqa: E501

LINK_COMMANDS_MAC = """\
quiet_cmd_alink = LIBTOOL-STATIC $@
cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %.o,$^)

quiet_cmd_link = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)

quiet_cmd_solink = SOLINK($(TOOLSET)) $@
cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)

quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
"""  # noqa: E501

LINK_COMMANDS_ANDROID = """\
quiet_cmd_alink = AR($(TOOLSET)) $@
cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^)

quiet_cmd_alink_thin = AR($(TOOLSET)) $@
cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^)

# Note: this does not handle spaces in paths
define xargs
  $(1) $(word 1,$(2))
$(if $(word 2,$(2)),$(call xargs,$(1),$(wordlist 2,$(words $(2)),$(2))))
endef

define write-to-file
  @: >$(1)
$(call xargs,@printf "%s\\n" >>$(1),$(2))
endef

OBJ_FILE_LIST := ar-file-list

define create_archive
        rm -f $(1) $(1).$(OBJ_FILE_LIST); mkdir -p `dirname $(1)`
        $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2)))
        $(AR.$(TOOLSET)) crs $(1) @$(1).$(OBJ_FILE_LIST)
endef

define create_thin_archive
        rm -f $(1) $(OBJ_FILE_LIST); mkdir -p `dirname $(1)`
        $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2)))
        $(AR.$(TOOLSET)) crsT $(1) @$(1).$(OBJ_FILE_LIST)
endef

# Due to circular dependencies between libraries :(, we wrap the
# special "figure out circular dependencies" flags around the entire
# input list during linking.
quiet_cmd_link = LINK($(TOOLSET)) $@
quiet_cmd_link_host = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS)
cmd_link_host = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS)

# Other shared-object link notes:
# - Set SONAME to the library filename so our binaries don't reference
# the local, absolute paths used on the link command-line.
quiet_cmd_solink = SOLINK($(TOOLSET)) $@
cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)

quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS)
quiet_cmd_solink_module_host = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module_host = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
"""  # noqa: E501


LINK_COMMANDS_AIX = """\
quiet_cmd_alink = AR($(TOOLSET)) $@
cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) -X32_64 crs $@ $(filter %.o,$^)

quiet_cmd_alink_thin = AR($(TOOLSET)) $@
cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) -X32_64 crs $@ $(filter %.o,$^)

quiet_cmd_link = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)

quiet_cmd_solink = SOLINK($(TOOLSET)) $@
cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)

quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
"""  # noqa: E501


LINK_COMMANDS_OS400 = """\
quiet_cmd_alink = AR($(TOOLSET)) $@
cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) -X64 crs $@ $(filter %.o,$^)

quiet_cmd_alink_thin = AR($(TOOLSET)) $@
cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) -X64 crs $@ $(filter %.o,$^)

quiet_cmd_link = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)

quiet_cmd_solink = SOLINK($(TOOLSET)) $@
cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)

quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
"""  # noqa: E501


LINK_COMMANDS_OS390 = """\
quiet_cmd_alink = AR($(TOOLSET)) $@
cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^)

quiet_cmd_alink_thin = AR($(TOOLSET)) $@
cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^)

quiet_cmd_link = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)

quiet_cmd_solink = SOLINK($(TOOLSET)) $@
cmd_solink = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)

quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
"""  # noqa: E501


# Header of toplevel Makefile.
# This should go into the build tree, but it's easier to keep it here for now.
SHARED_HEADER = (
    """\
# We borrow heavily from the kernel build setup, though we are simpler since
# we don't have Kconfig tweaking settings on us.

# The implicit make rules have it looking for RCS files, among other things.
# We instead explicitly write all the rules we care about.
# It's even quicker (saves ~200ms) to pass -r on the command line.
MAKEFLAGS=-r

# The source directory tree.
srcdir := %(srcdir)s
abs_srcdir := $(abspath $(srcdir))

# The name of the builddir.
builddir_name ?= %(builddir)s

# The V=1 flag on command line makes us verbosely print command lines.
ifdef V
  quiet=
else
  quiet=quiet_
endif

# Specify BUILDTYPE=Release on the command line for a release build.
BUILDTYPE ?= %(default_configuration)s

# Directory all our build output goes into.
# Note that this must be two directories beneath src/ for unit tests to pass,
# as they reach into the src/ directory for data with relative paths.
builddir ?= $(builddir_name)/$(BUILDTYPE)
abs_builddir := $(abspath $(builddir))
depsdir := $(builddir)/.deps

# Object output directory.
obj := $(builddir)/obj
abs_obj := $(abspath $(obj))

# We build up a list of every single one of the targets so we can slurp in the
# generated dependency rule Makefiles in one pass.
all_deps :=

%(make_global_settings)s

CC.target ?= %(CC.target)s
CFLAGS.target ?= $(CPPFLAGS) $(CFLAGS)
CXX.target ?= %(CXX.target)s
CXXFLAGS.target ?= $(CPPFLAGS) $(CXXFLAGS)
LINK.target ?= %(LINK.target)s
LDFLAGS.target ?= $(LDFLAGS)
AR.target ?= $(AR)
PLI.target ?= %(PLI.target)s

# C++ apps need to be linked with g++.
LINK ?= $(CXX.target)

# TODO(evan): move all cross-compilation logic to gyp-time so we don't need
# to replicate this environment fallback in make as well.
CC.host ?= %(CC.host)s
CFLAGS.host ?= $(CPPFLAGS_host) $(CFLAGS_host)
CXX.host ?= %(CXX.host)s
CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host)
LINK.host ?= %(LINK.host)s
LDFLAGS.host ?= $(LDFLAGS_host)
AR.host ?= %(AR.host)s
PLI.host ?= %(PLI.host)s

# Define a dir function that can handle spaces.
# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions
# "leading spaces cannot appear in the text of the first argument as written.
# These characters can be put into the argument value by variable substitution."
empty :=
space := $(empty) $(empty)

# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces
replace_spaces = $(subst $(space),"""
    + SPACE_REPLACEMENT
    + """,$1)
unreplace_spaces = $(subst """
    + SPACE_REPLACEMENT
    + """,$(space),$1)
dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1)))

# Flags to make gcc output dependency info.  Note that you need to be
# careful here to use the flags that ccache and distcc can understand.
# We write to a dep file on the side first and then rename at the end
# so we can't end up with a broken dep file.
depfile = $(depsdir)/$(call replace_spaces,$@).d
DEPFLAGS = %(makedep_args)s -MF $(depfile).raw

# We have to fixup the deps output in a few ways.
# (1) the file output should mention the proper .o file.
# ccache or distcc lose the path to the target, so we convert a rule of
# the form:
#   foobar.o: DEP1 DEP2
# into
#   path/to/foobar.o: DEP1 DEP2
# (2) we want missing files not to cause us to fail to build.
# We want to rewrite
#   foobar.o: DEP1 DEP2 \\
#               DEP3
# to
#   DEP1:
#   DEP2:
#   DEP3:
# so if the files are missing, they're just considered phony rules.
# We have to do some pretty insane escaping to get those backslashes
# and dollar signs past make, the shell, and sed at the same time.
# Doesn't work with spaces, but that's fine: .d files have spaces in
# their names replaced with other characters."""
    r"""
define fixup_dep
# The depfile may not exist if the input file didn't have any #includes.
touch $(depfile).raw
# Fixup path as in (1).
sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile)
# Add extra rules as in (2).
# We remove slashes and replace spaces with new lines;
# remove blank lines;
# delete the first line and append a colon to the remaining lines.
sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\
  grep -v '^$$'                             |\
  sed -e 1d -e 's|$$|:|'                     \
    >> $(depfile)
rm $(depfile).raw
endef
"""
    """
# Command definitions:
# - cmd_foo is the actual command to run;
# - quiet_cmd_foo is the brief-output summary of the command.

quiet_cmd_cc = CC($(TOOLSET)) $@
cmd_cc = $(CC.$(TOOLSET)) -o $@ $< $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c

quiet_cmd_cxx = CXX($(TOOLSET)) $@
cmd_cxx = $(CXX.$(TOOLSET)) -o $@ $< $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c
%(extra_commands)s
quiet_cmd_touch = TOUCH $@
cmd_touch = touch $@

quiet_cmd_copy = COPY $@
# send stderr to /dev/null to ignore messages when linking directories.
cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp %(copy_archive_args)s "$<" "$@")

quiet_cmd_symlink = SYMLINK $@
cmd_symlink = ln -sf "$<" "$@"

%(link_commands)s
"""  # noqa: E501
    r"""
# Define an escape_quotes function to escape single quotes.
# This allows us to handle quotes properly as long as we always use
# use single quotes and escape_quotes.
escape_quotes = $(subst ','\'',$(1))
# This comment is here just to include a ' to unconfuse syntax highlighting.
# Define an escape_vars function to escape '$' variable syntax.
# This allows us to read/write command lines with shell variables (e.g.
# $LD_LIBRARY_PATH), without triggering make substitution.
escape_vars = $(subst $$,$$$$,$(1))
# Helper that expands to a shell command to echo a string exactly as it is in
# make. This uses printf instead of echo because printf's behaviour with respect
# to escape sequences is more portable than echo's across different shells
# (e.g., dash, bash).
exact_echo = printf '%%s\n' '$(call escape_quotes,$(1))'
"""
    """
# Helper to compare the command we're about to run against the command
# we logged the last time we ran the command.  Produces an empty
# string (false) when the commands match.
# Tricky point: Make has no string-equality test function.
# The kernel uses the following, but it seems like it would have false
# positives, where one string reordered its arguments.
#   arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \\
#                       $(filter-out $(cmd_$@), $(cmd_$(1))))
# We instead substitute each for the empty string into the other, and
# say they're equal if both substitutions produce the empty string.
# .d files contain """
    + SPACE_REPLACEMENT
    + """ instead of spaces, take that into account.
command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\\
                       $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1))))

# Helper that is non-empty when a prerequisite changes.
# Normally make does this implicitly, but we force rules to always run
# so we can check their command lines.
#   $? -- new prerequisites
#   $| -- order-only dependencies
prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?))

# Helper that executes all postbuilds until one fails.
define do_postbuilds
  @E=0;\\
  for p in $(POSTBUILDS); do\\
    eval $$p;\\
    E=$$?;\\
    if [ $$E -ne 0 ]; then\\
      break;\\
    fi;\\
  done;\\
  if [ $$E -ne 0 ]; then\\
    rm -rf "$@";\\
    exit $$E;\\
  fi
endef

# do_cmd: run a command via the above cmd_foo names, if necessary.
# Should always run for a given target to handle command-line changes.
# Second argument, if non-zero, makes it do asm/C/C++ dependency munging.
# Third argument, if non-zero, makes it do POSTBUILDS processing.
# Note: We intentionally do NOT call dirx for depfile, since it contains """
    + SPACE_REPLACEMENT
    + """ for
# spaces already and dirx strips the """
    + SPACE_REPLACEMENT
    + """ characters.
define do_cmd
$(if $(or $(command_changed),$(prereq_changed)),
  @$(call exact_echo,  $($(quiet)cmd_$(1)))
  @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))"
  $(if $(findstring flock,$(word %(flock_index)d,$(cmd_$1))),
    @$(cmd_$(1))
    @echo "  $(quiet_cmd_$(1)): Finished",
    @$(cmd_$(1))
  )
  @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile)
  @$(if $(2),$(fixup_dep))
  $(if $(and $(3), $(POSTBUILDS)),
    $(call do_postbuilds)
  )
)
endef

# Declare the "%(default_target)s" target first so it is the default,
# even though we don't have the deps yet.
.PHONY: %(default_target)s
%(default_target)s:

# make looks for ways to re-generate included makefiles, but in our case, we
# don't have a direct way. Explicitly telling make that it has nothing to do
# for them makes it go faster.
%%.d: ;

# Use FORCE_DO_CMD to force a target to run.  Should be coupled with
# do_cmd.
.PHONY: FORCE_DO_CMD
FORCE_DO_CMD:

"""  # noqa: E501
)

SHARED_HEADER_MAC_COMMANDS = """
quiet_cmd_objc = CXX($(TOOLSET)) $@
cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<

quiet_cmd_objcxx = CXX($(TOOLSET)) $@
cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<

# Commands for precompiled header files.
quiet_cmd_pch_c = CXX($(TOOLSET)) $@
cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_pch_cc = CXX($(TOOLSET)) $@
cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_pch_m = CXX($(TOOLSET)) $@
cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<
quiet_cmd_pch_mm = CXX($(TOOLSET)) $@
cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<

# gyp-mac-tool is written next to the root Makefile by gyp.
# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd
# already.
quiet_cmd_mac_tool = MACTOOL $(4) $<
cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@"

quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@
cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4)

quiet_cmd_infoplist = INFOPLIST $@
cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@"
"""  # noqa: E501


def WriteRootHeaderSuffixRules(writer):
    extensions = sorted(COMPILABLE_EXTENSIONS.keys(), key=str.lower)

    writer.write("# Suffix rules, putting all outputs into $(obj).\n")
    for ext in extensions:
        writer.write("$(obj).$(TOOLSET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD\n" % ext)
        writer.write("\t@$(call do_cmd,%s,1)\n" % COMPILABLE_EXTENSIONS[ext])

    writer.write("\n# Try building from generated source, too.\n")
    for ext in extensions:
        writer.write(
            "$(obj).$(TOOLSET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD\n" % ext
        )
        writer.write("\t@$(call do_cmd,%s,1)\n" % COMPILABLE_EXTENSIONS[ext])
    writer.write("\n")
    for ext in extensions:
        writer.write("$(obj).$(TOOLSET)/%%.o: $(obj)/%%%s FORCE_DO_CMD\n" % ext)
        writer.write("\t@$(call do_cmd,%s,1)\n" % COMPILABLE_EXTENSIONS[ext])
    writer.write("\n")


SHARED_HEADER_OS390_COMMANDS = """
PLIFLAGS.target ?= -qlp=64 -qlimits=extname=31  $(PLIFLAGS)
PLIFLAGS.host ?= -qlp=64 -qlimits=extname=31 $(PLIFLAGS)

quiet_cmd_pli = PLI($(TOOLSET)) $@
cmd_pli = $(PLI.$(TOOLSET)) $(GYP_PLIFLAGS) $(PLIFLAGS.$(TOOLSET)) -c $< && \
          if [ -f $(notdir $@) ]; then /bin/cp $(notdir $@) $@; else true; fi
"""

SHARED_HEADER_SUFFIX_RULES_COMMENT1 = """\
# Suffix rules, putting all outputs into $(obj).
"""


SHARED_HEADER_SUFFIX_RULES_COMMENT2 = """\
# Try building from generated source, too.
"""


SHARED_FOOTER = """\
# "all" is a concatenation of the "all" targets from all the included
# sub-makefiles. This is just here to clarify.
all:

# Add in dependency-tracking rules.  $(all_deps) is the list of every single
# target in our tree. Only consider the ones with .d (dependency) info:
d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d))
ifneq ($(d_files),)
  include $(d_files)
endif
"""

header = """\
# This file is generated by gyp; do not edit.

"""

# Maps every compilable file extension to the do_cmd that compiles it.
COMPILABLE_EXTENSIONS = {
    ".c": "cc",
    ".cc": "cxx",
    ".cpp": "cxx",
    ".cxx": "cxx",
    ".s": "cc",
    ".S": "cc",
}


def Compilable(filename):
    """Return true if the file is compilable (should be in OBJS)."""
    return any(res for res in (filename.endswith(e) for e in COMPILABLE_EXTENSIONS))


def Linkable(filename):
    """Return true if the file is linkable (should be on the link line)."""
    return filename.endswith(".o")


def Target(filename):
    """Translate a compilable filename to its .o target."""
    return os.path.splitext(filename)[0] + ".o"


def EscapeShellArgument(s):
    """Quotes an argument so that it will be interpreted literally by a POSIX
    shell. Taken from
    http://stackoverflow.com/questions/35817/whats-the-best-way-to-escape-ossystem-calls-in-python
    """
    return "'" + s.replace("'", "'\\''") + "'"


def EscapeMakeVariableExpansion(s):
    """Make has its own variable expansion syntax using $. We must escape it for
    string to be interpreted literally."""
    return s.replace("$", "$$")


def EscapeCppDefine(s):
    """Escapes a CPP define so that it will reach the compiler unaltered."""
    s = EscapeShellArgument(s)
    s = EscapeMakeVariableExpansion(s)
    # '#' characters must be escaped even embedded in a string, else Make will
    # treat it as the start of a comment.
    return s.replace("#", r"\#")


def QuoteIfNecessary(string):
    """TODO: Should this ideally be replaced with one or more of the above
    functions?"""
    if '"' in string:
        string = '"' + string.replace('"', '\\"') + '"'
    return string


def StringToMakefileVariable(string):
    """Convert a string to a value that is acceptable as a make variable name."""
    return re.sub("[^a-zA-Z0-9_]", "_", string)


srcdir_prefix = ""


def Sourceify(path):
    """Convert a path to its source directory form."""
    if "$(" in path:
        return path
    if os.path.isabs(path):
        return path
    return srcdir_prefix + path


def QuoteSpaces(s, quote=r"\ "):
    return s.replace(" ", quote)


def SourceifyAndQuoteSpaces(path):
    """Convert a path to its source directory form and quote spaces."""
    return QuoteSpaces(Sourceify(path))


# Map from qualified target to path to output.
target_outputs = {}
# Map from qualified target to any linkable output.  A subset
# of target_outputs.  E.g. when mybinary depends on liba, we want to
# include liba in the linker line; when otherbinary depends on
# mybinary, we just want to build mybinary first.
target_link_deps = {}


class MakefileWriter:
    """MakefileWriter packages up the writing of one target-specific foobar.mk.

    Its only real entry point is Write(), and is mostly used for namespacing.
    """

    def __init__(self, generator_flags, flavor):
        self.generator_flags = generator_flags
        self.flavor = flavor

        self.suffix_rules_srcdir = {}
        self.suffix_rules_objdir1 = {}
        self.suffix_rules_objdir2 = {}

        # Generate suffix rules for all compilable extensions.
        for ext in COMPILABLE_EXTENSIONS:
            # Suffix rules for source folder.
            self.suffix_rules_srcdir.update(
                {
                    ext: (
                        """\
$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD
\t@$(call do_cmd,%s,1)
"""
                        % (ext, COMPILABLE_EXTENSIONS[ext])
                    )
                }
            )

            # Suffix rules for generated source files.
            self.suffix_rules_objdir1.update(
                {
                    ext: (
                        """\
$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD
\t@$(call do_cmd,%s,1)
"""
                        % (ext, COMPILABLE_EXTENSIONS[ext])
                    )
                }
            )
            self.suffix_rules_objdir2.update(
                {
                    ext: (
                        """\
$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
\t@$(call do_cmd,%s,1)
"""
                        % (ext, COMPILABLE_EXTENSIONS[ext])
                    )
                }
            )

    def Write(
        self, qualified_target, base_path, output_filename, spec, configs, part_of_all
    ):
        """The main entry point: writes a .mk file for a single target.

        Arguments:
          qualified_target: target we're generating
          base_path: path relative to source root we're building in, used to resolve
                     target-relative paths
          output_filename: output .mk file name to write
          spec, configs: gyp info
          part_of_all: flag indicating this target is part of 'all'
        """
        gyp.common.EnsureDirExists(output_filename)

        self.fp = open(output_filename, "w")

        self.fp.write(header)

        self.qualified_target = qualified_target
        self.path = base_path
        self.target = spec["target_name"]
        self.type = spec["type"]
        self.toolset = spec["toolset"]

        self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
        if self.flavor == "mac":
            self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
        else:
            self.xcode_settings = None

        deps, link_deps = self.ComputeDeps(spec)

        # Some of the generation below can add extra output, sources, or
        # link dependencies.  All of the out params of the functions that
        # follow use names like extra_foo.
        extra_outputs = []
        extra_sources = []
        extra_link_deps = []
        extra_mac_bundle_resources = []
        mac_bundle_deps = []

        if self.is_mac_bundle:
            self.output = self.ComputeMacBundleOutput(spec)
            self.output_binary = self.ComputeMacBundleBinaryOutput(spec)
        else:
            self.output = self.output_binary = self.ComputeOutput(spec)

        self.is_standalone_static_library = bool(
            spec.get("standalone_static_library", 0)
        )
        self._INSTALLABLE_TARGETS = ("executable", "loadable_module", "shared_library")
        if self.is_standalone_static_library or self.type in self._INSTALLABLE_TARGETS:
            self.alias = os.path.basename(self.output)
            install_path = self._InstallableTargetInstallPath()
        else:
            self.alias = self.output
            install_path = self.output

        self.WriteLn("TOOLSET := " + self.toolset)
        self.WriteLn("TARGET := " + self.target)

        # Actions must come first, since they can generate more OBJs for use below.
        if "actions" in spec:
            self.WriteActions(
                spec["actions"],
                extra_sources,
                extra_outputs,
                extra_mac_bundle_resources,
                part_of_all,
            )

        # Rules must be early like actions.
        if "rules" in spec:
            self.WriteRules(
                spec["rules"],
                extra_sources,
                extra_outputs,
                extra_mac_bundle_resources,
                part_of_all,
            )

        if "copies" in spec:
            self.WriteCopies(spec["copies"], extra_outputs, part_of_all)

        # Bundle resources.
        if self.is_mac_bundle:
            all_mac_bundle_resources = (
                spec.get("mac_bundle_resources", []) + extra_mac_bundle_resources
            )
            self.WriteMacBundleResources(all_mac_bundle_resources, mac_bundle_deps)
            self.WriteMacInfoPlist(mac_bundle_deps)

        # Sources.
        all_sources = spec.get("sources", []) + extra_sources
        if all_sources:
            self.WriteSources(
                configs,
                deps,
                all_sources,
                extra_outputs,
                extra_link_deps,
                part_of_all,
                gyp.xcode_emulation.MacPrefixHeader(
                    self.xcode_settings,
                    lambda p: Sourceify(self.Absolutify(p)),
                    self.Pchify,
                ),
            )
            sources = [x for x in all_sources if Compilable(x)]
            if sources:
                self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT1)
                extensions = {os.path.splitext(s)[1] for s in sources}
                for ext in extensions:
                    if ext in self.suffix_rules_srcdir:
                        self.WriteLn(self.suffix_rules_srcdir[ext])
                self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT2)
                for ext in extensions:
                    if ext in self.suffix_rules_objdir1:
                        self.WriteLn(self.suffix_rules_objdir1[ext])
                for ext in extensions:
                    if ext in self.suffix_rules_objdir2:
                        self.WriteLn(self.suffix_rules_objdir2[ext])
                self.WriteLn("# End of this set of suffix rules")

                # Add dependency from bundle to bundle binary.
                if self.is_mac_bundle:
                    mac_bundle_deps.append(self.output_binary)

        self.WriteTarget(
            spec,
            configs,
            deps,
            extra_link_deps + link_deps,
            mac_bundle_deps,
            extra_outputs,
            part_of_all,
        )

        # Update global list of target outputs, used in dependency tracking.
        target_outputs[qualified_target] = install_path

        # Update global list of link dependencies.
        if self.type in ("static_library", "shared_library"):
            target_link_deps[qualified_target] = self.output_binary

        # Currently any versions have the same effect, but in future the behavior
        # could be different.
        if self.generator_flags.get("android_ndk_version", None):
            self.WriteAndroidNdkModuleRule(self.target, all_sources, link_deps)

        self.fp.close()

    def WriteSubMake(self, output_filename, makefile_path, targets, build_dir):
        """Write a "sub-project" Makefile.

        This is a small, wrapper Makefile that calls the top-level Makefile to build
        the targets from a single gyp file (i.e. a sub-project).

        Arguments:
          output_filename: sub-project Makefile name to write
          makefile_path: path to the top-level Makefile
          targets: list of "all" targets for this sub-project
          build_dir: build output directory, relative to the sub-project
        """
        gyp.common.EnsureDirExists(output_filename)
        self.fp = open(output_filename, "w")
        self.fp.write(header)
        # For consistency with other builders, put sub-project build output in the
        # sub-project dir (see test/subdirectory/gyptest-subdir-all.py).
        self.WriteLn(
            "export builddir_name ?= %s"
            % os.path.join(os.path.dirname(output_filename), build_dir)
        )
        self.WriteLn(".PHONY: all")
        self.WriteLn("all:")
        if makefile_path:
            makefile_path = " -C " + makefile_path
        self.WriteLn("\t$(MAKE){} {}".format(makefile_path, " ".join(targets)))
        self.fp.close()

    def WriteActions(
        self,
        actions,
        extra_sources,
        extra_outputs,
        extra_mac_bundle_resources,
        part_of_all,
    ):
        """Write Makefile code for any 'actions' from the gyp input.

        extra_sources: a list that will be filled in with newly generated source
                       files, if any
        extra_outputs: a list that will be filled in with any outputs of these
                       actions (used to make other pieces dependent on these
                       actions)
        part_of_all: flag indicating this target is part of 'all'
        """
        env = self.GetSortedXcodeEnv()
        for action in actions:
            name = StringToMakefileVariable(
                "{}_{}".format(self.qualified_target, action["action_name"])
            )
            self.WriteLn('### Rules for action "%s":' % action["action_name"])
            inputs = action["inputs"]
            outputs = action["outputs"]

            # Build up a list of outputs.
            # Collect the output dirs we'll need.
            dirs = set()
            for out in outputs:
                dir = os.path.split(out)[0]
                if dir:
                    dirs.add(dir)
            if int(action.get("process_outputs_as_sources", False)):
                extra_sources += outputs
            if int(action.get("process_outputs_as_mac_bundle_resources", False)):
                extra_mac_bundle_resources += outputs

            # Write the actual command.
            action_commands = action["action"]
            if self.flavor == "mac":
                action_commands = [
                    gyp.xcode_emulation.ExpandEnvVars(command, env)
                    for command in action_commands
                ]
            command = gyp.common.EncodePOSIXShellList(action_commands)
            if "message" in action:
                self.WriteLn(
                    "quiet_cmd_{} = ACTION {} $@".format(name, action["message"])
                )
            else:
                self.WriteLn(f"quiet_cmd_{name} = ACTION {name} $@")
            if len(dirs) > 0:
                command = "mkdir -p %s" % " ".join(dirs) + "; " + command

            cd_action = "cd %s; " % Sourceify(self.path or ".")

            # command and cd_action get written to a toplevel variable called
            # cmd_foo. Toplevel variables can't handle things that change per
            # makefile like $(TARGET), so hardcode the target.
            command = command.replace("$(TARGET)", self.target)
            cd_action = cd_action.replace("$(TARGET)", self.target)

            # Set LD_LIBRARY_PATH in case the action runs an executable from this
            # build which links to shared libs from this build.
            # actions run on the host, so they should in theory only use host
            # libraries, but until everything is made cross-compile safe, also use
            # target libraries.
            # TODO(piman): when everything is cross-compile safe, remove lib.target
            if self.flavor in {"zos", "aix"}:
                self.WriteLn(
                    "cmd_%s = LIBPATH=$(builddir)/lib.host:"
                    "$(builddir)/lib.target:$$LIBPATH; "
                    "export LIBPATH; "
                    "%s%s" % (name, cd_action, command)
                )
            else:
                self.WriteLn(
                    "cmd_%s = LD_LIBRARY_PATH=$(builddir)/lib.host:"
                    "$(builddir)/lib.target:$$LD_LIBRARY_PATH; "
                    "export LD_LIBRARY_PATH; "
                    "%s%s" % (name, cd_action, command)
                )
            self.WriteLn()
            outputs = [self.Absolutify(o) for o in outputs]
            # The makefile rules are all relative to the top dir, but the gyp actions
            # are defined relative to their containing dir.  This replaces the obj
            # variable for the action rule with an absolute version so that the output
            # goes in the right place.
            # Only write the 'obj' and 'builddir' rules for the "primary" output (:1);
            # it's superfluous for the "extra outputs", and this avoids accidentally
            # writing duplicate dummy rules for those outputs.
            # Same for environment.
            self.WriteLn("%s: obj := $(abs_obj)" % QuoteSpaces(outputs[0]))
            self.WriteLn("%s: builddir := $(abs_builddir)" % QuoteSpaces(outputs[0]))
            self.WriteSortedXcodeEnv(outputs[0], self.GetSortedXcodeEnv())

            for input in inputs:
                assert " " not in input, (
                    "Spaces in action input filenames not supported (%s)" % input
                )
            for output in outputs:
                assert " " not in output, (
                    "Spaces in action output filenames not supported (%s)" % output
                )

            # See the comment in WriteCopies about expanding env vars.
            outputs = [gyp.xcode_emulation.ExpandEnvVars(o, env) for o in outputs]
            inputs = [gyp.xcode_emulation.ExpandEnvVars(i, env) for i in inputs]

            self.WriteDoCmd(
                outputs,
                [Sourceify(self.Absolutify(i)) for i in inputs],
                part_of_all=part_of_all,
                command=name,
            )

            # Stuff the outputs in a variable so we can refer to them later.
            outputs_variable = "action_%s_outputs" % name
            self.WriteLn("{} := {}".format(outputs_variable, " ".join(outputs)))
            extra_outputs.append("$(%s)" % outputs_variable)
            self.WriteLn()

        self.WriteLn()

    def WriteRules(
        self,
        rules,
        extra_sources,
        extra_outputs,
        extra_mac_bundle_resources,
        part_of_all,
    ):
        """Write Makefile code for any 'rules' from the gyp input.

        extra_sources: a list that will be filled in with newly generated source
                       files, if any
        extra_outputs: a list that will be filled in with any outputs of these
                       rules (used to make other pieces dependent on these rules)
        part_of_all: flag indicating this target is part of 'all'
        """
        env = self.GetSortedXcodeEnv()
        for rule in rules:
            name = StringToMakefileVariable(
                "{}_{}".format(self.qualified_target, rule["rule_name"])
            )
            count = 0
            self.WriteLn("### Generated for rule %s:" % name)

            all_outputs = []

            for rule_source in rule.get("rule_sources", []):
                dirs = set()
                (rule_source_dirname, rule_source_basename) = os.path.split(rule_source)
                (rule_source_root, rule_source_ext) = os.path.splitext(
                    rule_source_basename
                )

                outputs = [
                    self.ExpandInputRoot(out, rule_source_root, rule_source_dirname)
                    for out in rule["outputs"]
                ]

                for out in outputs:
                    dir = os.path.dirname(out)
                    if dir:
                        dirs.add(dir)
                if int(rule.get("process_outputs_as_sources", False)):
                    extra_sources += outputs
                if int(rule.get("process_outputs_as_mac_bundle_resources", False)):
                    extra_mac_bundle_resources += outputs
                inputs = [
                    Sourceify(self.Absolutify(i))
                    for i in [rule_source] + rule.get("inputs", [])
                ]
                actions = ["$(call do_cmd,%s_%d)" % (name, count)]

                if name == "resources_grit":
                    # HACK: This is ugly.  Grit intentionally doesn't touch the
                    # timestamp of its output file when the file doesn't change,
                    # which is fine in hash-based dependency systems like scons
                    # and forge, but not kosher in the make world.  After some
                    # discussion, hacking around it here seems like the least
                    # amount of pain.
                    actions += ["@touch --no-create $@"]

                # See the comment in WriteCopies about expanding env vars.
                outputs = [gyp.xcode_emulation.ExpandEnvVars(o, env) for o in outputs]
                inputs = [gyp.xcode_emulation.ExpandEnvVars(i, env) for i in inputs]

                outputs = [self.Absolutify(o) for o in outputs]
                all_outputs += outputs
                # Only write the 'obj' and 'builddir' rules for the "primary" output
                # (:1); it's superfluous for the "extra outputs", and this avoids
                # accidentally writing duplicate dummy rules for those outputs.
                self.WriteLn("%s: obj := $(abs_obj)" % outputs[0])
                self.WriteLn("%s: builddir := $(abs_builddir)" % outputs[0])
                self.WriteMakeRule(
                    outputs, inputs, actions, command="%s_%d" % (name, count)
                )
                # Spaces in rule filenames are not supported, but rule variables have
                # spaces in them (e.g. RULE_INPUT_PATH expands to '$(abspath $<)').
                # The spaces within the variables are valid, so remove the variables
                # before checking.
                variables_with_spaces = re.compile(r"\$\([^ ]* \$<\)")
                for output in outputs:
                    output = re.sub(variables_with_spaces, "", output)
                    assert " " not in output, (
                        "Spaces in rule filenames not yet supported (%s)" % output
                    )
                self.WriteLn("all_deps += %s" % " ".join(outputs))

                action = [
                    self.ExpandInputRoot(ac, rule_source_root, rule_source_dirname)
                    for ac in rule["action"]
                ]
                mkdirs = ""
                if len(dirs) > 0:
                    mkdirs = "mkdir -p %s; " % " ".join(dirs)
                cd_action = "cd %s; " % Sourceify(self.path or ".")

                # action, cd_action, and mkdirs get written to a toplevel variable
                # called cmd_foo. Toplevel variables can't handle things that change
                # per makefile like $(TARGET), so hardcode the target.
                if self.flavor == "mac":
                    action = [
                        gyp.xcode_emulation.ExpandEnvVars(command, env)
                        for command in action
                    ]
                action = gyp.common.EncodePOSIXShellList(action)
                action = action.replace("$(TARGET)", self.target)
                cd_action = cd_action.replace("$(TARGET)", self.target)
                mkdirs = mkdirs.replace("$(TARGET)", self.target)

                # Set LD_LIBRARY_PATH in case the rule runs an executable from this
                # build which links to shared libs from this build.
                # rules run on the host, so they should in theory only use host
                # libraries, but until everything is made cross-compile safe, also use
                # target libraries.
                # TODO(piman): when everything is cross-compile safe, remove lib.target
                self.WriteLn(
                    "cmd_%(name)s_%(count)d = LD_LIBRARY_PATH="
                    "$(builddir)/lib.host:$(builddir)/lib.target:$$LD_LIBRARY_PATH; "
                    "export LD_LIBRARY_PATH; "
                    "%(cd_action)s%(mkdirs)s%(action)s"
                    % {
                        "action": action,
                        "cd_action": cd_action,
                        "count": count,
                        "mkdirs": mkdirs,
                        "name": name,
                    }
                )
                self.WriteLn(
                    "quiet_cmd_%(name)s_%(count)d = RULE %(name)s_%(count)d $@"
                    % {"count": count, "name": name}
                )
                self.WriteLn()
                count += 1

            outputs_variable = "rule_%s_outputs" % name
            self.WriteList(all_outputs, outputs_variable)
            extra_outputs.append("$(%s)" % outputs_variable)

            self.WriteLn("### Finished generating for rule: %s" % name)
            self.WriteLn()
        self.WriteLn("### Finished generating for all rules")
        self.WriteLn("")

    def WriteCopies(self, copies, extra_outputs, part_of_all):
        """Write Makefile code for any 'copies' from the gyp input.

        extra_outputs: a list that will be filled in with any outputs of this action
                       (used to make other pieces dependent on this action)
        part_of_all: flag indicating this target is part of 'all'
        """
        self.WriteLn("### Generated for copy rule.")

        variable = StringToMakefileVariable(self.qualified_target + "_copies")
        outputs = []
        for copy in copies:
            for path in copy["files"]:
                # Absolutify() may call normpath, and will strip trailing slashes.
                path = Sourceify(self.Absolutify(path))
                filename = os.path.split(path)[1]
                output = Sourceify(
                    self.Absolutify(os.path.join(copy["destination"], filename))
                )

                # If the output path has variables in it, which happens in practice for
                # 'copies', writing the environment as target-local doesn't work,
                # because the variables are already needed for the target name.
                # Copying the environment variables into global make variables doesn't
                # work either, because then the .d files will potentially contain spaces
                # after variable expansion, and .d file handling cannot handle spaces.
                # As a workaround, manually expand variables at gyp time. Since 'copies'
                # can't run scripts, there's no need to write the env then.
                # WriteDoCmd() will escape spaces for .d files.
                env = self.GetSortedXcodeEnv()
                output = gyp.xcode_emulation.ExpandEnvVars(output, env)
                path = gyp.xcode_emulation.ExpandEnvVars(path, env)
                self.WriteDoCmd([output], [path], "copy", part_of_all)
                outputs.append(output)
        self.WriteLn(
            "{} = {}".format(variable, " ".join(QuoteSpaces(o) for o in outputs))
        )
        extra_outputs.append("$(%s)" % variable)
        self.WriteLn()

    def WriteMacBundleResources(self, resources, bundle_deps):
        """Writes Makefile code for 'mac_bundle_resources'."""
        self.WriteLn("### Generated for mac_bundle_resources")

        for output, res in gyp.xcode_emulation.GetMacBundleResources(
            generator_default_variables["PRODUCT_DIR"],
            self.xcode_settings,
            [Sourceify(self.Absolutify(r)) for r in resources],
        ):
            _, ext = os.path.splitext(output)
            if ext != ".xcassets":
                # Make does not supports '.xcassets' emulation.
                self.WriteDoCmd(
                    [output], [res], "mac_tool,,,copy-bundle-resource", part_of_all=True
                )
                bundle_deps.append(output)

    def WriteMacInfoPlist(self, bundle_deps):
        """Write Makefile code for bundle Info.plist files."""
        info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
            generator_default_variables["PRODUCT_DIR"],
            self.xcode_settings,
            lambda p: Sourceify(self.Absolutify(p)),
        )
        if not info_plist:
            return
        if defines:
            # Create an intermediate file to store preprocessed results.
            intermediate_plist = "$(obj).$(TOOLSET)/$(TARGET)/" + os.path.basename(
                info_plist
            )
            self.WriteList(
                defines,
                intermediate_plist + ": INFOPLIST_DEFINES",
                "-D",
                quoter=EscapeCppDefine,
            )
            self.WriteMakeRule(
                [intermediate_plist],
                [info_plist],
                [
                    "$(call do_cmd,infoplist)",
                    # "Convert" the plist so that any weird whitespace changes from the
                    # preprocessor do not affect the XML parser in mac_tool.
                    "@plutil -convert xml1 $@ $@",
                ],
            )
            info_plist = intermediate_plist
        # plists can contain envvars and substitute them into the file.
        self.WriteSortedXcodeEnv(
            out, self.GetSortedXcodeEnv(additional_settings=extra_env)
        )
        self.WriteDoCmd(
            [out], [info_plist], "mac_tool,,,copy-info-plist", part_of_all=True
        )
        bundle_deps.append(out)

    def WriteSources(
        self,
        configs,
        deps,
        sources,
        extra_outputs,
        extra_link_deps,
        part_of_all,
        precompiled_header,
    ):
        """Write Makefile code for any 'sources' from the gyp input.
        These are source files necessary to build the current target.

        configs, deps, sources: input from gyp.
        extra_outputs: a list of extra outputs this action should be dependent on;
                       used to serialize action/rules before compilation
        extra_link_deps: a list that will be filled in with any outputs of
                         compilation (to be used in link lines)
        part_of_all: flag indicating this target is part of 'all'
        """

        # Write configuration-specific variables for CFLAGS, etc.
        for configname in sorted(configs.keys()):
            config = configs[configname]
            self.WriteList(
                config.get("defines"),
                "DEFS_%s" % configname,
                prefix="-D",
                quoter=EscapeCppDefine,
            )

            if self.flavor == "mac":
                cflags = self.xcode_settings.GetCflags(
                    configname, arch=config.get("xcode_configuration_platform")
                )
                cflags_c = self.xcode_settings.GetCflagsC(configname)
                cflags_cc = self.xcode_settings.GetCflagsCC(configname)
                cflags_objc = self.xcode_settings.GetCflagsObjC(configname)
                cflags_objcc = self.xcode_settings.GetCflagsObjCC(configname)
            else:
                cflags = config.get("cflags")
                cflags_c = config.get("cflags_c")
                cflags_cc = config.get("cflags_cc")

            self.WriteLn("# Flags passed to all source files.")
            self.WriteList(cflags, "CFLAGS_%s" % configname)
            self.WriteLn("# Flags passed to only C files.")
            self.WriteList(cflags_c, "CFLAGS_C_%s" % configname)
            self.WriteLn("# Flags passed to only C++ files.")
            self.WriteList(cflags_cc, "CFLAGS_CC_%s" % configname)
            if self.flavor == "mac":
                self.WriteLn("# Flags passed to only ObjC files.")
                self.WriteList(cflags_objc, "CFLAGS_OBJC_%s" % configname)
                self.WriteLn("# Flags passed to only ObjC++ files.")
                self.WriteList(cflags_objcc, "CFLAGS_OBJCC_%s" % configname)
            includes = config.get("include_dirs")
            if includes:
                includes = [Sourceify(self.Absolutify(i)) for i in includes]
            self.WriteList(includes, "INCS_%s" % configname, prefix="-I")

        compilable = list(filter(Compilable, sources))
        objs = [self.Objectify(self.Absolutify(Target(c))) for c in compilable]
        self.WriteList(objs, "OBJS")

        for obj in objs:
            assert " " not in obj, "Spaces in object filenames not supported (%s)" % obj
        self.WriteLn(
            "# Add to the list of files we specially track " "dependencies for."
        )
        self.WriteLn("all_deps += $(OBJS)")
        self.WriteLn()

        # Make sure our dependencies are built first.
        if deps:
            self.WriteMakeRule(
                ["$(OBJS)"],
                deps,
                comment="Make sure our dependencies are built " "before any of us.",
                order_only=True,
            )

        # Make sure the actions and rules run first.
        # If they generate any extra headers etc., the per-.o file dep tracking
        # will catch the proper rebuilds, so order only is still ok here.
        if extra_outputs:
            self.WriteMakeRule(
                ["$(OBJS)"],
                extra_outputs,
                comment="Make sure our actions/rules run " "before any of us.",
                order_only=True,
            )

        pchdeps = precompiled_header.GetObjDependencies(compilable, objs)
        if pchdeps:
            self.WriteLn("# Dependencies from obj files to their precompiled headers")
            for source, obj, gch in pchdeps:
                self.WriteLn(f"{obj}: {gch}")
            self.WriteLn("# End precompiled header dependencies")

        if objs:
            extra_link_deps.append("$(OBJS)")
            self.WriteLn(
                """\
# CFLAGS et al overrides must be target-local.
# See "Target-specific Variable Values" in the GNU Make manual."""
            )
            self.WriteLn("$(OBJS): TOOLSET := $(TOOLSET)")
            self.WriteLn(
                "$(OBJS): GYP_CFLAGS := "
                "$(DEFS_$(BUILDTYPE)) "
                "$(INCS_$(BUILDTYPE)) "
                "%s " % precompiled_header.GetInclude("c") + "$(CFLAGS_$(BUILDTYPE)) "
                "$(CFLAGS_C_$(BUILDTYPE))"
            )
            self.WriteLn(
                "$(OBJS): GYP_CXXFLAGS := "
                "$(DEFS_$(BUILDTYPE)) "
                "$(INCS_$(BUILDTYPE)) "
                "%s " % precompiled_header.GetInclude("cc") + "$(CFLAGS_$(BUILDTYPE)) "
                "$(CFLAGS_CC_$(BUILDTYPE))"
            )
            if self.flavor == "mac":
                self.WriteLn(
                    "$(OBJS): GYP_OBJCFLAGS := "
                    "$(DEFS_$(BUILDTYPE)) "
                    "$(INCS_$(BUILDTYPE)) "
                    "%s " % precompiled_header.GetInclude("m")
                    + "$(CFLAGS_$(BUILDTYPE)) "
                    "$(CFLAGS_C_$(BUILDTYPE)) "
                    "$(CFLAGS_OBJC_$(BUILDTYPE))"
                )
                self.WriteLn(
                    "$(OBJS): GYP_OBJCXXFLAGS := "
                    "$(DEFS_$(BUILDTYPE)) "
                    "$(INCS_$(BUILDTYPE)) "
                    "%s " % precompiled_header.GetInclude("mm")
                    + "$(CFLAGS_$(BUILDTYPE)) "
                    "$(CFLAGS_CC_$(BUILDTYPE)) "
                    "$(CFLAGS_OBJCC_$(BUILDTYPE))"
                )

        self.WritePchTargets(precompiled_header.GetPchBuildCommands())

        # If there are any object files in our input file list, link them into our
        # output.
        extra_link_deps += [source for source in sources if Linkable(source)]

        self.WriteLn()

    def WritePchTargets(self, pch_commands):
        """Writes make rules to compile prefix headers."""
        if not pch_commands:
            return

        for gch, lang_flag, lang, input in pch_commands:
            extra_flags = {
                "c": "$(CFLAGS_C_$(BUILDTYPE))",
                "cc": "$(CFLAGS_CC_$(BUILDTYPE))",
                "m": "$(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE))",
                "mm": "$(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE))",
            }[lang]
            var_name = {
                "c": "GYP_PCH_CFLAGS",
                "cc": "GYP_PCH_CXXFLAGS",
                "m": "GYP_PCH_OBJCFLAGS",
                "mm": "GYP_PCH_OBJCXXFLAGS",
            }[lang]
            self.WriteLn(
                f"{gch}: {var_name} := {lang_flag} " + "$(DEFS_$(BUILDTYPE)) "
                "$(INCS_$(BUILDTYPE)) "
                "$(CFLAGS_$(BUILDTYPE)) " + extra_flags
            )

            self.WriteLn(f"{gch}: {input} FORCE_DO_CMD")
            self.WriteLn("\t@$(call do_cmd,pch_%s,1)" % lang)
            self.WriteLn("")
            assert " " not in gch, "Spaces in gch filenames not supported (%s)" % gch
            self.WriteLn("all_deps += %s" % gch)
            self.WriteLn("")

    def ComputeOutputBasename(self, spec):
        """Return the 'output basename' of a gyp spec.

        E.g., the loadable module 'foobar' in directory 'baz' will produce
          'libfoobar.so'
        """
        assert not self.is_mac_bundle

        if self.flavor == "mac" and self.type in (
            "static_library",
            "executable",
            "shared_library",
            "loadable_module",
        ):
            return self.xcode_settings.GetExecutablePath()

        target = spec["target_name"]
        target_prefix = ""
        target_ext = ""
        if self.type == "static_library":
            if target[:3] == "lib":
                target = target[3:]
            target_prefix = "lib"
            target_ext = ".a"
        elif self.type in ("loadable_module", "shared_library"):
            if target[:3] == "lib":
                target = target[3:]
            target_prefix = "lib"
            if self.flavor == "aix":
                target_ext = ".a"
            elif self.flavor == "zos":
                target_ext = ".x"
            else:
                target_ext = ".so"
        elif self.type == "none":
            target = "%s.stamp" % target
        elif self.type != "executable":
            print(
                "ERROR: What output file should be generated?",
                "type",
                self.type,
                "target",
                target,
            )

        target_prefix = spec.get("product_prefix", target_prefix)
        target = spec.get("product_name", target)
        product_ext = spec.get("product_extension")
        if product_ext:
            target_ext = "." + product_ext

        return target_prefix + target + target_ext

    def _InstallImmediately(self):
        return (
            self.toolset == "target"
            and self.flavor == "mac"
            and self.type
            in ("static_library", "executable", "shared_library", "loadable_module")
        )

    def ComputeOutput(self, spec):
        """Return the 'output' (full output path) of a gyp spec.

        E.g., the loadable module 'foobar' in directory 'baz' will produce
          '$(obj)/baz/libfoobar.so'
        """
        assert not self.is_mac_bundle

        path = os.path.join("$(obj)." + self.toolset, self.path)
        if self.type == "executable" or self._InstallImmediately():
            path = "$(builddir)"
        path = spec.get("product_dir", path)
        return os.path.join(path, self.ComputeOutputBasename(spec))

    def ComputeMacBundleOutput(self, spec):
        """Return the 'output' (full output path) to a bundle output directory."""
        assert self.is_mac_bundle
        path = generator_default_variables["PRODUCT_DIR"]
        return os.path.join(path, self.xcode_settings.GetWrapperName())

    def ComputeMacBundleBinaryOutput(self, spec):
        """Return the 'output' (full output path) to the binary in a bundle."""
        path = generator_default_variables["PRODUCT_DIR"]
        return os.path.join(path, self.xcode_settings.GetExecutablePath())

    def ComputeDeps(self, spec):
        """Compute the dependencies of a gyp spec.

        Returns a tuple (deps, link_deps), where each is a list of
        filenames that will need to be put in front of make for either
        building (deps) or linking (link_deps).
        """
        deps = []
        link_deps = []
        if "dependencies" in spec:
            deps.extend(
                [
                    target_outputs[dep]
                    for dep in spec["dependencies"]
                    if target_outputs[dep]
                ]
            )
            for dep in spec["dependencies"]:
                if dep in target_link_deps:
                    link_deps.append(target_link_deps[dep])
            deps.extend(link_deps)
            # TODO: It seems we need to transitively link in libraries (e.g. -lfoo)?
            # This hack makes it work:
            # link_deps.extend(spec.get('libraries', []))
        return (gyp.common.uniquer(deps), gyp.common.uniquer(link_deps))

    def GetSharedObjectFromSidedeck(self, sidedeck):
        """Return the shared object files based on sidedeck"""
        return re.sub(r"\.x$", ".so", sidedeck)

    def GetUnversionedSidedeckFromSidedeck(self, sidedeck):
        """Return the shared object files based on sidedeck"""
        return re.sub(r"\.\d+\.x$", ".x", sidedeck)

    def WriteDependencyOnExtraOutputs(self, target, extra_outputs):
        self.WriteMakeRule(
            [self.output_binary],
            extra_outputs,
            comment="Build our special outputs first.",
            order_only=True,
        )

    def WriteTarget(
        self, spec, configs, deps, link_deps, bundle_deps, extra_outputs, part_of_all
    ):
        """Write Makefile code to produce the final target of the gyp spec.

        spec, configs: input from gyp.
        deps, link_deps: dependency lists; see ComputeDeps()
        extra_outputs: any extra outputs that our target should depend on
        part_of_all: flag indicating this target is part of 'all'
        """

        self.WriteLn("### Rules for final target.")

        if extra_outputs:
            self.WriteDependencyOnExtraOutputs(self.output_binary, extra_outputs)
            self.WriteMakeRule(
                extra_outputs,
                deps,
                comment=("Preserve order dependency of " "special output on deps."),
                order_only=True,
            )

        target_postbuilds = {}
        if self.type != "none":
            for configname in sorted(configs.keys()):
                config = configs[configname]
                if self.flavor == "mac":
                    ldflags = self.xcode_settings.GetLdflags(
                        configname,
                        generator_default_variables["PRODUCT_DIR"],
                        lambda p: Sourceify(self.Absolutify(p)),
                        arch=config.get("xcode_configuration_platform"),
                    )

                    # TARGET_POSTBUILDS_$(BUILDTYPE) is added to postbuilds later on.
                    gyp_to_build = gyp.common.InvertRelativePath(self.path)
                    target_postbuild = self.xcode_settings.AddImplicitPostbuilds(
                        configname,
                        QuoteSpaces(
                            os.path.normpath(os.path.join(gyp_to_build, self.output))
                        ),
                        QuoteSpaces(
                            os.path.normpath(
                                os.path.join(gyp_to_build, self.output_binary)
                            )
                        ),
                    )
                    if target_postbuild:
                        target_postbuilds[configname] = target_postbuild
                else:
                    ldflags = config.get("ldflags", [])
                    # Compute an rpath for this output if needed.
                    if any(dep.endswith(".so") or ".so." in dep for dep in deps):
                        # We want to get the literal string "$ORIGIN"
                        # into the link command, so we need lots of escaping.
                        ldflags.append(r"-Wl,-rpath=\$$ORIGIN/")
                        ldflags.append(r"-Wl,-rpath-link=\$(builddir)/")
                library_dirs = config.get("library_dirs", [])
                ldflags += [("-L%s" % library_dir) for library_dir in library_dirs]
                self.WriteList(ldflags, "LDFLAGS_%s" % configname)
                if self.flavor == "mac":
                    self.WriteList(
                        self.xcode_settings.GetLibtoolflags(configname),
                        "LIBTOOLFLAGS_%s" % configname,
                    )
            libraries = spec.get("libraries")
            if libraries:
                # Remove duplicate entries
                libraries = gyp.common.uniquer(libraries)
                if self.flavor == "mac":
                    libraries = self.xcode_settings.AdjustLibraries(libraries)
            self.WriteList(libraries, "LIBS")
            self.WriteLn(
                "%s: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))"
                % QuoteSpaces(self.output_binary)
            )
            self.WriteLn("%s: LIBS := $(LIBS)" % QuoteSpaces(self.output_binary))

            if self.flavor == "mac":
                self.WriteLn(
                    "%s: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE))"
                    % QuoteSpaces(self.output_binary)
                )

        # Postbuild actions. Like actions, but implicitly depend on the target's
        # output.
        postbuilds = []
        if self.flavor == "mac":
            if target_postbuilds:
                postbuilds.append("$(TARGET_POSTBUILDS_$(BUILDTYPE))")
            postbuilds.extend(gyp.xcode_emulation.GetSpecPostbuildCommands(spec))

        if postbuilds:
            # Envvars may be referenced by TARGET_POSTBUILDS_$(BUILDTYPE),
            # so we must output its definition first, since we declare variables
            # using ":=".
            self.WriteSortedXcodeEnv(self.output, self.GetSortedXcodePostbuildEnv())

            for configname in target_postbuilds:
                self.WriteLn(
                    "%s: TARGET_POSTBUILDS_%s := %s"
                    % (
                        QuoteSpaces(self.output),
                        configname,
                        gyp.common.EncodePOSIXShellList(target_postbuilds[configname]),
                    )
                )

            # Postbuilds expect to be run in the gyp file's directory, so insert an
            # implicit postbuild to cd to there.
            postbuilds.insert(0, gyp.common.EncodePOSIXShellList(["cd", self.path]))
            for i, postbuild in enumerate(postbuilds):
                if not postbuild.startswith("$"):
                    postbuilds[i] = EscapeShellArgument(postbuild)
            self.WriteLn("%s: builddir := $(abs_builddir)" % QuoteSpaces(self.output))
            self.WriteLn(
                "%s: POSTBUILDS := %s"
                % (QuoteSpaces(self.output), " ".join(postbuilds))
            )

        # A bundle directory depends on its dependencies such as bundle resources
        # and bundle binary. When all dependencies have been built, the bundle
        # needs to be packaged.
        if self.is_mac_bundle:
            # If the framework doesn't contain a binary, then nothing depends
            # on the actions -- make the framework depend on them directly too.
            self.WriteDependencyOnExtraOutputs(self.output, extra_outputs)

            # Bundle dependencies. Note that the code below adds actions to this
            # target, so if you move these two lines, move the lines below as well.
            self.WriteList([QuoteSpaces(dep) for dep in bundle_deps], "BUNDLE_DEPS")
            self.WriteLn("%s: $(BUNDLE_DEPS)" % QuoteSpaces(self.output))

            # After the framework is built, package it. Needs to happen before
            # postbuilds, since postbuilds depend on this.
            if self.type in ("shared_library", "loadable_module"):
                self.WriteLn(
                    "\t@$(call do_cmd,mac_package_framework,,,%s)"
                    % self.xcode_settings.GetFrameworkVersion()
                )

            # Bundle postbuilds can depend on the whole bundle, so run them after
            # the bundle is packaged, not already after the bundle binary is done.
            if postbuilds:
                self.WriteLn("\t@$(call do_postbuilds)")
            postbuilds = []  # Don't write postbuilds for target's output.

            # Needed by test/mac/gyptest-rebuild.py.
            self.WriteLn("\t@true  # No-op, used by tests")

            # Since this target depends on binary and resources which are in
            # nested subfolders, the framework directory will be older than
            # its dependencies usually. To prevent this rule from executing
            # on every build (expensive, especially with postbuilds), expliclity
            # update the time on the framework directory.
            self.WriteLn("\t@touch -c %s" % QuoteSpaces(self.output))

        if postbuilds:
            assert not self.is_mac_bundle, (
                "Postbuilds for bundles should be done "
                "on the bundle, not the binary (target '%s')" % self.target
            )
            assert "product_dir" not in spec, (
                "Postbuilds do not work with " "custom product_dir"
            )

        if self.type == "executable":
            self.WriteLn(
                "%s: LD_INPUTS := %s"
                % (
                    QuoteSpaces(self.output_binary),
                    " ".join(QuoteSpaces(dep) for dep in link_deps),
                )
            )
            if self.toolset == "host" and self.flavor == "android":
                self.WriteDoCmd(
                    [self.output_binary],
                    link_deps,
                    "link_host",
                    part_of_all,
                    postbuilds=postbuilds,
                )
            else:
                self.WriteDoCmd(
                    [self.output_binary],
                    link_deps,
                    "link",
                    part_of_all,
                    postbuilds=postbuilds,
                )

        elif self.type == "static_library":
            for link_dep in link_deps:
                assert " " not in link_dep, (
                    "Spaces in alink input filenames not supported (%s)" % link_dep
                )
            if (
                self.flavor not in ("mac", "openbsd", "netbsd", "win")
                and not self.is_standalone_static_library
            ):
                if self.flavor in ("linux", "android"):
                    self.WriteMakeRule(
                        [self.output_binary],
                        link_deps,
                        actions=["$(call create_thin_archive,$@,$^)"],
                    )
                else:
                    self.WriteDoCmd(
                        [self.output_binary],
                        link_deps,
                        "alink_thin",
                        part_of_all,
                        postbuilds=postbuilds,
                    )
            else:
                if self.flavor in ("linux", "android"):
                    self.WriteMakeRule(
                        [self.output_binary],
                        link_deps,
                        actions=["$(call create_archive,$@,$^)"],
                    )
                else:
                    self.WriteDoCmd(
                        [self.output_binary],
                        link_deps,
                        "alink",
                        part_of_all,
                        postbuilds=postbuilds,
                    )
        elif self.type == "shared_library":
            self.WriteLn(
                "%s: LD_INPUTS := %s"
                % (
                    QuoteSpaces(self.output_binary),
                    " ".join(QuoteSpaces(dep) for dep in link_deps),
                )
            )
            self.WriteDoCmd(
                [self.output_binary],
                link_deps,
                "solink",
                part_of_all,
                postbuilds=postbuilds,
            )
            # z/OS has a .so target as well as a sidedeck .x target
            if self.flavor == "zos":
                self.WriteLn(
                    "%s: %s"
                    % (
                        QuoteSpaces(
                            self.GetSharedObjectFromSidedeck(self.output_binary)
                        ),
                        QuoteSpaces(self.output_binary),
                    )
                )
        elif self.type == "loadable_module":
            for link_dep in link_deps:
                assert " " not in link_dep, (
                    "Spaces in module input filenames not supported (%s)" % link_dep
                )
            if self.toolset == "host" and self.flavor == "android":
                self.WriteDoCmd(
                    [self.output_binary],
                    link_deps,
                    "solink_module_host",
                    part_of_all,
                    postbuilds=postbuilds,
                )
            else:
                self.WriteDoCmd(
                    [self.output_binary],
                    link_deps,
                    "solink_module",
                    part_of_all,
                    postbuilds=postbuilds,
                )
        elif self.type == "none":
            # Write a stamp line.
            self.WriteDoCmd(
                [self.output_binary], deps, "touch", part_of_all, postbuilds=postbuilds
            )
        else:
            print("WARNING: no output for", self.type, self.target)

        # Add an alias for each target (if there are any outputs).
        # Installable target aliases are created below.
        if (self.output and self.output != self.target) and (
            self.type not in self._INSTALLABLE_TARGETS
        ):
            self.WriteMakeRule(
                [self.target], [self.output], comment="Add target alias", phony=True
            )
            if part_of_all:
                self.WriteMakeRule(
                    ["all"],
                    [self.target],
                    comment='Add target alias to "all" target.',
                    phony=True,
                )

        # Add special-case rules for our installable targets.
        # 1) They need to install to the build dir or "product" dir.
        # 2) They get shortcuts for building (e.g. "make chrome").
        # 3) They are part of "make all".
        if self.type in self._INSTALLABLE_TARGETS or self.is_standalone_static_library:
            if self.type == "shared_library":
                file_desc = "shared library"
            elif self.type == "static_library":
                file_desc = "static library"
            else:
                file_desc = "executable"
            install_path = self._InstallableTargetInstallPath()
            installable_deps = []
            if self.flavor != "zos":
                installable_deps.append(self.output)
            if (
                self.flavor == "mac"
                and "product_dir" not in spec
                and self.toolset == "target"
            ):
                # On mac, products are created in install_path immediately.
                assert install_path == self.output, f"{install_path} != {self.output}"

            # Point the target alias to the final binary output.
            self.WriteMakeRule(
                [self.target], [install_path], comment="Add target alias", phony=True
            )
            if install_path != self.output:
                assert not self.is_mac_bundle  # See comment a few lines above.
                self.WriteDoCmd(
                    [install_path],
                    [self.output],
                    "copy",
                    comment="Copy this to the %s output path." % file_desc,
                    part_of_all=part_of_all,
                )
                if self.flavor != "zos":
                    installable_deps.append(install_path)
            if self.flavor == "zos" and self.type == "shared_library":
                # lib.target/libnode.so has a dependency on $(obj).target/libnode.so
                self.WriteDoCmd(
                    [self.GetSharedObjectFromSidedeck(install_path)],
                    [self.GetSharedObjectFromSidedeck(self.output)],
                    "copy",
                    comment="Copy this to the %s output path." % file_desc,
                    part_of_all=part_of_all,
                )
                # Create a symlink of libnode.x to libnode.version.x
                self.WriteDoCmd(
                    [self.GetUnversionedSidedeckFromSidedeck(install_path)],
                    [install_path],
                    "symlink",
                    comment="Symlnk this to the %s output path." % file_desc,
                    part_of_all=part_of_all,
                )
                # Place libnode.version.so and libnode.x symlink in lib.target dir
                installable_deps.append(self.GetSharedObjectFromSidedeck(install_path))
                installable_deps.append(
                    self.GetUnversionedSidedeckFromSidedeck(install_path)
                )
            if self.alias not in (self.output, self.target):
                self.WriteMakeRule(
                    [self.alias],
                    installable_deps,
                    comment="Short alias for building this %s." % file_desc,
                    phony=True,
                )
            if self.flavor == "zos" and self.type == "shared_library":
                # Make sure that .x symlink target is run
                self.WriteMakeRule(
                    ["all"],
                    [
                        self.GetUnversionedSidedeckFromSidedeck(install_path),
                        self.GetSharedObjectFromSidedeck(install_path),
                    ],
                    comment='Add %s to "all" target.' % file_desc,
                    phony=True,
                )
            elif part_of_all:
                self.WriteMakeRule(
                    ["all"],
                    [install_path],
                    comment='Add %s to "all" target.' % file_desc,
                    phony=True,
                )

    def WriteList(self, value_list, variable=None, prefix="", quoter=QuoteIfNecessary):
        """Write a variable definition that is a list of values.

        E.g. WriteList(['a','b'], 'foo', prefix='blah') writes out
             foo = blaha blahb
        but in a pretty-printed style.
        """
        values = ""
        if value_list:
            value_list = [quoter(prefix + value) for value in value_list]
            values = " \\\n\t" + " \\\n\t".join(value_list)
        self.fp.write(f"{variable} :={values}\n\n")

    def WriteDoCmd(
        self, outputs, inputs, command, part_of_all, comment=None, postbuilds=False
    ):
        """Write a Makefile rule that uses do_cmd.

        This makes the outputs dependent on the command line that was run,
        as well as support the V= make command line flag.
        """
        suffix = ""
        if postbuilds:
            assert "," not in command
            suffix = ",,1"  # Tell do_cmd to honor $POSTBUILDS
        self.WriteMakeRule(
            outputs,
            inputs,
            actions=[f"$(call do_cmd,{command}{suffix})"],
            comment=comment,
            command=command,
            force=True,
        )
        # Add our outputs to the list of targets we read depfiles from.
        # all_deps is only used for deps file reading, and for deps files we replace
        # spaces with ? because escaping doesn't work with make's $(sort) and
        # other functions.
        outputs = [QuoteSpaces(o, SPACE_REPLACEMENT) for o in outputs]
        self.WriteLn("all_deps += %s" % " ".join(outputs))

    def WriteMakeRule(
        self,
        outputs,
        inputs,
        actions=None,
        comment=None,
        order_only=False,
        force=False,
        phony=False,
        command=None,
    ):
        """Write a Makefile rule, with some extra tricks.

        outputs: a list of outputs for the rule (note: this is not directly
                 supported by make; see comments below)
        inputs: a list of inputs for the rule
        actions: a list of shell commands to run for the rule
        comment: a comment to put in the Makefile above the rule (also useful
                 for making this Python script's code self-documenting)
        order_only: if true, makes the dependency order-only
        force: if true, include FORCE_DO_CMD as an order-only dep
        phony: if true, the rule does not actually generate the named output, the
               output is just a name to run the rule
        command: (optional) command name to generate unambiguous labels
        """
        outputs = [QuoteSpaces(o) for o in outputs]
        inputs = [QuoteSpaces(i) for i in inputs]

        if comment:
            self.WriteLn("# " + comment)
        if phony:
            self.WriteLn(".PHONY: " + " ".join(outputs))
        if actions:
            self.WriteLn("%s: TOOLSET := $(TOOLSET)" % outputs[0])
        force_append = " FORCE_DO_CMD" if force else ""

        if order_only:
            # Order only rule: Just write a simple rule.
            # TODO(evanm): just make order_only a list of deps instead of this hack.
            self.WriteLn(
                "{}: | {}{}".format(" ".join(outputs), " ".join(inputs), force_append)
            )
        elif len(outputs) == 1:
            # Regular rule, one output: Just write a simple rule.
            self.WriteLn("{}: {}{}".format(outputs[0], " ".join(inputs), force_append))
        else:
            # Regular rule, more than one output: Multiple outputs are tricky in
            # make. We will write three rules:
            # - All outputs depend on an intermediate file.
            # - Make .INTERMEDIATE depend on the intermediate.
            # - The intermediate file depends on the inputs and executes the
            #   actual command.
            # - The intermediate recipe will 'touch' the intermediate file.
            # - The multi-output rule will have an do-nothing recipe.

            # Hash the target name to avoid generating overlong filenames.
            cmddigest = hashlib.sha1(
                (command or self.target).encode("utf-8")
            ).hexdigest()
            intermediate = "%s.intermediate" % cmddigest
            self.WriteLn("{}: {}".format(" ".join(outputs), intermediate))
            self.WriteLn("\t%s" % "@:")
            self.WriteLn("{}: {}".format(".INTERMEDIATE", intermediate))
            self.WriteLn(
                "{}: {}{}".format(intermediate, " ".join(inputs), force_append)
            )
            actions.insert(0, "$(call do_cmd,touch)")

        if actions:
            for action in actions:
                self.WriteLn("\t%s" % action)
        self.WriteLn()

    def WriteAndroidNdkModuleRule(self, module_name, all_sources, link_deps):
        """Write a set of LOCAL_XXX definitions for Android NDK.

        These variable definitions will be used by Android NDK but do nothing for
        non-Android applications.

        Arguments:
          module_name: Android NDK module name, which must be unique among all
              module names.
          all_sources: A list of source files (will be filtered by Compilable).
          link_deps: A list of link dependencies, which must be sorted in
              the order from dependencies to dependents.
        """
        if self.type not in ("executable", "shared_library", "static_library"):
            return

        self.WriteLn("# Variable definitions for Android applications")
        self.WriteLn("include $(CLEAR_VARS)")
        self.WriteLn("LOCAL_MODULE := " + module_name)
        self.WriteLn(
            "LOCAL_CFLAGS := $(CFLAGS_$(BUILDTYPE)) "
            "$(DEFS_$(BUILDTYPE)) "
            # LOCAL_CFLAGS is applied to both of C and C++.  There is
            # no way to specify $(CFLAGS_C_$(BUILDTYPE)) only for C
            # sources.
            "$(CFLAGS_C_$(BUILDTYPE)) "
            # $(INCS_$(BUILDTYPE)) includes the prefix '-I' while
            # LOCAL_C_INCLUDES does not expect it.  So put it in
            # LOCAL_CFLAGS.
            "$(INCS_$(BUILDTYPE))"
        )
        # LOCAL_CXXFLAGS is obsolete and LOCAL_CPPFLAGS is preferred.
        self.WriteLn("LOCAL_CPPFLAGS := $(CFLAGS_CC_$(BUILDTYPE))")
        self.WriteLn("LOCAL_C_INCLUDES :=")
        self.WriteLn("LOCAL_LDLIBS := $(LDFLAGS_$(BUILDTYPE)) $(LIBS)")

        # Detect the C++ extension.
        cpp_ext = {".cc": 0, ".cpp": 0, ".cxx": 0}
        default_cpp_ext = ".cpp"
        for filename in all_sources:
            ext = os.path.splitext(filename)[1]
            if ext in cpp_ext:
                cpp_ext[ext] += 1
                if cpp_ext[ext] > cpp_ext[default_cpp_ext]:
                    default_cpp_ext = ext
        self.WriteLn("LOCAL_CPP_EXTENSION := " + default_cpp_ext)

        self.WriteList(
            list(map(self.Absolutify, filter(Compilable, all_sources))),
            "LOCAL_SRC_FILES",
        )

        # Filter out those which do not match prefix and suffix and produce
        # the resulting list without prefix and suffix.
        def DepsToModules(deps, prefix, suffix):
            modules = []
            for filepath in deps:
                filename = os.path.basename(filepath)
                if filename.startswith(prefix) and filename.endswith(suffix):
                    modules.append(filename[len(prefix) : -len(suffix)])
            return modules

        # Retrieve the default value of 'SHARED_LIB_SUFFIX'
        params = {"flavor": "linux"}
        default_variables = {}
        CalculateVariables(default_variables, params)

        self.WriteList(
            DepsToModules(
                link_deps,
                generator_default_variables["SHARED_LIB_PREFIX"],
                default_variables["SHARED_LIB_SUFFIX"],
            ),
            "LOCAL_SHARED_LIBRARIES",
        )
        self.WriteList(
            DepsToModules(
                link_deps,
                generator_default_variables["STATIC_LIB_PREFIX"],
                generator_default_variables["STATIC_LIB_SUFFIX"],
            ),
            "LOCAL_STATIC_LIBRARIES",
        )

        if self.type == "executable":
            self.WriteLn("include $(BUILD_EXECUTABLE)")
        elif self.type == "shared_library":
            self.WriteLn("include $(BUILD_SHARED_LIBRARY)")
        elif self.type == "static_library":
            self.WriteLn("include $(BUILD_STATIC_LIBRARY)")
        self.WriteLn()

    def WriteLn(self, text=""):
        self.fp.write(text + "\n")

    def GetSortedXcodeEnv(self, additional_settings=None):
        return gyp.xcode_emulation.GetSortedXcodeEnv(
            self.xcode_settings,
            "$(abs_builddir)",
            os.path.join("$(abs_srcdir)", self.path),
            "$(BUILDTYPE)",
            additional_settings,
        )

    def GetSortedXcodePostbuildEnv(self):
        # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
        # TODO(thakis): It would be nice to have some general mechanism instead.
        strip_save_file = self.xcode_settings.GetPerTargetSetting(
            "CHROMIUM_STRIP_SAVE_FILE", ""
        )
        # Even if strip_save_file is empty, explicitly write it. Else a postbuild
        # might pick up an export from an earlier target.
        return self.GetSortedXcodeEnv(
            additional_settings={"CHROMIUM_STRIP_SAVE_FILE": strip_save_file}
        )

    def WriteSortedXcodeEnv(self, target, env):
        for k, v in env:
            # For
            #  foo := a\ b
            # the escaped space does the right thing. For
            #  export foo := a\ b
            # it does not -- the backslash is written to the env as literal character.
            # So don't escape spaces in |env[k]|.
            self.WriteLn(f"{QuoteSpaces(target)}: export {k} := {v}")

    def Objectify(self, path):
        """Convert a path to its output directory form."""
        if "$(" in path:
            path = path.replace("$(obj)/", "$(obj).%s/$(TARGET)/" % self.toolset)
        if "$(obj)" not in path:
            path = f"$(obj).{self.toolset}/$(TARGET)/{path}"
        return path

    def Pchify(self, path, lang):
        """Convert a prefix header path to its output directory form."""
        path = self.Absolutify(path)
        if "$(" in path:
            path = path.replace(
                "$(obj)/", f"$(obj).{self.toolset}/$(TARGET)/pch-{lang}"
            )
            return path
        return f"$(obj).{self.toolset}/$(TARGET)/pch-{lang}/{path}"

    def Absolutify(self, path):
        """Convert a subdirectory-relative path into a base-relative path.
        Skips over paths that contain variables."""
        if "$(" in path:
            # Don't call normpath in this case, as it might collapse the
            # path too aggressively if it features '..'. However it's still
            # important to strip trailing slashes.
            return path.rstrip("/")
        return os.path.normpath(os.path.join(self.path, path))

    def ExpandInputRoot(self, template, expansion, dirname):
        if "%(INPUT_ROOT)s" not in template and "%(INPUT_DIRNAME)s" not in template:
            return template
        path = template % {
            "INPUT_ROOT": expansion,
            "INPUT_DIRNAME": dirname,
        }
        return path

    def _InstallableTargetInstallPath(self):
        """Returns the location of the final output for an installable target."""
        # Functionality removed for all platforms to match Xcode and hoist
        # shared libraries into PRODUCT_DIR for users:
        # Xcode puts shared_library results into PRODUCT_DIR, and some gyp files
        # rely on this. Emulate this behavior for mac.
        # if self.type == "shared_library" and (
        #     self.flavor != "mac" or self.toolset != "target"
        # ):
        #    # Install all shared libs into a common directory (per toolset) for
        #    # convenient access with LD_LIBRARY_PATH.
        #    return "$(builddir)/lib.%s/%s" % (self.toolset, self.alias)
        if self.flavor == "zos" and self.type == "shared_library":
            return "$(builddir)/lib.%s/%s" % (self.toolset, self.alias)

        return "$(builddir)/" + self.alias


def WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files):
    """Write the target to regenerate the Makefile."""
    options = params["options"]
    build_files_args = [
        gyp.common.RelativePath(filename, options.toplevel_dir)
        for filename in params["build_files_arg"]
    ]

    gyp_binary = gyp.common.FixIfRelativePath(
        params["gyp_binary"], options.toplevel_dir
    )
    if not gyp_binary.startswith(os.sep):
        gyp_binary = os.path.join(".", gyp_binary)

    root_makefile.write(
        "quiet_cmd_regen_makefile = ACTION Regenerating $@\n"
        "cmd_regen_makefile = cd $(srcdir); %(cmd)s\n"
        "%(makefile_name)s: %(deps)s\n"
        "\t$(call do_cmd,regen_makefile)\n\n"
        % {
            "makefile_name": makefile_name,
            "deps": " ".join(SourceifyAndQuoteSpaces(bf) for bf in build_files),
            "cmd": gyp.common.EncodePOSIXShellList(
                [gyp_binary, "-fmake"] + gyp.RegenerateFlags(options) + build_files_args
            ),
        }
    )


def PerformBuild(data, configurations, params):
    options = params["options"]
    for config in configurations:
        arguments = ["make"]
        if options.toplevel_dir and options.toplevel_dir != ".":
            arguments += "-C", options.toplevel_dir
        arguments.append("BUILDTYPE=" + config)
        print(f"Building [{config}]: {arguments}")
        subprocess.check_call(arguments)


def GenerateOutput(target_list, target_dicts, data, params):
    options = params["options"]
    flavor = gyp.common.GetFlavor(params)
    generator_flags = params.get("generator_flags", {})
    builddir_name = generator_flags.get("output_dir", "out")
    android_ndk_version = generator_flags.get("android_ndk_version", None)
    default_target = generator_flags.get("default_target", "all")

    def CalculateMakefilePath(build_file, base_name):
        """Determine where to write a Makefile for a given gyp file."""
        # Paths in gyp files are relative to the .gyp file, but we want
        # paths relative to the source root for the master makefile.  Grab
        # the path of the .gyp file as the base to relativize against.
        # E.g. "foo/bar" when we're constructing targets for "foo/bar/baz.gyp".
        base_path = gyp.common.RelativePath(os.path.dirname(build_file), options.depth)
        # We write the file in the base_path directory.
        output_file = os.path.join(options.depth, base_path, base_name)
        if options.generator_output:
            output_file = os.path.join(
                options.depth, options.generator_output, base_path, base_name
            )
        base_path = gyp.common.RelativePath(
            os.path.dirname(build_file), options.toplevel_dir
        )
        return base_path, output_file

    # TODO:  search for the first non-'Default' target.  This can go
    # away when we add verification that all targets have the
    # necessary configurations.
    default_configuration = None
    toolsets = {target_dicts[target]["toolset"] for target in target_list}
    for target in target_list:
        spec = target_dicts[target]
        if spec["default_configuration"] != "Default":
            default_configuration = spec["default_configuration"]
            break
    if not default_configuration:
        default_configuration = "Default"

    srcdir = "."
    makefile_name = "Makefile" + options.suffix
    makefile_path = os.path.join(options.toplevel_dir, makefile_name)
    if options.generator_output:
        global srcdir_prefix
        makefile_path = os.path.join(
            options.toplevel_dir, options.generator_output, makefile_name
        )
        srcdir = gyp.common.RelativePath(srcdir, options.generator_output)
        srcdir_prefix = "$(srcdir)/"

    flock_command = "flock"
    copy_archive_arguments = "-af"
    makedep_arguments = "-MMD"
    header_params = {
        "default_target": default_target,
        "builddir": builddir_name,
        "default_configuration": default_configuration,
        "flock": flock_command,
        "flock_index": 1,
        "link_commands": LINK_COMMANDS_LINUX,
        "extra_commands": "",
        "srcdir": srcdir,
        "copy_archive_args": copy_archive_arguments,
        "makedep_args": makedep_arguments,
        "CC.target": GetEnvironFallback(("CC_target", "CC"), "$(CC)"),
        "AR.target": GetEnvironFallback(("AR_target", "AR"), "$(AR)"),
        "CXX.target": GetEnvironFallback(("CXX_target", "CXX"), "$(CXX)"),
        "LINK.target": GetEnvironFallback(("LINK_target", "LINK"), "$(LINK)"),
        "PLI.target": GetEnvironFallback(("PLI_target", "PLI"), "pli"),
        "CC.host": GetEnvironFallback(("CC_host", "CC"), "gcc"),
        "AR.host": GetEnvironFallback(("AR_host", "AR"), "ar"),
        "CXX.host": GetEnvironFallback(("CXX_host", "CXX"), "g++"),
        "LINK.host": GetEnvironFallback(("LINK_host", "LINK"), "$(CXX.host)"),
        "PLI.host": GetEnvironFallback(("PLI_host", "PLI"), "pli"),
    }
    if flavor == "mac":
        flock_command = "./gyp-mac-tool flock"
        header_params.update(
            {
                "flock": flock_command,
                "flock_index": 2,
                "link_commands": LINK_COMMANDS_MAC,
                "extra_commands": SHARED_HEADER_MAC_COMMANDS,
            }
        )
    elif flavor == "android":
        header_params.update({"link_commands": LINK_COMMANDS_ANDROID})
    elif flavor == "zos":
        copy_archive_arguments = "-fPR"
        CC_target = GetEnvironFallback(("CC_target", "CC"), "njsc")
        makedep_arguments = "-MMD"
        if CC_target == "clang":
            CC_host = GetEnvironFallback(("CC_host", "CC"), "clang")
            CXX_target = GetEnvironFallback(("CXX_target", "CXX"), "clang++")
            CXX_host = GetEnvironFallback(("CXX_host", "CXX"), "clang++")
        elif CC_target == "ibm-clang64":
            CC_host = GetEnvironFallback(("CC_host", "CC"), "ibm-clang64")
            CXX_target = GetEnvironFallback(("CXX_target", "CXX"), "ibm-clang++64")
            CXX_host = GetEnvironFallback(("CXX_host", "CXX"), "ibm-clang++64")
        elif CC_target == "ibm-clang":
            CC_host = GetEnvironFallback(("CC_host", "CC"), "ibm-clang")
            CXX_target = GetEnvironFallback(("CXX_target", "CXX"), "ibm-clang++")
            CXX_host = GetEnvironFallback(("CXX_host", "CXX"), "ibm-clang++")
        else:
            # Node.js versions prior to v18:
            makedep_arguments = "-qmakedep=gcc"
            CC_host = GetEnvironFallback(("CC_host", "CC"), "njsc")
            CXX_target = GetEnvironFallback(("CXX_target", "CXX"), "njsc++")
            CXX_host = GetEnvironFallback(("CXX_host", "CXX"), "njsc++")
        header_params.update(
            {
                "copy_archive_args": copy_archive_arguments,
                "makedep_args": makedep_arguments,
                "link_commands": LINK_COMMANDS_OS390,
                "extra_commands": SHARED_HEADER_OS390_COMMANDS,
                "CC.target": CC_target,
                "CXX.target": CXX_target,
                "CC.host": CC_host,
                "CXX.host": CXX_host,
            }
        )
    elif flavor == "solaris":
        copy_archive_arguments = "-pPRf@"
        header_params.update(
            {
                "copy_archive_args": copy_archive_arguments,
                "flock": "./gyp-flock-tool flock",
                "flock_index": 2,
            }
        )
    elif flavor == "freebsd":
        # Note: OpenBSD has sysutils/flock. lockf seems to be FreeBSD specific.
        header_params.update({"flock": "lockf"})
    elif flavor == "openbsd":
        copy_archive_arguments = "-pPRf"
        header_params.update({"copy_archive_args": copy_archive_arguments})
    elif flavor == "aix":
        copy_archive_arguments = "-pPRf"
        header_params.update(
            {
                "copy_archive_args": copy_archive_arguments,
                "link_commands": LINK_COMMANDS_AIX,
                "flock": "./gyp-flock-tool flock",
                "flock_index": 2,
            }
        )
    elif flavor == "os400":
        copy_archive_arguments = "-pPRf"
        header_params.update(
            {
                "copy_archive_args": copy_archive_arguments,
                "link_commands": LINK_COMMANDS_OS400,
                "flock": "./gyp-flock-tool flock",
                "flock_index": 2,
            }
        )

    build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
    make_global_settings_array = data[build_file].get("make_global_settings", [])
    wrappers = {}
    for key, value in make_global_settings_array:
        if key.endswith("_wrapper"):
            wrappers[key[: -len("_wrapper")]] = "$(abspath %s)" % value
    make_global_settings = ""
    for key, value in make_global_settings_array:
        if re.match(".*_wrapper", key):
            continue
        if value[0] != "$":
            value = "$(abspath %s)" % value
        wrapper = wrappers.get(key)
        if wrapper:
            value = f"{wrapper} {value}"
            del wrappers[key]
        if key in ("CC", "CC.host", "CXX", "CXX.host"):
            make_global_settings += (
                "ifneq (,$(filter $(origin %s), undefined default))\n" % key
            )
            # Let gyp-time envvars win over global settings.
            env_key = key.replace(".", "_")  # CC.host -> CC_host
            if env_key in os.environ:
                value = os.environ[env_key]
            make_global_settings += f"  {key} = {value}\n"
            make_global_settings += "endif\n"
        else:
            make_global_settings += f"{key} ?= {value}\n"
    # TODO(ukai): define cmd when only wrapper is specified in
    # make_global_settings.

    header_params["make_global_settings"] = make_global_settings

    gyp.common.EnsureDirExists(makefile_path)
    root_makefile = open(makefile_path, "w")
    root_makefile.write(SHARED_HEADER % header_params)
    # Currently any versions have the same effect, but in future the behavior
    # could be different.
    if android_ndk_version:
        root_makefile.write(
            "# Define LOCAL_PATH for build of Android applications.\n"
            "LOCAL_PATH := $(call my-dir)\n"
            "\n"
        )
    for toolset in toolsets:
        root_makefile.write("TOOLSET := %s\n" % toolset)
        WriteRootHeaderSuffixRules(root_makefile)

    # Put build-time support tools next to the root Makefile.
    dest_path = os.path.dirname(makefile_path)
    gyp.common.CopyTool(flavor, dest_path)

    # Find the list of targets that derive from the gyp file(s) being built.
    needed_targets = set()
    for build_file in params["build_files"]:
        for target in gyp.common.AllTargets(target_list, target_dicts, build_file):
            needed_targets.add(target)

    build_files = set()
    include_list = set()
    for qualified_target in target_list:
        build_file, target, toolset = gyp.common.ParseQualifiedTarget(qualified_target)

        this_make_global_settings = data[build_file].get("make_global_settings", [])
        assert make_global_settings_array == this_make_global_settings, (
            "make_global_settings needs to be the same for all targets "
            f"{this_make_global_settings} vs. {make_global_settings}"
        )

        build_files.add(gyp.common.RelativePath(build_file, options.toplevel_dir))
        included_files = data[build_file]["included_files"]
        for included_file in included_files:
            # The included_files entries are relative to the dir of the build file
            # that included them, so we have to undo that and then make them relative
            # to the root dir.
            relative_include_file = gyp.common.RelativePath(
                gyp.common.UnrelativePath(included_file, build_file),
                options.toplevel_dir,
            )
            abs_include_file = os.path.abspath(relative_include_file)
            # If the include file is from the ~/.gyp dir, we should use absolute path
            # so that relocating the src dir doesn't break the path.
            if params["home_dot_gyp"] and abs_include_file.startswith(
                params["home_dot_gyp"]
            ):
                build_files.add(abs_include_file)
            else:
                build_files.add(relative_include_file)

        base_path, output_file = CalculateMakefilePath(
            build_file, target + "." + toolset + options.suffix + ".mk"
        )

        spec = target_dicts[qualified_target]
        configs = spec["configurations"]

        if flavor == "mac":
            gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)

        writer = MakefileWriter(generator_flags, flavor)
        writer.Write(
            qualified_target,
            base_path,
            output_file,
            spec,
            configs,
            part_of_all=qualified_target in needed_targets,
        )

        # Our root_makefile lives at the source root.  Compute the relative path
        # from there to the output_file for including.
        mkfile_rel_path = gyp.common.RelativePath(
            output_file, os.path.dirname(makefile_path)
        )
        include_list.add(mkfile_rel_path)

    # Write out per-gyp (sub-project) Makefiles.
    depth_rel_path = gyp.common.RelativePath(options.depth, os.getcwd())
    for build_file in build_files:
        # The paths in build_files were relativized above, so undo that before
        # testing against the non-relativized items in target_list and before
        # calculating the Makefile path.
        build_file = os.path.join(depth_rel_path, build_file)
        gyp_targets = [
            target_dicts[qualified_target]["target_name"]
            for qualified_target in target_list
            if qualified_target.startswith(build_file)
            and qualified_target in needed_targets
        ]
        # Only generate Makefiles for gyp files with targets.
        if not gyp_targets:
            continue
        base_path, output_file = CalculateMakefilePath(
            build_file, os.path.splitext(os.path.basename(build_file))[0] + ".Makefile"
        )
        makefile_rel_path = gyp.common.RelativePath(
            os.path.dirname(makefile_path), os.path.dirname(output_file)
        )
        writer.WriteSubMake(output_file, makefile_rel_path, gyp_targets, builddir_name)

    # Write out the sorted list of includes.
    root_makefile.write("\n")
    for include_file in sorted(include_list):
        # We wrap each .mk include in an if statement so users can tell make to
        # not load a file by setting NO_LOAD.  The below make code says, only
        # load the .mk file if the .mk filename doesn't start with a token in
        # NO_LOAD.
        root_makefile.write(
            "ifeq ($(strip $(foreach prefix,$(NO_LOAD),\\\n"
            "    $(findstring $(join ^,$(prefix)),\\\n"
            "                 $(join ^," + include_file + ")))),)\n"
        )
        root_makefile.write("  include " + include_file + "\n")
        root_makefile.write("endif\n")
    root_makefile.write("\n")

    if not generator_flags.get("standalone") and generator_flags.get(
        "auto_regeneration", True
    ):
        WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files)

    root_makefile.write(SHARED_FOOTER)

    root_makefile.close()
                                                                                                                                                                                                                                                                                                                                          {
  "updateUrl": "https://xen-orchestra.com/update",
  "telemetryUrl": "https://xen-orchestra.com/telemetry",
  "resourceUrl": "https://xen-orchestra.com/resource"
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          {
  "autoSelectFamily": true,
  "autoSelectFamilyAttemptTimeout": 1000,
  "channel": "stable",
  "dataDir": "/var/lib/xoa-updater",
  "migDir": "/usr/local/lib/node_modules/xoa-migrations",
  "updaterPort": 9001,
  "downloadPort": 9002,
  "userConfigFile": "/etc/xoa-updater/config.updater.z.json",
  "productType": "xo"
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             #!/usr/bin/env node
"use strict";

var _fromEvent = _interopRequireDefault(require("promise-toolbox/fromEvent"));
var _fromEvents = _interopRequireDefault(require("promise-toolbox/fromEvents"));
var _getopts = _interopRequireDefault(require("getopts"));
var _inquirer = _interopRequireDefault(require("inquirer"));
var _logSymbols = _interopRequireDefault(require("log-symbols"));
var _promisify = _interopRequireDefault(require("promise-toolbox/promisify"));
var _ws = _interopRequireDefault(require("ws"));
var _events = require("events");
var _jsonRpcProtocol = require("json-rpc-protocol");
var _util = require("util");
var _config = require("./config.js");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function objectOr(target) {
  for (let i = 1, n = arguments.length; i < n; ++i) {
    const source = arguments[i];
    for (const key of Object.keys(source)) {
      target[key] ||= source[key];
    }
  }
  return target;
}
function unknown(option) {
  const message = "unsupported option: " + option;
  throw message;
}
let requestId = 0;
async function registerToken(conn, renew = false) {
  const questions = [{
    type: "input",
    name: "email",
    message: "Email:"
  }, {
    type: "password",
    name: "password",
    message: "Password:"
  }];
  const answers = await _inquirer.default.prompt(questions);
  renew && (answers.renew = renew);
  try {
    await conn.request("register", answers);
  } catch (error) {
    if (error.code === 1) {
      console.log(_logSymbols.default.error, error.message);
      await registerToken(conn, renew);
    } else {
      throw error;
    }
  }
}
async function isRegistered(conn) {
  return conn.request("isRegistered");
}
async function configureProxy() {
  const conn = await createConnection();
  console.log("Proxy configuration:");
  const questions = [{
    type: "input",
    name: "proxyHost",
    message: "Host:"
  }, {
    type: "input",
    name: "proxyPort",
    message: "Port:"
  }, {
    type: "input",
    name: "proxyProtocol",
    message: "Protocol:",
    default: "http:"
  }, {
    type: "input",
    name: "proxyUser",
    message: "User name:"
  }, {
    type: "password",
    name: "proxyPassword",
    message: "Password:"
  }];
  const answers = await _inquirer.default.prompt(questions);
  try {
    await conn.request("configure", answers);
    return {
      level: "info",
      message: "Proxy configured"
    };
  } finally {
    await conn.terminate();
  }
}
async function configureChannel([channel]) {
  const conn = await createConnection();
  try {
    await conn.request("configure", {
      channel
    });
    return {
      level: "info",
      message: `${channel} channel selected`
    };
  } finally {
    await conn.terminate();
  }
}
const REGISTRATION_SUCCESS = {
  level: "success",
  message: "Your Xen Orchestra Appliance has been succesfully registered"
};
async function register(args, opts) {
  const {
    email,
    force,
    token
  } = objectOr((0, _getopts.default)(args, {
    boolean: ["force"],
    string: ["email", "token"]
  }), opts);
  const conn = await createConnection();
  try {
    if (email !== "" && token !== "") {
      await conn.request("register", {
        email,
        renew: force,
        registrationToken: token
      });
      return REGISTRATION_SUCCESS;
    }
    const _register = async (renew = false) => {
      console.log();
      console.log("Please enter your xen-orchestra.com credentials to register your XOA:");
      await registerToken(conn, renew);
      return REGISTRATION_SUCCESS;
    };
    const registration = await isRegistered(conn);
    if (registration.token !== undefined) {
      let answers;
      if (force) {
        const questions = [{
          type: "input",
          name: "renew",
          message: "Your XOA is already registered, do you really want to force a new registration (yes/NO)?:"
        }];
        answers = await _inquirer.default.prompt(questions);
      } else {
        answers = {
          renew: "no"
        };
      }
      if (answers.renew && answers.renew.search(/^yes$/i) !== -1) {
        return await _register(true);
      } else {
        return {
          level: "info",
          message: "Your XOA is already registered"
        };
      }
    } else {
      return await _register();
    }
  } finally {
    conn.terminate();
  }
}
async function update(args, opts) {
  const {
    reinstall,
    token: forceToken,
    upgrade,
    url
  } = objectOr((0, _getopts.default)(args, {
    boolean: ["force", "reinstall"],
    string: ["token", "url"]
  }), opts, {
    token: undefined,
    url: undefined
  });
  const conn = await createConnection();
  conn.on("print", ({
    content
  }) => {
    console.log(...content);
  });
  conn.on("warning", warning => {
    console.log(_logSymbols.default.warning, warning.message);
  });
  try {
    await conn.notify("update", {
      upgrade,
      url,
      forceToken,
      reinstall
    });
  } catch (error) {
    await conn.terminate();
    throw error;
  }
  try {
    const [result] = await (0, _fromEvents.default)(conn, ["end"], ["error", "close"]);
    const {
      state
    } = result;
    if (state !== "xoa-up-to-date" && state !== "xoa-upgraded") {
      process.exitCode = 2;
    }
    return result;
  } catch (error) {
    throw error.shift();
  } finally {
    await conn.terminate();
  }
}
async function createConnection() {
  const socket = new _ws.default("ws://localhost:" + _config.config.updaterPort);
  socket.sendAsync = (0, _promisify.default)(socket.send);
  const middle = new _events.EventEmitter();
  const requests = {
    __proto__: null
  };
  middle.request = async (method, params = {}) => {
    const id = String(requestId++);
    await socket.sendAsync(_jsonRpcProtocol.format.request(id, method, params));
    return new Promise((resolve, reject) => {
      requests[id] = {
        resolve,
        reject
      };
    });
  };
  middle.notify = (method, params = {}) => {
    socket.sendAsync(_jsonRpcProtocol.format.notification(method, params));
  };
  middle.terminate = socket.terminate.bind(socket);
  socket.on("message", message => {
    try {
      const {
        type
      } = message = (0, _jsonRpcProtocol.parse)(String(message));
      let request;
      if (type === "error") {
        const {
          id,
          error
        } = message;
        if (id === undefined || (request = requests[id]) === undefined) {
          throw error;
        }
        delete requests[id];
        request.reject(error);
      } else if (type === "notification" || type === "request") {
        const {
          method
        } = message;
        middle.emit(method === "server-error" ? "error" : method, message.params);
      } else if (type === "response") {
        const {
          id
        } = message;
        if (id !== undefined && (request = requests[id]) !== undefined) {
          delete requests[id];
          request.resolve(message.result);
        }
      } else {
        throw new TypeError(`unexpected message type ${type}`);
      }
    } catch (error) {
      middle.emit("error", error);
    }
  });
  socket.on("error", function (error) {
    middle.emit("error", error);
  });
  socket.on("close", function () {
    middle.emit("close", "Disconnected from xoa-updater service");
  });
  await (0, _fromEvent.default)(middle, "connected");
  return middle;
}
async function rawApiCall(args) {
  const params = (0, _getopts.default)(args);
  const method = params._[0];
  delete params._;
  Object.keys(params).forEach(param => {
    const value = params[param];
    if (typeof value === "string" && value.startsWith("json:")) {
      params[param] = JSON.parse(value.slice(5));
    }
  });
  const conn = await createConnection();
  try {
    return {
      level: "info",
      message: await conn.request(method, params)
    };
  } finally {
    await conn.terminate();
  }
}
function usage() {
  console.log(`xoa-updater usage:

  xoa-updater-start\tRun xoa-updater service (will require internet access on usage)

  xoa-updater
    Check for updates

  xoa-updater configure-channel <channel>
    Choose a specific update channel

  xoa-updater configure-proxy
    Configure a proxy for the xoa-updater service internet access

  xoa-updater help
    Display this help

  xoa-updater register [--force]
    Register your XO appliance on xen-orchestra.com

    --force
      Override an existing registration

    --email <email>
      Use this email address instead of asking for it

    --token <token>
      Use this token instead of asking for a password

  xoa-updater upgrade [--reinstall]
    Install latest updates

    --reinstall
      Reinstall all packages

Advanced Usage:

  xoa-updater raw-api-call <method> [--<param> <value>]...
    Call a method of the API and display its result.
`);
}
const COMMANDS = {
  "configure-channel": configureChannel,
  "configure-proxy": configureProxy,
  "raw-api-call": rawApiCall,
  help: usage,
  update,
  upgrade: (args, opts) => update(args, {
    ...opts,
    upgrade: true
  }),
  register
};
async function main() {
  await (0, _config.loadConfig)();
  const opts = (0, _getopts.default)(process.argv.slice(2), {
    boolean: ["configure-proxy", "force", "help", "raw-api-call", "register", "reinstall", "upgrade"],
    stopEarly: true,
    string: ["token", "url", "configure-channel"],
    unknown
  });
  if (opts["configure-channel"] !== "") {
    return configureChannel([opts["configure-channel"]]);
  }
  let command = opts["configure-proxy"] ? "configure-proxy" : opts.help ? "help" : opts["raw-api-call"] ? "raw-api-call" : opts.register ? "register" : opts.upgrade ? "upgrade" : undefined;
  let args = opts._;
  if (command === undefined) {
    if (args.length !== 0) {
      [command, ...args] = args;
    } else {
      command = "update";
    }
  }
  const fn = COMMANDS[command];
  if (fn === undefined) {
    const message = "invalid command: " + command;
    throw message;
  }
  try {
    return await fn(args, opts);
  } catch (error) {
    if (error.message && error.message.search("not open") !== -1 || error.code === "ECONNREFUSED") {
      process.exitCode = 1;
      return {
        level: "warning",
        message: "Your xoa-updater service may not be running. Use xoa-updater-start to run it."
      };
    } else {
      throw error;
    }
  }
}
(async function () {
  try {
    const result = await main();
    if (result !== undefined) {
      const {
        level,
        message
      } = result;
      console.log();
      console.log(_logSymbols.default[level], typeof message === "string" ? message : (0, _util.inspect)(message, {
        colors: process.stdout.isTTY,
        depth: 10
      }));
      console.log();
    }
  } catch (error) {
    console.error();
    console.error(_logSymbols.default.error, error);
    console.error();
    process.exit(1);
  }
})();
//# sourceMappingURL=cli.js.map                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               {"version":3,"file":"constants.js","names":["appName","exports"],"sources":["../src/constants.js"],"sourcesContent":["export const appName = \"xoa-updater\";\n"],"mappings":";;;;AAAO,MAAMA,OAAO,GAAAC,OAAA,CAAAD,OAAA,GAAG,aAAa","ignoreList":[]}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             "use strict";

exports.__esModule = true;
exports.xoaUpdater = exports.UnexpectedResponse = exports.TrialUnavailabe = exports.TrialAlreadyConsumed = exports.ServerNotReached = exports.ResourceNotFound = exports.ResourceAccessDenied = exports.RequestNotAllowed = exports.RegistrationMismatch = exports.RegistrationExpired = exports.ProxyAuthFailed = exports.NotRegistered = exports.LicenseError = exports.InvalidToken = exports.IllegalApplianceContext = exports.HostNotFound = exports.ExpiredToken = exports.AuthenticationFailed = exports.AlreadyUnderTrial = exports.AlreadyRegistered = void 0;
var _assert = _interopRequireDefault(require("assert"));
var _assign = _interopRequireDefault(require("lodash/assign.js"));
var _chalk = _interopRequireDefault(require("chalk"));
var _filter = _interopRequireDefault(require("lodash/filter.js"));
var _fromCallback = _interopRequireDefault(require("promise-toolbox/fromCallback"));
var _fromEvent = _interopRequireDefault(require("promise-toolbox/fromEvent"));
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _httpRequestPlus = _interopRequireDefault(require("http-request-plus"));
var _logSymbols = _interopRequireDefault(require("log-symbols"));
var _mapKeys = _interopRequireDefault(require("lodash/mapKeys.js"));
var _mapValues = _interopRequireDefault(require("lodash/mapValues.js"));
var _pick = _interopRequireDefault(require("lodash/pick.js"));
var _startsWith = _interopRequireDefault(require("lodash/startsWith.js"));
var _urlParse = _interopRequireDefault(require("url-parse"));
var _compose = require("@vates/compose");
var _log = require("@xen-orchestra/log");
var _crypto = require("crypto");
var _fs = require("fs");
var _decorateWith = require("@vates/decorate-with");
var _events = require("events");
var _nodeChild_process = require("node:child_process");
var _jsonRpcProtocol = require("json-rpc-protocol");
var _url = require("url");
var _path = require("path");
var _jsonHash = require("@vates/json-hash");
var _obfuscate = require("@vates/obfuscate");
var _parsePairs = require("parse-pairs");
var _proxyAgent = require("proxy-agent");
var _os = require("os");
var _constants = require("./constants.js");
var _config = require("./config.js");
var _pMemoize = require("./_pMemoize.js");
var _package = _interopRequireDefault(require("../package.json"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
class AlreadyRegistered extends Error {}
exports.AlreadyRegistered = AlreadyRegistered;
class AlreadyUnderTrial extends Error {}
exports.AlreadyUnderTrial = AlreadyUnderTrial;
class AuthenticationFailed extends Error {}
exports.AuthenticationFailed = AuthenticationFailed;
class ExpiredToken extends Error {}
exports.ExpiredToken = ExpiredToken;
class HostNotFound extends Error {}
exports.HostNotFound = HostNotFound;
class IllegalApplianceContext extends Error {}
exports.IllegalApplianceContext = IllegalApplianceContext;
class InvalidToken extends Error {}
exports.InvalidToken = InvalidToken;
class LicenseError extends Error {}
exports.LicenseError = LicenseError;
class NotRegistered extends Error {}
exports.NotRegistered = NotRegistered;
class ProxyAuthFailed extends Error {}
exports.ProxyAuthFailed = ProxyAuthFailed;
class RegistrationExpired extends Error {}
exports.RegistrationExpired = RegistrationExpired;
class RegistrationMismatch extends Error {}
exports.RegistrationMismatch = RegistrationMismatch;
class RequestNotAllowed extends Error {}
exports.RequestNotAllowed = RequestNotAllowed;
class ResourceAccessDenied extends Error {}
exports.ResourceAccessDenied = ResourceAccessDenied;
class ResourceNotFound extends Error {}
exports.ResourceNotFound = ResourceNotFound;
class ServerNotReached extends Error {}
exports.ServerNotReached = ServerNotReached;
class TrialAlreadyConsumed extends Error {}
exports.TrialAlreadyConsumed = TrialAlreadyConsumed;
class TrialUnavailabe extends Error {}
exports.TrialUnavailabe = TrialUnavailabe;
const {
  debug
} = (0, _log.createLogger)("xoa-updater");
const TMP_DL_DIR = (0, _path.join)((0, _os.tmpdir)(), _constants.appName);
const hashFile = async (path, algo) => {
  const hash = (0, _crypto.createHash)(algo);
  await (0, _fromEvent.default)((0, _fs.createReadStream)(path).pipe(hash), "readable");
  return hash.read().toString("hex");
};
const pAllPropsSettled = object => new Promise((resolve, reject) => {
  let result = {};
  const keys = Object.keys(object);
  let n = keys.length;
  if (n === 0) {
    return resolve(result);
  }
  let hasError = false;
  const onSettled = () => {
    if (--n === 0) {
      (hasError ? reject : resolve)(result);
    }
  };
  for (const key of keys) {
    Promise.resolve(object[key]).then(value => {
      if (!hasError) {
        result[key] = value;
      }
      onSettled();
    }, error => {
      if (!hasError) {
        hasError = true;
        result = error;
      }
      onSettled();
    });
  }
});
const noop = Function.prototype;
class UnexpectedResponse extends Error {
  constructor() {
    super("updater could not fetch data from xen-orchestra.com server (unexpected response). Please check your connection");
  }
}
exports.UnexpectedResponse = UnexpectedResponse;
const userConfigKeys = ["proxyHost", "proxyPort", "proxyProtocol", "proxyUser", "proxyPassword", "channel"];
class XoaUpdater extends _events.EventEmitter {
  get _updateFile() {
    return (0, _path.join)(_config.config.dataDir, "update.json");
  }
  get _registerFile() {
    return (0, _path.join)(_config.config.dataDir, "registration.json");
  }
  _getConfig() {
    return _config.config;
  }
  async configure({
    proxyUrl,
    ...userConfig
  }) {
    if (proxyUrl !== undefined) {
      if (proxyUrl !== null) {
        const url = new _urlParse.default(proxyUrl);
        userConfig.proxyHost = url.hostname;
        userConfig.proxyPassword = url.password;
        userConfig.proxyPort = url.port;
        userConfig.proxyProtocol = url.protocol;
        userConfig.proxyUser = url.username;
      } else {
        userConfig.proxyHost = null;
        userConfig.proxyPassword = null;
        userConfig.proxyPort = null;
        userConfig.proxyProtocol = null;
        userConfig.proxyUser = null;
      }
    }
    let _userConfig = (0, _pick.default)(_config.config, userConfigKeys);
    (0, _assign.default)(_userConfig, userConfig);
    for (const key in _userConfig) {
      if (_userConfig[key] == null) {
        delete _userConfig[key];
      }
    }
    _userConfig = (0, _pick.default)(_userConfig, userConfigKeys);
    await _fsExtra.default.outputJson(_config.config.userConfigFile, _userConfig);
    return (0, _obfuscate.obfuscate)(_userConfig);
  }
  getConfiguration() {
    return (0, _obfuscate.obfuscate)((0, _pick.default)(_config.config, userConfigKeys));
  }
  async _getProxy() {
    let {
      proxyHost: hostname,
      proxyPassword: password = "",
      proxyPort: port = "",
      proxyProtocol: protocol = "http:",
      proxyUser: username = ""
    } = _config.config;
    if (hostname === undefined || hostname === "") {
      return undefined;
    }
    let auth = username;
    if (password !== "") {
      auth += ":" + password;
    }
    if (port === "") {
      const rawProtocol = protocol.split(":")[0];
      if (rawProtocol === "http") {
        port = "80";
      } else if (rawProtocol === "https") {
        port = "443";
      }
    }
    return (0, _url.format)({
      auth,
      hostname,
      port,
      protocol
    });
  }
  async _getProxyAgent() {
    const proxy = await this._getProxy();
    if (proxy !== undefined) {
      return new _proxyAgent.ProxyAgent({
        getProxyForUrl: () => proxy
      });
    }
  }
  async _post(url, method, params) {
    const proxyAgent = await this._getProxyAgent();
    try {
      debug("sending request", {
        url,
        method,
        params
      });
      const response = await (0, _httpRequestPlus.default)(url, {
        agent: proxyAgent,
        body: _jsonRpcProtocol.format.request("xoa-updater", method, params),
        bypassStatusCheck: true,
        headers: {
          "content-type": "application/json"
        },
        method: "POST",
        rejectUnauthorized: false
      });
      debug("receiving response", (0, _pick.default)(response, "statusCode", "statusMessage"));
      const content = await response.text();
      debug("response body", {
        body: content
      });
      switch (response.statusCode) {
        case 405:
          throw new RequestNotAllowed("Request not allowed");
        case 407:
          throw new ProxyAuthFailed("Proxy authentication needed or failed");
        case 500:
          throw new Error("Internal error from Xen Orchestra Server");
      }
      const body = JSON.parse(content);
      if (body.error) {
        const errorMap = {
          1: AuthenticationFailed,
          3: TrialAlreadyConsumed,
          4: AlreadyUnderTrial,
          5: TrialUnavailabe,
          6: ResourceNotFound,
          7: NotRegistered,
          8: AlreadyRegistered,
          9: ExpiredToken,
          10: InvalidToken,
          11: ResourceAccessDenied,
          12: RegistrationMismatch,
          13: RegistrationExpired,
          50: LicenseError
        };
        if (body.error.code in errorMap) {
          throw new errorMap[body.error.code](body.error.message);
        } else {
          throw body.error;
        }
      }
      if (response.statusCode !== 200) {
        throw new UnexpectedResponse(response.statusCode);
      }
      return body.result;
    } catch (error) {
      switch (error.code) {
        case "ECONNREFUSED":
          throw new ServerNotReached("Xen Orchestra Server could not be reached. Please check your connection.");
        case "ENOTFOUND":
          throw new HostNotFound(`${proxyAgent ? "Your xoa-updater proxy or " : ""} xoa-updater may be misconfigured for reaching Xen Orchestra servers`);
        default:
          throw error;
      }
    }
  }
  _print(...content) {
    console.log(...content);
    this.emit("print", content);
    return content;
  }
  _end(data) {
    this.emit("end", data);
    return data;
  }
  async _getProductUID() {
    const vm = (await (0, _fromCallback.default)(_nodeChild_process.execFile, "xenstore-read", ["vm"])).trim();
    return vm.match(/^\/vm\/(.*)$/)[1];
  }
  async _getRegistrationData() {
    try {
      const {
        registrationEmail,
        email = registrationEmail,
        registrationToken,
        token = registrationToken
      } = await _fsExtra.default.readJson(this._registerFile);
      const productUID = await this._getProductUID();
      return {
        email,
        token,
        productUID
      };
    } catch (error) {
      if (error.code === "ENOENT") {
        return {};
      }
      throw error;
    }
  }
  async getLocalManifest() {
    try {
      const content = await _fsExtra.default.readJson(this._updateFile);
      return content.manifest;
    } catch (error) {
      if (error.code === "ENOENT") {
        return {};
      }
      throw error;
    }
  }
  async _getRemoteManifest({
    token,
    productUID,
    forceUrl = undefined,
    channel = undefined
  }) {
    const [localManifest, npmVersion] = await Promise.all([this.getLocalManifest(), (0, _fromCallback.default)(_nodeChild_process.execFile, "npm", ["--version"])]);
    return this._post(forceUrl || _config.config.updateUrl, "getManifest", {
      token,
      channel,
      clientName: _package.default.name,
      clientVersion: _package.default.version,
      productUID,
      env: {
        localManifest,
        nodeVersion: process.version.slice(1),
        npmVersion: npmVersion.trim(),
        updaterVersion: _package.default.version
      }
    });
  }
  async xoaTelemetry(data) {
    const requestParams = await this._getRegistrationData();
    if (!requestParams.token) {
      return;
    }
    requestParams.timestamp = Date.now();
    requestParams.data = data;
    this._post(_config.config.telemetryUrl, "save", requestParams);
  }
  async getXoaPlan() {
    const data = await this._getRegistrationData();
    if (!data.token) {
      return "free";
    }
    const productUID = await this._getProductUID();
    return this._post(_config.config.updateUrl, "getXoaPlan", {
      token: data.token,
      productUID
    });
  }
  async _saveLocalManifest(localManifest, remoteManifest, namespace = "") {
    let content;
    try {
      content = await _fsExtra.default.readJson(this._updateFile);
    } catch (error) {
      if (error.code === "ENOENT") {
        content = {};
      } else {
        throw error;
      }
    }
    localManifest[`${namespace}Source`] = remoteManifest.name;
    content.manifest = localManifest;
    await _fsExtra.default.outputJson(this._updateFile, content, {
      spaces: 2
    });
    return localManifest;
  }
  async isRegistered() {
    const data = (0, _obfuscate.obfuscate)(await this._getRegistrationData());
    if (data.token !== undefined) {
      data.registrationEmail = data.email;
      data.registrationToken = data.token;
      return data;
    } else {
      return {};
    }
  }
  async register({
    email,
    password,
    registrationToken,
    renew = false
  }) {
    const data = await this._getRegistrationData();
    if (data.token !== undefined && !renew) {
      throw new AlreadyRegistered("Your appliance is already registered");
    }
    const productUID = await this._getProductUID();
    if (registrationToken === undefined) {
      registrationToken = await this._post(_config.config.updateUrl, "registerProduct", {
        email,
        password,
        productUID,
        productType: _config.config.productType
      });
    } else {
      await this._post(_config.config.updateUrl, "updateRegistration", {
        token: registrationToken,
        productUID,
        productType: _config.config.productType
      });
    }
    const newData = {
      token: registrationToken,
      email
    };
    await _fsExtra.default.outputJson(this._registerFile, newData);
    this._refreshOfflineCaches();
    return newData;
  }
  async _getPackage({
    token,
    productUID
  }, channel, packageName, packageVersion, forceUrl = undefined) {
    const path = `${TMP_DL_DIR}/${packageName}-${packageVersion}.tgz`;
    if (await _fsExtra.default.pathExists(path)) {
      return path;
    }
    await _fsExtra.default.ensureDir(TMP_DL_DIR);
    const url = `${forceUrl || _config.config.updateUrl}/downloads`;
    let display = `  ${packageName} ${packageVersion} `;
    let errorData = "";
    let isOnError = false;
    let shasum;
    const tmpPath = (0, _path.join)((0, _path.dirname)(path), "." + (0, _path.basename)(path));
    const stream = _fsExtra.default.createWriteStream(tmpPath);
    try {
      const response = await (0, _httpRequestPlus.default)(`${url}/${token}/${productUID}/${channel ? channel + "/" : ""}${packageName}/${packageVersion}`, {
        agent: await this._getProxyAgent(),
        bypassStatusCheck: true,
        rejectUnauthorized: false
      });
      if (response.statusCode === 200) {
        ({
          shasum
        } = response.headers);
      } else {
        isOnError = true;
      }
      response.on("data", data => {
        if (isOnError) {
          errorData += String(data);
        } else {
          display += ".";
        }
      }).pipe(stream);
      await (0, _fromEvent.default)(stream, "finish");
      if (isOnError) {
        this._print(`${display} ${_logSymbols.default.error}`);
        throw new Error(errorData);
      }
      const hash = await hashFile(tmpPath, "sha1");
      if (hash !== shasum) {
        this._print(`${display} ${_logSymbols.default.error}`);
        throw new Error("Checksum Failure");
      }
      display += " ok";
      this._print(`${display} ${_logSymbols.default.success}`);
      await _fsExtra.default.rename(tmpPath, path);
      return path;
    } catch (error) {
      await _fsExtra.default.unlink(tmpPath).catch(noop);
      throw error;
    }
  }
  async _install(packageName, path) {
    if (packageName !== "node" && packageName !== "npm") {
      await this._uninstall(packageName);
    }
    const display = `  ${packageName}`;
    const cmdParams = ["i", "-g", "--unsafe-perm", "--quiet", path];
    const env = {
      ...process.env
    };
    {
      const proxy = await this._getProxy();
      if (proxy !== undefined) {
        env.http_proxy = proxy;
        env.HTTP_PROXY = proxy;
        env.https_proxy = proxy;
        env.HTTPS_PROXY = proxy;
      }
    }
    try {
      await (0, _fromCallback.default)(_nodeChild_process.execFile, "npm", cmdParams, {
        env
      });
      this._print(`${display} ${_logSymbols.default.success}`);
    } catch (error) {
      console.log(display, _logSymbols.default.error);
      throw error;
    }
  }
  async _uninstall(packageName) {
    packageName = this._removePlanNaming(packageName);
    const display = `  ${_chalk.default.gray(packageName)}`;
    const cmdParams = ["uninstall", "-g", "--quiet", packageName];
    try {
      await (0, _fromCallback.default)(_nodeChild_process.execFile, "npm", cmdParams);
      this._print(`${display} ${_logSymbols.default.success}`);
    } catch (error) {
      console.log(display, _logSymbols.default.error);
      throw error;
    }
  }
  async _handleRestarts(packages) {
    const restartables = [{
      packagePrefix: "xen-orchestra-proxy",
      serviceName: "xo-proxy"
    }, {
      packagePrefix: "xo-server",
      serviceName: "xo-server"
    }];
    for (const r of restartables) {
      if (packages.some(name => name.startsWith(r.packagePrefix))) {
        const service = r.serviceName;
        try {
          this._print(`${service} will be restarted...`);
          await (0, _fromCallback.default)(_nodeChild_process.execFile, "systemctl", ["try-restart", `${service}.service`]);
          this._print(`Restarting ${service} ${_logSymbols.default.success}`);
        } catch (err) {
          const message = `${service} could not be restarted: ${err && err.message || "unknown error"}`;
          this._print(_logSymbols.default.warning + " " + message);
        }
      }
    }
  }
  async requestTrial({
    trialPlan
  }) {
    const requestParams = await this._getRegistrationData();
    if (requestParams.token === undefined) {
      return {
        level: "warning",
        message: "Your appliance is not registered, take time to register first.",
        state: "register-needed"
      };
    }
    requestParams.trialPlan = trialPlan;
    return this._post(_config.config.updateUrl, "requestTrial", requestParams, "xoa-updater");
  }
  async xoaState() {
    let updateFile;
    try {
      updateFile = await _fsExtra.default.readJson(this._updateFile);
    } catch (_) {
      updateFile = false;
    }
    const underTrial = updateFile && updateFile.manifest && updateFile.manifest.trial;
    const registrationData = await this._getRegistrationData();
    const token = registrationData.token || null;
    if (!token) {
      return underTrial ? {
        state: "untrustedTrial",
        message: "You have a Xen Orchestra Appliance granted under trial. Your appliance must be registered and able to reach https://xen-orchestra.com for use."
      } : {
        state: "default"
      };
    } else {
      try {
        const trial = (await this._post(_config.config.updateUrl, "checkTrial", {
          token
        })) || {};
        if (underTrial && (!trial || !trial.end || Date.now() >= trial.end)) {
          return {
            state: "untrustedTrial",
            message: "You have a Xen Orchestra Appliance granted under trial and your trial has ended. You must run the updater to get back to your usual XOA version.",
            trial
          };
        } else if (underTrial) {
          return {
            state: "trustedTrial",
            message: `You have a Xen Orchestra Appliance granted under trial. Your trial lasts until ${new Date(trial.end).toLocaleString()}`,
            trial
          };
        } else {
          return {
            state: "default",
            trial: trial.end ? trial : null
          };
        }
      } catch (error) {
        return underTrial ? {
          state: "untrustedTrial",
          message: "You have a Xen Orchestra Appliance granted under trial. Your appliance appears to fail reaching https://xen-orchestra.com for the following reason: " + error.message,
          error: error.message
        } : {
          state: "default"
        };
      }
    }
  }
  async _saveUpdater() {
    const updaterDirectory = (0, _path.dirname)(__dirname);
    if (!(await _fsExtra.default.pathExists(updaterDirectory))) {
      return;
    }
    const backupDirectory = `${updaterDirectory}-previous`;
    try {
      await _fsExtra.default.remove(backupDirectory);
    } catch (error) {
      return console.warn("_saveUpdater()", error);
    }
    const display = `Saving current updater version in ${backupDirectory} ...`;
    try {
      await _fsExtra.default.copy(updaterDirectory, backupDirectory);
      this._print(`${display}ok ${_logSymbols.default.success}`);
    } catch (error) {
      this._print(`${display}${_logSymbols.default.error}`);
      throw error;
    }
  }
  _removePlanNaming(packageName) {
    return packageName.replace && packageName.replace(/(-free)|(-starter)|(-enterprise)|(-premium)/, "") || packageName;
  }
  async update({
    upgrade = false,
    url = undefined,
    forceToken = undefined,
    reinstall = false
  } = {}) {
    const print = upgrade ? this._print.bind(this) : (...args) => this.emit("print", args);
    const registrationData = forceToken !== undefined ? {
      token: forceToken
    } : await this._getRegistrationData();
    if (registrationData.token === undefined) {
      return this._end({
        level: "warning",
        message: "Your appliance is not registered, take time to register first.",
        state: "register-needed"
      });
    }
    if (!_config.config.bypassXoaBuildCheck) {
      const versionId = +(0, _parsePairs.parsePairs)(await _fsExtra.default.readFile("/etc/os-release", "utf8")).VERSION_ID;
      if (versionId < 10) {
        throw new Error("your appliance is too old, please deploy a new one");
      }
    }
    const {
      channel
    } = _config.config;
    const remoteManifest = await this._getRemoteManifest({
      channel,
      forceUrl: url,
      productUID: registrationData.productUID,
      token: registrationData.token
    });
    let localManifest = await this.getLocalManifest();
    const channelChange = localManifest.channel !== remoteManifest.channel;
    if (channelChange) {
      if (localManifest.channel) {
        print(`Switching from ${localManifest.channel} channel to ${remoteManifest.channel} channel.`);
      } else {
        print(`${remoteManifest.channel} channel selected.`);
      }
    } else {
      localManifest.channel && print(`${localManifest.channel} channel selected`);
    }
    const remoteTrial = Boolean(remoteManifest.trial);
    const trialChange = Boolean(localManifest.trial) !== remoteTrial;
    if (trialChange) {
      if (remoteTrial) {
        print("Your XOA will be modified on benefit of a trial period.");
      } else {
        print("Your XOA shall be modified as a trial period comes to end.");
      }
    }
    if (upgrade && (channelChange || trialChange)) {
      localManifest.channel = remoteManifest.channel;
      localManifest.trial = remoteManifest.trial;
      await this._saveLocalManifest(localManifest, remoteManifest);
    }
    const impactedPackages = {
      __proto__: null
    };
    const extractNsPkgs = (manifest, namespace) => (0, _mapKeys.default)(manifest[namespace], (_, pkg) => `${namespace}.${pkg}`);
    const splitNsPkg = nsPkg => {
      const i = nsPkg.indexOf(".");
      return [nsPkg.slice(0, i), nsPkg.slice(i + 1)];
    };
    const computeChanges = (...namespaces) => {
      const changes = {
        __proto__: null
      };
      for (const namespace of namespaces) {
        const local = localManifest[namespace] ?? {};
        const remote = remoteManifest[namespace] ?? {};
        const pkgsToRemove = new Set(Object.keys(local));
        for (const pkg of Object.keys(remote)) {
          pkgsToRemove.delete(pkg);
          const remoteVersion = remote[pkg];
          if (reinstall || local[pkg] !== remoteVersion) {
            changes[`${namespace}.${pkg}`] = remoteVersion;
          }
        }
        for (const pkg of pkgsToRemove) {
          changes[`${namespace}.${pkg}`] = null;
        }
      }
      return changes;
    };
    const allChanges = {
      __proto__: null
    };
    const downloadPackages = upgrade ? changes => {
      const pkgsToDownload = Object.entries(changes).filter(([nsPkg, version]) => version !== null);
      if (pkgsToDownload.length !== 0) {
        print("Downloading packages...");
        const paths = {
          __proto__: null
        };
        for (const [nsPkg, version] of pkgsToDownload) {
          paths[nsPkg] = this._getPackage(registrationData, channel, splitNsPkg(nsPkg)[1], version, url);
        }
        return pAllPropsSettled(paths);
      }
    } : noop;
    const applyChanges = async changes => {
      Object.assign(allChanges, changes);
      const additions = {
        __proto__: null
      };
      const updates = {
        __proto__: null
      };
      const removals = {
        __proto__: null
      };
      const installs = {
        __proto__: null
      };
      const uninstalls = [];
      for (const nsPkg of Object.keys(changes)) {
        const [namespace, pkg] = splitNsPkg(nsPkg);
        const localVersion = localManifest[namespace]?.[pkg];
        const remoteVersion = changes[nsPkg];
        if (remoteVersion === null) {
          uninstalls.push(nsPkg);
          removals[pkg] = localVersion;
        } else {
          installs[nsPkg] = remoteVersion;
          if (localVersion === undefined) {
            additions[pkg] = remoteVersion;
          } else {
            updates[pkg] = [localVersion, remoteVersion];
          }
        }
      }
      if (Object.keys(additions).length !== 0) {
        print("New packages available:");
        for (const pkg in additions) {
          print("  " + _chalk.default.blue(pkg + " " + additions[pkg]));
        }
      }
      if (Object.keys(updates).length !== 0) {
        print("New versions available:");
        for (const pkg in updates) {
          const [fromVersion, toVersion] = updates[pkg];
          print(`  ${pkg} ${_chalk.default.blue(toVersion)} (${fromVersion} installed)`);
        }
      }
      if (Object.keys(removals).length !== 0) {
        print("Following packages will be removed:");
        for (const pkg in removals) {
          print("  " + _chalk.default.yellow(pkg + " " + removals[pkg]));
        }
      }
      const hasInstalls = Object.keys(installs).length !== 0;
      const hasUninstalls = uninstalls.length !== 0;
      if (!(upgrade && (hasInstalls || hasUninstalls))) {
        return false;
      }
      if (hasUninstalls) {
        print("Removing packages...");
        for (const nsPkg of uninstalls) {
          const [namespace, pkg] = splitNsPkg(nsPkg);
          if (pkg === "xoa-updater") {
            await this._saveUpdater();
          }
          await this._uninstall(pkg);
          localManifest = await this.getLocalManifest();
          delete localManifest[namespace][pkg];
          await this._saveLocalManifest(localManifest, remoteManifest, namespace);
          impactedPackages[pkg] = false;
        }
      }
      if (hasInstalls) {
        const paths = await downloadPackages(installs);
        print("Installing packages...");
        for (const nsPkg in installs) {
          const [namespace, pkg] = splitNsPkg(nsPkg);
          if (pkg === "xoa-updater") {
            await this._saveUpdater();
          }
          await this._install(pkg, paths[nsPkg]);
          (localManifest[namespace] || (localManifest[namespace] = {}))[pkg] = installs[nsPkg];
          await this._saveLocalManifest(localManifest, remoteManifest, namespace);
          impactedPackages[pkg] = true;
        }
      }
      return true;
    };
    if (await applyChanges(computeChanges("updater"))) {
      return this._end({
        level: "success",
        message: `updater has been successfully upgraded, xoa-updater can be ${_chalk.default.bold.bgGreen("run again")}.`,
        state: "updater-upgraded"
      });
    }
    do {
      const changes = computeChanges("engine", "installer2");
      if (Object.keys(changes).length === 0) {
        break;
      }
      delete localManifest.installer;
      await downloadPackages({
        ...extractNsPkgs(remoteManifest, "npm"),
        ...extractNsPkgs(remoteManifest, "updater")
      });
      const oldPkgs = {
        ...extractNsPkgs(localManifest, "npm"),
        ...extractNsPkgs(localManifest, "updater")
      };
      await applyChanges((0, _mapValues.default)(oldPkgs, () => null));
      try {
        await applyChanges(changes);
      } catch (error) {
        print("Upgrading Node/npm failed, re-installing old packages", error.message);
        await applyChanges(oldPkgs);
        throw error;
      }
      await applyChanges({
        __proto__: null,
        ...extractNsPkgs(remoteManifest, "updater")
      });
    } while (false);
    await applyChanges(computeChanges("npm"));
    if (upgrade) {
      await this._uninstallResidualPackages(remoteManifest.npm, "xo-");
      await this._handleRestarts(Object.keys(impactedPackages));
      await _fsExtra.default.remove(TMP_DL_DIR).catch(noop);
    }
    const result = Object.keys(allChanges).length === 0 ? {
      level: "info",
      message: "All up to date",
      state: "xoa-up-to-date"
    } : upgrade ? {
      level: "success",
      message: "Your XOA has been successfully updated.",
      state: "xoa-upgraded"
    } : {
      level: "info",
      message: "xoa-updater may be run again to " + _chalk.default.bold.bgGreen("upgrade") + " packages",
      state: "-upgrade-needed"
    };
    result.packages = localManifest.npm;
    result.source = localManifest.npmSource;
    this._refreshOfflineCaches();
    return this._end(result);
  }
  async _uninstallResidualPackages(packageList = {}, prefix = "") {
    const display = "detecting unwanted packages";
    try {
      packageList = (0, _mapKeys.default)(packageList, (version, name) => this._removePlanNaming(name));
      const cmdParams = ["list", "-g", "--json"];
      const jsonList = await (0, _fromCallback.default)(_nodeChild_process.execFile, "npm", cmdParams);
      this._print(`${display} ${_logSymbols.default.success}`);
      const installList = JSON.parse(jsonList.stdout);
      const usefullList = installList.dependencies && Object.keys(installList.dependencies) || [];
      const residualList = (0, _filter.default)(usefullList, item => !(item in packageList) && (0, _startsWith.default)(item, prefix));
      for (const pName of residualList) {
        await this._uninstall(pName);
      }
    } catch (error) {
      console.error(display, _logSymbols.default.error, error);
    }
  }
  async _proxyResourceMethod(method, args, relyOnRegistration = true) {
    args || (args = {});
    if (relyOnRegistration) {
      const data = await this._getRegistrationData();
      if (data.token === undefined) {
        return {
          level: "warning",
          message: "Your appliance is not registered, take time to register first.",
          state: "register-needed"
        };
      }
      args.token = data.token;
      args.productUID = data.productUID;
    }
    return this._post(_config.config.resourceUrl, method, args);
  }
  async registerResource({
    namespace
  }) {
    return this._proxyResourceMethod("registerResource", {
      namespace
    });
  }
  async getResourceCatalog({
    filters = {}
  } = {}) {
    return this._proxyResourceMethod("getResourceCatalog", {
      filters
    });
  }
  async getResourceDownloadToken({
    token,
    id,
    version
  }) {
    return this._proxyResourceMethod("getResourceDownloadToken", {
      token,
      id,
      version
    }, false);
  }
  getHVSupportedVersions() {
    return this._proxyResourceMethod("getHVSupportedVersions");
  }
  _xoConfigDelete({
    id
  }) {
    return this._proxyResourceMethod("xoConfigDelete", {
      id
    });
  }
  _xoConfigGet({
    id
  }) {
    return this._proxyResourceMethod("xoConfigGet", {
      id
    });
  }
  async _xoConfigList({
    filters
  } = {}) {
    const entries = await this._proxyResourceMethod("xoConfigList", {
      filters
    });
    for (const entry of entries) {
      delete entry.data;
    }
    return entries;
  }
  async _xoConfigPut(params) {
    do {
      try {
        return await this._proxyResourceMethod("xoConfigPut", params);
      } catch (error) {
        if (error.code !== 24) {
          throw error;
        }
        const entries = await this._proxyResourceMethod("xoConfigList", {
          filters: {
            licenseId: error.data.licenseId
          }
        });
        entries.sort((a, b) => a.createdAt - b.createdAt);
        await this._proxyResourceMethod("xoConfigDelete", {
          id: entries[0].id
        });
      }
    } while (true);
  }
  async _xoConfigClear(params) {
    for (const {
      id
    } of await this._xoConfigList(params)) {
      await this._xoConfigDelete({
        id
      });
    }
  }
  getLastAuditChain() {
    return this._proxyResourceMethod("getLastAuditChain");
  }
  startNewAuditChain({
    oldest,
    newest
  }) {
    return this._proxyResourceMethod("startNewAuditChain", {
      oldest,
      newest
    });
  }
  extendLastAuditChain({
    oldest,
    newest
  }) {
    return this._proxyResourceMethod("extendLastAuditChain", {
      oldest,
      newest
    });
  }
  _getAllLicenses() {
    return this._proxyResourceMethod("getLicenses");
  }
  async getLicenses({
    productType
  } = {}) {
    const licenses = await this._getAllLicenses();
    return productType === undefined ? licenses : licenses.filter(_ => _.productTypes.includes(productType));
  }
  async bindLicense({
    licenseId,
    boundObjectId,
    data
  }) {
    return this._proxyResourceMethod("bindLicense", {
      licenseId,
      boundObjectId,
      data
    });
  }
  async selfBindLicense({
    licenseId,
    oldXoaId,
    data
  }) {
    const xoaId = await this._getProductUID();
    if (oldXoaId === undefined) {
      return this._proxyResourceMethod("bindLicense", {
        licenseId,
        boundObjectId: xoaId,
        data
      });
    }
    return this._proxyResourceMethod("rebindLicense", {
      licenseId,
      oldBoundObjectId: oldXoaId,
      newBoundObjectId: xoaId,
      data
    });
  }
  async getSelfLicenses() {
    const boundObjectId = await this._getProductUID();
    return this._proxyResourceMethod("findBoundLicenses", {
      boundObjectId
    });
  }
  async getLicense({
    productId,
    boundObjectId
  }) {
    return this._proxyResourceMethod("getLicense", {
      productId,
      boundObjectId
    });
  }
  async unbindLicense({
    licenseId,
    productId,
    boundObjectId,
    data
  }) {
    return this._proxyResourceMethod("unbindLicense", {
      licenseId,
      productId,
      boundObjectId,
      data
    });
  }
  async rebindLicense({
    licenseId,
    oldBoundObjectId,
    newBoundObjectId,
    data
  }) {
    return this._proxyResourceMethod("rebindLicense", {
      licenseId,
      oldBoundObjectId,
      newBoundObjectId,
      data
    });
  }
  async createBoundXosanTrialLicense({
    boundObjectId
  }) {
    return this._proxyResourceMethod("createBoundXosanTrialLicense", {
      boundObjectId
    });
  }
  createXostorTrialLicenses({
    quantity
  }) {
    return this._proxyResourceMethod("createXostorTrialLicenses", {
      quantity
    });
  }
  createProxyTrialLicense() {
    return this._proxyResourceMethod("createProxyTrialLicense");
  }
  async getMessages() {
    const {
      token
    } = await this._getRegistrationData();
    if (token === undefined) {
      throw new NotRegistered("Not registered");
    }
    return this._post(_config.config.updateUrl, "getMessages", {
      token
    }, "xoa-updater");
  }
  async getReleaseChannels() {
    return this._post(_config.config.updateUrl, "getReleaseChannels");
  }
  async _refreshOfflineCaches() {
    await Promise.all([this._getAllLicenses().catch(noop), this.getSelfLicenses().catch(noop), this.getXoaPlan().catch(noop)]);
  }
}
const longMemoize = [_pMemoize.pMemoize, {
  duration: 2 * 3600 * 1e3
}];
const shortMemoize = [_pMemoize.pMemoize, {
  duration: 60 * 1e3
}];
function addFallbackCache(fn) {
  const {
    name
  } = fn;
  _assert.default.notEqual(name, "");
  return async function (...args) {
    let basename = name;
    if (args.length !== 0) {
      basename += "." + (0, _jsonHash.jsonHash)(args);
    }
    const cacheFile = (0, _path.join)(_config.config.dataDir, basename + ".cache.json");
    try {
      const result = await fn.apply(this, arguments);
      _fsExtra.default.outputJson(cacheFile, result, {
        mode: 0o600
      }).catch(error => {
        console.warn("failed to cache %s:", name, error);
      });
      return result;
    } catch (error) {
      console.warn("using cache because %s failed:", name, error);
      return _fsExtra.default.readJson(cacheFile);
    }
  };
}
(0, _decorateWith.decorateClass)(XoaUpdater, {
  getMessages: longMemoize,
  getHVSupportedVersions: longMemoize,
  getReleaseChannels: longMemoize,
  getLastAuditChain: shortMemoize,
  _getAllLicenses: (0, _compose.compose)(addFallbackCache, shortMemoize),
  getSelfLicenses: (0, _compose.compose)(addFallbackCache, shortMemoize),
  getXoaPlan: (0, _compose.compose)(addFallbackCache, shortMemoize)
});
const xoaUpdater = exports.xoaUpdater = new XoaUpdater();
(0, _config.onConfigUpdate)(() => {
  xoaUpdater.emit("notification", "configurationUpdated");
});
//# sourceMappingURL=xoa-updater.js.map                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              {"version":3,"file":"cli.js","names":["_fromEvent","_interopRequireDefault","require","_fromEvents","_getopts","_inquirer","_logSymbols","_promisify","_ws","_events","_jsonRpcProtocol","_util","_config","e","__esModule","default","objectOr","target","i","n","arguments","length","source","key","Object","keys","unknown","option","message","requestId","registerToken","conn","renew","questions","type","name","answers","inquirer","prompt","request","error","code","console","log","logSymbols","isRegistered","configureProxy","createConnection","level","terminate","configureChannel","channel","REGISTRATION_SUCCESS","register","args","opts","email","force","token","getopts","boolean","string","registrationToken","_register","registration","undefined","search","update","reinstall","forceToken","upgrade","url","on","content","warning","notify","result","fromEvents","state","process","exitCode","shift","socket","WebSocket","config","updaterPort","sendAsync","promisify","send","middle","EventEmitter","requests","__proto__","method","params","id","String","format","Promise","resolve","reject","notification","bind","parse","emit","TypeError","fromEvent","rawApiCall","_","forEach","param","value","startsWith","JSON","slice","usage","COMMANDS","help","main","loadConfig","argv","stopEarly","command","fn","inspect","colors","stdout","isTTY","depth","exit"],"sources":["../src/cli.js"],"sourcesContent":["#!/usr/bin/env node\n\nimport fromEvent from \"promise-toolbox/fromEvent\";\nimport fromEvents from \"promise-toolbox/fromEvents\";\nimport getopts from \"getopts\";\nimport inquirer from \"inquirer\";\nimport logSymbols from \"log-symbols\";\nimport promisify from \"promise-toolbox/promisify\";\nimport WebSocket from \"ws\";\nimport { EventEmitter } from \"events\";\nimport { format, parse } from \"json-rpc-protocol\";\nimport { inspect } from \"util\";\n\nimport { config, loadConfig } from \"./config.js\";\n\n// Similar to `Object.assign()` but use logical or\nfunction objectOr(target) {\n  for (let i = 1, n = arguments.length; i < n; ++i) {\n    const source = arguments[i];\n    for (const key of Object.keys(source)) {\n      target[key] ||= source[key];\n    }\n  }\n  return target;\n}\n\nfunction unknown(option) {\n  const message = \"unsupported option: \" + option;\n  throw message;\n}\n\nlet requestId = 0;\n\nasync function registerToken(conn, renew = false) {\n  const questions = [\n    {\n      type: \"input\",\n      name: \"email\",\n      message: \"Email:\",\n    },\n    {\n      type: \"password\",\n      name: \"password\",\n      message: \"Password:\",\n    },\n  ];\n\n  const answers = await inquirer.prompt(questions);\n  renew && (answers.renew = renew);\n\n  try {\n    await conn.request(\"register\", answers);\n  } catch (error) {\n    if (error.code === 1) {\n      // Authentication failed\n      console.log(logSymbols.error, error.message);\n      await registerToken(conn, renew);\n    } else {\n      throw error;\n    }\n  }\n}\n\nasync function isRegistered(conn) {\n  return conn.request(\"isRegistered\");\n}\n\nasync function configureProxy() {\n  const conn = await createConnection();\n\n  console.log(\"Proxy configuration:\");\n  const questions = [\n    {\n      type: \"input\",\n      name: \"proxyHost\",\n      message: \"Host:\",\n    },\n    {\n      type: \"input\",\n      name: \"proxyPort\",\n      message: \"Port:\",\n    },\n    {\n      type: \"input\",\n      name: \"proxyProtocol\",\n      message: \"Protocol:\",\n      default: \"http:\",\n    },\n    {\n      type: \"input\",\n      name: \"proxyUser\",\n      message: \"User name:\",\n    },\n    {\n      type: \"password\",\n      name: \"proxyPassword\",\n      message: \"Password:\",\n    },\n  ];\n\n  const answers = await inquirer.prompt(questions);\n  try {\n    await conn.request(\"configure\", answers);\n    return { level: \"info\", message: \"Proxy configured\" };\n  } finally {\n    await conn.terminate();\n  }\n}\n\nasync function configureChannel([channel]) {\n  const conn = await createConnection();\n  try {\n    await conn.request(\"configure\", { channel });\n    return {\n      level: \"info\",\n      message: `${channel} channel selected`,\n    };\n  } finally {\n    await conn.terminate();\n  }\n}\n\nconst REGISTRATION_SUCCESS = {\n  level: \"success\",\n  message: \"Your Xen Orchestra Appliance has been succesfully registered\",\n};\n\nasync function register(args, opts) {\n  const { email, force, token } = objectOr(\n    getopts(args, { boolean: [\"force\"], string: [\"email\", \"token\"] }),\n    opts\n  );\n\n  const conn = await createConnection();\n  try {\n    if (email !== \"\" && token !== \"\") {\n      await conn.request(\"register\", {\n        email,\n        renew: force,\n        registrationToken: token,\n      });\n      return REGISTRATION_SUCCESS;\n    }\n\n    const _register = async (renew = false) => {\n      console.log();\n      console.log(\n        \"Please enter your xen-orchestra.com credentials to register your XOA:\"\n      );\n      await registerToken(conn, renew);\n      return REGISTRATION_SUCCESS;\n    };\n\n    const registration = await isRegistered(conn);\n    if (registration.token !== undefined) {\n      let answers;\n      if (force) {\n        const questions = [\n          {\n            type: \"input\",\n            name: \"renew\",\n            message:\n              \"Your XOA is already registered, do you really want to force a new registration (yes/NO)?:\",\n          },\n        ];\n        answers = await inquirer.prompt(questions);\n      } else {\n        answers = { renew: \"no\" };\n      }\n      if (answers.renew && answers.renew.search(/^yes$/i) !== -1) {\n        return await _register(true);\n      } else {\n        return {\n          level: \"info\",\n          message: \"Your XOA is already registered\",\n        };\n      }\n    } else {\n      return await _register();\n    }\n  } finally {\n    conn.terminate();\n  }\n}\n\nasync function update(args, opts) {\n  const {\n    reinstall,\n    token: forceToken,\n    upgrade,\n    url,\n  } = objectOr(\n    getopts(args, {\n      boolean: [\"force\", \"reinstall\"],\n      string: [\"token\", \"url\"],\n    }),\n    opts,\n    { token: undefined, url: undefined }\n  );\n\n  const conn = await createConnection();\n  conn.on(\"print\", ({ content }) => {\n    console.log(...content);\n  });\n  conn.on(\"warning\", (warning) => {\n    console.log(logSymbols.warning, warning.message);\n  });\n\n  try {\n    await conn.notify(\"update\", { upgrade, url, forceToken, reinstall });\n  } catch (error) {\n    await conn.terminate();\n    throw error;\n  }\n\n  try {\n    const [result] = await fromEvents(conn, [\"end\"], [\"error\", \"close\"]);\n\n    const { state } = result;\n    if (state !== \"xoa-up-to-date\" && state !== \"xoa-upgraded\") {\n      process.exitCode = 2;\n    }\n\n    return result;\n  } catch (error) {\n    throw error.shift();\n  } finally {\n    await conn.terminate();\n  }\n}\n\nasync function createConnection() {\n  const socket = new WebSocket(\"ws://localhost:\" + config.updaterPort);\n  socket.sendAsync = promisify(socket.send);\n\n  const middle = new EventEmitter();\n  const requests = { __proto__: null };\n\n  middle.request = async (method, params = {}) => {\n    const id = String(requestId++);\n    await socket.sendAsync(format.request(id, method, params));\n    return new Promise((resolve, reject) => {\n      requests[id] = { resolve, reject };\n    });\n  };\n  middle.notify = (method, params = {}) => {\n    socket.sendAsync(format.notification(method, params));\n  };\n  middle.terminate = socket.terminate.bind(socket);\n\n  socket.on(\"message\", (message) => {\n    try {\n      const { type } = (message = parse(String(message)));\n      let request;\n      if (type === \"error\") {\n        const { id, error } = message;\n        if (id === undefined || (request = requests[id]) === undefined) {\n          throw error;\n        }\n        delete requests[id];\n        request.reject(error);\n      } else if (type === \"notification\" || type === \"request\") {\n        const { method } = message;\n        middle.emit(\n          method === \"server-error\" ? \"error\" : method,\n          message.params\n        );\n      } else if (type === \"response\") {\n        const { id } = message;\n        if (id !== undefined && (request = requests[id]) !== undefined) {\n          delete requests[id];\n          request.resolve(message.result);\n        }\n      } else {\n        throw new TypeError(`unexpected message type ${type}`);\n      }\n    } catch (error) {\n      middle.emit(\"error\", error);\n    }\n  });\n  socket.on(\"error\", function (error) {\n    middle.emit(\"error\", error);\n  });\n  socket.on(\"close\", function () {\n    middle.emit(\"close\", \"Disconnected from xoa-updater service\");\n  });\n\n  await fromEvent(middle, \"connected\");\n\n  return middle;\n}\n\nasync function rawApiCall(args) {\n  const params = getopts(args);\n  const method = params._[0];\n  delete params._;\n\n  Object.keys(params).forEach((param) => {\n    const value = params[param];\n    if (typeof value === \"string\" && value.startsWith(\"json:\")) {\n      params[param] = JSON.parse(value.slice(5));\n    }\n  });\n\n  const conn = await createConnection();\n\n  try {\n    return {\n      level: \"info\",\n      message: await conn.request(method, params),\n    };\n  } finally {\n    await conn.terminate();\n  }\n}\n\nfunction usage() {\n  console.log(\n    `xoa-updater usage:\n\n  xoa-updater-start\\tRun xoa-updater service (will require internet access on usage)\n\n  xoa-updater\n    Check for updates\n\n  xoa-updater configure-channel <channel>\n    Choose a specific update channel\n\n  xoa-updater configure-proxy\n    Configure a proxy for the xoa-updater service internet access\n\n  xoa-updater help\n    Display this help\n\n  xoa-updater register [--force]\n    Register your XO appliance on xen-orchestra.com\n\n    --force\n      Override an existing registration\n\n    --email <email>\n      Use this email address instead of asking for it\n\n    --token <token>\n      Use this token instead of asking for a password\n\n  xoa-updater upgrade [--reinstall]\n    Install latest updates\n\n    --reinstall\n      Reinstall all packages\n\nAdvanced Usage:\n\n  xoa-updater raw-api-call <method> [--<param> <value>]...\n    Call a method of the API and display its result.\n`\n  );\n}\n\nconst COMMANDS = {\n  \"configure-channel\": configureChannel,\n  \"configure-proxy\": configureProxy,\n  \"raw-api-call\": rawApiCall,\n  help: usage,\n  update,\n  upgrade: (args, opts) => update(args, { ...opts, upgrade: true }),\n  register,\n};\n\nasync function main() {\n  await loadConfig();\n\n  const opts = getopts(process.argv.slice(2), {\n    boolean: [\n      \"configure-proxy\",\n      \"force\",\n      \"help\",\n      \"raw-api-call\",\n      \"register\",\n      \"reinstall\",\n      \"upgrade\",\n    ],\n    stopEarly: true,\n    string: [\"token\", \"url\", \"configure-channel\"],\n    unknown,\n  });\n\n  if (opts[\"configure-channel\"] !== \"\") {\n    return configureChannel([opts[\"configure-channel\"]]);\n  }\n\n  let command = opts[\"configure-proxy\"]\n    ? \"configure-proxy\"\n    : opts.help\n    ? \"help\"\n    : opts[\"raw-api-call\"]\n    ? \"raw-api-call\"\n    : opts.register\n    ? \"register\"\n    : opts.upgrade\n    ? \"upgrade\"\n    : undefined;\n\n  let args = opts._;\n  if (command === undefined) {\n    if (args.length !== 0) {\n      [command, ...args] = args;\n    } else {\n      command = \"update\";\n    }\n  }\n\n  const fn = COMMANDS[command];\n  if (fn === undefined) {\n    const message = \"invalid command: \" + command;\n    throw message;\n  }\n\n  try {\n    return await fn(args, opts);\n  } catch (error) {\n    if (\n      (error.message && error.message.search(\"not open\") !== -1) ||\n      error.code === \"ECONNREFUSED\"\n    ) {\n      process.exitCode = 1;\n      return {\n        level: \"warning\",\n        message:\n          \"Your xoa-updater service may not be running. Use xoa-updater-start to run it.\",\n      };\n    } else {\n      throw error;\n    }\n  }\n}\n\n(async function () {\n  try {\n    const result = await main();\n\n    if (result !== undefined) {\n      const { level, message } = result;\n      console.log();\n      console.log(\n        logSymbols[level],\n        typeof message === \"string\"\n          ? message\n          : inspect(message, { colors: process.stdout.isTTY, depth: 10 })\n      );\n      console.log();\n    }\n  } catch (error) {\n    console.error();\n    console.error(logSymbols.error, error);\n    console.error();\n    // eslint-disable-next-line n/no-process-exit\n    process.exit(1);\n  }\n})();\n"],"mappings":"AAAA;AAAmB;;AAEnB,IAAAA,UAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,WAAA,GAAAF,sBAAA,CAAAC,OAAA;AACA,IAAAE,QAAA,GAAAH,sBAAA,CAAAC,OAAA;AACA,IAAAG,SAAA,GAAAJ,sBAAA,CAAAC,OAAA;AACA,IAAAI,WAAA,GAAAL,sBAAA,CAAAC,OAAA;AACA,IAAAK,UAAA,GAAAN,sBAAA,CAAAC,OAAA;AACA,IAAAM,GAAA,GAAAP,sBAAA,CAAAC,OAAA;AACA,IAAAO,OAAA,GAAAP,OAAA;AACA,IAAAQ,gBAAA,GAAAR,OAAA;AACA,IAAAS,KAAA,GAAAT,OAAA;AAEA,IAAAU,OAAA,GAAAV,OAAA;AAAiD,SAAAD,uBAAAY,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAGjD,SAASG,QAAQA,CAACC,MAAM,EAAE;EACxB,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEC,CAAC,GAAGC,SAAS,CAACC,MAAM,EAAEH,CAAC,GAAGC,CAAC,EAAE,EAAED,CAAC,EAAE;IAChD,MAAMI,MAAM,GAAGF,SAAS,CAACF,CAAC,CAAC;IAC3B,KAAK,MAAMK,GAAG,IAAIC,MAAM,CAACC,IAAI,CAACH,MAAM,CAAC,EAAE;MACrCL,MAAM,CAACM,GAAG,CAAC,KAAKD,MAAM,CAACC,GAAG,CAAC;IAC7B;EACF;EACA,OAAON,MAAM;AACf;AAEA,SAASS,OAAOA,CAACC,MAAM,EAAE;EACvB,MAAMC,OAAO,GAAG,sBAAsB,GAAGD,MAAM;EAC/C,MAAMC,OAAO;AACf;AAEA,IAAIC,SAAS,GAAG,CAAC;AAEjB,eAAeC,aAAaA,CAACC,IAAI,EAAEC,KAAK,GAAG,KAAK,EAAE;EAChD,MAAMC,SAAS,GAAG,CAChB;IACEC,IAAI,EAAE,OAAO;IACbC,IAAI,EAAE,OAAO;IACbP,OAAO,EAAE;EACX,CAAC,EACD;IACEM,IAAI,EAAE,UAAU;IAChBC,IAAI,EAAE,UAAU;IAChBP,OAAO,EAAE;EACX,CAAC,CACF;EAED,MAAMQ,OAAO,GAAG,MAAMC,iBAAQ,CAACC,MAAM,CAACL,SAAS,CAAC;EAChDD,KAAK,KAAKI,OAAO,CAACJ,KAAK,GAAGA,KAAK,CAAC;EAEhC,IAAI;IACF,MAAMD,IAAI,CAACQ,OAAO,CAAC,UAAU,EAAEH,OAAO,CAAC;EACzC,CAAC,CAAC,OAAOI,KAAK,EAAE;IACd,IAAIA,KAAK,CAACC,IAAI,KAAK,CAAC,EAAE;MAEpBC,OAAO,CAACC,GAAG,CAACC,mBAAU,CAACJ,KAAK,EAAEA,KAAK,CAACZ,OAAO,CAAC;MAC5C,MAAME,aAAa,CAACC,IAAI,EAAEC,KAAK,CAAC;IAClC,CAAC,MAAM;MACL,MAAMQ,KAAK;IACb;EACF;AACF;AAEA,eAAeK,YAAYA,CAACd,IAAI,EAAE;EAChC,OAAOA,IAAI,CAACQ,OAAO,CAAC,cAAc,CAAC;AACrC;AAEA,eAAeO,cAAcA,CAAA,EAAG;EAC9B,MAAMf,IAAI,GAAG,MAAMgB,gBAAgB,CAAC,CAAC;EAErCL,OAAO,CAACC,GAAG,CAAC,sBAAsB,CAAC;EACnC,MAAMV,SAAS,GAAG,CAChB;IACEC,IAAI,EAAE,OAAO;IACbC,IAAI,EAAE,WAAW;IACjBP,OAAO,EAAE;EACX,CAAC,EACD;IACEM,IAAI,EAAE,OAAO;IACbC,IAAI,EAAE,WAAW;IACjBP,OAAO,EAAE;EACX,CAAC,EACD;IACEM,IAAI,EAAE,OAAO;IACbC,IAAI,EAAE,eAAe;IACrBP,OAAO,EAAE,WAAW;IACpBb,OAAO,EAAE;EACX,CAAC,EACD;IACEmB,IAAI,EAAE,OAAO;IACbC,IAAI,EAAE,WAAW;IACjBP,OAAO,EAAE;EACX,CAAC,EACD;IACEM,IAAI,EAAE,UAAU;IAChBC,IAAI,EAAE,eAAe;IACrBP,OAAO,EAAE;EACX,CAAC,CACF;EAED,MAAMQ,OAAO,GAAG,MAAMC,iBAAQ,CAACC,MAAM,CAACL,SAAS,CAAC;EAChD,IAAI;IACF,MAAMF,IAAI,CAACQ,OAAO,CAAC,WAAW,EAAEH,OAAO,CAAC;IACxC,OAAO;MAAEY,KAAK,EAAE,MAAM;MAAEpB,OAAO,EAAE;IAAmB,CAAC;EACvD,CAAC,SAAS;IACR,MAAMG,IAAI,CAACkB,SAAS,CAAC,CAAC;EACxB;AACF;AAEA,eAAeC,gBAAgBA,CAAC,CAACC,OAAO,CAAC,EAAE;EACzC,MAAMpB,IAAI,GAAG,MAAMgB,gBAAgB,CAAC,CAAC;EACrC,IAAI;IACF,MAAMhB,IAAI,CAACQ,OAAO,CAAC,WAAW,EAAE;MAAEY;IAAQ,CAAC,CAAC;IAC5C,OAAO;MACLH,KAAK,EAAE,MAAM;MACbpB,OAAO,EAAE,GAAGuB,OAAO;IACrB,CAAC;EACH,CAAC,SAAS;IACR,MAAMpB,IAAI,CAACkB,SAAS,CAAC,CAAC;EACxB;AACF;AAEA,MAAMG,oBAAoB,GAAG;EAC3BJ,KAAK,EAAE,SAAS;EAChBpB,OAAO,EAAE;AACX,CAAC;AAED,eAAeyB,QAAQA,CAACC,IAAI,EAAEC,IAAI,EAAE;EAClC,MAAM;IAAEC,KAAK;IAAEC,KAAK;IAAEC;EAAM,CAAC,GAAG1C,QAAQ,CACtC,IAAA2C,gBAAO,EAACL,IAAI,EAAE;IAAEM,OAAO,EAAE,CAAC,OAAO,CAAC;IAAEC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO;EAAE,CAAC,CAAC,EACjEN,IACF,CAAC;EAED,MAAMxB,IAAI,GAAG,MAAMgB,gBAAgB,CAAC,CAAC;EACrC,IAAI;IACF,IAAIS,KAAK,KAAK,EAAE,IAAIE,KAAK,KAAK,EAAE,EAAE;MAChC,MAAM3B,IAAI,CAACQ,OAAO,CAAC,UAAU,EAAE;QAC7BiB,KAAK;QACLxB,KAAK,EAAEyB,KAAK;QACZK,iBAAiB,EAAEJ;MACrB,CAAC,CAAC;MACF,OAAON,oBAAoB;IAC7B;IAEA,MAAMW,SAAS,GAAG,MAAAA,CAAO/B,KAAK,GAAG,KAAK,KAAK;MACzCU,OAAO,CAACC,GAAG,CAAC,CAAC;MACbD,OAAO,CAACC,GAAG,CACT,uEACF,CAAC;MACD,MAAMb,aAAa,CAACC,IAAI,EAAEC,KAAK,CAAC;MAChC,OAAOoB,oBAAoB;IAC7B,CAAC;IAED,MAAMY,YAAY,GAAG,MAAMnB,YAAY,CAACd,IAAI,CAAC;IAC7C,IAAIiC,YAAY,CAACN,KAAK,KAAKO,SAAS,EAAE;MACpC,IAAI7B,OAAO;MACX,IAAIqB,KAAK,EAAE;QACT,MAAMxB,SAAS,GAAG,CAChB;UACEC,IAAI,EAAE,OAAO;UACbC,IAAI,EAAE,OAAO;UACbP,OAAO,EACL;QACJ,CAAC,CACF;QACDQ,OAAO,GAAG,MAAMC,iBAAQ,CAACC,MAAM,CAACL,SAAS,CAAC;MAC5C,CAAC,MAAM;QACLG,OAAO,GAAG;UAAEJ,KAAK,EAAE;QAAK,CAAC;MAC3B;MACA,IAAII,OAAO,CAACJ,KAAK,IAAII,OAAO,CAACJ,KAAK,CAACkC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE;QAC1D,OAAO,MAAMH,SAAS,CAAC,IAAI,CAAC;MAC9B,CAAC,MAAM;QACL,OAAO;UACLf,KAAK,EAAE,MAAM;UACbpB,OAAO,EAAE;QACX,CAAC;MACH;IACF,CAAC,MAAM;MACL,OAAO,MAAMmC,SAAS,CAAC,CAAC;IAC1B;EACF,CAAC,SAAS;IACRhC,IAAI,CAACkB,SAAS,CAAC,CAAC;EAClB;AACF;AAEA,eAAekB,MAAMA,CAACb,IAAI,EAAEC,IAAI,EAAE;EAChC,MAAM;IACJa,SAAS;IACTV,KAAK,EAAEW,UAAU;IACjBC,OAAO;IACPC;EACF,CAAC,GAAGvD,QAAQ,CACV,IAAA2C,gBAAO,EAACL,IAAI,EAAE;IACZM,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC;IAC/BC,MAAM,EAAE,CAAC,OAAO,EAAE,KAAK;EACzB,CAAC,CAAC,EACFN,IAAI,EACJ;IAAEG,KAAK,EAAEO,SAAS;IAAEM,GAAG,EAAEN;EAAU,CACrC,CAAC;EAED,MAAMlC,IAAI,GAAG,MAAMgB,gBAAgB,CAAC,CAAC;EACrChB,IAAI,CAACyC,EAAE,CAAC,OAAO,EAAE,CAAC;IAAEC;EAAQ,CAAC,KAAK;IAChC/B,OAAO,CAACC,GAAG,CAAC,GAAG8B,OAAO,CAAC;EACzB,CAAC,CAAC;EACF1C,IAAI,CAACyC,EAAE,CAAC,SAAS,EAAGE,OAAO,IAAK;IAC9BhC,OAAO,CAACC,GAAG,CAACC,mBAAU,CAAC8B,OAAO,EAAEA,OAAO,CAAC9C,OAAO,CAAC;EAClD,CAAC,CAAC;EAEF,IAAI;IACF,MAAMG,IAAI,CAAC4C,MAAM,CAAC,QAAQ,EAAE;MAAEL,OAAO;MAAEC,GAAG;MAAEF,UAAU;MAAED;IAAU,CAAC,CAAC;EACtE,CAAC,CAAC,OAAO5B,KAAK,EAAE;IACd,MAAMT,IAAI,CAACkB,SAAS,CAAC,CAAC;IACtB,MAAMT,KAAK;EACb;EAEA,IAAI;IACF,MAAM,CAACoC,MAAM,CAAC,GAAG,MAAM,IAAAC,mBAAU,EAAC9C,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAEpE,MAAM;MAAE+C;IAAM,CAAC,GAAGF,MAAM;IACxB,IAAIE,KAAK,KAAK,gBAAgB,IAAIA,KAAK,KAAK,cAAc,EAAE;MAC1DC,OAAO,CAACC,QAAQ,GAAG,CAAC;IACtB;IAEA,OAAOJ,MAAM;EACf,CAAC,CAAC,OAAOpC,KAAK,EAAE;IACd,MAAMA,KAAK,CAACyC,KAAK,CAAC,CAAC;EACrB,CAAC,SAAS;IACR,MAAMlD,IAAI,CAACkB,SAAS,CAAC,CAAC;EACxB;AACF;AAEA,eAAeF,gBAAgBA,CAAA,EAAG;EAChC,MAAMmC,MAAM,GAAG,IAAIC,WAAS,CAAC,iBAAiB,GAAGC,cAAM,CAACC,WAAW,CAAC;EACpEH,MAAM,CAACI,SAAS,GAAG,IAAAC,kBAAS,EAACL,MAAM,CAACM,IAAI,CAAC;EAEzC,MAAMC,MAAM,GAAG,IAAIC,oBAAY,CAAC,CAAC;EACjC,MAAMC,QAAQ,GAAG;IAAEC,SAAS,EAAE;EAAK,CAAC;EAEpCH,MAAM,CAAClD,OAAO,GAAG,OAAOsD,MAAM,EAAEC,MAAM,GAAG,CAAC,CAAC,KAAK;IAC9C,MAAMC,EAAE,GAAGC,MAAM,CAACnE,SAAS,EAAE,CAAC;IAC9B,MAAMqD,MAAM,CAACI,SAAS,CAACW,uBAAM,CAAC1D,OAAO,CAACwD,EAAE,EAAEF,MAAM,EAAEC,MAAM,CAAC,CAAC;IAC1D,OAAO,IAAII,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;MACtCT,QAAQ,CAACI,EAAE,CAAC,GAAG;QAAEI,OAAO;QAAEC;MAAO,CAAC;IACpC,CAAC,CAAC;EACJ,CAAC;EACDX,MAAM,CAACd,MAAM,GAAG,CAACkB,MAAM,EAAEC,MAAM,GAAG,CAAC,CAAC,KAAK;IACvCZ,MAAM,CAACI,SAAS,CAACW,uBAAM,CAACI,YAAY,CAACR,MAAM,EAAEC,MAAM,CAAC,CAAC;EACvD,CAAC;EACDL,MAAM,CAACxC,SAAS,GAAGiC,MAAM,CAACjC,SAAS,CAACqD,IAAI,CAACpB,MAAM,CAAC;EAEhDA,MAAM,CAACV,EAAE,CAAC,SAAS,EAAG5C,OAAO,IAAK;IAChC,IAAI;MACF,MAAM;QAAEM;MAAK,CAAC,GAAIN,OAAO,GAAG,IAAA2E,sBAAK,EAACP,MAAM,CAACpE,OAAO,CAAC,CAAE;MACnD,IAAIW,OAAO;MACX,IAAIL,IAAI,KAAK,OAAO,EAAE;QACpB,MAAM;UAAE6D,EAAE;UAAEvD;QAAM,CAAC,GAAGZ,OAAO;QAC7B,IAAImE,EAAE,KAAK9B,SAAS,IAAI,CAAC1B,OAAO,GAAGoD,QAAQ,CAACI,EAAE,CAAC,MAAM9B,SAAS,EAAE;UAC9D,MAAMzB,KAAK;QACb;QACA,OAAOmD,QAAQ,CAACI,EAAE,CAAC;QACnBxD,OAAO,CAAC6D,MAAM,CAAC5D,KAAK,CAAC;MACvB,CAAC,MAAM,IAAIN,IAAI,KAAK,cAAc,IAAIA,IAAI,KAAK,SAAS,EAAE;QACxD,MAAM;UAAE2D;QAAO,CAAC,GAAGjE,OAAO;QAC1B6D,MAAM,CAACe,IAAI,CACTX,MAAM,KAAK,cAAc,GAAG,OAAO,GAAGA,MAAM,EAC5CjE,OAAO,CAACkE,MACV,CAAC;MACH,CAAC,MAAM,IAAI5D,IAAI,KAAK,UAAU,EAAE;QAC9B,MAAM;UAAE6D;QAAG,CAAC,GAAGnE,OAAO;QACtB,IAAImE,EAAE,KAAK9B,SAAS,IAAI,CAAC1B,OAAO,GAAGoD,QAAQ,CAACI,EAAE,CAAC,MAAM9B,SAAS,EAAE;UAC9D,OAAO0B,QAAQ,CAACI,EAAE,CAAC;UACnBxD,OAAO,CAAC4D,OAAO,CAACvE,OAAO,CAACgD,MAAM,CAAC;QACjC;MACF,CAAC,MAAM;QACL,MAAM,IAAI6B,SAAS,CAAC,2BAA2BvE,IAAI,EAAE,CAAC;MACxD;IACF,CAAC,CAAC,OAAOM,KAAK,EAAE;MACdiD,MAAM,CAACe,IAAI,CAAC,OAAO,EAAEhE,KAAK,CAAC;IAC7B;EACF,CAAC,CAAC;EACF0C,MAAM,CAACV,EAAE,CAAC,OAAO,EAAE,UAAUhC,KAAK,EAAE;IAClCiD,MAAM,CAACe,IAAI,CAAC,OAAO,EAAEhE,KAAK,CAAC;EAC7B,CAAC,CAAC;EACF0C,MAAM,CAACV,EAAE,CAAC,OAAO,EAAE,YAAY;IAC7BiB,MAAM,CAACe,IAAI,CAAC,OAAO,EAAE,uCAAuC,CAAC;EAC/D,CAAC,CAAC;EAEF,MAAM,IAAAE,kBAAS,EAACjB,MAAM,EAAE,WAAW,CAAC;EAEpC,OAAOA,MAAM;AACf;AAEA,eAAekB,UAAUA,CAACrD,IAAI,EAAE;EAC9B,MAAMwC,MAAM,GAAG,IAAAnC,gBAAO,EAACL,IAAI,CAAC;EAC5B,MAAMuC,MAAM,GAAGC,MAAM,CAACc,CAAC,CAAC,CAAC,CAAC;EAC1B,OAAOd,MAAM,CAACc,CAAC;EAEfpF,MAAM,CAACC,IAAI,CAACqE,MAAM,CAAC,CAACe,OAAO,CAAEC,KAAK,IAAK;IACrC,MAAMC,KAAK,GAAGjB,MAAM,CAACgB,KAAK,CAAC;IAC3B,IAAI,OAAOC,KAAK,KAAK,QAAQ,IAAIA,KAAK,CAACC,UAAU,CAAC,OAAO,CAAC,EAAE;MAC1DlB,MAAM,CAACgB,KAAK,CAAC,GAAGG,IAAI,CAACV,KAAK,CAACQ,KAAK,CAACG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5C;EACF,CAAC,CAAC;EAEF,MAAMnF,IAAI,GAAG,MAAMgB,gBAAgB,CAAC,CAAC;EAErC,IAAI;IACF,OAAO;MACLC,KAAK,EAAE,MAAM;MACbpB,OAAO,EAAE,MAAMG,IAAI,CAACQ,OAAO,CAACsD,MAAM,EAAEC,MAAM;IAC5C,CAAC;EACH,CAAC,SAAS;IACR,MAAM/D,IAAI,CAACkB,SAAS,CAAC,CAAC;EACxB;AACF;AAEA,SAASkE,KAAKA,CAAA,EAAG;EACfzE,OAAO,CAACC,GAAG,CACT;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CACE,CAAC;AACH;AAEA,MAAMyE,QAAQ,GAAG;EACf,mBAAmB,EAAElE,gBAAgB;EACrC,iBAAiB,EAAEJ,cAAc;EACjC,cAAc,EAAE6D,UAAU;EAC1BU,IAAI,EAAEF,KAAK;EACXhD,MAAM;EACNG,OAAO,EAAEA,CAAChB,IAAI,EAAEC,IAAI,KAAKY,MAAM,CAACb,IAAI,EAAE;IAAE,GAAGC,IAAI;IAAEe,OAAO,EAAE;EAAK,CAAC,CAAC;EACjEjB;AACF,CAAC;AAED,eAAeiE,IAAIA,CAAA,EAAG;EACpB,MAAM,IAAAC,kBAAU,EAAC,CAAC;EAElB,MAAMhE,IAAI,GAAG,IAAAI,gBAAO,EAACoB,OAAO,CAACyC,IAAI,CAACN,KAAK,CAAC,CAAC,CAAC,EAAE;IAC1CtD,OAAO,EAAE,CACP,iBAAiB,EACjB,OAAO,EACP,MAAM,EACN,cAAc,EACd,UAAU,EACV,WAAW,EACX,SAAS,CACV;IACD6D,SAAS,EAAE,IAAI;IACf5D,MAAM,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,mBAAmB,CAAC;IAC7CnC;EACF,CAAC,CAAC;EAEF,IAAI6B,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,EAAE;IACpC,OAAOL,gBAAgB,CAAC,CAACK,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC;EACtD;EAEA,IAAImE,OAAO,GAAGnE,IAAI,CAAC,iBAAiB,CAAC,GACjC,iBAAiB,GACjBA,IAAI,CAAC8D,IAAI,GACT,MAAM,GACN9D,IAAI,CAAC,cAAc,CAAC,GACpB,cAAc,GACdA,IAAI,CAACF,QAAQ,GACb,UAAU,GACVE,IAAI,CAACe,OAAO,GACZ,SAAS,GACTL,SAAS;EAEb,IAAIX,IAAI,GAAGC,IAAI,CAACqD,CAAC;EACjB,IAAIc,OAAO,KAAKzD,SAAS,EAAE;IACzB,IAAIX,IAAI,CAACjC,MAAM,KAAK,CAAC,EAAE;MACrB,CAACqG,OAAO,EAAE,GAAGpE,IAAI,CAAC,GAAGA,IAAI;IAC3B,CAAC,MAAM;MACLoE,OAAO,GAAG,QAAQ;IACpB;EACF;EAEA,MAAMC,EAAE,GAAGP,QAAQ,CAACM,OAAO,CAAC;EAC5B,IAAIC,EAAE,KAAK1D,SAAS,EAAE;IACpB,MAAMrC,OAAO,GAAG,mBAAmB,GAAG8F,OAAO;IAC7C,MAAM9F,OAAO;EACf;EAEA,IAAI;IACF,OAAO,MAAM+F,EAAE,CAACrE,IAAI,EAAEC,IAAI,CAAC;EAC7B,CAAC,CAAC,OAAOf,KAAK,EAAE;IACd,IACGA,KAAK,CAACZ,OAAO,IAAIY,KAAK,CAACZ,OAAO,CAACsC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IACzD1B,KAAK,CAACC,IAAI,KAAK,cAAc,EAC7B;MACAsC,OAAO,CAACC,QAAQ,GAAG,CAAC;MACpB,OAAO;QACLhC,KAAK,EAAE,SAAS;QAChBpB,OAAO,EACL;MACJ,CAAC;IACH,CAAC,MAAM;MACL,MAAMY,KAAK;IACb;EACF;AACF;AAEA,CAAC,kBAAkB;EACjB,IAAI;IACF,MAAMoC,MAAM,GAAG,MAAM0C,IAAI,CAAC,CAAC;IAE3B,IAAI1C,MAAM,KAAKX,SAAS,EAAE;MACxB,MAAM;QAAEjB,KAAK;QAAEpB;MAAQ,CAAC,GAAGgD,MAAM;MACjClC,OAAO,CAACC,GAAG,CAAC,CAAC;MACbD,OAAO,CAACC,GAAG,CACTC,mBAAU,CAACI,KAAK,CAAC,EACjB,OAAOpB,OAAO,KAAK,QAAQ,GACvBA,OAAO,GACP,IAAAgG,aAAO,EAAChG,OAAO,EAAE;QAAEiG,MAAM,EAAE9C,OAAO,CAAC+C,MAAM,CAACC,KAAK;QAAEC,KAAK,EAAE;MAAG,CAAC,CAClE,CAAC;MACDtF,OAAO,CAACC,GAAG,CAAC,CAAC;IACf;EACF,CAAC,CAAC,OAAOH,KAAK,EAAE;IACdE,OAAO,CAACF,KAAK,CAAC,CAAC;IACfE,OAAO,CAACF,KAAK,CAACI,mBAAU,CAACJ,KAAK,EAAEA,KAAK,CAAC;IACtCE,OAAO,CAACF,KAAK,CAAC,CAAC;IAEfuC,OAAO,CAACkD,IAAI,CAAC,CAAC,CAAC;EACjB;AACF,CAAC,EAAE,CAAC","ignoreList":[]}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 "use strict";

exports.__esModule = true;
exports.pMemoize = pMemoize;
var _jsonHash = require("@vates/json-hash");
var _ttlcache = _interopRequireDefault(require("@isaacs/ttlcache"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const defaultCacheKey = (...args) => args.length === 0 ? "" : (0, _jsonHash.jsonHash)(args);
function pMemoize(fn, {
  duration,
  cache = new _ttlcache.default({
    ttl: duration
  }),
  cacheKey = defaultCacheKey,
  cacheRejection = false
}) {
  function memoized(arg) {
    const key = cacheKey.apply(this, arguments);
    let promise = cache.get(key);
    if (promise === undefined) {
      promise = new Promise(resolve => {
        resolve(fn.apply(this, arguments));
      });
      cache.set(key, promise);
      if (!cacheRejection) {
        promise.catch(() => {
          if (cache.get(key) === promise) {
            cache.delete(key);
          }
        });
      }
    }
    return promise;
  }
  memoized.cache = cache;
  memoized.cacheKey = cacheKey;
  return memoized;
}
//# sourceMappingURL=_pMemoize.js.map                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     "use strict";

exports.__esModule = true;
exports.config = void 0;
exports.loadConfig = loadConfig;
exports.onConfigUpdate = onConfigUpdate;
exports.stopWatchingConfig = void 0;
exports.watchConfig = watchConfig;
var _nodeUtil = require("node:util");
var _path = require("path");
var _appConf = require("app-conf");
var _constants = require("./constants.js");
const opts = {
  appDir: (0, _path.join)(__dirname, "..")
};
let config = exports.config = void 0;
async function loadConfig() {
  exports.config = config = await (0, _appConf.load)(_constants.appName, opts);
}
const listeners = new Set();
function onConfigUpdate(listener) {
  listeners.add(listener);
  return () => {
    listeners.remove(listener);
  };
}
async function watchConfig() {
  if (stopWatchingConfig !== undefined) {
    throw new Error("already watching config");
  }
  exports.stopWatchingConfig = stopWatchingConfig = () => {
    throw new Error("currently setting up watch");
  };
  const stopWatch = await (0, _appConf.watch)({
    ...opts,
    appName: _constants.appName,
    initialLoad: true
  }, (error, config_) => {
    if (error !== undefined) {
      console.warn("config watch", error);
    } else if (!(0, _nodeUtil.isDeepStrictEqual)(config, config_)) {
      exports.config = config = config_;
      listeners.forEach(listener => listener(config));
    }
  }).catch(error => {
    exports.stopWatchingConfig = stopWatchingConfig = undefined;
    throw error;
  });
  exports.stopWatchingConfig = stopWatchingConfig = async () => {
    await stopWatch();
    exports.stopWatchingConfig = stopWatchingConfig = undefined;
  };
}
let stopWatchingConfig = exports.stopWatchingConfig = void 0;
//# sourceMappingURL=config.js.map                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             {"version":3,"file":"isLoopback.js","names":["RE","isLoopback","exports","test","bind"],"sources":["../src/isLoopback.js"],"sourcesContent":["// - `::1` is IPv6 loopback address\n// - `::ffff:*` are IPv4 in IPv6 addresses\n// - `127.*` are IPv4 loopback addresses\nconst RE = /^(?:::1|(?:::ffff:)?127(?:\\.\\d+){3})$/;\n\nexport const isLoopback = RE.test.bind(RE);\n"],"mappings":";;;;AAGA,MAAMA,EAAE,GAAG,uCAAuC;AAE3C,MAAMC,UAAU,GAAAC,OAAA,CAAAD,UAAA,GAAGD,EAAE,CAACG,IAAI,CAACC,IAAI,CAACJ,EAAE,CAAC","ignoreList":[]}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         {"version":3,"file":"_pMemoize.js","names":["_jsonHash","require","_ttlcache","_interopRequireDefault","e","__esModule","default","defaultCacheKey","args","length","jsonHash","pMemoize","fn","duration","cache","TTLCache","ttl","cacheKey","cacheRejection","memoized","arg","key","apply","arguments","promise","get","undefined","Promise","resolve","set","catch","delete"],"sources":["../src/_pMemoize.js"],"sourcesContent":["import { jsonHash } from \"@vates/json-hash\";\nimport TTLCache from \"@isaacs/ttlcache\";\n\nconst defaultCacheKey = (...args) => (args.length === 0 ? \"\" : jsonHash(args));\n\nexport function pMemoize(\n  fn,\n  {\n    duration,\n\n    cache = new TTLCache({ ttl: duration }),\n    cacheKey = defaultCacheKey,\n    cacheRejection = false,\n  }\n) {\n  function memoized(arg) {\n    const key = cacheKey.apply(this, arguments);\n\n    let promise = cache.get(key);\n    if (promise === undefined) {\n      promise = new Promise((resolve) => {\n        resolve(fn.apply(this, arguments));\n      });\n      cache.set(key, promise);\n\n      if (!cacheRejection) {\n        promise.catch(() => {\n          if (cache.get(key) === promise) {\n            cache.delete(key);\n          }\n        });\n      }\n    }\n    return promise;\n  }\n  memoized.cache = cache;\n  memoized.cacheKey = cacheKey;\n  return memoized;\n}\n"],"mappings":";;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AACA,IAAAC,SAAA,GAAAC,sBAAA,CAAAF,OAAA;AAAwC,SAAAE,uBAAAC,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAExC,MAAMG,eAAe,GAAGA,CAAC,GAAGC,IAAI,KAAMA,IAAI,CAACC,MAAM,KAAK,CAAC,GAAG,EAAE,GAAG,IAAAC,kBAAQ,EAACF,IAAI,CAAE;AAEvE,SAASG,QAAQA,CACtBC,EAAE,EACF;EACEC,QAAQ;EAERC,KAAK,GAAG,IAAIC,iBAAQ,CAAC;IAAEC,GAAG,EAAEH;EAAS,CAAC,CAAC;EACvCI,QAAQ,GAAGV,eAAe;EAC1BW,cAAc,GAAG;AACnB,CAAC,EACD;EACA,SAASC,QAAQA,CAACC,GAAG,EAAE;IACrB,MAAMC,GAAG,GAAGJ,QAAQ,CAACK,KAAK,CAAC,IAAI,EAAEC,SAAS,CAAC;IAE3C,IAAIC,OAAO,GAAGV,KAAK,CAACW,GAAG,CAACJ,GAAG,CAAC;IAC5B,IAAIG,OAAO,KAAKE,SAAS,EAAE;MACzBF,OAAO,GAAG,IAAIG,OAAO,CAAEC,OAAO,IAAK;QACjCA,OAAO,CAAChB,EAAE,CAACU,KAAK,CAAC,IAAI,EAAEC,SAAS,CAAC,CAAC;MACpC,CAAC,CAAC;MACFT,KAAK,CAACe,GAAG,CAACR,GAAG,EAAEG,OAAO,CAAC;MAEvB,IAAI,CAACN,cAAc,EAAE;QACnBM,OAAO,CAACM,KAAK,CAAC,MAAM;UAClB,IAAIhB,KAAK,CAACW,GAAG,CAACJ,GAAG,CAAC,KAAKG,OAAO,EAAE;YAC9BV,KAAK,CAACiB,MAAM,CAACV,GAAG,CAAC;UACnB;QACF,CAAC,CAAC;MACJ;IACF;IACA,OAAOG,OAAO;EAChB;EACAL,QAAQ,CAACL,KAAK,GAAGA,KAAK;EACtBK,QAAQ,CAACF,QAAQ,GAAGA,QAAQ;EAC5B,OAAOE,QAAQ;AACjB","ignoreList":[]}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          {
  "name": "ip-address",
  "description": "A library for parsing IPv4 and IPv6 IP addresses in node and the browser.",
  "keywords": [
    "ipv6",
    "ipv4",
    "browser",
    "validation"
  ],
  "version": "9.0.5",
  "author": "Beau Gunderson <beau@beaugunderson.com> (https://beaugunderson.com/)",
  "license": "MIT",
  "main": "dist/ip-address.js",
  "types": "dist/ip-address.d.ts",
  "scripts": {
    "docs": "documentation build --github --output docs --format html ./ip-address.js",
    "build": "rm -rf dist; mkdir dist; tsc",
    "prepack": "npm run build",
    "release": "release-it",
    "test-ci": "nyc mocha",
    "test": "mocha",
    "watch": "mocha --watch"
  },
  "nyc": {
    "extension": [
      ".ts"
    ],
    "exclude": [
      "**/*.d.ts",
      ".eslintrc.js",
      "coverage/",
      "dist/",
      "test/",
      "tmp/"
    ],
    "reporter": [
      "html",
      "lcov",
      "text"
    ],
    "all": true
  },
  "engines": {
    "node": ">= 12"
  },
  "files": [
    "src",
    "dist"
  ],
  "repository": {
    "type": "git",
    "url": "git://github.com/beaugunderson/ip-address.git"
  },
  "dependencies": {
    "jsbn": "1.1.0",
    "sprintf-js": "^1.1.3"
  },
  "devDependencies": {
    "@types/chai": "^4.2.18",
    "@types/jsbn": "^1.2.31",
    "@types/mocha": "^10.0.1",
    "@types/sprintf-js": "^1.1.2",
    "@typescript-eslint/eslint-plugin": "^6.7.2",
    "@typescript-eslint/parser": "^6.7.2",
    "browserify": "^17.0.0",
    "chai": "^4.3.4",
    "codecov": "^3.8.2",
    "documentation": "^14.0.2",
    "eslint": "^8.50.0",
    "eslint-config-airbnb": "^19.0.4",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-filenames": "^1.3.2",
    "eslint-plugin-import": "^2.23.4",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-prettier": "^5.0.0",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
    "mocha": "^10.2.0",
    "nyc": "^15.1.0",
    "prettier": "^3.0.3",
    "release-it": "^16.2.0",
    "source-map-support": "^0.5.19",
    "ts-node": "^10.0.0",
    "typescript": "^5.2.2"
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             Copyright (C) 2011 by Beau Gunderson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           {
  "name": "readdirp",
  "description": "Recursive version of fs.readdir with streaming API.",
  "version": "3.6.0",
  "homepage": "https://github.com/paulmillr/readdirp",
  "repository": {
    "type": "git",
    "url": "git://github.com/paulmillr/readdirp.git"
  },
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/paulmillr/readdirp/issues"
  },
  "author": "Thorsten Lorenz <thlorenz@gmx.de> (thlorenz.com)",
  "contributors": [
    "Thorsten Lorenz <thlorenz@gmx.de> (thlorenz.com)",
    "Paul Miller (https://paulmillr.com)"
  ],
  "main": "index.js",
  "engines": {
    "node": ">=8.10.0"
  },
  "files": [
    "index.js",
    "index.d.ts"
  ],
  "keywords": [
    "recursive",
    "fs",
    "stream",
    "streams",
    "readdir",
    "filesystem",
    "find",
    "filter"
  ],
  "scripts": {
    "dtslint": "dtslint",
    "nyc": "nyc",
    "mocha": "mocha --exit",
    "lint": "eslint --report-unused-disable-directives --ignore-path .gitignore .",
    "test": "npm run lint && nyc npm run mocha"
  },
  "dependencies": {
    "picomatch": "^2.2.1"
  },
  "devDependencies": {
    "@types/node": "^14",
    "chai": "^4.2",
    "chai-subset": "^1.6",
    "dtslint": "^3.3.0",
    "eslint": "^7.0.0",
    "mocha": "^7.1.1",
    "nyc": "^15.0.0",
    "rimraf": "^3.0.0",
    "typescript": "^4.0.3"
  },
  "nyc": {
    "reporter": [
      "html",
      "text"
    ]
  },
  "eslintConfig": {
    "root": true,
    "extends": "eslint:recommended",
    "parserOptions": {
      "ecmaVersion": 9,
      "sourceType": "script"
    },
    "env": {
      "node": true,
      "es6": true
    },
    "rules": {
      "array-callback-return": "error",
      "no-empty": [
        "error",
        {
          "allowEmptyCatch": true
        }
      ],
      "no-else-return": [
        "error",
        {
          "allowElseIf": false
        }
      ],
      "no-lonely-if": "error",
      "no-var": "error",
      "object-shorthand": "error",
      "prefer-arrow-callback": [
        "error",
        {
          "allowNamedFunctions": true
        }
      ],
      "prefer-const": [
        "error",
        {
          "ignoreReadBeforeAssign": true
        }
      ],
      "prefer-destructuring": [
        "error",
        {
          "object": true,
          "array": false
        }
      ],
      "prefer-spread": "error",
      "prefer-template": "error",
      "radix": "error",
      "semi": "error",
      "strict": "error",
      "quotes": [
        "error",
        "single"
      ]
    }
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      ![CircleCI](https://img.shields.io/circleci/build/github/beaugunderson/ip-address)
[![codecov]](https://codecov.io/github/beaugunderson/ip-address?branch=master)
[![downloads]](https://www.npmjs.com/package/ip-address)
[![npm]](https://www.npmjs.com/package/ip-address)
[![snyk]](https://snyk.io/test/github/beaugunderson/ip-address)

[codecov]: https://codecov.io/github/beaugunderson/ip-address/coverage.svg?branch=master
[downloads]: https://img.shields.io/npm/dm/ip-address.svg
[npm]: https://img.shields.io/npm/v/ip-address.svg
[snyk]: https://snyk.io/test/github/beaugunderson/ip-address/badge.svg

## ip-address

`ip-address` is a library for validating and manipulating IPv4 and IPv6
addresses in JavaScript.


### Migrating from 6.x to 7.x

`ip-address` was rewritten in TypeScript for version 7. If you were using
version 6 you'll need to make these changes to upgrade:

- Instead of checking `isValid()`, which has been removed, you'll need to use a
  `try`/`catch` if you're accepting unknown input. This made the TypeScript
  types substantially easier as well as allowed the use of an `AddressError`
  class which will contain a `parseMessage` if an error occurred in the parsing
  step.
- Instead of using the `error`, `parseError`, and `valid` attributes you'll
  need to use the `message` and `parseMessage` of the thrown `AddressError`.

### Documentation

Documentation is available at [ip-address.js.org](http://ip-address.js.org/).

### Examples

```js
var Address6 = require('ip-address').Address6;

var address = new Address6('2001:0:ce49:7601:e866:efff:62c3:fffe');

var teredo = address.inspectTeredo();

teredo.client4;    // '157.60.0.1'
```

### Features

- Usable via CommonJS or ESM
- Parsing of all IPv6 notations
- Parsing of IPv6 addresses and ports from URLs with `Address6.fromURL(url)`
- Validity checking
- Decoding of the [Teredo
  information](http://en.wikipedia.org/wiki/Teredo_tunneling#IPv6_addressing)
  in an address
- Whether one address is a valid subnet of another
- What special properties a given address has (multicast prefix, unique
  local address prefix, etc.)
- Number of subnets of a certain size in a given address
- Display methods
  - Hex, binary, and decimal
  - Canonical form
  - Correct form
  - IPv4-compatible (i.e. `::ffff:192.168.0.1`)
- Works in [node](http://nodejs.org/) and the browser (with browserify)
- ~1,600 test cases

### Used by

- [anon](https://github.com/edsu/anon) which powers
  [@congressedits](https://twitter.com/congressedits), among
  [many others](https://github.com/edsu/anon#community)
- [base85](https://github.com/noseglid/base85): base85 encoding/decoding
- [contrail-web-core](https://github.com/Juniper/contrail-web-core): part of
  Contrail, a network virtualization solution made by Juniper Networks
- [dhcpjs](https://github.com/apaprocki/node-dhcpjs): a DHCP client and server
- [epochtalk](https://github.com/epochtalk/epochtalk): next generation forum
  software
- [geoip-web](https://github.com/tfrce/node-geoip-web): a server for
  quickly geolocating IP addresses
- [hexabus](https://github.com/mysmartgrid/hexabus): an IPv6-based home
  automation bus
- [hubot-deploy](https://github.com/atmos/hubot-deploy): GitHub Flow via hubot
- [heroku-portscanner](https://github.com/robison/heroku-portscanner): nmap
  hosted on Heroku
- [ipfs-swarm](https://github.com/diasdavid/node-ipfs-swarm): a swarm
  implementation based on IPFS
- [javascript-x-server](https://github.com/GothAck/javascript-x-server): an X
  server written in JavaScript
- [libnmap](https://github.com/jas-/node-libnmap): a node API for nmap
- [mail-io](https://github.com/mofux/mail-io): a lightweight SMTP server
- [maxmind-db-reader](https://github.com/PaddeK/node-maxmind-db): a library for
  reading MaxMind database files
- [proxy-protocol-v2](https://github.com/ably/proxy-protocol-v2): a proxy
  protocol encoder/decoder built by [Ably](https://www.ably.io/)
- [Samsara](https://github.com/mariusGundersen/Samsara): a Docker web interface
- [sis-api](https://github.com/sis-cmdb/sis-api): a configuration management
  database API
- [socks5-client](https://github.com/mattcg/socks5-client): a SOCKS v5 client
- [socksified](https://github.com/vially/node-socksified): a SOCKS v5 client
- [socksv5](https://github.com/mscdex/socksv5): a SOCKS v5 server/client
- [ssdapi](https://github.com/rsolomou/ssdapi): an API created by the
  University of Portsmouth
- [SwitchyOmega](https://github.com/FelisCatus/SwitchyOmega): a [Chrome
  extension](https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)
  for switching between multiple proxies with ~311k users!
- [swiz](https://github.com/racker/node-swiz): a serialization framework built
  and used by [Rackspace](http://www.rackspace.com/)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            'use strict';

const fs = require('fs');
const { Readable } = require('stream');
const sysPath = require('path');
const { promisify } = require('util');
const picomatch = require('picomatch');

const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const lstat = promisify(fs.lstat);
const realpath = promisify(fs.realpath);

/**
 * @typedef {Object} EntryInfo
 * @property {String} path
 * @property {String} fullPath
 * @property {fs.Stats=} stats
 * @property {fs.Dirent=} dirent
 * @property {String} basename
 */

const BANG = '!';
const RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';
const NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]);
const FILE_TYPE = 'files';
const DIR_TYPE = 'directories';
const FILE_DIR_TYPE = 'files_directories';
const EVERYTHING_TYPE = 'all';
const ALL_TYPES = [FILE_TYPE, DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE];

const isNormalFlowError = error => NORMAL_FLOW_ERRORS.has(error.code);
const [maj, min] = process.versions.node.split('.').slice(0, 2).map(n => Number.parseInt(n, 10));
const wantBigintFsStats = process.platform === 'win32' && (maj > 10 || (maj === 10 && min >= 5));

const normalizeFilter = filter => {
  if (filter === undefined) return;
  if (typeof filter === 'function') return filter;

  if (typeof filter === 'string') {
    const glob = picomatch(filter.trim());
    return entry => glob(entry.basename);
  }

  if (Array.isArray(filter)) {
    const positive = [];
    const negative = [];
    for (const item of filter) {
      const trimmed = item.trim();
      if (trimmed.charAt(0) === BANG) {
        negative.push(picomatch(trimmed.slice(1)));
      } else {
        positive.push(picomatch(trimmed));
      }
    }

    if (negative.length > 0) {
      if (positive.length > 0) {
        return entry =>
          positive.some(f => f(entry.basename)) && !negative.some(f => f(entry.basename));
      }
      return entry => !negative.some(f => f(entry.basename));
    }
    return entry => positive.some(f => f(entry.basename));
  }
};

class ReaddirpStream extends Readable {
  static get defaultOptions() {
    return {
      root: '.',
      /* eslint-disable no-unused-vars */
      fileFilter: (path) => true,
      directoryFilter: (path) => true,
      /* eslint-enable no-unused-vars */
      type: FILE_TYPE,
      lstat: false,
      depth: 2147483648,
      alwaysStat: false
    };
  }

  constructor(options = {}) {
    super({
      objectMode: true,
      autoDestroy: true,
      highWaterMark: options.highWaterMark || 4096
    });
    const opts = { ...ReaddirpStream.defaultOptions, ...options };
    const { root, type } = opts;

    this._fileFilter = normalizeFilter(opts.fileFilter);
    this._directoryFilter = normalizeFilter(opts.directoryFilter);

    const statMethod = opts.lstat ? lstat : stat;
    // Use bigint stats if it's windows and stat() supports options (node 10+).
    if (wantBigintFsStats) {
      this._stat = path => statMethod(path, { bigint: true });
    } else {
      this._stat = statMethod;
    }

    this._maxDepth = opts.depth;
    this._wantsDir = [DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type);
    this._wantsFile = [FILE_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type);
    this._wantsEverything = type === EVERYTHING_TYPE;
    this._root = sysPath.resolve(root);
    this._isDirent = ('Dirent' in fs) && !opts.alwaysStat;
    this._statsProp = this._isDirent ? 'dirent' : 'stats';
    this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };

    // Launch stream with one parent, the root dir.
    this.parents = [this._exploreDir(root, 1)];
    this.reading = false;
    this.parent = undefined;
  }

  async _read(batch) {
    if (this.reading) return;
    this.reading = true;

    try {
      while (!this.destroyed && batch > 0) {
        const { path, depth, files = [] } = this.parent || {};

        if (files.length > 0) {
          const slice = files.splice(0, batch).map(dirent => this._formatEntry(dirent, path));
          for (const entry of await Promise.all(slice)) {
            if (this.destroyed) return;

            const entryType = await this._getEntryType(entry);
            if (entryType === 'directory' && this._directoryFilter(entry)) {
              if (depth <= this._maxDepth) {
                this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
              }

              if (this._wantsDir) {
                this.push(entry);
                batch--;
              }
            } else if ((entryType === 'file' || this._includeAsFile(entry)) && this._fileFilter(entry)) {
              if (this._wantsFile) {
                this.push(entry);
                batch--;
              }
            }
          }
        } else {
          const parent = this.parents.pop();
          if (!parent) {
            this.push(null);
            break;
          }
          this.parent = await parent;
          if (this.destroyed) return;
        }
      }
    } catch (error) {
      this.destroy(error);
    } finally {
      this.reading = false;
    }
  }

  async _exploreDir(path, depth) {
    let files;
    try {
      files = await readdir(path, this._rdOptions);
    } catch (error) {
      this._onError(error);
    }
    return { files, depth, path };
  }

  async _formatEntry(dirent, path) {
    let entry;
    try {
      const basename = this._isDirent ? dirent.name : dirent;
      const fullPath = sysPath.resolve(sysPath.join(path, basename));
      entry = { path: sysPath.relative(this._root, fullPath), fullPath, basename };
      entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
    } catch (err) {
      this._onError(err);
    }
    return entry;
  }

  _onError(err) {
    if (isNormalFlowError(err) && !this.destroyed) {
      this.emit('warn', err);
    } else {
      this.destroy(err);
    }
  }

  async _getEntryType(entry) {
    // entry may be undefined, because a warning or an error were emitted
    // and the statsProp is undefined
    const stats = entry && entry[this._statsProp];
    if (!stats) {
      return;
    }
    if (stats.isFile()) {
      return 'file';
    }
    if (stats.isDirectory()) {
      return 'directory';
    }
    if (stats && stats.isSymbolicLink()) {
      const full = entry.fullPath;
      try {
        const entryRealPath = await realpath(full);
        const entryRealPathStats = await lstat(entryRealPath);
        if (entryRealPathStats.isFile()) {
          return 'file';
        }
        if (entryRealPathStats.isDirectory()) {
          const len = entryRealPath.length;
          if (full.startsWith(entryRealPath) && full.substr(len, 1) === sysPath.sep) {
            const recursiveError = new Error(
              `Circular symlink detected: "${full}" points to "${entryRealPath}"`
            );
            recursiveError.code = RECURSIVE_ERROR_CODE;
            return this._onError(recursiveError);
          }
          return 'directory';
        }
      } catch (error) {
        this._onError(error);
      }
    }
  }

  _includeAsFile(entry) {
    const stats = entry && entry[this._statsProp];

    return stats && this._wantsEverything && !stats.isDirectory();
  }
}

/**
 * @typedef {Object} ReaddirpArguments
 * @property {Function=} fileFilter
 * @property {Function=} directoryFilter
 * @property {String=} type
 * @property {Number=} depth
 * @property {String=} root
 * @property {Boolean=} lstat
 * @property {Boolean=} bigint
 */

/**
 * Main function which ends up calling readdirRec and reads all files and directories in given root recursively.
 * @param {String} root Root directory
 * @param {ReaddirpArguments=} options Options to specify root (start directory), filters and recursion depth
 */
const readdirp = (root, options = {}) => {
  let type = options.entryType || options.type;
  if (type === 'both') type = FILE_DIR_TYPE; // backwards-compatibility
  if (type) options.type = type;
  if (!root) {
    throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');
  } else if (typeof root !== 'string') {
    throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)');
  } else if (type && !ALL_TYPES.includes(type)) {
    throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);
  }

  options.root = root;
  return new ReaddirpStream(options);
};

const readdirpPromise = (root, options = {}) => {
  return new Promise((resolve, reject) => {
    const files = [];
    readdirp(root, options)
      .on('data', entry => files.push(entry))
      .on('end', () => resolve(files))
      .on('error', error => reject(error));
  });
};

readdirp.promise = readdirpPromise;
readdirp.ReaddirpStream = ReaddirpStream;
readdirp.default = readdirp;

module.exports = readdirp;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    // TypeScript Version: 3.2

/// <reference types="node" lib="esnext" />

import * as fs from 'fs';
import { Readable } from 'stream';

declare namespace readdir {
  interface EntryInfo {
    path: string;
    fullPath: string;
    basename: string;
    stats?: fs.Stats;
    dirent?: fs.Dirent;
  }

  interface ReaddirpOptions {
    root?: string;
    fileFilter?: string | string[] | ((entry: EntryInfo) => boolean);
    directoryFilter?: string | string[] | ((entry: EntryInfo) => boolean);
    type?: 'files' | 'directories' | 'files_directories' | 'all';
    lstat?: boolean;
    depth?: number;
    alwaysStat?: boolean;
  }

  interface ReaddirpStream extends Readable, AsyncIterable<EntryInfo> {
    read(): EntryInfo;
    [Symbol.asyncIterator](): AsyncIterableIterator<EntryInfo>;
  }

  function promise(
    root: string,
    options?: ReaddirpOptions
  ): Promise<EntryInfo[]>;
}

declare function readdir(
  root: string,
  options?: readdir.ReaddirpOptions
): readdir.ReaddirpStream;

export = readdir;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      # readdirp [![Weekly downloads](https://img.shields.io/npm/dw/readdirp.svg)](https://github.com/paulmillr/readdirp)

Recursive version of [fs.readdir](https://nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback). Exposes a **stream API** and a **promise API**.


```sh
npm install readdirp
```

```javascript
const readdirp = require('readdirp');

// Use streams to achieve small RAM & CPU footprint.
// 1) Streams example with for-await.
for await (const entry of readdirp('.')) {
  const {path} = entry;
  console.log(`${JSON.stringify({path})}`);
}

// 2) Streams example, non for-await.
// Print out all JS files along with their size within the current folder & subfolders.
readdirp('.', {fileFilter: '*.js', alwaysStat: true})
  .on('data', (entry) => {
    const {path, stats: {size}} = entry;
    console.log(`${JSON.stringify({path, size})}`);
  })
  // Optionally call stream.destroy() in `warn()` in order to abort and cause 'close' to be emitted
  .on('warn', error => console.error('non-fatal error', error))
  .on('error', error => console.error('fatal error', error))
  .on('end', () => console.log('done'));

// 3) Promise example. More RAM and CPU than streams / for-await.
const files = await readdirp.promise('.');
console.log(files.map(file => file.path));

// Other options.
readdirp('test', {
  fileFilter: '*.js',
  directoryFilter: ['!.git', '!*modules']
  // directoryFilter: (di) => di.basename.length === 9
  type: 'files_directories',
  depth: 1
});
```

For more examples, check out `examples` directory.

## API

`const stream = readdirp(root[, options])` — **Stream API**

- Reads given root recursively and returns a `stream` of [entry infos](#entryinfo)
- Optionally can be used like `for await (const entry of stream)` with node.js 10+ (`asyncIterator`).
- `on('data', (entry) => {})` [entry info](#entryinfo) for every file / dir.
- `on('warn', (error) => {})` non-fatal `Error` that prevents a file / dir from being processed. Example: inaccessible to the user.
- `on('error', (error) => {})` fatal `Error` which also ends the stream. Example: illegal options where passed.
- `on('end')` — we are done. Called when all entries were found and no more will be emitted.
- `on('close')` — stream is destroyed via `stream.destroy()`.
  Could be useful if you want to manually abort even on a non fatal error.
  At that point the stream is no longer `readable` and no more entries, warning or errors are emitted
- To learn more about streams, consult the very detailed [nodejs streams documentation](https://nodejs.org/api/stream.html)
  or the [stream-handbook](https://github.com/substack/stream-handbook)

`const entries = await readdirp.promise(root[, options])` — **Promise API**. Returns a list of [entry infos](#entryinfo).

First argument is awalys `root`, path in which to start reading and recursing into subdirectories.

### options

- `fileFilter: ["*.js"]`: filter to include or exclude files. A `Function`, Glob string or Array of glob strings.
    - **Function**: a function that takes an entry info as a parameter and returns true to include or false to exclude the entry
    - **Glob string**: a string (e.g., `*.js`) which is matched using [picomatch](https://github.com/micromatch/picomatch), so go there for more
        information. Globstars (`**`) are not supported since specifying a recursive pattern for an already recursive function doesn't make sense. Negated globs (as explained in the minimatch documentation) are allowed, e.g., `!*.txt` matches everything but text files.
    - **Array of glob strings**: either need to be all inclusive or all exclusive (negated) patterns otherwise an error is thrown.
        `['*.json', '*.js']` includes all JavaScript and Json files.
        `['!.git', '!node_modules']` includes all directories except the '.git' and 'node_modules'.
    - Directories that do not pass a filter will not be recursed into.
- `directoryFilter: ['!.git']`: filter to include/exclude directories found and to recurse into. Directories that do not pass a filter will not be recursed into.
- `depth: 5`: depth at which to stop recursing even if more subdirectories are found
- `type: 'files'`: determines if data events on the stream should be emitted for `'files'` (default), `'directories'`, `'files_directories'`, or `'all'`. Setting to `'all'` will also include entries for other types of file descriptors like character devices, unix sockets and named pipes.
- `alwaysStat: false`: always return `stats` property for every file. Default is `false`, readdirp will return `Dirent` entries. Setting it to `true` can double readdir execution time - use it only when you need file `size`, `mtime` etc. Cannot be enabled on node <10.10.0.
- `lstat: false`: include symlink entries in the stream along with files. When `true`, `fs.lstat` would be used instead of `fs.stat`

### `EntryInfo`

Has the following properties:

- `path: 'assets/javascripts/react.js'`: path to the file/directory (relative to given root)
- `fullPath: '/Users/dev/projects/app/assets/javascripts/react.js'`: full path to the file/directory found
- `basename: 'react.js'`: name of the file/directory
- `dirent: fs.Dirent`: built-in [dir entry object](https://nodejs.org/api/fs.html#fs_class_fs_dirent) - only with `alwaysStat: false`
- `stats: fs.Stats`: built in [stat object](https://nodejs.org/api/fs.html#fs_class_fs_stats) - only with `alwaysStat: true`

## Changelog

- 3.5 (Oct 13, 2020) disallows recursive directory-based symlinks.
  Before, it could have entered infinite loop.
- 3.4 (Mar 19, 2020) adds support for directory-based symlinks.
- 3.3 (Dec 6, 2019) stabilizes RAM consumption and enables perf management with `highWaterMark` option. Fixes race conditions related to `for-await` looping.
- 3.2 (Oct 14, 2019) improves performance by 250% and makes streams implementation more idiomatic.
- 3.1 (Jul 7, 2019) brings `bigint` support to `stat` output on Windows. This is backwards-incompatible for some cases. Be careful. It you use it incorrectly, you'll see "TypeError: Cannot mix BigInt and other types, use explicit conversions".
- 3.0 brings huge performance improvements and stream backpressure support.
- Upgrading 2.x to 3.x:
    - Signature changed from `readdirp(options)` to `readdirp(root, options)`
    - Replaced callback API with promise API.
    - Renamed `entryType` option to `type`
    - Renamed `entryType: 'both'` to `'files_directories'`
    - `EntryInfo`
        - Renamed `stat` to `stats`
            - Emitted only when `alwaysStat: true`
            - `dirent` is emitted instead of `stats` by default with `alwaysStat: false`
        - Renamed `name` to `basename`
        - Removed `parentDir` and `fullParentDir` properties
- Supported node.js versions:
    - 3.x: node 8+
    - 2.x: node 0.6+

## License

Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (<https://paulmillr.com>)

MIT License, see [LICENSE](LICENSE) file.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  {
	"name": "is-fullwidth-code-point",
	"version": "3.0.0",
	"description": "Check if the character represented by a given Unicode code point is fullwidth",
	"license": "MIT",
	"repository": "sindresorhus/is-fullwidth-code-point",
	"author": {
		"name": "Sindre Sorhus",
		"email": "sindresorhus@gmail.com",
		"url": "sindresorhus.com"
	},
	"engines": {
		"node": ">=8"
	},
	"scripts": {
		"test": "xo && ava && tsd-check"
	},
	"files": [
		"index.js",
		"index.d.ts"
	],
	"keywords": [
		"fullwidth",
		"full-width",
		"full",
		"width",
		"unicode",
		"character",
		"string",
		"codepoint",
		"code",
		"point",
		"is",
		"detect",
		"check"
	],
	"devDependencies": {
		"ava": "^1.3.1",
		"tsd-check": "^0.5.0",
		"xo": "^0.24.0"
	}
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               /* eslint-disable yoda */
'use strict';

const isFullwidthCodePoint = codePoint => {
	if (Number.isNaN(codePoint)) {
		return false;
	}

	// Code points are derived from:
	// http://www.unix.org/Public/UNIDATA/EastAsianWidth.txt
	if (
		codePoint >= 0x1100 && (
			codePoint <= 0x115F || // Hangul Jamo
			codePoint === 0x2329 || // LEFT-POINTING ANGLE BRACKET
			codePoint === 0x232A || // RIGHT-POINTING ANGLE BRACKET
			// CJK Radicals Supplement .. Enclosed CJK Letters and Months
			(0x2E80 <= codePoint && codePoint <= 0x3247 && codePoint !== 0x303F) ||
			// Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
			(0x3250 <= codePoint && codePoint <= 0x4DBF) ||
			// CJK Unified Ideographs .. Yi Radicals
			(0x4E00 <= codePoint && codePoint <= 0xA4C6) ||
			// Hangul Jamo Extended-A
			(0xA960 <= codePoint && codePoint <= 0xA97C) ||
			// Hangul Syllables
			(0xAC00 <= codePoint && codePoint <= 0xD7A3) ||
			// CJK Compatibility Ideographs
			(0xF900 <= codePoint && codePoint <= 0xFAFF) ||
			// Vertical Forms
			(0xFE10 <= codePoint && codePoint <= 0xFE19) ||
			// CJK Compatibility Forms .. Small Form Variants
			(0xFE30 <= codePoint && codePoint <= 0xFE6B) ||
			// Halfwidth and Fullwidth Forms
			(0xFF01 <= codePoint && codePoint <= 0xFF60) ||
			(0xFFE0 <= codePoint && codePoint <= 0xFFE6) ||
			// Kana Supplement
			(0x1B000 <= codePoint && codePoint <= 0x1B001) ||
			// Enclosed Ideographic Supplement
			(0x1F200 <= codePoint && codePoint <= 0x1F251) ||
			// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
			(0x20000 <= codePoint && codePoint <= 0x3FFFD)
		)
	) {
		return true;
	}

	return false;
};

module.exports = isFullwidthCodePoint;
module.exports.default = isFullwidthCodePoint;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    # is-fullwidth-code-point [![Build Status](https://travis-ci.org/sindresorhus/is-fullwidth-code-point.svg?branch=master)](https://travis-ci.org/sindresorhus/is-fullwidth-code-point)

> Check if the character represented by a given [Unicode code point](https://en.wikipedia.org/wiki/Code_point) is [fullwidth](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms)


## Install

```
$ npm install is-fullwidth-code-point
```


## Usage

```js
const isFullwidthCodePoint = require('is-fullwidth-code-point');

isFullwidthCodePoint('谢'.codePointAt(0));
//=> true

isFullwidthCodePoint('a'.codePointAt(0));
//=> false
```


## API

### isFullwidthCodePoint(codePoint)

#### codePoint

Type: `number`

The [code point](https://en.wikipedia.org/wiki/Code_point) of a character.


## License

MIT © [Sindre Sorhus](https://sindresorhus.com)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     MIT License

Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           {
  "name": "get-uri",
  "version": "6.0.4",
  "description": "Returns a `stream.Readable` from a URI string",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "files": [
    "dist"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/TooTallNate/proxy-agents.git",
    "directory": "packages/get-uri"
  },
  "keywords": [
    "uri",
    "read",
    "readstream",
    "stream",
    "get",
    "http",
    "https",
    "ftp",
    "file",
    "data",
    "protocol",
    "url"
  ],
  "author": "Nathan Rajlich <nathan@tootallnate.net> (http://n8.io/)",
  "license": "MIT",
  "devDependencies": {
    "@types/debug": "^4.1.7",
    "@types/ftpd": "^0.2.35",
    "@types/jest": "^29.5.1",
    "@types/node": "^14.18.45",
    "async-listen": "^3.0.0",
    "ftpd": "https://files-jg1s1zt9l.n8.io/ftpd-v0.2.14.tgz",
    "jest": "^29.5.0",
    "st": "^3.0.0",
    "ts-jest": "^29.1.0",
    "typescript": "^5.0.4",
    "tsconfig": "0.0.0"
  },
  "dependencies": {
    "basic-ftp": "^5.0.2",
    "data-uri-to-buffer": "^6.0.2",
    "debug": "^4.3.4"
  },
  "engines": {
    "node": ">= 14"
  },
  "scripts": {
    "build": "tsc",
    "test": "jest --env node --verbose --bail",
    "lint": "eslint . --ext .ts",
    "pack": "node ../../scripts/pack.mjs"
  }
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              (The MIT License)

Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   get-uri
=======
### Returns a `stream.Readable` from a URI string

This high-level module accepts a URI string and returns a `Readable` stream
instance. There is built-in support for a variety of "protocols", and it's
easily extensible with more:

| Protocol  | Description                     | Example
|:---------:|:-------------------------------:|:---------------------------------:
| `data`    | [Data URIs][data]               | `data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D`
| `file`    | [File URIs][file]               | `file:///c:/windows/example.ini`
| `ftp`     | [FTP URIs][ftp]                 | `ftp://ftp.kernel.org/pub/site/README`
| `http`    | [HTTP URIs][http]               | `http://www.example.com/path/to/name`
| `https`   | [HTTPS URIs][https]             | `https://www.example.com/path/to/name`

Example
-------

To simply get a `stream.Readable` instance from a `file:` URI, try something like:

```ts
import { getUri } from 'get-uri';

// `file:` maps to a `fs.ReadStream` instance…
const stream = await getUri('file:///Users/nrajlich/wat.json');
stream.pipe(process.stdout);
```


Missing Endpoints
-----------------

When you pass in a URI in which the resource referenced does not exist on the
destination server, then a `NotFoundError` will be thrown. The `code` of the
error instance is set to `"ENOTFOUND"`, so you can check for that value
to detect when a bad filename is requested:

```ts
try {
  await getUri('http://example.com/resource.json');
} catch (err) {
  if (err.code === 'ENOTFOUND') {
    // bad file path requested
  } else {
    // something else bad happened...
    throw err;
  }
}
```


Cacheability
------------

When calling `getUri()` with the same URI multiple times, the `get-uri` module
supports sending an indicator that the remote resource has not been modified
since the last time it has been retrieved from that node process.

To do this, define a `cache` property on the "options object" argument
with the value set to the `stream.Readable` instance that was previously
returned. If the remote resource has not been changed since the last call for
that same URI, then a `NotModifiedError` instance will be thrown with its
`code` property set to `"ENOTMODIFIED"`.

When the `"ENOTMODIFIED"` error occurs, then you can safely re-use the
results from the previous `getUri()` call for that same URI:

``` js
// First time fetches for real
const stream = await getUri('http://example.com/resource.json');

try {
  // … some time later, if you need to get this same URI again, pass in the
  // previous `stream.Readable` instance as `cache` option to potentially
  // have an "ENOTMODIFIED" error thrown:
  await getUri('http://example.com/resource.json', { cache: stream });
} catch (err) {
  if (err.code === 'ENOTMODIFIED') {
    // source file has not been modified since last time it was requested,
    // so you are expected to re-use results from a previous call to `getUri()`
  } else {
    // something else bad happened...
    throw err;
  }
}
```


API
---

### getUri(uri: string | URL, options?: Object]): Promise<Readable>

A `uri` is required. An optional `options` object may be passed in:

 - `cache` - A `stream.Readable` instance from a previous call to `getUri()` with the same URI. If this option is passed in, and the destination endpoint has not been modified, then an `ENOTMODIFIED` error is thrown

Any other options passed in to the `options` object will be passed through
to the low-level connection creation functions (`http.get()`, `ftp.connect()`,
etc).

Returns a `stream.Readable` instance to read the resource at the given `uri`.

[data]: http://tools.ietf.org/html/rfc2397
[file]: http://tools.ietf.org/html/draft-hoffman-file-uri-03
[ftp]: http://www.w3.org/Protocols/rfc959/
[http]: http://www.w3.org/Protocols/rfc2616/rfc2616.html
[https]: http://wikipedia.org/wiki/HTTP_Secure
                                                                                                                                                                                                                      {
  "name": "glob-parent",
  "version": "5.1.2",
  "description": "Extract the non-magic parent path from a glob string.",
  "author": "Gulp Team <team@gulpjs.com> (https://gulpjs.com/)",
  "contributors": [
    "Elan Shanker (https://github.com/es128)",
    "Blaine Bublitz <blaine.bublitz@gmail.com>"
  ],
  "repository": "gulpjs/glob-parent",
  "license": "ISC",
  "engines": {
    "node": ">= 6"
  },
  "main": "index.js",
  "files": [
    "LICENSE",
    "index.js"
  ],
  "scripts": {
    "lint": "eslint .",
    "pretest": "npm run lint",
    "test": "nyc mocha --async-only",
    "azure-pipelines": "nyc mocha --async-only --reporter xunit -O output=test.xunit",
    "coveralls": "nyc report --reporter=text-lcov | coveralls"
  },
  "dependencies": {
    "is-glob": "^4.0.1"
  },
  "devDependencies": {
    "coveralls": "^3.0.11",
    "eslint": "^2.13.1",
    "eslint-config-gulp": "^3.0.1",
    "expect": "^1.20.2",
    "mocha": "^6.0.2",
    "nyc": "^13.3.0"
  },
  "keywords": [
    "glob",
    "parent",
    "strip",
    "path",
    "dirname",
    "directory",
    "base",
    "wildcard"
  ]
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               The ISC License

Copyright (c) 2015, 2019 Elan Shanker

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ### [5.1.2](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2) (2021-03-06)


### Bug Fixes

* eliminate ReDoS ([#36](https://github.com/gulpjs/glob-parent/issues/36)) ([f923116](https://github.com/gulpjs/glob-parent/commit/f9231168b0041fea3f8f954b3cceb56269fc6366))

### [5.1.1](https://github.com/gulpjs/glob-parent/compare/v5.1.0...v5.1.1) (2021-01-27)


### Bug Fixes

* unescape exclamation mark ([#26](https://github.com/gulpjs/glob-parent/issues/26)) ([a98874f](https://github.com/gulpjs/glob-parent/commit/a98874f1a59e407f4fb1beb0db4efa8392da60bb))

## [5.1.0](https://github.com/gulpjs/glob-parent/compare/v5.0.0...v5.1.0) (2021-01-27)


### Features

* add `flipBackslashes` option to disable auto conversion of slashes (closes [#24](https://github.com/gulpjs/glob-parent/issues/24)) ([#25](https://github.com/gulpjs/glob-parent/issues/25)) ([eecf91d](https://github.com/gulpjs/glob-parent/commit/eecf91d5e3834ed78aee39c4eaaae654d76b87b3))

## [5.0.0](https://github.com/gulpjs/glob-parent/compare/v4.0.0...v5.0.0) (2021-01-27)


### ⚠ BREAKING CHANGES

* Drop support for node <6 & bump dependencies

### Miscellaneous Chores

* Drop support for node <6 & bump dependencies ([896c0c0](https://github.com/gulpjs/glob-parent/commit/896c0c00b4e7362f60b96e7fc295ae929245255a))

## [4.0.0](https://github.com/gulpjs/glob-parent/compare/v3.1.0...v4.0.0) (2021-01-27)


### ⚠ BREAKING CHANGES

* question marks are valid path characters on Windows so avoid flagging as a glob when alone
* Update is-glob dependency

### Features

* hoist regexps and strings for performance gains ([4a80667](https://github.com/gulpjs/glob-parent/commit/4a80667c69355c76a572a5892b0f133c8e1f457e))
* question marks are valid path characters on Windows so avoid flagging as a glob when alone ([2a551dd](https://github.com/gulpjs/glob-parent/commit/2a551dd0dc3235e78bf3c94843d4107072d17841))
* Update is-glob dependency ([e41fcd8](https://github.com/gulpjs/glob-parent/commit/e41fcd895d1f7bc617dba45c9d935a7949b9c281))

## [3.1.0](https://github.com/gulpjs/glob-parent/compare/v3.0.1...v3.1.0) (2021-01-27)


### Features

* allow basic win32 backslash use ([272afa5](https://github.com/gulpjs/glob-parent/commit/272afa5fd070fc0f796386a5993d4ee4a846988b))
* handle extglobs (parentheses) containing separators ([7db1bdb](https://github.com/gulpjs/glob-parent/commit/7db1bdb0756e55fd14619e8ce31aa31b17b117fd))
* new approach to braces/brackets handling ([8269bd8](https://github.com/gulpjs/glob-parent/commit/8269bd89290d99fac9395a354fb56fdcdb80f0be))
* pre-process braces/brackets sections ([9ef8a87](https://github.com/gulpjs/glob-parent/commit/9ef8a87f66b1a43d0591e7a8e4fc5a18415ee388))
* preserve escaped brace/bracket at end of string ([8cfb0ba](https://github.com/gulpjs/glob-parent/commit/8cfb0ba84202d51571340dcbaf61b79d16a26c76))


### Bug Fixes

* trailing escaped square brackets ([99ec9fe](https://github.com/gulpjs/glob-parent/commit/99ec9fecc60ee488ded20a94dd4f18b4f55c4ccf))

### [3.0.1](https://github.com/gulpjs/glob-parent/compare/v3.0.0...v3.0.1) (2021-01-27)


### Features

* use path-dirname ponyfill ([cdbea5f](https://github.com/gulpjs/glob-parent/commit/cdbea5f32a58a54e001a75ddd7c0fccd4776aacc))


### Bug Fixes

* unescape glob-escaped dirnames on output ([598c533](https://github.com/gulpjs/glob-parent/commit/598c533bdf49c1428bc063aa9b8db40c5a86b030))

## [3.0.0](https://github.com/gulpjs/glob-parent/compare/v2.0.0...v3.0.0) (2021-01-27)


### ⚠ BREAKING CHANGES

* update is-glob dependency

### Features

* update is-glob dependency ([5c5f8ef](https://github.com/gulpjs/glob-parent/commit/5c5f8efcee362a8e7638cf8220666acd8784f6bd))

## [2.0.0](https://github.com/gulpjs/glob-parent/compare/v1.3.0...v2.0.0) (2021-01-27)


### Features

* move up to dirname regardless of glob characters ([f97fb83](https://github.com/gulpjs/glob-parent/commit/f97fb83be2e0a9fc8d3b760e789d2ecadd6aa0c2))

## [1.3.0](https://github.com/gulpjs/glob-parent/compare/v1.2.0...v1.3.0) (2021-01-27)

## [1.2.0](https://github.com/gulpjs/glob-parent/compare/v1.1.0...v1.2.0) (2021-01-27)


### Reverts

* feat: make regex test strings smaller ([dc80fa9](https://github.com/gulpjs/glob-parent/commit/dc80fa9658dca20549cfeba44bbd37d5246fcce0))

## [1.1.0](https://github.com/gulpjs/glob-parent/compare/v1.0.0...v1.1.0) (2021-01-27)


### Features

* make regex test strings smaller ([cd83220](https://github.com/gulpjs/glob-parent/commit/cd832208638f45169f986d80fcf66e401f35d233))

## 1.0.0 (2021-01-27)

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  <p align="center">
  <a href="https://gulpjs.com">
    <img height="257" width="114" src="https://raw.githubusercontent.com/gulpjs/artwork/master/gulp-2x.png">
  </a>
</p>

# glob-parent

[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Azure Pipelines Build Status][azure-pipelines-image]][azure-pipelines-url] [![Travis Build Status][travis-image]][travis-url] [![AppVeyor Build Status][appveyor-image]][appveyor-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Gitter chat][gitter-image]][gitter-url]

Extract the non-magic parent path from a glob string.

## Usage

```js
var globParent = require('glob-parent');

globParent('path/to/*.js'); // 'path/to'
globParent('/root/path/to/*.js'); // '/root/path/to'
globParent('/*.js'); // '/'
globParent('*.js'); // '.'
globParent('**/*.js'); // '.'
globParent('path/{to,from}'); // 'path'
globParent('path/!(to|from)'); // 'path'
globParent('path/?(to|from)'); // 'path'
globParent('path/+(to|from)'); // 'path'
globParent('path/*(to|from)'); // 'path'
globParent('path/@(to|from)'); // 'path'
globParent('path/**/*'); // 'path'

// if provided a non-glob path, returns the nearest dir
globParent('path/foo/bar.js'); // 'path/foo'
globParent('path/foo/'); // 'path/foo'
globParent('path/foo'); // 'path' (see issue #3 for details)
```

## API

### `globParent(maybeGlobString, [options])`

Takes a string and returns the part of the path before the glob begins. Be aware of Escaping rules and Limitations below.

#### options

```js
{
  // Disables the automatic conversion of slashes for Windows
  flipBackslashes: true
}
```

## Escaping

The following characters have special significance in glob patterns and must be escaped if you want them to be treated as regular path characters:

- `?` (question mark) unless used as a path segment alone
- `*` (asterisk)
- `|` (pipe)
- `(` (opening parenthesis)
- `)` (closing parenthesis)
- `{` (opening curly brace)
- `}` (closing curly brace)
- `[` (opening bracket)
- `]` (closing bracket)

**Example**

```js
globParent('foo/[bar]/') // 'foo'
globParent('foo/\\[bar]/') // 'foo/[bar]'
```

## Limitations

### Braces & Brackets
This library attempts a quick and imperfect method of determining which path
parts have glob magic without fully parsing/lexing the pattern. There are some
advanced use cases that can trip it up, such as nested braces where the outer
pair is escaped and the inner one contains a path separator. If you find
yourself in the unlikely circumstance of being affected by this or need to
ensure higher-fidelity glob handling in your library, it is recommended that you
pre-process your input with [expand-braces] and/or [expand-brackets].

### Windows
Backslashes are not valid path separators for globs. If a path with backslashes
is provided anyway, for simple cases, glob-parent will replace the path
separator for you and return the non-glob parent path (now with
forward-slashes, which are still valid as Windows path separators).

This cannot be used in conjunction with escape characters.

```js
// BAD
globParent('C:\\Program Files \\(x86\\)\\*.ext') // 'C:/Program Files /(x86/)'

// GOOD
globParent('C:/Program Files\\(x86\\)/*.ext') // 'C:/Program Files (x86)'
```

If you are using escape characters for a pattern without path parts (i.e.
relative to `cwd`), prefix with `./` to avoid confusing glob-parent.

```js
// BAD
globParent('foo \\[bar]') // 'foo '
globParent('foo \\[bar]*') // 'foo '

// GOOD
globParent('./foo \\[bar]') // 'foo [bar]'
globParent('./foo \\[bar]*') // '.'
```

## License

ISC

[expand-braces]: https://github.com/jonschlinkert/expand-braces
[expand-brackets]: https://github.com/jonschlinkert/expand-brackets

[downloads-image]: https://img.shields.io/npm/dm/glob-parent.svg
[npm-url]: https://www.npmjs.com/package/glob-parent
[npm-image]: https://img.shields.io/npm/v/glob-parent.svg

[azure-pipelines-url]: https://dev.azure.com/gulpjs/gulp/_build/latest?definitionId=2&branchName=master
[azure-pipelines-image]: https://dev.azure.com/gulpjs/gulp/_apis/build/status/glob-parent?branchName=master

[travis-url]: https://travis-ci.org/gulpjs/glob-parent
[travis-image]: https://img.shields.io/travis/gulpjs/glob-parent.svg?label=travis-ci

[appveyor-url]: https://ci.appveyor.com/project/gulpjs/glob-parent
[appveyor-image]: https://img.shields.io/appveyor/ci/gulpjs/glob-parent.svg?label=appveyor

[coveralls-url]: https://coveralls.io/r/gulpjs/glob-parent
[coveralls-image]: https://img.shields.io/coveralls/gulpjs/glob-parent/master.svg

[gitter-url]: https://gitter.im/gulpjs/gulp
[gitter-image]: https://badges.gitter.im/gulpjs/gulp.svg
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          {
  "name": "json-rpc-protocol",
  "version": "0.13.2",
  "license": "ISC",
  "description": "JSON-RPC 2 protocol messages parsing and formatting",
  "keywords": [
    "json",
    "json-rpc",
    "jsonrpc",
    "jsonrpc2",
    "rpc"
  ],
  "homepage": "https://github.com/JsCommunity/json-rpc-protocol",
  "bugs": "https://github.com/JsCommunity/json-rpc-protocol/issues",
  "repository": {
    "type": "git",
    "url": "https://github.com/JsCommunity/json-rpc-protocol.git"
  },
  "author": {
    "name": "Julien Fontanet",
    "email": "julien.fontanet@isonoe.net"
  },
  "preferGlobal": false,
  "main": "dist/",
  "typings": "dist/index.d.ts",
  "bin": {},
  "files": [
    "dist/"
  ],
  "engines": {
    "node": ">=4"
  },
  "dependencies": {
    "make-error": "^1.3.0"
  },
  "devDependencies": {
    "cross-env": "^5.0.0",
    "dependency-check": "^3.1.0",
    "husky": "^1.1.1",
    "jest": "^23.1.0",
    "rimraf": "^2.6.1",
    "ts-jest": "^23.10.4",
    "tslint": "^5.10.0",
    "tslint-config-standard": "^8.0.1",
    "typescript": "^3.1.1"
  },
  "scripts": {
    "build": "cross-env NODE_ENV=production tsc",
    "dev": "cross-env NODE_ENV=development tsc --watch",
    "dev-test": "jest --bail --watch",
    "lint": "tslint --project tsconfig.json && tsc --noEmit",
    "pretest": "npm run lint",
    "posttest": "dependency-check ./package.json",
    "prebuild": "rimraf dist/",
    "predev": "npm run prebuild",
    "prepare": "npm run build",
    "test": "jest"
  },
  "jest": {
    "testEnvironment": "node",
    "roots": [
      "<rootDir>/src"
    ],
    "transform": {
      "^.+\\.ts?$": "ts-jest"
    },
    "testRegex": "\\.spec\\.ts$",
    "moduleFileExtensions": [
      "js",
      "ts"
    ]
  },
  "husky": {
    "hooks": {
      "commit-msg": "npm run test"
    }
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     # json-rpc-protocol [![Build Status](https://travis-ci.org/JsCommunity/json-rpc-protocol.png?branch=master)](https://travis-ci.org/JsCommunity/json-rpc-protocol) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-blue.svg)](https://www.typescriptlang.org/)

> JSON-RPC 2 protocol messages parsing and formatting

## Install

Installation of the [npm package](https://npmjs.org/package/json-rpc-protocol):

```
> npm install --save json-rpc-protocol
```

## Usage

### Errors

```javascript
// ES5
var protocol = require('json-rpc-protocol')

var JsonRpcError = protocol.JsonRpcError
var InvalidJson = protocol.InvalidJson
var InvalidRequest = protocol.InvalidRequest
var MethodNotFound = protocol.MethodNotFound
var InvalidParameters = protocol.InvalidParameters

// ES6
import {
  JsonRpcError,
  InvalidJson,
  InvalidRequest,
  MethodNotFound,
  InvalidParameters
} from 'json-rpc-protocol'
```

This is the base error for all JSON-RPC errors:

```javascript
throw new JsonRpcError(message, code)
```

The JSON-RPC 2 specification defined also the following specialized
errors:

```javascript
// Parse error: invalid JSON was received by the peer.
throw new InvalidJson()

// Invalid request: the JSON sent is not a valid JSON-RPC 2 message.
throw new InvalidRequest()

// Method not found: the method does not exist or is not available.
throw new MethodNotFound(methodName)

// Invalid parameters.
throw new InvalidParameters(data)
```

Custom errors can of course be created, they just have to inherit
`JsonRpcError`:

```javascript
// ES5
function MyError () {
  JsonRpcError.call(this, 'my error', 1)
}
MyError.prototype = Object.create(JsonRpcError.prototype, {
  constructor: {
    value: MyError
  }
})

// ES6
class MyError extends JsonRpcError {
  constructor () {
    super('my error', 1)
  }
}
```

### Parsing

```javascript
// ES5
var parse = require('json-rpc-protocol').parse

// ES6
import {parse} from 'json-rpc-protocol'
```

The `parse()` function parses, normalizes and validates JSON-RPC 1 or
JSON-RPC 2 messages.

These message can be either JS objects or JSON strings (they will be
parsed automatically).

This function may throws:

- `InvalidJson`: if the string cannot be parsed as a JSON;
- `InvalidRequest`: if the message is not a valid JSON-RPC message.

```javascript
parse('{"jsonrpc":"2.0", "method": "foo", "params": ["bar"]}')
// → {
//   [type: 'notification']
//   jsonrpc: '2.0',
//   method: 'foo',
//   params: ['bar']
// }

parse('{"jsonrpc":"2.0", "id": 0, "method": "add", "params": [1, 2]}')
// → {
//   [type: 'request']
//   jsonrpc: '2.0',
//   id: 0,
//   method: 'add',
//   params: [1, 2]
// }

parse('{"jsonrpc":"2.0", "id": 0, "result": 3}')
// → {
//   [type: 'response']
//   jsonrpc: '2.0',
//   id: 0,
//   result: 3
// }
```

> A parsed message has a non enumerable property `type` set to easily
> differentiate between types of JSON-RPC messages.


#### Response/Error

The `parse.result` helper parses and returns the result of a response message or throws the error of an error message:

```js
try {
  const result = await parse.result(message)
  // do something with the result
} catch (error) {
  // deal with the failure
}
```

### Formatting

```javascript
// ES5
var format = require('json-rpc-protocol').format

// ES6
import {format} from 'json-rpc-protocol'
```

The `format.*()` functions can be used to create valid JSON-RPC
messages (as JavaScript strings).

#### Notification

```javascript
format.notification('foo', ['bars'])
// → {
//   "jsonrpc": "2.0",
//   "method": "foo",
//   "params": ["bar"]
// }
```

The last argument, the parameters of the notification, is optional and
defaults to `undefined`.

#### Request

The last argument, the parameters of the request, is optional and
defaults to `undefined`.

```javascript
format.request(0, 'add', [1, 2])
// → {
//   "jsonrpc": "2.0",
//   "id": 0,
//   "method": "add",
//   "params": [1, 2]
// }
```

#### Response

A successful response:

```javascript
format.response(0, 3)
// → {
//   "jsonrpc": "2.0",
//   "id": 0,
//   "result": 3
// }
```

A failed response:

```javascript
var MethodNotFound = require('json-rpc-protocol').MethodNotFound

format.error(0, new MethodNotFound('add'))
// → {
//   "jsonrpc": "2.0",
//   "id": 0,
//   "error": {
//     "code": -3601,
//     "message": "method not found: add",
//     "data": "add"
//   }
// }
```

Note: the error to format must implement a `toJsonRpcError` function which returns an object or it
will be automatically replaced by an unknown error for security
reasons.

`toJsonRpcError` example:

```js
toJsonRpcError () {
  return {
    code: 42, // must be an integer
    message: 'Hacking too much time!', // must be a string
    data: [ 'Hackerman' ] // optional
  }
}
```

## Development

```
# Install dependencies
> npm install

# Run the tests
> npm test

# Continuously compile
> npm run dev

# Continuously run the tests
> npm run dev-test

# Build for production (automatically called by npm install)
> npm run build
```

## Related

- [json-rpc-peer](https://github.com/JsCommunity/json-rpc-peer) − High level interface

## Contributions

Contributions are *very* welcomed, either on the documentation or on
the code.

You may:

- report any [issue](https://github.com/JsCommunity/json-rpc-protocol/issues)
  you've encountered;
- fork and create a pull request.

## License

ISC © [Julien Fontanet](http://julien.isonoe.net)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             MIT License

Copyright (c) 2018 Nikita Skovoroda <chalkerx@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          /* eslint-disable node/no-deprecated-api */

'use strict'

var buffer = require('buffer')
var Buffer = buffer.Buffer

var safer = {}

var key

for (key in buffer) {
  if (!buffer.hasOwnProperty(key)) continue
  if (key === 'SlowBuffer' || key === 'Buffer') continue
  safer[key] = buffer[key]
}

var Safer = safer.Buffer = {}
for (key in Buffer) {
  if (!Buffer.hasOwnProperty(key)) continue
  if (key === 'allocUnsafe' || key === 'allocUnsafeSlow') continue
  Safer[key] = Buffer[key]
}

safer.Buffer.prototype = Buffer.prototype

if (!Safer.from || Safer.from === Uint8Array.from) {
  Safer.from = function (value, encodingOrOffset, length) {
    if (typeof value === 'number') {
      throw new TypeError('The "value" argument must not be of type number. Received type ' + typeof value)
    }
    if (value && typeof value.length === 'undefined') {
      throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ' + typeof value)
    }
    return Buffer(value, encodingOrOffset, length)
  }
}

if (!Safer.alloc) {
  Safer.alloc = function (size, fill, encoding) {
    if (typeof size !== 'number') {
      throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size)
    }
    if (size < 0 || size >= 2 * (1 << 30)) {
      throw new RangeError('The value "' + size + '" is invalid for option "size"')
    }
    var buf = Buffer(size)
    if (!fill || fill.length === 0) {
      buf.fill(0)
    } else if (typeof encoding === 'string') {
      buf.fill(fill, encoding)
    } else {
      buf.fill(fill)
    }
    return buf
  }
}

if (!safer.kStringMaxLength) {
  try {
    safer.kStringMaxLength = process.binding('buffer').kStringMaxLength
  } catch (e) {
    // we can't determine kStringMaxLength in environments where process.binding
    // is unsupported, so let's not set it
  }
}

if (!safer.constants) {
  safer.constants = {
    MAX_LENGTH: safer.kMaxLength
  }
  if (safer.kStringMaxLength) {
    safer.constants.MAX_STRING_LENGTH = safer.kStringMaxLength
  }
}

module.exports = safer
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  # safer-buffer [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![javascript style guide][standard-image]][standard-url] [![Security Responsible Disclosure][secuirty-image]][secuirty-url]

[travis-image]: https://travis-ci.org/ChALkeR/safer-buffer.svg?branch=master
[travis-url]: https://travis-ci.org/ChALkeR/safer-buffer
[npm-image]: https://img.shields.io/npm/v/safer-buffer.svg
[npm-url]: https://npmjs.org/package/safer-buffer
[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
[standard-url]: https://standardjs.com
[secuirty-image]: https://img.shields.io/badge/Security-Responsible%20Disclosure-green.svg
[secuirty-url]: https://github.com/nodejs/security-wg/blob/master/processes/responsible_disclosure_template.md

Modern Buffer API polyfill without footguns, working on Node.js from 0.8 to current.

## How to use?

First, port all `Buffer()` and `new Buffer()` calls to `Buffer.alloc()` and `Buffer.from()` API.

Then, to achieve compatibility with outdated Node.js versions (`<4.5.0` and 5.x `<5.9.0`), use
`const Buffer = require('safer-buffer').Buffer` in all files where you make calls to the new
Buffer API. _Use `var` instead of `const` if you need that for your Node.js version range support._

Also, see the
[porting Buffer](https://github.com/ChALkeR/safer-buffer/blob/master/Porting-Buffer.md) guide.

## Do I need it?

Hopefully, not — dropping support for outdated Node.js versions should be fine nowdays, and that
is the recommended path forward. You _do_ need to port to the `Buffer.alloc()` and `Buffer.from()`
though.

See the [porting guide](https://github.com/ChALkeR/safer-buffer/blob/master/Porting-Buffer.md)
for a better description.

## Why not [safe-buffer](https://npmjs.com/safe-buffer)?

_In short: while `safe-buffer` serves as a polyfill for the new API, it allows old API usage and
itself contains footguns._

`safe-buffer` could be used safely to get the new API while still keeping support for older
Node.js versions (like this module), but while analyzing ecosystem usage of the old Buffer API
I found out that `safe-buffer` is itself causing problems in some cases.

For example, consider the following snippet:

```console
$ cat example.unsafe.js
console.log(Buffer(20))
$ ./node-v6.13.0-linux-x64/bin/node example.unsafe.js
<Buffer 0a 00 00 00 00 00 00 00 28 13 de 02 00 00 00 00 05 00 00 00>
$ standard example.unsafe.js
standard: Use JavaScript Standard Style (https://standardjs.com)
  /home/chalker/repo/safer-buffer/example.unsafe.js:2:13: 'Buffer()' was deprecated since v6. Use 'Buffer.alloc()' or 'Buffer.from()' (use 'https://www.npmjs.com/package/safe-buffer' for '<4.5.0') instead.
```

This is allocates and writes to console an uninitialized chunk of memory.
[standard](https://www.npmjs.com/package/standard) linter (among others) catch that and warn people
to avoid using unsafe API.

Let's now throw in `safe-buffer`!

```console
$ cat example.safe-buffer.js
const Buffer = require('safe-buffer').Buffer
console.log(Buffer(20))
$ standard example.safe-buffer.js
$ ./node-v6.13.0-linux-x64/bin/node example.safe-buffer.js
<Buffer 08 00 00 00 00 00 00 00 28 58 01 82 fe 7f 00 00 00 00 00 00>
```

See the problem? Adding in `safe-buffer` _magically removes the lint warning_, but the behavior
remains identiсal to what we had before, and when launched on Node.js 6.x LTS — this dumps out
chunks of uninitialized memory.
_And this code will still emit runtime warnings on Node.js 10.x and above._

That was done by design. I first considered changing `safe-buffer`, prohibiting old API usage or
emitting warnings on it, but that significantly diverges from `safe-buffer` design. After some
discussion, it was decided to move my approach into a separate package, and _this is that separate
package_.

This footgun is not imaginary — I observed top-downloaded packages doing that kind of thing,
«fixing» the lint warning by blindly including `safe-buffer` without any actual changes.

Also in some cases, even if the API _was_ migrated to use of safe Buffer API — a random pull request
can bring unsafe Buffer API usage back to the codebase by adding new calls — and that could go
unnoticed even if you have a linter prohibiting that (becase of the reason stated above), and even
pass CI. _I also observed that being done in popular packages._

Some examples:
 * [webdriverio](https://github.com/webdriverio/webdriverio/commit/05cbd3167c12e4930f09ef7cf93b127ba4effae4#diff-124380949022817b90b622871837d56cR31)
   (a module with 548 759 downloads/month),
 * [websocket-stream](https://github.com/maxogden/websocket-stream/commit/c9312bd24d08271687d76da0fe3c83493871cf61)
   (218 288 d/m, fix in [maxogden/websocket-stream#142](https://github.com/maxogden/websocket-stream/pull/142)),
 * [node-serialport](https://github.com/node-serialport/node-serialport/commit/e8d9d2b16c664224920ce1c895199b1ce2def48c)
   (113 138 d/m, fix in [node-serialport/node-serialport#1510](https://github.com/node-serialport/node-serialport/pull/1510)),
 * [karma](https://github.com/karma-runner/karma/commit/3d94b8cf18c695104ca195334dc75ff054c74eec)
   (3 973 193 d/m, fix in [karma-runner/karma#2947](https://github.com/karma-runner/karma/pull/2947)),
 * [spdy-transport](https://github.com/spdy-http2/spdy-transport/commit/5375ac33f4a62a4f65bcfc2827447d42a5dbe8b1)
   (5 970 727 d/m, fix in [spdy-http2/spdy-transport#53](https://github.com/spdy-http2/spdy-transport/pull/53)).
 * And there are a lot more over the ecosystem.

I filed a PR at
[mysticatea/eslint-plugin-node#110](https://github.com/mysticatea/eslint-plugin-node/pull/110) to
partially fix that (for cases when that lint rule is used), but it is a semver-major change for
linter rules and presets, so it would take significant time for that to reach actual setups.
_It also hasn't been released yet (2018-03-20)._

Also, `safer-buffer` discourages the usage of `.allocUnsafe()`, which is often done by a mistake.
It still supports it with an explicit concern barier, by placing it under
`require('safer-buffer/dangereous')`.

## But isn't throwing bad?

Not really. It's an error that could be noticed and fixed early, instead of causing havoc later like
unguarded `new Buffer()` calls that end up receiving user input can do.

This package affects only the files where `var Buffer = require('safer-buffer').Buffer` was done, so
it is really simple to keep track of things and make sure that you don't mix old API usage with that.
Also, CI should hint anything that you might have missed.

New commits, if tested, won't land new usage of unsafe Buffer API this way.
_Node.js 10.x also deals with that by printing a runtime depecation warning._

### Would it affect third-party modules?

No, unless you explicitly do an awful thing like monkey-patching or overriding the built-in `Buffer`.
Don't do that.

### But I don't want throwing…

That is also fine!

Also, it could be better in some cases when you don't comprehensive enough test coverage.

In that case — just don't override `Buffer` and use
`var SaferBuffer = require('safer-buffer').Buffer` instead.

That way, everything using `Buffer` natively would still work, but there would be two drawbacks:

* `Buffer.from`/`Buffer.alloc` won't be polyfilled — use `SaferBuffer.from` and
  `SaferBuffer.alloc` instead.
* You are still open to accidentally using the insecure deprecated API — use a linter to catch that.

Note that using a linter to catch accidential `Buffer` constructor usage in this case is strongly
recommended. `Buffer` is not overriden in this usecase, so linters won't get confused.

## «Without footguns»?

Well, it is still possible to do _some_ things with `Buffer` API, e.g. accessing `.buffer` property
on older versions and duping things from there. You shouldn't do that in your code, probabably.

The intention is to remove the most significant footguns that affect lots of packages in the
ecosystem, and to do it in the proper way.

Also, this package doesn't protect against security issues affecting some Node.js versions, so for
usage in your own production code, it is still recommended to update to a Node.js version
[supported by upstream](https://github.com/nodejs/release#release-schedule).
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           # Porting to the Buffer.from/Buffer.alloc API

<a id="overview"></a>
## Overview

- [Variant 1: Drop support for Node.js ≤ 4.4.x and 5.0.0 — 5.9.x.](#variant-1) (*recommended*)
- [Variant 2: Use a polyfill](#variant-2)
- [Variant 3: manual detection, with safeguards](#variant-3)

### Finding problematic bits of code using grep

Just run `grep -nrE '[^a-zA-Z](Slow)?Buffer\s*\(' --exclude-dir node_modules`.

It will find all the potentially unsafe places in your own code (with some considerably unlikely
exceptions).

### Finding problematic bits of code using Node.js 8

If you’re using Node.js ≥ 8.0.0 (which is recommended), Node.js exposes multiple options that help with finding the relevant pieces of code:

- `--trace-warnings` will make Node.js show a stack trace for this warning and other warnings that are printed by Node.js.
- `--trace-deprecation` does the same thing, but only for deprecation warnings.
- `--pending-deprecation` will show more types of deprecation warnings. In particular, it will show the `Buffer()` deprecation warning, even on Node.js 8.

You can set these flags using an environment variable:

```console
$ export NODE_OPTIONS='--trace-warnings --pending-deprecation'
$ cat example.js
'use strict';
const foo = new Buffer('foo');
$ node example.js
(node:7147) [DEP0005] DeprecationWarning: The Buffer() and new Buffer() constructors are not recommended for use due to security and usability concerns. Please use the new Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() construction methods instead.
    at showFlaggedDeprecation (buffer.js:127:13)
    at new Buffer (buffer.js:148:3)
    at Object.<anonymous> (/path/to/example.js:2:13)
    [... more stack trace lines ...]
```

### Finding problematic bits of code using linters

Eslint rules [no-buffer-constructor](https://eslint.org/docs/rules/no-buffer-constructor)
or
[node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md)
also find calls to deprecated `Buffer()` API. Those rules are included in some pre-sets.

There is a drawback, though, that it doesn't always
[work correctly](https://github.com/chalker/safer-buffer#why-not-safe-buffer) when `Buffer` is
overriden e.g. with a polyfill, so recommended is a combination of this and some other method
described above.

<a id="variant-1"></a>
## Variant 1: Drop support for Node.js ≤ 4.4.x and 5.0.0 — 5.9.x.

This is the recommended solution nowadays that would imply only minimal overhead.

The Node.js 5.x release line has been unsupported since July 2016, and the Node.js 4.x release line reaches its End of Life in April 2018 (→ [Schedule](https://github.com/nodejs/Release#release-schedule)). This means that these versions of Node.js will *not* receive any updates, even in case of security issues, so using these release lines should be avoided, if at all possible.

What you would do in this case is to convert all `new Buffer()` or `Buffer()` calls to use `Buffer.alloc()` or `Buffer.from()`, in the following way:

- For `new Buffer(number)`, replace it with `Buffer.alloc(number)`.
- For `new Buffer(string)` (or `new Buffer(string, encoding)`), replace it with `Buffer.from(string)` (or `Buffer.from(string, encoding)`).
- For all other combinations of arguments (these are much rarer), also replace `new Buffer(...arguments)` with `Buffer.from(...arguments)`.

Note that `Buffer.alloc()` is also _faster_ on the current Node.js versions than
`new Buffer(size).fill(0)`, which is what you would otherwise need to ensure zero-filling.

Enabling eslint rule [no-buffer-constructor](https://eslint.org/docs/rules/no-buffer-constructor)
or
[node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md)
is recommended to avoid accidential unsafe Buffer API usage.

There is also a [JSCodeshift codemod](https://github.com/joyeecheung/node-dep-codemod#dep005)
for automatically migrating Buffer constructors to `Buffer.alloc()` or `Buffer.from()`.
Note that it currently only works with cases where the arguments are literals or where the
constructor is invoked with two arguments.

_If you currently support those older Node.js versions and dropping them would be a semver-major change
for you, or if you support older branches of your packages, consider using [Variant 2](#variant-2)
or [Variant 3](#variant-3) on older branches, so people using those older branches will also receive
the fix. That way, you will eradicate potential issues caused by unguarded Buffer API usage and
your users will not observe a runtime deprecation warning when running your code on Node.js 10._

<a id="variant-2"></a>
## Variant 2: Use a polyfill

Utilize [safer-buffer](https://www.npmjs.com/package/safer-buffer) as a polyfill to support older
Node.js versions.

You would take exacly the same steps as in [Variant 1](#variant-1), but with a polyfill
`const Buffer = require('safer-buffer').Buffer` in all files where you use the new `Buffer` api.

Make sure that you do not use old `new Buffer` API — in any files where the line above is added,
using old `new Buffer()` API will _throw_. It will be easy to notice that in CI, though.

Alternatively, you could use [buffer-from](https://www.npmjs.com/package/buffer-from) and/or
[buffer-alloc](https://www.npmjs.com/package/buffer-alloc) [ponyfills](https://ponyfill.com/) —
those are great, the only downsides being 4 deps in the tree and slightly more code changes to
migrate off them (as you would be using e.g. `Buffer.from` under a different name). If you need only
`Buffer.from` polyfilled — `buffer-from` alone which comes with no extra dependencies.

_Alternatively, you could use [safe-buffer](https://www.npmjs.com/package/safe-buffer) — it also
provides a polyfill, but takes a different approach which has
[it's drawbacks](https://github.com/chalker/safer-buffer#why-not-safe-buffer). It will allow you
to also use the older `new Buffer()` API in your code, though — but that's arguably a benefit, as
it is problematic, can cause issues in your code, and will start emitting runtime deprecation
warnings starting with Node.js 10._

Note that in either case, it is important that you also remove all calls to the old Buffer
API manually — just throwing in `safe-buffer` doesn't fix the problem by itself, it just provides
a polyfill for the new API. I have seen people doing that mistake.

Enabling eslint rule [no-buffer-constructor](https://eslint.org/docs/rules/no-buffer-constructor)
or
[node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md)
is recommended.

_Don't forget to drop the polyfill usage once you drop support for Node.js < 4.5.0._

<a id="variant-3"></a>
## Variant 3 — manual detection, with safeguards

This is useful if you create Buffer instances in only a few places (e.g. one), or you have your own
wrapper around them.

### Buffer(0)

This special case for creating empty buffers can be safely replaced with `Buffer.concat([])`, which
returns the same result all the way down to Node.js 0.8.x.

### Buffer(notNumber)

Before:

```js
var buf = new Buffer(notNumber, encoding);
```

After:

```js
var buf;
if (Buffer.from && Buffer.from !== Uint8Array.from) {
  buf = Buffer.from(notNumber, encoding);
} else {
  if (typeof notNumber === 'number')
    throw new Error('The "size" argument must be of type number.');
  buf = new Buffer(notNumber, encoding);
}
```

`encoding` is optional.

Note that the `typeof notNumber` before `new Buffer` is required (for cases when `notNumber` argument is not
hard-coded) and _is not caused by the deprecation of Buffer constructor_ — it's exactly _why_ the
Buffer constructor is deprecated. Ecosystem packages lacking this type-check caused numereous
security issues — situations when unsanitized user input could end up in the `Buffer(arg)` create
problems ranging from DoS to leaking sensitive information to the attacker from the process memory.

When `notNumber` argument is hardcoded (e.g. literal `"abc"` or `[0,1,2]`), the `typeof` check can
be omitted.

Also note that using TypeScript does not fix this problem for you — when libs written in
`TypeScript` are used from JS, or when user input ends up there — it behaves exactly as pure JS, as
all type checks are translation-time only and are not present in the actual JS code which TS
compiles to.

### Buffer(number)

For Node.js 0.10.x (and below) support:

```js
var buf;
if (Buffer.alloc) {
  buf = Buffer.alloc(number);
} else {
  buf = new Buffer(number);
  buf.fill(0);
}
```

Otherwise (Node.js ≥ 0.12.x):

```js
const buf = Buffer.alloc ? Buffer.alloc(number) : new Buffer(number).fill(0);
```

## Regarding Buffer.allocUnsafe

Be extra cautious when using `Buffer.allocUnsafe`:
 * Don't use it if you don't have a good reason to
   * e.g. you probably won't ever see a performance difference for small buffers, in fact, those
     might be even faster with `Buffer.alloc()`,
   * if your code is not in the hot code path — you also probably won't notice a difference,
   * keep in mind that zero-filling minimizes the potential risks.
 * If you use it, make sure that you never return the buffer in a partially-filled state,
   * if you are writing to it sequentially — always truncate it to the actuall written length

Errors in handling buffers allocated with `Buffer.allocUnsafe` could result in various issues,
ranged from undefined behaviour of your code to sensitive data (user input, passwords, certs)
leaking to the remote attacker.

_Note that the same applies to `new Buffer` usage without zero-filling, depending on the Node.js
version (and lacking type checks also adds DoS to the list of potential problems)._

<a id="faq"></a>
## FAQ

<a id="design-flaws"></a>
### What is wrong with the `Buffer` constructor?

The `Buffer` constructor could be used to create a buffer in many different ways:

- `new Buffer(42)` creates a `Buffer` of 42 bytes. Before Node.js 8, this buffer contained
  *arbitrary memory* for performance reasons, which could include anything ranging from
  program source code to passwords and encryption keys.
- `new Buffer('abc')` creates a `Buffer` that contains the UTF-8-encoded version of
  the string `'abc'`. A second argument could specify another encoding: For example,
  `new Buffer(string, 'base64')` could be used to convert a Base64 string into the original
  sequence of bytes that it represents.
- There are several other combinations of arguments.

This meant that, in code like `var buffer = new Buffer(foo);`, *it is not possible to tell
what exactly the contents of the generated buffer are* without knowing the type of `foo`.

Sometimes, the value of `foo` comes from an external source. For example, this function
could be exposed as a service on a web server, converting a UTF-8 string into its Base64 form:

```
function stringToBase64(req, res) {
  // The request body should have the format of `{ string: 'foobar' }`
  const rawBytes = new Buffer(req.body.string)
  const encoded = rawBytes.toString('base64')
  res.end({ encoded: encoded })
}
```

Note that this code does *not* validate the type of `req.body.string`:

- `req.body.string` is expected to be a string. If this is the case, all goes well.
- `req.body.string` is controlled by the client that sends the request.
- If `req.body.string` is the *number* `50`, the `rawBytes` would be 50 bytes:
  - Before Node.js 8, the content would be uninitialized
  - After Node.js 8, the content would be `50` bytes with the value `0`

Because of the missing type check, an attacker could intentionally send a number
as part of the request. Using this, they can either:

- Read uninitialized memory. This **will** leak passwords, encryption keys and other
  kinds of sensitive information. (Information leak)
- Force the program to allocate a large amount of memory. For example, when specifying
  `500000000` as the input value, each request will allocate 500MB of memory.
  This can be used to either exhaust the memory available of a program completely
  and make it crash, or slow it down significantly. (Denial of Service)

Both of these scenarios are considered serious security issues in a real-world
web server context.

when using `Buffer.from(req.body.string)` instead, passing a number will always
throw an exception instead, giving a controlled behaviour that can always be
handled by the program.

<a id="ecosystem-usage"></a>
### The `Buffer()` constructor has been deprecated for a while. Is this really an issue?

Surveys of code in the `npm` ecosystem have shown that the `Buffer()` constructor is still
widely used. This includes new code, and overall usage of such code has actually been
*increasing*.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      Copyright JS Foundation and other contributors, https://js.foundation/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         {
  "name": "esprima",
  "description": "ECMAScript parsing infrastructure for multipurpose analysis",
  "homepage": "http://esprima.org",
  "main": "dist/esprima.js",
  "bin": {
    "esparse": "./bin/esparse.js",
    "esvalidate": "./bin/esvalidate.js"
  },
  "version": "4.0.1",
  "files": [
    "bin",
    "dist/esprima.js"
  ],
  "engines": {
    "node": ">=4"
  },
  "author": {
    "name": "Ariya Hidayat",
    "email": "ariya.hidayat@gmail.com"
  },
  "maintainers": [
    {
      "name": "Ariya Hidayat",
      "email": "ariya.hidayat@gmail.com",
      "web": "http://ariya.ofilabs.com"
    }
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/jquery/esprima.git"
  },
  "bugs": {
    "url": "https://github.com/jquery/esprima/issues"
  },
  "license": "BSD-2-Clause",
  "devDependencies": {
    "codecov.io": "~0.1.6",
    "escomplex-js": "1.2.0",
    "everything.js": "~1.0.3",
    "glob": "~7.1.0",
    "istanbul": "~0.4.0",
    "json-diff": "~0.3.1",
    "karma": "~1.3.0",
    "karma-chrome-launcher": "~2.0.0",
    "karma-detect-browsers": "~2.2.3",
    "karma-edge-launcher": "~0.2.0",
    "karma-firefox-launcher": "~1.0.0",
    "karma-ie-launcher": "~1.0.0",
    "karma-mocha": "~1.3.0",
    "karma-safari-launcher": "~1.0.0",
    "karma-safaritechpreview-launcher": "~0.0.4",
    "karma-sauce-launcher": "~1.1.0",
    "lodash": "~3.10.1",
    "mocha": "~3.2.0",
    "node-tick-processor": "~0.0.2",
    "regenerate": "~1.3.2",
    "temp": "~0.8.3",
    "tslint": "~5.1.0",
    "typescript": "~2.3.2",
    "typescript-formatter": "~5.1.3",
    "unicode-8.0.0": "~0.7.0",
    "webpack": "~1.14.0"
  },
  "keywords": [
    "ast",
    "ecmascript",
    "esprima",
    "javascript",
    "parser",
    "syntax"
  ],
  "scripts": {
    "check-version": "node test/check-version.js",
    "tslint": "tslint src/*.ts",
    "code-style": "tsfmt --verify src/*.ts && tsfmt --verify test/*.js",
    "format-code": "tsfmt -r src/*.ts && tsfmt -r test/*.js",
    "complexity": "node test/check-complexity.js",
    "static-analysis": "npm run check-version && npm run tslint && npm run code-style && npm run complexity",
    "hostile-env-tests": "node test/hostile-environment-tests.js",
    "unit-tests": "node test/unit-tests.js",
    "api-tests": "mocha -R dot test/api-tests.js",
    "grammar-tests": "node test/grammar-tests.js",
    "regression-tests": "node test/regression-tests.js",
    "all-tests": "npm run verify-line-ending && npm run generate-fixtures && npm run unit-tests && npm run api-tests && npm run grammar-tests && npm run regression-tests && npm run hostile-env-tests",
    "verify-line-ending": "node test/verify-line-ending.js",
    "generate-fixtures": "node tools/generate-fixtures.js",
    "browser-tests": "npm run compile && npm run generate-fixtures && cd test && karma start --single-run",
    "saucelabs-evergreen": "cd test && karma start saucelabs-evergreen.conf.js",
    "saucelabs-safari": "cd test && karma start saucelabs-safari.conf.js",
    "saucelabs-ie": "cd test && karma start saucelabs-ie.conf.js",
    "saucelabs": "npm run saucelabs-evergreen && npm run saucelabs-ie && npm run saucelabs-safari",
    "analyze-coverage": "istanbul cover test/unit-tests.js",
    "check-coverage": "istanbul check-coverage --statement 100 --branch 100 --function 100",
    "dynamic-analysis": "npm run analyze-coverage && npm run check-coverage",
    "compile": "tsc -p src/ && webpack && node tools/fixupbundle.js",
    "test": "npm run compile && npm run all-tests && npm run static-analysis && npm run dynamic-analysis",
    "prepublish": "npm run compile",
    "profile": "node --prof test/profile.js && mv isolate*.log v8.log && node-tick-processor",
    "benchmark-parser": "node -expose_gc test/benchmark-parser.js",
    "benchmark-tokenizer": "node --expose_gc test/benchmark-tokenizer.js",
    "benchmark": "npm run benchmark-parser && npm run benchmark-tokenizer",
    "codecov": "istanbul report cobertura && codecov < ./coverage/cobertura-coverage.xml",
    "downstream": "node test/downstream.js",
    "travis": "npm test",
    "circleci": "npm test && npm run codecov && npm run downstream",
    "appveyor": "npm run compile && npm run all-tests && npm run browser-tests",
    "droneio": "npm run compile && npm run all-tests && npm run saucelabs",
    "generate-regex": "node tools/generate-identifier-regex.js",
    "generate-xhtml-entities": "node tools/generate-xhtml-entities.js"
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     2018-06-17: Version 4.0.1

      *  Fix parsing async get/set in a class (issue 1861, 1875)
      *  Account for different return statement argument (issue 1829, 1897, 1928)
      *  Correct the handling of HTML comment when parsing a module (issue 1841)
      *  Fix incorrect parse async with proto-identifier-shorthand (issue 1847)
      *  Fix negative column in binary expression (issue 1844)
      *  Fix incorrect YieldExpression  in object methods (issue 1834)
      *  Various documentation fixes

2017-06-10: Version 4.0.0

      * Support ES2017 async function and await expression (issue 1079)
      * Support ES2017 trailing commas in function parameters (issue 1550)
      * Explicitly distinguish parsing a module vs a script (issue 1576)
      * Fix JSX non-empty container (issue 1786)
      * Allow JSX element in a yield expression (issue 1765)
      * Allow `in` expression in a concise body with a function body (issue 1793)
      * Setter function argument must not be a rest parameter (issue 1693)
      * Limit strict mode directive to functions with a simple parameter list (issue 1677)
      * Prohibit any escape sequence in a reserved word (issue 1612)
      * Only permit hex digits in hex escape sequence (issue 1619)
      * Prohibit labelled class/generator/function declaration (issue 1484)
      * Limit function declaration as if statement clause only in non-strict mode (issue 1657)
      * Tolerate missing ) in a with and do-while statement (issue 1481)

2016-12-22: Version 3.1.3

      * Support binding patterns as rest element (issue 1681)
      * Account for different possible arguments of a yield expression (issue 1469)

2016-11-24: Version 3.1.2

      * Ensure that import specifier is more restrictive (issue 1615)
      * Fix duplicated JSX tokens (issue 1613)
      * Scan template literal in a JSX expression container (issue 1622)
      * Improve XHTML entity scanning in JSX (issue 1629)

2016-10-31: Version 3.1.1

      * Fix assignment expression problem in an export declaration (issue 1596)
      * Fix incorrect tokenization of hex digits (issue 1605)

2016-10-09: Version 3.1.0

      * Do not implicitly collect comments when comment attachment is specified (issue 1553)
      * Fix incorrect handling of duplicated proto shorthand fields (issue 1485)
      * Prohibit initialization in some variants of for statements (issue 1309, 1561)
      * Fix incorrect parsing of export specifier (issue 1578)
      * Fix ESTree compatibility for assignment pattern (issue 1575)

2016-09-03: Version 3.0.0

      * Support ES2016 exponentiation expression (issue 1490)
      * Support JSX syntax (issue 1467)
      * Use the latest Unicode 8.0 (issue 1475)
      * Add the support for syntax node delegate (issue 1435)
      * Fix ESTree compatibility on meta property (issue 1338)
      * Fix ESTree compatibility on default parameter value (issue 1081)
      * Fix ESTree compatibility on try handler (issue 1030)

2016-08-23: Version 2.7.3

      * Fix tokenizer confusion with a comment (issue 1493, 1516)

2016-02-02: Version 2.7.2

      * Fix out-of-bound error location in an invalid string literal (issue 1457)
      * Fix shorthand object destructuring defaults in variable declarations (issue 1459)

2015-12-10: Version 2.7.1

      * Do not allow trailing comma in a variable declaration (issue 1360)
      * Fix assignment to `let` in non-strict mode (issue 1376)
      * Fix missing delegate property in YieldExpression (issue 1407)

2015-10-22: Version 2.7.0

      * Fix the handling of semicolon in a break statement (issue 1044)
      * Run the test suite with major web browsers (issue 1259, 1317)
      * Allow `let` as an identifier in non-strict mode (issue 1289)
      * Attach orphaned comments as `innerComments` (issue 1328)
      * Add the support for token delegator (issue 1332)

2015-09-01: Version 2.6.0

      * Properly allow or prohibit `let` in a binding identifier/pattern (issue 1048, 1098)
      * Add sourceType field for Program node (issue 1159)
      * Ensure that strict mode reserved word binding throw an error (issue 1171)
      * Run the test suite with Node.js and IE 11 on Windows (issue 1294)
      * Allow binding pattern with no initializer in a for statement (issue 1301)

2015-07-31: Version 2.5.0

      * Run the test suite in a browser environment (issue 1004)
      * Ensure a comma between imported default binding and named imports (issue 1046)
      * Distinguish `yield` as a keyword vs an identifier (issue 1186)
      * Support ES6 meta property `new.target` (issue 1203)
      * Fix the syntax node for yield with expression (issue 1223)
      * Fix the check of duplicated proto in property names (issue 1225)
      * Fix ES6 Unicode escape in identifier name (issue 1229)
      * Support ES6 IdentifierStart and IdentifierPart (issue 1232)
      * Treat await as a reserved word when parsing as a module (issue 1234)
      * Recognize identifier characters from Unicode SMP (issue 1244)
      * Ensure that export and import can be followed by a comma (issue 1250)
      * Fix yield operator precedence (issue 1262)

2015-07-01: Version 2.4.1

      * Fix some cases of comment attachment (issue 1071, 1175)
      * Fix the handling of destructuring in function arguments (issue 1193)
      * Fix invalid ranges in assignment expression (issue 1201)

2015-06-26: Version 2.4.0

      * Support ES6 for-of iteration (issue 1047)
      * Support ES6 spread arguments (issue 1169)
      * Minimize npm payload (issue 1191)

2015-06-16: Version 2.3.0

      * Support ES6 generator (issue 1033)
      * Improve parsing of regular expressions with `u` flag (issue 1179)

2015-04-17: Version 2.2.0

      * Support ES6 import and export declarations (issue 1000)
      * Fix line terminator before arrow not recognized as error (issue 1009)
      * Support ES6 destructuring (issue 1045)
      * Support ES6 template literal (issue 1074)
      * Fix the handling of invalid/incomplete string escape sequences (issue 1106)
      * Fix ES3 static member access restriction (issue 1120)
      * Support for `super` in ES6 class (issue 1147)

2015-03-09: Version 2.1.0

      * Support ES6 class (issue 1001)
      * Support ES6 rest parameter (issue 1011)
      * Expand the location of property getter, setter, and methods (issue 1029)
      * Enable TryStatement transition to a single handler (issue 1031)
      * Support ES6 computed property name (issue 1037)
      * Tolerate unclosed block comment (issue 1041)
      * Support ES6 lexical declaration (issue 1065)

2015-02-06: Version 2.0.0

      * Support ES6 arrow function (issue 517)
      * Support ES6 Unicode code point escape (issue 521)
      * Improve the speed and accuracy of comment attachment (issue 522)
      * Support ES6 default parameter (issue 519)
      * Support ES6 regular expression flags (issue 557)
      * Fix scanning of implicit octal literals (issue 565)
      * Fix the handling of automatic semicolon insertion (issue 574)
      * Support ES6 method definition (issue 620)
      * Support ES6 octal integer literal (issue 621)
      * Support ES6 binary integer literal (issue 622)
      * Support ES6 object literal property value shorthand (issue 624)

2015-03-03: Version 1.2.5

      * Fix scanning of implicit octal literals (issue 565)

2015-02-05: Version 1.2.4

      * Fix parsing of LeftHandSideExpression in ForInStatement (issue 560)
      * Fix the handling of automatic semicolon insertion (issue 574)

2015-01-18: Version 1.2.3

      * Fix division by this (issue 616)

2014-05-18: Version 1.2.2

      * Fix duplicated tokens when collecting comments (issue 537)

2014-05-04: Version 1.2.1

      * Ensure that Program node may still have leading comments (issue 536)

2014-04-29: Version 1.2.0

      * Fix semicolon handling for expression statement (issue 462, 533)
      * Disallow escaped characters in regular expression flags (issue 503)
      * Performance improvement for location tracking (issue 520)
      * Improve the speed of comment attachment (issue 522)

2014-03-26: Version 1.1.1

      * Fix token handling of forward slash after an array literal (issue 512)

2014-03-23: Version 1.1.0

      * Optionally attach comments to the owning syntax nodes (issue 197)
      * Simplify binary parsing with stack-based shift reduce (issue 352)
      * Always include the raw source of literals (issue 376)
      * Add optional input source information (issue 386)
      * Tokenizer API for pure lexical scanning (issue 398)
      * Improve the web site and its online demos (issue 337, 400, 404)
      * Performance improvement for location tracking (issue 417, 424)
      * Support HTML comment syntax (issue 451)
      * Drop support for legacy browsers (issue 474)

2013-08-27: Version 1.0.4

      * Minimize the payload for packages (issue 362)
      * Fix missing cases on an empty switch statement (issue 436)
      * Support escaped ] in regexp literal character classes (issue 442)
      * Tolerate invalid left-hand side expression (issue 130)

2013-05-17: Version 1.0.3

      * Variable declaration needs at least one declarator (issue 391)
      * Fix benchmark's variance unit conversion (issue 397)
      * IE < 9: \v should be treated as vertical tab (issue 405)
      * Unary expressions should always have prefix: true (issue 418)
      * Catch clause should only accept an identifier (issue 423)
      * Tolerate setters without parameter (issue 426)

2012-11-02: Version 1.0.2

    Improvement:

      * Fix esvalidate JUnit output upon a syntax error (issue 374)

2012-10-28: Version 1.0.1

    Improvements:

      * esvalidate understands shebang in a Unix shell script (issue 361)
      * esvalidate treats fatal parsing failure as an error (issue 361)
      * Reduce Node.js package via .npmignore (issue 362)

2012-10-22: Version 1.0.0

    Initial release.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       {
  "name": "chokidar",
  "description": "Minimal and efficient cross-platform file watching library",
  "version": "3.6.0",
  "homepage": "https://github.com/paulmillr/chokidar",
  "author": "Paul Miller (https://paulmillr.com)",
  "contributors": [
    "Paul Miller (https://paulmillr.com)",
    "Elan Shanker"
  ],
  "engines": {
    "node": ">= 8.10.0"
  },
  "main": "index.js",
  "types": "./types/index.d.ts",
  "dependencies": {
    "anymatch": "~3.1.2",
    "braces": "~3.0.2",
    "glob-parent": "~5.1.2",
    "is-binary-path": "~2.1.0",
    "is-glob": "~4.0.1",
    "normalize-path": "~3.0.0",
    "readdirp": "~3.6.0"
  },
  "optionalDependencies": {
    "fsevents": "~2.3.2"
  },
  "devDependencies": {
    "@types/node": "^14",
    "chai": "^4.3",
    "dtslint": "^3.3.0",
    "eslint": "^7.0.0",
    "mocha": "^7.0.0",
    "rimraf": "^3.0.0",
    "sinon": "^9.0.1",
    "sinon-chai": "^3.3.0",
    "typescript": "^4.4.3",
    "upath": "^1.2.0"
  },
  "files": [
    "index.js",
    "lib/*.js",
    "types/index.d.ts"
  ],
  "repository": {
    "type": "git",
    "url": "git+https://github.com/paulmillr/chokidar.git"
  },
  "bugs": {
    "url": "https://github.com/paulmillr/chokidar/issues"
  },
  "license": "MIT",
  "scripts": {
    "dtslint": "dtslint types",
    "lint": "eslint --report-unused-disable-directives --ignore-path .gitignore .",
    "build": "npm ls",
    "mocha": "mocha --exit --timeout 90000",
    "test": "npm run lint && npm run mocha"
  },
  "keywords": [
    "fs",
    "watch",
    "watchFile",
    "watcher",
    "watching",
    "file",
    "fsevents"
  ],
  "funding": "https://paulmillr.com/funding/"
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                'use strict';

const { EventEmitter } = require('events');
const fs = require('fs');
const sysPath = require('path');
const { promisify } = require('util');
const readdirp = require('readdirp');
const anymatch = require('anymatch').default;
const globParent = require('glob-parent');
const isGlob = require('is-glob');
const braces = require('braces');
const normalizePath = require('normalize-path');

const NodeFsHandler = require('./lib/nodefs-handler');
const FsEventsHandler = require('./lib/fsevents-handler');
const {
  EV_ALL,
  EV_READY,
  EV_ADD,
  EV_CHANGE,
  EV_UNLINK,
  EV_ADD_DIR,
  EV_UNLINK_DIR,
  EV_RAW,
  EV_ERROR,

  STR_CLOSE,
  STR_END,

  BACK_SLASH_RE,
  DOUBLE_SLASH_RE,
  SLASH_OR_BACK_SLASH_RE,
  DOT_RE,
  REPLACER_RE,

  SLASH,
  SLASH_SLASH,
  BRACE_START,
  BANG,
  ONE_DOT,
  TWO_DOTS,
  GLOBSTAR,
  SLASH_GLOBSTAR,
  ANYMATCH_OPTS,
  STRING_TYPE,
  FUNCTION_TYPE,
  EMPTY_STR,
  EMPTY_FN,

  isWindows,
  isMacos,
  isIBMi
} = require('./lib/constants');

const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);

/**
 * @typedef {String} Path
 * @typedef {'all'|'add'|'addDir'|'change'|'unlink'|'unlinkDir'|'raw'|'error'|'ready'} EventName
 * @typedef {'readdir'|'watch'|'add'|'remove'|'change'} ThrottleType
 */

/**
 *
 * @typedef {Object} WatchHelpers
 * @property {Boolean} followSymlinks
 * @property {'stat'|'lstat'} statMethod
 * @property {Path} path
 * @property {Path} watchPath
 * @property {Function} entryPath
 * @property {Boolean} hasGlob
 * @property {Object} globFilter
 * @property {Function} filterPath
 * @property {Function} filterDir
 */

const arrify = (value = []) => Array.isArray(value) ? value : [value];
const flatten = (list, result = []) => {
  list.forEach(item => {
    if (Array.isArray(item)) {
      flatten(item, result);
    } else {
      result.push(item);
    }
  });
  return result;
};

const unifyPaths = (paths_) => {
  /**
   * @type {Array<String>}
   */
  const paths = flatten(arrify(paths_));
  if (!paths.every(p => typeof p === STRING_TYPE)) {
    throw new TypeError(`Non-string provided as watch path: ${paths}`);
  }
  return paths.map(normalizePathToUnix);
};

// If SLASH_SLASH occurs at the beginning of path, it is not replaced
//     because "//StoragePC/DrivePool/Movies" is a valid network path
const toUnix = (string) => {
  let str = string.replace(BACK_SLASH_RE, SLASH);
  let prepend = false;
  if (str.startsWith(SLASH_SLASH)) {
    prepend = true;
  }
  while (str.match(DOUBLE_SLASH_RE)) {
    str = str.replace(DOUBLE_SLASH_RE, SLASH);
  }
  if (prepend) {
    str = SLASH + str;
  }
  return str;
};

// Our version of upath.normalize
// TODO: this is not equal to path-normalize module - investigate why
const normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));

const normalizeIgnored = (cwd = EMPTY_STR) => (path) => {
  if (typeof path !== STRING_TYPE) return path;
  return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));
};

const getAbsolutePath = (path, cwd) => {
  if (sysPath.isAbsolute(path)) {
    return path;
  }
  if (path.startsWith(BANG)) {
    return BANG + sysPath.join(cwd, path.slice(1));
  }
  return sysPath.join(cwd, path);
};

const undef = (opts, key) => opts[key] === undefined;

/**
 * Directory entry.
 * @property {Path} path
 * @property {Set<Path>} items
 */
class DirEntry {
  /**
   * @param {Path} dir
   * @param {Function} removeWatcher
   */
  constructor(dir, removeWatcher) {
    this.path = dir;
    this._removeWatcher = removeWatcher;
    /** @type {Set<Path>} */
    this.items = new Set();
  }

  add(item) {
    const {items} = this;
    if (!items) return;
    if (item !== ONE_DOT && item !== TWO_DOTS) items.add(item);
  }

  async remove(item) {
    const {items} = this;
    if (!items) return;
    items.delete(item);
    if (items.size > 0) return;

    const dir = this.path;
    try {
      await readdir(dir);
    } catch (err) {
      if (this._removeWatcher) {
        this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));
      }
    }
  }

  has(item) {
    const {items} = this;
    if (!items) return;
    return items.has(item);
  }

  /**
   * @returns {Array<String>}
   */
  getChildren() {
    const {items} = this;
    if (!items) return;
    return [...items.values()];
  }

  dispose() {
    this.items.clear();
    delete this.path;
    delete this._removeWatcher;
    delete this.items;
    Object.freeze(this);
  }
}

const STAT_METHOD_F = 'stat';
const STAT_METHOD_L = 'lstat';
class WatchHelper {
  constructor(path, watchPath, follow, fsw) {
    this.fsw = fsw;
    this.path = path = path.replace(REPLACER_RE, EMPTY_STR);
    this.watchPath = watchPath;
    this.fullWatchPath = sysPath.resolve(watchPath);
    this.hasGlob = watchPath !== path;
    /** @type {object|boolean} */
    if (path === EMPTY_STR) this.hasGlob = false;
    this.globSymlink = this.hasGlob && follow ? undefined : false;
    this.globFilter = this.hasGlob ? anymatch(path, undefined, ANYMATCH_OPTS) : false;
    this.dirParts = this.getDirParts(path);
    this.dirParts.forEach((parts) => {
      if (parts.length > 1) parts.pop();
    });
    this.followSymlinks = follow;
    this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
  }

  checkGlobSymlink(entry) {
    // only need to resolve once
    // first entry should always have entry.parentDir === EMPTY_STR
    if (this.globSymlink === undefined) {
      this.globSymlink = entry.fullParentDir === this.fullWatchPath ?
        false : {realPath: entry.fullParentDir, linkPath: this.fullWatchPath};
    }

    if (this.globSymlink) {
      return entry.fullPath.replace(this.globSymlink.realPath, this.globSymlink.linkPath);
    }

    return entry.fullPath;
  }

  entryPath(entry) {
    return sysPath.join(this.watchPath,
      sysPath.relative(this.watchPath, this.checkGlobSymlink(entry))
    );
  }

  filterPath(entry) {
    const {stats} = entry;
    if (stats && stats.isSymbolicLink()) return this.filterDir(entry);
    const resolvedPath = this.entryPath(entry);
    const matchesGlob = this.hasGlob && typeof this.globFilter === FUNCTION_TYPE ?
      this.globFilter(resolvedPath) : true;
    return matchesGlob &&
      this.fsw._isntIgnored(resolvedPath, stats) &&
      this.fsw._hasReadPermissions(stats);
  }

  getDirParts(path) {
    if (!this.hasGlob) return [];
    const parts = [];
    const expandedPath = path.includes(BRACE_START) ? braces.expand(path) : [path];
    expandedPath.forEach((path) => {
      parts.push(sysPath.relative(this.watchPath, path).split(SLASH_OR_BACK_SLASH_RE));
    });
    return parts;
  }

  filterDir(entry) {
    if (this.hasGlob) {
      const entryParts = this.getDirParts(this.checkGlobSymlink(entry));
      let globstar = false;
      this.unmatchedGlob = !this.dirParts.some((parts) => {
        return parts.every((part, i) => {
          if (part === GLOBSTAR) globstar = true;
          return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i], ANYMATCH_OPTS);
        });
      });
    }
    return !this.unmatchedGlob && this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
  }
}

/**
 * Watches files & directories for changes. Emitted events:
 * `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
 *
 *     new FSWatcher()
 *       .add(directories)
 *       .on('add', path => log('File', path, 'was added'))
 */
class FSWatcher extends EventEmitter {
// Not indenting methods for history sake; for now.
constructor(_opts) {
  super();

  const opts = {};
  if (_opts) Object.assign(opts, _opts); // for frozen objects

  /** @type {Map<String, DirEntry>} */
  this._watched = new Map();
  /** @type {Map<String, Array>} */
  this._closers = new Map();
  /** @type {Set<String>} */
  this._ignoredPaths = new Set();

  /** @type {Map<ThrottleType, Map>} */
  this._throttled = new Map();

  /** @type {Map<Path, String|Boolean>} */
  this._symlinkPaths = new Map();

  this._streams = new Set();
  this.closed = false;

  // Set up default options.
  if (undef(opts, 'persistent')) opts.persistent = true;
  if (undef(opts, 'ignoreInitial')) opts.ignoreInitial = false;
  if (undef(opts, 'ignorePermissionErrors')) opts.ignorePermissionErrors = false;
  if (undef(opts, 'interval')) opts.interval = 100;
  if (undef(opts, 'binaryInterval')) opts.binaryInterval = 300;
  if (undef(opts, 'disableGlobbing')) opts.disableGlobbing = false;
  opts.enableBinaryInterval = opts.binaryInterval !== opts.interval;

  // Enable fsevents on OS X when polling isn't explicitly enabled.
  if (undef(opts, 'useFsEvents')) opts.useFsEvents = !opts.usePolling;

  // If we can't use fsevents, ensure the options reflect it's disabled.
  const canUseFsEvents = FsEventsHandler.canUse();
  if (!canUseFsEvents) opts.useFsEvents = false;

  // Use polling on Mac if not using fsevents.
  // Other platforms use non-polling fs_watch.
  if (undef(opts, 'usePolling') && !opts.useFsEvents) {
    opts.usePolling = isMacos;
  }

  // Always default to polling on IBM i because fs.watch() is not available on IBM i.
  if(isIBMi) {
    opts.usePolling = true;
  }

  // Global override (useful for end-developers that need to force polling for all
  // instances of chokidar, regardless of usage/dependency depth)
  const envPoll = process.env.CHOKIDAR_USEPOLLING;
  if (envPoll !== undefined) {
    const envLower = envPoll.toLowerCase();

    if (envLower === 'false' || envLower === '0') {
      opts.usePolling = false;
    } else if (envLower === 'true' || envLower === '1') {
      opts.usePolling = true;
    } else {
      opts.usePolling = !!envLower;
    }
  }
  const envInterval = process.env.CHOKIDAR_INTERVAL;
  if (envInterval) {
    opts.interval = Number.parseInt(envInterval, 10);
  }

  // Editor atomic write normalization enabled by default with fs.watch
  if (undef(opts, 'atomic')) opts.atomic = !opts.usePolling && !opts.useFsEvents;
  if (opts.atomic) this._pendingUnlinks = new Map();

  if (undef(opts, 'followSymlinks')) opts.followSymlinks = true;

  if (undef(opts, 'awaitWriteFinish')) opts.awaitWriteFinish = false;
  if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {};
  const awf = opts.awaitWriteFinish;
  if (awf) {
    if (!awf.stabilityThreshold) awf.stabilityThreshold = 2000;
    if (!awf.pollInterval) awf.pollInterval = 100;
    this._pendingWrites = new Map();
  }
  if (opts.ignored) opts.ignored = arrify(opts.ignored);

  let readyCalls = 0;
  this._emitReady = () => {
    readyCalls++;
    if (readyCalls >= this._readyCount) {
      this._emitReady = EMPTY_FN;
      this._readyEmitted = true;
      // use process.nextTick to allow time for listener to be bound
      process.nextTick(() => this.emit(EV_READY));
    }
  };
  this._emitRaw = (...args) => this.emit(EV_RAW, ...args);
  this._readyEmitted = false;
  this.options = opts;

  // Initialize with proper watcher.
  if (opts.useFsEvents) {
    this._fsEventsHandler = new FsEventsHandler(this);
  } else {
    this._nodeFsHandler = new NodeFsHandler(this);
  }

  // You’re frozen when your heart’s not open.
  Object.freeze(opts);
}

// Public methods

/**
 * Adds paths to be watched on an existing FSWatcher instance
 * @param {Path|Array<Path>} paths_
 * @param {String=} _origAdd private; for handling non-existent paths to be watched
 * @param {Boolean=} _internal private; indicates a non-user add
 * @returns {FSWatcher} for chaining
 */
add(paths_, _origAdd, _internal) {
  const {cwd, disableGlobbing} = this.options;
  this.closed = false;
  let paths = unifyPaths(paths_);
  if (cwd) {
    paths = paths.map((path) => {
      const absPath = getAbsolutePath(path, cwd);

      // Check `path` instead of `absPath` because the cwd portion can't be a glob
      if (disableGlobbing || !isGlob(path)) {
        return absPath;
      }
      return normalizePath(absPath);
    });
  }

  // set aside negated glob strings
  paths = paths.filter((path) => {
    if (path.startsWith(BANG)) {
      this._ignoredPaths.add(path.slice(1));
      return false;
    }

    // if a path is being added that was previously ignored, stop ignoring it
    this._ignoredPaths.delete(path);
    this._ignoredPaths.delete(path + SLASH_GLOBSTAR);

    // reset the cached userIgnored anymatch fn
    // to make ignoredPaths changes effective
    this._userIgnored = undefined;

    return true;
  });

  if (this.options.useFsEvents && this._fsEventsHandler) {
    if (!this._readyCount) this._readyCount = paths.length;
    if (this.options.persistent) this._readyCount += paths.length;
    paths.forEach((path) => this._fsEventsHandler._addToFsEvents(path));
  } else {
    if (!this._readyCount) this._readyCount = 0;
    this._readyCount += paths.length;
    Promise.all(
      paths.map(async path => {
        const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, 0, 0, _origAdd);
        if (res) this._emitReady();
        return res;
      })
    ).then(results => {
      if (this.closed) return;
      results.filter(item => item).forEach(item => {
        this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
      });
    });
  }

  return this;
}

/**
 * Close watchers or start ignoring events from specified paths.
 * @param {Path|Array<Path>} paths_ - string or array of strings, file/directory paths and/or globs
 * @returns {FSWatcher} for chaining
*/
unwatch(paths_) {
  if (this.closed) return this;
  const paths = unifyPaths(paths_);
  const {cwd} = this.options;

  paths.forEach((path) => {
    // convert to absolute path unless relative path already matches
    if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {
      if (cwd) path = sysPath.join(cwd, path);
      path = sysPath.resolve(path);
    }

    this._closePath(path);

    this._ignoredPaths.add(path);
    if (this._watched.has(path)) {
      this._ignoredPaths.add(path + SLASH_GLOBSTAR);
    }

    // reset the cached userIgnored anymatch fn
    // to make ignoredPaths changes effective
    this._userIgnored = undefined;
  });

  return this;
}

/**
 * Close watchers and remove all listeners from watched paths.
 * @returns {Promise<void>}.
*/
close() {
  if (this.closed) return this._closePromise;
  this.closed = true;

  // Memory management.
  this.removeAllListeners();
  const closers = [];
  this._closers.forEach(closerList => closerList.forEach(closer => {
    const promise = closer();
    if (promise instanceof Promise) closers.push(promise);
  }));
  this._streams.forEach(stream => stream.destroy());
  this._userIgnored = undefined;
  this._readyCount = 0;
  this._readyEmitted = false;
  this._watched.forEach(dirent => dirent.dispose());
  ['closers', 'watched', 'streams', 'symlinkPaths', 'throttled'].forEach(key => {
    this[`_${key}`].clear();
  });

  this._closePromise = closers.length ? Promise.all(closers).then(() => undefined) : Promise.resolve();
  return this._closePromise;
}

/**
 * Expose list of watched paths
 * @returns {Object} for chaining
*/
getWatched() {
  const watchList = {};
  this._watched.forEach((entry, dir) => {
    const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;
    watchList[key || ONE_DOT] = entry.getChildren().sort();
  });
  return watchList;
}

emitWithAll(event, args) {
  this.emit(...args);
  if (event !== EV_ERROR) this.emit(EV_ALL, ...args);
}

// Common helpers
// --------------

/**
 * Normalize and emit events.
 * Calling _emit DOES NOT MEAN emit() would be called!
 * @param {EventName} event Type of event
 * @param {Path} path File or directory path
 * @param {*=} val1 arguments to be passed with event
 * @param {*=} val2
 * @param {*=} val3
 * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
 */
async _emit(event, path, val1, val2, val3) {
  if (this.closed) return;

  const opts = this.options;
  if (isWindows) path = sysPath.normalize(path);
  if (opts.cwd) path = sysPath.relative(opts.cwd, path);
  /** @type Array<any> */
  const args = [event, path];
  if (val3 !== undefined) args.push(val1, val2, val3);
  else if (val2 !== undefined) args.push(val1, val2);
  else if (val1 !== undefined) args.push(val1);

  const awf = opts.awaitWriteFinish;
  let pw;
  if (awf && (pw = this._pendingWrites.get(path))) {
    pw.lastChange = new Date();
    return this;
  }

  if (opts.atomic) {
    if (event === EV_UNLINK) {
      this._pendingUnlinks.set(path, args);
      setTimeout(() => {
        this._pendingUnlinks.forEach((entry, path) => {
          this.emit(...entry);
          this.emit(EV_ALL, ...entry);
          this._pendingUnlinks.delete(path);
        });
      }, typeof opts.atomic === 'number' ? opts.atomic : 100);
      return this;
    }
    if (event === EV_ADD && this._pendingUnlinks.has(path)) {
      event = args[0] = EV_CHANGE;
      this._pendingUnlinks.delete(path);
    }
  }

  if (awf && (event === EV_ADD || event === EV_CHANGE) && this._readyEmitted) {
    const awfEmit = (err, stats) => {
      if (err) {
        event = args[0] = EV_ERROR;
        args[1] = err;
        this.emitWithAll(event, args);
      } else if (stats) {
        // if stats doesn't exist the file must have been deleted
        if (args.length > 2) {
          args[2] = stats;
        } else {
          args.push(stats);
        }
        this.emitWithAll(event, args);
      }
    };

    this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
    return this;
  }

  if (event === EV_CHANGE) {
    const isThrottled = !this._throttle(EV_CHANGE, path, 50);
    if (isThrottled) return this;
  }

  if (opts.alwaysStat && val1 === undefined &&
    (event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE)
  ) {
    const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;
    let stats;
    try {
      stats = await stat(fullPath);
    } catch (err) {}
    // Suppress event when fs_stat fails, to avoid sending undefined 'stat'
    if (!stats || this.closed) return;
    args.push(stats);
  }
  this.emitWithAll(event, args);

  return this;
}

/**
 * Common handler for errors
 * @param {Error} error
 * @returns {Error|Boolean} The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
 */
_handleError(error) {
  const code = error && error.code;
  if (error && code !== 'ENOENT' && code !== 'ENOTDIR' &&
    (!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))
  ) {
    this.emit(EV_ERROR, error);
  }
  return error || this.closed;
}

/**
 * Helper utility for throttling
 * @param {ThrottleType} actionType type being throttled
 * @param {Path} path being acted upon
 * @param {Number} timeout duration of time to suppress duplicate actions
 * @returns {Object|false} tracking object or false if action should be suppressed
 */
_throttle(actionType, path, timeout) {
  if (!this._throttled.has(actionType)) {
    this._throttled.set(actionType, new Map());
  }

  /** @type {Map<Path, Object>} */
  const action = this._throttled.get(actionType);
  /** @type {Object} */
  const actionPath = action.get(path);

  if (actionPath) {
    actionPath.count++;
    return false;
  }

  let timeoutObject;
  const clear = () => {
    const item = action.get(path);
    const count = item ? item.count : 0;
    action.delete(path);
    clearTimeout(timeoutObject);
    if (item) clearTimeout(item.timeoutObject);
    return count;
  };
  timeoutObject = setTimeout(clear, timeout);
  const thr = {timeoutObject, clear, count: 0};
  action.set(path, thr);
  return thr;
}

_incrReadyCount() {
  return this._readyCount++;
}

/**
 * Awaits write operation to finish.
 * Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
 * @param {Path} path being acted upon
 * @param {Number} threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
 * @param {EventName} event
 * @param {Function} awfEmit Callback to be called when ready for event to be emitted.
 */
_awaitWriteFinish(path, threshold, event, awfEmit) {
  let timeoutHandler;

  let fullPath = path;
  if (this.options.cwd && !sysPath.isAbsolute(path)) {
    fullPath = sysPath.join(this.options.cwd, path);
  }

  const now = new Date();

  const awaitWriteFinish = (prevStat) => {
    fs.stat(fullPath, (err, curStat) => {
      if (err || !this._pendingWrites.has(path)) {
        if (err && err.code !== 'ENOENT') awfEmit(err);
        return;
      }

      const now = Number(new Date());

      if (prevStat && curStat.size !== prevStat.size) {
        this._pendingWrites.get(path).lastChange = now;
      }
      const pw = this._pendingWrites.get(path);
      const df = now - pw.lastChange;

      if (df >= threshold) {
        this._pendingWrites.delete(path);
        awfEmit(undefined, curStat);
      } else {
        timeoutHandler = setTimeout(
          awaitWriteFinish,
          this.options.awaitWriteFinish.pollInterval,
          curStat
        );
      }
    });
  };

  if (!this._pendingWrites.has(path)) {
    this._pendingWrites.set(path, {
      lastChange: now,
      cancelWait: () => {
        this._pendingWrites.delete(path);
        clearTimeout(timeoutHandler);
        return event;
      }
    });
    timeoutHandler = setTimeout(
      awaitWriteFinish,
      this.options.awaitWriteFinish.pollInterval
    );
  }
}

_getGlobIgnored() {
  return [...this._ignoredPaths.values()];
}

/**
 * Determines whether user has asked to ignore this path.
 * @param {Path} path filepath or dir
 * @param {fs.Stats=} stats result of fs.stat
 * @returns {Boolean}
 */
_isIgnored(path, stats) {
  if (this.options.atomic && DOT_RE.test(path)) return true;
  if (!this._userIgnored) {
    const {cwd} = this.options;
    const ign = this.options.ignored;

    const ignored = ign && ign.map(normalizeIgnored(cwd));
    const paths = arrify(ignored)
      .filter((path) => typeof path === STRING_TYPE && !isGlob(path))
      .map((path) => path + SLASH_GLOBSTAR);
    const list = this._getGlobIgnored().map(normalizeIgnored(cwd)).concat(ignored, paths);
    this._userIgnored = anymatch(list, undefined, ANYMATCH_OPTS);
  }

  return this._userIgnored([path, stats]);
}

_isntIgnored(path, stat) {
  return !this._isIgnored(path, stat);
}

/**
 * Provides a set of common helpers and properties relating to symlink and glob handling.
 * @param {Path} path file, directory, or glob pattern being watched
 * @param {Number=} depth at any depth > 0, this isn't a glob
 * @returns {WatchHelper} object containing helpers for this path
 */
_getWatchHelpers(path, depth) {
  const watchPath = depth || this.options.disableGlobbing || !isGlob(path) ? path : globParent(path);
  const follow = this.options.followSymlinks;

  return new WatchHelper(path, watchPath, follow, this);
}

// Directory helpers
// -----------------

/**
 * Provides directory tracking objects
 * @param {String} directory path of the directory
 * @returns {DirEntry} the directory's tracking object
 */
_getWatchedDir(directory) {
  if (!this._boundRemove) this._boundRemove = this._remove.bind(this);
  const dir = sysPath.resolve(directory);
  if (!this._watched.has(dir)) this._watched.set(dir, new DirEntry(dir, this._boundRemove));
  return this._watched.get(dir);
}

// File helpers
// ------------

/**
 * Check for read permissions.
 * Based on this answer on SO: https://stackoverflow.com/a/11781404/1358405
 * @param {fs.Stats} stats - object, result of fs_stat
 * @returns {Boolean} indicates whether the file can be read
*/
_hasReadPermissions(stats) {
  if (this.options.ignorePermissionErrors) return true;

  // stats.mode may be bigint
  const md = stats && Number.parseInt(stats.mode, 10);
  const st = md & 0o777;
  const it = Number.parseInt(st.toString(8)[0], 10);
  return Boolean(4 & it);
}

/**
 * Handles emitting unlink events for
 * files and directories, and via recursion, for
 * files and directories within directories that are unlinked
 * @param {String} directory within which the following item is located
 * @param {String} item      base path of item/directory
 * @returns {void}
*/
_remove(directory, item, isDirectory) {
  // if what is being deleted is a directory, get that directory's paths
  // for recursive deleting and cleaning of watched object
  // if it is not a directory, nestedDirectoryChildren will be empty array
  const path = sysPath.join(directory, item);
  const fullPath = sysPath.resolve(path);
  isDirectory = isDirectory != null
    ? isDirectory
    : this._watched.has(path) || this._watched.has(fullPath);

  // prevent duplicate handling in case of arriving here nearly simultaneously
  // via multiple paths (such as _handleFile and _handleDir)
  if (!this._throttle('remove', path, 100)) return;

  // if the only watched file is removed, watch for its return
  if (!isDirectory && !this.options.useFsEvents && this._watched.size === 1) {
    this.add(directory, item, true);
  }

  // This will create a new entry in the watched object in either case
  // so we got to do the directory check beforehand
  const wp = this._getWatchedDir(path);
  const nestedDirectoryChildren = wp.getChildren();

  // Recursively remove children directories / files.
  nestedDirectoryChildren.forEach(nested => this._remove(path, nested));

  // Check if item was on the watched list and remove it
  const parent = this._getWatchedDir(directory);
  const wasTracked = parent.has(item);
  parent.remove(item);

  // Fixes issue #1042 -> Relative paths were detected and added as symlinks
  // (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),
  // but never removed from the map in case the path was deleted.
  // This leads to an incorrect state if the path was recreated:
  // https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553
  if (this._symlinkPaths.has(fullPath)) {
    this._symlinkPaths.delete(fullPath);
  }

  // If we wait for this file to be fully written, cancel the wait.
  let relPath = path;
  if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path);
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
    const event = this._pendingWrites.get(relPath).cancelWait();
    if (event === EV_ADD) return;
  }

  // The Entry will either be a directory that just got removed
  // or a bogus entry to a file, in either case we have to remove it
  this._watched.delete(path);
  this._watched.delete(fullPath);
  const eventName = isDirectory ? EV_UNLINK_DIR : EV_UNLINK;
  if (wasTracked && !this._isIgnored(path)) this._emit(eventName, path);

  // Avoid conflicts if we later create another file with the same name
  if (!this.options.useFsEvents) {
    this._closePath(path);
  }
}

/**
 * Closes all watchers for a path
 * @param {Path} path
 */
_closePath(path) {
  this._closeFile(path)
  const dir = sysPath.dirname(path);
  this._getWatchedDir(dir).remove(sysPath.basename(path));
}

/**
 * Closes only file-specific watchers
 * @param {Path} path
 */
_closeFile(path) {
  const closers = this._closers.get(path);
  if (!closers) return;
  closers.forEach(closer => closer());
  this._closers.delete(path);
}

/**
 *
 * @param {Path} path
 * @param {Function} closer
 */
_addPathCloser(path, closer) {
  if (!closer) return;
  let list = this._closers.get(path);
  if (!list) {
    list = [];
    this._closers.set(path, list);
  }
  list.push(closer);
}

_readdirp(root, opts) {
  if (this.closed) return;
  const options = {type: EV_ALL, alwaysStat: true, lstat: true, ...opts};
  let stream = readdirp(root, options);
  this._streams.add(stream);
  stream.once(STR_CLOSE, () => {
    stream = undefined;
  });
  stream.once(STR_END, () => {
    if (stream) {
      this._streams.delete(stream);
      stream = undefined;
    }
  });
  return stream;
}

}

// Export FSWatcher class
exports.FSWatcher = FSWatcher;

/**
 * Instantiates watcher with paths to be tracked.
 * @param {String|Array<String>} paths file/directory paths and/or globs
 * @param {Object=} options chokidar opts
 * @returns an instance of FSWatcher for chaining.
 */
const watch = (paths, options) => {
  const watcher = new FSWatcher(options);
  watcher.add(paths);
  return watcher;
};

exports.watch = watch;
                                                                                                                                                                                                                                                                                                     /* eslint-disable node/no-deprecated-api */

'use strict'

var buffer = require('buffer')
var Buffer = buffer.Buffer
var safer = require('./safer.js')
var Safer = safer.Buffer

var dangerous = {}

var key

for (key in safer) {
  if (!safer.hasOwnProperty(key)) continue
  dangerous[key] = safer[key]
}

var Dangereous = dangerous.Buffer = {}

// Copy Safer API
for (key in Safer) {
  if (!Safer.hasOwnProperty(key)) continue
  Dangereous[key] = Safer[key]
}

// Copy those missing unsafe methods, if they are present
for (key in Buffer) {
  if (!Buffer.hasOwnProperty(key)) continue
  if (Dangereous.hasOwnProperty(key)) continue
  Dangereous[key] = Buffer[key]
}

if (!Dangereous.allocUnsafe) {
  Dangereous.allocUnsafe = function (size) {
    if (typeof size !== 'number') {
      throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size)
    }
    if (size < 0 || size >= 2 * (1 << 30)) {
      throw new RangeError('The value "' + size + '" is invalid for option "size"')
    }
    return Buffer(size)
  }
}

if (!Dangereous.allocUnsafeSlow) {
  Dangereous.allocUnsafeSlow = function (size) {
    if (typeof size !== 'number') {
      throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size)
    }
    if (size < 0 || size >= 2 * (1 << 30)) {
      throw new RangeError('The value "' + size + '" is invalid for option "size"')
    }
    return buffer.SlowBuffer(size)
  }
}

module.exports = dangerous
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     # Chokidar [![Weekly downloads](https://img.shields.io/npm/dw/chokidar.svg)](https://github.com/paulmillr/chokidar) [![Yearly downloads](https://img.shields.io/npm/dy/chokidar.svg)](https://github.com/paulmillr/chokidar)

> Minimal and efficient cross-platform file watching library

[![NPM](https://nodei.co/npm/chokidar.png)](https://www.npmjs.com/package/chokidar)

## Why?

Node.js `fs.watch`:

* Doesn't report filenames on MacOS.
* Doesn't report events at all when using editors like Sublime on MacOS.
* Often reports events twice.
* Emits most changes as `rename`.
* Does not provide an easy way to recursively watch file trees.
* Does not support recursive watching on Linux.

Node.js `fs.watchFile`:

* Almost as bad at event handling.
* Also does not provide any recursive watching.
* Results in high CPU utilization.

Chokidar resolves these problems.

Initially made for **[Brunch](https://brunch.io/)** (an ultra-swift web app build tool), it is now used in
[Microsoft's Visual Studio Code](https://github.com/microsoft/vscode),
[gulp](https://github.com/gulpjs/gulp/),
[karma](https://karma-runner.github.io/),
[PM2](https://github.com/Unitech/PM2),
[browserify](http://browserify.org/),
[webpack](https://webpack.github.io/),
[BrowserSync](https://www.browsersync.io/),
and [many others](https://www.npmjs.com/browse/depended/chokidar).
It has proven itself in production environments.

Version 3 is out! Check out our blog post about it: [Chokidar 3: How to save 32TB of traffic every week](https://paulmillr.com/posts/chokidar-3-save-32tb-of-traffic/)

## How?

Chokidar does still rely on the Node.js core `fs` module, but when using
`fs.watch` and `fs.watchFile` for watching, it normalizes the events it
receives, often checking for truth by getting file stats and/or dir contents.

On MacOS, chokidar by default uses a native extension exposing the Darwin
`FSEvents` API. This provides very efficient recursive watching compared with
implementations like `kqueue` available on most \*nix platforms. Chokidar still
does have to do some work to normalize the events received that way as well.

On most other platforms, the `fs.watch`-based implementation is the default, which
avoids polling and keeps CPU usage down. Be advised that chokidar will initiate
watchers recursively for everything within scope of the paths that have been
specified, so be judicious about not wasting system resources by watching much
more than needed.

## Getting started

Install with npm:

```sh
npm install chokidar
```

Then `require` and use it in your code:

```javascript
const chokidar = require('chokidar');

// One-liner for current directory
chokidar.watch('.').on('all', (event, path) => {
  console.log(event, path);
});
```

## API

```javascript
// Example of a more typical implementation structure

// Initialize watcher.
const watcher = chokidar.watch('file, dir, glob, or array', {
  ignored: /(^|[\/\\])\../, // ignore dotfiles
  persistent: true
});

// Something to use when events are received.
const log = console.log.bind(console);
// Add event listeners.
watcher
  .on('add', path => log(`File ${path} has been added`))
  .on('change', path => log(`File ${path} has been changed`))
  .on('unlink', path => log(`File ${path} has been removed`));

// More possible events.
watcher
  .on('addDir', path => log(`Directory ${path} has been added`))
  .on('unlinkDir', path => log(`Directory ${path} has been removed`))
  .on('error', error => log(`Watcher error: ${error}`))
  .on('ready', () => log('Initial scan complete. Ready for changes'))
  .on('raw', (event, path, details) => { // internal
    log('Raw event info:', event, path, details);
  });

// 'add', 'addDir' and 'change' events also receive stat() results as second
// argument when available: https://nodejs.org/api/fs.html#fs_class_fs_stats
watcher.on('change', (path, stats) => {
  if (stats) console.log(`File ${path} changed size to ${stats.size}`);
});

// Watch new files.
watcher.add('new-file');
watcher.add(['new-file-2', 'new-file-3', '**/other-file*']);

// Get list of actual paths being watched on the filesystem
var watchedPaths = watcher.getWatched();

// Un-watch some files.
await watcher.unwatch('new-file*');

// Stop watching.
// The method is async!
watcher.close().then(() => console.log('closed'));

// Full list of options. See below for descriptions.
// Do not use this example!
chokidar.watch('file', {
  persistent: true,

  ignored: '*.txt',
  ignoreInitial: false,
  followSymlinks: true,
  cwd: '.',
  disableGlobbing: false,

  usePolling: false,
  interval: 100,
  binaryInterval: 300,
  alwaysStat: false,
  depth: 99,
  awaitWriteFinish: {
    stabilityThreshold: 2000,
    pollInterval: 100
  },

  ignorePermissionErrors: false,
  atomic: true // or a custom 'atomicity delay', in milliseconds (default 100)
});

```

`chokidar.watch(paths, [options])`

* `paths` (string or array of strings). Paths to files, dirs to be watched
recursively, or glob patterns.
    - Note: globs must not contain windows separators (`\`),
    because that's how they work by the standard —
    you'll need to replace them with forward slashes (`/`).
    - Note 2: for additional glob documentation, check out low-level
    library: [picomatch](https://github.com/micromatch/picomatch).
* `options` (object) Options object as defined below:

#### Persistence

* `persistent` (default: `true`). Indicates whether the process
should continue to run as long as files are being watched. If set to
`false` when using `fsevents` to watch, no more events will be emitted
after `ready`, even if the process continues to run.

#### Path filtering

* `ignored` ([anymatch](https://github.com/es128/anymatch)-compatible definition)
Defines files/paths to be ignored. The whole relative or absolute path is
tested, not just filename. If a function with two arguments is provided, it
gets called twice per path - once with a single argument (the path), second
time with two arguments (the path and the
[`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
object of that path).
* `ignoreInitial` (default: `false`). If set to `false` then `add`/`addDir` events are also emitted for matching paths while
instantiating the watching as chokidar discovers these file paths (before the `ready` event).
* `followSymlinks` (default: `true`). When `false`, only the
symlinks themselves will be watched for changes instead of following
the link references and bubbling events through the link's path.
* `cwd` (no default). The base directory from which watch `paths` are to be
derived. Paths emitted with events will be relative to this.
* `disableGlobbing` (default: `false`). If set to `true` then the strings passed to `.watch()` and `.add()` are treated as
literal path names, even if they look like globs.

#### Performance

* `usePolling` (default: `false`).
Whether to use fs.watchFile (backed by polling), or fs.watch. If polling
leads to high CPU utilization, consider setting this to `false`. It is
typically necessary to **set this to `true` to successfully watch files over
a network**, and it may be necessary to successfully watch files in other
non-standard situations. Setting to `true` explicitly on MacOS overrides the
`useFsEvents` default. You may also set the CHOKIDAR_USEPOLLING env variable
to true (1) or false (0) in order to override this option.
* _Polling-specific settings_ (effective when `usePolling: true`)
  * `interval` (default: `100`). Interval of file system polling, in milliseconds. You may also
    set the CHOKIDAR_INTERVAL env variable to override this option.
  * `binaryInterval` (default: `300`). Interval of file system
  polling for binary files.
  ([see list of binary extensions](https://github.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
* `useFsEvents` (default: `true` on MacOS). Whether to use the
`fsevents` watching interface if available. When set to `true` explicitly
and `fsevents` is available this supercedes the `usePolling` setting. When
set to `false` on MacOS, `usePolling: true` becomes the default.
* `alwaysStat` (default: `false`). If relying upon the
[`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
object that may get passed with `add`, `addDir`, and `change` events, set
this to `true` to ensure it is provided even in cases where it wasn't
already available from the underlying watch events.
* `depth` (default: `undefined`). If set, limits how many levels of
subdirectories will be traversed.
* `awaitWriteFinish` (default: `false`).
By default, the `add` event will fire when a file first appears on disk, before
the entire file has been written. Furthermore, in some cases some `change`
events will be emitted while the file is being written. In some cases,
especially when watching for large files there will be a need to wait for the
write operation to finish before responding to a file creation or modification.
Setting `awaitWriteFinish` to `true` (or a truthy value) will poll file size,
holding its `add` and `change` events until the size does not change for a
configurable amount of time. The appropriate duration setting is heavily
dependent on the OS and hardware. For accurate detection this parameter should
be relatively high, making file watching much less responsive.
Use with caution.
  * *`options.awaitWriteFinish` can be set to an object in order to adjust
  timing params:*
  * `awaitWriteFinish.stabilityThreshold` (default: 2000). Amount of time in
  milliseconds for a file size to remain constant before emitting its event.
  * `awaitWriteFinish.pollInterval` (default: 100). File size polling interval, in milliseconds.

#### Errors

* `ignorePermissionErrors` (default: `false`). Indicates whether to watch files
that don't have read permissions if possible. If watching fails due to `EPERM`
or `EACCES` with this set to `true`, the errors will be suppressed silently.
* `atomic` (default: `true` if `useFsEvents` and `usePolling` are `false`).
Automatically filters out artifacts that occur when using editors that use
"atomic writes" instead of writing directly to the source file. If a file is
re-added within 100 ms of being deleted, Chokidar emits a `change` event
rather than `unlink` then `add`. If the default of 100 ms does not work well
for you, you can override it by setting `atomic` to a custom value, in
milliseconds.

### Methods & Events

`chokidar.watch()` produces an instance of `FSWatcher`. Methods of `FSWatcher`:

* `.add(path / paths)`: Add files, directories, or glob patterns for tracking.
Takes an array of strings or just one string.
* `.on(event, callback)`: Listen for an FS event.
Available events: `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `ready`,
`raw`, `error`.
Additionally `all` is available which gets emitted with the underlying event
name and path for every event other than `ready`, `raw`, and `error`.  `raw` is internal, use it carefully.
* `.unwatch(path / paths)`: Stop watching files, directories, or glob patterns.
Takes an array of strings or just one string.
* `.close()`: **async** Removes all listeners from watched files. Asynchronous, returns Promise. Use with `await` to ensure bugs don't happen.
* `.getWatched()`: Returns an object representing all the paths on the file
system being watched by this `FSWatcher` instance. The object's keys are all the
directories (using absolute paths unless the `cwd` option was used), and the
values are arrays of the names of the items contained in each directory.

## CLI

If you need a CLI interface for your file watching, check out
[chokidar-cli](https://github.com/open-cli-tools/chokidar-cli), allowing you to
execute a command on each change, or get a stdio stream of change events.

## Install Troubleshooting

* `npm WARN optional dep failed, continuing fsevents@n.n.n`
  * This message is normal part of how `npm` handles optional dependencies and is
    not indicative of a problem. Even if accompanied by other related error messages,
    Chokidar should function properly.

* `TypeError: fsevents is not a constructor`
  * Update chokidar by doing `rm -rf node_modules package-lock.json yarn.lock && npm install`, or update your dependency that uses chokidar.

* Chokidar is producing `ENOSP` error on Linux, like this:
  * `bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell`
  `Error: watch /home/ ENOSPC`
  * This means Chokidar ran out of file handles and you'll need to increase their count by executing the following command in Terminal:
  `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`

## Changelog

For more detailed changelog, see [`full_changelog.md`](.github/full_changelog.md).
- **v3.5 (Jan 6, 2021):** Support for ARM Macs with Apple Silicon. Fixes for deleted symlinks.
- **v3.4 (Apr 26, 2020):** Support for directory-based symlinks. Fixes for macos file replacement.
- **v3.3 (Nov 2, 2019):** `FSWatcher#close()` method became async. That fixes IO race conditions related to close method.
- **v3.2 (Oct 1, 2019):** Improve Linux RAM usage by 50%. Race condition fixes. Windows glob fixes. Improve stability by using tight range of dependency versions.
- **v3.1 (Sep 16, 2019):** dotfiles are no longer filtered out by default. Use `ignored` option if needed. Improve initial Linux scan time by 50%.
- **v3 (Apr 30, 2019):** massive CPU & RAM consumption improvements; reduces deps / package size by a factor of 17x and bumps Node.js requirement to v8.16 and higher.
- **v2 (Dec 29, 2017):** Globs are now posix-style-only; without windows support. Tons of bugfixes.
- **v1 (Apr 7, 2015):** Glob support, symlink support, tons of bugfixes. Node 0.8+ is supported
- **v0.1 (Apr 20, 2012):** Initial release, extracted from [Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66)

## Also

Why was chokidar named this way? What's the meaning behind it?

>Chowkidar is a transliteration of a Hindi word meaning 'watchman, gatekeeper', चौकीदार. This ultimately comes from Sanskrit _ चतुष्क_ (crossway, quadrangle, consisting-of-four). This word is also used in other languages like Urdu as (چوکیدار) which is widely used in Pakistan and India. 

## License

MIT (c) Paul Miller (<https://paulmillr.com>), see [LICENSE](LICENSE) file.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        // ISC @ Julien Fontanet

"use strict";

// ===================================================================

var construct = typeof Reflect !== "undefined" ? Reflect.construct : undefined;
var defineProperty = Object.defineProperty;

// -------------------------------------------------------------------

var captureStackTrace = Error.captureStackTrace;
if (captureStackTrace === undefined) {
  captureStackTrace = function captureStackTrace(error) {
    var container = new Error();

    defineProperty(error, "stack", {
      configurable: true,
      get: function getStack() {
        var stack = container.stack;

        // Replace property with value for faster future accesses.
        defineProperty(this, "stack", {
          configurable: true,
          value: stack,
          writable: true,
        });

        return stack;
      },
      set: function setStack(stack) {
        defineProperty(error, "stack", {
          configurable: true,
          value: stack,
          writable: true,
        });
      },
    });
  };
}

// -------------------------------------------------------------------

function BaseError(message) {
  if (message !== undefined) {
    defineProperty(this, "message", {
      configurable: true,
      value: message,
      writable: true,
    });
  }

  var cname = this.constructor.name;
  if (cname !== undefined && cname !== this.name) {
    defineProperty(this, "name", {
      configurable: true,
      value: cname,
      writable: true,
    });
  }

  captureStackTrace(this, this.constructor);
}

BaseError.prototype = Object.create(Error.prototype, {
  // See: https://github.com/JsCommunity/make-error/issues/4
  constructor: {
    configurable: true,
    value: BaseError,
    writable: true,
  },
});

// -------------------------------------------------------------------

// Sets the name of a function if possible (depends of the JS engine).
var setFunctionName = (function() {
  function setFunctionName(fn, name) {
    return defineProperty(fn, "name", {
      configurable: true,
      value: name,
    });
  }
  try {
    var f = function() {};
    setFunctionName(f, "foo");
    if (f.name === "foo") {
      return setFunctionName;
    }
  } catch (_) {}
})();

// -------------------------------------------------------------------

function makeError(constructor, super_) {
  if (super_ == null || super_ === Error) {
    super_ = BaseError;
  } else if (typeof super_ !== "function") {
    throw new TypeError("super_ should be a function");
  }

  var name;
  if (typeof constructor === "string") {
    name = constructor;
    constructor =
      construct !== undefined
        ? function() {
            return construct(super_, arguments, this.constructor);
          }
        : function() {
            super_.apply(this, arguments);
          };

    // If the name can be set, do it once and for all.
    if (setFunctionName !== undefined) {
      setFunctionName(constructor, name);
      name = undefined;
    }
  } else if (typeof constructor !== "function") {
    throw new TypeError("constructor should be either a string or a function");
  }

  // Also register the super constructor also as `constructor.super_` just
  // like Node's `util.inherits()`.
  //
  // eslint-disable-next-line dot-notation
  constructor.super_ = constructor["super"] = super_;

  var properties = {
    constructor: {
      configurable: true,
      value: constructor,
      writable: true,
    },
  };

  // If the name could not be set on the constructor, set it on the
  // prototype.
  if (name !== undefined) {
    properties.name = {
      configurable: true,
      value: name,
      writable: true,
    };
  }
  constructor.prototype = Object.create(super_.prototype, properties);

  return constructor;
}
exports = module.exports = makeError;
exports.BaseError = BaseError;
                                                                                                                                                                                                                                                  /**
 * Create a new error constructor instance.
 */
declare function makeError(
  name: string
): makeError.Constructor<makeError.BaseError>;

/**
 * Set the constructor prototype to `BaseError`.
 */
declare function makeError<T extends Error>(super_: {
  new (...args: any[]): T;
}): makeError.Constructor<T & makeError.BaseError>;

/**
 * Create a specialized error instance.
 */
declare function makeError<T extends Error, K>(
  name: string | Function,
  super_: K
): K & makeError.SpecializedConstructor<T>;

declare namespace makeError {
  /**
   * Use with ES2015+ inheritance.
   */
  export class BaseError extends Error {
    message: string;
    name: string;
    stack: string;

    constructor(message?: string);
  }

  export interface Constructor<T> {
    new (message?: string): T;
    super_: any;
    prototype: T;
  }

  export interface SpecializedConstructor<T> {
    super_: any;
    prototype: T;
  }
}

export = makeError;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             # make-error

[![Package Version](https://badgen.net/npm/v/make-error)](https://npmjs.org/package/make-error) [![Build Status](https://travis-ci.org/JsCommunity/make-error.png?branch=master)](https://travis-ci.org/JsCommunity/make-error) [![PackagePhobia](https://badgen.net/packagephobia/install/make-error)](https://packagephobia.now.sh/result?p=make-error) [![Latest Commit](https://badgen.net/github/last-commit/JsCommunity/make-error)](https://github.com/JsCommunity/make-error/commits/master)

> Make your own error types!

## Features

- Compatible Node & browsers
- `instanceof` support
- `error.name` & `error.stack` support
- compatible with [CSP](https://en.wikipedia.org/wiki/Content_Security_Policy) (i.e. no `eval()`)

## Installation

### Node & [Browserify](http://browserify.org/)/[Webpack](https://webpack.js.org/)

Installation of the [npm package](https://npmjs.org/package/make-error):

```
> npm install --save make-error
```

Then require the package:

```javascript
var makeError = require("make-error");
```

### Browser

You can directly use the build provided at [unpkg.com](https://unpkg.com):

```html
<script src="https://unpkg.com/make-error@1/dist/make-error.js"></script>
```

## Usage

### Basic named error

```javascript
var CustomError = makeError("CustomError");

// Parameters are forwarded to the super class (here Error).
throw new CustomError("a message");
```

### Advanced error class

```javascript
function CustomError(customValue) {
  CustomError.super.call(this, "custom error message");

  this.customValue = customValue;
}
makeError(CustomError);

// Feel free to extend the prototype.
CustomError.prototype.myMethod = function CustomError$myMethod() {
  console.log("CustomError.myMethod (%s, %s)", this.code, this.message);
};

//-----

try {
  throw new CustomError(42);
} catch (error) {
  error.myMethod();
}
```

### Specialized error

```javascript
var SpecializedError = makeError("SpecializedError", CustomError);

throw new SpecializedError(42);
```

### Inheritance

> Best for ES2015+.

```javascript
import { BaseError } from "make-error";

class CustomError extends BaseError {
  constructor() {
    super("custom error message");
  }
}
```

## Related

- [make-error-cause](https://www.npmjs.com/package/make-error-cause): Make your own error types, with a cause!

## Contributions

Contributions are _very_ welcomed, either on the documentation or on
the code.

You may:

- report any [issue](https://github.com/JsCommunity/make-error/issues)
  you've encountered;
- fork and create a pull request.

## License

ISC © [Julien Fontanet](http://julien.isonoe.net)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           {
  "name": "app-conf",
  "version": "3.1.1",
  "license": "ISC",
  "author": "Julien Fontanet <julien.fontanet@isonoe.net> (http://julien.isonoe.net/)",
  "description": "",
  "keywords": [],
  "homepage": "https://github.com/julien-f/nodejs-app-conf",
  "bugs": "https://github.com/julien-f/nodejs-app-conf/issues/",
  "repository": {
    "type": "git",
    "url": "https://github.com/julien-f/nodejs-app-conf"
  },
  "files": [
    "_pMap.js",
    "_merge.js",
    "_readFile.js",
    "cli.js",
    "entries.js",
    "index.js",
    "serializers.js",
    "unknown-format-error.js"
  ],
  "bin": "./cli.js",
  "engines": {
    "node": ">=9.9"
  },
  "dependencies": {
    "chokidar": "^3.5.1",
    "debug": "^4.1.0",
    "glob": "^8.1.0",
    "lodash": "^4.17.11",
    "make-error": "^1.3.5",
    "promise-toolbox": "^0.21.0",
    "xdg-basedir": "^4.0.0"
  },
  "devDependencies": {
    "eslint": "^8.57.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-config-standard": "^17.1.0",
    "eslint-plugin-import": "^2.29.1",
    "eslint-plugin-n": "^16.6.2",
    "eslint-plugin-promise": "^6.1.1",
    "husky": "^9.0.11",
    "lint-staged": "^15.2.2",
    "mock-fs": "^5.2.0",
    "prettier": "^3.2.5",
    "test": "^3.3.0"
  },
  "scripts": {
    "test": "node--test && true",
    "prepare": "husky"
  },
  "lint-staged": {
    "*": [
      "npm test",
      "prettier --ignore-unknown --write"
    ],
    "*.{{,c,m}j,t}s{,x}": "eslint --ignore-pattern '!*'"
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 "use strict";

// ===================================================================

const chokidar = require("chokidar");
const dirname = require("path").dirname;
const homedir = require("os").homedir;
const resolvePath = require("path").resolve;

const debug = require("debug")("app-conf");
const flatten = require("lodash/flatten");

const entries = require("./entries");
const merge = require("./_merge");
const pMap = require("./_pMap");
const readFile = require("./_readFile");
const UnknownFormatError = require("./unknown-format-error");
const unserialize = require("./serializers").unserialize;

// ===================================================================

const clone = (value) => JSON.parse(JSON.stringify(value));

const RELATIVE_PATH_RE = /^\.{1,2}[/\\]/;
function resolvePaths(value, base) {
  if (typeof value === "string") {
    return value[0] === "~" && (value[1] === "/" || value[1] === "\\")
      ? homedir() + value.slice(1)
      : RELATIVE_PATH_RE.test(value)
        ? resolvePath(base, value)
        : value;
  }

  if (value !== null && typeof value === "object") {
    for (const key of Object.keys(value)) {
      value[key] = resolvePaths(value[key], base);
    }
    return value;
  }

  return value;
}

// ===================================================================

function load(
  appName,
  { appDir, defaults, entries: whitelist, ignoreUnknownFormats = false } = {},
) {
  const useWhitelist = whitelist !== undefined;
  if (useWhitelist) {
    whitelist = new Set(whitelist);
  }
  const entryOpts = { appDir, appName };
  return pMap(entries, (entry) => {
    if (useWhitelist && !whitelist.has(entry.name)) {
      return [];
    }

    const dirFn = entry.dir;
    const dir = typeof dirFn === "function" ? dirFn(entryOpts) : dirFn;
    return entry.list(entryOpts, dir) || [];
  })
    .then((files) => {
      files = flatten(files);
      return pMap(files, async (file) => {
        try {
          return await parse(file);
        } catch (error) {
          if (!(ignoreUnknownFormats && error instanceof UnknownFormatError)) {
            throw error;
          }
        }
      });
    })
    .then((data) =>
      data.reduce(
        (acc, cfg) => {
          if (cfg !== undefined) {
            merge(acc, cfg);
          }
          return acc;
        },
        defaults === undefined ? {} : clone(defaults),
      ),
    );
}
exports.load = load;

// ===================================================================

async function parse(path) {
  const file = await readFile(path);
  const data = unserialize(file);
  debug(file.path);
  return resolvePaths(data, dirname(file.path));
}
exports.parse = parse;

// ===================================================================

const ALL_ENTRIES = entries.map((_) => _.name);

exports.watch = function watch(
  {
    appName,
    defaults,
    entries: whitelist = ALL_ENTRIES,
    initialLoad = false,
    ...opts
  },
  cb,
) {
  return new Promise((resolve, reject) => {
    const dirs = [];
    const entryOpts = { appName, appDir: opts.appDir };
    entries.forEach((entry) => {
      // vendor config should not change and is therefore not watched
      //
      // otherwise it could interfere if the program is running during
      // uninstall/reinstall
      if (entry.name === "vendor") {
        return;
      }

      const dirFn = entry.dir;
      const dir = typeof dirFn === "function" ? dirFn(entryOpts) : dirFn;
      if (dir !== undefined) {
        dirs.push(dir);
      }
    });

    const watcher = chokidar.watch(dirs, {
      depth: 0,
      ignoreInitial: true,
      ignorePermissionErrors: true,
    });

    const loadWrapper = () => {
      load(appName, opts).then((config) => cb(undefined, config), cb);
    };

    watcher
      .on("all", loadWrapper)
      .once("error", reject)
      .once("ready", async () => {
        function unsubscribe() {
          return watcher.close();
        }

        // vendor config is only read once and merged to defaults to avoid issues
        // in case it has been deleted and another entry triggers a reload
        if (whitelist.includes("vendor")) {
          opts.entries = ["vendor"];

          const vendor = await load(appName, opts);

          opts.defaults =
            defaults === undefined ? vendor : merge(clone(defaults), vendor);
          opts.entries = whitelist.filter((_) => _ !== "vendor");
        }

        if (initialLoad) {
          try {
            cb(undefined, await load(appName, opts));
            resolve(unsubscribe);
          } catch (error) {
            try {
              await unsubscribe();
            } catch (_) {}
            reject(error);
          }
        } else {
          resolve(unsubscribe);
        }
      });
  });
};
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  "use strict";

module.exports = function merge(target, source) {
  if (source === undefined) {
    return target;
  }

  if (
    target === null ||
    source === null ||
    typeof target !== "object" ||
    typeof source !== "object"
  ) {
    return source;
  }

  for (const key of Object.keys(source)) {
    target[key] = merge(target[key], source[key]);
  }
  return target;
};
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               "use strict";

// ===================================================================

const makeError = require("make-error");

// ===================================================================

module.exports = makeError("UnknownFormatError");
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     "use strict";

module.exports = function pMap(array, iteratee) {
  const { then } = array;
  return typeof then === "function"
    ? then.call(array, (array) => pMap(array, iteratee))
    : Promise.all(array.map(iteratee));
};
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             "use strict";

// ===================================================================

const promisify = require("promise-toolbox/promisify");

const j = require("path").join;
const resolvePath = require("path").resolve;

const flatten = require("lodash/flatten");
const glob = promisify(require("glob"));
const xdgBasedir = require("xdg-basedir");

const pMap = require("./_pMap");

// ===================================================================

function ignoreAccessErrors(error) {
  if (error.code !== "EACCES") {
    throw error;
  }

  return [];
}

// ===================================================================

// Default configuration entries.
module.exports = [
  // Default vendor configuration.
  {
    name: "vendor",
    dir: (opts) => opts.appDir,
    list: (_, dir) => dir && glob(j(dir, "config.*")),
  },

  // Configuration for the whole system.
  {
    name: "system",
    dir: (opts) => j("/etc", opts.appName),
    list: (_, dir) => glob(j(dir, "config.*")),
  },

  // Configuration for the current user.
  {
    name: "global",
    dir: (opts) => {
      const configDir = xdgBasedir.config;
      return configDir && j(configDir, opts.appName);
    },
    list: (_, dir) => dir && glob(j(dir, "config.*")),
  },

  // Configuration of the current project (local to the file
  // hierarchy).
  {
    name: "local",
    list(opts) {
      const { appName } = opts;

      // Compute the list of paths from the current directory to the
      // root directory.
      const paths = [];
      let dir, prev;
      dir = process.cwd();
      while (dir !== prev) {
        paths.push(j(dir, "." + appName + ".*"));
        prev = dir;
        dir = resolvePath(dir, "..");
      }

      return pMap(paths.reverse(), (path) =>
        glob(path, {
          silent: true,
        }).catch(ignoreAccessErrors),
      ).then(flatten);
    },
  },
];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "use strict";

// ===================================================================

const findKey = require("lodash/findKey");

const formatJson = JSON.stringify;
const parseJson = (function () {
  try {
    // eslint-disable-next-line n/no-extraneous-require
    return require("json5").parse;
  } catch (_) {}

  try {
    // eslint-disable-next-line n/no-extraneous-require
    const stripJsonComments = require("strip-json-comments");
    return function parseJson(json) {
      return JSON.parse(stripJsonComments(json));
    };
  } catch (_) {}

  return JSON.parse;
})();

const UnknownFormatError = require("./unknown-format-error");

// ===================================================================

const serializers = Object.create(null);

serializers.json = {
  // Test whether this “file” seems to be on the correct format.
  test: function (file) {
    return file.path && /\.json5?$/i.test(file.path);
  },

  // Unserialize the content of the file.
  unserialize: function (file) {
    return parseJson(String(file.content));
  },

  // Serialize the value to a string/buffer.
  serialize: function (value, opts) {
    opts || (opts = {});
    const indent = "indent" in opts ? opts.indent : 2;

    return formatJson(value, null, indent);
  },
};

// Optional dependency.
try {
  // eslint-disable-next-line n/no-missing-require
  const CSON = require("cson-parse");

  serializers.cson = {
    test: function (file) {
      return file.path && /\.cson$/i.test(file.path);
    },
    unserialize: function (file) {
      return CSON.parse(String(file.content));
    },
    serialize: function (value) {
      return CSON.stringify(value);
    },
  };
} catch (error) {}

// Optional dependency.
try {
  // eslint-disable-next-line n/no-missing-require
  const ini = require("ini");

  serializers.ini = {
    test: function (file) {
      return file.path && /\.ini$/i.test(file.path);
    },
    unserialize: function (file) {
      return ini.decode(String(file.content));
    },
    serialize: function (value) {
      return ini.encode(value);
    },
  };
} catch (error) {}

try {
  // eslint-disable-next-line n/no-missing-require
  const { parse, stringify } = require("@iarna/toml");

  serializers.toml = {
    serialize: stringify,
    test: ({ path }) => path !== undefined && /\.toml$/i.test(path),
    unserialize: ({ content }) => parse(String(content)),
  };
} catch (error) {}

// Optional dependency.
try {
  // eslint-disable-next-line n/no-extraneous-require
  const yaml = require("js-yaml");

  let { dump, load } = yaml;

  // compat with js-yaml < 4
  if ("DEFAULT_SAFE_SCHEMA" in yaml) {
    dump = yaml.safeDump;
    load = yaml.safeLoad;
  }

  serializers.yaml = {
    test: function (file) {
      return file.path && /\.yaml$/i.test(file.path);
    },
    unserialize: function (file) {
      return load(String(file.content));
    },
    serialize: function (value, opts) {
      opts || (opts = {});
      const indent = "indent" in opts ? opts.indent : 2;

      return dump(value, {
        indent,
      });
    },
  };
} catch (error) {}

// --------------------------------------------------------------------

const detectFormat = function (file) {
  return findKey(serializers, function (serializer) {
    return serializer.test(file);
  });
};

const serialize = function (value, format, opts) {
  let buffer = serializers[format].serialize(value, opts);

  if (!(buffer instanceof Buffer)) {
    buffer = Buffer.from(buffer);
  }

  return buffer;
};

const unserialize = function (file, format) {
  format || (format = detectFormat(file));

  if (!format) {
    throw new UnknownFormatError("no compatible format found for " + file.path);
  }

  return serializers[format].unserialize(file);
};

// ===================================================================

exports.detectFormat = detectFormat;
exports.serialize = serialize;
exports.unserialize = unserialize;
                                                                                                                                                          "use strict";

const promisify = require("promise-toolbox/promisify");

const fs$readFile = promisify(require("fs").readFile);
const realpath = promisify(require("fs").realpath);

module.exports = function readFile(path) {
  return realpath(path).then(function (path) {
    return fs$readFile(path).then(function (buffer) {
      return {
        path,
        content: buffer,
      };
    });
  });
};
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            {
  "name": "anymatch",
  "version": "3.1.3",
  "description": "Matches strings against configurable strings, globs, regular expressions, and/or functions",
  "files": [
    "index.js",
    "index.d.ts"
  ],
  "dependencies": {
    "normalize-path": "^3.0.0",
    "picomatch": "^2.0.4"
  },
  "author": {
    "name": "Elan Shanker",
    "url": "https://github.com/es128"
  },
  "license": "ISC",
  "homepage": "https://github.com/micromatch/anymatch",
  "repository": {
    "type": "git",
    "url": "https://github.com/micromatch/anymatch"
  },
  "keywords": [
    "match",
    "any",
    "string",
    "file",
    "fs",
    "list",
    "glob",
    "regex",
    "regexp",
    "regular",
    "expression",
    "function"
  ],
  "scripts": {
    "test": "nyc mocha",
    "mocha": "mocha"
  },
  "devDependencies": {
    "mocha": "^6.1.3",
    "nyc": "^14.0.0"
  },
  "engines": {
    "node": ">= 8"
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        type AnymatchFn = (testString: string) => boolean;
type AnymatchPattern = string|RegExp|AnymatchFn;
type AnymatchMatcher = AnymatchPattern|AnymatchPattern[]
type AnymatchTester = {
  (testString: string|any[], returnIndex: true): number;
  (testString: string|any[]): boolean;
}

type PicomatchOptions = {dot: boolean};

declare const anymatch: {
  (matchers: AnymatchMatcher): AnymatchTester;
  (matchers: AnymatchMatcher, testString: null, returnIndex: true | PicomatchOptions): AnymatchTester;
  (matchers: AnymatchMatcher, testString: string|any[], returnIndex: true | PicomatchOptions): number;
  (matchers: AnymatchMatcher, testString: string|any[]): boolean;
}

export {AnymatchMatcher as Matcher}
export {AnymatchTester as Tester}
export default anymatch
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     The ISC License

Copyright (c) 2019 Elan Shanker, Paul Miller (https://paulmillr.com)

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                {
  "name": "inherits",
  "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()",
  "version": "2.0.4",
  "keywords": [
    "inheritance",
    "class",
    "klass",
    "oop",
    "object-oriented",
    "inherits",
    "browser",
    "browserify"
  ],
  "main": "./inherits.js",
  "browser": "./inherits_browser.js",
  "repository": "git://github.com/isaacs/inherits",
  "license": "ISC",
  "scripts": {
    "test": "tap"
  },
  "devDependencies": {
    "tap": "^14.2.4"
  },
  "files": [
    "inherits.js",
    "inherits_browser.js"
  ]
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           if (typeof Object.create === 'function') {
  // implementation from standard node.js 'util' module
  module.exports = function inherits(ctor, superCtor) {
    if (superCtor) {
      ctor.super_ = superCtor
      ctor.prototype = Object.create(superCtor.prototype, {
        constructor: {
          value: ctor,
          enumerable: false,
          writable: true,
          configurable: true
        }
      })
    }
  };
} else {
  // old school shim for old browsers
  module.exports = function inherits(ctor, superCtor) {
    if (superCtor) {
      ctor.super_ = superCtor
      var TempCtor = function () {}
      TempCtor.prototype = superCtor.prototype
      ctor.prototype = new TempCtor()
      ctor.prototype.constructor = ctor
    }
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               Browser-friendly inheritance fully compatible with standard node.js
[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).

This package exports standard `inherits` from node.js `util` module in
node environment, but also provides alternative browser-friendly
implementation through [browser
field](https://gist.github.com/shtylman/4339901). Alternative
implementation is a literal copy of standard one located in standalone
module to avoid requiring of `util`. It also has a shim for old
browsers with no `Object.create` support.

While keeping you sure you are using standard `inherits`
implementation in node.js environment, it allows bundlers such as
[browserify](https://github.com/substack/node-browserify) to not
include full `util` package to your client code if all you need is
just `inherits` function. It worth, because browser shim for `util`
package is large and `inherits` is often the single function you need
from it.

It's recommended to use this package instead of
`require('util').inherits` for any code that has chances to be used
not only in node.js but in browser too.

## usage

```js
var inherits = require('inherits');
// then use exactly as the standard one
```

## note on version ~1.0

Version ~1.0 had completely different motivation and is not compatible
neither with 2.0 nor with standard node.js `inherits`.

If you are using version ~1.0 and planning to switch to ~2.0, be
careful:

* new version uses `super_` instead of `super` for referencing
  superclass
* new version overwrites current prototype while old one preserves any
  existing fields on it
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       try {
  var util = require('util');
  /* istanbul ignore next */
  if (typeof util.inherits !== 'function') throw '';
  module.exports = util.inherits;
} catch (e) {
  /* istanbul ignore next */
  module.exports = require('./inherits_browser.js');
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      const EMPTYARR = []
const SHORTSPLIT = /$|[!-@[-`{-~][\s\S]*/g
const isArray = Array.isArray

const parseValue = function (any) {
  if (any === "") return ""
  if (any === "false") return false
  const maybe = +any
  return maybe * 0 === 0 ? maybe : any
}

const parseAlias = function (aliases) {
  let out = {},
    alias,
    prev,
    any

  for (let key in aliases) {
    any = aliases[key]
    alias = out[key] = isArray(any) ? any : [any]

    for (let i = 0; i < alias.length; i++) {
      prev = out[alias[i]] = [key]

      for (let k = 0; k < alias.length; k++) {
        if (i !== k) prev.push(alias[k])
      }
    }
  }

  return out
}

const parseDefault = function (aliases, defaults) {
  let out = {},
    alias,
    value

  for (let key in defaults) {
    alias = aliases[key]
    value = defaults[key]

    out[key] = value

    if (alias === undefined) {
      aliases[key] = EMPTYARR
    } else {
      for (let i = 0; i < alias.length; i++) {
        out[alias[i]] = value
      }
    }
  }

  return out
}

const parseOptions = function (aliases, options, value) {
  let out = {},
    key,
    alias

  if (options !== undefined) {
    for (let i = 0; i < options.length; i++) {
      key = options[i]
      alias = aliases[key]

      out[key] = value

      if (alias === undefined) {
        aliases[key] = EMPTYARR
      } else {
        for (let k = 0, end = alias.length; k < end; k++) {
          out[alias[k]] = value
        }
      }
    }
  }

  return out
}

const write = function (out, key, value, aliases, unknown) {
  let prev,
    alias = aliases[key],
    len = alias === undefined ? -1 : alias.length

  if (len >= 0 || unknown === undefined || unknown(key)) {
    prev = out[key]

    if (prev === undefined) {
      out[key] = value
    } else {
      if (isArray(prev)) {
        prev.push(value)
      } else {
        out[key] = [prev, value]
      }
    }

    for (let i = 0; i < len; i++) {
      out[alias[i]] = out[key]
    }
  }
}

export default function (argv, opts) {
  let unknown = (opts = opts || {}).unknown,
    aliases = parseAlias(opts.alias),
    strings = parseOptions(aliases, opts.string, ""),
    values = parseDefault(aliases, opts.default),
    bools = parseOptions(aliases, opts.boolean, false),
    stopEarly = opts.stopEarly,
    _ = [],
    out = { _ },
    key,
    arg,
    end,
    match,
    value

  for (let i = 0, len = argv.length; i < len; i++) {
    arg = argv[i]

    if (arg[0] !== "-" || arg === "-") {
      if (stopEarly) {
        while (i < len) {
          _.push(argv[i++])
        }
      } else {
        _.push(arg)
      }
    } else if (arg === "--") {
      while (++i < len) {
        _.push(argv[i])
      }
    } else if (arg[1] === "-") {
      end = arg.indexOf("=", 2)
      if (arg[2] === "n" && arg[3] === "o" && arg[4] === "-") {
        key = arg.slice(5, end >= 0 ? end : undefined)
        value = false
      } else if (end >= 0) {
        key = arg.slice(2, end)
        value =
          bools[key] !== undefined ||
          (strings[key] === undefined
            ? parseValue(arg.slice(end + 1))
            : arg.slice(end + 1))
      } else {
        key = arg.slice(2)
        value =
          bools[key] !== undefined ||
          (len === i + 1 || argv[i + 1][0] === "-"
            ? strings[key] === undefined
              ? true
              : ""
            : strings[key] === undefined
            ? parseValue(argv[++i])
            : argv[++i])
      }
      write(out, key, value, aliases, unknown)
    } else {
      SHORTSPLIT.lastIndex = 2
      match = SHORTSPLIT.exec(arg)
      end = match.index
      value = match[0]

      for (let k = 1; k < end; k++) {
        write(
          out,
          (key = arg[k]),
          k + 1 < end
            ? strings[key] === undefined ||
                arg.substring(k + 1, (k = end)) + value
            : value === ""
            ? len === i + 1 || argv[i + 1][0] === "-"
              ? strings[key] === undefined || ""
              : bools[key] !== undefined ||
                (strings[key] === undefined ? parseValue(argv[++i]) : argv[++i])
            : bools[key] !== undefined ||
              (strings[key] === undefined ? parseValue(value) : value),
          aliases,
          unknown
        )
      }
    }
  }

  for (let key in values) if (out[key] === undefined) out[key] = values[key]
  for (let key in bools) if (out[key] === undefined) out[key] = false
  for (let key in strings) if (out[key] === undefined) out[key] = ""

  return out
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  const EMPTYARR = []
const SHORTSPLIT = /$|[!-@[-`{-~][\s\S]*/g
const isArray = Array.isArray

const parseValue = function (any) {
  if (any === "") return ""
  if (any === "false") return false
  const maybe = +any
  return maybe * 0 === 0 ? maybe : any
}

const parseAlias = function (aliases) {
  let out = {},
    alias,
    prev,
    any

  for (let key in aliases) {
    any = aliases[key]
    alias = out[key] = isArray(any) ? any : [any]

    for (let i = 0; i < alias.length; i++) {
      prev = out[alias[i]] = [key]

      for (let k = 0; k < alias.length; k++) {
        if (i !== k) prev.push(alias[k])
      }
    }
  }

  return out
}

const parseDefault = function (aliases, defaults) {
  let out = {},
    alias,
    value

  for (let key in defaults) {
    alias = aliases[key]
    value = defaults[key]

    out[key] = value

    if (alias === undefined) {
      aliases[key] = EMPTYARR
    } else {
      for (let i = 0; i < alias.length; i++) {
        out[alias[i]] = value
      }
    }
  }

  return out
}

const parseOptions = function (aliases, options, value) {
  let out = {},
    key,
    alias

  if (options !== undefined) {
    for (let i = 0; i < options.length; i++) {
      key = options[i]
      alias = aliases[key]

      out[key] = value

      if (alias === undefined) {
        aliases[key] = EMPTYARR
      } else {
        for (let k = 0, end = alias.length; k < end; k++) {
          out[alias[k]] = value
        }
      }
    }
  }

  return out
}

const write = function (out, key, value, aliases, unknown) {
  let prev,
    alias = aliases[key],
    len = alias === undefined ? -1 : alias.length

  if (len >= 0 || unknown === undefined || unknown(key)) {
    prev = out[key]

    if (prev === undefined) {
      out[key] = value
    } else {
      if (isArray(prev)) {
        prev.push(value)
      } else {
        out[key] = [prev, value]
      }
    }

    for (let i = 0; i < len; i++) {
      out[alias[i]] = out[key]
    }
  }
}

module.exports = function (argv, opts) {
  let unknown = (opts = opts || {}).unknown,
    aliases = parseAlias(opts.alias),
    strings = parseOptions(aliases, opts.string, ""),
    values = parseDefault(aliases, opts.default),
    bools = parseOptions(aliases, opts.boolean, false),
    stopEarly = opts.stopEarly,
    _ = [],
    out = { _ },
    key,
    arg,
    end,
    match,
    value

  for (let i = 0, len = argv.length; i < len; i++) {
    arg = argv[i]

    if (arg[0] !== "-" || arg === "-") {
      if (stopEarly) {
        while (i < len) {
          _.push(argv[i++])
        }
      } else {
        _.push(arg)
      }
    } else if (arg === "--") {
      while (++i < len) {
        _.push(argv[i])
      }
    } else if (arg[1] === "-") {
      end = arg.indexOf("=", 2)
      if (arg[2] === "n" && arg[3] === "o" && arg[4] === "-") {
        key = arg.slice(5, end >= 0 ? end : undefined)
        value = false
      } else if (end >= 0) {
        key = arg.slice(2, end)
        value =
          bools[key] !== undefined ||
          (strings[key] === undefined
            ? parseValue(arg.slice(end + 1))
            : arg.slice(end + 1))
      } else {
        key = arg.slice(2)
        value =
          bools[key] !== undefined ||
          (len === i + 1 || argv[i + 1][0] === "-"
            ? strings[key] === undefined
              ? true
              : ""
            : strings[key] === undefined
            ? parseValue(argv[++i])
            : argv[++i])
      }
      write(out, key, value, aliases, unknown)
    } else {
      SHORTSPLIT.lastIndex = 2
      match = SHORTSPLIT.exec(arg)
      end = match.index
      value = match[0]

      for (let k = 1; k < end; k++) {
        write(
          out,
          (key = arg[k]),
          k + 1 < end
            ? strings[key] === undefined ||
                arg.substring(k + 1, (k = end)) + value
            : value === ""
            ? len === i + 1 || argv[i + 1][0] === "-"
              ? strings[key] === undefined || ""
              : bools[key] !== undefined ||
                (strings[key] === undefined ? parseValue(argv[++i]) : argv[++i])
            : bools[key] !== undefined ||
              (strings[key] === undefined ? parseValue(value) : value),
          aliases,
          unknown
        )
      }
    }
  }

  for (let key in values) if (out[key] === undefined) out[key] = values[key]
  for (let key in bools) if (out[key] === undefined) out[key] = false
  for (let key in strings) if (out[key] === undefined) out[key] = ""

  return out
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                # Getopts

> Parse CLI arguments.

- Lightweight drop-in replacement for `minimist` and clones.
- Small (180 LOC), focused, no dependencies.
- Up to [6x faster](#benchmarks) than alternatives!

Break up command-line arguments into key-value pairs for easy look-up and retrieval. Built upon [utility conventions](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02) that have been used for decades, Getopts sane defaults help you write CLI tools that look and feel like the real deal.

```console
$ example --type=module -o main.js *.{js,json}
```

```js
import getopts from "getopts"

const options = getopts(process.argv.slice(2), {
  alias: {
    output: ["o", "f"],
    type: "t",
  },
})
```

The result is an object populated with all the parsed arguments.

```js
{
  _: ["index.js", "package.json"],
  output: "main.js",
  type: "module",
  o: "main.js",
  f: "main.js",
  t: "module",
}
```

## Installation

```console
npm install getopts
```

## Parsing rules

### Short options

A short option consists of a `-` followed by a single alphabetic character. Multiple options can be grouped together without spaces. Short options are boolean by default unless followed by an [operand](#operand) (non-option) or if adjacent to any non-alphabetic characters:

```js
getopts(["-ab", "-c"]) //=> { _: [], a:true, b:true, c:true }
```

```js
getopts(["-a", "alpha"]) //=> { _: [], a:"alpha" }
```

```js
getopts(["-abc1"]) //=> { _: [], a:true, b:true, c:1 }
```

Use [`opts.string`](#optsstring) to parse an option as a string regardless.

```js
getopts(["-kF12"], {
  string: ["k"],
}) //=> { _: [], k:"F12" }
```

The first operand following an option will be used as its value. Use [`opts.boolean`](#optsboolean) to specify that an option should be parsed as a boolean regardless, causing the following argument to be treated as an operand instead.

```js
getopts(["-a", "alpha"], {
  boolean: ["a"],
}) //=> { _: ["alpha"], a:true }
```

Any character listed in the ASCII table can be used as a short option if it's the first character after the dash.

```js
getopts(["-9", "-#10", "-%0.01"]) //=> { _:[], 9:true, #:10, %:0.01 }
```

### Long options

A long option consists of a `--` followed by a name and a value separated by an `=`. Long options without a value are boolean by default.

```js
getopts(["--turbo", "--warp=10"]) //=> { _: [], turbo:true, warp:10 }
```

```js
getopts(["--warp=e=mc^2"]) //=> { _: [], warp:"e=mc^2" }
```

```js
getopts(["--@", "alpha"]) //=> { _: [], @:"alpha" }
```

Negated options start with `--no-` and are always `false`.

```js
getopts(["--no-turbo"]) //=> { _: [], turbo:false }
```

### Operands

Every non-option argument is an operand. Operands are saved to the `result._` operands array.

```js
getopts(["alpha", "-w9", "bravo"]) //=> { _: ["alpha", "bravo"], w:9 }
```

```js
getopts(["--code=alpha", "bravo"]) //=> { _: ["bravo"], code:"alpha" }
```

Everything after a standalone `--` is an operand.

```js
getopts(["--alpha", "--", "--bravo", "--turbo"]) //=> { _:["--bravo", "--turbo"], alpha:true }
```

A single `-` is also treated as an operand.

```js
getopts(["--turbo", "-"]) //=> { _:["-"], turbo:true }
```

### Other

Options specified as boolean or string will be added to the result object as `false` or `""` (even if missing from the arguments array).

```js
getopts([], {
  string: ["a"],
  boolean: ["b"],
}) //=> { _:[], a:"", b:false }
```

Repeated options are stored as arrays with every value in order of appearance.

```js
getopts(["-x?alpha=bravo", "-x3.14", "-x"] //=> { _:[], a:["?alpha=bravo", 3.14, true] }
```

A value may contain newlines or other control characters.

```js
getopts(["--text=top\n\tbottom"]) //=> { _:[], text:"top\n\tbottom" }
```

`="false"` is converted to boolean by default.

```js
getopts(["--turbo=false"]) //=> { _:[], turbo:false }
```

## API

### `getopts(argv, opts)`

Parse command-line arguments. Returns an object mapping argument names to their values.

### `argv[]`

An array of arguments, usually [`process.argv`](https://nodejs.org/docs/latest/api/process.html#process_process_argv).

### `opts.alias`

An object of option aliases. An alias can be a string or an array of strings. Aliases let you declare substitute names for an option, e.g., the short (abbreviated) and long (canonical) variations.

```js
getopts(["-t"], {
  alias: {
    turbo: ["t", "T"],
  },
}) //=> { _:[], t:true, T:true, turbo:true }
```

### `opts.boolean`

An array of options to parse as boolean. In the example below, `t` is parsed as a boolean, causing the following argument to be treated as an operand.

```js
getopts(["-t", "alpha"], {
  boolean: ["t"],
}) //=> { _:["alpha"], t:true }
```

### `opts.string`

An array of flags to parse as strings. In the example below, `t` is parsed as a string, causing all adjacent characters to be treated as a single value and not as individual options.

```js
getopts(["-atabc"], {
  string: ["t"],
}) //=> { _:[], a:true, t:"abc" }
```

### `opts.default`

An object of default values for options not present in the arguments array.

```js
getopts(["--warp=10"], {
  default: {
    warp: 15,
    turbo: true,
  },
}) //=> { _:[], warp:10, turbo:true }
```

### `opts.unknown()`

We call this function for each unknown option. Return `false` to discard the option. Unknown options are those that appear in the arguments array, but are not in `opts.string`, `opts.boolean`, `opts.default`, or `opts.alias`.

```js
getopts(["-abc"], {
  unknown: (option) => "a" === option,
}) //=> { _:[], a:true }
```

### `opts.stopEarly`

A boolean property. If `true`, the operands array `_` will be populated with all the arguments after the first operand.

```js
getopts(["-w9", "alpha", "--turbo", "bravo"], {
  stopEarly: true,
}) //=> { _:["alpha", "--turbo", "bravo"], w:9 }
```

This property is useful when implementing sub-commands in a CLI.

```js
import getopts from "getopts"
import { install, update, uninstall } from "./commands.js"

const options = getopts(process.argv.slice(2), {
  stopEarly: true,
})

const [command, subargv] = options._

if (command === "install") {
  install(subargv)
} else if (command === "update") {
  update(subargv)
} else if (command === "uninstall") {
  uninstall(subargv)
}
```

## Benchmarks

```console
npm --prefix bench start
```

## License

[MIT](LICENSE.md)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              Copyright © Jorge Bucaran <<https://jorgebucaran.com>>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        {
  "name": "is-extglob",
  "description": "Returns true if a string has an extglob.",
  "version": "2.1.1",
  "homepage": "https://github.com/jonschlinkert/is-extglob",
  "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
  "repository": "jonschlinkert/is-extglob",
  "bugs": {
    "url": "https://github.com/jonschlinkert/is-extglob/issues"
  },
  "license": "MIT",
  "files": [
    "index.js"
  ],
  "main": "index.js",
  "engines": {
    "node": ">=0.10.0"
  },
  "scripts": {
    "test": "mocha"
  },
  "devDependencies": {
    "gulp-format-md": "^0.1.10",
    "mocha": "^3.0.2"
  },
  "keywords": [
    "bash",
    "braces",
    "check",
    "exec",
    "expression",
    "extglob",
    "glob",
    "globbing",
    "globstar",
    "is",
    "match",
    "matches",
    "pattern",
    "regex",
    "regular",
    "string",
    "test"
  ],
  "verb": {
    "toc": false,
    "layout": "default",
    "tasks": [
      "readme"
    ],
    "plugins": [
      "gulp-format-md"
    ],
    "related": {
      "list": [
        "has-glob",
        "is-glob",
        "micromatch"
      ]
    },
    "reflinks": [
      "verb",
      "verb-generate-readme"
    ],
    "lint": {
      "reflinks": true
    }
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            /*!
 * is-extglob <https://github.com/jonschlinkert/is-extglob>
 *
 * Copyright (c) 2014-2016, Jon Schlinkert.
 * Licensed under the MIT License.
 */

module.exports = function isExtglob(str) {
  if (typeof str !== 'string' || str === '') {
    return false;
  }

  var match;
  while ((match = /(\\).|([@?!+*]\(.*\))/g.exec(str))) {
    if (match[2]) return true;
    str = str.slice(match.index + match[0].length);
  }

  return false;
};
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       # is-extglob [![NPM version](https://img.shields.io/npm/v/is-extglob.svg?style=flat)](https://www.npmjs.com/package/is-extglob) [![NPM downloads](https://img.shields.io/npm/dm/is-extglob.svg?style=flat)](https://npmjs.org/package/is-extglob) [![Build Status](https://img.shields.io/travis/jonschlinkert/is-extglob.svg?style=flat)](https://travis-ci.org/jonschlinkert/is-extglob)

> Returns true if a string has an extglob.

## Install

Install with [npm](https://www.npmjs.com/):

```sh
$ npm install --save is-extglob
```

## Usage

```js
var isExtglob = require('is-extglob');
```

**True**

```js
isExtglob('?(abc)');
isExtglob('@(abc)');
isExtglob('!(abc)');
isExtglob('*(abc)');
isExtglob('+(abc)');
```

**False**

Escaped extglobs:

```js
isExtglob('\\?(abc)');
isExtglob('\\@(abc)');
isExtglob('\\!(abc)');
isExtglob('\\*(abc)');
isExtglob('\\+(abc)');
```

Everything else...

```js
isExtglob('foo.js');
isExtglob('!foo.js');
isExtglob('*.js');
isExtglob('**/abc.js');
isExtglob('abc/*.js');
isExtglob('abc/(aaa|bbb).js');
isExtglob('abc/[a-z].js');
isExtglob('abc/{a,b}.js');
isExtglob('abc/?.js');
isExtglob('abc.js');
isExtglob('abc/def/ghi.js');
```

## History

**v2.0**

Adds support for escaping. Escaped exglobs no longer return true.

## About

### Related projects

* [has-glob](https://www.npmjs.com/package/has-glob): Returns `true` if an array has a glob pattern. | [homepage](https://github.com/jonschlinkert/has-glob "Returns `true` if an array has a glob pattern.")
* [is-glob](https://www.npmjs.com/package/is-glob): Returns `true` if the given string looks like a glob pattern or an extglob pattern… [more](https://github.com/jonschlinkert/is-glob) | [homepage](https://github.com/jonschlinkert/is-glob "Returns `true` if the given string looks like a glob pattern or an extglob pattern. This makes it easy to create code that only uses external modules like node-glob when necessary, resulting in much faster code execution and initialization time, and a bet")
* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")

### Contributing

Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).

### Building docs

_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_

To generate the readme and API documentation with [verb](https://github.com/verbose/verb):

```sh
$ npm install -g verb verb-generate-readme && verb
```

### Running tests

Install dev dependencies:

```sh
$ npm install -d && npm test
```

### Author

**Jon Schlinkert**

* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)

### License

Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT license](https://github.com/jonschlinkert/is-extglob/blob/master/LICENSE).

***

_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.31, on October 12, 2016._                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   {
  "name": "once",
  "version": "1.4.0",
  "description": "Run a function exactly one time",
  "main": "once.js",
  "directories": {
    "test": "test"
  },
  "dependencies": {
    "wrappy": "1"
  },
  "devDependencies": {
    "tap": "^7.0.1"
  },
  "scripts": {
    "test": "tap test/*.js"
  },
  "files": [
    "once.js"
  ],
  "repository": {
    "type": "git",
    "url": "git://github.com/isaacs/once"
  },
  "keywords": [
    "once",
    "function",
    "one",
    "single"
  ],
  "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
  "license": "ISC"
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  var wrappy = require('wrappy')
module.exports = wrappy(once)
module.exports.strict = wrappy(onceStrict)

once.proto = once(function () {
  Object.defineProperty(Function.prototype, 'once', {
    value: function () {
      return once(this)
    },
    configurable: true
  })

  Object.defineProperty(Function.prototype, 'onceStrict', {
    value: function () {
      return onceStrict(this)
    },
    configurable: true
  })
})

function once (fn) {
  var f = function () {
    if (f.called) return f.value
    f.called = true
    return f.value = fn.apply(this, arguments)
  }
  f.called = false
  return f
}

function onceStrict (fn) {
  var f = function () {
    if (f.called)
      throw new Error(f.onceError)
    f.called = true
    return f.value = fn.apply(this, arguments)
  }
  var name = fn.name || 'Function wrapped with `once`'
  f.onceError = name + " shouldn't be called more than once"
  f.called = false
  return f
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         # once

Only call a function once.

## usage

```javascript
var once = require('once')

function load (file, cb) {
  cb = once(cb)
  loader.load('file')
  loader.once('load', cb)
  loader.once('error', cb)
}
```

Or add to the Function.prototype in a responsible way:

```javascript
// only has to be done once
require('once').proto()

function load (file, cb) {
  cb = cb.once()
  loader.load('file')
  loader.once('load', cb)
  loader.once('error', cb)
}
```

Ironically, the prototype feature makes this module twice as
complicated as necessary.

To check whether you function has been called, use `fn.called`. Once the
function is called for the first time the return value of the original
function is saved in `fn.value` and subsequent calls will continue to
return this value.

```javascript
var once = require('once')

function load (cb) {
  cb = once(cb)
  var stream = createStream()
  stream.once('data', cb)
  stream.once('end', function () {
    if (!cb.called) cb(new Error('not found'))
  })
}
```

## `once.strict(func)`

Throw an error if the function is called twice.

Some functions are expected to be called only once. Using `once` for them would
potentially hide logical errors.

In the example below, the `greet` function has to call the callback only once:

```javascript
function greet (name, cb) {
  // return is missing from the if statement
  // when no name is passed, the callback is called twice
  if (!name) cb('Hello anonymous')
  cb('Hello ' + name)
}

function log (msg) {
  console.log(msg)
}

// this will print 'Hello anonymous' but the logical error will be missed
greet(null, once(msg))

// once.strict will print 'Hello anonymous' and throw an error when the callback will be called the second time
greet(null, once.strict(msg))
```
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    The ISC License

Copyright (c) Isaac Z. Schlueter and Contributors

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   # wrappy

Callback wrapping utility

## USAGE

```javascript
var wrappy = require("wrappy")

// var wrapper = wrappy(wrapperFunction)

// make sure a cb is called only once
// See also: http://npm.im/once for this specific use case
var once = wrappy(function (cb) {
  var called = false
  return function () {
    if (called) return
    called = true
    return cb.apply(this, arguments)
  }
})

function printBoo () {
  console.log('boo')
}
// has some rando property
printBoo.iAmBooPrinter = true

var onlyPrintOnce = once(printBoo)

onlyPrintOnce() // prints 'boo'
onlyPrintOnce() // does nothing

// random property is retained!
assert.equal(onlyPrintOnce.iAmBooPrinter, true)
```
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   {
  "name": "debug",
  "version": "4.4.0",
  "repository": {
    "type": "git",
    "url": "git://github.com/debug-js/debug.git"
  },
  "description": "Lightweight debugging utility for Node.js and the browser",
  "keywords": [
    "debug",
    "log",
    "debugger"
  ],
  "files": [
    "src",
    "LICENSE",
    "README.md"
  ],
  "author": "Josh Junon (https://github.com/qix-)",
  "contributors": [
    "TJ Holowaychuk <tj@vision-media.ca>",
    "Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
    "Andrew Rhyne <rhyneandrew@gmail.com>"
  ],
  "license": "MIT",
  "scripts": {
    "lint": "xo",
    "test": "npm run test:node && npm run test:browser && npm run lint",
    "test:node": "istanbul cover _mocha -- test.js test.node.js",
    "test:browser": "karma start --single-run",
    "test:coverage": "cat ./coverage/lcov.info | coveralls"
  },
  "dependencies": {
    "ms": "^2.1.3"
  },
  "devDependencies": {
    "brfs": "^2.0.1",
    "browserify": "^16.2.3",
    "coveralls": "^3.0.2",
    "istanbul": "^0.4.5",
    "karma": "^3.1.4",
    "karma-browserify": "^6.0.0",
    "karma-chrome-launcher": "^2.2.0",
    "karma-mocha": "^1.3.0",
    "mocha": "^5.2.0",
    "mocha-lcov-reporter": "^1.2.0",
    "sinon": "^14.0.0",
    "xo": "^0.23.0"
  },
  "peerDependenciesMeta": {
    "supports-color": {
      "optional": true
    }
  },
  "main": "./src/index.js",
  "browser": "./src/browser.js",
  "engines": {
    "node": ">=6.0"
  },
  "xo": {
    "rules": {
      "import/extensions": "off"
    }
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            (The MIT License)

Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2018-2021 Josh Junon

Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the 'Software'), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             # debug
[![OpenCollective](https://opencollective.com/debug/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/debug/sponsors/badge.svg)](#sponsors)

<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">

A tiny JavaScript debugging utility modelled after Node.js core's debugging
technique. Works in Node.js and web browsers.

## Installation

```bash
$ npm install debug
```

## Usage

`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.

Example [_app.js_](./examples/node/app.js):

```js
var debug = require('debug')('http')
  , http = require('http')
  , name = 'My App';

// fake app

debug('booting %o', name);

http.createServer(function(req, res){
  debug(req.method + ' ' + req.url);
  res.end('hello\n');
}).listen(3000, function(){
  debug('listening');
});

// fake worker of some kind

require('./worker');
```

Example [_worker.js_](./examples/node/worker.js):

```js
var a = require('debug')('worker:a')
  , b = require('debug')('worker:b');

function work() {
  a('doing lots of uninteresting work');
  setTimeout(work, Math.random() * 1000);
}

work();

function workb() {
  b('doing some work');
  setTimeout(workb, Math.random() * 2000);
}

workb();
```

The `DEBUG` environment variable is then used to enable these based on space or
comma-delimited names.

Here are some examples:

<img width="647" alt="screen shot 2017-08-08 at 12 53 04 pm" src="https://user-images.githubusercontent.com/71256/29091703-a6302cdc-7c38-11e7-8304-7c0b3bc600cd.png">
<img width="647" alt="screen shot 2017-08-08 at 12 53 38 pm" src="https://user-images.githubusercontent.com/71256/29091700-a62a6888-7c38-11e7-800b-db911291ca2b.png">
<img width="647" alt="screen shot 2017-08-08 at 12 53 25 pm" src="https://user-images.githubusercontent.com/71256/29091701-a62ea114-7c38-11e7-826a-2692bedca740.png">

#### Windows command prompt notes

##### CMD

On Windows the environment variable is set using the `set` command.

```cmd
set DEBUG=*,-not_this
```

Example:

```cmd
set DEBUG=* & node app.js
```

##### PowerShell (VS Code default)

PowerShell uses different syntax to set environment variables.

```cmd
$env:DEBUG = "*,-not_this"
```

Example:

```cmd
$env:DEBUG='app';node app.js
```

Then, run the program to be debugged as usual.

npm script example:
```js
  "windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js",
```

## Namespace Colors

Every debug instance has a color generated for it based on its namespace name.
This helps when visually parsing the debug output to identify which debug instance
a debug line belongs to.

#### Node.js

In Node.js, colors are enabled when stderr is a TTY. You also _should_ install
the [`supports-color`](https://npmjs.org/supports-color) module alongside debug,
otherwise debug will only use a small handful of basic colors.

<img width="521" src="https://user-images.githubusercontent.com/71256/29092181-47f6a9e6-7c3a-11e7-9a14-1928d8a711cd.png">

#### Web Browser

Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, Firefox ([since version
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
and the Firebug plugin for Firefox (any version).

<img width="524" src="https://user-images.githubusercontent.com/71256/29092033-b65f9f2e-7c39-11e7-8e32-f6f0d8e865c1.png">


## Millisecond diff

When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.

<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">

When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:

<img width="647" src="https://user-images.githubusercontent.com/71256/29091956-6bd78372-7c39-11e7-8c55-c948396d6edd.png">


## Conventions

If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser".  If you append a "*" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable.  You can then use it for normal output as well as debug output.

## Wildcards

The `*` character may be used as a wildcard. Suppose for example your library has
debuggers named "connect:bodyParser", "connect:compress", "connect:session",
instead of listing all three with
`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do
`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.

You can also exclude specific debuggers by prefixing them with a "-" character.
For example, `DEBUG=*,-connect:*` would include all debuggers except those
starting with "connect:".

## Environment Variables

When running through Node.js, you can set a few environment variables that will
change the behavior of the debug logging:

| Name      | Purpose                                         |
|-----------|-------------------------------------------------|
| `DEBUG`   | Enables/disables specific debugging namespaces. |
| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY).  |
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
| `DEBUG_DEPTH` | Object inspection depth.                    |
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |


__Note:__ The environment variables beginning with `DEBUG_` end up being
converted into an Options object that gets used with `%o`/`%O` formatters.
See the Node.js documentation for
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
for the complete list.

## Formatters

Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
Below are the officially supported formatters:

| Formatter | Representation |
|-----------|----------------|
| `%O`      | Pretty-print an Object on multiple lines. |
| `%o`      | Pretty-print an Object all on a single line. |
| `%s`      | String. |
| `%d`      | Number (both integer and float). |
| `%j`      | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
| `%%`      | Single percent sign ('%'). This does not consume an argument. |


### Custom formatters

You can add custom formatters by extending the `debug.formatters` object.
For example, if you wanted to add support for rendering a Buffer as hex with
`%h`, you could do something like:

```js
const createDebug = require('debug')
createDebug.formatters.h = (v) => {
  return v.toString('hex')
}

// …elsewhere
const debug = createDebug('foo')
debug('this is hex: %h', new Buffer('hello world'))
//   foo this is hex: 68656c6c6f20776f726c6421 +0ms
```


## Browser Support

You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
if you don't want to build it yourself.

Debug's enable state is currently persisted by `localStorage`.
Consider the situation shown below where you have `worker:a` and `worker:b`,
and wish to debug both. You can enable this using `localStorage.debug`:

```js
localStorage.debug = 'worker:*'
```

And then refresh the page.

```js
a = debug('worker:a');
b = debug('worker:b');

setInterval(function(){
  a('doing some work');
}, 1000);

setInterval(function(){
  b('doing some work');
}, 1200);
```

In Chromium-based web browsers (e.g. Brave, Chrome, and Electron), the JavaScript console will—by default—only show messages logged by `debug` if the "Verbose" log level is _enabled_.

<img width="647" src="https://user-images.githubusercontent.com/7143133/152083257-29034707-c42c-4959-8add-3cee850e6fcf.png">

## Output streams

  By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:

Example [_stdout.js_](./examples/node/stdout.js):

```js
var debug = require('debug');
var error = debug('app:error');

// by default stderr is used
error('goes to stderr!');

var log = debug('app:log');
// set this namespace to log via console.log
log.log = console.log.bind(console); // don't forget to bind to console!
log('goes to stdout');
error('still goes to stderr!');

// set all output to go via console.info
// overrides all per-namespace log settings
debug.log = console.info.bind(console);
error('now goes to stdout via console.info');
log('still goes to stdout, but via console.info now');
```

## Extend
You can simply extend debugger 
```js
const log = require('debug')('auth');

//creates new debug instance with extended namespace
const logSign = log.extend('sign');
const logLogin = log.extend('login');

log('hello'); // auth hello
logSign('hello'); //auth:sign hello
logLogin('hello'); //auth:login hello
```

## Set dynamically

You can also enable debug dynamically by calling the `enable()` method :

```js
let debug = require('debug');

console.log(1, debug.enabled('test'));

debug.enable('test');
console.log(2, debug.enabled('test'));

debug.disable();
console.log(3, debug.enabled('test'));

```

print :   
```
1 false
2 true
3 false
```

Usage :  
`enable(namespaces)`  
`namespaces` can include modes separated by a colon and wildcards.
   
Note that calling `enable()` completely overrides previously set DEBUG variable : 

```
$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"))'
=> false
```

`disable()`

Will disable all namespaces. The functions returns the namespaces currently
enabled (and skipped). This can be useful if you want to disable debugging
temporarily without knowing what was enabled to begin with.

For example:

```js
let debug = require('debug');
debug.enable('foo:*,-foo:bar');
let namespaces = debug.disable();
debug.enable(namespaces);
```

Note: There is no guarantee that the string will be identical to the initial
enable string, but semantically they will be identical.

## Checking whether a debug target is enabled

After you've created a debug instance, you can determine whether or not it is
enabled by checking the `enabled` property:

```javascript
const debug = require('debug')('http');

if (debug.enabled) {
  // do stuff...
}
```

You can also manually toggle this property to force the debug instance to be
enabled or disabled.

## Usage in child processes

Due to the way `debug` detects if the output is a TTY or not, colors are not shown in child processes when `stderr` is piped. A solution is to pass the `DEBUG_COLORS=1` environment variable to the child process.  
For example:

```javascript
worker = fork(WORKER_WRAP_PATH, [workerPath], {
  stdio: [
    /* stdin: */ 0,
    /* stdout: */ 'pipe',
    /* stderr: */ 'pipe',
    'ipc',
  ],
  env: Object.assign({}, process.env, {
    DEBUG_COLORS: 1 // without this settings, colors won't be shown
  }),
});

worker.stderr.pipe(process.stderr, { end: false });
```


## Authors

 - TJ Holowaychuk
 - Nathan Rajlich
 - Andrew Rhyne
 - Josh Junon

## Backers

Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]

<a href="https://opencollective.com/debug/backer/0/website" target="_blank"><img src="https://opencollective.com/debug/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/1/website" target="_blank"><img src="https://opencollective.com/debug/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/2/website" target="_blank"><img src="https://opencollective.com/debug/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/3/website" target="_blank"><img src="https://opencollective.com/debug/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/4/website" target="_blank"><img src="https://opencollective.com/debug/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/5/website" target="_blank"><img src="https://opencollective.com/debug/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/6/website" target="_blank"><img src="https://opencollective.com/debug/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/7/website" target="_blank"><img src="https://opencollective.com/debug/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/8/website" target="_blank"><img src="https://opencollective.com/debug/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/9/website" target="_blank"><img src="https://opencollective.com/debug/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/10/website" target="_blank"><img src="https://opencollective.com/debug/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/11/website" target="_blank"><img src="https://opencollective.com/debug/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/12/website" target="_blank"><img src="https://opencollective.com/debug/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/13/website" target="_blank"><img src="https://opencollective.com/debug/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/14/website" target="_blank"><img src="https://opencollective.com/debug/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/15/website" target="_blank"><img src="https://opencollective.com/debug/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/16/website" target="_blank"><img src="https://opencollective.com/debug/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/17/website" target="_blank"><img src="https://opencollective.com/debug/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/18/website" target="_blank"><img src="https://opencollective.com/debug/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/19/website" target="_blank"><img src="https://opencollective.com/debug/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/20/website" target="_blank"><img src="https://opencollective.com/debug/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/21/website" target="_blank"><img src="https://opencollective.com/debug/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/22/website" target="_blank"><img src="https://opencollective.com/debug/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/23/website" target="_blank"><img src="https://opencollective.com/debug/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/24/website" target="_blank"><img src="https://opencollective.com/debug/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/25/website" target="_blank"><img src="https://opencollective.com/debug/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/26/website" target="_blank"><img src="https://opencollective.com/debug/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/27/website" target="_blank"><img src="https://opencollective.com/debug/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/28/website" target="_blank"><img src="https://opencollective.com/debug/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/29/website" target="_blank"><img src="https://opencollective.com/debug/backer/29/avatar.svg"></a>


## Sponsors

Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]

<a href="https://opencollective.com/debug/sponsor/0/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/1/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/2/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/3/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/4/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/5/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/6/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/7/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/8/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/9/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/10/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/11/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/12/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/13/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/14/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/15/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/16/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/17/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/18/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/19/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/20/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/21/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/22/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/23/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/24/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/25/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/26/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/27/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/28/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/29/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/29/avatar.svg"></a>

## License

(The MIT License)

Copyright (c) 2014-2017 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
Copyright (c) 2018-2021 Josh Junon

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             Copyright (c) 2007-present, Alexandru Mărășteanu <hello@alexei.ro>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of this software nor the names of its contributors may be
  used to endorse or promote products derived from this software without
  specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       Alexander Rose [@arose](https://github.com/arose)
Alexandru Mărășteanu [@alexei](https://github.com/alexei)
Andras [@andrasq](https://github.com/andrasq)
Benoit Giannangeli [@giann](https://github.com/giann)
Branden Visser [@mrvisser](https://github.com/mrvisser)
David Baird
daurnimator [@daurnimator](https://github.com/daurnimator)
Doug Beck [@beck](https://github.com/beck)
Dzmitry Litskalau [@litmit](https://github.com/litmit)
Fred Ludlow [@fredludlow](https://github.com/fredludlow)
Hans Pufal
Henry [@alograg](https://github.com/alograg)
Johnny Shields [@johnnyshields](https://github.com/johnnyshields)
Kamal Abdali
Matt Simerson [@msimerson](https://github.com/msimerson)
Maxime Robert [@marob](https://github.com/marob)
MeriemKhelifi [@MeriemKhelifi](https://github.com/MeriemKhelifi)
Michael Schramm [@wodka](https://github.com/wodka)
Nazar Mokrynskyi [@nazar-pc](https://github.com/nazar-pc)
Oliver Salzburg [@oliversalzburg](https://github.com/oliversalzburg)
Pablo [@ppollono](https://github.com/ppollono)
Rabehaja Stevens [@RABEHAJA-STEVENS](https://github.com/RABEHAJA-STEVENS)
Raphael Pigulla [@pigulla](https://github.com/pigulla)
rebeccapeltz [@rebeccapeltz](https://github.com/rebeccapeltz)
Stefan Tingström [@stingstrom](https://github.com/stingstrom)
Tim Gates [@timgates42](https://github.com/timgates42)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           {
	"name": "type-fest",
	"version": "0.21.3",
	"description": "A collection of essential TypeScript types",
	"license": "(MIT OR CC0-1.0)",
	"repository": "sindresorhus/type-fest",
	"funding": "https://github.com/sponsors/sindresorhus",
	"author": {
		"name": "Sindre Sorhus",
		"email": "sindresorhus@gmail.com",
		"url": "https://sindresorhus.com"
	},
	"engines": {
		"node": ">=10"
	},
	"scripts": {
		"test": "xo && tsd && tsc"
	},
	"files": [
		"index.d.ts",
		"base.d.ts",
		"source",
		"ts41"
	],
	"keywords": [
		"typescript",
		"ts",
		"types",
		"utility",
		"util",
		"utilities",
		"omit",
		"merge",
		"json"
	],
	"devDependencies": {
		"@sindresorhus/tsconfig": "~0.7.0",
		"expect-type": "^0.11.0",
		"tsd": "^0.14.0",
		"typescript": "^4.1.3",
		"xo": "^0.36.1"
	},
	"types": "./index.d.ts",
	"typesVersions": {
		">=4.1": {
			"*": [
				"ts41/*"
			]
		}
	},
	"xo": {
		"rules": {
			"@typescript-eslint/ban-types": "off",
			"@typescript-eslint/indent": "off",
			"node/no-unsupported-features/es-builtins": "off"
		}
	}
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              // These are all the basic types that's compatible with all supported TypeScript versions.
export * from './base';
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             MIT License

Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https:/sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    // Types that are compatible with all supported TypeScript versions.
// It's shared between all TypeScript version-specific definitions.

// Basic
export * from './source/basic';
export * from './source/typed-array';

// Utilities
export {Except} from './source/except';
export {Mutable} from './source/mutable';
export {Merge} from './source/merge';
export {MergeExclusive} from './source/merge-exclusive';
export {RequireAtLeastOne} from './source/require-at-least-one';
export {RequireExactlyOne} from './source/require-exactly-one';
export {PartialDeep} from './source/partial-deep';
export {ReadonlyDeep} from './source/readonly-deep';
export {LiteralUnion} from './source/literal-union';
export {Promisable} from './source/promisable';
export {Opaque} from './source/opaque';
export {SetOptional} from './source/set-optional';
export {SetRequired} from './source/set-required';
export {ValueOf} from './source/value-of';
export {PromiseValue} from './source/promise-value';
export {AsyncReturnType} from './source/async-return-type';
export {ConditionalExcept} from './source/conditional-except';
export {ConditionalKeys} from './source/conditional-keys';
export {ConditionalPick} from './source/conditional-pick';
export {UnionToIntersection} from './source/union-to-intersection';
export {Stringified} from './source/stringified';
export {FixedLengthArray} from './source/fixed-length-array';
export {IterableElement} from './source/iterable-element';
export {Entry} from './source/entry';
export {Entries} from './source/entries';
export {SetReturnType} from './source/set-return-type';
export {Asyncify} from './source/asyncify';

// Miscellaneous
export {PackageJson} from './source/package-json';
export {TsConfigJson} from './source/tsconfig-json';
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              {
  "name": "is-number",
  "description": "Returns true if a number or string value is a finite number. Useful for regex matches, parsing, user input, etc.",
  "version": "7.0.0",
  "homepage": "https://github.com/jonschlinkert/is-number",
  "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
  "contributors": [
    "Jon Schlinkert (http://twitter.com/jonschlinkert)",
    "Olsten Larck (https://i.am.charlike.online)",
    "Rouven Weßling (www.rouvenwessling.de)"
  ],
  "repository": "jonschlinkert/is-number",
  "bugs": {
    "url": "https://github.com/jonschlinkert/is-number/issues"
  },
  "license": "MIT",
  "files": [
    "index.js"
  ],
  "main": "index.js",
  "engines": {
    "node": ">=0.12.0"
  },
  "scripts": {
    "test": "mocha"
  },
  "devDependencies": {
    "ansi": "^0.3.1",
    "benchmark": "^2.1.4",
    "gulp-format-md": "^1.0.0",
    "mocha": "^3.5.3"
  },
  "keywords": [
    "cast",
    "check",
    "coerce",
    "coercion",
    "finite",
    "integer",
    "is",
    "isnan",
    "is-nan",
    "is-num",
    "is-number",
    "isnumber",
    "isfinite",
    "istype",
    "kind",
    "math",
    "nan",
    "num",
    "number",
    "numeric",
    "parseFloat",
    "parseInt",
    "test",
    "type",
    "typeof",
    "value"
  ],
  "verb": {
    "toc": false,
    "layout": "default",
    "tasks": [
      "readme"
    ],
    "related": {
      "list": [
        "is-plain-object",
        "is-primitive",
        "isobject",
        "kind-of"
      ]
    },
    "plugins": [
      "gulp-format-md"
    ],
    "lint": {
      "reflinks": true
    }
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 The MIT License (MIT)

Copyright (c) 2014-present, Jon Schlinkert.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             # is-number [![NPM version](https://img.shields.io/npm/v/is-number.svg?style=flat)](https://www.npmjs.com/package/is-number) [![NPM monthly downloads](https://img.shields.io/npm/dm/is-number.svg?style=flat)](https://npmjs.org/package/is-number) [![NPM total downloads](https://img.shields.io/npm/dt/is-number.svg?style=flat)](https://npmjs.org/package/is-number) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/is-number.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/is-number)

> Returns true if the value is a finite number.

Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.

## Install

Install with [npm](https://www.npmjs.com/):

```sh
$ npm install --save is-number
```

## Why is this needed?

In JavaScript, it's not always as straightforward as it should be to reliably check if a value is a number. It's common for devs to use `+`, `-`, or `Number()` to cast a string value to a number (for example, when values are returned from user input, regex matches, parsers, etc). But there are many non-intuitive edge cases that yield unexpected results:

```js
console.log(+[]); //=> 0
console.log(+''); //=> 0
console.log(+'   '); //=> 0
console.log(typeof NaN); //=> 'number'
```

This library offers a performant way to smooth out edge cases like these.

## Usage

```js
const isNumber = require('is-number');
```

See the [tests](./test.js) for more examples.

### true

```js
isNumber(5e3);               // true
isNumber(0xff);              // true
isNumber(-1.1);              // true
isNumber(0);                 // true
isNumber(1);                 // true
isNumber(1.1);               // true
isNumber(10);                // true
isNumber(10.10);             // true
isNumber(100);               // true
isNumber('-1.1');            // true
isNumber('0');               // true
isNumber('012');             // true
isNumber('0xff');            // true
isNumber('1');               // true
isNumber('1.1');             // true
isNumber('10');              // true
isNumber('10.10');           // true
isNumber('100');             // true
isNumber('5e3');             // true
isNumber(parseInt('012'));   // true
isNumber(parseFloat('012')); // true
```

### False

Everything else is false, as you would expect:

```js
isNumber(Infinity);          // false
isNumber(NaN);               // false
isNumber(null);              // false
isNumber(undefined);         // false
isNumber('');                // false
isNumber('   ');             // false
isNumber('foo');             // false
isNumber([1]);               // false
isNumber([]);                // false
isNumber(function () {});    // false
isNumber({});                // false
```

## Release history

### 7.0.0

* Refactor. Now uses `.isFinite` if it exists.
* Performance is about the same as v6.0 when the value is a string or number. But it's now 3x-4x faster when the value is not a string or number.

### 6.0.0

* Optimizations, thanks to @benaadams.

### 5.0.0

**Breaking changes**

* removed support for `instanceof Number` and `instanceof String`

## Benchmarks

As with all benchmarks, take these with a grain of salt. See the [benchmarks](./benchmark/index.js) for more detail.

```
# all
v7.0 x 413,222 ops/sec ±2.02% (86 runs sampled)
v6.0 x 111,061 ops/sec ±1.29% (85 runs sampled)
parseFloat x 317,596 ops/sec ±1.36% (86 runs sampled)
fastest is 'v7.0'

# string
v7.0 x 3,054,496 ops/sec ±1.05% (89 runs sampled)
v6.0 x 2,957,781 ops/sec ±0.98% (88 runs sampled)
parseFloat x 3,071,060 ops/sec ±1.13% (88 runs sampled)
fastest is 'parseFloat,v7.0'

# number
v7.0 x 3,146,895 ops/sec ±0.89% (89 runs sampled)
v6.0 x 3,214,038 ops/sec ±1.07% (89 runs sampled)
parseFloat x 3,077,588 ops/sec ±1.07% (87 runs sampled)
fastest is 'v6.0'
```

## About

<details>
<summary><strong>Contributing</strong></summary>

Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).

</details>

<details>
<summary><strong>Running Tests</strong></summary>

Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:

```sh
$ npm install && npm test
```

</details>

<details>
<summary><strong>Building docs</strong></summary>

_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_

To generate the readme, run the following command:

```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```

</details>

### Related projects

You might also be interested in these projects:

* [is-plain-object](https://www.npmjs.com/package/is-plain-object): Returns true if an object was created by the `Object` constructor. | [homepage](https://github.com/jonschlinkert/is-plain-object "Returns true if an object was created by the `Object` constructor.")
* [is-primitive](https://www.npmjs.com/package/is-primitive): Returns `true` if the value is a primitive.  | [homepage](https://github.com/jonschlinkert/is-primitive "Returns `true` if the value is a primitive. ")
* [isobject](https://www.npmjs.com/package/isobject): Returns true if the value is an object and not an array or null. | [homepage](https://github.com/jonschlinkert/isobject "Returns true if the value is an object and not an array or null.")
* [kind-of](https://www.npmjs.com/package/kind-of): Get the native type of a value. | [homepage](https://github.com/jonschlinkert/kind-of "Get the native type of a value.")

### Contributors

| **Commits** | **Contributor** | 
| --- | --- |
| 49 | [jonschlinkert](https://github.com/jonschlinkert) |
| 5 | [charlike-old](https://github.com/charlike-old) |
| 1 | [benaadams](https://github.com/benaadams) |
| 1 | [realityking](https://github.com/realityking) |

### Author

**Jon Schlinkert**

* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)

### License

Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).

***

_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on June 15, 2018._                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              'use strict';
const os = require('os');
const tty = require('tty');
const hasFlag = require('has-flag');

const {env} = process;

let forceColor;
if (hasFlag('no-color') ||
	hasFlag('no-colors') ||
	hasFlag('color=false') ||
	hasFlag('color=never')) {
	forceColor = 0;
} else if (hasFlag('color') ||
	hasFlag('colors') ||
	hasFlag('color=true') ||
	hasFlag('color=always')) {
	forceColor = 1;
}

if ('FORCE_COLOR' in env) {
	if (env.FORCE_COLOR === 'true') {
		forceColor = 1;
	} else if (env.FORCE_COLOR === 'false') {
		forceColor = 0;
	} else {
		forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3);
	}
}

function translateLevel(level) {
	if (level === 0) {
		return false;
	}

	return {
		level,
		hasBasic: true,
		has256: level >= 2,
		has16m: level >= 3
	};
}

function supportsColor(haveStream, streamIsTTY) {
	if (forceColor === 0) {
		return 0;
	}

	if (hasFlag('color=16m') ||
		hasFlag('color=full') ||
		hasFlag('color=truecolor')) {
		return 3;
	}

	if (hasFlag('color=256')) {
		return 2;
	}

	if (haveStream && !streamIsTTY && forceColor === undefined) {
		return 0;
	}

	const min = forceColor || 0;

	if (env.TERM === 'dumb') {
		return min;
	}

	if (process.platform === 'win32') {
		// Windows 10 build 10586 is the first Windows release that supports 256 colors.
		// Windows 10 build 14931 is the first release that supports 16m/TrueColor.
		const osRelease = os.release().split('.');
		if (
			Number(osRelease[0]) >= 10 &&
			Number(osRelease[2]) >= 10586
		) {
			return Number(osRelease[2]) >= 14931 ? 3 : 2;
		}

		return 1;
	}

	if ('CI' in env) {
		if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {
			return 1;
		}

		return min;
	}

	if ('TEAMCITY_VERSION' in env) {
		return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
	}

	if (env.COLORTERM === 'truecolor') {
		return 3;
	}

	if ('TERM_PROGRAM' in env) {
		const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);

		switch (env.TERM_PROGRAM) {
			case 'iTerm.app':
				return version >= 3 ? 3 : 2;
			case 'Apple_Terminal':
				return 2;
			// No default
		}
	}

	if (/-256(color)?$/i.test(env.TERM)) {
		return 2;
	}

	if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
		return 1;
	}

	if ('COLORTERM' in env) {
		return 1;
	}

	return min;
}

function getSupportLevel(stream) {
	const level = supportsColor(stream, stream && stream.isTTY);
	return translateLevel(level);
}

module.exports = {
	supportsColor: getSupportLevel,
	stdout: translateLevel(supportsColor(true, tty.isatty(1))),
	stderr: translateLevel(supportsColor(true, tty.isatty(2)))
};
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    # supports-color [![Build Status](https://travis-ci.org/chalk/supports-color.svg?branch=master)](https://travis-ci.org/chalk/supports-color)

> Detect whether a terminal supports color


## Install

```
$ npm install supports-color
```


## Usage

```js
const supportsColor = require('supports-color');

if (supportsColor.stdout) {
	console.log('Terminal stdout supports color');
}

if (supportsColor.stdout.has256) {
	console.log('Terminal stdout supports 256 colors');
}

if (supportsColor.stderr.has16m) {
	console.log('Terminal stderr supports 16 million colors (truecolor)');
}
```


## API

Returns an `Object` with a `stdout` and `stderr` property for testing either streams. Each property is an `Object`, or `false` if color is not supported.

The `stdout`/`stderr` objects specifies a level of support for color through a `.level` property and a corresponding flag:

- `.level = 1` and `.hasBasic = true`: Basic color support (16 colors)
- `.level = 2` and `.has256 = true`: 256 color support
- `.level = 3` and `.has16m = true`: Truecolor support (16 million colors)


## Info

It obeys the `--color` and `--no-color` CLI flags.

For situations where using `--color` is not possible, use the environment variable `FORCE_COLOR=1` (level 1), `FORCE_COLOR=2` (level 2), or `FORCE_COLOR=3` (level 3) to forcefully enable color, or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks.

Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=16m` flags, respectively.


## Related

- [supports-color-cli](https://github.com/chalk/supports-color-cli) - CLI for this module
- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right


## Maintainers

- [Sindre Sorhus](https://github.com/sindresorhus)
- [Josh Junon](https://github.com/qix-)


---

<div align="center">
	<b>
		<a href="https://tidelift.com/subscription/pkg/npm-supports-color?utm_source=npm-supports-color&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
	</b>
	<br>
	<sub>
		Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
	</sub>
</div>

---
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          'use strict';
module.exports = {
	stdout: false,
	stderr: false
};
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             {
  "name": "forwarded",
  "description": "Parse HTTP X-Forwarded-For header",
  "version": "0.2.0",
  "contributors": [
    "Douglas Christopher Wilson <doug@somethingdoug.com>"
  ],
  "license": "MIT",
  "keywords": [
    "x-forwarded-for",
    "http",
    "req"
  ],
  "repository": "jshttp/forwarded",
  "devDependencies": {
    "beautify-benchmark": "0.2.4",
    "benchmark": "2.1.4",
    "deep-equal": "1.0.1",
    "eslint": "7.27.0",
    "eslint-config-standard": "14.1.1",
    "eslint-plugin-import": "2.23.4",
    "eslint-plugin-node": "11.1.0",
    "eslint-plugin-promise": "4.3.1",
    "eslint-plugin-standard": "4.1.0",
    "mocha": "8.4.0",
    "nyc": "15.1.0"
  },
  "files": [
    "LICENSE",
    "HISTORY.md",
    "README.md",
    "index.js"
  ],
  "engines": {
    "node": ">= 0.6"
  },
  "scripts": {
    "bench": "node benchmark/index.js",
    "lint": "eslint .",
    "test": "mocha --reporter spec --bail --check-leaks test/",
    "test-ci": "nyc --reporter=lcov --reporter=text npm test",
    "test-cov": "nyc --reporter=html --reporter=text npm test",
    "version": "node scripts/version-history.js && git add HISTORY.md"
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  (The MIT License)

Copyright (c) 2014-2017 Douglas Christopher Wilson

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          # forwarded

[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Node.js Version][node-version-image]][node-version-url]
[![Build Status][ci-image]][ci-url]
[![Test Coverage][coveralls-image]][coveralls-url]

Parse HTTP X-Forwarded-For header

## Installation

This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/). Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):

```sh
$ npm install forwarded
```

## API

```js
var forwarded = require('forwarded')
```

### forwarded(req)

```js
var addresses = forwarded(req)
```

Parse the `X-Forwarded-For` header from the request. Returns an array
of the addresses, including the socket address for the `req`, in reverse
order (i.e. index `0` is the socket address and the last index is the
furthest address, typically the end-user).

## Testing

```sh
$ npm test
```

## License

[MIT](LICENSE)

[ci-image]: https://badgen.net/github/checks/jshttp/forwarded/master?label=ci
[ci-url]: https://github.com/jshttp/forwarded/actions?query=workflow%3Aci
[npm-image]: https://img.shields.io/npm/v/forwarded.svg
[npm-url]: https://npmjs.org/package/forwarded
[node-version-image]: https://img.shields.io/node/v/forwarded.svg
[node-version-url]: https://nodejs.org/en/download/
[coveralls-image]: https://img.shields.io/coveralls/jshttp/forwarded/master.svg
[coveralls-url]: https://coveralls.io/r/jshttp/forwarded?branch=master
[downloads-image]: https://img.shields.io/npm/dm/forwarded.svg
[downloads-url]: https://npmjs.org/package/forwarded
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          {
  "name": "braces",
  "description": "Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.",
  "version": "3.0.3",
  "homepage": "https://github.com/micromatch/braces",
  "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
  "contributors": [
    "Brian Woodward (https://twitter.com/doowb)",
    "Elan Shanker (https://github.com/es128)",
    "Eugene Sharygin (https://github.com/eush77)",
    "hemanth.hm (http://h3manth.com)",
    "Jon Schlinkert (http://twitter.com/jonschlinkert)"
  ],
  "repository": "micromatch/braces",
  "bugs": {
    "url": "https://github.com/micromatch/braces/issues"
  },
  "license": "MIT",
  "files": [
    "index.js",
    "lib"
  ],
  "main": "index.js",
  "engines": {
    "node": ">=8"
  },
  "scripts": {
    "test": "mocha",
    "benchmark": "node benchmark"
  },
  "dependencies": {
    "fill-range": "^7.1.1"
  },
  "devDependencies": {
    "ansi-colors": "^3.2.4",
    "bash-path": "^2.0.1",
    "gulp-format-md": "^2.0.0",
    "mocha": "^6.1.1"
  },
  "keywords": [
    "alpha",
    "alphabetical",
    "bash",
    "brace",
    "braces",
    "expand",
    "expansion",
    "filepath",
    "fill",
    "fs",
    "glob",
    "globbing",
    "letter",
    "match",
    "matches",
    "matching",
    "number",
    "numerical",
    "path",
    "range",
    "ranges",
    "sh"
  ],
  "verb": {
    "toc": false,
    "layout": "default",
    "tasks": [
      "readme"
    ],
    "lint": {
      "reflinks": true
    },
    "plugins": [
      "gulp-format-md"
    ]
  }
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 'use strict';

const stringify = require('./lib/stringify');
const compile = require('./lib/compile');
const expand = require('./lib/expand');
const parse = require('./lib/parse');

/**
 * Expand the given pattern or create a regex-compatible string.
 *
 * ```js
 * const braces = require('braces');
 * console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)']
 * console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c']
 * ```
 * @param {String} `str`
 * @param {Object} `options`
 * @return {String}
 * @api public
 */

const braces = (input, options = {}) => {
  let output = [];

  if (Array.isArray(input)) {
    for (const pattern of input) {
      const result = braces.create(pattern, options);
      if (Array.isArray(result)) {
        output.push(...result);
      } else {
        output.push(result);
      }
    }
  } else {
    output = [].concat(braces.create(input, options));
  }

  if (options && options.expand === true && options.nodupes === true) {
    output = [...new Set(output)];
  }
  return output;
};

/**
 * Parse the given `str` with the given `options`.
 *
 * ```js
 * // braces.parse(pattern, [, options]);
 * const ast = braces.parse('a/{b,c}/d');
 * console.log(ast);
 * ```
 * @param {String} pattern Brace pattern to parse
 * @param {Object} options
 * @return {Object} Returns an AST
 * @api public
 */

braces.parse = (input, options = {}) => parse(input, options);

/**
 * Creates a braces string from an AST, or an AST node.
 *
 * ```js
 * const braces = require('braces');
 * let ast = braces.parse('foo/{a,b}/bar');
 * console.log(stringify(ast.nodes[2])); //=> '{a,b}'
 * ```
 * @param {String} `input` Brace pattern or AST.
 * @param {Object} `options`
 * @return {Array} Returns an array of expanded values.
 * @api public
 */

braces.stringify = (input, options = {}) => {
  if (typeof input === 'string') {
    return stringify(braces.parse(input, options), options);
  }
  return stringify(input, options);
};

/**
 * Compiles a brace pattern into a regex-compatible, optimized string.
 * This method is called by the main [braces](#braces) function by default.
 *
 * ```js
 * const braces = require('braces');
 * console.log(braces.compile('a/{b,c}/d'));
 * //=> ['a/(b|c)/d']
 * ```
 * @param {String} `input` Brace pattern or AST.
 * @param {Object} `options`
 * @return {Array} Returns an array of expanded values.
 * @api public
 */

braces.compile = (input, options = {}) => {
  if (typeof input === 'string') {
    input = braces.parse(input, options);
  }
  return compile(input, options);
};

/**
 * Expands a brace pattern into an array. This method is called by the
 * main [braces](#braces) function when `options.expand` is true. Before
 * using this method it's recommended that you read the [performance notes](#performance))
 * and advantages of using [.compile](#compile) instead.
 *
 * ```js
 * const braces = require('braces');
 * console.log(braces.expand('a/{b,c}/d'));
 * //=> ['a/b/d', 'a/c/d'];
 * ```
 * @param {String} `pattern` Brace pattern
 * @param {Object} `options`
 * @return {Array} Returns an array of expanded values.
 * @api public
 */

braces.expand = (input, options = {}) => {
  if (typeof input === 'string') {
    input = braces.parse(input, options);
  }

  let result = expand(input, options);

  // filter out empty strings if specified
  if (options.noempty === true) {
    result = result.filter(Boolean);
  }

  // filter out duplicates if specified
  if (options.nodupes === true) {
    result = [...new Set(result)];
  }

  return result;
};

/**
 * Processes a brace pattern and returns either an expanded array
 * (if `options.expand` is true), a highly optimized regex-compatible string.
 * This method is called by the main [braces](#braces) function.
 *
 * ```js
 * const braces = require('braces');
 * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
 * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
 * ```
 * @param {String} `pattern` Brace pattern
 * @param {Object} `options`
 * @return {Array} Returns an array of expanded values.
 * @api public
 */

braces.create = (input, options = {}) => {
  if (input === '' || input.length < 3) {
    return [input];
  }

  return options.expand !== true
    ? braces.compile(input, options)
    : braces.expand(input, options);
};

/**
 * Expose "braces"
 */

module.exports = braces;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    # braces [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces) [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/micromatch/braces.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/braces)

> Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.

Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.

## Install

Install with [npm](https://www.npmjs.com/):

```sh
$ npm install --save braces
```

## v3.0.0 Released!!

See the [changelog](CHANGELOG.md) for details.

## Why use braces?

Brace patterns make globs more powerful by adding the ability to match specific ranges and sequences of characters.

- **Accurate** - complete support for the [Bash 4.3 Brace Expansion](www.gnu.org/software/bash/) specification (passes all of the Bash braces tests)
- **[fast and performant](#benchmarks)** - Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
- **Organized code base** - The parser and compiler are easy to maintain and update when edge cases crop up.
- **Well-tested** - Thousands of test assertions, and passes all of the Bash, minimatch, and [brace-expansion](https://github.com/juliangruber/brace-expansion) unit tests (as of the date this was written).
- **Safer** - You shouldn't have to worry about users defining aggressive or malicious brace patterns that can break your application. Braces takes measures to prevent malicious regex that can be used for DDoS attacks (see [catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html)).
- [Supports lists](#lists) - (aka "sets") `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
- [Supports sequences](#sequences) - (aka "ranges") `{01..03}` => `['01', '02', '03']`
- [Supports steps](#steps) - (aka "increments") `{2..10..2}` => `['2', '4', '6', '8', '10']`
- [Supports escaping](#escaping) - To prevent evaluation of special characters.

## Usage

The main export is a function that takes one or more brace `patterns` and `options`.

```js
const braces = require('braces');
// braces(patterns[, options]);

console.log(braces(['{01..05}', '{a..e}']));
//=> ['(0[1-5])', '([a-e])']

console.log(braces(['{01..05}', '{a..e}'], { expand: true }));
//=> ['01', '02', '03', '04', '05', 'a', 'b', 'c', 'd', 'e']
```

### Brace Expansion vs. Compilation

By default, brace patterns are compiled into strings that are optimized for creating regular expressions and matching.

**Compiled**

```js
console.log(braces('a/{x,y,z}/b'));
//=> ['a/(x|y|z)/b']
console.log(braces(['a/{01..20}/b', 'a/{1..5}/b']));
//=> [ 'a/(0[1-9]|1[0-9]|20)/b', 'a/([1-5])/b' ]
```

**Expanded**

Enable brace expansion by setting the `expand` option to true, or by using [braces.expand()](#expand) (returns an array similar to what you'd expect from Bash, or `echo {1..5}`, or [minimatch](https://github.com/isaacs/minimatch)):

```js
console.log(braces('a/{x,y,z}/b', { expand: true }));
//=> ['a/x/b', 'a/y/b', 'a/z/b']

console.log(braces.expand('{01..10}'));
//=> ['01','02','03','04','05','06','07','08','09','10']
```

### Lists

Expand lists (like Bash "sets"):

```js
console.log(braces('a/{foo,bar,baz}/*.js'));
//=> ['a/(foo|bar|baz)/*.js']

console.log(braces.expand('a/{foo,bar,baz}/*.js'));
//=> ['a/foo/*.js', 'a/bar/*.js', 'a/baz/*.js']
```

### Sequences

Expand ranges of characters (like Bash "sequences"):

```js
console.log(braces.expand('{1..3}')); // ['1', '2', '3']
console.log(braces.expand('a/{1..3}/b')); // ['a/1/b', 'a/2/b', 'a/3/b']
console.log(braces('{a..c}', { expand: true })); // ['a', 'b', 'c']
console.log(braces('foo/{a..c}', { expand: true })); // ['foo/a', 'foo/b', 'foo/c']

// supports zero-padded ranges
console.log(braces('a/{01..03}/b')); //=> ['a/(0[1-3])/b']
console.log(braces('a/{001..300}/b')); //=> ['a/(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)/b']
```

See [fill-range](https://github.com/jonschlinkert/fill-range) for all available range-expansion options.

### Steppped ranges

Steps, or increments, may be used with ranges:

```js
console.log(braces.expand('{2..10..2}'));
//=> ['2', '4', '6', '8', '10']

console.log(braces('{2..10..2}'));
//=> ['(2|4|6|8|10)']
```

When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range](https://github.com/jonschlinkert/to-regex-range) for expansion.

### Nesting

Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved.

**"Expanded" braces**

```js
console.log(braces.expand('a{b,c,/{x,y}}/e'));
//=> ['ab/e', 'ac/e', 'a/x/e', 'a/y/e']

console.log(braces.expand('a/{x,{1..5},y}/c'));
//=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
```

**"Optimized" braces**

```js
console.log(braces('a{b,c,/{x,y}}/e'));
//=> ['a(b|c|/(x|y))/e']

console.log(braces('a/{x,{1..5},y}/c'));
//=> ['a/(x|([1-5])|y)/c']
```

### Escaping

**Escaping braces**

A brace pattern will not be expanded or evaluted if _either the opening or closing brace is escaped_:

```js
console.log(braces.expand('a\\{d,c,b}e'));
//=> ['a{d,c,b}e']

console.log(braces.expand('a{d,c,b\\}e'));
//=> ['a{d,c,b}e']
```

**Escaping commas**

Commas inside braces may also be escaped:

```js
console.log(braces.expand('a{b\\,c}d'));
//=> ['a{b,c}d']

console.log(braces.expand('a{d\\,c,b}e'));
//=> ['ad,ce', 'abe']
```

**Single items**

Following bash conventions, a brace pattern is also not expanded when it contains a single character:

```js
console.log(braces.expand('a{b}c'));
//=> ['a{b}c']
```

## Options

### options.maxLength

**Type**: `Number`

**Default**: `10,000`

**Description**: Limit the length of the input string. Useful when the input string is generated or your application allows users to pass a string, et cetera.

```js
console.log(braces('a/{b,c}/d', { maxLength: 3 })); //=> throws an error
```

### options.expand

**Type**: `Boolean`

**Default**: `undefined`

**Description**: Generate an "expanded" brace pattern (alternatively you can use the `braces.expand()` method, which does the same thing).

```js
console.log(braces('a/{b,c}/d', { expand: true }));
//=> [ 'a/b/d', 'a/c/d' ]
```

### options.nodupes

**Type**: `Boolean`

**Default**: `undefined`

**Description**: Remove duplicates from the returned array.

### options.rangeLimit

**Type**: `Number`

**Default**: `1000`

**Description**: To prevent malicious patterns from being passed by users, an error is thrown when `braces.expand()` is used or `options.expand` is true and the generated range will exceed the `rangeLimit`.

You can customize `options.rangeLimit` or set it to `Inifinity` to disable this altogether.

**Examples**

```js
// pattern exceeds the "rangeLimit", so it's optimized automatically
console.log(braces.expand('{1..1000}'));
//=> ['([1-9]|[1-9][0-9]{1,2}|1000)']

// pattern does not exceed "rangeLimit", so it's NOT optimized
console.log(braces.expand('{1..100}'));
//=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100']
```

### options.transform

**Type**: `Function`

**Default**: `undefined`

**Description**: Customize range expansion.

**Example: Transforming non-numeric values**

```js
const alpha = braces.expand('x/{a..e}/y', {
  transform(value, index) {
    // When non-numeric values are passed, "value" is a character code.
    return 'foo/' + String.fromCharCode(value) + '-' + index;
  },
});
console.log(alpha);
//=> [ 'x/foo/a-0/y', 'x/foo/b-1/y', 'x/foo/c-2/y', 'x/foo/d-3/y', 'x/foo/e-4/y' ]
```

**Example: Transforming numeric values**

```js
const numeric = braces.expand('{1..5}', {
  transform(value) {
    // when numeric values are passed, "value" is a number
    return 'foo/' + value * 2;
  },
});
console.log(numeric);
//=> [ 'foo/2', 'foo/4', 'foo/6', 'foo/8', 'foo/10' ]
```

### options.quantifiers

**Type**: `Boolean`

**Default**: `undefined`

**Description**: In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.

Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)

The `quantifiers` option tells braces to detect when [regex quantifiers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers) are defined in the given pattern, and not to try to expand them as lists.

**Examples**

```js
const braces = require('braces');
console.log(braces('a/b{1,3}/{x,y,z}'));
//=> [ 'a/b(1|3)/(x|y|z)' ]
console.log(braces('a/b{1,3}/{x,y,z}', { quantifiers: true }));
//=> [ 'a/b{1,3}/(x|y|z)' ]
console.log(braces('a/b{1,3}/{x,y,z}', { quantifiers: true, expand: true }));
//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
```

### options.keepEscaping

**Type**: `Boolean`

**Default**: `undefined`

**Description**: Do not strip backslashes that were used for escaping from the result.

## What is "brace expansion"?

Brace expansion is a type of parameter expansion that was made popular by unix shells for generating lists of strings, as well as regex-like matching when used alongside wildcards (globs).

In addition to "expansion", braces are also used for matching. In other words:

- [brace expansion](#brace-expansion) is for generating new lists
- [brace matching](#brace-matching) is for filtering existing lists

<details>
<summary><strong>More about brace expansion</strong> (click to expand)</summary>

There are two main types of brace expansion:

1. **lists**: which are defined using comma-separated values inside curly braces: `{a,b,c}`
2. **sequences**: which are defined using a starting value and an ending value, separated by two dots: `a{1..3}b`. Optionally, a third argument may be passed to define a "step" or increment to use: `a{1..100..10}b`. These are also sometimes referred to as "ranges".

Here are some example brace patterns to illustrate how they work:

**Sets**

```
{a,b,c}       => a b c
{a,b,c}{1,2}  => a1 a2 b1 b2 c1 c2
```

**Sequences**

```
{1..9}        => 1 2 3 4 5 6 7 8 9
{4..-4}       => 4 3 2 1 0 -1 -2 -3 -4
{1..20..3}    => 1 4 7 10 13 16 19
{a..j}        => a b c d e f g h i j
{j..a}        => j i h g f e d c b a
{a..z..3}     => a d g j m p s v y
```

**Combination**

Sets and sequences can be mixed together or used along with any other strings.

```
{a,b,c}{1..3}   => a1 a2 a3 b1 b2 b3 c1 c2 c3
foo/{a,b,c}/bar => foo/a/bar foo/b/bar foo/c/bar
```

The fact that braces can be "expanded" from relatively simple patterns makes them ideal for quickly generating test fixtures, file paths, and similar use cases.

## Brace matching

In addition to _expansion_, brace patterns are also useful for performing regular-expression-like matching.

For example, the pattern `foo/{1..3}/bar` would match any of following strings:

```
foo/1/bar
foo/2/bar
foo/3/bar
```

But not:

```
baz/1/qux
baz/2/qux
baz/3/qux
```

Braces can also be combined with [glob patterns](https://github.com/jonschlinkert/micromatch) to perform more advanced wildcard matching. For example, the pattern `*/{1..3}/*` would match any of following strings:

```
foo/1/bar
foo/2/bar
foo/3/bar
baz/1/qux
baz/2/qux
baz/3/qux
```

## Brace matching pitfalls

Although brace patterns offer a user-friendly way of matching ranges or sets of strings, there are also some major disadvantages and potential risks you should be aware of.

### tldr

**"brace bombs"**

- brace expansion can eat up a huge amount of processing resources
- as brace patterns increase _linearly in size_, the system resources required to expand the pattern increase exponentially
- users can accidentally (or intentially) exhaust your system's resources resulting in the equivalent of a DoS attack (bonus: no programming knowledge is required!)

For a more detailed explanation with examples, see the [geometric complexity](#geometric-complexity) section.

### The solution

Jump to the [performance section](#performance) to see how Braces solves this problem in comparison to other libraries.

### Geometric complexity

At minimum, brace patterns with sets limited to two elements have quadradic or `O(n^2)` complexity. But the complexity of the algorithm increases exponentially as the number of sets, _and elements per set_, increases, which is `O(n^c)`.

For example, the following sets demonstrate quadratic (`O(n^2)`) complexity:

```
{1,2}{3,4}      => (2X2)    => 13 14 23 24
{1,2}{3,4}{5,6} => (2X2X2)  => 135 136 145 146 235 236 245 246
```

But add an element to a set, and we get a n-fold Cartesian product with `O(n^c)` complexity:

```
{1,2,3}{4,5,6}{7,8,9} => (3X3X3) => 147 148 149 157 158 159 167 168 169 247 248
                                    249 257 258 259 267 268 269 347 348 349 357
                                    358 359 367 368 369
```

Now, imagine how this complexity grows given that each element is a n-tuple:

```
{1..100}{1..100}         => (100X100)     => 10,000 elements (38.4 kB)
{1..100}{1..100}{1..100} => (100X100X100) => 1,000,000 elements (5.76 MB)
```

Although these examples are clearly contrived, they demonstrate how brace patterns can quickly grow out of control.

**More information**

Interested in learning more about brace expansion?

- [linuxjournal/bash-brace-expansion](http://www.linuxjournal.com/content/bash-brace-expansion)
- [rosettacode/Brace_expansion](https://rosettacode.org/wiki/Brace_expansion)
- [cartesian product](https://en.wikipedia.org/wiki/Cartesian_product)

</details>

## Performance

Braces is not only screaming fast, it's also more accurate the other brace expansion libraries.

### Better algorithms

Fortunately there is a solution to the ["brace bomb" problem](#brace-matching-pitfalls): _don't expand brace patterns into an array when they're used for matching_.

Instead, convert the pattern into an optimized regular expression. This is easier said than done, and braces is the only library that does this currently.

**The proof is in the numbers**

Minimatch gets exponentially slower as patterns increase in complexity, braces does not. The following results were generated using `braces()` and `minimatch.braceExpand()`, respectively.

| **Pattern**                 | **braces**          | **[minimatch][]**            |
| --------------------------- | ------------------- | ---------------------------- |
| `{1..9007199254740991}`[^1] | `298 B` (5ms 459μs) | N/A (freezes)                |
| `{1..1000000000000000}`     | `41 B` (1ms 15μs)   | N/A (freezes)                |
| `{1..100000000000000}`      | `40 B` (890μs)      | N/A (freezes)                |
| `{1..10000000000000}`       | `39 B` (2ms 49μs)   | N/A (freezes)                |
| `{1..1000000000000}`        | `38 B` (608μs)      | N/A (freezes)                |
| `{1..100000000000}`         | `37 B` (397μs)      | N/A (freezes)                |
| `{1..10000000000}`          | `35 B` (983μs)      | N/A (freezes)                |
| `{1..1000000000}`           | `34 B` (798μs)      | N/A (freezes)                |
| `{1..100000000}`            | `33 B` (733μs)      | N/A (freezes)                |
| `{1..10000000}`             | `32 B` (5ms 632μs)  | `78.89 MB` (16s 388ms 569μs) |
| `{1..1000000}`              | `31 B` (1ms 381μs)  | `6.89 MB` (1s 496ms 887μs)   |
| `{1..100000}`               | `30 B` (950μs)      | `588.89 kB` (146ms 921μs)    |
| `{1..10000}`                | `29 B` (1ms 114μs)  | `48.89 kB` (14ms 187μs)      |
| `{1..1000}`                 | `28 B` (760μs)      | `3.89 kB` (1ms 453μs)        |
| `{1..100}`                  | `22 B` (345μs)      | `291 B` (196μs)              |
| `{1..10}`                   | `10 B` (533μs)      | `20 B` (37μs)                |
| `{1..3}`                    | `7 B` (190μs)       | `5 B` (27μs)                 |

### Faster algorithms

When you need expansion, braces is still much faster.

_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`, respectively)_

| **Pattern**     | **braces**                  | **[minimatch][]**            |
| --------------- | --------------------------- | ---------------------------- |
| `{1..10000000}` | `78.89 MB` (2s 698ms 642μs) | `78.89 MB` (18s 601ms 974μs) |
| `{1..1000000}`  | `6.89 MB` (458ms 576μs)     | `6.89 MB` (1s 491ms 621μs)   |
| `{1..100000}`   | `588.89 kB` (20ms 728μs)    | `588.89 kB` (156ms 919μs)    |
| `{1..10000}`    | `48.89 kB` (2ms 202μs)      | `48.89 kB` (13ms 641μs)      |
| `{1..1000}`     | `3.89 kB` (1ms 796μs)       | `3.89 kB` (1ms 958μs)        |
| `{1..100}`      | `291 B` (424μs)             | `291 B` (211μs)              |
| `{1..10}`       | `20 B` (487μs)              | `20 B` (72μs)                |
| `{1..3}`        | `5 B` (166μs)               | `5 B` (27μs)                 |

If you'd like to run these comparisons yourself, see [test/support/generate.js](test/support/generate.js).

## Benchmarks

### Running benchmarks

Install dev dependencies:

```bash
npm i -d && npm benchmark
```

### Latest results

Braces is more accurate, without sacrificing performance.

```bash
● expand - range (expanded)
     braces x 53,167 ops/sec ±0.12% (102 runs sampled)
  minimatch x 11,378 ops/sec ±0.10% (102 runs sampled)
● expand - range (optimized for regex)
     braces x 373,442 ops/sec ±0.04% (100 runs sampled)
  minimatch x 3,262 ops/sec ±0.18% (100 runs sampled)
● expand - nested ranges (expanded)
     braces x 33,921 ops/sec ±0.09% (99 runs sampled)
  minimatch x 10,855 ops/sec ±0.28% (100 runs sampled)
● expand - nested ranges (optimized for regex)
     braces x 287,479 ops/sec ±0.52% (98 runs sampled)
  minimatch x 3,219 ops/sec ±0.28% (101 runs sampled)
● expand - set (expanded)
     braces x 238,243 ops/sec ±0.19% (97 runs sampled)
  minimatch x 538,268 ops/sec ±0.31% (96 runs sampled)
● expand - set (optimized for regex)
     braces x 321,844 ops/sec ±0.10% (97 runs sampled)
  minimatch x 140,600 ops/sec ±0.15% (100 runs sampled)
● expand - nested sets (expanded)
     braces x 165,371 ops/sec ±0.42% (96 runs sampled)
  minimatch x 337,720 ops/sec ±0.28% (100 runs sampled)
● expand - nested sets (optimized for regex)
     braces x 242,948 ops/sec ±0.12% (99 runs sampled)
  minimatch x 87,403 ops/sec ±0.79% (96 runs sampled)
```

## About

<details>
<summary><strong>Contributing</strong></summary>

Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).

</details>

<details>
<summary><strong>Running Tests</strong></summary>

Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:

```sh
$ npm install && npm test
```

</details>

<details>
<summary><strong>Building docs</strong></summary>

_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_

To generate the readme, run the following command:

```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```

</details>

### Contributors

| **Commits** | **Contributor**                                               |
| ----------- | ------------------------------------------------------------- |
| 197         | [jonschlinkert](https://github.com/jonschlinkert)             |
| 4           | [doowb](https://github.com/doowb)                             |
| 1           | [es128](https://github.com/es128)                             |
| 1           | [eush77](https://github.com/eush77)                           |
| 1           | [hemanth](https://github.com/hemanth)                         |
| 1           | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |

### Author

**Jon Schlinkert**

- [GitHub Profile](https://github.com/jonschlinkert)
- [Twitter Profile](https://twitter.com/jonschlinkert)
- [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)

### License

Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).

---

_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on April 08, 2019._
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               'use strict';
const os = require('os');
const path = require('path');

const homeDirectory = os.homedir();
const {env} = process;

exports.data = env.XDG_DATA_HOME ||
	(homeDirectory ? path.join(homeDirectory, '.local', 'share') : undefined);

exports.config = env.XDG_CONFIG_HOME ||
	(homeDirectory ? path.join(homeDirectory, '.config') : undefined);

exports.cache = env.XDG_CACHE_HOME || (homeDirectory ? path.join(homeDirectory, '.cache') : undefined);

exports.runtime = env.XDG_RUNTIME_DIR || undefined;

exports.dataDirs = (env.XDG_DATA_DIRS || '/usr/local/share/:/usr/share/').split(':');

if (exports.data) {
	exports.dataDirs.unshift(exports.data);
}

exports.configDirs = (env.XDG_CONFIG_DIRS || '/etc/xdg').split(':');

if (exports.config) {
	exports.configDirs.unshift(exports.config);
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               declare const xdgBasedir: {
	/**
	Directory for user-specific data files.

	@example
	```js
	import xdgBasedir = require('xdg-basedir');

	xdgBasedir.data;
	//=> '/home/sindresorhus/.local/share'
	```
	*/
	readonly data?: string;

	/**
	Directory for user-specific configuration files.

	@example
	```js
	import xdgBasedir = require('xdg-basedir');

	xdgBasedir.config;
	//=> '/home/sindresorhus/.config'
	```
	*/
	readonly config?: string;

	/**
	Directory for user-specific non-essential data files.

	@example
	```js
	import xdgBasedir = require('xdg-basedir');

	xdgBasedir.cache;
	//=> '/home/sindresorhus/.cache'
	```
	*/
	readonly cache?: string;

	/**
	Directory for user-specific non-essential runtime files and other file objects (such as sockets, named pipes, etc).

	@example
	```js
	import xdgBasedir = require('xdg-basedir');

	xdgBasedir.runtime;
	//=> '/run/user/sindresorhus'
	```
	*/
	readonly runtime?: string;

	/**
	Preference-ordered array of base directories to search for data files in addition to `.data`.

	@example
	```js
	import xdgBasedir = require('xdg-basedir');

	xdgBasedir.dataDirs
	//=> ['/home/sindresorhus/.local/share', '/usr/local/share/', '/usr/share/']
	```
	*/
	readonly dataDirs: readonly string[];

	/**
	Preference-ordered array of base directories to search for configuration files in addition to `.config`.

	@example
	```js
	import xdgBasedir = require('xdg-basedir');

	xdgBasedir.configDirs;
	//=> ['/home/sindresorhus/.config', '/etc/xdg']
	```
	*/
	readonly configDirs: readonly string[];
};

export = xdgBasedir;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  MIT License

Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           "use strict";

const defer = () => {
  let resolve, reject;
  const promise = new Promise((resolve_, reject_) => {
    resolve = resolve_;
    reject = reject_;
  });
  return {
    promise,
    reject,
    resolve
  };
};

module.exports = defer;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         "use strict";

const promisify = require("./promisify");

const _require = require("./_utils"),
      forIn = _require.forIn;

const DEFAULT_MAPPER = (_, name) => !(name.endsWith("Sync") || name.endsWith("Async")) && name;

const promisifyAll = (obj, {
  mapper = DEFAULT_MAPPER,
  target = {},
  context = obj
} = {}) => {
  forIn(obj, (value, name) => {
    let newName;

    if (typeof value === "function" && (newName = mapper(value, name, obj))) {
      target[newName] = promisify(value, context);
    }
  });
  return target;
};

module.exports = promisifyAll;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         "use strict";

const isProgrammerError = require("./_isProgrammerError");

module.exports = function matchError(predicate, error) {
  if (predicate === undefined) {
    return !isProgrammerError(error);
  }

  const type = typeof predicate;

  if (type === "boolean") {
    return predicate;
  }

  if (type === "function") {
    return predicate === Error || predicate.prototype instanceof Error ? error instanceof predicate : predicate(error);
  }

  if (Array.isArray(predicate)) {
    const n = predicate.length;

    for (let i = 0; i < n; ++i) {
      if (matchError(predicate[i], error)) {
        return true;
      }
    }

    return false;
  }

  if (error != null && type === "object") {
    for (const key in predicate) {
      if (hasOwnProperty.call(predicate, key) && error[key] !== predicate[key]) {
        return false;
      }
    }

    return true;
  }
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   "use strict";

module.exports = value => value;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 "use strict";

module.exports = require("./makeAsyncIterator")(require("./_utils").forEach);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "use strict";

const evalDisposable = require("./_evalDisposable");

const isDisposable = require("./_isDisposable");

const pFinally = require("./_finally");

const setFunctionNameAndLength = require("./_setFunctionNameAndLength");

const wrapApply = require("./wrapApply");

const wrapCall = require("./wrapCall");

class Disposable {
  constructor(dispose, value) {
    if (typeof dispose !== "function") {
      throw new Error("dispose must be a function");
    }

    this._dispose = dispose;
    this._value = value;
  }

  get value() {
    if (this._dispose === undefined) {
      throw new TypeError("cannot get value of already disposed disposable");
    }

    return this._value;
  }

  dispose() {
    if (this._dispose === undefined) {
      throw new TypeError("cannot dispose already disposed disposable");
    }

    const d = this._dispose;
    this._dispose = this._value = undefined;
    return d();
  }

}

module.exports = Disposable;

Disposable.all = function all(iterable) {
  let disposables = [];

  const dispose = () => {
    const d = disposables;
    disposables = undefined;
    d.forEach(disposable => disposable.dispose());
  };

  const onFulfill = maybeDisposable => {
    if (disposables === undefined) {
      return isDisposable(maybeDisposable) && maybeDisposable.dispose();
    }

    if (isDisposable(maybeDisposable)) {
      disposables.push(maybeDisposable);
      return maybeDisposable.value;
    }

    return maybeDisposable;
  };

  const onReject = error => {
    if (disposables === undefined) {
      return;
    }

    dispose();
    throw error;
  };

  return Promise.all(Array.from(iterable, maybeDisposable => evalDisposable(maybeDisposable).then(onFulfill, onReject))).then(values => new Disposable(dispose, values));
};

const ExitStack = require("./_ExitStack");

Disposable.factory = genFn => setFunctionNameAndLength(function () {
  const gen = genFn.apply(this, arguments);

  const _ExitStack = new ExitStack(),
        dispose = _ExitStack.dispose,
        stack = _ExitStack.value;

  const onEvalDisposable = value => isDisposable(value) ? loop(stack.enter(value)) : value;

  const onFulfill = ({
    value
  }) => evalDisposable(value).then(onEvalDisposable);

  const loop = value => wrapCall(gen.next, value, gen).then(onFulfill);

  return loop().then(value => new Disposable(() => wrapCall(gen.return, undefined, gen).then(dispose), value), error => {
    const forwardError = () => {
      throw error;
    };

    return dispose().then(forwardError, forwardError);
  });
}, genFn.name, genFn.length);

const onHandlerFulfill = result => {
  const _ExitStack2 = new ExitStack(),
        dispose = _ExitStack2.dispose,
        stack = _ExitStack2.value;

  const onEvalDisposable = disposable => loop(stack.enter(disposable));

  const onFulfill = cursor => cursor.done ? cursor.value : evalDisposable(cursor.value).then(onEvalDisposable);

  const loop = value => wrapCall(result.next, value, result).then(onFulfill);

  return pFinally(loop(), dispose);
};

Disposable.use = function use() {
  let nDisposables = arguments.length - 1;

  if (nDisposables < 0) {
    throw new TypeError("Disposable.use expects at least 1 argument");
  }

  const handler = arguments[nDisposables];

  if (nDisposables === 0) {
    return new Promise(resolve => resolve(handler.call(this))).then(onHandlerFulfill);
  }

  let disposables;
  const spread = !Array.isArray(disposables = arguments[0]);

  if (spread) {
    disposables = Array.prototype.slice.call(arguments, 0, nDisposables);
  } else {
    nDisposables = disposables.length;
  }

  return Disposable.all(disposables).then(dAll => pFinally((spread ? wrapApply : wrapCall)(handler, dAll.value, this), () => dAll.dispose()));
};

Disposable.wrap = function wrap(generator) {
  return setFunctionNameAndLength(function () {
    return Disposable.use(() => generator.apply(this, arguments));
  }, generator.name, generator.length);
};                                                                                                                                              "use strict";

const matchError = require("./_matchError");

function handler(predicates, cb, reason) {
  return matchError(predicates, reason) ? cb(reason) : this;
}

module.exports = function pCatch() {
  let n = arguments.length;
  let cb;

  if (n === 0 || typeof (cb = arguments[--n]) !== "function") {
    return this;
  }

  return this.then(undefined, handler.bind(this, n === 0 ? undefined : n === 1 ? arguments[0] : Array.prototype.slice.call(arguments, 0, n), cb));
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 "use strict";

module.exports = Function.prototype;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             "use strict";

module.exports = function tapCatch(cb) {
  return this.then(undefined, cb).then(() => this);
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  "use strict";

const resolve = require("./_resolve");

const wrapApply = (fn, args, thisArg) => {
  try {
    return resolve(fn.apply(thisArg, args));
  } catch (error) {
    return Promise.reject(error);
  }
};

module.exports = wrapApply;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "use strict";

const pFinally = (p, cb) => p.then(cb, cb).then(() => p);

module.exports = pFinally;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "use strict";

const identity = require("./_identity");

const isPromise = require("./isPromise");

const toPromise = require("./_resolve");

const noop = Function.prototype;

function step(key, value) {
  let cursor;

  try {
    cursor = this._iterator[key](value);
  } catch (error) {
    this.finally();
    return this._reject(error);
  }

  value = cursor.value;

  if (cursor.done) {
    this.finally();

    this._resolve(value);
  } else {
    this.toPromise(value).then(this.next, this._throw);
  }
}

function AsyncFn(iterator, resolve, reject) {
  this._iterator = iterator;
  this._reject = reject;
  this._resolve = resolve;
  this._throw = step.bind(this, "throw");
  this.next = step.bind(this, "next");
}

AsyncFn.prototype.finally = noop;
AsyncFn.prototype.toPromise = toPromise;

const asyncFn = generator => function () {
  return new Promise((resolve, reject) => new AsyncFn(generator.apply(this, arguments), resolve, reject).next());
};

function CancelabledAsyncFn(cancelToken) {
  AsyncFn.apply(this, [].slice.call(arguments, 1));
  this._cancelToken = cancelToken;
  this._onCancel = noop;
  this.finally = cancelToken.addHandler(reason => {
    this._onCancel(reason);

    return new Promise(resolve => {
      this.finally = resolve;
    });
  });
}

Object.setPrototypeOf(CancelabledAsyncFn.prototype, Object.getPrototypeOf(AsyncFn.prototype)).toPromise = function (value) {
  if (Array.isArray(value)) {
    return toPromise(value[0]);
  }

  const cancelToken = this._cancelToken;

  if (cancelToken.requested) {
    return Promise.reject(cancelToken.reason);
  }

  return isPromise(value) ? new Promise((resolve, reject) => {
    value.then(resolve, reject);
    this._onCancel = reject;
  }) : Promise.resolve(value);
};

asyncFn.cancelable = (generator, getCancelToken = identity) => function () {
  const cancelToken = getCancelToken.apply(this, arguments);

  if (cancelToken.requested) {
    return Promise.reject(cancelToken.reason);
  }

  return new Promise((resolve, reject) => {
    new CancelabledAsyncFn(cancelToken, generator.apply(this, arguments), resolve, reject).next();
  });
};

module.exports = asyncFn;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "use strict";

const isDisposable = require("./_isDisposable");

const resolve = require("./_resolve");

module.exports = class ExitStack {
  constructor() {
    this._disposables = [];

    const dispose = () => {
      const disposable = this._disposables.pop();

      return disposable !== undefined ? resolve(disposable.dispose()).then(dispose) : Promise.resolve();
    };

    return {
      dispose,
      value: this
    };
  }

  enter(disposable) {
    if (!isDisposable(disposable)) {
      throw new TypeError("not a disposable");
    }

    this._disposables.push(disposable);

    return disposable.value;
  }

};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             "use strict";

module.exports = class Cancel {
  constructor(message = "this action has been canceled") {
    Object.defineProperty(this, "message", {
      enumerable: true,
      value: message
    });
  }

  toString() {
    return `Cancel: ${this.message}`;
  }

};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   # promise-toolbox

[![Package Version](https://badgen.net/npm/v/promise-toolbox)](https://npmjs.org/package/promise-toolbox) [![Build Status](https://travis-ci.org/JsCommunity/promise-toolbox.png?branch=master)](https://travis-ci.org/JsCommunity/promise-toolbox) [![PackagePhobia](https://badgen.net/packagephobia/install/promise-toolbox)](https://packagephobia.now.sh/result?p=promise-toolbox) [![Latest Commit](https://badgen.net/github/last-commit/JsCommunity/promise-toolbox)](https://github.com/JsCommunity/promise-toolbox/commits/master)

> Essential utils for promises.

**Features:**

- compatible with all promise implementations
- small (< 150 KB with all dependencies, < 5 KB with gzip)
- nice with ES2015 / ES2016 syntax

**Table of contents:**

- [Cancelation](#cancelation)
  - [Creation](#creation)
  - [Consumption](#consumption)
  - [Registering async handlers](#registering-async-handlers)
  - [Is cancel token?](#is-cancel-token)
  - [Compatibility with AbortSignal](#compatibility-with-abortsignal)
  - [@cancelable decorator](#cancelable-decorator)
- [Resource management](#resource-management)
  - [Creation](#creation-1)
  - [Combination](#combination)
  - [Consumption](#consumption-1)
- [Functions](#functions)
  - [asyncFn(generator)](#asyncfngenerator)
  - [asyncFn.cancelable(generator, [getCancelToken])](#asyncfncancelablegenerator-getcanceltoken)
  - [defer()](#defer)
  - [fromCallback(fn, arg1, ..., argn)](#fromcallbackfn-arg1--argn)
  - [fromEvent(emitter, event, [options]) => Promise](#fromeventemitter-event-options--promise)
  - [fromEvents(emitter, successEvents, errorEvents) => Promise](#fromeventsemitter-successevents-errorevents--promise)
  - [isPromise(value)](#ispromisevalue)
  - [nodeify(fn)](#nodeifyfn)
  - [pipe(fns)](#pipefns)
  - [pipe(value, ...fns)](#pipevalue-fns)
  - [promisify(fn, [ context ]) / promisifyAll(obj)](#promisifyfn--context---promisifyallobj)
  - [retry(fn, options, [args])](#retryfn-options-args)
  - [try(fn)](#tryfn)
  - [wrapApply(fn, args, [thisArg]) / wrapCall(fn, arg, [thisArg])](#wrapapplyfn-args-thisarg--wrapcallfn-arg-thisarg)
- [Pseudo-methods](#pseudo-methods)
  - [promise::asCallback(cb)](#promiseascallbackcb)
  - [promise::catch(predicate, cb)](#promisecatchpredicate-cb)
  - [promise::delay(ms, [value])](#promisedelayms-value)
  - [collection::forEach(cb)](#collectionforeachcb)
  - [promise::ignoreErrors()](#promiseignoreerrors)
  - [promise::finally(cb)](#promisefinallycb)
  - [promise::reflect()](#promisereflect)
  - [promises::some(count)](#promisessomecount)
  - [promise::suppressUnhandledRejections()](#promisesuppressunhandledrejections)
  - [promise::tap(onResolved, onRejected)](#promisetaponresolved-onrejected)
  - [promise::tapCatch(onRejected)](#promisetapcatchonrejected)
  - [promise::timeout(ms, [cb])](#promisetimeoutms-cb-or-rejectionvalue)

### Node & [Browserify](http://browserify.org/)/[Webpack](https://webpack.js.org/)

Installation of the [npm package](https://npmjs.org/package/promise-toolbox):

```
> npm install --save promise-toolbox
```

### Browser

You can directly use the build provided at [unpkg.com](https://unpkg.com):

```html
<script src="https://unpkg.com/promise-toolbox@0.8/dist/umd.js"></script>
```

## Usage

### Promise support

If your environment may not natively support promises, you should use a polyfill such as [native-promise-only](https://github.com/getify/native-promise-only).

On Node, if you want to use a specific promise implementation,
[Bluebird](http://bluebirdjs.com/docs/why-bluebird.html) for instance
to have better performance, you can override the global Promise
variable:

```js
global.Promise = require("bluebird");
```

> Note that it should only be done at the application level, never in
> a library!

### Imports

You can either import all the tools directly:

```js
import * as PT from "promise-toolbox";

console.log(PT.isPromise(value));
```

Or import individual tools from the main module:

```js
import { isPromise } from "promise-toolbox";

console.log(isPromise(value));
```

Each tool is also exported with a `p` prefix to work around reserved keywords
and to help differentiate with other tools (like `lodash.map`):

```js
import { pCatch, pMap } from "promise-toolbox";
```

If you are bundling your application (Browserify, Rollup, Webpack, etc.), you
can cherry-pick the tools directly:

```js
import isPromise from "promise-toolbox/isPromise";
import pCatch from "promise-toolbox/catch";
```

## API

### Cancelation

This library provides an implementation of `CancelToken` from the
[cancelable promises specification](https://tc39.github.io/proposal-cancelable-promises/).

A cancel token is an object which can be passed to asynchronous
functions to represent cancelation state.

```js
import { CancelToken } from "promise-toolbox";
```

#### Creation

A cancel token is created by the initiator of the async work and its
cancelation state may be requested at any time.

```js
// Create a token which requests cancelation when a button is clicked.
const token = new CancelToken((cancel) => {
  $("#some-button").on("click", () => cancel("button clicked"));
});
```

```js
const { cancel, token } = CancelToken.source();
```

A list of existing tokens can be passed to `source()` to make the created token
follow their cancelation:

```js
// `source.token` will be canceled (synchronously) as soon as `token1` or
// `token2` or token3` is, with the same reason.
const { cancel, token } = CancelToken.source([token1, token2, token3]);
```

#### Consumption

The receiver of the token (the function doing the async work) can:

1. synchronously check whether cancelation has been requested
2. synchronously throw if cancelation has been requested
3. register a callback that will be executed if cancelation is requested
4. pass the token to subtasks

```js
// 1.
if (token.reason) {
  console.log("cancelation has been requested", token.reason.message);
}

// 2.
try {
  token.throwIfRequested();
} catch (reason) {
  console.log("cancelation has been requested", reason.message);
}

// 3.
token.promise.then((reason) => {
  console.log("cancelation has been requested", reason.message);
});

// 4.
subtask(token);
```

See [`asyncFn.cancelable`](#asyncfncancelablegenerator) for an easy way to create async functions with built-in cancelation support.

#### Registering async handlers

> Asynchronous handlers are executed on token cancelation and the
> promise returned by the `cancel` function will wait for all handlers
> to settle.

```js
function httpRequest(cancelToken, opts) {
  const req = http.request(opts);
  req.end();
  cancelToken.addHandler(() => {
    req.abort();

    // waits for the socket to really close for the cancelation to be
    // complete
    return fromEvent(req, "close");
  });
  return fromEvent(req, "response");
}

const { cancel, token } = CancelToken.source();

httpRequest(token, {
  hostname: "example.org",
}).then((response) => {
  // do something with the response of the request
});

// wraps with Promise.resolve() because cancel only returns a promise
// if a handler has returned a promise
Promise.resolve(cancel()).then(() => {
  // the request has been properly canceled
});
```

#### Is cancel token?

```js
if (CancelToken.isCancelToken(value)) {
  console.log("value is a cancel token");
}
```

#### @cancelable decorator

> **This is deprecated, instead explicitely pass a cancel token or an abort signal:**

```js
const asyncFunction = async (a, b, { cancelToken = CancelToken.none } = {}) => {
  cancelToken.promise.then(() => {
    // do stuff regarding the cancelation request.
  });

  // do other stuff.
};
```

> Make your async functions cancelable.

If the first argument passed to the cancelable function is not a
cancel token, a new one is created and injected and the returned
promise will have a `cancel()` method.

```js
import { cancelable, CancelToken } from "promise-toolbox";

const asyncFunction = cancelable(async ($cancelToken, a, b) => {
  $cancelToken.promise.then(() => {
    // do stuff regarding the cancelation request.
  });

  // do other stuff.
});

// Either a cancel token is passed:
const source = CancelToken.source();
const promise1 = asyncFunction(source.token, "foo", "bar");
source.cancel("reason");

// Or the returned promise will have a cancel() method:
const promise2 = asyncFunction("foo", "bar");
promise2.cancel("reason");
```

If the function is a method of a class or an object, you can use
`cancelable` as a decorator:

```js
class MyClass {
  @cancelable
  async asyncMethod($cancelToken, a, b) {
    // ...
  }
}
```

#### Compatibility with AbortSignal

A cancel token can be created from an abort signal:

```js
const token = CancelToken.from(abortSignal);
```

> If `abortSignal` is already a `CancelToken`, it will be returned directly, making it a breeze to create code accepting both :-)

A cancel token is API compatible with an abort signal and can be used as such:

```js
const { cancel, token } = CancelToken.source();

await fetch(url, { signal: token });
```

### Resource management

> See [Bluebird documentation](http://bluebirdjs.com/docs/api/resource-management.html) for a good explanation.

#### Creation

A disposable is a simple object, which contains a dispose method and possibily a value:

```js
const disposable = { dispose: () => db.close(), value: db };
```

The dispose method may be asynchronous and return a promise.

As a convenience, you can use the `Disposable` class:

```js
import { Disposable } from "promise-toolbox";

const disposable = new Disposable(() => db.close(), db);
```

If the process is more complicated, maybe because this disposable depends on
other disposables, you can use a generator function alongside the
`Disposable.factory` decorator:

```js
const getTable = Disposable.factory(async function* () {
  // simply yield a disposable to use it
  const db = yield getDb();

  const table = await db.getTable();
  try {
    // yield the value to expose it
    yield table;
  } finally {
    // this is where you can dispose of the resource
    await table.close();
  }
});
```

#### Combination

Independent disposables can be acquired and disposed in parallel, to achieve
this, you can use `Disposable.all`:

```js
const combined = await Disposable.all([disposable1, disposable2]);
```

Similarly to `Promise.all`, the value of such a disposable, is an array whose
values are the values of the disposables combined.

#### Consumption

To ensure all resources are properly disposed of, disposables must never be
used manually, but via the `Disposable.use` function:

```js
import { Disposable } from "promise-toolbox";

await Disposable.use(
  // Don't await the promise here, resource acquisition should be handled by
  // `Disposable.use` otherwise, in case of failure, other resources may failed
  // to be disposed of.
  getTable(),

  // If the function can throw synchronously, a wrapper function can be passed
  // directly to `Disposable.use`.
  () => getTable(),

  async (table1, table2) => {
    // do something with table1 and table 2
    //
    // both `table1` and `table2` are guaranteed to be deallocated by the time
    // the promise returned by `Disposable.use` is settled
  }
);
```

For more complex use cases, just like `Disposable.factory`, the handler can be
a generator function when no disposables are passed:

```js
await Disposable.use(async function* () {
  const table1 = yield getTable();
  const table2 = yield getTable();

  // do something with table1 and table 2
  //
  // both `table1` and `table2` are guaranteed to be deallocated by the time
  // the promise returned by `Disposable.use` is settled
});
```

To enable an entire function to use disposables, you can use `Disposable.wrap`:

```js
// context (`this`) and all arguments are forwarded from the call
const disposableUser = Disposable.wrap(async function* (arg1, arg2) {
  const table = yield getDisposable(arg1, arg2);

  // do something with table
});

// similar to
function disposableUser(arg1, arg2) {
  return Disposable.use(async function* (arg1, arg2) {
    const table = yield getDisposable(arg1, arg2);

    // do something with table
  });
}
```

### Functions

#### asyncFn(generator)

> Create an [async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) from [a generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)
>
> Similar to [`Bluebird.coroutine`](http://bluebirdjs.com/docs/api/promise.coroutine.html).

```js
import { asyncFn } from 'promise-toolbox'

const getUserName = asyncFn(function * (db, userId)) {
  const user = yield db.getRecord(userId)
  return user.name
})
```

#### asyncFn.cancelable(generator, [getCancelToken])

> Like [`asyncFn(generator)`](#asyncfngenerator) but the created async function supports [cancelation](#cancelation).
>
> Similar to [CAF](https://github.com/getify/CAF).

```js
import { asyncFn, CancelToken } from 'promise-toolbox'

const getUserName = asyncFn.cancelable(function * (cancelToken, db, userId)) {
  // this yield will throw if the cancelToken is activated
  const user = yield db.getRecord(userId)
  return user.name
})

const source = CancelToken.source()

getUserName(source.token, db, userId).then(
  name => {
    console.log('user name is', name)
  },
  error => {
    console.error(error)
  }
)

// only wait 5 seconds to fetch the user from the database
setTimeout(source.cancel, 5e3)
```

```js
const cancelableAsyncFunction = asyncFn.cancelable(function* (
  cancelToken,
  ...args
) {
  // await otherAsyncFunction() but will throw if cancelToken is activated
  yield otherAsyncFunction();

  // if aborting on cancelation is unwanted (eg because the called function
  // already handles cancelation), wrap the promise in an array
  yield [otherAsyncFunction(cancelToken)];

  // cancelation, just like any rejection, can be catch
  try {
    yield otherAsyncFunction();
  } catch (error) {
    if (CancelToken.isCancelToken(error)) {
      // do some clean-up here
      // the rest of the function has been added as an async handler of the
      // CancelToken which will make `cancel` waits for it
    }

    throw error;
  }

  return result;
});
```

If the cancel token is not the first param of the decorated function, a getter should be passed to `asyncFn.cancelable`, it's called with the same context and arguments as the decorated function and returns the cancel token:

```js
const cancelableAsyncFunction = asyncFn.cancelable(
  function*(arg1, arg2, options) {
    // function logic
  },
  (_arg1, _arg2, { cancelToken = CancelToken.none } = {}) => cancelToken;
);
```

#### defer()

> Discouraged but sometimes necessary way to create a promise.

```js
import { defer } from "promise-toolbox";

const { promise, resolve } = defer();

promise.then((value) => {
  console.log(value);
});

resolve(3);
```

#### fromCallback(fn, arg1, ..., argn)

> Easiest and most efficient way to promisify a function call.

```js
import { fromCallback } from "promise-toolbox";

// callback is appended to the list of arguments passed to the function
fromCallback(fs.readFile, "foo.txt").then((content) => {
  console.log(content);
});

// if the callback does not go at the end, you can wrap the call
fromCallback((cb) => foo("bar", cb, "baz")).then(() => {
  // ...
});

// you can use `.call` to specify the context of execution
fromCallback.call(thisArg, fn, ...args).then(() => {
  // ...
});

// finally, if you want to call a method, you can pass its name instead of a
// function
fromCallback.call(object, "method", ...args).then(() => {
  // ...
});
```

#### fromEvent(emitter, event, [options]) => Promise

> Wait for one event. The first parameter of the emitted event is used
> to resolve/reject the promise.

```js
const promise = fromEvent(emitter, "foo", {
  // whether the promise resolves to an array of all the event args
  // instead of simply the first arg
  array: false,

  // whether the error event can reject the promise
  ignoreErrors: false,

  // name of the error event
  error: "error",
});

promise.then(
  (value) => {
    console.log("foo event was emitted with value", value);
  },
  (reason) => {
    console.error("an error has been emitted", reason);
  }
);
```

#### fromEvents(emitter, successEvents, errorEvents) => Promise

> Wait for one of multiple events. The array of all the parameters of
> the emitted event is used to resolve/reject the promise.
>
> The array also has an `event` property indicating which event has
> been emitted.

```js
fromEvents(emitter, ["foo", "bar"], ["error1", "error2"]).then(
  (event) => {
    console.log(
      "event %s have been emitted with values",
      event.name,
      event.args
    );
  },
  (reasons) => {
    console.error(
      "error event %s has been emitted with errors",
      event.names,
      event.args
    );
  }
);
```

#### isPromise(value)

```js
import { isPromise } from "promise-toolbox";

if (isPromise(foo())) {
  console.log("foo() returns a promise");
}
```

#### nodeify(fn)

> From async functions return promises, create new ones taking node-style
> callbacks.

```js
import { nodeify } = require('promise-toolbox')

const writable = new Writable({
  write: nodeify(async function (chunk, encoding) {
    // ...
  })
})
```

#### pipe(fns)

> Create a new function from the composition of async functions.

```js
import { pipe } from "promise-toolbox";

const getUserPreferences = pipe(getUser, getPreferences);
```

#### pipe(value, ...fns)

> Makes value flow through a list of async functions.

```js
import { pipe } from "promise-toolbox";

const output = await pipe(
  input, // plain value or promise
  transform1, // sync or async function
  transform2,
  transform3
);
```

#### promisify(fn, [ context ]) / promisifyAll(obj)

> From async functions taking node-style callbacks, create new ones
> returning promises.

```js
import fs from "fs";
import { promisify, promisifyAll } from "promise-toolbox";

// Promisify a single function.
//
// If possible, the function name is kept and the new length is set.
const readFile = promisify(fs.readFile);

// Or all functions (own or inherited) exposed on a object.
const fsPromise = promisifyAll(fs);

readFile(__filename).then((content) => console.log(content));

fsPromise.readFile(__filename).then((content) => console.log(content));
```

#### retry(fn, options, [args])

> Retries an async function when it fails.

```js
import { retry } from "promise-toolbox";

(async () => {
  await retry(
    async () => {
      const response = await fetch("https://pokeapi.co/api/v2/pokemon/3/");

      if (response.status === 500) {
        // no need to retry in this case
        throw retry.bail(new Error(response.statusText));
      }

      if (response.status !== 200) {
        throw new Error(response.statusText);
      }

      return response.json();
    },
    {
      // predicate when to retry, default on always but programmer errors
      // (ReferenceError, SyntaxError and TypeError)
      //
      // similar to `promise-toolbox/catch`, it can be a constructor, an object,
      // a function, or an array of the previous
      when: { message: "my error message" },

      // this function is called before a retry is scheduled (before the delay)
      async onRetry(error) {
        console.warn("attempt", this.attemptNumber, "failed with error", error);
        console.warn("next try in", this.delay, "milliseconds");

        // Other information available:
        // - this.fn: function that failed
        // - this.arguments: arguments passed to fn
        // - this.this: context passed to fn

        // This function can throw to prevent any retry.

        // The retry delay will start only after this function has finished.
      },

      // delay before a retry, default to 1000 ms
      delay: 2000,

      // number of tries including the first one, default to 10
      //
      // cannot be used with `retries`
      tries: 3,

      // number of retries (excluding the initial run), default to undefined
      //
      // cannot be used with `tries`
      retries: 4,

      // instead of passing `delay`, `tries` and `retries`, you can pass an
      // iterable of delays to use to retry
      //
      // in this example, it will retry 3 times, first after 1 second, then
      // after 2 seconds and one last time after 4 seconds
      //
      // for more advanced uses, see https://github.com/JsCommunity/iterable-backoff
      delays: [1e3, 2e3, 4e3],
    }
  );
})().catch(console.error.bind(console));
```

The most efficient way to make a function automatically retry is to wrap it:

```js
MyClass.prototype.myMethod = retry.wrap(MyClass.prototype.myMethod, {
  delay: 1e3,
  retries: 10,
  when: MyError,
});
```

In that case `options` can also be a function which will be used to compute the options from the context and the arguments:

```js
MyClass.prototype.myMethod = retry.wrap(
  MyClass.prototype.myMethod,
  function getOptions(arg1, arg2) {
    return this._computeRetryOptions(arg1, arg2);
  }
);
```

#### try(fn)

> Starts a chain of promises.

```js
import PromiseToolbox from "promise-toolbox";

const getUserById = (id) =>
  PromiseToolbox.try(() => {
    if (typeof id !== "number") {
      throw new Error("id must be a number");
    }
    return db.getUserById(id);
  });
```

> Note: similar to `Promise.resolve().then(fn)` but calls `fn()`
> synchronously.

#### wrapApply(fn, args, [thisArg]) / wrapCall(fn, arg, [thisArg])

> Wrap a call to a function to always return a promise.

```js
function getUserById(id) {
  if (typeof id !== "number") {
    throw new TypeError("id must be a number");
  }
  return db.getUser(id);
}

wrapCall(getUserById, "foo").catch((error) => {
  // id must be a number
});
```

### Pseudo-methods

This function can be used as if they were methods, i.e. by passing the
promise (or promises) as the context.

This is extremely easy using [ES2016's bind syntax](https://github.com/zenparsing/es-function-bind).

```js
const promises = [Promise.resolve("foo"), Promise.resolve("bar")];

promises::all().then((values) => {
  console.log(values);
});
// → [ 'foo', 'bar' ]
```

If you are still an older version of ECMAScript, fear not: simply pass
the promise (or promises) as the first argument of the `.call()`
method:

```js
const promises = [Promise.resolve("foo"), Promise.resolve("bar")];

all.call(promises).then(function (values) {
  console.log(values);
});
// → [ 'foo', 'bar' ]
```

#### promise::asCallback(cb)

> Register a node-style callback on this promise.

```js
import { asCallback } from "promise-toolbox";

// This function can be used either with node-style callbacks or with
// promises.
function getDataFor(input, callback) {
  return dataFromDataBase(input)::asCallback(callback);
}
```

#### promise::catch(predicate, cb)

> Similar to `Promise#catch()` but:
>
> - support predicates
> - do not catch `ReferenceError`, `SyntaxError` or `TypeError` unless
>   they match a predicate because they are usually programmer errors
>   and should be handled separately.

```js
somePromise
  .then(() => {
    return a.b.c.d();
  })
  ::pCatch(TypeError, ReferenceError, (reason) => {
    // Will end up here on programmer error
  })
  ::pCatch(NetworkError, TimeoutError, (reason) => {
    // Will end up here on expected everyday network errors
  })
  ::pCatch((reason) => {
    // Catch any unexpected errors
  });
```

#### promise::delay(ms, [value])

> Delays the resolution of a promise by `ms` milliseconds.
>
> Note: the rejection is not delayed.

```js
console.log(await Promise.resolve("500ms passed")::delay(500));
// → 500 ms passed
```

Also works with a value:

```js
console.log(await delay(500, "500ms passed"));
// → 500 ms passed
```

Like `setTimeout` in Node, it is possible to
[`unref`](https://nodejs.org/dist/latest-v11.x/docs/api/timers.html#timers_timeout_unref)
the timer:

```js
await delay(500).unref();
```

#### collection::forEach(cb)

> Iterates in order over a collection, or promise of collection, which
> contains a mix of promises and values, waiting for each call of cb
> to be resolved before the next one.

The returned promise will resolve to `undefined` when the iteration is
complete.

```js
["foo", Promise.resolve("bar")]::forEach((value) => {
  console.log(value);

  // Wait for the promise to be resolve before the next item.
  return new Promise((resolve) => setTimeout(resolve, 10));
});
// →
// foo
// bar
```

#### promise::ignoreErrors()

> Ignore (operational) errors for this promise.

```js
import { ignoreErrors } from "promise-toolbox";

// will not emit an unhandled rejection error if the file does not
// exist
readFileAsync("foo.txt")
  .then((content) => {
    console.log(content);
  })
  ::ignoreErrors();

// will emit an unhandled rejection error due to the typo
readFileAsync("foo.txt")
  .then((content) => {
    console.lgo(content); // typo
  })
  ::ignoreErrors();
```

#### promise::finally(cb)

> Execute a handler regardless of the promise fate. Similar to the
> `finally` block in synchronous codes.
>
> The resolution value or rejection reason of the initial promise is
> forwarded unless the callback rejects.

```js
import { pFinally } from "promise-toolbox";

function ajaxGetAsync(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.addEventListener("error", reject);
    xhr.addEventListener("load", resolve);
    xhr.open("GET", url);
    xhr.send(null);
  })::pFinally(() => {
    $("#ajax-loader-animation").hide();
  });
}
```

#### promise::reflect()

> Returns a promise which resolves to an objects which reflects the
> resolution of this promise.

```js
import { reflect } from "promise-toolbox";

const inspection = await promise::reflect();

if (inspection.isFulfilled()) {
  console.log(inspection.value());
} else {
  console.error(inspection.reason());
}
```

#### promises::some(count)

> Waits for `count` promises in a collection to be resolved.

```js
import { some } from "promise-toolbox";

const [first, seconds] = await [
  ping("ns1.example.org"),
  ping("ns2.example.org"),
  ping("ns3.example.org"),
  ping("ns4.example.org"),
]::some(2);
```

#### promise::suppressUnhandledRejections()

> Suppress unhandled rejections, needed when error handlers are attached
> asynchronously after the promise has rejected.
>
> Similar to [`Bluebird#suppressUnhandledRejections()`](http://bluebirdjs.com/docs/api/suppressunhandledrejections.html).

```js
const promise = getUser()::suppressUnhandledRejections();
$(document).on("ready", () => {
  promise.catch((error) => {
    console.error("error while getting user", error);
  });
});
```

#### promise::tap(onResolved, onRejected)

> Like `.then()` but the original resolution/rejection is forwarded.
>
> Like `::finally()`, if the callback rejects, it takes over the
> original resolution/rejection.

```js
import { tap } from "promise-toolbox";

// Contrary to .then(), using ::tap() does not change the resolution
// value.
const promise1 = Promise.resolve(42)::tap((value) => {
  console.log(value);
});

// Like .then, the second param is used in case of rejection.
const promise2 = Promise.reject(42)::tap(null, (reason) => {
  console.error(reason);
});
```

#### promise::tapCatch(onRejected)

> Alias to [`promise:tap(null, onRejected)`](#promisetaponresolved-onrejected).

#### promise::timeout(ms, [cb or rejectionValue])

> Call a callback if the promise is still pending after `ms`
> milliseconds. Its resolution/rejection is forwarded.
>
> If the callback is omitted, the returned promise is rejected with a
> `TimeoutError`.

```js
import { timeout, TimeoutError } from "promise-toolbox";

await doLongOperation()::timeout(100, () => {
  return doFallbackOperation();
});

await doLongOperation()::timeout(100);

await doLongOperation()::timeout(
  100,
  new Error("the long operation has failed")
);
```

> Note: `0` is a special value which disable the timeout, useful if the delay is
> configurable in your app.

## Development

```
# Install dependencies
> npm install

# Run the tests
> npm test

# Continuously compile
> npm run dev

# Continuously run the tests
> npm run dev-test

# Build for production
> npm run build
```

## Contributions

Contributions are _very_ welcomed, either on the documentation or on
the code.

You may:

- report any [issue](https://github.com/JsCommunity/promise-toolbox/issues)
  you've encountered;
- fork and create a pull request.

## License

ISC © [Julien Fontanet](https://github.com/julien-f)
     "use strict";

function resolver(fn, args, resolve, reject) {
  args.push((error, result) => error != null && error !== false ? reject(error) : resolve(result));
  fn.apply(this, args);
}

module.exports = function fromCallback(fn, ...args) {
  return new Promise(resolver.bind(this, typeof fn === "function" ? fn : this[fn], args));
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "use strict";

const isPromise = value => value != null && typeof value.then === "function";

module.exports = isPromise;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       The ISC License

Copyright (c) Isaac Z. Schlueter and Contributors

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   degenerator
===========
### Compiles sync functions into async functions

Sometimes you need to write sync looking code that's really async under the hood.
This module takes a String to one or more synchronous JavaScript functions, and
returns a new String that with those JS functions transpiled into `async`
functions.

So this:

```js
function foo() {
  return a('bar') || b();
}
```

Gets compiled into:

```js
async function foo() {
    return await a('bar') || await b();
}
```

With the compiled output code, you can evaluate the code using the `vm` module
in Node.js, or save the code to a file and require it, or whatever.

Example
-------

You must explicitly specify the names of the functions that should be
"asyncified". So say we wanted to expose a `get(url)` function that did
and HTTP request and returned the response body.

The user has provided us with this implementation:

``` js
function myFn() {
  const one = get('https://google.com');
  const two = get('http://nodejs.org');
  const three = JSON.parse(get('http://jsonip.org'));
  return [one, two, three];
}
```

Now we can compile this into an asyncronous function, implement the
async `get()` function, and finally evaluate it into a real JavaScript function
instance with the `vm` module:


```typescript
import vm from 'vm';
import { degenerator } from 'degenerator';

// The `get()` function is Promise-based (error handling omitted for brevity)
function get(endpoint: string) {
  return new Promise((resolve, reject) => {
    var mod = 0 == endpoint.indexOf('https:') ? require('https') : require('http');
    var req = mod.get(endpoint);
    req.on('response', function (res) {
      var data = '';
      res.setEncoding('utf8');
      res.on('data', function (b) { data += b; });
      res.on('end', function () {
        resolve(data);
      });
    });
  });
}

// Convert the JavaScript string provided from the user (assumed to be `str` var)
str = degenerator(str, [ 'get' ]);

// Turn the JS String into a real async function instance
const asyncFn = vm.runInNewContext(`(${str})`, { get });

// Now we can invoke the function asynchronously
asyncFn().then((res) => {
  // Do something with `res`...
});
```


API
---

### degenerator(code: string, names: Array<string|RegExp>): String

Returns a "degeneratorified" JavaScript string, with `async`/`await` transplanted.


License
-------

(The MIT License)

Copyright (c) 2013 Nathan Rajlich &lt;nathan@tootallnate.net&gt;

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  "use strict";

const resolve = require("./_resolve");

const _require = require("./_utils"),
      forEach = _require.forEach;

const _some = (promises, count) => new Promise((resolve, reject) => {
  let values = [];
  let errors = [];

  const onFulfillment = value => {
    if (!values) {
      return;
    }

    values.push(value);

    if (--count === 0) {
      resolve(values);
      values = errors = undefined;
    }
  };

  let acceptableErrors = -count;

  const onRejection = reason => {
    if (!values) {
      return;
    }

    errors.push(reason);

    if (--acceptableErrors === 0) {
      reject(errors);
      values = errors = undefined;
    }
  };

  forEach(promises, promise => {
    ++acceptableErrors;
    resolve(promise).then(onFulfillment, onRejection);
  });
});

module.exports = function some(count) {
  return resolve(this).then(promises => _some(promises, count));
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           "use strict";

module.exports = function asCallback(cb) {
  if (typeof cb === "function") {
    this.then(value => cb(undefined, value), cb);
  }

  return this;
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "use strict";

module.exports = require("./makeAsyncIterator")(require("./_utils").forIterable);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "use strict";

module.exports = v => v != null && typeof v.dispose === "function";                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              "use strict";

const noop = require("./_noop");

const _require = require("./_utils"),
      makeAsyncIterator = _require.makeAsyncIterator;

const makeAsyncIteratorWrapper = iterator => {
  const asyncIterator = makeAsyncIterator(iterator);
  return function asyncIteratorWrapper(iteratee) {
    return asyncIterator(this, iteratee).then(noop);
  };
};

module.exports = makeAsyncIteratorWrapper;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   "use strict";

const setFunctionNameAndLength = require("./_setFunctionNameAndLength");

const promisify = (fn, context) => setFunctionNameAndLength(function () {
  const length = arguments.length;
  const args = new Array(length + 1);

  for (let i = 0; i < length; ++i) {
    args[i] = arguments[i];
  }

  return new Promise((resolve, reject) => {
    args[length] = (error, result) => error != null && error !== false ? reject(error) : resolve(result);

    fn.apply(context === undefined ? this : context, args);
  });
}, fn.name, fn.length - 1);

module.exports = promisify;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "use strict";

module.exports = require("./makeAsyncIterator")(require("./_utils").forIn);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      "use strict";

const FN_FALSE = () => false;

const FN_TRUE = () => true;

const onFulfilled = (__proto__ => _value => ({
  __proto__: __proto__,
  value: () => _value
}))({
  isFulfilled: FN_TRUE,
  isPending: FN_FALSE,
  isRejected: FN_FALSE,
  reason: () => {
    throw new Error("no reason, the promise has resolved");
  }
});

const onRejected = (__proto__ => _reason => ({
  __proto__: __proto__,
  reason: () => _reason
}))({
  isFulfilled: FN_FALSE,
  isPending: FN_FALSE,
  isRejected: FN_TRUE,
  value: () => {
    throw new Error("no value, the promise has rejected");
  }
});

module.exports = function () {
  return this.then(onFulfilled, onRejected);
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     "use strict";

const setFunctionNameAndLength = require("./_setFunctionNameAndLength");

const _require = require("./CancelToken"),
      isCancelToken = _require.isCancelToken,
      source = _require.source;

const cancelable = (target, name, descriptor) => {
  const fn = descriptor !== undefined ? descriptor.value : target;
  const wrapper = setFunctionNameAndLength(function cancelableWrapper() {
    const length = arguments.length;

    if (length !== 0 && isCancelToken(arguments[0])) {
      return fn.apply(this, arguments);
    }

    const _source = source(),
          cancel = _source.cancel,
          token = _source.token;

    const args = new Array(length + 1);
    args[0] = token;

    for (let i = 0; i < length; ++i) {
      args[i + 1] = arguments[i];
    }

    const promise = fn.apply(this, args);
    promise.cancel = cancel;
    return promise;
  }, fn.name, fn.length - 1);

  if (descriptor !== undefined) {
    descriptor.value = wrapper;
    return descriptor;
  }

  return wrapper;
};

module.exports = cancelable;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      "use strict";

const isProgrammerError = require("./_isProgrammerError");

const cb = error => {
  if (isProgrammerError(error)) {
    throw error;
  }
};

module.exports = function ignoreErrors() {
  return this.then(undefined, cb);
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "use strict";

const noop = require("./_noop");

module.exports = function suppressUnhandledRejections() {
  const native = this.suppressUnhandledRejections;

  if (typeof native === "function") {
    native.call(this);
  } else {
    this.then(undefined, noop);
  }

  return this;
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   "use strict";

const resolve = require("./_resolve");

const wrapCall = (fn, arg, thisArg) => {
  try {
    return resolve(fn.call(thisArg, arg));
  } catch (error) {
    return Promise.reject(error);
  }
};

module.exports = wrapCall;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     "use strict";

const resolve = require("./_resolve");

module.exports = function pTry(fn) {
  try {
    return resolve(fn());
  } catch (error) {
    return Promise.reject(error);
  }
};                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      "use strict";

module.exports = reason => reason instanceof ReferenceError || reason instanceof SyntaxError || reason instanceof TypeError;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     "use strict";

const isPromise = require("./isPromise");

module.exports = value => isPromise(value) ? value : Promise.resolve(value);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          "use strict";

module.exports = require("./makeAsyncIterator")(require("./_utils").forArray);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   