npmvitepinia

Vite library - pinia stores


I tried to create my application basics as a npm library with vite, vue, vuetify and pinia.

So.... I created a simple vue@latest project and modified the package.json and vite.config.ts.

In my library the package.json

{
  "name": "my-lib",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "main": "./dist/my-lib.cjs.js",
  "module": "./dist/my-lib.es.js",
  "types": "./dist/types/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/my-lib.es.js",
      "require": "./dist/my-lib.cjs.js",
      "types": "./dist/types/index.d.ts"
    },
    "./components": {
      "import": "./dist/my-lib.es.js",
      "require": "./dist/my-lib.cjs.js",
      "types": "./dist/types/index.d.ts"
    }
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "dev": "vite",
    "build": "run-p type-check \"build-only {@}\" -- && run-p build-types",
    "preview": "vite preview",
    "build-only": "vite build",
    "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
    "build-types": "vue-tsc -p tsconfig.lib.json --composite false --emitDeclarationOnly",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
    "format": "prettier --write src/"
  },
  "dependencies": {
    "pinia": "^2.1.7",
    "vue": "^3.3.4",
    "vue-router": "^4.2.5",
    "vuetify": "^3.3.23"
  },
  "devDependencies": {
    "@babel/types": "^7.23.0",
    "@types/jsdom": "^21.1.3",
    "jsdom": "^22.1.0",
    "@rushstack/eslint-patch": "^1.3.3",
    "@tsconfig/node18": "^18.2.2",
    "@types/node": "^18.18.5",
    "@vitejs/plugin-vue": "^4.4.0",
    "@vue/eslint-config-prettier": "^8.0.0",
    "@vue/eslint-config-typescript": "^12.0.0",
    "@vue/tsconfig": "^0.4.0",
    "eslint": "^8.49.0",
    "eslint-plugin-vue": "^9.17.0",
    "npm-run-all2": "^6.1.1",
    "prettier": "^3.0.3",
    "typescript": "~5.2.0",
    "vite": "^4.4.11",
    "vue-tsc": "^1.8.19"
  }
}

and my vite.config.ts:

import {fileURLToPath, URL} from 'node:url'
import {resolve} from "path"
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
    build: {
        sourcemap: true,
        lib: {
            name: 'my-lib',
            entry: {
                'my-lib': resolve(__dirname, 'src/index.ts'),
                'components': resolve(__dirname, 'src/components/index.ts'),
                'stores': resolve(__dirname, 'src/stores/index.ts'),
            },
            fileName: (fmt, name) => `${name}.${fmt}.js`,
        },
        rollupOptions: {
            external: ['vue', 'pinia', 'vue-router', 'axios', 'vue-i18n', 'vuetify', '@vueuse/core'],
            output: {
                globals: {
                    vue: 'Vue',
                },
            }
        }
    },
    plugins: [vue()],
    resolve: {
        alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}
    }
})

created component is exported in the lib with the store

import type {App} from 'vue'
import CounterButton from './components/CounterButton.vue';
import {useCounterStore} from './stores/counter'

function install(app: App) {
    app.component('CounterButton', CounterButton)
}

export default install
export {CounterButton, useCounterStore}
the component is really simple

<script lang="ts" setup>
import {useCounterStore} from "@/stores/counter";
const counterStore = useCounterStore();
</script>

<template>
  <v-btn :text="'count of ' + counterStore.count" @click="counterStore.increment()"/>
</template>

That seems to work, great. Then I created a second vue@latest project, ran npm link ../my-lib in this one use the store and component from my library.

<script lang="ts" setup>
import {CounterButton, useCounterStore} from "my-lib"
import {storeToRefs} from "pinia";

const counterStore = useCounterStore();
const {count} = storeToRefs(counterStore)

</script>

<template>
  <main>
    <div>
      <CounterButton/>
    </div>
    <div>Current Counter: {{ count }}</div>
  </main>
</template>

All works fine when running with npm run dev but when using npm run build && npm run preview I'm getting a runtime error on the page

TypeError: Cannot read properties of undefined (reading '_s')
    at i (pinia.mjs:1714:20)
    at setup (HomeView.vue:5:22)
    at Tn (runtime-core.esm-bundler.js:158:18)
    at ag (runtime-core.esm-bundler.js:7290:25)
    at lg (runtime-core.esm-bundler.js:7251:36)
    at j (runtime-core.esm-bundler.js:5647:7)
    at O (runtime-core.esm-bundler.js:5613:9)
    at g (runtime-core.esm-bundler.js:5088:11)
    at ws.te [as fn] (runtime-core.esm-bundler.js:5821:9)
    at ws.run (reactivity.esm-bundler.js:178:19)

It seems that the lib can't access the pinia instance when it is built. pinia.mjs

...
pinia = activePinia;
        if (!pinia._s.has(id)) {
            // creating the store registers it in `pinia._s`
            if (isSetupStore) {
                createSetupStore(id, setup, options, pinia);
            }
            else {
                createOptionsStore(id, options, pinia);
            }
            /* istanbul ignore else */
            if ((process.env.NODE_ENV !== 'production')) {
                // @ts-expect-error: not the right inferred type
                useStore._pinia = pinia;
            }
        }
...

Does anyone have a hint as to what my error is?

You can find my sample project on Github


Solution

  • You can use vite's shared options resolve.dedupe to resolve this issue

    import { defineConfig } from 'vite'
    export default defineConfig(() => {
      
      return {
        // ...
        resolve: {
          dedupe: ['pinia'],
        },
        // ...
      }
    })