vite-ssg icon indicating copy to clipboard operation
vite-ssg copied to clipboard

Build failing when pinia store is used in modules

Open isokosan opened this issue 2 years ago • 22 comments

Please check this issue here, its either pinia related or vite-ssg - but in any case I couldn't figure out the best way to install global middleware that uses the store, that doesn't throw this error on build.

Any suggestions on how to access the store in a router.beforeEach, which is initialized with the app?

And in case the store uses localStorage initialization which won't be available during generation, any best practices there?

Would be great if one could direct me to an example repo with such middleware.

isokosan avatar Sep 12 '21 20:09 isokosan

It would be better to see how you define your pinia store but I guess can do something like that.

import { defineStore } from 'pinia'

const useAuthStore= defineStore('authStore', {
  state: () => {
    return {
      user: typeof window !== 'undefined' ? localStorage.user : 'guest',
    }
  },
})

Or you can use https://vueuse.org/core/useStorage/

import { defineStore } from 'pinia'
import { useStorage } from '@vueuse/core'

const useAuthStore= defineStore('authStore', {
  state: () => {
    return {
      // 'user' = localStorage.user
      // 'guest' = default value
      user: useStorage('user', 'guest'),
    }
  },
})

sibbng avatar Sep 15 '21 10:09 sibbng

Thanks, that is the way I currently set it up, by checking for the window variable. I'll check out useStorage from @vueuse/core as well, thank you!

My question regarding setting up global router hooks that use pinia stores remains though. I want to define some router.beforeEach and router.afterEach hooks that have access to pinia stores, but when I use useAuthStore for example to look for a user in the beforeEach hook, I get the following error upon build:

I think it would be great if we can show a router middleware implementation using stores, and ideally merge that into the vitesse templates. If you can direct me in the right way I'd be happy to write some examples and submit a pull request there.

Cheers

[Vue Router warn]: uncaught error during route navigation:
TypeError: Cannot read property '_s' of undefined
    at useStore (/home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/pinia/dist/pinia.cjs.js:1544:20)
    at /home/deniz/dev/metalink/metalink-frontend/.vite-ssg-temp/main.js:267:18
    at /home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:1920:35
    at new Promise (<anonymous>)
    at /home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:1894:18
    at /home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:3368:65
TypeError: Cannot read property '_s' of undefined
    at useStore (/home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/pinia/dist/pinia.cjs.js:1544:20)
    at /home/deniz/dev/metalink/metalink-frontend/.vite-ssg-temp/main.js:267:18
    at /home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:1920:35
    at new Promise (<anonymous>)
    at /home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:1894:18
    at /home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:3368:65
 ELIFECYCLE  Command failed with exit code 1.

The pinia module is so:

import { createPinia } from 'pinia'
import type { RouteParams } from 'vue-router'
import { ModuleInstall } from '~/types'
import { useAuthStore } from '~/stores/auth'
import { useLayoutStore } from '~/stores/layout'

// Setup Pinia
// https://pinia.esm.dev/
export const install: ModuleInstall = async ({ isClient, initialState, app, router }) => {
  const pinia = createPinia()
  app.use(pinia)
  // Refer to
  // https://github.com/antfu/vite-ssg/blob/main/README.md#state-serialization
  // for other serialization strategies.
  if (isClient) {
    pinia.state.value = (initialState.pinia) || {}
  } else {
    initialState.pinia = pinia.state.value
  }

  router.beforeEach((to, from): boolean | string | RouteParams => {
    const auth = useAuthStore()
    if (to.meta.redirect) {
      return to.meta.redirect
    }
    if (to.meta.middleware) {
      if (to.meta.middleware === 'auth' && !auth.user) {
        return { name: 'login' }
      }
      if (to.meta.middleware === 'guest' && auth.user) {
        return { name: 'index' }
      }
    }
    return true
  })
  router.afterEach((to, from): void => {
    const layout = useLayoutStore()
    layout.navigationDrawer = false
    layout.sidebar = false
  })
}

isokosan avatar Sep 15 '21 10:09 isokosan

There is nothing wrong with your approach to access pinia store in router hook. Only thing you have to avoid calling browser APIs in your useAuthStore store for build time.

It would be nice to see what is going on that useAuthStore definition and at /home/deniz/dev/metalink/metalink-frontend/.vite-ssg-temp/main.js:267:18 based on this error message.

sibbng avatar Sep 15 '21 12:09 sibbng

The line the error is thrown in .vite-ssh-temp/main.js is the line where we call const auth = useAuthStore();

I removed everything to bare bones as to rule out any other errors so the auth store and the layout store I attempt to import are currently in this state:

~/stores/auth.ts

import type { Ref } from 'vue'
import { acceptHMRUpdate, defineStore } from 'pinia'

export const useAuthStore = defineStore('user', () => {
  const user: Ref<User | null> = ref(null)

  return {
    user
  }
})

if (import.meta.hot) { import.meta.hot.accept(acceptHMRUpdate(useAuthStore, import.meta.hot)) }

~/stores/layout.ts

import type { Ref } from 'vue'
import { acceptHMRUpdate, defineStore } from 'pinia'

export const useLayoutStore = defineStore('layout', () => {
  const navigationDrawer: Ref<boolean> = ref(false)
  const sidebar: Ref<boolean> = ref(false)

  return {
    navigationDrawer,
    sidebar
  }
})

if (import.meta.hot) { import.meta.hot.accept(acceptHMRUpdate(useLayoutStore, import.meta.hot)) }

Others are apparently also having this issue when attempting the build with vite-ssg, you can check this issue over at pinia's issues.

Thanks for looking into it!

isokosan avatar Sep 15 '21 13:09 isokosan

Does it throw error when your stores like that? You shouldn't have any issue with this stores. I guess you removed the actual code that causes an issue.

sibbng avatar Sep 15 '21 13:09 sibbng

No, it does throw the same error. You can try it yourself, all you have to do is call useSomeStore in any module.

isokosan avatar Sep 15 '21 13:09 isokosan

Yeah, I got same error.

I fixed this issue by adding await router.isReady() right before router.beforeEach call. Can you try it?

sibbng avatar Sep 15 '21 15:09 sibbng

Interesting: Just tried it, and still get the same error. Plus from what I understand, the router.isReady waits for the initial navigations to all complete, which wouldn't make sense to do right before a route middleware, or?

TypeError: Cannot read property '_s' of undefined
    at useStore (/home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/pinia/dist/pinia.cjs.js:1544:20)
    at /home/deniz/dev/metalink/metalink-frontend/.vite-ssg-temp/main.js:165:20
    at triggerAfterEach (/home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:3116:13)
    at /home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:3019:13
    at async /home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected]_a05020cb06f51988f10705a8ab55a652/node_modules/vite-ssg/dist/node/cli.js:187:7
    at async Promise.all (index 0)
    at async build (/home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected]_a05020cb06f51988f10705a8ab55a652/node_modules/vite-ssg/dist/node/cli.js:184:3)
    at async Object.handler (/home/deniz/dev/metalink/metalink-frontend/node_modules/.pnpm/[email protected]_a05020cb06f51988f10705a8ab55a652/node_modules/vite-ssg/dist/node/cli.js:267:3)

isokosan avatar Sep 15 '21 20:09 isokosan

Can you show your main.ts?

sibbng avatar Sep 15 '21 20:09 sibbng

@isokosan Please provide a minimal reproduction repo so we can better help.

antfu avatar Sep 15 '21 21:09 antfu

Probably you are trying to access pinia store before it's getting installed or; registered another router hook on other modules.

Try putting await router.isReady() on main.ts like that:

export const createApp = ViteSSG(
  App,
  { routes },
  async(ctx) => {
    await ctx.router.isReady()

    // install all modules under `modules/`
    Object.values(import.meta.globEager('./modules/*.ts')).map(i => i.install?.(ctx))
  },
)

This would fix your issue if you are trying to access pinia store on other modules.

sibbng avatar Sep 15 '21 22:09 sibbng

That was it @sibbng, the ngprogress module from the vitesse starter template was also accessing the router. Thank you so much for your time and support. :) And maybe this is something to document, as it was quite a challenge to debug.

isokosan avatar Sep 16 '21 05:09 isokosan

You're welcome.

If that router hooks defined in ngprogress module wrapped in if statement as here, the issue is not there 👍

sibbng avatar Sep 16 '21 06:09 sibbng

Yeah, then maybe the pwa but that also waits for the router.isReady. If I find some time tomorrow I'll fork the vitesse template and try to demonstrate the issue. Would be awesome to have you take a look, also at my approach at making middleware nuxt style with the globEager imports.

Thanks again mate. :)

isokosan avatar Sep 16 '21 07:09 isokosan

I'm re-opening this issue guys because router.isReady waits for the initial navigation to complete, which is useless when trying to build router middleware, also when I await it the routes don't resolve at all.

Could you please demonstrate an example of how one could implement a router hook that uses a pinia store, which does not fail upon build with the error above? I made a fork from the vitesse template here, with a minimal example of adding a router.beforeEach hook that accesses a pinia store.

isokosan avatar Sep 17 '21 13:09 isokosan

Can you upgrade to latest version and try again without router.isReady. Seems related to https://github.com/antfu/vite-ssg/pull/106

sibbng avatar Sep 17 '21 14:09 sibbng

I have the latest version in the fork, and the build is still failing. The comment I made: "when I await router.isReady() the routes don't resolve at all." seems to be the result of the merge #106, which is ok, but the build still throws the error without router.isReady.

[Vue Router warn]: uncaught error during route navigation:
TypeError: Cannot read property '_s' of undefined
    at useStore (/home/deniz/dev/examples/vitesse/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/pinia/dist/pinia.cjs:1564:20)
    at /home/deniz/dev/examples/vitesse/.vite-ssg-temp/main.js:783:18
    at /home/deniz/dev/examples/vitesse/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:1920:35
    at new Promise (<anonymous>)
    at /home/deniz/dev/examples/vitesse/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:1894:18
    at /home/deniz/dev/examples/vitesse/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:3368:65
TypeError: Cannot read property '_s' of undefined
    at useStore (/home/deniz/dev/examples/vitesse/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/pinia/dist/pinia.cjs:1564:20)
    at /home/deniz/dev/examples/vitesse/.vite-ssg-temp/main.js:783:18
    at /home/deniz/dev/examples/vitesse/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:1920:35
    at new Promise (<anonymous>)
    at /home/deniz/dev/examples/vitesse/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:1894:18
    at /home/deniz/dev/examples/vitesse/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-router/dist/vue-router.cjs.js:3368:65
 ELIFECYCLE  Command failed with exit code 1.

isokosan avatar Sep 17 '21 14:09 isokosan

Try awaiting router only on server side with if (!ctx.isClient)

export const createApp = ViteSSG(
  App,
  { routes },
  async(ctx) => {
    if (!ctx.isClient)
      await ctx.router.isReady()

    // install all modules under `modules/`
    Object.values(import.meta.globEager('./modules/*.ts')).map(i => i.install?.(ctx))
  },
)

sibbng avatar Sep 17 '21 14:09 sibbng

That did the trick!

isokosan avatar Sep 17 '21 14:09 isokosan

I noticed yesterday that the vite-ssg build command will not generate pages when the router.isReady is awaited. So instead, I wrapped all store accessing logic in an isClient if closure.

I think the issue should stay open for the time being, since pinia in vite-ssg should not really throw an error and still function even when generating the pages.

export const createApp = ViteSSG(
  App,
  { routes },
  async (ctx) => {
    // Breaks generation
    // if (!ctx.isClient) {
    //   await ctx.router.isReady()
    // }

    // install all modules under `modules/`
    Object.values(import.meta.globEager('./modules/*.ts')).map(i => i.install?.(ctx))

    if (ctx.isClient) {
      // we can only use stores in client mode,
      const auth = useAuthStore()
      await auth.me()

      const layout = useLayoutStore()
      ctx.router.afterEach((to, from): void => {
        layout.navigationDrawer = false
        layout.sidebar = false
      })
    }
  }
)

isokosan avatar Sep 22 '21 13:09 isokosan

Hey, same issue here, tried every mentioned solution but noting is working. Could it be because I use a pina store in an other pinia store ?

nohehf avatar Mar 08 '22 00:03 nohehf

I also encounter the same issue when I call the useStore() function from a composable (SPA)

supa-freak avatar Oct 27 '23 09:10 supa-freak