diff --git a/packages/plugins/tanstack-query/package.json b/packages/plugins/tanstack-query/package.json index fe86c1419..537de9460 100644 --- a/packages/plugins/tanstack-query/package.json +++ b/packages/plugins/tanstack-query/package.json @@ -110,6 +110,7 @@ "replace-in-file": "^7.0.1", "svelte": "^4.2.1", "swr": "^2.0.3", + "tmp": "^0.2.3", "vue": "^3.3.4" } } diff --git a/packages/plugins/tanstack-query/src/generator.ts b/packages/plugins/tanstack-query/src/generator.ts index 6a4ba53d9..e29a9ef0e 100644 --- a/packages/plugins/tanstack-query/src/generator.ts +++ b/packages/plugins/tanstack-query/src/generator.ts @@ -136,8 +136,8 @@ function generateQueryHook( }); if (version === 'v5' && infinite && ['react', 'svelte'].includes(target)) { - // initialPageParam and getNextPageParam options are required in v5 - func.addStatements([`options = options ?? { initialPageParam: undefined, getNextPageParam: () => null };`]); + // getNextPageParam option is required in v5 + func.addStatements([`options = options ?? { getNextPageParam: () => null };`]); } func.addStatements([ @@ -668,20 +668,20 @@ function makeQueryOptions( ? `Omit, 'queryKey'>` : `Omit>, 'queryKey'>` + }InfiniteQueryOptions<${returnType}, TError, InfiniteData<${dataType}>>, 'queryKey' | 'initialPageParam'>` : `Omit, 'queryKey'>` ) .with('vue', () => { - const baseOption = `Omit, 'queryKey'>`; + const baseOption = infinite + ? `Omit>, 'queryKey' | 'initialPageParam'>` + : `Omit, 'queryKey'>`; return `MaybeRefOrGetter<${baseOption}> | ComputedRef<${baseOption}>`; }) .with('svelte', () => infinite ? version === 'v4' ? `Omit, 'queryKey'>` - : `StoreOrVal>, 'queryKey'>>` + : `StoreOrVal>, 'queryKey' | 'initialPageParam'>>` : version === 'v4' ? `Omit, 'queryKey'>` : `StoreOrVal, 'queryKey'>>` diff --git a/packages/plugins/tanstack-query/src/runtime-v5/react.ts b/packages/plugins/tanstack-query/src/runtime-v5/react.ts index b169ce4fa..e8017befa 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/react.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/react.ts @@ -120,7 +120,7 @@ export function useInfiniteModelQuery( model: string, url: string, args: unknown, - options: Omit>, 'queryKey'>, + options: Omit>, 'queryKey' | 'initialPageParam'>, fetch?: FetchFn ) { return useInfiniteQuery({ @@ -128,6 +128,7 @@ export function useInfiniteModelQuery( queryFn: ({ pageParam }) => { return fetcher(makeUrl(url, pageParam ?? args), undefined, fetch, false); }, + initialPageParam: args, ...options, }); } @@ -146,7 +147,10 @@ export function useSuspenseInfiniteModelQuery( model: string, url: string, args: unknown, - options: Omit>, 'queryKey'>, + options: Omit< + UseSuspenseInfiniteQueryOptions>, + 'queryKey' | 'initialPageParam' + >, fetch?: FetchFn ) { return useSuspenseInfiniteQuery({ @@ -154,6 +158,7 @@ export function useSuspenseInfiniteModelQuery( queryFn: ({ pageParam }) => { return fetcher(makeUrl(url, pageParam ?? args), undefined, fetch, false); }, + initialPageParam: args, ...options, }); } diff --git a/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts b/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts index 1c58f83be..0ff05c1f0 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts @@ -107,7 +107,9 @@ export function useInfiniteModelQuery( model: string, url: string, args: unknown, - options: StoreOrVal>, 'queryKey'>>, + options: StoreOrVal< + Omit>, 'queryKey' | 'initialPageParam'> + >, fetch?: FetchFn ) { const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); @@ -115,12 +117,17 @@ export function useInfiniteModelQuery( fetcher(makeUrl(url, pageParam ?? args), undefined, fetch, false); let mergedOpt: StoreOrVal>>; - if (isStore>>(options)) { + if ( + isStore< + Omit>, 'queryKey' | 'initialPageParam'> + >(options) + ) { // options is store mergedOpt = derived([options], ([$opt]) => { return { queryKey, queryFn, + initialPageParam: args, ...$opt, }; }); @@ -129,6 +136,7 @@ export function useInfiniteModelQuery( mergedOpt = { queryKey, queryFn, + initialPageParam: args, ...options, }; } diff --git a/packages/plugins/tanstack-query/src/runtime/vue.ts b/packages/plugins/tanstack-query/src/runtime/vue.ts index 016414722..b024d8940 100644 --- a/packages/plugins/tanstack-query/src/runtime/vue.ts +++ b/packages/plugins/tanstack-query/src/runtime/vue.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/ban-types */ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { InfiniteData } from '@tanstack/react-query-v5'; import { useInfiniteQuery, useMutation, @@ -103,8 +104,12 @@ export function useInfiniteModelQuery( url: string, args?: MaybeRefOrGetter | ComputedRef, options?: - | MaybeRefOrGetter, 'queryKey'>> - | ComputedRef, 'queryKey'>>, + | MaybeRefOrGetter< + Omit>, 'queryKey' | 'initialPageParam'> + > + | ComputedRef< + Omit>, 'queryKey' | 'initialPageParam'> + >, fetch?: FetchFn ) { // CHECKME: vue-query's `useInfiniteQuery`'s input typing seems wrong @@ -115,10 +120,11 @@ export function useInfiniteModelQuery( const reqUrl = makeUrl(url, pageParam ?? toValue(args)); return fetcher(reqUrl, undefined, fetch, false); }, + initialPageParam: toValue(args), ...toValue(options), })); - return useInfiniteQuery(queryOptions); + return useInfiniteQuery>(queryOptions); } /** diff --git a/packages/plugins/tanstack-query/tests/plugin.test.ts b/packages/plugins/tanstack-query/tests/plugin.test.ts index 3dfb2dec0..d5f5c7d3a 100644 --- a/packages/plugins/tanstack-query/tests/plugin.test.ts +++ b/packages/plugins/tanstack-query/tests/plugin.test.ts @@ -48,6 +48,19 @@ model Foo { } `; + const reactAppSource = { + name: 'main.ts', + content: ` + import { useInfiniteFindManypost_Item } from './hooks'; + + const { data, fetchNextPage, hasNextPage } = useInfiniteFindManypost_Item(); + useInfiniteFindManypost_Item({ where: { published: true } }); + useInfiniteFindManypost_Item(undefined, { getNextPageParam: () => null }); + console.log(data?.pages[0][0].published); + console.log(data?.pageParams[0]); + `, + }; + it('react-query run plugin v4', async () => { await loadSchema( ` @@ -66,6 +79,7 @@ ${sharedModel} extraDependencies: ['react@18.2.0', '@types/react@18.2.0', '@tanstack/react-query@4.29.7'], copyDependencies: [path.resolve(__dirname, '../dist')], compile: true, + extraSourceFiles: [reactAppSource], } ); }); @@ -87,10 +101,38 @@ ${sharedModel} extraDependencies: ['react@18.2.0', '@types/react@18.2.0', '@tanstack/react-query@^5.0.0'], copyDependencies: [path.resolve(__dirname, '../dist')], compile: true, + extraSourceFiles: [ + reactAppSource, + { + name: 'suspense.ts', + content: ` + import { useSuspenseInfiniteFindManypost_Item } from './hooks'; + + const { data, fetchNextPage, hasNextPage } = useSuspenseInfiniteFindManypost_Item(); + useSuspenseInfiniteFindManypost_Item({ where: { published: true } }); + useSuspenseInfiniteFindManypost_Item(undefined, { getNextPageParam: () => null }); + console.log(data?.pages[0][0].published); + console.log(data?.pageParams[0]); + `, + }, + ], } ); }); + const vueAppSource = { + name: 'main.ts', + content: ` + import { useInfiniteFindManypost_Item } from './hooks'; + + const { data, fetchNextPage, hasNextPage } = useInfiniteFindManypost_Item(); + useInfiniteFindManypost_Item({ where: { published: true } }); + useInfiniteFindManypost_Item(undefined, { getNextPageParam: () => null }); + console.log(data.value?.pages[0][0].published); + console.log(data.value?.pageParams[0]); + `, + }; + it('vue-query run plugin v4', async () => { await loadSchema( ` @@ -109,6 +151,7 @@ ${sharedModel} extraDependencies: ['vue@^3.3.4', '@tanstack/vue-query@4.37.0'], copyDependencies: [path.resolve(__dirname, '../dist')], compile: true, + extraSourceFiles: [vueAppSource], } ); }); @@ -130,10 +173,25 @@ ${sharedModel} extraDependencies: ['vue@^3.3.4', '@tanstack/vue-query@latest'], copyDependencies: [path.resolve(__dirname, '../dist')], compile: true, + extraSourceFiles: [vueAppSource], } ); }); + const svelteAppSource = { + name: 'main.ts', + content: ` + import { get } from 'svelte/store'; + import { useInfiniteFindManypost_Item } from './hooks'; + + const { data, fetchNextPage, hasNextPage } = get(useInfiniteFindManypost_Item()); + useInfiniteFindManypost_Item({ where: { published: true } }); + useInfiniteFindManypost_Item(undefined, { getNextPageParam: () => null }); + console.log(data?.pages[0][0].published); + console.log(data?.pageParams[0]); + `, + }; + it('svelte-query run plugin v4', async () => { await loadSchema( ` @@ -152,6 +210,7 @@ ${sharedModel} extraDependencies: ['svelte@^3.0.0', '@tanstack/svelte-query@4.29.7'], copyDependencies: [path.resolve(__dirname, '../dist')], compile: true, + extraSourceFiles: [svelteAppSource], } ); }); @@ -173,6 +232,7 @@ ${sharedModel} extraDependencies: ['svelte@^3.0.0', '@tanstack/svelte-query@^5.0.0'], copyDependencies: [path.resolve(__dirname, '../dist')], compile: true, + extraSourceFiles: [svelteAppSource], } ); }); diff --git a/packages/plugins/tanstack-query/tests/react-hooks-v5.test.tsx b/packages/plugins/tanstack-query/tests/react-hooks-v5.test.tsx index d5e23c374..e772c12e1 100644 --- a/packages/plugins/tanstack-query/tests/react-hooks-v5.test.tsx +++ b/packages/plugins/tanstack-query/tests/react-hooks-v5.test.tsx @@ -10,7 +10,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query-v5'; import { act, renderHook, waitFor } from '@testing-library/react'; import nock from 'nock'; import React from 'react'; -import { RequestHandlerContext, useModelMutation, useModelQuery } from '../src/runtime-v5/react'; +import { RequestHandlerContext, useInfiniteModelQuery, useModelMutation, useModelQuery } from '../src/runtime-v5/react'; import { getQueryKey } from '../src/runtime/common'; import { modelMeta } from './test-model-meta'; @@ -60,6 +60,45 @@ describe('Tanstack Query React Hooks V5 Test', () => { }); }); + it('infinite query', async () => { + const { queryClient, wrapper } = createWrapper(); + + const queryArgs = { where: { id: '1' } }; + const data = [{ id: '1', name: 'foo' }]; + + nock(makeUrl('User', 'findMany', queryArgs)) + .get(/.*/) + .reply(200, () => { + console.log('Query findMany:', queryArgs); + return { + data: data, + }; + }); + + const { result } = renderHook( + () => + useInfiniteModelQuery('User', makeUrl('User', 'findMany'), queryArgs, { + getNextPageParam: () => null, + }), + { + wrapper, + } + ); + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + const resultData = result.current.data!; + expect(resultData.pages).toHaveLength(1); + expect(resultData.pages[0]).toMatchObject(data); + expect(resultData?.pageParams).toHaveLength(1); + expect(resultData?.pageParams[0]).toMatchObject(queryArgs); + expect(result.current.hasNextPage).toBe(false); + const cacheData: any = queryClient.getQueryData( + getQueryKey('User', 'findMany', queryArgs, { infinite: true, optimisticUpdate: false }) + ); + expect(cacheData.pages[0]).toMatchObject(data); + }); + }); + it('independent mutation and query', async () => { const { wrapper } = createWrapper(); diff --git a/packages/plugins/tanstack-query/tests/react-hooks.test.tsx b/packages/plugins/tanstack-query/tests/react-hooks.test.tsx index 7bd952fad..df913da7a 100644 --- a/packages/plugins/tanstack-query/tests/react-hooks.test.tsx +++ b/packages/plugins/tanstack-query/tests/react-hooks.test.tsx @@ -11,7 +11,7 @@ import { act, renderHook, waitFor } from '@testing-library/react'; import nock from 'nock'; import React from 'react'; import { getQueryKey } from '../src/runtime/common'; -import { RequestHandlerContext, useModelMutation, useModelQuery } from '../src/runtime/react'; +import { RequestHandlerContext, useInfiniteModelQuery, useModelMutation, useModelQuery } from '../src/runtime/react'; import { modelMeta } from './test-model-meta'; describe('Tanstack Query React Hooks V4 Test', () => { @@ -60,6 +60,45 @@ describe('Tanstack Query React Hooks V4 Test', () => { }); }); + it('infinite query', async () => { + const { queryClient, wrapper } = createWrapper(); + + const queryArgs = { where: { id: '1' } }; + const data = [{ id: '1', name: 'foo' }]; + + nock(makeUrl('User', 'findMany', queryArgs)) + .get(/.*/) + .reply(200, () => { + console.log('Query findMany:', queryArgs); + return { + data: data, + }; + }); + + const { result } = renderHook( + () => + useInfiniteModelQuery('User', makeUrl('User', 'findMany'), queryArgs, { + getNextPageParam: () => null, + }), + { + wrapper, + } + ); + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + const resultData = result.current.data!; + expect(resultData.pages).toHaveLength(1); + expect(resultData.pages[0]).toMatchObject(data); + expect(resultData?.pageParams).toHaveLength(1); + expect(resultData?.pageParams[0]).toBeUndefined(); + expect(result.current.hasNextPage).toBe(false); + const cacheData: any = queryClient.getQueryData( + getQueryKey('User', 'findMany', queryArgs, { infinite: true, optimisticUpdate: false }) + ); + expect(cacheData.pages[0]).toMatchObject(data); + }); + }); + it('independent mutation and query', async () => { const { wrapper } = createWrapper(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef1ebfa0f..c8a6235a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -337,6 +337,9 @@ importers: swr: specifier: ^2.0.3 version: 2.0.3(react@18.2.0) + tmp: + specifier: ^0.2.3 + version: 0.2.3 vue: specifier: ^3.3.4 version: 3.3.4 @@ -5399,7 +5402,7 @@ packages: parse-semver: 1.1.1 read: 1.0.7 semver: 5.7.1 - tmp: 0.2.1 + tmp: 0.2.3 typed-rest-client: 1.8.10 url-join: 4.0.1 xml2js: 0.5.0