// src/actions/deplint/getExternalImportsFromFiles.ts
import fs3 from "node:fs";
import PATH from "node:path";
import chalk from "chalk";

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

// src/actions/deplint/getExtendsFromTsconfigs.ts
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("/", 1)[0];
}

// src/actions/deplint/getExtendsFromTsconfigs.ts
function isMatchingTsconfigPathPattern(pattern, importPath) {
  const patternParts = pattern.split("/");
  const importParts = importPath.split("/");
  let patternIndex = 0;
  let importIndex = 0;
  while (patternIndex < patternParts.length && importIndex < importParts.length) {
    const patternPart = patternParts[patternIndex];
    if (patternPart === "*") {
      if (patternIndex === patternParts.length - 1) return true;
      patternIndex++;
      importIndex++;
      continue;
    }
    const importPart = importParts[importIndex];
    if (patternPart !== importPart) return false;
    patternIndex++;
    importIndex++;
  }
  if (patternIndex === patternParts.length - 1 && patternParts[patternIndex] === "*") return true;
  return patternIndex === patternParts.length && importIndex === importParts.length;
}
function isTsconfigPathAlias(importPath, pathPatterns) {
  return pathPatterns.some((pattern) => isMatchingTsconfigPathPattern(pattern, importPath));
}

// src/actions/deplint/getImportsFromFile.ts
import fs from "node:fs";
import path from "node: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 = fs.readFileSync(filePath, "utf8");
  const isJsFile = filePath.endsWith(".mjs") || filePath.endsWith(".cjs");
  const sourceFile = ts.createSourceFile(
    path.basename(filePath),
    sourceCode,
    ts.ScriptTarget.Latest,
    true,
    isJsFile ? ts.ScriptKind.JS : void 0
  );
  const imports = [];
  const typeImports = [];
  const isDeclarationFile2 = filePath.endsWith(".d.ts") || filePath.endsWith(".d.mts") || filePath.endsWith(".d.cts");
  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.every((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].push(filePath);
  }
  for (const imp of cleanedTypeImports) {
    typeImportPaths[imp] ??= [];
    typeImportPaths[imp].push(filePath);
  }
  return [cleanedImports, cleanedTypeImports];
}

// src/actions/deplint/getStorybookConfigImports.ts
import fs2 from "node:fs";
import path2 from "node:path";
import ts2 from "typescript";
var storybookMainPattern = /(?:^|[\\/])\.storybook[\\/]main\.[cm]?[jt]sx?$/;
var npmPackageNamePattern = /^@?[a-z0-9][\w./-]*$/i;
function isStorybookMainFile(filePath) {
  return storybookMainPattern.test(filePath);
}
function getPropertyName(prop) {
  if (ts2.isIdentifier(prop.name)) return prop.name.text;
  if (ts2.isStringLiteral(prop.name) || ts2.isNoSubstitutionTemplateLiteral(prop.name)) return prop.name.text;
  return void 0;
}
function pushIfPackage(text, collected) {
  const trimmed = text.trim();
  if (trimmed.length === 0) return;
  if (trimmed.startsWith(".") || trimmed.startsWith("/") || trimmed.startsWith("node:")) return;
  if (!npmPackageNamePattern.test(trimmed)) return;
  collected.push(trimmed);
}
function extractFromEntry(node, collected) {
  if (ts2.isStringLiteral(node) || ts2.isNoSubstitutionTemplateLiteral(node)) {
    pushIfPackage(node.text, collected);
    return;
  }
  if (ts2.isObjectLiteralExpression(node)) {
    for (const prop of node.properties) {
      if (ts2.isPropertyAssignment(prop) && getPropertyName(prop) === "name") {
        extractFromEntry(prop.initializer, collected);
      }
    }
    return;
  }
  if (ts2.isCallExpression(node)) {
    for (const arg of node.arguments) {
      if (ts2.isStringLiteral(arg) || ts2.isNoSubstitutionTemplateLiteral(arg)) {
        pushIfPackage(arg.text, collected);
      }
    }
    return;
  }
  if (ts2.isArrayLiteralExpression(node)) {
    for (const element of node.elements) extractFromEntry(element, collected);
  }
}
function getStorybookConfigImports(filePath, importPaths) {
  if (!isStorybookMainFile(filePath)) return [];
  let sourceCode;
  try {
    sourceCode = fs2.readFileSync(filePath, "utf8");
  } catch {
    return [];
  }
  const sourceFile = ts2.createSourceFile(
    path2.basename(filePath),
    sourceCode,
    ts2.ScriptTarget.Latest,
    true
  );
  const collected = [];
  function visit(node) {
    if (ts2.isPropertyAssignment(node)) {
      const name = getPropertyName(node);
      if (name === "addons") {
        if (ts2.isArrayLiteralExpression(node.initializer)) {
          for (const element of node.initializer.elements) extractFromEntry(element, collected);
        }
      } else if (name === "framework" || name === "builder") {
        extractFromEntry(node.initializer, collected);
      }
    }
    ts2.forEachChild(node, visit);
  }
  visit(sourceFile);
  const cleaned = [...new Set(collected.map(getBasePackageName))];
  for (const imp of cleaned) {
    importPaths[imp] ??= [];
    if (!importPaths[imp].includes(filePath)) importPaths[imp].push(filePath);
  }
  return cleaned;
}

// src/actions/deplint/getExternalImportsFromFiles.ts
var internalImportPrefixes = [".", "#", "node:"];
var removeInternalImports = (imports) => {
  return imports.filter((imp) => internalImportPrefixes.every((prefix) => !imp.startsWith(prefix)));
};
var removeTsconfigPathAliases = (imports, pathPatterns) => {
  if (pathPatterns.length === 0) return imports;
  return imports.filter((imp) => !isTsconfigPathAlias(imp, pathPatterns));
};
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 formatExportPath(pathParts) {
  return pathParts.map((part, index) => {
    if (index === 0) return part;
    if (part.startsWith("[")) return part;
    return /^[a-zA-Z_$][\w$]*$/.test(part) ? `.${part}` : `[${JSON.stringify(part)}]`;
  }).join("");
}
function collectExportTargets(value, pathParts = ["exports"]) {
  if (typeof value === "string") {
    if (!value.startsWith("./")) return [];
    return [{ path: formatExportPath(pathParts), target: value }];
  }
  if (Array.isArray(value)) {
    return value.flatMap((entry, index) => collectExportTargets(entry, [...pathParts, `[${index}]`]));
  }
  if (typeof value !== "object" || value === null) return [];
  return Object.entries(value).flatMap(([key, nested]) => collectExportTargets(nested, [...pathParts, key]));
}
function hasExportTarget(location, target) {
  if (target.includes("*")) return findFilesByGlob(location, target).length > 0;
  return fs3.existsSync(PATH.resolve(location, target));
}
function readPackageJson(location) {
  try {
    return JSON.parse(fs3.readFileSync(`${location}/package.json`, "utf8"));
  } catch {
    return void 0;
  }
}
function warnMissingExportTargets(name, location) {
  const pkg = readPackageJson(location);
  const targets = collectExportTargets(pkg?.exports);
  const warnedTargets = /* @__PURE__ */ new Set();
  const prefix = name != null && name.length > 0 ? `[${chalk.blue(name)}] ` : "";
  for (const { path: exportPath, target } of targets) {
    if (warnedTargets.has(target)) continue;
    warnedTargets.add(target);
    if (hasExportTarget(location, target)) continue;
    const description = target.includes("*") ? "pattern matched no files" : "file missing";
    console.warn(`${prefix}${chalk.yellow(`deplint: package.json export ${description}: ${target} (${exportPath})`)}`);
  }
}
function hasShippedTsconfig(location) {
  const pkg = readPackageJson(location);
  const files = pkg?.files;
  return files?.some((f) => f === "tsconfig.json" || f.includes("tsconfig")) ?? false;
}
function getEffectiveRuntimeImportPaths({
  distImportPaths,
  distTypeImportPaths,
  hasDist,
  hasRuntimeRoots,
  srcImportPaths,
  srcTypeImportPaths
}) {
  if (hasDist) {
    return {
      importPaths: distImportPaths,
      typeImportPaths: distTypeImportPaths
    };
  }
  if (!hasRuntimeRoots) {
    return {
      importPaths: {},
      typeImportPaths: {}
    };
  }
  return {
    importPaths: excludeToolingConfigImports(srcImportPaths),
    typeImportPaths: excludeToolingConfigImports(srcTypeImportPaths)
  };
}
function getExternalImportsFromFiles({
  allFiles,
  distFiles,
  hasRuntimeRoots = true,
  location,
  name,
  tsconfigExtends = [],
  tsconfigPaths = [],
  tsconfigTypes = []
}) {
  const srcImportPaths = {};
  const srcTypeImportPaths = {};
  for (const path3 of allFiles) {
    getImportsFromFile(path3, srcImportPaths, srcTypeImportPaths);
    getStorybookConfigImports(path3, srcImportPaths);
  }
  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 path3 of distCodeFiles) getImportsFromFile(path3, distImportPaths, distImportPaths);
    for (const path3 of distTypeFiles) getImportsFromFile(path3, distTypeImportPaths, distTypeImportPaths);
  }
  warnMissingExportTargets(name, location);
  const {
    importPaths: effectiveDistImportPaths,
    typeImportPaths: effectiveDistTypeImportPaths
  } = getEffectiveRuntimeImportPaths({
    distImportPaths,
    distTypeImportPaths,
    hasDist,
    hasRuntimeRoots,
    srcImportPaths,
    srcTypeImportPaths
  });
  const allImports = Object.keys(allImportPaths);
  const distImports = Object.keys(effectiveDistImportPaths);
  const externalAllImports = removeTsconfigPathAliases(removeInternalImports(allImports), tsconfigPaths);
  const externalDistImports = removeTsconfigPathAliases(removeInternalImports(distImports), tsconfigPaths);
  const externalDistTypeImports = removeTsconfigPathAliases(
    removeInternalImports(Object.keys(effectiveDistTypeImportPaths)),
    tsconfigPaths
  );
  const isDistTsconfig = hasShippedTsconfig(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]
  };
}
export {
  getExternalImportsFromFiles
};
//# sourceMappingURL=getExternalImportsFromFiles.mjs.map
