Repository navigation
feat: Performance v0.4 — static <img> checks + multi-category foundation (#10) - #36
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ings (#10) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 5 minutes and 3 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (12)
📝 WalkthroughWalkthroughAdds a Performance analysis category to svelte-vitals (v0.4) with two static ChangesPerformance v0.4 — image analysis and multi-category reporting
Sequence Diagram(s)sequenceDiagram
participant CLI as analyzeProject
participant ImageProv as sourceImageProvider
participant Parser as parseFile (AST)
participant Core as runRules
participant Reporters as console/json/sarif/github/agent
CLI->>ImageProv: collect(rt, cwd, config)
ImageProv->>Parser: read + parse +page.svelte & layouts
Parser-->>ImageProv: ParsedFile { images: ParsedImage[] }
ImageProv-->>CLI: ResolvedImages[] (per route)
CLI->>Core: runRules({ heads, images, project, config })
Core->>Core: imageRule → PERF001 / PERF002 checks
Core-->>CLI: Result[] with category + line
CLI->>Reporters: formatConsoleReport / buildJsonReport / formatSarifReport
Reporters->>Reporters: scoresByCategory(results) → per-category scores
Reporters-->>CLI: category-aware output with Performance section
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
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 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
🧹 Nitpick comments (3)
packages/cli/src/index.ts (1)
90-92: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winParallelize provider collection to reduce analysis latency.
Line 90 and Line 91 run head/image collection sequentially. Running them concurrently removes unnecessary wait time on larger projects.
♻️ Suggested change
- const heads = (await sourceHeadProvider.collect(rt, cwd, config)).filter((h) => matches(h.route)); - const images = (await sourceImageProvider.collect(rt, cwd, config)).filter((i) => matches(i.route)); + const [allHeads, allImages] = await Promise.all([ + sourceHeadProvider.collect(rt, cwd, config), + sourceImageProvider.collect(rt, cwd, config) + ]); + const heads = allHeads.filter((h) => matches(h.route)); + const images = allImages.filter((i) => matches(i.route));🤖 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 `@packages/cli/src/index.ts` around lines 90 - 92, The sourceHeadProvider.collect and sourceImageProvider.collect calls on lines 90-91 are executed sequentially, creating unnecessary wait time. Refactor these two lines to run concurrently by using Promise.all to invoke both collect methods in parallel, then apply the filter operations to each result. This allows both providers to collect data simultaneously instead of waiting for one to complete before starting the other.packages/core/src/rule.ts (1)
5-9: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winUpdate stale
RuleContextdoc comment to reflectimagessupport.Line 5 still states rules only see
ResolvedHead[], but Line 9 addsimages?: ResolvedImages[]. Keeping this comment accurate prevents API misunderstanding for rule implementers.Suggested patch
-/** Input given to every rule. Mode-independent: rules see only ResolvedHead[] (design §8, §10). */ +/** Input given to every rule. Mode-independent shape shared across rule categories. */ export interface RuleContext { heads: ResolvedHead[]; /** Per-route <img> elements for Performance rules (absent in modes that don't collect them). */ images?: ResolvedImages[];🤖 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 `@packages/core/src/rule.ts` around lines 5 - 9, The JSDoc comment for the RuleContext interface states that rules only see ResolvedHead[], but the interface now also includes an optional images field. Update the comment above the RuleContext interface definition to accurately reflect that rules receive both a heads field and an optional images field for Performance rules, making it clear that the interface is no longer mode-independent in the way the current comment suggests.packages/core/test/agent-report.test.ts (1)
51-51: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winTighten the heading assertion to actually catch SEO-specific regressions.
/^# svelte-vitals/mwill still pass if the heading reverts to something like# svelte-vitals — SEO fixes. Assert the exact expected heading.Suggested test update
- expect(md).toMatch(/^# svelte-vitals/m); // heading no longer says "SEO fixes" + expect(md).toContain('# svelte-vitals — fixes'); + expect(md).not.toContain('SEO fixes');🤖 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 `@packages/core/test/agent-report.test.ts` at line 51, The regex assertion in the test at the expect(md).toMatch() call is too permissive and will pass even if the heading reverts to including "SEO fixes". Instead of using the partial match regex /^# svelte-vitals/m, make the assertion more specific by matching the exact expected heading format so that any regression where "SEO fixes" gets added back to the heading will cause the test to fail.
🤖 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/core/src/reporter/sarif.ts`:
- Around line 50-52: The partialFingerprints fingerprint construction for the
'svelteVitals/v1' key is missing location information, which allows distinct
findings from different files with the same route and line to collide. Update
the template string that builds the fingerprint value to include the location
field (r.location) in the fingerprint key construction so that findings remain
distinct across different files.
In `@packages/core/src/rules/perf/image-rule.ts`:
- Around line 55-57: The result object in the findings emission always includes
line: img.line regardless of whether img.line is a valid 1-based line number.
The line field in Result is optional and should only be included when img.line
is greater than 0 (valid). Modify the result object construction to
conditionally include the line property only when img.line is a valid positive
number, otherwise omit it entirely from the object.
---
Nitpick comments:
In `@packages/cli/src/index.ts`:
- Around line 90-92: The sourceHeadProvider.collect and
sourceImageProvider.collect calls on lines 90-91 are executed sequentially,
creating unnecessary wait time. Refactor these two lines to run concurrently by
using Promise.all to invoke both collect methods in parallel, then apply the
filter operations to each result. This allows both providers to collect data
simultaneously instead of waiting for one to complete before starting the other.
In `@packages/core/src/rule.ts`:
- Around line 5-9: The JSDoc comment for the RuleContext interface states that
rules only see ResolvedHead[], but the interface now also includes an optional
images field. Update the comment above the RuleContext interface definition to
accurately reflect that rules receive both a heads field and an optional images
field for Performance rules, making it clear that the interface is no longer
mode-independent in the way the current comment suggests.
In `@packages/core/test/agent-report.test.ts`:
- Line 51: The regex assertion in the test at the expect(md).toMatch() call is
too permissive and will pass even if the heading reverts to including "SEO
fixes". Instead of using the partial match regex /^# svelte-vitals/m, make the
assertion more specific by matching the exact expected heading format so that
any regression where "SEO fixes" gets added back to the heading will cause the
test to fail.
🪄 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: fb24a000-773d-401a-ad8d-91ec546f5767
📒 Files selected for processing (36)
.changeset/performance-v0.4.mdREADME.mddocs/superpowers/plans/2026-06-22-performance-v0.4.mddocs/superpowers/specs/2026-06-22-performance-v0.4-design.mdpackages/cli/src/index.tspackages/cli/src/providers/source/parse.tspackages/cli/src/providers/source/routes.tspackages/cli/test/fixtures/basic-project/src/routes/img/+page.sveltepackages/cli/test/parse-file.test.tspackages/cli/test/run.test.tspackages/cli/test/source-provider.test.tspackages/core/src/images.tspackages/core/src/index.tspackages/core/src/reporter/agent.tspackages/core/src/reporter/console.tspackages/core/src/reporter/github.tspackages/core/src/reporter/json.tspackages/core/src/reporter/sarif.tspackages/core/src/rule.tspackages/core/src/rules/index.tspackages/core/src/rules/perf/image-rule.tspackages/core/src/rules/perf/images.tspackages/core/src/rules/seo/head-tag-rule.tspackages/core/src/rules/seo/project-rules.tspackages/core/src/rules/seo/seo001-title.tspackages/core/src/scoring/score.tspackages/core/src/types.tspackages/core/test/agent-report.test.tspackages/core/test/console-report.test.tspackages/core/test/github-report.test.tspackages/core/test/json-report.test.tspackages/core/test/perf-rules.test.tspackages/core/test/rule-category.test.tspackages/core/test/sarif-report.test.tspackages/core/test/score.test.tspackages/vite/test/analyze.test.ts
…rint (#10) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds the first non-SEO Vitals category (Performance v0.4) to @svelte-vitals/core and wires up a reusable multi-category foundation (category-tagged results, per-category scoring, and category-aware reporters). This extends static-mode analysis to produce per-<img> findings with file + line locations while keeping existing SEO scoring/output behavior backward compatible (top-level JSON score remains SEO-only).
Changes:
- Introduces PERF001 (missing
<img>width/height) and PERF002 (missingloading) plus a sharedimageRulefactory and image IR (ImageInfo/ResolvedImages). - Adds multi-category plumbing:
Result.category/Result.line,scoresByCategory, and category-aware console/json/agent/github/sarif reporting. - Extends the CLI static provider to collect
<img>facts across each route’s layout chain and feed them intoRuleContext.images.
Reviewed changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| README.md | Roadmap/docs updated to reflect Performance v0.4 shipping and upcoming categories. |
| packages/vite/test/analyze.test.ts | Updates plugin-mode console header expectation after reporter header changes. |
| packages/core/test/score.test.ts | Adds coverage for scoresByCategory behavior and SEO defaulting. |
| packages/core/test/sarif-report.test.ts | Verifies SARIF startLine and fingerprint disambiguation using Result.line. |
| packages/core/test/rule-category.test.ts | Ensures rule results carry category info (SEO rules emit seo). |
| packages/core/test/perf-rules.test.ts | Adds unit tests for PERF001/PERF002 and line/file behavior. |
| packages/core/test/json-report.test.ts | Asserts JSON includes categories and per-issue category/line. |
| packages/core/test/github-report.test.ts | Asserts GitHub annotations include line when available. |
| packages/core/test/console-report.test.ts | Asserts console includes Performance score section and file:line output. |
| packages/core/test/agent-report.test.ts | Updates agent heading and verifies perf findings are included. |
| packages/core/src/types.ts | Extends Result with category? and line?; defines Category union. |
| packages/core/src/scoring/score.ts | Adds scoresByCategory helper to reuse computeScore per category. |
| packages/core/src/rules/seo/seo001-title.ts | Tags SEO001 results with category: 'seo'. |
| packages/core/src/rules/seo/project-rules.ts | Tags project SEO rules with category: 'seo'. |
| packages/core/src/rules/seo/head-tag-rule.ts | Tags head-tag-rule-produced results with category: 'seo'. |
| packages/core/src/rules/perf/images.ts | Defines PERF001/PERF002 rule configurations using imageRule. |
| packages/core/src/rules/perf/image-rule.ts | Adds imageRule factory emitting per-image findings with optional line. |
| packages/core/src/rules/index.ts | Registers/re-exports Performance rules alongside existing SEO rules. |
| packages/core/src/rule.ts | Extends RuleContext with optional images?: ResolvedImages[]. |
| packages/core/src/reporter/sarif.ts | Uses Result.line for SARIF region startLine + fingerprints. |
| packages/core/src/reporter/json.ts | Adds categories map; tags issues with category and optional line. |
| packages/core/src/reporter/github.ts | Adds line=... to workflow-command annotations when present. |
| packages/core/src/reporter/console.ts | Prints per-category score lines and appends :line to locations. |
| packages/core/src/reporter/agent.ts | Makes the agent heading category-neutral (“fixes”). |
| packages/core/src/index.ts | Re-exports new image/perf/scoring APIs. |
| packages/core/src/images.ts | Introduces mode-independent image IR types. |
| packages/cli/test/source-provider.test.ts | Adds coverage for layout+page image collection per route. |
| packages/cli/test/run.test.ts | Adds e2e assertion that PERF001 surfaces in JSON output. |
| packages/cli/test/parse-file.test.ts | Adds parsing tests for <img> attribute presence + line collection. |
| packages/cli/test/fixtures/basic-project/src/routes/img/+page.svelte | Adds a fixture route containing an <img> missing dimensions. |
| packages/cli/src/providers/source/routes.ts | Adds sourceImageProvider to collect per-route images via layout chain. |
| packages/cli/src/providers/source/parse.ts | Adds <img> collection + line extraction to the static parser. |
| packages/cli/src/index.ts | Wires collected images into runRules via RuleContext.images. |
| docs/superpowers/specs/2026-06-22-performance-v0.4-design.md | Design spec documenting v0.4 scope and architecture. |
| docs/superpowers/plans/2026-06-22-performance-v0.4.md | Implementation plan/checklist for the work. |
| .changeset/performance-v0.4.md | Changeset for publishing core + cli minors for Performance v0.4. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…itives (#10) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ing (#10) Address code-review findings on the Performance v0.4 branch: - Collect heads and images in one walk of each route's layout chain (collectRoutes), so every .svelte file is read and parsed once instead of twice. analyzeProject now collects in a single call. - PERF rules emit nothing for routes with no <img>, so a zero-image project hides the Performance category instead of reporting 100/100, and the console "Passed" section is no longer padded with per-route PERF passes for image-less routes. - Tidy the console header (Svelte Vitals · <mode>) and drop the unused _config param (subsumed by the single-pass refactor). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rop dead provider (#10) - console: render per-category score lines from the already-computed scoresByCategory result instead of recomputing computeScore per category - perf: passing seed result no longer carries a fix (nothing to remediate); add a regression assertion - cli: remove the unused, untyped sourceImageProvider (collectRoutes is the single collection seam); point its test at collectRoutes - docs: note non-<img> image sources (enhanced:img, picture, svelte:element, component images) as out of scope for v0.4 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First Vitals category beyond SEO, plus the multi-category foundation later categories reuse. Implements the Performance v0.4 item of #10 (static-analysis only; no runtime Core Web Vitals).
What's new
warning): an<img>missing explicitwidth/height(CLS risk). A dynamically-bound dimension (width={w}) counts as present and passes.info): an<img>with noloadingattribute (advisory; static analysis can't know the LCP image, so it never fails the build).<img>, withlocation(file) +line.Multi-category foundation
Result.category+line; image IR (ImageInfo/ResolvedImages) +RuleContext.images; animageRulefactory.scoresByCategory— per-category scores by reusing the existing route-averagecomputeScore.SEO Score:/Performance Score:); json adds acategoriesmap and tags issues withcategory/line; agent uses a category-neutral heading; github/sarif carry the finding line.<img>in a single walk of each route's layout chain (collectRoutes) — one read + parse per file (a layout image surfaces per child route, like inherited head findings).Additivity
The change is additive: a missing
categorydefaults to'seo', the json top-levelscorestays the SEO subset, and the Performance reporting only adds sections. All existing SEO findings, scores, and output are unchanged.Scope
Static mode only;
@svelte-vitals/viteuntouched. Per #10's plan and the 1.0 philosophy,preload/adapter/large-import checks, plugin-mode images, and the combined weighted Health Report are later 0.x increments (1.0 is the polished culmination, not where new integration lands).Validation
pnpm -r test— 240 passed (core 89, vite 31, cli 111, mcp 9)pnpm -r typecheck,pnpm build,pnpm lint, publint + attw (esm-only) — greenNotes for reviewers
<img>elements. PERF rules emit nothing for an image-less route, so a zero-image project hides the Performance section entirely (rather than reporting a hollowPerformance Score: 100/100), and the consolePassedlist is no longer padded with per-route PERF passes for image-less routes. Routes whose images all pass still seed a100for the per-category score.partialFingerprintsare disambiguated byline(andlocation) so multiple per-route image findings don't collapse into one code-scanning alert (SEO fingerprints unchanged).Review follow-up (addressed in this branch)
collectRoutes), parsing every.sveltefile once instead of twice (the head and image providers previously each walked + parsed the whole chain independently).Svelte Vitals · <mode>; the unused_configparameter was removed by the single-pass refactor.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
width/height) andloadingattribute advisoriesDocumentation