Code Monkey home page Code Monkey logo

openapi-typescript-code-generator's Introduction

@himenon/openapi-typescript-code-generator

日本語

This library provides TypeScript type definitions and extracted parameters from OpenAPI v3.0.x compliant specifications. TypeScript AST is used to generate the code, which is accurately converted to TypeScript code. Since the parameters extracted from OpenAPI can be used freely, it can be used for automatic generation of API Client and Server Side code, load balancer configuration files, etc.

Playground

Installation

npm  i   -D @himenon/openapi-typescript-code-generator
# or
pnpm i   -D @himenon/openapi-typescript-code-generator
# or
yarn add -D @himenon/openapi-typescript-code-generator

DEMO

Usage

The example shown here can be cloned from the DEMO repository to see how it works.

Generate typedef-only code

import * as fs from "fs";

import { CodeGenerator } from "@himenon/openapi-typescript-code-generator";

const main = () => {
  const codeGenerator = new CodeGenerator("your/openapi/spec.yml");
  const code = codeGenerator.generateTypeDefinition();
  fs.writeFileSync("client.ts", code, { encoding: "utf-8" });
};

main();

Generate code containing the API Client

import * as fs from "fs";

import { CodeGenerator } from "@himenon/openapi-typescript-code-generator";
import * as Templates from "@himenon/openapi-typescript-code-generator/dist/templates";
import type * as Types from "@himenon/openapi-typescript-code-generator/dist/types";

const main = () => {
  const codeGenerator = new CodeGenerator("your/openapi/spec.yml");

  const apiClientGeneratorTemplate: Types.CodeGenerator.CustomGenerator<Templates.FunctionalApiClient.Option> = {
    generator: Templates.FunctionalApiClient.generator,
    option: {},
  };

  const code = codeGenerator.generateTypeDefinition([
    codeGenerator.getAdditionalTypeDefinitionCustomCodeGenerator(),
    apiClientGeneratorTemplate,
  ]);

  fs.writeFileSync("client.ts", code, { encoding: "utf-8" });
};

main();

The variation of template code

This library provides three types of templates

import * as Templates from "@himenon/openapi-typescript-code-generator/dist/templates";

Templates.ClassApiClient.generator;
Templates.FunctionalApiClient.generator;
Templates.CurryingFunctionalApiClient.generator;

Templates.ClassApiClient.generator

We provide a class-based API client. Please inject the API client dependency and use it instead of constructor.

export interface RequestArgs {
  httpMethod: HttpMethod;
  url: string;
  headers: ObjectLike | any;
  requestBody?: ObjectLike | any;
  requestBodyEncoding?: Record<string, Encoding>;
  queryParameters?: QueryParameters | undefined;
}

export interface ApiClient<RequestOption> {
  request: <T = SuccessResponses>(requestArgs: RequestArgs, options?: RequestOption) => Promise<T>;
}

export class Client<RequestOption> {
  private baseUrl: string;
  constructor(
    private apiClient: ApiClient<RequestOption>,
    baseUrl: string,
  ) {
    this.baseUrl = baseUrl.replace(/\/$/, "");
  }

  public async createPublisherV2<RequestContentType extends RequestContentType$createPublisherV2>(
    params: Params$createPublisherV2<RequestContentType>,
    option?: RequestOption,
  ): Promise<Response$createPublisherV2$Status$200["application/json"]> {
    const url = this.baseUrl + `/create/v2/publisher/{id}`;
    const headers = {
      "Content-Type": params.headers["Content-Type"],
      Accept: "application/json",
    };
    const requestEncodings = {
      "application/x-www-form-urlencoded": {
        color: {
          style: "form",
          explode: false,
        },
      },
      "application/json": {
        color: {
          style: "form",
          explode: false,
        },
      },
    };
    return this.apiClient.request(
      {
        httpMethod: "POST",
        url,
        headers,
        requestBody: params.requestBody,
        requestBodyEncoding: requestEncodings[params.headers["Content-Type"]],
      },
      option,
    );
  }
}

Templates.FunctionalApiClient.generator

We also provide a function-based API client that replaces the class-based API client with createClient. Please inject the API client dependency and use it.

export interface RequestArgs {
  httpMethod: HttpMethod;
  url: string;
  headers: ObjectLike | any;
  requestBody?: ObjectLike | any;
  requestBodyEncoding?: Record<string, Encoding>;
  queryParameters?: QueryParameters | undefined;
}

export interface ApiClient<RequestOption> {
  request: <T = SuccessResponses>(requestArgs: RequestArgs, options?: RequestOption) => Promise<T>;
}

export const createClient = <RequestOption>(apiClient: ApiClient<RequestOption>, baseUrl: string) => {
  const _baseUrl = baseUrl.replace(/\/$/, "");
  return {
    createPublisherV2: <RequestContentType extends RequestContentType$createPublisherV2>(
      params: Params$createPublisherV2<RequestContentType>,
      option?: RequestOption,
    ): Promise<Response$createPublisherV2$Status$200["application/json"]> => {
      const url = _baseUrl + `/create/v2/publisher/{id}`;
      const headers = {
        "Content-Type": params.headers["Content-Type"],
        Accept: "application/json",
      };
      const requestEncodings = {
        "application/x-www-form-urlencoded": {
          color: {
            style: "form",
            explode: false,
          },
        },
        "application/json": {
          color: {
            style: "form",
            explode: false,
          },
        },
      };
      return apiClient.request(
        {
          httpMethod: "POST",
          url,
          headers,
          requestBody: params.requestBody,
          requestBodyEncoding: requestEncodings[params.headers["Content-Type"]],
        },
        option,
      );
    },
  };
};

Templates.CurryingFunctionalApiClient.generator

Tree shaking support

We also provide a curried function-based API client that requires injection of API client for each operationId. The first function argument demands ApiClient while the second function argument demands RequestArgs. The ApiClient interface is different from the others, as it requires uri as an argument.

This is designed for use cases that utilize tree shaking.

export interface RequestArgs {
  httpMethod: HttpMethod;
  uri: string; // <------------------ Note that the uri
  headers: ObjectLike | any;
  requestBody?: ObjectLike | any;
  requestBodyEncoding?: Record<string, Encoding>;
  queryParameters?: QueryParameters | undefined;
}
export interface ApiClient<RequestOption> {
  request: <T = SuccessResponses>(requestArgs: RequestArgs, options?: RequestOption) => Promise<T>;
}
export const createPublisherV2 =
  <RequestOption>(apiClient: ApiClient<RequestOption>) =>
  <RequestContentType extends RequestContentType$createPublisherV2>(
    params: Params$createPublisherV2<RequestContentType>,
    option?: RequestOption,
  ): Promise<Response$createPublisherV2$Status$200["application/json"]> => {
    const uri = `/create/v2/publisher/{id}`;
    const headers = {
      "Content-Type": params.headers["Content-Type"],
      Accept: "application/json",
    };
    const requestEncodings = {
      "application/x-www-form-urlencoded": {
        color: {
          style: "form",
          explode: false,
        },
      },
      "application/json": {
        color: {
          style: "form",
          explode: false,
        },
      },
    };
    return apiClient.request(
      {
        httpMethod: "POST",
        uri,
        headers,
        requestBody: params.requestBody,
        requestBodyEncoding: requestEncodings[params.headers["Content-Type"]],
      },
      option,
    );
  };

Split the type definition file and the API Client implementation

import * as fs from "fs";

import { CodeGenerator } from "@himenon/openapi-typescript-code-generator";
import * as Templates from "@himenon/openapi-typescript-code-generator/dist/templates";
import type * as Types from "@himenon/openapi-typescript-code-generator/dist/types";

const main = () => {
  const codeGenerator = new CodeGenerator("your/openapi/spec.yml");

  const apiClientGeneratorTemplate: Types.CodeGenerator.CustomGenerator<Templates.FunctionalApiClient.Option> = {
    generator: Templates.FunctionalApiClient.generator,
    option: {},
  };

  const typeDefCode = codeGenerator.generateTypeDefinition();
  const apiClientCode = codeGenerator.generateCode([
    {
      generator: () => {
        return [`import { Schemas, Responses } from "./types";`];
      },
    },
    codeGenerator.getAdditionalTypeDefinitionCustomCodeGenerator(),
    apiClientGeneratorTemplate,
  ]);

  fs.writeFileSync("types.ts", typeDefCode, { encoding: "utf-8" });
  fs.writeFileSync("apiClient.ts", apiClientCode, { encoding: "utf-8" });
};

main();

Create a Code Template

The examples in this section can be used in the following ways

import * as fs from "fs";

import { CodeGenerator } from "@himenon/openapi-typescript-code-generator";
import type * as Types from "@himenon/openapi-typescript-code-generator/dist/types";

/** Write the definition of the Code Template here. */
const customGenerator: Types.CodeGenerator.CustomGenerator<{}> = {
  /** .... */
};

const codeGenerator = new CodeGenerator("your/openapi/spec.yml");

const code = codeGenerator.generateCode([customGenerator]);

fs.writeFileSync("output/file/name", code, { encoding: "utf-8" });

Define a text-based code template

A self-defined code generator can return an array of string.

import * as Types from "@himenon/openapi-typescript-code-generator/dist/types";

interface Option {
  showLog?: boolean;
}

const generator: Types.CodeGenerator.GenerateFunction<Option> = (payload: Types.CodeGenerator.Params[], option): string[] => {
  if (option && option.showLog) {
    console.log("show log message");
  }
  return ["Hello world"];
};

const customGenerator: Types.CodeGenerator.CustomGenerator<Option> = {
  generator: generator,
  option: {},
};

Define using the information extracted from OpenAPI Schema

The self-defined code generator can accept parameters extracted from OpenAPI Schema. See Type definitions for available parameters.

import * as Types from "@himenon/openapi-typescript-code-generator/dist/types";

interface Option {}

const generator: Types.CodeGenerator.GenerateFunction<Option> = (payload: Types.CodeGenerator.Params[], option): string[] => {
  return payload.map(params => {
    return `function ${params.operationId}() { console.log("${params.comment}") }`;
  });
};

const customGenerator: Types.CodeGenerator.CustomGenerator<Option> = {
  generator: generator,
  option: {},
};

Define any Data Types Format

Convert a Data Type with the following format to any type definition.

components:
  schemas:
    Binary:
      type: string
      format: binary
    IntOrString:
      type: string
      format: int-or-string
    AandB:
      type: string
      format: A-and-B

The option to convert the Data Type Format to an arbitrary type definition is defined as follows.

import { CodeGenerator, Option } from "@himenon/openapi-typescript-code-generator";
const option: Option = {
  convertOption: {
    formatConversions: [
      {
        selector: {
          format: "binary",
        },
        output: {
          type: ["Blob"],
        },
      },
      {
        selector: {
          format: "int-or-string",
        },
        output: {
          type: ["number", "string"],
        },
      },
      {
        selector: {
          format: "A-and-B",
        },
        output: {
          type: ["A", "B"],
          multiType: "allOf",
        },
      },
    ],
  },
};
const codeGenerator = new CodeGenerator(inputFilename, option);

The typedef generated by this will look like this

export namespace Schemas {
  export type Binary = Blob;
  export type IntOrString = number | string;
  export type AandB = A & B;
}

Define a code template with TypeScript AST

You can extend your code using the API of TypeScript AST. You can directly use the API of TypeScript AST or use the wrapper API of TypeScript AST provided by this library.

import * as Types from "@himenon/openapi-typescript-code-generator/dist/types";
import { TsGenerator } from "@himenon/openapi-typescript-code-generator/dist/api";

interface Option {}

const factory = TsGenerator.Factory.create();

const generator: Types.CodeGenerator.GenerateFunction<Option> = (
  payload: Types.CodeGenerator.Params[],
  option,
): Types.CodeGenerator.IntermediateCode[] => {
  return payload.map(params => {
    return factory.InterfaceDeclaration.create({
      export: true,
      name: params.functionName,
      members: [],
    });
  });
};

const customGenerator: Types.CodeGenerator.CustomGenerator<Option> = {
  generator: generator,
  option: {},
};

API

CodeGenerator

import { CodeGenerator } from "@himenon/openapi-typescript-code-generator";

validateOpenApiSchema

Performs validation of the input OpenAPI Schema.

generateTypeDefinition

Generates code that converts OpenAPI Schema to TypeScript type definitions.

generateCode

You can specify several of your own code generators, and the generators can use parameters extracted from OpenAPI Schema. It internally performs the conversion of an array of string or ts.Statement as a string.

For example, creating a generator in units of file divisions increases the reusability of the generator.

getCodeGeneratorParamsArray

It provides parameters extracted from OpenAPI Schema.

getAdditionalTypeDefinitionCustomCodeGenerator

This is a type definition file for Templates.FunctionalApiClient. The reason it is not included in generateTypeDefinition is that you may not use the type definition generated by this function depending on your usage.

※ The reason it is not included in generateTypeDefinition is that you may not use the type definitions generated by this function depending on your application.

TsGenerator

import { TsGenerator } from "@himenon/openapi-typescript-code-generator/dist/api";

This is a wrapper API for the TypeScript AST used internally. It is subject to change without notice.

OpenApiTools

import { OpenApiTools } from "@himenon/openapi-typescript-code-generator/dist/api";

Parser

  • OpenApiTools.Parser

This is the API for parsing OpenAPI Schema. It is subject to change without notice.

Restrictions

Directory Restrictions for Remote Reference

There is a limitation on the directory structure supported. To simplify implementation when converting directory structures to TypeScript namespaces, Remote References using $ref should only be defined in the following directory structures. If you want to extend it, please fork this repository and do it yourself.

spec.yml // entry file
components/
  headers/
  parameters/
  pathItems/
  requestBodies/
  responses/
  schemas/
  paths/

HTTP communication restrictions for Remote Reference

$ref: http://.... Currently not supported. We hope to support it in the future.

Contributions

First of all, thank you for your interest. When converting from the API specification to TypeScript code, resolving reference relationships can be particularly challenging, and there may not be enough test cases. Adding test cases is a very powerful support for stabilizing the behavior, so please report any bugs you find that are behaving strangely. Also, the basic design concepts of this repository can be found below. If you want to make changes that do not follow these concepts, please fork and extend them. If your changes are in line with the design concept, please submit a pull request or issue!

Design Concept

  • Be typedef first.
  • Typedefs should not contain any entities (file size should be 0 when typedefs are converted to .js)
  • The directory structure should be mapped to the typedef structure.
  • No dependency on any API client library.
  • Can be extended by TypeScript AST.
  • Conform to the OpenAPI specification.
  • It should be a single file to maintain portability.

Development

git clone https://github.com/Himenon/openapi-typescript-code-generator.git
cd openapi-typescript-code-generator
pnpm i
#### your change
pnpm build
pnpm run test:code:gen
pnpm run update:snapshot # if you changed
pnpm run test

Useful development tools

TypeScript AST

LICENCE

@himenon/openapi-typescript-code-generator, MIT

Reference implementation

Validation Design

openapi-typescript-code-generator's People

Contributors

dependabot[bot] avatar himenon avatar hrsh7th avatar kamijin-fanta avatar keita1714 avatar taiki-fw avatar thiry1 avatar tkow 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

openapi-typescript-code-generator's Issues

Do you need a CLI?

English

Hello to all our users. Thank you for using our site on a daily basis.

As the title says, if you had a CLI, how much functionality would you want?

This library needs to write import statements with some String to make API Client Dependency Injection and file splitting, which is surprisingly difficult to express in the CLI.

One way to look at it differently is to prepare a CLI that generates code for the generator as a more meta CLI. However, I am not too keen on this idea, because it can be solved by writing a working code in the README with a copy and paste.

So, if you have any ideas for a CLI that would be good for users, please feel free to contribute!

(Comments can be in English or Japanese.)

日本語

ユーザーの皆さんこんにちは。日々利用してくださり、ありがとうございます。

表題のとおりですが、CLIがあったとしたらどこまでの機能がほしいですか?

本ライブラリはAPI ClientをDependency Injectionしたりファイル分割するために、import文を多少のStringで記述する必要があり、CLIで表現するのは意外と難しいです。

見方を変えて、更にメタ的なCLIとしてジェネレーター用のコードを生成するCLIを用意するというのも1つの方法があります。とはいえ、READMEにコピペで動くコードを書いていれば解決する話でもあるのであまり乗り気ではありません。

ということで、ユーザーの皆さんにこういったCLIがあると良い、というアイディアがあれば気軽に投稿してみてください!

(コメントはEnglish/日本語どちらでも構いません)

Bug: Union keys with union response types don't work when each response status has different media type

Steps To Reproduce

Remove this comment out , switch yaml from remote instead of local.

run bun i && bun ./gen.ts && npx tsc --noEmit ./src/client.ts in repo.

/accounts/{accountId}/urlscanner/scan/{scanId}/screenshot:
  get:
   ...
    200:
      content:
        "image/png":
    202:
      content:
        "application/json":

This generate types

type ResponseContentType$urlscanner$get$scan$screenshot = "image/png" |  "application/json
export const urlscanner$get$scan$screenshot = <RequestOption>(apiClient: ApiClient<RequestOption>) => <ResponseContentType extends ResponseContentType$urlscanner$get$scan$screenshot>(params: Params$urlscanner$get$scan$screenshot<ResponseContentType>, option?: RequestOption): Promise<(Response$urlscanner$get$scan$screenshot$Status$200 | Response$urlscanner$get$scan$screenshot$Status$202)[ResponseContentType]>

This emits an error because ResponseContentType keys require both types above to have all properties .

The current behavior

As ever described.

The expected behavior

Generate each ResponseContentType and union

<200ResponseContentType, 202ResponseContentType> ...
  Promise<
    Response$urlscanner$get$scan$screenshot$Status$200[200ResponseContentType] | 
    Response$urlscanner$get$scan$screenshot$Status$202)[202ResponseContentType]
>

or

  Promise<
    Response$urlscanner$get$scan$screenshot$Status$200[keyof  Response$urlscanner$get$scan$screenshot$Status$200] | 
    Response$urlscanner$get$scan$screenshot$Status$202[keyof Response$urlscanner$get$scan$screenshot$Status$202]
>

or

  <ResponseContentType extends {200: keyof 200Response, 202: keyof 202Response}>
  Promise<
    Response$urlscanner$get$scan$screenshot$Status$200[ResponseContentType['200']] | 
    Response$urlscanner$get$scan$screenshot$Status$202)[ResponseContentType['202']]
>

Final approach looks suitable for me.

Bug: DevelopmentError

Steps To Reproduce

  1. copy this api definition and paste it in specs/test.yaml
  2. copy sample generator code and paste it in src/generator.ts
  3. replace path in src/generator.ts
  4. execute ts-node src/generator.ts

The current behavior

This error occured.

DevelopmentError: This is an implementation error. Please report any reproducible information below.
https://github.com/Himenon/openapi-typescript-code-generator/issues/new/choose

    at resolveLocalReference (/workspace/node_modules/@himenon/src/internal/ResolveReference/index.ts:95:15)
    at /workspace/node_modules/@himenon/src/internal/ResolveReference/index.ts:111:18
    at Array.forEach (<anonymous>)
    at resolveLocalReference (/workspace/node_modules/@himenon/src/internal/ResolveReference/index.ts:110:25)
    at /workspace/node_modules/@himenon/src/internal/ResolveReference/index.ts:111:18
    at Array.forEach (<anonymous>)
    at resolveLocalReference (/workspace/node_modules/@himenon/src/internal/ResolveReference/index.ts:110:25)
    at /workspace/node_modules/@himenon/src/internal/ResolveReference/index.ts:111:18
    at Array.forEach (<anonymous>)
    at resolveLocalReference (/workspace/node_modules/@himenon/src/internal/ResolveReference/index.ts:110:25)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.


Same issue occurred for this api definition.

The expected behavior

Bug: parameter.name is not referenced correctly

Steps To Reproduce

components:
  parameters:
    client-id:
      name: client_id
      in: path
      required: true
      description: The client ID of your GitHub app.
      schema:
        type: string

paths:
  /get/{client_id}:
    get:
      operationId: getBooks
      parameters:
        - $ref: "#/components/parameters/client-id"
      responses:
        200:
          description: "ok"

Playground

The current behavior

export interface Parameter$getBooks {
    client-id: Parameters.client-id;
}

The expected behavior

export interface Parameter$getBooks {
    client_id: Parameters.client-id;
}

BUG: nullable schema

Hi! I think this project is useful and so interesting.

I have a question. Why do generator use interface for schema definition?

I think type alias is more useful.

e.g.

https://swagger.io/specification/#:~:text=A%20true%20value%20adds%20%22null%22%20to%20the%20allowed%20type%20specified%20by%20the%20type%20keyword%2C%20only%20if%20type%20is%20explicitly%20defined%20within%20the%20same%20Schema%20Object.

schema:
  type: object
  nullable: true
  properties:
    foo:
      type: integer
type schema = { foo: number } | null;

Bug: failed to resolve a reference to inner property of an another reference

Steps To Reproduce

Failed to resolve the references following:

components:
  schemas:
    Foo: 
      type: object
      properties:
        bar:
          type: object
          properties:
            baz:
              type: string
    Bar:
      $ref: '#/components/schemas/Foo/properties/bar'
    Baz:
      $ref: '#/components/schemas/Bar/properties/baz' # failed!

The current behavior

/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/Guard.ts:58
  return !!schema.oneOf && typeof schema.oneOf !== "boolean" && Array.isArray(schema.oneOf);
                  ^
TypeError: Cannot read properties of undefined (reading 'oneOf')
    at Object.isOneOfSchema (/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/Guard.ts:58:19)
    at Object.convert (/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/toTypeNode.ts:129:13)
    at createTypeNode (/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/components/Schemas.ts:43:29)
    at /Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/components/Schemas.ts:51:19
    at Array.forEach (<anonymous>)
    at Object.generateNamespace (/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/components/Schemas.ts:30:27)
    at Parser.initialize (/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/Parser.ts:41:17)
    at new Parser (/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/Parser.ts:33:10)
    at CodeGenerator.createParser (/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/index.ts:32:12)
    at new CodeGenerator (/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/index.ts:27:24)

The expected behavior

No error to resolve the referenceBaz.

Bug: complex remote reference is broken

Steps To Reproduce

Apply this patch: @himenon/[email protected]:openapi-typescript-code-generator:test/add-broken-case

pnpm build
pnpm run test:code:gen

The current behavior

Generate Code : test/code/typedef-with-template/ref-access.ts

/Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:99
  const findSchemaByPathArray = (
                                ^
RangeError: Maximum call stack size exceeded
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:99:33)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)
    at findSchemaByPathArray (/Users/kenta.sato/src/github.com/karupanerura/openapi-typescript-code-generator/src/internal/OpenApiTools/TypeNodeContext.ts:105:14)

The expected behavior

Accept this reference.

Bug: Infer type if type is not specified

Steps To Reproduce

It is below.

The current behavior

Build Error

The expected behavior

components:
  schemas:
    Item:          # -> type Item =  { description: any }
      required:
        description
components:
  schemas:
    Items:          # -> type Items =  "a" | "b" | "c";
      enum: [a, b, c]
components:
  schemas:
    Obj:          # -> type Obj =  "a" | "b" | "c";
      properties:
        filed:
components:
  schemas:
    Obj:          # -> interface Obj { field: string }
      properties:
        filed:
          type: string
components:
  schemas:
    List:          # -> type List = string[]
      items:
        type: string

Bug: component schemas cannot be in random order

Steps To Reproduce

The following is a valid OpenAPI document. In openapi-typescript-code-generator, however, build error occurs.
(demo in the playground)

openapi: 3.0.3
info:
  title: demo
  version: 1.0.0
paths: {}

components:
  schemas:
    Consumer:
      type: object
      properties:
        provider:
          $ref: '#/components/schemas/Provider'
    Provider:
      type: integer

The current behavior

Build error occurs.

The expected behavior

We don't need to care order.

Experimental interface feedback for OpenApiOperation

EN

It exports an Interface called OpenApiOperation. This interface has an important role in the internal implementation, but at the same time it has enough information for users to create code templates.

Therefore, a change in the internal implementation may have a significant impact on the users of this interface. Please give us specific feedback on how you are using it. This will help us to minimize the scale of the change.

JP

OpenApiOperationというInterfaceをexportしています。このinterfaceは内部実装で重要な役割を持っていますが、同時に利用者がコードテンプレートを作成する上で十分な情報を持っています。

そのため、内部の実装の変更によりこのinterfaceの利用者に対して大きな影響を与えることが考えられます。どのように利用しているか具体的なフィードバックをお伝え下さい。それによって変更の規模を最小限に留めることが可能です。

Bug: Parameters field in the PathItem is ignored

Bug: Incorrectly publishing for ESM

Steps To Reproduce

enviroment

% node --version                     
v20.11.0
  • package.json
{
  "name": "openapi-esm",
  "type": "module",
  "scripts": {
    "generate": "node index.js"
  },
  "devDependencies": {
    "@himenon/openapi-typescript-code-generator": "^0.27.4"
  }
}
  • index.js
import * as fs from "fs";

import { CodeGenerator } from "@himenon/openapi-typescript-code-generator";

const main = () => {
  const codeGenerator = new CodeGenerator("spec/openapi.yml");
  const code = codeGenerator.generateTypeDefinition();
  fs.writeFileSync("client.ts", code, { encoding: "utf-8" });
};

main();

The current behavior

% node index.js                      
file:///path/to/repo/index.js:3
import { CodeGenerator } from "@himenon/openapi-typescript-code-generator";
         ^^^^^^^^^^^^^
SyntaxError: Named export 'CodeGenerator' not found. The requested module '@himenon/openapi-typescript-code-generator' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '@himenon/openapi-typescript-code-generator';
const { CodeGenerator } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)

The expected behavior

To success generate files.

my opinion

This project is made from CommonJS currently.
And Node.js ESM needs whether package.json's "type": "module" or .mjs file extension.

Thus, entry package.json file should be below.

{
  "name": "@himenon/openapi-typescript-code-generator",
  ...
  "module": "$esm/index.mjs",
  "browser": "$esm/index.mjs",
  ...
  "exports": {
    ...
    ".": {
      "import": "./$esm/index.mjs",
      "node": "./$cjs/index.js",
      "browser": "./$esm/index.mjs",
      "default": "./$cjs/index.js",
      "types": "./$types/index.js"
    },
    ...
  }
}

The build .mjs files needs to be strict modulespecifier for import declaration.

// ❌
import * as Api from "./api";
// ✅
import * as Api from "./api.mjs";

feat: PDFをダウンロードするAPIの型

freeeAPIスキーマ
https://raw.githubusercontent.com/freee/freee-api-schema/master/v2020_06_15/open-api-3/api-schema.yml
の下記の箇所

paths:
  "/api/1/journals/reports/{id}/download"
    get:
      responses:
        '200':
          content:
            application/pdf:
              schema:
                format: binary
                type: string

他、

          content:
            image/*:
              schema:
                format: binary
                type: string

というパターンもあります。

The current behavior

返り値の型が「string」になる

The expected behavior

blobなどbinary系の型を付ける

なお、実際にどの型を付けるべきかは、ユーザ側の環境依存で変わる可能性が高いので、ユーザ側で付ける型を変えられれば理想的だと思います

Bug: RequestBody type omits application/json when application/json doesn't have schema property

Steps To Reproduce

If oas file has lacked schema property about media type, RequestBody type omits application/json type. This cause build error because Param type generates reference to it. See followed by an example.

requestBody:
        description: The request body is currently not used.
        required: true
        content:
          application/json:
            example: '{}'

I thought it's loose schema definition, but OAS doesn't require it must be specified.

Run bun i && bun gen.ts && npx tsc --noEmit ./src/client.ts in https://github.com/tkow/cloudflare-ts-client.

スクリーンショット 2024-02-12 17 06 35

The current behavior

Params type generate requestBody refers to RequstBodyType,

requestBody: RequestBody$zone$cache$settings$start$cache$reserve$clear["application/json"]

but empty RequestBody type is generated.

export interface RequestBody$zone$cache$settings$start$cache$reserve$clear {
}

The expected behavior

It may be suitable that it generates

export interface RequestBody$zone$cache$settings$start$cache$reserve$clear {
  "application/json": any
}

Bug: path parameter should be encoded

Because the path parameters are not encoded, passing values containing '/' or '?' will result in the destination path becoming unintended. I guess we shoulld encode the path parameters using encodeURIComponent.

Steps To Reproduce

// generated code
export const createClient = <RequestOption>(apiClient: ApiClient<RequestOption>, baseUrl: string) => {
    const _baseUrl = baseUrl.replace(/\/$/, "");
    return {
        getBook: (params: Params$getBook, option?: RequestOption): Promise<Response$getBook$Status$200["application/json"]> => {
            const url = _baseUrl + `/get/book/${params.parameter.id}`;
            const headers = {
                Accept: "application/json"
            };
            return apiClient.request({
                httpMethod: "GET",
                url,
                headers
            }, option);
        },
    }
}
const client = createClient(something);
client.getBook({
  params: {
    parameter: {
      id: "foo/bar" // params contains `/`
    },
  }
})

The current behavior

request to /get/book/foo/bar

The expected behavior

request to /get/book/foo%2Fbar

Bug: test:snapshot is failed on main branch

Steps To Reproduce

pnpm clean && pnpm build
pnpm test:snapshot

The current behavior

pnpm test:snapshot
% pnpm test:snapshot

> @himenon/[email protected] test:snapshot /Users/karupanerura/src/github.com/karupanerura/openapi-typescript-code-generator
> jest -c ./jest.snapshot.config.js


 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts

 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts

 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts

 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts
 PASS  test/__tests__/parameter-test.ts

 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts

 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts
 FAIL  test/__tests__/unknown-schema-domain-test.ts

 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts
  ● Unknown › client.ts

    expect(received).toMatchSnapshot()

    Snapshot name: `Unknown client.ts 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/unknown.schema.domain/client.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 | });
      13 |

      at Object.<anonymous> (test/__tests__/unknown-schema-domain-test.ts:10:18)


 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts
 › 1 snapshot failed.

 RUNS  test/__tests__/template-only-test.ts
 RUNS  test/__tests__/typedef-only-test.ts

 FAIL  test/__tests__/format.domain.ts

  ● Format Types › format.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Format Types format.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/format.domain/code.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 | });
      13 |

      at Object.<anonymous> (test/__tests__/format.domain.ts:10:18)


 › 1 snapshot failed.

 FAIL  test/__tests__/template-only-test.ts

  ● Template Only › api.test.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Template Only api.test.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/template-only/api.test.domain.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 |   test("async-api.test.domain", () => {
      13 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/template-only/sync-api.test.domain.ts"), { encoding: "utf-8" });

      at Object.<anonymous> (test/__tests__/template-only-test.ts:10:18)

  ● Template Only › async-api.test.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Template Only async-api.test.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

      13 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/template-only/sync-api.test.domain.ts"), { encoding: "utf-8" });
      14 |     const text = Utils.replaceVersionInfo(generateCode);
    > 15 |     expect(text).toMatchSnapshot();
         |                  ^
      16 |   });
      17 |   test("infer.domain", () => {
      18 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/template-only/infer.domain.ts"), { encoding: "utf-8" });

      at Object.<anonymous> (test/__tests__/template-only-test.ts:15:18)

  ● Template Only › infer.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Template Only infer.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

      18 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/template-only/infer.domain.ts"), { encoding: "utf-8" });
      19 |     const text = Utils.replaceVersionInfo(generateCode);
    > 20 |     expect(text).toMatchSnapshot();
         |                  ^
      21 |   });
      22 | });
      23 |

      at Object.<anonymous> (test/__tests__/template-only-test.ts:20:18)


 › 3 snapshots failed.

 FAIL  test/__tests__/multi-type.test.domain.ts

  ● Multi Type › types

    expect(received).toMatchSnapshot()

    Snapshot name: `Multi Type types 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.0.1
      // 
      // License  : MIT
      // 

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/mulit-type-test.domain/types.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 |
      13 |   test("apiClient", () => {

      at Object.<anonymous> (test/__tests__/multi-type.test.domain.ts:10:18)

  ● Multi Type › apiClient

    expect(received).toMatchSnapshot()

    Snapshot name: `Multi Type apiClient 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.0.1
      // 
      // License  : MIT
      // 

      14 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/mulit-type-test.domain/apiClient.ts"), { encoding: "utf-8" });
      15 |     const text = Utils.replaceVersionInfo(generateCode);
    > 16 |     expect(text).toMatchSnapshot();
         |                  ^
      17 |   });
      18 | });
      19 |

      at Object.<anonymous> (test/__tests__/multi-type.test.domain.ts:16:18)


 › 2 snapshots failed.

 FAIL  test/__tests__/typedef-only-test.ts

  ● Typedef only › typedef-api.test.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Typedef only typedef-api.test.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-only/api.test.domain.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 |   test("typedef-infer.domain", () => {
      13 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-only/infer.domain.ts"), { encoding: "utf-8" });

      at Object.<anonymous> (test/__tests__/typedef-only-test.ts:10:18)

  ● Typedef only › typedef-infer.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Typedef only typedef-infer.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

      13 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-only/infer.domain.ts"), { encoding: "utf-8" });
      14 |     const text = Utils.replaceVersionInfo(generateCode);
    > 15 |     expect(text).toMatchSnapshot();
         |                  ^
      16 |   });
      17 | });
      18 |

      at Object.<anonymous> (test/__tests__/typedef-only-test.ts:15:18)


 › 2 snapshots failed.

 FAIL  test/__tests__/spit-code-test.ts

  ● Split Code › types

    expect(received).toMatchSnapshot()

    Snapshot name: `Split Code types 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/split/types.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 |
      13 |   test("apiClient", () => {

      at Object.<anonymous> (test/__tests__/spit-code-test.ts:10:18)

  ● Split Code › apiClient

    expect(received).toMatchSnapshot()

    Snapshot name: `Split Code apiClient 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

      14 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/split/apiClient.ts"), { encoding: "utf-8" });
      15 |     const text = Utils.replaceVersionInfo(generateCode);
    > 16 |     expect(text).toMatchSnapshot();
         |                  ^
      17 |   });
      18 | });
      19 |

      at Object.<anonymous> (test/__tests__/spit-code-test.ts:16:18)


 › 2 snapshots failed.

 FAIL  test/__tests__/argo-rollout-test.ts

  ● Argo Rollout › client.ts

    expect(received).toMatchSnapshot()

    Snapshot name: `Argo Rollout client.ts 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.0.0
      // 
      // 
      ↵

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/argo-rollout/client.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 | });
      13 |

      at Object.<anonymous> (test/__tests__/argo-rollout-test.ts:10:18)


 › 1 snapshot failed.

 FAIL  test/__tests__/typedef-with-template-test.ts
  ● Typedef with template › api.test.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Typedef with template api.test.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/api.test.domain.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 |   test("api.v2.domain", () => {
      13 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/api.v2.domain.ts"), { encoding: "utf-8" });

      at Object.<anonymous> (test/__tests__/typedef-with-template-test.ts:10:18)

  ● Typedef with template › api.v2.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Typedef with template api.v2.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

      13 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/api.v2.domain.ts"), { encoding: "utf-8" });
      14 |     const text = Utils.replaceVersionInfo(generateCode);
    > 15 |     expect(text).toMatchSnapshot();
         |                  ^
      16 |   });
      17 |   test("async-api.test.domain", () => {
      18 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/sync-api.test.domain.ts"), { encoding: "utf-8" });

      at Object.<anonymous> (test/__tests__/typedef-with-template-test.ts:15:18)

  ● Typedef with template › async-api.test.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Typedef with template async-api.test.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

      18 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/sync-api.test.domain.ts"), { encoding: "utf-8" });
      19 |     const text = Utils.replaceVersionInfo(generateCode);
    > 20 |     expect(text).toMatchSnapshot();
         |                  ^
      21 |   });
      22 |   test("infer.domain", () => {
      23 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/infer.domain.ts"), { encoding: "utf-8" });

      at Object.<anonymous> (test/__tests__/typedef-with-template-test.ts:20:18)

  ● Typedef with template › infer.domain

    expect(received).toMatchSnapshot()

    Snapshot name: `Typedef with template infer.domain 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

      23 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/infer.domain.ts"), { encoding: "utf-8" });
      24 |     const text = Utils.replaceVersionInfo(generateCode);
    > 25 |     expect(text).toMatchSnapshot();
         |                  ^
      26 |   });
      27 |   test("ref-access", () => {
      28 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/ref-access.ts"), { encoding: "utf-8" });

      at Object.<anonymous> (test/__tests__/typedef-with-template-test.ts:25:18)

  ● Typedef with template › ref-access

    expect(received).toMatchSnapshot()

    Snapshot name: `Typedef with template ref-access 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.1.0
      // 
      // License  : MIT
      // 

      28 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/ref-access.ts"), { encoding: "utf-8" });
      29 |     const text = Utils.replaceVersionInfo(generateCode);
    > 30 |     expect(text).toMatchSnapshot();
         |                  ^
      31 |   });
      32 | });
      33 |

      at Object.<anonymous> (test/__tests__/typedef-with-template-test.ts:30:18)

 › 5 snapshots failed.
 FAIL  test/__tests__/kubernetes-test.ts
  ● Kubernetes › client-v1.18.5.ts

    expect(received).toMatchSnapshot()

    Snapshot name: `Kubernetes client-v1.18.5.ts 1`

    - Snapshot  - 1
    + Received  + 1

    @@ -1,7 +1,7 @@
      // 
    - // Generated by @himenon/openapi-typescript-code-generator
    + // Generated by dummy-name vdummy
      // 
      // OpenApi : 3.0.0
      // 
      // 
      ↵

       8 |     const generateCode = fs.readFileSync(path.join(__dirname, "../code/kubernetes/client-v1.18.5.ts"), { encoding: "utf-8" });
       9 |     const text = Utils.replaceVersionInfo(generateCode);
    > 10 |     expect(text).toMatchSnapshot();
         |                  ^
      11 |   });
      12 | });
      13 |

      at Object.<anonymous> (test/__tests__/kubernetes-test.ts:10:18)

 › 1 snapshot failed.
 PASS  test/__tests__/client-generate-test.ts (6.861 s)
  ● Console

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSON

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrArray

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrStringArray

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrArray

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSON

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrArray

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrStringArray

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrArray

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)

    console.log
      Warning: Schema could not be identified. Therefore, it is treated as any. io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray

      at Object.warn (src/internal/Logger/index.ts:19:11)
          at Array.forEach (<anonymous>)


Snapshot Summary
 › 18 snapshots failed from 9 test suites. Inspect your code changes or run `npm run test:snapshot -- -u` to update them.

Test Suites: 9 failed, 2 passed, 11 total
Tests:       18 failed, 4 passed, 22 total
Snapshots:   18 failed, 2 passed, 20 total
Time:        8.013 s, estimated 11 s
Ran all test suites.
 ELIFECYCLE  Command failed with exit code 1.

The expected behavior

pass all snapshot tests

Bug: `encoding` option in requestBody is not respected.

Steps To Reproduce

According to the OpenAPI 3.0 definition, requestBody has encoding option.
However, the encoding option is missing from the generated code.

The current behavior

encoding option is dissappear

The expected behavior

encoding option should be respected.
IMO, if Content-Type is application/x-www-form-urlencoded, encoding option is required for the type of requestBody.

Bug: `paths` like `/foo/{id}.json` does not work

Steps To Reproduce

For example,

openapi: 3.1.0
info:
  version: 1.0.0
  title: ref.access
  description: Library test schema
  license:
    name: MIT

components:
  schemas:
    Book:
      type: object
      properties:
        name:
          type: string

paths:
  # 🈁 
  /book/{id}.json:
    parameters:
      - name: id
        in: path
        required: true
        description: Book ID
        schema:
          type: string
          format: uuid
    get:
      operationId: getBook
      responses:
        200:
          description: Get Books
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Book"
  

Paste this spec at https://openapi-typescript-code-generator.netlify.app/v0.26.0/index.html

The current behavior

image

/book/${params.parameter.id}/.json is invalid path.

The expected behavior

It should be /book/${params.parameter.id}.json.

Do you have the plan to suppor tree-shaking?

I'm experimenting this code generator. It's functionality is meet to my use-case.

However, I have one problem to use this. The generated code is big one class so if I try to use one of the API in the client, then all api's are bundled.

Do you have the plan to improve this?

Bug: TS 1214 occured

Steps To Reproduce

components:
  schemas:
    package: // reserved word
      type: object
      properties:
        id:
          type: number

The current behavior

TS Error

A software package

Identifier expected. 'package' is a reserved word in strict mode. Modules are automatically in strict mode.ts(1214)

The expected behavior

Feature Request: adapt name includes parentheses string

Steps To Reproduce

Run bun i && bun gen.ts in https://github.com/tkow/cloudflare-ts-client.

This spec includes parentheses in operationId , which is valid though It's not recommended in OAS 3.0.3.
operationId: secondary-dns-(-primary-zone)-create-primary-zone-configuration

The current behavior

See generated codes from a coudflare spec.
https://github.com/tkow/cloudflare-ts-client/blob/main/src/client.ts

The expected behavior

Sanitize them to valid function name.

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.