@canonical/react-ssr 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/bin/serve-express.js +86 -0
- package/dist/esm/bin/serve-express.js.map +1 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/index.js +3 -0
- package/dist/esm/lib/index.js.map +1 -0
- package/dist/esm/lib/renderer/Extractor.js +172 -0
- package/dist/esm/lib/renderer/Extractor.js.map +1 -0
- package/dist/esm/lib/renderer/JSXRenderer.js +283 -0
- package/dist/esm/lib/renderer/JSXRenderer.js.map +1 -0
- package/dist/esm/lib/renderer/SitemapRenderer.js +241 -0
- package/dist/esm/lib/renderer/SitemapRenderer.js.map +1 -0
- package/dist/esm/lib/renderer/TextRenderer.js +124 -0
- package/dist/esm/lib/renderer/TextRenderer.js.map +1 -0
- package/dist/esm/lib/renderer/constants.js.map +1 -0
- package/dist/esm/lib/renderer/index.js +6 -0
- package/dist/esm/lib/renderer/index.js.map +1 -0
- package/dist/esm/lib/renderer/types.js +10 -0
- package/dist/esm/lib/renderer/types.js.map +1 -0
- package/dist/esm/lib/server/index.js +3 -0
- package/dist/esm/lib/server/index.js.map +1 -0
- package/dist/esm/lib/server/serveStream.js +53 -0
- package/dist/esm/lib/server/serveStream.js.map +1 -0
- package/dist/esm/lib/server/serveString.js +49 -0
- package/dist/esm/lib/server/serveString.js.map +1 -0
- package/dist/types/bin/serve-express.d.ts +24 -0
- package/dist/types/bin/serve-express.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/index.d.ts +3 -0
- package/dist/types/lib/index.d.ts.map +1 -0
- package/dist/types/lib/renderer/Extractor.d.ts +93 -0
- package/dist/types/lib/renderer/Extractor.d.ts.map +1 -0
- package/dist/types/lib/renderer/JSXRenderer.d.ts +163 -0
- package/dist/types/lib/renderer/JSXRenderer.d.ts.map +1 -0
- package/dist/types/lib/renderer/SitemapRenderer.d.ts +153 -0
- package/dist/types/lib/renderer/SitemapRenderer.d.ts.map +1 -0
- package/dist/types/lib/renderer/TextRenderer.d.ts +83 -0
- package/dist/types/lib/renderer/TextRenderer.d.ts.map +1 -0
- package/dist/types/lib/renderer/constants.d.ts.map +1 -0
- package/dist/types/lib/renderer/index.d.ts +7 -0
- package/dist/types/lib/renderer/index.d.ts.map +1 -0
- package/dist/types/lib/renderer/types.d.ts +161 -0
- package/dist/types/lib/renderer/types.d.ts.map +1 -0
- package/dist/types/lib/server/index.d.ts +3 -0
- package/dist/types/lib/server/index.d.ts.map +1 -0
- package/dist/types/lib/server/serveStream.d.ts +41 -0
- package/dist/types/lib/server/serveStream.d.ts.map +1 -0
- package/dist/types/lib/server/serveString.d.ts +37 -0
- package/dist/types/lib/server/serveString.d.ts.map +1 -0
- package/package.json +32 -17
- package/dist/esm/renderer/Extractor.js +0 -127
- package/dist/esm/renderer/Extractor.js.map +0 -1
- package/dist/esm/renderer/JSXRenderer.js +0 -168
- package/dist/esm/renderer/JSXRenderer.js.map +0 -1
- package/dist/esm/renderer/constants.js.map +0 -1
- package/dist/esm/renderer/index.js +0 -4
- package/dist/esm/renderer/index.js.map +0 -1
- package/dist/esm/renderer/types.js +0 -2
- package/dist/esm/renderer/types.js.map +0 -1
- package/dist/esm/server/index.js +0 -2
- package/dist/esm/server/index.js.map +0 -1
- package/dist/esm/server/serve-express.js +0 -58
- package/dist/esm/server/serve-express.js.map +0 -1
- package/dist/esm/server/serve.js +0 -41
- package/dist/esm/server/serve.js.map +0 -1
- package/dist/types/renderer/Extractor.d.ts +0 -68
- package/dist/types/renderer/Extractor.d.ts.map +0 -1
- package/dist/types/renderer/JSXRenderer.d.ts +0 -71
- package/dist/types/renderer/JSXRenderer.d.ts.map +0 -1
- package/dist/types/renderer/constants.d.ts.map +0 -1
- package/dist/types/renderer/index.d.ts +0 -5
- package/dist/types/renderer/index.d.ts.map +0 -1
- package/dist/types/renderer/types.d.ts +0 -35
- package/dist/types/renderer/types.d.ts.map +0 -1
- package/dist/types/server/index.d.ts +0 -2
- package/dist/types/server/index.d.ts.map +0 -1
- package/dist/types/server/serve-express.d.ts +0 -3
- package/dist/types/server/serve-express.d.ts.map +0 -1
- package/dist/types/server/serve.d.ts +0 -30
- package/dist/types/server/serve.d.ts.map +0 -1
- /package/dist/esm/{renderer → lib/renderer}/constants.js +0 -0
- /package/dist/types/{renderer → lib/renderer}/constants.d.ts +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Collected representation of an HTML element extracted during SAX parsing.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the subset of DOM element data needed to construct a React element:
|
|
6
|
+
* the tag name, its attributes, and optional text content.
|
|
7
|
+
*/
|
|
8
|
+
interface CollectedElement {
|
|
9
|
+
name: string;
|
|
10
|
+
attribs: Record<string, string>;
|
|
11
|
+
text: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The three buckets into which `parseHeadElements` dispatches extracted tags.
|
|
15
|
+
*/
|
|
16
|
+
interface ParseResult {
|
|
17
|
+
links: CollectedElement[];
|
|
18
|
+
scripts: CollectedElement[];
|
|
19
|
+
others: CollectedElement[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Extracts `<head>` elements from an HTML string and converts them to React elements.
|
|
23
|
+
*
|
|
24
|
+
* The primary use case is server-side rendering with Vite: the build process produces
|
|
25
|
+
* an HTML shell containing `<script>`, `<link>`, `<meta>`, `<title>`, `<style>`, and
|
|
26
|
+
* `<base>` tags. This class parses that shell via `parseHeadElements` (a SAX handler —
|
|
27
|
+
* no DOM tree is constructed) and exposes the extracted tags as React elements that the
|
|
28
|
+
* server entrypoint component can inject into its rendered output.
|
|
29
|
+
*
|
|
30
|
+
* Parsing happens once in the constructor. The three getter methods return the
|
|
31
|
+
* elements in the order they appeared in the original HTML.
|
|
32
|
+
*/
|
|
33
|
+
export default class Extractor {
|
|
34
|
+
/** Parsed head elements, grouped by category. */
|
|
35
|
+
protected readonly parsed: ParseResult;
|
|
36
|
+
/**
|
|
37
|
+
* Create an Extractor for the given HTML string.
|
|
38
|
+
*
|
|
39
|
+
* @param html - The full HTML string to parse (typically from a Vite build).
|
|
40
|
+
*/
|
|
41
|
+
constructor(html: string);
|
|
42
|
+
/**
|
|
43
|
+
* Convert an HTML attribute name to the corresponding React prop name.
|
|
44
|
+
*
|
|
45
|
+
* Checks `REACT_KEYS_DICTIONARY` for known exceptions (e.g. `class` becomes
|
|
46
|
+
* `className`). Falls back to generic camelCase conversion, which correctly
|
|
47
|
+
* handles `data-*` and `aria-*` attributes.
|
|
48
|
+
*
|
|
49
|
+
* @param key - The HTML attribute name, e.g. `"crossorigin"` or `"data-test-id"`.
|
|
50
|
+
* @returns The React prop name, e.g. `"crossOrigin"` or `"dataTestId"`.
|
|
51
|
+
*/
|
|
52
|
+
protected convertKeyToReactKey(key: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Convert a collected element into a `React.createElement` call.
|
|
55
|
+
*
|
|
56
|
+
* Attributes are mapped to React prop names via `convertKeyToReactKey`.
|
|
57
|
+
* A stable `key` prop is synthesised from the tag name and the element's
|
|
58
|
+
* position index so that React can reconcile lists of head elements.
|
|
59
|
+
*
|
|
60
|
+
* If the element has text content (e.g. `<title>My App</title>` or
|
|
61
|
+
* `<style>.body { color: red }</style>`), it is passed as the element's
|
|
62
|
+
* `children` argument.
|
|
63
|
+
*
|
|
64
|
+
* @param element - The collected element data from SAX parsing.
|
|
65
|
+
* @param index - The position of this element within its sibling group.
|
|
66
|
+
* @returns A React element matching the original HTML tag.
|
|
67
|
+
*/
|
|
68
|
+
protected convertToReactElement(element: CollectedElement, index: number): React.ReactElement;
|
|
69
|
+
/**
|
|
70
|
+
* Return all `<link>` elements as React elements, in document order.
|
|
71
|
+
*
|
|
72
|
+
* Typically used to inject stylesheet and preload links into the
|
|
73
|
+
* server-rendered `<head>`.
|
|
74
|
+
*/
|
|
75
|
+
getLinkElements(): React.ReactElement[];
|
|
76
|
+
/**
|
|
77
|
+
* Return all `<script>` elements as React elements, in document order.
|
|
78
|
+
*
|
|
79
|
+
* Preserving order is important: Vite dev mode emits module scripts
|
|
80
|
+
* that depend on being evaluated in sequence.
|
|
81
|
+
*/
|
|
82
|
+
getScriptElements(): React.ReactElement[];
|
|
83
|
+
/**
|
|
84
|
+
* Return all non-script, non-link head elements as React elements, in document order.
|
|
85
|
+
*
|
|
86
|
+
* This covers `<title>`, `<style>`, `<meta>`, and `<base>`. Elements are returned
|
|
87
|
+
* in the order they appeared in the HTML, preserving inter-type ordering (a `<meta>`
|
|
88
|
+
* between two `<style>` tags stays between them).
|
|
89
|
+
*/
|
|
90
|
+
getOtherHeadElements(): React.ReactElement[];
|
|
91
|
+
}
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=Extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Extractor.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/Extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAoB1B;;;;;GAKG;AACH,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED;;GAEG;AACH,UAAU,WAAW;IACnB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAuED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B,iDAAiD;IACjD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAEvC;;;;OAIG;gBACS,IAAI,EAAE,MAAM;IAIxB;;;;;;;;;OASG;IACH,SAAS,CAAC,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAInD;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,qBAAqB,CAC7B,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC,YAAY;IAWrB;;;;;OAKG;IACI,eAAe,IAAI,KAAK,CAAC,YAAY,EAAE;IAI9C;;;;;OAKG;IACI,iBAAiB,IAAI,KAAK,CAAC,YAAY,EAAE;IAMhD;;;;;;OAMG;IACI,oBAAoB,IAAI,KAAK,CAAC,YAAY,EAAE;CAGpD"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { type RenderToPipeableStreamOptions } from "react-dom/server";
|
|
2
|
+
import Extractor from "./Extractor.js";
|
|
3
|
+
import type { PipeableStreamResult, RendererOptions, ServerEntrypoint, ServerEntrypointProps } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Server-side renderer for a React component.
|
|
6
|
+
*
|
|
7
|
+
* Accepts a React `ServerEntrypoint` component, optional initial data for
|
|
8
|
+
* hydration, and an optional HTML shell string (from a Vite build) whose
|
|
9
|
+
* `<head>` tags are extracted and injected into the rendered output.
|
|
10
|
+
*
|
|
11
|
+
* Three rendering strategies are available:
|
|
12
|
+
*
|
|
13
|
+
* - **ReadableStream** (`renderToReadableStream`) — returns a web `ReadableStream`.
|
|
14
|
+
* Works natively with Bun, Deno, Cloudflare Workers, and any runtime that
|
|
15
|
+
* supports the Web Streams API. Supports Suspense and progressive rendering.
|
|
16
|
+
*
|
|
17
|
+
* - **PipeableStream** (`renderToPipeableStream`) — returns a Node.js pipeable stream.
|
|
18
|
+
* Works with Express, Fastify, and Node's built-in `http` module. Supports Suspense
|
|
19
|
+
* and progressive rendering.
|
|
20
|
+
*
|
|
21
|
+
* - **String** (`renderToString`) — returns the full HTML as a string. All Suspense
|
|
22
|
+
* boundaries resolve synchronously. The output is cacheable and works with Vite
|
|
23
|
+
* HMR in dev mode.
|
|
24
|
+
*
|
|
25
|
+
* All strategies inject `<script>` and `<link>` tags from the HTML shell via
|
|
26
|
+
* React's `bootstrapScripts` / `bootstrapModules` mechanism, and embed
|
|
27
|
+
* `initialData` as a global `window.__INITIAL_DATA__` variable for client
|
|
28
|
+
* hydration.
|
|
29
|
+
*
|
|
30
|
+
* The renderer is transport-agnostic — it never writes to a response object.
|
|
31
|
+
* HTTP status codes and metadata are exposed via `statusCode` and `statusReady`
|
|
32
|
+
* for the consumer to use when constructing the response.
|
|
33
|
+
*
|
|
34
|
+
* @typeParam TComponent - The server entrypoint component type.
|
|
35
|
+
* @typeParam InitialData - Shape of the data embedded for client hydration.
|
|
36
|
+
*/
|
|
37
|
+
export default class JSXRenderer<TComponent extends ServerEntrypoint<InitialData>, InitialData extends Record<string, unknown>> {
|
|
38
|
+
protected readonly Component: TComponent;
|
|
39
|
+
protected readonly initialData: InitialData;
|
|
40
|
+
protected readonly options: RendererOptions;
|
|
41
|
+
protected extractor: Extractor | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* HTTP status code determined during rendering.
|
|
44
|
+
*
|
|
45
|
+
* Starts at 200 and is set to 500 if a shell error occurs during streaming.
|
|
46
|
+
* For `renderToString`, it is always 200 (errors throw instead).
|
|
47
|
+
*
|
|
48
|
+
* Read this after the render method returns (for `renderToReadableStream` and
|
|
49
|
+
* `renderToString`) or after awaiting `statusReady` (for `renderToPipeableStream`).
|
|
50
|
+
*/
|
|
51
|
+
statusCode: number;
|
|
52
|
+
/**
|
|
53
|
+
* Resolves when `statusCode` is determined.
|
|
54
|
+
*
|
|
55
|
+
* For `renderToReadableStream` and `renderToString`, this is already resolved
|
|
56
|
+
* by the time the method returns. For `renderToPipeableStream`, it resolves
|
|
57
|
+
* asynchronously when the shell is ready or errors.
|
|
58
|
+
*/
|
|
59
|
+
statusReady: Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Create a renderer bound to a specific component and initial data.
|
|
62
|
+
*
|
|
63
|
+
* If `options.htmlString` is provided, the HTML is parsed once to extract
|
|
64
|
+
* `<head>` elements. These elements are then available as React elements
|
|
65
|
+
* via `getComponentProps()` for injection during rendering.
|
|
66
|
+
*
|
|
67
|
+
* @param Component - The React server entrypoint component.
|
|
68
|
+
* @param initialData - Data to embed in `window.__INITIAL_DATA__` for client hydration.
|
|
69
|
+
* @param options - Renderer configuration: locale, HTML shell, and stream options.
|
|
70
|
+
*/
|
|
71
|
+
constructor(Component: TComponent, initialData?: InitialData, options?: RendererOptions);
|
|
72
|
+
/**
|
|
73
|
+
* Return the locale for the rendered page.
|
|
74
|
+
*
|
|
75
|
+
* Defaults to `"en"` when no `defaultLocale` was provided in options.
|
|
76
|
+
* The locale is passed as the `lang` prop to the server entrypoint component,
|
|
77
|
+
* which typically sets it as the `<html lang>` attribute.
|
|
78
|
+
*/
|
|
79
|
+
getLocale(): string;
|
|
80
|
+
/**
|
|
81
|
+
* Assemble the props passed to the server entrypoint component.
|
|
82
|
+
*
|
|
83
|
+
* Combines the locale, initial data, and (when an HTML shell was provided)
|
|
84
|
+
* the extracted script, link, and other head elements into a single props
|
|
85
|
+
* object conforming to `ServerEntrypointProps`.
|
|
86
|
+
*/
|
|
87
|
+
protected getComponentProps(): ServerEntrypointProps<InitialData>;
|
|
88
|
+
/**
|
|
89
|
+
* Extract `src` URLs from script elements that match a given loading strategy.
|
|
90
|
+
*
|
|
91
|
+
* Filters the provided React `<script>` elements by their `type` attribute:
|
|
92
|
+
* `"module"` selects ES module scripts, `"classic"` selects everything else.
|
|
93
|
+
* Returns only the `src` values, discarding inline scripts that have no `src`.
|
|
94
|
+
*
|
|
95
|
+
* @param scripts - React elements representing `<script>` tags.
|
|
96
|
+
* @param type - `"module"` for ES modules, `"classic"` for traditional scripts.
|
|
97
|
+
* @returns An array of script source URLs.
|
|
98
|
+
*/
|
|
99
|
+
protected getScriptSourcesByType(scripts: React.ReactElement[], type: "module" | "classic"): string[];
|
|
100
|
+
/**
|
|
101
|
+
* Merge renderer-managed options into the user-provided stream options.
|
|
102
|
+
*
|
|
103
|
+
* Populates three React streaming options unless the caller already supplied them:
|
|
104
|
+
*
|
|
105
|
+
* - `bootstrapScriptContent` — a `<script>` body that assigns `initialData` to
|
|
106
|
+
* `window.__INITIAL_DATA__`. The JSON is escaped to prevent `</script>` injection.
|
|
107
|
+
* - `bootstrapScripts` — `src` URLs for classic (non-module) scripts extracted from
|
|
108
|
+
* the HTML shell. React strips `<script>` tags during streaming, so these must
|
|
109
|
+
* be re-injected through this mechanism.
|
|
110
|
+
* - `bootstrapModules` — same as above, for ES module scripts.
|
|
111
|
+
*
|
|
112
|
+
* @param props - The assembled component props (used to read `initialData` and `scriptElements`).
|
|
113
|
+
* @returns A merged options object safe to pass to either streaming API.
|
|
114
|
+
*/
|
|
115
|
+
protected enrichRendererOptions(props: ServerEntrypointProps<InitialData>): RenderToPipeableStreamOptions;
|
|
116
|
+
/**
|
|
117
|
+
* Render the component to a web `ReadableStream`.
|
|
118
|
+
*
|
|
119
|
+
* Uses `react-dom/server.renderToReadableStream` for environments that support
|
|
120
|
+
* the Web Streams API (Bun, Deno, Cloudflare Workers, browsers). Supports
|
|
121
|
+
* Suspense and progressive rendering.
|
|
122
|
+
*
|
|
123
|
+
* On shell error, `statusCode` is set to 500 and a fallback HTML stream is
|
|
124
|
+
* returned. On success, `statusCode` is 200.
|
|
125
|
+
*
|
|
126
|
+
* @note This method is impure — it mutates `statusCode` and `statusReady`.
|
|
127
|
+
*
|
|
128
|
+
* @param signal - Optional `AbortSignal` for request cancellation.
|
|
129
|
+
* @returns A `ReadableStream` of the rendered HTML.
|
|
130
|
+
*/
|
|
131
|
+
renderToReadableStream: (signal?: AbortSignal) => Promise<ReadableStream>;
|
|
132
|
+
/**
|
|
133
|
+
* Render the component to a Node.js pipeable stream.
|
|
134
|
+
*
|
|
135
|
+
* Uses `react-dom/server.renderToPipeableStream` for Node.js environments
|
|
136
|
+
* (Express, Fastify, plain `http.createServer`). Supports Suspense and
|
|
137
|
+
* progressive rendering.
|
|
138
|
+
*
|
|
139
|
+
* Returns `{ pipe, abort }` synchronously. The `statusCode` is set
|
|
140
|
+
* asynchronously when the shell is ready or errors — await `statusReady`
|
|
141
|
+
* before reading it.
|
|
142
|
+
*
|
|
143
|
+
* @note This method is impure — it mutates `statusCode` and `statusReady`.
|
|
144
|
+
*
|
|
145
|
+
* @returns The pipe/abort handles for the rendered stream.
|
|
146
|
+
*/
|
|
147
|
+
renderToPipeableStream: () => PipeableStreamResult;
|
|
148
|
+
/**
|
|
149
|
+
* Render the component to a complete HTML string.
|
|
150
|
+
*
|
|
151
|
+
* Uses `react-dom/server.renderToString` to produce the full HTML
|
|
152
|
+
* synchronously. All Suspense boundaries resolve before the method returns.
|
|
153
|
+
* The output is cacheable and compatible with Vite's HMR in dev mode.
|
|
154
|
+
*
|
|
155
|
+
* Sets `statusCode` to 200 on success. On error, throws (consumer catches).
|
|
156
|
+
*
|
|
157
|
+
* @note This method is impure — it mutates `statusCode`.
|
|
158
|
+
*
|
|
159
|
+
* @returns The complete HTML string.
|
|
160
|
+
*/
|
|
161
|
+
renderToString: () => string;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=JSXRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JSXRenderer.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/JSXRenderer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,6BAA6B,EAInC,MAAM,kBAAkB,CAAC;AAE1B,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW,CAC9B,UAAU,SAAS,gBAAgB,CAAC,WAAW,CAAC,EAChD,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAoCzC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,UAAU;IACxC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAC3C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe;IApC7C,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IAE3C;;;;;;;;OAQG;IACI,UAAU,SAAO;IAExB;;;;;;OAMG;IACI,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAqB;IAEtD;;;;;;;;;;OAUG;gBAEkB,SAAS,EAAE,UAAU,EACrB,WAAW,GAAE,WAA+B,EAC5C,OAAO,GAAE,eAAoB;IAOlD;;;;;;OAMG;IACI,SAAS,IAAI,MAAM;IAI1B;;;;;;OAMG;IACH,SAAS,CAAC,iBAAiB,IAAI,qBAAqB,CAAC,WAAW,CAAC;IAUjE;;;;;;;;;;OAUG;IACH,SAAS,CAAC,sBAAsB,CAC9B,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,EAC7B,IAAI,EAAE,QAAQ,GAAG,SAAS,GACzB,MAAM,EAAE;IAmBX;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,qBAAqB,CAC7B,KAAK,EAAE,qBAAqB,CAAC,WAAW,CAAC,GACxC,6BAA6B;IA8BhC;;;;;;;;;;;;;;OAcG;IACH,sBAAsB,GACpB,SAAS,WAAW,KACnB,OAAO,CAAC,cAAc,CAAC,CAuCxB;IAEF;;;;;;;;;;;;;;OAcG;IACH,sBAAsB,QAAO,oBAAoB,CA0C/C;IAEF;;;;;;;;;;;;OAYG;IACH,cAAc,QAAO,MAAM,CAOzB;CACH"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { PipeableStreamResult, SitemapConfig, SitemapGetter, SitemapItem } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Renders an XML sitemap from a set of async data sources.
|
|
4
|
+
*
|
|
5
|
+
* Unlike `JSXRenderer`, this renderer produces XML (not HTML) and does not use
|
|
6
|
+
* React. The render pipeline is functional: each stage returns data rather than
|
|
7
|
+
* mutating instance state, so calling render methods multiple times produces
|
|
8
|
+
* identical results for the same underlying data.
|
|
9
|
+
*
|
|
10
|
+
* Implements the same three render methods as `JSXRenderer` — `renderToReadableStream`,
|
|
11
|
+
* `renderToPipeableStream`, and `renderToString` — with the same `statusCode` /
|
|
12
|
+
* `statusReady` metadata contract. This allows consumers to use either renderer
|
|
13
|
+
* interchangeably.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const renderer = new SitemapRenderer(
|
|
18
|
+
* [() => fetchPages(), () => fetchPosts()],
|
|
19
|
+
* { baseUrl: "https://example.com", defaultChangefreq: "weekly" },
|
|
20
|
+
* );
|
|
21
|
+
*
|
|
22
|
+
* const stream = await renderer.renderToReadableStream();
|
|
23
|
+
* return new Response(stream, {
|
|
24
|
+
* status: renderer.statusCode,
|
|
25
|
+
* headers: { "Content-Type": "application/xml; charset=utf-8" },
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export default class SitemapRenderer {
|
|
30
|
+
protected readonly getters: readonly SitemapGetter[];
|
|
31
|
+
protected readonly config: SitemapConfig;
|
|
32
|
+
/**
|
|
33
|
+
* HTTP status code determined during rendering.
|
|
34
|
+
*
|
|
35
|
+
* Set to 200 on successful render. Errors from getters propagate as
|
|
36
|
+
* thrown exceptions rather than setting this to 500 — the consumer's
|
|
37
|
+
* error handler decides the status code for unexpected failures.
|
|
38
|
+
*/
|
|
39
|
+
statusCode: number;
|
|
40
|
+
/**
|
|
41
|
+
* Resolves when `statusCode` is determined.
|
|
42
|
+
*
|
|
43
|
+
* For all three render methods on `SitemapRenderer`, this resolves by
|
|
44
|
+
* the time the method's returned Promise settles (or synchronously for
|
|
45
|
+
* the portions that are sync).
|
|
46
|
+
*/
|
|
47
|
+
statusReady: Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Create a sitemap renderer.
|
|
50
|
+
*
|
|
51
|
+
* @param getters - Async functions that each return a batch of sitemap items.
|
|
52
|
+
* Called concurrently via `Promise.all` during rendering.
|
|
53
|
+
* @param config - Base URL and optional defaults for changefreq / priority.
|
|
54
|
+
*/
|
|
55
|
+
constructor(getters: readonly SitemapGetter[], config: SitemapConfig);
|
|
56
|
+
/**
|
|
57
|
+
* Load sitemap items from all configured getters.
|
|
58
|
+
*
|
|
59
|
+
* Calls every getter concurrently and flattens the results into a single
|
|
60
|
+
* array. This is the only async step in the pipeline.
|
|
61
|
+
*
|
|
62
|
+
* @note This method is impure — it calls external async data sources.
|
|
63
|
+
* @returns A flat array of raw sitemap items from all getters.
|
|
64
|
+
*/
|
|
65
|
+
protected loadItems(): Promise<SitemapItem[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Resolve URLs and apply defaults to raw sitemap items.
|
|
68
|
+
*
|
|
69
|
+
* For each item:
|
|
70
|
+
* - Relative `loc` values are resolved against `config.baseUrl`. An empty
|
|
71
|
+
* `loc` resolves to the base URL itself.
|
|
72
|
+
* - `lastmod` dates are formatted to `YYYY-MM-DD` (ISO 8601 date-only).
|
|
73
|
+
* - Missing `changefreq` and `priority` are filled from `config` defaults.
|
|
74
|
+
*
|
|
75
|
+
* @param items - Raw items as returned by `loadItems`.
|
|
76
|
+
* @returns A new array of items with resolved URLs and applied defaults.
|
|
77
|
+
*/
|
|
78
|
+
protected formatItems(items: readonly SitemapItem[]): SitemapItem[];
|
|
79
|
+
/**
|
|
80
|
+
* Format a `Date` object or ISO string to a `YYYY-MM-DD` date string.
|
|
81
|
+
*
|
|
82
|
+
* The Sitemaps protocol specifies W3C Datetime format; the date-only
|
|
83
|
+
* variant (`YYYY-MM-DD`) is the most common form used in practice.
|
|
84
|
+
*
|
|
85
|
+
* @param date - A `Date` instance or an ISO 8601 date/datetime string.
|
|
86
|
+
* @returns The date formatted as `YYYY-MM-DD`.
|
|
87
|
+
*/
|
|
88
|
+
protected static formatDate(date: Date | string): string;
|
|
89
|
+
/**
|
|
90
|
+
* Escape the five XML special characters in a string.
|
|
91
|
+
*
|
|
92
|
+
* Prevents malformed XML when interpolating user-supplied URLs that may
|
|
93
|
+
* contain `&` (common in query strings), `<`, `>`, `"`, or `'`.
|
|
94
|
+
*
|
|
95
|
+
* @param value - The raw string to escape.
|
|
96
|
+
* @returns The string with `&`, `<`, `>`, `"`, and `'` replaced by their XML entities.
|
|
97
|
+
*/
|
|
98
|
+
protected static escapeXml(value: string): string;
|
|
99
|
+
/**
|
|
100
|
+
* Serialise a list of formatted sitemap items into an XML sitemap string.
|
|
101
|
+
*
|
|
102
|
+
* Produces a complete `<?xml>` document with a `<urlset>` root element
|
|
103
|
+
* conforming to the Sitemaps 0.9 schema. Only non-null optional fields
|
|
104
|
+
* (`lastmod`, `changefreq`, `priority`) are included in the output.
|
|
105
|
+
*
|
|
106
|
+
* @param items - Formatted items (URLs resolved, dates formatted).
|
|
107
|
+
* @returns The complete XML sitemap as a string.
|
|
108
|
+
*/
|
|
109
|
+
protected toXml(items: readonly SitemapItem[]): string;
|
|
110
|
+
/**
|
|
111
|
+
* Build the full XML string by loading items, applying formatting, and
|
|
112
|
+
* serialising to XML.
|
|
113
|
+
*/
|
|
114
|
+
protected buildXml(): Promise<string>;
|
|
115
|
+
/**
|
|
116
|
+
* Render the sitemap to a web `ReadableStream`.
|
|
117
|
+
*
|
|
118
|
+
* Loads items from all getters, formats them, serialises to XML, and
|
|
119
|
+
* wraps the result in a `ReadableStream`. Sets `statusCode` to 200.
|
|
120
|
+
*
|
|
121
|
+
* @note This method is impure — it calls external data sources and mutates `statusCode`.
|
|
122
|
+
*
|
|
123
|
+
* @param _signal - Accepted for API compatibility with `JSXRenderer`. Not used.
|
|
124
|
+
* @returns A `ReadableStream` of the XML sitemap.
|
|
125
|
+
*/
|
|
126
|
+
renderToReadableStream: (_signal?: AbortSignal) => Promise<ReadableStream>;
|
|
127
|
+
/**
|
|
128
|
+
* Render the sitemap to a Node.js pipeable stream.
|
|
129
|
+
*
|
|
130
|
+
* Returns `{ pipe, abort }` synchronously. The actual XML generation is
|
|
131
|
+
* async (getters are called), so data is pushed to the stream when ready.
|
|
132
|
+
* `statusReady` resolves when the XML is built and `statusCode` is set.
|
|
133
|
+
*
|
|
134
|
+
* @note This method is impure — it calls external data sources and mutates `statusCode`.
|
|
135
|
+
*
|
|
136
|
+
* @returns The pipe/abort handles for the XML stream.
|
|
137
|
+
*/
|
|
138
|
+
renderToPipeableStream: () => PipeableStreamResult;
|
|
139
|
+
/**
|
|
140
|
+
* Render the sitemap to a complete XML string.
|
|
141
|
+
*
|
|
142
|
+
* Loads items from all getters, formats them, and returns the serialised
|
|
143
|
+
* XML. Sets `statusCode` to 200.
|
|
144
|
+
*
|
|
145
|
+
* Async because the getters are async.
|
|
146
|
+
*
|
|
147
|
+
* @note This method is impure — it calls external data sources and mutates `statusCode`.
|
|
148
|
+
*
|
|
149
|
+
* @returns The complete XML sitemap string.
|
|
150
|
+
*/
|
|
151
|
+
renderToString: () => Promise<string>;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=SitemapRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SitemapRenderer.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/SitemapRenderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,OAAO,OAAO,eAAe;IA2BhC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE;IACpD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa;IA3B1C;;;;;;OAMG;IACI,UAAU,SAAO;IAExB;;;;;;OAMG;IACI,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAqB;IAEtD;;;;;;OAMG;gBAEkB,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,MAAM,EAAE,aAAa;IAG1C;;;;;;;;OAQG;cACa,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAKnD;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,EAAE;IAcnE;;;;;;;;OAQG;IACH,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM;IAKxD;;;;;;;;OAQG;IACH,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASjD;;;;;;;;;OASG;IACH,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM;IA6BtD;;;OAGG;cACa,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAM3C;;;;;;;;;;OAUG;IACH,sBAAsB,GACpB,UAAU,WAAW,KACpB,OAAO,CAAC,cAAc,CAAC,CAWxB;IAEF;;;;;;;;;;OAUG;IACH,sBAAsB,QAAO,oBAAoB,CAqB/C;IAEF;;;;;;;;;;;OAWG;IACH,cAAc,QAAa,OAAO,CAAC,MAAM,CAAC,CAKxC;CACH"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { PipeableStreamResult, TextGetter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Renders a plain-text document from async data sources.
|
|
4
|
+
*
|
|
5
|
+
* Produces output suitable for `llms.txt`, `humans.txt`, `security.txt`, or
|
|
6
|
+
* any text file that requires dynamic data (e.g. fetching page descriptions
|
|
7
|
+
* from a CMS for an LLM context file).
|
|
8
|
+
*
|
|
9
|
+
* The renderer is intentionally minimal — it accepts an array of async getters,
|
|
10
|
+
* each returning a string, and concatenates the results. No structure is imposed.
|
|
11
|
+
* The consumer controls formatting entirely.
|
|
12
|
+
*
|
|
13
|
+
* Implements the same three render methods and `statusCode` / `statusReady`
|
|
14
|
+
* contract as `JSXRenderer` and `SitemapRenderer`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const renderer = new TextRenderer([
|
|
19
|
+
* async () => "# My App\n\nContext for LLMs about this application.\n",
|
|
20
|
+
* async () => {
|
|
21
|
+
* const pages = await fetchPages();
|
|
22
|
+
* return pages.map(p => `- ${p.title}: ${p.url}`).join("\n");
|
|
23
|
+
* },
|
|
24
|
+
* ]);
|
|
25
|
+
*
|
|
26
|
+
* const stream = await renderer.renderToReadableStream();
|
|
27
|
+
* return new Response(stream, {
|
|
28
|
+
* status: renderer.statusCode,
|
|
29
|
+
* headers: { "Content-Type": "text/plain; charset=utf-8" },
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export default class TextRenderer {
|
|
34
|
+
protected readonly getters: readonly TextGetter[];
|
|
35
|
+
/**
|
|
36
|
+
* HTTP status code determined during rendering.
|
|
37
|
+
*
|
|
38
|
+
* Set to 200 on successful render. Errors from getters propagate as
|
|
39
|
+
* thrown exceptions — the consumer's error handler decides the status code.
|
|
40
|
+
*/
|
|
41
|
+
statusCode: number;
|
|
42
|
+
/**
|
|
43
|
+
* Resolves when `statusCode` is determined.
|
|
44
|
+
*/
|
|
45
|
+
statusReady: Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Create a text renderer.
|
|
48
|
+
*
|
|
49
|
+
* @param getters - Async functions that each return a string. Called
|
|
50
|
+
* sequentially (order matters) and concatenated into the final document.
|
|
51
|
+
*/
|
|
52
|
+
constructor(getters: readonly TextGetter[]);
|
|
53
|
+
/**
|
|
54
|
+
* Build the full text by calling all getters sequentially and concatenating.
|
|
55
|
+
*/
|
|
56
|
+
protected buildText(): Promise<string>;
|
|
57
|
+
/**
|
|
58
|
+
* Render to a web `ReadableStream`.
|
|
59
|
+
*
|
|
60
|
+
* @note This method is impure — it calls external data sources and mutates `statusCode`.
|
|
61
|
+
*
|
|
62
|
+
* @param _signal - Accepted for API compatibility. Not used.
|
|
63
|
+
* @returns A `ReadableStream` of the plain-text document.
|
|
64
|
+
*/
|
|
65
|
+
renderToReadableStream: (_signal?: AbortSignal) => Promise<ReadableStream>;
|
|
66
|
+
/**
|
|
67
|
+
* Render to a Node.js pipeable stream.
|
|
68
|
+
*
|
|
69
|
+
* Returns `{ pipe, abort }` synchronously. Data is pushed when ready.
|
|
70
|
+
*
|
|
71
|
+
* @note This method is impure — it calls external data sources and mutates `statusCode`.
|
|
72
|
+
*/
|
|
73
|
+
renderToPipeableStream: () => PipeableStreamResult;
|
|
74
|
+
/**
|
|
75
|
+
* Render to a complete string.
|
|
76
|
+
*
|
|
77
|
+
* Async because the getters are async.
|
|
78
|
+
*
|
|
79
|
+
* @note This method is impure — it calls external data sources and mutates `statusCode`.
|
|
80
|
+
*/
|
|
81
|
+
renderToString: () => Promise<string>;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=TextRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextRenderer.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/TextRenderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,OAAO,OAAO,YAAY;IAoBnB,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE;IAnB7D;;;;;OAKG;IACI,UAAU,SAAO;IAExB;;OAEG;IACI,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAqB;IAEtD;;;;;OAKG;gBAC4B,OAAO,EAAE,SAAS,UAAU,EAAE;IAE7D;;OAEG;cACa,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAQ5C;;;;;;;OAOG;IACH,sBAAsB,GACpB,UAAU,WAAW,KACpB,OAAO,CAAC,cAAc,CAAC,CAWxB;IAEF;;;;;;OAMG;IACH,sBAAsB,QAAO,oBAAoB,CAqB/C;IAEF;;;;;;OAMG;IACH,cAAc,QAAa,OAAO,CAAC,MAAM,CAAC,CAKxC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./constants.js";
|
|
2
|
+
export { default as Extractor } from "./Extractor.js";
|
|
3
|
+
export { default as JSXRenderer } from "./JSXRenderer.js";
|
|
4
|
+
export { default as SitemapRenderer } from "./SitemapRenderer.js";
|
|
5
|
+
export { default as TextRenderer } from "./TextRenderer.js";
|
|
6
|
+
export type { PipeableStreamResult, RendererOptions, ServerEntrypoint, ServerEntrypointProps, SitemapConfig, SitemapGetter, SitemapItem, TextGetter, } from "./types.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC5D,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC"}
|