// src/actions/deplint/checkPackage/getUnlistedDependencies.ts
import { builtinModules } from "node:module";
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 addDep(pkg, dep, version, section) {
  const sectionObj = pkg[section] ?? {};
  sectionObj[dep] = version;
  pkg[section] = sectionObj;
}
function formatPeerRange(version, boundType = "major") {
  const parsed = semver.parse(version);
  if (!parsed) return `~${version}`;
  if (boundType === "minor") return `>=${parsed.version} <${parsed.major}.${parsed.minor + 1}`;
  return `>=${parsed.version} <${parsed.major + 1}`;
}
function inferPeerBoundType(devRange) {
  return devRange?.startsWith("~") ?? false ? "minor" : "major";
}
function readInstalledVersion(location, dep) {
  const depPkgPath = findDepPackageJson(location, dep);
  if (!(depPkgPath != null && depPkgPath.length > 0)) return void 0;
  try {
    const depPkg = JSON.parse(fs3.readFileSync(depPkgPath, "utf8"));
    return depPkg.version;
  } catch {
    return void 0;
  }
}
function resolvePeerLowerBound(location, dep, devRange) {
  if (devRange != null && devRange.length > 0 && devRange !== "*" && !devRange.startsWith("workspace:") && !devRange.startsWith("catalog:") && !devRange.startsWith("npm:")) {
    try {
      const min = semver.minVersion(devRange);
      if (min) return min.version;
    } catch {
    }
  }
  return readInstalledVersion(location, dep);
}
function resolveDepVersion(location, dep) {
  const version = readInstalledVersion(location, dep);
  return version != null && version.length > 0 ? `~${version}` : "*";
}
function resolvePeerDepVersion(location, dep, devRange) {
  const lower = resolvePeerLowerBound(location, dep, devRange);
  return lower != null && lower.length > 0 ? formatPeerRange(lower, inferPeerBoundType(devRange)) : "*";
}
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/tsScriptsAliases.ts
var VARIANT_MAP = {
  "@xylabs/toolchain": "@xylabs/toolchain",
  "@xylabs/ts-scripts-react-yarn3": "@xylabs/ts-scripts-react-pnpm",
  "@xylabs/ts-scripts-react-pnpm": "@xylabs/ts-scripts-react-yarn3"
};
function isSatisfiedByTsScriptsVariant(imp, allDeps) {
  const variant = VARIANT_MAP[imp];
  return variant !== void 0 && allDeps.includes(variant);
}

// src/actions/deplint/checkPackage/getUnlistedDependencies.ts
function isRuntimeImportListed(imp, name, dependencies, peerDependencies) {
  return dependencies.includes(imp) || imp === name || peerDependencies.includes(imp) || builtinModules.includes(imp);
}
function isDistTypeImportListed(imp, name, dependencies, devDependencies, peerDependencies) {
  return dependencies.includes(imp) || imp === name || dependencies.includes(`@types/${imp}`) || devDependencies.includes(`@types/${imp}`) || peerDependencies.includes(imp) || peerDependencies.includes(`@types/${imp}`) || builtinModules.includes(imp);
}
function logMissing(name, imp, importPaths) {
  console.log(`[${chalk2.blue(name)}] Missing dependency in package.json: ${chalk2.red(imp)}`);
  if (Object.hasOwn(importPaths, imp)) {
    console.log(`  ${importPaths[imp].join("\n ")}`);
  }
}
function shouldBePeerDependency(imp, _location, _currentPeers, _distImports, workspaceNames) {
  if (workspaceNames?.has(imp) ?? false) return false;
  if (imp.startsWith("@types/")) return false;
  return true;
}
function findUnlistedDependencies(name, dependencies, devDependencies, peerDependencies, externalDistImports, externalDistTypeImports) {
  const allDeps = [...dependencies, ...devDependencies, ...peerDependencies];
  const results = [];
  for (const imp of externalDistImports) {
    if (!isRuntimeImportListed(imp, name, dependencies, peerDependencies) && !isSatisfiedByTsScriptsVariant(imp, allDeps)) {
      results.push({ imp, section: "dependencies" });
    }
  }
  for (const imp of externalDistTypeImports) {
    if (!isDistTypeImportListed(imp, name, dependencies, devDependencies, peerDependencies) && !isSatisfiedByTsScriptsVariant(imp, allDeps)) {
      results.push({ imp, section: "dependencies" });
    }
  }
  return results;
}
function getUnlistedDependencies({ name, location }, {
  dependencies,
  devDependencies,
  peerDependencies
}, {
  externalDistImports,
  externalDistTypeImports,
  distImportPaths
}) {
  const unlisted = findUnlistedDependencies(name, dependencies, devDependencies, peerDependencies, externalDistImports, externalDistTypeImports);
  for (const { imp } of unlisted) {
    logMissing(name, imp, distImportPaths);
  }
  if (unlisted.length > 0) {
    console.log(`  ${chalk2.yellow(`${location}/package.json`)}
`);
  }
  return unlisted.length;
}
function fixUnlistedDependencies(workspace, {
  dependencies,
  devDependencies,
  peerDependencies
}, { externalDistImports, externalDistTypeImports }, workspaceNames, isTerminal) {
  const unlisted = findUnlistedDependencies(workspace.name, dependencies, devDependencies, peerDependencies, externalDistImports, externalDistTypeImports);
  if (unlisted.length === 0) return 0;
  const pkg = readPackageJson(workspace.location);
  const isTerminalResolved = isTerminal ?? isTerminalPackage(workspace.location);
  const devVersions = pkg.devDependencies ?? {};
  for (const { imp, section } of unlisted) {
    const isPeer = !isTerminalResolved && section === "dependencies" && shouldBePeerDependency(imp, workspace.location, peerDependencies, externalDistImports, workspaceNames);
    if (isPeer) {
      const devVersion = resolveDepVersion(workspace.location, imp);
      const peerVersion = resolvePeerDepVersion(workspace.location, imp, devVersions[imp] ?? devVersion);
      addDep(pkg, imp, peerVersion, "peerDependencies");
      logFix(workspace, "added to peerDependencies", imp, peerVersion);
      addDep(pkg, imp, devVersion, "devDependencies");
      logFix(workspace, "added to devDependencies", imp, devVersion);
    } else {
      const version = resolveDepVersion(workspace.location, imp);
      addDep(pkg, imp, version, section);
      logFix(workspace, `added to ${section}`, imp, version);
    }
  }
  writePackageJson(workspace.location, pkg);
  return unlisted.length;
}
export {
  findUnlistedDependencies,
  fixUnlistedDependencies,
  getUnlistedDependencies,
  shouldBePeerDependency
};
//# sourceMappingURL=getUnlistedDependencies.mjs.map
