@7nohe/openapi-react-query-codegen 3.0.0-beta.1 → 3.0.0-beta.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/dist/constants.mjs
CHANGED
|
@@ -117,3 +117,67 @@ export function buildMutationKeyFn(op) {
|
|
|
117
117
|
],
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Build the client options type for infinite queries.
|
|
122
|
+
* The page parameter is excluded because TanStack Query supplies it via the
|
|
123
|
+
* pageParam mechanism (#140).
|
|
124
|
+
* Example:
|
|
125
|
+
* export type FindPaginatedPetsInfiniteClientOptions = Omit<Options<FindPaginatedPetsData, true>, "query"> &
|
|
126
|
+
* { query?: Omit<NonNullable<FindPaginatedPetsData["query"]>, "page"> };
|
|
127
|
+
*/
|
|
128
|
+
export function buildInfiniteClientOptionsType(op, ctx) {
|
|
129
|
+
const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
|
|
130
|
+
? `${op.capitalizedMethodName}Data`
|
|
131
|
+
: "unknown";
|
|
132
|
+
const type = dataTypeName === "unknown"
|
|
133
|
+
? "Options<unknown, true>"
|
|
134
|
+
: `Omit<Options<${dataTypeName}, true>, "query"> & { query?: Omit<NonNullable<${dataTypeName}["query"]>, "${ctx.pageParam}"> }`;
|
|
135
|
+
return {
|
|
136
|
+
kind: StructureKind.TypeAlias,
|
|
137
|
+
isExported: true,
|
|
138
|
+
name: `${op.capitalizedMethodName}InfiniteClientOptions`,
|
|
139
|
+
type,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Build the infinite query key constant.
|
|
144
|
+
* Kept distinct from the plain query key so cached InfiniteData never
|
|
145
|
+
* collides with plain query data for the same operation (#140).
|
|
146
|
+
* Example: export const useFindPaginatedPetsInfiniteKey = "FindPaginatedPetsInfinite";
|
|
147
|
+
*/
|
|
148
|
+
export function buildInfiniteQueryKeyConst(op) {
|
|
149
|
+
return {
|
|
150
|
+
kind: StructureKind.VariableStatement,
|
|
151
|
+
isExported: true,
|
|
152
|
+
declarationKind: VariableDeclarationKind.Const,
|
|
153
|
+
declarations: [
|
|
154
|
+
{
|
|
155
|
+
name: `use${op.capitalizedMethodName}InfiniteKey`,
|
|
156
|
+
initializer: `"${op.capitalizedMethodName}Infinite"`,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Build the infinite query key function.
|
|
163
|
+
* Example: export const UseFindPaginatedPetsInfiniteKeyFn = (clientOptions: FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) =>
|
|
164
|
+
* [useFindPaginatedPetsInfiniteKey, ...(queryKey ?? [clientOptions])];
|
|
165
|
+
*/
|
|
166
|
+
export function buildInfiniteQueryKeyFn(op) {
|
|
167
|
+
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
168
|
+
const params = [
|
|
169
|
+
`clientOptions: ${op.capitalizedMethodName}InfiniteClientOptions${defaultValue}`,
|
|
170
|
+
"queryKey?: Array<unknown>",
|
|
171
|
+
];
|
|
172
|
+
return {
|
|
173
|
+
kind: StructureKind.VariableStatement,
|
|
174
|
+
isExported: true,
|
|
175
|
+
declarationKind: VariableDeclarationKind.Const,
|
|
176
|
+
declarations: [
|
|
177
|
+
{
|
|
178
|
+
name: `Use${op.capitalizedMethodName}InfiniteKeyFn`,
|
|
179
|
+
initializer: `(${params.join(", ")}) => [use${op.capitalizedMethodName}InfiniteKey, ...(queryKey ?? [clientOptions])]`,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -39,10 +39,19 @@ function getOptionsTypeName(hookType) {
|
|
|
39
39
|
return "UseQueryOptions";
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the generated Data type name for an operation, falling back to
|
|
44
|
+
* unknown when the operation has no generated Data type.
|
|
45
|
+
*/
|
|
46
|
+
export function getDataTypeName(op, ctx) {
|
|
47
|
+
return ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
|
|
48
|
+
? `${op.capitalizedMethodName}Data`
|
|
49
|
+
: "unknown";
|
|
50
|
+
}
|
|
42
51
|
/**
|
|
43
52
|
* Build the client options parameter string.
|
|
44
53
|
*/
|
|
45
|
-
function buildClientOptionsParam(op, ctx) {
|
|
54
|
+
export function buildClientOptionsParam(op, ctx) {
|
|
46
55
|
const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
|
|
47
56
|
? `${op.capitalizedMethodName}Data`
|
|
48
57
|
: "unknown";
|
|
@@ -120,7 +129,7 @@ export function buildUseSuspenseQueryHook(op, ctx) {
|
|
|
120
129
|
* Build the nested type for getNextPageParam.
|
|
121
130
|
* E.g., "meta.next" becomes "{ meta: { next: number } }"
|
|
122
131
|
*/
|
|
123
|
-
function buildNestedNextPageType(nextPageParam) {
|
|
132
|
+
export function buildNestedNextPageType(nextPageParam) {
|
|
124
133
|
const segments = nextPageParam.split(".");
|
|
125
134
|
return segments.reduceRight((acc, segment) => {
|
|
126
135
|
return `{ ${segment}: ${acc} }`;
|
|
@@ -136,19 +145,19 @@ export function buildUseInfiniteQueryHook(op, ctx) {
|
|
|
136
145
|
const hookName = `use${op.capitalizedMethodName}Infinite`;
|
|
137
146
|
const errorType = getErrorType(op, ctx);
|
|
138
147
|
const baseDataType = `Common.${op.capitalizedMethodName}DefaultResponse`;
|
|
139
|
-
const dataTypeName =
|
|
140
|
-
|
|
141
|
-
|
|
148
|
+
const dataTypeName = getDataTypeName(op, ctx);
|
|
149
|
+
// Infinite queries take a dedicated options type that excludes the page
|
|
150
|
+
// parameter — it is supplied by TanStack Query's pageParam mechanism
|
|
142
151
|
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
143
|
-
const clientOptionsParam = `clientOptions:
|
|
152
|
+
const clientOptionsParam = `clientOptions: Common.${op.capitalizedMethodName}InfiniteClientOptions${defaultValue}`;
|
|
144
153
|
// Build the queryFn with pageParam handling
|
|
145
|
-
const queryFn = `({ pageParam }) => ${op.methodName}({ ...clientOptions, query: { ...clientOptions.query, ${ctx.pageParam}: pageParam as number } }).then(response => response.data as TData) as TData`;
|
|
154
|
+
const queryFn = `({ pageParam }) => ${op.methodName}({ ...clientOptions, query: { ...clientOptions.query, ${ctx.pageParam}: pageParam as number } } as Options<${dataTypeName}, true>).then(response => response.data as TData) as TData`;
|
|
146
155
|
// Build getNextPageParam with nested type
|
|
147
156
|
const nestedType = buildNestedNextPageType(ctx.nextPageParam);
|
|
148
157
|
const getNextPageParam = `getNextPageParam: (response) => (response as ${nestedType}).${ctx.nextPageParam}`;
|
|
149
158
|
// initialPageParam is a string literal
|
|
150
159
|
const infiniteOptions = `initialPageParam: "${ctx.initialPageParam}", ${getNextPageParam}`;
|
|
151
|
-
const body = `useInfiniteQuery({ queryKey: Common.Use${op.capitalizedMethodName}
|
|
160
|
+
const body = `useInfiniteQuery({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
152
161
|
return {
|
|
153
162
|
kind: StructureKind.VariableStatement,
|
|
154
163
|
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
|
|
2
|
+
import { buildClientOptionsParam, buildNestedNextPageType, getDataTypeName, } from "./buildQueryHooks.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Build a queryOptions factory for a GET operation.
|
|
5
|
+
* The factory centralizes queryKey and queryFn so they can be reused with
|
|
6
|
+
* every TanStack Query utility (useQuery, useQueries, prefetchQuery,
|
|
7
|
+
* ensureQueryData, setQueryData, ...) with full type safety.
|
|
8
|
+
* Example:
|
|
9
|
+
* export const findPetsOptions = (clientOptions: Options<FindPetsData, true> = {}, queryKey?: Array<unknown>) =>
|
|
10
|
+
* queryOptions({
|
|
11
|
+
* queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey),
|
|
12
|
+
* queryFn: () => findPets({ ...clientOptions }).then(response => response.data),
|
|
13
|
+
* });
|
|
14
|
+
*/
|
|
15
|
+
export function buildQueryOptionsFn(op, ctx) {
|
|
16
|
+
const fnName = `${op.methodName}Options`;
|
|
17
|
+
const clientOptionsParam = buildClientOptionsParam(op, ctx);
|
|
18
|
+
const queryFn = `() => ${op.methodName}({ ...clientOptions }).then(response => response.data)`;
|
|
19
|
+
const body = `queryOptions({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn} })`;
|
|
20
|
+
return {
|
|
21
|
+
kind: StructureKind.VariableStatement,
|
|
22
|
+
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
23
|
+
leadingTrivia: op.jsDoc ? `${op.jsDoc}\n` : undefined,
|
|
24
|
+
isExported: true,
|
|
25
|
+
declarationKind: VariableDeclarationKind.Const,
|
|
26
|
+
declarations: [
|
|
27
|
+
{
|
|
28
|
+
name: fnName,
|
|
29
|
+
initializer: `(${clientOptionsParam}, queryKey?: Array<unknown>) => ${body}`,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build an infiniteQueryOptions factory for a paginatable GET operation.
|
|
36
|
+
* Uses the dedicated infinite query key and page-less options type.
|
|
37
|
+
* Example:
|
|
38
|
+
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) =>
|
|
39
|
+
* infiniteQueryOptions({
|
|
40
|
+
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions, queryKey),
|
|
41
|
+
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam } } as Options<FindPaginatedPetsData, true>).then(response => response.data),
|
|
42
|
+
* initialPageParam: 1,
|
|
43
|
+
* getNextPageParam: (response) => (response as { nextPage: number }).nextPage,
|
|
44
|
+
* });
|
|
45
|
+
*/
|
|
46
|
+
export function buildInfiniteQueryOptionsFn(op, ctx) {
|
|
47
|
+
if (!op.isPaginatable) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const fnName = `${op.methodName}InfiniteOptions`;
|
|
51
|
+
const dataTypeName = getDataTypeName(op, ctx);
|
|
52
|
+
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
53
|
+
const clientOptionsParam = `clientOptions: Common.${op.capitalizedMethodName}InfiniteClientOptions${defaultValue}`;
|
|
54
|
+
const queryFn = `({ pageParam }) => ${op.methodName}({ ...clientOptions, query: { ...clientOptions.query, ${ctx.pageParam}: pageParam } } as Options<${dataTypeName}, true>).then(response => response.data)`;
|
|
55
|
+
// Emit a numeric literal when possible so the inferred pageParam type
|
|
56
|
+
// matches what getNextPageParam returns
|
|
57
|
+
const initialPageParam = /^-?\d+$/.test(ctx.initialPageParam)
|
|
58
|
+
? ctx.initialPageParam
|
|
59
|
+
: JSON.stringify(ctx.initialPageParam);
|
|
60
|
+
const nestedType = buildNestedNextPageType(ctx.nextPageParam);
|
|
61
|
+
const getNextPageParam = `(response) => (response as ${nestedType}).${ctx.nextPageParam}`;
|
|
62
|
+
const body = `infiniteQueryOptions({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, initialPageParam: ${initialPageParam}, getNextPageParam: ${getNextPageParam} })`;
|
|
63
|
+
return {
|
|
64
|
+
kind: StructureKind.VariableStatement,
|
|
65
|
+
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
66
|
+
leadingTrivia: op.jsDoc ? `${op.jsDoc}\n` : undefined,
|
|
67
|
+
isExported: true,
|
|
68
|
+
declarationKind: VariableDeclarationKind.Const,
|
|
69
|
+
declarations: [
|
|
70
|
+
{
|
|
71
|
+
name: fnName,
|
|
72
|
+
initializer: `(${clientOptionsParam}, queryKey?: Array<unknown>) => ${body}`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { StructureKind, } from "ts-morph";
|
|
2
2
|
import { OpenApiRqFiles } from "../constants.mjs";
|
|
3
|
-
import { buildDefaultResponseType, buildMutationKeyConst, buildMutationKeyFn, buildMutationResultType, buildQueryKeyConst, buildQueryKeyFn, buildQueryResultType, } from "./buildCommon.mjs";
|
|
3
|
+
import { buildDefaultResponseType, buildInfiniteClientOptionsType, buildInfiniteQueryKeyConst, buildInfiniteQueryKeyFn, buildMutationKeyConst, buildMutationKeyFn, buildMutationResultType, buildQueryKeyConst, buildQueryKeyFn, buildQueryResultType, } from "./buildCommon.mjs";
|
|
4
4
|
import { buildUseMutationHook } from "./buildMutationHooks.mjs";
|
|
5
5
|
import { buildEnsureQueryDataFn, buildPrefetchFn, buildUseInfiniteQueryHook, buildUseQueryHook, buildUseSuspenseQueryHook, } from "./buildQueryHooks.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { buildInfiniteQueryOptionsFn, buildQueryOptionsFn, } from "./buildQueryOptions.mjs";
|
|
7
|
+
import { buildAxiosErrorImport, buildClientImport, buildCommonImport, buildModelImport, buildQueryImport, buildQueryOptionsImport, buildServiceImport, createGenerationProject, } from "./projectFactory.mjs";
|
|
7
8
|
/**
|
|
8
9
|
* Build imports for common.ts file.
|
|
9
10
|
*/
|
|
@@ -43,6 +44,10 @@ function generateIndexFile(ctx) {
|
|
|
43
44
|
kind: StructureKind.ExportDeclaration,
|
|
44
45
|
moduleSpecifier: "./queries",
|
|
45
46
|
},
|
|
47
|
+
{
|
|
48
|
+
kind: StructureKind.ExportDeclaration,
|
|
49
|
+
moduleSpecifier: "./queryOptions",
|
|
50
|
+
},
|
|
46
51
|
];
|
|
47
52
|
sourceFile.addExportDeclarations(exports);
|
|
48
53
|
return sourceFile.getFullText();
|
|
@@ -65,6 +70,12 @@ function generateCommonFile(operations, ctx) {
|
|
|
65
70
|
sourceFile.addVariableStatement(buildQueryKeyConst(op));
|
|
66
71
|
sourceFile.addVariableStatement(buildQueryKeyFn(op, ctx));
|
|
67
72
|
}
|
|
73
|
+
// Add dedicated infinite query types and keys for paginatable operations
|
|
74
|
+
for (const op of getOperations.filter((o) => o.isPaginatable)) {
|
|
75
|
+
sourceFile.addTypeAlias(buildInfiniteClientOptionsType(op, ctx));
|
|
76
|
+
sourceFile.addVariableStatement(buildInfiniteQueryKeyConst(op));
|
|
77
|
+
sourceFile.addVariableStatement(buildInfiniteQueryKeyFn(op));
|
|
78
|
+
}
|
|
68
79
|
// Add mutation types and keys
|
|
69
80
|
for (const op of mutationOperations) {
|
|
70
81
|
sourceFile.addTypeAlias(buildMutationResultType(op));
|
|
@@ -94,6 +105,37 @@ function generateQueriesFile(operations, ctx) {
|
|
|
94
105
|
}
|
|
95
106
|
return sourceFile.getFullText();
|
|
96
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Generate the queryOptions.ts file content.
|
|
110
|
+
*/
|
|
111
|
+
function generateQueryOptionsFile(operations, ctx) {
|
|
112
|
+
const project = createGenerationProject();
|
|
113
|
+
const sourceFile = project.createSourceFile(`${OpenApiRqFiles.queryOptions}.ts`, undefined, { overwrite: true });
|
|
114
|
+
// Add imports
|
|
115
|
+
const imports = [
|
|
116
|
+
buildCommonImport(),
|
|
117
|
+
buildQueryOptionsImport(),
|
|
118
|
+
buildClientImport(ctx),
|
|
119
|
+
buildServiceImport(ctx),
|
|
120
|
+
];
|
|
121
|
+
const modelImport = buildModelImport(ctx);
|
|
122
|
+
if (modelImport) {
|
|
123
|
+
imports.push(modelImport);
|
|
124
|
+
}
|
|
125
|
+
sourceFile.addImportDeclarations(imports);
|
|
126
|
+
// Only GET operations have query options
|
|
127
|
+
const getOperations = operations.filter((op) => op.httpMethod === "GET");
|
|
128
|
+
for (const op of getOperations) {
|
|
129
|
+
sourceFile.addVariableStatement(buildQueryOptionsFn(op, ctx));
|
|
130
|
+
}
|
|
131
|
+
for (const op of getOperations) {
|
|
132
|
+
const infiniteOptions = buildInfiniteQueryOptionsFn(op, ctx);
|
|
133
|
+
if (infiniteOptions) {
|
|
134
|
+
sourceFile.addVariableStatement(infiniteOptions);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return sourceFile.getFullText();
|
|
138
|
+
}
|
|
97
139
|
/**
|
|
98
140
|
* Generate the suspense.ts file content.
|
|
99
141
|
*/
|
|
@@ -185,6 +227,10 @@ export function generateAllFiles(operations, ctx) {
|
|
|
185
227
|
name: `${OpenApiRqFiles.queries}.ts`,
|
|
186
228
|
content: addHeaderComment(generateQueriesFile(operations, ctx), ctx.version),
|
|
187
229
|
},
|
|
230
|
+
{
|
|
231
|
+
name: `${OpenApiRqFiles.queryOptions}.ts`,
|
|
232
|
+
content: addHeaderComment(generateQueryOptionsFile(operations, ctx), ctx.version),
|
|
233
|
+
},
|
|
188
234
|
{
|
|
189
235
|
name: `${OpenApiRqFiles.suspense}.ts`,
|
|
190
236
|
content: addHeaderComment(generateSuspenseFile(operations, ctx), ctx.version),
|
|
@@ -52,6 +52,16 @@ export function buildQueryImport() {
|
|
|
52
52
|
],
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Build import structure for the queryOptions/infiniteQueryOptions helpers.
|
|
57
|
+
*/
|
|
58
|
+
export function buildQueryOptionsImport() {
|
|
59
|
+
return {
|
|
60
|
+
kind: StructureKind.ImportDeclaration,
|
|
61
|
+
moduleSpecifier: "@tanstack/react-query",
|
|
62
|
+
namedImports: [{ name: "queryOptions" }, { name: "infiniteQueryOptions" }],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
55
65
|
/**
|
|
56
66
|
* Build import structure for services.
|
|
57
67
|
*/
|