Skip to content

v0.7.4

Choose a tag to compare

@github-actions github-actions released this 27 May 09:41
· 132 commits to main since this release
de7c69d

v0.7.4 — Next 15 instrumentation fix + full Next 15/16 matrix verified

The follow-up to v0.7.3. After unblocking Next 15 + next.config.ts at the package-resolve layer, the next failure surfaced on first npm run dev:

⨯ Error: An error occurred while loading instrumentation hook:
  Cannot find module './register-node.js'

Root cause

Next compiles instrumentation.ts into .next/server/instrumentation.js and inlines our package's instrumentation body. The original await import('./register-node.js') then resolved relative to .next/server/, which has no dist files.

Worse, v0.7.3's smoke-test missed this because it used Node's native dynamic import. Next's webpack-based instrumentation compiler stubs unknown dynamic imports with webpackEmptyAsyncContext, which throws MODULE_NOT_FOUND for any specifier it didn't statically trace.

The fix (two parts)

1 · New ./internal/register-node exports subpath

A package-name specifier routes through node_modules + our exports map regardless of which deep path Next inlines us into:

"./internal/register-node": {
  "import":  { "types": "./dist/register-node.d.ts",  "default": "./dist/register-node.js"  },
  "require": { "types": "./dist/register-node.d.cts", "default": "./dist/register-node.cjs" }
}

2 · Opaque dynamic import via new Function

const dynamicImport: (s: string) => Promise<unknown> =
  new Function('s', 'return import(s)') as (s: string) => Promise<unknown>;

webpack / Turbopack can't statically analyse a string passed into Function, so they neither trace the target into the bundle nor replace it with the throwing stub. The constructed function returns Node's real ESM dynamic-import at runtime, which honours the consumer's node_modules + our exports map.

Verification matrix

All five (Next version × bundler × config) combinations smoke-tested end-to-end — next dev boots, instrumentation loads, Hover service starts on 51789, rendered HTML contains data-hover-widget + __HOVER_PORT__:

Next Bundler Config Verified in
15.5 webpack .ts examples/turbo-monorepo/apps/web (original bug repro)
15.5 Turbopack (--turbopack) .ts examples/turbo-monorepo/apps/web
16.2.6 Turbopack (default) .mjs examples/next-app
16.2.6 Turbopack (default) .ts examples/next-app, temporarily switched
16.2.6 webpack (--webpack) .ts examples/next-app, temporarily switched

examples/turbo-monorepo/ (new)

A real turbo + pnpm-workspace monorepo committed as a test bed. Two Next.js 15 apps:

  • apps/web — Next 15 + next.config.ts (the failing shape), wired to Hover
  • apps/game — second workspace intentionally NOT wired, so npx @hover-dev/cli add at the root triggers the multi-workspace picker

Exercises both v0.7.3 install paths (monorepo workspace dispatch, lockfile walk-up) and the v0.7.4 instrumentation fix. Validation steps in the example's README.

Docs rewrite

The previous install docs were correct but scattered — the README had two overlapping install sections, and the docs site missed the Next.js manual <HoverScript /> step.

  • README — single, three-step Install section: prereqs (Node + agent CLI in a clean table), npx @hover-dev/cli add with dry-run / force-bundler / monorepo / Next details collapsed into <details> blocks at install time, then pnpm dev + the ✨ colour states.
  • docs/get-started/install.md — full stepwise guide. Before-you-start. Option A (CLI). Option B (manual install per-bundler, Next.js's three pieces each annotated). Monorepos (full match-count table, lockfile walk-up, links to turbo-monorepo example). Verify-the-install-worked (the two service log lines you should see, ✨ colour table). Troubleshooting — Couldn't detect bundler, EADDRINUSE, widget missing, and the two specific Next 15 errors from v0.7.3 and v0.7.4.

Compatibility footnotes

  • @hover-dev/next ships dual ESM + CJS since v0.7.3. Both Next 15 (which loads .ts configs via CJS require) and Next 16 (which loads via native ESM import) work.
  • HoverScript is an async Server Component since v0.7.3. React 19 / App Router renders these natively. The widget is a no-op in production builds — process.env.NODE_ENV !== 'development' returns null from the component.

Packages on npm

All seven publishable packages updated:

@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 — all at 0.7.4.

Full Changelog: v0.7.3...v0.7.4