'use strict';

var fs = require('fs-extra');
var semver = require('semver');
var parsers = require('@yarnpkg/parsers');
var lockfile = require('@yarnpkg/lockfile');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var semver__default = /*#__PURE__*/_interopDefaultLegacy(semver);

const ENTRY_PATTERN = /^((?:@[^/]+\/)?[^@/]+)@(.+)$/;
function parseLockfile(lockfileContents) {
  try {
    return {
      object: parsers.parseSyml(lockfileContents),
      type: "success"
    };
  } catch (err) {
    return {
      object: null,
      type: err
    };
  }
}
const NEW_HEADER = `${[
  `# This file is generated by running "yarn install" inside your project.
`,
  `# Manual changes might be lost - proceed with caution!
`
].join(``)}
`;
function stringifyLockfile(data, legacy) {
  return legacy ? lockfile.stringify(data) : NEW_HEADER + parsers.stringifySyml(data);
}
const LEGACY_REGEX = /^(#.*(\r?\n))*?#\s+yarn\s+lockfile\s+v1\r?\n/i;
const SPECIAL_OBJECT_KEYS = [
  `__metadata`,
  `version`,
  `resolution`,
  `dependencies`,
  `peerDependencies`,
  `dependenciesMeta`,
  `peerDependenciesMeta`,
  `binaries`
];
class Lockfile {
  constructor(path, packages, data, legacy = false) {
    this.path = path;
    this.packages = packages;
    this.data = data;
    this.legacy = legacy;
  }
  static async load(path) {
    var _a;
    const lockfileContents = await fs__default["default"].readFile(path, "utf8");
    const legacy = LEGACY_REGEX.test(lockfileContents);
    const lockfile = parseLockfile(lockfileContents);
    if (lockfile.type !== "success") {
      throw new Error(`Failed yarn.lock parse with ${lockfile.type}`);
    }
    const data = lockfile.object;
    const packages = /* @__PURE__ */ new Map();
    for (const [key, value] of Object.entries(data)) {
      if (SPECIAL_OBJECT_KEYS.includes(key))
        continue;
      const [, name, range] = (_a = ENTRY_PATTERN.exec(key)) != null ? _a : [];
      if (!name) {
        throw new Error(`Failed to parse yarn.lock entry '${key}'`);
      }
      let queries = packages.get(name);
      if (!queries) {
        queries = [];
        packages.set(name, queries);
      }
      queries.push({ range, version: value.version });
    }
    return new Lockfile(path, packages, data, legacy);
  }
  get(name) {
    return this.packages.get(name);
  }
  keys() {
    return this.packages.keys();
  }
  analyze(options) {
    var _a;
    const { filter } = options != null ? options : {};
    const result = {
      invalidRanges: [],
      newVersions: [],
      newRanges: []
    };
    for (const [name, allEntries] of this.packages) {
      if (filter && !filter(name)) {
        continue;
      }
      const invalid = allEntries.filter((e) => !semver__default["default"].validRange(e.range));
      result.invalidRanges.push(...invalid.map(({ range }) => ({ name, range })));
      const entries = allEntries.filter((e) => semver__default["default"].validRange(e.range));
      if (entries.length < 2) {
        continue;
      }
      const versions = Array.from(new Set(entries.map((e) => e.version))).sort((v1, v2) => semver__default["default"].rcompare(v1, v2));
      if (versions.length < 2) {
        continue;
      }
      const acceptedVersions = /* @__PURE__ */ new Set();
      for (const { version, range } of entries) {
        const acceptedVersion = versions.find((v) => semver__default["default"].satisfies(v, range));
        if (!acceptedVersion) {
          throw new Error(`No existing version was accepted for range ${range}, searching through ${versions}, for package ${name}`);
        }
        if (acceptedVersion !== version) {
          result.newVersions.push({
            name,
            range,
            newVersion: acceptedVersion,
            oldVersion: version
          });
        }
        acceptedVersions.add(acceptedVersion);
      }
      if (acceptedVersions.size === 1) {
        continue;
      }
      const maxVersion = Array.from(acceptedVersions).sort(semver__default["default"].rcompare)[0];
      const maxEntry = (_a = entries.filter((e) => semver__default["default"].satisfies(maxVersion, e.range)).map((e) => ({ e, min: semver__default["default"].minVersion(e.range) })).filter((p) => p.min).sort((a, b) => semver__default["default"].rcompare(a.min, b.min))[0]) == null ? void 0 : _a.e;
      if (!maxEntry) {
        throw new Error(`No entry found that satisfies max version '${maxVersion}'`);
      }
      for (const { version, range } of entries) {
        if (semver__default["default"].satisfies(maxVersion, range)) {
          continue;
        }
        result.newRanges.push({
          name,
          oldRange: range,
          newRange: maxEntry.range,
          oldVersion: version,
          newVersion: maxVersion
        });
      }
    }
    return result;
  }
  remove(name, range) {
    var _a;
    const query = `${name}@${range}`;
    const existed = Boolean(this.data[query]);
    delete this.data[query];
    const newEntries = (_a = this.packages.get(name)) == null ? void 0 : _a.filter((e) => e.range !== range);
    if (newEntries) {
      this.packages.set(name, newEntries);
    }
    return existed;
  }
  replaceVersions(results) {
    var _a;
    for (const { name, range, oldVersion, newVersion } of results) {
      const query = `${name}@${range}`;
      const entryData = this.data[query];
      if (!entryData) {
        throw new Error(`No entry data for ${query}`);
      }
      if (entryData.version !== oldVersion) {
        throw new Error(`Expected existing version data for ${query} to be ${oldVersion}, was ${entryData.version}`);
      }
      const matchingEntry = Object.entries(this.data).find(([q, e]) => q.startsWith(`${name}@`) && e.version === newVersion);
      if (!matchingEntry) {
        throw new Error(`No matching entry found for ${name} at version ${newVersion}`);
      }
      this.data[query] = matchingEntry[1];
      const entry = (_a = this.packages.get(name)) == null ? void 0 : _a.find((e) => e.range === range);
      if (!entry) {
        throw new Error(`No entry data for ${query}`);
      }
      if (entry.version !== oldVersion) {
        throw new Error(`Expected existing version data for ${query} to be ${oldVersion}, was ${entryData.version}`);
      }
      entry.version = newVersion;
    }
  }
  async save() {
    await fs__default["default"].writeFile(this.path, this.toString(), "utf8");
  }
  toString() {
    return stringifyLockfile(this.data, this.legacy);
  }
}

exports.Lockfile = Lockfile;
//# sourceMappingURL=Lockfile-48dc675e.cjs.js.map
