Skip to content

Commit 08d3ffb

Browse files
author
SamTV12345
committed
chore(docs): make prerendering React 19 compatible
react-ssr-prepass reads ReactCurrentDispatcher from the React internals, which React 19 no longer exposes, so the docs prerender crashed. It was only used to await AsyncComponent.preload() before renderToString, and the loading-state render already triggers preload() itself, so a yarn patch on the documentation-framework replaces it with re-rendering until no loading placeholder is left. The same fix should land in the documentation-framework itself.
1 parent 26817af commit 08d3ffb

4 files changed

Lines changed: 146 additions & 3 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ lerna-debug.log
3131
.vscode
3232
# For vim
3333
*.swp
34-
.yarn
34+
.yarn/*
35+
!.yarn/patches
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
diff --git a/scripts/webpack/prerender.js b/scripts/webpack/prerender.js
2+
index 37306139a11cabc0c89e2eee637ea8af88693cd9..03f0c1d909a9bc516c5006cb4f8805c250e060bf 100644
3+
--- a/scripts/webpack/prerender.js
4+
+++ b/scripts/webpack/prerender.js
5+
@@ -1,10 +1,44 @@
6+
const React = require('react');
7+
const ReactDOMServer = require('react-dom/server');
8+
const { ServerLocation } = require('@reach/router');
9+
-const ssrPrepass = require('react-ssr-prepass');
10+
// react, react-dom, and @reach/router are all EXCLUDED from the ssr-bundle.js
11+
// The versions imported above are used instead, which allows us to use <ServerLocation>
12+
-// const ssrPrepass = require('react-ssr-prepass');
13+
+
14+
+// Let pending dynamic imports (AsyncComponent.preload) settle before re-rendering
15+
+const flushAsync = () => new Promise((resolve) => setImmediate(() => setImmediate(resolve)));
16+
+
17+
+// The loading state rendered by asyncComponentFactory while a page chunk loads
18+
+const LOADING_MARKER = 'style="height:100vh">Loading...';
19+
+
20+
+// react-ssr-prepass walked the element tree to await AsyncComponent.preload(), but it
21+
+// relies on React internals that no longer exist in React 19. AsyncComponent already
22+
+// kicks off preload() from its loading-state render, so rendering to a fixpoint with
23+
+// plain renderToString preloads the same chunks using only public React APIs.
24+
+let prerenderCount = 0;
25+
+
26+
+async function renderToStringWithPreload(element) {
27+
+ const maxPasses = 4;
28+
+ const start = Date.now();
29+
+ let passes = 1;
30+
+ let html = ReactDOMServer.renderToString(element);
31+
+ if (++prerenderCount % 25 === 0) {
32+
+ console.log(` [prerender #${prerenderCount}] heap=${Math.round(process.memoryUsage().heapUsed / 1048576)}MB`);
33+
+ }
34+
+
35+
+ for (; passes < maxPasses && html.includes(LOADING_MARKER); passes++) {
36+
+ // The first render has kicked off preload(); give the dynamic imports a chance
37+
+ // to settle, then render again until no loading placeholder is left.
38+
+ await (passes === 1 ? flushAsync() : new Promise((resolve) => setTimeout(resolve, 50)));
39+
+ html = ReactDOMServer.renderToString(element);
40+
+ }
41+
+
42+
+ const elapsed = Date.now() - start;
43+
+ if (elapsed > 5000 || html.includes(LOADING_MARKER)) {
44+
+ console.log(` rendered in ${passes} passes, ${elapsed}ms${html.includes(LOADING_MARKER) ? ' (loading placeholder left!)' : ''}`);
45+
+ }
46+
+
47+
+ return html;
48+
+}
49+
50+
// This function is effectively synchronous because it mutates global.setTimeout
51+
// Only allow one copy at a time to run
52+
@@ -44,12 +78,7 @@ async function prerender(url) {
53+
const WrappedApp = React.createElement(ServerLocation, { url },
54+
React.createElement(App)
55+
);
56+
- await ssrPrepass(WrappedApp, element => {
57+
- if (element.type.name === 'AsyncComponent') {
58+
- return element.type.preload();
59+
- }
60+
- });
61+
- const string = ReactDOMServer.renderToString(WrappedApp);
62+
+ const string = await renderToStringWithPreload(WrappedApp);
63+
64+
return string;
65+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
]
125125
},
126126
"resolutions": {
127-
"dompurify": "3.4.13"
127+
"dompurify": "3.4.13",
128+
"@patternfly/documentation-framework": "patch:@patternfly/documentation-framework@npm%3A6.49.2#~/.yarn/patches/@patternfly-documentation-framework-npm-6.49.2-02e7a8d840.patch"
128129
}
129130
}

yarn.lock

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4963,7 +4963,7 @@ __metadata:
49634963
languageName: node
49644964
linkType: hard
49654965

4966-
"@patternfly/documentation-framework@npm:^6.40.0":
4966+
"@patternfly/documentation-framework@npm:6.49.2":
49674967
version: 6.49.2
49684968
resolution: "@patternfly/documentation-framework@npm:6.49.2"
49694969
dependencies:
@@ -5039,6 +5039,82 @@ __metadata:
50395039
languageName: node
50405040
linkType: hard
50415041

5042+
"@patternfly/documentation-framework@patch:@patternfly/documentation-framework@npm%3A6.49.2#~/.yarn/patches/@patternfly-documentation-framework-npm-6.49.2-02e7a8d840.patch":
5043+
version: 6.49.2
5044+
resolution: "@patternfly/documentation-framework@patch:@patternfly/documentation-framework@npm%3A6.49.2#~/.yarn/patches/@patternfly-documentation-framework-npm-6.49.2-02e7a8d840.patch::version=6.49.2&hash=e7d433"
5045+
dependencies:
5046+
"@babel/core": "npm:^7.29.0"
5047+
"@babel/preset-env": "npm:7.29.0"
5048+
"@babel/preset-react": "npm:^7.28.5"
5049+
"@mdx-js/util": "npm:1.6.22"
5050+
"@patternfly/ast-helpers": "npm:^1.26.2"
5051+
"@reach/router": "npm:@gatsbyjs/reach-router@1.3.9"
5052+
"@rspack/core": "npm:^1.7.7"
5053+
"@rspack/dev-server": "npm:^1.2.1"
5054+
autoprefixer: "npm:10.4.27"
5055+
babel-loader: "npm:^10.0.0"
5056+
camelcase-css: "npm:2.0.1"
5057+
chokidar: "npm:4.0.3"
5058+
codesandbox: "npm:2.2.3"
5059+
commander: "npm:4.1.1"
5060+
css-loader: "npm:6.11.0"
5061+
detab: "npm:2.0.4"
5062+
express: "npm:4.22.1"
5063+
file-loader: "npm:6.2.0"
5064+
file-saver: "npm:1.3.8"
5065+
fs-extra: "npm:9.1.0"
5066+
glob: "npm:12.0.0"
5067+
handlebars: "npm:4.7.8"
5068+
hast-to-hyperscript: "npm:9.0.1"
5069+
hast-util-to-text: "npm:2.0.1"
5070+
html-formatter: "npm:0.1.9"
5071+
html-webpack-plugin: "npm:5.6.6"
5072+
js-yaml: "npm:4.1.1"
5073+
mdast-util-to-hast: "npm:9.1.2"
5074+
mdurl: "npm:1.0.1"
5075+
null-loader: "npm:4.0.1"
5076+
parse-entities: "npm:2.0.0"
5077+
path-browserify: "npm:1.0.1"
5078+
postcss: "npm:^8.5.8"
5079+
postcss-loader: "npm:7.3.4"
5080+
process: "npm:^0.11.10"
5081+
puppeteer: "npm:^24.37.5"
5082+
puppeteer-cluster: "npm:^0.25.0"
5083+
react-docgen: "npm:6.0.4"
5084+
react-ssr-prepass: "npm:1.6.0"
5085+
remark-footnotes: "npm:1.0.0"
5086+
remark-frontmatter: "npm:2.0.0"
5087+
remark-mdx: "npm:2.0.0-next.8"
5088+
remark-mdxjs: "npm:2.0.0-next.8"
5089+
remark-parse: "npm:8.0.3"
5090+
remark-squeeze-paragraphs: "npm:4.0.0"
5091+
sharp: "npm:0.34.5"
5092+
style-to-object: "npm:0.4.4"
5093+
to-vfile: "npm:6.1.0"
5094+
typedoc: "npm:0.23.28"
5095+
typescript: "npm:4.9.5"
5096+
unified: "npm:9.2.2"
5097+
unist-util-remove: "npm:2.1.0"
5098+
unist-util-visit: "npm:2.0.3"
5099+
url-loader: "npm:4.1.1"
5100+
vfile-reporter: "npm:6.0.2"
5101+
webpack-bundle-analyzer: "npm:5.2.0"
5102+
webpack-dev-server: "npm:5.2.3"
5103+
webpack-merge: "npm:5.10.0"
5104+
peerDependencies:
5105+
"@patternfly/patternfly": ^6.6.0
5106+
"@patternfly/react-code-editor": ^6.6.0
5107+
"@patternfly/react-core": ^6.6.0
5108+
"@patternfly/react-icons": ^6.6.0
5109+
"@patternfly/react-table": ^6.6.0
5110+
react: ^17.0.0 || ^18.0.0
5111+
react-dom: ^17.0.0 || ^18.0.0
5112+
bin:
5113+
pf-docs-framework: scripts/cli/cli.js
5114+
checksum: 10c0/315007bd0c1b780c3f789027c8443267e30564fc4b6fc31f14d06dca1cbed8907b7b5bfa006c3149803365c58ee1380df09f5f10e0994426771bb39516418ef3
5115+
languageName: node
5116+
linkType: hard
5117+
50425118
"@patternfly/patternfly-a11y@npm:5.2.1":
50435119
version: 5.2.1
50445120
resolution: "@patternfly/patternfly-a11y@npm:5.2.1"

0 commit comments

Comments
 (0)