Open renyuneyun opened 6 months ago
You should add the pinia
package to your app dependencies.
This lead to the circular object issue which is related to store state hydration. There is some circular references in the Session object.
When pre-rendering at server side, the store state is serialized and inlined in the page markup. Later when the page is rendered at client-side, the store is primed with server-initialized state.
The issue happens when serializing the state.
Anyway, I assume you don't want to initialize the client-side store state with a Session instantiated during SSR, so you can proceed as follows:
// https://github.com/renyuneyun/solid-helper-vue/blob/master/src/stores/session.ts
/**
* The Session object. Reactivity is lost. Useful mainly for its functions (e.g. `fetch()`)
*/
session: typeof window !== 'undefined' ? new Session() : undefined, // no Session at server-side
Note that you can also serialize the state manually.
First enable the manualStoreSerialization
option in the quasar.config.js
file:
ssr: {
manualStoreSerialization: true
},
Then create a Quasar boot file:
// src/boot/serialize-state.ts
import { boot } from 'quasar/wrappers'
import { stringify } from 'flatted'; // this package is used to serialize state, it handles circular object,
// You will need to add it to your app dependencies
// "async" is optional;
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
export default boot(async ({ ssrContext }) => {
if(!ssrContext) {
return;
}
ssrContext.onRendered(() => {
// at that time the store state is available in ssrContext.state
// the lines below add a <script> to the pre-rendered markup that injects the serialized state into window.__INITIAL_STATE__
// window.__INITIAL_STATE__ is used at client-side to prime the store
const autoRemove = 'document.currentScript.remove()'
ssrContext._meta.headTags = `<script>window.__INITIAL_STATE__=${stringify(ssrContext.state)};${autoRemove}</script>`+ ssrContext._meta.headTags;
})
})
And only use it server-side:
// quasar.config.js
boot: [{
client: false,
path: 'serialize-state'
}],
Thanks for the explanation a lot. However, after applying the first patch, another (or a related?) Pinia issue is still there:
TypeError: pinia is undefined
useStore pinia.mjs:1705
setup IndexPage.vue:15
callWithErrorHandling runtime-core.esm-bundler.js:158
setupStatefulComponent runtime-core.esm-bundler.js:7331
setupComponent runtime-core.esm-bundler.js:7292
mountComponent runtime-core.esm-bundler.js:5687
hydrateNode runtime-core.esm-bundler.js:4691
hydrateSubTree runtime-core.esm-bundler.js:5763
componentUpdateFn runtime-core.esm-bundler.js:5783
run reactivity.esm-bundler.js:178
update runtime-core.esm-bundler.js:5902
setupRenderEffect runtime-core.esm-bundler.js:5910
mountComponent runtime-core.esm-bundler.js:5700
hydrateNode runtime-core.esm-bundler.js:4691
hydrateChildren runtime-core.esm-bundler.js:4853
hydrateElement runtime-core.esm-bundler.js:4809
hydrateNode runtime-core.esm-bundler.js:4672
hydrateSubTree runtime-core.esm-bundler.js:5763
componentUpdateFn runtime-core.esm-bundler.js:5783
run reactivity.esm-bundler.js:178
update runtime-core.esm-bundler.js:5902
setupRenderEffect runtime-core.esm-bundler.js:5910
mountComponent runtime-core.esm-bundler.js:5700
hydrateNode runtime-core.esm-bundler.js:4691
hydrateChildren runtime-core.esm-bundler.js:4853
hydrateElement runtime-core.esm-bundler.js:4809
hydrateNode runtime-core.esm-bundler.js:4672
hydrateSubTree runtime-core.esm-bundler.js:5763
componentUpdateFn runtime-core.esm-bundler.js:5783
run reactivity.esm-bundler.js:178
update runtime-core.esm-bundler.js:5902
setupRenderEffect runtime-core.esm-bundler.js:5910
mountComponent runtime-core.esm-bundler.js:5700
hydrateNode runtime-core.esm-bundler.js:4691
hydrateSubTree runtime-core.esm-bundler.js:5763
componentUpdateFn runtime-core.esm-bundler.js:5783
run reactivity.esm-bundler.js:178
update runtime-core.esm-bundler.js:5902
setupRenderEffect runtime-core.esm-bundler.js:5910
mountComponent runtime-core.esm-bundler.js:5700
hydrateNode runtime-core.esm-bundler.js:4691
hydrateSubTree runtime-core.esm-bundler.js:5763
componentUpdateFn runtime-core.esm-bundler.js:5783
run reactivity.esm-bundler.js:178
update runtime-core.esm-bundler.js:5902
setupRenderEffect runtime-core.esm-bundler.js:5910
mountComponent runtime-core.esm-bundler.js:5700
hydrateNode runtime-core.esm-bundler.js:4691
hydrateSubTree runtime-core.esm-bundler.js:5763
componentUpdateFn runtime-core.esm-bundler.js:5783
run reactivity.esm-bundler.js:178
update runtime-core.esm-bundler.js:5902
setupRenderEffect runtime-core.esm-bundler.js:5910
mountComponent runtime-core.esm-bundler.js:5700
hydrateNode runtime-core.esm-bundler.js:4691
hydrateSubTree runtime-core.esm-bundler.js:5763
componentUpdateFn runtime-core.esm-bundler.js:5783
run reactivity.esm-bundler.js:178
update runtime-core.esm-bundler.js:5902
setupRenderEffect runtime-core.esm-bundler.js:5910
mountComponent runtime-core.esm-bundler.js:5700
hydrateNode runtime-core.esm-bundler.js:4691
hydrate runtime-core.esm-bundler.js:4555
mount runtime-core.esm-bundler.js:3853
mount runtime-dom.esm-bundler.js:1486
start client-entry.js:72
promise callback*start client-entry.js:70
promise callback* client-entry.js:79
[runtime-core.esm-bundler.js:226:12](http://127.0.0.1:4000/@vue/runtime-core/dist/runtime-core.esm-bundler.js)
Hydration completed but contains mismatches.
This can be reproduced regardless of which variant used in the other repo.
After clicking the line of code (i.e. the link with useStore pinia.mjs:1705
), it jumps to a pinia source file under the helper library (solid-helper-vue/node_modules/pinia/
), rather than a pinia source file under the main/application project. I can confirm that Pinia is installed in the main/application project, and a separate pinia source file exists in node_modules/pinia/
.
(I also tried to remove the node_modules
directory in my filesystem under solid-helper-vue
, which seems to be irrelevant.)
Presumably this is caused because that separate pinia source results in a separate "instance" of pinia which is not properly initialized? It seems somehow the dependency resolution / reproduction in the SSR/SSG is problematic? Or maybe I misconfigured somewhere, which made pinia not fully externalized or not recognized?
The vue
and pinia
packages should not be direct dependencies of your library. You should add them as peerDependencies
. It is the responsibility of the consumer of your library to install them. In your case, the consumer is your Quasar application.
For the @inrupt/solid-client-authn-browser
package, you can add it as a direct dependency of your library, because your library seems to using it internally to provide authentication via actions and exposes the Session via its store state.
It means you should also remove this package from your Quasar app.
You should also add vue
and pinia
to the rollup external option. The @inrupt/solid-client-authn-browser
package should be added to the rollup external option, only for ESM output format, because it will be handled by the consumer via a bundler (vite, webpack, etc...). The only case this package should be bundled in your library (not added to the rollup external option) is for the UMD output format.
Your issue about pinia occurs because the dep is loaded from your library instead of your Quasar app. This is because the dep is resolved locally ../../solid-helpers-vue
where Node.js find a node_modules
folder containing the pinia package. To solve this, I would recommend you to use yalc
for development purposes.
To use your library correctly for SSR, don't forget to change this line:
session: typeof window !== 'undefined' ? new Session() : null
It should be set to null
and not undefined
for SSR, otherwise, the session
property will not be defined when the store state will be primed with the server-initialized state at client-side, therefore it cannot be modified later.
To actually create a new Session instance at client-side, I would recommend you these lines of code:
import { onMounted } from 'vue';
import { useSessionStore } from 'solid-helper-vue';
const sessionStore = useSessionStore();
onMounted(() => {
if (sessionStore.session === null) {
sessionStore.$reset() // reset the state only at client-side when the component is mounted to avoid hydration mismatches
}
})
(This is a further attempt after #375. The previous issue contains useful background, but may not be directly related to this issue.)
I have managed to successfully build a static site from my project. However, I encounter the error related to pinia when opening it in browser.
I have produced a MRE at https://github.com/renyuneyun/minimal-reproduction/tree/5779796a11a816d5e4958baf4f60b893868c28da/quasar-solid-ssg, with instructions in README.
(There is also the circular object issue. Shall I open a separate GH Issue for that?)