initOrder

tcmts.loader.initOrder · #34poabla · term · runs anywhere

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];
}

uses

used by