fix: better type narrowing escape hatches for custom rel/type#735
Conversation
…type The `satisfies GenericLink` / `satisfies GenericScript` pattern the v3 docstrings and migration guide recommended did not actually type-check against useHead, because `GenericLink` / `GenericScript` are intentionally excluded from the `Link` / `Script` unions to prevent silent absorption of known `rel` / `type` values. Introduce `defineLink` and `defineScript` helpers that keep known-value strictness (e.g. `rel: 'preload'` still requires `as`, `type: 'module'` still requires `src` or inline content) while accepting custom values via `GenericLink` / `GenericScript`. Update docstrings, migration guide, release notes, and the useHead API reference accordingly.
… etc.) Several standard `<link>` rel keywords were missing from `KnownLinkRel` and the `Link` union, making them look like "custom" rels that needed `defineLink`. Add dedicated interfaces and union members for: `me`, `webmention`, `privacy-policy`, `terms-of-service`, `expect`, `compression-dictionary`, and `alternate stylesheet`. `alternate stylesheet` requires `title` per spec; `expect` carries an optional `blocking: 'render'`. Update `defineLink` examples and docs to use genuinely non-standard rels (`openid2.provider`, `EditURI`) instead of rels that are now directly supported.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds typed helpers Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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 |
Bundle Size Analysis
|
There was a problem hiding this comment.
Pull request overview
This PR fixes the previously recommended (but non-type-checking) pattern for custom <link rel> / <script type> values by introducing typed defineLink / defineScript helpers that preserve strict discriminated-union narrowing for known values while allowing truly unknown values via the generic fallback types. It also expands the built-in KnownLinkRel set to include several missing standard rel keywords so they work directly with useHead.
Changes:
- Add
defineLink/defineScripthelpers (exported fromunhead) with conditional inference types (InferLink/InferScript). - Expand
KnownLinkReland theLinkunion with additional standard rel interfaces (includingalternate stylesheetwith requiredtitle). - Update docs/migration guidance and add new type-level unit tests covering strictness + custom-value acceptance.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/unhead/test/unit/define.test.ts | Adds type-level tests covering new helpers and new standard rels. |
| packages/unhead/src/types/schema/script.ts | Updates GenericScript docs and adds KnownScriptType + InferScript inference utilities. |
| packages/unhead/src/types/schema/link.ts | Adds new standard rel interfaces, extends KnownLinkRel/Link, and adds InferLink. |
| packages/unhead/src/types/schema/head.ts | Re-exports the newly added link/script types and inference helpers. |
| packages/unhead/src/index.ts | Exposes defineLink / defineScript from the main package entry. |
| packages/unhead/src/define.ts | Implements the new defineLink / defineScript runtime helpers. |
| docs/head/7.api/composables/0.use-head.md | Updates API docs to recommend helpers instead of satisfies GenericLink/GenericScript. |
| docs/7.releases/1.v3.md | Updates v3 notes to reference the new helpers for custom rel/type values. |
| docs/6.migration-guide/1.v3.md | Updates migration guide examples to use defineLink / defineScript. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/unhead/src/types/schema/script.ts`:
- Around line 481-487: The Data script unions allow empty payloads because
DataScriptTextContent uses optional textContent/innerHTML; update the
InferScript-related union branches so that for JSON/LD/speculation scripts the
branch types (e.g., JsonLdScript, SpeculationRulesScript, ApplicationJsonScript
/ the DataScriptTextContent union used by InferScript) require at least one
content field (make textContent or innerHTML non-optional in their respective
union arms) so defineScript({ type: 'application/ld+json' }) without content is
rejected, and add a unit test asserting that creating those scripts without
textContent or innerHTML fails to prevent regression.
In `@packages/unhead/test/unit/define.test.ts`:
- Line 85: Update the two test titles in
packages/unhead/test/unit/define.test.ts that currently read 'rel="..."' to
reference 'type="..."' instead; specifically change the description string in
the it(...) call that begins with 'still enforces `src` or inline content on
rel="module"' (and the similar one at the other occurrence) so they read 'still
enforces `src` or inline content on type="module"' to accurately reflect script
type constraints.
🪄 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: ab74e4c2-7a92-4053-a570-efa96d265b05
📒 Files selected for processing (9)
docs/6.migration-guide/1.v3.mddocs/7.releases/1.v3.mddocs/head/7.api/composables/0.use-head.mdpackages/unhead/src/define.tspackages/unhead/src/index.tspackages/unhead/src/types/schema/head.tspackages/unhead/src/types/schema/link.tspackages/unhead/src/types/schema/script.tspackages/unhead/test/unit/define.test.ts
DataScriptTextContent used optional textContent/innerHTML in both union
branches, allowing empty data scripts like `{ type: 'application/ld+json' }`
to type-check with no content at all. Make each branch require its
respective content field so JsonLdScript, SpeculationRulesScript, and
ApplicationJsonScript enforce non-empty payloads at the type level.
Add type tests covering empty-payload rejection for ld+json, speculation
rules, application/json, and importmap. Rename two script test titles
from `rel="..."` to `type="..."` per review feedback.
🔗 Linked issue
❓ Type of change
📚 Description
The v3 docstrings and migration guide recommended
{ rel: '...' } satisfies GenericLink(and the script equivalent) as the pattern for custom rel/type values. That pattern never type-checked against `useHead`, because `GenericLink` and `GenericScript` are intentionally excluded from the `Link` / `Script` unions to prevent silent absorption of known values (e.g. `rel: 'preload'` without `as` staying an error instead of collapsing into the permissive shape).Two fixes:
Docstrings on `GenericLink`, `Link`, `GenericScript`, `Script`, plus the migration guide, use-head API reference, and v3 release notes all updated to point at the helpers (and to use genuinely non-standard rels like `openid2.provider` in examples). 17 new type-level tests in `packages/unhead/test/unit/define.test.ts` cover custom-value acceptance, strict-known-value enforcement, the newly-added standard rels, and edge cases like the Favicon rel union and preload image with only `imagesrcset`.
Summary by CodeRabbit
New Features
Documentation
Tests