単一のHTMLファイルで簡単にOpenAPI定義を表示する方法(Dockerなし、ES Modules使用)

# はじめに(この記事でできること)

- Dockerを使用することなく、無料かつローカル環境でOpenAPI 3.0定義を表示する
  - 単一のHTMLファイルもしくはJavaScriptファイルで機能する
  - openapi定義の場所がローカル(相対パス)かリモートURLかにかかわらず、機能する

## 結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/2b504d37-a94a-2d12-2c84-1e23be6eea35.png)

# 必要な環境

- [Visual Studio Code](https://code.visualstudio.com/)
  - [Live Preview拡張機能](https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server)

## 操作手順

- ソースコードのhtmlファイルを任意のフォルダに配置する
- Visual Studio Codeでワークスペースを開く
- 右クリック⇒[プレビューを表示]


# ソースコード

## 単一のHTMLファイルを使用する場合

```swagger-ui.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>SwaggerUI</title>
    <script type="importmap">
      {
        "imports": {
          "swagger-ui-dist": "https://esm.sh/swagger-ui-dist@4.15.5/swagger-ui-bundle.js",
          "swagger-ui-dist-css": "https://esm.sh/swagger-ui-dist@4.15.5/swagger-ui.css"
        }
      }
    </script>
    <script type="module">
        import SwaggerUIBundle from "swagger-ui-dist";
        import sheet from "swagger-ui-dist-css" assert { type: "css" };
        import openapi from "https://petstore3.swagger.io/api/v3/openapi.json" assert { type: "json" };
        // or
        // import openapi from "./openapi.json" assert { type: "json" };

        document.adoptedStyleSheets = [sheet];
        SwaggerUIBundle({
            spec: openapi,
            dom_id: "#swagger-ui",
            filter: true,
        });
    </script>
</head>
<body>
    <div id="swagger-ui"></div>
</body>
</html>
```

```redoc.html
<!DOCTYPE html>
<html>
<head>
    <!-- needed for adaptive design -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Redoc</title>
    <!--
    Redoc doesn't change outer page styles
    -->
    <style>
        body {
            margin: 0;
            padding: 0;
        }
    </style>
    <script type="importmap">
        {
          "imports": {
            "redoc": "https://esm.sh/redoc@2.0.0/bundles/redoc.standalone.js",
            "montserrat-css": "https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
          }
        }
    </script>
    <script type="module">
        import sheet from "montserrat-css" assert { type: "css" };
        import Redoc from "redoc";

        document.adoptedStyleSheets = [sheet];
    </script>
</head>
<body>
    <redoc spec-url='https://petstore3.swagger.io/api/v3/openapi.json'></redoc>
</body>
</html>
```

## 単一のjsファイルにJavaScript(とCSS)を切り出す場合

```index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>SwaggerUI</title>
    <script type="module">
      import { swagger } from "./index.js";
      import openapi from "https://petstore3.swagger.io/api/v3/openapi.json" assert { type: "json" };
      swagger(openapi);
    </script>
  </head>
  <body>
    <div id="swagger-ui"></div>
  </body>
</html>
```

```index.js
// @deno-types="https://esm.sh/@types/swagger-ui-dist@3.30.1/index.d.ts"
import SwaggerUIBundle from "https://esm.sh/swagger-ui-dist@4.14.0/swagger-ui-bundle.js";
import sheet from "https://esm.sh/swagger-ui-dist@4.14.0/swagger-ui.css" assert { type: "css" };

document.adoptedStyleSheets = [sheet];

/** @param { import("https://esm.sh/@types/swagger-ui-dist@3.30.1/index.d.ts").Spec} spec */
export function swagger(spec) {
  /** @type { import("https://esm.sh/@types/swagger-ui-dist@3.30.1/index.d.ts").SwaggerConfigs}  */
  const config = {
    spec: spec,
    dom_id: '#swagger-ui',
    filter: true
  }
  SwaggerUIBundle(config);
}
```

# 情報源

## swagger-ui

https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/installation.md#unpkg

## ES Modules(ESM)

https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules#html_%E3%81%AB%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92%E9%81%A9%E7%94%A8%E3%81%99%E3%82%8B

https://esm.sh/

## CSS Module Scripts

https://zenn.dev/akfm/articles/e7615e8e826df8#css-module-scripts

## JSON import (import-assertions)

https://v8.dev/features/import-assertions