@7nohe/openapi-react-query-codegen 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -12
- package/dist/cli.mjs +3 -1
- package/dist/common.mjs +2 -0
- package/dist/constants.mjs +7 -0
- package/dist/createExports.mjs +16 -1
- package/dist/createPrefetch.mjs +58 -0
- package/dist/createSource.mjs +24 -14
- package/dist/createUseQuery.mjs +53 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
> Node.js library that generates [React Query (also called TanStack Query)](https://tanstack.com/query) hooks based on an OpenAPI specification file.
|
|
4
4
|
|
|
5
|
+
[](https://badge.fury.io/js/%407nohe%2Fopenapi-react-query-codegen)
|
|
6
|
+
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
9
|
+
- Generates custom react hooks that use React Query's `useQuery`, `useSuspenseQuery` and `useMutation` hooks
|
|
10
|
+
- Generates query keys and functions for query caching
|
|
11
|
+
- Generates pure TypeScript clients generated by [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts)
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Installation
|
|
12
14
|
|
|
13
15
|
```
|
|
14
16
|
$ npm install -D @7nohe/openapi-react-query-codegen
|
|
@@ -48,7 +50,7 @@ Options:
|
|
|
48
50
|
--format <value> Process output folder with formatter? ['biome', 'prettier']
|
|
49
51
|
--lint <value> Process output folder with linter? ['eslint', 'biome']
|
|
50
52
|
--operationId Use operation ID to generate operation names?
|
|
51
|
-
--serviceResponse <value> Define shape of returned value from service calls ['body', '
|
|
53
|
+
--serviceResponse <value> Define shape of returned value from service calls ['body', 'response'] (default: "body")
|
|
52
54
|
--base <value> Manually set base in OpenAPI config instead of inferring from server value
|
|
53
55
|
--enums <value> Generate JavaScript objects from enum definitions? ['javascript', 'typescript']
|
|
54
56
|
--useDateType Use Date type instead of string for date types for models, this will not convert the data to a Date object
|
|
@@ -58,27 +60,32 @@ Options:
|
|
|
58
60
|
-h, --help display help for command
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
### Example Usage
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
#### Command
|
|
64
66
|
|
|
65
67
|
```
|
|
66
68
|
$ openapi-rq -i ./petstore.yaml
|
|
67
69
|
```
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
#### Output directory structure
|
|
70
72
|
|
|
71
73
|
```
|
|
72
74
|
- openapi
|
|
73
75
|
- queries
|
|
74
|
-
- index.ts <- main file that exports common types, variables, and hooks
|
|
76
|
+
- index.ts <- main file that exports common types, variables, and queries. Does not export suspense or prefetch hooks
|
|
75
77
|
- common.ts <- common types
|
|
76
78
|
- queries.ts <- generated query hooks
|
|
77
79
|
- suspenses.ts <- generated suspense hooks
|
|
80
|
+
- prefetch.ts <- generated prefetch hooks learn more about prefetching in in link below
|
|
78
81
|
- requests <- output code generated by @hey-api/openapi-ts
|
|
79
82
|
```
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
- [Prefetching docs](https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data)
|
|
85
|
+
|
|
86
|
+
#### In your app
|
|
87
|
+
|
|
88
|
+
##### Using the generated hooks
|
|
82
89
|
|
|
83
90
|
```tsx
|
|
84
91
|
// App.tsx
|
|
@@ -97,7 +104,7 @@ function App() {
|
|
|
97
104
|
export default App;
|
|
98
105
|
```
|
|
99
106
|
|
|
100
|
-
|
|
107
|
+
##### Using the generated typescript client
|
|
101
108
|
|
|
102
109
|
```tsx
|
|
103
110
|
import { useQuery } from "@tanstack/react-query";
|
|
@@ -120,7 +127,7 @@ function App() {
|
|
|
120
127
|
export default App;
|
|
121
128
|
```
|
|
122
129
|
|
|
123
|
-
|
|
130
|
+
##### Using Suspense Hooks
|
|
124
131
|
|
|
125
132
|
```tsx
|
|
126
133
|
// App.tsx
|
|
@@ -153,6 +160,103 @@ function App() {
|
|
|
153
160
|
export default App;
|
|
154
161
|
```
|
|
155
162
|
|
|
163
|
+
##### Using Mutation hooks
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// App.tsx
|
|
167
|
+
import { usePetServiceAddPet } from "../openapi/queries";
|
|
168
|
+
|
|
169
|
+
function App() {
|
|
170
|
+
const { mutate } = usePetServiceAddPet();
|
|
171
|
+
|
|
172
|
+
const handleAddPet = () => {
|
|
173
|
+
mutate({ name: "Fluffy", status: "available" });
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div className="App">
|
|
178
|
+
<h1>Add Pet</h1>
|
|
179
|
+
<button onClick={handleAddPet}>Add Pet</button>
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export default App;
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
##### Invalidating queries after mutation
|
|
188
|
+
|
|
189
|
+
Invalidating queries after a mutation is important to ensure the cache is updated with the new data. This is done by calling the `queryClient.invalidateQueries` function with the query key used by the query hook.
|
|
190
|
+
|
|
191
|
+
Learn more about invalidating queries [here](https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation).
|
|
192
|
+
|
|
193
|
+
To ensure the query key is created the same way as the query hook, you can use the query key function exported by the generated query hooks.
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import {
|
|
197
|
+
usePetServiceFindPetsByStatus,
|
|
198
|
+
usePetServiceAddPet,
|
|
199
|
+
UsePetServiceFindPetsByStatusKeyFn,
|
|
200
|
+
} from "../openapi/queries";
|
|
201
|
+
|
|
202
|
+
// App.tsx
|
|
203
|
+
function App() {
|
|
204
|
+
const { data } = usePetServiceFindPetsByStatus({ status: ["available"] });
|
|
205
|
+
const { mutate } = usePetServiceAddPet({
|
|
206
|
+
onSuccess: () => {
|
|
207
|
+
queryClient.invalidateQueries({
|
|
208
|
+
// Call the query key function to get the query key, this is important to ensure the query key is created the same way as the query hook, this insures the cache is invalidated correctly and is typed correctly
|
|
209
|
+
queryKey: [UsePetServiceFindPetsByStatusKeyFn()],
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<div className="App">
|
|
216
|
+
<h1>Pet List</h1>
|
|
217
|
+
<ul>{data?.map((pet) => <li key={pet.id}>{pet.name}</li>)}</ul>
|
|
218
|
+
<button
|
|
219
|
+
onClick={() => {
|
|
220
|
+
mutate({ name: "Fluffy", status: "available" });
|
|
221
|
+
}}
|
|
222
|
+
>
|
|
223
|
+
Add Pet
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export default App;
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
##### Runtime Configuration
|
|
233
|
+
|
|
234
|
+
You can modify the default values used by the generated service calls by modifying the OpenAPI configuration singleton object.
|
|
235
|
+
|
|
236
|
+
It's default location is `openapi/requests/core/OpenAPI.ts` and it is also exported from `openapi/index.ts`
|
|
237
|
+
|
|
238
|
+
Import the constant into your runtime and modify it before setting up the react app.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
/** main.tsx */
|
|
242
|
+
import { OpenAPI as OpenAPIConfig } from './openapi/requests/core/OpenAPI';
|
|
243
|
+
...
|
|
244
|
+
OpenAPIConfig.BASE = 'www.domain.com/api';
|
|
245
|
+
OpenAPIConfig.HEADERS = {
|
|
246
|
+
'x-header-1': 'value-1',
|
|
247
|
+
'x-header-2': 'value-2',
|
|
248
|
+
};
|
|
249
|
+
...
|
|
250
|
+
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
|
251
|
+
<React.StrictMode>
|
|
252
|
+
<QueryClientProvider client={queryClient}>
|
|
253
|
+
<App />
|
|
254
|
+
</QueryClientProvider>
|
|
255
|
+
</React.StrictMode>
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
|
|
156
260
|
## License
|
|
157
261
|
|
|
158
262
|
MIT
|
package/dist/cli.mjs
CHANGED
|
@@ -25,7 +25,9 @@ async function setupProgram() {
|
|
|
25
25
|
.addOption(new Option("--format <value>", "Process output folder with formatter?").choices(["biome", "prettier"]))
|
|
26
26
|
.addOption(new Option("--lint <value>", "Process output folder with linter?").choices(["biome", "eslint"]))
|
|
27
27
|
.option("--operationId", "Use operation ID to generate operation names?")
|
|
28
|
-
.addOption(new Option("--serviceResponse <value>", "Define shape of returned value from service calls")
|
|
28
|
+
.addOption(new Option("--serviceResponse <value>", "Define shape of returned value from service calls")
|
|
29
|
+
.choices(["body", "response"])
|
|
30
|
+
.default("body"))
|
|
29
31
|
.option("--base <value>", "Manually set base in OpenAPI config instead of inferring from server value")
|
|
30
32
|
.addOption(new Option("--enums <value>", "Generate JavaScript objects from enum definitions?").choices(["javascript", "typescript"]))
|
|
31
33
|
.option("--useDateType", "Use Date type instead of string for date types for models, this will not convert the data to a Date object")
|
package/dist/common.mjs
CHANGED
|
@@ -5,6 +5,8 @@ import { queriesOutputPath, requestsOutputPath } from "./constants.mjs";
|
|
|
5
5
|
export const TData = ts.factory.createIdentifier("TData");
|
|
6
6
|
export const TError = ts.factory.createIdentifier("TError");
|
|
7
7
|
export const TContext = ts.factory.createIdentifier("TContext");
|
|
8
|
+
export const EqualsOrGreaterThanToken = ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken);
|
|
9
|
+
export const QuestionToken = ts.factory.createToken(ts.SyntaxKind.QuestionToken);
|
|
8
10
|
export const queryKeyGenericType = ts.factory.createTypeReferenceNode("TQueryKey");
|
|
9
11
|
export const queryKeyConstraint = ts.factory.createTypeReferenceNode("Array", [
|
|
10
12
|
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
|
package/dist/constants.mjs
CHANGED
|
@@ -3,3 +3,10 @@ export const queriesOutputPath = "queries";
|
|
|
3
3
|
export const requestsOutputPath = "requests";
|
|
4
4
|
export const serviceFileName = "services.gen";
|
|
5
5
|
export const modalsFileName = "types.gen";
|
|
6
|
+
export const OpenApiRqFiles = {
|
|
7
|
+
queries: "queries",
|
|
8
|
+
common: "common",
|
|
9
|
+
suspense: "suspense",
|
|
10
|
+
index: "index",
|
|
11
|
+
prefetch: "prefetch",
|
|
12
|
+
};
|
package/dist/createExports.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createUseQuery } from "./createUseQuery.mjs";
|
|
2
2
|
import { createUseMutation } from "./createUseMutation.mjs";
|
|
3
|
+
import { createPrefetch } from "./createPrefetch.mjs";
|
|
3
4
|
export const createExports = (service) => {
|
|
4
5
|
const { klasses } = service;
|
|
5
6
|
const methods = klasses.map((k) => k.methods).flat();
|
|
@@ -9,6 +10,7 @@ export const createExports = (service) => {
|
|
|
9
10
|
const allPatch = methods.filter((m) => m.httpMethodName.toUpperCase().includes("PATCH"));
|
|
10
11
|
const allDelete = methods.filter((m) => m.httpMethodName.toUpperCase().includes("DELETE"));
|
|
11
12
|
const allGetQueries = allGet.map((m) => createUseQuery(m));
|
|
13
|
+
const allPrefetchQueries = allGet.map((m) => createPrefetch(m));
|
|
12
14
|
const allPostMutations = allPost.map((m) => createUseMutation(m));
|
|
13
15
|
const allPutMutations = allPut.map((m) => createUseMutation(m));
|
|
14
16
|
const allPatchMutations = allPatch.map((m) => createUseMutation(m));
|
|
@@ -21,7 +23,12 @@ export const createExports = (service) => {
|
|
|
21
23
|
...allDeleteMutations,
|
|
22
24
|
];
|
|
23
25
|
const commonInQueries = allQueries
|
|
24
|
-
.map(({ apiResponse, returnType, key }) => [
|
|
26
|
+
.map(({ apiResponse, returnType, key, queryKeyFn }) => [
|
|
27
|
+
apiResponse,
|
|
28
|
+
returnType,
|
|
29
|
+
key,
|
|
30
|
+
queryKeyFn,
|
|
31
|
+
])
|
|
25
32
|
.flat();
|
|
26
33
|
const commonInMutations = allMutations
|
|
27
34
|
.map(({ mutationResult }) => [mutationResult])
|
|
@@ -36,6 +43,10 @@ export const createExports = (service) => {
|
|
|
36
43
|
.map(({ suspenseQueryHook }) => [suspenseQueryHook])
|
|
37
44
|
.flat();
|
|
38
45
|
const suspenseExports = [...suspenseQueries];
|
|
46
|
+
const allPrefetches = allPrefetchQueries
|
|
47
|
+
.map(({ prefetchHook }) => [prefetchHook])
|
|
48
|
+
.flat();
|
|
49
|
+
const allPrefetchExports = [...allPrefetches];
|
|
39
50
|
return {
|
|
40
51
|
/**
|
|
41
52
|
* Common types and variables between queries (regular and suspense) and mutations
|
|
@@ -49,5 +60,9 @@ export const createExports = (service) => {
|
|
|
49
60
|
* Suspense exports are the hooks that are used in the suspense components
|
|
50
61
|
*/
|
|
51
62
|
suspenseExports,
|
|
63
|
+
/**
|
|
64
|
+
* Prefetch exports are the hooks that are used in the prefetch components
|
|
65
|
+
*/
|
|
66
|
+
allPrefetchExports,
|
|
52
67
|
};
|
|
53
68
|
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { BuildCommonTypeName, extractPropertiesFromObjectParam, getNameFromMethod, } from "./common.mjs";
|
|
3
|
+
import { createQueryKeyFromMethod, getRequestParamFromMethod, hookNameFromMethod, } from "./createUseQuery.mjs";
|
|
4
|
+
import { addJSDocToNode } from "./util.mjs";
|
|
5
|
+
/**
|
|
6
|
+
* Creates a prefetch function for a query
|
|
7
|
+
*/
|
|
8
|
+
function createPrefetchHook({ requestParams, method, className, }) {
|
|
9
|
+
const methodName = getNameFromMethod(method);
|
|
10
|
+
const queryName = hookNameFromMethod({ method, className });
|
|
11
|
+
const customHookName = `prefetch${queryName.charAt(0).toUpperCase() + queryName.slice(1)}`;
|
|
12
|
+
const queryKey = createQueryKeyFromMethod({ method, className });
|
|
13
|
+
// const
|
|
14
|
+
const hookExport = ts.factory.createVariableStatement(
|
|
15
|
+
// export
|
|
16
|
+
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
|
|
17
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier(customHookName), undefined, undefined, ts.factory.createArrowFunction(undefined, undefined, [
|
|
18
|
+
ts.factory.createParameterDeclaration(undefined, undefined, "queryClient", undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("QueryClient"))),
|
|
19
|
+
...requestParams,
|
|
20
|
+
], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createCallExpression(ts.factory.createIdentifier("queryClient.prefetchQuery"), undefined, [
|
|
21
|
+
ts.factory.createObjectLiteralExpression([
|
|
22
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryKey"), ts.factory.createArrayLiteralExpression([
|
|
23
|
+
BuildCommonTypeName(queryKey),
|
|
24
|
+
method.getParameters().length
|
|
25
|
+
? ts.factory.createArrayLiteralExpression([
|
|
26
|
+
ts.factory.createObjectLiteralExpression(method
|
|
27
|
+
.getParameters()
|
|
28
|
+
.map((param) => extractPropertiesFromObjectParam(param).map((p) => ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name))))
|
|
29
|
+
.flat()),
|
|
30
|
+
])
|
|
31
|
+
: ts.factory.createArrayLiteralExpression([]),
|
|
32
|
+
], false)),
|
|
33
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryFn"), ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(className), ts.factory.createIdentifier(methodName)), undefined, method.getParameters().length
|
|
34
|
+
? [
|
|
35
|
+
ts.factory.createObjectLiteralExpression(method
|
|
36
|
+
.getParameters()
|
|
37
|
+
.map((param) => extractPropertiesFromObjectParam(param).map((p) => ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name))))
|
|
38
|
+
.flat()),
|
|
39
|
+
]
|
|
40
|
+
: undefined))),
|
|
41
|
+
]),
|
|
42
|
+
]))),
|
|
43
|
+
], ts.NodeFlags.Const));
|
|
44
|
+
return hookExport;
|
|
45
|
+
}
|
|
46
|
+
export const createPrefetch = ({ className, method, jsDoc, }) => {
|
|
47
|
+
const requestParam = getRequestParamFromMethod(method);
|
|
48
|
+
const requestParams = requestParam ? [requestParam] : [];
|
|
49
|
+
const prefetchHook = createPrefetchHook({
|
|
50
|
+
requestParams,
|
|
51
|
+
method,
|
|
52
|
+
className,
|
|
53
|
+
});
|
|
54
|
+
const hookWithJsDoc = addJSDocToNode(prefetchHook, jsDoc);
|
|
55
|
+
return {
|
|
56
|
+
prefetchHook: hookWithJsDoc,
|
|
57
|
+
};
|
|
58
|
+
};
|
package/dist/createSource.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
+
import { Project } from "ts-morph";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { OpenApiRqFiles } from "./constants.mjs";
|
|
2
5
|
import { createImports } from "./createImports.mjs";
|
|
3
6
|
import { createExports } from "./createExports.mjs";
|
|
4
7
|
import { getServices } from "./service.mjs";
|
|
5
|
-
import { Project } from "ts-morph";
|
|
6
|
-
import { join } from "path";
|
|
7
8
|
const createSourceFile = async (outputPath, serviceEndName) => {
|
|
8
9
|
const project = new Project({
|
|
9
10
|
// Optionally specify compiler options, tsconfig.json, in-memory file system, and more here.
|
|
@@ -21,29 +22,32 @@ const createSourceFile = async (outputPath, serviceEndName) => {
|
|
|
21
22
|
});
|
|
22
23
|
const exports = createExports(service);
|
|
23
24
|
const commonSource = ts.factory.createSourceFile([...imports, ...exports.allCommon], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
24
|
-
const commonImport = ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, ts.factory.createIdentifier("* as Common"), undefined), ts.factory.createStringLiteral(
|
|
25
|
-
const commonExport = ts.factory.createExportDeclaration(undefined, false, undefined, ts.factory.createStringLiteral(
|
|
26
|
-
const queriesExport = ts.factory.createExportDeclaration(undefined, false, undefined, ts.factory.createStringLiteral(
|
|
25
|
+
const commonImport = ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, ts.factory.createIdentifier("* as Common"), undefined), ts.factory.createStringLiteral(`./${OpenApiRqFiles.common}`), undefined);
|
|
26
|
+
const commonExport = ts.factory.createExportDeclaration(undefined, false, undefined, ts.factory.createStringLiteral(`./${OpenApiRqFiles.common}`), undefined);
|
|
27
|
+
const queriesExport = ts.factory.createExportDeclaration(undefined, false, undefined, ts.factory.createStringLiteral(`./${OpenApiRqFiles.queries}`), undefined);
|
|
27
28
|
const mainSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.mainExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
28
29
|
const suspenseSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.suspenseExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
29
30
|
const indexSource = ts.factory.createSourceFile([commonExport, queriesExport], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
31
|
+
const prefetchSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.allPrefetchExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
30
32
|
return {
|
|
31
33
|
commonSource,
|
|
32
34
|
mainSource,
|
|
33
35
|
suspenseSource,
|
|
34
36
|
indexSource,
|
|
37
|
+
prefetchSource,
|
|
35
38
|
};
|
|
36
39
|
};
|
|
37
40
|
export const createSource = async ({ outputPath, version, serviceEndName, }) => {
|
|
38
|
-
const queriesFile = ts.createSourceFile(
|
|
39
|
-
const commonFile = ts.createSourceFile(
|
|
40
|
-
const suspenseFile = ts.createSourceFile(
|
|
41
|
-
const indexFile = ts.createSourceFile(
|
|
41
|
+
const queriesFile = ts.createSourceFile(`${OpenApiRqFiles.queries}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
42
|
+
const commonFile = ts.createSourceFile(`${OpenApiRqFiles.common}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
43
|
+
const suspenseFile = ts.createSourceFile(`${OpenApiRqFiles.suspense}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
44
|
+
const indexFile = ts.createSourceFile(`${OpenApiRqFiles.index}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
45
|
+
const prefetchFile = ts.createSourceFile(`${OpenApiRqFiles.prefetch}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
42
46
|
const printer = ts.createPrinter({
|
|
43
47
|
newLine: ts.NewLineKind.LineFeed,
|
|
44
48
|
removeComments: false,
|
|
45
49
|
});
|
|
46
|
-
const { commonSource, mainSource, suspenseSource, indexSource } = await createSourceFile(outputPath, serviceEndName);
|
|
50
|
+
const { commonSource, mainSource, suspenseSource, indexSource, prefetchSource, } = await createSourceFile(outputPath, serviceEndName);
|
|
47
51
|
const comment = `// generated with @7nohe/openapi-react-query-codegen@${version} \n\n`;
|
|
48
52
|
const commonResult = comment +
|
|
49
53
|
printer.printNode(ts.EmitHint.Unspecified, commonSource, commonFile);
|
|
@@ -53,22 +57,28 @@ export const createSource = async ({ outputPath, version, serviceEndName, }) =>
|
|
|
53
57
|
printer.printNode(ts.EmitHint.Unspecified, suspenseSource, suspenseFile);
|
|
54
58
|
const indexResult = comment +
|
|
55
59
|
printer.printNode(ts.EmitHint.Unspecified, indexSource, indexFile);
|
|
60
|
+
const prefetchResult = comment +
|
|
61
|
+
printer.printNode(ts.EmitHint.Unspecified, prefetchSource, prefetchFile);
|
|
56
62
|
return [
|
|
57
63
|
{
|
|
58
|
-
name:
|
|
64
|
+
name: `${OpenApiRqFiles.index}.ts`,
|
|
59
65
|
content: indexResult,
|
|
60
66
|
},
|
|
61
67
|
{
|
|
62
|
-
name:
|
|
68
|
+
name: `${OpenApiRqFiles.common}.ts`,
|
|
63
69
|
content: commonResult,
|
|
64
70
|
},
|
|
65
71
|
{
|
|
66
|
-
name:
|
|
72
|
+
name: `${OpenApiRqFiles.queries}.ts`,
|
|
67
73
|
content: mainResult,
|
|
68
74
|
},
|
|
69
75
|
{
|
|
70
|
-
name:
|
|
76
|
+
name: `${OpenApiRqFiles.suspense}.ts`,
|
|
71
77
|
content: suspenseResult,
|
|
72
78
|
},
|
|
79
|
+
{
|
|
80
|
+
name: `${OpenApiRqFiles.prefetch}.ts`,
|
|
81
|
+
content: prefetchResult,
|
|
82
|
+
},
|
|
73
83
|
];
|
|
74
84
|
};
|
package/dist/createUseQuery.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
-
import { BuildCommonTypeName, capitalizeFirstLetter, extractPropertiesFromObjectParam, getNameFromMethod, getShortType, queryKeyConstraint, queryKeyGenericType, TData, TError, } from "./common.mjs";
|
|
2
|
+
import { BuildCommonTypeName, capitalizeFirstLetter, EqualsOrGreaterThanToken, extractPropertiesFromObjectParam, getNameFromMethod, getShortType, queryKeyConstraint, queryKeyGenericType, QuestionToken, TData, TError, } from "./common.mjs";
|
|
3
3
|
import { addJSDocToNode } from "./util.mjs";
|
|
4
4
|
export const createApiResponseType = ({ className, methodName, }) => {
|
|
5
5
|
/** Awaited<ReturnType<typeof myClass.myMethod>> */
|
|
@@ -14,12 +14,15 @@ export const createApiResponseType = ({ className, methodName, }) => {
|
|
|
14
14
|
const apiResponse = ts.factory.createTypeAliasDeclaration([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(`${capitalizeFirstLetter(className)}${capitalizeFirstLetter(methodName)}DefaultResponse`), undefined, awaitedResponseDataType);
|
|
15
15
|
const responseDataType = ts.factory.createTypeParameterDeclaration(undefined, TData.text, undefined, ts.factory.createTypeReferenceNode(BuildCommonTypeName(apiResponse.name)));
|
|
16
16
|
return {
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* DefaultResponseDataType
|
|
19
|
+
*
|
|
18
20
|
* export type MyClassMethodDefaultResponse = Awaited<ReturnType<typeof myClass.myMethod>>
|
|
19
21
|
*/
|
|
20
22
|
apiResponse,
|
|
21
23
|
/**
|
|
22
|
-
* will be the name of the type of the response type of the method
|
|
24
|
+
* This will be the name of the type of the response type of the method
|
|
25
|
+
*
|
|
23
26
|
* MyClassMethodDefaultResponse
|
|
24
27
|
*/
|
|
25
28
|
responseDataType,
|
|
@@ -54,6 +57,7 @@ export function getRequestParamFromMethod(method) {
|
|
|
54
57
|
}
|
|
55
58
|
/**
|
|
56
59
|
* Return Type
|
|
60
|
+
*
|
|
57
61
|
* export const classNameMethodNameQueryResult<TData = MyClassMethodDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>;
|
|
58
62
|
*/
|
|
59
63
|
export function createReturnTypeExport({ className, methodName, defaultApiResponse, }) {
|
|
@@ -73,11 +77,11 @@ export function createQueryKeyExport({ className, methodName, queryKey, }) {
|
|
|
73
77
|
ts.factory.createVariableDeclaration(ts.factory.createIdentifier(queryKey), undefined, undefined, ts.factory.createStringLiteral(`${className}${capitalizeFirstLetter(methodName)}`)),
|
|
74
78
|
], ts.NodeFlags.Const));
|
|
75
79
|
}
|
|
76
|
-
function hookNameFromMethod({ method, className, }) {
|
|
80
|
+
export function hookNameFromMethod({ method, className, }) {
|
|
77
81
|
const methodName = getNameFromMethod(method);
|
|
78
82
|
return `use${className}${capitalizeFirstLetter(methodName)}`;
|
|
79
83
|
}
|
|
80
|
-
function createQueryKeyFromMethod({ method, className, }) {
|
|
84
|
+
export function createQueryKeyFromMethod({ method, className, }) {
|
|
81
85
|
const customHookName = hookNameFromMethod({ method, className });
|
|
82
86
|
const queryKey = `${customHookName}Key`;
|
|
83
87
|
return queryKey;
|
|
@@ -87,7 +91,7 @@ function createQueryKeyFromMethod({ method, className, }) {
|
|
|
87
91
|
* @param queryString The type of query to use from react-query
|
|
88
92
|
* @param suffix The suffix to append to the hook name
|
|
89
93
|
*/
|
|
90
|
-
function createQueryHook({ queryString, suffix, responseDataType, requestParams, method, className, }) {
|
|
94
|
+
export function createQueryHook({ queryString, suffix, responseDataType, requestParams, method, className, }) {
|
|
91
95
|
const methodName = getNameFromMethod(method);
|
|
92
96
|
const customHookName = hookNameFromMethod({ method, className });
|
|
93
97
|
const queryKey = createQueryKeyFromMethod({ method, className });
|
|
@@ -109,23 +113,21 @@ function createQueryHook({ queryString, suffix, responseDataType, requestParams,
|
|
|
109
113
|
ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("queryFn")),
|
|
110
114
|
]),
|
|
111
115
|
])),
|
|
112
|
-
], undefined,
|
|
116
|
+
], undefined, EqualsOrGreaterThanToken, ts.factory.createCallExpression(ts.factory.createIdentifier(queryString), [
|
|
113
117
|
ts.factory.createTypeReferenceNode(TData),
|
|
114
118
|
ts.factory.createTypeReferenceNode(TError),
|
|
115
119
|
], [
|
|
116
120
|
ts.factory.createObjectLiteralExpression([
|
|
117
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryKey"), ts.factory.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
ts.factory.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
], false)),
|
|
128
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryFn"), ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createAsExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(className), ts.factory.createIdentifier(methodName)), undefined, method.getParameters().length
|
|
121
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryKey"), ts.factory.createCallExpression(BuildCommonTypeName(getQueryKeyFnName(queryKey)), undefined, method.getParameters().length
|
|
122
|
+
? [
|
|
123
|
+
ts.factory.createObjectLiteralExpression(method
|
|
124
|
+
.getParameters()
|
|
125
|
+
.map((param) => extractPropertiesFromObjectParam(param).map((p) => ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name))))
|
|
126
|
+
.flat()),
|
|
127
|
+
ts.factory.createIdentifier("queryKey"),
|
|
128
|
+
]
|
|
129
|
+
: [])),
|
|
130
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryFn"), ts.factory.createArrowFunction(undefined, undefined, [], undefined, EqualsOrGreaterThanToken, ts.factory.createAsExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(className), ts.factory.createIdentifier(methodName)), undefined, method.getParameters().length
|
|
129
131
|
? [
|
|
130
132
|
ts.factory.createObjectLiteralExpression(method
|
|
131
133
|
.getParameters()
|
|
@@ -176,11 +178,43 @@ export const createUseQuery = ({ className, method, jsDoc, }) => {
|
|
|
176
178
|
methodName,
|
|
177
179
|
queryKey,
|
|
178
180
|
});
|
|
181
|
+
const queryKeyFn = createQueryKeyFnExport(queryKey, method);
|
|
179
182
|
return {
|
|
180
183
|
apiResponse: defaultApiResponse,
|
|
181
184
|
returnType: returnTypeExport,
|
|
182
185
|
key: queryKeyExport,
|
|
183
186
|
queryHook: hookWithJsDoc,
|
|
184
187
|
suspenseQueryHook: suspenseHookWithJsDoc,
|
|
188
|
+
queryKeyFn,
|
|
185
189
|
};
|
|
186
190
|
};
|
|
191
|
+
function getQueryKeyFnName(queryKey) {
|
|
192
|
+
return `${capitalizeFirstLetter(queryKey)}Fn`;
|
|
193
|
+
}
|
|
194
|
+
function createQueryKeyFnExport(queryKey, method) {
|
|
195
|
+
const params = getRequestParamFromMethod(method);
|
|
196
|
+
// override key is used to allow the user to override the the queryKey values
|
|
197
|
+
const overrideKey = ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier("queryKey"), QuestionToken, ts.factory.createTypeReferenceNode("Array<unknown>", []));
|
|
198
|
+
return ts.factory.createVariableStatement([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
|
|
199
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier(getQueryKeyFnName(queryKey)), undefined, undefined, ts.factory.createArrowFunction(undefined, undefined, params ? [params, overrideKey] : [], undefined, EqualsOrGreaterThanToken, queryKeyFn(queryKey, method))),
|
|
200
|
+
], ts.NodeFlags.Const));
|
|
201
|
+
}
|
|
202
|
+
function queryKeyFn(queryKey, method) {
|
|
203
|
+
const params = getRequestParamFromMethod(method);
|
|
204
|
+
if (!params) {
|
|
205
|
+
return ts.factory.createArrayLiteralExpression([
|
|
206
|
+
ts.factory.createIdentifier(queryKey),
|
|
207
|
+
]);
|
|
208
|
+
}
|
|
209
|
+
return ts.factory.createArrayLiteralExpression([
|
|
210
|
+
ts.factory.createIdentifier(queryKey),
|
|
211
|
+
ts.factory.createSpreadElement(ts.factory.createParenthesizedExpression(ts.factory.createBinaryExpression(ts.factory.createIdentifier("queryKey"), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), method.getParameters().length
|
|
212
|
+
? ts.factory.createArrayLiteralExpression([
|
|
213
|
+
ts.factory.createObjectLiteralExpression(method
|
|
214
|
+
.getParameters()
|
|
215
|
+
.map((param) => extractPropertiesFromObjectParam(param).map((p) => ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name))))
|
|
216
|
+
.flat()),
|
|
217
|
+
])
|
|
218
|
+
: ts.factory.createArrayLiteralExpression([])))),
|
|
219
|
+
], false);
|
|
220
|
+
}
|