tableOf
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
- tcmts.core.analyze.nodeIndex
- tcmts.core.analyze.tableOptions
- tcmts.core.extract.DefSite
- tcmts.core.schema.TableSchema
- tcmts.core.schema.typeSchema
- tcmts.core.ts.TypeChecker