reactivated 0.45.3 → 0.46.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/client.js CHANGED
@@ -1,5 +1,5 @@
1
+ import { hydrateRoot } from "react-dom/client";
1
2
  import React from "react";
2
- import { hydrate } from "react-dom";
3
3
  const props = window.__PRELOADED_STATE__;
4
4
  if (module.hot) {
5
5
  module.hot.accept();
@@ -7,6 +7,11 @@ if (module.hot) {
7
7
  // tslint:disable-next-line
8
8
  const Template = require("client/templates/" + props.template_name + ".tsx").default;
9
9
  export const bootstrap = () => {
10
- hydrate(React.createElement(Template, { ...props }), document.getElementById("root"));
10
+ const root = document.getElementById("root");
11
+ if (!root) {
12
+ console.error("div#root is missing!");
13
+ return;
14
+ }
15
+ hydrateRoot(root, React.createElement(Template, { ...props }));
11
16
  };
12
17
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAElC,MAAM,KAAK,GAAI,MAAc,CAAC,mBAAmB,CAAC;AAElD,IAAK,MAAc,CAAC,GAAG,EAAE,CAAC;IACrB,MAAc,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;AACjC,CAAC;AAED,2BAA2B;AAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,mBAAmB,GAAG,KAAK,CAAC,aAAa,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC;AAErF,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE;IAC1B,OAAO,CAAC,oBAAC,QAAQ,OAAK,KAAK,GAAI,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,KAAK,GAAI,MAAc,CAAC,mBAAmB,CAAC;AAElD,IAAK,MAAc,CAAC,GAAG,EAAE,CAAC;IACrB,MAAc,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;AACjC,CAAC;AAED,2BAA2B;AAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,mBAAmB,GAAG,KAAK,CAAC,aAAa,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC;AAErF,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE;IAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACtC,OAAO;IACX,CAAC;IACD,WAAW,CAAC,IAAI,EAAE,oBAAC,QAAQ,OAAK,KAAK,GAAI,CAAC,CAAC;AAC/C,CAAC,CAAC"}
package/dist/render.d.mts CHANGED
@@ -1,16 +1,8 @@
1
1
  import { Request } from "express";
2
- import { HelmetServerState } from "react-helmet-async";
3
- export declare const renderPage: ({ html, vite, helmet, context, props, mode, entryPoint, }: {
4
- html: string;
5
- vite: string;
6
- helmet: HelmetServerState;
7
- context: any;
8
- props: any;
9
- mode: "production" | "development";
10
- entryPoint: string;
11
- }) => string;
2
+ import { type JSX } from "react";
3
+ import { Response } from "express";
12
4
  export type Renderer<T = unknown> = (content: JSX.Element, req: {
13
5
  context: T;
14
6
  props: unknown;
15
7
  }) => Promise<JSX.Element>;
16
- export declare const render: (req: Request, vite: string, mode: "production" | "development", entryPoint: string) => Promise<string>;
8
+ export declare const render: (req: Request, res: Response, vite: string, mode: "production" | "development", entryPoint: string, ssrFixStacktrace?: (error: Error) => void) => Promise<void>;
package/dist/render.mjs CHANGED
@@ -1,14 +1,21 @@
1
- import { HelmetProvider } from "react-helmet-async";
2
- import * as React from "react";
3
- import * as ReactDOMServer from "react-dom/server";
1
+ import React from "react";
2
+ import { renderToPipeableStream } from "react-dom/server";
3
+ import { Transform } from "node:stream";
4
+ import { serializeError } from "./errors.js";
4
5
  // @ts-ignore
5
6
  import { Provider } from "@reactivated";
6
7
  // @ts-ignore
7
8
  import { getTemplate } from "@reactivated/template";
8
- export const renderPage = ({ html, vite, helmet, context, props, mode, entryPoint, }) => {
9
- const scriptNonce = context.request.csp_nonce
10
- ? `nonce="${context.request.csp_nonce}"`
11
- : "";
9
+ const defaultRenderer = (content) => Promise.resolve(content);
10
+ function serJSON(data) {
11
+ return JSON.stringify(data).replace(/</g, "\\u003c");
12
+ }
13
+ export const render = async (req, res, vite, mode, entryPoint, ssrFixStacktrace) => {
14
+ // @ts-ignore
15
+ const customConfiguration = import.meta.glob("@client/renderer.tsx", { eager: true })["/client/renderer.tsx"];
16
+ const { context, props } = req.body;
17
+ const Template = await getTemplate(context);
18
+ const scriptNonce = context.request.csp_nonce ?? undefined;
12
19
  const { STATIC_URL } = context;
13
20
  if (STATIC_URL == null) {
14
21
  console.error("Ensure your context processor includes STATIC_URL");
@@ -19,56 +26,47 @@ export const renderPage = ({ html, vite, helmet, context, props, mode, entryPoin
19
26
  const js = mode == "production"
20
27
  ? `<script type="module" src="${STATIC_URL}dist/${entryPoint}.js" defer crossOrigin="anonymous"></script>`
21
28
  : `<script type="module" src="${STATIC_URL}dist/client/${entryPoint}.tsx"></script>`;
22
- return `
23
- <!DOCTYPE html>
24
- <html ${helmet.htmlAttributes.toString()}>
25
- <head>
26
- ${vite}
27
- <script ${scriptNonce}>
28
- // These go first because scripts below need them.
29
- // WARNING: See the following for security issues around embedding JSON in HTML:
30
- // http://redux.js.org/recipes/ServerRendering.html#security-considerations
31
- window.__PRELOADED_PROPS__ = ${JSON.stringify(props).replace(/</g, "\\u003c")}
32
- window.__PRELOADED_CONTEXT__ = ${JSON.stringify(context).replace(/</g, "\\u003c")}
33
- </script>
34
- ${css}
35
-
36
- ${helmet.base.toString()}
37
- ${helmet.link.toString()}
38
- ${helmet.meta.toString()}
39
- ${helmet.noscript.toString()}
40
- ${helmet.script.toString()}
41
- ${helmet.style.toString()}
42
- ${helmet.title.toString()}
43
- </head>
44
- <body ${helmet.bodyAttributes.toString()}>
45
- <div id="root">${html}</div>
46
- ${js}
47
- </body>
48
- </html>`;
49
- };
50
- const defaultRenderer = (content) => Promise.resolve(content);
51
- export const render = async (req, vite, mode, entryPoint) => {
52
- // @ts-ignore
53
- const customConfiguration = import.meta.glob("@client/renderer.tsx", { eager: true })["/client/renderer.tsx"];
54
- const { context, props } = req.body;
55
- const Template = await getTemplate(context);
56
- const helmetContext = {};
57
- const content = React.createElement(React.StrictMode, {}, React.createElement(HelmetProvider, { context: helmetContext }, React.createElement(Provider, { value: context }, React.createElement(Template, props))));
58
- const html = ReactDOMServer.renderToString(await (customConfiguration?.default ?? defaultRenderer)(content, {
29
+ const content = React.createElement(React.StrictMode, {}, React.createElement(Provider, { value: context }, React.createElement(Template, props)));
30
+ let hasError = false;
31
+ const { pipe } = renderToPipeableStream(await (customConfiguration?.default ?? defaultRenderer)(content, {
59
32
  context,
60
33
  props,
61
- }));
62
- const { helmet } = helmetContext;
63
- const rendered = renderPage({
64
- html,
65
- vite,
66
- helmet,
67
- props,
68
- context,
69
- mode,
70
- entryPoint,
34
+ }), {
35
+ nonce: scriptNonce,
36
+ bootstrapScriptContent: `
37
+ window.__PRELOADED_PROPS__ = ${serJSON(props)};
38
+ window.__PRELOADED_CONTEXT__ = ${serJSON(context)};
39
+ `,
40
+ onError(error) {
41
+ hasError = true;
42
+ if (ssrFixStacktrace) {
43
+ console.log("fixing stacktrace");
44
+ ssrFixStacktrace(error);
45
+ }
46
+ const errResp = {
47
+ error: serializeError(error),
48
+ };
49
+ res.status(500).json(errResp);
50
+ },
51
+ onAllReady() {
52
+ if (hasError) {
53
+ return;
54
+ }
55
+ res.status(200);
56
+ // Seems like the renderer.py, at least for unix socket, requires a charset
57
+ // unlike the React docs
58
+ res.setHeader("content-type", "text/html; charset=utf-8");
59
+ const transformStream = new Transform({
60
+ transform(chunk, encoding, callback) {
61
+ res.write(chunk, encoding);
62
+ callback();
63
+ },
64
+ });
65
+ transformStream.on("finish", () => {
66
+ res.end(vite + css + js);
67
+ });
68
+ pipe(transformStream);
69
+ },
71
70
  });
72
- return rendered;
73
71
  };
74
72
  //# sourceMappingURL=render.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.mjs","sourceRoot":"","sources":["../src/render.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render.mjs","sourceRoot":"","sources":["../src/render.mts"],"names":[],"mappings":"AACA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAGtC,OAAO,EAAC,sBAAsB,EAAC,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAmB,cAAc,EAAC,MAAM,aAAa,CAAC;AAC7D,aAAa;AACb,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AACtC,aAAa;AACb,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAUlD,MAAM,eAAe,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAExE,SAAS,OAAO,CAAC,IAAa;IAC1B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,EACvB,GAAY,EACZ,GAAa,EACb,IAAY,EACZ,IAAkC,EAClC,UAAkB,EAClB,gBAAyC,EAC3C,EAAE;IACA,aAAa;IACb,MAAM,mBAAmB,GAAgC,MAAM,CAAC,IAAI,CAAC,IAAI,CACrE,sBAAsB,EACtB,EAAC,KAAK,EAAE,IAAI,EAAC,CAChB,CAAC,sBAAsB,CAAC,CAAC;IAE1B,MAAM,EAAC,OAAO,EAAE,KAAK,EAAC,GAAG,GAAG,CAAC,IAAI,CAAC;IAClC,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,IAAI,SAAS,CAAC;IAE3D,MAAM,EAAC,UAAU,EAAC,GAAG,OAAO,CAAC;IAE7B,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,GAAG,GACL,IAAI,IAAI,YAAY;QAChB,CAAC,CAAC,gDAAgD,UAAU,QAAQ,UAAU,QAAQ;QACtF,CAAC,CAAC,EAAE,CAAC;IACb,MAAM,EAAE,GACJ,IAAI,IAAI,YAAY;QAChB,CAAC,CAAC,8BAA8B,UAAU,QAAQ,UAAU,8CAA8C;QAC1G,CAAC,CAAC,8BAA8B,UAAU,eAAe,UAAU,iBAAiB,CAAC;IAE7F,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,CAC/B,KAAK,CAAC,UAAU,EAChB,EAAE,EACF,KAAK,CAAC,aAAa,CACf,QAAQ,EACR,EAAC,KAAK,EAAE,OAAO,EAAC,EAChB,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CACvC,CACJ,CAAC;IAEF,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,EAAC,IAAI,EAAC,GAAG,sBAAsB,CACjC,MAAM,CAAC,mBAAmB,EAAE,OAAO,IAAI,eAAe,CAAC,CAAC,OAAO,EAAE;QAC7D,OAAO;QACP,KAAK;KACR,CAAC,EAEF;QACI,KAAK,EAAE,WAAW;QAClB,sBAAsB,EAAE;uCACG,OAAO,CAAC,KAAK,CAAC;yCACZ,OAAO,CAAC,OAAO,CAAC;SAChD;QACG,OAAO,CAAC,KAAK;YACT,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,gBAAgB,EAAE,CAAC;gBACnB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;gBACjC,gBAAgB,CAAC,KAAY,CAAC,CAAC;YACnC,CAAC;YACD,MAAM,OAAO,GAAqB;gBAC9B,KAAK,EAAE,cAAc,CAAC,KAAY,CAAC;aACtC,CAAC;YACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,UAAU;YACN,IAAI,QAAQ,EAAE,CAAC;gBACX,OAAO;YACX,CAAC;YACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChB,2EAA2E;YAC3E,wBAAwB;YACxB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;YAC1D,MAAM,eAAe,GAAG,IAAI,SAAS,CAAC;gBAClC,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ;oBAC/B,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBAC3B,QAAQ,EAAE,CAAC;gBACf,CAAC;aACJ,CAAC,CAAC;YACH,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;gBAC9B,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,eAAe,CAAC,CAAC;QAC1B,CAAC;KACJ,CACJ,CAAC;AACN,CAAC,CAAC"}
package/dist/server.mjs CHANGED
@@ -4,18 +4,12 @@ import http from "node:http";
4
4
  import os from "node:os";
5
5
  import { serializeError } from "./errors.js";
6
6
  import { render } from "./render.mjs";
7
- const isProduction = process.env.NODE_ENV === "production";
8
7
  const socketPath = path.join(os.tmpdir(), `reactivated.${process.pid}.sock`);
9
- const base = process.env.BASE || "/";
10
- const escapedBase = base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11
- const reactivatedEndpoint = "/_reactivated/".replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12
8
  const app = express();
13
9
  app.use(express.json({ limit: "200mb" }));
14
10
  app.use("/_reactivated/", async (req, res) => {
15
- const { context, props } = req.body;
16
11
  try {
17
- const rendered = await render(req, "", "production", "index");
18
- res.status(200).set({ "Content-Type": "text/html" }).end(rendered);
12
+ render(req, res, "", "production", "index");
19
13
  }
20
14
  catch (error) {
21
15
  const errResp = {
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","sourceRoot":"","sources":["../src/server.mts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAGzB,OAAO,EAAmB,cAAc,EAAC,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AAQpC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AAC7E,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC;AACrC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAChE,MAAM,mBAAmB,GAAG,gBAAgB,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAEpF,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,OAAO,EAAC,CAAC,CAAC,CAAC;AACxC,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACzC,MAAM,EAAC,OAAO,EAAE,KAAK,EAAC,GAAG,GAAG,CAAC,IAAI,CAAC;IAElC,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;QAC9D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAC,cAAc,EAAE,WAAW,EAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,OAAO,GAAqB;YAC9B,KAAK,EAAE,cAAc,CAAC,KAAY,CAAC;SACtC,CAAC;QACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE;IAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,UAAU,YAAY,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,MAAM,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"server.mjs","sourceRoot":"","sources":["../src/server.mts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAmB,cAAc,EAAC,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AAEpC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AAE7E,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAC,KAAK,EAAE,OAAO,EAAC,CAAC,CAAC,CAAC;AACxC,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACzC,IAAI,CAAC;QACD,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,OAAO,GAAqB;YAC9B,KAAK,EAAE,cAAc,CAAC,KAAY,CAAC;SACtC,CAAC;QACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE;IAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,UAAU,YAAY,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,MAAM,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC,CAAC,CAAC"}
package/dist/vite.mjs CHANGED
@@ -50,12 +50,7 @@ const rendererConfig = {
50
50
  vanillaExtractPlugin(),
51
51
  cjsInterop({
52
52
  // List of CJS dependencies that require interop
53
- dependencies: [
54
- "react-syntax-highlighter",
55
- "lz-string",
56
- "react-use",
57
- "react-helmet-async",
58
- ],
53
+ dependencies: ["react-syntax-highlighter", "lz-string", "react-use"],
59
54
  }),
60
55
  ],
61
56
  resolve: {
@@ -79,8 +74,7 @@ app.use("/_reactivated/", async (req, res) => {
79
74
  // manage.py build step necessary. So we ensure vite head always points
80
75
  // to the per-request STATIC_URL.
81
76
  const viteHead = (await vite.transformIndexHtml(url, "")).replaceAll(base, `${context.STATIC_URL}dist/`);
82
- const rendered = await render(req, viteHead, "development", "index");
83
- res.status(200).set({ "Content-Type": "text/html" }).end(rendered);
77
+ render(req, res, viteHead, "development", "index", vite.ssrFixStacktrace);
84
78
  }
85
79
  catch (error) {
86
80
  vite.ssrFixStacktrace(error);
package/dist/vite.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vite.mjs","sourceRoot":"","sources":["../src/vite.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vite.mjs","sourceRoot":"","sources":["../src/vite.mts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reactivated",
3
- "version": "0.45.3",
3
+ "version": "0.46.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "exports": {
@@ -10,6 +10,7 @@
10
10
  "./dist/context": "./dist/context.js",
11
11
  "./dist/conf": "./dist/conf.js",
12
12
  "./dist/vite.mjs": "./dist/vite.mjs",
13
+ "./dist/shell": "./dist/shell.js",
13
14
  "./dist/render.mjs": "./dist/render.mjs",
14
15
  "./dist/server.mjs": "./dist/server.mjs",
15
16
  "./dist/rpc": "./dist/rpc.js"
@@ -30,13 +31,13 @@
30
31
  },
31
32
  "license": "ISC",
32
33
  "peerDependencies": {
33
- "@eslint/js": "^9.14.0",
34
+ "@eslint/js": "^9.18.0",
34
35
  "@types/eslint__eslintrc": "^2.1.2",
35
36
  "@types/eslint__js": "^8.42.3",
36
37
  "@types/eslint-config-prettier": "^6.11.3",
37
38
  "@types/express": "^5.0.0",
38
- "@types/react": "^18.3.12",
39
- "@types/react-dom": "^18.3.1",
39
+ "@types/react": "^19.0.2",
40
+ "@types/react-dom": "^19.0.2",
40
41
  "@vanilla-extract/css": "^1.16.0",
41
42
  "@vanilla-extract/css-utils": "^0.1.4",
42
43
  "@vanilla-extract/dynamic": "^2.1.2",
@@ -45,7 +46,7 @@
45
46
  "@vanilla-extract/sprinkles": "^1.6.3",
46
47
  "@vanilla-extract/vite-plugin": "3.9.5",
47
48
  "@vitejs/plugin-react": "^4.3.3",
48
- "eslint": "^9.14.0",
49
+ "eslint": "^9.18.0",
49
50
  "eslint-config-prettier": "^9.1.0",
50
51
  "eslint-import-resolver-typescript": "^3.6.3",
51
52
  "eslint-plugin-import": "^2.31.0",
@@ -57,13 +58,12 @@
57
58
  "jiti": "^2.4.1",
58
59
  "json-schema-to-typescript": "^15.0.2",
59
60
  "prettier": "^3.3.3",
60
- "react": "^18.3.1",
61
- "react-dom": "^18.3.1",
62
- "react-helmet-async": "^2.0.5",
61
+ "react": "^19.0.0",
62
+ "react-dom": "^19.0.0",
63
63
  "terser": "^5.36.0",
64
64
  "ts-morph": "^24.0.0",
65
65
  "typescript": "^5.6.3",
66
- "typescript-eslint": "^8.13.0",
66
+ "typescript-eslint": "^8.20.0",
67
67
  "vite": "5.1.4",
68
68
  "vite-plugin-cjs-interop": "2.0.6"
69
69
  }
package/src/client.tsx CHANGED
@@ -1,5 +1,5 @@
1
+ import {hydrateRoot} from "react-dom/client";
1
2
  import React from "react";
2
- import {hydrate} from "react-dom";
3
3
 
4
4
  const props = (window as any).__PRELOADED_STATE__;
5
5
 
@@ -11,5 +11,10 @@ if ((module as any).hot) {
11
11
  const Template = require("client/templates/" + props.template_name + ".tsx").default;
12
12
 
13
13
  export const bootstrap = () => {
14
- hydrate(<Template {...props} />, document.getElementById("root"));
14
+ const root = document.getElementById("root");
15
+ if (!root) {
16
+ console.error("div#root is missing!");
17
+ return;
18
+ }
19
+ hydrateRoot(root, <Template {...props} />);
15
20
  };
package/src/render.mts CHANGED
@@ -1,83 +1,16 @@
1
1
  import {Request} from "express";
2
- import {HelmetProvider, HelmetServerState} from "react-helmet-async";
3
- import * as React from "react";
4
- import * as ReactDOMServer from "react-dom/server";
2
+ import React, {type JSX} from "react";
3
+ import {Response} from "express";
4
+ import ReactDOMServer from "react-dom/server";
5
+ import {renderToPipeableStream} from "react-dom/server";
6
+ import {Transform} from "node:stream";
5
7
 
8
+ import {SSRErrorResponse, serializeError} from "./errors.js";
6
9
  // @ts-ignore
7
10
  import {Provider} from "@reactivated";
8
11
  // @ts-ignore
9
12
  import {getTemplate} from "@reactivated/template";
10
13
 
11
- export const renderPage = ({
12
- html,
13
- vite,
14
- helmet,
15
- context,
16
- props,
17
- mode,
18
- entryPoint,
19
- }: {
20
- html: string;
21
- vite: string;
22
- helmet: HelmetServerState;
23
- context: any;
24
- props: any;
25
- mode: "production" | "development";
26
- entryPoint: string;
27
- }) => {
28
- const scriptNonce = context.request.csp_nonce
29
- ? `nonce="${context.request.csp_nonce}"`
30
- : "";
31
- const {STATIC_URL} = context;
32
-
33
- if (STATIC_URL == null) {
34
- console.error("Ensure your context processor includes STATIC_URL");
35
- }
36
-
37
- const css =
38
- mode == "production"
39
- ? `<link rel="stylesheet" type="text/css" href="${STATIC_URL}dist/${entryPoint}.css">`
40
- : "";
41
- const js =
42
- mode == "production"
43
- ? `<script type="module" src="${STATIC_URL}dist/${entryPoint}.js" defer crossOrigin="anonymous"></script>`
44
- : `<script type="module" src="${STATIC_URL}dist/client/${entryPoint}.tsx"></script>`;
45
-
46
- return `
47
- <!DOCTYPE html>
48
- <html ${helmet.htmlAttributes.toString()}>
49
- <head>
50
- ${vite}
51
- <script ${scriptNonce}>
52
- // These go first because scripts below need them.
53
- // WARNING: See the following for security issues around embedding JSON in HTML:
54
- // http://redux.js.org/recipes/ServerRendering.html#security-considerations
55
- window.__PRELOADED_PROPS__ = ${JSON.stringify(props).replace(
56
- /</g,
57
- "\\u003c",
58
- )}
59
- window.__PRELOADED_CONTEXT__ = ${JSON.stringify(context).replace(
60
- /</g,
61
- "\\u003c",
62
- )}
63
- </script>
64
- ${css}
65
-
66
- ${helmet.base.toString()}
67
- ${helmet.link.toString()}
68
- ${helmet.meta.toString()}
69
- ${helmet.noscript.toString()}
70
- ${helmet.script.toString()}
71
- ${helmet.style.toString()}
72
- ${helmet.title.toString()}
73
- </head>
74
- <body ${helmet.bodyAttributes.toString()}>
75
- <div id="root">${html}</div>
76
- ${js}
77
- </body>
78
- </html>`;
79
- };
80
-
81
14
  export type Renderer<T = unknown> = (
82
15
  content: JSX.Element,
83
16
  req: {
@@ -88,11 +21,17 @@ export type Renderer<T = unknown> = (
88
21
 
89
22
  const defaultRenderer: Renderer = (content) => Promise.resolve(content);
90
23
 
24
+ function serJSON(data: unknown): string {
25
+ return JSON.stringify(data).replace(/</g, "\\u003c");
26
+ }
27
+
91
28
  export const render = async (
92
29
  req: Request,
30
+ res: Response,
93
31
  vite: string,
94
32
  mode: "production" | "development",
95
33
  entryPoint: string,
34
+ ssrFixStacktrace?: (error: Error) => void,
96
35
  ) => {
97
36
  // @ts-ignore
98
37
  const customConfiguration: {default?: Renderer} | null = import.meta.glob(
@@ -102,39 +41,77 @@ export const render = async (
102
41
 
103
42
  const {context, props} = req.body;
104
43
  const Template = await getTemplate(context);
105
- const helmetContext = {} as {helmet: HelmetServerState};
44
+ const scriptNonce = context.request.csp_nonce ?? undefined;
45
+
46
+ const {STATIC_URL} = context;
47
+
48
+ if (STATIC_URL == null) {
49
+ console.error("Ensure your context processor includes STATIC_URL");
50
+ }
51
+ const css =
52
+ mode == "production"
53
+ ? `<link rel="stylesheet" type="text/css" href="${STATIC_URL}dist/${entryPoint}.css">`
54
+ : "";
55
+ const js =
56
+ mode == "production"
57
+ ? `<script type="module" src="${STATIC_URL}dist/${entryPoint}.js" defer crossOrigin="anonymous"></script>`
58
+ : `<script type="module" src="${STATIC_URL}dist/client/${entryPoint}.tsx"></script>`;
106
59
 
107
60
  const content = React.createElement(
108
61
  React.StrictMode,
109
62
  {},
110
63
  React.createElement(
111
- HelmetProvider,
112
- {context: helmetContext},
113
- React.createElement(
114
- Provider,
115
- {value: context},
116
- React.createElement(Template, props),
117
- ),
64
+ Provider,
65
+ {value: context},
66
+ React.createElement(Template, props),
118
67
  ),
119
68
  );
120
69
 
121
- const html = ReactDOMServer.renderToString(
70
+ let hasError = false;
71
+
72
+ const {pipe} = renderToPipeableStream(
122
73
  await (customConfiguration?.default ?? defaultRenderer)(content, {
123
74
  context,
124
75
  props,
125
76
  }),
126
- );
127
- const {helmet} = helmetContext;
128
77
 
129
- const rendered = renderPage({
130
- html,
131
- vite,
132
- helmet,
133
- props,
134
- context,
135
- mode,
136
- entryPoint,
137
- });
78
+ {
79
+ nonce: scriptNonce,
80
+ bootstrapScriptContent: `
81
+ window.__PRELOADED_PROPS__ = ${serJSON(props)};
82
+ window.__PRELOADED_CONTEXT__ = ${serJSON(context)};
83
+ `,
84
+ onError(error) {
85
+ hasError = true;
86
+ if (ssrFixStacktrace) {
87
+ console.log("fixing stacktrace");
88
+ ssrFixStacktrace(error as any);
89
+ }
90
+ const errResp: SSRErrorResponse = {
91
+ error: serializeError(error as any),
92
+ };
93
+ res.status(500).json(errResp);
94
+ },
95
+ onAllReady() {
96
+ if (hasError) {
97
+ return;
98
+ }
99
+ res.status(200);
100
+ // Seems like the renderer.py, at least for unix socket, requires a charset
101
+ // unlike the React docs
102
+ res.setHeader("content-type", "text/html; charset=utf-8");
103
+ const transformStream = new Transform({
104
+ transform(chunk, encoding, callback) {
105
+ res.write(chunk, encoding);
106
+ callback();
107
+ },
108
+ });
109
+ transformStream.on("finish", () => {
110
+ res.end(vite + css + js);
111
+ });
138
112
 
139
- return rendered;
113
+ pipe(transformStream);
114
+ },
115
+ },
116
+ );
140
117
  };
package/src/server.mts CHANGED
@@ -1,34 +1,18 @@
1
- import React from "react";
2
1
  import express from "express";
3
2
  import path from "node:path";
4
3
  import http from "node:http";
5
4
  import os from "node:os";
6
- import react from "@vitejs/plugin-react";
7
- import ReactDOMServer from "react-dom/server";
8
5
  import {SSRErrorResponse, serializeError} from "./errors.js";
9
6
  import {render} from "./render.mjs";
10
7
 
11
- import {Helmet, HelmetProvider, HelmetServerState} from "react-helmet-async";
12
- import {vanillaExtractPlugin} from "@vanilla-extract/vite-plugin";
13
-
14
- // @ts-ignore
15
- import {Provider, getTemplate} from "@reactivated";
16
-
17
- const isProduction = process.env.NODE_ENV === "production";
18
8
  const socketPath = path.join(os.tmpdir(), `reactivated.${process.pid}.sock`);
19
- const base = process.env.BASE || "/";
20
- const escapedBase = base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
21
- const reactivatedEndpoint = "/_reactivated/".replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22
9
 
23
10
  const app = express();
24
11
 
25
12
  app.use(express.json({limit: "200mb"}));
26
13
  app.use("/_reactivated/", async (req, res) => {
27
- const {context, props} = req.body;
28
-
29
14
  try {
30
- const rendered = await render(req, "", "production", "index");
31
- res.status(200).set({"Content-Type": "text/html"}).end(rendered);
15
+ render(req, res, "", "production", "index");
32
16
  } catch (error) {
33
17
  const errResp: SSRErrorResponse = {
34
18
  error: serializeError(error as any),
package/src/vite.mts CHANGED
@@ -59,12 +59,7 @@ const rendererConfig: InlineConfig = {
59
59
  vanillaExtractPlugin(),
60
60
  cjsInterop({
61
61
  // List of CJS dependencies that require interop
62
- dependencies: [
63
- "react-syntax-highlighter",
64
- "lz-string",
65
- "react-use",
66
- "react-helmet-async",
67
- ],
62
+ dependencies: ["react-syntax-highlighter", "lz-string", "react-use"],
68
63
  }),
69
64
  ],
70
65
  resolve: {
@@ -98,9 +93,7 @@ app.use("/_reactivated/", async (req, res) => {
98
93
  base,
99
94
  `${context.STATIC_URL}dist/`,
100
95
  );
101
- const rendered = await render(req, viteHead, "development", "index");
102
-
103
- res.status(200).set({"Content-Type": "text/html"}).end(rendered);
96
+ render(req, res, viteHead, "development", "index", vite.ssrFixStacktrace);
104
97
  } catch (error) {
105
98
  vite.ssrFixStacktrace(error as any);
106
99
  const errResp: SSRErrorResponse = {