@7nohe/openapi-react-query-codegen 1.4.1 → 1.5.1
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 +77 -9
- package/dist/cli.mjs +2 -0
- package/dist/common.mjs +1 -1
- package/dist/constants.mjs +1 -0
- package/dist/createExports.mjs +9 -2
- package/dist/createSource.mjs +13 -4
- package/dist/createUseQuery.mjs +79 -15
- package/dist/generate.mjs +2 -0
- package/package.json +22 -21
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- Generates custom react hooks that use React Query's `useQuery`, `useSuspenseQuery` and `
|
|
9
|
+
- Generates custom react hooks that use React Query's `useQuery`, `useSuspenseQuery`, `useMutation` and `useInfiniteQuery` hooks
|
|
10
10
|
- Generates query keys and functions for query caching
|
|
11
11
|
- Generates pure TypeScript clients generated by [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts)
|
|
12
12
|
|
|
@@ -45,18 +45,20 @@ Options:
|
|
|
45
45
|
-V, --version output the version number
|
|
46
46
|
-i, --input <value> OpenAPI specification, can be a path, url or string content (required)
|
|
47
47
|
-o, --output <value> Output directory (default: "openapi")
|
|
48
|
-
-c, --client <value> HTTP client to generate
|
|
48
|
+
-c, --client <value> HTTP client to generate (choices: "angular", "axios", "fetch", "node", "xhr", default: "fetch")
|
|
49
49
|
--request <value> Path to custom request file
|
|
50
|
-
--format <value> Process output folder with formatter?
|
|
51
|
-
--lint
|
|
50
|
+
--format <value> Process output folder with formatter? (choices: "biome", "prettier")
|
|
51
|
+
--lint <value> Process output folder with linter? (choices: "biome", "eslint")
|
|
52
52
|
--operationId Use operation ID to generate operation names?
|
|
53
|
-
--serviceResponse <value> Define shape of returned value from service calls
|
|
53
|
+
--serviceResponse <value> Define shape of returned value from service calls (choices: "body", "response", default: "body")
|
|
54
54
|
--base <value> Manually set base in OpenAPI config instead of inferring from server value
|
|
55
|
-
--enums <value> Generate JavaScript objects from enum definitions?
|
|
55
|
+
--enums <value> Generate JavaScript objects from enum definitions? (choices: "javascript", "typescript")
|
|
56
56
|
--useDateType Use Date type instead of string for date types for models, this will not convert the data to a Date object
|
|
57
|
-
--debug
|
|
58
|
-
--noSchemas Disable generating schemas
|
|
59
|
-
--
|
|
57
|
+
--debug Run in debug mode?
|
|
58
|
+
--noSchemas Disable generating JSON schemas
|
|
59
|
+
--schemaType <value> Type of JSON schema [Default: 'json'] (choices: "form", "json")
|
|
60
|
+
--pageParam <value> Name of the query parameter used for pagination (default: "page")
|
|
61
|
+
--nextPageParam <value> Name of the response parameter used for next page (default: "nextPage")
|
|
60
62
|
-h, --help display help for command
|
|
61
63
|
```
|
|
62
64
|
|
|
@@ -234,6 +236,72 @@ function App() {
|
|
|
234
236
|
export default App;
|
|
235
237
|
```
|
|
236
238
|
|
|
239
|
+
##### Using Infinite Query hooks
|
|
240
|
+
|
|
241
|
+
This feature will generate a function in infiniteQueries.ts when the name specified by the `pageParam` option exists in the query parameters and the name specified by the `nextPageParam` option exists in the response.
|
|
242
|
+
|
|
243
|
+
Example Schema:
|
|
244
|
+
|
|
245
|
+
```yml
|
|
246
|
+
paths:
|
|
247
|
+
/paginated-pets:
|
|
248
|
+
get:
|
|
249
|
+
description: |
|
|
250
|
+
Returns paginated pets from the system that the user has access to
|
|
251
|
+
operationId: findPaginatedPets
|
|
252
|
+
parameters:
|
|
253
|
+
- name: page
|
|
254
|
+
in: query
|
|
255
|
+
description: page number
|
|
256
|
+
required: false
|
|
257
|
+
schema:
|
|
258
|
+
type: integer
|
|
259
|
+
format: int32
|
|
260
|
+
- name: tags
|
|
261
|
+
in: query
|
|
262
|
+
description: tags to filter by
|
|
263
|
+
required: false
|
|
264
|
+
style: form
|
|
265
|
+
schema:
|
|
266
|
+
type: array
|
|
267
|
+
items:
|
|
268
|
+
type: string
|
|
269
|
+
- name: limit
|
|
270
|
+
in: query
|
|
271
|
+
description: maximum number of results to return
|
|
272
|
+
required: false
|
|
273
|
+
schema:
|
|
274
|
+
type: integer
|
|
275
|
+
format: int32
|
|
276
|
+
responses:
|
|
277
|
+
'200':
|
|
278
|
+
description: pet response
|
|
279
|
+
content:
|
|
280
|
+
application/json:
|
|
281
|
+
schema:
|
|
282
|
+
type: object
|
|
283
|
+
properties:
|
|
284
|
+
pets:
|
|
285
|
+
type: array
|
|
286
|
+
items:
|
|
287
|
+
$ref: '#/components/schemas/Pet'
|
|
288
|
+
nextPage:
|
|
289
|
+
type: integer
|
|
290
|
+
format: int32
|
|
291
|
+
minimum: 1
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Usage of Generated Hooks:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { useDefaultServiceFindPaginatedPetsInfinite } from "@/openapi/queries/infiniteQueries";
|
|
298
|
+
|
|
299
|
+
const { data, fetchNextPage } = useDefaultServiceFindPaginatedPetsInfinite({
|
|
300
|
+
limit: 10,
|
|
301
|
+
tags: [],
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
237
305
|
##### Runtime Configuration
|
|
238
306
|
|
|
239
307
|
You can modify the default values used by the generated service calls by modifying the OpenAPI configuration singleton object.
|
package/dist/cli.mjs
CHANGED
|
@@ -34,6 +34,8 @@ async function setupProgram() {
|
|
|
34
34
|
.option("--debug", "Run in debug mode?")
|
|
35
35
|
.option("--noSchemas", "Disable generating JSON schemas")
|
|
36
36
|
.addOption(new Option("--schemaType <value>", "Type of JSON schema [Default: 'json']").choices(["form", "json"]))
|
|
37
|
+
.option("--pageParam <value>", "Name of the query parameter used for pagination", "page")
|
|
38
|
+
.option("--nextPageParam <value>", "Name of the response parameter used for next page", "nextPage")
|
|
37
39
|
.parse();
|
|
38
40
|
const options = program.opts();
|
|
39
41
|
await generate(options, version);
|
package/dist/common.mjs
CHANGED
|
@@ -80,7 +80,7 @@ export function extractPropertiesFromObjectParam(param) {
|
|
|
80
80
|
* TODO: Replace with a more robust solution.
|
|
81
81
|
*/
|
|
82
82
|
export function getShortType(type) {
|
|
83
|
-
return type.replaceAll(/import\("
|
|
83
|
+
return type.replaceAll(/import\(".*?"\)\./g, "");
|
|
84
84
|
}
|
|
85
85
|
export function getClassesFromService(node) {
|
|
86
86
|
const klasses = node.getClasses();
|
package/dist/constants.mjs
CHANGED
package/dist/createExports.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createPrefetch } from "./createPrefetch.mjs";
|
|
2
2
|
import { createUseMutation } from "./createUseMutation.mjs";
|
|
3
3
|
import { createUseQuery } from "./createUseQuery.mjs";
|
|
4
|
-
export const createExports = (service) => {
|
|
4
|
+
export const createExports = (service, pageParam, nextPageParam) => {
|
|
5
5
|
const { klasses } = service;
|
|
6
6
|
const methods = klasses.flatMap((k) => k.methods);
|
|
7
7
|
const allGet = methods.filter((m) => m.httpMethodName.toUpperCase().includes("GET"));
|
|
@@ -9,7 +9,7 @@ export const createExports = (service) => {
|
|
|
9
9
|
const allPut = methods.filter((m) => m.httpMethodName.toUpperCase().includes("PUT"));
|
|
10
10
|
const allPatch = methods.filter((m) => m.httpMethodName.toUpperCase().includes("PATCH"));
|
|
11
11
|
const allDelete = methods.filter((m) => m.httpMethodName.toUpperCase().includes("DELETE"));
|
|
12
|
-
const allGetQueries = allGet.map((m) => createUseQuery(m));
|
|
12
|
+
const allGetQueries = allGet.map((m) => createUseQuery(m, pageParam, nextPageParam));
|
|
13
13
|
const allPrefetchQueries = allGet.map((m) => createPrefetch(m));
|
|
14
14
|
const allPostMutations = allPost.map((m) => createUseMutation(m));
|
|
15
15
|
const allPutMutations = allPut.map((m) => createUseMutation(m));
|
|
@@ -37,6 +37,9 @@ export const createExports = (service) => {
|
|
|
37
37
|
mutationHook,
|
|
38
38
|
]);
|
|
39
39
|
const mainExports = [...mainQueries, ...mainMutations];
|
|
40
|
+
const infiniteQueriesExports = allQueries
|
|
41
|
+
.flatMap(({ infiniteQueryHook }) => [infiniteQueryHook])
|
|
42
|
+
.filter(Boolean);
|
|
40
43
|
const suspenseQueries = allQueries.flatMap(({ suspenseQueryHook }) => [
|
|
41
44
|
suspenseQueryHook,
|
|
42
45
|
]);
|
|
@@ -54,6 +57,10 @@ export const createExports = (service) => {
|
|
|
54
57
|
* Main exports are the hooks that are used in the components
|
|
55
58
|
*/
|
|
56
59
|
mainExports,
|
|
60
|
+
/**
|
|
61
|
+
* Infinite queries exports are the hooks that are used in the infinite scroll components
|
|
62
|
+
*/
|
|
63
|
+
infiniteQueriesExports,
|
|
57
64
|
/**
|
|
58
65
|
* Suspense exports are the hooks that are used in the suspense components
|
|
59
66
|
*/
|
package/dist/createSource.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { OpenApiRqFiles } from "./constants.mjs";
|
|
|
5
5
|
import { createExports } from "./createExports.mjs";
|
|
6
6
|
import { createImports } from "./createImports.mjs";
|
|
7
7
|
import { getServices } from "./service.mjs";
|
|
8
|
-
const createSourceFile = async (outputPath, serviceEndName) => {
|
|
8
|
+
const createSourceFile = async (outputPath, serviceEndName, pageParam, nextPageParam) => {
|
|
9
9
|
const project = new Project({
|
|
10
10
|
// Optionally specify compiler options, tsconfig.json, in-memory file system, and more here.
|
|
11
11
|
// If you initialize with a tsconfig.json, then it will automatically populate the project
|
|
@@ -20,25 +20,28 @@ const createSourceFile = async (outputPath, serviceEndName) => {
|
|
|
20
20
|
serviceEndName,
|
|
21
21
|
project,
|
|
22
22
|
});
|
|
23
|
-
const exports = createExports(service);
|
|
23
|
+
const exports = createExports(service, pageParam, nextPageParam);
|
|
24
24
|
const commonSource = ts.factory.createSourceFile([...imports, ...exports.allCommon], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
25
25
|
const commonImport = ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, ts.factory.createIdentifier("* as Common"), undefined), ts.factory.createStringLiteral(`./${OpenApiRqFiles.common}`), undefined);
|
|
26
26
|
const commonExport = ts.factory.createExportDeclaration(undefined, false, undefined, ts.factory.createStringLiteral(`./${OpenApiRqFiles.common}`), undefined);
|
|
27
27
|
const queriesExport = ts.factory.createExportDeclaration(undefined, false, undefined, ts.factory.createStringLiteral(`./${OpenApiRqFiles.queries}`), undefined);
|
|
28
28
|
const mainSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.mainExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
29
|
+
const infiniteQueriesSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.infiniteQueriesExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
29
30
|
const suspenseSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.suspenseExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
30
31
|
const indexSource = ts.factory.createSourceFile([commonExport, queriesExport], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
31
32
|
const prefetchSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.allPrefetchExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
|
|
32
33
|
return {
|
|
33
34
|
commonSource,
|
|
35
|
+
infiniteQueriesSource,
|
|
34
36
|
mainSource,
|
|
35
37
|
suspenseSource,
|
|
36
38
|
indexSource,
|
|
37
39
|
prefetchSource,
|
|
38
40
|
};
|
|
39
41
|
};
|
|
40
|
-
export const createSource = async ({ outputPath, version, serviceEndName, }) => {
|
|
42
|
+
export const createSource = async ({ outputPath, version, serviceEndName, pageParam, nextPageParam, }) => {
|
|
41
43
|
const queriesFile = ts.createSourceFile(`${OpenApiRqFiles.queries}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
44
|
+
const infiniteQueriesFile = ts.createSourceFile(`${OpenApiRqFiles.infiniteQueries}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
42
45
|
const commonFile = ts.createSourceFile(`${OpenApiRqFiles.common}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
43
46
|
const suspenseFile = ts.createSourceFile(`${OpenApiRqFiles.suspense}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
44
47
|
const indexFile = ts.createSourceFile(`${OpenApiRqFiles.index}.ts`, "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
@@ -47,12 +50,14 @@ export const createSource = async ({ outputPath, version, serviceEndName, }) =>
|
|
|
47
50
|
newLine: ts.NewLineKind.LineFeed,
|
|
48
51
|
removeComments: false,
|
|
49
52
|
});
|
|
50
|
-
const { commonSource, mainSource, suspenseSource, indexSource, prefetchSource, } = await createSourceFile(outputPath, serviceEndName);
|
|
53
|
+
const { commonSource, mainSource, infiniteQueriesSource, suspenseSource, indexSource, prefetchSource, } = await createSourceFile(outputPath, serviceEndName, pageParam, nextPageParam);
|
|
51
54
|
const comment = `// generated with @7nohe/openapi-react-query-codegen@${version} \n\n`;
|
|
52
55
|
const commonResult = comment +
|
|
53
56
|
printer.printNode(ts.EmitHint.Unspecified, commonSource, commonFile);
|
|
54
57
|
const mainResult = comment +
|
|
55
58
|
printer.printNode(ts.EmitHint.Unspecified, mainSource, queriesFile);
|
|
59
|
+
const infiniteQueriesResult = comment +
|
|
60
|
+
printer.printNode(ts.EmitHint.Unspecified, infiniteQueriesSource, infiniteQueriesFile);
|
|
56
61
|
const suspenseResult = comment +
|
|
57
62
|
printer.printNode(ts.EmitHint.Unspecified, suspenseSource, suspenseFile);
|
|
58
63
|
const indexResult = comment +
|
|
@@ -68,6 +73,10 @@ export const createSource = async ({ outputPath, version, serviceEndName, }) =>
|
|
|
68
73
|
name: `${OpenApiRqFiles.common}.ts`,
|
|
69
74
|
content: commonResult,
|
|
70
75
|
},
|
|
76
|
+
{
|
|
77
|
+
name: `${OpenApiRqFiles.infiniteQueries}.ts`,
|
|
78
|
+
content: infiniteQueriesResult,
|
|
79
|
+
},
|
|
71
80
|
{
|
|
72
81
|
name: `${OpenApiRqFiles.queries}.ts`,
|
|
73
82
|
content: mainResult,
|
package/dist/createUseQuery.mjs
CHANGED
|
@@ -28,13 +28,15 @@ export const createApiResponseType = ({ className, methodName, }) => {
|
|
|
28
28
|
responseDataType,
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
|
-
export function getRequestParamFromMethod(method) {
|
|
31
|
+
export function getRequestParamFromMethod(method, pageParam) {
|
|
32
32
|
if (!method.getParameters().length) {
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
const params = method.getParameters().flatMap((param) => {
|
|
36
36
|
const paramNodes = extractPropertiesFromObjectParam(param);
|
|
37
|
-
return paramNodes
|
|
37
|
+
return paramNodes
|
|
38
|
+
.filter((p) => p.name !== pageParam)
|
|
39
|
+
.map((refParam) => ({
|
|
38
40
|
name: refParam.name,
|
|
39
41
|
typeName: getShortType(refParam.type.getText()),
|
|
40
42
|
optional: refParam.optional,
|
|
@@ -88,20 +90,33 @@ export function createQueryKeyFromMethod({ method, className, }) {
|
|
|
88
90
|
* @param queryString The type of query to use from react-query
|
|
89
91
|
* @param suffix The suffix to append to the hook name
|
|
90
92
|
*/
|
|
91
|
-
export function createQueryHook({ queryString, suffix, responseDataType, requestParams, method, className, }) {
|
|
93
|
+
export function createQueryHook({ queryString, suffix, responseDataType, requestParams, method, className, pageParam, nextPageParam, }) {
|
|
92
94
|
const methodName = getNameFromMethod(method);
|
|
93
95
|
const customHookName = hookNameFromMethod({ method, className });
|
|
94
96
|
const queryKey = createQueryKeyFromMethod({ method, className });
|
|
97
|
+
if (queryString === "useInfiniteQuery" &&
|
|
98
|
+
(pageParam === undefined || nextPageParam === undefined)) {
|
|
99
|
+
throw new Error("pageParam and nextPageParam are required for infinite queries");
|
|
100
|
+
}
|
|
101
|
+
const isInfiniteQuery = queryString === "useInfiniteQuery";
|
|
102
|
+
const responseDataTypeRef = responseDataType.default;
|
|
103
|
+
const responseDataTypeIdentifier = responseDataTypeRef.typeName;
|
|
95
104
|
const hookExport = ts.factory.createVariableStatement([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
|
|
96
105
|
ts.factory.createVariableDeclaration(ts.factory.createIdentifier(`${customHookName}${suffix}`), undefined, undefined, ts.factory.createArrowFunction(undefined, ts.factory.createNodeArray([
|
|
97
|
-
|
|
106
|
+
isInfiniteQuery
|
|
107
|
+
? ts.factory.createTypeParameterDeclaration(undefined, TData, undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("InfiniteData"), [
|
|
108
|
+
ts.factory.createTypeReferenceNode(responseDataTypeIdentifier),
|
|
109
|
+
]))
|
|
110
|
+
: responseDataType,
|
|
98
111
|
ts.factory.createTypeParameterDeclaration(undefined, TError, undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)),
|
|
99
112
|
ts.factory.createTypeParameterDeclaration(undefined, "TQueryKey", queryKeyConstraint, ts.factory.createArrayTypeNode(ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword))),
|
|
100
113
|
]), [
|
|
101
114
|
...requestParams,
|
|
102
115
|
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier("queryKey"), ts.factory.createToken(ts.SyntaxKind.QuestionToken), queryKeyGenericType),
|
|
103
116
|
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier("options"), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("Omit"), [
|
|
104
|
-
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(
|
|
117
|
+
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(isInfiniteQuery
|
|
118
|
+
? "UseInfiniteQueryOptions"
|
|
119
|
+
: "UseQueryOptions"), [
|
|
105
120
|
ts.factory.createTypeReferenceNode(TData),
|
|
106
121
|
ts.factory.createTypeReferenceNode(TError),
|
|
107
122
|
]),
|
|
@@ -110,33 +125,44 @@ export function createQueryHook({ queryString, suffix, responseDataType, request
|
|
|
110
125
|
ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("queryFn")),
|
|
111
126
|
]),
|
|
112
127
|
])),
|
|
113
|
-
], undefined, EqualsOrGreaterThanToken, ts.factory.createCallExpression(ts.factory.createIdentifier(queryString),
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
128
|
+
], undefined, EqualsOrGreaterThanToken, ts.factory.createCallExpression(ts.factory.createIdentifier(queryString), isInfiniteQuery
|
|
129
|
+
? []
|
|
130
|
+
: [
|
|
131
|
+
ts.factory.createTypeReferenceNode(TData),
|
|
132
|
+
ts.factory.createTypeReferenceNode(TError),
|
|
133
|
+
], [
|
|
117
134
|
ts.factory.createObjectLiteralExpression([
|
|
118
135
|
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryKey"), ts.factory.createCallExpression(BuildCommonTypeName(getQueryKeyFnName(queryKey)), undefined, method.getParameters().length
|
|
119
136
|
? [
|
|
120
|
-
ts.factory.createObjectLiteralExpression(method
|
|
121
|
-
.
|
|
122
|
-
.
|
|
137
|
+
ts.factory.createObjectLiteralExpression(method.getParameters().flatMap((param) => extractPropertiesFromObjectParam(param)
|
|
138
|
+
.filter((p) => p.name !== pageParam)
|
|
139
|
+
.map((p) => ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name))))),
|
|
123
140
|
ts.factory.createIdentifier("queryKey"),
|
|
124
141
|
]
|
|
125
142
|
: [ts.factory.createIdentifier("queryKey")])),
|
|
126
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryFn"), ts.factory.createArrowFunction(undefined, undefined,
|
|
143
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryFn"), ts.factory.createArrowFunction(undefined, undefined, isInfiniteQuery
|
|
144
|
+
? [
|
|
145
|
+
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createObjectBindingPattern([
|
|
146
|
+
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier("pageParam"), undefined),
|
|
147
|
+
]), undefined, undefined),
|
|
148
|
+
]
|
|
149
|
+
: [], undefined, EqualsOrGreaterThanToken, ts.factory.createAsExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(className), ts.factory.createIdentifier(methodName)), undefined, method.getParameters().length
|
|
127
150
|
? [
|
|
128
151
|
ts.factory.createObjectLiteralExpression(method
|
|
129
152
|
.getParameters()
|
|
130
|
-
.flatMap((param) => extractPropertiesFromObjectParam(param).map((p) =>
|
|
153
|
+
.flatMap((param) => extractPropertiesFromObjectParam(param).map((p) => p.name === pageParam
|
|
154
|
+
? ts.factory.createPropertyAssignment(ts.factory.createIdentifier(p.name), ts.factory.createAsExpression(ts.factory.createIdentifier("pageParam"), ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)))
|
|
155
|
+
: ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name))))),
|
|
131
156
|
]
|
|
132
157
|
: undefined), ts.factory.createTypeReferenceNode(TData)))),
|
|
158
|
+
...createInfiniteQueryParams(pageParam, nextPageParam),
|
|
133
159
|
ts.factory.createSpreadAssignment(ts.factory.createIdentifier("options")),
|
|
134
160
|
]),
|
|
135
161
|
]))),
|
|
136
162
|
], ts.NodeFlags.Const));
|
|
137
163
|
return hookExport;
|
|
138
164
|
}
|
|
139
|
-
export const createUseQuery = ({ className, method, jsDoc,
|
|
165
|
+
export const createUseQuery = ({ className, method, jsDoc }, pageParam, nextPageParam) => {
|
|
140
166
|
const methodName = getNameFromMethod(method);
|
|
141
167
|
const queryKey = createQueryKeyFromMethod({ method, className });
|
|
142
168
|
const { apiResponse: defaultApiResponse, responseDataType } = createApiResponseType({
|
|
@@ -144,7 +170,15 @@ export const createUseQuery = ({ className, method, jsDoc, }) => {
|
|
|
144
170
|
methodName,
|
|
145
171
|
});
|
|
146
172
|
const requestParam = getRequestParamFromMethod(method);
|
|
173
|
+
const infiniteRequestParam = getRequestParamFromMethod(method, pageParam);
|
|
147
174
|
const requestParams = requestParam ? [requestParam] : [];
|
|
175
|
+
const requestParamNames = requestParams
|
|
176
|
+
.filter((p) => p.name.kind === ts.SyntaxKind.ObjectBindingPattern)
|
|
177
|
+
.map((p) => p.name);
|
|
178
|
+
const requestParamTexts = requestParamNames
|
|
179
|
+
.at(0)
|
|
180
|
+
?.elements.filter((e) => e.name.kind === ts.SyntaxKind.Identifier)
|
|
181
|
+
.map((e) => e.name.escapedText);
|
|
148
182
|
const queryHook = createQueryHook({
|
|
149
183
|
queryString: "useQuery",
|
|
150
184
|
suffix: "",
|
|
@@ -161,8 +195,24 @@ export const createUseQuery = ({ className, method, jsDoc, }) => {
|
|
|
161
195
|
method,
|
|
162
196
|
className,
|
|
163
197
|
});
|
|
198
|
+
const isInfiniteQuery = requestParamTexts?.includes(pageParam) ?? false;
|
|
199
|
+
const infiniteQueryHook = isInfiniteQuery
|
|
200
|
+
? createQueryHook({
|
|
201
|
+
queryString: "useInfiniteQuery",
|
|
202
|
+
suffix: "Infinite",
|
|
203
|
+
responseDataType,
|
|
204
|
+
requestParams: infiniteRequestParam ? [infiniteRequestParam] : [],
|
|
205
|
+
method,
|
|
206
|
+
className,
|
|
207
|
+
pageParam,
|
|
208
|
+
nextPageParam,
|
|
209
|
+
})
|
|
210
|
+
: undefined;
|
|
164
211
|
const hookWithJsDoc = addJSDocToNode(queryHook, jsDoc);
|
|
165
212
|
const suspenseHookWithJsDoc = addJSDocToNode(suspenseQueryHook, jsDoc);
|
|
213
|
+
const infiniteHookWithJsDoc = infiniteQueryHook
|
|
214
|
+
? addJSDocToNode(infiniteQueryHook, jsDoc)
|
|
215
|
+
: undefined;
|
|
166
216
|
const returnTypeExport = createReturnTypeExport({
|
|
167
217
|
className,
|
|
168
218
|
methodName,
|
|
@@ -180,6 +230,7 @@ export const createUseQuery = ({ className, method, jsDoc, }) => {
|
|
|
180
230
|
key: queryKeyExport,
|
|
181
231
|
queryHook: hookWithJsDoc,
|
|
182
232
|
suspenseQueryHook: suspenseHookWithJsDoc,
|
|
233
|
+
infiniteQueryHook: infiniteHookWithJsDoc,
|
|
183
234
|
queryKeyFn,
|
|
184
235
|
};
|
|
185
236
|
};
|
|
@@ -206,3 +257,16 @@ function queryKeyFn(queryKey, method) {
|
|
|
206
257
|
: ts.factory.createArrayLiteralExpression([])))),
|
|
207
258
|
], false);
|
|
208
259
|
}
|
|
260
|
+
function createInfiniteQueryParams(pageParam, nextPageParam) {
|
|
261
|
+
if (pageParam === undefined || nextPageParam === undefined) {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
return [
|
|
265
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("initialPageParam"), ts.factory.createNumericLiteral(1)),
|
|
266
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("getNextPageParam"),
|
|
267
|
+
// (response) => (response as { nextPage: number }).nextPage,
|
|
268
|
+
ts.factory.createArrowFunction(undefined, undefined, [
|
|
269
|
+
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier("response"), undefined, undefined),
|
|
270
|
+
], undefined, EqualsOrGreaterThanToken, ts.factory.createPropertyAccessExpression(ts.factory.createParenthesizedExpression(ts.factory.createAsExpression(ts.factory.createIdentifier("response"), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(`{ ${nextPageParam}: number }`)))), ts.factory.createIdentifier(nextPageParam)))),
|
|
271
|
+
];
|
|
272
|
+
}
|
package/dist/generate.mjs
CHANGED
|
@@ -39,6 +39,8 @@ export async function generate(options, version) {
|
|
|
39
39
|
outputPath: openApiOutputPath,
|
|
40
40
|
version,
|
|
41
41
|
serviceEndName: "Service", // we are hard coding this because changing the service end name was depreciated in @hey-api/openapi-ts
|
|
42
|
+
pageParam: formattedOptions.pageParam,
|
|
43
|
+
nextPageParam: formattedOptions.nextPageParam,
|
|
42
44
|
});
|
|
43
45
|
await print(source, formattedOptions);
|
|
44
46
|
const queriesOutputPath = buildQueriesOutputPath(options.output);
|
package/package.json
CHANGED
|
@@ -1,23 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7nohe/openapi-react-query-codegen",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "OpenAPI React Query Codegen",
|
|
5
|
-
"bin": {
|
|
6
|
-
"openapi-rq": "dist/cli.mjs"
|
|
7
|
-
},
|
|
8
|
-
"type": "module",
|
|
9
|
-
"workspaces": [
|
|
10
|
-
"examples/*"
|
|
11
|
-
],
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/7nohe/openapi-react-query-codegen.git"
|
|
15
|
-
},
|
|
16
|
-
"homepage": "https://github.com/7nohe/openapi-react-query-codegen",
|
|
17
|
-
"bugs": "https://github.com/7nohe/openapi-react-query-codegen/issues",
|
|
18
|
-
"files": [
|
|
19
|
-
"dist"
|
|
20
|
-
],
|
|
21
5
|
"keywords": [
|
|
22
6
|
"codegen",
|
|
23
7
|
"react-query",
|
|
@@ -28,8 +12,24 @@
|
|
|
28
12
|
"openapi-typescript-codegen",
|
|
29
13
|
"@hey-api/openapi-ts"
|
|
30
14
|
],
|
|
31
|
-
"
|
|
15
|
+
"homepage": "https://github.com/7nohe/openapi-react-query-codegen",
|
|
16
|
+
"bugs": "https://github.com/7nohe/openapi-react-query-codegen/issues",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/7nohe/openapi-react-query-codegen.git"
|
|
20
|
+
},
|
|
32
21
|
"license": "MIT",
|
|
22
|
+
"author": "Daiki Urata (@7nohe)",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"bin": {
|
|
25
|
+
"openapi-rq": "dist/cli.mjs"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"workspaces": [
|
|
31
|
+
"examples/*"
|
|
32
|
+
],
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@hey-api/openapi-ts": "0.45.1"
|
|
35
35
|
},
|
|
@@ -53,7 +53,8 @@
|
|
|
53
53
|
"typescript": "5.x"
|
|
54
54
|
},
|
|
55
55
|
"engines": {
|
|
56
|
-
"node": ">=14"
|
|
56
|
+
"node": ">=14",
|
|
57
|
+
"pnpm": ">=9"
|
|
57
58
|
},
|
|
58
59
|
"scripts": {
|
|
59
60
|
"build": "rimraf dist && tsc -p tsconfig.json",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"lint:fix": "biome check --apply .",
|
|
62
63
|
"preview": "npm run build && npm -C examples/react-app run generate:api",
|
|
63
64
|
"release": "npx git-ensure -a && npx bumpp --commit --tag --push",
|
|
64
|
-
"
|
|
65
|
-
"
|
|
65
|
+
"snapshot": "vitest --update",
|
|
66
|
+
"test": "vitest --coverage.enabled true"
|
|
66
67
|
}
|
|
67
68
|
}
|