Repository navigation
fix(pages): compare a Link's query when marking it current - #719
Merged
Merged
Conversation
A Link to the current path was always marked with aria-current="page" and data-void-active, even when its href changed only the query. On /posts?page=2, Previous and Next pagination links were both announced as the current page. A link with a query is now current only when each of its params has the same value in the current URL. The current URL can have extra params in any order, so a filter tab stays current while paging. GET data counts as part of the query, links without a query still ignore it, the hash is still ignored, and activeMatch="prefix" still compares only the path on nested pages. Fixes voidzero-dev#713 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cpojer
pushed a commit
that referenced
this pull request
Nov 5, 2026
## What changed Locally, React Pages apps in this repo could stop hydrating after `vp run build`. The browser threw: ``` SyntaxError: The requested module '/node_modules/.vite/deps/void_pages-client.js?v=…' does not provide an export named 'isVoidActionError' ``` The client entry never loaded, so `#app` was never marked hydrated. `websockets-playground.test.ts` timed out on `/chat` and `/notifications` (noted in #719). CI passed because it starts with no Vite cache. ### Cause In dev, the React adapter listed `void/pages-client` and `void/pages-prefetch` in the client `optimizeDeps.include` (#620). It listed `void/live/client` in the worker include. Here `void` is a linked workspace package. Vite serves a linked package as source unless it is listed in `include`, and a listed one is pre-bundled into `node_modules/.vite`. That cache is keyed by the lockfile, config and `NODE_ENV`, not by the linked package's files, so rebuilding Void left the old copy in place. #715 added `isVoidActionError` to `void/pages-client` and re-exported it from `@void/react`. Any cache written before #715 then broke the client. ### Fix `installedDependencies()` keeps a Void entry in `include` only when it resolves into `node_modules`: | `void` install | Before | After | | --- | --- | --- | | From the registry (`node_modules`) | pre-bundled | pre-bundled (keeps #620's cold-start fix) | | Linked workspace package | pre-bundled, stale after a rebuild | served as source, always the current build | This only changes dev behavior for linked installs. Apps that install `void` from npm see no difference. ## For reviewers - **Served as source:** in a linked install, these entries pull in only `@void/edge`, which is linked too and has no imports. Serving them as source therefore can't trigger a late dependency discovery or a full reload. - **`void/live/client`:** the worker include had the same stale-copy hazard, so the same rule applies there. - **Resolution:** `import.meta.resolve` from `@void/react`. `void` is a peer dependency, so this finds the same `void` the app uses. ## Verification - Replayed the failure: built `void` at #715's parent and ran the websockets suite to fill the cache. Then I rebuilt on this branch and ran it again. Before this change: 2 failed. After: 3/3 passed on two runs. - `packages/react/test/unit/plugin-pages.test.ts`: 34 passed. A new unit test covers installed paths (pnpm, npm, Windows) and a linked path. The dev-config test now checks that this workspace's linked `void` is not pre-bundled. - `vp run check`, lint and format are clean. - Full `vp test run`: 774 of 777 files passed (1 skipped). The other 2 files were the Vue and Svelte `*-pages-client` preview suites. They reuse an existing playground `dist`, and this worktree's predated their newest fixtures. After deleting those `dist` folders, both passed (94 tests). To clear a stale cache without this change: ```sh rm -rf playground/<app>/node_modules/.vite ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes #713.
What changed
Linkaddedaria-current="page"anddata-void-activeto any link whose path matched the current page, ignoring the query. On/posts?page=2, both the Previous (?page=1) and Next (?page=3) links were announced as "current page".linkActiveAttributes()now uses the subset rule agreed in the issue, the same default as TanStack Router and Angular:/postsstays current on/posts?page=2.datacounts as part of the query, since it's merged into thehref.activeMatch="prefix"still compares only the path on nested pages./posts?page=2/posts?page=1/posts?page=2/posts?page=2/posts?page=2/posts/issues?status=open&page=2/issues?status=open/issues?status=open/issues?status=closed/search/searchwithdata={{ q: 'void' }}/docs#install#usage/reports/42/reports?range=30dwithactiveMatch="prefix"data-void-activedata-void-activeFor reviewers
prefix: aprefixlink on its own path with a different query is now not active at all (/reports?range=30don/reports?range=7d). Only nested pages compare the path alone. Otherwise, filter tabs usingprefixwould all bedata-void-activeat once.packages/void/src/pages/link.ts.pages-link.test.tscover the table above, plus query-only hrefs, repeated params, encoded values (+and%20) and empty values. The new cases fail on the old code.?view=compactlink is not current on/link-behaviorand becomes current after a client navigation to it. That runs in dev and preview for every adapter, through SSR, hydration and a query-only navigation.docs/guide/pages-routing/overview.mdcovers the query rule, theprefixbehavior, and setting the attributes yourself on default-value links such as an "All" tab.Verification
vp test runforpages-link.test.tsand the four adapter browser suites (react-pages-client,vue-pages-client,svelte-pages-client,solid-pages-playground): 245 tests passed.vp run checkand lint are clean.vp test run: 775 of 777 files passed (1 skipped), plus one failing file. Its 2 failures are inwebsockets-playground.test.ts, where/chatand/notificationsnever hydrate. They fail the same way onmainwithout this change, so that's being looked at separately.🤖 Generated with Claude Code