@7nohe/openapi-react-query-codegen 1.6.1 → 2.0.0-beta.1
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/LICENSE +21 -0
- package/README.md +2 -362
- package/dist/cli.mjs +4 -9
- package/dist/common.mjs +93 -10
- package/dist/constants.mjs +2 -1
- package/dist/createExports.mjs +60 -14
- package/dist/createImports.mjs +10 -5
- package/dist/createPrefetchOrEnsure.mjs +56 -0
- package/dist/createSource.mjs +27 -5
- package/dist/createUseMutation.mjs +47 -32
- package/dist/createUseQuery.mjs +66 -110
- package/dist/format.mjs +46 -0
- package/dist/generate.mjs +11 -6
- package/dist/service.mjs +63 -63
- package/package.json +37 -27
- package/dist/createPrefetch.mjs +0 -53
package/dist/format.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sync } from "cross-spawn";
|
|
1
2
|
import { IndentationText, NewLineKind, Project, QuoteKind } from "ts-morph";
|
|
2
3
|
export const formatOutput = async (outputPath) => {
|
|
3
4
|
const project = new Project({
|
|
@@ -19,3 +20,48 @@ export const formatOutput = async (outputPath) => {
|
|
|
19
20
|
});
|
|
20
21
|
await Promise.all(tasks);
|
|
21
22
|
};
|
|
23
|
+
const formatters = {
|
|
24
|
+
biome: {
|
|
25
|
+
args: (path) => ["format", "--write", path],
|
|
26
|
+
command: "biome",
|
|
27
|
+
name: "Biome (Format)",
|
|
28
|
+
},
|
|
29
|
+
prettier: {
|
|
30
|
+
args: (path) => [
|
|
31
|
+
"--ignore-unknown",
|
|
32
|
+
path,
|
|
33
|
+
"--write",
|
|
34
|
+
"--ignore-path",
|
|
35
|
+
"./.prettierignore",
|
|
36
|
+
],
|
|
37
|
+
command: "prettier",
|
|
38
|
+
name: "Prettier",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Map of supported linters
|
|
43
|
+
*/
|
|
44
|
+
const linters = {
|
|
45
|
+
biome: {
|
|
46
|
+
args: (path) => ["lint", "--write", path],
|
|
47
|
+
command: "biome",
|
|
48
|
+
name: "Biome (Lint)",
|
|
49
|
+
},
|
|
50
|
+
eslint: {
|
|
51
|
+
args: (path) => [path, "--fix"],
|
|
52
|
+
command: "eslint",
|
|
53
|
+
name: "ESLint",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
export const processOutput = async ({ output, format, lint, }) => {
|
|
57
|
+
if (format) {
|
|
58
|
+
const module = formatters[format];
|
|
59
|
+
console.log(`✨ Running ${module.name} on queries`);
|
|
60
|
+
sync(module.command, module.args(output));
|
|
61
|
+
}
|
|
62
|
+
if (lint) {
|
|
63
|
+
const module = linters[lint];
|
|
64
|
+
console.log(`✨ Running ${module.name} on queries`);
|
|
65
|
+
sync(module.command, module.args(output));
|
|
66
|
+
}
|
|
67
|
+
};
|
package/dist/generate.mjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { createClient } from "@hey-api/openapi-ts";
|
|
2
2
|
import { buildQueriesOutputPath, buildRequestsOutputPath, formatOptions, } from "./common.mjs";
|
|
3
3
|
import { createSource } from "./createSource.mjs";
|
|
4
|
-
import { formatOutput } from "./format.mjs";
|
|
4
|
+
import { formatOutput, processOutput } from "./format.mjs";
|
|
5
5
|
import { print } from "./print.mjs";
|
|
6
6
|
export async function generate(options, version) {
|
|
7
7
|
const openApiOutputPath = buildRequestsOutputPath(options.output);
|
|
8
8
|
const formattedOptions = formatOptions(options);
|
|
9
9
|
const config = {
|
|
10
|
-
base: formattedOptions.base,
|
|
11
10
|
client: formattedOptions.client,
|
|
12
11
|
debug: formattedOptions.debug,
|
|
13
12
|
dryRun: false,
|
|
@@ -18,15 +17,16 @@ export async function generate(options, version) {
|
|
|
18
17
|
path: openApiOutputPath,
|
|
19
18
|
},
|
|
20
19
|
input: formattedOptions.input,
|
|
21
|
-
request: formattedOptions.request,
|
|
22
20
|
schemas: {
|
|
23
21
|
export: !formattedOptions.noSchemas,
|
|
24
22
|
type: formattedOptions.schemaType,
|
|
25
23
|
},
|
|
26
24
|
services: {
|
|
27
25
|
export: true,
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
asClass: false,
|
|
27
|
+
operationId: formattedOptions.operationId !== undefined
|
|
28
|
+
? formattedOptions.operationId
|
|
29
|
+
: true,
|
|
30
30
|
},
|
|
31
31
|
types: {
|
|
32
32
|
dates: formattedOptions.useDateType,
|
|
@@ -38,8 +38,8 @@ export async function generate(options, version) {
|
|
|
38
38
|
await createClient(config);
|
|
39
39
|
const source = await createSource({
|
|
40
40
|
outputPath: openApiOutputPath,
|
|
41
|
+
client: formattedOptions.client,
|
|
41
42
|
version,
|
|
42
|
-
serviceEndName: "Service", // we are hard coding this because changing the service end name was depreciated in @hey-api/openapi-ts
|
|
43
43
|
pageParam: formattedOptions.pageParam,
|
|
44
44
|
nextPageParam: formattedOptions.nextPageParam,
|
|
45
45
|
initialPageParam: formattedOptions.initialPageParam.toString(),
|
|
@@ -47,4 +47,9 @@ export async function generate(options, version) {
|
|
|
47
47
|
await print(source, formattedOptions);
|
|
48
48
|
const queriesOutputPath = buildQueriesOutputPath(options.output);
|
|
49
49
|
await formatOutput(queriesOutputPath);
|
|
50
|
+
await processOutput({
|
|
51
|
+
output: queriesOutputPath,
|
|
52
|
+
format: formattedOptions.format,
|
|
53
|
+
lint: formattedOptions.lint,
|
|
54
|
+
});
|
|
50
55
|
}
|
package/dist/service.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
-
import { getClassNameFromClassNode, getClassesFromService, } from "./common.mjs";
|
|
3
2
|
import { serviceFileName } from "./constants.mjs";
|
|
4
3
|
export async function getServices(project) {
|
|
5
4
|
const node = project
|
|
@@ -8,72 +7,73 @@ export async function getServices(project) {
|
|
|
8
7
|
if (!node) {
|
|
9
8
|
throw new Error("No service node found");
|
|
10
9
|
}
|
|
11
|
-
const
|
|
10
|
+
const methods = getMethodsFromService(node);
|
|
12
11
|
return {
|
|
13
|
-
|
|
14
|
-
className,
|
|
15
|
-
klass,
|
|
16
|
-
methods: getMethodsFromService(node, klass),
|
|
17
|
-
})),
|
|
12
|
+
methods,
|
|
18
13
|
node,
|
|
19
14
|
};
|
|
20
15
|
}
|
|
21
|
-
function getMethodsFromService(node
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
return [tsNode].concat(allChildren.flat());
|
|
16
|
+
export function getMethodsFromService(node) {
|
|
17
|
+
const variableStatements = node.getVariableStatements();
|
|
18
|
+
// The first variable statement is `const client = createClient(createConfig())`, so we skip it
|
|
19
|
+
return variableStatements.splice(1).flatMap((variableStatement) => {
|
|
20
|
+
const declarations = variableStatement.getDeclarations();
|
|
21
|
+
return declarations.map((declaration) => {
|
|
22
|
+
if (!ts.isVariableDeclaration(declaration.compilerNode)) {
|
|
23
|
+
throw new Error("Variable declaration not found");
|
|
24
|
+
}
|
|
25
|
+
const initializer = declaration.getInitializer();
|
|
26
|
+
if (!initializer) {
|
|
27
|
+
throw new Error("Initializer not found");
|
|
28
|
+
}
|
|
29
|
+
if (!ts.isArrowFunction(initializer.compilerNode)) {
|
|
30
|
+
throw new Error("Arrow function not found");
|
|
31
|
+
}
|
|
32
|
+
const methodBlockNode = initializer.compilerNode.body;
|
|
33
|
+
if (!methodBlockNode || !ts.isBlock(methodBlockNode)) {
|
|
34
|
+
throw new Error("Method block not found");
|
|
35
|
+
}
|
|
36
|
+
const foundReturnStatement = methodBlockNode.statements.find((s) => s.kind === ts.SyntaxKind.ReturnStatement);
|
|
37
|
+
if (!foundReturnStatement) {
|
|
38
|
+
throw new Error("Return statement not found");
|
|
39
|
+
}
|
|
40
|
+
const returnStatement = foundReturnStatement;
|
|
41
|
+
const foundCallExpression = returnStatement.expression;
|
|
42
|
+
if (!foundCallExpression) {
|
|
43
|
+
throw new Error("Call expression not found");
|
|
44
|
+
}
|
|
45
|
+
const callExpression = foundCallExpression;
|
|
46
|
+
const propertyAccessExpression = callExpression.expression;
|
|
47
|
+
const httpMethodName = propertyAccessExpression.name.getText();
|
|
48
|
+
if (!httpMethodName) {
|
|
49
|
+
throw new Error("httpMethodName not found");
|
|
56
50
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
51
|
+
const getAllChildren = (tsNode) => {
|
|
52
|
+
const childItems = tsNode.getChildren(node.compilerNode);
|
|
53
|
+
if (childItems.length) {
|
|
54
|
+
const allChildren = childItems.map(getAllChildren);
|
|
55
|
+
return [tsNode].concat(allChildren.flat());
|
|
56
|
+
}
|
|
57
|
+
return [tsNode];
|
|
58
|
+
};
|
|
59
|
+
const children = getAllChildren(initializer.compilerNode);
|
|
60
|
+
// get all JSDoc comments
|
|
61
|
+
// this should be an array of 1 or 0
|
|
62
|
+
const jsDocs = children
|
|
63
|
+
.filter((c) => c.kind === ts.SyntaxKind.JSDoc)
|
|
64
|
+
.map((c) => c.getText(node.compilerNode));
|
|
65
|
+
// get the first JSDoc comment
|
|
66
|
+
const jsDoc = jsDocs?.[0];
|
|
67
|
+
const isDeprecated = children.some((c) => c.kind === ts.SyntaxKind.JSDocDeprecatedTag);
|
|
68
|
+
const methodDescription = {
|
|
69
|
+
node,
|
|
70
|
+
method: declaration,
|
|
71
|
+
methodBlock: methodBlockNode,
|
|
72
|
+
httpMethodName,
|
|
73
|
+
jsDoc,
|
|
74
|
+
isDeprecated,
|
|
75
|
+
};
|
|
76
|
+
return methodDescription;
|
|
77
|
+
});
|
|
78
78
|
});
|
|
79
79
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7nohe/openapi-react-query-codegen",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"description": "OpenAPI React Query Codegen",
|
|
5
|
+
"bin": {
|
|
6
|
+
"openapi-rq": "dist/cli.mjs"
|
|
7
|
+
},
|
|
8
|
+
"private": false,
|
|
9
|
+
"type": "module",
|
|
10
|
+
"workspaces": [
|
|
11
|
+
"examples/*"
|
|
12
|
+
],
|
|
13
|
+
"exports": [
|
|
14
|
+
{
|
|
15
|
+
"import": "./dist/generate.mjs",
|
|
16
|
+
"require": "./dist/generate.mjs",
|
|
17
|
+
"types": "./dist/generate.d.mts"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/7nohe/openapi-react-query-codegen.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/7nohe/openapi-react-query-codegen",
|
|
25
|
+
"bugs": "https://github.com/7nohe/openapi-react-query-codegen/issues",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
5
29
|
"keywords": [
|
|
6
30
|
"codegen",
|
|
7
31
|
"react-query",
|
|
@@ -12,44 +36,28 @@
|
|
|
12
36
|
"openapi-typescript-codegen",
|
|
13
37
|
"@hey-api/openapi-ts"
|
|
14
38
|
],
|
|
15
|
-
"homepage": "https://github.com/7nohe/openapi-react-query-codegen",
|
|
16
|
-
"bugs": "https://github.com/7nohe/openapi-react-query-codegen/issues",
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/7nohe/openapi-react-query-codegen.git"
|
|
20
|
-
},
|
|
21
39
|
"license": "MIT",
|
|
22
40
|
"author": "Daiki Urata (@7nohe)",
|
|
23
|
-
"type": "module",
|
|
24
|
-
"bin": {
|
|
25
|
-
"openapi-rq": "dist/cli.mjs"
|
|
26
|
-
},
|
|
27
|
-
"files": [
|
|
28
|
-
"dist"
|
|
29
|
-
],
|
|
30
|
-
"workspaces": [
|
|
31
|
-
"examples/*"
|
|
32
|
-
],
|
|
33
41
|
"dependencies": {
|
|
34
|
-
"@hey-api/
|
|
42
|
+
"@hey-api/client-fetch": "0.4.0",
|
|
43
|
+
"@hey-api/openapi-ts": "0.53.8",
|
|
44
|
+
"cross-spawn": "^7.0.3"
|
|
35
45
|
},
|
|
36
46
|
"devDependencies": {
|
|
37
|
-
"@biomejs/biome": "^1.
|
|
38
|
-
"@types/
|
|
47
|
+
"@biomejs/biome": "^1.9.3",
|
|
48
|
+
"@types/cross-spawn": "^6.0.6",
|
|
49
|
+
"@types/node": "^22.7.4",
|
|
39
50
|
"@vitest/coverage-v8": "^1.5.0",
|
|
40
51
|
"commander": "^12.0.0",
|
|
41
|
-
"glob": "^10.3.10",
|
|
42
52
|
"lefthook": "^1.6.10",
|
|
43
53
|
"rimraf": "^5.0.5",
|
|
44
54
|
"ts-morph": "^23.0.0",
|
|
45
|
-
"ts-node": "^10.9.2",
|
|
46
55
|
"typescript": "^5.5.4",
|
|
47
56
|
"vitest": "^1.5.0"
|
|
48
57
|
},
|
|
49
58
|
"peerDependencies": {
|
|
50
59
|
"commander": "12.x",
|
|
51
|
-
"
|
|
52
|
-
"ts-morph": "22.x",
|
|
60
|
+
"ts-morph": "23.x",
|
|
53
61
|
"typescript": "5.x"
|
|
54
62
|
},
|
|
55
63
|
"engines": {
|
|
@@ -60,9 +68,11 @@
|
|
|
60
68
|
"build": "rimraf dist && tsc -p tsconfig.json",
|
|
61
69
|
"lint": "biome check .",
|
|
62
70
|
"lint:fix": "biome check --write .",
|
|
63
|
-
"preview": "npm run build && npm -C examples/react-app run generate:api",
|
|
71
|
+
"preview:react": "npm run build && npm -C examples/react-app run generate:api",
|
|
72
|
+
"preview:nextjs": "npm run build && npm -C examples/nextjs-app run generate:api",
|
|
73
|
+
"preview:tanstack-router": "npm run build && npm -C examples/tanstack-router-app run generate:api",
|
|
64
74
|
"release": "npx git-ensure -a && npx bumpp --commit --tag --push",
|
|
65
|
-
"
|
|
66
|
-
"
|
|
75
|
+
"test": "vitest --coverage.enabled true",
|
|
76
|
+
"snapshot": "vitest --update"
|
|
67
77
|
}
|
|
68
78
|
}
|
package/dist/createPrefetch.mjs
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import ts from "typescript";
|
|
2
|
-
import { BuildCommonTypeName, extractPropertiesFromObjectParam, getNameFromMethod, } from "./common.mjs";
|
|
3
|
-
import { createQueryKeyFromMethod, getQueryKeyFnName, getRequestParamFromMethod, hookNameFromMethod, } from "./createUseQuery.mjs";
|
|
4
|
-
import { addJSDocToNode } from "./util.mjs";
|
|
5
|
-
/**
|
|
6
|
-
* Creates a prefetch function for a query
|
|
7
|
-
*/
|
|
8
|
-
function createPrefetchHook({ requestParams, method, className, }) {
|
|
9
|
-
const methodName = getNameFromMethod(method);
|
|
10
|
-
const queryName = hookNameFromMethod({ method, className });
|
|
11
|
-
const customHookName = `prefetch${queryName.charAt(0).toUpperCase() + queryName.slice(1)}`;
|
|
12
|
-
const queryKey = createQueryKeyFromMethod({ method, className });
|
|
13
|
-
// const
|
|
14
|
-
const hookExport = ts.factory.createVariableStatement(
|
|
15
|
-
// export
|
|
16
|
-
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
|
|
17
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier(customHookName), undefined, undefined, ts.factory.createArrowFunction(undefined, undefined, [
|
|
18
|
-
ts.factory.createParameterDeclaration(undefined, undefined, "queryClient", undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("QueryClient"))),
|
|
19
|
-
...requestParams,
|
|
20
|
-
], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createCallExpression(ts.factory.createIdentifier("queryClient.prefetchQuery"), undefined, [
|
|
21
|
-
ts.factory.createObjectLiteralExpression([
|
|
22
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryKey"), ts.factory.createCallExpression(BuildCommonTypeName(getQueryKeyFnName(queryKey)), undefined, method.getParameters().length
|
|
23
|
-
? [
|
|
24
|
-
ts.factory.createObjectLiteralExpression(method
|
|
25
|
-
.getParameters()
|
|
26
|
-
.flatMap((param) => extractPropertiesFromObjectParam(param).map((p) => ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name))))),
|
|
27
|
-
]
|
|
28
|
-
: [])),
|
|
29
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier("queryFn"), ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(className), ts.factory.createIdentifier(methodName)), undefined, method.getParameters().length
|
|
30
|
-
? [
|
|
31
|
-
ts.factory.createObjectLiteralExpression(method
|
|
32
|
-
.getParameters()
|
|
33
|
-
.flatMap((param) => extractPropertiesFromObjectParam(param).map((p) => ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier(p.name))))),
|
|
34
|
-
]
|
|
35
|
-
: undefined))),
|
|
36
|
-
]),
|
|
37
|
-
]))),
|
|
38
|
-
], ts.NodeFlags.Const));
|
|
39
|
-
return hookExport;
|
|
40
|
-
}
|
|
41
|
-
export const createPrefetch = ({ className, method, jsDoc, }) => {
|
|
42
|
-
const requestParam = getRequestParamFromMethod(method);
|
|
43
|
-
const requestParams = requestParam ? [requestParam] : [];
|
|
44
|
-
const prefetchHook = createPrefetchHook({
|
|
45
|
-
requestParams,
|
|
46
|
-
method,
|
|
47
|
-
className,
|
|
48
|
-
});
|
|
49
|
-
const hookWithJsDoc = addJSDocToNode(prefetchHook, jsDoc);
|
|
50
|
-
return {
|
|
51
|
-
prefetchHook: hookWithJsDoc,
|
|
52
|
-
};
|
|
53
|
-
};
|