v0.7.5
v0.7.5 — @hover-dev/next accepts plugins via register() second arg
A small but user-visible API addition. Before v0.7.5 there was no way to wire @hover-dev/security (or any future Hover plugin) into a Next.js project — the docs claimed Vite / Astro / Nuxt / Next / Webpack all mirror hover({}, securityMode()), but Next had no slot for it. The other four still do; Next now takes plugins via a different shape because of how it compiles instrumentation.ts.
What changed
// instrumentation.ts
import { register as registerHover } from '@hover-dev/next/instrumentation';
export async function register() {
await registerHover({}, [
'@hover-dev/security',
// Object form for plugins that take options:
// { module: '@hover-dev/security', options: { cdpPort: 9444 } },
]);
}The second argument is a PluginSpec[] — either a bare module-specifier string or a { module, options } pair.
Why a string, not import securityMode from '@hover-dev/security'
Next compiles instrumentation.ts for both the Node and Edge runtimes. A top-level import of a plugin package would be statically traced into the Edge bundle, and plugin packages like @hover-dev/security carry Node-only deps (mockttp, playwright-core) that error out under Edge compilation.
The specifier is resolved at runtime inside @hover-dev/next/internal/register-node via the same new Function('s','return import(s)') opaque dynamic import the v0.7.4 release used to load the Node-runtime entry itself. Turbopack's static tracer can't follow a string-constructed function, so plugin code stays strictly Node-runtime-only.
Why we don't take plugins through withHover()
Two reasons:
HoverOptionsis env-var-serialised across thewithHover()↔register()boundary (since v0.7.3). Plugin manifests carry hook closures / functions that don't survive JSON.- Plugin imports at the config layer would still be statically traced into Edge by the same mechanism described above.
So Next's wiring lives entirely in instrumentation.ts, which is already Node-runtime-gated.
Resolver: walks node_modules from process.cwd()
Plugin packages ship ESM-only — their exports map has no require condition, and most don't expose ./package.json as a subpath. createRequire(...).resolve('@hover-dev/security') (CJS) errors with "No exports main defined"; createRequire(...).resolve('@hover-dev/security/package.json') errors with "Package subpath not defined".
The new resolver walks up from process.cwd() looking for node_modules/<plugin>/package.json (Node's own algorithm), then reads exports['.'].import / module / main itself and loads the resulting absolute path via a file:// dynamic import. Sidesteps every conditional-exports edge case at once.
Verified end-to-end in:
| Example | Layout | Verified |
|---|---|---|
examples/next-app |
Flat repo, Next 16.2.6, Turbopack default | Service log reads plugins=[@hover-dev/security]; CDP probe of the widget shadow root shows mode bar with label "Security testing"; WS set-mode { modeId: "security" } broadcasts { current: "security" } |
examples/turbo-monorepo/apps/web |
turbo + pnpm-workspace, Next 15.5 |
Same — resolver walks up from apps/web/ to find the workspace-linked plugin |
Docs
README.md— Security section and Optional Security mode block both now show the correct Next wiring. The "Astro / Nuxt / Next / Webpack mirror the same pattern" line (incorrect for Next) is gone.CLAUDE.md—@hover-dev/nextdescription now spells out thePluginSpec[]design + Edge-tracer reasoning, so future contributors don't try to add a vararg slot towithHover().packages/next-integration/README.md(new) — brings@hover-dev/nextin line with the other four integration READMEs, with a dedicated "Plugins" section.
Packages on npm
All nine publishable packages updated to 0.7.5:
@hover-dev/core · @hover-dev/widget-bootstrap · @hover-dev/astro · @hover-dev/nuxt · @hover-dev/next · @hover-dev/cli · @hover-dev/security · vite-plugin-hover · webpack-plugin-hover
The only package whose behaviour actually changed is @hover-dev/next; the others are republished at the same version so monorepos that pin all @hover-dev/* to one tag don't fall out of sync.
Full Changelog: v0.7.4...v0.7.5