Open12

OAS 3.1.0 の Schema Object をパースして TypeScript の型宣言を生成

SUWA ShigekiSUWA Shigeki

JSON Schema に全乗っかりしたらしいので、既存のツールでパースした結果が、tsコード自動生成に使えるかどうか実験する

SUWA ShigekiSUWA Shigeki
const fs = require("fs");
const path = require("path");
const jsYaml = require("js-yaml");
const jsonSchemaToTypeScript = require("json-schema-to-typescript");

const apiDocRaw = fs.readFileSync(path.resolve("assets/api.yaml")).toString()
const apiDoc = jsYaml.load(apiDocRaw);

const schemasEntries = Object.entries(apiDoc.components?.schemas ?? {})

const declPromises = schemasEntries.map(async ([name, schema]) => {
  // stringify する JSON Schema 本体に、dereference 用の components プロパティをそのままくっつける
  const parsable = { ...schema, components: apiDoc.components };
  const schemaName = schema.title ?? name

  return await jsonSchemaToTypeScript.compile(parsable, schemaName, {
    bannerComment: "",
    declareExternallyReferenced: false,
  });
})

Promise.all(declPromises).then((decls) => {
  const outDirPath = path.resolve("out")
  fs.mkdirSync(outDirPath, { recursive: true })
  const outPath = path.join(outDirPath, "models.ts");
  fs.writeFileSync(outPath, decls.join("\n"));
})

↓ こういうのは出てくる。
readOnly/writeOnly のプロパティを捨てる機能はない(それはそう)。
$ref した他のモデルの型名を(名前空間 prefix をつけるために)renameする機能はない(ほしい)。

export interface User {
  id: number;
  firstName: string;
  lastName: string;
  email: string;
  dateOfBirth: string | null;
  emailVerified: boolean;
  createDate: string;
  [k: string]: unknown;
}

export interface Article {
  id: number;
  author: User;
  title: string;
  body: string;
  published: boolean;
  publishedAt: string;
  lastUpdatedAt: string;
  [k: string]: unknown;
}
SUWA ShigekiSUWA Shigeki

external schema ( $ref してるスキーマ) の型名を変えられるようにしたい

import * as models from "./models.ts"

してるとき等

SUWA ShigekiSUWA Shigeki

jsonschemaの型付きでopenapidocをパース

writeOnly,readOnlyなど含んだ中間表現に一旦変形する←ここが独自実装になってしまうので避けたい

そこからオプションを受け取ってTSのAPIを使ってコード生成

SUWA ShigekiSUWA Shigeki

writeOnly,readOnlyは、純粋なjsonschema to typescriptで考慮するものではないとは思うかも

オプションを拡張実装して
excludeWriteOnly → title: FooReadable
excludeReadOnly → title: FooWritable
で2回codegenする?
もちろんrecursiveで($ref先、properties, items の中も見る)

SUWA ShigekiSUWA Shigeki
author:
  $ref: '#/components/schemas/User'
  readOnly: true

↑だとStoplightStudioで認識してくれなくて、GUIでいじると readOnly が消えちゃう
↓だと authorreadOnly なのは認識してくれて、GUIでいじっても消えない

author:
  allOf:
    - $ref: '#/components/schemas/User'
    - readOnly: true

:thinking_face:

SUWA ShigekiSUWA Shigeki

各schemaのreadOnly/writeOnly版をrenderする ・・・ readOnly/writeOnlyプロパティを含むかどうか求める

circularなschemaも考慮してあげたい