@anfo/vite-dialogs-plugin
v1.0.8
Published
A Vite plugin that auto-generates a type-safe virtual module for mounting Vue dialog/drawer components programmatically.
Maintainers
Readme
@anfo/vite-dialogs-plugin
A Vite plugin that auto-scans a directory for Vue dialog/drawer components and exposes them through a fully type-safe virtual module (virtual:dialogs). Dialogs are mounted programmatically into isolated Vue app instances — no <Teleport> boilerplate, no global store, automatic cleanup on close.
Installation
npm install -D @anfo/vite-dialogs-plugin
viteis a peer dependency and must already be present in your project.
Quick Start
1. Register the plugin — vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { dialogsPlugin } from "@anfo/vite-dialogs-plugin";
import { resolve } from "node:path";
export default defineConfig({
plugins: [
vue(),
dialogsPlugin({
dir: resolve(__dirname, "src/dialogs"), // folder that contains your dialog components
}),
],
});2. Include the generated declaration — tsconfig.json
The plugin writes src/dialogs.d.ts automatically on every build/dev-server start.
{
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"]
}3. Create a dialog component
Every .vue file whose name ends with Dialog.vue or Drawer.vue inside your dir becomes a key on the dialogs object. Import your runtime helpers directly from the package — no manual Dialog.vue setup required.
<!-- src/dialogs/ConfirmDialog.vue -->
<script setup lang="ts">
import {
useDialogContext,
createDialogExpose,
type DialogExposed,
} from "@anfo/vite-dialogs-plugin/runtime";
defineProps<{ message: string }>();
const { resolve, reject } = useDialogContext<boolean>();
defineExpose<DialogExposed<boolean>>(createDialogExpose<boolean>());
</script>
<template>
<div class="overlay">
<div class="box">
<p>{{ message }}</p>
<button @click="resolve(true)">Confirm</button>
<button @click="resolve(false)">Cancel</button>
</div>
</div>
</template>4. Open dialogs from anywhere
import { dialogs } from "virtual:dialogs";
// Await the result
const result = await dialogs.ConfirmDialog({ message: "Delete this item?" });
if (result.type === "resolve" && result.value) {
// confirmed
}
// Or chain callbacks
dialogs
.ConfirmDialog({ message: "Are you sure?" })
.resolve((value) => console.log("resolved:", value))
.reject((reason) => console.log("rejected:", reason));5. Share plugins with dialogs (Pinia, Router, UI libs)
Each dialog is mounted into its own isolated createApp() instance, so plugins registered on the main app are not available inside dialogs by default. Register them once with configureDialogs() — it runs app.use(...) on every dialog app:
// src/main.ts
import { createApp } from "vue";
import { createPinia } from "pinia";
import ElementPlus from "element-plus";
import { configureDialogs } from "@anfo/vite-dialogs-plugin/runtime";
const pinia = createPinia();
configureDialogs({
use: [
pinia, // shared store state between the main app and dialogs
ElementPlus,
(app) => {
app.config.globalProperties.$t = translate;
},
],
});
createApp(App).use(pinia).mount("#app");Both plugin objects and plain (app) => void functions are accepted. Call it once, before any dialog is opened.
Plugin Options
| Option | Type | Default | Description |
|---|---|---|---|
| dir | string | — | Required. Absolute path to the dialogs directory. |
| pattern | RegExp | /(Dialog\|Drawer)\.vue$/ | RegExp to identify which files are dialogs. |
| dts | string | "src/dialogs.d.ts" | Root-relative path for the generated declaration file. |
Package Exports
| Specifier | Contents |
|---|---|
| @anfo/vite-dialogs-plugin | Vite plugin factory — dialogsPlugin(options) |
| @anfo/vite-dialogs-plugin/runtime | Types, useDialogContext, createDialogExpose, configureDialogs, DialogExposed |
Project Structure
src/
├── dialogs.d.ts ← Auto-generated — do not edit
└── dialogs/
├── ConfirmDialog.vue
├── AlertDialog.vue
└── UserDrawer.vueHow It Works
- On build start (and on file add/remove in dev), the plugin scans
dirfor matching.vuefiles. - It generates a virtual module (
virtual:dialogs) that imports each component and wraps it inmountDialog(). mountDialog()creates an isolatedcreateApp()instance per call, applies any plugins registered viaconfigureDialogs(), provides aDialogControllervia injection, mounts it into a temporary<div>, and returns a Promise-based handle.- When the component calls
resolve(value)orreject(reason), the app is unmounted and the host element removed automatically. - A
.d.tsfile is written so everydialogs.*entry is typed end-to-end — props, return value, and callbacks.
See SKILL.md for step-by-step examples covering all dialog patterns.
License
MIT
