// src/actions/dead/collectSymbols.ts
import fs from "node:fs";
import path from "node:path";
import ts2 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/collectSymbols.ts
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, 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 ts2.isIdentifier(node.name) && node.name.text === "global";
}
function tryNamedDeclaration(node) {
  if (ts2.isFunctionDeclaration(node) && node.name) {
    if (isFunctionOverloadSignature(node)) return void 0;
    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) && ts2.isIdentifier(node.name)) {
    if (isGlobalAugmentation(node)) return void 0;
    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 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 (ts2.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 && ts2.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : void 0;
  const isRelative = moduleSpec?.startsWith(".") ?? false;
  if (node.exportClause && ts2.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 (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 (!isDeclarationOrBindingPosition(node, parent)) {
      identifierRefs.add(node.text);
    }
  }
  ts2.forEachChild(node, (child) => visitForReferences(child, identifierRefs));
}
function collectSymbolsFromFile(filePath) {
  const sourceText = fs.readFileSync(filePath, "utf8");
  const isJsLike = /\.(?:js|mjs|cjs)$/i.test(filePath);
  const sourceFile = ts2.createSourceFile(
    path.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
  };
}
export {
  collectSymbolsFromFile
};
//# sourceMappingURL=collectSymbols.mjs.map
