@7nohe/openapi-react-query-codegen 3.0.1 → 3.0.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/parseOperations.d.mts +1 -1
- package/dist/parseOperations.mjs +25 -7
- package/dist/tsmorph/buildCommon.d.mts +1 -1
- package/dist/tsmorph/buildCommon.mjs +5 -10
- package/dist/tsmorph/buildMutationHooks.mjs +2 -18
- package/dist/tsmorph/buildQueryHooks.d.mts +25 -12
- package/dist/tsmorph/buildQueryHooks.mjs +48 -44
- package/dist/tsmorph/buildQueryOptions.d.mts +4 -3
- package/dist/tsmorph/buildQueryOptions.mjs +13 -8
- package/dist/tsmorph/generateFiles.mjs +25 -64
- package/dist/tsmorph/operationNames.d.mts +16 -0
- package/dist/tsmorph/operationNames.mjs +29 -0
- package/dist/tsmorph/projectFactory.d.mts +6 -0
- package/dist/tsmorph/projectFactory.mjs +24 -1
- package/dist/types.d.mts +14 -0
- package/package.json +1 -1
- package/dist/tsmorph/buildKeys.d.mts +0 -42
- package/dist/tsmorph/buildKeys.mjs +0 -110
package/dist/parseOperations.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Node, } from "ts-morph";
|
|
1
2
|
import ts from "typescript";
|
|
2
3
|
import { capitalizeFirstLetter, extractPropertiesFromObjectParam, getNameFromVariable, getShortType, getVariableArrowFunctionParameters, } from "./common.mjs";
|
|
3
4
|
import { modelsFileName, serviceFileName } from "./constants.mjs";
|
|
@@ -36,8 +37,25 @@ function extractParameters(method, pageParam) {
|
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
39
|
-
*
|
|
40
|
+
* Read the operation's Data type name from the first type argument of the
|
|
41
|
+
* SDK options parameter (`Options<XData, ThrowOnError>`).
|
|
42
|
+
* See OperationInfo.dataTypeName for why the name is read from the signature
|
|
43
|
+
* instead of derived from the method name (#213).
|
|
44
|
+
*/
|
|
45
|
+
function getDataTypeNameFromSignature(optionsParam) {
|
|
46
|
+
const typeNode = optionsParam?.getTypeNode();
|
|
47
|
+
if (!typeNode || !Node.isTypeReference(typeNode))
|
|
48
|
+
return undefined;
|
|
49
|
+
if (typeNode.getTypeName().getText() !== "Options")
|
|
50
|
+
return undefined;
|
|
51
|
+
const [dataTypeArg] = typeNode.getTypeArguments();
|
|
52
|
+
return dataTypeArg?.getText();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get paginatable Data types by checking if they have the pageParam in their query property.
|
|
40
56
|
* Uses TypeScript compiler API for accurate AST traversal.
|
|
57
|
+
* The map is keyed by the Data type name (e.g., "FindPetsData") so callers can
|
|
58
|
+
* look it up with the name read from the SDK signature.
|
|
41
59
|
*/
|
|
42
60
|
function getPaginatableMethods(project, pageParam) {
|
|
43
61
|
const modelsFile = project
|
|
@@ -70,13 +88,9 @@ function getPaginatableMethods(project, pageParam) {
|
|
|
70
88
|
continue;
|
|
71
89
|
const pageParamNode = queryType.members.find((m) => ts.isPropertySignature(m) && m.name?.getText() === pageParam);
|
|
72
90
|
if (pageParamNode) {
|
|
73
|
-
// Extract method name from Data type name (e.g., "FindPetsData" -> "findPets")
|
|
74
|
-
const methodName = key.slice(0, -4); // Remove "Data" suffix
|
|
75
|
-
// Convert first letter to lowercase
|
|
76
|
-
const methodNameLower = methodName.charAt(0).toLowerCase() + methodName.slice(1);
|
|
77
91
|
const pageParamType = pageParamNode.type?.getText(modelsFile.compilerNode);
|
|
78
92
|
const resolvedType = typeChecker.getTypeAtLocation(pageParamNode.type ?? pageParamNode);
|
|
79
|
-
paginatableMethods.set(
|
|
93
|
+
paginatableMethods.set(key, {
|
|
80
94
|
type: pageParamType ?? "unknown",
|
|
81
95
|
typeKind: getPageParamTypeKind(resolvedType),
|
|
82
96
|
});
|
|
@@ -100,11 +114,15 @@ export async function parseOperations(project, pageParam) {
|
|
|
100
114
|
// via extractPropertiesFromObjectParam for type alias properties (path, url).
|
|
101
115
|
const sdkParams = getVariableArrowFunctionParameters(desc.method);
|
|
102
116
|
const allParamsOptional = sdkParams.length === 0 || sdkParams[0].isOptional();
|
|
103
|
-
const
|
|
117
|
+
const dataTypeName = getDataTypeNameFromSignature(sdkParams[0]);
|
|
118
|
+
const pageParamInfo = dataTypeName
|
|
119
|
+
? paginatableMethods.get(dataTypeName)
|
|
120
|
+
: undefined;
|
|
104
121
|
const isPaginatable = httpMethod === "GET" && pageParamInfo !== undefined;
|
|
105
122
|
return {
|
|
106
123
|
methodName,
|
|
107
124
|
capitalizedMethodName: capitalizeFirstLetter(methodName),
|
|
125
|
+
dataTypeName,
|
|
108
126
|
httpMethod,
|
|
109
127
|
jsDoc: desc.jsDoc,
|
|
110
128
|
isDeprecated: desc.isDeprecated,
|
|
@@ -30,7 +30,7 @@ export declare function buildMutationKeyConst(op: OperationInfo): VariableStatem
|
|
|
30
30
|
* Example: export const UseFindPetsKeyFn = (clientOptions: Options<FindPetsData, true> = {}, queryKey?: Array<unknown>) =>
|
|
31
31
|
* [useFindPetsKey, ...(queryKey ?? [clientOptions])];
|
|
32
32
|
*/
|
|
33
|
-
export declare function buildQueryKeyFn(op: OperationInfo
|
|
33
|
+
export declare function buildQueryKeyFn(op: OperationInfo): VariableStatementStructure;
|
|
34
34
|
/**
|
|
35
35
|
* Build mutation key function.
|
|
36
36
|
* Example: export const UseAddPetKeyFn = (mutationKey?: Array<unknown>) =>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
|
|
2
|
+
import { getDataTypeName } from "./operationNames.mjs";
|
|
2
3
|
/**
|
|
3
4
|
* Build the default response type alias.
|
|
4
5
|
* Example: export type FindPetsDefaultResponse = Awaited<ReturnType<typeof findPets>>["data"];
|
|
@@ -78,10 +79,8 @@ export function buildMutationKeyConst(op) {
|
|
|
78
79
|
* Example: export const UseFindPetsKeyFn = (clientOptions: Options<FindPetsData, true> = {}, queryKey?: Array<unknown>) =>
|
|
79
80
|
* [useFindPetsKey, ...(queryKey ?? [clientOptions])];
|
|
80
81
|
*/
|
|
81
|
-
export function buildQueryKeyFn(op
|
|
82
|
-
const dataTypeName =
|
|
83
|
-
? `${op.capitalizedMethodName}Data`
|
|
84
|
-
: "unknown";
|
|
82
|
+
export function buildQueryKeyFn(op) {
|
|
83
|
+
const dataTypeName = getDataTypeName(op);
|
|
85
84
|
const params = [];
|
|
86
85
|
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
87
86
|
params.push(`clientOptions: Options<${dataTypeName}, true>${defaultValue}`);
|
|
@@ -126,12 +125,8 @@ export function buildMutationKeyFn(op) {
|
|
|
126
125
|
* { query?: Omit<NonNullable<FindPaginatedPetsData["query"]>, "page"> };
|
|
127
126
|
*/
|
|
128
127
|
export function buildInfiniteClientOptionsType(op, ctx) {
|
|
129
|
-
const dataTypeName =
|
|
130
|
-
|
|
131
|
-
: "unknown";
|
|
132
|
-
const type = dataTypeName === "unknown"
|
|
133
|
-
? "Options<unknown, true>"
|
|
134
|
-
: `Omit<Options<${dataTypeName}, true>, "query"> & { query?: Omit<NonNullable<${dataTypeName}["query"]>, "${ctx.pageParam}"> }`;
|
|
128
|
+
const dataTypeName = getDataTypeName(op);
|
|
129
|
+
const type = `Omit<Options<${dataTypeName}, true>, "query"> & { query?: Omit<NonNullable<${dataTypeName}["query"]>, "${ctx.pageParam}"> }`;
|
|
135
130
|
return {
|
|
136
131
|
kind: StructureKind.TypeAlias,
|
|
137
132
|
isExported: true,
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
|
|
2
2
|
import { SDK_CALL_ARGS } from "./buildQueryHooks.mjs";
|
|
3
|
-
|
|
4
|
-
* Get the error type string based on client type.
|
|
5
|
-
*/
|
|
6
|
-
function getErrorType(op, ctx) {
|
|
7
|
-
const errorTypeName = `${op.capitalizedMethodName}Error`;
|
|
8
|
-
// Operations without error responses have no generated Error type
|
|
9
|
-
const errorType = ctx.modelNames.includes(errorTypeName)
|
|
10
|
-
? errorTypeName
|
|
11
|
-
: "unknown";
|
|
12
|
-
if (ctx.client === "@hey-api/client-axios") {
|
|
13
|
-
return `AxiosError<${errorType}>`;
|
|
14
|
-
}
|
|
15
|
-
return errorType;
|
|
16
|
-
}
|
|
3
|
+
import { getDataTypeName, getErrorType } from "./operationNames.mjs";
|
|
17
4
|
/**
|
|
18
5
|
* Build useMutation hook.
|
|
19
6
|
* Example:
|
|
@@ -30,10 +17,7 @@ export function buildUseMutationHook(op, ctx) {
|
|
|
30
17
|
const hookName = `use${op.capitalizedMethodName}`;
|
|
31
18
|
const errorType = getErrorType(op, ctx);
|
|
32
19
|
const dataTypeDefault = `Common.${op.capitalizedMethodName}MutationResult`;
|
|
33
|
-
const
|
|
34
|
-
? `${op.capitalizedMethodName}Data`
|
|
35
|
-
: "unknown";
|
|
36
|
-
const optionsType = `Options<${dataTypeName}, true>`;
|
|
20
|
+
const optionsType = `Options<${getDataTypeName(op)}, true>`;
|
|
37
21
|
const mutationFn = `clientOptions => ${op.methodName}(${SDK_CALL_ARGS}) as unknown as Promise<TData>`;
|
|
38
22
|
const body = `useMutation<TData, TError, ${optionsType}, TContext>({ mutationKey: Common.Use${op.capitalizedMethodName}KeyFn(mutationKey), mutationFn: ${mutationFn}, ...options })`;
|
|
39
23
|
return {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { type VariableStatementStructure } from "ts-morph";
|
|
2
2
|
import type { GenerationContext, OperationInfo } from "../types.mjs";
|
|
3
|
-
/**
|
|
4
|
-
* Resolve the generated Data type name for an operation, falling back to
|
|
5
|
-
* unknown when the operation has no generated Data type.
|
|
6
|
-
*/
|
|
7
|
-
export declare function getDataTypeName(op: OperationInfo, ctx: GenerationContext): string;
|
|
8
3
|
/**
|
|
9
4
|
* SDK call arguments shared by every generated queryFn/mutationFn.
|
|
10
5
|
* throwOnError: true forces the SDK call to reject on error responses; the
|
|
@@ -19,17 +14,28 @@ export declare function getPageParamType(op: OperationInfo): string;
|
|
|
19
14
|
/**
|
|
20
15
|
* Build the client options parameter string.
|
|
21
16
|
*/
|
|
22
|
-
export declare function buildClientOptionsParam(op: OperationInfo
|
|
17
|
+
export declare function buildClientOptionsParam(op: OperationInfo): string;
|
|
23
18
|
/**
|
|
24
19
|
* Build the clientOptions parameter typed with the page-less infinite
|
|
25
20
|
* options type — the page parameter is supplied by TanStack Query's
|
|
26
21
|
* pageParam mechanism.
|
|
27
22
|
*/
|
|
28
23
|
export declare function buildInfiniteClientOptionsParam(op: OperationInfo): string;
|
|
24
|
+
/**
|
|
25
|
+
* The type of a single page. TanStack Query instantiates the infinite options
|
|
26
|
+
* with this as TQueryFnData, so it is what `getNextPageParam` receives as
|
|
27
|
+
* `lastPage` (#203). NonNullable because `throwOnError: true` means a resolved
|
|
28
|
+
* page is always present.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getPageType(op: OperationInfo): string;
|
|
29
31
|
/**
|
|
30
32
|
* Build the paginated SDK call shared by every infinite query builder.
|
|
33
|
+
* The resolved response is narrowed to the page type so it matches the
|
|
34
|
+
* TQueryFnData the infinite options are instantiated with (#203). Spelled as
|
|
35
|
+
* a cast rather than `!` because generated code is linted downstream, and a
|
|
36
|
+
* non-null assertion trips biome's noNonNullAssertion.
|
|
31
37
|
*/
|
|
32
|
-
export declare function buildPagedQueryFn(op: OperationInfo, ctx: GenerationContext
|
|
38
|
+
export declare function buildPagedQueryFn(op: OperationInfo, ctx: GenerationContext): string;
|
|
33
39
|
/**
|
|
34
40
|
* Format the initialPageParam literal. Emits `undefined` when the caller opted
|
|
35
41
|
* to omit it (#177); otherwise a numeric literal when possible so the inferred
|
|
@@ -51,8 +57,15 @@ export declare function buildGetNextPageParamExpr(ctx: GenerationContext, op?: O
|
|
|
51
57
|
* Build an options type where the pagination fields TanStack Query marks as
|
|
52
58
|
* required become optional overrides: the generator supplies them, and
|
|
53
59
|
* callers may replace them for custom pagination schemes (#156, #146).
|
|
60
|
+
*
|
|
61
|
+
* The first type argument is TQueryFnData — a single page — not TData (#203).
|
|
62
|
+
* Passing TData there made `getNextPageParam` receive the aggregated
|
|
63
|
+
* `InfiniteData<...>` as `lastPage`, so any custom pagination logic failed to
|
|
64
|
+
* compile. Only the first three type arguments are written: TanStack Query
|
|
65
|
+
* dropped TQueryData from `UseInfiniteQueryOptions` within the v5 line, so
|
|
66
|
+
* positions beyond TData are not stable across the supported peer range.
|
|
54
67
|
*/
|
|
55
|
-
export declare function buildOverridableInfiniteOptionsType(optionsTypeName: string): string;
|
|
68
|
+
export declare function buildOverridableInfiniteOptionsType(optionsTypeName: string, pageType: string): string;
|
|
56
69
|
/**
|
|
57
70
|
* Build useQuery hook.
|
|
58
71
|
* Example:
|
|
@@ -89,14 +102,14 @@ export declare function buildUseSuspenseInfiniteQueryHook(op: OperationInfo, ctx
|
|
|
89
102
|
* ...options
|
|
90
103
|
* });
|
|
91
104
|
*/
|
|
92
|
-
export declare function buildPrefetchFn(op: OperationInfo
|
|
105
|
+
export declare function buildPrefetchFn(op: OperationInfo): VariableStatementStructure;
|
|
93
106
|
/**
|
|
94
107
|
* Build prefetchInfiniteQuery function for a paginatable operation.
|
|
95
108
|
* Example:
|
|
96
|
-
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<Common.FindPaginatedPetsDefaultResponse
|
|
109
|
+
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, NonNullable<Common.FindPaginatedPetsDefaultResponse>> }) =>
|
|
97
110
|
* queryClient.prefetchInfiniteQuery({
|
|
98
111
|
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions),
|
|
99
|
-
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
|
|
112
|
+
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>),
|
|
100
113
|
* initialPageParam: 1,
|
|
101
114
|
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
|
|
102
115
|
* ...options
|
|
@@ -113,4 +126,4 @@ export declare function buildPrefetchInfiniteQueryFn(op: OperationInfo, ctx: Gen
|
|
|
113
126
|
* ...options
|
|
114
127
|
* });
|
|
115
128
|
*/
|
|
116
|
-
export declare function buildEnsureQueryDataFn(op: OperationInfo
|
|
129
|
+
export declare function buildEnsureQueryDataFn(op: OperationInfo): VariableStatementStructure;
|
|
@@ -1,27 +1,5 @@
|
|
|
1
1
|
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
|
|
2
|
-
|
|
3
|
-
* Get the error type string based on client type.
|
|
4
|
-
*/
|
|
5
|
-
function getErrorType(op, ctx) {
|
|
6
|
-
const errorTypeName = `${op.capitalizedMethodName}Error`;
|
|
7
|
-
// Operations without error responses have no generated Error type
|
|
8
|
-
const errorType = ctx.modelNames.includes(errorTypeName)
|
|
9
|
-
? errorTypeName
|
|
10
|
-
: "unknown";
|
|
11
|
-
if (ctx.client === "@hey-api/client-axios") {
|
|
12
|
-
return `AxiosError<${errorType}>`;
|
|
13
|
-
}
|
|
14
|
-
return errorType;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Resolve the generated Data type name for an operation, falling back to
|
|
18
|
-
* unknown when the operation has no generated Data type.
|
|
19
|
-
*/
|
|
20
|
-
export function getDataTypeName(op, ctx) {
|
|
21
|
-
return ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
|
|
22
|
-
? `${op.capitalizedMethodName}Data`
|
|
23
|
-
: "unknown";
|
|
24
|
-
}
|
|
2
|
+
import { getDataTypeName, getErrorType } from "./operationNames.mjs";
|
|
25
3
|
/**
|
|
26
4
|
* SDK call arguments shared by every generated queryFn/mutationFn.
|
|
27
5
|
* throwOnError: true forces the SDK call to reject on error responses; the
|
|
@@ -38,8 +16,8 @@ export function getPageParamType(op) {
|
|
|
38
16
|
/**
|
|
39
17
|
* Build the client options parameter string.
|
|
40
18
|
*/
|
|
41
|
-
export function buildClientOptionsParam(op
|
|
42
|
-
const dataTypeName = getDataTypeName(op
|
|
19
|
+
export function buildClientOptionsParam(op) {
|
|
20
|
+
const dataTypeName = getDataTypeName(op);
|
|
43
21
|
const hasParams = op.parameters.length > 0;
|
|
44
22
|
if (!hasParams) {
|
|
45
23
|
return `clientOptions: Options<${dataTypeName}, true> = {}`;
|
|
@@ -56,14 +34,25 @@ export function buildInfiniteClientOptionsParam(op) {
|
|
|
56
34
|
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
57
35
|
return `clientOptions: Common.${op.capitalizedMethodName}InfiniteClientOptions${defaultValue}`;
|
|
58
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The type of a single page. TanStack Query instantiates the infinite options
|
|
39
|
+
* with this as TQueryFnData, so it is what `getNextPageParam` receives as
|
|
40
|
+
* `lastPage` (#203). NonNullable because `throwOnError: true` means a resolved
|
|
41
|
+
* page is always present.
|
|
42
|
+
*/
|
|
43
|
+
export function getPageType(op) {
|
|
44
|
+
return `NonNullable<Common.${op.capitalizedMethodName}DefaultResponse>`;
|
|
45
|
+
}
|
|
59
46
|
/**
|
|
60
47
|
* Build the paginated SDK call shared by every infinite query builder.
|
|
48
|
+
* The resolved response is narrowed to the page type so it matches the
|
|
49
|
+
* TQueryFnData the infinite options are instantiated with (#203). Spelled as
|
|
50
|
+
* a cast rather than `!` because generated code is linted downstream, and a
|
|
51
|
+
* non-null assertion trips biome's noNonNullAssertion.
|
|
61
52
|
*/
|
|
62
|
-
export function buildPagedQueryFn(op, ctx
|
|
63
|
-
const dataTypeName = getDataTypeName(op
|
|
64
|
-
const thenClause =
|
|
65
|
-
? ".then(response => response.data as TData) as TData"
|
|
66
|
-
: ".then(response => response.data)";
|
|
53
|
+
export function buildPagedQueryFn(op, ctx) {
|
|
54
|
+
const dataTypeName = getDataTypeName(op);
|
|
55
|
+
const thenClause = `.then(response => response.data as ${getPageType(op)})`;
|
|
67
56
|
const pageParamType = getPageParamType(op);
|
|
68
57
|
// When the initial page param is omitted, the first request must send no
|
|
69
58
|
// page param at all, so spread it in only once TanStack Query provides one.
|
|
@@ -116,9 +105,16 @@ export function buildGetNextPageParamExpr(ctx, op) {
|
|
|
116
105
|
* Build an options type where the pagination fields TanStack Query marks as
|
|
117
106
|
* required become optional overrides: the generator supplies them, and
|
|
118
107
|
* callers may replace them for custom pagination schemes (#156, #146).
|
|
108
|
+
*
|
|
109
|
+
* The first type argument is TQueryFnData — a single page — not TData (#203).
|
|
110
|
+
* Passing TData there made `getNextPageParam` receive the aggregated
|
|
111
|
+
* `InfiniteData<...>` as `lastPage`, so any custom pagination logic failed to
|
|
112
|
+
* compile. Only the first three type arguments are written: TanStack Query
|
|
113
|
+
* dropped TQueryData from `UseInfiniteQueryOptions` within the v5 line, so
|
|
114
|
+
* positions beyond TData are not stable across the supported peer range.
|
|
119
115
|
*/
|
|
120
|
-
export function buildOverridableInfiniteOptionsType(optionsTypeName) {
|
|
121
|
-
const instantiated = `${optionsTypeName}
|
|
116
|
+
export function buildOverridableInfiniteOptionsType(optionsTypeName, pageType) {
|
|
117
|
+
const instantiated = `${optionsTypeName}<${pageType}, TError, TData>`;
|
|
122
118
|
return `Omit<${instantiated}, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<${instantiated}, "initialPageParam" | "getNextPageParam">>`;
|
|
123
119
|
}
|
|
124
120
|
/**
|
|
@@ -138,7 +134,7 @@ export function buildUseQueryHook(op, ctx) {
|
|
|
138
134
|
const hookName = `use${op.capitalizedMethodName}`;
|
|
139
135
|
const errorType = getErrorType(op, ctx);
|
|
140
136
|
const dataTypeDefault = `Common.${op.capitalizedMethodName}DefaultResponse`;
|
|
141
|
-
const clientOptionsParam = buildClientOptionsParam(op
|
|
137
|
+
const clientOptionsParam = buildClientOptionsParam(op);
|
|
142
138
|
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data as TData) as TData`;
|
|
143
139
|
const body = `useQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`;
|
|
144
140
|
return {
|
|
@@ -162,7 +158,7 @@ export function buildUseSuspenseQueryHook(op, ctx) {
|
|
|
162
158
|
const hookName = `use${op.capitalizedMethodName}Suspense`;
|
|
163
159
|
const errorType = getErrorType(op, ctx);
|
|
164
160
|
const dataTypeDefault = `NonNullable<Common.${op.capitalizedMethodName}DefaultResponse>`;
|
|
165
|
-
const clientOptionsParam = buildClientOptionsParam(op
|
|
161
|
+
const clientOptionsParam = buildClientOptionsParam(op);
|
|
166
162
|
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data as TData) as TData`;
|
|
167
163
|
const body = `useSuspenseQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`;
|
|
168
164
|
return {
|
|
@@ -201,7 +197,7 @@ function buildInfiniteHook(op, ctx, suspense) {
|
|
|
201
197
|
const dataTypeDefault = suspense
|
|
202
198
|
? `InfiniteData<NonNullable<${baseDataType}>>`
|
|
203
199
|
: `InfiniteData<${baseDataType}>`;
|
|
204
|
-
const queryFn = buildPagedQueryFn(op, ctx
|
|
200
|
+
const queryFn = buildPagedQueryFn(op, ctx);
|
|
205
201
|
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;
|
|
206
202
|
const body = `${hookCall}({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
207
203
|
return {
|
|
@@ -213,7 +209,7 @@ function buildInfiniteHook(op, ctx, suspense) {
|
|
|
213
209
|
declarations: [
|
|
214
210
|
{
|
|
215
211
|
name: hookName,
|
|
216
|
-
initializer: `<TData = ${dataTypeDefault}, TError = ${errorType}, TQueryKey extends Array<unknown> = unknown[]>(${buildInfiniteClientOptionsParam(op)}, queryKey?: TQueryKey, options?: ${buildOverridableInfiniteOptionsType(optionsTypeName)}) => ${body}`,
|
|
212
|
+
initializer: `<TData = ${dataTypeDefault}, TError = ${errorType}, TQueryKey extends Array<unknown> = unknown[]>(${buildInfiniteClientOptionsParam(op)}, queryKey?: TQueryKey, options?: ${buildOverridableInfiniteOptionsType(optionsTypeName, getPageType(op))}) => ${body}`,
|
|
217
213
|
},
|
|
218
214
|
],
|
|
219
215
|
};
|
|
@@ -240,7 +236,7 @@ export function buildUseSuspenseInfiniteQueryHook(op, ctx) {
|
|
|
240
236
|
* ...options
|
|
241
237
|
* });
|
|
242
238
|
*/
|
|
243
|
-
export function buildPrefetchFn(op
|
|
239
|
+
export function buildPrefetchFn(op) {
|
|
244
240
|
const fnName = `prefetchUse${op.capitalizedMethodName}`;
|
|
245
241
|
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`;
|
|
246
242
|
const optionsParam = `options?: Omit<FetchQueryOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn">`;
|
|
@@ -254,7 +250,7 @@ export function buildPrefetchFn(op, ctx) {
|
|
|
254
250
|
declarations: [
|
|
255
251
|
{
|
|
256
252
|
name: fnName,
|
|
257
|
-
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op
|
|
253
|
+
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op)}, ${optionsParam}) => ${body}`,
|
|
258
254
|
},
|
|
259
255
|
],
|
|
260
256
|
};
|
|
@@ -262,10 +258,10 @@ export function buildPrefetchFn(op, ctx) {
|
|
|
262
258
|
/**
|
|
263
259
|
* Build prefetchInfiniteQuery function for a paginatable operation.
|
|
264
260
|
* Example:
|
|
265
|
-
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<Common.FindPaginatedPetsDefaultResponse
|
|
261
|
+
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, NonNullable<Common.FindPaginatedPetsDefaultResponse>> }) =>
|
|
266
262
|
* queryClient.prefetchInfiniteQuery({
|
|
267
263
|
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions),
|
|
268
|
-
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
|
|
264
|
+
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>),
|
|
269
265
|
* initialPageParam: 1,
|
|
270
266
|
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
|
|
271
267
|
* ...options
|
|
@@ -276,9 +272,17 @@ export function buildPrefetchInfiniteQueryFn(op, ctx) {
|
|
|
276
272
|
return null;
|
|
277
273
|
}
|
|
278
274
|
const fnName = `prefetchUse${op.capitalizedMethodName}Infinite`;
|
|
279
|
-
const queryFn = buildPagedQueryFn(op, ctx
|
|
275
|
+
const queryFn = buildPagedQueryFn(op, ctx);
|
|
280
276
|
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;
|
|
281
|
-
|
|
277
|
+
// The pagination fields are re-added as optional overrides: `...options` is
|
|
278
|
+
// spread after the defaults, so replacing them works at runtime and must not
|
|
279
|
+
// be forbidden by the type (#203). getNextPageParam is spelled out rather
|
|
280
|
+
// than Picked, because FetchInfiniteQueryOptions only exposes it on the
|
|
281
|
+
// union member that also requires `pages`.
|
|
282
|
+
const pageType = getPageType(op);
|
|
283
|
+
const instantiated = `FetchInfiniteQueryOptions<${pageType}>`;
|
|
284
|
+
const overrides = `Partial<Pick<${instantiated}, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, ${pageType}> }`;
|
|
285
|
+
const optionsParam = `options?: Omit<${instantiated}, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & ${overrides}`;
|
|
282
286
|
const body = `queryClient.prefetchInfiniteQuery({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
283
287
|
return {
|
|
284
288
|
kind: StructureKind.VariableStatement,
|
|
@@ -304,7 +308,7 @@ export function buildPrefetchInfiniteQueryFn(op, ctx) {
|
|
|
304
308
|
* ...options
|
|
305
309
|
* });
|
|
306
310
|
*/
|
|
307
|
-
export function buildEnsureQueryDataFn(op
|
|
311
|
+
export function buildEnsureQueryDataFn(op) {
|
|
308
312
|
const fnName = `ensureUse${op.capitalizedMethodName}Data`;
|
|
309
313
|
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`;
|
|
310
314
|
const optionsParam = `options?: Omit<EnsureQueryDataOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn">`;
|
|
@@ -318,7 +322,7 @@ export function buildEnsureQueryDataFn(op, ctx) {
|
|
|
318
322
|
declarations: [
|
|
319
323
|
{
|
|
320
324
|
name: fnName,
|
|
321
|
-
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op
|
|
325
|
+
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op)}, ${optionsParam}) => ${body}`,
|
|
322
326
|
},
|
|
323
327
|
],
|
|
324
328
|
};
|
|
@@ -12,17 +12,18 @@ import type { GenerationContext, OperationInfo } from "../types.mjs";
|
|
|
12
12
|
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data),
|
|
13
13
|
* });
|
|
14
14
|
*/
|
|
15
|
-
export declare function buildQueryOptionsFn(op: OperationInfo
|
|
15
|
+
export declare function buildQueryOptionsFn(op: OperationInfo): VariableStatementStructure;
|
|
16
16
|
/**
|
|
17
17
|
* Build an infiniteQueryOptions factory for a paginatable GET operation.
|
|
18
18
|
* Uses the dedicated infinite query key and page-less options type.
|
|
19
19
|
* Example:
|
|
20
|
-
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown
|
|
20
|
+
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>, options?: Partial<Pick<UseInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam" | "getNextPageParam">>) =>
|
|
21
21
|
* infiniteQueryOptions({
|
|
22
22
|
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions, queryKey),
|
|
23
|
-
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
|
|
23
|
+
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>),
|
|
24
24
|
* initialPageParam: 1,
|
|
25
25
|
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
|
|
26
|
+
* ...options,
|
|
26
27
|
* });
|
|
27
28
|
*/
|
|
28
29
|
export declare function buildInfiniteQueryOptionsFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
|
|
2
|
-
import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClientOptionsParam, buildPagedQueryFn, formatInitialPageParam, QUERY_SDK_CALL_ARGS, } from "./buildQueryHooks.mjs";
|
|
2
|
+
import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClientOptionsParam, buildPagedQueryFn, formatInitialPageParam, getPageType, QUERY_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
|
|
@@ -12,9 +12,9 @@ import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClient
|
|
|
12
12
|
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data),
|
|
13
13
|
* });
|
|
14
14
|
*/
|
|
15
|
-
export function buildQueryOptionsFn(op
|
|
15
|
+
export function buildQueryOptionsFn(op) {
|
|
16
16
|
const fnName = `${op.methodName}Options`;
|
|
17
|
-
const clientOptionsParam = buildClientOptionsParam(op
|
|
17
|
+
const clientOptionsParam = buildClientOptionsParam(op);
|
|
18
18
|
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_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 {
|
|
@@ -35,12 +35,13 @@ export function buildQueryOptionsFn(op, ctx) {
|
|
|
35
35
|
* Build an infiniteQueryOptions factory for a paginatable GET operation.
|
|
36
36
|
* Uses the dedicated infinite query key and page-less options type.
|
|
37
37
|
* Example:
|
|
38
|
-
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown
|
|
38
|
+
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>, options?: Partial<Pick<UseInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam" | "getNextPageParam">>) =>
|
|
39
39
|
* infiniteQueryOptions({
|
|
40
40
|
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions, queryKey),
|
|
41
|
-
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } 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 as NonNullable<Common.FindPaginatedPetsDefaultResponse>),
|
|
42
42
|
* initialPageParam: 1,
|
|
43
43
|
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
|
|
44
|
+
* ...options,
|
|
44
45
|
* });
|
|
45
46
|
*/
|
|
46
47
|
export function buildInfiniteQueryOptionsFn(op, ctx) {
|
|
@@ -48,9 +49,13 @@ export function buildInfiniteQueryOptionsFn(op, ctx) {
|
|
|
48
49
|
return null;
|
|
49
50
|
}
|
|
50
51
|
const fnName = `${op.methodName}InfiniteOptions`;
|
|
51
|
-
const queryFn = buildPagedQueryFn(op, ctx
|
|
52
|
+
const queryFn = buildPagedQueryFn(op, ctx);
|
|
52
53
|
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;
|
|
53
|
-
|
|
54
|
+
// Only the pagination fields are overridable here: the factory's return type
|
|
55
|
+
// is what every downstream consumer infers from, and a wider options type
|
|
56
|
+
// (select, placeholderData, ...) would make that inference ambiguous (#203).
|
|
57
|
+
const optionsParam = `options?: Partial<Pick<UseInfiniteQueryOptions<${getPageType(op)}>, "initialPageParam" | "getNextPageParam">>`;
|
|
58
|
+
const body = `infiniteQueryOptions({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
54
59
|
return {
|
|
55
60
|
kind: StructureKind.VariableStatement,
|
|
56
61
|
// Copy the operation's JSDoc (description and @deprecated) from the SDK function
|
|
@@ -60,7 +65,7 @@ export function buildInfiniteQueryOptionsFn(op, ctx) {
|
|
|
60
65
|
declarations: [
|
|
61
66
|
{
|
|
62
67
|
name: fnName,
|
|
63
|
-
initializer: `(${buildInfiniteClientOptionsParam(op)}, queryKey?: Array<unknown
|
|
68
|
+
initializer: `(${buildInfiniteClientOptionsParam(op)}, queryKey?: Array<unknown>, ${optionsParam}) => ${body}`,
|
|
64
69
|
},
|
|
65
70
|
],
|
|
66
71
|
};
|
|
@@ -3,30 +3,20 @@ import { buildDefaultResponseType, buildInfiniteClientOptionsType, buildInfinite
|
|
|
3
3
|
import { buildUseMutationHook } from "./buildMutationHooks.mjs";
|
|
4
4
|
import { buildEnsureQueryDataFn, buildPrefetchFn, buildPrefetchInfiniteQueryFn, buildUseInfiniteQueryHook, buildUseQueryHook, buildUseSuspenseInfiniteQueryHook, buildUseSuspenseQueryHook, } from "./buildQueryHooks.mjs";
|
|
5
5
|
import { buildInfiniteQueryOptionsFn, buildQueryOptionsFn, } from "./buildQueryOptions.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { buildCommonFileImports, buildHookFileImports, buildQueryOptionsFileImports, createGenerationProject, } from "./projectFactory.mjs";
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Add one variable statement per operation, skipping the ones the builder
|
|
9
|
+
* declines. A builder returns null when the operation is out of its scope —
|
|
10
|
+
* every infinite-query builder returns null for non-paginatable operations,
|
|
11
|
+
* which is how the paginatable subset gets selected.
|
|
9
12
|
*/
|
|
10
|
-
function
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const modelImport = buildModelImport(ctx);
|
|
17
|
-
if (modelImport) {
|
|
18
|
-
imports.push(modelImport);
|
|
19
|
-
}
|
|
20
|
-
if (ctx.client === "@hey-api/client-axios") {
|
|
21
|
-
imports.push(buildAxiosErrorImport());
|
|
13
|
+
function addStatements(sourceFile, operations, build) {
|
|
14
|
+
for (const op of operations) {
|
|
15
|
+
const statement = build(op);
|
|
16
|
+
if (statement) {
|
|
17
|
+
sourceFile.addVariableStatement(statement);
|
|
18
|
+
}
|
|
22
19
|
}
|
|
23
|
-
return imports;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Build imports for hook files (queries, suspense, infinite, prefetch, ensure).
|
|
27
|
-
*/
|
|
28
|
-
function buildHookFileImports(ctx) {
|
|
29
|
-
return [buildCommonImport(), ...buildCommonFileImports(ctx)];
|
|
30
20
|
}
|
|
31
21
|
/**
|
|
32
22
|
* Generate the index.ts file content.
|
|
@@ -51,7 +41,7 @@ function generateCommonFile(operations, ctx) {
|
|
|
51
41
|
sourceFile.addTypeAlias(buildDefaultResponseType(op));
|
|
52
42
|
sourceFile.addTypeAlias(buildQueryResultType(op));
|
|
53
43
|
sourceFile.addVariableStatement(buildQueryKeyConst(op));
|
|
54
|
-
sourceFile.addVariableStatement(buildQueryKeyFn(op
|
|
44
|
+
sourceFile.addVariableStatement(buildQueryKeyFn(op));
|
|
55
45
|
}
|
|
56
46
|
// Add dedicated infinite query types and keys for paginatable operations
|
|
57
47
|
for (const op of getOperations.filter((o) => o.isPaginatable)) {
|
|
@@ -95,28 +85,14 @@ function generateQueryOptionsFile(operations, ctx) {
|
|
|
95
85
|
const project = createGenerationProject();
|
|
96
86
|
const sourceFile = project.createSourceFile(`${OpenApiRqFiles.queryOptions}.ts`, undefined, { overwrite: true });
|
|
97
87
|
// Add imports
|
|
98
|
-
|
|
99
|
-
buildCommonImport(),
|
|
100
|
-
buildQueryOptionsImport(),
|
|
101
|
-
buildClientImport(ctx),
|
|
102
|
-
buildServiceImport(ctx),
|
|
103
|
-
];
|
|
104
|
-
const modelImport = buildModelImport(ctx);
|
|
105
|
-
if (modelImport) {
|
|
106
|
-
imports.push(modelImport);
|
|
107
|
-
}
|
|
108
|
-
sourceFile.addImportDeclarations(imports);
|
|
88
|
+
sourceFile.addImportDeclarations(buildQueryOptionsFileImports(ctx));
|
|
109
89
|
// Only GET operations have query options
|
|
110
90
|
const getOperations = operations.filter((op) => op.httpMethod === "GET");
|
|
111
91
|
for (const op of getOperations) {
|
|
112
|
-
sourceFile.addVariableStatement(buildQueryOptionsFn(op
|
|
113
|
-
}
|
|
114
|
-
for (const op of getOperations) {
|
|
115
|
-
const infiniteOptions = buildInfiniteQueryOptionsFn(op, ctx);
|
|
116
|
-
if (infiniteOptions) {
|
|
117
|
-
sourceFile.addVariableStatement(infiniteOptions);
|
|
118
|
-
}
|
|
92
|
+
sourceFile.addVariableStatement(buildQueryOptionsFn(op));
|
|
119
93
|
}
|
|
94
|
+
// Add infiniteQueryOptions factories
|
|
95
|
+
addStatements(sourceFile, getOperations, (op) => buildInfiniteQueryOptionsFn(op, ctx));
|
|
120
96
|
return sourceFile.getFullText();
|
|
121
97
|
}
|
|
122
98
|
/**
|
|
@@ -133,13 +109,8 @@ function generateSuspenseFile(operations, ctx) {
|
|
|
133
109
|
for (const op of getOperations) {
|
|
134
110
|
sourceFile.addVariableStatement(buildUseSuspenseQueryHook(op, ctx));
|
|
135
111
|
}
|
|
136
|
-
// Add useSuspenseInfiniteQuery hooks
|
|
137
|
-
|
|
138
|
-
const hook = buildUseSuspenseInfiniteQueryHook(op, ctx);
|
|
139
|
-
if (hook) {
|
|
140
|
-
sourceFile.addVariableStatement(hook);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
112
|
+
// Add useSuspenseInfiniteQuery hooks
|
|
113
|
+
addStatements(sourceFile, getOperations, (op) => buildUseSuspenseInfiniteQueryHook(op, ctx));
|
|
143
114
|
return sourceFile.getFullText();
|
|
144
115
|
}
|
|
145
116
|
/**
|
|
@@ -150,15 +121,10 @@ function generateInfiniteQueriesFile(operations, ctx) {
|
|
|
150
121
|
const sourceFile = project.createSourceFile(`${OpenApiRqFiles.infiniteQueries}.ts`, undefined, { overwrite: true });
|
|
151
122
|
// Add imports
|
|
152
123
|
sourceFile.addImportDeclarations(buildHookFileImports(ctx));
|
|
153
|
-
// Only
|
|
154
|
-
const
|
|
124
|
+
// Only GET operations can be paginatable
|
|
125
|
+
const getOperations = operations.filter((op) => op.httpMethod === "GET");
|
|
155
126
|
// Add useInfiniteQuery hooks
|
|
156
|
-
|
|
157
|
-
const hook = buildUseInfiniteQueryHook(op, ctx);
|
|
158
|
-
if (hook) {
|
|
159
|
-
sourceFile.addVariableStatement(hook);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
127
|
+
addStatements(sourceFile, getOperations, (op) => buildUseInfiniteQueryHook(op, ctx));
|
|
162
128
|
return sourceFile.getFullText();
|
|
163
129
|
}
|
|
164
130
|
/**
|
|
@@ -173,15 +139,10 @@ function generatePrefetchFile(operations, ctx) {
|
|
|
173
139
|
const getOperations = operations.filter((op) => op.httpMethod === "GET");
|
|
174
140
|
// Add prefetch functions
|
|
175
141
|
for (const op of getOperations) {
|
|
176
|
-
sourceFile.addVariableStatement(buildPrefetchFn(op
|
|
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
|
-
}
|
|
142
|
+
sourceFile.addVariableStatement(buildPrefetchFn(op));
|
|
184
143
|
}
|
|
144
|
+
// Add prefetchInfiniteQuery functions
|
|
145
|
+
addStatements(sourceFile, getOperations, (op) => buildPrefetchInfiniteQueryFn(op, ctx));
|
|
185
146
|
return sourceFile.getFullText();
|
|
186
147
|
}
|
|
187
148
|
/**
|
|
@@ -196,7 +157,7 @@ function generateEnsureQueryDataFile(operations, ctx) {
|
|
|
196
157
|
const getOperations = operations.filter((op) => op.httpMethod === "GET");
|
|
197
158
|
// Add ensureQueryData functions
|
|
198
159
|
for (const op of getOperations) {
|
|
199
|
-
sourceFile.addVariableStatement(buildEnsureQueryDataFn(op
|
|
160
|
+
sourceFile.addVariableStatement(buildEnsureQueryDataFn(op));
|
|
200
161
|
}
|
|
201
162
|
return sourceFile.getFullText();
|
|
202
163
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GenerationContext, OperationInfo } from "../types.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the generated Data type name for an operation, falling back to
|
|
4
|
+
* unknown when the SDK signature exposes no Data type.
|
|
5
|
+
* See OperationInfo.dataTypeName for why the name is read from the signature
|
|
6
|
+
* instead of derived from the method name (#213).
|
|
7
|
+
*/
|
|
8
|
+
export declare function getDataTypeName(op: OperationInfo): string;
|
|
9
|
+
/**
|
|
10
|
+
* Get the error type string based on client type.
|
|
11
|
+
* The Error type shares its stem with the Data type — hey-api mints both from
|
|
12
|
+
* the operationId (see OperationInfo.dataTypeName, #213) — so the stem comes
|
|
13
|
+
* from `dataTypeName` rather than the method name. The modelNames probe stays
|
|
14
|
+
* because operations without error responses have no generated Error type.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getErrorType(op: OperationInfo, ctx: GenerationContext): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the generated Data type name for an operation, falling back to
|
|
3
|
+
* unknown when the SDK signature exposes no Data type.
|
|
4
|
+
* See OperationInfo.dataTypeName for why the name is read from the signature
|
|
5
|
+
* instead of derived from the method name (#213).
|
|
6
|
+
*/
|
|
7
|
+
export function getDataTypeName(op) {
|
|
8
|
+
return op.dataTypeName ?? "unknown";
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get the error type string based on client type.
|
|
12
|
+
* The Error type shares its stem with the Data type — hey-api mints both from
|
|
13
|
+
* the operationId (see OperationInfo.dataTypeName, #213) — so the stem comes
|
|
14
|
+
* from `dataTypeName` rather than the method name. The modelNames probe stays
|
|
15
|
+
* because operations without error responses have no generated Error type.
|
|
16
|
+
*/
|
|
17
|
+
export function getErrorType(op, ctx) {
|
|
18
|
+
const stem = op.dataTypeName
|
|
19
|
+
? op.dataTypeName.replace(/Data$/, "")
|
|
20
|
+
: op.capitalizedMethodName;
|
|
21
|
+
const errorTypeName = `${stem}Error`;
|
|
22
|
+
const errorType = ctx.modelNames.includes(errorTypeName)
|
|
23
|
+
? errorTypeName
|
|
24
|
+
: "unknown";
|
|
25
|
+
if (ctx.client === "@hey-api/client-axios") {
|
|
26
|
+
return `AxiosError<${errorType}>`;
|
|
27
|
+
}
|
|
28
|
+
return errorType;
|
|
29
|
+
}
|
|
@@ -45,3 +45,9 @@ export declare function buildCommonFileImports(ctx: GenerationContext): ImportDe
|
|
|
45
45
|
* Build all imports needed for hook files (queries, suspense, infinite).
|
|
46
46
|
*/
|
|
47
47
|
export declare function buildHookFileImports(ctx: GenerationContext): ImportDeclarationStructure[];
|
|
48
|
+
/**
|
|
49
|
+
* Build all imports needed for the queryOptions file.
|
|
50
|
+
* Narrower than the hook file imports: queryOptions only needs the
|
|
51
|
+
* queryOptions/infiniteQueryOptions helpers, not the TanStack hooks.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildQueryOptionsFileImports(ctx: GenerationContext): ImportDeclarationStructure[];
|
|
@@ -53,6 +53,7 @@ export function buildQueryImport() {
|
|
|
53
53
|
{ name: "InfiniteData", isTypeOnly: true },
|
|
54
54
|
{ name: "FetchQueryOptions", isTypeOnly: true },
|
|
55
55
|
{ name: "FetchInfiniteQueryOptions", isTypeOnly: true },
|
|
56
|
+
{ name: "GetNextPageParamFunction", isTypeOnly: true },
|
|
56
57
|
{ name: "EnsureQueryDataOptions", isTypeOnly: true },
|
|
57
58
|
],
|
|
58
59
|
};
|
|
@@ -64,7 +65,11 @@ export function buildQueryOptionsImport() {
|
|
|
64
65
|
return {
|
|
65
66
|
kind: StructureKind.ImportDeclaration,
|
|
66
67
|
moduleSpecifier: "@tanstack/react-query",
|
|
67
|
-
namedImports: [
|
|
68
|
+
namedImports: [
|
|
69
|
+
{ name: "queryOptions" },
|
|
70
|
+
{ name: "infiniteQueryOptions" },
|
|
71
|
+
{ name: "UseInfiniteQueryOptions", isTypeOnly: true },
|
|
72
|
+
],
|
|
68
73
|
};
|
|
69
74
|
}
|
|
70
75
|
/**
|
|
@@ -137,3 +142,21 @@ export function buildCommonFileImports(ctx) {
|
|
|
137
142
|
export function buildHookFileImports(ctx) {
|
|
138
143
|
return [buildCommonImport(), ...buildCommonFileImports(ctx)];
|
|
139
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Build all imports needed for the queryOptions file.
|
|
147
|
+
* Narrower than the hook file imports: queryOptions only needs the
|
|
148
|
+
* queryOptions/infiniteQueryOptions helpers, not the TanStack hooks.
|
|
149
|
+
*/
|
|
150
|
+
export function buildQueryOptionsFileImports(ctx) {
|
|
151
|
+
const imports = [
|
|
152
|
+
buildCommonImport(),
|
|
153
|
+
buildQueryOptionsImport(),
|
|
154
|
+
buildClientImport(ctx),
|
|
155
|
+
buildServiceImport(ctx),
|
|
156
|
+
];
|
|
157
|
+
const modelImport = buildModelImport(ctx);
|
|
158
|
+
if (modelImport) {
|
|
159
|
+
imports.push(modelImport);
|
|
160
|
+
}
|
|
161
|
+
return imports;
|
|
162
|
+
}
|
package/dist/types.d.mts
CHANGED
|
@@ -7,6 +7,20 @@ export interface OperationInfo {
|
|
|
7
7
|
methodName: string;
|
|
8
8
|
/** Capitalized method name (e.g., "FindPets") */
|
|
9
9
|
capitalizedMethodName: string;
|
|
10
|
+
/**
|
|
11
|
+
* Generated Data type name read from the SDK function's own
|
|
12
|
+
* `Options<XData, ThrowOnError>` signature (e.g., "FindPetsData").
|
|
13
|
+
* Not derivable from the method name: for digit-leading operationIds
|
|
14
|
+
* hey-api prefixes the function but strips the digits from the type (#213).
|
|
15
|
+
* Undefined when the SDK signature exposes no Data type.
|
|
16
|
+
*
|
|
17
|
+
* The rule this encodes: names hey-api owns (Data, Error) must be read from
|
|
18
|
+
* the SDK signature or share this field's stem; names this codegen mints
|
|
19
|
+
* itself (DefaultResponse, MutationResult, key fns) may be derived from
|
|
20
|
+
* capitalizedMethodName because they anchor back to the SDK via
|
|
21
|
+
* `typeof methodName`.
|
|
22
|
+
*/
|
|
23
|
+
dataTypeName?: string;
|
|
10
24
|
/** HTTP method (e.g., "GET", "POST", "PUT", "PATCH", "DELETE") */
|
|
11
25
|
httpMethod: string;
|
|
12
26
|
/** JSDoc comment string (if present) */
|
package/package.json
CHANGED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { type VariableStatementStructure } from "ts-morph";
|
|
2
|
-
import type { GenerationContext, OperationInfo } from "../types.mjs";
|
|
3
|
-
/**
|
|
4
|
-
* Build query key constant name (e.g., "findPetsQueryKey").
|
|
5
|
-
*/
|
|
6
|
-
export declare function getQueryKeyName(op: OperationInfo): string;
|
|
7
|
-
/**
|
|
8
|
-
* Build mutation key constant name (e.g., "addPetMutationKey").
|
|
9
|
-
*/
|
|
10
|
-
export declare function getMutationKeyName(op: OperationInfo): string;
|
|
11
|
-
/**
|
|
12
|
-
* Build query key fn name (e.g., "FindPetsQueryKeyFn").
|
|
13
|
-
*/
|
|
14
|
-
export declare function getQueryKeyFnName(op: OperationInfo): string;
|
|
15
|
-
/**
|
|
16
|
-
* Build mutation key fn name (e.g., "AddPetMutationKeyFn").
|
|
17
|
-
*/
|
|
18
|
-
export declare function getMutationKeyFnName(op: OperationInfo): string;
|
|
19
|
-
/**
|
|
20
|
-
* Build the query key constant export.
|
|
21
|
-
* Example: export const findPetsQueryKey = "FindPets";
|
|
22
|
-
*/
|
|
23
|
-
export declare function buildQueryKeyExport(op: OperationInfo): VariableStatementStructure;
|
|
24
|
-
/**
|
|
25
|
-
* Build the mutation key constant export.
|
|
26
|
-
* Example: export const addPetMutationKey = "AddPet";
|
|
27
|
-
*/
|
|
28
|
-
export declare function buildMutationKeyExport(op: OperationInfo): VariableStatementStructure;
|
|
29
|
-
/**
|
|
30
|
-
* Build the query key function export.
|
|
31
|
-
* Example:
|
|
32
|
-
* export const FindPetsQueryKeyFn = (clientOptions: Options<FindPetsData, true>, queryKey?: Array<unknown>) =>
|
|
33
|
-
* [findPetsQueryKey, ...(queryKey ?? [clientOptions])] as const;
|
|
34
|
-
*/
|
|
35
|
-
export declare function buildQueryKeyFnExport(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
|
|
36
|
-
/**
|
|
37
|
-
* Build the mutation key function export.
|
|
38
|
-
* Example:
|
|
39
|
-
* export const AddPetMutationKeyFn = (mutationKey?: Array<unknown>) =>
|
|
40
|
-
* [addPetMutationKey, ...(mutationKey ?? [])] as const;
|
|
41
|
-
*/
|
|
42
|
-
export declare function buildMutationKeyFnExport(op: OperationInfo): VariableStatementStructure;
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
|
|
2
|
-
/**
|
|
3
|
-
* Build query key constant name (e.g., "findPetsQueryKey").
|
|
4
|
-
*/
|
|
5
|
-
export function getQueryKeyName(op) {
|
|
6
|
-
return `${op.methodName}QueryKey`;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Build mutation key constant name (e.g., "addPetMutationKey").
|
|
10
|
-
*/
|
|
11
|
-
export function getMutationKeyName(op) {
|
|
12
|
-
return `${op.methodName}MutationKey`;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Build query key fn name (e.g., "FindPetsQueryKeyFn").
|
|
16
|
-
*/
|
|
17
|
-
export function getQueryKeyFnName(op) {
|
|
18
|
-
return `${op.capitalizedMethodName}QueryKeyFn`;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Build mutation key fn name (e.g., "AddPetMutationKeyFn").
|
|
22
|
-
*/
|
|
23
|
-
export function getMutationKeyFnName(op) {
|
|
24
|
-
return `${op.capitalizedMethodName}MutationKeyFn`;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Build the query key constant export.
|
|
28
|
-
* Example: export const findPetsQueryKey = "FindPets";
|
|
29
|
-
*/
|
|
30
|
-
export function buildQueryKeyExport(op) {
|
|
31
|
-
return {
|
|
32
|
-
kind: StructureKind.VariableStatement,
|
|
33
|
-
isExported: true,
|
|
34
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
35
|
-
declarations: [
|
|
36
|
-
{
|
|
37
|
-
name: getQueryKeyName(op),
|
|
38
|
-
initializer: `"${op.capitalizedMethodName}"`,
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Build the mutation key constant export.
|
|
45
|
-
* Example: export const addPetMutationKey = "AddPet";
|
|
46
|
-
*/
|
|
47
|
-
export function buildMutationKeyExport(op) {
|
|
48
|
-
return {
|
|
49
|
-
kind: StructureKind.VariableStatement,
|
|
50
|
-
isExported: true,
|
|
51
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
52
|
-
declarations: [
|
|
53
|
-
{
|
|
54
|
-
name: getMutationKeyName(op),
|
|
55
|
-
initializer: `"${op.capitalizedMethodName}"`,
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Build the query key function export.
|
|
62
|
-
* Example:
|
|
63
|
-
* export const FindPetsQueryKeyFn = (clientOptions: Options<FindPetsData, true>, queryKey?: Array<unknown>) =>
|
|
64
|
-
* [findPetsQueryKey, ...(queryKey ?? [clientOptions])] as const;
|
|
65
|
-
*/
|
|
66
|
-
export function buildQueryKeyFnExport(op, ctx) {
|
|
67
|
-
const hasParams = op.parameters.length > 0;
|
|
68
|
-
const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
|
|
69
|
-
? `${op.capitalizedMethodName}Data`
|
|
70
|
-
: "unknown";
|
|
71
|
-
const params = [];
|
|
72
|
-
if (hasParams) {
|
|
73
|
-
const defaultValue = op.allParamsOptional ? " = {}" : "";
|
|
74
|
-
params.push(`clientOptions: Options<${dataTypeName}, true>${defaultValue}`);
|
|
75
|
-
}
|
|
76
|
-
params.push("queryKey?: Array<unknown>");
|
|
77
|
-
const fallbackArray = hasParams ? "[clientOptions]" : "[]";
|
|
78
|
-
const body = `[${getQueryKeyName(op)}, ...(queryKey ?? ${fallbackArray})] as const`;
|
|
79
|
-
return {
|
|
80
|
-
kind: StructureKind.VariableStatement,
|
|
81
|
-
isExported: true,
|
|
82
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
83
|
-
declarations: [
|
|
84
|
-
{
|
|
85
|
-
name: getQueryKeyFnName(op),
|
|
86
|
-
initializer: `(${params.join(", ")}) => ${body}`,
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Build the mutation key function export.
|
|
93
|
-
* Example:
|
|
94
|
-
* export const AddPetMutationKeyFn = (mutationKey?: Array<unknown>) =>
|
|
95
|
-
* [addPetMutationKey, ...(mutationKey ?? [])] as const;
|
|
96
|
-
*/
|
|
97
|
-
export function buildMutationKeyFnExport(op) {
|
|
98
|
-
const body = `[${getMutationKeyName(op)}, ...(mutationKey ?? [])] as const`;
|
|
99
|
-
return {
|
|
100
|
-
kind: StructureKind.VariableStatement,
|
|
101
|
-
isExported: true,
|
|
102
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
103
|
-
declarations: [
|
|
104
|
-
{
|
|
105
|
-
name: getMutationKeyFnName(op),
|
|
106
|
-
initializer: `(mutationKey?: Array<unknown>) => ${body}`,
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
};
|
|
110
|
-
}
|