--- /dev/null
+/**
+ * @jest-environment node
+ */
+import { createPinia } from '../src'
+import { useCartStore } from './pinia/stores/cart'
+import { createSSRApp, inject } from 'vue'
+import { renderToString, ssrInterpolate } from '@vue/server-renderer'
+import { useUserStore } from './pinia/stores/user'
+
+describe('SSR', () => {
+ const App = {
+ ssrRender(ctx: any, push: any, parent: any) {
+ push(
+ `<div>${ssrInterpolate(ctx.user.name)}: ${ssrInterpolate(
+ ctx.cart.items
+ )}</div>`
+ )
+ },
+ setup() {
+ const cart = useCartStore()
+ const user = useUserStore()
+ user.name = inject('name', 'default')
+ cart.addItem('one')
+ return { cart, user }
+ },
+ }
+
+ function createMyApp() {
+ const app = createSSRApp(App)
+ const pinia = createPinia()
+ app.use(pinia)
+ // const rootEl = document.createElement('div')
+ // document.body.appendChild(rootEl)
+
+ return { app }
+ }
+
+ it('keeps apps separated', async () => {
+ const { app: a1 } = createMyApp()
+ const { app: a2 } = createMyApp()
+
+ expect(await renderToString(a1)).toMatchInlineSnapshot(`
+ "<div>default: [
+ {
+ "name": "one",
+ "amount": 1
+ }
+ ]</div>"
+ `)
+ expect(await renderToString(a2)).toMatchInlineSnapshot(`
+ "<div>default: [
+ {
+ "name": "one",
+ "amount": 1
+ }
+ ]</div>"
+ `)
+ })
+
+ it('can use a different store', async () => {
+ const { app: a1 } = createMyApp()
+ const { app: a2 } = createMyApp()
+ a1.provide('name', 'a1')
+ a2.provide('name', 'a2')
+
+ expect(await renderToString(a1)).toMatchInlineSnapshot(`
+ "<div>a1: [
+ {
+ "name": "one",
+ "amount": 1
+ }
+ ]</div>"
+ `)
+ expect(await renderToString(a2)).toMatchInlineSnapshot(`
+ "<div>a2: [
+ {
+ "name": "one",
+ "amount": 1
+ }
+ ]</div>"
+ `)
+ })
+})
-import { App } from 'vue'
+import { App, InjectionKey, Plugin } from 'vue'
import { NonNullObject, StateTree, GenericStore } from './types'
/**
export const setClientApp = (app: App) => (clientApp = app)
export const getClientApp = () => clientApp
-export function createPinia() {
- return {
+export interface Pinia {
+ install: Exclude<Plugin['install'], undefined>
+ store<F extends (req?: NonNullObject) => GenericStore>(
+ useStore: F
+ ): ReturnType<F>
+}
+
+export const piniaSymbol = (__DEV__
+ ? Symbol('pinia')
+ : Symbol()) as InjectionKey<Pinia>
+
+export function createPinia(): Pinia {
+ const pinia: Pinia = {
install(app: App) {
+ app.provide(piniaSymbol, pinia)
+ // TODO: strip out if no need for
setClientApp(app)
},
+ store<F extends (req?: NonNullObject) => GenericStore>(
+ useStore: F
+ ): ReturnType<F> {
+ const store = useStore(pinia) as ReturnType<F>
+ return store
+ },
}
+
+ return pinia
}
/**
-import { ref, watch, computed, Ref, reactive } from 'vue'
+import {
+ ref,
+ watch,
+ computed,
+ Ref,
+ reactive,
+ inject,
+ getCurrentInstance,
+} from 'vue'
import {
StateTree,
StoreWithState,
storesMap,
getInitialState,
getClientApp,
+ piniaSymbol,
} from './rootStore'
import { addDevtools } from './devtools'
}) {
const { id, state, getters, actions } = options
- return function useStore(reqKey?: object): Store<Id, S, G, A> {
+ return function useStore(reqKey?: object | null): Store<Id, S, G, A> {
+ // avoid injecting if `useStore` when not possible
+ reqKey = reqKey || (getCurrentInstance() && inject(piniaSymbol))
if (reqKey) setActiveReq(reqKey)
const req = getActiveReq()
let stores = storesMap.get(req)