Files
WRNexusJS/.publish/ssr/README.md
T
2026-07-12 15:55:18 +05:30

106 lines
6.0 KiB
Markdown

# @wrnexus/ssr
> Server-side rendering: wraps a page's HTML body in a complete HTML document with a metadata-driven `<head>`.
Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.
## Overview
Pages in WrNexus return an HTML string for the body. `@wrnexus/ssr` takes that body and produces a full HTML document — building the `<head>` from page metadata and global SEO defaults, resolving canonical/Open Graph/Twitter tags, and injecting module preloads and `<script type="module">` tags. It is deliberately server-only: nothing in this package touches the DOM or ships to the browser, keeping server code genuinely server-only. Reach for it on the server when turning a rendered page body into a response document.
## Installation
```bash
bun add @wrnexus/ssr
```
> Private package — the machine must be authenticated to the `wrnexus` npm org
> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported).
## API
The package has a single export.
### `renderDocument(opts: RenderOptions): string`
Renders a complete HTML document as a string, beginning with `<!doctype html>`. All metadata is HTML-escaped (via `escapeHtml` from `@wrnexus/core`), so a malicious title or description cannot break out of its element or attribute. The body is placed inside `<div id="app">`.
#### `RenderOptions`
| Field | Type | Description |
| -------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `meta` | `PageMeta` | Page metadata for the document head (required). |
| `body` | `string` | Rendered HTML for the body, placed inside `#app` (required). |
| `seo` | `SeoConfig` | Global SEO defaults, typically from `wrnexus.config.ts`. |
| `url` | `URL` | Current request URL, used to resolve canonical/Open Graph URLs. |
| `scripts` | `string[]` | URLs of `<script type="module">` tags to load (e.g. per-island chunks or the reactive runtime). Each also gets a `<link rel="modulepreload">`. |
| `defaultTitle` | `string` | Default document title used when `meta.title` is absent. |
| `extraHead` | `string` | Raw HTML injected at the end of `<head>` (trusted, framework-controlled — not escaped). |
| `extraBody` | `string` | Raw HTML injected at the end of `<body>` (trusted, framework-controlled — not escaped). |
| `htmlAttrs` | `string` | Attributes for the `<html>` element, e.g. ` data-theme="dark"` (trusted). |
`PageMeta` and `SeoConfig` come from `@wrnexus/core`. `PageMeta` is an alias of `SeoConfig`, whose fields are all optional:
```ts
type SeoConfig = {
title?: string;
titleTemplate?: string; // e.g. "%s — My Site"; %s is replaced with the page title
description?: string;
canonical?: string;
canonicalBase?: string; // origin used to absolutize canonical/image URLs
robots?: string;
keywords?: string | string[];
image?: string;
siteName?: string;
type?: string; // Open Graph type; defaults to "website"
locale?: string;
twitterCard?: string; // defaults to "summary"
twitterSite?: string;
themeColor?: string;
};
```
#### Metadata resolution
`renderDocument` merges page metadata (`meta`) over global defaults (`seo`), field by field, so per-page values win. Notable behavior:
- **Title**: uses `meta.title`, else `seo.title`, else `defaultTitle`, else `"WrNexus"`. When the page sets its own title and `seo.titleTemplate` contains `%s`, the template is applied.
- **Canonical / image URLs**: resolved against `canonicalBase` (or the request `url`'s origin) into absolute URLs when possible.
- **Keywords**: an array is joined with `", "`.
- **Emitted tags**: `<title>`, and as applicable `description`, `robots`, `keywords`, `theme-color`, and `canonical` link, plus Open Graph (`og:title`, `og:description`, `og:type`, `og:url`, `og:site_name`, `og:locale`, `og:image`) and Twitter (`twitter:card`, `twitter:title`, `twitter:description`, `twitter:image`, `twitter:site`) meta tags. The document always includes `charset`, `viewport`, and a `/favicon.ico` icon link.
## Usage
```ts
import { renderDocument } from "@wrnexus/ssr";
const html = renderDocument({
meta: {
title: "About Us",
description: "Learn more about our team.",
},
seo: {
titleTemplate: "%s — Acme",
siteName: "Acme",
canonicalBase: "https://acme.example",
twitterSite: "@acme",
},
url: new URL("https://acme.example/about"),
body: "<h1>About Us</h1>",
scripts: ["/_wire/runtime.js", "/_wire/islands/about.js"],
htmlAttrs: ' data-theme="dark"',
});
return new Response(html, {
headers: { "content-type": "text/html; charset=utf-8" },
});
```
The produced document has `<title>About Us — Acme</title>`, the SEO/Open Graph/Twitter tags derived from the merged metadata, a `modulepreload` link and module `<script>` for each entry in `scripts`, and the body wrapped in `<div id="app">`.
## Requirements / Notes
- **Server-only.** This module never imports or touches the DOM and is safe to keep out of client bundles.
- **Depends on [`@wrnexus/core`](../core)** for `escapeHtml` and the `PageMeta` / `SeoConfig` types.
- **Bun-only** — like the rest of WrNexus, this package targets the Bun runtime (Node is not supported).