vike icon indicating copy to clipboard operation
vike copied to clipboard

Crash during hydration

Open hannessolo opened this issue 3 years ago • 4 comments

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

hannessolo avatar Aug 11 '22 15:08 hannessolo

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).

brillout avatar Aug 11 '22 16:08 brillout

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 } };

hannessolo avatar Aug 11 '22 17:08 hannessolo

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.

brillout avatar Aug 11 '22 19:08 brillout

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

hannessolo avatar Aug 11 '22 20:08 hannessolo

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?

brillout avatar Aug 12 '22 05:08 brillout

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.

brillout avatar Aug 12 '22 05:08 brillout

Let's dig further.

I will try a couple of things and let you know if I find anything insightful

hannessolo avatar Aug 12 '22 06:08 hannessolo

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.

brillout avatar Aug 12 '22 06:08 brillout

~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.

hannessolo avatar Aug 12 '22 06:08 hannessolo

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.

brillout avatar Aug 12 '22 06:08 brillout

Scratch that, I still triggered it somehow.

Let's dig into injectHtmlSnippet.js instead then.

brillout avatar Aug 12 '22 06:08 brillout

(Btw. an alternative would be a reproduction, but I guess that's difficult to achieve.)

brillout avatar Aug 12 '22 06:08 brillout

...
vps:stream user stream ended
vps:stream vps end injection written
vps:stream vps wrapper stream ended

This looks good.

hannessolo avatar Aug 12 '22 06:08 hannessolo

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

hannessolo avatar Aug 12 '22 07:08 hannessolo

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 = []

hannessolo avatar Aug 12 '22 07:08 hannessolo

So you are using https://github.com/brillout/react-streaming, correct? (Scripts are only injected at the beginning when using react-streaming.)

brillout avatar Aug 12 '22 08:08 brillout

Yes, correct

hannessolo avatar Aug 12 '22 08:08 hannessolo

Alright, I think I see what's going on. Let me think of a solution.

brillout avatar Aug 12 '22 08:08 brillout

Thank you, and let me know if there's anything from me that could help (more logs etc).

hannessolo avatar Aug 12 '22 08:08 hannessolo

If it makes a difference, we are rendering to the stream with the disable option set:

const stream = await renderToStream(page, {
    disable: true
});

hannessolo avatar Aug 12 '22 08:08 hannessolo

I didn't try the fix but I believe it should work.

Update both react-streaming and vps.

brillout avatar Aug 12 '22 15:08 brillout

@brillout Thanks for the quick fix! Looks like the issue is gone from a very quick test.

hannessolo avatar Aug 12 '22 15:08 hannessolo

👍 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.

brillout avatar Aug 12 '22 16:08 brillout

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.

hannessolo avatar Aug 12 '22 19:08 hannessolo

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.

brillout avatar Aug 13 '22 06:08 brillout