// src/actions/deplint/checkPackage/getUnnecessaryPeerDependencies.ts
import chalk2 from "chalk";

// src/actions/deplint/isTerminalPackage.ts
import fs from "node:fs";
var EXPORT_META_KEYS = /* @__PURE__ */ new Set([".", "./package.json", "./README.md"]);
function hasBinSetup(bin) {
  if (typeof bin === "string") return bin.length > 0;
  if (typeof bin !== "object" || bin === null || Array.isArray(bin)) return false;
  return Object.keys(bin).length > 0;
}
function isCliOnlyExports(exports) {
  if (exports === void 0) return true;
  if (typeof exports === "string") return true;
  if (typeof exports !== "object" || exports === null || Array.isArray(exports)) return false;
  return Object.keys(exports).every((k) => EXPORT_META_KEYS.has(k));
}
function hasLibrarySurface(pkg) {
  return pkg.main !== void 0 || pkg.module !== void 0 || !isCliOnlyExports(pkg.exports);
}
function withTerminalOverride(classification, isTerminalOverride) {
  if (isTerminalOverride === void 0) return classification;
  if (isTerminalOverride) {
    return {
      isTerminalPackage: true,
      kind: classification.kind === "terminal[private]" ? "terminal[private]" : "terminal[cli]"
    };
  }
  return {
    isTerminalPackage: false,
    kind: classification.kind === "library/cli" || classification.kind === "terminal[cli]" ? "library/cli" : "library"
  };
}
function classifyDeplintPackage(location, isTerminalOverride) {
  try {
    const pkg = JSON.parse(
      fs.readFileSync(`${location}/package.json`, "utf8")
    );
    if (pkg.private === true) {
      return withTerminalOverride({
        isTerminalPackage: true,
        kind: "terminal[private]"
      }, isTerminalOverride);
    }
    if (pkg.xy?.commands?.deplint?.terminal === true) {
      return withTerminalOverride({
        isTerminalPackage: true,
        kind: "terminal[cli]"
      }, isTerminalOverride);
    }
    const hasBin = hasBinSetup(pkg.bin);
    if (hasBin && hasLibrarySurface(pkg)) {
      return withTerminalOverride({
        isTerminalPackage: false,
        kind: "library/cli"
      }, isTerminalOverride);
    }
    if (hasBin) {
      return withTerminalOverride({
        isTerminalPackage: true,
        kind: "terminal[cli]"
      }, isTerminalOverride);
    }
    return withTerminalOverride({
      isTerminalPackage: false,
      kind: "library"
    }, isTerminalOverride);
  } catch {
    return withTerminalOverride({
      isTerminalPackage: false,
      kind: "library"
    }, isTerminalOverride);
  }
}
function isTerminalPackage(location) {
  return classifyDeplintPackage(location).isTerminalPackage;
}

// src/actions/deplint/packageJsonEditor.ts
import fs3 from "node:fs";
import chalk from "chalk";
import semver from "semver";
import sortPackageJson from "sort-package-json";

// src/actions/deplint/getRequiredPeerDependencies.ts
import fs2 from "node:fs";
import path from "node:path";
function findDepPackageJson(location, dep) {
  let dir = location;
  while (true) {
    const candidate = path.join(dir, "node_modules", dep, "package.json");
    if (fs2.existsSync(candidate)) return candidate;
    const parent = path.dirname(dir);
    if (parent === dir) return void 0;
    dir = parent;
  }
}

// src/actions/deplint/packageJsonEditor.ts
function readPackageJson(location) {
  return JSON.parse(fs3.readFileSync(`${location}/package.json`, "utf8"));
}
function writePackageJson(location, pkg) {
  const sorted = sortPackageJson(JSON.stringify(pkg, null, 2));
  fs3.writeFileSync(`${location}/package.json`, `${sorted}
`);
}
function removeDep(pkg, dep, section) {
  const sectionObj = pkg[section];
  if (!sectionObj || !Object.hasOwn(sectionObj, dep)) return;
  delete sectionObj[dep];
  if (Object.keys(sectionObj).length === 0) {
    delete pkg[section];
  }
  if (section === "peerDependencies") {
    removePeerMeta(pkg, dep);
  }
}
function removePeerMeta(pkg, dep) {
  const meta = pkg.peerDependenciesMeta;
  if (!meta || !Object.hasOwn(meta, dep)) return;
  delete meta[dep];
  if (Object.keys(meta).length === 0) {
    delete pkg.peerDependenciesMeta;
  }
}
function logFix(workspace, action, dep, detail) {
  const detailStr = detail != null && detail.length > 0 ? ` (${detail})` : "";
  console.log(`[${chalk.blue(workspace.name)}] Fixed: ${action} ${chalk.green(dep)}${detailStr}`);
}

// src/actions/deplint/checkPackage/getUnsatisfiedPeerDependencies.ts
import fs4 from "node:fs";
import semver2 from "semver";
function readDepPackageJson(location, dep) {
  const depPkgPath = findDepPackageJson(location, dep);
  if (!(depPkgPath != null && depPkgPath.length > 0)) return void 0;
  try {
    return JSON.parse(fs4.readFileSync(depPkgPath, "utf8"));
  } catch {
    return void 0;
  }
}
function readPeerDeps(location, dep) {
  const pkg = readDepPackageJson(location, dep);
  if (!pkg) return [];
  const peers = pkg.peerDependencies;
  return Object.keys(peers ?? {});
}
var MAX_TRANSITIVE_DEPTH = 2;
function collectPeerSources(location, dependencies, devDependencies, peerDependencies = []) {
  const peerSources = /* @__PURE__ */ new Map();
  const visited = /* @__PURE__ */ new Set();
  function addPeerSource(peer, source) {
    const sources = peerSources.get(peer) ?? [];
    sources.push(source);
    peerSources.set(peer, sources);
  }
  function collectFromDeps(deps, isRuntime, chain) {
    for (const dep of deps) {
      const visitKey = `${dep}:${chain.join(">")}`;
      if (visited.has(visitKey)) continue;
      visited.add(visitKey);
      for (const peer of readPeerDeps(location, dep)) {
        addPeerSource(peer, {
          chain: chain.length > 0 ? [...chain, dep] : void 0,
          isRuntimeDep: isRuntime,
          requiredBy: dep
        });
      }
      if (chain.length < MAX_TRANSITIVE_DEPTH) {
        const depPkg = readDepPackageJson(location, dep);
        if (depPkg) {
          const transitiveDeps = Object.keys(depPkg.dependencies ?? {});
          collectFromDeps(transitiveDeps, isRuntime, [...chain, dep]);
        }
      }
    }
  }
  const runtimeRoots = [.../* @__PURE__ */ new Set([...dependencies, ...peerDependencies])];
  collectFromDeps(runtimeRoots, true, []);
  const devOnly = devDependencies.filter((d) => !runtimeRoots.includes(d));
  collectFromDeps(devOnly, false, []);
  return peerSources;
}
function getRequiredTransitivePeers(location, {
  dependencies,
  devDependencies,
  peerDependencies
}) {
  const peerSources = collectPeerSources(location, dependencies, devDependencies, peerDependencies);
  return new Set(peerSources.keys());
}

// src/actions/deplint/checkPackage/getUnnecessaryPeerDependencies.ts
function isPeerImportedInDist(dep, externalDistImports, externalDistTypeImports) {
  const baseName = dep.replace(/^@types\//, "");
  return externalDistImports.includes(dep) || externalDistImports.includes(baseName) || externalDistTypeImports.includes(dep) || externalDistTypeImports.includes(baseName);
}
function findUnrequestedPeerDeps(location, packageParams, { externalDistImports, externalDistTypeImports }, isTerminal, exclude) {
  const requiredPeers = isTerminal ? null : getRequiredTransitivePeers(location, packageParams);
  const unrequested = [];
  for (const dep of packageParams.peerDependencies) {
    if (exclude?.has(dep) ?? false) continue;
    if (packageParams.dependencies.includes(dep)) continue;
    if (!isPeerImportedInDist(dep, externalDistImports, externalDistTypeImports)) continue;
    if (!(requiredPeers?.has(dep) ?? false)) {
      unrequested.push(dep);
    }
  }
  return unrequested;
}
function getUnrequestedPeerDependencies({ name, location }, packageParams, sourceParams, exclude, isTerminal) {
  const isTerminalResolved = isTerminal ?? isTerminalPackage(location);
  if (!isTerminalResolved) return 0;
  const unrequested = findUnrequestedPeerDeps(location, packageParams, sourceParams, true, exclude);
  for (const dep of unrequested) {
    console.log(
      `[${chalk2.blue(name)}] Unnecessary peerDependency ${chalk2.red(dep)} \u2014 imported in dist but no dependency requires it as a peer; should be a dependency`
    );
  }
  if (unrequested.length > 0) {
    console.log(`  ${chalk2.yellow(`${location}/package.json`)}
`);
  }
  return unrequested.length;
}
function fixUnrequestedPeerDependencies(workspace, packageParams, sourceParams, exclude, isTerminal) {
  const isTerminalResolved = isTerminal ?? isTerminalPackage(workspace.location);
  const unrequested = findUnrequestedPeerDeps(workspace.location, packageParams, sourceParams, isTerminalResolved, exclude);
  if (unrequested.length === 0) return 0;
  const tsconfigExtendsSet = new Set(sourceParams.tsconfigExtends);
  const fixable = isTerminalResolved ? unrequested : unrequested.filter((dep) => tsconfigExtendsSet.has(dep));
  if (fixable.length === 0) return 0;
  const pkg = readPackageJson(workspace.location);
  for (const dep of fixable) {
    const version = packageParams.peerDependencyVersions[dep] ?? "*";
    removeDep(pkg, dep, "peerDependencies");
    pkg.dependencies ??= {};
    pkg.dependencies[dep] = version;
    logFix(workspace, "moved from peerDependencies to dependencies", dep);
  }
  writePackageJson(workspace.location, pkg);
  return fixable.length;
}
function findRedundantPeerDeps({ peerDependencies, dependencies }, exclude) {
  const depsSet = new Set(dependencies);
  const redundant = [];
  for (const dep of peerDependencies) {
    if (exclude?.has(dep) ?? false) continue;
    if (depsSet.has(dep)) {
      redundant.push(dep);
    }
  }
  return redundant;
}
function getRedundantPeerDependencies({ name, location }, packageParams, exclude) {
  const redundant = findRedundantPeerDeps(packageParams, exclude);
  for (const dep of redundant) {
    console.log(
      `[${chalk2.blue(name)}] Redundant peerDependency ${chalk2.red(dep)} \u2014 already listed in dependencies`
    );
  }
  if (redundant.length > 0) {
    console.log(`  ${chalk2.yellow(`${location}/package.json`)}
`);
  }
  return redundant.length;
}
function fixRedundantPeerDependencies(workspace, packageParams, exclude) {
  const redundant = findRedundantPeerDeps(packageParams, exclude);
  if (redundant.length === 0) return 0;
  const pkg = readPackageJson(workspace.location);
  for (const dep of redundant) {
    removeDep(pkg, dep, "peerDependencies");
    logFix(workspace, "removed redundant peerDependency (kept in dependencies)", dep);
  }
  writePackageJson(workspace.location, pkg);
  return redundant.length;
}
export {
  findRedundantPeerDeps,
  findUnrequestedPeerDeps,
  fixRedundantPeerDependencies,
  fixUnrequestedPeerDependencies,
  getRedundantPeerDependencies,
  getUnrequestedPeerDependencies
};
//# sourceMappingURL=getUnnecessaryPeerDependencies.mjs.map
