// src/actions/dead/dead.ts
import chalk6 from "chalk";

// src/lib/deepMerge.ts
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
function isPlainObject(value) {
  if (typeof value !== "object" || value === null) return false;
  const proto = Object.getPrototypeOf(value);
  return proto === Object.prototype || proto === null;
}
function isMergeable(value) {
  return Array.isArray(value) || isPlainObject(value);
}
function cloneValue(value) {
  if (Array.isArray(value)) {
    return value.map((item) => cloneValue(item));
  }
  if (isPlainObject(value)) {
    const result = {};
    for (const [key, item] of Object.entries(value)) {
      if (UNSAFE_KEYS.has(key)) continue;
      result[key] = cloneValue(item);
    }
    return result;
  }
  return value;
}
function mergeObject(target, source, options) {
  const destination = {};
  if (isPlainObject(target)) {
    for (const [key, item] of Object.entries(target)) {
      if (UNSAFE_KEYS.has(key)) continue;
      destination[key] = cloneValue(item);
    }
  }
  for (const [key, sourceValue] of Object.entries(source)) {
    if (UNSAFE_KEYS.has(key)) continue;
    if (Object.hasOwn(target, key) && isMergeable(sourceValue)) {
      const customMerge = options.customMerge?.(key);
      destination[key] = (customMerge ?? deepMerge)(target[key], sourceValue, options);
    } else {
      destination[key] = cloneValue(sourceValue);
    }
  }
  return destination;
}
function deepMerge(target, source, options = {}) {
  const isSourceIsArray = Array.isArray(source);
  const isTargetIsArray = Array.isArray(target);
  if (isSourceIsArray !== isTargetIsArray) {
    return cloneValue(source);
  }
  if (isSourceIsArray) {
    return [...target, ...source].map((item) => cloneValue(item));
  }
  return mergeObject(target, source, options);
}

// src/pm/workspaces.ts
import { existsSync, readFileSync } from "node:fs";
import PATH from "node:path";
import { globSync } from "glob";
function readJsonFile(path13) {
  if (!existsSync(path13)) return void 0;
  try {
    return JSON.parse(readFileSync(path13, "utf8"));
  } catch {
    return void 0;
  }
}
function readRootPackageJson(cwd = process.cwd()) {
  return readJsonFile(PATH.resolve(cwd, "package.json"));
}
function readWorkspacePatternsFromPackageJson(cwd = process.cwd()) {
  const pkg = readRootPackageJson(cwd);
  if (!pkg?.workspaces) return [];
  if (Array.isArray(pkg.workspaces)) return [...pkg.workspaces];
  return pkg.workspaces.packages ? [...pkg.workspaces.packages] : [];
}
function readPnpmWorkspacePatterns(cwd = process.cwd()) {
  const wsPath = PATH.resolve(cwd, "pnpm-workspace.yaml");
  if (!existsSync(wsPath)) return [];
  const content = readFileSync(wsPath, "utf8");
  const patterns = [];
  let isInPackages = false;
  for (const line of content.split("\n")) {
    if (/^packages\s*:/.test(line.trim())) {
      isInPackages = true;
      continue;
    }
    if (isInPackages) {
      const match = /^\s+-\s+['"]?([^'"]+)['"]?\s*$/.exec(line);
      if (match) {
        patterns.push(match[1]);
      } else if (line.trim() !== "" && !/^\s/.test(line)) {
        break;
      }
    }
  }
  return patterns;
}
function readWorkspacePatterns(cwd = process.cwd()) {
  const fromPackageJson = readWorkspacePatternsFromPackageJson(cwd);
  return fromPackageJson.length > 0 ? fromPackageJson : readPnpmWorkspacePatterns(cwd);
}
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/loadConfig.ts
import chalk from "chalk";
import { cosmiconfig } from "cosmiconfig";
import { TypeScriptLoader } from "cosmiconfig-typescript-loader";
var configState = {
  config: void 0,
  rootConfigPath: void 0
};
var workspaceConfigCache = /* @__PURE__ */ new Map();
var deprecationWarned = /* @__PURE__ */ new Set();
function isRecord(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function createExplorer() {
  return cosmiconfig("xy", { cache: true, loaders: { ".ts": TypeScriptLoader() } });
}
var loadConfig = async (params) => {
  if (configState.config === void 0) {
    const cosmicConfigResult = await createExplorer().search();
    configState.config = cosmicConfigResult?.config ?? {};
    configState.rootConfigPath = cosmicConfigResult?.filepath;
    const configFilePath = cosmicConfigResult?.filepath;
    if (configFilePath !== void 0) {
      console.log(chalk.green(`Loaded config from ${configFilePath}`));
      if (Boolean(configState.config.verbose)) {
        console.log(chalk.gray(JSON.stringify(configState.config, null, 2)));
      }
    }
  }
  return deepMerge(configState.config, params ?? {});
};
async function loadWorkspaceConfig(workspaceDir) {
  const cached = workspaceConfigCache.get(workspaceDir);
  if (cached !== void 0) return cached;
  const result = await createExplorer().search(workspaceDir);
  if (!result || result.filepath === configState.rootConfigPath) {
    workspaceConfigCache.set(workspaceDir, {});
    return {};
  }
  const wsConfig = result.config ?? {};
  workspaceConfigCache.set(workspaceDir, wsConfig);
  return wsConfig;
}
var DEPRECATED_COMMAND_FIELDS = /* @__PURE__ */ new Set(["deplint", "publint"]);
function resolveCommandField(cfg, commandName, configPath) {
  const commands = cfg.commands;
  const fromCommands = commands?.[commandName];
  if (fromCommands !== void 0 && typeof fromCommands === "object") {
    return fromCommands;
  }
  const fromTopLevel = cfg[commandName];
  if (fromTopLevel !== void 0 && typeof fromTopLevel === "object" && DEPRECATED_COMMAND_FIELDS.has(commandName)) {
    const key = `${configPath ?? "unknown"}:${commandName}`;
    if (!deprecationWarned.has(key)) {
      deprecationWarned.add(key);
      console.warn(chalk.yellow(
        `[xy] Deprecated: top-level "${commandName}" in ${configPath ?? "xy.config"} \u2014 move to "commands.${commandName}"`
      ));
    }
    return fromTopLevel;
  }
  return {};
}
function mergeRuleConfigs(target, source) {
  if (isRecord(target) && isRecord(source)) {
    return { ...target, ...source };
  }
  return source;
}
function mergeCommandConfig(rootCmd, workspaceCmd, commandName) {
  return deepMerge(rootCmd, workspaceCmd, {
    customMerge: (key) => (commandName === "deplint" || commandName === "dead") && key === "rules" ? mergeRuleConfigs : void 0
  });
}
async function loadWorkspaceCommandConfig(workspaceDir, commandName) {
  const root = await loadConfig();
  const rootCmd = resolveCommandField(root, commandName, configState.rootConfigPath);
  const wsConfig = await loadWorkspaceConfig(workspaceDir);
  const wsConfigPath = workspaceConfigCache.has(workspaceDir) ? workspaceDir : void 0;
  const wsCmd = resolveCommandField(wsConfig, commandName, wsConfigPath);
  return mergeCommandConfig(rootCmd, wsCmd, commandName);
}

// src/lib/match.ts
import { minimatch } from "minimatch";
function makeMatcher(pattern, options = {}) {
  const patterns = Array.isArray(pattern) ? pattern : [pattern];
  return (input) => patterns.some((p) => minimatch(input, p, options));
}
function isMatch(input, pattern, options = {}) {
  return makeMatcher(pattern, options)(input);
}

// src/lib/reporting/xyReporter.ts
var reporterState = {
  currentCommand: "unknown",
  isEnvelopeEmitted: false,
  isJsonModeEnabled: false,
  isStrictModeEnabled: false,
  originalStdoutWrite: void 0,
  startTime: 0
};
function isJsonMode() {
  return reporterState.isJsonModeEnabled;
}

// src/actions/dead/fixDeadCode.ts
import fs5 from "node:fs";
import path4 from "node:path";
import chalk3 from "chalk";
import ts5 from "typescript";

// src/actions/dead/deprecation.ts
import ts from "typescript";
function getLeadingRanges(sourceFile, node) {
  const ranges = [
    ...ts.getLeadingCommentRanges(sourceFile.text, node.pos) ?? [],
    ...ts.getLeadingCommentRanges(sourceFile.text, node.getFullStart()) ?? []
  ];
  const seen = /* @__PURE__ */ new Set();
  return ranges.filter((range) => {
    if (seen.has(range.pos)) return false;
    seen.add(range.pos);
    return true;
  });
}
function hasDeprecatedTag(sourceFile, node) {
  for (const range of getLeadingRanges(sourceFile, node)) {
    const text = sourceFile.text.slice(range.pos, range.pos + range.end - range.pos);
    if (/@deprecated\b/.test(text)) return true;
  }
  const jsDocs = ts.getJSDocCommentsAndTags(node);
  return jsDocs.some((doc) => {
    if (!ts.isJSDoc(doc)) return false;
    return doc.tags?.some((tag) => tag.tagName.text === "deprecated") === true;
  });
}

// src/actions/dead/pruneDeadFiles.ts
import fs4 from "node:fs";
import path3 from "node:path";
import chalk2 from "chalk";
import ts4 from "typescript";

// src/actions/deplint/findFiles.ts
import fs from "node:fs";
import path from "node:path";
import ts2 from "typescript";

// src/actions/deplint/findFilesByGlob.ts
import { globSync as globSync2 } from "glob";
function findFilesByGlob(cwd, pattern, ignore) {
  return globSync2(pattern, {
    cwd,
    absolute: true,
    ignore,
    nodir: true,
    dot: true
  });
}

// src/actions/deplint/findFiles.ts
var codeExtensions = "*.{ts,tsx,mts,cts,js,mjs,cjs}";
var codeExtensionRe = /\.(?:ts|tsx|mts|cts|js|mjs|cjs)$/i;
var resolutionExtensions = [".mjs", ".cjs", ".js", ".d.mts", ".d.cts", ".d.ts", ".mts", ".cts", ".ts", ".tsx"];
var dotfileIgnorePatterns = [
  "**/.git/**",
  "**/.yarn/**",
  "**/.pnpm/**",
  "**/.pnpm-store/**",
  "**/.next/**",
  "**/.nuxt/**",
  "**/.svelte-kit/**",
  "**/.angular/**",
  "**/.vercel/**",
  "**/.netlify/**",
  "**/.turbo/**",
  "**/.cache/**",
  "**/.parcel-cache/**",
  "**/.docusaurus/**",
  "**/.vite/**",
  "**/.output/**"
];
function normalizeSourceExcludePattern(location, pattern) {
  const trimmed = pattern.trim();
  if (trimmed === "" || trimmed.startsWith("!")) return void 0;
  const relativePattern = path.isAbsolute(trimmed) ? path.relative(location, trimmed) : trimmed;
  if (relativePattern.startsWith("..")) return void 0;
  return relativePattern.replaceAll(path.sep, "/").replace(/^\.\//, "");
}
function toSourceIgnorePatterns(location, sourceExclude = []) {
  const patterns = /* @__PURE__ */ new Set();
  for (const exclude of sourceExclude) {
    const pattern = normalizeSourceExcludePattern(location, exclude);
    if (!(pattern != null && pattern.length > 0)) continue;
    patterns.add(pattern);
    if (!pattern.endsWith("/**")) patterns.add(`${pattern}/**`);
  }
  return [...patterns];
}
function createSourceFileFilter(location, sourceIgnorePatterns) {
  if (sourceIgnorePatterns.length === 0) return () => true;
  const isIgnored = makeMatcher(sourceIgnorePatterns, { dot: true });
  return (filePath) => {
    const relativePath = path.relative(location, filePath).replaceAll(path.sep, "/");
    return !isIgnored(relativePath);
  };
}
function readPackageJson(location) {
  try {
    return JSON.parse(fs.readFileSync(`${location}/package.json`, "utf8"));
  } catch {
    return void 0;
  }
}
function collectExportTargets(value) {
  if (typeof value === "string") return value.startsWith("./") ? [value] : [];
  if (Array.isArray(value)) return value.flatMap(collectExportTargets);
  if (typeof value !== "object" || value === null) return [];
  return Object.values(value).flatMap(collectExportTargets);
}
function getManifestRuntimeEntries(pkg) {
  return [
    ...collectExportTargets(pkg?.exports),
    pkg?.main,
    pkg?.module,
    pkg?.types,
    pkg?.typings
  ].filter((entry) => typeof entry === "string");
}
function getBinEntries(pkg) {
  const bin = pkg?.bin;
  if (bin === void 0) return [];
  return typeof bin === "string" ? [bin] : Object.values(bin).filter((entry) => typeof entry === "string");
}
function canResolveToCode(entry) {
  const ext = path.extname(entry);
  return ext === "" || codeExtensionRe.test(entry);
}
function resolveCodeFile(filePath) {
  if (fs.existsSync(filePath) && codeExtensionRe.test(filePath)) return filePath;
  const ext = path.extname(filePath);
  if (ext) {
    const withoutExt = filePath.slice(0, -ext.length);
    for (const candidateExt of resolutionExtensions) {
      const candidate = `${withoutExt}${candidateExt}`;
      if (fs.existsSync(candidate)) return candidate;
    }
  }
  for (const ext2 of resolutionExtensions) {
    const candidate = `${filePath}${ext2}`;
    if (fs.existsSync(candidate)) return candidate;
  }
  for (const ext2 of resolutionExtensions) {
    const candidate = path.join(filePath, `index${ext2}`);
    if (fs.existsSync(candidate)) return candidate;
  }
  return void 0;
}
function resolveRuntimeEntry(location, entry) {
  if (path.isAbsolute(entry)) return [];
  const relativeEntry = entry.startsWith(".") ? entry : `./${entry}`;
  if (relativeEntry.includes("*")) return findFilesByGlob(location, relativeEntry).filter((file) => codeExtensionRe.test(file));
  const resolved = resolveCodeFile(path.resolve(location, relativeEntry));
  return resolved != null && resolved.length > 0 ? [resolved] : [];
}
function collectLocalImportSpecifiers(filePath) {
  const sourceCode = fs.readFileSync(filePath, "utf8");
  const isJsFile = /\.(?:js|mjs|cjs)$/i.test(filePath);
  const sourceFile = ts2.createSourceFile(
    path.basename(filePath),
    sourceCode,
    ts2.ScriptTarget.Latest,
    true,
    isJsFile ? ts2.ScriptKind.JS : void 0
  );
  const imports = [];
  function addModuleSpecifier(moduleSpecifier) {
    if (moduleSpecifier && ts2.isStringLiteral(moduleSpecifier) && moduleSpecifier.text.startsWith(".")) {
      imports.push(moduleSpecifier.text);
    }
  }
  function visit(node) {
    if (ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node)) {
      addModuleSpecifier(node.moduleSpecifier);
    } else if (ts2.isCallExpression(node) && node.expression.kind === ts2.SyntaxKind.ImportKeyword) {
      addModuleSpecifier(node.arguments[0]);
    } else if (ts2.isCallExpression(node) && ts2.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length > 0) {
      addModuleSpecifier(node.arguments[0]);
    }
    ts2.forEachChild(node, visit);
  }
  visit(sourceFile);
  return imports;
}
function getReachableRuntimeFiles(runtimeRoots, shouldIncludeFile) {
  const seen = /* @__PURE__ */ new Set();
  const queue = runtimeRoots.filter(shouldIncludeFile);
  const result = [];
  while (queue.length > 0) {
    const file = queue.shift();
    if (!(file != null && file.length > 0) || seen.has(file)) continue;
    if (!shouldIncludeFile(file)) continue;
    seen.add(file);
    result.push(file);
    for (const specifier of collectLocalImportSpecifiers(file)) {
      const resolved = resolveCodeFile(path.resolve(path.dirname(file), specifier));
      if (resolved != null && resolved.length > 0 && shouldIncludeFile(resolved) && !seen.has(resolved)) queue.push(resolved);
    }
  }
  return result;
}
var launcherNodeTargetRe = /\bnode(?:\.exe)?\b[^\n]*?["']?([^"'\s]+\.(?:mjs|cjs|js))(?=["'\s]|$)/i;
var launcherDirVarRe = /^(?:\$\{?\w+\}?|%\w+%|%~dp0)\/?/;
var MAX_LAUNCHER_BYTES = 64 * 1024;
function resolveLauncherTarget(launcherAbs) {
  try {
    const stat = fs.statSync(launcherAbs);
    if (!stat.isFile() || stat.size > MAX_LAUNCHER_BYTES) return void 0;
    const match = launcherNodeTargetRe.exec(fs.readFileSync(launcherAbs, "utf8"));
    if (!match) return void 0;
    const target = match[1].replaceAll("\\", "/").replace(launcherDirVarRe, "");
    return resolveCodeFile(path.resolve(path.dirname(launcherAbs), target));
  } catch {
    return void 0;
  }
}
function getBinFiles(location, pkg) {
  const bin = pkg?.bin;
  if (bin === void 0) return [];
  const entries = typeof bin === "string" ? [bin] : Object.values(bin);
  const results = [];
  for (const entry of entries) {
    if (typeof entry !== "string") continue;
    const abs = path.resolve(location, entry);
    if (codeExtensionRe.test(abs)) {
      if (fs.existsSync(abs)) results.push(abs);
      continue;
    }
    if (!fs.existsSync(abs)) continue;
    const launcherTarget = resolveLauncherTarget(abs);
    if (launcherTarget != null && launcherTarget.length > 0) results.push(launcherTarget);
  }
  return results;
}
function appendUniqueFiles(list, seen, result) {
  for (const file of list) {
    if (seen.has(file)) continue;
    seen.add(file);
    result.push(file);
  }
}
function uniqueFiles(...lists) {
  const seen = /* @__PURE__ */ new Set();
  const result = [];
  for (const list of lists) {
    appendUniqueFiles(list, seen, result);
  }
  return result;
}
function findFiles(location, sourceExclude) {
  const pkg = readPackageJson(location);
  const workspaceIgnores = readWorkspacePatterns(location).filter((p) => !p.startsWith("!")).map((w) => `${w}/**`);
  const sourceIgnorePatterns = toSourceIgnorePatterns(location, sourceExclude);
  const shouldIncludeFile = createSourceFileFilter(location, sourceIgnorePatterns);
  const ignore = [
    "**/node_modules/**",
    "dist/**",
    ...dotfileIgnorePatterns,
    ...workspaceIgnores,
    ...sourceIgnorePatterns
  ];
  const allFiles = findFilesByGlob(location, `./**/${codeExtensions}`, ignore);
  const manifestRuntimeEntries = getManifestRuntimeEntries(pkg);
  const runtimeEntries = [...new Set([
    ...manifestRuntimeEntries,
    ...getBinEntries(pkg)
  ].filter(canResolveToCode))];
  const binFiles = getBinFiles(location, pkg);
  const manifestRuntimeFiles = manifestRuntimeEntries.flatMap((entry) => resolveRuntimeEntry(location, entry));
  const runtimeRoots = uniqueFiles(manifestRuntimeFiles, binFiles);
  const distFiles = getReachableRuntimeFiles(runtimeRoots, shouldIncludeFile);
  return {
    allFiles,
    distFiles,
    runtimeRoots: runtimeEntries
  };
}

// src/actions/dead/runtimeEntries.ts
import fs2 from "node:fs";
import path2 from "node:path";
var codeExtensionRe2 = /\.(?:ts|tsx|mts|cts|js|mjs|cjs)$/i;
var resolutionExtensions2 = [".mjs", ".cjs", ".js", ".d.mts", ".d.cts", ".d.ts", ".mts", ".cts", ".ts", ".tsx"];
function readPackageManifest(location) {
  try {
    return JSON.parse(fs2.readFileSync(path2.join(location, "package.json"), "utf8"));
  } catch {
    return void 0;
  }
}
function collectExportTargets2(value) {
  if (typeof value === "string") return value.startsWith("./") ? [value] : [];
  if (Array.isArray(value)) return value.flatMap(collectExportTargets2);
  if (typeof value !== "object" || value === null) return [];
  return Object.values(value).flatMap(collectExportTargets2);
}
function resolveImportSpecifierCandidates(fromFile, specifier) {
  const absolute = path2.resolve(path2.dirname(fromFile), specifier);
  const candidates = /* @__PURE__ */ new Set([absolute]);
  const ext = path2.extname(absolute);
  if (ext) {
    const withoutExt = absolute.slice(0, -ext.length);
    for (const candidateExt of resolutionExtensions2) {
      candidates.add(`${withoutExt}${candidateExt}`);
    }
  } else {
    for (const candidateExt of resolutionExtensions2) {
      candidates.add(`${absolute}${candidateExt}`);
    }
    for (const candidateExt of resolutionExtensions2) {
      candidates.add(path2.join(absolute, `index${candidateExt}`));
    }
  }
  return [...candidates].map((candidate) => path2.resolve(candidate));
}
function resolveCodeFile2(filePath) {
  if (fs2.existsSync(filePath) && codeExtensionRe2.test(filePath)) return filePath;
  const ext = path2.extname(filePath);
  if (ext) {
    const withoutExt = filePath.slice(0, -ext.length);
    for (const candidateExt of resolutionExtensions2) {
      const candidate = `${withoutExt}${candidateExt}`;
      if (fs2.existsSync(candidate)) return candidate;
    }
  }
  for (const candidateExt of resolutionExtensions2) {
    const candidate = `${filePath}${candidateExt}`;
    if (fs2.existsSync(candidate)) return candidate;
  }
  for (const candidateExt of resolutionExtensions2) {
    const candidate = path2.join(filePath, `index${candidateExt}`);
    if (fs2.existsSync(candidate)) return candidate;
  }
  return void 0;
}
function resolveManifestEntry(location, entry) {
  if (path2.isAbsolute(entry)) return void 0;
  if (entry.includes("*")) return void 0;
  const relativeEntry = entry.startsWith(".") ? entry : `./${entry}`;
  return resolveCodeFile2(path2.resolve(location, relativeEntry));
}
function getRuntimeRootFiles(location, manifest) {
  if (!manifest) return [];
  const candidates = [
    ...collectExportTargets2(manifest.exports),
    manifest.main,
    manifest.module,
    manifest.types,
    manifest.typings
  ].filter((entry) => typeof entry === "string");
  const bin = manifest.bin;
  if (bin !== void 0) {
    if (typeof bin === "string") candidates.push(bin);
    else for (const value of Object.values(bin)) if (typeof value === "string") candidates.push(value);
  }
  const seen = /* @__PURE__ */ new Set();
  const results = [];
  for (const entry of candidates) {
    const resolved = resolveManifestEntry(location, entry);
    if (!(resolved != null && resolved.length > 0) || seen.has(resolved)) continue;
    const distMapped = mapDistToSrc(location, resolved);
    const final = distMapped ?? resolved;
    if (seen.has(final)) continue;
    seen.add(final);
    results.push(final);
  }
  return results;
}
function mapDistToSrc(location, file) {
  const rel = path2.relative(location, file);
  if (!rel.startsWith("dist" + path2.sep) && !rel.startsWith("dist/")) return void 0;
  const tail = rel.slice("dist".length).replace(/^[\\/]/, "");
  const tailNoExt = tail.replace(/\.(?:mjs|cjs|js|d\.mts|d\.cts|d\.ts)$/i, "");
  const candidates = [
    path2.join(location, "src", tailNoExt),
    path2.join(location, "src", tailNoExt + ".ts"),
    path2.join(location, "src", tailNoExt + ".tsx"),
    path2.join(location, "src", tailNoExt + ".mts"),
    path2.join(location, "src", tailNoExt + ".cts")
  ];
  for (const candidate of candidates) {
    const resolved = resolveCodeFile2(candidate);
    if (resolved != null && resolved.length > 0) return resolved;
  }
  return void 0;
}

// src/actions/dead/sourceEdits.ts
import fs3 from "node:fs";
import ts3 from "typescript";
function loadSourceFile(filePath, sourceText = fs3.readFileSync(filePath, "utf8")) {
  const isJsLike = /\.(?:js|mjs|cjs)$/i.test(filePath);
  return ts3.createSourceFile(
    filePath,
    sourceText,
    ts3.ScriptTarget.Latest,
    true,
    isJsLike ? ts3.ScriptKind.JS : void 0
  );
}
function isEmptySourceFile(sourceText, filePath) {
  if (sourceText.trim() === "") return true;
  return loadSourceFile(filePath, sourceText).statements.length === 0;
}
function removeEndAfterStatement(sourceText, statementEnd) {
  let end = statementEnd;
  while (end < sourceText.length && (sourceText[end] === " " || sourceText[end] === "	")) {
    end += 1;
  }
  if (sourceText[end] === "\r") end += 1;
  if (sourceText[end] === "\n") end += 1;
  return end;
}
function removeStatementFromSource(sourceText, sourceFile, statement) {
  const rangeStart = statement.getFullStart();
  const rangeEnd = removeEndAfterStatement(sourceText, statement.end);
  return `${sourceText.slice(0, rangeStart)}${sourceText.slice(rangeEnd)}`;
}
function removeStatementsFromSource(sourceText, sourceFile, statements) {
  const sorted = [...statements].toSorted((a, b) => b.getFullStart() - a.getFullStart());
  let result = sourceText;
  for (const statement of sorted) {
    result = removeStatementFromSource(result, sourceFile, statement);
  }
  return result;
}

// src/actions/dead/pruneDeadFiles.ts
function getRelativeModuleSpecifier(statement) {
  if (ts4.isImportDeclaration(statement)) return statement.moduleSpecifier;
  if (ts4.isExportDeclaration(statement)) return statement.moduleSpecifier;
  return void 0;
}
function resolveRelativeImport(fromFile, specifier) {
  return resolveCodeFile2(path3.resolve(path3.dirname(fromFile), specifier));
}
function isModuleSpecifierTargetingDeleted(moduleSpecifier, fromFile, deletedFiles) {
  if (!ts4.isStringLiteral(moduleSpecifier)) return false;
  if (!moduleSpecifier.text.startsWith(".")) return false;
  return resolveImportSpecifierCandidates(fromFile, moduleSpecifier.text).some((candidate) => deletedFiles.has(candidate));
}
function collectImportStatementsTargeting(sourceFile, filePath, deletedFiles) {
  const statements = [];
  for (const statement of sourceFile.statements) {
    if (ts4.isImportDeclaration(statement) && isModuleSpecifierTargetingDeleted(statement.moduleSpecifier, filePath, deletedFiles)) {
      statements.push(statement);
    }
    if (ts4.isExportDeclaration(statement) && statement.moduleSpecifier && isModuleSpecifierTargetingDeleted(statement.moduleSpecifier, filePath, deletedFiles)) {
      statements.push(statement);
    }
  }
  return statements;
}
function stripImportsTargetingDeletedFiles(filePath, deletedFiles) {
  const sourceText = fs4.readFileSync(filePath, "utf8");
  const sourceFile = loadSourceFile(filePath, sourceText);
  const toRemove = collectImportStatementsTargeting(sourceFile, filePath, deletedFiles);
  if (toRemove.length === 0) return null;
  const updated = removeStatementsFromSource(sourceText, sourceFile, toRemove);
  return updated === sourceText ? null : updated;
}
function listPackageCodeFiles(packageLocation) {
  return findFiles(packageLocation).allFiles.map((file) => path3.resolve(file)).filter((file) => fs4.existsSync(file));
}
function indexImporterStatement(filePath, statement, addImporter) {
  const moduleSpecifier = getRelativeModuleSpecifier(statement);
  if (!moduleSpecifier || !ts4.isStringLiteral(moduleSpecifier) || !moduleSpecifier.text.startsWith(".")) return;
  const resolved = resolveRelativeImport(filePath, moduleSpecifier.text);
  if (resolved != null && resolved.length > 0) addImporter(resolved, filePath);
}
function indexImportersInFile(filePath, addImporter) {
  const sourceFile = loadSourceFile(filePath);
  for (const statement of sourceFile.statements) {
    indexImporterStatement(filePath, statement, addImporter);
  }
}
function indexImportersInPackage(packageLocation, addImporter) {
  for (const filePath of listPackageCodeFiles(packageLocation)) {
    indexImportersInFile(filePath, addImporter);
  }
}
function buildImporterIndex(analyses) {
  const importersByTarget = /* @__PURE__ */ new Map();
  const addImporter = (target, importer) => {
    const resolvedTarget = path3.resolve(target);
    const resolvedImporter = path3.resolve(importer);
    let bucket = importersByTarget.get(resolvedTarget);
    if (!bucket) {
      bucket = /* @__PURE__ */ new Set();
      importersByTarget.set(resolvedTarget, bucket);
    }
    bucket.add(resolvedImporter);
  };
  for (const analysis of analyses) {
    indexImportersInPackage(analysis.location, addImporter);
  }
  return importersByTarget;
}
function normalizeWrittenSource(sourceText) {
  return sourceText.endsWith("\n") ? sourceText : `${sourceText}
`;
}
function pruneEmptyDirectories(packageRoot) {
  const root = path3.resolve(packageRoot);
  let removed = 0;
  const visit = (dir) => {
    const entries = fs4.readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      if (entry.isDirectory()) {
        visit(path3.join(dir, entry.name));
      }
    }
    if (dir !== root && fs4.readdirSync(dir).length === 0) {
      fs4.rmdirSync(dir);
      removed += 1;
    }
  };
  visit(root);
  return removed;
}
function processImportersForDeletedFile(absolutePath, importersByTarget, deletedFiles, queueIfEmpty, isVerbose) {
  const importers = importersByTarget.get(absolutePath);
  importersByTarget.delete(absolutePath);
  if (!importers) return;
  for (const importer of importers) {
    updateImporterAfterTargetDeleted(importer, deletedFiles, queueIfEmpty, isVerbose);
  }
}
function updateImporterAfterTargetDeleted(importer, deletedFiles, queueIfEmpty, isVerbose) {
  if (deletedFiles.has(importer) || !fs4.existsSync(importer)) return;
  const updated = stripImportsTargetingDeletedFiles(importer, deletedFiles);
  if (updated === null) return;
  fs4.writeFileSync(importer, normalizeWrittenSource(updated), "utf8");
  if (isVerbose) console.log(chalk2.green(`  removed imports of deleted file from ${importer}`));
  queueIfEmpty(importer, updated);
}
function pruneEmptyFilesAfterRemoval(analyses, seedFiles, isVerbose = false) {
  const deletedFiles = /* @__PURE__ */ new Set();
  const pendingDeletion = /* @__PURE__ */ new Set();
  const importersByTarget = buildImporterIndex(analyses);
  const queueIfEmpty = (filePath, sourceText) => {
    const absolutePath = path3.resolve(filePath);
    if (deletedFiles.has(absolutePath) || !fs4.existsSync(absolutePath)) return;
    const text = sourceText ?? fs4.readFileSync(absolutePath, "utf8");
    if (isEmptySourceFile(text, absolutePath)) pendingDeletion.add(absolutePath);
  };
  for (const filePath of seedFiles) queueIfEmpty(filePath);
  while (pendingDeletion.size > 0) {
    const filePath = pendingDeletion.values().next().value;
    pendingDeletion.delete(filePath);
    const absolutePath = path3.resolve(filePath);
    if (deletedFiles.has(absolutePath) || !fs4.existsSync(absolutePath)) continue;
    if (!isEmptySourceFile(fs4.readFileSync(absolutePath, "utf8"), absolutePath)) continue;
    fs4.unlinkSync(absolutePath);
    deletedFiles.add(absolutePath);
    if (isVerbose) console.log(chalk2.green(`  deleted empty file ${absolutePath}`));
    processImportersForDeletedFile(absolutePath, importersByTarget, deletedFiles, queueIfEmpty, isVerbose);
  }
  let dirsRemoved = 0;
  for (const analysis of analyses) {
    dirsRemoved += pruneEmptyDirectories(analysis.location);
  }
  if (isVerbose && dirsRemoved > 0) {
    console.log(chalk2.green(`  removed ${dirsRemoved} empty director${dirsRemoved === 1 ? "y" : "ies"}`));
  }
  return {
    dirsRemoved,
    filesDeleted: deletedFiles.size
  };
}

// src/actions/dead/fixDeadCode.ts
var DEPRECATION_MESSAGE = "Flagged as dead code by xy dead";
function dedupeFindings(findings) {
  const seen = /* @__PURE__ */ new Set();
  const out = [];
  for (const finding of findings) {
    const key = `${finding.pkg}\0${finding.file}\0${finding.line}\0${finding.column}\0${finding.symbol}`;
    if (seen.has(key)) continue;
    seen.add(key);
    out.push(finding);
  }
  return out;
}
function resolveFixLocations(findings, analyses) {
  const locationByPackage = new Map(analyses.map((analysis) => [analysis.packageName, analysis.location]));
  return dedupeFindings(findings).flatMap((finding) => {
    const packageLocation = locationByPackage.get(finding.pkg);
    if (!(packageLocation != null && packageLocation.length > 0)) return [];
    return [{
      absolutePath: path4.resolve(packageLocation, finding.file),
      column: finding.column,
      file: finding.file,
      line: finding.line,
      symbol: finding.symbol
    }];
  });
}
function isNodeMatchingSymbol(node, symbol) {
  if (ts5.isIdentifier(node) && node.text === symbol) return true;
  if (ts5.isExportAssignment(node) && symbol === "default") return true;
  if (ts5.isExportSpecifier(node) && (node.name.text === symbol || node.propertyName?.text === symbol)) return true;
  return false;
}
function isStatementContainingSymbol(statement, symbol) {
  let isFound = false;
  const visit = (node) => {
    if (isFound) return;
    if (isNodeMatchingSymbol(node, symbol)) {
      isFound = true;
      return;
    }
    ts5.forEachChild(node, visit);
  };
  visit(statement);
  return isFound;
}
function getStatementNode(node) {
  let current = node;
  while (true) {
    const parent = current.parent;
    if (parent === void 0) return current;
    if (ts5.isSourceFile(parent)) return current;
    if (ts5.isBlock(parent)) return current;
    current = parent;
  }
}
function statementStartLine(sourceFile, statement) {
  return sourceFile.getLineAndCharacterOfPosition(statement.getStart(sourceFile)).line + 1;
}
function statementsForSymbol(sourceFile, symbol) {
  return sourceFile.statements.filter((statement) => isStatementContainingSymbol(statement, symbol));
}
function findTargetStatement(sourceFile, location) {
  const candidates = statementsForSymbol(sourceFile, location.symbol);
  if (candidates.length === 0) return void 0;
  const lineMatches = candidates.filter((statement) => statementStartLine(sourceFile, statement) === location.line);
  if (lineMatches.length === 1) return getStatementNode(lineMatches[0]);
  if (candidates.length === 1) return getStatementNode(candidates[0]);
  const lineIndex = location.line - 1;
  if (lineIndex >= 0 && lineIndex < sourceFile.getLineStarts().length) {
    const position = sourceFile.getPositionOfLineAndCharacter(lineIndex, Math.max(0, location.column - 1));
    let best;
    const visit = (node) => {
      const start = node.getStart(sourceFile);
      const end = node.getEnd();
      if (position < start || position >= end) return;
      if (isNodeMatchingSymbol(node, location.symbol)) {
        best = getStatementNode(node);
      }
      ts5.forEachChild(node, visit);
    };
    for (const statement of sourceFile.statements) {
      visit(statement);
    }
    if (best !== void 0 && isStatementContainingSymbol(best, location.symbol)) return best;
  }
  return getStatementNode(candidates[0]);
}
function sortFixLocations(locations) {
  return locations.toSorted((a, b) => {
    if (a.absolutePath !== b.absolutePath) return a.absolutePath.localeCompare(b.absolutePath);
    if (a.line !== b.line) return b.line - a.line;
    return b.column - a.column;
  });
}
function indentationAt(sourceFile, position) {
  const { line } = sourceFile.getLineAndCharacterOfPosition(position);
  const lineStart = sourceFile.getPositionOfLineAndCharacter(line, 0);
  const prefix = sourceFile.text.slice(lineStart, position);
  return /^[\t ]*/.exec(prefix)?.[0] ?? "";
}
function insertDeprecation(sourceText, sourceFile, statement) {
  const insertAt = statement.getStart(sourceFile, false);
  const indent = indentationAt(sourceFile, insertAt);
  const comment = `${indent}/** @deprecated ${DEPRECATION_MESSAGE} */
`;
  return `${sourceText.slice(0, insertAt)}${comment}${sourceText.slice(insertAt)}`;
}
function applyFixToLocation(location, mode) {
  if (!fs5.existsSync(location.absolutePath)) return "missing";
  const sourceText = fs5.readFileSync(location.absolutePath, "utf8");
  const sourceFile = loadSourceFile(location.absolutePath);
  const statement = findTargetStatement(sourceFile, location);
  if (!statement) return "missing";
  if (mode === "deprecate") {
    if (hasDeprecatedTag(sourceFile, statement)) return "skipped";
    const updated2 = insertDeprecation(sourceText, sourceFile, statement);
    if (updated2 === sourceText) return "skipped";
    fs5.writeFileSync(location.absolutePath, updated2, "utf8");
    return "applied";
  }
  const updated = removeStatementFromSource(sourceText, sourceFile, statement);
  if (updated === sourceText) return "skipped";
  fs5.writeFileSync(location.absolutePath, updated.endsWith("\n") ? updated : `${updated}
`, "utf8");
  return "applied";
}
function logFixOutcome(outcome, location, mode, isVerbose) {
  if (!isVerbose) return;
  if (outcome === "applied") {
    const action = mode === "deprecate" ? "deprecated" : "removed";
    console.log(chalk3.green(`  ${action} ${location.symbol} at ${location.file}:${location.line}`));
    return;
  }
  if (outcome === "skipped") {
    console.log(chalk3.gray(`  skipped ${location.symbol} at ${location.file}:${location.line}`));
    return;
  }
  console.log(chalk3.yellow(`  could not fix ${location.symbol} at ${location.file}:${location.line}`));
}
function logFixSummary(mode, applied, files, filesDeleted, dirsRemoved) {
  const action = mode === "deprecate" ? "Deprecated" : "Removed";
  console.log(chalk3.green(`${action} ${applied} dead symbol${applied === 1 ? "" : "s"} in ${files.size} file${files.size === 1 ? "" : "s"}`));
  if ((filesDeleted ?? 0) > 0) {
    console.log(chalk3.green(`Deleted ${filesDeleted} empty file${filesDeleted === 1 ? "" : "s"}`));
  }
  if ((dirsRemoved ?? 0) > 0) {
    console.log(chalk3.green(`Removed ${dirsRemoved} empty director${dirsRemoved === 1 ? "y" : "ies"}`));
  }
}
function applyDeadFixes(findings, analyses, mode, isVerbose = false) {
  const locations = sortFixLocations(resolveFixLocations(findings, analyses));
  const files = /* @__PURE__ */ new Set();
  let applied = 0;
  let skipped = 0;
  for (const location of locations) {
    const outcome = applyFixToLocation(location, mode);
    if (outcome === "applied") {
      applied += 1;
      files.add(location.absolutePath);
    } else if (outcome === "skipped") {
      skipped += 1;
    }
    logFixOutcome(outcome, location, mode, isVerbose);
  }
  let filesDeleted;
  let dirsRemoved;
  if (mode === "remove" && (applied > 0 || files.size > 0)) {
    const pruned = pruneEmptyFilesAfterRemoval(analyses, files, isVerbose);
    filesDeleted = pruned.filesDeleted;
    dirsRemoved = pruned.dirsRemoved;
  }
  if (!isVerbose && applied > 0) {
    logFixSummary(mode, applied, files, filesDeleted, dirsRemoved);
  }
  return {
    applied,
    dirsRemoved,
    files: [...files],
    filesDeleted,
    skipped
  };
}

// src/actions/dead/reporters.ts
import chalk4 from "chalk";

// src/actions/dead/ruleNames.ts
function deadRuleShortName(ruleId) {
  const parts = ruleId.split(".");
  return parts.slice(2).join(".");
}

// src/actions/dead/reporters.ts
function groupBy(items, keyFn) {
  const out = /* @__PURE__ */ new Map();
  for (const item of items) {
    const key = keyFn(item);
    let bucket = out.get(key);
    if (!bucket) {
      bucket = [];
      out.set(key, bucket);
    }
    bucket.push(item);
  }
  return out;
}
function renderFindingLabel(finding) {
  return `${finding.scope}/${deadRuleShortName(finding.rule)}`;
}
function renderDeprecatedColumn(finding) {
  return finding.deprecated === true ? chalk4.gray("(deprecated)") : "";
}
function renderFinding(finding) {
  const loc = `${finding.file}:${finding.line}:${finding.column}`;
  const label = chalk4.yellow(renderFindingLabel(finding).padEnd(28));
  const symbolCol = finding.symbol.padEnd(28);
  const deprecatedCol = renderDeprecatedColumn(finding).padEnd(14);
  const detail = finding.detail != null && finding.detail.length > 0 ? chalk4.gray(`  (${finding.detail})`) : "";
  console.log(`  ${loc.padEnd(40)} ${label} ${symbolCol}${deprecatedCol}${detail}`);
}
function renderFindingsByPackage(findings) {
  const byPkg = groupBy(findings, (f) => f.pkg);
  for (const [pkg, packageFindings] of byPkg) {
    console.log(chalk4.green(`Dead Code [${pkg}]`));
    packageFindings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column);
    for (const finding of packageFindings) renderFinding(finding);
  }
}
function renderDisabledWorkspaceRules(disabledWorkspaceRules) {
  if (disabledWorkspaceRules.length === 0) return;
  console.log(chalk4.gray(`  workspace rules disabled (no code-workspace): ${disabledWorkspaceRules.join(", ")}`));
}
function renderSummaryFooter(result) {
  const { summary } = result;
  const byScope = groupBy(result.findings, (f) => f.scope);
  console.log(chalk4.gray(`
  scanned ${summary.scannedFiles} files in ${summary.scannedPackages} package${summary.scannedPackages === 1 ? "" : "s"}`));
  const parts = [...byScope].map(([scope, scopedFindings]) => `${scopedFindings.length} ${scope}`);
  if (parts.length > 0) console.log(chalk4.yellow(`  ${parts.join(", ")}`));
  if (summary.errorCount > 0 || summary.warningCount > 0) {
    console.log(chalk4.gray(`  ${summary.errorCount} error${summary.errorCount === 1 ? "" : "s"}, ${summary.warningCount} warning${summary.warningCount === 1 ? "" : "s"}`));
  }
  if ((summary.fixApplied ?? 0) > 0) {
    const mode = summary.fixMode === "remove" ? "removed" : "deprecated";
    console.log(chalk4.green(`  ${mode} ${summary.fixApplied} symbol${summary.fixApplied === 1 ? "" : "s"}`));
  }
  if (summary.workspaceFile != null && summary.workspaceFile.length > 0) {
    console.log(chalk4.gray(`  workspace: ${summary.workspaceFile}`));
  }
  renderDisabledWorkspaceRules(summary.disabledWorkspaceRules);
}
function renderHuman(result) {
  if (result.findings.length === 0) {
    console.log(chalk4.green(`Dead Code: no findings (${result.summary.scannedFiles} files in ${result.summary.scannedPackages} package${result.summary.scannedPackages === 1 ? "" : "s"})`));
    renderDisabledWorkspaceRules(result.summary.disabledWorkspaceRules);
    return;
  }
  renderFindingsByPackage(result.findings);
  renderSummaryFooter(result);
}
function getDeadReporter(format) {
  if (format === "json") {
    return {
      render(result) {
        console.log(JSON.stringify(result, null, 2));
      }
    };
  }
  return { render: renderHuman };
}

// src/actions/dead/runDeadAnalysis.ts
import fs11 from "node:fs";
import path12 from "node:path";
import chalk5 from "chalk";

// src/actions/dead/analyzePackage.ts
import path6 from "node:path";

// src/actions/dead/classifyPackageVisibility.ts
function isScopedPackageName(name) {
  return typeof name === "string" && name.startsWith("@");
}
function classifyPackageVisibility(manifest) {
  if (manifest?.private === true) return "private";
  const access = manifest?.publishConfig;
  if (typeof access === "object" && access !== null && !Array.isArray(access)) {
    const publishAccess = access.access;
    if (publishAccess === "restricted") return "restricted";
    if (publishAccess === "public") return "public";
  }
  if (isScopedPackageName(manifest?.name)) return "restricted";
  return "public";
}

// src/actions/dead/collectSymbols.ts
import fs6 from "node:fs";
import path5 from "node:path";
import ts6 from "typescript";
function getLineCol(sourceFile, pos) {
  const lc = sourceFile.getLineAndCharacterOfPosition(pos);
  return { column: lc.character + 1, line: lc.line + 1 };
}
function hasExportModifier(node) {
  if (!ts6.canHaveModifiers(node)) return false;
  const mods = ts6.getModifiers(node);
  if (!mods) return false;
  return mods.some((m) => m.kind === ts6.SyntaxKind.ExportKeyword);
}
function hasDefaultModifier(node) {
  if (!ts6.canHaveModifiers(node)) return false;
  const mods = ts6.getModifiers(node);
  if (!mods) return false;
  return mods.some((m) => m.kind === ts6.SyntaxKind.DefaultKeyword);
}
function pushDeclaration(declarations, sourceFile, name, nameNode, isExported, exportedAs, declarationNode) {
  const { line, column } = getLineCol(sourceFile, nameNode.getStart(sourceFile));
  const deprecated = hasDeprecatedTag(sourceFile, declarationNode) ? true : void 0;
  declarations.push({
    column,
    deprecated,
    isExported,
    exportedAs,
    line,
    name
  });
}
function isFunctionOverloadSignature(node) {
  return node.body === void 0;
}
function isGlobalAugmentation(node) {
  return ts6.isIdentifier(node.name) && node.name.text === "global";
}
function tryNamedDeclaration(node) {
  if (ts6.isFunctionDeclaration(node) && node.name) {
    if (isFunctionOverloadSignature(node)) return void 0;
    return { name: node.name };
  }
  if (ts6.isClassDeclaration(node) && node.name) return { name: node.name };
  if (ts6.isInterfaceDeclaration(node)) return { name: node.name };
  if (ts6.isTypeAliasDeclaration(node)) return { name: node.name };
  if (ts6.isEnumDeclaration(node)) return { name: node.name };
  if (ts6.isModuleDeclaration(node) && ts6.isIdentifier(node.name)) {
    if (isGlobalAugmentation(node)) return void 0;
    return { name: node.name };
  }
  return void 0;
}
function collectBindingNames(node, push) {
  if (ts6.isIdentifier(node)) {
    push(node.text);
    return;
  }
  if (ts6.isObjectBindingPattern(node) || ts6.isArrayBindingPattern(node)) {
    for (const el of node.elements) {
      if (ts6.isBindingElement(el)) collectBindingNames(el.name, push);
    }
  }
}
function handleVariableStatement(node, declarations, sourceFile) {
  const isExported = hasExportModifier(node);
  for (const decl of node.declarationList.declarations) {
    collectBindingNames(decl.name, (name) => {
      pushDeclaration(declarations, sourceFile, name, decl.name, isExported, name, node);
    });
  }
}
function handleExportAssignment(node, declarations, sourceFile) {
  if (ts6.isIdentifier(node.expression)) {
    const name = node.expression.text;
    const existing = declarations.find((d) => d.name === name);
    if (existing) {
      existing.isExported = true;
      existing.exportedAs = "default";
    }
  }
  return getLineCol(sourceFile, node.getStart(sourceFile));
}
function handleNamedExportClauseWithModule(clause, moduleSpec, reExportsFromRelative) {
  for (const element of clause.elements) {
    const exportedName = element.name.text;
    const importedName = element.propertyName?.text ?? exportedName;
    reExportsFromRelative.push({
      exportedAs: exportedName,
      imported: importedName,
      specifier: moduleSpec
    });
  }
}
function markExportedDeclarations(declarations, importedName, exportedName) {
  for (const existing of declarations) {
    if (existing.name !== importedName) continue;
    existing.isExported = true;
    existing.exportedAs = exportedName;
  }
}
function handleNamedExportClauseLocal(clause, declarations) {
  for (const element of clause.elements) {
    const exportedName = element.name.text;
    const importedName = element.propertyName?.text ?? exportedName;
    markExportedDeclarations(declarations, importedName, exportedName);
  }
}
function handleExportDeclaration(node, declarations, reExportsFromRelative, reExportAllFromRelative) {
  const moduleSpec = node.moduleSpecifier && ts6.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : void 0;
  const isRelative = moduleSpec?.startsWith(".") ?? false;
  if (node.exportClause && ts6.isNamedExports(node.exportClause)) {
    if (moduleSpec != null && moduleSpec.length > 0 && isRelative) {
      handleNamedExportClauseWithModule(node.exportClause, moduleSpec, reExportsFromRelative);
    } else if (!(moduleSpec != null && moduleSpec.length > 0)) {
      handleNamedExportClauseLocal(node.exportClause, declarations);
    }
    return;
  }
  if (!node.exportClause && (moduleSpec != null && moduleSpec.length > 0) && isRelative) {
    reExportAllFromRelative.add(moduleSpec);
  }
}
function visitTopLevel(node, ctx) {
  const named = tryNamedDeclaration(node);
  if (named?.name) {
    pushDeclaration(
      ctx.declarations,
      ctx.sourceFile,
      named.name.text,
      named.name,
      hasExportModifier(node),
      hasDefaultModifier(node) ? "default" : named.name.text,
      node
    );
    return;
  }
  if (ts6.isVariableStatement(node)) {
    handleVariableStatement(node, ctx.declarations, ctx.sourceFile);
    return;
  }
  if (ts6.isExportAssignment(node)) {
    ctx.setDefaultExport(handleExportAssignment(node, ctx.declarations, ctx.sourceFile));
    return;
  }
  if (ts6.isExportDeclaration(node)) {
    handleExportDeclaration(node, ctx.declarations, ctx.reExportsFromRelative, ctx.reExportAllFromRelative);
  }
}
function handleNamedImports(spec, clause, ctx) {
  for (const element of clause.elements) {
    const importedName = element.propertyName?.text ?? element.name.text;
    const alias = element.name.text;
    ctx.importBindingNames.add(alias);
    ctx.namedImportsFromRelative.push({
      alias,
      imported: importedName,
      specifier: spec
    });
  }
}
function visitImports(node, ctx) {
  if (!ts6.isImportDeclaration(node)) return;
  if (!ts6.isStringLiteral(node.moduleSpecifier)) return;
  const spec = node.moduleSpecifier.text;
  if (!spec.startsWith(".")) return;
  const clause = node.importClause;
  if (!clause) {
    ctx.sideEffectFromRelative.add(spec);
    return;
  }
  if (clause.name) {
    const alias = clause.name.text;
    ctx.importBindingNames.add(alias);
    ctx.namedImportsFromRelative.push({
      alias,
      imported: "default",
      specifier: spec
    });
  }
  if (clause.namedBindings) {
    if (ts6.isNamespaceImport(clause.namedBindings)) {
      ctx.importBindingNames.add(clause.namedBindings.name.text);
      ctx.namespaceImportFromRelative.add(spec);
    } else if (ts6.isNamedImports(clause.namedBindings)) {
      handleNamedImports(spec, clause.namedBindings, ctx);
    }
  }
}
function isTopLevelDeclarationName(node, parent) {
  if (ts6.isFunctionDeclaration(parent) && parent.name === node) return true;
  if (ts6.isClassDeclaration(parent) && parent.name === node) return true;
  if (ts6.isInterfaceDeclaration(parent) && parent.name === node) return true;
  if (ts6.isTypeAliasDeclaration(parent) && parent.name === node) return true;
  if (ts6.isEnumDeclaration(parent) && parent.name === node) return true;
  if (ts6.isModuleDeclaration(parent) && parent.name === node) return true;
  return false;
}
function isLocalBindingName(node, parent) {
  if (ts6.isVariableDeclaration(parent) && parent.name === node) return true;
  if (ts6.isParameter(parent) && parent.name === node) return true;
  if (ts6.isBindingElement(parent) && parent.name === node) return true;
  return false;
}
function isMemberName(node, parent) {
  if (ts6.isPropertyAssignment(parent) && parent.name === node) return true;
  if (ts6.isPropertyDeclaration(parent) && parent.name === node) return true;
  if (ts6.isMethodDeclaration(parent) && parent.name === node) return true;
  if (ts6.isPropertySignature(parent) && parent.name === node) return true;
  if (ts6.isMethodSignature(parent) && parent.name === node) return true;
  return false;
}
function isImportExportBindingName(node, parent) {
  if (ts6.isImportSpecifier(parent) && (parent.name === node || parent.propertyName === node)) return true;
  if (ts6.isImportClause(parent) && parent.name === node) return true;
  if (ts6.isNamespaceImport(parent) && parent.name === node) return true;
  if (ts6.isExportSpecifier(parent) && (parent.name === node || parent.propertyName === node)) return true;
  return false;
}
function isQualifiedMemberAccess(node, parent) {
  if (ts6.isQualifiedName(parent) && parent.right === node) return true;
  if (ts6.isPropertyAccessExpression(parent) && parent.name === node) return true;
  return false;
}
function isDeclarationOrBindingPosition(node, parent) {
  return isTopLevelDeclarationName(node, parent) || isLocalBindingName(node, parent) || isMemberName(node, parent) || isImportExportBindingName(node, parent) || isQualifiedMemberAccess(node, parent);
}
function visitForReferences(node, identifierRefs) {
  if (ts6.isIdentifier(node)) {
    const parent = node.parent;
    if (!isDeclarationOrBindingPosition(node, parent)) {
      identifierRefs.add(node.text);
    }
  }
  ts6.forEachChild(node, (child) => visitForReferences(child, identifierRefs));
}
function collectSymbolsFromFile(filePath) {
  const sourceText = fs6.readFileSync(filePath, "utf8");
  const isJsLike = /\.(?:js|mjs|cjs)$/i.test(filePath);
  const sourceFile = ts6.createSourceFile(
    path5.basename(filePath),
    sourceText,
    ts6.ScriptTarget.Latest,
    true,
    isJsLike ? ts6.ScriptKind.JS : void 0
  );
  const declarations = [];
  const identifierRefs = /* @__PURE__ */ new Set();
  const namedImportsFromRelative = [];
  const namespaceImportFromRelative = /* @__PURE__ */ new Set();
  const sideEffectFromRelative = /* @__PURE__ */ new Set();
  const reExportsFromRelative = [];
  const reExportAllFromRelative = /* @__PURE__ */ new Set();
  let defaultExport;
  const importBindingNames = /* @__PURE__ */ new Set();
  const topCtx = {
    declarations,
    reExportAllFromRelative,
    reExportsFromRelative,
    setDefaultExport: (pos) => {
      defaultExport = pos;
    },
    sourceFile
  };
  const importCtx = {
    importBindingNames,
    namedImportsFromRelative,
    namespaceImportFromRelative,
    sideEffectFromRelative
  };
  for (const stmt of sourceFile.statements) {
    visitTopLevel(stmt, topCtx);
    visitImports(stmt, importCtx);
  }
  visitForReferences(sourceFile, identifierRefs);
  return {
    declarations,
    defaultExport,
    identifierRefs,
    namedImportsFromRelative,
    namespaceImportFromRelative,
    reExportAllFromRelative,
    reExportsFromRelative,
    sideEffectFromRelative
  };
}

// src/actions/deplint/getStorybookConfigImports.ts
import ts7 from "typescript";

// src/actions/deplint/getBasePackageName.ts
function getBasePackageName(importName) {
  const importNameScrubbed = importName.replaceAll('"', "").trim();
  if (importNameScrubbed.startsWith("@")) {
    const parts = importNameScrubbed.split("/");
    return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : importNameScrubbed;
  }
  return importNameScrubbed.split("/", 1)[0];
}

// src/actions/deplint/getStorybookConfigImports.ts
var storybookMainPattern = /(?:^|[\\/])\.storybook[\\/]main\.[cm]?[jt]sx?$/;
function isStorybookMainFile(filePath) {
  return storybookMainPattern.test(filePath);
}

// src/actions/dead/storybookConventions.ts
var storyFilePattern = /\.stories\.[cm]?[jt]sx?$/i;
var storybookPreviewPattern = /(?:^|[\\/])\.storybook[\\/]preview\.[cm]?[jt]sx?$/i;
var storybookManagerPattern = /(?:^|[\\/])\.storybook[\\/]manager\.[cm]?[jt]sx?$/i;
function isStorybookConventionFile(filePath) {
  return storyFilePattern.test(filePath) || storybookPreviewPattern.test(filePath) || storybookManagerPattern.test(filePath) || isStorybookMainFile(filePath);
}

// src/actions/dead/analyzePackage.ts
function isAnalyzableFile(file) {
  if (!codeExtensionRe2.test(file)) return false;
  if (file.endsWith(".d.ts") || file.endsWith(".d.mts") || file.endsWith(".d.cts")) return false;
  return true;
}
function resolveRelativeImport2(fromFile, specifier) {
  return resolveCodeFile2(path6.resolve(path6.dirname(fromFile), specifier));
}
function loadReports(files) {
  const reports = [];
  for (const file of files) {
    if (!isAnalyzableFile(file)) continue;
    const symbols = collectSymbolsFromFile(file);
    const resolvedRelative = /* @__PURE__ */ new Map();
    const specifiers = /* @__PURE__ */ new Set([
      ...symbols.namedImportsFromRelative.map((i) => i.specifier),
      ...symbols.namespaceImportFromRelative,
      ...symbols.sideEffectFromRelative,
      ...symbols.reExportsFromRelative.map((r) => r.specifier),
      ...symbols.reExportAllFromRelative
    ]);
    for (const spec of specifiers) {
      const resolved = resolveRelativeImport2(file, spec);
      if (resolved != null && resolved.length > 0) resolvedRelative.set(spec, resolved);
    }
    reports.push({
      file,
      resolvedRelative,
      symbols
    });
  }
  return reports;
}
function computeCrossFileUsage(reports) {
  const namedSymbolUsage = /* @__PURE__ */ new Map();
  const filesWithFullExportUse = /* @__PURE__ */ new Set();
  function addNamed(targetFile, name) {
    let bucket = namedSymbolUsage.get(targetFile);
    if (!bucket) {
      bucket = /* @__PURE__ */ new Set();
      namedSymbolUsage.set(targetFile, bucket);
    }
    bucket.add(name);
  }
  for (const report of reports) {
    for (const named of report.symbols.namedImportsFromRelative) {
      const target = report.resolvedRelative.get(named.specifier);
      if (target != null && target.length > 0) addNamed(target, named.imported);
    }
    for (const reExport of report.symbols.reExportsFromRelative) {
      const target = report.resolvedRelative.get(reExport.specifier);
      if (target != null && target.length > 0) addNamed(target, reExport.imported);
    }
    for (const spec of report.symbols.namespaceImportFromRelative) {
      const target = report.resolvedRelative.get(spec);
      if (target != null && target.length > 0) filesWithFullExportUse.add(target);
    }
    for (const spec of report.symbols.reExportAllFromRelative) {
      const target = report.resolvedRelative.get(spec);
      if (target != null && target.length > 0) filesWithFullExportUse.add(target);
    }
  }
  return { filesWithFullExportUse, namedSymbolUsage };
}
function addStarDeclarations(file, report, out) {
  for (const decl of report.symbols.declarations) {
    if (!decl.isExported || !(decl.exportedAs != null && decl.exportedAs.length > 0) || decl.exportedAs === "default") continue;
    out.set(decl.exportedAs, {
      column: decl.column,
      file,
      line: decl.line
    });
  }
}
function addNamedStarReExports(file, report, ctx, out) {
  for (const reExport of report.symbols.reExportsFromRelative) {
    if (reExport.exportedAs === "default") continue;
    const target = report.resolvedRelative.get(reExport.specifier);
    const origin = target != null && target.length > 0 ? findOrigin(target, reExport.imported, ctx) : void 0;
    out.set(reExport.exportedAs, origin ?? {
      column: 1,
      file,
      line: 1
    });
  }
}
function addChildStarExports(report, ctx, out) {
  for (const spec of report.symbols.reExportAllFromRelative) {
    const target = report.resolvedRelative.get(spec);
    if (!(target != null && target.length > 0)) continue;
    const child = getStarExports(target, ctx);
    for (const [name, origin] of child) {
      if (!out.has(name)) out.set(name, origin);
    }
  }
}
function getStarExports(file, ctx) {
  const cached = ctx.starExportMemo.get(file);
  if (cached !== void 0) return cached;
  if (ctx.visiting.has(file)) return /* @__PURE__ */ new Map();
  ctx.visiting.add(file);
  const out = /* @__PURE__ */ new Map();
  const report = ctx.byFile.get(file);
  if (report !== void 0) {
    addStarDeclarations(file, report, out);
    addNamedStarReExports(file, report, ctx, out);
    addChildStarExports(report, ctx, out);
  }
  ctx.visiting.delete(file);
  ctx.starExportMemo.set(file, out);
  return out;
}
function findOrigin(targetFile, name, ctx) {
  const stars = getStarExports(targetFile, ctx);
  return stars.get(name);
}
function addDirectDeclarations(report, rootFile, results) {
  for (const decl of report.symbols.declarations) {
    if (!decl.isExported || !(decl.exportedAs != null && decl.exportedAs.length > 0)) continue;
    const name = decl.exportedAs;
    if (results.has(name)) continue;
    results.set(name, {
      column: decl.column,
      file: rootFile,
      line: decl.line,
      name,
      rootFile
    });
  }
}
function addDefaultExport(report, rootFile, results) {
  const def = report.symbols.defaultExport;
  if (!def || results.has("default")) return;
  results.set("default", {
    column: def.column,
    file: rootFile,
    line: def.line,
    name: "default",
    rootFile
  });
}
function addReExports(report, rootFile, ctx, results) {
  for (const reExport of report.symbols.reExportsFromRelative) {
    const exportedAs = reExport.exportedAs;
    if (results.has(exportedAs)) continue;
    const target = report.resolvedRelative.get(reExport.specifier);
    const origin = target != null && target.length > 0 ? findOrigin(target, reExport.imported, ctx) : void 0;
    results.set(exportedAs, {
      column: origin?.column ?? 1,
      file: origin?.file ?? rootFile,
      line: origin?.line ?? 1,
      name: exportedAs,
      rootFile
    });
  }
}
function addReExportAll(report, rootFile, ctx, results) {
  for (const spec of report.symbols.reExportAllFromRelative) {
    const target = report.resolvedRelative.get(spec);
    if (!(target != null && target.length > 0)) continue;
    const stars = getStarExports(target, ctx);
    const pendingStars = [...stars].filter(([name]) => !results.has(name));
    for (const [name, origin] of pendingStars) {
      results.set(name, {
        column: origin.column,
        file: origin.file,
        line: origin.line,
        name,
        rootFile
      });
    }
  }
}
function collectPublicExports(reports, runtimeRoots) {
  const byFile = new Map(reports.map((r) => [r.file, r]));
  const ctx = {
    byFile,
    results: /* @__PURE__ */ new Map(),
    starExportMemo: /* @__PURE__ */ new Map(),
    visiting: /* @__PURE__ */ new Set()
  };
  const results = /* @__PURE__ */ new Map();
  for (const rootFile of runtimeRoots) {
    const report = byFile.get(rootFile);
    if (!report) continue;
    addDirectDeclarations(report, rootFile, results);
    addDefaultExport(report, rootFile, results);
    addReExports(report, rootFile, ctx, results);
    addReExportAll(report, rootFile, ctx, results);
  }
  return results.values().toArray();
}
function buildPublicSurfaceKeys(publicExports) {
  const keys = /* @__PURE__ */ new Set();
  for (const exp of publicExports) {
    keys.add(`${exp.file}\0${exp.name}`);
    keys.add(`${exp.file}\0${exp.line}\0${exp.column}`);
  }
  return keys;
}
function isOnPublicSurface(decl, file, publicSurfaceKeys) {
  const exportedAs = decl.exportedAs ?? decl.name;
  if (publicSurfaceKeys.has(`${file}\0${exportedAs}`)) return true;
  return publicSurfaceKeys.has(`${file}\0${decl.line}\0${decl.column}`);
}
function isUsedInPackage(decl, file, hasFileFullExportUse, namedUsesForFile, report) {
  if (report.symbols.identifierRefs.has(decl.name)) return true;
  if (decl.isExported && isStorybookConventionFile(file)) return true;
  if (!decl.isExported) return false;
  const exportedAs = decl.exportedAs ?? decl.name;
  if (namedUsesForFile.has(exportedAs) || namedUsesForFile.has(decl.name)) return true;
  return hasFileFullExportUse;
}
function buildAnalyzedSymbol(decl, file, hasFileFullExportUse, namedUsesForFile, report, publicSurfaceKeys) {
  const isSymbolUsedInPackage = isUsedInPackage(decl, file, hasFileFullExportUse, namedUsesForFile, report);
  return {
    column: decl.column,
    deprecated: decl.deprecated,
    exportedAs: decl.isExported ? decl.exportedAs ?? decl.name : void 0,
    file,
    line: decl.line,
    name: decl.name,
    onPublicSurface: isOnPublicSurface(decl, file, publicSurfaceKeys),
    usedInMonorepo: isSymbolUsedInPackage,
    isUsedInPackage: isSymbolUsedInPackage
  };
}
function applyMonorepoUsage(analyses, repoUsage) {
  return analyses.map((analysis) => {
    const pkgUsage = repoUsage.get(analysis.packageName);
    if (!pkgUsage) return analysis;
    return {
      ...analysis,
      symbols: analysis.symbols.map((symbol) => {
        if (symbol.usedInMonorepo || symbol.onPublicSurface) return symbol;
        const exportedAs = symbol.exportedAs;
        if (!(exportedAs != null && exportedAs.length > 0)) return symbol;
        const isConsumed2 = pkgUsage.hasNamespace || exportedAs === "default" && pkgUsage.hasDefault || pkgUsage.named.has(exportedAs);
        return isConsumed2 ? { ...symbol, usedInMonorepo: true } : symbol;
      })
    };
  });
}
function analyzePackage(workspace) {
  const location = path6.resolve(workspace.location);
  const manifest = readPackageManifest(location);
  const manifestName = manifest && typeof manifest.name === "string" ? manifest.name : void 0;
  const packageName = manifestName ?? workspace.name;
  const { allFiles } = findFiles(location);
  const reports = loadReports(allFiles);
  const runtimeRoots = getRuntimeRootFiles(location, manifest);
  const usage = computeCrossFileUsage(reports);
  const publicExports = collectPublicExports(reports, runtimeRoots);
  const publicSurfaceKeys = buildPublicSurfaceKeys(publicExports);
  const symbols = [];
  for (const report of reports) {
    const isFileHasFullExportUse = usage.filesWithFullExportUse.has(report.file);
    const namedUsesForFile = usage.namedSymbolUsage.get(report.file) ?? /* @__PURE__ */ new Set();
    for (const decl of report.symbols.declarations) {
      symbols.push(buildAnalyzedSymbol(
        decl,
        report.file,
        isFileHasFullExportUse,
        namedUsesForFile,
        report,
        publicSurfaceKeys
      ));
    }
  }
  return {
    location,
    packageName,
    publicExports,
    scannedFiles: reports.length,
    symbols,
    visibility: classifyPackageVisibility(manifest),
    workspaceName: workspace.name
  };
}

// src/actions/dead/collectExternalUsage.ts
import fs7 from "node:fs";
import path7 from "node:path";
import ts8 from "typescript";
function getOrCreateUsage(map, pkg) {
  let entry = map.get(pkg);
  if (!entry) {
    entry = {
      hasDefault: false,
      hasNamespace: false,
      named: /* @__PURE__ */ new Set()
    };
    map.set(pkg, entry);
  }
  return entry;
}
function recordSpecifier(summary, targets, out) {
  const base = getBasePackageName(summary.spec);
  if (!targets.has(base)) return;
  const usage = getOrCreateUsage(out, base);
  if (summary.namespace) usage.hasNamespace = true;
  if (summary.defaultBinding) usage.hasDefault = true;
  for (const n of summary.named) {
    usage.named.add(n.imported === "default" ? "default" : n.imported);
  }
}
function summarizeImportDeclaration(node) {
  if (!ts8.isStringLiteral(node.moduleSpecifier)) return void 0;
  const summary = {
    defaultBinding: false,
    named: [],
    namespace: false,
    spec: node.moduleSpecifier.text
  };
  const clause = node.importClause;
  if (!clause) return summary;
  if (clause.name) {
    summary.defaultBinding = true;
    summary.named.push({ alias: clause.name.text, imported: "default" });
  }
  if (clause.namedBindings) {
    if (ts8.isNamespaceImport(clause.namedBindings)) summary.namespace = true;
    else if (ts8.isNamedImports(clause.namedBindings)) {
      for (const element of clause.namedBindings.elements) {
        const importedName = element.propertyName?.text ?? element.name.text;
        summary.named.push({ alias: element.name.text, imported: importedName });
      }
    }
  }
  return summary;
}
function summarizeExportDeclaration(node) {
  if (!node.moduleSpecifier || !ts8.isStringLiteral(node.moduleSpecifier)) return void 0;
  const summary = {
    defaultBinding: false,
    named: [],
    namespace: false,
    spec: node.moduleSpecifier.text
  };
  if (!node.exportClause) {
    summary.namespace = true;
    return summary;
  }
  if (ts8.isNamedExports(node.exportClause)) {
    for (const element of node.exportClause.elements) {
      const importedName = element.propertyName?.text ?? element.name.text;
      summary.named.push({ alias: element.name.text, imported: importedName });
    }
  } else if (ts8.isNamespaceExport(node.exportClause)) {
    summary.namespace = true;
  }
  return summary;
}
function summarizeDynamicImport(node) {
  if (node.expression.kind !== ts8.SyntaxKind.ImportKeyword) return void 0;
  if (node.arguments.length === 0 || !ts8.isStringLiteral(node.arguments[0])) return void 0;
  return {
    defaultBinding: false,
    named: [],
    namespace: true,
    spec: node.arguments[0].text
  };
}
function summarizeNode(node) {
  if (ts8.isImportDeclaration(node)) return summarizeImportDeclaration(node);
  if (ts8.isExportDeclaration(node)) return summarizeExportDeclaration(node);
  if (ts8.isCallExpression(node)) return summarizeDynamicImport(node);
  return void 0;
}
function collectFromFile(file, targets, out) {
  let sourceText;
  try {
    sourceText = fs7.readFileSync(file, "utf8");
  } catch {
    return;
  }
  const isJsLike = /\.(?:js|mjs|cjs)$/i.test(file);
  const sourceFile = ts8.createSourceFile(
    path7.basename(file),
    sourceText,
    ts8.ScriptTarget.Latest,
    true,
    isJsLike ? ts8.ScriptKind.JS : void 0
  );
  function visit(node) {
    const summary = summarizeNode(node);
    if (summary) recordSpecifier(summary, targets, out);
    ts8.forEachChild(node, visit);
  }
  visit(sourceFile);
}
function collectExternalUsage(folderRoots, targetPackageNames) {
  const map = /* @__PURE__ */ new Map();
  if (targetPackageNames.size === 0) return map;
  for (const root of folderRoots) {
    let allFiles;
    try {
      ({ allFiles } = findFiles(root));
    } catch {
      continue;
    }
    const codeFiles = allFiles.filter(
      (file) => codeExtensionRe2.test(file) && !file.endsWith(".d.ts") && !file.endsWith(".d.mts") && !file.endsWith(".d.cts")
    );
    for (const file of codeFiles) {
      collectFromFile(file, targetPackageNames, map);
    }
  }
  return map;
}

// src/actions/dead/evaluateRules.ts
import path8 from "node:path";

// src/actions/dead/ruleConfig.ts
var VALID_RULE_LEVELS = /* @__PURE__ */ new Set(["off", "warn", "error"]);
function isRecord2(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isRuleLevel(value) {
  return typeof value === "string" && VALID_RULE_LEVELS.has(value);
}
function severityFromLevel(level) {
  return level === "warn" ? "warning" : "error";
}
function formatRuleConfigLocation(ruleId) {
  return `commands.dead.rules["${ruleId}"]`;
}
function parseRuleConfig(ruleId, value) {
  if (typeof value === "string") {
    if (!isRuleLevel(value)) {
      throw new TypeError(`${formatRuleConfigLocation(ruleId)} must be one of "off", "warn", or "error"`);
    }
    return { level: value, options: {} };
  }
  if (Array.isArray(value)) {
    if (value.length !== 2) {
      throw new TypeError(`${formatRuleConfigLocation(ruleId)} tuple must be [level, options]`);
    }
    const level = value[0];
    if (!isRuleLevel(level)) {
      throw new TypeError(`${formatRuleConfigLocation(ruleId)} level must be one of "off", "warn", or "error"`);
    }
    const options = value[1];
    if (!isRecord2(options)) {
      throw new TypeError(`${formatRuleConfigLocation(ruleId)} options must be an object`);
    }
    return { level, options: { ...options } };
  }
  if (isRecord2(value)) {
    return { level: "warn", options: { ...value } };
  }
  throw new TypeError(`${formatRuleConfigLocation(ruleId)} must be a level, options object, or [level, options] tuple`);
}
function validateIgnore(ruleId, options) {
  if (options.ignore === void 0) return;
  if (!Array.isArray(options.ignore) || options.ignore.some((pattern) => typeof pattern !== "string")) {
    throw new TypeError(`${formatRuleConfigLocation(ruleId)}.ignore must be an array of strings`);
  }
}
function validateOptions(ruleId, options) {
  validateIgnore(ruleId, options);
  for (const key of Object.keys(options)) {
    if (key !== "ignore") {
      throw new TypeError(`${formatRuleConfigLocation(ruleId)} has unknown option "${key}"`);
    }
  }
}
function normalizeDeadRules(rules, config) {
  const ruleConfigs = config?.rules ?? {};
  const ruleIds = new Set(rules.map((rule) => rule.id));
  const unknownRuleIds = Object.keys(ruleConfigs).filter((ruleId) => !ruleIds.has(ruleId));
  if (unknownRuleIds.length > 0) {
    throw new TypeError(`Unknown dead rule ${unknownRuleIds.map((ruleId) => `"${ruleId}"`).join(", ")} in commands.dead.rules`);
  }
  return rules.map((rule) => {
    const ruleConfig = ruleConfigs[rule.id];
    if (ruleConfig === void 0) {
      return {
        ...rule,
        enabled: rule.enabled,
        meta: {
          ...rule.meta,
          severity: rule.enabled ? rule.meta.severity ?? "warning" : void 0
        },
        options: {}
      };
    }
    const { level, options } = parseRuleConfig(rule.id, ruleConfig);
    validateOptions(rule.id, options);
    return {
      ...rule,
      enabled: level !== "off",
      meta: level === "off" ? { ...rule.meta } : { ...rule.meta, severity: severityFromLevel(level) },
      options
    };
  });
}
function isPackageIgnoredByRule(packageName, options) {
  return options?.ignore?.some((pattern) => pattern === packageName || isMatch(packageName, pattern)) ?? false;
}

// src/actions/dead/evaluateRules.ts
var SCOPE_NARROWNESS = {
  package: 0,
  repo: 1,
  workspace: 2
};
function isConsumed(name, pkgUsage) {
  if (pkgUsage.hasNamespace) return true;
  if (name === "default" && pkgUsage.hasDefault) return true;
  return pkgUsage.named.has(name);
}
function makeFinding(analysis, rule, scope, file, symbol, line, column, detail, isDeprecated) {
  return {
    column,
    deprecated: isDeprecated,
    detail,
    file: path8.relative(analysis.location, file),
    line,
    pkg: analysis.packageName,
    rule,
    scope,
    symbol
  };
}
function symbolDeprecated(analysis, file, line, column, symbol) {
  return analysis.symbols.find((candidate) => candidate.file === file && candidate.line === line && candidate.column === column && candidate.name === symbol)?.deprecated;
}
function makeExportFinding(analysis, rule, scope, exp, detail) {
  return makeFinding(
    analysis,
    rule,
    scope,
    exp.file,
    exp.name,
    exp.line,
    exp.column,
    detail,
    symbolDeprecated(analysis, exp.file, exp.line, exp.column, exp.name)
  );
}
function findingDedupeKey(finding) {
  return `${finding.pkg}\0${finding.file}\0${finding.line}\0${finding.column}\0${finding.symbol}`;
}
function dedupeToNarrowestScope(findings) {
  const best = /* @__PURE__ */ new Map();
  for (const finding of findings) {
    const key = findingDedupeKey(finding);
    const existing = best.get(key);
    if (!existing || SCOPE_NARROWNESS[finding.scope] < SCOPE_NARROWNESS[existing.scope]) {
      best.set(key, finding);
    }
  }
  return best.values().toArray();
}
function isRuleApplicableToPackage(rule, analysis) {
  if (isPackageIgnoredByRule(analysis.packageName, rule.options)) return false;
  if (!rule.appliesToVisibility) return true;
  return rule.appliesToVisibility.includes(analysis.visibility);
}
function evaluateInternalRule(analysis, rule, shouldUseMonorepoRefs) {
  const findings = [];
  for (const symbol of analysis.symbols) {
    const isReferenced = symbol[shouldUseMonorepoRefs ? "usedInMonorepo" : "isUsedInPackage"];
    if (symbol.onPublicSurface || isReferenced) continue;
    findings.push(makeFinding(
      analysis,
      rule.id,
      rule.meta.scope,
      symbol.file,
      symbol.name,
      symbol.line,
      symbol.column,
      void 0,
      symbol.deprecated
    ));
  }
  return findings;
}
function evaluateExportRule(analysis, rule, usage, detail) {
  const pkgUsage = usage.get(analysis.packageName);
  const findings = [];
  for (const exp of analysis.publicExports) {
    if (pkgUsage && isConsumed(exp.name, pkgUsage)) continue;
    findings.push(makeExportFinding(analysis, rule.id, rule.meta.scope, exp, detail));
  }
  return findings;
}
function evaluatePackageRules(analysis, rule) {
  if (rule.id === "dead.package.internal") {
    return evaluateInternalRule(analysis, rule, false);
  }
  return [];
}
function evaluateRepoRules(analysis, rule, repoUsage) {
  if (rule.id === "dead.repo.internal") {
    return evaluateInternalRule(analysis, rule, true);
  }
  if (rule.id === "dead.repo.export") {
    return evaluateExportRule(analysis, rule, repoUsage, "no consumer in monorepo");
  }
  return [];
}
function evaluateWorkspaceRules(analysis, rule, workspaceUsage) {
  if (rule.id === "dead.workspace.export" || rule.id === "dead.workspace.export-published") {
    return evaluateExportRule(analysis, rule, workspaceUsage, "no consumer in workspace");
  }
  return [];
}
function evaluateRuleForAnalysis(analysis, rule, repoUsage, workspaceUsage) {
  if (!isRuleApplicableToPackage(rule, analysis)) return [];
  if (rule.meta.scope === "package") {
    return evaluatePackageRules(analysis, rule);
  }
  if (rule.meta.scope === "repo") {
    return evaluateRepoRules(analysis, rule, repoUsage);
  }
  if (rule.meta.scope === "workspace" && workspaceUsage !== void 0) {
    return evaluateWorkspaceRules(analysis, rule, workspaceUsage);
  }
  return [];
}
function evaluateDeadRules(analyses, rules, repoUsage, workspaceUsage) {
  const findings = [];
  const enabledRules = rules.filter((rule) => rule.enabled);
  for (const analysis of analyses) {
    for (const rule of enabledRules) {
      findings.push(...evaluateRuleForAnalysis(analysis, rule, repoUsage, workspaceUsage));
    }
  }
  return dedupeToNarrowestScope(findings);
}
function getDisabledWorkspaceRules(rules, hasCodeWorkspace) {
  if (hasCodeWorkspace) return [];
  return rules.filter((rule) => rule.requiresCodeWorkspace === true && rule.enabled).map((rule) => rule.id);
}

// src/actions/dead/findWorkspaceFile.ts
import fs10 from "node:fs";
import path11 from "node:path";

// src/actions/dead/detectActiveCodeWorkspace.ts
import fs9 from "node:fs";
import os from "node:os";
import path10 from "node:path";

// src/actions/dead/parseCodeWorkspace.ts
import fs8 from "node:fs";
import path9 from "node:path";
function consumeString(input, ctx, quote) {
  ctx.out += input[ctx.i];
  ctx.i += 1;
  while (ctx.i < input.length) {
    const ch = input[ctx.i];
    ctx.out += ch;
    if (ch === "\\" && ctx.i + 1 < input.length) {
      ctx.out += input[ctx.i + 1];
      ctx.i += 2;
      continue;
    }
    if (ch === quote) {
      ctx.i += 1;
      return;
    }
    ctx.i += 1;
  }
}
function consumeLineComment(input, ctx) {
  while (ctx.i < input.length && input[ctx.i] !== "\n") ctx.i += 1;
}
function consumeBlockComment(input, ctx) {
  ctx.i += 2;
  while (ctx.i < input.length && !(input[ctx.i] === "*" && input[ctx.i + 1] === "/")) ctx.i += 1;
  ctx.i += 2;
}
function stripJsonc(input) {
  const ctx = { i: 0, out: "" };
  while (ctx.i < input.length) {
    const ch = input[ctx.i];
    if (ch === '"' || ch === "'") {
      consumeString(input, ctx, ch);
      continue;
    }
    const next = ctx.i + 1 < input.length ? input[ctx.i + 1] : "";
    if (ch === "/" && next === "/") {
      consumeLineComment(input, ctx);
      continue;
    }
    if (ch === "/" && next === "*") {
      consumeBlockComment(input, ctx);
      continue;
    }
    ctx.out += ch;
    ctx.i += 1;
  }
  return ctx.out.replaceAll(/,(\s*[}\]])/g, "$1");
}
function parseWorkspaceJson(workspaceFilePath, stripped) {
  try {
    return JSON.parse(stripped);
  } catch (ex) {
    throw new Error(
      `Failed to parse code-workspace file ${workspaceFilePath}: ${ex instanceof Error ? ex.message : String(ex)}`,
      { cause: ex }
    );
  }
}
function parseCodeWorkspace(workspaceFilePath) {
  const raw = fs8.readFileSync(workspaceFilePath, "utf8");
  const stripped = stripJsonc(raw);
  const parsed = parseWorkspaceJson(workspaceFilePath, stripped);
  if (!parsed.folders || !Array.isArray(parsed.folders)) return [];
  const baseDir = path9.dirname(workspaceFilePath);
  const results = [];
  for (const entry of parsed.folders) {
    if (typeof entry.path !== "string") continue;
    const rawPath = entry.path;
    const absolutePath = path9.isAbsolute(rawPath) ? rawPath : path9.resolve(baseDir, rawPath);
    results.push({
      absolutePath,
      name: typeof entry.name === "string" ? entry.name : void 0,
      rawPath
    });
  }
  return results;
}

// src/actions/dead/detectActiveCodeWorkspace.ts
function uriToPath(uri) {
  if (!uri.startsWith("file://")) return void 0;
  try {
    const parsed = new URL(uri);
    let pathname = decodeURIComponent(parsed.pathname);
    if (process.platform === "win32" && pathname.startsWith("/") && /^\/[A-Za-z]:/.test(pathname)) {
      pathname = pathname.slice(1);
    }
    return pathname;
  } catch {
    return void 0;
  }
}
function isPathContainedIn(container, target) {
  const resolvedContainer = path10.resolve(container);
  const resolvedTarget = path10.resolve(target);
  return resolvedTarget === resolvedContainer || resolvedTarget.startsWith(`${resolvedContainer}${path10.sep}`);
}
function hasWorkspaceFileContainingPath(workspaceFilePath, targetPath) {
  try {
    const folders = parseCodeWorkspace(workspaceFilePath);
    return folders.some((folder) => isPathContainedIn(folder.absolutePath, targetPath));
  } catch {
    return false;
  }
}
function getEditorStorageRoots() {
  const home = os.homedir();
  return [
    path10.join(home, "Library", "Application Support", "Code", "User", "workspaceStorage"),
    path10.join(home, "Library", "Application Support", "Cursor", "User", "workspaceStorage"),
    path10.join(home, ".config", "Code", "User", "workspaceStorage"),
    path10.join(home, ".config", "Cursor", "User", "workspaceStorage")
  ].filter((root) => fs9.existsSync(root));
}
function readWorkspaceCandidate(workspaceJsonPath, resolvedStart) {
  let parsed;
  try {
    parsed = JSON.parse(fs9.readFileSync(workspaceJsonPath, "utf8"));
  } catch {
    return void 0;
  }
  if (typeof parsed.workspace !== "string") return void 0;
  const candidate = uriToPath(parsed.workspace);
  if (!(candidate != null && candidate.length > 0)) return void 0;
  if (!candidate.endsWith(".code-workspace")) return void 0;
  if (!fs9.existsSync(candidate)) return void 0;
  if (!hasWorkspaceFileContainingPath(candidate, resolvedStart)) return void 0;
  return candidate;
}
function updateBestFromEntry(storageRoot, entry, resolvedStart, best) {
  const workspaceJsonPath = path10.join(storageRoot, entry, "workspace.json");
  let stat;
  try {
    stat = fs9.statSync(workspaceJsonPath);
  } catch {
    return best;
  }
  const candidate = readWorkspaceCandidate(workspaceJsonPath, resolvedStart);
  if (!(candidate != null && candidate.length > 0)) return best;
  if (!best || stat.mtimeMs > best.mtimeMs) {
    return { mtimeMs: stat.mtimeMs, path: candidate };
  }
  return best;
}
function scanStorageRoot(storageRoot, resolvedStart, best) {
  let entries;
  try {
    entries = fs9.readdirSync(storageRoot);
  } catch {
    return best;
  }
  let nextBest = best;
  for (const entry of entries) {
    nextBest = updateBestFromEntry(storageRoot, entry, resolvedStart, nextBest);
  }
  return nextBest;
}
function detectActiveCodeWorkspace(startDir) {
  const resolvedStart = path10.resolve(startDir);
  let best;
  for (const storageRoot of getEditorStorageRoots()) {
    best = scanStorageRoot(storageRoot, resolvedStart, best);
  }
  return best?.path;
}

// src/actions/dead/findWorkspaceFile.ts
function listWorkspaceCandidates(dir) {
  let entries;
  try {
    entries = fs10.readdirSync(dir);
  } catch {
    entries = [];
  }
  return entries.filter((name) => name.endsWith(".code-workspace")).map((name) => path11.join(dir, name)).toSorted((a, b) => a.localeCompare(b));
}
function scoreWorkspaceCandidate(filePath, startDir) {
  if (!fs10.existsSync(filePath)) return -1;
  return hasWorkspaceFileContainingPath(filePath, startDir) ? 2 : 1;
}
function considerWorkspaceCandidate(candidate, candidates, startDir, best) {
  const score = scoreWorkspaceCandidate(candidate, startDir);
  if (score < 0) return best;
  if (!best || score > best.score || score === best.score && candidate.localeCompare(best.path) < 0) {
    return {
      alternates: candidates.filter((file) => file !== candidate),
      path: candidate,
      score
    };
  }
  return best;
}
function pickBestInDirectory(dir, startDir, best) {
  const candidates = listWorkspaceCandidates(dir);
  let nextBest = best;
  for (const candidate of candidates) {
    nextBest = considerWorkspaceCandidate(candidate, candidates, startDir, nextBest);
  }
  return nextBest;
}
function findCodeWorkspaceFile(startDir) {
  let dir = path11.resolve(startDir);
  let best;
  while (true) {
    best = pickBestInDirectory(dir, startDir, best);
    if (best?.score === 2) break;
    const parent = path11.dirname(dir);
    if (parent === dir) break;
    dir = parent;
  }
  if (!best) return void 0;
  return {
    alternates: best.alternates.length > 0 ? best.alternates : void 0,
    path: best.path,
    source: "walk-up"
  };
}
function resolveCodeWorkspaceFile(explicit, startDir) {
  if (explicit != null && explicit.length > 0) {
    const absolute = path11.isAbsolute(explicit) ? explicit : path11.resolve(startDir, explicit);
    if (!fs10.existsSync(absolute)) return void 0;
    return { path: absolute, source: "explicit" };
  }
  const active = detectActiveCodeWorkspace(startDir);
  if (active != null && active.length > 0 && fs10.existsSync(active)) {
    return { path: active, source: "active-editor" };
  }
  const walked = findCodeWorkspaceFile(startDir);
  return walked;
}

// src/actions/dead/rules.ts
var DEAD_RULES = [
  {
    appliesToVisibility: void 0,
    enabled: true,
    id: "dead.package.internal",
    meta: {
      description: "Declarations that are neither referenced in the package nor part of its public export surface",
      scope: "package",
      severity: "warning"
    },
    options: {}
  },
  {
    appliesToVisibility: void 0,
    enabled: true,
    id: "dead.repo.internal",
    meta: {
      description: "Declarations that are neither referenced anywhere in the monorepo nor part of the public export surface",
      scope: "repo",
      severity: "warning"
    },
    options: {}
  },
  {
    appliesToVisibility: ["private"],
    enabled: true,
    id: "dead.repo.export",
    meta: {
      description: "Public exports of private packages that no other monorepo package imports",
      scope: "repo",
      severity: "warning"
    },
    options: {}
  },
  {
    appliesToVisibility: ["restricted"],
    enabled: true,
    id: "dead.workspace.export",
    meta: {
      description: "Public exports of restricted or scoped packages that no other folder in the VS Code workspace imports",
      scope: "workspace",
      severity: "warning"
    },
    options: {},
    requiresCodeWorkspace: true
  },
  {
    appliesToVisibility: ["public"],
    enabled: false,
    id: "dead.workspace.export-published",
    meta: {
      description: "Public exports of explicitly public packages that no other folder in the VS Code workspace imports",
      scope: "workspace",
      severity: "warning"
    },
    options: {},
    requiresCodeWorkspace: true
  }
];
function loadDeadRules(config) {
  return normalizeDeadRules(DEAD_RULES, config);
}

// src/actions/dead/runDeadAnalysis.ts
function resolveExternalFolders(workspaceFilePath, ownLocations) {
  const folders = parseCodeWorkspace(workspaceFilePath);
  const externalFolders = [];
  for (const folder of folders) {
    if (!fs11.existsSync(folder.absolutePath)) continue;
    if (!fs11.existsSync(path12.join(folder.absolutePath, "package.json"))) continue;
    if (ownLocations.has(path12.resolve(folder.absolutePath))) continue;
    externalFolders.push(folder.absolutePath);
  }
  return externalFolders;
}
function resolveRepoConsumerFolders(targets) {
  const pm = getPackageManager();
  const ownLocations = new Set(targets.map((target) => path12.resolve(target.location)));
  return pm.listWorkspaces().map((workspace) => path12.resolve(workspace.location)).filter((location) => !ownLocations.has(location)).filter((location) => fs11.existsSync(path12.join(location, "package.json")));
}
function logWorkspaceLookup(lookup, isVerbose) {
  if (!isVerbose) return;
  const sourceLabel = lookup.source === "active-editor" ? "active VS Code/Cursor workspace" : lookup.source === "explicit" ? "explicit --workspace" : "walk-up discovery";
  console.log(chalk5.gray(`Using code-workspace (${sourceLabel}): ${lookup.path}`));
  if (lookup.alternates?.length != null && !Number.isNaN(lookup.alternates?.length) && lookup.alternates?.length !== 0) {
    for (const alt of lookup.alternates) console.log(chalk5.gray(`  (also present: ${alt})`));
  }
}
async function buildDeadAnalysisContext(targets, workspace, isVerbose) {
  const deadConfig = await loadWorkspaceCommandConfig(targets[0].location, "dead");
  const rules = loadDeadRules(deadConfig);
  const analyses = targets.map(analyzePackage);
  const targetPackageNames = new Set(analyses.map((analysis) => analysis.packageName));
  const repoConsumerFolders = resolveRepoConsumerFolders(targets);
  const repoUsage = collectExternalUsage(repoConsumerFolders, targetPackageNames);
  const lookup = resolveCodeWorkspaceFile(workspace, INIT_CWD());
  const hasCodeWorkspace = lookup !== void 0;
  const disabledWorkspaceRules = getDisabledWorkspaceRules(rules, hasCodeWorkspace);
  let workspaceUsage;
  if (lookup) {
    logWorkspaceLookup(lookup, isVerbose);
    const ownLocations = new Set(targets.map((target) => path12.resolve(target.location)));
    workspaceUsage = collectExternalUsage(resolveExternalFolders(lookup.path, ownLocations), targetPackageNames);
  } else if (isVerbose) {
    console.log(chalk5.gray("No *.code-workspace file found \u2014 workspace-scoped dead rules are disabled"));
  }
  const effectiveRules = rules.map((rule) => {
    if (rule.requiresCodeWorkspace !== true || hasCodeWorkspace) return rule;
    return { ...rule, enabled: false };
  });
  return {
    analyses,
    disabledWorkspaceRules,
    effectiveRules,
    lookup,
    repoUsage,
    targetPackageNames,
    targets,
    workspaceUsage
  };
}
function evaluateFindings(context) {
  const enrichedAnalyses = applyMonorepoUsage(context.analyses, context.repoUsage);
  return evaluateDeadRules(enrichedAnalyses, context.effectiveRules, context.repoUsage, context.workspaceUsage);
}
function refreshFindingsAfterRemoval(context) {
  const refreshedAnalyses = context.targets.map(analyzePackage);
  const refreshedRepoUsage = collectExternalUsage(
    resolveRepoConsumerFolders(context.targets),
    context.targetPackageNames
  );
  const refreshedEnriched = applyMonorepoUsage(refreshedAnalyses, refreshedRepoUsage);
  const refreshedWorkspaceUsage = context.lookup ? collectExternalUsage(
    resolveExternalFolders(
      context.lookup.path,
      new Set(context.targets.map((target) => path12.resolve(target.location)))
    ),
    context.targetPackageNames
  ) : void 0;
  return evaluateDeadRules(refreshedEnriched, context.effectiveRules, refreshedRepoUsage, refreshedWorkspaceUsage);
}

// src/actions/dead/dead.ts
function resolveTargets(pkg) {
  const pm = getPackageManager();
  const workspaces = pm.listWorkspaces();
  if (pkg === void 0) return workspaces;
  const target = pm.findWorkspace(pkg);
  return target ? [target] : void 0;
}
function errorEnvelope(message) {
  if (!isJsonMode()) console.error(chalk6.red(message));
  return {
    envelope: {
      errorCount: 1,
      errors: [{ message }]
    },
    exitCode: 1
  };
}
function countBySeverity(findings, rules) {
  const severityByRule = new Map(rules.map((rule) => [rule.id, rule.meta.severity ?? "warning"]));
  let errorCount = 0;
  let warningCount = 0;
  for (const finding of findings) {
    const severity = severityByRule.get(finding.rule) ?? "warning";
    if (severity === "error") errorCount += 1;
    else warningCount += 1;
  }
  return { errorCount, warningCount };
}
var dead = async (options = {}) => {
  const effectiveFormat = options.format ?? (isJsonMode() ? "json" : "human");
  const targets = resolveTargets(options.pkg);
  if (!targets || targets.length === 0) {
    return errorEnvelope(options.pkg != null && options.pkg.length > 0 ? `Workspace not found: ${options.pkg}` : "No workspaces found");
  }
  const context = await buildDeadAnalysisContext(targets, options.workspace, options.verbose === true);
  let findings = evaluateFindings(context);
  let fixApplied;
  let fixSkipped;
  let fixMode;
  if (options.fix === true && findings.length > 0) {
    fixMode = options.remove === true ? "remove" : "deprecate";
    const fixResult = applyDeadFixes(findings, context.analyses, fixMode, options.verbose === true);
    fixApplied = fixResult.applied;
    fixSkipped = fixResult.skipped;
    if (fixResult.applied > 0) {
      findings = refreshFindingsAfterRemoval(context);
    }
  }
  const { errorCount, warningCount } = countBySeverity(findings, context.effectiveRules);
  const summary = {
    disabledWorkspaceRules: context.disabledWorkspaceRules,
    errorCount,
    fixApplied,
    fixMode,
    fixSkipped,
    scannedFiles: context.analyses.reduce((sum, analysis) => sum + analysis.scannedFiles, 0),
    scannedPackages: context.analyses.length,
    warningCount,
    workspaceFile: context.lookup?.path
  };
  const result = { findings, summary };
  if (!isJsonMode()) {
    getDeadReporter(effectiveFormat).render(result);
  }
  return {
    envelope: {
      data: result,
      errorCount,
      warningCount
    },
    exitCode: errorCount > 0 ? 1 : 0
  };
};
export {
  dead
};
//# sourceMappingURL=dead.mjs.map
