// src/actions/dead/collectExternalUsage.ts
import fs2 from "node:fs";
import path2 from "node:path";
import ts2 from "typescript";

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

// 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));
}

// src/pm/workspaces.ts
import { existsSync, readFileSync } from "node:fs";
import PATH from "node:path";
import { globSync } from "glob";
function readJsonFile(path3) {
  if (!existsSync(path3)) return void 0;
  try {
    return JSON.parse(readFileSync(path3, "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 = 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) 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;
}
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) 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) 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(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/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/dead/runtimeEntries.ts
var codeExtensionRe2 = /\.(?:ts|tsx|mts|cts|js|mjs|cjs)$/i;

// src/actions/dead/collectExternalUsage.ts
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 (!ts2.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 (ts2.isNamespaceImport(clause.namedBindings)) summary.namespace = true;
    else if (ts2.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 || !ts2.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 (ts2.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 (ts2.isNamespaceExport(node.exportClause)) {
    summary.namespace = true;
  }
  return summary;
}
function summarizeDynamicImport(node) {
  if (node.expression.kind !== ts2.SyntaxKind.ImportKeyword) return void 0;
  if (node.arguments.length === 0 || !ts2.isStringLiteral(node.arguments[0])) return void 0;
  return {
    defaultBinding: false,
    named: [],
    namespace: true,
    spec: node.arguments[0].text
  };
}
function summarizeNode(node) {
  if (ts2.isImportDeclaration(node)) return summarizeImportDeclaration(node);
  if (ts2.isExportDeclaration(node)) return summarizeExportDeclaration(node);
  if (ts2.isCallExpression(node)) return summarizeDynamicImport(node);
  return void 0;
}
function collectFromFile(file, targets, out) {
  let sourceText;
  try {
    sourceText = fs2.readFileSync(file, "utf8");
  } catch {
    return;
  }
  const isJsLike = /\.(?:js|mjs|cjs)$/i.test(file);
  const sourceFile = ts2.createSourceFile(
    path2.basename(file),
    sourceText,
    ts2.ScriptTarget.Latest,
    true,
    isJsLike ? ts2.ScriptKind.JS : void 0
  );
  function visit(node) {
    const summary = summarizeNode(node);
    if (summary) recordSpecifier(summary, targets, out);
    ts2.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;
}
export {
  collectExternalUsage
};
//# sourceMappingURL=collectExternalUsage.mjs.map
