import type { ComponentMetadata } from "../decorators/Component.ts"; /** * Server components. * * A server component is a class that renders html on the server. It behaves * like a template, but it is written in TypeScript, may inject services and * may be asynchronous. */ /** Context handed to a server component. */ export interface ComponentContext { /** * Defers a chunk of html until the end of the response. * * The response is streamed, therefore the shell reaches the browser * before the deferred content is rendered. */ defer(id: string, content: string | Promise): void; /** Locals of the current request. */ locals: Record; /** Route parameters of the current request. */ params: Record; } /** Declarative output from a decorated server component. */ export interface SpugComponentRender { /** Component template name or path, resolved as SPUG. */ spug: string; /** STCSS source or a stylesheet path relative to the component directory. */ stcss?: string | string[]; /** Props merged with the invocation props before rendering the SPUG template. */ props?: Record; } /** Contract of a server component. */ export interface ServerComponent { /** Renders the component. */ render( props: Record, context: ComponentContext, ): Promise | string | SpugComponentRender; } export function isSpugComponentRender(value: unknown): value is SpugComponentRender { return typeof value === "object" && value !== null && typeof (value as { spug?: unknown }).spug === "string"; } /** A registered server component. */ export interface ComponentDefinition { /** Name used inside the templates. */ name: string; component: ServerComponent; /** `true` when the component is rendered after the shell. */ deferred: boolean; description?: string; params?: Record; /** Decorated components must return a SPUG and STCSS composition. */ requiresSpug?: boolean; } /** Registry of the server components of an application. */ export type ComponentRegistry = Map; /** Builds a component definition from a decorated class. */ export function defineComponent( name: string, component: ServerComponent, metadata?: ComponentMetadata, ): ComponentDefinition { return { name, component, deferred: metadata?.deferred === true, description: metadata?.description, params: metadata?.params, requiresSpug: metadata !== undefined, }; }