Skip to content

feat: PERF009 — heavy dependency import (Bundle slice) - #74

Merged
oekazuma merged 2 commits into
mainfrom
feat/bundle-heavy-imports
Jul 1, 2026
Merged

oekazuma merged 2 commits into
mainfrom
feat/bundle-heavy-imports

Conversation

@oekazuma

@oekazuma oekazuma commented Jul 1, 2026 •

Copy link
Copy Markdown
Owner

The Bundle/perf slice of #69 — flag imports of well-known heavy / non-tree-shakeable packages that bloat the bundle (a common "AI wrote import _ from 'lodash'" mistake). Allowlist-precise (exact specifier match), reusing the component-body scan (ctx.components, CLI/static).

New rule

ID Check Severity
PERF009 Heavy dependency import info

Flags import … from 'lodash' / 'moment'. Matched exactly — a subpath import (lodash/debounce) is the fix, so it passes. Reported under the existing performance category.

What

  • ComponentFacts gains imports — module specifiers of every import in the instance and module <script> (ESTree ImportDeclaration.source.value).
  • componentRule's ComponentCategory widens to include 'performance' (a component-scoped perf rule).
  • Allowlist maps each heavy package to its lighter alternative; extensible.

Tests / docs

  • Parser: imports from instance + module scripts; subpath specifiers verbatim.
  • Rule: flags lodash/moment; passes lodash/debounce / date-fns / no imports; no-op when ctx.components unset.
  • 2 docs pages (PERF009 en+ja), changeset, design spec.

pnpm -r test (536: core 241 / vite 76 / cli 210 / mcp 9), pnpm -r typecheck, pnpm lint, pnpm --filter docs build (107 pages) — all green.

Out of scope

Real byte-size / bundle analysis (needs a bundler), configurable allowlist, and import * as namespace heuristics — allowlist only for now.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a new performance check that flags direct imports from a small set of heavy dependencies and suggests lighter alternatives.
    • Expanded rule documentation with guidance and examples in English and Japanese.
  • Bug Fixes

    • Improved component analysis to capture imports from both standard and module scripts, including when files fail to parse.
  • Tests

    • Added coverage for import detection and the new performance rule behavior.

Flags an import from a well-known heavy / non-tree-shakeable package (lodash,
moment), matched by exact specifier so subpath imports (lodash/debounce) pass.
Reused the component scan; ComponentFacts gains `imports` (module specifiers from
the instance + module scripts). componentRule's category widens to 'performance';
reported under the performance category (info).

Docs (en+ja), changeset, spec, tests (import capture + rule). pnpm -r test 536
green; typecheck, lint, docs build (107 pages) green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@oekazuma, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

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, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b46cb17-a3e3-4172-8ced-551d2c9275a3

📥 Commits

Reviewing files that changed from the base of the PR and between 32698e0 and 5845847.

📒 Files selected for processing (2)
  • packages/core/src/rules/performance/perf009-heavy-import.ts
  • packages/core/test/bundle-rules.test.ts
📝 Walkthrough

Walkthrough

This PR adds PERF009, a performance rule flagging exact imports of heavy, non-tree-shakeable packages (lodash, moment). It extends ComponentFacts with an imports field populated by CLI parsing, extends ComponentCategory with performance, registers the new rule, and adds tests and documentation.

Changes

PERF009 Heavy Import Rule

Layer / File(s) Summary
Import collection in ComponentFacts and CLI parser
packages/core/src/component.ts, packages/cli/src/providers/source/parse.ts, packages/cli/src/providers/source/components.ts, packages/cli/test/parse-component-facts.test.ts
ComponentFacts gains an imports: string[] field; parseComponentFacts collects import specifiers from module and instance scripts via a new collectImportSources helper; parse-failure fallback and tests updated accordingly.
Rule category, implementation, and registry wiring
packages/core/src/rules/component-rule.ts, packages/core/src/rules/performance/perf009-heavy-import.ts, packages/core/src/rules/index.ts, packages/core/src/index.ts
ComponentCategory extended with performance; new perf009HeavyImport rule flags exact matches against a HEAVY_PACKAGES allowlist (lodash, moment) with recommendations; rule registered in allRules and re-exported.
Test coverage
packages/core/test/bundle-rules.test.ts, packages/core/test/architecture-rules.test.ts, packages/core/test/correctness-rules.test.ts, packages/core/test/security-rules.test.ts
New suite validates flagged/non-flagged imports and channel behavior; existing fixtures updated with imports: [].
Documentation and design spec
.changeset/bundle-heavy-imports.md, docs/src/content/docs/rules/perf009.md, docs/src/content/docs/ja/rules/perf009.md, docs/superpowers/specs/2026-07-01-bundle-heavy-imports-design.md
Adds changeset, English/Japanese rule docs, and design spec describing scope, matching rules, and out-of-scope items.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI Parser
  participant Facts as ComponentFacts
  participant Rule as perf009HeavyImport
  participant Report as Findings Report

  CLI->>Facts: collect imports from script blocks
  Facts->>Rule: provide imports[] via RuleContext
  Rule->>Rule: match against HEAVY_PACKAGES allowlist
  alt heavy package matched
    Rule->>Report: emit performance/info finding with recommendation
  else no match
    Rule-->>Report: no finding
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Possibly related PRs

  • oekazuma/svelte-vitals#68: Both PRs extend the same parseComponentFacts/ComponentFacts pipeline in packages/cli/src/providers/source/parse.ts and packages/core/src/component.ts.
  • oekazuma/svelte-vitals#71: Both PRs extend ComponentFacts and ComponentCategory handling with new fields/categories in the same core files.
  • oekazuma/svelte-vitals#62: Both PRs extend the core rule registry and public exports to add new performance rules via allRules.

Poem

A rabbit spied a heavy sack,
"Lodash whole? No, give it back!"
Now imports counted, one by one,
PERF009 flags the ones un-shrunk. 🐇
Hop lighter, bundles trim and neat—
Tree-shaken code, oh what a treat! 🌳

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the new PERF009 heavy dependency import rule in the bundle slice.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/bundle-heavy-imports

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/core/src/component.ts (1)

43-44: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

imports lacks location info, forcing PERF009 to report line: 0.

Unlike htmlTags/javascriptUrls (which use SourceSpan[]), imports is plain string[]. As a result, perf009HeavyImport's bad() hardcodes line: 0 for every finding (see packages/core/src/rules/performance/perf009-heavy-import.ts), so all PERF009 diagnostics point to the top of the file instead of the actual import line — this will look like a bug in generated reports.

Consider changing imports to carry the source line (e.g., { specifier: string; line: number }[]) so collectImportSources in parse.ts can populate it and the rule can emit accurate locations.

♻️ Sketch of the type change
-  /** Module specifiers of every `import` in the instance + module scripts (Bundle PERF009). */
-  imports: string[];
+  /** Every `import` in the instance + module scripts, with source line (Bundle PERF009). */
+  imports: { specifier: string; line: number }[];
🤖 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/component.ts` around lines 43 - 44, The `imports` field in
`Component` only stores specifiers, so `perf009HeavyImport` cannot report real
source locations and falls back to `line: 0`. Update the `Component` type to
carry line information for imports (similar to `htmlTags` and `javascriptUrls`),
then adjust `collectImportSources` in `parse.ts` to populate that data from each
import’s source span. Finally, update `perf009HeavyImport.bad()` in
`perf009-heavy-import.ts` to read the stored line from `imports` instead of
hardcoding `0`.
packages/core/src/rules/performance/perf009-heavy-import.ts (1)

7-10: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Guard against prototype-chain lookups in HEAVY_PACKAGES.

src in HEAVY_PACKAGES checks the prototype chain, so a component importing a package literally named constructor, toString, or hasOwnProperty would incorrectly match, and HEAVY_PACKAGES[src] would resolve to an inherited Object.prototype value in the message.

🛡️ Proposed fix
   applies: (c) => c.imports.length > 0,
   bad: (c) =>
     c.imports
-      .filter((src) => src in HEAVY_PACKAGES)
+      .filter((src) => Object.hasOwn(HEAVY_PACKAGES, src))
       .map((src) => ({ line: 0, message: `Heavy import "${src}" — ${HEAVY_PACKAGES[src]}` }))
 });

Also applies to: 22-25

🤖 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/rules/performance/perf009-heavy-import.ts` around lines 7 -
10, `HEAVY_PACKAGES` lookup in `perf009-heavy-import.ts` should not rely on
prototype-chain membership checks. Update the logic that decides whether a
package is heavy (the `HEAVY_PACKAGES` map lookup used with `src`) to use an
own-property check or a null-prototype map so imports like `constructor` or
`toString` cannot match inherited `Object.prototype` keys. Keep the warning
message generation in sync with the safe lookup in the same rule implementation.
🤖 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.

Nitpick comments:
In `@packages/core/src/component.ts`:
- Around line 43-44: The `imports` field in `Component` only stores specifiers,
so `perf009HeavyImport` cannot report real source locations and falls back to
`line: 0`. Update the `Component` type to carry line information for imports
(similar to `htmlTags` and `javascriptUrls`), then adjust `collectImportSources`
in `parse.ts` to populate that data from each import’s source span. Finally,
update `perf009HeavyImport.bad()` in `perf009-heavy-import.ts` to read the
stored line from `imports` instead of hardcoding `0`.

In `@packages/core/src/rules/performance/perf009-heavy-import.ts`:
- Around line 7-10: `HEAVY_PACKAGES` lookup in `perf009-heavy-import.ts` should
not rely on prototype-chain membership checks. Update the logic that decides
whether a package is heavy (the `HEAVY_PACKAGES` map lookup used with `src`) to
use an own-property check or a null-prototype map so imports like `constructor`
or `toString` cannot match inherited `Object.prototype` keys. Keep the warning
message generation in sync with the safe lookup in the same rule implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f206f6c5-c419-4414-b500-b0e96239ac87

📥 Commits

Reviewing files that changed from the base of the PR and between f6a0121 and 32698e0.

📒 Files selected for processing (16)
  • .changeset/bundle-heavy-imports.md
  • docs/src/content/docs/ja/rules/perf009.md
  • docs/src/content/docs/rules/perf009.md
  • docs/superpowers/specs/2026-07-01-bundle-heavy-imports-design.md
  • packages/cli/src/providers/source/components.ts
  • packages/cli/src/providers/source/parse.ts
  • packages/cli/test/parse-component-facts.test.ts
  • packages/core/src/component.ts
  • packages/core/src/index.ts
  • packages/core/src/rules/component-rule.ts
  • packages/core/src/rules/index.ts
  • packages/core/src/rules/performance/perf009-heavy-import.ts
  • packages/core/test/architecture-rules.test.ts
  • packages/core/test/bundle-rules.test.ts
  • packages/core/test/correctness-rules.test.ts
  • packages/core/test/security-rules.test.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new component-scoped performance rule (PERF009) to flag bare imports of known heavy, non-tree-shakeable dependencies (currently lodash and moment) using the existing CLI/static component-facts channel (ctx.components). This expands ComponentFacts to include collected import specifiers from both instance and module scripts, enabling deterministic bundle/perf hygiene checks aligned with the roadmap in #69.

Changes:

  • Introduces PERF009 rule (info, performance) that flags exact-match heavy dependency imports and suggests lighter/subpath alternatives.
  • Extends CLI component parsing to collect import specifiers from both <script> and <script module> into ComponentFacts.imports.
  • Adds tests, docs (EN/JA), a design spec, and a changeset for the new rule.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/core/test/security-rules.test.ts Updates ComponentFacts test helper to include new imports field.
packages/core/test/correctness-rules.test.ts Updates ComponentFacts test helper to include new imports field.
packages/core/test/architecture-rules.test.ts Updates ComponentFacts test helper to include new imports field.
packages/core/test/bundle-rules.test.ts Adds new PERF009 unit tests for pass/fail/no-op behaviors.
packages/core/src/rules/performance/perf009-heavy-import.ts Implements PERF009 heavy import rule using component facts.
packages/core/src/rules/index.ts Registers/exports PERF009 in the core rules list and exports.
packages/core/src/rules/component-rule.ts Extends component-rule categories to allow performance-scoped component rules.
packages/core/src/index.ts Re-exports PERF009 from the core entrypoint.
packages/core/src/component.ts Adds imports: string[] to ComponentFacts.
packages/cli/test/parse-component-facts.test.ts Adds parser tests for collecting imports from instance/module scripts and preserving subpaths.
packages/cli/src/providers/source/parse.ts Collects ImportDeclaration.source.value into imports during static parsing.
packages/cli/src/providers/source/components.ts Ensures parse-failure fallback ComponentFacts includes imports: [].
docs/superpowers/specs/2026-07-01-bundle-heavy-imports-design.md Adds design spec for PERF009 and import-fact capture.
docs/src/content/docs/rules/perf009.md Adds EN rule documentation page for PERF009.
docs/src/content/docs/ja/rules/perf009.md Adds JA rule documentation page for PERF009.
.changeset/bundle-heavy-imports.md Declares release notes/version bumps for PERF009 and ComponentFacts.imports.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/core/src/rules/performance/perf009-heavy-import.ts Outdated
…review)

Match the heavy-package allowlist with Object.hasOwn (not `in`), so inherited keys
like `toString`/`constructor` never false-match; dedupe per specifier so the same
package imported in both <script module> and <script> isn't double-penalized.
Tests added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants