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

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

// src/pm/workspaces.ts
import { existsSync, readFileSync } from "node:fs";
import PATH from "node:path";
import { globSync } from "glob";
function readJsonFile(path5) {
  if (!existsSync(path5)) return void 0;
  try {
    return JSON.parse(readFileSync(path5, "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 inPackages = false;
  for (const line of content.split("\n")) {
    if (/^packages\s*:/.test(line.trim())) {
      inPackages = true;
      continue;
    }
    if (inPackages) {
      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);
}

// 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) continue;
    patterns.add(pattern);
    if (!pattern.endsWith("/**")) patterns.add(`${pattern}/**`);
  }
  return [...patterns];
}
function createSourceFileFilter(location, sourceIgnorePatterns) {
  if (sourceIgnorePatterns.length === 0) return () => true;
  const isIgnored = picomatch(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) return [];
  return typeof bin === "string" ? [bin] : Object.values(bin).filter((entry) => typeof entry === "string");
}
function mayResolveToCode(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 ? [resolved] : [];
}
function collectLocalImportSpecifiers(filePath) {
  const sourceCode = fs.readFileSync(filePath, "utf8");
  const isJsFile = /\.(?:js|mjs|cjs)$/i.test(filePath);
  const sourceFile = ts.createSourceFile(
    path.basename(filePath),
    sourceCode,
    ts.ScriptTarget.Latest,
    true,
    isJsFile ? ts.ScriptKind.JS : void 0
  );
  const imports = [];
  function addModuleSpecifier(moduleSpecifier) {
    if (moduleSpecifier && ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text.startsWith(".")) {
      imports.push(moduleSpecifier.text);
    }
  }
  function visit(node) {
    if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
      addModuleSpecifier(node.moduleSpecifier);
    } else if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
      addModuleSpecifier(node.arguments[0]);
    } else if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length > 0) {
      addModuleSpecifier(node.arguments[0]);
    }
    ts.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 || 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 && shouldIncludeFile(resolved) && !seen.has(resolved)) queue.push(resolved);
    }
  }
  return result;
}
function getBinFiles(location, pkg) {
  const bin = pkg?.bin;
  if (!bin) 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)) continue;
    if (!fs.existsSync(abs)) continue;
    results.push(abs);
  }
  return results;
}
function uniqueFiles(...lists) {
  const seen = /* @__PURE__ */ new Set();
  const result = [];
  for (const list of lists) {
    for (const file of list) {
      if (seen.has(file)) continue;
      seen.add(file);
      result.push(file);
    }
  }
  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(mayResolveToCode))];
  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/collectSymbols.ts
import fs2 from "node:fs";
import path2 from "node:path";
import ts2 from "typescript";
function getLineCol(sourceFile, pos) {
  const lc = sourceFile.getLineAndCharacterOfPosition(pos);
  return { column: lc.character + 1, line: lc.line + 1 };
}
function hasExportModifier(node) {
  if (!ts2.canHaveModifiers(node)) return false;
  const mods = ts2.getModifiers(node);
  if (!mods) return false;
  return mods.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword);
}
function hasDefaultModifier(node) {
  if (!ts2.canHaveModifiers(node)) return false;
  const mods = ts2.getModifiers(node);
  if (!mods) return false;
  return mods.some((m) => m.kind === ts2.SyntaxKind.DefaultKeyword);
}
function pushDeclaration(declarations, sourceFile, name, nameNode, exported, exportedAs) {
  const { line, column } = getLineCol(sourceFile, nameNode.getStart(sourceFile));
  declarations.push({
    column,
    exported,
    exportedAs,
    line,
    name
  });
}
function tryNamedDeclaration(node) {
  if (ts2.isFunctionDeclaration(node) && node.name) return { name: node.name };
  if (ts2.isClassDeclaration(node) && node.name) return { name: node.name };
  if (ts2.isInterfaceDeclaration(node)) return { name: node.name };
  if (ts2.isTypeAliasDeclaration(node)) return { name: node.name };
  if (ts2.isEnumDeclaration(node)) return { name: node.name };
  if (ts2.isModuleDeclaration(node) && node.name && ts2.isIdentifier(node.name)) return { name: node.name };
  return void 0;
}
function collectBindingNames(node, push) {
  if (ts2.isIdentifier(node)) {
    push(node.text);
    return;
  }
  if (ts2.isObjectBindingPattern(node) || ts2.isArrayBindingPattern(node)) {
    for (const el of node.elements) {
      if (ts2.isBindingElement(el)) collectBindingNames(el.name, push);
    }
  }
}
function handleVariableStatement(node, declarations, sourceFile) {
  const exported = hasExportModifier(node);
  for (const decl of node.declarationList.declarations) {
    collectBindingNames(decl.name, (name) => {
      pushDeclaration(declarations, sourceFile, name, decl.name, exported, name);
    });
  }
}
function handleExportAssignment(node, declarations, sourceFile) {
  const pos = getLineCol(sourceFile, node.getStart(sourceFile));
  if (ts2.isIdentifier(node.expression)) {
    const name = node.expression.text;
    const existing = declarations.find((d) => d.name === name);
    if (existing) {
      existing.exported = true;
      existing.exportedAs = "default";
    }
  }
  return pos;
}
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 handleNamedExportClauseLocal(clause, declarations) {
  for (const element of clause.elements) {
    const exportedName = element.name.text;
    const importedName = element.propertyName?.text ?? exportedName;
    const existing = declarations.find((d) => d.name === importedName);
    if (existing) {
      existing.exported = true;
      existing.exportedAs = exportedName;
    }
  }
}
function handleExportDeclaration(node, declarations, reExportsFromRelative, reExportAllFromRelative) {
  const moduleSpec = node.moduleSpecifier && ts2.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : void 0;
  const isRelative = moduleSpec?.startsWith(".") ?? false;
  if (node.exportClause && ts2.isNamedExports(node.exportClause)) {
    if (moduleSpec && isRelative) {
      handleNamedExportClauseWithModule(node.exportClause, moduleSpec, reExportsFromRelative);
    } else if (!moduleSpec) {
      handleNamedExportClauseLocal(node.exportClause, declarations);
    }
    return;
  }
  if (!node.exportClause && moduleSpec && 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
    );
    return;
  }
  if (ts2.isVariableStatement(node)) {
    handleVariableStatement(node, ctx.declarations, ctx.sourceFile);
    return;
  }
  if (ts2.isExportAssignment(node)) {
    ctx.setDefaultExport(handleExportAssignment(node, ctx.declarations, ctx.sourceFile));
    return;
  }
  if (ts2.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 (!ts2.isImportDeclaration(node)) return;
  if (!ts2.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 (ts2.isNamespaceImport(clause.namedBindings)) {
      ctx.importBindingNames.add(clause.namedBindings.name.text);
      ctx.namespaceImportFromRelative.add(spec);
    } else if (ts2.isNamedImports(clause.namedBindings)) {
      handleNamedImports(spec, clause.namedBindings, ctx);
    }
  }
}
function isTopLevelDeclarationName(node, parent) {
  if (ts2.isFunctionDeclaration(parent) && parent.name === node) return true;
  if (ts2.isClassDeclaration(parent) && parent.name === node) return true;
  if (ts2.isInterfaceDeclaration(parent) && parent.name === node) return true;
  if (ts2.isTypeAliasDeclaration(parent) && parent.name === node) return true;
  if (ts2.isEnumDeclaration(parent) && parent.name === node) return true;
  if (ts2.isModuleDeclaration(parent) && parent.name === node) return true;
  return false;
}
function isLocalBindingName(node, parent) {
  if (ts2.isVariableDeclaration(parent) && parent.name === node) return true;
  if (ts2.isParameter(parent) && parent.name === node) return true;
  if (ts2.isBindingElement(parent) && parent.name === node) return true;
  return false;
}
function isMemberName(node, parent) {
  if (ts2.isPropertyAssignment(parent) && parent.name === node) return true;
  if (ts2.isPropertyDeclaration(parent) && parent.name === node) return true;
  if (ts2.isMethodDeclaration(parent) && parent.name === node) return true;
  if (ts2.isPropertySignature(parent) && parent.name === node) return true;
  if (ts2.isMethodSignature(parent) && parent.name === node) return true;
  return false;
}
function isImportExportBindingName(node, parent) {
  if (ts2.isImportSpecifier(parent) && (parent.name === node || parent.propertyName === node)) return true;
  if (ts2.isImportClause(parent) && parent.name === node) return true;
  if (ts2.isNamespaceImport(parent) && parent.name === node) return true;
  if (ts2.isExportSpecifier(parent) && (parent.name === node || parent.propertyName === node)) return true;
  return false;
}
function isQualifiedMemberAccess(node, parent) {
  if (ts2.isQualifiedName(parent) && parent.right === node) return true;
  if (ts2.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 (ts2.isIdentifier(node)) {
    const parent = node.parent;
    if (!parent || !isDeclarationOrBindingPosition(node, parent)) {
      identifierRefs.add(node.text);
    }
  }
  ts2.forEachChild(node, (child) => visitForReferences(child, identifierRefs));
}
function collectSymbolsFromFile(filePath) {
  const sourceText = fs2.readFileSync(filePath, "utf8");
  const isJsLike = /\.(?:js|mjs|cjs)$/i.test(filePath);
  const sourceFile = ts2.createSourceFile(
    path2.basename(filePath),
    sourceText,
    ts2.ScriptTarget.Latest,
    true,
    isJsLike ? ts2.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/dead/runtimeEntries.ts
import fs3 from "node:fs";
import path3 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(fs3.readFileSync(path3.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 resolveCodeFile2(filePath) {
  if (fs3.existsSync(filePath) && codeExtensionRe2.test(filePath)) return filePath;
  const ext = path3.extname(filePath);
  if (ext) {
    const withoutExt = filePath.slice(0, -ext.length);
    for (const candidateExt of resolutionExtensions2) {
      const candidate = `${withoutExt}${candidateExt}`;
      if (fs3.existsSync(candidate)) return candidate;
    }
  }
  for (const candidateExt of resolutionExtensions2) {
    const candidate = `${filePath}${candidateExt}`;
    if (fs3.existsSync(candidate)) return candidate;
  }
  for (const candidateExt of resolutionExtensions2) {
    const candidate = path3.join(filePath, `index${candidateExt}`);
    if (fs3.existsSync(candidate)) return candidate;
  }
  return void 0;
}
function resolveManifestEntry(location, entry) {
  if (path3.isAbsolute(entry)) return void 0;
  if (entry.includes("*")) return void 0;
  const relativeEntry = entry.startsWith(".") ? entry : `./${entry}`;
  return resolveCodeFile2(path3.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) {
    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 || 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 = path3.relative(location, file);
  if (!rel.startsWith("dist" + path3.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 = [
    path3.join(location, "src", tailNoExt),
    path3.join(location, "src", tailNoExt + ".ts"),
    path3.join(location, "src", tailNoExt + ".tsx"),
    path3.join(location, "src", tailNoExt + ".mts"),
    path3.join(location, "src", tailNoExt + ".cts")
  ];
  for (const candidate of candidates) {
    const resolved = resolveCodeFile2(candidate);
    if (resolved) return resolved;
  }
  return void 0;
}

// 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 resolveRelativeImport(fromFile, specifier) {
  return resolveCodeFile2(path4.resolve(path4.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 = resolveRelativeImport(file, spec);
      if (resolved) 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) addNamed(target, named.imported);
    }
    for (const reExport of report.symbols.reExportsFromRelative) {
      const target = report.resolvedRelative.get(reExport.specifier);
      if (target) addNamed(target, reExport.imported);
    }
    for (const spec of report.symbols.namespaceImportFromRelative) {
      const target = report.resolvedRelative.get(spec);
      if (target) filesWithFullExportUse.add(target);
    }
    for (const spec of report.symbols.reExportAllFromRelative) {
      const target = report.resolvedRelative.get(spec);
      if (target) filesWithFullExportUse.add(target);
    }
  }
  return { filesWithFullExportUse, namedSymbolUsage };
}
function computeReachableFiles(reports, runtimeRoots) {
  const reachable = /* @__PURE__ */ new Set();
  const byFile = new Map(reports.map((r) => [r.file, r]));
  const queue = [];
  for (const root of runtimeRoots) {
    if (byFile.has(root) && !reachable.has(root)) {
      reachable.add(root);
      queue.push(root);
    }
  }
  while (queue.length > 0) {
    const current = queue.shift();
    if (!current) break;
    const report = byFile.get(current);
    if (!report) continue;
    for (const target of report.resolvedRelative.values()) {
      if (!byFile.has(target) || reachable.has(target)) continue;
      reachable.add(target);
      queue.push(target);
    }
  }
  return reachable;
}
function getStarExports(file, ctx) {
  const cached = ctx.starExportMemo.get(file);
  if (cached) 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) {
    for (const decl of report.symbols.declarations) {
      if (!decl.exported || !decl.exportedAs || decl.exportedAs === "default") continue;
      out.set(decl.exportedAs, {
        column: decl.column,
        file,
        line: decl.line
      });
    }
    for (const reExport of report.symbols.reExportsFromRelative) {
      if (reExport.exportedAs === "default") continue;
      const target = report.resolvedRelative.get(reExport.specifier);
      const origin = target ? findOrigin(target, reExport.imported, ctx) : void 0;
      out.set(reExport.exportedAs, origin ?? {
        column: 1,
        file,
        line: 1
      });
    }
    for (const spec of report.symbols.reExportAllFromRelative) {
      const target = report.resolvedRelative.get(spec);
      if (!target) continue;
      const child = getStarExports(target, ctx);
      for (const [name, origin] of child) {
        if (!out.has(name)) out.set(name, origin);
      }
    }
  }
  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.exported || !decl.exportedAs) 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 ? 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) continue;
    const stars = getStarExports(target, ctx);
    for (const [name, origin] of stars) {
      if (results.has(name)) continue;
      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()];
}
function makeUnusedLocalFinding(packageName, workspaceLocation, file, decl) {
  return {
    column: decl.column,
    file: path4.relative(workspaceLocation, file),
    kind: "unused-local",
    line: decl.line,
    pkg: packageName,
    symbol: decl.name
  };
}
function analyzePackage(workspace) {
  const location = path4.resolve(workspace.location);
  const manifest = readPackageManifest(location);
  const packageName = manifest && typeof manifest.name === "string" ? manifest.name : workspace.name;
  const { allFiles } = findFiles(location);
  const reports = loadReports(allFiles);
  const runtimeRoots = getRuntimeRootFiles(location, manifest);
  const reachable = computeReachableFiles(reports, runtimeRoots);
  const usage = computeCrossFileUsage(reports);
  const localFindings = [];
  for (const report of reports) {
    const fileHasFullExportUse = usage.filesWithFullExportUse.has(report.file);
    const namedUsesForFile = usage.namedSymbolUsage.get(report.file) ?? /* @__PURE__ */ new Set();
    const fileIsReachable = reachable.has(report.file);
    for (const decl of report.symbols.declarations) {
      const usedInternallyInFile = report.symbols.identifierRefs.has(decl.name);
      if (!decl.exported) {
        if (!usedInternallyInFile) {
          localFindings.push(makeUnusedLocalFinding(packageName, location, report.file, decl));
        }
        continue;
      }
      const exportedAs = decl.exportedAs ?? decl.name;
      const namedConsumed = namedUsesForFile.has(exportedAs) || namedUsesForFile.has(decl.name);
      const consumedByStarOrNamespace = fileHasFullExportUse;
      const onPublicSurface = fileIsReachable;
      const alive = usedInternallyInFile || namedConsumed || consumedByStarOrNamespace || onPublicSurface;
      if (!alive) {
        localFindings.push({
          ...makeUnusedLocalFinding(packageName, location, report.file, decl),
          detail: "exported but not referenced in this package"
        });
      }
    }
  }
  const publicExports = collectPublicExports(reports, runtimeRoots);
  return {
    localFindings,
    packageName,
    publicExports,
    scannedFiles: reports.length,
    workspaceName: workspace.name
  };
}
export {
  analyzePackage
};
//# sourceMappingURL=analyzePackage.mjs.map
