// src/lib/dependencies/detectDuplicateDependencies.ts
import { execSync } from "child_process";

// src/pm/workspaces.ts
import { existsSync, readFileSync } from "fs";
import PATH from "path";
import { globSync } from "glob";
function readJsonFile(path) {
  if (!existsSync(path)) return void 0;
  try {
    return JSON.parse(readFileSync(path, "utf8"));
  } catch {
    return void 0;
  }
}
function readRootPackageJson(cwd = process.cwd()) {
  return readJsonFile(PATH.resolve(cwd, "package.json"));
}
function detectPackageManagerFromPackageJson(cwd = process.cwd()) {
  const packageManager = readRootPackageJson(cwd)?.packageManager;
  if (!packageManager) return void 0;
  return packageManager.split("@")[0];
}

// src/pm/detectPackageManager.ts
import { existsSync as existsSync2 } from "fs";
var knownPackageManagers = /* @__PURE__ */ new Set(["bun", "npm", "pnpm", "yarn"]);
function detectPackageManager() {
  const packageManager = detectPackageManagerFromPackageJson();
  if (packageManager && knownPackageManagers.has(packageManager)) {
    return packageManager;
  }
  if (existsSync2("pnpm-lock.yaml") || existsSync2("pnpm-workspace.yaml")) return "pnpm";
  if (existsSync2("bun.lock") || existsSync2("bun.lockb")) return "bun";
  if (existsSync2("yarn.lock") || existsSync2(".yarnrc.yml")) return "yarn";
  if (existsSync2("package-lock.json") || existsSync2("npm-shrinkwrap.json")) return "npm";
  return "yarn";
}

// src/pm/registry.ts
var implementations = /* @__PURE__ */ new Map();
function getPackageManager(name) {
  const pmName = name ?? detectPackageManager();
  const pm = implementations.get(pmName);
  if (!pm) {
    throw new Error(
      `No package manager implementation registered for "${pmName}". Ensure registerPackageManager() has been called before getPackageManager().`
    );
  }
  return pm;
}

// src/lib/processEx.ts
import chalk from "chalk";

// src/lib/withError.ts
var withError = (ex, closure, predicate = (ex2) => !!ex2.name && !!ex2.message) => {
  return predicate(ex) ? closure(ex) : void 0;
};

// src/lib/withErrnoException.ts
var withErrnoException = (ex, closure) => {
  return withError(ex, closure, (ex2) => ex2.errno !== void 0);
};

// src/lib/processEx.ts
var processEx = (ex) => {
  const error = typeof ex === "string" ? new Error(ex) : ex;
  const exitCode = withErrnoException(error, (error2) => {
    if (error2.code === "ENOENT") {
      console.error(chalk.red(`'${error2.path}' not found.`));
    } else {
      console.error(chalk.red(`Errno: ${error2.code}`));
    }
    return error2.errno ?? -1;
  }) ?? withError(error, (error2) => {
    console.error(chalk.red(`${error2.name}: ${error2.message}`));
    return -1;
  }) ?? (() => {
    console.error(chalk.red(`Unexpected Error: ${JSON.stringify(ex, null, 2)}`));
    return -1;
  })();
  process.exit(process.exitCode ?? exitCode);
};

// src/lib/safeExit.ts
var safeExit = (func, exitOnFail = true) => {
  try {
    const result = func();
    if (result && exitOnFail) {
      process.exit(result);
    }
    return result;
  } catch (ex) {
    return processEx(ex);
  }
};

// src/lib/dependencies/DuplicateDetector.ts
import { EOL } from "os";
import chalk2 from "chalk";

// src/lib/jsonFormatters.ts
var multiLineToJSONArray = (output) => {
  const withCommas = output.replaceAll("\r\n", "").replaceAll("\n", ",");
  const cleanCollection = withCommas.slice(0, Math.max(0, withCommas.length - 1));
  const collection = `[${cleanCollection}]`;
  return JSON.parse(collection);
};

// src/lib/dependencies/DuplicateDetector.ts
var trimVirtualMeta = (value) => {
  const virtualParts = value.split("virtual:");
  if (virtualParts.length > 1) {
    const hashParts = virtualParts[1].split("#");
    return virtualParts[0] + hashParts[1];
  } else {
    return value;
  }
};
var trimObjectDependencyVirtualMeta = (obj) => {
  const resultObj = {};
  for (const [key, value] of Object.entries(obj)) {
    resultObj[trimVirtualMeta(key)] = {
      descriptor: trimVirtualMeta(value.descriptor),
      locator: trimVirtualMeta(value.locator)
    };
  }
  return resultObj;
};
var trimDependencyVirtualMeta = (dependencies) => {
  return dependencies.map((dependency) => {
    return { children: trimObjectDependencyVirtualMeta(dependency.children), value: trimVirtualMeta(dependency.value) };
  });
};
function extractPnpmVersions(data, depName) {
  const versions = /* @__PURE__ */ new Set();
  function walk(obj) {
    if (Array.isArray(obj)) {
      for (const item of obj) walk(item);
    } else if (obj && typeof obj === "object") {
      const record = obj;
      if (typeof record.version === "string" && typeof record.from === "string" && record.from === depName) {
        versions.add(record.version);
      }
      for (const val of Object.values(record)) walk(val);
    }
  }
  walk(data);
  return [...versions];
}
var DuplicateDetector = class {
  dependency;
  duplicateVersions;
  constructor(output, dependency) {
    this.dependency = dependency;
    this.duplicateVersions = this.parseVersions(output, dependency);
  }
  detect() {
    if (this.duplicateVersions.length > 1) {
      console.log(chalk2.yellow(`${EOL}Duplicates found for: ${this.dependency}`));
      const duplicateVersions = this.duplicateVersions.join(`${EOL}  `);
      console.log(chalk2.grey(`  ${duplicateVersions}`, EOL));
      return 1;
    } else {
      console.log(`${this.dependency} - OK`);
      return 0;
    }
  }
  parseVersions(output, dependency) {
    try {
      const parsed = JSON.parse(output);
      if (Array.isArray(parsed)) {
        return extractPnpmVersions(parsed, dependency);
      }
    } catch {
    }
    try {
      const entries = trimDependencyVirtualMeta(multiLineToJSONArray(output));
      const versions = /* @__PURE__ */ new Set();
      for (const entry of entries) {
        for (const key of Object.keys(entry.children)) {
          const version = key.includes("@virtual:") ? void 0 : key;
          if (version) versions.add(version);
        }
      }
      return [...versions];
    } catch {
      console.warn(chalk2.yellow(`Could not parse dependency output for ${dependency}`));
      return [];
    }
  }
};

// src/lib/dependencies/detectDuplicateDependencies.ts
var detectDuplicateDependencies = (depsFromPackageJSON, DefaultDependencies) => {
  let exitCode = 0;
  const dependencies = depsFromPackageJSON?.length ? depsFromPackageJSON : DefaultDependencies;
  return safeExit(() => {
    if (dependencies) {
      for (const dependency of dependencies) {
        let output;
        try {
          const pm = getPackageManager();
          const cmd = `${pm.command} why ${dependency} --json`;
          output = execSync(cmd, { maxBuffer: 64 * 1024 * 1024 }).toString();
        } catch (e) {
          console.error(`Error running dependency check: ${String(e)}`);
          exitCode = 1;
          continue;
        }
        if (output) {
          exitCode = new DuplicateDetector(output, dependency).detect();
        } else {
          console.log(`${dependency} - N/A`);
          if (depsFromPackageJSON) {
            exitCode = 1;
            console.log(`\u{1F6A8} Library ${dependency} was requested in package.json but not found`);
          }
        }
      }
      return exitCode;
    } else {
      console.log("\u{1F6A8} No dependencies where passed");
      return exitCode;
    }
  });
};

// src/lib/dependencies/workspaceCycles.ts
import { readFileSync as readFileSync2 } from "fs";
import PATH2 from "path";
import chalk3 from "chalk";
function detectWorkspaceCycles(workspaces) {
  const workspaceNames = new Set(workspaces.map((ws) => ws.name));
  const graph = /* @__PURE__ */ new Map();
  for (const ws of workspaces) {
    const pkgPath = PATH2.join(ws.location, "package.json");
    const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
    const deps = pkg.dependencies ?? {};
    const devDeps = pkg.devDependencies ?? {};
    const edges = [];
    for (const dep of Object.keys(deps)) {
      if (workspaceNames.has(dep)) edges.push({ name: dep, type: "dep" });
    }
    for (const dep of Object.keys(devDeps)) {
      if (workspaceNames.has(dep)) edges.push({ name: dep, type: "devDep" });
    }
    graph.set(ws.name, edges);
  }
  const WHITE = 0;
  const GRAY = 1;
  const BLACK = 2;
  const color = /* @__PURE__ */ new Map();
  const cycles = [];
  const path = [];
  const onPath = /* @__PURE__ */ new Set();
  for (const name of workspaceNames) color.set(name, WHITE);
  function dfs(u, incomingEdgeType) {
    color.set(u, GRAY);
    onPath.add(u);
    path.push({ name: u, edgeType: incomingEdgeType });
    for (const edge of graph.get(u) ?? []) {
      const v = edge.name;
      if (onPath.has(v)) {
        const cycleEdges = [];
        const startIdx = path.findIndex((p) => p.name === v);
        for (let i = startIdx; i < path.length - 1; i++) {
          const nextEdgeType = path[i + 1].edgeType ?? "dep";
          cycleEdges.push({
            from: path[i].name,
            to: path[i + 1].name,
            type: nextEdgeType
          });
        }
        cycleEdges.push({
          from: u,
          to: v,
          type: edge.type
        });
        cycles.push(cycleEdges);
      } else if (color.get(v) === WHITE) {
        dfs(v, edge.type);
      }
    }
    path.pop();
    onPath.delete(u);
    color.set(u, BLACK);
  }
  for (const name of workspaceNames) {
    if (color.get(name) === WHITE) dfs(name);
  }
  return cycles;
}
function printWorkspaceCycles(cycles) {
  for (const cycle of cycles) {
    console.error(chalk3.red("  Workspace dependency cycle:"));
    for (const edge of cycle) {
      const edgeLabel = edge.type === "devDep" ? chalk3.yellow("devDep") : chalk3.cyan("dep");
      console.error(`    ${chalk3.white(edge.from)} --(${edgeLabel})--> ${chalk3.white(edge.to)}`);
    }
  }
}
export {
  DuplicateDetector,
  detectDuplicateDependencies,
  detectWorkspaceCycles,
  printWorkspaceCycles
};
//# sourceMappingURL=index.mjs.map