appParts

tcmts.studio.appParts · #iw6rw53p · term · runs on the server

uses tcmts.cli.data_pages.dataTables, which uses tcmts.cli.data_pages.withStore, which uses node:path

What an app is made of, as tcm build plans it (and the editor's Apps view shows it): its code that runs in the browser, the server functions the browser calls, the tables that server code reaches (each with its rows here), and, in its namespace, its recurring jobs and its tests; what's wrong with its plan, if anything is.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import { dataTables } from "#codebase/tcmts/cli/data_pages";
import { isSchedule } from "#codebase/tcmts/cli/pages";
import type { Placement } from "#codebase/tcmts/cli/placement";
import { codebaseRoles } from "#codebase/tcmts/cli/roles";
import { isTestPath } from "#codebase/tcmts/cli/testing";

export function appParts(
  cb: Codebase,
  placement: Placement,
  hash: string,
  path: string,
): {
  browser: { path: string; place: string }[];
  endpoints: { path: string; signature: string }[];
  tables: { path: string; table: string; rows: number }[];
  schedules: { path: string; spec: string }[];
  tests: string[];
  errors: string[];
} {
  const plan = placement.planApp(hash);
  const named = (hs: Iterable<string>) =>
    [...hs]
      .flatMap((h) => (cb.store.pathsOf(h)[0] ? [{ h, path: cb.store.pathsOf(h)[0]! }] : []))
      .sort((a, b) => a.path.localeCompare(b.path));
  // the tables the server functions' code reaches
  const tables = new Map<string, string>();
  const seen = new Set<string>();
  const queue = [...plan.endpoints];
  while (queue.length) {
    const h = queue.pop()!;
    if (seen.has(h)) continue;
    seen.add(h);
    const rec = cb.store.getDef(h);
    if (!rec) continue;
    if (rec.table && cb.store.pathsOf(h).length)
      tables.set(h, rec.table.name ?? cb.store.pathsOf(h)[0]!);
    for (const d of rec.deps) if (d.ref.startsWith("#")) queue.push(d.ref.slice(1));
  }
  const rows = new Map(dataTables(cb).map((t) => [t.table, t.rows]));
  const ns = path.includes(".") ? path.slice(0, path.lastIndexOf(".") + 1) : "";
  const inNs = cb.store
    .names()
    .filter((n) => n.path.startsWith(ns) && !n.path.slice(ns.length).includes("."));
  const roles = codebaseRoles(cb);
  return {
    browser: named(plan.browser).map((x) => ({
      path: x.path,
      place: placement.placeOf(x.h).place,
    })),
    endpoints: named(plan.endpoints).map((x) => {
      const rec = cb.store.getDef(x.h);
      return { path: x.path, signature: rec ? cb.store.shownSignature(rec) : "" };
    }),
    tables: named(tables.keys()).map((x) => ({
      path: x.path,
      table: tables.get(x.h)!,
      rows: rows.get(tables.get(x.h)!) ?? 0,
    })),
    schedules: inNs
      .filter(
        (n) =>
          n.kind === "term" &&
          roles.get(n.hash) !== "every" &&
          !isTestPath(n.path) &&
          isSchedule(cb.store.getDef(n.hash)!, roles),
      )
      .map((n) => ({
        path: n.path,
        spec: /every\(\s*(["'`])(.*?)\1/.exec(cb.store.getDef(n.hash)!.sourceText)?.[2] ?? "",
      })),
    tests: inNs
      .filter((n) => n.kind !== "type" && isTestPath(n.path))
      .map((n) => n.path)
      .sort(),
    errors: plan.errors,
  };
}

uses

used by