@7nohe/openapi-react-query-codegen 0.5.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  - Supports generation of custom react hooks that use React Query's `useQuery` and `useMutation` hooks
8
8
  - Supports generation of query keys for query caching
9
- - Supports the option to use pure TypeScript clients generated by [OpenAPI Typescript Codegen](https://github.com/ferdikoomen/openapi-typescript-codegen)
9
+ - Supports the option to use pure TypeScript clients generated by [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts)
10
10
 
11
11
  ## Install
12
12
 
@@ -14,7 +14,7 @@
14
14
  $ npm install -D @7nohe/openapi-react-query-codegen
15
15
  ```
16
16
 
17
- Register the command to the `scripts` property in your package.json file.
17
+ Register the command to the `scripts` property in your package.json file.
18
18
 
19
19
  ```json
20
20
  {
@@ -30,7 +30,6 @@ You can also run the command without installing it in your project using the npx
30
30
  $ npx --package @7nohe/openapi-react-query-codegen openapi-rq -i ./petstore.yaml -c axios
31
31
  ```
32
32
 
33
-
34
33
  ## Usage
35
34
 
36
35
  ```
@@ -45,12 +44,14 @@ Options:
45
44
  -i, --input <value> OpenAPI specification, can be a path, url or string content (required)
46
45
  -o, --output <value> Output directory (default: "openapi")
47
46
  -c, --client <value> HTTP client to generate [fetch, xhr, node, axios, angular] (default: "fetch")
48
- --useUnionTypes Use union types (default: false)
49
- --exportSchemas <value> Write schemas to disk (default: false)
50
- --indent <value> Indentation options [4, 2, tabs] (default: "4")
51
- --postfixServices <value> Service name postfix (default: "Service")
52
- --postfixModels <value> Modal name postfix
53
47
  --request <value> Path to custom request file
48
+ --useDateType Use Date type instead of string for date types for models, this will not convert the data to a Date object
49
+ --enums Generate JavaScript objects from enum definitions?
50
+ --base <value> Manually set base in OpenAPI config instead of inferring from server value
51
+ --serviceResponse <value> Define shape of returned value from service calls ['body', 'generics', 'response']
52
+ --operationId Use operation ID to generate operation names?
53
+ --lint Process output folder with linter?
54
+ --format Process output folder with formatter?
54
55
  -h, --help display help for command
55
56
  ```
56
57
 
@@ -67,17 +68,18 @@ $ openapi-rq -i ./petstore.yaml
67
68
  ```
68
69
  - openapi
69
70
  - queries
70
- - index.ts <- custom react hooks
71
- - requests <- output code generated by OpenAPI Typescript Codegen
71
+ - index.ts <- main file that exports common types, variables, and hooks
72
+ - common.ts <- common types
73
+ - queries.ts <- generated query hooks
74
+ - suspenses.ts <- generated suspense hooks
75
+ - requests <- output code generated by @hey-api/openapi-ts
72
76
  ```
73
77
 
74
78
  ### In your app
75
79
 
76
80
  ```tsx
77
81
  // App.tsx
78
- import {
79
- usePetServiceFindPetsByStatus,
80
- } from "../openapi/queries";
82
+ import { usePetServiceFindPetsByStatus } from "../openapi/queries";
81
83
  function App() {
82
84
  const { data } = usePetServiceFindPetsByStatus({ status: ["available"] });
83
85
 
@@ -100,22 +102,57 @@ You can also use pure TS clients.
100
102
 
101
103
  ```tsx
102
104
  import { useQuery } from "@tanstack/react-query";
103
- import { PetService } from '../openapi/requests/services/PetService';
104
- import {
105
- usePetServiceFindPetsByStatusKey,
106
- } from "../openapi/queries";
105
+ import { PetService } from "../openapi/requests/services";
106
+ import { usePetServiceFindPetsByStatusKey } from "../openapi/queries";
107
107
 
108
108
  function App() {
109
109
  // You can still use the auto-generated query key
110
- const { data } = useQuery([usePetServiceFindPetsByStatusKey], () => {
111
- // Do something here
112
-
113
- return PetService.findPetsByStatus(['available']);
110
+ const { data } = useQuery({
111
+ queryKey: [usePetServiceFindPetsByStatusKey],
112
+ queryFn: () => {
113
+ // Do something here
114
+ return PetService.findPetsByStatus(["available"]);
115
+ },
114
116
  });
115
117
 
118
+ return <div className="App">{/* .... */}</div>;
119
+ }
120
+
121
+ export default App;
122
+ ```
123
+
124
+ You can also use suspense hooks.
125
+
126
+ ```tsx
127
+ // App.tsx
128
+ import { useDefaultClientFindPetsSuspense } from "../openapi/queries/suspense";
129
+ function ChildComponent() {
130
+ const { data } = useDefaultClientFindPetsSuspense({ tags: [], limit: 10 });
131
+
132
+ return (
133
+ <ul>
134
+ {data?.map((pet, index) => (
135
+ <li key={pet.id}>{pet.name}</li>
136
+ ))}
137
+ </ul>
138
+ );
139
+ }
140
+
141
+ function ParentComponent() {
142
+ return (
143
+ <>
144
+ <Suspense fallback={<>loading...</>}>
145
+ <ChildComponent />
146
+ </Suspense>
147
+ </>
148
+ );
149
+ }
150
+
151
+ function App() {
116
152
  return (
117
153
  <div className="App">
118
- {/* .... */}
154
+ <h1>Pet List</h1>
155
+ <ParentComponent />
119
156
  </div>
120
157
  );
121
158
  }
@@ -124,4 +161,5 @@ export default App;
124
161
  ```
125
162
 
126
163
  ## License
164
+
127
165
  MIT
package/dist/cli.mjs ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { generate } from "./generate.mjs";
3
+ import { Command, Option } from "commander";
4
+ import { readFile } from "fs/promises";
5
+ import { dirname, join } from "path";
6
+ import { fileURLToPath } from "node:url";
7
+ const program = new Command();
8
+ async function setupProgram() {
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ const file = await readFile(join(__dirname, "../package.json"), "utf-8");
12
+ const packageJson = JSON.parse(file);
13
+ const version = packageJson.version;
14
+ program
15
+ .name("openapi-rq")
16
+ .version(version)
17
+ .description("Generate React Query code based on OpenAPI")
18
+ .requiredOption("-i, --input <value>", "OpenAPI specification, can be a path, url or string content (required)")
19
+ .option("-o, --output <value>", "Output directory", "openapi")
20
+ .addOption(new Option("-c, --client <value>", "HTTP client to generate")
21
+ .choices(["angular", "axios", "fetch", "node", "xhr"])
22
+ .default("fetch"))
23
+ .option("--request <value>", "Path to custom request file")
24
+ .option("--format", "Process output folder with formatter?")
25
+ .option("--lint", "Process output folder with linter?")
26
+ .option("--operationId", "Use operation ID to generate operation names?")
27
+ .addOption(new Option("--serviceResponse <value>", "Define shape of returned value from service calls").choices(["body", "generics", "response"]))
28
+ .option("--base <value>", "Manually set base in OpenAPI config instead of inferring from server value")
29
+ .option("--enums", "Generate JavaScript objects from enum definitions?")
30
+ .option("--useDateType", "Use Date type instead of string for date types for models, this will not convert the data to a Date object")
31
+ .parse();
32
+ const options = program.opts();
33
+ generate(options, version);
34
+ }
35
+ setupProgram();
@@ -0,0 +1,64 @@
1
+ import { stat } from "fs/promises";
2
+ import ts from "typescript";
3
+ export const TData = ts.factory.createIdentifier("TData");
4
+ export const TError = ts.factory.createIdentifier("TError");
5
+ export const TContext = ts.factory.createIdentifier("TContext");
6
+ export const queryKeyGenericType = ts.factory.createTypeReferenceNode("TQueryKey");
7
+ export const queryKeyConstraint = ts.factory.createTypeReferenceNode("Array", [
8
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
9
+ ]);
10
+ export const capitalizeFirstLetter = (str) => {
11
+ return str.charAt(0).toUpperCase() + str.slice(1);
12
+ };
13
+ export const lowercaseFirstLetter = (str) => {
14
+ return str.charAt(0).toLowerCase() + str.slice(1);
15
+ };
16
+ export const getNameFromMethod = (method) => {
17
+ return method.getName();
18
+ };
19
+ export async function exists(f) {
20
+ try {
21
+ await stat(f);
22
+ return true;
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ }
28
+ const Common = "Common";
29
+ /**
30
+ * Build a common type name by prepending the Common namespace.
31
+ */
32
+ export function BuildCommonTypeName(name) {
33
+ if (typeof name === "string") {
34
+ return ts.factory.createIdentifier(`${Common}.${name}`);
35
+ }
36
+ return ts.factory.createIdentifier(`${Common}.${name.text}`);
37
+ }
38
+ /**
39
+ * Safely parse a value into a number. Checks for NaN and Infinity.
40
+ * Returns NaN if the string is not a valid number.
41
+ * @param value The value to parse.
42
+ * @returns The parsed number or NaN if the value is not a valid number.
43
+ */
44
+ export function safeParseNumber(value) {
45
+ const parsed = Number(value);
46
+ if (!isNaN(parsed) && isFinite(parsed)) {
47
+ return parsed;
48
+ }
49
+ return NaN;
50
+ }
51
+ export function extractPropertiesFromObjectParam(param) {
52
+ const referenced = param.findReferences()[0];
53
+ const def = referenced.getDefinition();
54
+ const paramNodes = def
55
+ .getNode()
56
+ .getType()
57
+ .getProperties()
58
+ .map((prop) => ({
59
+ name: prop.getName(),
60
+ optional: prop.isOptional(),
61
+ type: prop.getValueDeclaration()?.getType(),
62
+ }));
63
+ return paramNodes;
64
+ }
@@ -0,0 +1,3 @@
1
+ export const defaultOutputPath = "openapi";
2
+ export const queriesOutputPath = "queries";
3
+ export const requestsOutputPath = "requests";
@@ -0,0 +1,40 @@
1
+ import { createUseQuery } from "./createUseQuery.mjs";
2
+ import { createUseMutation } from "./createUseMutation.mjs";
3
+ export const createExports = (service) => {
4
+ const { klasses } = service;
5
+ const methods = klasses.map((k) => k.methods).flat();
6
+ const allGet = methods.filter((m) => m.httpMethodName === "'GET'");
7
+ const allPost = methods.filter((m) => m.httpMethodName === "'POST'");
8
+ const allQueries = allGet.map((m) => createUseQuery(m));
9
+ const allMutations = allPost.map((m) => createUseMutation(m));
10
+ const commonInQueries = allQueries
11
+ .map(({ apiResponse, returnType, key }) => [apiResponse, returnType, key])
12
+ .flat();
13
+ const commonInMutations = allMutations
14
+ .map(({ mutationResult }) => [mutationResult])
15
+ .flat();
16
+ const allCommon = [...commonInQueries, ...commonInMutations];
17
+ const mainQueries = allQueries.map(({ queryHook }) => [queryHook]).flat();
18
+ const mainMutations = allMutations
19
+ .map(({ mutationHook }) => [mutationHook])
20
+ .flat();
21
+ const mainExports = [...mainQueries, ...mainMutations];
22
+ const suspenseQueries = allQueries
23
+ .map(({ suspenseQueryHook }) => [suspenseQueryHook])
24
+ .flat();
25
+ const suspenseExports = [...suspenseQueries];
26
+ return {
27
+ /**
28
+ * Common types and variables between queries (regular and suspense) and mutations
29
+ */
30
+ allCommon,
31
+ /**
32
+ * Main exports are the hooks that are used in the components
33
+ */
34
+ mainExports,
35
+ /**
36
+ * Suspense exports are the hooks that are used in the suspense components
37
+ */
38
+ suspenseExports,
39
+ };
40
+ };
@@ -0,0 +1,47 @@
1
+ import ts from "typescript";
2
+ import { posix } from "path";
3
+ const { join } = posix;
4
+ export const createImports = ({ service, serviceEndName, project, }) => {
5
+ const { klasses } = service;
6
+ // get all class names
7
+ const classNames = klasses.map(({ className }) => className);
8
+ // remove duplicates
9
+ const uniqueClassNames = [...new Set(classNames)];
10
+ const modelsFile = project
11
+ .getSourceFiles()
12
+ .find((sourceFile) => sourceFile.getFilePath().includes("models.ts"));
13
+ const serviceFile = project
14
+ .getSourceFiles()
15
+ .find((sourceFile) => sourceFile.getFilePath().includes("services.ts"));
16
+ if (!modelsFile) {
17
+ throw new Error("No models file found");
18
+ }
19
+ if (!serviceFile) {
20
+ throw new Error("No service file found");
21
+ }
22
+ const modalNames = Array.from(modelsFile.getExportedDeclarations().keys());
23
+ const serviceExports = Array.from(serviceFile.getExportedDeclarations().keys());
24
+ const serviceNames = serviceExports.filter((name) => name.endsWith(serviceEndName));
25
+ const serviceNamesData = serviceExports.filter((name) => name.endsWith("Data"));
26
+ return [
27
+ ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([
28
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("useQuery")),
29
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("useSuspenseQuery")),
30
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("useMutation")),
31
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("UseQueryResult")),
32
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("UseQueryOptions")),
33
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("UseMutationOptions")),
34
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("UseMutationResult")),
35
+ ])), ts.factory.createStringLiteral("@tanstack/react-query"), undefined),
36
+ ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([
37
+ // import all class names from service file
38
+ ...serviceNames.map((serviceName) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(serviceName))),
39
+ // import all data objects from service file
40
+ ...serviceNamesData.map((dataName) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(dataName))),
41
+ ])), ts.factory.createStringLiteral(join("../requests")), undefined),
42
+ // import all the models by name
43
+ ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([
44
+ ...modalNames.map((modelName) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(modelName))),
45
+ ])), ts.factory.createStringLiteral(join("../requests/models")), undefined),
46
+ ];
47
+ };
@@ -0,0 +1,74 @@
1
+ import ts from "typescript";
2
+ import { createImports } from "./createImports.mjs";
3
+ import { createExports } from "./createExports.mjs";
4
+ import { getServices } from "./service.mjs";
5
+ import { Project } from "ts-morph";
6
+ import { join } from "path";
7
+ const createSourceFile = async (outputPath, serviceEndName) => {
8
+ const project = new Project({
9
+ // Optionally specify compiler options, tsconfig.json, in-memory file system, and more here.
10
+ // If you initialize with a tsconfig.json, then it will automatically populate the project
11
+ // with the associated source files.
12
+ // Read more: https://ts-morph.com/setup/
13
+ skipAddingFilesFromTsConfig: true,
14
+ });
15
+ const sourceFiles = join(process.cwd(), outputPath);
16
+ project.addSourceFilesAtPaths(`${sourceFiles}/**/*`);
17
+ const service = await getServices(project);
18
+ const imports = createImports({
19
+ service,
20
+ serviceEndName,
21
+ project,
22
+ });
23
+ const exports = createExports(service);
24
+ const commonSource = ts.factory.createSourceFile([...imports, ...exports.allCommon], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
25
+ const commonImport = ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, ts.factory.createIdentifier("* as Common"), undefined), ts.factory.createStringLiteral("./common"), undefined);
26
+ const commonExport = ts.factory.createExportDeclaration(undefined, false, undefined, ts.factory.createStringLiteral("./common"), undefined);
27
+ const queriesExport = ts.factory.createExportDeclaration(undefined, false, undefined, ts.factory.createStringLiteral("./queries"), undefined);
28
+ const mainSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.mainExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
29
+ const suspenseSource = ts.factory.createSourceFile([commonImport, ...imports, ...exports.suspenseExports], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
30
+ const indexSource = ts.factory.createSourceFile([commonExport, queriesExport], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None);
31
+ return {
32
+ commonSource,
33
+ mainSource,
34
+ suspenseSource,
35
+ indexSource,
36
+ };
37
+ };
38
+ export const createSource = async ({ outputPath, version, serviceEndName, }) => {
39
+ const queriesFile = ts.createSourceFile("queries.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
40
+ const commonFile = ts.createSourceFile("common.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
41
+ const suspenseFile = ts.createSourceFile("suspense.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
42
+ const indexFile = ts.createSourceFile("index.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
43
+ const printer = ts.createPrinter({
44
+ newLine: ts.NewLineKind.LineFeed,
45
+ removeComments: false,
46
+ });
47
+ const { commonSource, mainSource, suspenseSource, indexSource } = await createSourceFile(outputPath, serviceEndName);
48
+ const commonResult = `// generated with @7nohe/openapi-react-query-codegen@${version} \n` +
49
+ printer.printNode(ts.EmitHint.Unspecified, commonSource, commonFile);
50
+ const mainResult = `// generated with @7nohe/openapi-react-query-codegen@${version} \n` +
51
+ printer.printNode(ts.EmitHint.Unspecified, mainSource, queriesFile);
52
+ const suspenseResult = `// generated with @7nohe/openapi-react-query-codegen@${version} \n` +
53
+ printer.printNode(ts.EmitHint.Unspecified, suspenseSource, suspenseFile);
54
+ const indexResult = `// generated with @7nohe/openapi-react-query-codegen@${version} \n` +
55
+ printer.printNode(ts.EmitHint.Unspecified, indexSource, indexFile);
56
+ return [
57
+ {
58
+ name: "index.ts",
59
+ content: indexResult,
60
+ },
61
+ {
62
+ name: "common.ts",
63
+ content: commonResult,
64
+ },
65
+ {
66
+ name: "queries.ts",
67
+ content: mainResult,
68
+ },
69
+ {
70
+ name: "suspense.ts",
71
+ content: suspenseResult,
72
+ },
73
+ ];
74
+ };
@@ -0,0 +1,98 @@
1
+ import ts from "typescript";
2
+ import { BuildCommonTypeName, TContext, TData, TError, capitalizeFirstLetter, extractPropertiesFromObjectParam, getNameFromMethod, } from "./common.mjs";
3
+ import { addJSDocToNode } from "./util.mjs";
4
+ /**
5
+ * Awaited<ReturnType<typeof myClass.myMethod>>
6
+ */
7
+ function generateAwaitedReturnType({ className, methodName, }) {
8
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("Awaited"), [
9
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("ReturnType"), [
10
+ ts.factory.createTypeQueryNode(ts.factory.createQualifiedName(ts.factory.createIdentifier(className), ts.factory.createIdentifier(methodName)), undefined),
11
+ ]),
12
+ ]);
13
+ }
14
+ export const createUseMutation = ({ node, className, method, jsDoc = [], isDeprecated = false, }) => {
15
+ const methodName = getNameFromMethod(method);
16
+ const awaitedResponseDataType = generateAwaitedReturnType({
17
+ className,
18
+ methodName,
19
+ });
20
+ const mutationResult = ts.factory.createTypeAliasDeclaration([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(`${className}${capitalizeFirstLetter(methodName)}MutationResult`), undefined, awaitedResponseDataType);
21
+ const responseDataType = ts.factory.createTypeParameterDeclaration(undefined, TData, undefined, ts.factory.createTypeReferenceNode(BuildCommonTypeName(mutationResult.name)));
22
+ const methodParameters = method.getParameters().length !== 0
23
+ ? ts.factory.createTypeLiteralNode(method
24
+ .getParameters()
25
+ .map((param) => {
26
+ const paramNodes = extractPropertiesFromObjectParam(param);
27
+ return paramNodes.map((refParam) => ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier(refParam.name), refParam.optional
28
+ ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
29
+ : undefined,
30
+ // refParam.questionToken ?? refParam.initializer
31
+ // ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
32
+ // : refParam.questionToken,
33
+ ts.factory.createTypeReferenceNode(refParam.type.getText(param))));
34
+ })
35
+ .flat()
36
+ // return ts.factory.createPropertySignature(
37
+ // undefined,
38
+ // ts.factory.createIdentifier(param.getName()),
39
+ // param.compilerNode.questionToken ?? param.compilerNode.initializer
40
+ // ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
41
+ // : param.compilerNode.questionToken,
42
+ // param.compilerNode.type
43
+ // );
44
+ )
45
+ : ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
46
+ const exportHook = ts.factory.createVariableStatement([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
47
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier(`use${className}${capitalizeFirstLetter(methodName)}`), undefined, undefined, ts.factory.createArrowFunction(undefined, ts.factory.createNodeArray([
48
+ responseDataType,
49
+ ts.factory.createTypeParameterDeclaration(undefined, TError, undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)),
50
+ ts.factory.createTypeParameterDeclaration(undefined, TContext, undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)),
51
+ ]), [
52
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier("options"), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("Omit"), [
53
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("UseMutationOptions"), [
54
+ ts.factory.createTypeReferenceNode(TData),
55
+ ts.factory.createTypeReferenceNode(TError),
56
+ methodParameters,
57
+ ts.factory.createTypeReferenceNode(TContext),
58
+ ]),
59
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("mutationFn")),
60
+ ]), undefined),
61
+ ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createCallExpression(ts.factory.createIdentifier("useMutation"), [
62
+ ts.factory.createTypeReferenceNode(TData),
63
+ ts.factory.createTypeReferenceNode(TError),
64
+ methodParameters,
65
+ ts.factory.createTypeReferenceNode(TContext),
66
+ ], [
67
+ ts.factory.createObjectLiteralExpression([
68
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier("mutationFn"), ts.factory.createArrowFunction(undefined, undefined, method.getParameters().length !== 0
69
+ ? [
70
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createObjectBindingPattern(method
71
+ .getParameters()
72
+ .map((param) => {
73
+ const paramNodes = extractPropertiesFromObjectParam(param);
74
+ return paramNodes.map((refParam) => ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier(refParam.name), undefined));
75
+ })
76
+ .flat()), undefined, undefined, undefined),
77
+ ]
78
+ : [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createAsExpression(ts.factory.createAsExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(className), ts.factory.createIdentifier(methodName)), undefined, method.getParameters().length !== 0
79
+ ? [
80
+ ts.factory.createObjectLiteralExpression(method
81
+ .getParameters()
82
+ .map((params) => {
83
+ const paramNodes = extractPropertiesFromObjectParam(params);
84
+ return paramNodes.map((refParam) => ts.factory.createShorthandPropertyAssignment(refParam.name));
85
+ })
86
+ .flat()),
87
+ ]
88
+ : []), ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("Promise"), [ts.factory.createTypeReferenceNode(TData)])))),
89
+ ts.factory.createSpreadAssignment(ts.factory.createIdentifier("options")),
90
+ ]),
91
+ ]))),
92
+ ], ts.NodeFlags.Const));
93
+ const hookWithJsDoc = addJSDocToNode(exportHook, node, isDeprecated, jsDoc);
94
+ return {
95
+ mutationResult,
96
+ mutationHook: hookWithJsDoc,
97
+ };
98
+ };