quietLoad

tcmts.cli.suites.quietLoad · #uf5klt6o · term · runs on the server

uses tcmts.cli.npm.isPureExport, which uses tcmts.cli.npm.npmFacts, which uses node:path

Whether loading a computed constant does nothing anyone could see but make its value; if so, the state computing it could change: by hash, what holds state (an object, an array, a let, a class, a computed value) among the definitions its computation uses, through what they use, and any it assigns to. Its computation is its initializer less what it only holds: a function or a definition written into its value (run: (a) => … in an object in an array), not passed to anything, isn't called or used by loading it. Loading it does something (undefined) if that code reaches outside (reaches: the clock, files, the network, the environment, output), uses an npm package not known to be pure, uses a global other than JavaScript's own (QUIET_GLOBALS: not globalThis, process or timers), assigns to one or a built-in's prototype, imports a module, or waits; and, to be sure, if it isn't a const of one name alone in its component.

import type { CodebaseCore } from "#codebase/tcmts/cli/codebase";
import { reaches } from "#codebase/tcmts/cli/effects";
import { holdsState, usesPackages } from "#codebase/tcmts/cli/kept";
import { isPureExport, parseExtRef } from "#codebase/tcmts/cli/npm";
import { QUIET_GLOBALS, quietLoads } from "#codebase/tcmts/cli/suites";
import { ownEffects } from "#codebase/tcmts/core/effects";
import { declarationOf, defParts, initializerOf } from "#codebase/tcmts/core/ir";
import type { DefRecord } from "#codebase/tcmts/core/types";

export function quietLoad(cb: CodebaseCore, rec: DefRecord): string[] | undefined {
  const known = quietLoads.get(rec.hash);
  if (known !== undefined) return known ?? undefined;
  const isKey = (c: unknown, key: string) => Array.isArray(c) && c[0] === key;
  const child = (n: unknown[], key: string) =>
    (n.find((c) => isKey(c, key)) as unknown[] | undefined)?.[1];
  // what code changes: the definition (by hash) or global (by name) an assignment's target belongs to
  const owner = (n: unknown): unknown => {
    while (
      Array.isArray(n) &&
      /^(PropertyAccessExpression|ElementAccessExpression|ParenthesizedExpression|NonNullExpression)$/.test(
        String(n[0]),
      )
    )
      n = child(n, "expression");
    return n;
  };
  const decide = (): string[] | undefined => {
    const { tag, body } = defParts(rec.ir);
    const decl = declarationOf(rec.ir);
    if (
      rec.kind !== "term" ||
      rec.componentSize !== 1 ||
      rec.initKind !== "strict" ||
      tag !== "Const" ||
      body.length !== 1 ||
      decl?.[0] !== "VariableDeclaration" ||
      !/^(Cyc|Named)$/.test(String((child(decl, "name") as unknown[] | undefined)?.[0]))
    )
      return undefined;
    const init = initializerOf(rec.ir);
    if (
      !Array.isArray(init) ||
      /^(ArrowFunction|FunctionExpression|ClassExpression)$/.test(String(init[0]))
    )
      return undefined;
    const uses = new Set<string>();
    const exts = new Set<string>();
    const state = new Set<string>();
    let loud = false;
    // code as loading runs it; `held`: every node above this one only holds what's under it (an object, an array,
    // a property's value, parentheses), so a function or definition here is only kept in the value
    const visit = (n: unknown, held: boolean): unknown => {
      if (!Array.isArray(n)) return n;
      const tag = String(n[0]);
      // (a node's child, by its key: types do nothing)
      if (/^[a-z]/.test(tag))
        return /^type(Arguments|Parameters)?$/.test(tag)
          ? null
          : [tag, ...n.slice(1).map((c) => visit(c, held))];
      if (
        held &&
        /^(ArrowFunction|FunctionExpression|MethodDeclaration|GetAccessor|SetAccessor)$/.test(tag)
      )
        // (a method's computed name is worked out, though)
        return [
          tag,
          ...n
            .slice(1)
            .filter((c) => isKey(c, "name"))
            .map((c) => visit(c, false)),
        ];
      if (held && tag === "Ref") return null;
      if (tag === "Ref") uses.add(String(n[1]));
      else if (tag === "Ext")
        exts.add(`${String(n[1])}${n[2] ? `/${String(n[2])}` : ""}#${String(n[3])}`);
      else if (tag === "G" && !QUIET_GLOBALS.has(String(n[1]))) loud = true;
      else if (/^(ImportKeyword|AwaitExpression|YieldExpression)$/.test(tag)) loud = true;
      else if (
        tag === "PropertyAccessExpression" &&
        isKey(child(n, "expression"), "G") &&
        JSON.stringify(child(n, "name")) === '["N","prototype"]'
      )
        loud = true;
      const target =
        tag === "BinaryExpression" &&
        /^(?!EqualsEquals|ExclamationEquals|LessThanEquals|GreaterThanEquals)\w*EqualsToken$/.test(
          String((child(n, "operatorToken") as unknown[] | undefined)?.[0]),
        )
          ? child(n, "left")
          : /^(Prefix|Postfix)UnaryExpression$/.test(tag) && /^(\+\+|--)$/.test(String(n[1]))
            ? child(n, "operand")
            : tag === "DeleteExpression"
              ? child(n, "expression")
              : undefined;
      if (target !== undefined) {
        const o = owner(target);
        if (isKey(o, "G")) loud = true;
        else if (isKey(o, "Ref")) state.add(String((o as unknown[])[1]));
      }
      const holds =
        held &&
        /^(ObjectLiteralExpression|ArrayLiteralExpression|PropertyAssignment|ShorthandPropertyAssignment|ParenthesizedExpression|AsExpression|SatisfiesExpression|NonNullExpression|\[)$/.test(
          tag,
        );
      return [
        tag,
        ...n
          .slice(1)
          .map((c) => visit(c, holds && !(tag === "PropertyAssignment" && isKey(c, "name")))),
      ];
    };
    const run = visit(init, true);
    if (loud || ownEffects(run).length) return undefined;
    const pure = (ref: string) => {
      const e = parseExtRef(ref);
      return !!e && isPureExport(cb.lay.cacheDir, e);
    };
    if ([...exts].some((e) => !e.startsWith("node:") && !pure(e))) return undefined;
    const values = new Set(rec.deps.filter((d) => d.pos === "value").map((d) => d.ref.slice(1)));
    const used = [...uses].filter((h) => values.has(h));
    const src = cb.source();
    for (const effects of reaches(cb, used, src).values()) if (effects.length) return undefined;
    // what it uses runs (a function it calls) or is read (a value), and what that uses: whatever holds state
    const seen = new Set<string>();
    for (const queue = [...used]; queue.length;) {
      const h = queue.pop()!;
      if (seen.has(h)) continue;
      seen.add(h);
      const d = src.get(h);
      if (!d) return undefined;
      if (d.kind === "type") continue;
      if (usesPackages(src, d.componentHash, pure)) return undefined;
      const holds = holdsState(d);
      if (holds === "state" || holds === "maybe") state.add(h);
      visit(JSON.parse(d.ir), false);
      if (loud) return undefined;
      for (const dep of d.deps)
        if (dep.pos === "value" && dep.ref.startsWith("#")) queue.push(dep.ref.slice(1));
    }
    state.delete(rec.hash);
    return [...state].sort();
  };
  const r = decide();
  quietLoads.set(rec.hash, r ?? null);
  return r;
}

uses

used by