diff --git a/.changeset/curried-query-ssr.md b/.changeset/curried-query-ssr.md new file mode 100644 index 0000000000..de6258761f --- /dev/null +++ b/.changeset/curried-query-ssr.md @@ -0,0 +1,5 @@ +--- +'@tanstack/solid-query': patch +--- + +Wait for curried queries before rendering Solid query status branches on the server. diff --git a/packages/solid-query/src/__tests__/fixtures/ssr-entry.tsx b/packages/solid-query/src/__tests__/fixtures/ssr-entry.tsx new file mode 100644 index 0000000000..abc650f19e --- /dev/null +++ b/packages/solid-query/src/__tests__/fixtures/ssr-entry.tsx @@ -0,0 +1,91 @@ +import { Match, Suspense, Switch } from 'solid-js' +import { renderToStringAsync } from 'solid-js/web' +import { + QueryClient, + QueryClientProvider, + queryOptions, + useQuery, +} from '../../index' + +const QUERY_NAME = 'shell-data' +const QUERY_SLUG = '/' +export const expectedQueryResult = `ok: ${QUERY_SLUG}` +export const initialQueryResult = 'initial data' +export const successLabel = 'Success:' + +export function getCoverage() { + return Reflect.get(globalThis, '__VITEST_COVERAGE__') +} + +export async function render() { + const queryClient = new QueryClient() + let queryFnCalls = 0 + const getOptions = (slug: string) => + queryOptions({ + queryKey: [QUERY_NAME, slug], + queryFn: () => { + queryFnCalls += 1 + return Promise.resolve(`ok: ${slug}`) + }, + }) + + function Page() { + const query = useQuery(() => getOptions(QUERY_SLUG)) + + return ( + + PENDING + ERROR + + {successLabel} {query.data} + + + ) + } + + try { + const markup = await renderToStringAsync(() => ( + + + + + + )) + + return { markup, queryFnCalls } + } finally { + queryClient.clear() + } +} + +export async function renderWithInitialData() { + const queryClient = new QueryClient() + let queryFnCalls = 0 + + function Page() { + const query = useQuery(() => ({ + queryKey: [QUERY_NAME, 'initial'], + queryFn: () => { + queryFnCalls += 1 + return Promise.resolve(expectedQueryResult) + }, + initialData: initialQueryResult, + })) + + return query.isSuccess ? `${successLabel} ${query.data}` : 'PENDING' + } + + try { + const markup = await renderToStringAsync(() => ( + + + + + + )) + + return { markup, queryFnCalls } + } finally { + queryClient.clear() + } +} diff --git a/packages/solid-query/src/__tests__/ssr.test.ts b/packages/solid-query/src/__tests__/ssr.test.ts new file mode 100644 index 0000000000..cae861bb73 --- /dev/null +++ b/packages/solid-query/src/__tests__/ssr.test.ts @@ -0,0 +1,123 @@ +/** @vitest-environment node */ +// cspell:ignore instrumenter + +import { createRequire } from 'node:module' +import { fileURLToPath } from 'node:url' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { createServer } from 'vite' +import solid from 'vite-plugin-solid' +import type { Plugin, ViteDevServer } from 'vite' +import type * as SsrEntry from './fixtures/ssr-entry' + +const PACKAGE_ROOT = fileURLToPath(new URL('../..', import.meta.url)) +const SSR_ENTRY_PATH = '/src/__tests__/fixtures/ssr-entry.tsx' +const USE_BASE_QUERY_PATH = fileURLToPath( + new URL('../useBaseQuery.ts', import.meta.url), +) +const SCRIPT_TAG = '