Skip to content

Commit 4e30ddb

Browse files
fabien0102SferaDev
andauthored
feat!: React query key cache manager (#49)
* Add initial key manager generation * Add actual usage * Fix bad type * Add util addPathParam * Update key builder Signed-off-by: Alexis Rico <[email protected]> * Simplify code * Update context * Remove old helper * Improve types * Update addPathParam Signed-off-by: Alexis Rico <[email protected]> * Add some changes Signed-off-by: Alexis Rico <[email protected]> * Provide a nice & tested `queryKeyFn` template * Unify SyntaxKind usage * Update fallback value for QueryOperation * Use underscore prefix to show that the param is unused Co-authored-by: Alexis Rico <[email protected]>
1 parent cb4d68f commit 4e30ddb

File tree

7 files changed

+591
-27
lines changed

7 files changed

+591
-27
lines changed

plugins/typescript/src/generators/generateReactQueryComponents.test.ts

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,16 @@ describe("generateReactQueryComponents", () => {
106106
/**
107107
* Get all the pets
108108
*/
109-
export const useListPets = (queryKey: reactQuery.QueryKey, variables: ListPetsVariables, options?: Omit<reactQuery.UseQueryOptions<ListPetsResponse, undefined, ListPetsResponse>, \\"queryKey\\" | \\"queryFn\\">) => { const { fetcherOptions, queryOptions, queryKeyFn } = usePetstoreContext(options); return reactQuery.useQuery<ListPetsResponse, undefined, ListPetsResponse>(queryKeyFn(queryKey), () => fetchListPets({ ...fetcherOptions, ...variables }), {
109+
export const useListPets = (variables: ListPetsVariables, options?: Omit<reactQuery.UseQueryOptions<ListPetsResponse, undefined, ListPetsResponse>, \\"queryKey\\" | \\"queryFn\\">) => { const { fetcherOptions, queryOptions, queryKeyFn } = usePetstoreContext(options); return reactQuery.useQuery<ListPetsResponse, undefined, ListPetsResponse>(queryKeyFn({ path: \\"/pets\\", operationId: \\"listPets\\", variables }), () => fetchListPets({ ...fetcherOptions, ...variables }), {
110110
...options,
111111
...queryOptions
112112
}); };
113+
114+
export type QueryOperation = {
115+
path: \\"/pets\\";
116+
operationId: \\"listPets\\";
117+
variables: ListPetsVariables;
118+
};
113119
"
114120
`);
115121
});
@@ -219,10 +225,16 @@ describe("generateReactQueryComponents", () => {
219225
/**
220226
* Get all the pets
221227
*/
222-
export const useListPets = (queryKey: reactQuery.QueryKey, variables: ListPetsVariables, options?: Omit<reactQuery.UseQueryOptions<ListPetsResponse, undefined, ListPetsResponse>, \\"queryKey\\" | \\"queryFn\\">) => { const { fetcherOptions, queryOptions, queryKeyFn } = usePetstoreContext(options); return reactQuery.useQuery<ListPetsResponse, undefined, ListPetsResponse>(queryKeyFn(queryKey), () => fetchListPets({ ...fetcherOptions, ...variables }), {
228+
export const useListPets = (variables: ListPetsVariables, options?: Omit<reactQuery.UseQueryOptions<ListPetsResponse, undefined, ListPetsResponse>, \\"queryKey\\" | \\"queryFn\\">) => { const { fetcherOptions, queryOptions, queryKeyFn } = usePetstoreContext(options); return reactQuery.useQuery<ListPetsResponse, undefined, ListPetsResponse>(queryKeyFn({ path: \\"/pets\\", operationId: \\"listPets\\", variables }), () => fetchListPets({ ...fetcherOptions, ...variables }), {
223229
...options,
224230
...queryOptions
225231
}); };
232+
233+
export type QueryOperation = {
234+
path: \\"/pets\\";
235+
operationId: \\"listPets\\";
236+
variables: ListPetsVariables;
237+
};
226238
"
227239
`);
228240
});
@@ -336,10 +348,16 @@ describe("generateReactQueryComponents", () => {
336348
/**
337349
* Get all the pets
338350
*/
339-
export const useListPets = (queryKey: reactQuery.QueryKey, variables: ListPetsVariables, options?: Omit<reactQuery.UseQueryOptions<ListPetsResponse, undefined, ListPetsResponse>, \\"queryKey\\" | \\"queryFn\\">) => { const { fetcherOptions, queryOptions, queryKeyFn } = usePetstoreContext(options); return reactQuery.useQuery<ListPetsResponse, undefined, ListPetsResponse>(queryKeyFn(queryKey), () => fetchListPets({ ...fetcherOptions, ...variables }), {
351+
export const useListPets = (variables: ListPetsVariables, options?: Omit<reactQuery.UseQueryOptions<ListPetsResponse, undefined, ListPetsResponse>, \\"queryKey\\" | \\"queryFn\\">) => { const { fetcherOptions, queryOptions, queryKeyFn } = usePetstoreContext(options); return reactQuery.useQuery<ListPetsResponse, undefined, ListPetsResponse>(queryKeyFn({ path: \\"/pets\\", operationId: \\"listPets\\", variables }), () => fetchListPets({ ...fetcherOptions, ...variables }), {
340352
...options,
341353
...queryOptions
342354
}); };
355+
356+
export type QueryOperation = {
357+
path: \\"/pets\\";
358+
operationId: \\"listPets\\";
359+
variables: ListPetsVariables;
360+
};
343361
"
344362
`);
345363
});
@@ -424,10 +442,16 @@ describe("generateReactQueryComponents", () => {
424442
/**
425443
* Get all the pets
426444
*/
427-
export const useListPets = (queryKey: reactQuery.QueryKey, variables: ListPetsVariables, options?: Omit<reactQuery.UseQueryOptions<ListPetsResponse, undefined, ListPetsResponse>, \\"queryKey\\" | \\"queryFn\\">) => { const { fetcherOptions, queryOptions, queryKeyFn } = usePetstoreContext(options); return reactQuery.useQuery<ListPetsResponse, undefined, ListPetsResponse>(queryKeyFn(queryKey), () => fetchListPets({ ...fetcherOptions, ...variables }), {
445+
export const useListPets = (variables: ListPetsVariables, options?: Omit<reactQuery.UseQueryOptions<ListPetsResponse, undefined, ListPetsResponse>, \\"queryKey\\" | \\"queryFn\\">) => { const { fetcherOptions, queryOptions, queryKeyFn } = usePetstoreContext(options); return reactQuery.useQuery<ListPetsResponse, undefined, ListPetsResponse>(queryKeyFn({ path: \\"/pets\\", operationId: \\"listPets\\", variables }), () => fetchListPets({ ...fetcherOptions, ...variables }), {
428446
...options,
429447
...queryOptions
430448
}); };
449+
450+
export type QueryOperation = {
451+
path: \\"/pets\\";
452+
operationId: \\"listPets\\";
453+
variables: ListPetsVariables;
454+
};
431455
"
432456
`);
433457
});
@@ -548,6 +572,12 @@ describe("generateReactQueryComponents", () => {
548572
const { fetcherOptions } = usePetstoreContext();
549573
return reactQuery.useMutation<string, AddPetError, AddPetVariables>((variables: AddPetVariables) => fetchAddPet({ ...fetcherOptions, ...variables }), options);
550574
};
575+
576+
export type QueryOperation = {
577+
path: string;
578+
operationId: never;
579+
variables: unknown;
580+
};
551581
"
552582
`);
553583
});
@@ -669,6 +699,12 @@ describe("generateReactQueryComponents", () => {
669699
const { fetcherOptions } = usePetstoreContext();
670700
return reactQuery.useMutation<string, AddPetError, AddPetVariables>((variables: AddPetVariables) => fetchAddPet({ ...fetcherOptions, ...variables }), options);
671701
};
702+
703+
export type QueryOperation = {
704+
path: string;
705+
operationId: never;
706+
variables: unknown;
707+
};
672708
"
673709
`);
674710
});
@@ -790,6 +826,12 @@ describe("generateReactQueryComponents", () => {
790826
const { fetcherOptions } = usePetstoreContext();
791827
return reactQuery.useMutation<string, AddPetError, AddPetVariables>((variables: AddPetVariables) => fetchAddPet({ ...fetcherOptions, ...variables }), options);
792828
};
829+
830+
export type QueryOperation = {
831+
path: string;
832+
operationId: never;
833+
variables: unknown;
834+
};
793835
"
794836
`);
795837
});
@@ -890,6 +932,12 @@ describe("generateReactQueryComponents", () => {
890932
const { fetcherOptions } = usePetstoreContext();
891933
return reactQuery.useMutation<string, undefined, UpdatePetVariables>((variables: UpdatePetVariables) => fetchUpdatePet({ ...fetcherOptions, ...variables }), options);
892934
};
935+
936+
export type QueryOperation = {
937+
path: string;
938+
operationId: never;
939+
variables: unknown;
940+
};
893941
"
894942
`);
895943
});

plugins/typescript/src/generators/generateReactQueryComponents.ts

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export const generateReactQueryComponents = async (
7575
const contextTypeName = `${c.pascal(filenamePrefix)}Context`;
7676
const contextHookName = `use${c.pascal(filenamePrefix)}Context`;
7777
const nodes: ts.Node[] = [];
78+
const keyManagerItems: ts.TypeLiteralNode[] = [];
7879

7980
const fetcherFilename = formatFilename(filenamePrefix + "-fetcher");
8081
const contextFilename = formatFilename(filenamePrefix + "-context");
@@ -91,7 +92,10 @@ export const generateReactQueryComponents = async (
9192
}
9293

9394
if (!context.existsFile(`${contextFilename}.ts`)) {
94-
context.writeFile(`${contextFilename}.ts`, getContext(filenamePrefix));
95+
context.writeFile(
96+
`${contextFilename}.ts`,
97+
getContext(filenamePrefix, filename)
98+
);
9599
}
96100

97101
// Generate `useQuery` & `useMutation`
@@ -146,6 +150,31 @@ export const generateReactQueryComponents = async (
146150
Valid options: "useMutate", "useQuery"`);
147151
}
148152

153+
if (component === "useQuery") {
154+
keyManagerItems.push(
155+
f.createTypeLiteralNode([
156+
f.createPropertySignature(
157+
undefined,
158+
f.createIdentifier("path"),
159+
undefined,
160+
f.createLiteralTypeNode(f.createStringLiteral(route))
161+
),
162+
f.createPropertySignature(
163+
undefined,
164+
f.createIdentifier("operationId"),
165+
undefined,
166+
f.createLiteralTypeNode(f.createStringLiteral(operationId))
167+
),
168+
f.createPropertySignature(
169+
undefined,
170+
f.createIdentifier("variables"),
171+
undefined,
172+
variablesType
173+
),
174+
])
175+
);
176+
}
177+
149178
nodes.push(
150179
...createOperationFetcherFnNodes({
151180
dataType,
@@ -169,6 +198,8 @@ export const generateReactQueryComponents = async (
169198
variablesType,
170199
contextHookName,
171200
name: `use${c.pascal(operationId)}`,
201+
operationId,
202+
url: route,
172203
})
173204
: createMutationHook({
174205
operationFetcherFnName,
@@ -184,6 +215,35 @@ export const generateReactQueryComponents = async (
184215
}
185216
);
186217

218+
const queryKeyManager = f.createTypeAliasDeclaration(
219+
undefined,
220+
[f.createModifier(ts.SyntaxKind.ExportKeyword)],
221+
"QueryOperation",
222+
undefined,
223+
keyManagerItems.length > 0
224+
? f.createUnionTypeNode(keyManagerItems)
225+
: f.createTypeLiteralNode([
226+
f.createPropertySignature(
227+
undefined,
228+
f.createIdentifier("path"),
229+
undefined,
230+
f.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
231+
),
232+
f.createPropertySignature(
233+
undefined,
234+
f.createIdentifier("operationId"),
235+
undefined,
236+
f.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)
237+
),
238+
f.createPropertySignature(
239+
undefined,
240+
f.createIdentifier("variables"),
241+
undefined,
242+
f.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
243+
),
244+
])
245+
);
246+
187247
await context.writeFile(
188248
filename + ".ts",
189249
printNodes([
@@ -196,6 +256,7 @@ export const generateReactQueryComponents = async (
196256
createNamedImport(fetcherFn, `./${fetcherFilename}`),
197257
...getUsedImports(nodes, config.schemasFiles),
198258
...nodes,
259+
queryKeyManager,
199260
])
200261
);
201262
};
@@ -352,11 +413,15 @@ const createQueryHook = ({
352413
errorType,
353414
variablesType,
354415
name,
416+
operationId,
355417
operation,
418+
url,
356419
}: {
357420
operationFetcherFnName: string;
358421
contextHookName: string;
359422
name: string;
423+
operationId: string;
424+
url: string;
360425
dataType: ts.TypeNode;
361426
errorType: ts.TypeNode;
362427
variablesType: ts.TypeNode;
@@ -379,21 +444,6 @@ const createQueryHook = ({
379444
undefined,
380445
undefined,
381446
[
382-
f.createParameterDeclaration(
383-
undefined,
384-
undefined,
385-
undefined,
386-
f.createIdentifier("queryKey"),
387-
undefined,
388-
f.createTypeReferenceNode(
389-
f.createQualifiedName(
390-
f.createIdentifier("reactQuery"),
391-
f.createIdentifier("QueryKey")
392-
),
393-
undefined
394-
),
395-
undefined
396-
),
397447
f.createParameterDeclaration(
398448
undefined,
399449
undefined,
@@ -462,7 +512,21 @@ const createQueryHook = ({
462512
f.createCallExpression(
463513
f.createIdentifier("queryKeyFn"),
464514
undefined,
465-
[f.createIdentifier("queryKey")]
515+
[
516+
f.createObjectLiteralExpression([
517+
f.createPropertyAssignment(
518+
"path",
519+
f.createStringLiteral(url)
520+
),
521+
f.createPropertyAssignment(
522+
"operationId",
523+
f.createStringLiteral(operationId)
524+
),
525+
f.createShorthandPropertyAssignment(
526+
f.createIdentifier("variables")
527+
),
528+
]),
529+
]
466530
),
467531
f.createArrowFunction(
468532
undefined,

plugins/typescript/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export { generateFetchers } from "./generators/generateFetchers";
66
// Utils
77
export { renameComponent } from "./utils/renameComponent";
88
export { forceReactQueryComponent } from "./utils/forceReactQueryComponent";
9+
export { addPathParam } from "./utils/addPathParam";

0 commit comments

Comments
 (0)