Repository navigation
refactor(pages): share the Link contract across framework adapters - #672
Merged
Merged
Conversation
Each adapter declared its own copy of the Link props, prefetch strategies and method union, and repeated the prefetch, click and visibility logic. Move them into `void/pages-client` next to the shared Form helpers: - `BaseLinkProps`, `LinkPrefetchStrategy`, `LinkNavigateEvent`, `ResolvedLink` - `resolveLink`, `linkPrefetchStrategies`, `prefetchLink`, `handleLinkClick`, `observeLinkVisible` Adapters keep reactivity, event naming, user handler composition, style merging and rendering. Vue's runtime prop check now compares against the shared props. This also removes drift between the copies: Svelte's `prefetch` accepts readonly arrays like the other adapters, and Vue clears a pending hover timer before starting a new one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yangshun
marked this pull request as ready for review
November 2, 2026 12:03
yangshun
marked this pull request as draft
November 2, 2026 12:48
yangshun
marked this pull request as ready for review
November 2, 2026 13:10
The runtime prop check now compares against the shared `BaseLinkProps`, not the removed `VoidLinkProps`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cpojer
approved these changes
Nov 3, 2026
cpojer
left a comment
Member
There was a problem hiding this comment.
Nice cleanup. Definitely best to keep the client logic abstract and shared across all the frameworks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
prefetcharrays.<Link prefetch={['hover', 'visible'] as const}>was a type error in Svelte only, because its props took a mutableArray. Svelte now acceptsReadonlyArraylike React, Vue and Solid.BaseLinkProps, so a mismatch failsvp run check.Summary
Each framework adapter had its own copy of the
Linkprops, the prefetch strategy and method unions, and roughly 150 lines of prefetch, click and visibility logic. This PR moves them intovoid/pages-client, next to the shared<Form>helpers fromform-presentation.ts:BaseLinkProps,LinkPrefetchStrategy,LinkNavigateEvent,ResolvedLink. They reuse the existingNavigationMethodandCacheForInputtypes instead of new copies.resolveLink: works out the method and destination, and throws the two "GET only" errorslinkPrefetchStrategiesprefetchLinkhandleLinkClick: the click checks, thenonNavigate, thenvisitobserveLinkVisibleEach adapter keeps only what differs by framework: reactivity, event-name casing, calling the user's own handlers (Vue handler arrays, Solid
[fn, data]pairs), mergingstyle, and rendering. The four adapter Link files are 466 lines shorter in total.The four copies had already diverged. This PR picks one behavior for each difference:
prefetcharray typeArray, soprefetch={['hover', 'visible'] as const}was a type error in Svelte onlyReadonlyArrayPrefetchOptionswithoutmethod; Vue and Svelte used the engine type and passedmethod: 'GET'method. It was alwaysGET, which is the router's defaultprefetchleft undefinedresolveLinkandlinkPrefetchStrategiestreatundefinedas offEverything else behaves as before: the same error messages, modified-click, download and cross-origin handling,
onNavigatecancellation, GET data merging, and hover/click/mount/visible prefetching.Not changed here:
LinkPropsis still exported publicly only from@void/solid. Exporting it from every adapter is a public API decision for a follow-up.Test plan
vp run check: all 33 projects pass, including Vue's runtime-props-vs-BaseLinkPropscheckvp lintandvp fmtpackages/void/test/unit/pages-link.test.ts: 22 tests for the shared helpershreftype checks: 219 passed after rebuilding the playgrounds withvp run build:allvp test run: 15,547 passed. The one failure isplugin-cloudflare-v2.test.ts> "keeps Container builds deferred…", which hit the 5-second limit at 5.5 seconds under full-suite load and passes on its own in about 1.5 seconds. It doesn't touch Link.Note for local testing: the adapter integration suites only build their playground when
distis missing. A staleplayground/*-pages/distmakes them test old code, so runvp run build:allfirst.🤖 Generated with Claude Code