text: var childProcess = require("child_process");
  var escalade = require("escalade/sync");
  var pico = require("picocolors");
  var path = require("path");
  var fs = require("fs");
  
  var BrowserslistError = require("./error");
  
  function detectLockfile() {
    var packageDir = escalade(".", function (dir, names) {
      return names.indexOf("package.json") !== -1 ? dir : "";
    });
  
    if (!packageDir) {
      throw new BrowserslistError(
        "Cannot find package.json. " +
          "Is this the right directory to run `npx browserslist --update-db` in?",
      );
    }
  
    var lockfileNpm = path.join(packageDir, "package-lock.json");
    var lockfileShrinkwrap = path.join(packageDir, "npm-shrinkwrap.json");
    var lockfileYarn = path.join(packageDir, "yarn.lock");
    var lockfilePnpm = path.join(packageDir, "pnpm-lock.yaml");
  
    if (fs.existsSync(lockfilePnpm)) {
      return { mode: "pnpm", file: lockfilePnpm };
    } else if (fs.existsSync(lockfileNpm)) {
      return { mode: "npm", file: lockfileNpm };
    } else if (fs.existsSync(lockfileYarn)) {
      var lock = { mode: "yarn", file: lockfileYarn };
      lock.content = fs.readFileSync(lock.file).toString();
      lock.version = /# yarn lockfile v1/.test(lock.content) ? 1 : 2;
      return lock;
    } else if (fs.existsSync(lockfileShrinkwrap)) {
      return { mode: "npm", file: lockfileShrinkwrap };
    }
    throw new BrowserslistError(
      'No lockfile found. Run "npm install", "yarn install" or "pnpm install"',
    );
  }
  
  function getLatestInfo(lock) {
    if (lock.mode === "yarn") {
      if (lock.version === 1) {
        return JSON.parse(
          childProcess.execSync("yarn info caniuse-lite --json").toString(),
        ).data;
      } else {
        return JSON.parse(
          childProcess.execSync("yarn npm info caniuse-lite --json").toString(),
        );
      }
    }
    return JSON.parse(
      childProcess.execSync("npm show caniuse-lite --json").toString(),
    );
  }
  
  function getBrowsersList() {
    return childProcess
      .execSync("npx browserslist")
      .toString()
      .trim()
      .split("\n")
      .map(function (line) {
        return line.trim().split(" ");
      })
      .reduce(function (result, entry) {
        if (!result[entry[0]]) {
          result[entry[0]] = [];
        }
        result[entry[0]].push(entry[1]);
        return result;
      }, {});
  }
  
  function diffBrowsersLists(old, current) {
    var browsers = Object.keys(old).concat(
      Object.keys(current).filter(function (browser) {
        return old[browser] === undefined;
      }),
    );
    return browsers
      .map(function (browser) {
        var oldVersions = old[browser] || [];
        var currentVersions = current[browser] || [];
        var intersection = oldVersions.filter(function (version) {
          return currentVersions.indexOf(version) !== -1;
        });
        var addedVersions = currentVersions.filter(function (version) {
          return intersection.indexOf(version) === -1;
        });
        var removedVersions = oldVersions.filter(function (version) {
          return intersection.indexOf(version) === -1;
        });
        return removedVersions
          .map(function (version) {
            return pico.red("- " + browser + " " + version);
          })
          .concat(
            addedVersions.map(function (version) {
              return pico.green("+ " + browser + " " + version);
            }),
          );
      })
      .reduce(function (result, array) {
        return result.concat(array);
      }, [])
      .join("\n");
  }
  
  function updateNpmLockfile(lock, latest) {
    var metadata = { latest: latest, versions: [] };
    var content = deletePackage(JSON.parse(lock.content), metadata);
    metadata.content = JSON.stringify(content, null, "  ");
    return metadata;
  }
  
  function deletePackage(node, metadata) {
    if (node.dependencies) {
      if (node.dependencies["caniuse-lite"]) {
        var version = node.dependencies["caniuse-lite"].version;
        metadata.versions[version] = true;
        delete node.dependencies["caniuse-lite"];
      }
      for (var i in node.dependencies) {
        node.dependencies[i] = deletePackage(node.dependencies[i], metadata);
      }
    }
    return node;
  }
  
  var yarnVersionRe = /version "(.*?)"/;
  
  function updateYarnLockfile(lock, latest) {
    var blocks = lock.content.split(/(\n{2,})/).map(function (block) {
      return block.split("\n");
    });
    var versions = {};
    blocks.forEach(function (lines) {
      if (lines[0].indexOf("caniuse-lite@") !== -1) {
        var match = yarnVersionRe.exec(lines[1]);
        versions[match[1]] = true;
        if (match[1] !== latest.version) {
          lines[1] = lines[1].replace(
            /version "[^"]+"/,
            'version "' + latest.version + '"',
          );
          lines[2] = lines[2].replace(
            /resolved "[^"]+"/,
            'resolved "' + latest.dist.tarball + '"',
          );
          if (lines.length === 4) {
            lines[3] = latest.dist.integrity
              ? lines[3].replace(
                /integrity .+/,
                "integrity " + latest.dist.integrity,
              )
              : "";
          }
        }
      }
    });
    var content = blocks
      .map(function (lines) {
        return lines.join("\n");
      })
      .join("");
    return { content: content, versions: versions };
  }
  
  function updateLockfile(lock, latest) {
    if (!lock.content) lock.content = fs.readFileSync(lock.file).toString();
  
    if (lock.mode === "yarn") {
      return updateYarnLockfile(lock, latest);
    } else {
      return updateNpmLockfile(lock, latest);
    }
  }
  
  function updatePackageManually(print, lock, latest) {
    var lockfileData = updateLockfile(lock, latest);
    var caniuseVersions = Object.keys(lockfileData.versions).sort();
    if (caniuseVersions.length === 1 && caniuseVersions[0] === latest.version) {
      print(
        "Installed version:  " +
          pico.bold(pico.green(latest.version)) +
          "\n" +
          pico.bold(pico.green("caniuse-lite is up to date")) +
          "\n",
      );
      return;
    }
  
    if (caniuseVersions.length === 0) {
      caniuseVersions[0] = "none";
    }
    print(
      "Installed version" +
        (caniuseVersions.length === 1 ? ":  " : "s: ") +
        pico.bold(pico.red(caniuseVersions.join(", "))) +
        "\n" +
        "Removing old caniuse-lite from lock file\n",
    );
    fs.writeFileSync(lock.file, lockfileData.content);
  
    var install = lock.mode === "yarn" ? "yarn add -W" : lock.mode + " install";
    print(
      "Installing new caniuse-lite version\n" +
        pico.yellow("$ " + install + " caniuse-lite") +
        "\n",
    );
    try {
      childProcess.execSync(install + " caniuse-lite");
    } catch (e) {
      /* c8 ignore start */ print(
        pico.red(
          "\n" +
            e.stack +
            "\n\n" +
            "Problem with `" +
            install +
            " caniuse-lite` call. " +
            "Run it manually.\n",
        ),
      );
      process.exit(1);
    } /* c8 ignore end */
  
    var del = lock.mode === "yarn" ? "yarn remove -W" : lock.mode + " uninstall";
    print(
      "Cleaning package.json dependencies from caniuse-lite\n" +
        pico.yellow("$ " + del + " caniuse-lite") +
        "\n",
    );
    childProcess.execSync(del + " caniuse-lite");
  }
  
  function updateWith(print, cmd) {
    print("Updating caniuse-lite version\n" + pico.yellow("$ " + cmd) + "\n");
    try {
      childProcess.execSync(cmd);
    } catch (e) {
      /* c8 ignore start */ print(pico.red(e.stdout.toString()));
      print(
        pico.red(
          "\n" +
            e.stack +
            "\n\n" +
            "Problem with `" +
            cmd +
            "` call. " +
            "Run it manually.\n",
        ),
      );
      process.exit(1);
    } /* c8 ignore end */
  }
  
  module.exports = function updateDB(print) {
    var lock = detectLockfile();
    var latest = getLatestInfo(lock);
  
    var browsersListRetrievalError;
    var oldBrowsersList;
    try {
      oldBrowsersList = getBrowsersList();
    } catch (e) {
      browsersListRetrievalError = e;
    }
  
    print("Latest version:     " + pico.bold(pico.green(latest.version)) + "\n");
  
    if (lock.mode === "yarn" && lock.version !== 1) {
      updateWith(print, "yarn up -R caniuse-lite");
    } else if (lock.mode === "pnpm") {
      updateWith(print, "pnpm up caniuse-lite");
    } else {
      updatePackageManually(print, lock, latest);
    }
  
    print("caniuse-lite has been successfully updated\n");
  
    var currentBrowsersList;
    if (!browsersListRetrievalError) {
      try {
        currentBrowsersList = getBrowsersList();
      } catch (e) {
        /* c8 ignore start */ browsersListRetrievalError = e;
      } /* c8 ignore end */
    }
  
    if (browsersListRetrievalError) {
      print(
        pico.red(
          "\n" +
            browsersListRetrievalError.stack +
            "\n\n" +
            "Problem with browser list retrieval.\n" +
            "Target browser changes won’t be shown.\n",
        ),
      );
    } else {
      var targetBrowserChanges = diffBrowsersLists(
        oldBrowsersList,
        currentBrowsersList,
      );
      if (targetBrowserChanges) {
        print("\nTarget browser changes:\n");
        print(targetBrowserChanges + "\n");
      } else {
        print("\n" + pico.green("No target browser changes") + "\n");
      }
    }
  };
  