{"version":3,"sources":["../src/renderer.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\n/**\n * A rendering-library integration used by Farm's compiler, server renderer,\n * and browser hydration runtime.\n *\n * Renderer descriptors intentionally contain module identifiers instead of\n * implementation functions. This keeps farm.config.ts serializable and lets\n * every module resolve from the application that selected the renderer.\n */\nexport interface FarmRenderer {\n  /** Stable public identifier used in diagnostics and generated manifests. */\n  name: string;\n  /** Module exporting the renderer's Vite plugin factory. */\n  vite: string;\n  /** Module exporting Farm's server-renderer compatibility contract. */\n  server: string;\n  /** Module exporting Farm's browser-renderer compatibility contract. */\n  client: string;\n  /** JSX import source written to generated TypeScript configuration. */\n  jsxImportSource?: string;\n  /** Additional file extensions used for renderer-owned route components. */\n  componentExtensions?: readonly string[];\n  /** Packages that must share one module instance in a Farm application. */\n  dedupe?: readonly string[];\n  /** Renderer packages seeded into Vite's dependency optimizer. */\n  optimizeDeps?: readonly string[];\n  /**\n   * Scheduling policy for the production client and SSR graphs.\n   *\n   * Most renderer plugins are safe to run in parallel. Renderers whose\n   * compiler plugins keep process-global mutable state can opt into serial\n   * builds so one graph cannot invalidate the other's transforms.\n   */\n  buildConcurrency?: \"parallel\" | \"serial\";\n  /** Runtime features this renderer intentionally supports. */\n  capabilities?: FarmRendererCapabilitiesInput;\n  /**\n   * Serializable options consumed by the renderer's Vite integration.\n   *\n   * Keeping renderer-owned configuration on the descriptor lets one rendering\n   * library expose multiple compiler modes without teaching Farm's core about\n   * each option.\n   */\n  options?: Readonly<Record<string, unknown>>;\n}\n\nexport interface FarmRendererStreamingCapabilities {\n  /** Supports Node.js writable streams through renderToPipeableStream(). */\n  node: boolean;\n  /** Supports WHATWG ReadableStream output through renderToReadableStream(). */\n  web: boolean;\n}\n\nexport interface FarmRendererCapabilities {\n  streaming: FarmRendererStreamingCapabilities;\n  /**\n   * Whether re-rendering an existing root diffs the new tree against the live\n   * DOM instead of rebuilding it.\n   *\n   * Virtual-DOM renderers (React, Preact, Vue) compare the incoming tree with\n   * what is mounted, so a client navigation that re-renders a shared layout\n   * keeps the matching DOM nodes, their focus, and their component state.\n   *\n   * Compile-time fine-grained renderers (Solid, Svelte) have no virtual DOM to\n   * diff against. Their updates flow through bindings created when elements\n   * were constructed, so handing them a freshly materialized tree replaces the\n   * nodes. That is a property of those runtimes, not a gap in their adapters,\n   * and callers that need state to survive a re-render must keep it in a root\n   * they do not re-render rather than expect reconciliation here.\n   */\n  reconcilesRerenders: boolean;\n}\n\nexport interface FarmRendererCapabilitiesInput {\n  streaming?: Partial<FarmRendererStreamingCapabilities>;\n  reconcilesRerenders?: boolean;\n}\n\nconst DEFAULT_RENDERER_CAPABILITIES: Readonly<FarmRendererCapabilities> = Object.freeze({\n  streaming: Object.freeze({ node: false, web: false }),\n  // Conservative default: assume a re-render rebuilds until a renderer states\n  // otherwise, so callers do not silently rely on reconciliation.\n  reconcilesRerenders: false,\n});\n\nexport function getFarmRendererCapabilities(\n  renderer?: Pick<FarmRenderer, \"capabilities\">,\n): FarmRendererCapabilities {\n  return {\n    streaming: {\n      node: renderer?.capabilities?.streaming?.node ?? DEFAULT_RENDERER_CAPABILITIES.streaming.node,\n      web: renderer?.capabilities?.streaming?.web ?? DEFAULT_RENDERER_CAPABILITIES.streaming.web,\n    },\n    reconcilesRerenders:\n      renderer?.capabilities?.reconcilesRerenders ??\n      DEFAULT_RENDERER_CAPABILITIES.reconcilesRerenders,\n  };\n}\n\nexport const FARM_COMPONENT_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\"] as const;\n\nexport function resolveFarmComponentExtensions(extensions: readonly string[] = []): string[] {\n  return Array.from(\n    new Set(\n      [...FARM_COMPONENT_EXTENSIONS, ...extensions].map((extension) => {\n        const normalized = extension.trim().toLowerCase();\n        return normalized.startsWith(\".\") ? normalized : `.${normalized}`;\n      }),\n    ),\n  ).filter((extension) => extension.length > 1);\n}\n\nexport function getFarmRendererComponentExtensions(\n  renderer?: Pick<FarmRenderer, \"componentExtensions\">,\n): string[] {\n  return resolveFarmComponentExtensions(renderer?.componentExtensions);\n}\n\nexport interface FarmRendererViteModule {\n  createFarmRendererPlugin(options?: {\n    ssr?: boolean;\n    rendererOptions?: Readonly<Record<string, unknown>>;\n  }): unknown | readonly unknown[] | Promise<unknown | readonly unknown[]>;\n}\n\nexport interface FarmServerRendererRuntime {\n  readonly name: string;\n  readonly Fragment: unknown;\n  readonly Suspense: unknown;\n  createElement(type: unknown, props?: unknown, ...children: unknown[]): unknown;\n  isValidElement(value: unknown): boolean;\n  /** Wraps a route-owned client tree so compiled leaf boundaries stay inside that React root. */\n  wrapClientGraph?(element: unknown): unknown;\n  /**\n   * Optional: locate where a streamed chunk stops being the static shell.\n   *\n   * Partial prerendering caches everything before the first dynamic boundary\n   * and refreshes the rest on the client, so it has to know where that\n   * boundary is. The markers are renderer-specific (React streams Fizz\n   * boundary ids and `$RC`/`$RS` reveal calls, Solid streams `<template\n   * id=\"pl-N\">` with `$df(N)`), so the renderer that emits them owns finding\n   * them. Returns the index to cut at, or -1 when the chunk is entirely\n   * static.\n   *\n   * A renderer that does not implement this gets no static shell at all:\n   * guessing would mean caching a per-request response as if it were shared.\n   */\n  findStaticShellBoundary?(chunk: string): number;\n  renderToString(element: unknown): string | Promise<string>;\n  /**\n   * Optional variant for renderers whose components emit document-head markup\n   * during render (e.g. <svelte:head>). `html` is the body markup exactly as\n   * renderToString would produce it; `head` is injected into the assembled\n   * document's <head>.\n   */\n  renderToStringWithHead?(\n    element: unknown,\n  ): { html: string; head: string } | Promise<{ html: string; head: string }>;\n  /** Optional runtime copy of the descriptor capabilities for diagnostics. */\n  readonly capabilities?: FarmRendererCapabilities;\n  /** Optional bootstrap required before this renderer hydrates server markup. */\n  generateHydrationScript?: () => string;\n  /** Optional component used to render route-level failures. */\n  ErrorBoundary?: unknown;\n  /** Optional streaming primitive. Renderers without one use buffered SSR. */\n  renderToPipeableStream?: (\n    element: unknown,\n    callbacks: {\n      onShellReady(): void;\n      onShellError(error: unknown): void;\n      onError(error: unknown): void;\n    },\n  ) => { pipe(destination: NodeJS.WritableStream): void };\n  /** WHATWG streaming primitive used by Web-stream-capable renderers. */\n  renderToReadableStream?: (\n    element: unknown,\n  ) => ReadableStream<Uint8Array | string> | Promise<ReadableStream<Uint8Array | string>>;\n}\n\nexport const REACT_RENDERER: Readonly<FarmRenderer> = Object.freeze({\n  name: \"react\",\n  vite: \"@farm.js/core/renderer/react/vite\",\n  server: \"@farm.js/core/renderer/react/server\",\n  client: \"@farm.js/core/renderer/react/client\",\n  jsxImportSource: \"react\",\n  dedupe: [\"react\", \"react-dom\", \"react/jsx-runtime\", \"react/jsx-dev-runtime\"],\n  optimizeDeps: [\n    \"react\",\n    \"react-dom\",\n    \"react-dom/client\",\n    \"react/jsx-runtime\",\n    \"react/jsx-dev-runtime\",\n  ],\n  capabilities: {\n    streaming: { node: true, web: false },\n    reconcilesRerenders: true,\n  },\n});\n\nexport function defineRenderer<const TRenderer extends FarmRenderer>(\n  renderer: TRenderer,\n): TRenderer {\n  return renderer;\n}\n\nexport function resolveFarmRenderer(renderer?: FarmRenderer): FarmRenderer {\n  const resolved = renderer || REACT_RENDERER;\n  const fields = [\"name\", \"vite\", \"server\", \"client\"] as const;\n\n  for (const field of fields) {\n    if (typeof resolved[field] !== \"string\" || resolved[field].trim().length === 0) {\n      throw new TypeError(`Farm renderer \\`${field}\\` must be a non-empty string.`);\n    }\n  }\n\n  return {\n    ...resolved,\n    componentExtensions: [...(resolved.componentExtensions || [])],\n    dedupe: [...(resolved.dedupe || [])],\n    optimizeDeps: [...(resolved.optimizeDeps || [])],\n    buildConcurrency: resolved.buildConcurrency || \"parallel\",\n    capabilities: getFarmRendererCapabilities(resolved),\n    options: resolved.options ? { ...resolved.options } : undefined,\n  };\n}\n\nexport async function readFarmRendererWebStream(\n  stream: ReadableStream<Uint8Array | string>,\n): Promise<string> {\n  const reader = stream.getReader();\n  const decoder = new TextDecoder();\n  let html = \"\";\n\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    html += typeof value === \"string\" ? value : decoder.decode(value, { stream: true });\n  }\n\n  return html + decoder.decode();\n}\n\nexport function isReactRenderer(renderer: Pick<FarmRenderer, \"name\"> | undefined): boolean {\n  return !renderer || renderer.name === \"react\";\n}\n\n/** Resolve an optional renderer module from the application's dependency graph. */\nexport function resolveFarmRendererModule(root: string, specifier: string): string {\n  const requireFromApp = createRequire(path.join(path.resolve(root), \"package.json\"));\n  return requireFromApp.resolve(specifier);\n}\n\nexport async function loadFarmRendererVitePlugins(\n  renderer: FarmRenderer,\n  root: string,\n  options: { ssr?: boolean } = {},\n): Promise<unknown[]> {\n  // React uses Vite's default automatic JSX transform today. Keep the legacy\n  // path dependency-free and avoid resolving Farm's own built package while\n  // running directly from source in the monorepo.\n  if (renderer.vite === REACT_RENDERER.vite) return [];\n\n  const modulePath = resolveFarmRendererModule(root, renderer.vite);\n  const rendererModule = (await import(pathToFileURL(modulePath).href)) as FarmRendererViteModule;\n\n  if (typeof rendererModule.createFarmRendererPlugin !== \"function\") {\n    throw new Error(\n      `Renderer \\`${renderer.name}\\` module ${renderer.vite} must export createFarmRendererPlugin().`,\n    );\n  }\n\n  const created = await rendererModule.createFarmRendererPlugin({\n    ...options,\n    rendererOptions: renderer.options,\n  });\n  if (!created) return [];\n  return Array.isArray(created) ? [...created] : [created];\n}\n"],"mappings":"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","names":["path"]}