Skip to content

Give vite build mode component-scoped rule coverage (Correctness/Security/Architecture/PERF009-010) - #112

Merged
oekazuma merged 14 commits into
mainfrom
vite-component-rules
Jul 5, 2026
Merged

oekazuma merged 14 commits into
mainfrom
vite-component-rules

Conversation

@oekazuma

@oekazuma oekazuma commented Jul 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • @svelte-vitals/vite's build mode previously only ran SEO/Performance rules against rendered HTML — it never populated RuleContext.components, so CORRECT001–004, SEC001–002, ARCH001–002, and PERF009–010 (component-scoped rules) silently emitted nothing
  • Extracted the CLI's .svelte-source parser (parseComponentFacts + shared AST utilities, ~350 lines) from svelte-vitals into @svelte-vitals/core (svelte-ast.ts, component-parse.ts) so it can be reused without duplication
  • Added a new, small file-scanning collector to @svelte-vitals/vite (direct tinyglobby + node:fs, no Runtime abstraction needed) and wired it into analyze()
  • All 10 rules are enabled by default in build mode, matching existing CLI precedent for shipping new rules within minor releases; opt out per-rule via the existing rules option
  • Dev overlay is unchanged (still SEO/Performance-only, rendered-HTML-based) — architecturally can't support a whole-project source scan on a per-request view
  • Docs updated (English + Japanese): plugin-mode.md, dev-overlay.md, choosing-a-package.md

Design

See docs/superpowers/specs/2026-07-05-vite-component-rules-design.md (approved design) and docs/superpowers/plans/2026-07-05-vite-component-rules.md (implementation plan).

Post-review fix (user-visible CLI behavior change)

Code review (94990ed) found and fixed a pre-existing bug in attrText (carried over verbatim from the original CLI code during extraction, not introduced by this PR): a mixed static/dynamic attribute value like href="/base/{slug}" was incorrectly treated as fully literal, returning the truncated static prefix ("/base/") instead of undefined. This is used by the CLI's <svelte:head> parsing for meta[content] (description/robots), link[rel|as|hreflang|href] (incl. PERF008 origin analysis), and script[type|src]. The fix makes attrText correctly return undefined for any such mixed value, matching its own doc comment and the sibling attrTextOf helper's existing behavior. Added a CLI-level regression test (parse-link-attrs.test.ts) locking in the corrected semantics. This is a correctness fix, not a feature change — the original (buggy) behavior was never intentional.

Test plan

  • pnpm test — all packages green (core 343, cli 284, vite 79, mcp 9)
  • pnpm typecheck — zero errors
  • pnpm lint — clean
  • pnpm --filter docs build — succeeds
  • pnpm build — all packages build cleanly (core's new svelte dependency resolves correctly, dist/ verified consistent)
  • End-to-end integration test (packages/vite/test/analyze.test.ts) traces a real unkeyed {#each} through the vite collector → parseComponentFacts → analyze() → a real CORRECT001 finding in the console report — not mocked at any layer
  • Extraction itself is a pure relocation (verified byte-for-byte identical during review); the one behavior change is the attrText bug fix described above, which is intentional and now covered by a regression test

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Build mode now scans .svelte source files under src/ (in addition to prerendered HTML) and applies component-scoped rules alongside existing checks, with the console summary including the number of components scanned.
    • The shared package now exposes parseComponentFacts and additional Svelte AST helper utilities for reuse.
  • Documentation
    • Updated build-mode vs dev overlay guides to clarify component-scoped rule coverage and the differing scope of analysis.
    • Plugin-mode docs now better explain what build-time verification covers and the ESM-only requirement.

oekazuma and others added 12 commits July 5, 2026 01:17
Extract parseComponentFacts (+ its shared AST utilities) from the CLI
package into @svelte-vitals/core so the vite plugin's build mode can add
CORRECT001-004, SEC001-002, ARCH001-002, and PERF009-010 support without
duplicating ~350 lines of AST-walking logic.
…rser

Also fixes packages/cli/test/parse-file.test.ts's attrValueOf import,
which the task brief's file list omitted but has the same dependency
on the relocated svelte-ast helper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qoHchC2eWjmw2rdm91kLq
…dency

Task 2 added `svelte` to packages/core/package.json but the lockfile
update was never committed alongside it.
… label (task 6 review)

ja docs mixed a bare English "component スコープ"/"componentルール" into
Japanese prose instead of the コンポーネントスコープ convention already
established in cli.md's suppression-directive section. Also replaced the
one-off "Bundle-Performance rules" label (used nowhere else in the docs)
with "component-scoped Performance rules", matching plugin-mode.md and
choosing-a-package.md's existing wording for the same PERF009/PERF010 pair.
@coderabbitai

coderabbitai Bot commented Jul 5, 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: 50 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: f3294941-6b03-437b-8d74-839dc7d22746

📥 Commits

Reviewing files that changed from the base of the PR and between 94990ed and a161cdb.

📒 Files selected for processing (1)
  • packages/cli/test/parse-link-attrs.test.ts
📝 Walkthrough

Walkthrough

This PR moves shared component-facts parsing and Svelte AST helpers into @svelte-vitals/core, updates CLI imports to use them, adds Vite build-mode scanning of src/**/*.svelte, and updates docs and changesets to match the expanded component-scoped rule coverage.

Changes

Component-facts relocation and Vite build-mode wiring

Layer / File(s) Summary
Core Svelte-AST utilities
packages/core/src/svelte-ast.ts, packages/core/src/index.ts, packages/core/package.json, packages/core/test/svelte-ast.test.ts
Adds shared AST helpers, re-exports them from the core barrel, adds the svelte dependency, and tests value/text/attribute/line helpers.
Core parseComponentFacts implementation
packages/core/src/component-parse.ts, packages/core/src/index.ts, packages/core/test/component-parse.test.ts
Adds parseComponentFacts in core and tests the extracted component-facts pipeline.
CLI switches to core parser
packages/cli/src/providers/source/parse.ts, packages/cli/src/providers/source/components.ts, packages/cli/src/providers/source/adapters/*, packages/cli/test/*
Switches CLI parsing and adapter imports to @svelte-vitals/core and removes the local component-analysis implementation.
Vite build-mode component scanning
packages/vite/src/providers/source/components.ts, packages/vite/src/analyze.ts, packages/vite/test/*
Adds filesystem-based component scanning, passes component facts into analyze(), updates coverage reporting, and adds integration tests.
Documentation and release notes
docs/src/content/docs/guides/*, docs/src/content/docs/ja/guides/*, docs/superpowers/plans/*, docs/superpowers/specs/*, .changeset/*
Updates guides, planning/spec documents, and release notes to describe the new build-mode component-scoped coverage.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • oekazuma/svelte-vitals#68: Introduces the component-facts pipeline that this PR extracts into core and reuses from Vite build mode.
  • oekazuma/svelte-vitals#70: Extends the same ComponentFacts surface with HTML and javascript: URL facts that rely on the shared parser moved here.
  • oekazuma/svelte-vitals#110: Touches the same component-facts/suppression surface that this refactor centralizes in @svelte-vitals/core.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: extending Vite build mode to cover component-scoped rules, including the listed rule groups.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vite-component-rules

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.

Actionable comments posted: 3

🧹 Nitpick comments (5)
packages/vite/src/providers/source/components.ts (1)

20-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated "empty ComponentFacts" default object.

This default fallback object duplicates the shape defined by ComponentFacts in @svelte-vitals/core (and likely mirrors an equivalent default in the CLI's collector). Extracting a shared emptyComponentFacts(file) helper (or default constant) in core would keep both packages in sync if the interface gains a field.

🤖 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/vite/src/providers/source/components.ts` around lines 20 - 32, The
fallback “empty ComponentFacts” object is duplicated here and should be
centralized to avoid drift from the shared ComponentFacts shape. Extract a
shared helper or default factory such as emptyComponentFacts(file) in
`@svelte-vitals/core`, then update the components.ts fallback to use it so this
source and any CLI collector stay in sync if ComponentFacts changes.
packages/vite/src/analyze.ts (1)

46-50: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Independent collectors run sequentially.

collectComponentFacts(cwd) doesn't depend on project/htmlLang, so it could run concurrently with collectRenderedProject via Promise.all. Given this runs once per build, the reward is marginal, so treating this as optional.

🤖 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/vite/src/analyze.ts` around lines 46 - 50, The collectors in
analyze() are independent, and collectComponentFacts(cwd) does not depend on the
htmlLang/project result. Update the analyze flow to run
collectRenderedProject(cwd, htmlLang) and collectComponentFacts(cwd)
concurrently with Promise.all, while keeping
collectRenderedHeads(prerenderPagesDir) before them and preserving the same data
passed into runRules and applyRuleSeverities.
packages/core/test/svelte-ast.test.ts (1)

54-70: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a mixed-content regression test for attrText.

Once the attrText fix (flagged in svelte-ast.ts) lands, add a case like attrText([{ type: 'Attribute', name: 'name', value: [{ type: 'Text', data: 'prefix' }, { type: 'ExpressionTag' }] }], 'name') asserting undefined, to prevent regressions.

🤖 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/svelte-ast.test.ts` around lines 54 - 70, Add a regression
test in the attrText spec to cover mixed-content attributes. Update the existing
attrText test block in svelte-ast.test.ts to assert that when an Attribute value
contains both Text and an ExpressionTag, attrText returns undefined rather than
a string. Use the attrText helper and the mixed-value Attribute shape to target
the same behavior covered by the svelte-ast.ts fix.
docs/src/content/docs/guides/plugin-mode.md (1)

8-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify the scan scope wording.

collectComponentFacts() walks src/**/*.svelte, so "directly under src/" reads as top-level-only and understates the actual coverage for nested components. Please reword this to say "all .svelte files under src/" or "recursively under src/".

♻️ Proposed wording tweak
-Build mode additionally scans your `.svelte` source directly under `src/` for ...
+Build mode additionally scans all `.svelte` files under `src/` recursively for ...
🤖 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 `@docs/src/content/docs/guides/plugin-mode.md` at line 8, The build-mode scan
scope wording in the plugin-mode docs is too narrow: `collectComponentFacts()`
actually walks all `.svelte` files recursively under `src/`, not just files
directly under `src/`. Update the description near the `@svelte-vitals/vite`
overview to say “all `.svelte` files under `src/`” or “recursively under `src/`”
so the documented coverage matches the `collectComponentFacts()` behavior.
packages/cli/src/providers/source/adapters/svelte-meta-tags.ts (1)

9-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate findAttr with the core export.

This file (and svelte-seo.ts) still defines a local findAttr that is functionally identical to findAttr now exported from @svelte-vitals/core (already used directly in parse.ts, e.g. findAttr(node.attributes, 'as')). Since this PR's goal is centralizing shared AST helpers, both adapters could import findAttr from core instead of re-declaring it, avoiding future drift between the two copies.

♻️ Proposed consolidation
-import { attrValueOf, attrTextOf } from '`@svelte-vitals/core`';
+import { attrValueOf, attrTextOf, findAttr } from '`@svelte-vitals/core`';

-function findAttr(attributes: Node[], name: string): Node | undefined {
-  return attributes.find((a) => a?.type === 'Attribute' && a.name === name);
-}

Please confirm core's findAttr implementation matches this local one exactly (same type === 'Attribute' check) before removing the duplicates.

🤖 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/providers/source/adapters/svelte-meta-tags.ts` around lines
9 - 11, The local findAttr helper is duplicated in the Svelte adapter and should
be consolidated with the shared core export. Update the adapter to import
findAttr from `@svelte-vitals/core` instead of redefining it, and make the same
change in the matching svelte-seo.ts adapter so both use the single shared
helper. Before removing the local copies, verify the core findAttr behavior
matches the current Node[] lookup and type === 'Attribute' check used by
findAttr.
🤖 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 `@docs/superpowers/specs/2026-07-05-vite-component-rules-design.md`:
- Around line 201-205: The fenced snippet in the spec is missing a language tag,
causing the markdownlint MD040 warning. Update the fenced block around the
analyzed output text to use the appropriate tag (ts) so the documentation stays
lint-clean. Locate the affected fence in the spec section containing the
“Analyzed ${heads.length} prerendered route(s)” text and change only the fence
marker, not the snippet contents.

In `@packages/core/src/svelte-ast.ts`:
- Around line 55-67: The attrText helper is treating mixed static/dynamic
attributes as fully static because it only collects Text nodes from the
attr.value array. Update attrText in svelte-ast.ts to use the same mixed-content
check as attrTextOf/valueFromNodes, so any ExpressionTag anywhere in the array
causes undefined to be returned. If helpful, refactor attrText to delegate to
attrTextOf to remove duplication, and ensure attrTextOf is defined before it is
called or reorder the helpers accordingly.

In `@packages/vite/src/providers/source/components.ts`:
- Around line 16-34: The catch in collectComponentFacts is swallowing read/parse
failures and returning an empty fact object, which makes failed files look
successfully scanned. Update collectComponentFacts and the CLI mirror to surface
the error (for example via logging or an error collection) when readFile or
parseComponentFacts fails, and do not treat the file as a clean component in the
normal results. Keep using the existing collectComponentFacts flow and its
component file handling, but ensure failures are visible instead of silently
producing empty facts.

---

Nitpick comments:
In `@docs/src/content/docs/guides/plugin-mode.md`:
- Line 8: The build-mode scan scope wording in the plugin-mode docs is too
narrow: `collectComponentFacts()` actually walks all `.svelte` files recursively
under `src/`, not just files directly under `src/`. Update the description near
the `@svelte-vitals/vite` overview to say “all `.svelte` files under `src/`” or
“recursively under `src/`” so the documented coverage matches the
`collectComponentFacts()` behavior.

In `@packages/cli/src/providers/source/adapters/svelte-meta-tags.ts`:
- Around line 9-11: The local findAttr helper is duplicated in the Svelte
adapter and should be consolidated with the shared core export. Update the
adapter to import findAttr from `@svelte-vitals/core` instead of redefining it,
and make the same change in the matching svelte-seo.ts adapter so both use the
single shared helper. Before removing the local copies, verify the core findAttr
behavior matches the current Node[] lookup and type === 'Attribute' check used
by findAttr.

In `@packages/core/test/svelte-ast.test.ts`:
- Around line 54-70: Add a regression test in the attrText spec to cover
mixed-content attributes. Update the existing attrText test block in
svelte-ast.test.ts to assert that when an Attribute value contains both Text and
an ExpressionTag, attrText returns undefined rather than a string. Use the
attrText helper and the mixed-value Attribute shape to target the same behavior
covered by the svelte-ast.ts fix.

In `@packages/vite/src/analyze.ts`:
- Around line 46-50: The collectors in analyze() are independent, and
collectComponentFacts(cwd) does not depend on the htmlLang/project result.
Update the analyze flow to run collectRenderedProject(cwd, htmlLang) and
collectComponentFacts(cwd) concurrently with Promise.all, while keeping
collectRenderedHeads(prerenderPagesDir) before them and preserving the same data
passed into runRules and applyRuleSeverities.

In `@packages/vite/src/providers/source/components.ts`:
- Around line 20-32: The fallback “empty ComponentFacts” object is duplicated
here and should be centralized to avoid drift from the shared ComponentFacts
shape. Extract a shared helper or default factory such as
emptyComponentFacts(file) in `@svelte-vitals/core`, then update the components.ts
fallback to use it so this source and any CLI collector stay in sync if
ComponentFacts changes.
🪄 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: 037edd11-5630-4a13-9600-27c8cad402db

📥 Commits

Reviewing files that changed from the base of the PR and between 969b07f and 2f94444.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (28)
  • .changeset/vite-component-rules-cli.md
  • .changeset/vite-component-rules-core.md
  • .changeset/vite-component-rules-vite.md
  • docs/src/content/docs/guides/choosing-a-package.md
  • docs/src/content/docs/guides/dev-overlay.md
  • docs/src/content/docs/guides/plugin-mode.md
  • docs/src/content/docs/ja/guides/choosing-a-package.md
  • docs/src/content/docs/ja/guides/dev-overlay.md
  • docs/src/content/docs/ja/guides/plugin-mode.md
  • docs/superpowers/plans/2026-07-05-vite-component-rules.md
  • docs/superpowers/specs/2026-07-05-vite-component-rules-design.md
  • packages/cli/src/providers/source/adapters/svelte-meta-tags.ts
  • packages/cli/src/providers/source/adapters/svelte-seo.ts
  • packages/cli/src/providers/source/components.ts
  • packages/cli/src/providers/source/parse.ts
  • packages/cli/test/collect-component-facts.test.ts
  • packages/cli/test/parse-file.test.ts
  • packages/cli/test/suppression-e2e.test.ts
  • packages/core/package.json
  • packages/core/src/component-parse.ts
  • packages/core/src/index.ts
  • packages/core/src/svelte-ast.ts
  • packages/core/test/component-parse.test.ts
  • packages/core/test/svelte-ast.test.ts
  • packages/vite/src/analyze.ts
  • packages/vite/src/providers/source/components.ts
  • packages/vite/test/analyze.test.ts
  • packages/vite/test/collect-component-facts.test.ts

Comment thread docs/superpowers/specs/2026-07-05-vite-component-rules-design.md Outdated
Comment thread packages/core/src/svelte-ast.ts
Comment thread packages/vite/src/providers/source/components.ts
attrText's array branch filtered for Text nodes but never checked for
an ExpressionTag alongside them, so href="prefix{expr}" incorrectly
returned "prefix" instead of undefined — inconsistent with its own doc
comment and with the sibling attrTextOf, which already guards this
case. Also fixes a missing MD040 language tag on a fenced code block
in the design doc.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qoHchC2eWjmw2rdm91kLq

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

This PR extends @svelte-vitals/vite build-mode analysis to include component-scoped rules by scanning .svelte source under src/ and wiring the resulting components facts into the core rule pipeline. To avoid duplication, it extracts the CLI’s component-facts parser (and shared Svelte AST helpers) into @svelte-vitals/core, then updates CLI/vite to consume the shared implementation and documents the new coverage.

Changes:

  • Extract .svelte AST utilities + parseComponentFacts() into @svelte-vitals/core (with new tests and a new svelte dependency).
  • Add a new vite build-mode source collector (src/**/*.svelte) and pass components into runRules(); extend console coverage output and add integration/unit tests.
  • Update CLI import paths, add changesets, and update English/Japanese docs to reflect build-mode vs dev-overlay scope.

Reviewed changes

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

Show a summary per file
File Description
pnpm-lock.yaml Locks the new svelte dependency for packages/core.
packages/vite/test/collect-component-facts.test.ts Adds filesystem-based unit tests for the new vite component collector.
packages/vite/test/analyze.test.ts Extends analyze integration test to assert a real component-scoped finding is produced.
packages/vite/src/providers/source/components.ts New vite build-mode collector that globs + parses src/**/*.svelte.
packages/vite/src/analyze.ts Wires collected component facts into rule execution and updates coverage note output.
packages/core/test/svelte-ast.test.ts Adds unit tests for extracted Svelte AST helper utilities.
packages/core/test/component-parse.test.ts Moves/creates tests for parseComponentFacts() in core.
packages/core/src/svelte-ast.ts New shared AST helper module exported from core.
packages/core/src/index.ts Re-exports parseComponentFacts and AST helpers from the core package root.
packages/core/src/component-parse.ts New shared parseComponentFacts() implementation in core.
packages/core/package.json Adds svelte as a core dependency (for svelte/compiler).
packages/cli/test/suppression-e2e.test.ts Updates parseComponentFacts import to come from @svelte-vitals/core.
packages/cli/test/parse-file.test.ts Updates tests to import attrValueOf from core (no longer from CLI parse module).
packages/cli/test/collect-component-facts.test.ts Adds a focused CLI test for the Runtime-based component collector.
packages/cli/src/providers/source/parse.ts Removes duplicated AST/component parsing helpers; consumes shared core helpers for head parsing.
packages/cli/src/providers/source/components.ts Switches component parsing to @svelte-vitals/core export.
packages/cli/src/providers/source/adapters/svelte-seo.ts Switches attrValueOf/attrTextOf imports to core.
packages/cli/src/providers/source/adapters/svelte-meta-tags.ts Switches attrValueOf/attrTextOf imports to core.
docs/superpowers/specs/2026-07-05-vite-component-rules-design.md Adds the approved design spec documenting the approach and constraints.
docs/superpowers/plans/2026-07-05-vite-component-rules.md Adds the implementation plan document.
docs/src/content/docs/ja/guides/plugin-mode.md Documents new build-mode source scanning and component-scoped rule coverage (JA).
docs/src/content/docs/ja/guides/dev-overlay.md Clarifies dev overlay remains rendered-only and excludes component-scoped rules (JA).
docs/src/content/docs/ja/guides/choosing-a-package.md Updates comparison table and explanation of coverage differences (JA).
docs/src/content/docs/guides/plugin-mode.md Documents new build-mode source scanning and component-scoped rule coverage (EN).
docs/src/content/docs/guides/dev-overlay.md Clarifies dev overlay remains rendered-only and excludes component-scoped rules (EN).
docs/src/content/docs/guides/choosing-a-package.md Updates comparison table and explanation of coverage differences (EN).
.changeset/vite-component-rules-vite.md Declares a minor release for new default build-mode coverage in the vite plugin.
.changeset/vite-component-rules-core.md Declares a minor release for new core exports + svelte dependency.
.changeset/vite-component-rules-cli.md Declares a patch release for the CLI internal refactor to shared core parser.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

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

Comment on lines +54 to +67
/** Static string of an attribute (e.g. name="description"), or undefined if dynamic/absent. */
export function attrText(attributes: Node[], name: string): string | undefined {
const attr = findAttr(attributes, name);
if (!attr) return undefined;
const v = attr.value;
if (v === true) return '';
if (Array.isArray(v)) {
if (v.some((n: Node) => n?.type === 'ExpressionTag')) return undefined;
return v
.filter((n: Node) => n?.type === 'Text')
.map((n: Node) => String(n.data ?? ''))
.join('');
}
return undefined; // single ExpressionTag → not a literal

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good catch, and you're right — this is a genuine user-visible CLI behavior change, not purely internal to this PR's refactor. Updated the PR description to call it out explicitly, and added a CLI-level regression test in parse-link-attrs.test.ts (commit a161cdb) covering <link href="/base/{slug}">: before the fix this returned the truncated "/base/" as if it were a complete literal URL (which could have fed wrong data into PERF008's origin analysis); now it correctly returns undefined so PERF008 skips it as non-literal. The stricter behavior is intentional — it's a correctness fix for a pre-existing bug, not a new feature.

Regression test for the attrText fix in 94990ed, at the CLI's actual
<svelte:head> parsing boundary (PERF008 origin analysis reads
link.href from this path).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qoHchC2eWjmw2rdm91kLq
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