Skip to content

Commit

Permalink
fix(tanstack): infinite query typing issues (#1480)
Browse files Browse the repository at this point in the history
  • Loading branch information
ymc9 authored Jun 2, 2024
1 parent a783800 commit e158372
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 17 deletions.
1 change: 1 addition & 0 deletions packages/plugins/tanstack-query/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
14 changes: 7 additions & 7 deletions packages/plugins/tanstack-query/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down Expand Up @@ -668,20 +668,20 @@ function makeQueryOptions(
? `Omit<UseInfiniteQueryOptions<${returnType}, TError, ${dataType}>, 'queryKey'>`
: `Omit<Use${
suspense ? 'Suspense' : ''
}InfiniteQueryOptions<${returnType}, TError, InfiniteData<${dataType}>>, 'queryKey'>`
}InfiniteQueryOptions<${returnType}, TError, InfiniteData<${dataType}>>, 'queryKey' | 'initialPageParam'>`
: `Omit<Use${suspense ? 'Suspense' : ''}QueryOptions<${returnType}, TError, ${dataType}>, 'queryKey'>`
)
.with('vue', () => {
const baseOption = `Omit<Use${
infinite ? 'Infinite' : ''
}QueryOptions<${returnType}, TError, ${dataType}>, 'queryKey'>`;
const baseOption = infinite
? `Omit<UseInfiniteQueryOptions<${returnType}, TError, InfiniteData<${dataType}>>, 'queryKey' | 'initialPageParam'>`
: `Omit<UseQueryOptions<${returnType}, TError, ${dataType}>, 'queryKey'>`;
return `MaybeRefOrGetter<${baseOption}> | ComputedRef<${baseOption}>`;
})
.with('svelte', () =>
infinite
? version === 'v4'
? `Omit<CreateInfiniteQueryOptions<${returnType}, TError, ${dataType}>, 'queryKey'>`
: `StoreOrVal<Omit<CreateInfiniteQueryOptions<${returnType}, TError, InfiniteData<${dataType}>>, 'queryKey'>>`
: `StoreOrVal<Omit<CreateInfiniteQueryOptions<${returnType}, TError, InfiniteData<${dataType}>>, 'queryKey' | 'initialPageParam'>>`
: version === 'v4'
? `Omit<CreateQueryOptions<${returnType}, TError, ${dataType}>, 'queryKey'>`
: `StoreOrVal<Omit<CreateQueryOptions<${returnType}, TError, ${dataType}>, 'queryKey'>>`
Expand Down
9 changes: 7 additions & 2 deletions packages/plugins/tanstack-query/src/runtime-v5/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,15 @@ export function useInfiniteModelQuery<TQueryFnData, TData, TError>(
model: string,
url: string,
args: unknown,
options: Omit<UseInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>, 'queryKey'>,
options: Omit<UseInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>, 'queryKey' | 'initialPageParam'>,
fetch?: FetchFn
) {
return useInfiniteQuery({
queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }),
queryFn: ({ pageParam }) => {
return fetcher<TQueryFnData, false>(makeUrl(url, pageParam ?? args), undefined, fetch, false);
},
initialPageParam: args,
...options,
});
}
Expand All @@ -146,14 +147,18 @@ export function useSuspenseInfiniteModelQuery<TQueryFnData, TData, TError>(
model: string,
url: string,
args: unknown,
options: Omit<UseSuspenseInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>, 'queryKey'>,
options: Omit<
UseSuspenseInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>,
'queryKey' | 'initialPageParam'
>,
fetch?: FetchFn
) {
return useSuspenseInfiniteQuery({
queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }),
queryFn: ({ pageParam }) => {
return fetcher<TQueryFnData, false>(makeUrl(url, pageParam ?? args), undefined, fetch, false);
},
initialPageParam: args,
...options,
});
}
Expand Down
12 changes: 10 additions & 2 deletions packages/plugins/tanstack-query/src/runtime-v5/svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,27 @@ export function useInfiniteModelQuery<TQueryFnData, TData, TError>(
model: string,
url: string,
args: unknown,
options: StoreOrVal<Omit<CreateInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>, 'queryKey'>>,
options: StoreOrVal<
Omit<CreateInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>, 'queryKey' | 'initialPageParam'>
>,
fetch?: FetchFn
) {
const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false });
const queryFn = ({ pageParam }: { pageParam: unknown }) =>
fetcher<TQueryFnData, false>(makeUrl(url, pageParam ?? args), undefined, fetch, false);

let mergedOpt: StoreOrVal<CreateInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>>;
if (isStore<CreateInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>>(options)) {
if (
isStore<
Omit<CreateInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>, 'queryKey' | 'initialPageParam'>
>(options)
) {
// options is store
mergedOpt = derived([options], ([$opt]) => {
return {
queryKey,
queryFn,
initialPageParam: args,
...$opt,
};
});
Expand All @@ -129,6 +136,7 @@ export function useInfiniteModelQuery<TQueryFnData, TData, TError>(
mergedOpt = {
queryKey,
queryFn,
initialPageParam: args,
...options,
};
}
Expand Down
12 changes: 9 additions & 3 deletions packages/plugins/tanstack-query/src/runtime/vue.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -103,8 +104,12 @@ export function useInfiniteModelQuery<TQueryFnData, TData, TError>(
url: string,
args?: MaybeRefOrGetter<unknown> | ComputedRef<unknown>,
options?:
| MaybeRefOrGetter<Omit<UseInfiniteQueryOptions<TQueryFnData, TError, TData>, 'queryKey'>>
| ComputedRef<Omit<UseInfiniteQueryOptions<TQueryFnData, TError, TData>, 'queryKey'>>,
| MaybeRefOrGetter<
Omit<UseInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>, 'queryKey' | 'initialPageParam'>
>
| ComputedRef<
Omit<UseInfiniteQueryOptions<TQueryFnData, TError, InfiniteData<TData>>, 'queryKey' | 'initialPageParam'>
>,
fetch?: FetchFn
) {
// CHECKME: vue-query's `useInfiniteQuery`'s input typing seems wrong
Expand All @@ -115,10 +120,11 @@ export function useInfiniteModelQuery<TQueryFnData, TData, TError>(
const reqUrl = makeUrl(url, pageParam ?? toValue(args));
return fetcher<TQueryFnData, false>(reqUrl, undefined, fetch, false);
},
initialPageParam: toValue(args),
...toValue(options),
}));

return useInfiniteQuery<TQueryFnData, TError, TData>(queryOptions);
return useInfiniteQuery<TQueryFnData, TError, InfiniteData<TData>>(queryOptions);
}

/**
Expand Down
60 changes: 60 additions & 0 deletions packages/plugins/tanstack-query/tests/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
`
Expand All @@ -66,6 +79,7 @@ ${sharedModel}
extraDependencies: ['[email protected]', '@types/[email protected]', '@tanstack/[email protected]'],
copyDependencies: [path.resolve(__dirname, '../dist')],
compile: true,
extraSourceFiles: [reactAppSource],
}
);
});
Expand All @@ -87,10 +101,38 @@ ${sharedModel}
extraDependencies: ['[email protected]', '@types/[email protected]', '@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(
`
Expand All @@ -109,6 +151,7 @@ ${sharedModel}
extraDependencies: ['vue@^3.3.4', '@tanstack/[email protected]'],
copyDependencies: [path.resolve(__dirname, '../dist')],
compile: true,
extraSourceFiles: [vueAppSource],
}
);
});
Expand All @@ -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(
`
Expand All @@ -152,6 +210,7 @@ ${sharedModel}
extraDependencies: ['svelte@^3.0.0', '@tanstack/[email protected]'],
copyDependencies: [path.resolve(__dirname, '../dist')],
compile: true,
extraSourceFiles: [svelteAppSource],
}
);
});
Expand All @@ -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],
}
);
});
Expand Down
41 changes: 40 additions & 1 deletion packages/plugins/tanstack-query/tests/react-hooks-v5.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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();

Expand Down
41 changes: 40 additions & 1 deletion packages/plugins/tanstack-query/tests/react-hooks.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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();

Expand Down
Loading

0 comments on commit e158372

Please sign in to comment.