GithubHelp home page GithubHelp logo

kubb-project / kubb Goto Github PK

View Code? Open in Web Editor NEW
432.0 5.0 29.0 37.65 MB

OpenAPI to TypeScript, React-Query, Zod, Zodios, Faker.js, MSW and Axios.

Home Page: https://kubb.dev

License: MIT License

JavaScript 0.53% Shell 0.01% TypeScript 99.40% Handlebars 0.06%
plugin-manager swagger typescript axios codegen kubb openapi react react-query zod vue-query solid solid-query svelte svelte-query vue swr zodios faker msw

kubb's Introduction

kubb's People

Contributors

allcontributors[bot] avatar b6pzeusbc54tvhw5jgpyw8pwz2x6gs avatar chiptus avatar dmitry-blackwave avatar github-actions[bot] avatar helt avatar jafin avatar koengommers avatar la55u avatar louis-genestier avatar ngocvantran avatar p-chan avatar pafnuty avatar raveclassic avatar ronanru avatar stijnvanhulle avatar thesumm avatar wickynilliams avatar xphentom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

kubb's Issues

Better FileManager

Sometimes we have delays causing addOrAppend to use the previous file instead of the latest file set. We can introduce a queue system when we need to add code to one specific file(mode=file).

@kubb/swagger-ts Add a `enumType` option to control "enum" types

/**
 * enumType: 'type'
 * generate "enum" types as union types
 */
export const notificationSeverity = {
    "Info": 0,
    "Success": 1,
    "Warn": 2,
    "Error": 3,
    "Fatal": 4
} as const;
export type NotificationSeverity = (typeof notificationSeverity)[keyof typeof notificationSeverity];
/**
 * enumType: 'enum'
 * generate "enum" types as enums
 */
export enum NotificationSeverity {
    Info = 0,
    Success = 1,
    Warn = 2,
    Error = 3,
    Fatal = 4,
  }

Plugins config as JSON format

Make it possible to use plugins in JSON format(inside a ts/js file).

  • Check if the plugin is a function or an array

Option Function

import { defineConfig } from '@kubb/core'
import createSwagger from '@kubb/swagger'
import createSwaggerTypescript from '@kubb/swagger-typescript'
import createSwaggerReactQuery from '@kubb/swagger-react-query'

export default defineConfig({
  root: '.',
  input: {
    path: './petStore.yaml',
  },
  output: {
    path: './src/gen',
    clean: true,
  },
  hooks: {
    done: 'eslint --fix ./src/gen',
  },
  logLevel: 'warn',
  plugins: [createSwagger({ version: '3' }), createSwaggerTypescript({ output: 'models' }), createSwaggerReactQuery({ output: './hooks' })],
})

Option JSON

import { defineConfig } from '@kubb/core'
import createSwagger from '@kubb/swagger'
import createSwaggerTypescript from '@kubb/swagger-typescript'
import createSwaggerReactQuery from '@kubb/swagger-react-query'

export default defineConfig({
  root: '.',
  input: {
    path: './petStore.yaml',
  },
  output: {
    path: './src/gen',
    clean: true,
  },
  hooks: {
    done: 'eslint --fix ./src/gen',
  },
  logLevel: 'warn',
  plugins: [["@kubb/swagger", { version: '3' }], ["@kubb/swagger-typescript", { output: 'models' }], ["@kubb/swagger-react-query", { output: 'hooks' }]
})

@kubb/swagger-client `xxxDeleteQueryParams` is lost

image

import { defineConfig } from "@kubb/core";
import createSwagger from "@kubb/swagger";
import createSwaggerTS from "@kubb/swagger-ts";
import createSwaggerClient from "@kubb/swagger-client";
import createSwaggerTanstackQuery from "@kubb/swagger-tanstack-query";

const download = require("download");

export default defineConfig(async () => {
  await download("https://demo.aspnetzero.com/swagger/v1/swagger.json", ".");

  return {
    root: ".",
    input: {
      path: "./swagger.json",
    },
    output: {
      path: "./src/api",
      clean: true,
    },
    logLevel: "warn",
    plugins: [
      createSwagger({ output: false }),
      createSwaggerTS({ output: "models", groupBy: "tag" }),
      createSwaggerClient({ output: "./clients", groupBy: "tag" }),
      createSwaggerTanstackQuery({
        output: "./hooks",
        groupBy: "tag",
        framework: "react",
      }),
    ],
  };
});

@kubb-swagger-zodios

Create a Zodios generation based on the @kubb-swagger-zod plugin

import { makeApi, Zodios, type ZodiosOptions } from "@zodios/core";
import { z } from "zod";

const Category = z.object({ id: z.number().int(), name: z.string() }).partial();
const Tag = z.object({ id: z.number().int(), name: z.string() }).partial();
const Pet = z.object({
    id: z.number().int().optional(),
    name: z.string(),
    category: Category.optional(),
    photoUrls: z.array(z.string()),
    tags: z.array(Tag).optional(),
    status: z.enum(["available", "pending", "sold"]).optional(),
});
const ApiResponse = z.object({ code: z.number().int(), type: z.string(), message: z.string() }).partial();
const Order = z
    .object({
        id: z.number().int(),
        petId: z.number().int(),
        quantity: z.number().int(),
        shipDate: z.string().datetime(),
        status: z.enum(["placed", "approved", "delivered"]),
        complete: z.boolean(),
    })
    .partial();
const User = z
    .object({
        id: z.number().int(),
        username: z.string(),
        firstName: z.string(),
        lastName: z.string(),
        email: z.string(),
        password: z.string(),
        phone: z.string(),
        userStatus: z.number().int(),
    })
    .partial();

export const schemas = {
    Category,
    Tag,
    Pet,
    ApiResponse,
    Order,
    User,
};

const endpoints = makeApi([
    {
        method: "put",
        path: "/pet",
        description: `Update an existing pet by Id`,
        requestFormat: "json",
        parameters: [
            {
                name: "body",
                description: `Update an existent pet in the store`,
                type: "Body",
                schema: Pet,
            },
        ],
        response: Pet,
        errors: [
            {
                status: 400,
                description: `Invalid ID supplied`,
                schema: z.void(),
            },
            {
                status: 404,
                description: `Pet not found`,
                schema: z.void(),
            },
            {
                status: 405,
                description: `Validation exception`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "post",
        path: "/pet",
        description: `Add a new pet to the store`,
        requestFormat: "json",
        parameters: [
            {
                name: "body",
                description: `Create a new pet in the store`,
                type: "Body",
                schema: Pet,
            },
        ],
        response: Pet,
        errors: [
            {
                status: 405,
                description: `Invalid input`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "get",
        path: "/pet/:petId",
        description: `Returns a single pet`,
        requestFormat: "json",
        parameters: [
            {
                name: "petId",
                type: "Path",
                schema: z.number().int(),
            },
        ],
        response: Pet,
        errors: [
            {
                status: 400,
                description: `Invalid ID supplied`,
                schema: z.void(),
            },
            {
                status: 404,
                description: `Pet not found`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "post",
        path: "/pet/:petId",
        requestFormat: "json",
        parameters: [
            {
                name: "petId",
                type: "Path",
                schema: z.number().int(),
            },
            {
                name: "name",
                type: "Query",
                schema: z.string().optional(),
            },
            {
                name: "status",
                type: "Query",
                schema: z.string().optional(),
            },
        ],
        response: z.void(),
        errors: [
            {
                status: 405,
                description: `Invalid input`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "delete",
        path: "/pet/:petId",
        description: `delete a pet`,
        requestFormat: "json",
        parameters: [
            {
                name: "api_key",
                type: "Header",
                schema: z.string().optional(),
            },
            {
                name: "petId",
                type: "Path",
                schema: z.number().int(),
            },
        ],
        response: z.void(),
        errors: [
            {
                status: 400,
                description: `Invalid pet value`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "post",
        path: "/pet/:petId/uploadImage",
        requestFormat: "binary",
        parameters: [
            {
                name: "body",
                type: "Body",
                schema: z.instanceof(File),
            },
            {
                name: "petId",
                type: "Path",
                schema: z.number().int(),
            },
            {
                name: "additionalMetadata",
                type: "Query",
                schema: z.string().optional(),
            },
        ],
        response: ApiResponse,
    },
    {
        method: "get",
        path: "/pet/findByStatus",
        description: `Multiple status values can be provided with comma separated strings`,
        requestFormat: "json",
        parameters: [
            {
                name: "status",
                type: "Query",
                schema: z.enum(["available", "pending", "sold"]).optional().default("available"),
            },
        ],
        response: z.array(Pet),
        errors: [
            {
                status: 400,
                description: `Invalid status value`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "get",
        path: "/pet/findByTags",
        description: `Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.`,
        requestFormat: "json",
        parameters: [
            {
                name: "tags",
                type: "Query",
                schema: z.array(z.string()).optional(),
            },
        ],
        response: z.array(Pet),
        errors: [
            {
                status: 400,
                description: `Invalid tag value`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "get",
        path: "/store/inventory",
        description: `Returns a map of status codes to quantities`,
        requestFormat: "json",
        response: z.record(z.number()),
    },
    {
        method: "post",
        path: "/store/order",
        description: `Place a new order in the store`,
        requestFormat: "json",
        parameters: [
            {
                name: "body",
                type: "Body",
                schema: Order,
            },
        ],
        response: Order,
        errors: [
            {
                status: 405,
                description: `Invalid input`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "get",
        path: "/store/order/:orderId",
        description: `For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.`,
        requestFormat: "json",
        parameters: [
            {
                name: "orderId",
                type: "Path",
                schema: z.number().int(),
            },
        ],
        response: Order,
        errors: [
            {
                status: 400,
                description: `Invalid ID supplied`,
                schema: z.void(),
            },
            {
                status: 404,
                description: `Order not found`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "delete",
        path: "/store/order/:orderId",
        description: `For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors`,
        requestFormat: "json",
        parameters: [
            {
                name: "orderId",
                type: "Path",
                schema: z.number().int(),
            },
        ],
        response: z.void(),
        errors: [
            {
                status: 400,
                description: `Invalid ID supplied`,
                schema: z.void(),
            },
            {
                status: 404,
                description: `Order not found`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "post",
        path: "/user",
        description: `This can only be done by the logged in user.`,
        requestFormat: "json",
        parameters: [
            {
                name: "body",
                description: `Created user object`,
                type: "Body",
                schema: User,
            },
        ],
        response: z.void(),
    },
    {
        method: "get",
        path: "/user/:username",
        requestFormat: "json",
        parameters: [
            {
                name: "username",
                type: "Path",
                schema: z.string(),
            },
        ],
        response: User,
        errors: [
            {
                status: 400,
                description: `Invalid username supplied`,
                schema: z.void(),
            },
            {
                status: 404,
                description: `User not found`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "put",
        path: "/user/:username",
        description: `This can only be done by the logged in user.`,
        requestFormat: "json",
        parameters: [
            {
                name: "body",
                description: `Update an existent user in the store`,
                type: "Body",
                schema: User,
            },
            {
                name: "username",
                type: "Path",
                schema: z.string(),
            },
        ],
        response: z.void(),
    },
    {
        method: "delete",
        path: "/user/:username",
        description: `This can only be done by the logged in user.`,
        requestFormat: "json",
        parameters: [
            {
                name: "username",
                type: "Path",
                schema: z.string(),
            },
        ],
        response: z.void(),
        errors: [
            {
                status: 400,
                description: `Invalid username supplied`,
                schema: z.void(),
            },
            {
                status: 404,
                description: `User not found`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "post",
        path: "/user/createWithList",
        description: `Creates list of users with given input array`,
        requestFormat: "json",
        parameters: [
            {
                name: "body",
                type: "Body",
                schema: z.array(User),
            },
        ],
        response: User,
    },
    {
        method: "get",
        path: "/user/login",
        requestFormat: "json",
        parameters: [
            {
                name: "username",
                type: "Query",
                schema: z.string().optional(),
            },
            {
                name: "password",
                type: "Query",
                schema: z.string().optional(),
            },
        ],
        response: z.string(),
        errors: [
            {
                status: 400,
                description: `Invalid username/password supplied`,
                schema: z.void(),
            },
        ],
    },
    {
        method: "get",
        path: "/user/logout",
        requestFormat: "json",
        response: z.void(),
    },
]);

export const api = new Zodios(endpoints);

export function createApiClient(baseUrl: string, options?: ZodiosOptions) {
    return new Zodios(baseUrl, endpoints, options);
}

if input.path is a remote url, there will be a download error. (on Windows)

$ kubb --debug
 💾 Config loaded
 Something went wrong

{
  stack: 'ResolverError: Error downloading http://localhost:62114/swagger/v1/swagger.json \n' +
    'socket hang up\n' +
    '    at D:\\Workspace\\ICMS\\v2\\reactjs\\node_modules\\@apidevtools\\json-schema-ref-parser\\lib\\resolvers\\http.js:127:16',
  code: 'ERESOLVER',
  message: 'Error downloading http://localhost:62114/swagger/v1/swagger.json \n' +
    'socket hang up',
  source: 'http://localhost:62114/swagger/v1/swagger.json',
  path: null,
  toJSON: [Function: toJSON],
  ioErrorCode: 'ECONNRESET',
  name: 'ResolverError',
  toString: [Function: toString]
}


Error: Error downloading http://localhost:62114/swagger/v1/swagger.json 
socket hang up (plugin: swagger-tanstack-query, hook: buildStart)

    at PluginManager.catcher (file:///D:/Workspace/ICMS/v2/reactjs/node_modules/@kubb/core/src/managers/pluginManager/PluginManager.ts:355:5)
    at file:///D:/Workspace/ICMS/v2/reactjs/node_modules/@kubb/core/src/managers/pluginManager/PluginManager.ts:320:9
    at async Promise.all (index 4)
    at async buildImplementation (file:///D:/Workspace/ICMS/v2/reactjs/node_modules/@kubb/core/src/build.ts:71:19)
    at async file:///D:/Workspace/ICMS/v2/reactjs/node_modules/@kubb/core/src/build.ts:83:3 {
  [cause]: {
    stack: 'ResolverError: Error downloading http://localhost:62114/swagger/v1/swagger.json \n' +
      'socket hang up\n' +
      '    at D:\\Workspace\\ICMS\\v2\\reactjs\\node_modules\\@apidevtools\\json-schema-ref-parser\\lib\\resolvers\\http.js:127:16',
    code: 'ERESOLVER',
    message: 'Error downloading http://localhost:62114/swagger/v1/swagger.json \n' +
      'socket hang up',
    source: 'http://localhost:62114/swagger/v1/swagger.json',
    path: null,
    toJSON: [Function: toJSON],
    ioErrorCode: 'ECONNRESET',
    name: 'ResolverError',
    toString: [Function: toString]
  }
}
Done in 7.02s.

Specify the base URL for the generated requests

What is the problem this feature would solve?

If your API Server is on a separate domain or just routed with an URL prefix like /api/, there seem to be no way of telling this to the generator. This results in manual editing the generated files to prepend the "baseUrl" before the generated paths.

What is the feature you are proposing to solve the problem?

At this point, I can think of 2 possible ways to tackle the issue.

  • One could use an entry in the kubb config.
  • Another way could be to read the base URL from severs.url field in the openapi input file.
  • In both ways you can just stick it in front of each generated URL in the queryFn.

The config way being more versatile, if the openapi file does not match the current routing (i.e. local development).
Reading the information from the openapi file seems to be more standart-ish (i.e. @7nohe/openapi-react-query-codegen seems to do it that way).

What alternatives have you considered?

No response

Add queue instead of eventEmitter

Some big Swagger files are causing a big delay in the FileManager.
Add logs so with the debug flag we can track the status of the queue/FileManager

generation error: object is being an array in typescript

Hey again,

so I was checking the generated types and discovered that there is a model inside the swagger is being transferred to array of the same type
so in json file

              "references": {
                "type": "object",
                "nullable": false,
                "items": { "$ref": "#/components/schemas/references" }
              },
              "suppliers": { "type": "array", "items": { "$ref": "#/components/schemas/supplier" } }

and in ts model it's

  /**
     * @type object
     */
    references: References[]
    /**
     * @type array
     */
    suppliers: Supplier[]

another example

  /**
   * @type object
   */
  quoted_price: Pricing[]
  /**
   * @type object | undefined
   */
  booking_price?: Pricing[] | undefined
  /**
   * @description The latest booking status that this room has
   * @type string
   */

link for testing: https://codesandbox.io/p/sandbox/jovial-artem-qgz29y?file=%2Fbackend.json&selection=%5B%7B%22endColumn%22%3A101%2C%22endLineNumber%22%3A75%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A70%7D%5D

thank you
Omar

cli: hide file path

Hello, Nice to see you are back 👍

Could be possible to have some flag to hide the file path from the cli output or the spinners?
Maybe using flag to enable or disable the isSilent opt of ora.

Context: I'm using a url with a private access token as input path and would like it not to be logged.

Thanks for you work, kubb is really cool!

Windows Support

Weird imports when generating clients

import client from './........client'
import type { AddPetRequest, AddPetResponse } from './......models\tsAddPet'

@kubb/swagger-zod default export

Hey!! , so when trying to generate zod validation from schema , the code is always generated with Zod default import

import z from 'zod';

export const petSchema = z.object({
    id: z.number(),
    name: z.string(),
    tag: z.string().optional(),
});

But Zod don"t have a default export it should always be

import {z} from 'zod';

I suggest adjusting the order of data and params, sometimes data is not needed

image

    "/api/TokenAuth/ImpersonatedAuthenticate": {
      "post": {
        "tags": [
          "TokenAuth"
        ],
        "operationId": "ImpersonatedAuthenticate",
        "parameters": [
          {
            "name": "impersonationToken",
            "in": "query",
            "schema": {
              "type": "string",
              "nullable": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ImpersonatedAuthenticateResultModel"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImpersonatedAuthenticateResultModel"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImpersonatedAuthenticateResultModel"
                }
              }
            }
          }
        }
      }
    },

[bug] TypeError: s.replace is not a function

TypeError: s.replace is not a function
    at escapeString (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:16192:14)
    at escapeNonAsciiString (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:16195:9)
    at getLiteralText (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:13588:24)
    at getLiteralTextOfNode (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:113610:14)
    at emitLiteral (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:110871:20)
    at pipelineEmitWithHintWorker (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:110685:20)
    at pipelineEmitWithHint (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:110322:9)
    at pipelineEmitWithComments (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:114101:7)
    at pipelineEmit (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:110271:7)
    at emitExpression (D:\Workspace\examples\react-query\node_modules\typescript\lib\typescript.js:110255:7)

kubb.config.ts

import { defineConfig } from '@kubb/core'
import createSwagger from '@kubb/swagger'
import createSwaggerTS from '@kubb/swagger-ts'
import createSwaggerTanstackQuery from '@kubb/swagger-tanstack-query'

export default defineConfig({
  root: '.',
  input: {
    //path: './petStore.yaml',
    //path: 'https://zwyunqismr.zgkw.cn/swagger/v1/swagger.json',
    path: 'https://demo.aspnetzero.com/swagger/v1/swagger.json',
  },
  output: {
    path: './src/gen',
    clean: true,
  },
  // hooks: {
  //   done: 'eslint --fix ./src/gen',
  // },
  logLevel: 'warn',
  plugins: [
    createSwagger({ output: false }),
    createSwaggerTS({ output: 'models' }),
    createSwaggerTanstackQuery({ output: './hooks', groupBy: 'tag', framework: 'react' }),
  ],
})

Nice project

Hello @stijnvanhulle, it's a really nice project and I really like the way you structured it. What do you think about helping me on orval or merging our project somehow? I don't have a lot of time to maintain and improve it so would be great to combine forces. Let me know what you think about this.

DELETE method lost parameters

https://demo.aspnetzero.com/swagger/v1/swagger.json

    "/api/services/app/User/DeleteUser": {
      "delete": {
        "tags": [
          "User"
        ],
        "operationId": "DeleteUser",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    },
import client from "../../../utils/axios-client";
import type { DeleteUserRequest, DeleteUserResponse } from "../../models/User/DeleteUser";

        /**
* @link /api/services/app/User/DeleteUser
*/
        export function deleteUser <TData = DeleteUserResponse, TVariables = DeleteUserRequest>() {
          return client<TData, TVariables>({
            method: "delete",
            url: `/api/services/app/User/DeleteUser`
            params, // missing !!!
          });
        };

DeleteUser method lost

        "parameters": [
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],

some problems about groupBy, exportAs

here is my config

import { defineConfig } from "@kubb/core";
import createSwagger from "@kubb/swagger";
import createSwaggerTS from "@kubb/swagger-ts";
import createSwaggerTanstackQuery from "@kubb/swagger-tanstack-query";

export default defineConfig(async () => {
  const client = "./src/utils/axios-client.ts";

  return {
    root: ".",
    input: { path: "swagger.json" },
    output: { path: "./src/api", clean: true },
    plugins: [
      createSwagger({ output: false }),
      createSwaggerTS({
        output: "models",
        groupBy: {
          type: "tag",
          output: "models/{{tag}}Models",
        },
        enumType: "enum",
      }),
      createSwaggerTanstackQuery({
        output: "hooks",
        groupBy: {
          type: "tag",
          output: "hooks/{{tag}}Hooks",
          exportAs: "{{tag}}Hooks",
        },
        client,
      }),
    ],
  };
});

and, output
1

but, i want
2

  1. export * as {{tag}}Hooks from "./hooks/{{tag}}Hooks"; in ./index.ts move to ./hooks/index.ts;
  2. remove export * from "./{tag}Models"; in ./models/index.ts when set groupBy.type: "tag", avoid the same object name.

codes: https://github.com/weicong/kubb-gen-test.git

thanks.

`useInfiniteQuery` support for `Tanstack/query`

What is the problem this feature would solve?

Creating support for the hook useInfiniteQuery.

What is the feature you are proposing to solve the problem?

Creating support for the hook useInfiniteQuery.

What alternatives have you considered?

No response

Example of Orval

export const getListPetsInfiniteQueryOptions = <
  TData = Awaited<ReturnType<typeof listPets>>,
  TError = ErrorType<Error>,
>(
  params?: ListPetsParams,
  version = 1,
  options?: {
    query?: UseInfiniteQueryOptions<
      Awaited<ReturnType<typeof listPets>>,
      TError,
      TData
    >;
  },
): UseInfiniteQueryOptions<
  Awaited<ReturnType<typeof listPets>>,
  TError,
  TData
> & { queryKey: QueryKey } => {
  const { query: queryOptions } = options ?? {};

  const queryKey =
    queryOptions?.queryKey ?? getListPetsQueryKey(params, version);

  const queryFn: QueryFunction<Awaited<ReturnType<typeof listPets>>> = ({
    signal,
    pageParam,
  }) => listPets({ limit: pageParam, ...params }, version, signal);

  return { queryKey, queryFn, enabled: !!version, ...queryOptions };
};
export const useListPetsInfinite = <
  TData = Awaited<ReturnType<typeof listPets>>,
  TError = ErrorType<Error>,
>(
  params?: ListPetsParams,
  version = 1,
  options?: {
    query?: UseInfiniteQueryOptions<
      Awaited<ReturnType<typeof listPets>>,
      TError,
      TData
    >;
  },
): UseInfiniteQueryResult<TData, TError> & { queryKey: QueryKey } => {
  const queryOptions = getListPetsInfiniteQueryOptions(
    params,
    version,
    options,
  );

  const query = useInfiniteQuery(queryOptions) as UseInfiniteQueryResult<
    TData,
    TError
  > & { queryKey: QueryKey };

  query.queryKey = queryOptions.queryKey;

  return query;
};

Sort on ZodBuilder and TypeBuilder

 const generated = this.items.sort(typeSorter).map(({ schema, name, description }) => {
      const generator = new ZodGenerator(this.oas, { withJSDocs: this.config.withJSDocs, nameResolver: this.config.nameResolver })
      const type = generator.build(schema, this.config.nameResolver?.(name) || name, description)
      return {
        refs: generator.refs,
        name,
        type,
      }
    })

Here we need to add sorting so that the ones with refs are placed at the bottom of the file.

Enum generate type

For example

export type PetCallingCode =
  (typeof PetCallingCode)[keyof typeof PetCallingCode];

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const PetCallingCode = {
  "+33": "+33",
  "+420": "+420",
} as const;

https://ts-ast-viewer.com/#code/KYDwDg9gTgLgBDAnmYcAKwYGECGAbPASwDsBzLCAE1QF4AoOOACiRQgDN1NcCTyrgASgDaAa2CIOCZMCkZs+ImQrUAugG46dAPTa4wAM5KYAWkqEDOAEZ5gJ4qFNLUAAVaGAxlEJhTh49rEECZQwNQeeDihdKCQsHAeEMQG8PI8SvzUcDRwAN4McABEANQAzKWFAFxFZRUANAUlACwATAAMVTWtHQ0AvnA4BglJKepAA

Generated mutations by @kubb/swagger-tanstack-query don't add types by to variables

What version of kubb is running?

1.1.8

What platform is your computer?

Windows

What steps can reproduce the bug?

I'm using this configuration for generate code:

export default defineConfig({
  root: '.',
  input: {
    path: 'http://localhost:5281/swagger/v1/swagger.json',
  },
  output: {
    path: './src/api',
    clean: true,
  },
  plugins: [
    createSwagger({ output: false }),
    ZodGenerator({ output: './validator' }),
    createSwaggerTS({}),
    createFaker({}),
    createSwaggerTanstackQuery({
      // client: 'hooks/useCustomClient.ts',
    }),
  ],
});

and run this comand: kubb --config ./kubb.config.ts

then it generates this code for usePostAPIv!Accountants():

import { useMutation, UseMutationOptions } from '@tanstack/react-query';
import client from '@kubb/swagger-client/client';
import type { PostApiV1AccountantsMutationResponse } from '.\\..\\models\\PostApiV1Accountants';

/**
 * @link /api/v1/Accountants
 */
export function usePostApiV1Accountants<TData = PostApiV1AccountantsMutationResponse, TError = unknown>(options?: {
  mutation?: UseMutationOptions<TData, TError>;
}) {
  const { mutation: mutationOptions } = options ?? {};

  return useMutation<TData, TError>({
    mutationFn: () => {
      return client<TData, TError>({
        method: 'post',
        url: `/api/v1/Accountants`,
      });
    },
    ...mutationOptions,
  });
}

What is the expected behavior?

The variable parameter of the mutate function returned by useMutation should have the corresponding type definition instead of void.
Screenshot 2023-06-11 011319

What do you see instead?

It throws a TypeScript error because the parameters are not defined.

Argument of type '{ data: { amount: number; reason: string; type: "0" | "1"; note?: string | null | undefined; }; }' is not assignable to parameter of type 'void'.ts(2345)
(property) data: {
    amount: number;
    reason: string;
    type: "0" | "1";
    note?: string | null | undefined;
}

Additional information

No response

Is there a way to convert asConst enums to PascalCase?

Instead of this:

export const bankTransferType = {
  INTERNAL: 'INTERNAL',
  EXTERNAL: 'EXTERNAL',
} as const;

I'd prefer to have this:

export const BankTransferType = {
  INTERNAL: 'INTERNAL',
  EXTERNAL: 'EXTERNAL',
} as const;

or BANK_TRANSFER_TYPE.

IS there any way to achieve this currently?

Thank you, very nice lib!

Generating clients from a subset of the input file

What is the problem this feature would solve?

The problem is that sometimes schemas and paths unrelated to the project are also included in the Swagger file to keep the definitions in the same place for multiple related projects e.g. CMS and frontend. Our current openapi.yml file is over 40k lines long, but only about 1/3 of that is actually needed for the generated frontend client. The generated types and client files are huge which makes the IDE freeze and autocompletion slow and it's generally hard to find which exact function you'd like to use if they're named similarly.

What is the feature you are proposing to solve the problem?

I propose a config option that would enable to skip certain parts of the input spec file in the generator process. This could be either per tags, schema name, path name etc. It could be a regex which, in our case would allow us to skip many tags since they reflect the controller name on the backend: PlacesCreateRequestBody vs CmsPlacesCreateRequestBody etc.

What alternatives have you considered?

Setting the config to generate each type/request into it's own file and then manually deleting the ones that I don't need via a script. (error-prone)

Thanks for your work!

`z.union` does not work on single element

What version of kubb is running?

1.1.11

What platform is your computer?

MacOS

What steps can reproduce the bug?

export const createEvent201Schema = z.union([z.lazy(() => competitorEventJsonSchema)]);

What is the expected behavior?

export const createEvent201Schema = [z.lazy(() => competitorEventJsonSchema)];

Swagger/OpenAPI file?

No response

Additional information

No response

[question] Where is the output of the hooks being logged to?

I tried adding a done hook to list the files in my CI, but I can't see any output of it even when the logLevel is set to "info". Maybe I'm using it wrong, any help would be appreciated: Here's my config:

export default defineConfig(async () => {
  return {
    root: '.',
    input: {
      path: './openapi.yml',
    },
    output: {
      path: './src/gen',
    },
    logLevel: 'info',
    hooks: {
      done: ['pwd', 'ls -lah', 'prettier --write .'],
    },
    plugins: [
      createSwagger({ output: false }),
      createSwaggerTS({ output: 'models.ts' }),
      createSwaggerClient({ client: './src/client.ts', output: 'requests.ts' }),
      createSwaggerTanstackQuery({ client: './src/client.ts', output: 'hooks.ts' }),
    ],
  };
});

And the output:

✔ 💾 Config loaded(kubb.config.ts)
✔ 🚀 Build completed(./openapi.yml)
✔ 🪂 Executed hooks(done) pwd
✔ 🪂 Executed hooks(done) ls -lah
✔ 🪂 Executed hooks(done) prettier --write .

  Plugins:      4 successful, 4 total
Generated:      4 files
     Time:      19.085s

Thank you for this wonderful lib!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.