sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
79.93k stars 4.25k forks source link

Svelte 5 next.179+: SSR regression #13115

Closed jamesst20 closed 2 months ago

jamesst20 commented 2 months ago

Edit: The described bug has been resolved, but there is a new one. It might be faster to simply skip to this comment: https://github.com/sveltejs/svelte/issues/13115#issuecomment-2330053369

Describe the bug

For some reasons, starting with Svelte 5 build 179 up to the last I have tested (next.241) I am getting this error when using SSR:

Screenshot 2024-08-31 at 6 09 07 PM

I have tried to pin point what in the DOM could be causing this but I was unsuccessful. Sometimes only wrapping something in a div trigger the error.

Reproduction

ssr-reproduction-bug.zip

npm install -f
npm run dev

Svelte 5 build 178 works perfectly. Svelte 5 build 179 we get a first render then the error Svelte 5 build 180+ blank page identical error

Logs

No response

System Info

System:
    OS: macOS 14.5
    CPU: (12) arm64 Apple M3 Pro
    Memory: 91.00 MB / 18.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.16.0 - ~/.nvm/versions/node/v20.16.0/bin/node
    Yarn: 1.22.22 - ~/.nvm/versions/node/v20.16.0/bin/yarn
    npm: 10.8.1 - ~/.nvm/versions/node/v20.16.0/bin/npm
    pnpm: 9.7.0 - ~/.nvm/versions/node/v20.16.0/bin/pnpm
  Browsers:
    Chrome: 128.0.6613.114
    Safari: 17.5
  npmPackages:
    svelte: 5.0.0-next.178 => 5.0.0-next.178

Severity

blocking an upgrade

Conduitry commented 2 months ago

Please provide a smaller reproduction. There are a ton of things going on here.

As an initial guess without looking at any details, are you running the SSR output through an HTML minifier and/or stripping comments? Server-rendered comments are necessary in order to be able to properly hydrate in the browser.

jamesst20 commented 2 months ago

Please provide a smaller reproduction. There are a ton of things going on here.

As an initial guess without looking at any details, are you running the SSR output through an HTML minifier and/or stripping comments? Server-rendered comments are necessary in order to be able to properly hydrate in the browser.

Thanks @Conduitry for the quick reply. How do we build a smaller reproductible exemple with SSR? Got any templates for this?

What I can do is give you the output though. Comments don't appear to be stripped

source_ssr.html.txt source_full_response.html.txt

SSR output Screenshot 2024-09-03 at 9 00 44 AM

Browser output Screenshot 2024-09-03 at 9 01 01 AM

SSR disabled output Screenshot 2024-09-03 at 9 02 27 AM

Inertia expects an empty body with only a <div id=app that includes a data-page attribute for the router information. Based on this it runs Svelte app.

dummdidumm commented 2 months ago

How do we build a smaller reproductible exemple with SSR? Got any templates for this?

You can use npm create svelte@latest, then select "skeleton project" and the Svelte 5 option, then add the Svelte components that reproduce this (import them into +page.svelte)

jamesst20 commented 2 months ago

@dummdidumm @Conduitry Here is a minimal reproduction bug

1. Extract zip
2. npm install
3. npm run dev

You will see it works just fine. Now upgrade Svelte and it's broken

ssr-reproduction-bug.zip

Edit: Updated first post too

paoloricciuti commented 2 months ago

I started exploring this a bit but got stuck and had to leave the desk. It seems that it's trying to hydrate an empty text node but that's the only thing i discovered so far.

trueadm commented 2 months ago

So trying to look into this, where is your HTML generated for SSR? I ask because it's generating HTML without a <body> element Screenshot 2024-09-04 at 15 14 13

This will completely break Svelte 5 as it uses opening and closing comment markers to mark where hydration starts and ends – but because you didn't put in a <body> the browser puts it in for you – after the first hydration marker.

Update: I think your server.js is wrong, and the line should be

const html = template
      .replace(`<!--app-head-->`, rendered.head?.[0] ?? "")
-      .replace(templateBody, rendered.body ?? "");
+      .replace(templateBody, `<body>${rendered.body ?? ""}</body>`);

There's still another issue, looking into that too.

trueadm commented 2 months ago

So the culprit here looks to be the wonky stuff that svelte-inertia is doing, which breaks with Svelte 5:

https://github.com/inertiajs/inertia/blob/master/packages/svelte/src/lib/components/Render.svelte

In fact, svelte-inertia seems to be doing lots odd things that simply won't work with Svelte 5, it would be good if that package was updated to properly support Svelte 5. It looks like it doesn't even process the same components during SSR as it does during hydration:

https://github.com/inertiajs/inertia/blob/master/packages/svelte/src/lib/createInertiaApp.ts#L31-L90

Which means that our special comment markers will be missing from the SSR output – meaning that we incorrectly try and match the current hydration DOM node up to the wrong piece of content (as we're expecting comments to be there for blocks, but instead we get actual elements instead).

I think there is still an issue with Svelte 5 here too though as at best we should be capturing a hydration mismatch issue here. I wonder if we this sequence of blocks is enough to break the compiler:

{#if store.component}
  {#key key}
    <svelte:component this={component} {...props}>
      {#each childComponents as child, index (component && component.length === index ? store.key : null)}
        <svelte:self {...child} />
      {/each}
    </svelte:component>
  {/key}
{/if}

Update 2: digging more into to above, it seems that the if block is the culprit for this error:

<!-- {#if store.component} -->
  {#key key}
    <svelte:component this={component} {...props}>
      {#each childComponents as child, index (component && component.length === index ? store.key : null)}
        <svelte:self {...child} />
      {/each}
    </svelte:component>
  {/key}
<!-- {/if} -->

Making that change fixes the issue entirely. The HTML output between the two is also interesting:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module" src="/@vite/client"></script>

    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Svelte + TS</title>
    <script type="module" src="/src/entry-client.ts"></script>
    <!--[-->
    <!--]-->
    <title>Home - SSR Reproduction</title>
  </head>
  <!--[-->
  <div
    data-server-rendered="true"
    id="app"
    data-page='{"component":"Home","props":{"appName":"SSR Reproduction","users":[{"id":1,"name":"Test","email":"test@example.com"}]},"url":"http://localhost:5173/","version":"12345"}'
  >
    <!--[--><!----><!---->
    <nav class="flex items-center space-x-6 bg-slate-800 px-10 py-6 text-white">
      <div class="rounded-lg bg-slate-700 px-4 py-1">SSR Reproduction</div>
      <a href="/" class="hover:underline">Home</a>
      <a href="/users" class="hover:underline">Users</a>
      <a href="/article" class="hover:underline">Article</a>
      <a href="/form" class="hover:underline">Form</a>
      <button type="button" class="hover:underline">Logout</button>
    </nav>
    <main class="px-10 py-8">
      <!----><!--[--><!--[--><!----><!---->
      <h1 class="text-3xl">Home</h1>
      <p class="mt-6">
        <a href="/article#far-down" class="text-blue-700 underline"
          >Link to bottom of article page</a
        >
      </p>
      <!----><!----><!--]--><!----><!--]--><!---->
    </main>
    <!----><!----><!--]--><!----><!---->
  </div>
  <!--]-->
</html>

With if block removed:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module" src="/@vite/client"></script>

    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Svelte + TS</title>
    <script type="module" src="/src/entry-client.ts"></script>
    <!--[-->
    <!--]-->
    <title>Home - SSR Reproduction</title>
  </head>
  <!--[-->
  <div
    data-server-rendered="true"
    id="app"
    data-page='{"component":"Home","props":{"appName":"SSR Reproduction","users":[{"id":1,"name":"Test","email":"test@example.com"}]},"url":"http://localhost:5173/","version":"12345"}'
  >
    <!----><!---->
    <nav class="flex items-center space-x-6 bg-slate-800 px-10 py-6 text-white">
      <div class="rounded-lg bg-slate-700 px-4 py-1">SSR Reproduction</div>
      <a href="/" class="hover:underline">Home</a>
      <a href="/users" class="hover:underline">Users</a>
      <a href="/article" class="hover:underline">Article</a>
      <a href="/form" class="hover:underline">Form</a>
      <button type="button" class="hover:underline">Logout</button>
    </nav>
    <main class="px-10 py-8">
      <!----><!--[--><!----><!---->
      <h1 class="text-3xl">Home</h1>
      <p class="mt-6">
        <a href="/article#far-down" class="text-blue-700 underline"
          >Link to bottom of article page</a
        >
      </p>
      <!----><!----><!----><!--]--><!---->
    </main>
    <!----><!----><!----><!---->
  </div>
  <!--]-->
</html>
jamesst20 commented 2 months ago

So the culprit here looks to be the wonky stuff that svelte-inertia is doing, which breaks with Svelte 5:

https://github.com/inertiajs/inertia/blob/master/packages/svelte/src/lib/components/Render.svelte

In fact, svelte-inertia seems to be doing lots odd things that simply won't work with Svelte 5, it would be good if that package was updated to properly support Svelte 5. It looks like it doesn't even process the same components during SSR as it does during hydration:

https://github.com/inertiajs/inertia/blob/master/packages/svelte/src/lib/createInertiaApp.ts#L31-L90

Which means that our special comment markers will be missing from the SSR output – meaning that we incorrectly try and match the current hydration DOM node up to the wrong piece of content (as we're expecting comments to be there for blocks, but instead we get actual elements instead).

I think there is still an issue with Svelte 5 here too though as at best we should be capturing a hydration mismatch issue here. I wonder if we this sequence of blocks is enough to break the compiler:

{#if store.component}
  {#key key}
    <svelte:component this={component} {...props}>
      {#each childComponents as child, index (component && component.length === index ? store.key : null)}
        <svelte:self {...child} />
      {/each}
    </svelte:component>
  {/key}
{/if}

Update 2: digging more into to above, it seems that the if block is the culprit for this error:

<!-- {#if store.component} -->
  {#key key}
    <svelte:component this={component} {...props}>
      {#each childComponents as child, index (component && component.length === index ? store.key : null)}
        <svelte:self {...child} />
      {/each}
    </svelte:component>
  {/key}
<!-- {/if} -->

Making that change fixes the issue entirely.

Thanks for the quick investigation @trueadm :)

My reproduction exemple use my own fork of Inertia because I have converted the components to be Svelte 5 so the proper source would be https://github.com/jamesst20/inertia/blob/main/packages/svelte/src/lib/components/Render.svelte

What should be done instead? I could investigate further why the component could be null, but the createInertiaApp(...) signature I believe expects null components to be valid I guess if only props update are expected and the router shouldn't act.

trueadm commented 2 months ago

@jamesst20 Actually, I noticed that the above commenting out of the if block still hits a hydration mismatch, except you are not passing recover: false to the hydrate call in entry-client.ts so the app just gets scrapped and rebuilt on the client. Looking into it more, it seems you're also setting the hydration target to be div#app but the server rendered content starts outside of the this div?

So I think the server.js replacement should be:

    const html = template
      .replace(`<!--app-head-->`, rendered.head?.[0] ?? "")
      .replace('<!--app-body-->', rendered.body ?? "");

Then in your hbs you need an <!--app-body--> comment to replace it with.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Svelte + TS</title>
    <script type="module" src="/src/entry-client.ts"></script>
    <!--app-head-->
  </head>
  <body>
    <div id="app" data-page='{{ page }}'><!--app-body--></div>
  </body>
</html>

However, that doesn't seem to work and I can't tell why as the whole thing is wonky. It also seems like we also render another element with the id of app inside the <!--app-body--> that comes from somewhere – which doesn't seem right either.

It's so hard to debug what's going on because of the odd way this project is setup. However, the target you provide hydrate must be an exclusive element with all the SSR content within that element. Part of the problem with this case is that this element isn't exclusive and there seems to be lots of odd things around that. If you can fix these issues, then it should make debugging the hydration failing cases easier.

sukeshpabolu commented 2 months ago

Hi @trueadm I am facing this exact issue after sveltekit svelte 5.0.0-next.178 and with ssr w.r.t web components. This is the repro. You can find bundled web component in static folder

jamesst20 commented 2 months ago

@jamesst20 Actually, I noticed that the above commenting out of the if block still hits a hydration mismatch, except you are not passing recover: false to the hydrate call in entry-client.ts so the app just gets scrapped and rebuilt on the client. Looking into it more, it seems you're also setting the hydration target to be div#app but the server rendered content starts outside of the this div?

So I think the server.js replacement should be:

    const html = template
      .replace(`<!--app-head-->`, rendered.head?.[0] ?? "")
      .replace('<!--app-body-->', rendered.body ?? "");

Then in your hbs you need an <!--app-body--> comment to replace it with.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Svelte + TS</title>
    <script type="module" src="/src/entry-client.ts"></script>
    <!--app-head-->
  </head>
  <body>
    <div id="app" data-page='{{ page }}'><!--app-body--></div>
  </body>
</html>

However, that doesn't seem to work and I can't tell why as the whole thing is wonky. It also seems like we also render another element with the id of app inside the <!--app-body--> that comes from somewhere – which doesn't seem right either.

It's so hard to debug what's going on because of the odd way this project is setup. However, the target you provide hydrate must be an exclusive element with all the SSR content within that element. Part of the problem with this case is that this element isn't exclusive and there seems to be lots of odd things around that. If you can fix these issues, then it should make debugging the hydration failing cases easier.

I'm really sorry about the reproduction exemple. It's very possible it is a little off as it is not a real world usage at all, I was required to provide a smaller reproduction exemple. A real world demo that is supposed to be accurate is https://github.com/jamesst20/inertia/tree/main/playgrounds/svelte if you check first post edits, I had given instructions on how to test it

I believe one of your question the answer lies here: https://github.com/jamesst20/inertia/blob/main/packages/svelte/src/lib/components/SSR.svelte#L13 and here https://github.com/jamesst20/inertia/blob/main/packages/svelte/src/lib/createInertiaApp.ts#L49

I don't know why Inertia works this way but the way it is meant to work is

  1. When SSR is disabled, the response is supposed to provide <div id="app" data-page="...">. That div is given by the backend itself.
  2. When SSR is enabled, the response is supposed to be identical with an extra data-server-rendered="true". That div is given by the Svelte component "SSR" which render as a child the "App" component.
trueadm commented 2 months ago

Hi @trueadm I am facing this exact issue after sveltekit svelte 5.0.0-next.178 and with ssr w.r.t web components. This is the repro. You can find bundled web component in static folder

So looking into this, and it appears the issue is you have a <style> element as the first child of the custom-select element? Svelte doesn't expect your custom element to have any children other than the ones it puts inside it, so this will break. If you remove the <style> element you add in custom-comp.js then everything works as expected.

paoloricciuti commented 2 months ago

Hi @trueadm I am facing this exact issue after sveltekit svelte 5.0.0-next.178 and with ssr w.r.t web components. This is the repro. You can find bundled web component in static folder

So looking into this, and it appears the issue is you have a <style> element as the first child of the custom-select element? Svelte doesn't expect your custom element to have any children other than the ones it puts inside it, so this will break. If you remove the <style> element you add in custom-comp.js then everything works as expected.

This feels like a different bug but a bug nonetheless...the custom element is not from svelte so this seems something we should probably address right if we want custom elements to work in general

trueadm commented 2 months ago

This feels like a different bug but a bug nonetheless...the custom element is not from svelte so this seems something we should probably address right if we want custom elements to work in general

I think a new issue around this would be good then, as Svelte 5 is working as intended here. If someone mutates the DOM outside of Svelte's knowledge, then things are obviously going to break because we now create templates from their code. If they put a <style></style> element in their Svelte template and then reference it in the custom element, then that too should work.

trueadm commented 2 months ago

I'm really sorry about the reproduction exemple. It's very possible it is a little off as it is not a real world usage at all, I was required to provide a smaller reproduction exemple. A real world demo that is supposed to be accurate is https://github.com/jamesst20/inertia/tree/main/playgrounds/svelte if you check first post edits, I had given instructions on how to test it

I believe one of your question the answer lies here: https://github.com/jamesst20/inertia/blob/main/packages/svelte/src/lib/components/SSR.svelte#L13 and here https://github.com/jamesst20/inertia/blob/main/packages/svelte/src/lib/createInertiaApp.ts#L49

I don't know why Inertia works this way but the way it is meant to work is

  1. When SSR is disabled, the response is supposed to provide <div id="app" data-page="...">. That div is given by the backend itself.
  2. When SSR is enabled, the response is supposed to be identical with an extra data-server-rendered="true". That div is given by the Svelte component "SSR" which render as a child the "App" component.

I was unable to get it working – sorry. Would it be possible for you to try without Inertia and using something like SvelteKit?

jamesst20 commented 2 months ago

@trueadm

Not sure if this made that clear, but if I were to do

const html = template
      .replace(`<!--app-head-->`, rendered.head?.[0] ?? "")
      .replace('<!--app-body-->', rendered.body ?? "");
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Svelte + TS</title>
    <script type="module" src="/src/entry-client.ts"></script>
    <!--app-head-->
  </head>
  <body>
    <div id="app" data-page='{{ page }}'><!--app-body--></div>
  </body>
</html>

because the SSR renders the Svelte SSR component which already includes the <div id="app" ... it would end up being

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Svelte + TS</title>
    <script type="module" src="/src/entry-client.ts"></script>
    <!--app-head-->
  </head>
  <body>
    <div id="app" data-page='...'>
       <div id="app" data-server-rendered="true" data-page='...'>
              some html rendered content
       </div>
    </div>
  </body>
</html>

this is why I am doing this in the reproduction exemple, I replace the whole body content

    const rendered = await render(
      await res.inertiaObject(component, { appName: "SSR Reproduction", users })
    );

    const templateBody = parse(template).querySelector("body").toString();

    const html = template
      .replace(`<!--app-head-->`, rendered.head?.[0] ?? "")
      .replace(templateBody, rendered.body ?? "");
SukeshP1995 commented 2 months ago

This feels like a different bug but a bug nonetheless...the custom element is not from svelte so this seems something we should probably address right if we want custom elements to work in general

I think a new issue around this would be good then, as Svelte 5 is working as intended here. If someone mutates the DOM outside of Svelte's knowledge, then things are obviously going to break because we now create templates from their code. If they put a <style></style> element in their Svelte template and then reference it in the custom element, then that too should work.

Shall I create a new issue?

paoloricciuti commented 2 months ago

This feels like a different bug but a bug nonetheless...the custom element is not from svelte so this seems something we should probably address right if we want custom elements to work in general

I think a new issue around this would be good then, as Svelte 5 is working as intended here. If someone mutates the DOM outside of Svelte's knowledge, then things are obviously going to break because we now create templates from their code. If they put a <style></style> element in their Svelte template and then reference it in the custom element, then that too should work.

Shall I create a new issue?

Yes please 🙏🏻

trueadm commented 2 months ago

Shall I create a new issue?

Please.

@jamesst20

Not sure if this made that clear, but if I were to do

So you can't do that. The content that Svelte renders, the <div id="app" .. is also wrapped in hydration markers, which are indicators when to start hydrating. However, you're calling hydrate with the reference to the div#app element?! You can't hydrate an element that is part of the hydrated output, it needs to be completely isolated from it. This is likely the main culprit for why you're experiencing all these issues.

You should never have Svelte to render the HTML for the element that you intend to use as your entry point into your app.

SukeshP1995 commented 2 months ago

This feels like a different bug but a bug nonetheless...the custom element is not from svelte so this seems something we should probably address right if we want custom elements to work in general

I think a new issue around this would be good then, as Svelte 5 is working as intended here. If someone mutates the DOM outside of Svelte's knowledge, then things are obviously going to break because we now create templates from their code. If they put a <style></style> element in their Svelte template and then reference it in the custom element, then that too should work.

Shall I create a new issue?

Yes please 🙏🏻

Done

https://github.com/sveltejs/svelte/issues/13133

jamesst20 commented 2 months ago

Shall I create a new issue?

Please.

@jamesst20

Not sure if this made that clear, but if I were to do

So you can't do that. The content that Svelte renders, the <div id="app" .. is also wrapped in hydration markers, which are indicators when to start hydrating. However, you're calling hydrate with the reference to the div#app element?! You can't hydrate an element that is part of the hydrated output, it needs to be completely isolated from it. This is likely the main culprit for why you're experiencing all these issues.

You should never have Svelte to render the HTML for the element that you intend to use as your entry point into your app.

I have removed Inertia as a dependency and written simplified code to mimic only necessary part ssr-reproduction-bug-simplified.zip

There is a boolean in server.js to toggle ON/OFF SSR but you can also use ssr=false/true as a get parameter.

I created a different html file for ssr and for normal rendering.

About your explanation, I am not really sure if it is something I don't see or understand or something you might have not seen about inertia. I hope the example clarifies it.

Edit: Ok I think I get your explanation I am investigating further now

jamesst20 commented 2 months ago

@trueadm

You were right about the markers. Because the <div id="app" ...> was rendered by a Svelte component during SSR but not when the response is generated by the backend, in the SSR scenarios there were markers outside that div and this is what caused the issue.

I wrote a fix here to ensure only the content of the div is rendered by Svelte https://github.com/jamesst20/inertia/commit/c8493ab9358fb520461940ef7f043987f0b39d17

It now renders properly, but I still get an hydratation error missmatch

App.svelte:19 [svelte] hydration_mismatch
Hydration failed because the initial UI does not match what was rendered on the server

By looking at the before/after, I can see there are markers at the end in the SSR response but not after hydratation. Do you have any idea? Breakpoint is before hydratation is called

Screenshot 2024-09-04 at 4 48 42 PM

Screenshot 2024-09-04 at 4 48 53 PM

trueadm commented 2 months ago

Thats good, I can look into that tomorrow. Is the repro updated to reflect your changes?

jamesst20 commented 2 months ago

Thats good, I can look into that tomorrow. Is the repro updated to reflect your changes?

This repo is reflecting yes:

(Have a compatible PHP version installed like 8.1)

1. git clone https://github.com/jamesst20/inertia.git
2. cd inertia
3. pnpm install
4. pnpm run build:all
5. cd playgrounds/svelte
6. cp .env.example .env
7. composer install
8. php artisan keys:generate
9. Terminal 1: php artisan serve
10. Terminal 2: pnpm run dev
11. Terminal 3: pnpm run build && php artisan inertia:start-ssr
12. Visit http://localhost:8000/

It really is odd, I tried emptying the layout to keep only <slot /> and fully emptying the Home.svelte and I still get the same warning about the mismatch. I added a debugger breakpoint just beforehydrate(...) is called and copy pasted from chrome inspector the DOM to have a comparison of before/after.

Here is how it looks: Left before, Right after. (Both formatted with Prettier for readability) Screenshot 2024-09-04 at 8 24 14 PM

The only difference I see is the Svelte comments (anchors)

Edit: You can set in both ssr.js/app.js progress: false to the createInertiaApp({ ...here }) call to remove the added CSS and stuff to the <head>

jamesst20 commented 2 months ago

@trueadm

Have you had the chance to take a look? Should I create a new issue and close this one as I believe it might be unrelated?

trueadm commented 2 months ago

@jamesst20 I'm still digging into it. I'll let you know when I find out more :)

trueadm commented 2 months ago

I get a ton of errors when trying to run php artisan keys:generate from above. I'm using PHP 8.3.11 (cli) (built: Aug 27 2024 19:16:34) (NTS)

PHP Warning: require(/Users/trueadm/projects/inertia/playgrounds/svelte/vendor/autoload.php): Failed to open stream: No such file or directory in /Users/trueadm/projects/inertia/playgrounds/svelte/artisan on line 18

jamesst20 commented 2 months ago

I get a ton of errors when trying to run php artisan keys:generate from above. I'm using PHP 8.3.11 (cli) (built: Aug 27 2024 19:16:34) (NTS)

PHP Warning: require(/Users/trueadm/projects/inertia/playgrounds/svelte/vendor/autoload.php): Failed to open stream: No such file or directory in /Users/trueadm/projects/inertia/playgrounds/svelte/artisan on line 18

This is my bad, after step 6 you need to run composer install

if it says command not found you can also grab it here https://getcomposer.org/download/2.7.9/composer.phar and run instead php composer.phar install

trueadm commented 2 months ago

@jamesst20 Thanks I got it working. When I remove the content from Layout.svelte so its empty, why is the SSR content still including the content? Update: ah, I have to run one the scripts again.

trueadm commented 2 months ago

So it seems like disabling hmr fixes this issue:

 svelte({
      compilerOptions: {
          hmr: false
      }
  }),

Not sure why hmr is causing these issues though, I'll need to dig further.

dummdidumm commented 2 months ago

With HMR turned on, in some places extra comments are added on the client so HMR can replace the components

trueadm commented 2 months ago

Okay, so after digging into this for hours with @dummdidumm it turns out the issue is with laravel-vite-plugin. It doesn't seem to be calling into the same Svelte compiler as the vite-plugin used for the client hydration – in fact it seems completely detached. This is where the issue is though – as the server components are compiled with hmr always disabled and the client compiler runs with it enabled – and this is why you are having your mismatch.

jamesst20 commented 2 months ago

Okay, so after digging into this for hours with @dummdidumm it turns out the issue is with laravel-vite-plugin. It doesn't seem to be calling into the same Svelte compiler as the vite-plugin used for the client hydration – in fact it seems completely detached. This is where the issue is though – as the server components are compiled with hmr always disabled and the client compiler runs with it enabled – and this is why you are having your mismatch.

What should be done? I must admit I do not use Laravel in my own projects, I use Inertia with Ruby on Rails but I did my testing with the closest playground I had which was the Laravel one. Should we open an issue here? https://github.com/laravel/vite-plugin I wouldn't know what to say though

trueadm commented 2 months ago

I honestly don't know enough about laravel-vite-plugin either. It seems to not be using the @sveltejs/vite-plugin-svelte plugin though, as if I put a console.log inside the vite-plugin-svelte in node_modules and check for ssr, then it's never run with true. So I'm utterly confused as to how laravel-vite-plugin is working.

dummdidumm commented 2 months ago

Maybe it's this line that's setting hmr to false for Vite, but only on the server? And because nothing is set on the client v-p-s sets it true there?

https://github.com/laravel/vite-plugin/blob/7252168213fa838a1df5f4d893cfca1cc465e6a6/src/index.ts#L162-L165

cc @dominikg

jamesst20 commented 2 months ago

I have created an issue at the plugin repository just in case they can provide some insights about this issue as it doesn't appear Svelte is at fault? https://github.com/laravel/vite-plugin/issues/304

dominikg commented 2 months ago

Maybe it's this line that's setting hmr to false for Vite, but only on the server? And because nothing is set on the client v-p-s sets it true there?

https://github.com/laravel/vite-plugin/blob/7252168213fa838a1df5f4d893cfca1cc465e6a6/src/index.ts#L162-L165

cc @dominikg

vps sets hmr here

https://github.com/sveltejs/vite-plugin-svelte/blob/9be5d4bfcbac36235fd46dfcc91807f43534c9b3/packages/vite-plugin-svelte/src/utils/options.js#L202

server refers to the vite devserver here so there is no separate client.hmr option. It is possible that the two plugins are incompatible right now because they both try to manage the hmr option for the user.

edit: vps does not set viteConfig.server.hmr, it just reads it. Maybe try running the setup with DEBUG=vite-plugin-svelte:* to see what it uses exactly.

trueadm commented 2 months ago

Closing this as I feel like there's a solution linked from https://github.com/laravel/vite-plugin/issues/304.