Skip to content

Commit c7f76ec

Browse files
authored
feat(prefetch): add prefetch query hook generation (#92)
1 parent cf175e6 commit c7f76ec

File tree

11 files changed

+340
-28
lines changed

11 files changed

+340
-28
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,16 @@ $ openapi-rq -i ./petstore.yaml
7171
```
7272
- openapi
7373
- queries
74-
- index.ts <- main file that exports common types, variables, and hooks
74+
- index.ts <- main file that exports common types, variables, and queries. Does not export suspense or prefetch hooks
7575
- common.ts <- common types
7676
- queries.ts <- generated query hooks
7777
- suspenses.ts <- generated suspense hooks
78+
- prefetch.ts <- generated prefetch hooks learn more about prefetching in in link below
7879
- requests <- output code generated by @hey-api/openapi-ts
7980
```
8081

82+
- [Prefetching docs](https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data)
83+
8184
### In your app
8285

8386
#### Using the generated hooks

examples/react-app/src/main.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@ import App from "./App";
44
import "./index.css";
55
import { QueryClientProvider } from "@tanstack/react-query";
66
import { queryClient } from "./queryClient";
7+
import { prefetchUseDefaultServiceFindPets } from "../openapi/queries/prefetch";
78

8-
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
9-
<React.StrictMode>
10-
<QueryClientProvider client={queryClient}>
11-
<App />
12-
</QueryClientProvider>
13-
</React.StrictMode>
14-
);
9+
async function PrefetchData() {
10+
await prefetchUseDefaultServiceFindPets(queryClient);
11+
}
12+
13+
PrefetchData().then(() => {
14+
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
15+
<React.StrictMode>
16+
<QueryClientProvider client={queryClient}>
17+
<App />
18+
</QueryClientProvider>
19+
</React.StrictMode>
20+
);
21+
});

src/constants.mts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,11 @@ export const requestsOutputPath = "requests";
44

55
export const serviceFileName = "services.gen";
66
export const modalsFileName = "types.gen";
7+
8+
export const OpenApiRqFiles = {
9+
queries: "queries",
10+
common: "common",
11+
suspense: "suspense",
12+
index: "index",
13+
prefetch: "prefetch",
14+
} as const;

src/createExports.mts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createUseQuery } from "./createUseQuery.mjs";
22
import { createUseMutation } from "./createUseMutation.mjs";
33
import { Service } from "./service.mjs";
4+
import { createPrefetch } from "./createPrefetch.mjs";
45

56
export const createExports = (service: Service) => {
67
const { klasses } = service;
@@ -23,6 +24,7 @@ export const createExports = (service: Service) => {
2324
);
2425

2526
const allGetQueries = allGet.map((m) => createUseQuery(m));
27+
const allPrefetchQueries = allGet.map((m) => createPrefetch(m));
2628

2729
const allPostMutations = allPost.map((m) => createUseMutation(m));
2830
const allPutMutations = allPut.map((m) => createUseMutation(m));
@@ -59,6 +61,12 @@ export const createExports = (service: Service) => {
5961

6062
const suspenseExports = [...suspenseQueries];
6163

64+
const allPrefetches = allPrefetchQueries
65+
.map(({ prefetchHook }) => [prefetchHook])
66+
.flat();
67+
68+
const allPrefetchExports = [...allPrefetches];
69+
6270
return {
6371
/**
6472
* Common types and variables between queries (regular and suspense) and mutations
@@ -72,5 +80,9 @@ export const createExports = (service: Service) => {
7280
* Suspense exports are the hooks that are used in the suspense components
7381
*/
7482
suspenseExports,
83+
/**
84+
* Prefetch exports are the hooks that are used in the prefetch components
85+
*/
86+
allPrefetchExports,
7587
};
7688
};

src/createPrefetch.mts

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import ts from "typescript";
2+
import { MethodDeclaration } from "ts-morph";
3+
import {
4+
BuildCommonTypeName,
5+
extractPropertiesFromObjectParam,
6+
getNameFromMethod,
7+
} from "./common.mjs";
8+
import { type MethodDescription } from "./common.mjs";
9+
import {
10+
createQueryKeyFromMethod,
11+
getRequestParamFromMethod,
12+
hookNameFromMethod,
13+
} from "./createUseQuery.mjs";
14+
import { addJSDocToNode } from "./util.mjs";
15+
16+
/**
17+
* Creates a prefetch function for a query
18+
*/
19+
function createPrefetchHook({
20+
requestParams,
21+
method,
22+
className,
23+
}: {
24+
requestParams: ts.ParameterDeclaration[];
25+
method: MethodDeclaration;
26+
className: string;
27+
}) {
28+
const methodName = getNameFromMethod(method);
29+
const queryName = hookNameFromMethod({ method, className });
30+
const customHookName = `prefetch${queryName.charAt(0).toUpperCase() + queryName.slice(1)}`;
31+
const queryKey = createQueryKeyFromMethod({ method, className });
32+
33+
// const
34+
const hookExport = ts.factory.createVariableStatement(
35+
// export
36+
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
37+
ts.factory.createVariableDeclarationList(
38+
[
39+
ts.factory.createVariableDeclaration(
40+
ts.factory.createIdentifier(customHookName),
41+
undefined,
42+
undefined,
43+
ts.factory.createArrowFunction(
44+
undefined,
45+
undefined,
46+
[
47+
ts.factory.createParameterDeclaration(
48+
undefined,
49+
undefined,
50+
"queryClient",
51+
undefined,
52+
ts.factory.createTypeReferenceNode(
53+
ts.factory.createIdentifier("QueryClient")
54+
)
55+
),
56+
...requestParams,
57+
],
58+
undefined,
59+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
60+
ts.factory.createCallExpression(
61+
ts.factory.createIdentifier("queryClient.prefetchQuery"),
62+
undefined,
63+
[
64+
ts.factory.createObjectLiteralExpression([
65+
ts.factory.createPropertyAssignment(
66+
ts.factory.createIdentifier("queryKey"),
67+
ts.factory.createArrayLiteralExpression(
68+
[
69+
BuildCommonTypeName(queryKey),
70+
method.getParameters().length
71+
? ts.factory.createArrayLiteralExpression([
72+
ts.factory.createObjectLiteralExpression(
73+
method
74+
.getParameters()
75+
.map((param) =>
76+
extractPropertiesFromObjectParam(param).map(
77+
(p) =>
78+
ts.factory.createShorthandPropertyAssignment(
79+
ts.factory.createIdentifier(p.name)
80+
)
81+
)
82+
)
83+
.flat()
84+
),
85+
])
86+
: ts.factory.createArrayLiteralExpression([]),
87+
],
88+
false
89+
)
90+
),
91+
ts.factory.createPropertyAssignment(
92+
ts.factory.createIdentifier("queryFn"),
93+
ts.factory.createArrowFunction(
94+
undefined,
95+
undefined,
96+
[],
97+
undefined,
98+
ts.factory.createToken(
99+
ts.SyntaxKind.EqualsGreaterThanToken
100+
),
101+
ts.factory.createCallExpression(
102+
ts.factory.createPropertyAccessExpression(
103+
ts.factory.createIdentifier(className),
104+
ts.factory.createIdentifier(methodName)
105+
),
106+
undefined,
107+
method.getParameters().length
108+
? [
109+
ts.factory.createObjectLiteralExpression(
110+
method
111+
.getParameters()
112+
.map((param) =>
113+
extractPropertiesFromObjectParam(param).map(
114+
(p) =>
115+
ts.factory.createShorthandPropertyAssignment(
116+
ts.factory.createIdentifier(p.name)
117+
)
118+
)
119+
)
120+
.flat()
121+
),
122+
]
123+
: undefined
124+
)
125+
)
126+
),
127+
]),
128+
]
129+
)
130+
)
131+
),
132+
],
133+
ts.NodeFlags.Const
134+
)
135+
);
136+
return hookExport;
137+
}
138+
139+
export const createPrefetch = ({
140+
className,
141+
method,
142+
jsDoc,
143+
}: MethodDescription) => {
144+
const requestParam = getRequestParamFromMethod(method);
145+
146+
const requestParams = requestParam ? [requestParam] : [];
147+
148+
const prefetchHook = createPrefetchHook({
149+
requestParams,
150+
method,
151+
className,
152+
});
153+
154+
const hookWithJsDoc = addJSDocToNode(prefetchHook, jsDoc);
155+
156+
return {
157+
prefetchHook: hookWithJsDoc,
158+
};
159+
};

0 commit comments

Comments
 (0)