rehashComponent
uses tcmts.core.hash.hashIR, which uses node:crypto
A component's hashes again after definitions it refers to outside itself moved (swap gives each one's new
hash): what analyzing its code would give, worked out from its members' IR as stored (by member index, with
references inside the cycle as ["Cyc", i]). A cycle's members are ordered as the analyzer orders them, by a hash
that leaves those references out, then by what they refer to; undefined if two can't be told apart that way (the
analyzer orders those as written, which the IR doesn't say). index[i] is where member i goes.
import { hashIR } from "#codebase/tcmts/core/hash"; import type { IR } from "#codebase/tcmts/core/hash"; export function rehashComponent( irs: IR[], swap: (hash: string) => string, ): { irs: IR[]; componentHash: string; hashes: string[]; index: number[] } | undefined { const outside = (ir: IR): IR => Array.isArray(ir) ? ir[0] === "Ref" && typeof ir[1] === "string" ? ["Ref", swap(ir[1])] : ir.map(outside) : ir; const isCyc = (ir: IR): ir is [string, string] => Array.isArray(ir) && ir.length === 2 && ir[0] === "Cyc" && typeof ir[1] === "string"; const cyc = (ir: IR, f: (i: number) => IR): IR => isCyc(ir) ? f(Number(ir[1])) : Array.isArray(ir) ? ir.map((c) => cyc(c, f)) : ir; const cycRefs = (ir: IR, out: number[] = []): number[] => { if (isCyc(ir)) out.push(Number(ir[1])); else if (Array.isArray(ir)) for (const c of ir) cycRefs(c, out); return out; }; const swapped = irs.map(outside); if (swapped.length === 1) { const h = hashIR(["Component", swapped[0]!]); return { irs: swapped, componentHash: h, hashes: [h], index: [0] }; } const pre = swapped.map((ir) => hashIR(cyc(ir, () => ["Cyc", "?"]))); const refined = swapped.map((ir, i) => hashIR([pre[i]!, ...cycRefs(ir).map((j) => pre[j]!)])); if (new Set(swapped.map((_, i) => `${pre[i]} ${refined[i]}`)).size < swapped.length) return undefined; const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0); const order = swapped .map((_, i) => i) .sort((a, b) => cmp(pre[a]!, pre[b]!) || cmp(refined[a]!, refined[b]!)); const index: number[] = []; order.forEach((old, i) => (index[old] = i)); const out = order.map((old) => cyc(swapped[old]!, (j) => ["Cyc", String(index[j])])); const componentHash = hashIR(["Component", ...out]); return { irs: out, componentHash, hashes: out.map((_, i) => hashIR(["Elem", componentHash, String(i)])), index, }; }