main.ts explicitly calls installPrimeVue(app) AFTER registerPlugins(app)
per the comment in main.ts ("so PrimeVue lives outside the Vuexy @core
machine"). The intent was a single registration site outside the
auto-discovery loop.
Bug: registerPlugins (src/@core/utils/plugins.ts) globs
plugins/*/index.{ts,js} eagerly and invokes the `default` export of
each match. plugins/primevue/index.ts was exporting installPrimeVue
as the default, so registerPlugins also picked it up and called it.
End result: PrimeVue and its three services (Toast, Confirmation,
Dialog) were each registered twice on every app boot. Visible
symptoms: duplicate Toast emissions on a single Toast.add() call,
and ConfirmationService callbacks firing twice for one user
confirmation.
Fix: convert `export default function installPrimeVue` to a NAMED
export, and update main.ts's import to `{ installPrimeVue }`. The
registerPlugins glob still picks up the module path but the
`pluginImportModule.default?.(app)` invocation becomes a no-op via
optional chaining (no default export to call). main.ts remains the
single registration site.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
// Iconify-Tabler runtime data — side-effect import must run before any
|
||
// component renders an Icon, so the @iconify/vue runtime resolves
|
||
// names locally instead of falling back to the api.iconify.design CDN
|
||
// (blocked by our CSP). See plugins/iconify.ts for the bootstrap.
|
||
import '@/plugins/iconify'
|
||
|
||
import { createApp } from 'vue'
|
||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||
import { queryClientConfig } from '@/lib/query-client'
|
||
import { initSentry, installContextBinding } from '@/observability'
|
||
import { router } from '@/plugins/1.router'
|
||
|
||
import App from '@/App.vue'
|
||
import { registerPlugins } from '@core/utils/plugins'
|
||
import { installPrimeVue } from '@/plugins/primevue'
|
||
|
||
// Styles
|
||
import '@styles/tailwind.css'
|
||
import '@core/scss/template/index.scss'
|
||
import '@styles/styles.scss'
|
||
|
||
// Create vue app
|
||
const app = createApp(App)
|
||
|
||
// RFC-WS-7 — Sentry init runs before plugin registration so the SDK can
|
||
// hook Vue's errorHandler before any plugin or component initialises.
|
||
// Empty DSN = SDK no-op (mirrors backend behaviour, RFC §3.3).
|
||
initSentry({
|
||
app,
|
||
router,
|
||
dsn: import.meta.env.VITE_SENTRY_DSN_FRONTEND ?? '',
|
||
release: import.meta.env.VITE_SENTRY_RELEASE ?? '',
|
||
environment: import.meta.env.MODE,
|
||
})
|
||
|
||
// Register plugins (router, pinia, vuetify, …).
|
||
registerPlugins(app)
|
||
|
||
// PrimeVue runs alongside Vuetify during the F3–F6 parallel-mode window.
|
||
// Registered AFTER registerPlugins(app) so PrimeVue lives outside the
|
||
// Vuexy @core/ machine — F6 can remove @core/ without affecting PrimeVue.
|
||
installPrimeVue(app)
|
||
|
||
// Bind auth-scope tags per route navigation. Must run after pinia is set
|
||
// up by registerPlugins (the guard reads useAuthStore / useOrganisationStore).
|
||
installContextBinding(router)
|
||
|
||
app.use(VueQueryPlugin, queryClientConfig)
|
||
|
||
// Mount vue app
|
||
app.mount('#app')
|