initOrder
Order component members so module-init-time references are already initialized. A function called as a member is
initialized (const yieldNow = makePrimitive(…)) runs its body then: what that uses is needed first too
(makePrimitiveProto), and so on through the functions it calls. One only read (const Reference = Service) isn't
run.
import type { DefRecord } from "#codebase/tcmts/core/types"; export function initOrder(members: DefRecord[]): DefRecord[] { // an uninitialized `let` first: TypeScript reports reads in functions declared above it as unassigned const isLet = (m: DefRecord) => /^(?:\/\/[^\n]*\n)*let /.test(m.sourceText); const hoisted = members .filter((m) => m.initKind === "hoisted") .sort((a, b) => Number(isLet(b)) - Number(isLet(a))); const strict = members.filter((m) => m.initKind === "strict"); const out: DefRecord[] = []; const done = new Set<string>(); const visiting = new Set<string>(); const byHash = new Map(members.map((m) => [m.hash, m])); const member = (ref: string) => (ref.startsWith("#") ? byHash.get(ref.slice(1)) : undefined); // (a function, a constant holding one, a class: what's nominal is, but a `let`) const callable = (m: DefRecord) => m.nominal && !isLet(m); const visit = (m: DefRecord) => { if (done.has(m.hash) || visiting.has(m.hash)) return; // a strict cycle fails at runtime, exactly as in plain TS visiting.add(m.hash); const seen = new Set<string>(); const ran = new Set<string>(); const need = (dep: DefRecord, called: boolean) => { if (!seen.has(dep.hash)) { seen.add(dep.hash); if (dep.initKind === "strict") visit(dep); } if (!called || ran.has(dep.hash)) return; ran.add(dep.hash); // calling a function runs its body; calling a constant not written as one (`Reference = Service as any`) calls // what it was made from const fn = callable(dep); for (const d of dep.deps) { const used = fn || d.strict ? member(d.ref) : undefined; if (used) need(used, fn ? !!d.called : true); } }; for (const d of m.deps) { const dep = d.strict ? member(d.ref) : undefined; if (dep) need(dep, !!d.called); } visiting.delete(m.hash); done.add(m.hash); out.push(m); }; strict.forEach(visit); return [...hoisted, ...out]; }