// src/actions/deplint/findFiles.ts
import fs from "fs";

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

// src/actions/deplint/findFiles.ts
var codeExtensions = "*.{ts,tsx,mts,cts,js,mjs,cjs}";
function getWorkspaceIgnores(location) {
  try {
    const raw = fs.readFileSync(`${location}/package.json`, "utf8");
    const pkg = JSON.parse(raw);
    return pkg.workspaces ?? [];
  } catch {
    return [];
  }
}
function findFiles(location) {
  const workspaceIgnores = getWorkspaceIgnores(location).map((w) => `${w}/**`);
  const ignore = ["**/node_modules/**", "dist/**", ...workspaceIgnores];
  const allFiles = findFilesByGlob(location, `./**/${codeExtensions}`, ignore);
  const distFiles = [
    ...findFilesByGlob(location, "./dist/**/*.d.ts"),
    ...findFilesByGlob(location, `./dist/**/${codeExtensions}`)
  ];
  return { allFiles, distFiles };
}

// src/actions/deplint/getDependenciesFromPackageJson.ts
import fs2 from "fs";
import path from "path";
function getDependenciesFromPackageJson(packageJsonPath) {
  const packageJsonFullPath = path.resolve(packageJsonPath);
  const rawContent = fs2.readFileSync(packageJsonFullPath, "utf8");
  const packageJson = JSON.parse(rawContent);
  const depsRecord = packageJson.dependencies ?? {};
  const devDepsRecord = packageJson.devDependencies ?? {};
  const peerDepsRecord = packageJson.peerDependencies ?? {};
  const dependencies = Object.keys(depsRecord);
  const devDependencies = Object.keys(devDepsRecord);
  const peerDependencies = Object.keys(peerDepsRecord);
  const dependencyVersions = depsRecord;
  const devDependencyVersions = devDepsRecord;
  const peerDependencyVersions = peerDepsRecord;
  return {
    dependencies,
    dependencyVersions,
    devDependencies,
    devDependencyVersions,
    peerDependencies,
    peerDependencyVersions
  };
}

// src/actions/deplint/getExtendsFromTsconfigs.ts
import fs3 from "fs";
import { globSync as globSync2 } from "glob";

// 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("/")[0];
}

// src/actions/deplint/getExtendsFromTsconfigs.ts
var isExternalReference = (ref) => !ref.startsWith(".") && !ref.startsWith("/");
function parseExtendsField(value) {
  if (typeof value === "string") return [value];
  if (Array.isArray(value)) return value.filter((v) => typeof v === "string");
  return [];
}
function parseTypesField(value) {
  if (Array.isArray(value)) return value.filter((v) => typeof v === "string");
  return [];
}
function parseTsconfig(file) {
  const content = fs3.readFileSync(file, "utf8");
  const cleaned = content.replaceAll(/\/\/.*/g, "").replaceAll(/,\s*([}\]])/g, "$1");
  return JSON.parse(cleaned);
}
function getExtendsFromTsconfigs(location) {
  const tsconfigFiles = globSync2("./tsconfig*.json", { cwd: location, absolute: true });
  const packages = /* @__PURE__ */ new Set();
  for (const file of tsconfigFiles) {
    try {
      const parsed = parseTsconfig(file);
      const refs = parseExtendsField(parsed.extends);
      for (const ref of refs) {
        if (isExternalReference(ref)) {
          packages.add(getBasePackageName(ref));
        }
      }
    } catch {
    }
  }
  return [...packages];
}
function getTypesFromTsconfigs(location) {
  const tsconfigFiles = globSync2("./tsconfig*.json", { cwd: location, absolute: true });
  const packages = /* @__PURE__ */ new Set();
  for (const file of tsconfigFiles) {
    try {
      const parsed = parseTsconfig(file);
      const compilerOptions = parsed.compilerOptions;
      const refs = parseTypesField(compilerOptions?.types);
      for (const ref of refs) {
        packages.add(getBasePackageName(ref));
      }
    } catch {
    }
  }
  return [...packages];
}

// src/actions/deplint/getExternalImportsFromFiles.ts
import fs5 from "fs";
import chalk from "chalk";

// src/actions/deplint/getImportsFromFile.ts
import fs4 from "fs";
import path2 from "path";
import ts from "typescript";
function isTypeOnlyImportClause(clause) {
  if (clause === void 0) {
    return false;
  }
  if ("phaseModifier" in clause) {
    const mod = clause.phaseModifier;
    const kind = typeof mod === "number" ? mod : mod?.kind;
    return kind === ts.SyntaxKind.TypeKeyword;
  }
  return clause.isTypeOnly ?? false;
}
function getImportsFromFile(filePath, importPaths, typeImportPaths) {
  const sourceCode = fs4.readFileSync(filePath, "utf8");
  const isMjsFile = filePath.endsWith(".mjs");
  const sourceFile = ts.createSourceFile(
    path2.basename(filePath),
    sourceCode,
    ts.ScriptTarget.Latest,
    true,
    isMjsFile ? ts.ScriptKind.JS : void 0
  );
  const imports = [];
  const typeImports = [];
  const isDeclarationFile2 = filePath.endsWith(".d.ts");
  function visit(node) {
    if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
      const moduleSpecifier = node.moduleSpecifier?.getFullText();
      const isTypeImport = ts.isImportDeclaration(node) ? isTypeOnlyImportClause(node.importClause) : false;
      if (typeof moduleSpecifier === "string") {
        const trimmed = moduleSpecifier.replaceAll("'", "").replaceAll('"', "").trim();
        if (isTypeImport || isDeclarationFile2) {
          typeImports.push(trimmed);
        } else {
          imports.push(trimmed);
        }
      }
    } else if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
      const [arg] = node.arguments;
      if (ts.isStringLiteral(arg)) {
        imports.push(arg.text);
      }
    } else if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length > 0 && ts.isStringLiteral(node.arguments[0])) {
      imports.push(node.arguments[0].text);
    }
    ts.forEachChild(node, visit);
  }
  visit(sourceFile);
  for (const ref of sourceFile.typeReferenceDirectives) {
    typeImports.push(ref.fileName);
  }
  const importsStartsWithExcludes = [".", "#", "node:"];
  const isValidImport = (imp) => !importsStartsWithExcludes.some((exc) => imp.startsWith(exc)) && !imp.includes("*") && !imp.includes("!");
  const cleanedImports = imports.filter(isValidImport).map(getBasePackageName);
  const cleanedTypeImports = typeImports.filter(isValidImport).map(getBasePackageName);
  for (const imp of cleanedImports) {
    importPaths[imp] = importPaths[imp] ?? [];
    importPaths[imp].push(filePath);
  }
  for (const imp of cleanedTypeImports) {
    typeImportPaths[imp] = typeImportPaths[imp] ?? [];
    typeImportPaths[imp].push(filePath);
  }
  return [cleanedImports, cleanedTypeImports];
}

// src/actions/deplint/getExternalImportsFromFiles.ts
var internalImportPrefixes = [".", "#", "node:"];
var removeInternalImports = (imports) => {
  return imports.filter((imp) => !internalImportPrefixes.some((prefix) => imp.startsWith(prefix)));
};
var isDeclarationFile = (file) => file.endsWith(".d.ts") || file.endsWith(".d.cts") || file.endsWith(".d.mts");
var toolingConfigFilePattern = /(?:^|[\\/])xy\.config\.[cm]?[jt]sx?$/;
var isToolingConfigFile = (file) => toolingConfigFilePattern.test(file);
function excludeToolingConfigImports(paths) {
  const filtered = {};
  for (const [imp, files] of Object.entries(paths)) {
    const kept = files.filter((file) => !isToolingConfigFile(file));
    if (kept.length > 0) filtered[imp] = kept;
  }
  return filtered;
}
function warnMissingDist(name, location, allFiles) {
  if (allFiles.length > 0 && fs5.existsSync(`${location}/src`)) {
    const prefix = name ? `[${chalk.blue(name)}] ` : "";
    console.warn(`${prefix}${chalk.yellow("deplint: dist/ not found \u2014 using source imports (compile first for best accuracy)")}`);
  }
}
function shipsTsconfig(location) {
  try {
    const raw = fs5.readFileSync(`${location}/package.json`, "utf8");
    const pkg = JSON.parse(raw);
    const files = pkg.files;
    return files?.some((f) => f === "tsconfig.json" || f.includes("tsconfig")) ?? false;
  } catch {
    return false;
  }
}
function getExternalImportsFromFiles({
  allFiles,
  distFiles,
  location,
  name,
  tsconfigExtends = [],
  tsconfigTypes = []
}) {
  const srcImportPaths = {};
  const srcTypeImportPaths = {};
  for (const path4 of allFiles) getImportsFromFile(path4, srcImportPaths, srcTypeImportPaths);
  const allImportPaths = { ...srcImportPaths };
  for (const [key, paths] of Object.entries(srcTypeImportPaths)) {
    allImportPaths[key] = [...allImportPaths[key] ?? [], ...paths];
  }
  const distImportPaths = {};
  const distTypeImportPaths = {};
  const hasDist = distFiles.length > 0;
  if (hasDist) {
    const distTypeFiles = distFiles.filter(isDeclarationFile);
    const distCodeFiles = distFiles.filter((file) => !isDeclarationFile(file));
    for (const path4 of distCodeFiles) getImportsFromFile(path4, distImportPaths, distImportPaths);
    for (const path4 of distTypeFiles) getImportsFromFile(path4, distTypeImportPaths, distTypeImportPaths);
  } else {
    warnMissingDist(name, location, allFiles);
  }
  const effectiveDistImportPaths = hasDist ? distImportPaths : excludeToolingConfigImports(srcImportPaths);
  const effectiveDistTypeImportPaths = hasDist ? distTypeImportPaths : excludeToolingConfigImports(srcTypeImportPaths);
  const allImports = Object.keys(allImportPaths);
  const distImports = Object.keys(effectiveDistImportPaths);
  const externalAllImports = removeInternalImports(allImports);
  const externalDistImports = removeInternalImports(distImports);
  const externalDistTypeImports = removeInternalImports(Object.keys(effectiveDistTypeImportPaths));
  const isDistTsconfig = shipsTsconfig(location);
  for (const ext of tsconfigExtends) {
    if (!externalAllImports.includes(ext)) externalAllImports.push(ext);
    if (isDistTsconfig && !externalDistImports.includes(ext)) externalDistImports.push(ext);
  }
  for (const typ of tsconfigTypes) {
    if (!externalAllImports.includes(typ)) externalAllImports.push(typ);
  }
  return {
    allImportPaths,
    allImports,
    distImportPaths: effectiveDistImportPaths,
    distImports,
    externalAllImports,
    externalDistImports,
    externalDistTypeImports,
    tsconfigExtends: [...tsconfigExtends]
  };
}

// src/actions/deplint/getRequiredPeerDependencies.ts
import fs6 from "fs";
import path3 from "path";
function findDepPackageJson(location, dep) {
  let dir = location;
  while (true) {
    const candidate = path3.join(dir, "node_modules", dep, "package.json");
    if (fs6.existsSync(candidate)) return candidate;
    const parent = path3.dirname(dir);
    if (parent === dir) return void 0;
    dir = parent;
  }
}

// src/actions/deplint/isTerminalPackage.ts
import fs7 from "fs";
var EXPORT_META_KEYS = /* @__PURE__ */ new Set([".", "./package.json", "./README.md"]);
function exportsLookCliOnly(exports) {
  if (exports === void 0) return true;
  if (typeof exports === "string") return true;
  return Object.keys(exports).every((k) => EXPORT_META_KEYS.has(k));
}
function isTerminalPackage(location) {
  try {
    const pkg = JSON.parse(
      fs7.readFileSync(`${location}/package.json`, "utf8")
    );
    if (pkg.private === true) return true;
    if (pkg.xy?.commands?.deplint?.terminal === true) return true;
    const hasBin = pkg.bin !== void 0;
    const hasNoLegacyEntries = !pkg.main && !pkg.module;
    if (hasBin && hasNoLegacyEntries && exportsLookCliOnly(pkg.exports)) {
      return true;
    }
    return false;
  } catch {
    return false;
  }
}

// src/actions/deplint/checkPackage/getUnsatisfiedPeerDependencies.ts
import fs8 from "fs";
import chalk2 from "chalk";
import semver from "semver";
import sortPackageJson from "sort-package-json";
function readDepPackageJson(location, dep) {
  const depPkgPath = findDepPackageJson(location, dep);
  if (!depPkgPath) return void 0;
  try {
    return JSON.parse(fs8.readFileSync(depPkgPath, "utf8"));
  } catch {
    return void 0;
  }
}
function readPeerDeps(location, dep) {
  const pkg = readDepPackageJson(location, dep);
  if (!pkg) return [];
  const peers = pkg.peerDependencies;
  return Object.keys(peers ?? {});
}
var MAX_TRANSITIVE_DEPTH = 2;
function collectPeerSources(location, dependencies, devDependencies) {
  const peerSources = /* @__PURE__ */ new Map();
  const visited = /* @__PURE__ */ new Set();
  function addPeerSource(peer, source) {
    const sources = peerSources.get(peer) ?? [];
    sources.push(source);
    peerSources.set(peer, sources);
  }
  function collectFromDeps(deps, isRuntime, chain) {
    for (const dep of deps) {
      const visitKey = `${dep}:${chain.join(">")}`;
      if (visited.has(visitKey)) continue;
      visited.add(visitKey);
      for (const peer of readPeerDeps(location, dep)) {
        addPeerSource(peer, {
          chain: chain.length > 0 ? [...chain, dep] : void 0,
          isRuntimeDep: isRuntime,
          requiredBy: dep
        });
      }
      if (chain.length < MAX_TRANSITIVE_DEPTH) {
        const depPkg = readDepPackageJson(location, dep);
        if (depPkg) {
          const transitiveDeps = Object.keys(depPkg.dependencies ?? {});
          collectFromDeps(transitiveDeps, isRuntime, [...chain, dep]);
        }
      }
    }
  }
  collectFromDeps(dependencies, true, []);
  const devOnly = devDependencies.filter((d) => !dependencies.includes(d));
  collectFromDeps(devOnly, false, []);
  return peerSources;
}
function getRequiredTransitivePeers(location, { dependencies, devDependencies }) {
  const peerSources = collectPeerSources(location, dependencies, devDependencies);
  return new Set(peerSources.keys());
}

// src/actions/deplint/snapshot.ts
function buildWorkspaceContext(workspace, exclude, workspaceNames, forceDeps, forcePeers, forceDevs) {
  const { allFiles, distFiles } = findFiles(workspace.location);
  const tsconfigExtends = getExtendsFromTsconfigs(workspace.location);
  const tsconfigTypes = getTypesFromTsconfigs(workspace.location);
  const sourceParams = getExternalImportsFromFiles({
    allFiles,
    distFiles,
    location: workspace.location,
    name: workspace.name,
    tsconfigExtends,
    tsconfigTypes
  });
  const packageJsonPath = `${workspace.location}/package.json`;
  const packageParams = getDependenciesFromPackageJson(packageJsonPath);
  const requiredTransitivePeers = getRequiredTransitivePeers(workspace.location, packageParams);
  return {
    exclude,
    fileContext: { allFiles, distFiles },
    forceDeps: forceDeps ?? /* @__PURE__ */ new Set(),
    forceDevs: forceDevs ?? /* @__PURE__ */ new Set(),
    forcePeers: forcePeers ?? /* @__PURE__ */ new Set(),
    isTerminalPackage: isTerminalPackage(workspace.location),
    packageJsonPath,
    packageParams,
    sourceParams,
    unusedDependencyExclude: /* @__PURE__ */ new Set([...exclude ?? [], ...requiredTransitivePeers, ...forcePeers ?? []]),
    workspace,
    workspaceNames
  };
}
export {
  buildWorkspaceContext
};
//# sourceMappingURL=snapshot.mjs.map