@canonical/react-ssr 0.14.0 → 0.15.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/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # @canonical/react-ssr
2
2
 
3
- Server-side rendering utilities for React applications. Provides streaming HTML rendering with `JSXRenderer`, Express middleware with `serveStream`, and automatic script/link tag injection from your build output.
3
+ Server-side rendering utilities for React applications. Provides streaming HTML rendering, Express middleware with `serveStream`,
4
+ and automatic script/link tag injection from your build output.
4
5
 
5
6
  ## Installation
6
7
 
@@ -10,6 +11,28 @@ bun add @canonical/react-ssr
10
11
 
11
12
  Peer dependencies: `react`, `react-dom`, `express` (for Express usage).
12
13
 
14
+ ## SSR flavors
15
+
16
+ There are usually two different flavors of SSR to be considered.
17
+
18
+ Source: [Tanstack Router SSR Guide](https://tanstack.com/router/latest/docs/framework/react/guide/ssr)
19
+
20
+ ### Non-streaming SSR
21
+
22
+ The entire page is rendered on the server and sent to the client in one single HTML request,
23
+ including the serialized data the application needs to hydrate on the client.
24
+
25
+ This is what `JSXRenderer.renderToString()` offers.
26
+
27
+ ### Streaming SSR
28
+
29
+ The critical first paint of the page is rendered on the server and sent to the client in one single HTML request,
30
+ including the serialized data the application needs to hydrate on the client.
31
+
32
+ The rest of the page is then streamed to the client as it is rendered on the server.
33
+
34
+ This is accomplished by using `JSXRenderer.renderToStream()`.
35
+
13
36
  ## Express Server
14
37
 
15
38
  Create a renderer that wraps your server entry component:
@@ -20,7 +43,8 @@ import { JSXRenderer } from "@canonical/react-ssr/renderer";
20
43
  import htmlString from "../../dist/client/index.html?raw";
21
44
  import EntryServer from "./entry-server.js";
22
45
 
23
- const Renderer = new JSXRenderer(EntryServer, { htmlString });
46
+ const initialData: Record<string, unknown> = {};
47
+ const Renderer = new JSXRenderer(EntryServer, initialData, { htmlString });
24
48
  export default Renderer.render;
25
49
  ```
26
50
 
@@ -156,12 +180,12 @@ The client build produces `dist/client/index.html` with bundled script/link tags
156
180
  Pass options to React's `renderToPipeableStream`:
157
181
 
158
182
  ```ts
159
- const Renderer = new JSXRenderer(EntryServer, {
183
+ const initialData: Record<string, unknown> = {};
184
+ const Renderer = new JSXRenderer(EntryServer, initialData, {
160
185
  htmlString,
186
+ initialData,
161
187
  renderToPipeableStreamOptions: {
162
188
  bootstrapModules: ["src/ssr/entry-client.tsx"],
163
- onShellReady() { console.log("Shell ready"); },
164
- onError(err) { console.error(err); },
165
189
  },
166
190
  });
167
191
  ```
@@ -1,18 +1,44 @@
1
1
  import { casing } from "@canonical/utils";
2
+ import { NodeWithChildren } from "domhandler";
2
3
  import { parseDocument } from "htmlparser2";
3
4
  import React from "react";
5
+ const REACT_KEYS_DICTIONARY = {
6
+ class: "className",
7
+ for: "htmlFor",
8
+ crossorigin: "crossOrigin",
9
+ charset: "charSet",
10
+ };
4
11
  /**
5
- * Parses an HTML string to extract and convert script and link tags to React.createElement calls.
12
+ * Parses an HTML string to extract and convert the <head> tags to React.createElement calls.
13
+ * The tags extracted are:
14
+ * - title
15
+ * - style
16
+ * - meta
17
+ * - link
18
+ * - script
19
+ * - base
6
20
  */
7
21
  class Extractor {
8
- // biome-ignore lint/suspicious/noExplicitAny: explicit any needed for the document type
22
+ /**
23
+ * A document object representing the DOM of a page.
24
+ */
9
25
  document;
26
+ /**
27
+ * Creates an Extractor object for a given HTML string.
28
+ */
10
29
  constructor(html) {
11
30
  this.document = parseDocument(html);
12
31
  }
32
+ /**
33
+ * Searches elements with the specified tag in the document.
34
+ *
35
+ * @remark The method uses the parsed {@link Extractor.document | document} to navigate the
36
+ * whole DOM (usinig a stack) and checks for the elements with the tag name that matches
37
+ * the given parameter.
38
+ */
13
39
  getElementsByTagName(tagName) {
14
40
  const elements = [];
15
- const stack = [this.document];
41
+ const stack = [...this.document.children];
16
42
  while (stack.length) {
17
43
  const node = stack.pop();
18
44
  if (!node)
@@ -24,39 +50,77 @@ class Extractor {
24
50
  if (node.type === "script" && tagName === "script") {
25
51
  elements.push(node);
26
52
  }
27
- if (node.children) {
53
+ if (node instanceof NodeWithChildren) {
28
54
  stack.push(...node.children);
29
55
  }
30
56
  }
31
- console.log(`Found ${elements.length} <${tagName}> tags`);
32
57
  return elements;
33
58
  }
59
+ /**
60
+ * Converts HTML keys to React keys.
61
+ *
62
+ * @remark There are some HTML attributes that don't map exactly to React with the same name.
63
+ * For example, class -> className.
64
+ */
34
65
  convertKeyToReactKey(key) {
35
- switch (key.toLowerCase()) {
36
- case "class":
37
- return "className";
38
- case "for":
39
- return "htmlFor";
40
- case "crossorigin":
41
- return "crossOrigin";
42
- default:
43
- return casing.toCamelCase(key);
44
- }
66
+ const reactKey = REACT_KEYS_DICTIONARY[key.toLowerCase()];
67
+ return reactKey ? reactKey : casing.toCamelCase(key);
45
68
  }
46
- convertToReactElement(element) {
69
+ /**
70
+ * Converts a parsed {@link domhandler#Element | DOM Element} into a {@link react#React.ReactElement | ReactElement}.
71
+ *
72
+ * @remark The method takes into account the attributes of the parsed {@link domhandler#Element | Element}
73
+ * and passes them as props when creating the {@link react#React.ReactElement | ReactElement}.
74
+ * It only handles children of type "text".
75
+ */
76
+ convertToReactElement(element, index) {
47
77
  const props = {};
48
78
  for (const [key, value] of Object.entries(element.attribs)) {
49
79
  props[this.convertKeyToReactKey(key)] = value;
50
80
  }
51
- return React.createElement(element.name, props);
81
+ // some tags from <head> have one children of type text
82
+ let elementChildren;
83
+ if (element.children.length === 1 && element.firstChild?.type === "text") {
84
+ elementChildren = element.firstChild.data;
85
+ }
86
+ props.key = `${element.name}_${index}`;
87
+ return React.createElement(element.name, props, elementChildren);
52
88
  }
53
- getLinkTags() {
89
+ /**
90
+ * Finds all <link> elements in the {@link Extractor.document | document} and converts them
91
+ * into {@link react#React.ReactElement | ReactElements}.
92
+ *
93
+ * @remark The list of elements returned will be in order of appearance in the DOM.
94
+ */
95
+ getLinkElements() {
54
96
  const linkElements = this.getElementsByTagName("link");
55
- return linkElements.map(this.convertToReactElement, this);
97
+ // reverse keeps the original order in the HTML (they are extracted with a stack in reverse)
98
+ // the order might be important for some scripts (i.e. in Vite Dev mode)
99
+ return linkElements.reverse().map(this.convertToReactElement, this);
56
100
  }
57
- getScriptTags() {
101
+ /**
102
+ * Finds all <script> elements in the {@link Extractor.document | document} and converts them
103
+ * into {@link react#React.ReactElement | ReactElements}.
104
+ *
105
+ * @remark The list of elements returned will be in order of appearance in the DOM.
106
+ */
107
+ getScriptElements() {
58
108
  const scriptElements = this.getElementsByTagName("script");
59
- return scriptElements.map(this.convertToReactElement, this);
109
+ // reverse keeps the original order in the HTML (they are extracted with a stack in reverse)
110
+ // the order might be important for some scripts (i.e. in Vite Dev mode)
111
+ return scriptElements.reverse().map(this.convertToReactElement, this);
112
+ }
113
+ /**
114
+ * Finds all the <head> elements which are not "script" or "link" in the {@link Extractor.document | document}
115
+ * and converts them into {@link react#React.ReactElement | ReactElements}.
116
+ *
117
+ * @remark The list of elements returned will be in order of appearance in the DOM.
118
+ */
119
+ getOtherHeadElements() {
120
+ const otherHeadElements = ["title", "style", "meta", "base"].flatMap((elementName) => this.getElementsByTagName(elementName));
121
+ // reverse keeps the original order in the HTML (they are extracted with a stack in reverse)
122
+ // the order might be important for some scripts (i.e. in Vite Dev mode)
123
+ return otherHeadElements.reverse().map(this.convertToReactElement, this);
60
124
  }
61
125
  }
62
126
  export default Extractor;
@@ -1 +1 @@
1
- {"version":3,"file":"Extractor.js","sourceRoot":"","sources":["../../../src/renderer/Extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;GAEG;AACH,MAAM,SAAS;IACb,wFAAwF;IACvE,QAAQ,CAAM;IAE/B,YAAY,IAAY;QACtB,IAAI,CAAC,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAEO,oBAAoB,CAAC,OAAe;QAC1C,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE9B,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI;gBAAE,SAAS;YAEpB,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBACjD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;YAED,qCAAqC;YACrC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACnD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;YAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,SAAS,QAAQ,CAAC,MAAM,KAAK,OAAO,QAAQ,CAAC,CAAC;QAC1D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,oBAAoB,CAAC,GAAW;QACtC,QAAQ,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;YAC1B,KAAK,OAAO;gBACV,OAAO,WAAW,CAAC;YACrB,KAAK,KAAK;gBACR,OAAO,SAAS,CAAC;YACnB,KAAK,aAAa;gBAChB,OAAO,aAAa,CAAC;YACvB;gBACE,OAAO,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAEO,qBAAqB,CAAC,OAAgB;QAC5C,MAAM,KAAK,GAA8B,EAAE,CAAC;QAE5C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3D,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QAChD,CAAC;QAED,OAAO,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;IAEM,WAAW;QAChB,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QACvD,OAAO,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;IAC5D,CAAC;IAEM,aAAa;QAClB,MAAM,cAAc,GAAG,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAC3D,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;IAC9D,CAAC;CACF;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"Extractor.js","sourceRoot":"","sources":["../../../src/renderer/Extractor.ts"],"names":[],"mappings":"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"}
@@ -1,79 +1,161 @@
1
1
  import { createElement } from "react";
2
- import { renderToPipeableStream, } from "react-dom/server";
2
+ import { renderToPipeableStream, renderToString, } from "react-dom/server";
3
+ import { INITIAL_DATA_KEY } from "./constants.js";
3
4
  import Extractor from "./Extractor.js";
4
- // This class is responsible for rendering a React component to a readable stream.
5
- export default class Renderer {
5
+ /**
6
+ * This class is responsible for rendering a React JSX component and sending it as response to a client.
7
+ * It offers 2 ways of doing it:
8
+ * - As string
9
+ * - As stream
10
+ * Each way has its advantages and inconveniences. You can read more about them in the package README.
11
+ */
12
+ export default class JSXRenderer {
6
13
  Component;
14
+ initialData;
7
15
  options;
8
- locale;
9
- // private messages: any;
10
16
  extractor;
11
- constructor(Component, options = {}) {
17
+ /**
18
+ * Creates a renderer instance which can be used to write Server Side Rendered HTML
19
+ * into a {@link node:http#ServerResponse | ServerResponse}.
20
+ */
21
+ constructor(Component, initialData = {}, options = {}) {
12
22
  this.Component = Component;
23
+ this.initialData = initialData;
13
24
  this.options = options;
14
- // this.prepareLocale = this.prepareLocale.bind(this);
15
- this.render = this.render.bind(this);
16
25
  this.extractor = this.options.htmlString
17
26
  ? new Extractor(this.options.htmlString)
18
27
  : undefined;
19
28
  }
20
- //async prepareLocale(header: string | undefined) {
21
- // if (this.options.loadMessages) {
22
- // this.locale = header
23
- // ? header.slice(0, 2)
24
- // : this.options.defaultLocale || "en";
25
- // //this.messages = await this.options.loadMessages(this.locale);
26
- // }
27
- //}
28
29
  /**
29
- * Gets the props needed to render the component
30
- * @return The props needed to render the component
31
- * @private
30
+ * Gets the locale to be used for the rendered page.
31
+ * Default if there was no locale passed as option is "en".
32
+ */
33
+ getLocale() {
34
+ return this.options.defaultLocale || "en";
35
+ }
36
+ /**
37
+ * Gets the props needed to render the component.
32
38
  */
33
39
  getComponentProps() {
34
40
  return {
35
- lang: this.locale,
36
- scriptTags: this.extractor?.getScriptTags(),
37
- linkTags: this.extractor?.getLinkTags(),
38
- // todo implement message passing
39
- // messages: this.messages,
41
+ lang: this.getLocale(),
42
+ scriptElements: this.extractor?.getScriptElements(),
43
+ linkElements: this.extractor?.getLinkElements(),
44
+ otherHeadElements: this.extractor?.getOtherHeadElements(),
45
+ initialData: this.initialData,
40
46
  };
41
47
  }
42
48
  /**
43
- * Renders this renderer's JSX component as a transmittable stream and sends it to the client
44
- * TODO add a render function for ReadableStream, and rename this to be focused on PipeableStream
45
- * @param req Client's request
46
- * @param res Response object that will be sent to the client
47
- * @return {RenderResult} The stream that was sent to the client
49
+ * Gets a list of all the "src" attributes of the given scripts that match the passed type.
50
+ */
51
+ getScriptSourcesByType(scripts, type) {
52
+ return (scripts
53
+ .map((script) => script)
54
+ .filter((script) => {
55
+ if (type === "module") {
56
+ return script.props.type === "module";
57
+ }
58
+ else {
59
+ return script.props.type !== "module";
60
+ }
61
+ })
62
+ .map((script) => script.props.src)
63
+ .filter((src) => typeof src === "string") || []);
64
+ }
65
+ /**
66
+ * Adds some properties to the options that are passed to {@link react-dom#renderToPipeableStream | renderToPipeableStream}.
67
+ *
68
+ * @remark The options that are added are:
69
+ * - bootstrapScriptContent: includes the initial data passed as prop to the component in a <script> so that it
70
+ * is available when rendering the page in the browser (to avoid hydration mismatches).
71
+ * - bootstrapScripts: classic scripts which react strips out of the page. The only way to add them is to include them
72
+ * in this property.
73
+ * - bootstrapModules: module scripts which react also strips out of the page and need to be added like this.
74
+ */
75
+ enrichRendererOptions(props) {
76
+ const enrichedOptions = { ...this.options.renderToPipeableStreamOptions };
77
+ // options passed by the user always take priority
78
+ if (!enrichedOptions.bootstrapScriptContent) {
79
+ if (props.initialData) {
80
+ enrichedOptions.bootstrapScriptContent = `window.${INITIAL_DATA_KEY} = ${JSON.stringify(props.initialData)}`;
81
+ }
82
+ }
83
+ if (!enrichedOptions.bootstrapScripts) {
84
+ if (props.scriptElements) {
85
+ enrichedOptions.bootstrapScripts = this.getScriptSourcesByType(props.scriptElements, "classic");
86
+ }
87
+ }
88
+ if (!enrichedOptions.bootstrapModules) {
89
+ if (props.scriptElements) {
90
+ enrichedOptions.bootstrapModules = this.getScriptSourcesByType(props.scriptElements, "module");
91
+ }
92
+ }
93
+ return enrichedOptions;
94
+ }
95
+ /**
96
+ * This function is responsible for rendering a React component and sending it to the client through
97
+ * a pipeable stream.
98
+ *
99
+ * @remark See the README to understand the difference between rendering options.
100
+ *
101
+ * The streaming might improve the time taken for the page to be rendered and interactive
102
+ * (at least in part), using React's Suspense/lazy API and pipeable streams.
103
+ *
104
+ * CAUTION: The resulting HTML rendered this way is not cacheable.
48
105
  */
49
- render = (req, res) => {
50
- // await this.prepareLocale(req.headers.get("accept-language") || undefined);
51
- const jsx = createElement(this.Component, this.getComponentProps());
52
- let renderingError;
106
+ renderToStream = (_req, res) => {
107
+ const errorRef = { current: undefined };
108
+ const props = this.getComponentProps();
109
+ const jsx = createElement(this.Component, props);
53
110
  const jsxStream = renderToPipeableStream(jsx, {
54
- ...this.options.renderToPipeableStreamOptions,
111
+ ...this.enrichRendererOptions(props),
112
+ // Error occurred during rendering, after the shell & headers were sent - store the error for usage after stream is sent
113
+ onError(error) {
114
+ errorRef.current = error;
115
+ console.error(error);
116
+ },
55
117
  // Early error, before the shell is prepared
56
- onShellError() {
57
- res
58
- .writeHead(500, { "Content-Type": "text/html; charset=utf-8" })
59
- .end("<h1>Something went wrong</h1>");
60
- throw new Error("An error occurred while constructing the SSR shell");
118
+ onShellError(error) {
119
+ if (!res.headersSent) {
120
+ res
121
+ .writeHead(500, { "Content-Type": "text/html; charset=utf-8" })
122
+ .end("<h1>Something went wrong</h1>");
123
+ }
124
+ console.error(error);
61
125
  },
62
126
  onShellReady() {
63
- res.writeHead(renderingError ? 500 : 200, {
64
- "Content-Type": "text/html; charset=utf-8",
65
- });
127
+ if (!res.headersSent) {
128
+ res.writeHead(errorRef.current ? 500 : 200, {
129
+ "Content-Type": "text/html; charset=utf-8",
130
+ });
131
+ }
66
132
  jsxStream.pipe(res);
67
133
  res.on("finish", () => {
68
134
  res.end();
69
135
  });
70
136
  },
71
- // Error occurred during rendering, after the shell & headers were sent - store the error for usage after stream is sent
72
- onError(error) {
73
- renderingError = error;
74
- },
75
137
  });
76
- return jsxStream;
138
+ };
139
+ /**
140
+ * Renders this renderer's JSX component as a string and writes it to the given
141
+ * {@link node:http#ServerResponse | ServerResponse}.
142
+ *
143
+ * @remark See the README to understand the difference between rendering options.
144
+ *
145
+ * Rendering to string means all <Suspense> components are loaded synchronously and the response
146
+ * won't be sent to the client until all components have finished loading data and processing.
147
+ *
148
+ * renderToString is useful in Vite Dev mode, as the HMR doesn't work well with Suspense
149
+ * and the Pipeable Stream rendering. Also if the resulting document needs to be cached.
150
+ */
151
+ renderToString = (_req, res) => {
152
+ const props = this.getComponentProps();
153
+ const jsx = createElement(this.Component, props);
154
+ const html = renderToString(jsx);
155
+ res
156
+ .writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
157
+ .write(html);
158
+ res.end();
77
159
  };
78
160
  }
79
161
  //# sourceMappingURL=JSXRenderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"JSXRenderer.js","sourceRoot":"","sources":["../../../src/renderer/JSXRenderer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAqB,MAAM,OAAO,CAAC;AACzD,OAAO,EAGL,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAyCvC,kFAAkF;AAClF,MAAM,CAAC,OAAO,OAAO,QAAQ;IAUR;IACA;IANX,MAAM,CAAqB;IACnC,yBAAyB;IACjB,SAAS,CAAwB;IAEzC,YACmB,SAAqB,EACrB,UAA2B,EAAE;QAD7B,cAAS,GAAT,SAAS,CAAY;QACrB,YAAO,GAAP,OAAO,CAAsB;QAE9C,sDAAsD;QACtD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU;YACtC,CAAC,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;YACxC,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAED,mDAAmD;IACnD,oCAAoC;IACpC,0BAA0B;IAC1B,4BAA4B;IAC5B,6CAA6C;IAC7C,qEAAqE;IACrE,KAAK;IACL,GAAG;IAEH;;;;OAIG;IACK,iBAAiB;QACvB,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,MAAM;YACjB,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE;YAC3C,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE;YACvC,iCAAiC;YACjC,2BAA2B;SACT,CAAC;IACvB,CAAC;IAED;;;;;;OAMG;IACH,MAAM,GAAkB,CACtB,GAAoB,EACpB,GAAmB,EACL,EAAE;QAChB,6EAA6E;QAC7E,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAEpE,IAAI,cAAqB,CAAC;QAE1B,MAAM,SAAS,GAAG,sBAAsB,CAAC,GAAG,EAAE;YAC5C,GAAG,IAAI,CAAC,OAAO,CAAC,6BAA6B;YAC7C,4CAA4C;YAC5C,YAAY;gBACV,GAAG;qBACA,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC;qBAC9D,GAAG,CAAC,+BAA+B,CAAC,CAAC;gBAExC,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;YACxE,CAAC;YACD,YAAY;gBACV,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE;oBACxC,cAAc,EAAE,0BAA0B;iBAC3C,CAAC,CAAC;gBAEH,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEpB,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;oBACpB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC;YACD,wHAAwH;YACxH,OAAO,CAAC,KAAK;gBACX,cAAc,GAAG,KAAc,CAAC;YAClC,CAAC;SACF,CAAC,CAAC;QACH,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;CACH"}
1
+ {"version":3,"file":"JSXRenderer.js","sourceRoot":"","sources":["../../../src/renderer/JSXRenderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export const INITIAL_DATA_KEY = "__INITIAL_DATA__";
2
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/renderer/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC"}
@@ -1,3 +1,4 @@
1
+ export * from "./constants.js";
1
2
  export { default as Extractor } from "./Extractor.js";
2
3
  export { default as JSXRenderer } from "./JSXRenderer.js";
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/renderer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQtD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/renderer/types.ts"],"names":[],"mappings":""}
@@ -21,6 +21,10 @@ const { values, positionals } = parseArgs({
21
21
  alias: "r",
22
22
  default: "assets",
23
23
  },
24
+ streaming: {
25
+ type: "boolean",
26
+ default: false,
27
+ },
24
28
  },
25
29
  strict: true,
26
30
  allowPositionals: true,
@@ -34,7 +38,14 @@ if (!rendererFilePath) {
34
38
  console.error("Usage: node server.js <renderer-path>");
35
39
  process.exit(1);
36
40
  }
37
- const handler = await import(rendererFilePath).then((module) => module.default);
41
+ const handler = await import(rendererFilePath).then((module) => {
42
+ if (values.streaming) {
43
+ return module.default.renderToStream;
44
+ }
45
+ else {
46
+ return module.default.renderToString;
47
+ }
48
+ });
38
49
  if (typeof handler !== "function") {
39
50
  throw new Error("Renderer file must default-export a renderer function.");
40
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"serve-express.js","sourceRoot":"","sources":["../../../src/server/serve-express.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACxC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3B,OAAO,EAAE;QACP,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,MAAM;SAChB;QACD,cAAc,EAAE;YACd,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,oBAAoB;SAC9B;QACD,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,QAAQ;SAClB;KACF;IACD,MAAM,EAAE,IAAI;IACZ,gBAAgB,EAAE,IAAI;CACvB,CAAC,CAAC;AAEH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;AACjC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAC1B,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,cAAc,IAAI,QAAQ,CAAC,CAAC;AACpE,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,QAAQ,CAAC;AAEnD,IAAI,CAAC,gBAAgB,EAAE,CAAC;IACtB,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,GAAkB,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAChE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAC3B,CAAC;AAEF,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;IAClC,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,GAAG,CAAC,GAAG,CAAC,IAAI,WAAW,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;AACtD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AAE9B,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,GAAG,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"serve-express.js","sourceRoot":"","sources":["../../../src/server/serve-express.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACxC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3B,OAAO,EAAE;QACP,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,MAAM;SAChB;QACD,cAAc,EAAE;YACd,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,oBAAoB;SAC9B;QACD,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,QAAQ;SAClB;QACD,SAAS,EAAE;YACT,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;SACf;KACF;IACD,MAAM,EAAE,IAAI;IACZ,gBAAgB,EAAE,IAAI;CACvB,CAAC,CAAC;AAEH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;AACjC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAC1B,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,cAAc,IAAI,QAAQ,CAAC,CAAC;AACpE,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,QAAQ,CAAC;AAEnD,IAAI,CAAC,gBAAgB,EAAE,CAAC;IACtB,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,GAAkB,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;IAC5E,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC;IACvC,CAAC;SAAM,CAAC;QACN,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC;IACvC,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;IAClC,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,GAAG,CAAC,GAAG,CAAC,IAAI,WAAW,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;AACtD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AAE9B,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,GAAG,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC"}
@@ -12,7 +12,7 @@
12
12
  * import htmlString from "../../dist/client/index.html?raw";
13
13
  * import EntryServer from "./entry-server.js";
14
14
  *
15
- * // `EntryServer` is an instance of `@canonical/react-ssr/renderer/ReactServerEntrypointComponent`
15
+ * // `EntryServer` is an instance of `@canonical/react-ssr/renderer/ServerEntrypoint`
16
16
  * const Renderer = new JSXRenderer(EntryServer, {
17
17
  * htmlString,
18
18
  * });
@@ -1,15 +1,68 @@
1
+ import { type Document, type Element } from "domhandler";
1
2
  import React from "react";
2
3
  /**
3
- * Parses an HTML string to extract and convert script and link tags to React.createElement calls.
4
+ * Parses an HTML string to extract and convert the <head> tags to React.createElement calls.
5
+ * The tags extracted are:
6
+ * - title
7
+ * - style
8
+ * - meta
9
+ * - link
10
+ * - script
11
+ * - base
4
12
  */
5
13
  declare class Extractor {
6
- private readonly document;
14
+ /**
15
+ * A document object representing the DOM of a page.
16
+ */
17
+ protected readonly document: Document;
18
+ /**
19
+ * Creates an Extractor object for a given HTML string.
20
+ */
7
21
  constructor(html: string);
8
- private getElementsByTagName;
9
- private convertKeyToReactKey;
10
- private convertToReactElement;
11
- getLinkTags(): React.ReactElement[];
12
- getScriptTags(): React.ReactElement[];
22
+ /**
23
+ * Searches elements with the specified tag in the document.
24
+ *
25
+ * @remark The method uses the parsed {@link Extractor.document | document} to navigate the
26
+ * whole DOM (usinig a stack) and checks for the elements with the tag name that matches
27
+ * the given parameter.
28
+ */
29
+ protected getElementsByTagName(tagName: string): Element[];
30
+ /**
31
+ * Converts HTML keys to React keys.
32
+ *
33
+ * @remark There are some HTML attributes that don't map exactly to React with the same name.
34
+ * For example, class -> className.
35
+ */
36
+ protected convertKeyToReactKey(key: string): string;
37
+ /**
38
+ * Converts a parsed {@link domhandler#Element | DOM Element} into a {@link react#React.ReactElement | ReactElement}.
39
+ *
40
+ * @remark The method takes into account the attributes of the parsed {@link domhandler#Element | Element}
41
+ * and passes them as props when creating the {@link react#React.ReactElement | ReactElement}.
42
+ * It only handles children of type "text".
43
+ */
44
+ protected convertToReactElement(element: Element, index: number): React.ReactElement;
45
+ /**
46
+ * Finds all <link> elements in the {@link Extractor.document | document} and converts them
47
+ * into {@link react#React.ReactElement | ReactElements}.
48
+ *
49
+ * @remark The list of elements returned will be in order of appearance in the DOM.
50
+ */
51
+ getLinkElements(): React.ReactElement[];
52
+ /**
53
+ * Finds all <script> elements in the {@link Extractor.document | document} and converts them
54
+ * into {@link react#React.ReactElement | ReactElements}.
55
+ *
56
+ * @remark The list of elements returned will be in order of appearance in the DOM.
57
+ */
58
+ getScriptElements(): React.ReactElement[];
59
+ /**
60
+ * Finds all the <head> elements which are not "script" or "link" in the {@link Extractor.document | document}
61
+ * and converts them into {@link react#React.ReactElement | ReactElements}.
62
+ *
63
+ * @remark The list of elements returned will be in order of appearance in the DOM.
64
+ */
65
+ getOtherHeadElements(): React.ReactElement[];
13
66
  }
14
67
  export default Extractor;
15
68
  //# sourceMappingURL=Extractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Extractor.d.ts","sourceRoot":"","sources":["../../../src/renderer/Extractor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;GAEG;AACH,cAAM,SAAS;IAEb,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAM;gBAEnB,IAAI,EAAE,MAAM;IAIxB,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,qBAAqB;IAUtB,WAAW,IAAI,KAAK,CAAC,YAAY,EAAE;IAKnC,aAAa,IAAI,KAAK,CAAC,YAAY,EAAE;CAI7C;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"Extractor.d.ts","sourceRoot":"","sources":["../../../src/renderer/Extractor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,OAAO,EAAoB,MAAM,YAAY,CAAC;AAE3E,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B;;;;;;;;;GASG;AACH,cAAM,SAAS;IACb;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAEtC;;OAEG;gBACS,IAAI,EAAE,MAAM;IAIxB;;;;;;OAMG;IACH,SAAS,CAAC,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE;IAwB1D;;;;;OAKG;IACH,SAAS,CAAC,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAKnD;;;;;;OAMG;IACH,SAAS,CAAC,qBAAqB,CAC7B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC,YAAY;IAiBrB;;;;;OAKG;IACI,eAAe,IAAI,KAAK,CAAC,YAAY,EAAE;IAO9C;;;;;OAKG;IACI,iBAAiB,IAAI,KAAK,CAAC,YAAY,EAAE;IAOhD;;;;;OAKG;IACI,oBAAoB,IAAI,KAAK,CAAC,YAAY,EAAE;CAQpD;AAED,eAAe,SAAS,CAAC"}
@@ -1,51 +1,71 @@
1
- import type { IncomingMessage, ServerResponse } from "node:http";
2
- import type * as React from "react";
3
- import { type PipeableStream, type RenderToPipeableStreamOptions } from "react-dom/server";
4
- export interface RendererOptions {
5
- defaultLocale?: string;
6
- loadMessages?: (locale: string) => string;
7
- /** The HTML string to extract the script and link tags from */
8
- htmlString?: string;
1
+ import { type RenderToPipeableStreamOptions } from "react-dom/server";
2
+ import Extractor from "./Extractor.js";
3
+ import type { RendererOptions, RenderHandler, ServerEntrypoint, ServerEntrypointProps } from "./types.js";
4
+ /**
5
+ * This class is responsible for rendering a React JSX component and sending it as response to a client.
6
+ * It offers 2 ways of doing it:
7
+ * - As string
8
+ * - As stream
9
+ * Each way has its advantages and inconveniences. You can read more about them in the package README.
10
+ */
11
+ export default class JSXRenderer<TComponent extends ServerEntrypoint<InitialData>, InitialData extends Record<string, unknown>> {
12
+ protected readonly Component: TComponent;
13
+ protected readonly initialData: InitialData;
14
+ protected readonly options: RendererOptions;
15
+ protected extractor: Extractor | undefined;
9
16
  /**
10
- * Options to pass to `react-dom/server.renderToPipeableStream`
11
- * We specifically exclude `onShellReady()`, `onError()`, and `onShellError()` as they are implemented by `JSXRenderer.render().`
12
- * See https://react.dev/reference/react-dom/server/renderToPipeableStream#parameters
17
+ * Creates a renderer instance which can be used to write Server Side Rendered HTML
18
+ * into a {@link node:http#ServerResponse | ServerResponse}.
13
19
  */
14
- renderToPipeableStreamOptions?: Omit<RenderToPipeableStreamOptions, "onShellReady" | "onError" | "onShellError">;
15
- }
16
- /** The props that the server entrypoint component will receive */
17
- export interface RendererServerEntrypointProps {
18
- /** The language of the page. This is typically read from the request headers. */
19
- lang?: string;
20
- /** The script tags to include in the page */
21
- scriptTags?: string;
22
- /** The link tags to include in the page */
23
- linkTags?: string;
24
- }
25
- /** The result of rendering a React component */
26
- export type RenderResult = PipeableStream;
27
- /** A function that renders a React component */
28
- export type RenderHandler = (req: IncomingMessage, res: ServerResponse) => RenderResult;
29
- export type ReactServerEntrypointComponent<TComponentProps extends RendererServerEntrypointProps> = React.ComponentType<TComponentProps>;
30
- export default class Renderer<TComponent extends ReactServerEntrypointComponent<TComponentProps>, TComponentProps extends RendererServerEntrypointProps = RendererServerEntrypointProps> {
31
- private readonly Component;
32
- private readonly options;
33
- private locale;
34
- private extractor;
35
- constructor(Component: TComponent, options?: RendererOptions);
20
+ constructor(Component: TComponent, initialData?: InitialData, options?: RendererOptions);
21
+ /**
22
+ * Gets the locale to be used for the rendered page.
23
+ * Default if there was no locale passed as option is "en".
24
+ */
25
+ getLocale(): string;
26
+ /**
27
+ * Gets the props needed to render the component.
28
+ */
29
+ protected getComponentProps(): ServerEntrypointProps<InitialData>;
30
+ /**
31
+ * Gets a list of all the "src" attributes of the given scripts that match the passed type.
32
+ */
33
+ protected getScriptSourcesByType(scripts: React.ReactElement[], type: "module" | "classic"): string[];
34
+ /**
35
+ * Adds some properties to the options that are passed to {@link react-dom#renderToPipeableStream | renderToPipeableStream}.
36
+ *
37
+ * @remark The options that are added are:
38
+ * - bootstrapScriptContent: includes the initial data passed as prop to the component in a <script> so that it
39
+ * is available when rendering the page in the browser (to avoid hydration mismatches).
40
+ * - bootstrapScripts: classic scripts which react strips out of the page. The only way to add them is to include them
41
+ * in this property.
42
+ * - bootstrapModules: module scripts which react also strips out of the page and need to be added like this.
43
+ */
44
+ protected enrichRendererOptions(props: ServerEntrypointProps<InitialData>): RenderToPipeableStreamOptions;
36
45
  /**
37
- * Gets the props needed to render the component
38
- * @return The props needed to render the component
39
- * @private
46
+ * This function is responsible for rendering a React component and sending it to the client through
47
+ * a pipeable stream.
48
+ *
49
+ * @remark See the README to understand the difference between rendering options.
50
+ *
51
+ * The streaming might improve the time taken for the page to be rendered and interactive
52
+ * (at least in part), using React's Suspense/lazy API and pipeable streams.
53
+ *
54
+ * CAUTION: The resulting HTML rendered this way is not cacheable.
40
55
  */
41
- private getComponentProps;
56
+ renderToStream: RenderHandler;
42
57
  /**
43
- * Renders this renderer's JSX component as a transmittable stream and sends it to the client
44
- * TODO add a render function for ReadableStream, and rename this to be focused on PipeableStream
45
- * @param req Client's request
46
- * @param res Response object that will be sent to the client
47
- * @return {RenderResult} The stream that was sent to the client
58
+ * Renders this renderer's JSX component as a string and writes it to the given
59
+ * {@link node:http#ServerResponse | ServerResponse}.
60
+ *
61
+ * @remark See the README to understand the difference between rendering options.
62
+ *
63
+ * Rendering to string means all <Suspense> components are loaded synchronously and the response
64
+ * won't be sent to the client until all components have finished loading data and processing.
65
+ *
66
+ * renderToString is useful in Vite Dev mode, as the HMR doesn't work well with Suspense
67
+ * and the Pipeable Stream rendering. Also if the resulting document needs to be cached.
48
68
  */
49
- render: RenderHandler;
69
+ renderToString: RenderHandler;
50
70
  }
51
71
  //# sourceMappingURL=JSXRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"JSXRenderer.d.ts","sourceRoot":"","sources":["../../../src/renderer/JSXRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,6BAA6B,EAEnC,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,6BAA6B,CAAC,EAAE,IAAI,CAClC,6BAA6B,EAC7B,cAAc,GAAG,SAAS,GAAG,cAAc,CAC5C,CAAC;CACH;AAED,kEAAkE;AAClE,MAAM,WAAW,6BAA6B;IAC5C,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAGD,gDAAgD;AAChD,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC;AAC1C,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,KAChB,YAAY,CAAC;AAElB,MAAM,MAAM,8BAA8B,CACxC,eAAe,SAAS,6BAA6B,IACnD,KAAK,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;AAGzC,MAAM,CAAC,OAAO,OAAO,QAAQ,CAC3B,UAAU,SAAS,8BAA8B,CAAC,eAAe,CAAC,EAClE,eAAe,SACb,6BAA6B,GAAG,6BAA6B;IAO7D,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,CAAC,SAAS,CAAwB;gBAGtB,SAAS,EAAE,UAAU,EACrB,OAAO,GAAE,eAAoB;IAkBhD;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;;;;;OAMG;IACH,MAAM,EAAE,aAAa,CAoCnB;CACH"}
1
+ {"version":3,"file":"JSXRenderer.d.ts","sourceRoot":"","sources":["../../../src/renderer/JSXRenderer.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,6BAA6B,EAGnC,MAAM,kBAAkB,CAAC;AAE1B,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EAEb,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW,CAC9B,UAAU,SAAS,gBAAgB,CAAC,WAAW,CAAC,EAChD,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IASzC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,UAAU;IACxC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAC3C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe;IAT7C,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IAE3C;;;OAGG;gBAEkB,SAAS,EAAE,UAAU,EACrB,WAAW,GAAE,WAA+B,EAC5C,OAAO,GAAE,eAAoB;IAOlD;;;OAGG;IACI,SAAS,IAAI,MAAM;IAI1B;;OAEG;IACH,SAAS,CAAC,iBAAiB,IAAI,qBAAqB,CAAC,WAAW,CAAC;IAUjE;;OAEG;IACH,SAAS,CAAC,sBAAsB,CAC9B,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,EAC7B,IAAI,EAAE,QAAQ,GAAG,SAAS,GACzB,MAAM,EAAE;IAsBX;;;;;;;;;OASG;IACH,SAAS,CAAC,qBAAqB,CAC7B,KAAK,EAAE,qBAAqB,CAAC,WAAW,CAAC,GACxC,6BAA6B;IA6BhC;;;;;;;;;;OAUG;IACH,cAAc,EAAE,aAAa,CAqC3B;IAEF;;;;;;;;;;;OAWG;IACH,cAAc,EAAE,aAAa,CAW3B;CACH"}
@@ -0,0 +1,2 @@
1
+ export declare const INITIAL_DATA_KEY = "__INITIAL_DATA__";
2
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/renderer/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,qBAAqB,CAAC"}
@@ -1,4 +1,5 @@
1
+ export * from "./constants.js";
1
2
  export { default as Extractor } from "./Extractor.js";
2
- export type { ReactServerEntrypointComponent, RendererOptions, RendererServerEntrypointProps, RenderHandler, RenderResult, } from "./JSXRenderer.js";
3
3
  export { default as JSXRenderer } from "./JSXRenderer.js";
4
+ export type { RendererOptions, RenderHandler, RenderResult, ServerEntrypoint, ServerEntrypointProps, } from "./types.js";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/renderer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACtD,YAAY,EACV,8BAA8B,EAC9B,eAAe,EACf,6BAA6B,EAC7B,aAAa,EACb,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/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,YAAY,EACV,eAAe,EACf,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type * as React from "react";
3
+ import type { PipeableStream, RenderToPipeableStreamOptions } from "react-dom/server";
4
+ export interface RendererOptions {
5
+ defaultLocale?: string;
6
+ /** An HTML string to extract the head tags from */
7
+ htmlString?: string;
8
+ /**
9
+ * Options to pass to `react-dom/server.renderToPipeableStream`
10
+ * We specifically exclude `onShellReady()`, `onError()`, `onShellError()` and `onAllReady()` as they are
11
+ * implemented by `JSXRenderer.renderToString()` and `JSXRenderer.renderToStream()`.
12
+ * See https://react.dev/reference/react-dom/server/renderToPipeableStream#parameters
13
+ */
14
+ renderToPipeableStreamOptions?: Omit<RenderToPipeableStreamOptions, "onShellReady" | "onError" | "onShellError" | "onAllReady">;
15
+ }
16
+ /** The props that the server entrypoint component will receive */
17
+ export interface ServerEntrypointProps<InitialData extends Record<string, unknown>> {
18
+ /** The language of the page. This is typically read from the request headers. */
19
+ lang?: string;
20
+ /** The script tags to include in the page */
21
+ scriptElements?: React.ReactElement[];
22
+ /** The link tags to include in the page */
23
+ linkElements?: React.ReactElement[];
24
+ /** Other head elements: title, base, style & meta */
25
+ otherHeadElements?: React.ReactElement[];
26
+ /**
27
+ * Initial data used in the server to render the React application, which needs to be
28
+ * embedded in the resulting HTML so that the hydration in the client matches that of the server.
29
+ */
30
+ initialData?: InitialData;
31
+ }
32
+ export type ServerEntrypoint<InitialData extends Record<string, unknown>> = React.ComponentType<ServerEntrypointProps<InitialData>>;
33
+ /** The result of rendering a React component */
34
+ export type RenderResult = PipeableStream;
35
+ /** A function that renders a React component */
36
+ export type RenderHandler = (req: IncomingMessage, res: ServerResponse) => void;
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/renderer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC9B,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,6BAA6B,CAAC,EAAE,IAAI,CAClC,6BAA6B,EAC7B,cAAc,GAAG,SAAS,GAAG,cAAc,GAAG,YAAY,CAC3D,CAAC;CACH;AAED,kEAAkE;AAClE,MAAM,WAAW,qBAAqB,CACpC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE3C,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,cAAc,CAAC,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;IACtC,2CAA2C;IAC3C,YAAY,CAAC,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;IACpC,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;IACzC;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,MAAM,gBAAgB,CAAC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACtE,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC;AAI1D,gDAAgD;AAChD,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC;AAC1C,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC"}
@@ -14,7 +14,7 @@ import type { RenderHandler } from "../renderer/index.js";
14
14
  * import htmlString from "../../dist/client/index.html?raw";
15
15
  * import EntryServer from "./entry-server.js";
16
16
  *
17
- * // `EntryServer` is an instance of `@canonical/react-ssr/renderer/ReactServerEntrypointComponent`
17
+ * // `EntryServer` is an instance of `@canonical/react-ssr/renderer/ServerEntrypoint`
18
18
  * const Renderer = new JSXRenderer(EntryServer, {
19
19
  * htmlString,
20
20
  * });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@canonical/react-ssr",
3
3
  "description": "TBD",
4
- "version": "0.14.0",
4
+ "version": "0.15.0",
5
5
  "type": "module",
6
6
  "module": "dist/esm/index.js",
7
7
  "types": "dist/types/index.d.ts",
@@ -44,6 +44,10 @@
44
44
  "import": "./dist/esm/renderer/index.js",
45
45
  "types": "./dist/types/renderer/index.d.ts"
46
46
  },
47
+ "./renderer/constants": {
48
+ "import": "./dist/esm/renderer/constants.js",
49
+ "types": "./dist/types/renderer/constants.d.ts"
50
+ },
47
51
  "./server": {
48
52
  "import": "./dist/esm/server/index.js",
49
53
  "types": "./dist/types/server/index.d.ts"
@@ -51,9 +55,9 @@
51
55
  },
52
56
  "devDependencies": {
53
57
  "@biomejs/biome": "2.3.14",
54
- "@canonical/biome-config": "^0.14.0",
55
- "@canonical/typescript-config-react": "^0.14.0",
56
- "@canonical/webarchitect": "^0.14.0",
58
+ "@canonical/biome-config": "^0.15.0",
59
+ "@canonical/typescript-config-react": "^0.15.0",
60
+ "@canonical/webarchitect": "^0.15.0",
57
61
  "@types/express": "^5.0.6",
58
62
  "@types/node": "^24.10.13",
59
63
  "@types/react": "^19.2.13",
@@ -61,12 +65,12 @@
61
65
  "typescript": "^5.9.3"
62
66
  },
63
67
  "dependencies": {
64
- "@canonical/utils": "^0.14.0",
68
+ "@canonical/utils": "^0.15.0",
65
69
  "domhandler": "^5.0.3",
66
70
  "express": "^5.2.1",
67
71
  "htmlparser2": "^10.1.0",
68
72
  "react": "^19.2.4",
69
73
  "react-dom": "^19.2.4"
70
74
  },
71
- "gitHead": "3bcc701893d91c5b7d36454e0c00fb3b490f55cc"
75
+ "gitHead": "60c6f6f355d4a80ce110d8dd44c429695301ad86"
72
76
  }