// src/pm/workspaces.ts
import { existsSync, readFileSync } from "node:fs";
import PATH from "node: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 != null && packageManager.length > 0)) return void 0;
  return packageManager.split("@", 1)[0];
}

// src/pm/detectPackageManager.ts
import { existsSync as existsSync2 } from "node:fs";
var knownPackageManagers = /* @__PURE__ */ new Set(["bun", "npm", "pnpm", "yarn"]);
function detectPackageManager() {
  const packageManager = detectPackageManagerFromPackageJson();
  if (packageManager != null && packageManager.length > 0 && 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/initCwd.ts
function INIT_CWD() {
  return process.env.INIT_CWD ?? process.cwd();
}

// src/lib/latestVersions.ts
var latestVersions = {
  node: "24.14.1",
  nodeLtsCodename: "Krypton",
  npm: "11.11.0",
  pnpm: "10.33.0",
  yarn: "4.13.0"
};

// src/lib/reporting/runWithEnvelope.ts
import { performance as performance2 } from "node:perf_hooks";

// src/lib/reporting/xyReporter.ts
import { performance } from "node:perf_hooks";
var reporterState = {
  currentCommand: "unknown",
  isEnvelopeEmitted: false,
  isJsonModeEnabled: false,
  isStrictModeEnabled: false,
  originalStdoutWrite: void 0,
  startTime: 0
};
function isJsonMode() {
  return reporterState.isJsonModeEnabled;
}
function setCurrentCommand(name) {
  reporterState.currentCommand = name;
  reporterState.startTime = performance.now();
  reporterState.isEnvelopeEmitted = false;
}
function emitJson(envelope) {
  if (reporterState.isEnvelopeEmitted) return;
  reporterState.isEnvelopeEmitted = true;
  const json = JSON.stringify(envelope) + "\n";
  if (reporterState.originalStdoutWrite) {
    reporterState.originalStdoutWrite(json);
  } else {
    process.stdout.write(json);
  }
}

// src/lib/reporting/runWithEnvelope.ts
async function runWithEnvelope(command, action) {
  setCurrentCommand(command);
  const start = performance2.now();
  let exitCode;
  let envelope;
  try {
    const ret = await action();
    if (typeof ret === "number") {
      exitCode = ret;
    } else {
      exitCode = ret.exitCode;
      envelope = ret.envelope;
    }
  } catch (ex) {
    exitCode = 1;
    const message = ex instanceof Error ? ex.message : String(ex);
    envelope = {
      errorCount: 1,
      errors: [{ message }]
    };
    if (!isJsonMode()) {
      console.error(message);
    }
  }
  if (isJsonMode()) {
    emitJson({
      command,
      data: envelope?.data,
      durationMs: Math.round(performance2.now() - start),
      errorCount: envelope?.errorCount ?? (exitCode === 0 ? 0 : 1),
      errors: envelope?.errors,
      exitCode,
      schemaVersion: 1,
      success: exitCode === 0,
      warningCount: envelope?.warningCount ?? 0,
      warnings: envelope?.warnings
    });
  }
  return exitCode;
}
async function withEnvelope(command, action) {
  process.exitCode = await runWithEnvelope(command, action);
}

// src/actions/node-lint.ts
import { readFileSync as readFileSync2, writeFileSync } from "node:fs";
import PATH2 from "node:path";
import chalk from "chalk";
import semver from "semver";
var REQUIRED_NODE_RANGE = ">=22";
var nodeConditionNames = /* @__PURE__ */ new Set(["node", "node-addons"]);
function readPackageJson(path) {
  try {
    return JSON.parse(readFileSync2(path, "utf8"));
  } catch {
    return void 0;
  }
}
function writePackageJson(path, pkg) {
  writeFileSync(path, `${JSON.stringify(pkg, null, 2)}
`, "utf8");
}
function targetPathPlatform(target) {
  const segments = target.replace(/^\.\//, "").split(/[\\/]+/);
  const distIndex = segments.indexOf("dist");
  return distIndex === -1 ? void 0 : segments[distIndex + 1];
}
function hasNodeExport(value, isNodeCondition = false) {
  if (typeof value === "string") {
    return isNodeCondition || targetPathPlatform(value) === "node";
  }
  if (Array.isArray(value)) {
    return value.some((entry) => hasNodeExport(entry, isNodeCondition));
  }
  if (typeof value !== "object" || value === null) return false;
  return Object.entries(value).some(([key, nested]) => {
    const isSubpath = key === "." || key.startsWith("./");
    return hasNodeExport(nested, isNodeCondition || !isSubpath && nodeConditionNames.has(key));
  });
}
function isNodePackage(pkg) {
  if (pkg.bin !== void 0) return true;
  return hasNodeExport(pkg.exports);
}
function checkVoltaNodeVersion(cwd, shouldFix) {
  const rootPath = PATH2.resolve(cwd, "package.json");
  const rootPkg = readPackageJson(rootPath);
  if (!rootPkg?.volta) return [];
  const pinned = rootPkg.volta.node;
  const latest = latestVersions.node;
  const isOutdated = pinned === void 0 || semver.valid(pinned) === null || semver.lt(pinned, latest);
  if (!isOutdated) return [];
  if (shouldFix) {
    rootPkg.volta.node = latest;
    writePackageJson(rootPath, rootPkg);
    console.log(chalk.green(`  \u2714 Fixed: set volta.node to ${latest} in root package.json`));
    return [];
  }
  const message = pinned === void 0 ? `Root volta config has no node pin \u2014 expected latest stable node version ${latest}` : `Root volta.node "${pinned}" is older than the latest stable node version ${latest}`;
  return [{
    file: "package.json",
    message,
    rule: "volta-node-latest"
  }];
}
function checkPackageEngines(cwd, location, shouldFix) {
  const pkgPath = PATH2.resolve(cwd, location, "package.json");
  const pkg = readPackageJson(pkgPath);
  if (!pkg) return [];
  if (pkg.private === true) return [];
  const label = pkg.name ?? location;
  const file = PATH2.join(location, "package.json");
  const nodeRange = pkg.engines?.node;
  if (isNodePackage(pkg)) {
    const isValidRange = nodeRange !== void 0 && semver.validRange(nodeRange) !== null && semver.subset(nodeRange, REQUIRED_NODE_RANGE);
    if (isValidRange) return [];
    if (shouldFix) {
      pkg.engines = { ...pkg.engines, node: REQUIRED_NODE_RANGE };
      writePackageJson(pkgPath, pkg);
      console.log(chalk.green(`  \u2714 Fixed: set engines.node to "${REQUIRED_NODE_RANGE}" in ${file}`));
      return [];
    }
    const message = nodeRange === void 0 ? `${label} has node exports but no engines.node \u2014 expected "${REQUIRED_NODE_RANGE}"` : `${label} engines.node "${nodeRange}" allows versions below ${REQUIRED_NODE_RANGE.replace(">=", "")} \u2014 expected a range within "${REQUIRED_NODE_RANGE}"`;
    return [{
      file,
      message,
      rule: "engines-node-range"
    }];
  }
  if (nodeRange === void 0) return [];
  if (shouldFix) {
    const engines = { ...pkg.engines };
    delete engines.node;
    if (Object.keys(engines).length === 0) {
      delete pkg.engines;
    } else {
      pkg.engines = engines;
    }
    writePackageJson(pkgPath, pkg);
    console.log(chalk.green(`  \u2714 Fixed: removed engines.node from ${file}`));
    return [];
  }
  return [{
    file,
    message: `${label} has no node exports (browser/neutral only) but declares engines.node "${nodeRange}"`,
    rule: "engines-node-portable"
  }];
}
function nodeLint(options = {}) {
  const { shouldFix: isFix = false, verbose: isVerbose = false } = options;
  const cwd = INIT_CWD();
  if (!readPackageJson(PATH2.resolve(cwd, "package.json"))) {
    console.error(chalk.red("Could not read package.json"));
    return 1;
  }
  console.log(chalk.green("Node Lint"));
  const workspaces = getPackageManager().listWorkspaces();
  const locations = workspaces.length > 0 ? workspaces.map((ws) => ws.location) : ["."];
  const warnings = [
    ...checkVoltaNodeVersion(cwd, isFix),
    ...locations.flatMap((location) => {
      if (isVerbose) console.log(chalk.gray(`  checking ${location}`));
      return checkPackageEngines(cwd, location, isFix);
    })
  ];
  for (const warning of warnings) {
    console.log(chalk.yellow(`  \u26A0 ${warning.message} (fixable)`));
  }
  if (warnings.length === 0) {
    console.log(chalk.green("  \u2713 All node-lint checks passed"));
  }
  return {
    envelope: { warningCount: warnings.length, warnings },
    exitCode: 0
  };
}

// src/xy/lint/nodeLintCommand.ts
var nodeLintCommand = {
  command: "node-lint",
  describe: "Node Lint - Check volta node pin is latest stable and engines.node matches each package's platform (warns by default)",
  builder: (yargs) => {
    return yargs.option("fix", {
      alias: "f",
      default: false,
      description: "Fix issues: update volta.node, set engines.node on node packages, remove engines.node from browser/neutral packages",
      type: "boolean"
    });
  },
  handler: async (argv) => {
    if (Boolean(argv.verbose)) console.log("Node Lint");
    await withEnvelope("node-lint", () => nodeLint({ shouldFix: !!Boolean(argv.fix), verbose: !!Boolean(argv.verbose) }));
  }
};
export {
  nodeLintCommand
};
//# sourceMappingURL=nodeLintCommand.mjs.map
