Skip to content

Plugins

Plugins extend both the dev server and the build. The API is intentionally close to the Rollup/Vite shape, so the ecosystem's mental model carries over — but every type is Vantris-owned, and the bundler (Rolldown) is never exposed.

Referencing plugins

Plugins are listed in the config by string — a package name or a path to a local file:

ts
// vantris.config.ts
import { defineConfig } from "vantris";

export default defineConfig({
  plugins: ["@vantris/react", "./plugins/my-plugin"],
});

Each string is loaded from the project root, and its default export is used — a plugin object (typically wrapped in definePlugin) or a function returning one.

Passing options to a plugin

Because plugins are referenced by string, you can't call a factory with options inline. Instead, wrap the plugin in a tiny local module and reference that file. See @vantris/react → With options.

Authoring a plugin

A plugin is a plain object with a name and one or more optional hooks, authored with the typed definePlugin helper:

ts
// plugins/my-plugin.ts
import { definePlugin } from "vantris";

export default definePlugin({
  name: "my-plugin",
  enforce: "pre", // optional: "pre" | "post"

  // Read or amend the config (returned partials are deep-merged).
  config: (config, env) => ({ define: { __BUILT_AT__: Date.now() } }),

  // Provide a module's source instead of reading disk (virtual modules, codegen).
  load(id) {
    if (id.endsWith("version.ts")) return `export const version = "1.0.0";`;
    return null;
  },

  // Rewrite a module's code. Runs in dev and build.
  transform(code, id) {
    if (id.endsWith(".tsx")) return `${code}\n// touched by my-plugin`;
    return null;
  },

  // Customise HMR for a change (e.g. framework Fast Refresh).
  handleHotUpdate(ctx) {
    /* ctx.file, ctx.modules, ctx.server.send(event, data), … */
  },
});

The hooks

HookRuns inPurpose
configdev + buildRead or amend the user config (partials deep-merged).
configResolveddev + buildRead the fully resolved config.
configureServerdev onlyAccess the running dev server (URL + HMR engine).
transformIndexHtmldev + buildRewrite the HTML entry document.
buildStartbuildBefore the module pipeline starts.
resolveIddev + buildResolve an import to a module id (incl. virtual).
loaddev + buildProvide a module's source.
transformdev + buildRewrite a module's code.
buildEndbuildAfter the module pipeline finishes.
handleHotUpdatedev onlyCustomise HMR for a change.

resolveId / load / transform run in both dev and buildresolveId can redirect an import to a virtual module whose source comes from load. Full signatures on the Plugin API page.

Ordering with enforce

Plugins run in the order pre → normal → post, through one container shared by the dev server and the bundler:

ts
definePlugin({ name: "runs-first", enforce: "pre", /* … */ });
definePlugin({ name: "runs-normal", /* … */ });
definePlugin({ name: "runs-last", enforce: "post", /* … */ });

Within the same bucket, plugins run in the order they're listed in plugins.

The plugin context

Pipeline hooks (resolveId/load/transform, buildStart/buildEnd) run with a this PluginContextresolve, emitFile, addWatchFile, warn, error, meta. In the build, this is backed by the bundler's own rich context; in dev, Vantris provides the same surface (a few build-only methods throw).

Where to go next

Released under the MIT License.