useFlight
uses node:fs
Load the codebase's server code (the cache's modules, and the packages they import) as React's server components
environment does: with the react-server condition, so it gets React's server build (cache, no hooks) and
Flight's server. A module of browser code a server component renders (a component, a context it provides, a
lazy one: sent.client) loads as Flight client references, by hash, instead of its code: it runs in the
browser. A server function server components give the browser (sent.server) is registered as a server reference.
Packages load from server code's own copies of them (mirrorPackage), so server rendering (the page's first
paint), which has its own React, never shares one with it.
For this process, from now on. members sees definitions not saved yet (a scratch file's tests).
import { registerTransformHook } from "#codebase/tcmts/cli/codebase"; import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { flightServerFile, mirrorPackage, packageReferences } from "#codebase/tcmts/cli/flight"; import type { FlightGraph, FlightHooks } from "#codebase/tcmts/cli/flight"; import { exportId } from "#codebase/tcmts/core/hash"; import type { DefRecord } from "#codebase/tcmts/core/types"; import fs from "node:fs"; import { registerHooks } from "node:module"; import { pathToFileURL } from "node:url"; export function useFlight( cb: Codebase, sent: { client(hash: string): boolean; server(hash: string): boolean; chunks?(componentHash: string): string[]; packageImports?(componentHash: string): string[]; }, members: (componentHash: string) => DefRecord[] = (c) => cb.store.componentMembers(c), ): void { const g = ((globalThis as { __tcmFlight?: FlightHooks }).__tcmFlight ??= { graphs: new Map(), hooked: false, }); // (by its real path: what Node resolves files to) const dir = fs.realpathSync(cb.lay.cacheDir); g.graphs.set(pathToFileURL(dir).href + "/", { dir, isRef: (h) => sent.client(h), isServerRef: (h) => sent.server(h), chunks: (c) => sent.chunks?.(c) ?? [], packageImports: (c) => sent.packageImports?.(c) ?? [], members: (c) => members(c) .filter((m) => m.kind !== "type") .map((m) => m.hash), server: pathToFileURL(flightServerFile(cb)).href, }); if (g.hooked) return; g.hooked = true; // (the hook compiling cache modules goes first: hooks run newest first, so this one sees each module before it, and // one registered later, by code that opens a codebase, would come before this one and load browser code as code) registerTransformHook(); // the graph a module's in: a cache's own, or a package's file in server code's copies (`mirrorPackage`) const graphOf = (url: string): [string, FlightGraph] | undefined => { for (const [dir, graph] of g.graphs) if (url.startsWith(dir) && !url.slice(dir.length).startsWith("node_modules/")) return [dir, graph]; return undefined; }; registerHooks({ resolve(specifier, context, next) { const at = context.parentURL ? graphOf(context.parentURL) : undefined; if (!at) return next(specifier, context); const r = next(specifier, { ...context, conditions: [...(context.conditions ?? []), "react-server"], }); // a package of the cache's: its copy (of its directory in pnpm's store, `.pnpm/<entry>`, or npm's flat // `node_modules`, `<name>` or `@scope/<name>`) const [dir, graph] = at; const installed = `${dir}node_modules/`; if (!r.url.startsWith(installed)) return r; const rest = r.url.slice(installed.length); const parts = rest.split("/"); const depth = parts[0] === ".pnpm" || parts[0]!.startsWith("@") ? 2 : 1; mirrorPackage(graph.dir, parts.slice(0, depth).map(decodeURIComponent).join("/")); return { ...r, url: `${dir}server_modules/${rest}` }; }, load(url, context, next) { const m = /\/([a-z2-7]{52})\.tsx?$/.exec(url); const graph = m ? graphOf(url)?.[1] : undefined; if (!m || !graph) return next(url, context); const members = graph.members(m[1]!); if (!members.some((h) => graph.isRef(h))) { // a server function server components give the browser: Flight sends it as a reference to its hash, which the // browser calls as it calls any server function const given = members.filter((h) => graph.isServerRef(h)); const packaged = graph.packageImports(m[1]!); if (!given.length && !packaged.length) return next(url, context); const loaded = next(url, context); const source = [ // a package's browser component it renders: a reference to it, which the browser has, not its code packageReferences(String(loaded.source), packaged, graph.server), ...(given.length ? [ `import { registerServerReference as __tcm_serverReference } from ${JSON.stringify(graph.server)};`, // (its exports, as the module's own: what it names them inside isn't known here) `import * as __tcm_self from ${JSON.stringify(url)};`, ] : []), ...given.map( (h) => `__tcm_serverReference(__tcm_self.${exportId(h)}, ${JSON.stringify(h)}, "c");`, ), ].join("\n"); return { ...loaded, source }; } // (each by its hash, which the browser's runtime finds it by; its bundles, which server rendering puts in the // page as scripts, are only the chunks it loads with import(), a `lazy` one's: the browser has the rest) const bundles = JSON.stringify(graph.chunks(m[1]!)); const source = [ `import { createClientReference } from ${JSON.stringify(graph.server)};`, ...members.map( (h) => `export const ${exportId(h)} = createClientReference(${JSON.stringify(h)}, "c", ${bundles});`, ), ].join("\n"); return { format: "module", source, shortCircuit: true }; }, }); }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.codebase.registerTransformHook
- tcmts.cli.flight.FlightGraph
- tcmts.cli.flight.FlightHooks
- tcmts.cli.flight.flightServerFile
- tcmts.cli.flight.mirrorPackage
- tcmts.cli.flight.packageReferences
- tcmts.core.hash.exportId
- tcmts.core.types.DefRecord