@7nohe/openapi-react-query-codegen 3.0.0-beta.4 → 3.0.0
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/cli.d.mts +18 -0
- package/dist/cli.mjs +1 -1
- package/dist/common.d.mts +51 -0
- package/dist/constants.d.mts +15 -0
- package/dist/createSource.d.mts +15 -0
- package/dist/format.d.mts +6 -0
- package/dist/generate.d.mts +3 -0
- package/dist/generate.mjs +51 -13
- package/dist/parseOperations.d.mts +10 -0
- package/dist/parseOperations.mjs +29 -6
- package/dist/print.d.mts +5 -0
- package/dist/service.d.mts +8 -0
- package/dist/tsmorph/buildCommon.d.mts +66 -0
- package/dist/tsmorph/buildKeys.d.mts +42 -0
- package/dist/tsmorph/buildMutationHooks.d.mts +15 -0
- package/dist/tsmorph/buildQueryHooks.d.mts +116 -0
- package/dist/tsmorph/buildQueryHooks.mjs +29 -14
- package/dist/tsmorph/buildQueryOptions.d.mts +28 -0
- package/dist/tsmorph/buildQueryOptions.mjs +3 -3
- package/dist/tsmorph/generateFiles.d.mts +5 -0
- package/dist/tsmorph/index.d.mts +5 -0
- package/dist/tsmorph/projectFactory.d.mts +45 -0
- package/dist/types.d.mts +66 -0
- package/dist/util.d.mts +2 -0
- package/package.json +1 -1
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export type LimitedUserConfig = {
|
|
3
|
+
input: string;
|
|
4
|
+
output: string;
|
|
5
|
+
client?: "@hey-api/client-fetch" | "@hey-api/client-axios";
|
|
6
|
+
format?: "biome" | "prettier";
|
|
7
|
+
lint?: "biome" | "eslint";
|
|
8
|
+
noOperationId?: boolean;
|
|
9
|
+
enums?: "javascript" | "typescript" | false;
|
|
10
|
+
useDateType?: boolean;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
noSchemas?: boolean;
|
|
13
|
+
schemaType?: "form" | "json";
|
|
14
|
+
pageParam: string;
|
|
15
|
+
nextPageParam: string;
|
|
16
|
+
initialPageParam: string | number;
|
|
17
|
+
omitInitialPageParam?: boolean;
|
|
18
|
+
};
|
package/dist/cli.mjs
CHANGED
|
@@ -25,7 +25,7 @@ async function setupProgram() {
|
|
|
25
25
|
.addOption(new Option("--lint <value>", "Process output folder with linter?").choices(["biome", "eslint"]))
|
|
26
26
|
.option("--noOperationId", "Do not use operationId to generate operation names")
|
|
27
27
|
.addOption(new Option("--enums <value>", "Generate JavaScript objects from enum definitions?").choices(["javascript", "typescript"]))
|
|
28
|
-
.option("--useDateType", "Use Date
|
|
28
|
+
.option("--useDateType", "Use Date for date/date-time model properties and convert response values to Date objects")
|
|
29
29
|
.option("--debug", "Run in debug mode?")
|
|
30
30
|
.option("--noSchemas", "Disable generating JSON schemas")
|
|
31
31
|
.addOption(new Option("--schemaType <value>", "Type of JSON schema [Default: 'json']").choices(["form", "json"]))
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { PathLike } from "node:fs";
|
|
2
|
+
import { type ClassDeclaration, type ParameterDeclaration, type SourceFile, type VariableDeclaration } from "ts-morph";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
import type { LimitedUserConfig } from "./cli.mjs";
|
|
5
|
+
export declare const capitalizeFirstLetter: (str: string) => string;
|
|
6
|
+
export declare const lowercaseFirstLetter: (str: string) => string;
|
|
7
|
+
export declare const getVariableArrowFunctionParameters: (variable: VariableDeclaration) => ParameterDeclaration[];
|
|
8
|
+
export declare const getNameFromVariable: (variable: VariableDeclaration) => string;
|
|
9
|
+
export type FunctionDescription = {
|
|
10
|
+
node: SourceFile;
|
|
11
|
+
method: VariableDeclaration;
|
|
12
|
+
methodBlock?: ts.Block;
|
|
13
|
+
httpMethodName: string;
|
|
14
|
+
jsDoc: string;
|
|
15
|
+
isDeprecated: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare function exists(f: PathLike): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Build a common type name by prepending the Common namespace.
|
|
20
|
+
*/
|
|
21
|
+
export declare function BuildCommonTypeName(name: string | ts.Identifier): ts.Identifier;
|
|
22
|
+
/**
|
|
23
|
+
* Safely parse a value into a number. Checks for NaN and Infinity.
|
|
24
|
+
* Returns NaN if the string is not a valid number.
|
|
25
|
+
* @param value The value to parse.
|
|
26
|
+
* @returns The parsed number or NaN if the value is not a valid number.
|
|
27
|
+
*/
|
|
28
|
+
export declare function safeParseNumber(value: unknown): number;
|
|
29
|
+
export declare function extractPropertiesFromObjectParam(param: ParameterDeclaration): {
|
|
30
|
+
name: string;
|
|
31
|
+
optional: boolean;
|
|
32
|
+
type: import("ts-morph").Type<import("ts-morph").ts.Type> | undefined;
|
|
33
|
+
}[];
|
|
34
|
+
/**
|
|
35
|
+
* Replace the import("...") surrounding the type if there is one.
|
|
36
|
+
* This can happen when the type is imported from another file, but
|
|
37
|
+
* we are already importing all the types from that file.
|
|
38
|
+
*
|
|
39
|
+
* https://regex101.com/r/3DyHaQ/1
|
|
40
|
+
*
|
|
41
|
+
* TODO: Replace with a more robust solution.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getShortType(type: string): string;
|
|
44
|
+
export declare function getClassesFromService(node: SourceFile): {
|
|
45
|
+
className: string;
|
|
46
|
+
klass: ClassDeclaration;
|
|
47
|
+
}[];
|
|
48
|
+
export declare function getClassNameFromClassNode(klass: ClassDeclaration): string;
|
|
49
|
+
export declare function formatOptions(options: LimitedUserConfig): LimitedUserConfig;
|
|
50
|
+
export declare function buildRequestsOutputPath(outputPath: string): string;
|
|
51
|
+
export declare function buildQueriesOutputPath(outputPath: string): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const defaultOutputPath = "openapi";
|
|
2
|
+
export declare const queriesOutputPath = "queries";
|
|
3
|
+
export declare const requestsOutputPath = "requests";
|
|
4
|
+
export declare const serviceFileName = "sdk.gen";
|
|
5
|
+
export declare const modelsFileName = "types.gen";
|
|
6
|
+
export declare const OpenApiRqFiles: {
|
|
7
|
+
readonly queries: "queries";
|
|
8
|
+
readonly queryOptions: "queryOptions";
|
|
9
|
+
readonly infiniteQueries: "infiniteQueries";
|
|
10
|
+
readonly common: "common";
|
|
11
|
+
readonly suspense: "suspense";
|
|
12
|
+
readonly index: "index";
|
|
13
|
+
readonly prefetch: "prefetch";
|
|
14
|
+
readonly ensureQueryData: "ensureQueryData";
|
|
15
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GeneratedFile } from "./types.mjs";
|
|
2
|
+
type ClientType = "@hey-api/client-fetch" | "@hey-api/client-axios";
|
|
3
|
+
/**
|
|
4
|
+
* Create source files using ts-morph based generation.
|
|
5
|
+
*/
|
|
6
|
+
export declare const createSource: ({ outputPath, client, version, pageParam, nextPageParam, initialPageParam, omitInitialPageParam, }: {
|
|
7
|
+
outputPath: string;
|
|
8
|
+
client: ClientType;
|
|
9
|
+
version: string;
|
|
10
|
+
pageParam: string;
|
|
11
|
+
nextPageParam: string;
|
|
12
|
+
initialPageParam: string;
|
|
13
|
+
omitInitialPageParam: boolean;
|
|
14
|
+
}) => Promise<GeneratedFile[]>;
|
|
15
|
+
export {};
|
package/dist/generate.mjs
CHANGED
|
@@ -1,10 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { createClient } from "@hey-api/openapi-ts";
|
|
4
5
|
import { buildQueriesOutputPath, buildRequestsOutputPath, formatOptions, } from "./common.mjs";
|
|
5
6
|
import { createSource } from "./createSource.mjs";
|
|
6
7
|
import { formatOutput, processOutput } from "./format.mjs";
|
|
7
8
|
import { print } from "./print.mjs";
|
|
9
|
+
// openapi-ts's own tsconfig auto-detection walks up from its *install*
|
|
10
|
+
// location, which in this monorepo reaches this repo's own root tsconfig
|
|
11
|
+
// (NodeNext) instead of the caller's project config. Anchoring the search
|
|
12
|
+
// at cwd instead finds the tsconfig that actually applies to the generated
|
|
13
|
+
// output, matching the resolution the caller's own `tsc` will use.
|
|
14
|
+
export function findNearestTsConfigPath(startDir) {
|
|
15
|
+
let dir = startDir;
|
|
16
|
+
while (true) {
|
|
17
|
+
const candidates = readdirSync(dir).filter((file) => file.startsWith("tsconfig") && file.endsWith(".json"));
|
|
18
|
+
if (candidates.length > 0) {
|
|
19
|
+
candidates.sort((a) => (a === "tsconfig.json" ? -1 : 1));
|
|
20
|
+
return path.join(dir, candidates[0]);
|
|
21
|
+
}
|
|
22
|
+
const parent = path.dirname(dir);
|
|
23
|
+
if (parent === dir)
|
|
24
|
+
return undefined;
|
|
25
|
+
dir = parent;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
8
28
|
export async function generate(options, version) {
|
|
9
29
|
const openApiOutputPath = buildRequestsOutputPath(options.output);
|
|
10
30
|
const formattedOptions = formatOptions(options);
|
|
@@ -18,20 +38,31 @@ export async function generate(options, version) {
|
|
|
18
38
|
enums: formattedOptions.enums,
|
|
19
39
|
}
|
|
20
40
|
: "@hey-api/typescript";
|
|
21
|
-
const sdkPlugin =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
const sdkPlugin = {
|
|
42
|
+
name: "@hey-api/sdk",
|
|
43
|
+
...(formattedOptions.noOperationId
|
|
44
|
+
? {
|
|
45
|
+
// `operationId: false` was deprecated in favor of `operations.nesting`
|
|
46
|
+
operations: {
|
|
47
|
+
nesting: "id",
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
: {}),
|
|
51
|
+
...(formattedOptions.useDateType
|
|
52
|
+
? { transformer: "@hey-api/transformers" }
|
|
53
|
+
: {}),
|
|
54
|
+
};
|
|
30
55
|
const plugins = [
|
|
31
56
|
clientPlugin,
|
|
32
57
|
typescriptPlugin,
|
|
33
58
|
sdkPlugin,
|
|
34
59
|
];
|
|
60
|
+
if (formattedOptions.useDateType) {
|
|
61
|
+
plugins.push({
|
|
62
|
+
name: "@hey-api/transformers",
|
|
63
|
+
dates: "date",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
35
66
|
// Conditionally add schemas plugin
|
|
36
67
|
if (!formattedOptions.noSchemas) {
|
|
37
68
|
plugins.push(formattedOptions.schemaType
|
|
@@ -44,13 +75,20 @@ export async function generate(options, version) {
|
|
|
44
75
|
const config = {
|
|
45
76
|
dryRun: false,
|
|
46
77
|
input: formattedOptions.input,
|
|
47
|
-
output:
|
|
78
|
+
output: {
|
|
79
|
+
path: openApiOutputPath,
|
|
80
|
+
tsConfigPath: findNearestTsConfigPath(process.cwd()) ?? null,
|
|
81
|
+
},
|
|
48
82
|
plugins,
|
|
49
83
|
};
|
|
50
84
|
await createClient(config);
|
|
51
85
|
// Generate backward-compatible services.gen.ts shim
|
|
52
|
-
// client.gen.ts has the `client` instance; sdk.gen.ts has SDK functions
|
|
53
|
-
|
|
86
|
+
// client.gen.ts has the `client` instance; sdk.gen.ts has SDK functions.
|
|
87
|
+
// Mirror whatever extension convention openapi-ts used for its own
|
|
88
|
+
// cross-file imports (e.g. `.js` for NodeNext, none for bundler resolution).
|
|
89
|
+
const sdkGenContent = await readFile(path.join(openApiOutputPath, "sdk.gen.ts"), "utf-8");
|
|
90
|
+
const importExtension = sdkGenContent.match(/from ['"]\.\/client\.gen(\.\S*)?['"]/)?.[1] ?? "";
|
|
91
|
+
const shimContent = `// This file is auto-generated for backward compatibility\nexport * from './client.gen${importExtension}';\nexport * from './sdk.gen${importExtension}';\n`;
|
|
54
92
|
await writeFile(path.join(openApiOutputPath, "services.gen.ts"), shimContent);
|
|
55
93
|
const source = await createSource({
|
|
56
94
|
outputPath: openApiOutputPath,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Project } from "ts-morph";
|
|
2
|
+
import type { GenerationContext, OperationInfo } from "./types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Parse operations from the OpenAPI-generated service file and return normalized DTOs.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseOperations(project: Project, pageParam: string): Promise<OperationInfo[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Build generation context from project configuration.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildGenerationContext(project: Project, client: GenerationContext["client"], pageParam: string, nextPageParam: string, initialPageParam: string, omitInitialPageParam: boolean, version: string): GenerationContext;
|
package/dist/parseOperations.mjs
CHANGED
|
@@ -2,6 +2,20 @@ import ts from "typescript";
|
|
|
2
2
|
import { capitalizeFirstLetter, extractPropertiesFromObjectParam, getNameFromVariable, getShortType, getVariableArrowFunctionParameters, } from "./common.mjs";
|
|
3
3
|
import { modelsFileName, serviceFileName } from "./constants.mjs";
|
|
4
4
|
import { getServices } from "./service.mjs";
|
|
5
|
+
function getPageParamTypeKind(type) {
|
|
6
|
+
const types = type.isUnion()
|
|
7
|
+
? type.types.filter((item) => !(item.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)))
|
|
8
|
+
: [type];
|
|
9
|
+
if (types.length > 0 &&
|
|
10
|
+
types.every((item) => item.flags & ts.TypeFlags.StringLike)) {
|
|
11
|
+
return "string";
|
|
12
|
+
}
|
|
13
|
+
if (types.length > 0 &&
|
|
14
|
+
types.every((item) => item.flags & ts.TypeFlags.NumberLike)) {
|
|
15
|
+
return "number";
|
|
16
|
+
}
|
|
17
|
+
return "other";
|
|
18
|
+
}
|
|
5
19
|
/**
|
|
6
20
|
* Extract parameter information from a method's variable declaration.
|
|
7
21
|
*/
|
|
@@ -30,8 +44,9 @@ function getPaginatableMethods(project, pageParam) {
|
|
|
30
44
|
.getSourceFiles()
|
|
31
45
|
.find((sf) => sf.getFilePath().includes(modelsFileName));
|
|
32
46
|
if (!modelsFile)
|
|
33
|
-
return
|
|
34
|
-
const paginatableMethods =
|
|
47
|
+
return new Map();
|
|
48
|
+
const paginatableMethods = new Map();
|
|
49
|
+
const typeChecker = project.getTypeChecker().compilerObject;
|
|
35
50
|
const modelDeclarations = modelsFile.getExportedDeclarations();
|
|
36
51
|
const entries = modelDeclarations.entries();
|
|
37
52
|
for (const [key, value] of entries) {
|
|
@@ -53,13 +68,18 @@ function getPaginatableMethods(project, pageParam) {
|
|
|
53
68
|
const queryType = query.type;
|
|
54
69
|
if (!queryType || queryType.kind !== ts.SyntaxKind.TypeLiteral)
|
|
55
70
|
continue;
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
71
|
+
const pageParamNode = queryType.members.find((m) => ts.isPropertySignature(m) && m.name?.getText() === pageParam);
|
|
72
|
+
if (pageParamNode) {
|
|
58
73
|
// Extract method name from Data type name (e.g., "FindPetsData" -> "findPets")
|
|
59
74
|
const methodName = key.slice(0, -4); // Remove "Data" suffix
|
|
60
75
|
// Convert first letter to lowercase
|
|
61
76
|
const methodNameLower = methodName.charAt(0).toLowerCase() + methodName.slice(1);
|
|
62
|
-
|
|
77
|
+
const pageParamType = pageParamNode.type?.getText(modelsFile.compilerNode);
|
|
78
|
+
const resolvedType = typeChecker.getTypeAtLocation(pageParamNode.type ?? pageParamNode);
|
|
79
|
+
paginatableMethods.set(methodNameLower, {
|
|
80
|
+
type: pageParamType ?? "unknown",
|
|
81
|
+
typeKind: getPageParamTypeKind(resolvedType),
|
|
82
|
+
});
|
|
63
83
|
}
|
|
64
84
|
}
|
|
65
85
|
return paginatableMethods;
|
|
@@ -80,7 +100,8 @@ export async function parseOperations(project, pageParam) {
|
|
|
80
100
|
// via extractPropertiesFromObjectParam for type alias properties (path, url).
|
|
81
101
|
const sdkParams = getVariableArrowFunctionParameters(desc.method);
|
|
82
102
|
const allParamsOptional = sdkParams.length === 0 || sdkParams[0].isOptional();
|
|
83
|
-
const
|
|
103
|
+
const pageParamInfo = paginatableMethods.get(methodName);
|
|
104
|
+
const isPaginatable = httpMethod === "GET" && pageParamInfo !== undefined;
|
|
84
105
|
return {
|
|
85
106
|
methodName,
|
|
86
107
|
capitalizedMethodName: capitalizeFirstLetter(methodName),
|
|
@@ -90,6 +111,8 @@ export async function parseOperations(project, pageParam) {
|
|
|
90
111
|
parameters,
|
|
91
112
|
allParamsOptional,
|
|
92
113
|
isPaginatable,
|
|
114
|
+
pageParamType: isPaginatable ? pageParamInfo.type : undefined,
|
|
115
|
+
pageParamTypeKind: isPaginatable ? pageParamInfo.typeKind : undefined,
|
|
93
116
|
};
|
|
94
117
|
});
|
|
95
118
|
}
|
package/dist/print.d.mts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Project, SourceFile } from "ts-morph";
|
|
2
|
+
import type { FunctionDescription } from "./common.mjs";
|
|
3
|
+
export type Service = {
|
|
4
|
+
node: SourceFile;
|
|
5
|
+
methods: Array<FunctionDescription>;
|
|
6
|
+
};
|
|
7
|
+
export declare function getServices(project: Project): Promise<Service>;
|
|
8
|
+
export declare function getMethodsFromService(node: SourceFile): FunctionDescription[];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type TypeAliasDeclarationStructure, type VariableStatementStructure } from "ts-morph";
|
|
2
|
+
import type { GenerationContext, OperationInfo } from "../types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Build the default response type alias.
|
|
5
|
+
* Example: export type FindPetsDefaultResponse = Awaited<ReturnType<typeof findPets>>["data"];
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildDefaultResponseType(op: OperationInfo): TypeAliasDeclarationStructure;
|
|
8
|
+
/**
|
|
9
|
+
* Build the query result type alias.
|
|
10
|
+
* Example: export type FindPetsQueryResult<TData = FindPetsDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>;
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildQueryResultType(op: OperationInfo): TypeAliasDeclarationStructure;
|
|
13
|
+
/**
|
|
14
|
+
* Build the mutation result type alias.
|
|
15
|
+
* Example: export type AddPetMutationResult = Awaited<ReturnType<typeof addPet>>;
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildMutationResultType(op: OperationInfo): TypeAliasDeclarationStructure;
|
|
18
|
+
/**
|
|
19
|
+
* Build query key constant.
|
|
20
|
+
* Example: export const useFindPetsKey = "FindPets";
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildQueryKeyConst(op: OperationInfo): VariableStatementStructure;
|
|
23
|
+
/**
|
|
24
|
+
* Build mutation key constant.
|
|
25
|
+
* Example: export const useAddPetKey = "AddPet";
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildMutationKeyConst(op: OperationInfo): VariableStatementStructure;
|
|
28
|
+
/**
|
|
29
|
+
* Build query key function.
|
|
30
|
+
* Example: export const UseFindPetsKeyFn = (clientOptions: Options<FindPetsData, true> = {}, queryKey?: Array<unknown>) =>
|
|
31
|
+
* [useFindPetsKey, ...(queryKey ?? [clientOptions])];
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildQueryKeyFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
|
|
34
|
+
/**
|
|
35
|
+
* Build mutation key function.
|
|
36
|
+
* Example: export const UseAddPetKeyFn = (mutationKey?: Array<unknown>) =>
|
|
37
|
+
* [useAddPetKey, ...(mutationKey ?? [])];
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildMutationKeyFn(op: OperationInfo): VariableStatementStructure;
|
|
40
|
+
/**
|
|
41
|
+
* Build the client options type for infinite queries.
|
|
42
|
+
* The page parameter is excluded because TanStack Query supplies it via the
|
|
43
|
+
* pageParam mechanism (#140).
|
|
44
|
+
* Example:
|
|
45
|
+
* export type FindPaginatedPetsInfiniteClientOptions = Omit<Options<FindPaginatedPetsData, true>, "query"> &
|
|
46
|
+
* { query?: Omit<NonNullable<FindPaginatedPetsData["query"]>, "page"> };
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildInfiniteClientOptionsType(op: OperationInfo, ctx: GenerationContext): TypeAliasDeclarationStructure;
|
|
49
|
+
/**
|
|
50
|
+
* Build the infinite query key constant.
|
|
51
|
+
* Shares the plain query key as its first segment so a single
|
|
52
|
+
* `invalidateQueries({ queryKey: [useXKey] })` matches both the plain and the
|
|
53
|
+
* infinite cache entries of an operation (#174), while the extra "infinite"
|
|
54
|
+
* segment keeps cached InfiniteData from colliding with plain query data (#140).
|
|
55
|
+
* Example: export const useFindPaginatedPetsInfiniteKey = [useFindPaginatedPetsKey, "infinite"] as const;
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildInfiniteQueryKeyConst(op: OperationInfo): VariableStatementStructure;
|
|
58
|
+
/**
|
|
59
|
+
* Build the infinite query key function.
|
|
60
|
+
* The custom queryKey argument only replaces the params segment — the
|
|
61
|
+
* hierarchical [opKey, "infinite"] prefix is always preserved so
|
|
62
|
+
* prefix-based invalidation keeps working.
|
|
63
|
+
* Example: export const UseFindPaginatedPetsInfiniteKeyFn = (clientOptions: FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) =>
|
|
64
|
+
* [...useFindPaginatedPetsInfiniteKey, ...(queryKey ?? [clientOptions])];
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildInfiniteQueryKeyFn(op: OperationInfo): VariableStatementStructure;
|
|
@@ -0,0 +1,42 @@
|
|
|
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;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type VariableStatementStructure } from "ts-morph";
|
|
2
|
+
import type { GenerationContext, OperationInfo } from "../types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Build useMutation hook.
|
|
5
|
+
* Example:
|
|
6
|
+
* export const useAddPet = <TData = Common.AddPetMutationResult, TError = AddPetError, TQueryKey extends Array<unknown> = unknown[], TContext = unknown>(
|
|
7
|
+
* mutationKey?: TQueryKey,
|
|
8
|
+
* options?: Omit<UseMutationOptions<TData, TError, Options<AddPetData, true>, TContext>, "mutationKey" | "mutationFn">
|
|
9
|
+
* ) => useMutation<TData, TError, Options<AddPetData, true>, TContext>({
|
|
10
|
+
* mutationKey: Common.UseAddPetKeyFn(mutationKey),
|
|
11
|
+
* mutationFn: clientOptions => addPet(clientOptions) as unknown as Promise<TData>,
|
|
12
|
+
* ...options
|
|
13
|
+
* });
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildUseMutationHook(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type VariableStatementStructure } from "ts-morph";
|
|
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
|
+
/**
|
|
9
|
+
* SDK call arguments shared by every generated queryFn/mutationFn.
|
|
10
|
+
* throwOnError: true forces the SDK call to reject on error responses; the
|
|
11
|
+
* hey-api runtime default is false, which would resolve undefined data and
|
|
12
|
+
* swallow the error instead of surfacing it to TanStack Query (#172).
|
|
13
|
+
*/
|
|
14
|
+
export declare const SDK_CALL_ARGS = "{ ...clientOptions, throwOnError: true }";
|
|
15
|
+
/** SDK call arguments for TanStack query functions with cancellation. */
|
|
16
|
+
export declare const QUERY_SDK_CALL_ARGS = "{ ...clientOptions, signal, throwOnError: true }";
|
|
17
|
+
/** Resolve the OpenAPI page parameter type, preserving older numeric output. */
|
|
18
|
+
export declare function getPageParamType(op: OperationInfo): string;
|
|
19
|
+
/**
|
|
20
|
+
* Build the client options parameter string.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildClientOptionsParam(op: OperationInfo, ctx: GenerationContext): string;
|
|
23
|
+
/**
|
|
24
|
+
* Build the clientOptions parameter typed with the page-less infinite
|
|
25
|
+
* options type — the page parameter is supplied by TanStack Query's
|
|
26
|
+
* pageParam mechanism.
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildInfiniteClientOptionsParam(op: OperationInfo): string;
|
|
29
|
+
/**
|
|
30
|
+
* Build the paginated SDK call shared by every infinite query builder.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildPagedQueryFn(op: OperationInfo, ctx: GenerationContext, castTData: boolean): string;
|
|
33
|
+
/**
|
|
34
|
+
* Format the initialPageParam literal. Emits `undefined` when the caller opted
|
|
35
|
+
* to omit it (#177); otherwise a numeric literal when possible so the inferred
|
|
36
|
+
* pageParam type matches what getNextPageParam returns.
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatInitialPageParam(ctx: GenerationContext, op?: OperationInfo): string;
|
|
39
|
+
/**
|
|
40
|
+
* Build the nested type for getNextPageParam.
|
|
41
|
+
* E.g., "meta.next" becomes "{ meta: { next: number } }"
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildNestedNextPageType(nextPageParam: string, pageParamType?: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Build the getNextPageParam expression. The parameter is annotated because
|
|
46
|
+
* not every TanStack entry point contextually types it (prefetchInfiniteQuery
|
|
47
|
+
* does not, which would fail noImplicitAny).
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildGetNextPageParamExpr(ctx: GenerationContext, op?: OperationInfo): string;
|
|
50
|
+
/**
|
|
51
|
+
* Build an options type where the pagination fields TanStack Query marks as
|
|
52
|
+
* required become optional overrides: the generator supplies them, and
|
|
53
|
+
* callers may replace them for custom pagination schemes (#156, #146).
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildOverridableInfiniteOptionsType(optionsTypeName: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Build useQuery hook.
|
|
58
|
+
* Example:
|
|
59
|
+
* export const useFindPets = <TData = Common.FindPetsDefaultResponse, TError = FindPetsError, TQueryKey extends Array<unknown> = unknown[]>(
|
|
60
|
+
* clientOptions: Options<FindPetsData, true> = {},
|
|
61
|
+
* queryKey?: TQueryKey,
|
|
62
|
+
* options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">
|
|
63
|
+
* ) => useQuery<TData, TError>({
|
|
64
|
+
* queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey),
|
|
65
|
+
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data as TData) as TData,
|
|
66
|
+
* ...options
|
|
67
|
+
* });
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildUseQueryHook(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
|
|
70
|
+
/**
|
|
71
|
+
* Build useSuspenseQuery hook.
|
|
72
|
+
*/
|
|
73
|
+
export declare function buildUseSuspenseQueryHook(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
|
|
74
|
+
/**
|
|
75
|
+
* Build useInfiniteQuery hook.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildUseInfiniteQueryHook(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure | null;
|
|
78
|
+
/**
|
|
79
|
+
* Build useSuspenseInfiniteQuery hook.
|
|
80
|
+
*/
|
|
81
|
+
export declare function buildUseSuspenseInfiniteQueryHook(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure | null;
|
|
82
|
+
/**
|
|
83
|
+
* Build prefetch function.
|
|
84
|
+
* Example:
|
|
85
|
+
* export const prefetchUseFindPets = (queryClient: QueryClient, clientOptions: Options<FindPetsData, true> = {}, options?: Omit<FetchQueryOptions<Common.FindPetsDefaultResponse>, "queryKey" | "queryFn">) =>
|
|
86
|
+
* queryClient.prefetchQuery({
|
|
87
|
+
* queryKey: Common.UseFindPetsKeyFn(clientOptions),
|
|
88
|
+
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data),
|
|
89
|
+
* ...options
|
|
90
|
+
* });
|
|
91
|
+
*/
|
|
92
|
+
export declare function buildPrefetchFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
|
|
93
|
+
/**
|
|
94
|
+
* Build prefetchInfiniteQuery function for a paginatable operation.
|
|
95
|
+
* Example:
|
|
96
|
+
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<Common.FindPaginatedPetsDefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">) =>
|
|
97
|
+
* queryClient.prefetchInfiniteQuery({
|
|
98
|
+
* 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),
|
|
100
|
+
* initialPageParam: 1,
|
|
101
|
+
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
|
|
102
|
+
* ...options
|
|
103
|
+
* });
|
|
104
|
+
*/
|
|
105
|
+
export declare function buildPrefetchInfiniteQueryFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure | null;
|
|
106
|
+
/**
|
|
107
|
+
* Build ensureQueryData function.
|
|
108
|
+
* Example:
|
|
109
|
+
* export const ensureUseFindPetsData = (queryClient: QueryClient, clientOptions: Options<FindPetsData, true> = {}, options?: Omit<EnsureQueryDataOptions<Common.FindPetsDefaultResponse>, "queryKey" | "queryFn">) =>
|
|
110
|
+
* queryClient.ensureQueryData({
|
|
111
|
+
* queryKey: Common.UseFindPetsKeyFn(clientOptions),
|
|
112
|
+
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data),
|
|
113
|
+
* ...options
|
|
114
|
+
* });
|
|
115
|
+
*/
|
|
116
|
+
export declare function buildEnsureQueryDataFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
|
|
@@ -29,6 +29,12 @@ export function getDataTypeName(op, ctx) {
|
|
|
29
29
|
* swallow the error instead of surfacing it to TanStack Query (#172).
|
|
30
30
|
*/
|
|
31
31
|
export const SDK_CALL_ARGS = "{ ...clientOptions, throwOnError: true }";
|
|
32
|
+
/** SDK call arguments for TanStack query functions with cancellation. */
|
|
33
|
+
export const QUERY_SDK_CALL_ARGS = "{ ...clientOptions, signal, throwOnError: true }";
|
|
34
|
+
/** Resolve the OpenAPI page parameter type, preserving older numeric output. */
|
|
35
|
+
export function getPageParamType(op) {
|
|
36
|
+
return op.pageParamType ?? "number";
|
|
37
|
+
}
|
|
32
38
|
/**
|
|
33
39
|
* Build the client options parameter string.
|
|
34
40
|
*/
|
|
@@ -58,22 +64,31 @@ export function buildPagedQueryFn(op, ctx, castTData) {
|
|
|
58
64
|
const thenClause = castTData
|
|
59
65
|
? ".then(response => response.data as TData) as TData"
|
|
60
66
|
: ".then(response => response.data)";
|
|
67
|
+
const pageParamType = getPageParamType(op);
|
|
61
68
|
// When the initial page param is omitted, the first request must send no
|
|
62
69
|
// page param at all, so spread it in only once TanStack Query provides one.
|
|
63
70
|
const pageQuery = ctx.omitInitialPageParam
|
|
64
|
-
? `...(pageParam === undefined ? {} : { ${ctx.pageParam}: pageParam as
|
|
65
|
-
: `${ctx.pageParam}: pageParam as
|
|
66
|
-
return `({ pageParam }) => ${op.methodName}({ ...clientOptions, query: { ...clientOptions.query, ${pageQuery} }, throwOnError: true } as Options<${dataTypeName}, true>)${thenClause}`;
|
|
71
|
+
? `...(pageParam === undefined ? {} : { ${ctx.pageParam}: pageParam as ${pageParamType} })`
|
|
72
|
+
: `${ctx.pageParam}: pageParam as ${pageParamType}`;
|
|
73
|
+
return `({ pageParam, signal }) => ${op.methodName}({ ...clientOptions, query: { ...clientOptions.query, ${pageQuery} }, signal, throwOnError: true } as Options<${dataTypeName}, true>)${thenClause}`;
|
|
67
74
|
}
|
|
68
75
|
/**
|
|
69
76
|
* Format the initialPageParam literal. Emits `undefined` when the caller opted
|
|
70
77
|
* to omit it (#177); otherwise a numeric literal when possible so the inferred
|
|
71
78
|
* pageParam type matches what getNextPageParam returns.
|
|
72
79
|
*/
|
|
73
|
-
export function formatInitialPageParam(ctx) {
|
|
80
|
+
export function formatInitialPageParam(ctx, op) {
|
|
74
81
|
if (ctx.omitInitialPageParam) {
|
|
75
82
|
return "undefined";
|
|
76
83
|
}
|
|
84
|
+
const isStringPageParam = op
|
|
85
|
+
? op.pageParamTypeKind === "string" ||
|
|
86
|
+
(op.pageParamTypeKind === undefined &&
|
|
87
|
+
/\bstring\b/.test(getPageParamType(op)))
|
|
88
|
+
: false;
|
|
89
|
+
if (isStringPageParam) {
|
|
90
|
+
return JSON.stringify(ctx.initialPageParam);
|
|
91
|
+
}
|
|
77
92
|
return /^-?\d+$/.test(ctx.initialPageParam)
|
|
78
93
|
? ctx.initialPageParam
|
|
79
94
|
: JSON.stringify(ctx.initialPageParam);
|
|
@@ -82,19 +97,19 @@ export function formatInitialPageParam(ctx) {
|
|
|
82
97
|
* Build the nested type for getNextPageParam.
|
|
83
98
|
* E.g., "meta.next" becomes "{ meta: { next: number } }"
|
|
84
99
|
*/
|
|
85
|
-
export function buildNestedNextPageType(nextPageParam) {
|
|
100
|
+
export function buildNestedNextPageType(nextPageParam, pageParamType = "number") {
|
|
86
101
|
const segments = nextPageParam.split(".");
|
|
87
102
|
return segments.reduceRight((acc, segment) => {
|
|
88
103
|
return `{ ${segment}: ${acc} }`;
|
|
89
|
-
},
|
|
104
|
+
}, pageParamType);
|
|
90
105
|
}
|
|
91
106
|
/**
|
|
92
107
|
* Build the getNextPageParam expression. The parameter is annotated because
|
|
93
108
|
* not every TanStack entry point contextually types it (prefetchInfiniteQuery
|
|
94
109
|
* does not, which would fail noImplicitAny).
|
|
95
110
|
*/
|
|
96
|
-
export function buildGetNextPageParamExpr(ctx) {
|
|
97
|
-
const nestedType = buildNestedNextPageType(ctx.nextPageParam);
|
|
111
|
+
export function buildGetNextPageParamExpr(ctx, op) {
|
|
112
|
+
const nestedType = buildNestedNextPageType(ctx.nextPageParam, op ? getPageParamType(op) : "number");
|
|
98
113
|
return `(response: unknown) => (response as ${nestedType}).${ctx.nextPageParam}`;
|
|
99
114
|
}
|
|
100
115
|
/**
|
|
@@ -124,7 +139,7 @@ export function buildUseQueryHook(op, ctx) {
|
|
|
124
139
|
const errorType = getErrorType(op, ctx);
|
|
125
140
|
const dataTypeDefault = `Common.${op.capitalizedMethodName}DefaultResponse`;
|
|
126
141
|
const clientOptionsParam = buildClientOptionsParam(op, ctx);
|
|
127
|
-
const queryFn = `() => ${op.methodName}(${
|
|
142
|
+
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data as TData) as TData`;
|
|
128
143
|
const body = `useQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`;
|
|
129
144
|
return {
|
|
130
145
|
kind: StructureKind.VariableStatement,
|
|
@@ -148,7 +163,7 @@ export function buildUseSuspenseQueryHook(op, ctx) {
|
|
|
148
163
|
const errorType = getErrorType(op, ctx);
|
|
149
164
|
const dataTypeDefault = `NonNullable<Common.${op.capitalizedMethodName}DefaultResponse>`;
|
|
150
165
|
const clientOptionsParam = buildClientOptionsParam(op, ctx);
|
|
151
|
-
const queryFn = `() => ${op.methodName}(${
|
|
166
|
+
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data as TData) as TData`;
|
|
152
167
|
const body = `useSuspenseQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`;
|
|
153
168
|
return {
|
|
154
169
|
kind: StructureKind.VariableStatement,
|
|
@@ -187,7 +202,7 @@ function buildInfiniteHook(op, ctx, suspense) {
|
|
|
187
202
|
? `InfiniteData<NonNullable<${baseDataType}>>`
|
|
188
203
|
: `InfiniteData<${baseDataType}>`;
|
|
189
204
|
const queryFn = buildPagedQueryFn(op, ctx, true);
|
|
190
|
-
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx)}`;
|
|
205
|
+
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;
|
|
191
206
|
const body = `${hookCall}({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
192
207
|
return {
|
|
193
208
|
kind: StructureKind.VariableStatement,
|
|
@@ -227,7 +242,7 @@ export function buildUseSuspenseInfiniteQueryHook(op, ctx) {
|
|
|
227
242
|
*/
|
|
228
243
|
export function buildPrefetchFn(op, ctx) {
|
|
229
244
|
const fnName = `prefetchUse${op.capitalizedMethodName}`;
|
|
230
|
-
const queryFn = `() => ${op.methodName}(${
|
|
245
|
+
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`;
|
|
231
246
|
const optionsParam = `options?: Omit<FetchQueryOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn">`;
|
|
232
247
|
const body = `queryClient.prefetchQuery({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions), queryFn: ${queryFn}, ...options })`;
|
|
233
248
|
return {
|
|
@@ -262,7 +277,7 @@ export function buildPrefetchInfiniteQueryFn(op, ctx) {
|
|
|
262
277
|
}
|
|
263
278
|
const fnName = `prefetchUse${op.capitalizedMethodName}Infinite`;
|
|
264
279
|
const queryFn = buildPagedQueryFn(op, ctx, false);
|
|
265
|
-
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx)}`;
|
|
280
|
+
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;
|
|
266
281
|
const optionsParam = `options?: Omit<FetchInfiniteQueryOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">`;
|
|
267
282
|
const body = `queryClient.prefetchInfiniteQuery({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
|
|
268
283
|
return {
|
|
@@ -291,7 +306,7 @@ export function buildPrefetchInfiniteQueryFn(op, ctx) {
|
|
|
291
306
|
*/
|
|
292
307
|
export function buildEnsureQueryDataFn(op, ctx) {
|
|
293
308
|
const fnName = `ensureUse${op.capitalizedMethodName}Data`;
|
|
294
|
-
const queryFn = `() => ${op.methodName}(${
|
|
309
|
+
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`;
|
|
295
310
|
const optionsParam = `options?: Omit<EnsureQueryDataOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn">`;
|
|
296
311
|
const body = `queryClient.ensureQueryData({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions), queryFn: ${queryFn}, ...options })`;
|
|
297
312
|
return {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type VariableStatementStructure } from "ts-morph";
|
|
2
|
+
import type { GenerationContext, OperationInfo } from "../types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Build a queryOptions factory for a GET operation.
|
|
5
|
+
* The factory centralizes queryKey and queryFn so they can be reused with
|
|
6
|
+
* every TanStack Query utility (useQuery, useQueries, prefetchQuery,
|
|
7
|
+
* ensureQueryData, setQueryData, ...) with full type safety.
|
|
8
|
+
* Example:
|
|
9
|
+
* export const findPetsOptions = (clientOptions: Options<FindPetsData, true> = {}, queryKey?: Array<unknown>) =>
|
|
10
|
+
* queryOptions({
|
|
11
|
+
* queryKey: Common.UseFindPetsKeyFn(clientOptions, queryKey),
|
|
12
|
+
* queryFn: () => findPets({ ...clientOptions, throwOnError: true }).then(response => response.data),
|
|
13
|
+
* });
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildQueryOptionsFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
|
|
16
|
+
/**
|
|
17
|
+
* Build an infiniteQueryOptions factory for a paginatable GET operation.
|
|
18
|
+
* Uses the dedicated infinite query key and page-less options type.
|
|
19
|
+
* Example:
|
|
20
|
+
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) =>
|
|
21
|
+
* infiniteQueryOptions({
|
|
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),
|
|
24
|
+
* initialPageParam: 1,
|
|
25
|
+
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
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,
|
|
2
|
+
import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClientOptionsParam, buildPagedQueryFn, formatInitialPageParam, 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
|
|
@@ -15,7 +15,7 @@ import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClient
|
|
|
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 = `({ 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 {
|
|
21
21
|
kind: StructureKind.VariableStatement,
|
|
@@ -49,7 +49,7 @@ export function buildInfiniteQueryOptionsFn(op, ctx) {
|
|
|
49
49
|
}
|
|
50
50
|
const fnName = `${op.methodName}InfiniteOptions`;
|
|
51
51
|
const queryFn = buildPagedQueryFn(op, ctx, false);
|
|
52
|
-
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx)}`;
|
|
52
|
+
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;
|
|
53
53
|
const body = `infiniteQueryOptions({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions} })`;
|
|
54
54
|
return {
|
|
55
55
|
kind: StructureKind.VariableStatement,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ImportDeclarationStructure, Project } from "ts-morph";
|
|
2
|
+
import type { GenerationContext } from "../types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Create a shared ts-morph Project for code generation.
|
|
5
|
+
* Uses consistent formatting settings to match existing output.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createGenerationProject(): Project;
|
|
8
|
+
/**
|
|
9
|
+
* Build import structure for the Options type.
|
|
10
|
+
* sdk.gen re-exports Options extended with `client` and `meta`, which the
|
|
11
|
+
* base client Options lacks; hooks must accept those properties.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildClientImport(_ctx: GenerationContext): ImportDeclarationStructure;
|
|
14
|
+
/**
|
|
15
|
+
* Build import structure for TanStack Query.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildQueryImport(): ImportDeclarationStructure;
|
|
18
|
+
/**
|
|
19
|
+
* Build import structure for the queryOptions/infiniteQueryOptions helpers.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildQueryOptionsImport(): ImportDeclarationStructure;
|
|
22
|
+
/**
|
|
23
|
+
* Build import structure for services.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildServiceImport(ctx: GenerationContext): ImportDeclarationStructure;
|
|
26
|
+
/**
|
|
27
|
+
* Build import structure for models.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildModelImport(ctx: GenerationContext): ImportDeclarationStructure | null;
|
|
30
|
+
/**
|
|
31
|
+
* Build import structure for axios error type.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildAxiosErrorImport(): ImportDeclarationStructure;
|
|
34
|
+
/**
|
|
35
|
+
* Build import for Common namespace.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildCommonImport(): ImportDeclarationStructure;
|
|
38
|
+
/**
|
|
39
|
+
* Build all imports needed for the common file.
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildCommonFileImports(ctx: GenerationContext): ImportDeclarationStructure[];
|
|
42
|
+
/**
|
|
43
|
+
* Build all imports needed for hook files (queries, suspense, infinite).
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildHookFileImports(ctx: GenerationContext): ImportDeclarationStructure[];
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized operation information extracted from the OpenAPI service.
|
|
3
|
+
* This is a pure JSON-serializable structure that can be consumed by generators.
|
|
4
|
+
*/
|
|
5
|
+
export interface OperationInfo {
|
|
6
|
+
/** Method/function name as defined in service (e.g., "findPets") */
|
|
7
|
+
methodName: string;
|
|
8
|
+
/** Capitalized method name (e.g., "FindPets") */
|
|
9
|
+
capitalizedMethodName: string;
|
|
10
|
+
/** HTTP method (e.g., "GET", "POST", "PUT", "PATCH", "DELETE") */
|
|
11
|
+
httpMethod: string;
|
|
12
|
+
/** JSDoc comment string (if present) */
|
|
13
|
+
jsDoc?: string;
|
|
14
|
+
/** Whether the operation is deprecated */
|
|
15
|
+
isDeprecated: boolean;
|
|
16
|
+
/** Parameter information for the operation */
|
|
17
|
+
parameters: OperationParameter[];
|
|
18
|
+
/** Whether all parameters are optional */
|
|
19
|
+
allParamsOptional: boolean;
|
|
20
|
+
/** Whether this operation supports pagination (for infinite queries) */
|
|
21
|
+
isPaginatable: boolean;
|
|
22
|
+
/** Type of the configured page query parameter */
|
|
23
|
+
pageParamType?: string;
|
|
24
|
+
/** Resolved primitive kind of the configured page query parameter */
|
|
25
|
+
pageParamTypeKind?: PageParamTypeKind;
|
|
26
|
+
}
|
|
27
|
+
export type PageParamTypeKind = "string" | "number" | "other";
|
|
28
|
+
export interface OperationParameter {
|
|
29
|
+
/** Parameter name */
|
|
30
|
+
name: string;
|
|
31
|
+
/** TypeScript type as string */
|
|
32
|
+
typeName: string;
|
|
33
|
+
/** Whether this parameter is optional */
|
|
34
|
+
optional: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Context for generating hooks and utilities.
|
|
38
|
+
* Contains shared information needed across all generators.
|
|
39
|
+
*/
|
|
40
|
+
export interface GenerationContext {
|
|
41
|
+
/** Client type: "@hey-api/client-fetch" or "@hey-api/client-axios" */
|
|
42
|
+
client: "@hey-api/client-fetch" | "@hey-api/client-axios";
|
|
43
|
+
/** Model type names exported from the models file */
|
|
44
|
+
modelNames: string[];
|
|
45
|
+
/** Service function names exported from the service file */
|
|
46
|
+
serviceNames: string[];
|
|
47
|
+
/** Page param name for infinite queries (e.g., "page") */
|
|
48
|
+
pageParam: string;
|
|
49
|
+
/** Next page param name for infinite queries (e.g., "nextPage") */
|
|
50
|
+
nextPageParam: string;
|
|
51
|
+
/** Initial page param value for infinite queries */
|
|
52
|
+
initialPageParam: string;
|
|
53
|
+
/** Omit the initial page param entirely (sends no page param on the first request) */
|
|
54
|
+
omitInitialPageParam: boolean;
|
|
55
|
+
/** Package version for generated comment */
|
|
56
|
+
version: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generated output for a single file.
|
|
60
|
+
*/
|
|
61
|
+
export interface GeneratedFile {
|
|
62
|
+
/** Filename without path (e.g., "queries.ts") */
|
|
63
|
+
name: string;
|
|
64
|
+
/** File content as string */
|
|
65
|
+
content: string;
|
|
66
|
+
}
|
package/dist/util.d.mts
ADDED