Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(validate): false positives for warmup preloads and charset position#732

Merged
harlan-zw merged 1 commit into
mainfrom
fix/validate-false-positives
Apr 9, 2026
Merged

fix(validate): false positives for warmup preloads and charset position#732
harlan-zw merged 1 commit into
mainfrom
fix/validate-false-positives

Conversation

@harlan-zw

@harlan-zw harlan-zw commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator

🔗 Linked issue

Related to #725

❓ Type of change

  • 📖 Documentation
  • 🐞 Bug fix
  • 👌 Enhancement
  • ✨ New feature
  • 🧹 Chore
  • ⚠️ Breaking change

📚 Description

Fixes a few false positives in the recently-added performance validation rules and tightens the offending-tag context passed to report().

Warmup preloads (useScript)

useScript ships a preload link with as="script" and fetchpriority="low" to start fetching analytics-style scripts early at low priority. The new perf rules flagged this pattern twice:

  • preload-fetchpriority-conflict -- now skipped when as="script" (the warmup pattern is intentional).
  • preload-async-defer-conflict -- now skipped when the preload itself has fetchpriority="low".

Charset position

charset-not-early was producing two kinds of false positive:

  • Running on the client where the DOM order is already fixed by SSR + capo weights, so the rule had nothing to act on; now gated behind head.ssr.
  • Counting virtual tags (templateParams, titleTemplate) toward the head position, pushing <meta charset> past the threshold even though they don't render. Now sorts by capo weight (_w/_p) and filters to actual head element tags before measuring.

Better tag context in reports

canonical-og-url-mismatch, og-image-missing-dimensions, and missing-template-params-plugin now pass the offending tag to report() so consumers (and devtools) can highlight it.

Tests added for each new branch (preload+script warmup, preload+defer+low priority, virtual-tag charset position). 120/120 validate tests passing.

Summary by CodeRabbit

  • Bug Fixes
    • Refined preload and fetchpriority conflict detection for improved accuracy across script and non-script resources.
    • Enhanced validation error reporting with better tag context for clearer issue identification.
    • Improved charset positioning validation for server-side rendering scenarios with smarter tag filtering.

- Allow `preload` + `fetchpriority="low"` for `as="script"` (the warmup
  pattern used by `useScript` to start fetching at low priority).
- Skip `preload-async-defer-conflict` when the preload uses
  `fetchpriority="low"` for the same reason.
- Run `charset-not-early` only on SSR (DOM order is already set after
  hydration), and sort by capo weight while filtering virtual tags
  (`templateParams`, `titleTemplate`) so they don't inflate the position
  count.
- Pass tag references to several `report()` calls so consumers can
  surface the offending tag in error messages.
@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Refined multiple validation rules in the Unhead validation plugin: preload-fetchpriority-conflict now exempts non-script resources; preload-async-defer-conflict ignores low-priority preloads; enhanced reporting by attaching HeadTag context to specific rules; improved missing-template-params detection; and restricted charset-not-early validation to SSR contexts with weight-based tag ordering.

Changes

Cohort / File(s) Summary
Validation Rule Refinements
packages/unhead/src/plugins/validate.ts
Updated preload-fetchpriority-conflict to exclude non-script resources from conflict detection; refined preload-async-defer-conflict to skip conflicts when fetchpriority="low"; enhanced canonical-og-url-mismatch and og-image-missing-dimensions reports with HeadTag context; switched missing-template-params detection from some() to find() for tag attachment; restricted charset-not-early to SSR mode with capo weight-based tag ordering.
Validation Tests
packages/unhead/test/unit/plugins/validate.test.ts
Added tests validating preload-fetchpriority-conflict exemptions for non-script assets and fetchpriority="low" cases; new tests for preload-async-defer-conflict scenarios with low-priority preloads on both defer and async scripts; added edge-case test confirming charset-not-early position counting excludes virtual tags (templateParams, titleTemplate).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • PR #691 — Directly introduces the initial performance validation rules (preload-fetchpriority-conflict, preload-async-defer-conflict) that this PR refines and improves.
  • PR #725 — Also modifies the same validate.ts validations including charset-not-early and preload/fetchpriority-related checks with overlapping scope.
  • PR #722 — Modifies missing-template-params-plugin reporting behavior and HeadTag attachment logic, which this PR further improves.

Poem

🐰 With whiskers twitching, I check each rule,
Preloads and fetchpriority, no exception's cool,
Scripts say "nay!" to low-priority's call,
Tags attach themselves, contexts and all!
Validation runs true when SSR takes the stage,

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main changes: fixing false positives in validation rules for warmup preloads and charset position detection.
Description check ✅ Passed The description provides comprehensive coverage with linked issue, type of change selections, and detailed explanation of the three main areas of fixes with specific patterns addressed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/validate-false-positives

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 and usage tips.

@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Bundle Size Gzipped
Client (Minimal) 10.6 kB 4.3 kB
Server (Minimal) 10.3 kB 4.2 kB
Vue Client (Minimal) 11.6 kB 4.8 kB
Vue Server (Minimal) 11.3 kB 4.6 kB

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/unhead/src/plugins/validate.ts (1)

556-568: ⚠️ Potential issue | 🟡 Minor

Fix the remediation text for preload/script conflicts.

The warning now says to add fetchpriority="low" to the script, but this branch only skips the rule when the preload link has fetchpriority="low". Following the current message won't clear the warning.

✏️ Suggested wording
-                report('preload-async-defer-conflict', `Script "${tag.props.src}" is preloaded but has "${attr}" — preload escalates priority, defeating the purpose of ${attr}. Remove the preload or add fetchpriority="low" to the script.`, 'warn', preloadTag)
+                report('preload-async-defer-conflict', `Script "${tag.props.src}" is preloaded but has "${attr}" — preload escalates priority, defeating the purpose of ${attr}. Remove the preload or add fetchpriority="low" to the preload link.`, 'warn', preloadTag)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/unhead/src/plugins/validate.ts` around lines 556 - 568, The warning
text in the preload/script conflict check (see preloadScriptHrefs map and the
report call inside the loop that checks tag.tag === 'script' and
tag.props.async/defer) incorrectly advises adding fetchpriority="low" to the
script; update the remediation string so it tells the user to either remove the
preload or add fetchpriority="low" to the preload link (not the script), keeping
the rest of the message (script src, attr name, warn level, and preloadTag)
unchanged.
🧹 Nitpick comments (2)
packages/unhead/test/unit/plugins/validate.test.ts (1)

1046-1061: Add a client-side regression for the new SSR guard.

This case covers virtual-tag filtering, but it doesn't assert the other half of the fix: charset-not-early should be skipped when head.ssr is false. A small client createHead test here would keep that guard from regressing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/unhead/test/unit/plugins/validate.test.ts` around lines 1046 - 1061,
The test currently covers the SSR path using createValidationHead and
renderSSRHead for the 'charset-not-early' rule but lacks a client-side (non-SSR)
regression check; add a small test that uses createHead (or createValidationHead
with head.ssr set to false) to assert that when head.ssr is false the
'charset-not-early' rule is skipped (i.e., rules.find(r => r.id ===
'charset-not-early') returns falsy), ensuring the SSR guard preventing
client-side validation regressions remains enforced—place the new test alongside
the existing one and mirror the setup (title, templateParams, titleTemplate,
meta charset) but ensure head.ssr is explicitly false or use createHead to
simulate client behavior.
packages/unhead/src/plugins/validate.ts (1)

503-507: Please add regression coverage for the new tag payload.

These report(..., tag) calls change a devtools-facing contract, but the suite still only checks rule presence/source. A direct assertion that rule.tag points at the offending og:url, og:image, and templateParams tag would keep this from regressing.

Also applies to: 588-590

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/unhead/src/plugins/validate.ts` around lines 503 - 507, Tests do not
assert the new report(...) tag payload so regressions can reintroduce incorrect
tag pointers; update the validation test suite to add assertions that the
reported rule objects include a tag property pointing to the offending elements
for the rules 'canonical-og-url-mismatch' and 'og-image-missing-dimensions' (and
similarly for the rules around templateParams mentioned at lines ~588-590).
Locate the tests that currently check rule presence/source and extend them to
fetch the report for those rule ids and assert that report.tag === ogUrl (for
canonical-og-url-mismatch) and report.tag === metaByKey.get('og:image') (for
og-image-missing-dimensions), and add equivalent assertions for the
templateParams rule so the devtools-facing contract is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/unhead/src/plugins/validate.ts`:
- Around line 556-568: The warning text in the preload/script conflict check
(see preloadScriptHrefs map and the report call inside the loop that checks
tag.tag === 'script' and tag.props.async/defer) incorrectly advises adding
fetchpriority="low" to the script; update the remediation string so it tells the
user to either remove the preload or add fetchpriority="low" to the preload link
(not the script), keeping the rest of the message (script src, attr name, warn
level, and preloadTag) unchanged.

---

Nitpick comments:
In `@packages/unhead/src/plugins/validate.ts`:
- Around line 503-507: Tests do not assert the new report(...) tag payload so
regressions can reintroduce incorrect tag pointers; update the validation test
suite to add assertions that the reported rule objects include a tag property
pointing to the offending elements for the rules 'canonical-og-url-mismatch' and
'og-image-missing-dimensions' (and similarly for the rules around templateParams
mentioned at lines ~588-590). Locate the tests that currently check rule
presence/source and extend them to fetch the report for those rule ids and
assert that report.tag === ogUrl (for canonical-og-url-mismatch) and report.tag
=== metaByKey.get('og:image') (for og-image-missing-dimensions), and add
equivalent assertions for the templateParams rule so the devtools-facing
contract is covered.

In `@packages/unhead/test/unit/plugins/validate.test.ts`:
- Around line 1046-1061: The test currently covers the SSR path using
createValidationHead and renderSSRHead for the 'charset-not-early' rule but
lacks a client-side (non-SSR) regression check; add a small test that uses
createHead (or createValidationHead with head.ssr set to false) to assert that
when head.ssr is false the 'charset-not-early' rule is skipped (i.e.,
rules.find(r => r.id === 'charset-not-early') returns falsy), ensuring the SSR
guard preventing client-side validation regressions remains enforced—place the
new test alongside the existing one and mirror the setup (title, templateParams,
titleTemplate, meta charset) but ensure head.ssr is explicitly false or use
createHead to simulate client behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e81dafb5-47a8-4a5a-9dd8-09a9260c5ab5

📥 Commits

Reviewing files that changed from the base of the PR and between d03116f and 199465f.

📒 Files selected for processing (2)
  • packages/unhead/src/plugins/validate.ts
  • packages/unhead/test/unit/plugins/validate.test.ts

@harlan-zw harlan-zw merged commit 9c83934 into main Apr 9, 2026
6 checks passed
@harlan-zw harlan-zw mentioned this pull request Apr 9, 2026
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.

1 participant