executed
uses node:url
The codebase's definitions a run executed, from the V8 coverage its processes wrote to dir: each one's own
code, by name. A component's module counts as executed if a function in it ran, or if loading it does something.
A class counts by the methods that ran (Class#method, each its own code: #pjtj4qky) and the rest of it
(Class#), so a change to a method a run didn't use leaves its result. A constant whose value is written out
(isLiteralValue), or computed doing nothing on load but make it (#i4bzoy3f), counts only when what
ran refers to it, with what its value names: a change to it matters only to the code that reads it. A computed
one also counts if a function in it ran, or if state its computation could change counts.
A definition is named as it was when the run started (names, by hash: namesByHash), or, one named
since, as it is now: a save during the run (on its branch) can replace what ran, and the run still ran it. One
no name follows (a build's older code, say) counts through the code referring to it, by hash; if none of what
counts does, nothing says when it changes, and what the run executed isn't known: undefined. (Unless it ran only
from another codebase's cache: a codebase a test made, whose code is the test's, that this one once had too.)
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { evaluatedOnLoad, isLiteralValue, memberCode, ownCode, quietLoad } from "#codebase/tcmts/cli/suites"; import type { DefRecord } from "#codebase/tcmts/core/types"; import fs from "node:fs"; import path from "node:path"; import { pathToFileURL } from "node:url"; export function executed( cb: Codebase, dir: string, names?: ReadonlyMap<string, string>, ): Record<string, string> | undefined { const nameOf = (h: string): string | undefined => names?.get(h) ?? cb.store.pathsOf(h)[0]; // the components loaded from this codebase's cache or a build, not only another codebase's (a test's own), and // this codebase's cache as module URLs have it (symlinks followed) const ours = new Set<string>(); let cache: string | undefined; try { cache = `${pathToFileURL(fs.realpathSync(cb.lay.cacheDir)).href}/`; } catch { // (none yet) } const elsewhere = (url: string) => /\/\.tcm\/cache\/[^/]+$/.test(url) && !(cache && url.startsWith(cache)); // by component: whether a function in it ran, and the names of those that did const loaded = new Map<string, Set<string> | undefined>(); for (const f of fs.readdirSync(dir)) { let scripts: { url: string; functions: { functionName: string; ranges: { startOffset: number; count: number }[] }[]; }[]; try { scripts = JSON.parse(fs.readFileSync(path.join(dir, f), "utf8")).result; } catch { continue; } for (const s of scripts) { // a component's module, as the codebase runs it (.tcm/cache) or a package packed from it has it (modules/) const component = /\/([a-z2-7]{52})\.(?:tsx?|js)$/.exec(s.url)?.[1]; if (!component || !cb.store.componentMembers(component).length) continue; if (!elsewhere(s.url)) ours.add(component); // (the first function is the module's own top level; `__tcm_kept` is tcm's, giving a kept value its constant) const ran = s.functions.filter( (fn) => !(fn.functionName === "" && fn.ranges[0]!.startOffset === 0) && !/^__tcm_kept(_\d+)?$/.test(fn.functionName) && fn.ranges[0]!.count > 0, ); const had = loaded.get(component); if (!ran.length) { if (!loaded.has(component)) loaded.set(component, undefined); continue; } const names = had ?? new Set<string>(); for (const fn of ran) names.add(fn.functionName.replace(/^(get|set) /, "")); loaded.set(component, names); } } const out: Record<string, string> = {}; // the constants counted only when read, each with the state its computation could change (none, written out) const unread = new Map<string, { rec: DefRecord; state: string[] }>(); const reached: DefRecord[] = []; const counted = new Set<string>(); // what ran that no name follows; and what the code counted whole refers to (by hash, for those) const unnamed = new Set<string>(); const referred = new Set<string>(); const whole = (m: DefRecord) => { for (const d of m.deps) if (d.ref.startsWith("#") && d.pos !== "type") referred.add(d.ref.slice(1)); return ownCode(cb, m, nameOf); }; const add = (m: DefRecord, ran: Set<string> | undefined) => { const name = nameOf(m.hash); if (!name) { if (ours.has(m.componentHash)) unnamed.add(m.hash); } else if (m.kind === "class" && ran) { // the methods that ran, by name (a nested function named alike counts its method too: more, never less) out[`${name}#`] = memberCode(cb, m, "", nameOf)!; for (const method of ran) { const code = memberCode(cb, m, method, nameOf); if (code) out[`${name}#${method}`] = code; } } else out[name] = whole(m); counted.add(m.hash); reached.push(m); }; for (const [component, ran] of loaded) { for (const m of cb.store.componentMembers(component)) { if (m.kind === "type") continue; const state = isLiteralValue(m) ? [] : !ran && evaluatedOnLoad(m) ? quietLoad(cb, m) : undefined; if (state) unread.set(m.hash, { rec: m, state }); else if (ran || evaluatedOnLoad(m)) add(m, ran); } } // one counted, with what it names (a function in it, or whose code it holds) const count = (u: DefRecord) => { unread.delete(u.hash); add(u, undefined); for (const n of u.deps) { const named = n.ref.startsWith("#") && n.pos !== "type" ? cb.store.getDef(n.ref.slice(1)) : undefined; // (one no name follows is in `u`'s own code by hash) const name = named && nameOf(named.hash); if ( named && named.kind !== "type" && name && !(name in out) && !unread.has(named.hash) && !isLiteralValue(named) ) { out[name] = whole(named); counted.add(named.hash); } } }; // the constants what ran refers to (and what they refer to); then any computing state that counts (what it did to // that state is part of what ran), and what that refers to, until none is left for (let i = 0; ;) { for (; i < reached.length; i++) { for (const d of reached[i]!.deps) { if (!d.ref.startsWith("#") || d.pos === "type") continue; const u = unread.get(d.ref.slice(1)); if (u) count(u.rec); } } const changing = [...unread.values()].find((u) => u.state.some((h) => counted.has(h))); if (!changing) break; count(changing.rec); } for (const h of unnamed) if (!referred.has(h)) return undefined; return out; }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.suites.evaluatedOnLoad
- tcmts.cli.suites.isLiteralValue
- tcmts.cli.suites.memberCode
- tcmts.cli.suites.ownCode
- tcmts.cli.suites.quietLoad
- tcmts.core.types.DefRecord