@7nohe/openapi-react-query-codegen 3.0.0-beta.2 โ 3.0.0-beta.4
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 +55 -5
- package/dist/cli.mjs +1 -0
- package/dist/common.mjs +6 -0
- package/dist/createSource.mjs +2 -2
- package/dist/generate.mjs +1 -0
- package/dist/parseOperations.mjs +2 -1
- package/dist/tsmorph/buildCommon.mjs +11 -6
- package/dist/tsmorph/buildMutationHooks.mjs +2 -1
- package/dist/tsmorph/buildQueryHooks.mjs +158 -94
- package/dist/tsmorph/buildQueryOptions.mjs +9 -18
- package/dist/tsmorph/generateFiles.mjs +19 -22
- package/dist/tsmorph/index.mjs +3 -3
- package/dist/tsmorph/projectFactory.mjs +5 -0
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -1,12 +1,62 @@
|
|
|
1
1
|
# OpenAPI React Query Codegen
|
|
2
2
|
|
|
3
|
-
> Code generator for
|
|
3
|
+
> Code generator for [TanStack Query (React Query)](https://tanstack.com/query) based on your OpenAPI schema โ `queryOptions` factories following the official TanStack Query v5 pattern, plus ready-to-use hooks, prefetch, ensure, suspense, and infinite query helpers.
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/%407nohe%2Fopenapi-react-query-codegen)
|
|
6
6
|
|
|
7
|
+
๐ **[Documentation](https://openapi-react-query-codegen.vercel.app)** ยท [Migrating to v3](https://openapi-react-query-codegen.vercel.app/guides/migrating-to-v3/)
|
|
8
|
+
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- **`queryOptions` / `infiniteQueryOptions` factories** for every GET operation โ the [TanStack Query v5 recommended pattern](https://tanstack.com/query/latest/docs/framework/react/guides/query-options), composable with `useQuery`, `useQueries`, `useSuspenseQuery`, `prefetchQuery`, `ensureQueryData`, and `setQueryData` with full type safety
|
|
12
|
+
- **Custom hooks**: `useQuery`, `useSuspenseQuery`, `useMutation`, `useInfiniteQuery`, and `useSuspenseInfiniteQuery` variants per operation
|
|
13
|
+
- **SSR helpers**: `prefetchQuery`, `prefetchInfiniteQuery`, and `ensureQueryData` functions per operation โ ready for Next.js App Router hydration
|
|
14
|
+
- **Hierarchical query keys** with exported key constants and functions: invalidate one exact query, all infinite pages of an operation, or every cache entry of an operation with a single prefix
|
|
15
|
+
- **Pure TypeScript clients** generated by [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts) (fetch and axios)
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -D @7nohe/openapi-react-query-codegen
|
|
21
|
+
npx openapi-rq -i ./petstore.yaml
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { useQuery } from "@tanstack/react-query";
|
|
26
|
+
import { findPetsOptions } from "./openapi/queries";
|
|
27
|
+
|
|
28
|
+
function Pets() {
|
|
29
|
+
const { data } = useQuery(findPetsOptions({ query: { limit: 10 } }));
|
|
30
|
+
// ...or use the generated hook directly: useFindPets({ query: { limit: 10 } })
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
See the [documentation](https://openapi-react-query-codegen.vercel.app) for CLI options, SSR recipes, and infinite query usage.
|
|
35
|
+
|
|
36
|
+
## How it compares
|
|
37
|
+
|
|
38
|
+
| | This library | @hey-api tanstack-query plugin | Orval |
|
|
39
|
+
|---|---|---|---|
|
|
40
|
+
| `queryOptions` / `infiniteQueryOptions` factories (TanStack v5 pattern) | โ
| โ
| โ |
|
|
41
|
+
| Ready-to-use hooks (`useQuery` / suspense / infinite variants) | โ
| โ (options only) | โ
|
|
|
42
|
+
| SSR helpers (`prefetchQuery` / `prefetchInfiniteQuery` / `ensureQueryData`) | โ
| โ | Partial (`usePrefetch`) |
|
|
43
|
+
| Hierarchical query keys for granular invalidation | โ
| โ
(tags) | Partial |
|
|
44
|
+
| Stable release line | โ
SemVer | pre-1.0, frequent breaking changes | โ
|
|
|
45
|
+
| MSW mock generation | โ (out of scope) | โ | โ
|
|
|
46
|
+
| Vue / Solid / Svelte / Angular | โ React-focused | โ
| โ
|
|
|
47
|
+
|
|
48
|
+
**Scope**: this library is deliberately React-focused and does not generate API mocks โ use Orval if MSW mocks are your priority, or hey-api's own plugin if you need non-React frameworks.
|
|
49
|
+
|
|
50
|
+
## Stability policy
|
|
51
|
+
|
|
52
|
+
This library builds on [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts), which is pre-1.0 and moves fast. We **pin the exact hey-api version** and absorb its breaking changes for you: hey-api upgrades land here only after our full snapshot-test suite passes, and are released as minor versions. Your generated API surface follows SemVer โ breaking output changes only happen in major versions, with a migration guide.
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- Node.js 22.18+
|
|
57
|
+
- `@tanstack/react-query` 5.x (peer dependency)
|
|
58
|
+
- `typescript` 5.x or 6.x, `ts-morph` 28.x, `commander` 12โ15 (peer dependencies)
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
package/dist/cli.mjs
CHANGED
|
@@ -32,6 +32,7 @@ async function setupProgram() {
|
|
|
32
32
|
.option("--pageParam <value>", "Name of the query parameter used for pagination", "page")
|
|
33
33
|
.option("--nextPageParam <value>", "Name of the response parameter used for next page", "nextPage")
|
|
34
34
|
.option("--initialPageParam <value>", "Initial page value to query", "1")
|
|
35
|
+
.option("--omitInitialPageParam", "Send no initial page parameter at all (overrides --initialPageParam)")
|
|
35
36
|
.parse();
|
|
36
37
|
const options = program.opts();
|
|
37
38
|
await generate(options, version);
|
package/dist/common.mjs
CHANGED
|
@@ -52,6 +52,12 @@ export function BuildCommonTypeName(name) {
|
|
|
52
52
|
* @returns The parsed number or NaN if the value is not a valid number.
|
|
53
53
|
*/
|
|
54
54
|
export function safeParseNumber(value) {
|
|
55
|
+
// `Number("")` is 0, which would silently turn a blank option such as
|
|
56
|
+
// `--initialPageParam ""` into a numeric 0. Treat blank strings as NaN so
|
|
57
|
+
// callers keep the original value.
|
|
58
|
+
if (typeof value === "string" && value.trim() === "") {
|
|
59
|
+
return Number.NaN;
|
|
60
|
+
}
|
|
55
61
|
const parsed = Number(value);
|
|
56
62
|
if (!Number.isNaN(parsed) && Number.isFinite(parsed)) {
|
|
57
63
|
return parsed;
|
package/dist/createSource.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { generateAllFiles } from "./tsmorph/index.mjs";
|
|
|
5
5
|
/**
|
|
6
6
|
* Create source files using ts-morph based generation.
|
|
7
7
|
*/
|
|
8
|
-
export const createSource = async ({ outputPath, client, version, pageParam, nextPageParam, initialPageParam, }) => {
|
|
8
|
+
export const createSource = async ({ outputPath, client, version, pageParam, nextPageParam, initialPageParam, omitInitialPageParam, }) => {
|
|
9
9
|
// Initialize ts-morph project to read the generated OpenAPI client
|
|
10
10
|
const project = new Project({
|
|
11
11
|
skipAddingFilesFromTsConfig: true,
|
|
@@ -15,7 +15,7 @@ export const createSource = async ({ outputPath, client, version, pageParam, nex
|
|
|
15
15
|
// Parse operations from the service file
|
|
16
16
|
const operations = await parseOperations(project, pageParam);
|
|
17
17
|
// Build generation context
|
|
18
|
-
const ctx = buildGenerationContext(project, client, pageParam, nextPageParam, initialPageParam, version);
|
|
18
|
+
const ctx = buildGenerationContext(project, client, pageParam, nextPageParam, initialPageParam, omitInitialPageParam, version);
|
|
19
19
|
// Generate all files using ts-morph
|
|
20
20
|
return generateAllFiles(operations, ctx);
|
|
21
21
|
};
|
package/dist/generate.mjs
CHANGED
|
@@ -59,6 +59,7 @@ export async function generate(options, version) {
|
|
|
59
59
|
pageParam: formattedOptions.pageParam,
|
|
60
60
|
nextPageParam: formattedOptions.nextPageParam,
|
|
61
61
|
initialPageParam: formattedOptions.initialPageParam.toString(),
|
|
62
|
+
omitInitialPageParam: formattedOptions.omitInitialPageParam ?? false,
|
|
62
63
|
});
|
|
63
64
|
await print(source, formattedOptions);
|
|
64
65
|
const queriesOutputPath = buildQueriesOutputPath(options.output);
|
package/dist/parseOperations.mjs
CHANGED
|
@@ -96,7 +96,7 @@ export async function parseOperations(project, pageParam) {
|
|
|
96
96
|
/**
|
|
97
97
|
* Build generation context from project configuration.
|
|
98
98
|
*/
|
|
99
|
-
export function buildGenerationContext(project, client, pageParam, nextPageParam, initialPageParam, version) {
|
|
99
|
+
export function buildGenerationContext(project, client, pageParam, nextPageParam, initialPageParam, omitInitialPageParam, version) {
|
|
100
100
|
const modelsFile = project
|
|
101
101
|
.getSourceFiles()
|
|
102
102
|
.find((sf) => sf.getFilePath().includes(modelsFileName));
|
|
@@ -117,6 +117,7 @@ export function buildGenerationContext(project, client, pageParam, nextPageParam
|
|
|
117
117
|
pageParam,
|
|
118
118
|
nextPageParam,
|
|
119
119
|
initialPageParam,
|
|
120
|
+
omitInitialPageParam,
|
|
120
121
|
version,
|
|
121
122
|
};
|
|
122
123
|
}
|
|
@@ -141,9 +141,11 @@ export function buildInfiniteClientOptionsType(op, ctx) {
|
|
|
141
141
|
}
|
|
142
142
|
/**
|
|
143
143
|
* Build the infinite query key constant.
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
144
|
+
* Shares the plain query key as its first segment so a single
|
|
145
|
+
* `invalidateQueries({ queryKey: [useXKey] })` matches both the plain and the
|
|
146
|
+
* infinite cache entries of an operation (#174), while the extra "infinite"
|
|
147
|
+
* segment keeps cached InfiniteData from colliding with plain query data (#140).
|
|
148
|
+
* Example: export const useFindPaginatedPetsInfiniteKey = [useFindPaginatedPetsKey, "infinite"] as const;
|
|
147
149
|
*/
|
|
148
150
|
export function buildInfiniteQueryKeyConst(op) {
|
|
149
151
|
return {
|
|
@@ -153,15 +155,18 @@ export function buildInfiniteQueryKeyConst(op) {
|
|
|
153
155
|
declarations: [
|
|
154
156
|
{
|
|
155
157
|
name: `use${op.capitalizedMethodName}InfiniteKey`,
|
|
156
|
-
initializer: `
|
|
158
|
+
initializer: `[use${op.capitalizedMethodName}Key, "infinite"] as const`,
|
|
157
159
|
},
|
|
158
160
|
],
|
|
159
161
|
};
|
|
160
162
|
}
|
|
161
163
|
/**
|
|
162
164
|
* Build the infinite query key function.
|
|
165
|
+
* The custom queryKey argument only replaces the params segment โ the
|
|
166
|
+
* hierarchical [opKey, "infinite"] prefix is always preserved so
|
|
167
|
+
* prefix-based invalidation keeps working.
|
|
163
168
|
* Example: export const UseFindPaginatedPetsInfiniteKeyFn = (clientOptions: FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) =>
|
|
164
|
-
* [useFindPaginatedPetsInfiniteKey, ...(queryKey ?? [clientOptions])];
|
|
169
|
+
* [...useFindPaginatedPetsInfiniteKey, ...(queryKey ?? [clientOptions])];
|
|
165
170
|
*/
|
|
166
171
|
export function buildInfiniteQueryKeyFn(op) {
|
|
167
172
|
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
@@ -176,7 +181,7 @@ export function buildInfiniteQueryKeyFn(op) {
|
|
|
176
181
|
declarations: [
|
|
177
182
|
{
|
|
178
183
|
name: `Use${op.capitalizedMethodName}InfiniteKeyFn`,
|
|
179
|
-
initializer: `(${params.join(", ")}) => [use${op.capitalizedMethodName}InfiniteKey, ...(queryKey ?? [clientOptions])]`,
|
|
184
|
+
initializer: `(${params.join(", ")}) => [...use${op.capitalizedMethodName}InfiniteKey, ...(queryKey ?? [clientOptions])]`,
|
|
180
185
|
},
|
|
181
186
|
],
|
|
182
187
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
|
|
2
|
+
import { SDK_CALL_ARGS } from "./buildQueryHooks.mjs";
|
|
2
3
|
/**
|
|
3
4
|
* Get the error type string based on client type.
|
|
4
5
|
*/
|
|
@@ -33,7 +34,7 @@ export function buildUseMutationHook(op, ctx) {
|
|
|
33
34
|
? `${op.capitalizedMethodName}Data`
|
|
34
35
|
: "unknown";
|
|
35
36
|
const optionsType = `Options<${dataTypeName}, true>`;
|
|
36
|
-
const mutationFn = `clientOptions => ${op.methodName}(
|
|
37
|
+
const mutationFn = `clientOptions => ${op.methodName}(${SDK_CALL_ARGS}) as unknown as Promise<TData>`;
|
|
37
38
|
const body = `useMutation<TData, TError, ${optionsType}, TContext>({ mutationKey: Common.Use${op.capitalizedMethodName}KeyFn(mutationKey), mutationFn: ${mutationFn}, ...options })`;
|
|
38
39
|
return {
|
|
39
40
|
kind: StructureKind.VariableStatement,
|
|
@@ -13,32 +13,6 @@ function getErrorType(op, ctx) {
|
|
|
13
13
|
}
|
|
14
14
|
return errorType;
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Get the data type based on hook type.
|
|
18
|
-
*/
|
|
19
|
-
function getDataTypeDefault(op, hookType) {
|
|
20
|
-
const baseType = `Common.${op.capitalizedMethodName}DefaultResponse`;
|
|
21
|
-
if (hookType === "useSuspenseQuery") {
|
|
22
|
-
return `NonNullable<${baseType}>`;
|
|
23
|
-
}
|
|
24
|
-
if (hookType === "useInfiniteQuery") {
|
|
25
|
-
return `InfiniteData<${baseType}>`;
|
|
26
|
-
}
|
|
27
|
-
return baseType;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Get the options type name.
|
|
31
|
-
*/
|
|
32
|
-
function getOptionsTypeName(hookType) {
|
|
33
|
-
switch (hookType) {
|
|
34
|
-
case "useSuspenseQuery":
|
|
35
|
-
return "UseSuspenseQueryOptions";
|
|
36
|
-
case "useInfiniteQuery":
|
|
37
|
-
return "UseInfiniteQueryOptions";
|
|
38
|
-
default:
|
|
39
|
-
return "UseQueryOptions";
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
16
|
/**
|
|
43
17
|
* Resolve the generated Data type name for an operation, falling back to
|
|
44
18
|
* unknown when the operation has no generated Data type.
|
|
@@ -48,13 +22,18 @@ export function getDataTypeName(op, ctx) {
|
|
|
48
22
|
? `${op.capitalizedMethodName}Data`
|
|
49
23
|
: "unknown";
|
|
50
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* SDK call arguments shared by every generated queryFn/mutationFn.
|
|
27
|
+
* throwOnError: true forces the SDK call to reject on error responses; the
|
|
28
|
+
* hey-api runtime default is false, which would resolve undefined data and
|
|
29
|
+
* swallow the error instead of surfacing it to TanStack Query (#172).
|
|
30
|
+
*/
|
|
31
|
+
export const SDK_CALL_ARGS = "{ ...clientOptions, throwOnError: true }";
|
|
51
32
|
/**
|
|
52
33
|
* Build the client options parameter string.
|
|
53
34
|
*/
|
|
54
35
|
export function buildClientOptionsParam(op, ctx) {
|
|
55
|
-
const dataTypeName =
|
|
56
|
-
? `${op.capitalizedMethodName}Data`
|
|
57
|
-
: "unknown";
|
|
36
|
+
const dataTypeName = getDataTypeName(op, ctx);
|
|
58
37
|
const hasParams = op.parameters.length > 0;
|
|
59
38
|
if (!hasParams) {
|
|
60
39
|
return `clientOptions: Options<${dataTypeName}, true> = {}`;
|
|
@@ -62,6 +41,71 @@ export function buildClientOptionsParam(op, ctx) {
|
|
|
62
41
|
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
63
42
|
return `clientOptions: Options<${dataTypeName}, true>${defaultValue}`;
|
|
64
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Build the clientOptions parameter typed with the page-less infinite
|
|
46
|
+
* options type โ the page parameter is supplied by TanStack Query's
|
|
47
|
+
* pageParam mechanism.
|
|
48
|
+
*/
|
|
49
|
+
export function buildInfiniteClientOptionsParam(op) {
|
|
50
|
+
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
51
|
+
return `clientOptions: Common.${op.capitalizedMethodName}InfiniteClientOptions${defaultValue}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build the paginated SDK call shared by every infinite query builder.
|
|
55
|
+
*/
|
|
56
|
+
export function buildPagedQueryFn(op, ctx, castTData) {
|
|
57
|
+
const dataTypeName = getDataTypeName(op, ctx);
|
|
58
|
+
const thenClause = castTData
|
|
59
|
+
? ".then(response => response.data as TData) as TData"
|
|
60
|
+
: ".then(response => response.data)";
|
|
61
|
+
// When the initial page param is omitted, the first request must send no
|
|
62
|
+
// page param at all, so spread it in only once TanStack Query provides one.
|
|
63
|
+
const pageQuery = ctx.omitInitialPageParam
|
|
64
|
+
? `...(pageParam === undefined ? {} : { ${ctx.pageParam}: pageParam as number })`
|
|
65
|
+
: `${ctx.pageParam}: pageParam as number`;
|
|
66
|
+
return `({ pageParam }) => ${op.methodName}({ ...clientOptions, query: { ...clientOptions.query, ${pageQuery} }, throwOnError: true } as Options<${dataTypeName}, true>)${thenClause}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Format the initialPageParam literal. Emits `undefined` when the caller opted
|
|
70
|
+
* to omit it (#177); otherwise a numeric literal when possible so the inferred
|
|
71
|
+
* pageParam type matches what getNextPageParam returns.
|
|
72
|
+
*/
|
|
73
|
+
export function formatInitialPageParam(ctx) {
|
|
74
|
+
if (ctx.omitInitialPageParam) {
|
|
75
|
+
return "undefined";
|
|
76
|
+
}
|
|
77
|
+
return /^-?\d+$/.test(ctx.initialPageParam)
|
|
78
|
+
? ctx.initialPageParam
|
|
79
|
+
: JSON.stringify(ctx.initialPageParam);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build the nested type for getNextPageParam.
|
|
83
|
+
* E.g., "meta.next" becomes "{ meta: { next: number } }"
|
|
84
|
+
*/
|
|
85
|
+
export function buildNestedNextPageType(nextPageParam) {
|
|
86
|
+
const segments = nextPageParam.split(".");
|
|
87
|
+
return segments.reduceRight((acc, segment) => {
|
|
88
|
+
return `{ ${segment}: ${acc} }`;
|
|
89
|
+
}, "number");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Build the getNextPageParam expression. The parameter is annotated because
|
|
93
|
+
* not every TanStack entry point contextually types it (prefetchInfiniteQuery
|
|
94
|
+
* does not, which would fail noImplicitAny).
|
|
95
|
+
*/
|
|
96
|
+
export function buildGetNextPageParamExpr(ctx) {
|
|
97
|
+
const nestedType = buildNestedNextPageType(ctx.nextPageParam);
|
|
98
|
+
return `(response: unknown) => (response as ${nestedType}).${ctx.nextPageParam}`;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Build an options type where the pagination fields TanStack Query marks as
|
|
102
|
+
* required become optional overrides: the generator supplies them, and
|
|
103
|
+
* callers may replace them for custom pagination schemes (#156, #146).
|
|
104
|
+
*/
|
|
105
|
+
export function buildOverridableInfiniteOptionsType(optionsTypeName) {
|
|
106
|
+
const instantiated = `${optionsTypeName}<TData, TError>`;
|
|
107
|
+
return `Omit<${instantiated}, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<${instantiated}, "initialPageParam" | "getNextPageParam">>`;
|
|
108
|
+
}
|
|
65
109
|
/**
|
|
66
110
|
* Build useQuery hook.
|
|
67
111
|
* Example:
|
|
@@ -71,19 +115,16 @@ export function buildClientOptionsParam(op, ctx) {
|
|
|
71
115
|
* options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">
|
|
72
116
|
* ) => useQuery<TData, TError>({
|
|
73
117
|
* queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey),
|
|
74
|
-
* queryFn: () => findPets({ ...clientOptions }).then(response => response.data as TData) as TData,
|
|
118
|
+
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data as TData) as TData,
|
|
75
119
|
* ...options
|
|
76
120
|
* });
|
|
77
121
|
*/
|
|
78
122
|
export function buildUseQueryHook(op, ctx) {
|
|
79
123
|
const hookName = `use${op.capitalizedMethodName}`;
|
|
80
124
|
const errorType = getErrorType(op, ctx);
|
|
81
|
-
const dataTypeDefault =
|
|
125
|
+
const dataTypeDefault = `Common.${op.capitalizedMethodName}DefaultResponse`;
|
|
82
126
|
const clientOptionsParam = buildClientOptionsParam(op, ctx);
|
|
83
|
-
const
|
|
84
|
-
// Build the queryFn body
|
|
85
|
-
const callArgs = hasParams ? "{ ...clientOptions }" : "{ ...clientOptions }";
|
|
86
|
-
const queryFn = `() => ${op.methodName}(${callArgs}).then(response => response.data as TData) as TData`;
|
|
127
|
+
const queryFn = `() => ${op.methodName}(${SDK_CALL_ARGS}).then(response => response.data as TData) as TData`;
|
|
87
128
|
const body = `useQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`;
|
|
88
129
|
return {
|
|
89
130
|
kind: StructureKind.VariableStatement,
|
|
@@ -105,11 +146,9 @@ export function buildUseQueryHook(op, ctx) {
|
|
|
105
146
|
export function buildUseSuspenseQueryHook(op, ctx) {
|
|
106
147
|
const hookName = `use${op.capitalizedMethodName}Suspense`;
|
|
107
148
|
const errorType = getErrorType(op, ctx);
|
|
108
|
-
const dataTypeDefault =
|
|
149
|
+
const dataTypeDefault = `NonNullable<Common.${op.capitalizedMethodName}DefaultResponse>`;
|
|
109
150
|
const clientOptionsParam = buildClientOptionsParam(op, ctx);
|
|
110
|
-
const
|
|
111
|
-
const callArgs = hasParams ? "{ ...clientOptions }" : "{ ...clientOptions }";
|
|
112
|
-
const queryFn = `() => ${op.methodName}(${callArgs}).then(response => response.data as TData) as TData`;
|
|
151
|
+
const queryFn = `() => ${op.methodName}(${SDK_CALL_ARGS}).then(response => response.data as TData) as TData`;
|
|
113
152
|
const body = `useSuspenseQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`;
|
|
114
153
|
return {
|
|
115
154
|
kind: StructureKind.VariableStatement,
|
|
@@ -126,38 +165,30 @@ export function buildUseSuspenseQueryHook(op, ctx) {
|
|
|
126
165
|
};
|
|
127
166
|
}
|
|
128
167
|
/**
|
|
129
|
-
* Build
|
|
130
|
-
*
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const segments = nextPageParam.split(".");
|
|
134
|
-
return segments.reduceRight((acc, segment) => {
|
|
135
|
-
return `{ ${segment}: ${acc} }`;
|
|
136
|
-
}, "number");
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Build useInfiniteQuery hook.
|
|
168
|
+
* Build a useInfiniteQuery / useSuspenseInfiniteQuery hook. Both variants
|
|
169
|
+
* share the infinite query key (and therefore the cache); they differ only
|
|
170
|
+
* in the TanStack hook called, the options type, and the NonNullable TData
|
|
171
|
+
* default of the suspense variant.
|
|
140
172
|
*/
|
|
141
|
-
|
|
173
|
+
function buildInfiniteHook(op, ctx, suspense) {
|
|
142
174
|
if (!op.isPaginatable) {
|
|
143
175
|
return null;
|
|
144
176
|
}
|
|
145
|
-
const
|
|
177
|
+
const hookCall = suspense ? "useSuspenseInfiniteQuery" : "useInfiniteQuery";
|
|
178
|
+
const optionsTypeName = suspense
|
|
179
|
+
? "UseSuspenseInfiniteQueryOptions"
|
|
180
|
+
: "UseInfiniteQueryOptions";
|
|
181
|
+
const hookName = suspense
|
|
182
|
+
? `use${op.capitalizedMethodName}SuspenseInfinite`
|
|
183
|
+
: `use${op.capitalizedMethodName}Infinite`;
|
|
146
184
|
const errorType = getErrorType(op, ctx);
|
|
147
185
|
const baseDataType = `Common.${op.capitalizedMethodName}DefaultResponse`;
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
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`;
|
|
155
|
-
// Build getNextPageParam with nested type
|
|
156
|
-
const nestedType = buildNestedNextPageType(ctx.nextPageParam);
|
|
157
|
-
const getNextPageParam = `getNextPageParam: (response) => (response as ${nestedType}).${ctx.nextPageParam}`;
|
|
158
|
-
// initialPageParam is a string literal
|
|
159
|
-
const infiniteOptions = `initialPageParam: "${ctx.initialPageParam}", ${getNextPageParam}`;
|
|
160
|
-
const body = `useInfiniteQuery({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
186
|
+
const dataTypeDefault = suspense
|
|
187
|
+
? `InfiniteData<NonNullable<${baseDataType}>>`
|
|
188
|
+
: `InfiniteData<${baseDataType}>`;
|
|
189
|
+
const queryFn = buildPagedQueryFn(op, ctx, true);
|
|
190
|
+
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx)}`;
|
|
191
|
+
const body = `${hookCall}({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
161
192
|
return {
|
|
162
193
|
kind: StructureKind.VariableStatement,
|
|
163
194
|
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
@@ -167,33 +198,38 @@ export function buildUseInfiniteQueryHook(op, ctx) {
|
|
|
167
198
|
declarations: [
|
|
168
199
|
{
|
|
169
200
|
name: hookName,
|
|
170
|
-
initializer: `<TData =
|
|
201
|
+
initializer: `<TData = ${dataTypeDefault}, TError = ${errorType}, TQueryKey extends Array<unknown> = unknown[]>(${buildInfiniteClientOptionsParam(op)}, queryKey?: TQueryKey, options?: ${buildOverridableInfiniteOptionsType(optionsTypeName)}) => ${body}`,
|
|
171
202
|
},
|
|
172
203
|
],
|
|
173
204
|
};
|
|
174
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Build useInfiniteQuery hook.
|
|
208
|
+
*/
|
|
209
|
+
export function buildUseInfiniteQueryHook(op, ctx) {
|
|
210
|
+
return buildInfiniteHook(op, ctx, false);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Build useSuspenseInfiniteQuery hook.
|
|
214
|
+
*/
|
|
215
|
+
export function buildUseSuspenseInfiniteQueryHook(op, ctx) {
|
|
216
|
+
return buildInfiniteHook(op, ctx, true);
|
|
217
|
+
}
|
|
175
218
|
/**
|
|
176
219
|
* Build prefetch function.
|
|
177
220
|
* Example:
|
|
178
|
-
* export const prefetchUseFindPets = (queryClient: QueryClient, clientOptions: Options<FindPetsData, true> = {}) =>
|
|
221
|
+
* export const prefetchUseFindPets = (queryClient: QueryClient, clientOptions: Options<FindPetsData, true> = {}, options?: Omit<FetchQueryOptions<Common.FindPetsDefaultResponse>, "queryKey" | "queryFn">) =>
|
|
179
222
|
* queryClient.prefetchQuery({
|
|
180
223
|
* queryKey: Common.UseFindPetsKeyFn(clientOptions),
|
|
181
|
-
* queryFn: () => findPets({ ...clientOptions }).then(response => response.data)
|
|
224
|
+
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data),
|
|
225
|
+
* ...options
|
|
182
226
|
* });
|
|
183
227
|
*/
|
|
184
228
|
export function buildPrefetchFn(op, ctx) {
|
|
185
229
|
const fnName = `prefetchUse${op.capitalizedMethodName}`;
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const hasParams = op.parameters.length > 0;
|
|
190
|
-
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
191
|
-
const clientOptionsParam = hasParams
|
|
192
|
-
? `clientOptions: Options<${dataTypeName}, true>${defaultValue}`
|
|
193
|
-
: `clientOptions: Options<${dataTypeName}, true> = {}`;
|
|
194
|
-
const callArgs = "{ ...clientOptions }";
|
|
195
|
-
const queryFn = `() => ${op.methodName}(${callArgs}).then(response => response.data)`;
|
|
196
|
-
const body = `queryClient.prefetchQuery({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions), queryFn: ${queryFn} })`;
|
|
230
|
+
const queryFn = `() => ${op.methodName}(${SDK_CALL_ARGS}).then(response => response.data)`;
|
|
231
|
+
const optionsParam = `options?: Omit<FetchQueryOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn">`;
|
|
232
|
+
const body = `queryClient.prefetchQuery({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions), queryFn: ${queryFn}, ...options })`;
|
|
197
233
|
return {
|
|
198
234
|
kind: StructureKind.VariableStatement,
|
|
199
235
|
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
@@ -203,7 +239,42 @@ export function buildPrefetchFn(op, ctx) {
|
|
|
203
239
|
declarations: [
|
|
204
240
|
{
|
|
205
241
|
name: fnName,
|
|
206
|
-
initializer: `(queryClient: QueryClient, ${
|
|
242
|
+
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op, ctx)}, ${optionsParam}) => ${body}`,
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Build prefetchInfiniteQuery function for a paginatable operation.
|
|
249
|
+
* Example:
|
|
250
|
+
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<Common.FindPaginatedPetsDefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">) =>
|
|
251
|
+
* queryClient.prefetchInfiniteQuery({
|
|
252
|
+
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions),
|
|
253
|
+
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
|
|
254
|
+
* initialPageParam: 1,
|
|
255
|
+
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
|
|
256
|
+
* ...options
|
|
257
|
+
* });
|
|
258
|
+
*/
|
|
259
|
+
export function buildPrefetchInfiniteQueryFn(op, ctx) {
|
|
260
|
+
if (!op.isPaginatable) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
const fnName = `prefetchUse${op.capitalizedMethodName}Infinite`;
|
|
264
|
+
const queryFn = buildPagedQueryFn(op, ctx, false);
|
|
265
|
+
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx)}`;
|
|
266
|
+
const optionsParam = `options?: Omit<FetchInfiniteQueryOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">`;
|
|
267
|
+
const body = `queryClient.prefetchInfiniteQuery({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
268
|
+
return {
|
|
269
|
+
kind: StructureKind.VariableStatement,
|
|
270
|
+
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
271
|
+
leadingTrivia: op.jsDoc ? `${op.jsDoc}\n` : undefined,
|
|
272
|
+
isExported: true,
|
|
273
|
+
declarationKind: VariableDeclarationKind.Const,
|
|
274
|
+
declarations: [
|
|
275
|
+
{
|
|
276
|
+
name: fnName,
|
|
277
|
+
initializer: `(queryClient: QueryClient, ${buildInfiniteClientOptionsParam(op)}, ${optionsParam}) => ${body}`,
|
|
207
278
|
},
|
|
208
279
|
],
|
|
209
280
|
};
|
|
@@ -211,25 +282,18 @@ export function buildPrefetchFn(op, ctx) {
|
|
|
211
282
|
/**
|
|
212
283
|
* Build ensureQueryData function.
|
|
213
284
|
* Example:
|
|
214
|
-
* export const ensureUseFindPetsData = (queryClient: QueryClient, clientOptions: Options<FindPetsData, true> = {}) =>
|
|
285
|
+
* export const ensureUseFindPetsData = (queryClient: QueryClient, clientOptions: Options<FindPetsData, true> = {}, options?: Omit<EnsureQueryDataOptions<Common.FindPetsDefaultResponse>, "queryKey" | "queryFn">) =>
|
|
215
286
|
* queryClient.ensureQueryData({
|
|
216
287
|
* queryKey: Common.UseFindPetsKeyFn(clientOptions),
|
|
217
|
-
* queryFn: () => findPets({ ...clientOptions }).then(response => response.data)
|
|
288
|
+
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data),
|
|
289
|
+
* ...options
|
|
218
290
|
* });
|
|
219
291
|
*/
|
|
220
292
|
export function buildEnsureQueryDataFn(op, ctx) {
|
|
221
293
|
const fnName = `ensureUse${op.capitalizedMethodName}Data`;
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const hasParams = op.parameters.length > 0;
|
|
226
|
-
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
227
|
-
const clientOptionsParam = hasParams
|
|
228
|
-
? `clientOptions: Options<${dataTypeName}, true>${defaultValue}`
|
|
229
|
-
: `clientOptions: Options<${dataTypeName}, true> = {}`;
|
|
230
|
-
const callArgs = "{ ...clientOptions }";
|
|
231
|
-
const queryFn = `() => ${op.methodName}(${callArgs}).then(response => response.data)`;
|
|
232
|
-
const body = `queryClient.ensureQueryData({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions), queryFn: ${queryFn} })`;
|
|
294
|
+
const queryFn = `() => ${op.methodName}(${SDK_CALL_ARGS}).then(response => response.data)`;
|
|
295
|
+
const optionsParam = `options?: Omit<EnsureQueryDataOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn">`;
|
|
296
|
+
const body = `queryClient.ensureQueryData({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions), queryFn: ${queryFn}, ...options })`;
|
|
233
297
|
return {
|
|
234
298
|
kind: StructureKind.VariableStatement,
|
|
235
299
|
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
@@ -239,7 +303,7 @@ export function buildEnsureQueryDataFn(op, ctx) {
|
|
|
239
303
|
declarations: [
|
|
240
304
|
{
|
|
241
305
|
name: fnName,
|
|
242
|
-
initializer: `(queryClient: QueryClient, ${
|
|
306
|
+
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op, ctx)}, ${optionsParam}) => ${body}`,
|
|
243
307
|
},
|
|
244
308
|
],
|
|
245
309
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
|
|
2
|
-
import { buildClientOptionsParam,
|
|
2
|
+
import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClientOptionsParam, buildPagedQueryFn, formatInitialPageParam, SDK_CALL_ARGS, } from "./buildQueryHooks.mjs";
|
|
3
3
|
/**
|
|
4
4
|
* Build a queryOptions factory for a GET operation.
|
|
5
5
|
* The factory centralizes queryKey and queryFn so they can be reused with
|
|
@@ -9,13 +9,13 @@ import { buildClientOptionsParam, buildNestedNextPageType, getDataTypeName, } fr
|
|
|
9
9
|
* export const findPetsOptions = (clientOptions: Options<FindPetsData, true> = {}, queryKey?: Array<unknown>) =>
|
|
10
10
|
* queryOptions({
|
|
11
11
|
* queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey),
|
|
12
|
-
* queryFn: () => findPets({ ...clientOptions }).then(response => response.data),
|
|
12
|
+
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data),
|
|
13
13
|
* });
|
|
14
14
|
*/
|
|
15
15
|
export function buildQueryOptionsFn(op, ctx) {
|
|
16
16
|
const fnName = `${op.methodName}Options`;
|
|
17
17
|
const clientOptionsParam = buildClientOptionsParam(op, ctx);
|
|
18
|
-
const queryFn = `() => ${op.methodName}({
|
|
18
|
+
const queryFn = `() => ${op.methodName}(${SDK_CALL_ARGS}).then(response => response.data)`;
|
|
19
19
|
const body = `queryOptions({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn} })`;
|
|
20
20
|
return {
|
|
21
21
|
kind: StructureKind.VariableStatement,
|
|
@@ -38,9 +38,9 @@ export function buildQueryOptionsFn(op, ctx) {
|
|
|
38
38
|
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) =>
|
|
39
39
|
* infiniteQueryOptions({
|
|
40
40
|
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions, queryKey),
|
|
41
|
-
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam } } as Options<FindPaginatedPetsData, true>).then(response => response.data),
|
|
41
|
+
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
|
|
42
42
|
* initialPageParam: 1,
|
|
43
|
-
* getNextPageParam: (response) => (response as { nextPage: number }).nextPage,
|
|
43
|
+
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
|
|
44
44
|
* });
|
|
45
45
|
*/
|
|
46
46
|
export function buildInfiniteQueryOptionsFn(op, ctx) {
|
|
@@ -48,18 +48,9 @@ export function buildInfiniteQueryOptionsFn(op, ctx) {
|
|
|
48
48
|
return null;
|
|
49
49
|
}
|
|
50
50
|
const fnName = `${op.methodName}InfiniteOptions`;
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
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} })`;
|
|
51
|
+
const queryFn = buildPagedQueryFn(op, ctx, false);
|
|
52
|
+
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx)}`;
|
|
53
|
+
const body = `infiniteQueryOptions({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions} })`;
|
|
63
54
|
return {
|
|
64
55
|
kind: StructureKind.VariableStatement,
|
|
65
56
|
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
@@ -69,7 +60,7 @@ export function buildInfiniteQueryOptionsFn(op, ctx) {
|
|
|
69
60
|
declarations: [
|
|
70
61
|
{
|
|
71
62
|
name: fnName,
|
|
72
|
-
initializer: `(${
|
|
63
|
+
initializer: `(${buildInfiniteClientOptionsParam(op)}, queryKey?: Array<unknown>) => ${body}`,
|
|
73
64
|
},
|
|
74
65
|
],
|
|
75
66
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { StructureKind, } from "ts-morph";
|
|
2
1
|
import { OpenApiRqFiles } from "../constants.mjs";
|
|
3
2
|
import { buildDefaultResponseType, buildInfiniteClientOptionsType, buildInfiniteQueryKeyConst, buildInfiniteQueryKeyFn, buildMutationKeyConst, buildMutationKeyFn, buildMutationResultType, buildQueryKeyConst, buildQueryKeyFn, buildQueryResultType, } from "./buildCommon.mjs";
|
|
4
3
|
import { buildUseMutationHook } from "./buildMutationHooks.mjs";
|
|
5
|
-
import { buildEnsureQueryDataFn, buildPrefetchFn, buildUseInfiniteQueryHook, buildUseQueryHook, buildUseSuspenseQueryHook, } from "./buildQueryHooks.mjs";
|
|
4
|
+
import { buildEnsureQueryDataFn, buildPrefetchFn, buildPrefetchInfiniteQueryFn, buildUseInfiniteQueryHook, buildUseQueryHook, buildUseSuspenseInfiniteQueryHook, buildUseSuspenseQueryHook, } from "./buildQueryHooks.mjs";
|
|
6
5
|
import { buildInfiniteQueryOptionsFn, buildQueryOptionsFn, } from "./buildQueryOptions.mjs";
|
|
7
6
|
import { buildAxiosErrorImport, buildClientImport, buildCommonImport, buildModelImport, buildQueryImport, buildQueryOptionsImport, buildServiceImport, createGenerationProject, } from "./projectFactory.mjs";
|
|
8
7
|
/**
|
|
@@ -31,26 +30,10 @@ function buildHookFileImports(ctx) {
|
|
|
31
30
|
}
|
|
32
31
|
/**
|
|
33
32
|
* Generate the index.ts file content.
|
|
33
|
+
* The content is constant, so no ts-morph project is needed.
|
|
34
34
|
*/
|
|
35
|
-
function generateIndexFile(
|
|
36
|
-
|
|
37
|
-
const sourceFile = project.createSourceFile(`${OpenApiRqFiles.index}.ts`, undefined, { overwrite: true });
|
|
38
|
-
const exports = [
|
|
39
|
-
{
|
|
40
|
-
kind: StructureKind.ExportDeclaration,
|
|
41
|
-
moduleSpecifier: "./common",
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
kind: StructureKind.ExportDeclaration,
|
|
45
|
-
moduleSpecifier: "./queries",
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
kind: StructureKind.ExportDeclaration,
|
|
49
|
-
moduleSpecifier: "./queryOptions",
|
|
50
|
-
},
|
|
51
|
-
];
|
|
52
|
-
sourceFile.addExportDeclarations(exports);
|
|
53
|
-
return sourceFile.getFullText();
|
|
35
|
+
function generateIndexFile() {
|
|
36
|
+
return `export * from "./common";\nexport * from "./queries";\nexport * from "./queryOptions";\n`;
|
|
54
37
|
}
|
|
55
38
|
/**
|
|
56
39
|
* Generate the common.ts file content.
|
|
@@ -150,6 +133,13 @@ function generateSuspenseFile(operations, ctx) {
|
|
|
150
133
|
for (const op of getOperations) {
|
|
151
134
|
sourceFile.addVariableStatement(buildUseSuspenseQueryHook(op, ctx));
|
|
152
135
|
}
|
|
136
|
+
// Add useSuspenseInfiniteQuery hooks for paginatable operations
|
|
137
|
+
for (const op of getOperations.filter((o) => o.isPaginatable)) {
|
|
138
|
+
const hook = buildUseSuspenseInfiniteQueryHook(op, ctx);
|
|
139
|
+
if (hook) {
|
|
140
|
+
sourceFile.addVariableStatement(hook);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
153
143
|
return sourceFile.getFullText();
|
|
154
144
|
}
|
|
155
145
|
/**
|
|
@@ -185,6 +175,13 @@ function generatePrefetchFile(operations, ctx) {
|
|
|
185
175
|
for (const op of getOperations) {
|
|
186
176
|
sourceFile.addVariableStatement(buildPrefetchFn(op, ctx));
|
|
187
177
|
}
|
|
178
|
+
// Add prefetchInfiniteQuery functions for paginatable operations
|
|
179
|
+
for (const op of getOperations.filter((o) => o.isPaginatable)) {
|
|
180
|
+
const fn = buildPrefetchInfiniteQueryFn(op, ctx);
|
|
181
|
+
if (fn) {
|
|
182
|
+
sourceFile.addVariableStatement(fn);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
188
185
|
return sourceFile.getFullText();
|
|
189
186
|
}
|
|
190
187
|
/**
|
|
@@ -217,7 +214,7 @@ export function generateAllFiles(operations, ctx) {
|
|
|
217
214
|
return [
|
|
218
215
|
{
|
|
219
216
|
name: `${OpenApiRqFiles.index}.ts`,
|
|
220
|
-
content: addHeaderComment(generateIndexFile(
|
|
217
|
+
content: addHeaderComment(generateIndexFile(), ctx.version),
|
|
221
218
|
},
|
|
222
219
|
{
|
|
223
220
|
name: `${OpenApiRqFiles.common}.ts`,
|
package/dist/tsmorph/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { generateAllFiles } from "./generateFiles.mjs";
|
|
2
|
-
export { createGenerationProject } from "./projectFactory.mjs";
|
|
3
1
|
export * from "./buildCommon.mjs";
|
|
4
|
-
export * from "./buildQueryHooks.mjs";
|
|
5
2
|
export * from "./buildMutationHooks.mjs";
|
|
3
|
+
export * from "./buildQueryHooks.mjs";
|
|
4
|
+
export { generateAllFiles } from "./generateFiles.mjs";
|
|
5
|
+
export { createGenerationProject } from "./projectFactory.mjs";
|
|
@@ -41,14 +41,19 @@ export function buildQueryImport() {
|
|
|
41
41
|
{ name: "useQuery" },
|
|
42
42
|
{ name: "useSuspenseQuery" },
|
|
43
43
|
{ name: "useInfiniteQuery" },
|
|
44
|
+
{ name: "useSuspenseInfiniteQuery" },
|
|
44
45
|
{ name: "useMutation" },
|
|
45
46
|
{ name: "UseQueryResult" },
|
|
46
47
|
{ name: "UseQueryOptions" },
|
|
47
48
|
{ name: "UseInfiniteQueryOptions" },
|
|
49
|
+
{ name: "UseSuspenseInfiniteQueryOptions" },
|
|
48
50
|
{ name: "UseMutationOptions" },
|
|
49
51
|
{ name: "UseMutationResult" },
|
|
50
52
|
{ name: "UseSuspenseQueryOptions" },
|
|
51
53
|
{ name: "InfiniteData" },
|
|
54
|
+
{ name: "FetchQueryOptions", isTypeOnly: true },
|
|
55
|
+
{ name: "FetchInfiniteQueryOptions", isTypeOnly: true },
|
|
56
|
+
{ name: "EnsureQueryDataOptions", isTypeOnly: true },
|
|
52
57
|
],
|
|
53
58
|
};
|
|
54
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7nohe/openapi-react-query-codegen",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.4",
|
|
4
4
|
"description": "OpenAPI React Query Codegen",
|
|
5
5
|
"bin": {
|
|
6
6
|
"openapi-rq": "dist/cli.mjs"
|
|
@@ -43,20 +43,22 @@
|
|
|
43
43
|
"cross-spawn": "^7.0.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@biomejs/biome": "^
|
|
46
|
+
"@biomejs/biome": "^2.5.4",
|
|
47
47
|
"@types/cross-spawn": "^6.0.6",
|
|
48
|
-
"@types/node": "^22.
|
|
48
|
+
"@types/node": "^22.20.1",
|
|
49
49
|
"@types/semver": "^7.7.1",
|
|
50
|
-
"@vitest/coverage-v8": "^1.
|
|
51
|
-
"commander": "^
|
|
52
|
-
"lefthook": "^1.
|
|
53
|
-
"rimraf": "^
|
|
50
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
51
|
+
"commander": "^15.0.0",
|
|
52
|
+
"lefthook": "^2.1.10",
|
|
53
|
+
"rimraf": "^6.1.3",
|
|
54
54
|
"ts-morph": "^28.0.0",
|
|
55
55
|
"typescript": "^6.0.3",
|
|
56
|
-
"
|
|
56
|
+
"vite": "^7",
|
|
57
|
+
"vitest": "^4.1.10"
|
|
57
58
|
},
|
|
58
59
|
"peerDependencies": {
|
|
59
|
-
"
|
|
60
|
+
"@tanstack/react-query": "^5.0.0",
|
|
61
|
+
"commander": "12.x || 13.x || 14.x || 15.x",
|
|
60
62
|
"ts-morph": "28.x",
|
|
61
63
|
"typescript": "5.x || 6.x"
|
|
62
64
|
},
|