Repository navigation
feat(vite): dev-time overlay handle (@svelte-vitals/vite/hooks) - #26
Conversation
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-vitals/vite/hooks) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughA new ChangesDev-time SEO diagnostics handle
Sequence DiagramsequenceDiagram
participant Browser
participant SvelteKit
participant svelteVitalsHandle
participant formatDevReport
participant Terminal
Browser->>SvelteKit: navigate to route
SvelteKit->>svelteVitalsHandle: handle(event, resolve)
Note over svelteVitalsHandle: dev mode only
svelteVitalsHandle->>SvelteKit: resolve(event, { transformPageChunk })
SvelteKit-->>svelteVitalsHandle: stream HTML chunks
svelteVitalsHandle->>svelteVitalsHandle: buffer chunks until done=true
svelteVitalsHandle->>svelteVitalsHandle: parse <head>, run SEO rules
svelteVitalsHandle->>svelteVitalsHandle: compute findingSignature
alt signature differs from last visit
svelteVitalsHandle->>formatDevReport: formatDevReport(route, results, config)
formatDevReport-->>svelteVitalsHandle: formatted multi-line report
svelteVitalsHandle->>Terminal: console.warn(report)
end
svelteVitalsHandle->>SvelteKit: return unmodified Response
SvelteKit->>Browser: serve page
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
README.md (1)
143-145:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMove "Dev overlay" from "Upcoming" to "Shipped" in the roadmap.
The "Dev overlay" feature is being shipped in this PR; the roadmap incorrectly lists it under "Upcoming". Update the roadmap to reflect that it is now available.
📝 Proposed roadmap update
Move this line from the "Upcoming" section (line 145):
- **Dev overlay** ([`#9`](https://github.com/oekazuma/svelte-vitals/issues/9)) — warn in-place while developing via `transformPageChunk`, so dynamic routes are seen with real values as pages are visited.Add it to the "Shipped" section (after line 141):
- **Dev overlay** ([`#9`](https://github.com/oekazuma/svelte-vitals/issues/9)) — SvelteKit handle (`svelteVitalsHandle`, exported from `@svelte-vitals/vite/hooks`) that intercepts rendered `<head>` as pages are visited and prints deduplicated terminal warnings — dev-only, request-driven, never mutates responses.Then remove the duplicate line from "Upcoming".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@README.md` around lines 143 - 145, The README.md roadmap incorrectly lists the "Dev overlay" feature under the "Upcoming" section when it is being shipped in this PR. Move the "Dev overlay" item from the "Upcoming" section (which mentions `transformPageChunk`) to the "Shipped" section and update its description to reflect the actual implementation using the SvelteKit handle (`svelteVitalsHandle`) exported from `@svelte-vitals/vite/hooks`. Remove the old "Dev overlay" entry from the "Upcoming" section to avoid duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/vite/package.json`:
- Around line 50-56: The package.json currently lists `@sveltejs/kit` as a regular
dependency, but since only the ./hooks subpath requires it at runtime and the
main svelteVitals plugin has no runtime dependency on it, you should make it an
optional peer dependency. Move `@sveltejs/kit` from the dependencies section to a
new peerDependencies section, then add a peerDependenciesMeta section to mark
`@sveltejs/kit` as optional (set optional to true). Keep `@sveltejs/kit` in
devDependencies for local development and testing purposes. This approach
reduces install requirements for users who only use the main plugin without the
hooks functionality.
In `@packages/vite/src/hooks/format.ts`:
- Around line 24-27: The findingSignature function creates a signature using
only the result id and effective severity, which can cause signature collisions
when a finding changes state but retains the same id and severity, leading to
incorrect deduplication. Modify the map operation within findingSignature to
include additional differentiating information from each Result object (such as
state or message) in the signature string alongside the id and
effectiveSeverity, ensuring that findings which change state produce distinct
signatures even when their id and severity remain the same.
---
Outside diff comments:
In `@README.md`:
- Around line 143-145: The README.md roadmap incorrectly lists the "Dev overlay"
feature under the "Upcoming" section when it is being shipped in this PR. Move
the "Dev overlay" item from the "Upcoming" section (which mentions
`transformPageChunk`) to the "Shipped" section and update its description to
reflect the actual implementation using the SvelteKit handle
(`svelteVitalsHandle`) exported from `@svelte-vitals/vite/hooks`. Remove the old
"Dev overlay" entry from the "Upcoming" section to avoid duplication.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6e17babb-e2de-4095-a6df-6ce01a94da75
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (11)
.changeset/dev-overlay-handle.mdREADME.mdpackages/vite/package.jsonpackages/vite/src/hooks/format.tspackages/vite/src/hooks/handle.tspackages/vite/src/hooks/index.tspackages/vite/src/hooks/options.tspackages/vite/test/dev-format.test.tspackages/vite/test/dev-handle.test.tspackages/vite/tsup.config.tspnpm-workspace.yaml
Only the ./hooks subpath (svelteVitalsHandle) needs SvelteKit; the main Vite plugin does not. Marking it optional avoids widening install requirements and spurious peer warnings for plugin-only consumers. Addresses CodeRabbit review on #26. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A penalized finding can change state while keeping the same rule id and severity (e.g. a tag going missing -> present-but-empty). Keying the dedup signature only on id+severity suppressed the legitimate re-warning; include detection.presence/value so changed findings re-print. Addresses CodeRabbit review on #26. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a dev-only SvelteKit handle export from @svelte-vitals/vite/hooks that analyzes each visited page’s rendered <head> via transformPageChunk and prints SEO warnings to the dev-server terminal, complementing the existing build-time (prerendered) analysis.
Changes:
- Add
@svelte-vitals/vite/hookssubpath export providingsvelteVitalsHandle(options?): Handleplus formatter/dedup helpers. - Add dev overlay tests covering warnings, clean-route silence, chunk pass-through, dedup, production no-op, and bad-HTML resilience.
- Update package exports/build config and docs; add
@sveltejs/kitas an optional peer (type-only) and dev dependency.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents how to add the dev-time handle in src/hooks.server.ts. |
| pnpm-workspace.yaml | Adds @sveltejs/kit to the workspace catalog for consistent versioning. |
| pnpm-lock.yaml | Locks the new dependency graph after adding @sveltejs/kit (and transitive deps). |
| packages/vite/tsup.config.ts | Builds an additional entrypoint for the new hooks subpath export. |
| packages/vite/test/dev-handle.test.ts | Adds tests for the SvelteKit handle behavior (warnings, dedup, prod pass-through, etc.). |
| packages/vite/test/dev-format.test.ts | Adds tests for dev report formatting and stable dedup signatures. |
| packages/vite/src/hooks/options.ts | Introduces SvelteVitalsHookOptions for the handle’s supported configuration subset. |
| packages/vite/src/hooks/index.ts | Exports svelteVitalsHandle and hook option types for the ./hooks subpath. |
| packages/vite/src/hooks/handle.ts | Implements the dev-only SvelteKit handle using transformPageChunk to observe rendered HTML. |
| packages/vite/src/hooks/format.ts | Adds terminal formatting and signature computation for per-route deduping. |
| packages/vite/package.json | Adds ./hooks export and @sveltejs/kit as optional peer + dev dependency. |
| .changeset/dev-overlay-handle.md | Declares a minor bump for @svelte-vitals/vite for the new feature. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
process.env.NODE_ENV threw a ReferenceError where process is undefined (edge adapters), crashing every request instead of no-op'ing. Guard typeof process and pass through when unavailable. Also restore NODE_ENV by deleting when it was originally unset (avoids the string 'undefined' polluting later tests). Addresses Copilot review on #26. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… response path Replace the hand-rolled `typeof process` / `NODE_ENV` dev guard with esm-env's `DEV`. It resolves statically to `false` in production builds (so it's tree-shaken out, not a runtime read) and its fallback reads no bare `process`, covering edge runtimes too. Move the guard to the factory so the rule set isn't built outside dev. Run analysis fire-and-forget on the final chunk so the dev response is never blocked on parsing/rule execution; the chunk is still returned unchanged (observe-only). Drop the redundant `treatDynamicAs`/`failOn` config (defaults; both moot here) and add an opt-in `SVELTE_VITALS_DEBUG` escape hatch to surface swallowed tool errors. Tests: mock esm-env to exercise the not-in-dev no-op (DEV is a static import, so toggling env vars wouldn't flip it) and flush a macrotask where assertions depend on the now-detached analysis. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t both Add tests that `options.rules` overrides flow into the handle's config and that `SVELTE_VITALS_DEBUG` surfaces otherwise-swallowed analysis errors. Document the handle's `metaComponents`/`rules` options and the debug env var in the README, and include @svelte-vitals/vite in `check:publish` so the new `./hooks` export map is validated by publint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes #9.
What
A dev-time overlay: a SvelteKit server
handlethat, in dev only, analyzes each page's rendered<head>as you navigate and prints SEO warnings for the current route to the dev-server terminal. Because it sees the rendered output, dynamic routes ({data.title}) are checked against what actually renders — the gap static mode and the build plugin can't cover per-navigation.How
@svelte-vitals/vite/hooks→svelteVitalsHandle(options?): Handle.parseHtmlHeadand the core pipeline (selectRules/runRules/applyRuleSeverities) — core is untouched. New code is theHandlewrapper plus two pure helpers (formatDevReport,findingSignature).transformPageChunkaccumulates the page HTML and returns each chunk unchanged (observe-only, never mutates the response). Analysis runs on the final chunk fire-and-forget, so the dev response is never blocked on parsing/rule execution.Projectmarks robots/sitemap present (SEO006/007 aren't page-scoped) and feeds the document's realhtmlLang(SEO009). Only penalized findings are printed; clean routes are silent. Per-route dedup re-warns only when findings change.DEVfromesm-env. It resolves statically tofalsein production builds (tree-shaken out, not a runtimeNODE_ENVread) and its fallback reads no bareprocess, so non-Node runtimes (edge adapters) pass through without aReferenceError. The guard sits at the factory level, so the rule set isn't built outside dev. This is the same DEV/PROD signal Svelte itself uses, replacing the earlierprocess.env.NODE_ENVcheck.try/catchso a tool bug never breaks a request; setSVELTE_VITALS_DEBUGto surface those swallowed errors while debugging.esm-envadded as a runtime dependency.@sveltejs/kitadded as a type-only optional peer + dev dependency (Handletype only; no runtime import).Scope (intentionally out)
Browser overlay UI, per-request robots/sitemap detection, exit codes / CI gating, cross-route session summary.
Testing
Built via brainstorming → writing-plans → subagent-driven-development (3 tasks, per-task spec+quality review, final whole-branch review = ready to merge). Full suite green: 181 tests (core 65 / vite 31 / cli 85), typecheck clean, prettier + eslint pass,
check:publish(publint) covers the new./hooksexport. New tests cover the formatter (penalized-only, ordering, dedup signature) and the handle (missing-title warning, clean-route silence, chunk passthrough, dedup, not-in-dev no-op via anesm-envmock,options.rulesplumbing,SVELTE_VITALS_DEBUGdebug hatch, unparseable-HTML resilience). Where assertions depend on the now-detached fire-and-forget analysis, tests flush a macrotask first.Changeset:
@svelte-vitals/viteminor. Core unchanged.🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Documentation
Tests