Crash during hydration
Description
Reporting an error trace as requested in the error message.
"vite": "^3.0.2",
"vite-plugin-ssr": "^0.4.13",
Error Stack
newError newError.ts:13
assert assert.js:25
getPageContextSerializedInHtml getPageContextSerializedInHtml.js:7
getPageContext getPageContext.js:9
hydrate entry.js:9
<anonymous> entry.js:6
Update vps and, if the error still persists, let me know the content of getPageContextSerializedInHtml.js:7 (or the new line number if it changed).
Sorry, I didn't check the package-lock - we are actually already on 0.4.19.
The line that fails is:
const pageContextJson = (_a = document.getElementById('vite-plugin-ssr_pageContext')) === null || _a === void 0 ? void 0 : _a.textContent;
assert(pageContextJson); // <--- This assertion fails
Looking at the HTML, the script with vite-plugin-ssr_pageContext seems to be there, but injected in a weird place:
...[omitted]...
<div data-cmp-lazythreshold="0"
data-cmp-src="...[omitted]..."
data-asset="...[omitted]..."
data-asset-id="...[omitted]..." data-title="...[omitted]..."
data-cmp-data-layer="<script id=" vite-plugin-ssr_pageContext" type="application/json">
{"pageContext":{...[omitted]...}}
</script>
You can see the script was injected into a html attribute.
If I hard refresh the page (cmd+shift+R), the script gets injected properly at the end of the document. It stays that way until I navigate to a different page, or restart the server.
Not sure if this information is useful, but:
- I'm running in dev mode, with vite dev server in middleware mode for express
- I haven't encountered the issue with a production build yet
- Not using client side routing
- We're passing this object in as the page context:
const pageContextInit = { url: req.originalUrl, fetch, customParams: { tenant: appConfig.tenant } };
What does data-cmp-data.layer represent?
Is it possible that the attribute's content is data-cmp-data.layer="</body>..." or data-cmp-data.layer="</html>..."?
That would explain why it's inserted there.
So I deleted the data-cmp-data-layer attribute, and now the script is being placed in a different strange location:
<div>
<button role="button" aria-label="main menu" class="close-icon" aria-controls="closeIcon" data-toggle="button" a<script="" id="vite-plugin-ssr_pageContext" type="application/json">{"pageContext":{ [...omitted...] }}}<script type="module" async="">
import RefreshRuntime from "/@react-refresh"
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
import("/@vite/client");
import("/@fs/Users/hhertach/Documents/code/franklin-mcd-project/node_modules/vite-plugin-ssr/dist/esm/client/entry.js");
</script></button>
</div>
The error changed (I guess it was able to parse the script id this time):
Uncaught (in promise) SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data at line 2 column 1 of the JSON data
parse node_modules/@brillout/json-s/dist/esm/parse.js?v=9bb1d91a:5
getPageContextSerializedInHtml node_modules/vite-plugin-ssr/dist/esm/client/getPageContextSerializedInHtml.js:8
getPageContext node_modules/vite-plugin-ssr/dist/esm/client/getPageContext.js:9
hydrate node_modules/vite-plugin-ssr/dist/esm/client/entry.js:9
<anonymous> node_modules/vite-plugin-ssr/dist/esm/client/entry.js:6
parse.js:5:24
The JSON parsing error is quite insightful. It makes me think that the problem is outside of vps land. Ar you using some kind of HTML processor?
It makes me think that the problem is outside of vps land.
Although since HTML is now most likely invalid, anything could be happening here. I could be wrong with that assumption.
Let's dig further.
Let's dig further.
I will try a couple of things and let you know if I find anything insightful
This is where the injection logic lives/starts: https://github.com/brillout/vite-plugin-ssr/blob/a5b4bd678e06e6d3430750e0be15633e7dd3c665/vite-plugin-ssr/node/html/injectAssets/injectHtmlSnippet.ts#L26-L36
Adding a couple of console.log() at node_modules/vite-plugin-ssr/dist/cjs/node/html/injectAssets/injectHtmlSnippet.js will probably be very insightful.
~I changed the dev server to not use streaming:~
~This was before:~
const pageContext = await renderPage(pageContextInit);
const { httpResponse } = pageContext;
httpResponse.pipe(res);
~I changed it to:~
const pageContext = await renderPage(pageContextInit);
const body = await pageContext.httpResponse.getBody()
const { statusCode, contentType } = httpResponse
res.status(statusCode).type(contentType).send(body)
~The issue no longer occurs this way.~
Scratch that, I still triggered it somehow.
It should always inject at stream end:
https://github.com/brillout/vite-plugin-ssr/blob/a5b4bd678e06e6d3430750e0be15633e7dd3c665/vite-plugin-ssr/node/html/stream.ts#L328-L348
You can activate the debug logs with DEBUG=vps:stream npm run dev.
Scratch that, I still triggered it somehow.
Let's dig into injectHtmlSnippet.js instead then.
(Btw. an alternative would be a reproduction, but I guess that's difficult to achieve.)
...
vps:stream user stream ended
vps:stream vps end injection written
vps:stream vps wrapper stream ended
This looks good.
It should always inject at stream end:
Looks like its injecting at stream start. I added a console log here https://github.com/brillout/vite-plugin-ssr/blob/a5b4bd678e06e6d3430750e0be15633e7dd3c665/vite-plugin-ssr/node/html/injectAssets/injectHtmlSnippet.ts#L15 to verify that the injection is actually the script in question.
This logs
vps:stream render() hook returned `react-streaming` result
vps:stream render() hook returned Node.js Stream Pipe
vps:stream stream begin
injectAtStreamBeginning: Injecting string at begin
injectHtmlSnippet:16: [ Console log of the correct html snippet ]
vps:stream vps begin injection written
vps:stream data written
This looks good.
I logged the htmlSnippetsAtBegin and htmlSnippetsAtEnd variables in https://github.com/brillout/vite-plugin-ssr/blob/a5b4bd678e06e6d3430750e0be15633e7dd3c665/vite-plugin-ssr/node/html/injectAssets.ts#L76.
htmlSnippetsAtBegin = [
{ htmlSnippet: [Function: htmlSnippet], position: 'STREAM' },
{
htmlSnippet: '<script type="module" async>\n' +
'import RefreshRuntime from "/@react-refresh"\n' +
'RefreshRuntime.injectIntoGlobalHook(window)\n' +
'window.$RefreshReg$ = () => {}\n' +
'window.$RefreshSig$ = () => (type) => type\n' +
'window.__vite_plugin_react_preamble_installed__ = true\n' +
'import("/@vite/client");\n' +
'import("/.../node_modules/vite-plugin-ssr/dist/esm/client/entry.js");\n' +
'</script>',
position: 'STREAM'
}
]
htmlSnippetsAtEnd = []
So you are using https://github.com/brillout/react-streaming, correct? (Scripts are only injected at the beginning when using react-streaming.)
Yes, correct
Alright, I think I see what's going on. Let me think of a solution.
Thank you, and let me know if there's anything from me that could help (more logs etc).
If it makes a difference, we are rendering to the stream with the disable option set:
const stream = await renderToStream(page, {
disable: true
});
I didn't try the fix but I believe it should work.
Update both react-streaming and vps.
@brillout Thanks for the quick fix! Looks like the issue is gone from a very quick test.
👍 Let me know if there is anything else.
Btw. I just created a GitHub sponsorship page (waiting for GitHub approval). In case that's something Adobe would be up to.
Thanks, I really appreciate your fast help on these issues.
I'm not the one calling the shots with regards to sponsorship, but I'll make sure to forward the sponsorship page to the team once its up, especially if we go forward with using VPS in a larger project.
Makes sense and not worries if it never happens.
if we go forward with using VPS in a larger project.
As always, let me know if you run into blockers. I often implement (reasonable) feature requests within 24h.