@7nohe/openapi-react-query-codegen 1.0.6 → 1.1.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/README.md CHANGED
@@ -46,7 +46,7 @@ Options:
46
46
  -c, --client <value> HTTP client to generate [fetch, xhr, node, axios, angular] (default: "fetch")
47
47
  --request <value> Path to custom request file
48
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?
49
+ --enums <value> Generate JavaScript objects from enum definitions? ['javascript', 'typescript']
50
50
  --base <value> Manually set base in OpenAPI config instead of inferring from server value
51
51
  --serviceResponse <value> Define shape of returned value from service calls ['body', 'generics', 'response']
52
52
  --operationId Use operation ID to generate operation names?
package/dist/cli.mjs CHANGED
@@ -26,10 +26,10 @@ async function setupProgram() {
26
26
  .option("--operationId", "Use operation ID to generate operation names?")
27
27
  .addOption(new Option("--serviceResponse <value>", "Define shape of returned value from service calls").choices(["body", "generics", "response"]))
28
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?")
29
+ .addOption(new Option("--enums <value>", "Generate JavaScript objects from enum definitions?").choices(["javascript", "typescript"]))
30
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
31
  .parse();
32
32
  const options = program.opts();
33
- generate(options, version);
33
+ await generate(options, version);
34
34
  }
35
35
  setupProgram();
@@ -3,10 +3,23 @@ import { createUseMutation } from "./createUseMutation.mjs";
3
3
  export const createExports = (service) => {
4
4
  const { klasses } = service;
5
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));
6
+ const allGet = methods.filter((m) => m.httpMethodName.toUpperCase().includes("GET"));
7
+ const allPost = methods.filter((m) => m.httpMethodName.toUpperCase().includes("POST"));
8
+ const allPut = methods.filter((m) => m.httpMethodName.toUpperCase().includes("PUT"));
9
+ const allPatch = methods.filter((m) => m.httpMethodName.toUpperCase().includes("PATCH"));
10
+ const allDelete = methods.filter((m) => m.httpMethodName.toUpperCase().includes("DELETE"));
11
+ const allGetQueries = allGet.map((m) => createUseQuery(m));
12
+ const allPostMutations = allPost.map((m) => createUseMutation(m));
13
+ const allPutMutations = allPut.map((m) => createUseMutation(m));
14
+ const allPatchMutations = allPatch.map((m) => createUseMutation(m));
15
+ const allDeleteMutations = allDelete.map((m) => createUseMutation(m));
16
+ const allQueries = [...allGetQueries];
17
+ const allMutations = [
18
+ ...allPostMutations,
19
+ ...allPutMutations,
20
+ ...allPatchMutations,
21
+ ...allDeleteMutations,
22
+ ];
10
23
  const commonInQueries = allQueries
11
24
  .map(({ apiResponse, returnType, key }) => [apiResponse, returnType, key])
12
25
  .flat();
@@ -1,12 +1,7 @@
1
1
  import ts from "typescript";
2
2
  import { posix } from "path";
3
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)];
4
+ export const createImports = ({ serviceEndName, project, }) => {
10
5
  const modelsFile = project
11
6
  .getSourceFiles()
12
7
  .find((sourceFile) => sourceFile.getFilePath().includes("models.ts"));
@@ -14,16 +9,20 @@ export const createImports = ({ service, serviceEndName, project, }) => {
14
9
  .getSourceFiles()
15
10
  .find((sourceFile) => sourceFile.getFilePath().includes("services.ts"));
16
11
  if (!modelsFile) {
17
- throw new Error("No models file found");
12
+ console.warn(`
13
+ ⚠️ WARNING: No models file found.
14
+ This may be an error if \`.components.schemas\` or \`.components.parameters\` is defined in your OpenAPI input.`);
18
15
  }
19
16
  if (!serviceFile) {
20
17
  throw new Error("No service file found");
21
18
  }
22
- const modalNames = Array.from(modelsFile.getExportedDeclarations().keys());
19
+ const modelNames = modelsFile
20
+ ? Array.from(modelsFile.getExportedDeclarations().keys())
21
+ : [];
23
22
  const serviceExports = Array.from(serviceFile.getExportedDeclarations().keys());
24
23
  const serviceNames = serviceExports.filter((name) => name.endsWith(serviceEndName));
25
24
  const serviceNamesData = serviceExports.filter((name) => name.endsWith("Data"));
26
- return [
25
+ const imports = [
27
26
  ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([
28
27
  ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("useQuery")),
29
28
  ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("useSuspenseQuery")),
@@ -39,9 +38,12 @@ export const createImports = ({ service, serviceEndName, project, }) => {
39
38
  // import all data objects from service file
40
39
  ...serviceNamesData.map((dataName) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(dataName))),
41
40
  ])), 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
41
  ];
42
+ if (modelsFile) {
43
+ // import all the models by name
44
+ imports.push(ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([
45
+ ...modelNames.map((modelName) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(modelName))),
46
+ ])), ts.factory.createStringLiteral(join("../requests/models")), undefined));
47
+ }
48
+ return imports;
47
49
  };
@@ -16,7 +16,6 @@ const createSourceFile = async (outputPath, serviceEndName) => {
16
16
  project.addSourceFilesAtPaths(`${sourceFiles}/**/*`);
17
17
  const service = await getServices(project);
18
18
  const imports = createImports({
19
- service,
20
19
  serviceEndName,
21
20
  project,
22
21
  });
@@ -25,32 +25,44 @@ export const createApiResponseType = ({ className, methodName, }) => {
25
25
  responseDataType,
26
26
  };
27
27
  };
28
+ /**
29
+ * Replace the import("...") surrounding the type if there is one.
30
+ * This can happen when the type is imported from another file, but
31
+ * we are already importing all the types from that file.
32
+ */
33
+ function getShortType(type) {
34
+ return type.replaceAll(/import\("[a-zA-Z\/\.-]*"\)\./g, "");
35
+ }
28
36
  export function getRequestParamFromMethod(method) {
29
37
  if (!method.getParameters().length) {
30
38
  return null;
31
39
  }
32
- // we need to get the properties of the object
33
- return ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createObjectBindingPattern(method
34
- .getParameters()
35
- .map((param) => {
36
- const paramNodes = extractPropertiesFromObjectParam(param);
37
- return paramNodes.map((refParam) => ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier(refParam.name), undefined));
38
- })
39
- .flat()), undefined, ts.factory.createTypeLiteralNode(method
40
+ const params = method
40
41
  .getParameters()
41
42
  .map((param) => {
42
43
  const paramNodes = extractPropertiesFromObjectParam(param);
43
- return paramNodes.map((refParam) => {
44
- return ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier(refParam.name), refParam.optional
45
- ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
46
- : undefined,
47
- // param.hasQuestionToken() ?? param.getInitializer()?.compilerNode
48
- // ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
49
- // : param.getQuestionTokenNode()?.compilerNode,
50
- ts.factory.createTypeReferenceNode(refParam.type.getText()));
51
- });
44
+ return paramNodes.map((refParam) => ({
45
+ name: refParam.name,
46
+ typeName: getShortType(refParam.type.getText()),
47
+ optional: refParam.optional,
48
+ }));
52
49
  })
53
- .flat()));
50
+ .flat();
51
+ const areAllPropertiesOptional = params.every((param) => param.optional);
52
+ return ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createObjectBindingPattern(params.map((refParam) => ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier(refParam.name), undefined))), undefined, ts.factory.createTypeLiteralNode(params.map((refParam) => {
53
+ return ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier(refParam.name), refParam.optional
54
+ ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
55
+ : undefined,
56
+ // param.hasQuestionToken() ?? param.getInitializer()?.compilerNode
57
+ // ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
58
+ // : param.getQuestionTokenNode()?.compilerNode,
59
+ ts.factory.createTypeReferenceNode(refParam.typeName));
60
+ })),
61
+ // if all params are optional, we create an empty object literal
62
+ // so the hook can be called without any parameters
63
+ areAllPropertiesOptional
64
+ ? ts.factory.createObjectLiteralExpression()
65
+ : undefined);
54
66
  }
55
67
  /**
56
68
  * Return Type
@@ -107,7 +119,6 @@ function createQueryHook({ queryString, suffix, responseDataType, requestParams,
107
119
  ts.factory.createUnionTypeNode([
108
120
  ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("queryKey")),
109
121
  ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("queryFn")),
110
- ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("initialData")),
111
122
  ]),
112
123
  ])),
113
124
  ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createCallExpression(ts.factory.createIdentifier(queryString), [
package/dist/generate.mjs CHANGED
@@ -22,6 +22,9 @@ export async function generate(options, version) {
22
22
  else if (value === "false") {
23
23
  acc[typedKey] = false;
24
24
  }
25
+ else {
26
+ acc[typedKey] = typedValue;
27
+ }
25
28
  return acc;
26
29
  }, options);
27
30
  const config = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7nohe/openapi-react-query-codegen",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "OpenAPI React Query Codegen",
5
5
  "bin": {
6
6
  "openapi-rq": "dist/cli.mjs"