tableOf

tcmts.core.analyze.tableOf · #fqwobawq · term · runs on the server

uses node:crypto

const users = table<User>("users") with table a codebase definition with the table role (roleOf: by its hash, as the server library's has), or where the definition was a table (was: code analyzed again): the row type is erased at runtime, but it's known here, so its schema (and that schema's hash) travel with the definition.

import { nodeIndex, tableOptions } from "#codebase/tcmts/core/analyze";
import type { DefSite } from "#codebase/tcmts/core/extract";
import { typeSchema } from "#codebase/tcmts/core/schema";
import type { TableSchema } from "#codebase/tcmts/core/schema";
import type { TypeChecker } from "#codebase/tcmts/core/ts";
import { createHash } from "node:crypto";
import {
  isAsExpression as ts_isAsExpression, isCallExpression as ts_isCallExpression, isIdentifier as ts_isIdentifier,
  isParenthesizedExpression as ts_isParenthesizedExpression, isSatisfiesExpression as ts_isSatisfiesExpression,
  isStringLiteralLikeNode as ts_isStringLiteralLike, SyntaxKind as ts_SyntaxKind,
} from "typescript/ast";
import type {
  ImportSpecifier as ts_ImportSpecifier, VariableDeclaration as ts_VariableDeclaration,
} from "typescript/ast";

export function tableOf(
  site: DefSite,
  checker: TypeChecker,
  roleOf?: (d: ts_ImportSpecifier) => string | undefined,
  was = false,
): TableSchema | undefined {
  if (site.tag !== "Const") return undefined;
  let init = (site.nodes[0] as ts_VariableDeclaration).initializer;
  while (
    init &&
    (ts_isParenthesizedExpression(init) ||
      ts_isAsExpression(init) ||
      ts_isSatisfiesExpression(init))
  )
    init = init.expression;
  if (
    !init ||
    !ts_isCallExpression(init) ||
    !init.typeArguments?.length ||
    !ts_isIdentifier(init.expression)
  )
    return undefined;
  const handle = checker.getSymbolAtLocation(init.expression)?.declarations?.[0];
  const d =
    handle?.kind === ts_SyntaxKind.ImportSpecifier && handle.path === site.sf.path
      ? (nodeIndex(site.sf).get(handle.index) as ts_ImportSpecifier | undefined)
      : undefined;
  if (!d || !(was || roleOf?.(d) === "table")) return undefined;
  const { s, defs } = typeSchema(checker, checker.getTypeFromTypeNode(init.typeArguments[0]!));
  const arg = init.arguments[0];
  const hash = createHash("sha256").update(JSON.stringify({ s, defs })).digest("hex").slice(0, 32);
  return {
    ...(arg && ts_isStringLiteralLike(arg) ? { name: arg.text } : {}),
    ...(init.arguments.length > 1 ? { hasOptions: true } : {}),
    hash,
    s,
    defs,
    ...tableOptions(init.arguments[1], checker),
  };
}

uses

used by