From da65fc4ffa350fea93decfa31693bd263b43b2a8 Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Thu, 3 Jul 2025 23:40:20 -0500 Subject: [PATCH 1/7] fix: solid loaders getting called twice on ssr --- packages/solid-router/src/Transitioner.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx index 51112e3bbc..af01168e60 100644 --- a/packages/solid-router/src/Transitioner.tsx +++ b/packages/solid-router/src/Transitioner.tsx @@ -7,6 +7,7 @@ import { import { useRouter } from './useRouter' import { useRouterState } from './useRouterState' import { usePrevious } from './utils' +import { isServer } from 'solid-js/web' export function Transitioner() { const router = useRouter() @@ -66,6 +67,7 @@ export function Transitioner() { // Try to load the initial location Solid.createRenderEffect(() => { + if (isServer) return; Solid.untrack(() => { if ( (typeof window !== 'undefined' && router.clientSsr) || From cc0b89e8f7ce6c0d9127dd59cdc9dce08bcf72b1 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 04:43:05 +0000 Subject: [PATCH 2/7] ci: apply automated fixes --- packages/solid-router/src/Transitioner.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx index af01168e60..2bc063f697 100644 --- a/packages/solid-router/src/Transitioner.tsx +++ b/packages/solid-router/src/Transitioner.tsx @@ -67,7 +67,7 @@ export function Transitioner() { // Try to load the initial location Solid.createRenderEffect(() => { - if (isServer) return; + if (isServer) return Solid.untrack(() => { if ( (typeof window !== 'undefined' && router.clientSsr) || From 52616cc8930f316a77bbd71a0c846b361f80b02c Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Thu, 3 Jul 2025 23:52:58 -0500 Subject: [PATCH 3/7] chore: fix linting --- packages/solid-router/src/Transitioner.tsx | 2 +- packages/solid-router/tests/router.test.tsx | 46 ++++++++++----------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx index 2bc063f697..895885764e 100644 --- a/packages/solid-router/src/Transitioner.tsx +++ b/packages/solid-router/src/Transitioner.tsx @@ -4,10 +4,10 @@ import { handleHashScroll, trimPathRight, } from '@tanstack/router-core' +import { isServer } from 'solid-js/web' import { useRouter } from './useRouter' import { useRouterState } from './useRouterState' import { usePrevious } from './utils' -import { isServer } from 'solid-js/web' export function Transitioner() { const router = useRouter() diff --git a/packages/solid-router/tests/router.test.tsx b/packages/solid-router/tests/router.test.tsx index 244007334e..c66be371d7 100644 --- a/packages/solid-router/tests/router.test.tsx +++ b/packages/solid-router/tests/router.test.tsx @@ -1207,27 +1207,18 @@ describe('search params in URL', () => { ValidatorFn, { search: string }>, ValidatorObj, { search: string }>, ] = [ - { - ['~standard']: { - validate: (input) => { - const result = z.object({ search: z.string() }).safeParse(input) - if (result.success) { - return { value: result.data } - } - return new TestValidationError(result.error.issues) + { + ['~standard']: { + validate: (input) => { + const result = z.object({ search: z.string() }).safeParse(input) + if (result.success) { + return { value: result.data } + } + return new TestValidationError(result.error.issues) + }, }, }, - }, - ({ search }) => { - if (typeof search !== 'string') { - throw new TestValidationError([ - { message: 'search must be a string' }, - ]) - } - return { search } - }, - { - parse: ({ search }) => { + ({ search }) => { if (typeof search !== 'string') { throw new TestValidationError([ { message: 'search must be a string' }, @@ -1235,8 +1226,17 @@ describe('search params in URL', () => { } return { search } }, - }, - ] + { + parse: ({ search }) => { + if (typeof search !== 'string') { + throw new TestValidationError([ + { message: 'search must be a string' }, + ]) + } + return { search } + }, + }, + ] describe.each(testCases)('search param validation', (validateSearch) => { it('does not throw an error when the search param is valid', async () => { @@ -1574,7 +1574,7 @@ describe('does not strip search params if search validation fails', () => { expect(window.location.search).toBe('?root=hello&index=world') }) - it('root is missing', async () => { + it('root is missing', () => { window.history.replaceState(null, 'root', '/?index=world') const router = getRouter() render(() => ) @@ -1582,7 +1582,7 @@ describe('does not strip search params if search validation fails', () => { expect(window.location.search).toBe('?index=world') }) - it('index is missing', async () => { + it('index is missing', () => { window.history.replaceState(null, 'root', '/?root=hello') const router = getRouter() From e0f3640ea7aa202b0e7da4140d6e8d9bb5462c28 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 04:54:20 +0000 Subject: [PATCH 4/7] ci: apply automated fixes --- packages/solid-router/tests/router.test.tsx | 42 ++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/solid-router/tests/router.test.tsx b/packages/solid-router/tests/router.test.tsx index c66be371d7..5d45208562 100644 --- a/packages/solid-router/tests/router.test.tsx +++ b/packages/solid-router/tests/router.test.tsx @@ -1207,18 +1207,27 @@ describe('search params in URL', () => { ValidatorFn, { search: string }>, ValidatorObj, { search: string }>, ] = [ - { - ['~standard']: { - validate: (input) => { - const result = z.object({ search: z.string() }).safeParse(input) - if (result.success) { - return { value: result.data } - } - return new TestValidationError(result.error.issues) - }, + { + ['~standard']: { + validate: (input) => { + const result = z.object({ search: z.string() }).safeParse(input) + if (result.success) { + return { value: result.data } + } + return new TestValidationError(result.error.issues) }, }, - ({ search }) => { + }, + ({ search }) => { + if (typeof search !== 'string') { + throw new TestValidationError([ + { message: 'search must be a string' }, + ]) + } + return { search } + }, + { + parse: ({ search }) => { if (typeof search !== 'string') { throw new TestValidationError([ { message: 'search must be a string' }, @@ -1226,17 +1235,8 @@ describe('search params in URL', () => { } return { search } }, - { - parse: ({ search }) => { - if (typeof search !== 'string') { - throw new TestValidationError([ - { message: 'search must be a string' }, - ]) - } - return { search } - }, - }, - ] + }, + ] describe.each(testCases)('search param validation', (validateSearch) => { it('does not throw an error when the search param is valid', async () => { From 9ff266d4fb4a9c819c35da2cbc0a214593b3a163 Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Fri, 4 Jul 2025 20:50:51 -0500 Subject: [PATCH 5/7] chore: add tests --- .../react-router/tests/Transitioner.test.tsx | 65 +++++++++++++++++++ packages/solid-router/src/Transitioner.tsx | 2 +- .../solid-router/tests/Transitioner.test.tsx | 65 +++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 packages/react-router/tests/Transitioner.test.tsx create mode 100644 packages/solid-router/tests/Transitioner.test.tsx diff --git a/packages/react-router/tests/Transitioner.test.tsx b/packages/react-router/tests/Transitioner.test.tsx new file mode 100644 index 0000000000..59d6dd6f62 --- /dev/null +++ b/packages/react-router/tests/Transitioner.test.tsx @@ -0,0 +1,65 @@ +import { describe, expect, it, vi } from 'vitest' +import { render, waitFor } from '@testing-library/react' +import { createMemoryHistory, createRootRoute, createRoute, createRouter } from '../src' +import { RouterProvider } from '../src/RouterProvider' + +describe('Transitioner', () => { + it('should call router.load() when Transitioner mounts on the client', async () => { + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Index
, + }) + + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ + routeTree, + history: createMemoryHistory({ + initialEntries: ['/'], + }), + }) + + // Mock router.load() to verify it gets called + const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) + + render() + + // Wait for the createRenderEffect to run and call router.load() + await waitFor(() => { + expect(loadSpy).toHaveBeenCalledTimes(1) + }) + + loadSpy.mockRestore() + }) + + it('should not call router.load() when on the server', async () => { + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Index
, + }) + + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ + routeTree, + history: createMemoryHistory({ + initialEntries: ['/'], + }), + isServer: true + }) + + // Mock router.load() to verify it gets called + const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) + + render(() => ) + + // Wait for the createRenderEffect to run and call router.load() + await waitFor(() => { + expect(loadSpy).toHaveBeenCalledTimes(0) + }) + + loadSpy.mockRestore() + }) +}) diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx index 895885764e..c29b78ec42 100644 --- a/packages/solid-router/src/Transitioner.tsx +++ b/packages/solid-router/src/Transitioner.tsx @@ -67,7 +67,7 @@ export function Transitioner() { // Try to load the initial location Solid.createRenderEffect(() => { - if (isServer) return + if (router.isServer) return Solid.untrack(() => { if ( (typeof window !== 'undefined' && router.clientSsr) || diff --git a/packages/solid-router/tests/Transitioner.test.tsx b/packages/solid-router/tests/Transitioner.test.tsx new file mode 100644 index 0000000000..c306790b2c --- /dev/null +++ b/packages/solid-router/tests/Transitioner.test.tsx @@ -0,0 +1,65 @@ +import { describe, expect, it, vi } from 'vitest' +import { render, waitFor } from '@solidjs/testing-library' +import { createMemoryHistory, createRootRoute, createRoute, createRouter } from '../src' +import { RouterProvider } from '../src/RouterProvider' + +describe('Transitioner', () => { + it('should call router.load() when Transitioner mounts on the client', async () => { + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Index
, + }) + + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ + routeTree, + history: createMemoryHistory({ + initialEntries: ['/'], + }), + }) + + // Mock router.load() to verify it gets called + const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) + + render(() => ) + + // Wait for the createRenderEffect to run and call router.load() + await waitFor(() => { + expect(loadSpy).toHaveBeenCalledTimes(1) + }) + + loadSpy.mockRestore() + }) + + it('should not call router.load() when on the server', async () => { + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Index
, + }) + + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ + routeTree, + history: createMemoryHistory({ + initialEntries: ['/'], + }), + isServer: true + }) + + // Mock router.load() to verify it gets called + const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) + + render(() => ) + + // Wait for the createRenderEffect to run and call router.load() + await waitFor(() => { + expect(loadSpy).toHaveBeenCalledTimes(0) + }) + + loadSpy.mockRestore() + }) +}) From 0fd4a50802ab2306edf6e736a2d967f3ffde8611 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 5 Jul 2025 01:52:04 +0000 Subject: [PATCH 6/7] ci: apply automated fixes --- .../react-router/tests/Transitioner.test.tsx | 99 ++++++++++--------- .../solid-router/tests/Transitioner.test.tsx | 9 +- 2 files changed, 59 insertions(+), 49 deletions(-) diff --git a/packages/react-router/tests/Transitioner.test.tsx b/packages/react-router/tests/Transitioner.test.tsx index 59d6dd6f62..c873864c25 100644 --- a/packages/react-router/tests/Transitioner.test.tsx +++ b/packages/react-router/tests/Transitioner.test.tsx @@ -1,65 +1,70 @@ import { describe, expect, it, vi } from 'vitest' import { render, waitFor } from '@testing-library/react' -import { createMemoryHistory, createRootRoute, createRoute, createRouter } from '../src' +import { + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' import { RouterProvider } from '../src/RouterProvider' describe('Transitioner', () => { - it('should call router.load() when Transitioner mounts on the client', async () => { - const rootRoute = createRootRoute() - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () =>
Index
, - }) - - const routeTree = rootRoute.addChildren([indexRoute]) - const router = createRouter({ - routeTree, - history: createMemoryHistory({ - initialEntries: ['/'], - }), - }) + it('should call router.load() when Transitioner mounts on the client', async () => { + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Index
, + }) - // Mock router.load() to verify it gets called - const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ + routeTree, + history: createMemoryHistory({ + initialEntries: ['/'], + }), + }) - render() + // Mock router.load() to verify it gets called + const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) - // Wait for the createRenderEffect to run and call router.load() - await waitFor(() => { - expect(loadSpy).toHaveBeenCalledTimes(1) - }) + render() - loadSpy.mockRestore() + // Wait for the createRenderEffect to run and call router.load() + await waitFor(() => { + expect(loadSpy).toHaveBeenCalledTimes(1) }) - it('should not call router.load() when on the server', async () => { - const rootRoute = createRootRoute() - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () =>
Index
, - }) + loadSpy.mockRestore() + }) - const routeTree = rootRoute.addChildren([indexRoute]) - const router = createRouter({ - routeTree, - history: createMemoryHistory({ - initialEntries: ['/'], - }), - isServer: true - }) + it('should not call router.load() when on the server', async () => { + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Index
, + }) - // Mock router.load() to verify it gets called - const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ + routeTree, + history: createMemoryHistory({ + initialEntries: ['/'], + }), + isServer: true, + }) - render(() => ) + // Mock router.load() to verify it gets called + const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) - // Wait for the createRenderEffect to run and call router.load() - await waitFor(() => { - expect(loadSpy).toHaveBeenCalledTimes(0) - }) + render(() => ) - loadSpy.mockRestore() + // Wait for the createRenderEffect to run and call router.load() + await waitFor(() => { + expect(loadSpy).toHaveBeenCalledTimes(0) }) + + loadSpy.mockRestore() + }) }) diff --git a/packages/solid-router/tests/Transitioner.test.tsx b/packages/solid-router/tests/Transitioner.test.tsx index c306790b2c..5467795e22 100644 --- a/packages/solid-router/tests/Transitioner.test.tsx +++ b/packages/solid-router/tests/Transitioner.test.tsx @@ -1,6 +1,11 @@ import { describe, expect, it, vi } from 'vitest' import { render, waitFor } from '@solidjs/testing-library' -import { createMemoryHistory, createRootRoute, createRoute, createRouter } from '../src' +import { + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' import { RouterProvider } from '../src/RouterProvider' describe('Transitioner', () => { @@ -47,7 +52,7 @@ describe('Transitioner', () => { history: createMemoryHistory({ initialEntries: ['/'], }), - isServer: true + isServer: true, }) // Mock router.load() to verify it gets called From b0dc899efea8a6292fe560335e68dac6e35d09ec Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Fri, 4 Jul 2025 21:02:42 -0500 Subject: [PATCH 7/7] chore: fix lint --- .../react-router/tests/Transitioner.test.tsx | 70 ------------------- packages/solid-router/src/Transitioner.tsx | 1 - 2 files changed, 71 deletions(-) delete mode 100644 packages/react-router/tests/Transitioner.test.tsx diff --git a/packages/react-router/tests/Transitioner.test.tsx b/packages/react-router/tests/Transitioner.test.tsx deleted file mode 100644 index c873864c25..0000000000 --- a/packages/react-router/tests/Transitioner.test.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { render, waitFor } from '@testing-library/react' -import { - createMemoryHistory, - createRootRoute, - createRoute, - createRouter, -} from '../src' -import { RouterProvider } from '../src/RouterProvider' - -describe('Transitioner', () => { - it('should call router.load() when Transitioner mounts on the client', async () => { - const rootRoute = createRootRoute() - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () =>
Index
, - }) - - const routeTree = rootRoute.addChildren([indexRoute]) - const router = createRouter({ - routeTree, - history: createMemoryHistory({ - initialEntries: ['/'], - }), - }) - - // Mock router.load() to verify it gets called - const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) - - render() - - // Wait for the createRenderEffect to run and call router.load() - await waitFor(() => { - expect(loadSpy).toHaveBeenCalledTimes(1) - }) - - loadSpy.mockRestore() - }) - - it('should not call router.load() when on the server', async () => { - const rootRoute = createRootRoute() - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () =>
Index
, - }) - - const routeTree = rootRoute.addChildren([indexRoute]) - const router = createRouter({ - routeTree, - history: createMemoryHistory({ - initialEntries: ['/'], - }), - isServer: true, - }) - - // Mock router.load() to verify it gets called - const loadSpy = vi.spyOn(router, 'load').mockResolvedValue(undefined) - - render(() => ) - - // Wait for the createRenderEffect to run and call router.load() - await waitFor(() => { - expect(loadSpy).toHaveBeenCalledTimes(0) - }) - - loadSpy.mockRestore() - }) -}) diff --git a/packages/solid-router/src/Transitioner.tsx b/packages/solid-router/src/Transitioner.tsx index c29b78ec42..d1af606994 100644 --- a/packages/solid-router/src/Transitioner.tsx +++ b/packages/solid-router/src/Transitioner.tsx @@ -4,7 +4,6 @@ import { handleHashScroll, trimPathRight, } from '@tanstack/router-core' -import { isServer } from 'solid-js/web' import { useRouter } from './useRouter' import { useRouterState } from './useRouterState' import { usePrevious } from './utils'