compare

tcmts.cli.semver_checks.compare · #4wcp4fta · term · runs anywhere

import { callable, change, FLAGS, levelOf, properties, typeChanges } from "#codebase/tcmts/cli/semver_checks";
import type { Finding, LintId, Pair } from "#codebase/tcmts/cli/semver_checks";
import type { TypeChecker } from "#codebase/tcmts/core/ts";
import { SignatureKind as ts_SignatureKind, SymbolFlags as ts_SymbolFlags } from "typescript/sync";
import type { Symbol as ts_Symbol, Type as ts_Type } from "typescript/sync";

export function compare(checker: TypeChecker, p: Pair, o: ts_Symbol, n: ts_Symbol): Finding[] {
  const out: Finding[] = [];
  const add = (lint: LintId, detail?: string) => out.push({ lint, path: p.path, detail });
  const str = (t: ts_Type) => checker.typeToString(t, undefined, FLAGS);
  const assignable = (a: ts_Type, b: ts_Type) => checker.isTypeAssignableTo(a, b);
  const isValue = (s: ts_Symbol) => !!(s.flags & ts_SymbolFlags.Value);
  const isType = (s: ts_Symbol) => !!(s.flags & (ts_SymbolFlags.Type | ts_SymbolFlags.Class));
  if (isValue(o) !== isValue(n) || (isType(o) && !isType(n))) {
    add("kind_changed");
    return out;
  }
  if (isValue(o)) {
    const [ot, nt] = [checker.getTypeOfSymbol(o), checker.getTypeOfSymbol(n)];
    callable(checker, ot, nt, ts_SignatureKind.Call, add);
    if (o.flags & ts_SymbolFlags.Class) callable(checker, ot, nt, ts_SignatureKind.Construct, add);
    const plain = !checker.getSignaturesOfType(ot, ts_SignatureKind.Call).length && !checker.getSignaturesOfType(nt, ts_SignatureKind.Call).length && !(o.flags & ts_SymbolFlags.Class);
    if (plain) {
      if (!assignable(nt, ot)) {
        // say what about it, where the type is an object
        const before = out.length;
        properties(checker, ot, nt, add);
        if (out.length === before) add("value_type_widened", change("", str(ot), str(nt)));
      } else if (!assignable(ot, nt)) add("value_type_narrowed", change("", str(ot), str(nt)));
    }
  }
  if (isType(o) && !(o.flags & ts_SymbolFlags.Class && out.some((f) => levelOf(f) === "major"))) {
    const [ot, nt] = [checker.getDeclaredTypeOfSymbol(o), checker.getDeclaredTypeOfSymbol(n)];
    typeChanges(checker, ot, nt, o, n, add);
  }
  if (!out.length) add("implementation_changed");
  return out;
}

uses

used by