diff --git a/.changeset/paths-node-base.md b/.changeset/paths-node-base.md new file mode 100644 index 00000000..9ebcbfbd --- /dev/null +++ b/.changeset/paths-node-base.md @@ -0,0 +1,5 @@ +--- +"@solidjs/router": patch +--- + +Under a `base`, `navigate(paths.about)` and `useLinkState(paths.about)` no longer add the base twice. A paths node already includes the base, so with `base: "/app"` they gave `/app/app/about`. They now use the node's path as-is and give `/app/about`. `useLinkState(paths.about)` also works under `hashHistory`, where it never reported the link as current before. diff --git a/src/routing.ts b/src/routing.ts index 46ed03aa..8a561d19 100644 --- a/src/routing.ts +++ b/src/routing.ts @@ -405,7 +405,13 @@ export const useLinkState = ( ): LinkState => { const router = useRouter(); const location = router.location; - const to = useResolvedPath(() => String(href())); + const route = useRoute(); + // a paths node's brand already holds the base; resolving would add it again + const to = createMemo(() => { + const h = href(); + const brand = typeof h !== "string" ? ((h as any)[HREF] as string | undefined) : undefined; + return brand !== undefined ? brand : route.resolvePath(String(h)); + }); const base = router.base.path(); const state = createMemo(() => linkMatcher(location, base, options.end)(to())); return { @@ -1085,9 +1091,10 @@ export function createRouterContext( }; resolvedTo = compose(headed); } else { - if (typeof to !== "string") to = href || to.toString(); - resolvedTo = resolveTarget(to); - if (resolvedTo === undefined) throw unroutable(to); + // the brand already holds the base, so it skips resolution + if (typeof to !== "string" && href === undefined) to = to.toString(); + resolvedTo = href !== undefined ? href : resolveTarget(to as string); + if (resolvedTo === undefined) throw unroutable(to as string); } const navigationDepth = diff --git a/test/paths-base.spec.tsx b/test/paths-base.spec.tsx new file mode 100644 index 00000000..a3a27d2c --- /dev/null +++ b/test/paths-base.spec.tsx @@ -0,0 +1,154 @@ +import { render } from "@solidjs/web"; +import { vi } from "vitest"; +import { + createRouter, + hashHistory, + memoryHistory, + useLinkState, + useNavigate, + type LinkState, + type Navigator +} from "../src/index.js"; + +const settle = () => new Promise(r => setTimeout(r, 30)); + +describe("paths nodes under a base", () => { + const originalScrollTo = window.scrollTo; + beforeEach(() => { + window.scrollTo = vi.fn() as any; + }); + afterAll(() => { + window.scrollTo = originalScrollTo; + }); + + const routes = [ + { path: "/", component: () =>
home
}, + { path: "/about", component: () =>about
} + ] as const; + + test("navigate(paths.about) lands on /app/about", async () => { + const history = memoryHistory("/app"); + const Router = createRouter({ routes, base: "/app", history }); + let navigate!: Navigator; + const div = document.createElement("div"); + const dispose = render( + () =>home
}, + { path: "/about", component: () =>about
} + ] as const, + history: hashHistory() + }); + let link!: LinkState; + const div = document.createElement("div"); + const dispose = render( + () =>home
}, + { path: "/about", component: () =>about
} + ] as const, + base: "/app", + history: hashHistory() + }); + window.history.replaceState(null, "", "/#/app"); + let navigate!: Navigator; + let link!: LinkState; + const div = document.createElement("div"); + const dispose = render( + () => ( +