From a0399b6ea42f4cdb26c4a9dd15e8728554ed40dc Mon Sep 17 00:00:00 2001 From: Young Jun Joo Date: Mon, 21 Apr 2025 12:44:22 -0400 Subject: [PATCH] =?UTF-8?q?docs(openapi-generator):=20Refactor=20documenta?= =?UTF-8?q?tion=20to=20Di=C3=A1taxis=20Reference=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit significantly refactors the documentation for the `@api-ts/openapi-generator` package to align with the Reference section principles of the Diátaxis framework. The goal is to provide users with a clear, structured, and authoritative technical description suitable for direct consultation during development. **Key Changes:** * Restructured the entire README.md into a dedicated Reference section, replacing the previous narrative format. * Organized content into logical subsections for improved navigability and focus: * **Command-Line Interface (CLI):** Provides a technical overview, usage syntax, and detailed descriptions of the `` argument, options (`--name`, `--version`, `--codec-file`), and flags (`--internal`, `--help`), along with concrete usage examples. * **Configuration:** Clearly documents the requirements for preparing external types packages (specifying `files` and `source` in the external package's `package.json`). Details the two distinct methods for defining custom codec schemas: 1. Via `openapi-gen.config.js` within the types package (recommended for type authors), including file naming, `package.json` requirements (`customCodecFile`, `files`), and file structure. 2. Via the `--codec-file` CLI option for consumers, specifying the required file structure (grouping by package name). * **Supported `io-ts` Primitives:** Includes a definitive list of `io-ts` primitives and combinators that the generator can automatically interpret to produce OpenAPI schemas. * **JSDoc Annotations:** Exhaustively documents the supported JSDoc tags used to enrich the generated OpenAPI specification, categorized by: * *Endpoint Annotations* (applied to `httpRoute` variables): Covers Summary, Description, `@operationId`, `@tag`, `@private`, `@unstable`, `@example`, and the handling of unknown tags (`x-unknown-tags`), explaining their direct mapping to OpenAPI Operation Object fields (like `summary`, `description`, `operationId`, `tags`, `x-internal`, `x-unstable`, `example`, `x-unknown-tags`). * *Schema Annotations* (applied to `io-ts` types/fields): Details how to add descriptions via JSDoc text and lists all supported standard OpenAPI tags (`@default`, `@example`, `@minLength`, `@maximum`, `@pattern`, `@format`, etc.) and custom tags (`@private`, `@deprecated`), explaining their effect on the resulting OpenAPI Schema Object or Property (like `description`, `default`, `minLength`, `deprecated`, `x-internal`). Includes comprehensive examples demonstrating tag usage. * Removed introductory explanations and narrative prose, focusing strictly on factual, technical descriptions of the tool's features, configuration requirements, and behavior based on inputs (like JSDoc tags). **Impact:** This restructuring provides developers using `@api-ts/openapi-generator` with significantly improved technical reference documentation. The information is now presented in a consistent, predictable, and easily searchable format, adhering to the Diátaxis framework's guidelines for effective reference material. This makes it easier for users to find the specific technical details they need regarding CLI usage, configuration, supported types, and JSDoc annotation capabilities while working with the generator. chore: apply feedback chore: fix heading to use title casing chore: add . after sentences chore: apply feedback --- .../docs/reference/openapi-generator/cli.md | 53 ++++ .../openapi-generator/configuration.md | 142 ++++++++++ .../docs/reference/openapi-generator/index.md | 16 ++ .../docs/reference/openapi-generator/jsdoc.md | 253 ++++++++++++++++++ .../reference/openapi-generator/support.md | 34 +++ website/sidebars.js | 14 + 6 files changed, 512 insertions(+) create mode 100644 website/docs/reference/openapi-generator/cli.md create mode 100644 website/docs/reference/openapi-generator/configuration.md create mode 100644 website/docs/reference/openapi-generator/index.md create mode 100644 website/docs/reference/openapi-generator/jsdoc.md create mode 100644 website/docs/reference/openapi-generator/support.md diff --git a/website/docs/reference/openapi-generator/cli.md b/website/docs/reference/openapi-generator/cli.md new file mode 100644 index 00000000..26041f17 --- /dev/null +++ b/website/docs/reference/openapi-generator/cli.md @@ -0,0 +1,53 @@ +--- +sidebar_position: 2 +--- + +# Command-line Interface + +## Overview + +The `openapi-generator` CLI tool converts your `@api-ts/io-ts-http` `apiSpec` definition +into an OpenAPI 3.0 specification. When you run this tool, it reads a TypeScript file +containing your API definition and outputs the OpenAPI specification to stdout. + +## Usage Syntax + +```shell +openapi-generator [OPTIONS] [FLAGS] +``` + +## Arguments + +- ``: (Required) Path to the TypeScript file containing the exported `apiSpec` + definition. + +## Options + +- `--name`, `-n `: Specifies the API name in the generated specification. +- `--version`, `-v `: Specifies the API version in the generated OpenAPI + specification. If an `@version` JSDoc tag is present on the `apiSpec` export, that + value takes precedence. +- `--codec-file`, `-c `: Path to a JavaScript configuration file defining + schemas for custom or external io-ts codecs. See + [Defining custom codec schemas](./configuration#defining-custom-codec-schemas) for + details. + +## Flags + +- `--internal`, `-i`: Includes routes marked with the `@private` JSDoc tag in the + generated output. By default, private routes are excluded. +- `--help`, `-h`: Displays the help message describing arguments, options, and flags. + +## Examples + +You can generate an OpenAPI specification and save it to a file: + +```shell +npx openapi-generator src/index.ts > openapi-spec.json +``` + +You can specify API name, version, and custom codec definitions: + +```shell +npx openapi-generator --name "My Service API" --version "2.1.0" --codec-file ./custom-codecs.js src/api.ts +``` diff --git a/website/docs/reference/openapi-generator/configuration.md b/website/docs/reference/openapi-generator/configuration.md new file mode 100644 index 00000000..670a698e --- /dev/null +++ b/website/docs/reference/openapi-generator/configuration.md @@ -0,0 +1,142 @@ +--- +sidebar_position: 3 +--- + +# Configuration + +## Overview + +You need to configure the generator to: + +- Correctly interpret `io-ts` types from external packages. +- Define OpenAPI schemas for custom `io-ts` codecs that can't be automatically derived + from the Abstract Syntax Tree (AST). + +## Preparing External Types Packages + +To process `io-ts` types imported from other npm packages, ensure the following in the +external package's `package.json`: + +1. Include source code in the published npm bundle by adding the source directory to the + `files` array: + + ```json + // package.json of the external types package + { + "name": "my-external-types", + "version": "1.0.0", + "files": [ + "dist/", + "src/" // Include source code + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "source": "src/index.ts" // Add this field + // ... rest of package.json + } + ``` + +2. Specify the source entry point using the `source` field (for example, + `"source": "src/index.ts"`). + +## Defining Custom Codec Schemas + +For custom `io-ts` codecs (such as those using `new t.Type(...)` or complex types not +directly supported), you must define schemas manually using one of these methods: + +### Method 1: Via `openapi-gen.config.js` (Recommended For Type Authors) + +You can define schemas directly within the package that declares the custom codecs: + +1. Create a file named `openapi-gen.config.js` in the root of the types package. + +2. Update the package's `package.json` to include: + +- The `customCodecFile` field pointing to this file. +- The config file in the `files` array. + + ```json + // package.json of the types package defining custom codecs + { + "name": "my-custom-codec-package", + // ... + "files": [ + "dist/", + "src/", + "openapi-gen.config.js" // Include the config file + ], + "customCodecFile": "openapi-gen.config.js" // Point to the file + // ... + } + ``` + +3. Structure the `openapi-gen.config.js` file as follows: + + ```javascript + // openapi-gen.config.js + module.exports = (E) => { + return { + // Key matches the exported codec name (e.g., export const MyCustomString = ...) + MyCustomString: () => + E.right({ + type: 'string', + format: 'custom-format', + description: 'A custom string type definition', + }), + AnotherCustomType: () => + E.right({ + type: 'object', + properties: { + /* ... */ + }, + }), + // ... other custom codec definitions + }; + }; + ``` + +The exported function receives the `fp-ts/Either` namespace (`E`) as an argument. You +should return an object where: + +- Keys are the exported names of your custom codecs. +- Values are functions that return `E.right()` with an OpenAPI schema object. + +### Method 2: Via `--codec-file` Option (For Consumers) + +You can define schemas in a configuration file within your project and pass the file +path via the `--codec-file` option: + +1. Create a JavaScript file (for example, `custom-codecs.js`). + +2. Structure the file similarly to Method 1, but group definitions by package: + + ```javascript + // custom-codecs.js + module.exports = (E) => { + return { + 'io-ts-bigint': { + // Package name + BigIntFromString: () => E.right({ type: 'string', format: 'bigint' }), + NonZeroBigIntFromString: () => + E.right({ type: 'string', format: 'bigint' /* constraints */ }), + // ... other codecs from 'io-ts-bigint' + }, + 'my-other-custom-package': { + // Another package + SomeType: () => E.right({ type: 'number', format: 'float' }), + }, + // ... other packages + }; + }; + ``` + +In this structure: + +- Keys of the top-level object are package names. +- Values are objects that map codec names to their schema definitions. + +3. Run the generator with the `--codec-file` option: + + ```shell + npx openapi-generator --codec-file ./custom-codecs.js src/index.ts + ``` diff --git a/website/docs/reference/openapi-generator/index.md b/website/docs/reference/openapi-generator/index.md new file mode 100644 index 00000000..bd45766c --- /dev/null +++ b/website/docs/reference/openapi-generator/index.md @@ -0,0 +1,16 @@ +--- +sidebar_position: 1 +--- + +# OpenAPI Generator + +This section provides technical reference for the `@api-ts/openapi-generator` +command-line utility. The documentation covers: + +- CLI usage and options. +- Configuration settings. +- Supported `io-ts` primitives. +- JSDoc annotation system. + +You can use this reference to generate OpenAPI 3.0 specifications from your +`@api-ts/io-ts-http` API definitions. diff --git a/website/docs/reference/openapi-generator/jsdoc.md b/website/docs/reference/openapi-generator/jsdoc.md new file mode 100644 index 00000000..6b9ad700 --- /dev/null +++ b/website/docs/reference/openapi-generator/jsdoc.md @@ -0,0 +1,253 @@ +# JSDoc Annotations + +You can use JSDoc comments to enrich the generated OpenAPI specification. This reference +describes the supported annotations for both endpoint definitions and schema +definitions. + +## Endpoint Annotations + +You can add JSDoc comments to variables holding `h.httpRoute` definitions to provide +metadata for the corresponding OpenAPI operation object. + +### Summary + +When you add a JSDoc comment, the first line becomes the `summary` field: + +```typescript +/** + * Retrieve a user by their ID. + */ +const GetUserRoute = h.httpRoute({ + /* ... */ +}); +``` + +### Description + +Any subsequent untagged lines following the summary become the `description` field +(newlines preserved): + +```typescript +/** + * Retrieve a user by their ID. + * Provides detailed information about the user, + * including profile and settings. + */ +const GetUserRoute = h.httpRoute({ + /* ... */ +}); +``` + +### @operationId + +This tag specifies the `operationId` field. It is required for unique identification: + +```typescript +/** + * @operationId getUserById + */ +const GetUserRoute = h.httpRoute({ + /* ... */ +}); +``` + +### @tag + +This tag assigns the endpoint to a specific tag group. It populates the `tags` array +field. You can use multiple `@tag` lines: + +```typescript +/** + * @tag User + * @tag Profile + */ +const GetUserRoute = h.httpRoute({ + /* ... */ +}); +``` + +### @private + +This tag marks the endpoint as internal. It adds `x-internal: true` to the operation +object. These routes are omitted by default unless you use the `--internal` flag: + +```typescript +/** + * Admin-only route. + * @private + * @operationId adminGetUser + * @tag Admin + */ +const AdminGetUserRoute = h.httpRoute({ + /* ... */ +}); +``` + +### @unstable + +This tag marks the endpoint as unstable or under development. It adds `x-unstable: true` +to the operation object: + +```typescript +/** + * New feature, API may change. + * @unstable + * @operationId betaFeature + * @tag Beta + */ +const BetaRoute = h.httpRoute({ + /* ... */ +}); +``` + +### @example + +This tag provides an example payload. It adds the JSON object to the `example` field: + +```typescript +/** + * @example { "userId": "user-123", "name": "Example User" } + */ +const GetUserRoute = h.httpRoute({ + /* ... */ +}); +``` + +### Unknown Tags + +The generator treats any JSDoc tag not listed above as an "unknown tag". It collects +these tags into the `x-unknown-tags` object: + +```typescript +/** + * @version 2 + * @department Billing + */ +const GetUserRoute = h.httpRoute({ + /* ... */ +}); +``` + +In this example, `@version 2` becomes `{ "version": "2" }` and `@department Billing` +becomes `{ "department": "Billing" }` in the `x-unknown-tags` object. + +## Schema Annotations + +When you add JSDoc comments to `io-ts` type definitions or fields within `t.type`, +`t.partial`, etc., they add detail to the corresponding OpenAPI schema object or +property. + +### Description + +When you add JSDoc text directly above a type definition or field, it becomes the +`description` field in the schema: + +```typescript +import * as t from 'io-ts'; + +/** + * Unique identifier for the resource. + */ +const id = t.string; + +/** Represents a user profile. */ +const UserProfile = t.type({ + /** User's primary email address. */ + email: t.string, + id: id, +}); +``` + +### Supported OpenAPI Tags + +You can use the following JSDoc tags, which map directly to standard OpenAPI schema +keywords: + +| JSDoc Tag | OpenAPI Property | Description | +| ------------------------- | ------------------ | ---------------------------------- | +| `@default ` | `default` | Default value | +| `@example ` | `example` | Example value | +| `@minLength ` | `minLength` | Minimum string length | +| `@maxLength ` | `maxLength` | Maximum string length | +| `@pattern ` | `pattern` | Regex pattern string | +| `@minimum ` | `minimum` | Minimum numeric value | +| `@maximum ` | `maximum` | Maximum numeric value | +| `@minItems ` | `minItems` | Minimum array items | +| `@maxItems ` | `maxItems` | Maximum array items | +| `@minProperties ` | `minProperties` | Minimum object properties | +| `@maxProperties ` | `maxProperties` | Maximum object properties | +| `@exclusiveMinimum` | `exclusiveMinimum` | Exclusive minimum flag | +| `@exclusiveMaximum` | `exclusiveMaximum` | Exclusive maximum flag | +| `@multipleOf ` | `multipleOf` | Multiple of value | +| `@uniqueItems` | `uniqueItems` | Unique array items flag | +| `@readOnly` | `readOnly` | Read-only flag | +| `@writeOnly` | `writeOnly` | Write-only flag | +| `@format ` | `format` | Format (e.g., `uuid`, `date-time`) | +| `@title ` | `title` | Schema title | + +### Custom Tags + +| JSDoc Tag | OpenAPI Property | Description | +| ------------- | ------------------ | -------------------------------- | +| `@private` | `x-internal: true` | Marks schema/field as internal | +| `@deprecated` | `deprecated: true` | Marks schema/field as deprecated | + +## Example Schema With Annotations + +```typescript +import * as t from 'io-ts'; + +/** + * @title Detailed Item Schema + * Describes an item with various constraints. + */ +const ItemSchema = t.type({ + /** + * The unique identifier for the item. + * @format uuid + * @readOnly + * @example "f47ac10b-58cc-4372-a567-0e02b2c3d479" + */ + id: t.string, + + /** + * Name of the item. + * @minLength 1 + * @maxLength 100 + * @default "Unnamed Item" + */ + name: t.string, + + /** + * Item quantity in stock. + * @minimum 0 + * @example 25 + */ + quantity: t.number, + + /** + * Tags associated with the item. Must contain unique tags. + * @minItems 1 + * @maxItems 10 + * @uniqueItems + */ + tags: t.array(t.string), + + /** @deprecated Use 'tags' instead. */ + legacyCategory: t.string, + + /** + * Internal tracking code. + * @private + * @pattern ^[A-Z]{3}-[0-9]{4}$ + */ + internalCode: t.string, + + /** + * Price of the item. Must be greater than 0. + * @minimum 0 + * @exclusiveMinimum + */ + price: t.number, +}); +``` diff --git a/website/docs/reference/openapi-generator/support.md b/website/docs/reference/openapi-generator/support.md new file mode 100644 index 00000000..b48f889d --- /dev/null +++ b/website/docs/reference/openapi-generator/support.md @@ -0,0 +1,34 @@ +# Supported `io-ts` Primitives + +When you use the OpenAPI generator, it automatically derives schemas from the following +`io-ts` primitives and combinators: + +- `string` +- `number` +- `bigint` +- `boolean` +- `null` +- `nullType` +- `undefined` +- `unknown` +- `any` +- `array` +- `readonlyArray` +- `object` +- `type` +- `partial` +- `exact` +- `strict` +- `record` +- `union` +- `intersection` +- `literal` +- `keyof` +- `brand` +- `UnknownRecord` +- `void` + +For codecs not built using these primitives, you may need to define schemas manually. +You can see +[Defining custom codec schemas](./configuration#defining-custom-codec-schemas) for +instructions. diff --git a/website/sidebars.js b/website/sidebars.js index 35b149ae..91d742dc 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -64,6 +64,20 @@ const sidebars = { 'reference/io-ts-http/interfaces', ], }, + { + type: 'category', + label: 'openapi-generator', + link: { + type: 'doc', + id: 'reference/openapi-generator/index', + }, + items: [ + 'reference/openapi-generator/cli', + 'reference/openapi-generator/configuration', + 'reference/openapi-generator/support', + 'reference/openapi-generator/jsdoc', + ], + }, ], }, ],