npm.io
0.1.1 • Published yesterday

@ape-egg/vite-plugin-vibe

Licence
ISC
Version
0.1.1
Deps
1
Size
82 kB
Vulns
0
Weekly
0

@ape-egg/vite-plugin-vibe

Early alpha — subject to change. This plugin is a development-only HMR layer for Vibe. Vibe itself is still evolving, so achieving full 1-to-1 feature parity is a moving target — and not the priority right now.

Because the plugin only runs in development (it powers vite dev HMR and is never shipped to production), it is acceptable for it to have rough edges while Vibe stabilises. It updates alongside Vibe, will keep catching up to new runtime features, and will reach a stable release once the underlying framework settles.

Vite plugin for Vibe projects. Gives you hot refresh of components and pages without losing state — window.$ is untouched across edits.

Install

bun add -d @ape-egg/vite-plugin-vibe
# or
npm install -D @ape-egg/vite-plugin-vibe

Usage

// vite.config.js
import { defineConfig } from 'vite';
import vibe from '@ape-egg/vite-plugin-vibe';

export default defineConfig({
  plugins: [vibe()],
});

That's it. bun dev / vite now serves your Vibe project with:

  • Component HMR — saving a file in components/ morphs the rendered component in place
  • Page HMR — saving an HTML page updates the <slot> content; the rest of the page stays mounted
  • Normal reload fallback — pages without Vibe (<slot> not present) reload normally
  • Component preview — an opt-in gallery at /__vibe (see below)

Component preview

Opt-in: enable it with a boolean flag, then open /__vibe while the dev server is running. Every component in your project shows up automatically — no registration, no maintained manifest. Adding a file under components/ makes it appear.

vibe({ preview: true })            // enable with defaults
  • Router page (/__vibe) lists all components grouped by folder, with a name filter and a count.
  • Per-component page (/__vibe/<path>) renders the component standalone in an isolated stage (its own window.$, so nothing leaks between components), with:
    • a DataInspector showing the component's declared state + the inputs it reads (@[name], <!-- each name as x -->). Each key has a type toggle (string / number / array / object); editing a value updates the live preview.
    • an editable source view (powered by @ape-egg/codie) — edits re-mount the preview instantly and are never written to disk.

State for each component lives in a single committed __vibe.component-previewer.json at your project root, keyed by component path. The plugin keeps it in sync with your components automatically (adds new keys, prunes removed ones, preserves any types you set). When the preview is disabled this file is never written or updated.

The preview is plain static-deployable output: bakePreview() (from @ape-egg/vite-plugin-vibe/preview) emits the whole gallery — pages, component list, analysis, fixtures, and sources — as static files with no backend, so it can be hosted or shared. In a baked deploy, type-toggles and source edits still work but persist only in the browser session.

Options

vibe({
  components: 'components', // directory to serve raw + watch for HMR (default: 'components')

  // Preview is OFF by default. Enable with `preview: true`, or pass an object
  // with `enabled: true` plus configuration. Anything else (omitted, false, or
  // an object without `enabled`) leaves it disabled — and never writes the
  // __vibe.component-previewer.json fixture file.
  preview: {
    enabled: true,                    // the enable flag (or use `preview: true`)
    route: '/__vibe',                 // route prefix for the gallery
    css: ['/index.css'],              // stylesheets the isolated stage includes (default: none)
    stageAttrs: ['stylecheat'],       // boolean attrs set on the stage <body> for attribute-gated CSS
    vibeUrl: '/nodemodules/@ape-egg/vibe/index.js',   // where kit pages load the runtime
    codieUrl: '/nodemodules/@ape-egg/codie/codie.js', // where kit pages load the editor
  },
})

By default the stage renders a component on the Vibe base only. Use preview.css (and preview.stageAttrs for frameworks like Stylecheat that key off <body> attributes) to give a component the styling it needs.

Requirements

  • Vite >= 5.0
  • @ape-egg/vibe >= 1.9.2 — the plugin relies on Vibe preserving <slot> in the processed DOM and on internal hooks (_vibeRawSource, _vibeIterPropIds, _vibeIterPropExprs, _vibeReplacedBy) used for surgical HMR (1.9.2 added the iteration-prop ownership transfer and clone-path this.X fixes the plugin depends on)
  • @ape-egg/codieoptional; powers the preview's editable source cell. The kit pages load it from preview.codieUrl (default /nodemodules/@ape-egg/codie/codie.js). If it isn't installed/served, the per-component page still renders (inspector + live stage) and just shows the source cell as disabled.
  • acorn — bundled dependency; the preview uses it to statically read each component's declared-state literal and binding surface (no bespoke parser).

How it works

Vibe fetches components at runtime via <component src="">. Vite's default HTML pipeline would inject its HMR client and rewrite <script type="module"> inside every HTML file it serves — which breaks Vibe's component processing.

This plugin:

  1. Serves component HTML raw through a middleware, bypassing Vite's HTML transform for files under your components directory.
  2. Watches component files and sends custom vibe:component-update events on change. The client injects a small runtime that replaces the rendered component's DOM in place — fetching the new template, executing any scripts if changed, and preserving the original slot content captured before initial hydration.
  3. Intercepts Vite's full-reload for HTML pages via handleHotUpdate. If Vibe is on the page (detected by presence of <slot>), the client fetches the new page HTML, extracts the content inside <component src>, and swaps the <slot> element's content. Vibe's MutationObserver re-hydrates bindings/conditionals/iterations naturally.

No server-state, no WebSocket churn, no user configuration of which files are pages vs components — the plugin watches components/ and lets Vite handle everything else.

Limitations

  • Components with slot content containing <script type="module"> that has changed require a full page reload (scripts can't be cleanly re-executed in place)
  • If a page file changes HTML outside the <component src="..."> wrapper (e.g., new <body> children), the plugin falls back to location.reload() automatically
  • Preview: components that read global $ inside their <script> (e.g. $.characters[...]) render best-effort — the inspector surfaces declared state + the inputs referenced in the template, not arbitrary $ reads, so such a component may render its empty/else branch. Failures are isolated to that component's stage cell.

License

ISC

Keywords