UNPKG

seia.js

Version:

Lightweight SSR framework for React Server Components

112 lines (111 loc) 3.33 kB
import { mkdir, writeFile } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import { P, match } from 'ts-pattern'; import { mergeConfig, build as vite } from 'vite'; import { detectBoundaries } from './plugins/detect-boundaries.js'; import { filterCss } from './plugins/filter-css.js'; import { injectClient } from './plugins/inject-client.js'; import { rscTransform } from './plugins/rsc-transform.js'; import { silenceDirective } from './plugins/silence-directive.js'; const defaultConfig = { plugins: [ silenceDirective() ], build: { target: 'esnext', lib: { formats: [ 'es' ] } } }; const writeAsset = async ({ fileName, source }, { root, paths: { dist } })=>{ const resolvedDestination = join(root, dist, fileName); await mkdir(dirname(resolvedDestination), { recursive: true }); return writeFile(resolvedDestination, source); }; /** * Build the Seia project with the given configuration. */ export const build = async (config)=>{ const { paths: { src, entry: entryFile } } = config; const entry = join(src, entryFile); const rawBoundariesOutput = await vite(mergeConfig(defaultConfig, { plugins: [ detectBoundaries({ config }) ], build: { cssMinify: true, lib: { entry }, ssr: true, ssrEmitAssets: true, write: false } })); // BoundariesOutput should be array if (!Array.isArray(rawBoundariesOutput)) throw new Error('boundariesOutput is not an array'); const [{ output: boundariesOutput }] = rawBoundariesOutput; const assets = boundariesOutput.filter((file)=>file.type === 'asset'); const boundariesManifest = match(assets.find(({ fileName })=>fileName === 'boundaries-manifest.json')?.source).with(P.string, (source)=>JSON.parse(source)).run(); const styleAssets = assets.filter(({ fileName })=>fileName.endsWith('.css')); // Hydration await vite(mergeConfig(defaultConfig, { plugins: [ filterCss(), injectClient({ clientBoundaries: boundariesManifest, config }) ], build: { lib: { entry: '\0client.js', fileName: 'client' }, emptyOutDir: true } })); // Flush bundled style assets await Promise.all(styleAssets.map(async (asset)=>writeAsset(asset, config))); // RSC await vite(mergeConfig(defaultConfig, { plugins: [ filterCss(), rscTransform({ config }) ], build: { lib: { entry }, outDir: 'dist/rsc', ssr: true }, ssr: { external: true } })); // SSR if (boundariesManifest.length > 0) await vite(mergeConfig(defaultConfig, { plugins: [ filterCss() ], build: { lib: { entry: boundariesManifest }, outDir: 'dist/ssr', ssr: true }, ssr: { external: true } })); };