From 14d5e5f9add8417d622a8dbf7fcb17ec3c38f625 Mon Sep 17 00:00:00 2001 From: Yunfei He Date: Sun, 28 Jun 2026 22:41:28 +0800 Subject: [PATCH] feat(vite)!: don't bundle @vitejs/plugin-vue; compose it explicitly (0.2.0) (#234) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(vite)!: don't bundle @vitejs/plugin-vue; compose it explicitly BREAKING: vueTui() no longer includes @vitejs/plugin-vue. Add your own SFC/JSX compiler alongside it — `[vueTui(), vue()]` for SFCs (JSX was already explicit: `[vueTui(), vueJsx()]`). This makes SFC and JSX consistent, matches Vite convention (your framework plugin is visible in the config), removes the "is plugin-vue already bundled?" ambiguity, and keeps @vue-tui/vite focused on the terminal dev server (HMR) + build wiring. The dev plugin already force-client-compiles whichever plugin-vue/plugin-vue-jsx is present (matched by name in configResolved), so user-provided plugins work identically to the previously-bundled one. - index.ts: drop the bundled vue() and the `vue` passthrough option. - package.json: @vitejs/plugin-vue moves from dependencies to devDependencies + an OPTIONAL peerDependency (^6) — optional because JSX (plugin-vue-jsx) and h() users don't need it; the version is still validated when present. Bump 0.1.2 -> 0.2.0. - Update all in-repo SFC configs (examples + fixtures) and the dev/build tests to `[vueTui(), vue()]`; update basic-template's README. vp run ready green (vite suite 129 tests, runtime examples 6). Co-Authored-By: Claude Opus 4.8 (1M context) * test(vite): use the public [vueTui(), vue()] form in dev tests The decouple migration left the tests on the spread `[...vueTui(), vue()]` while configs/examples/docs use `[vueTui(), vue()]` (Vite flattens the nested plugin array — verified it works in createServer too). Align the tests to the documented consumer form for consistency. Vite suite green (12 files, 28 tests). Co-Authored-By: Claude Opus 4.8 (1M context) * refactor(vite): compiler plugin first in order — [vue(), vueTui()] Convention: list the SFC/JSX compiler first and vueTui() last ([vue(), vueTui()] / [vueJsx(), vueTui()]). Order is functionally irrelevant (devPlugin force-compiles whichever plugin-vue/vue-jsx is present by name in configResolved), so this is purely stylistic consistency across configs, examples, fixtures, tests, and docs. Also refresh @vue-tui/vite's own README, which still showed the pre-decouple [vueTui()] form and documented the now-removed `vue` passthrough option. Vite suite green (28 tests, incl. the JSX path). Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- examples/basic-jsx/vite.config.ts | 2 +- examples/basic-template/README.md | 3 ++- examples/basic-template/vite.config.ts | 3 ++- examples/coding-agent/vite.config.ts | 3 ++- packages/vite/README.md | 19 ++++++++++--------- packages/vite/package.json | 10 ++++++---- packages/vite/src/index.ts | 18 +++++++----------- packages/vite/test/build.sequential.test.ts | 10 ++++++++-- .../test/cli-shortcuts.sequential.test.ts | 8 +++++++- packages/vite/test/color.sequential.test.ts | 8 +++++++- packages/vite/test/dev.sequential.test.ts | 5 +++-- .../vite/test/fixtures/basic/vite.config.ts | 3 ++- .../vite/test/fixtures/build/vite.config.ts | 3 ++- .../vite/test/fixtures/color/vite.config.ts | 3 ++- .../vite/test/fixtures/exit/vite.config.ts | 3 ++- .../vite/test/fixtures/jsx/vite.config.ts | 2 +- .../vite/test/fixtures/overlay/vite.config.ts | 3 ++- .../vite/test/fixtures/reload/vite.config.ts | 3 ++- .../vite/test/full-reload.sequential.test.ts | 12 +++++++++--- packages/vite/test/jsx.sequential.test.ts | 2 +- packages/vite/test/overlay.sequential.test.ts | 3 ++- pnpm-lock.yaml | 7 +++---- 22 files changed, 83 insertions(+), 50 deletions(-) diff --git a/examples/basic-jsx/vite.config.ts b/examples/basic-jsx/vite.config.ts index 6f548c3..cd32ae7 100644 --- a/examples/basic-jsx/vite.config.ts +++ b/examples/basic-jsx/vite.config.ts @@ -6,5 +6,5 @@ import { vueTui } from "@vue-tui/vite"; // is added alongside vueTui(); the `entry` option points both the dev launcher and // the production build at src/main.tsx instead of the default src/main.ts. export default defineConfig({ - plugins: [vueTui({ entry: "/src/main.tsx" }), vueJsx()], + plugins: [vueJsx(), vueTui({ entry: "/src/main.tsx" })], }); diff --git a/examples/basic-template/README.md b/examples/basic-template/README.md index a6f0389..bfd68fe 100644 --- a/examples/basic-template/README.md +++ b/examples/basic-template/README.md @@ -8,10 +8,11 @@ canonical reference for wiring up the `@vue-tui/vite` plugin. ```ts // vite.config.ts import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "@vue-tui/vite"; export default defineConfig({ - plugins: [vueTui()], + plugins: [vue(), vueTui()], }); ``` diff --git a/examples/basic-template/vite.config.ts b/examples/basic-template/vite.config.ts index 69441b9..2bdd8fc 100644 --- a/examples/basic-template/vite.config.ts +++ b/examples/basic-template/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "@vue-tui/vite"; export default defineConfig({ - plugins: [vueTui()], + plugins: [vue(), vueTui()], }); diff --git a/examples/coding-agent/vite.config.ts b/examples/coding-agent/vite.config.ts index 69441b9..2bdd8fc 100644 --- a/examples/coding-agent/vite.config.ts +++ b/examples/coding-agent/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "@vue-tui/vite"; export default defineConfig({ - plugins: [vueTui()], + plugins: [vue(), vueTui()], }); diff --git a/packages/vite/README.md b/packages/vite/README.md index e699bf6..ab0530c 100644 --- a/packages/vite/README.md +++ b/packages/vite/README.md @@ -6,19 +6,23 @@ with HMR, plus a production build, for Vue apps that render to the terminal via ## Install ```sh -npm install -D @vue-tui/vite +npm install -D @vue-tui/vite @vitejs/plugin-vue # peer deps: vite ^8, @vue-tui/runtime ``` ## Usage +`vueTui()` adds the terminal dev server (HMR) and the production build. Bring your own SFC/JSX +compiler alongside it — `@vitejs/plugin-vue` for SFCs (or `@vitejs/plugin-vue-jsx` for JSX): + ```ts // vite.config.ts import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "@vue-tui/vite"; export default defineConfig({ - plugins: [vueTui()], + plugins: [vue(), vueTui()], }); ``` @@ -31,20 +35,16 @@ export default defineConfig({ ```ts vueTui({ entry: "src/main.ts", // default; the app entry (a .ts/.tsx file, not an index.html) - vue: { - // options forwarded to @vitejs/plugin-vue - /* … */ - }, }); ``` -For a JSX/TSX entry, add `@vitejs/plugin-vue-jsx` alongside it and point `entry` at the `.tsx` file: +For a JSX/TSX entry, use `@vitejs/plugin-vue-jsx` and point `entry` at the `.tsx` file: ```ts import vueJsx from "@vitejs/plugin-vue-jsx"; export default defineConfig({ - plugins: [vueTui({ entry: "/src/main.tsx" }), vueJsx()], + plugins: [vueJsx(), vueTui({ entry: "/src/main.tsx" })], }); ``` @@ -59,10 +59,11 @@ bundled but Node builtins — e.g. toward a standalone binary), set your own bui `vite.config.ts` and the plugin yields to them: ```ts +import vue from "@vitejs/plugin-vue"; import { isBuiltin } from "node:module"; export default defineConfig({ - plugins: [vueTui()], + plugins: [vue(), vueTui()], build: { // Vite 8 is Rolldown-powered: the field is `rolldownOptions` (`rollupOptions` is the alias). rolldownOptions: { diff --git a/packages/vite/package.json b/packages/vite/package.json index 16d58a3..6e7e66b 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "@vue-tui/vite", - "version": "0.1.2", + "version": "0.2.0", "description": "Vite plugin for vue-tui: in-process terminal dev server with HMR + production build.", "license": "MIT", "files": [ @@ -25,11 +25,9 @@ "check:type": "tsc --noEmit", "prepublishOnly": "vp run build" }, - "dependencies": { - "@vitejs/plugin-vue": "^6" - }, "devDependencies": { "@types/node": "catalog:", + "@vitejs/plugin-vue": "^6", "@vitejs/plugin-vue-jsx": "catalog:", "@vue-tui/runtime": "workspace:*", "typescript": "^6.0.3", @@ -37,10 +35,14 @@ "vue": "^3.4.0" }, "peerDependencies": { + "@vitejs/plugin-vue": "^6", "@vue-tui/runtime": "workspace:^", "vite": "^8" }, "peerDependenciesMeta": { + "@vitejs/plugin-vue": { + "optional": true + }, "vite": { "optional": true } diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index b23fc6f..ce34b3c 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -1,27 +1,23 @@ -import vue from "@vitejs/plugin-vue"; import type { Plugin } from "vite"; import { devVmodPlugin } from "./dev-vmod.ts"; import { devPlugin } from "./dev.ts"; import { buildConfigPlugin } from "./build.ts"; export interface VueTuiOptions { - vue?: Parameters[0]; entry?: string; } export function vueTui(options: VueTuiOptions = {}): Plugin[] { // devPlugin (apply:"serve") and buildConfigPlugin (apply:"build") never run together — Vite // picks the right set per mode. normalizeEntry() (below) derives the entry string each needs. - // plugin-vue here (and any plugin-vue-jsx the user adds) is force-client-compiled in - // devPlugin's configResolved, so it runs in the SSR dev environment but emits CLIENT - // render functions for the terminal renderer. + // + // Bring your own SFC/JSX compiler alongside vueTui() — `[vue(), vueTui()]` for SFCs, or + // `[vueJsx(), vueTui()]` for JSX. devPlugin's configResolved finds whichever is present (by + // plugin name) and force-client-compiles it, so it emits CLIENT render functions for the + // terminal renderer even in Vite's SSR dev environment. vueTui deliberately does NOT bundle + // @vitejs/plugin-vue: the app's authoring format is the consumer's choice, kept explicit. const { dev, build } = normalizeEntry(options.entry); - return [ - devPlugin({ entry: dev }), - buildConfigPlugin({ entry: build }), - devVmodPlugin(), - vue(options.vue) as Plugin, - ]; + return [devPlugin({ entry: dev }), buildConfigPlugin({ entry: build }), devVmodPlugin()]; } // Reconcile the entry for dev (matched against the absolute module id via endsWith) and build diff --git a/packages/vite/test/build.sequential.test.ts b/packages/vite/test/build.sequential.test.ts index 93bc917..5566106 100644 --- a/packages/vite/test/build.sequential.test.ts +++ b/packages/vite/test/build.sequential.test.ts @@ -17,6 +17,7 @@ import { fileURLToPath } from "node:url"; import { existsSync, rmSync } from "node:fs"; import { isBuiltin } from "node:module"; import { build, type Rollup } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../src/index.ts"; const root = fileURLToPath(new URL("./fixtures/build", import.meta.url)); @@ -28,7 +29,12 @@ afterEach(() => { test("vite build emits a single self-contained Node entry with deps externalized", async () => { rmSync(dist, { recursive: true, force: true }); - const output = await build({ root, configFile: false, plugins: vueTui(), logLevel: "silent" }); + const output = await build({ + root, + configFile: false, + plugins: [vue(), vueTui()], + logLevel: "silent", + }); // A single, named input resolves to one RollupOutput (not a watcher). const result = (Array.isArray(output) ? output[0] : output) as Rollup.RollupOutput; @@ -57,7 +63,7 @@ test("a consumer's own rolldownOptions.external overrides the plugin default (se const output = await build({ root, configFile: false, - plugins: vueTui(), + plugins: [vue(), vueTui()], logLevel: "silent", build: { rolldownOptions: { diff --git a/packages/vite/test/cli-shortcuts.sequential.test.ts b/packages/vite/test/cli-shortcuts.sequential.test.ts index e8cfcc6..a63026b 100644 --- a/packages/vite/test/cli-shortcuts.sequential.test.ts +++ b/packages/vite/test/cli-shortcuts.sequential.test.ts @@ -4,6 +4,7 @@ import { test, expect, afterEach } from "vite-plus/test"; import { fileURLToPath } from "node:url"; import { createServer, type ViteDevServer } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../src/index.ts"; import { capture, waitFor } from "./helpers.ts"; @@ -24,7 +25,12 @@ afterEach(async () => { // server._shortcutsState (the readline). Assert vueTui leaves no _shortcutsState. test("vueTui disables Vite's CLI keyboard shortcuts so they can't hijack the TUI's stdin", async () => { const read = capture(); - server = await createServer({ root, logLevel: "silent", configFile: false, plugins: vueTui() }); + server = await createServer({ + root, + logLevel: "silent", + configFile: false, + plugins: [vue(), vueTui()], + }); await server.listen(); // a real httpServer is required for bindCLIShortcuts to act await waitFor(read, "count="); // let the app mount with the normal (non-TTY) stdin first diff --git a/packages/vite/test/color.sequential.test.ts b/packages/vite/test/color.sequential.test.ts index 3ba9e25..5e2cd85 100644 --- a/packages/vite/test/color.sequential.test.ts +++ b/packages/vite/test/color.sequential.test.ts @@ -18,6 +18,7 @@ import { test, expect, afterEach } from "vite-plus/test"; import { fileURLToPath } from "node:url"; import { createServer, type ViteDevServer } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../src/index.ts"; import { capture, waitFor } from "./helpers.ts"; @@ -37,7 +38,12 @@ afterEach(async () => { test("#214: dev-mode emits real ANSI color", async () => { const read = capture(); - server = await createServer({ root, logLevel: "silent", configFile: false, plugins: vueTui() }); + server = await createServer({ + root, + logLevel: "silent", + configFile: false, + plugins: [vue(), vueTui()], + }); await server.listen(); await waitFor(read, "COLORTEST"); // Asserting the SGR (not just the text) is what distinguishes the fix from #214 — under the diff --git a/packages/vite/test/dev.sequential.test.ts b/packages/vite/test/dev.sequential.test.ts index c8d50eb..809015e 100644 --- a/packages/vite/test/dev.sequential.test.ts +++ b/packages/vite/test/dev.sequential.test.ts @@ -13,6 +13,7 @@ import { test, expect, afterEach } from "vite-plus/test"; import { fileURLToPath } from "node:url"; import { writeFileSync, readFileSync } from "node:fs"; import { createServer, type ViteDevServer } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../src/index.ts"; import { capture, waitFor } from "./helpers.ts"; @@ -34,7 +35,7 @@ test("boots the app in-process and renders a frame", async () => { root, logLevel: "silent", configFile: false, - plugins: vueTui(), + plugins: [vue(), vueTui()], }); await server.listen(); await waitFor(read, "LABEL-A"); @@ -47,7 +48,7 @@ test("template-only edit hot-swaps with state preserved (no reload)", async () = root, logLevel: "silent", configFile: false, - plugins: vueTui(), + plugins: [vue(), vueTui()], }); await server.listen(); await waitFor(read, "count=3"); // let the counter tick diff --git a/packages/vite/test/fixtures/basic/vite.config.ts b/packages/vite/test/fixtures/basic/vite.config.ts index d14d986..fd8c8a0 100644 --- a/packages/vite/test/fixtures/basic/vite.config.ts +++ b/packages/vite/test/fixtures/basic/vite.config.ts @@ -1,3 +1,4 @@ import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../../../src/index.ts"; -export default defineConfig({ plugins: [vueTui()] }); +export default defineConfig({ plugins: [vue(), vueTui()] }); diff --git a/packages/vite/test/fixtures/build/vite.config.ts b/packages/vite/test/fixtures/build/vite.config.ts index d14d986..fd8c8a0 100644 --- a/packages/vite/test/fixtures/build/vite.config.ts +++ b/packages/vite/test/fixtures/build/vite.config.ts @@ -1,3 +1,4 @@ import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../../../src/index.ts"; -export default defineConfig({ plugins: [vueTui()] }); +export default defineConfig({ plugins: [vue(), vueTui()] }); diff --git a/packages/vite/test/fixtures/color/vite.config.ts b/packages/vite/test/fixtures/color/vite.config.ts index d14d986..fd8c8a0 100644 --- a/packages/vite/test/fixtures/color/vite.config.ts +++ b/packages/vite/test/fixtures/color/vite.config.ts @@ -1,3 +1,4 @@ import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../../../src/index.ts"; -export default defineConfig({ plugins: [vueTui()] }); +export default defineConfig({ plugins: [vue(), vueTui()] }); diff --git a/packages/vite/test/fixtures/exit/vite.config.ts b/packages/vite/test/fixtures/exit/vite.config.ts index d14d986..fd8c8a0 100644 --- a/packages/vite/test/fixtures/exit/vite.config.ts +++ b/packages/vite/test/fixtures/exit/vite.config.ts @@ -1,3 +1,4 @@ import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../../../src/index.ts"; -export default defineConfig({ plugins: [vueTui()] }); +export default defineConfig({ plugins: [vue(), vueTui()] }); diff --git a/packages/vite/test/fixtures/jsx/vite.config.ts b/packages/vite/test/fixtures/jsx/vite.config.ts index e3c2ce5..3975231 100644 --- a/packages/vite/test/fixtures/jsx/vite.config.ts +++ b/packages/vite/test/fixtures/jsx/vite.config.ts @@ -1,4 +1,4 @@ import { defineConfig } from "vite"; import vueJsx from "@vitejs/plugin-vue-jsx"; import { vueTui } from "../../../src/index.ts"; -export default defineConfig({ plugins: [vueTui({ entry: "/src/main.tsx" }), vueJsx()] }); +export default defineConfig({ plugins: [vueJsx(), vueTui({ entry: "/src/main.tsx" })] }); diff --git a/packages/vite/test/fixtures/overlay/vite.config.ts b/packages/vite/test/fixtures/overlay/vite.config.ts index d14d986..fd8c8a0 100644 --- a/packages/vite/test/fixtures/overlay/vite.config.ts +++ b/packages/vite/test/fixtures/overlay/vite.config.ts @@ -1,3 +1,4 @@ import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../../../src/index.ts"; -export default defineConfig({ plugins: [vueTui()] }); +export default defineConfig({ plugins: [vue(), vueTui()] }); diff --git a/packages/vite/test/fixtures/reload/vite.config.ts b/packages/vite/test/fixtures/reload/vite.config.ts index d14d986..fd8c8a0 100644 --- a/packages/vite/test/fixtures/reload/vite.config.ts +++ b/packages/vite/test/fixtures/reload/vite.config.ts @@ -1,3 +1,4 @@ import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../../../src/index.ts"; -export default defineConfig({ plugins: [vueTui()] }); +export default defineConfig({ plugins: [vue(), vueTui()] }); diff --git a/packages/vite/test/full-reload.sequential.test.ts b/packages/vite/test/full-reload.sequential.test.ts index 0e3413c..b8b0c80 100644 --- a/packages/vite/test/full-reload.sequential.test.ts +++ b/packages/vite/test/full-reload.sequential.test.ts @@ -12,6 +12,7 @@ import { test, expect, afterEach } from "vite-plus/test"; import { fileURLToPath } from "node:url"; import { writeFileSync, readFileSync } from "node:fs"; import { createServer, type ViteDevServer } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../src/index.ts"; import { capture, waitUntil } from "./helpers.ts"; @@ -38,7 +39,12 @@ function counts(chunk: string): number[] { test("an entry-level (non-HMR) change restarts the app in-process with no zombie", async () => { const read = capture(); - server = await createServer({ root, logLevel: "silent", configFile: false, plugins: vueTui() }); + server = await createServer({ + root, + logLevel: "silent", + configFile: false, + plugins: [vue(), vueTui()], + }); await server.listen(); // Let the original app boot and tick a few times. @@ -100,7 +106,7 @@ test("survives a SECOND full reload when @vue-tui/runtime is EXTERNALIZED (the p root, logLevel: "silent", configFile: false, - plugins: vueTui(), + plugins: [vue(), vueTui()], ssr: { external: ["@vue-tui/runtime", "@vue-tui/runtime/internal"] }, server: { middlewareMode: true }, }); @@ -145,7 +151,7 @@ test("a genuine app exit closes the in-process dev server", async () => { root: exitRoot, logLevel: "silent", configFile: false, - plugins: vueTui(), + plugins: [vue(), vueTui()], }); // Spy on close so we can detect the runtime-driven teardown. dev.ts captured // `server` and calls server.close() at exit time, so it sees this wrapper. diff --git a/packages/vite/test/jsx.sequential.test.ts b/packages/vite/test/jsx.sequential.test.ts index 8d9ea9b..fdf5761 100644 --- a/packages/vite/test/jsx.sequential.test.ts +++ b/packages/vite/test/jsx.sequential.test.ts @@ -27,7 +27,7 @@ test("JSX (.tsx) components render in the in-process dev server", async () => { root, logLevel: "silent", configFile: false, - plugins: [vueTui({ entry: "/src/main.tsx" }), vueJsx()], + plugins: [vueJsx(), vueTui({ entry: "/src/main.tsx" })], }); await server.listen(); await waitFor(read, "JSX-LABEL"); diff --git a/packages/vite/test/overlay.sequential.test.ts b/packages/vite/test/overlay.sequential.test.ts index 627adca..6b76c6e 100644 --- a/packages/vite/test/overlay.sequential.test.ts +++ b/packages/vite/test/overlay.sequential.test.ts @@ -16,6 +16,7 @@ import { test, expect, afterEach } from "vite-plus/test"; import { fileURLToPath } from "node:url"; import { writeFileSync, readFileSync } from "node:fs"; import { createServer, type ViteDevServer } from "vite"; +import vue from "@vitejs/plugin-vue"; import { vueTui } from "../src/index.ts"; import { capture, waitFor } from "./helpers.ts"; @@ -37,7 +38,7 @@ test("a build error renders the in-process dev overlay", async () => { root, logLevel: "silent", configFile: false, - plugins: vueTui(), + plugins: [vue(), vueTui()], }); await server.listen(); await waitFor(read, "LABEL-A"); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ea69a1..04a7696 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -338,14 +338,13 @@ importers: version: 3.5.34(typescript@6.0.3) packages/vite: - dependencies: - '@vitejs/plugin-vue': - specifier: ^6 - version: 6.0.7(vite@8.1.0(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))(vue@3.5.34(typescript@6.0.3)) devDependencies: '@types/node': specifier: ^24.12.4 version: 24.12.4 + '@vitejs/plugin-vue': + specifier: ^6 + version: 6.0.7(vite@8.1.0(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))(vue@3.5.34(typescript@6.0.3)) '@vitejs/plugin-vue-jsx': specifier: 'catalog:' version: 5.1.5(vite@8.1.0(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))(vue@3.5.34(typescript@6.0.3))