Skip to content

Commit

Permalink
inline enums #5
Browse files Browse the repository at this point in the history
  • Loading branch information
vladkens committed Jan 18, 2025
1 parent 924d80b commit da3e3c7
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 8 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.PHONY: test-ts test-ts-matrix clean

default: test-ts-matrix
default:
yarn ci

test-ts:
@echo "-- $(v) --------------------"
Expand Down
21 changes: 21 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ const pet = await api.pet.getPetById(1)
const createdAt: Date = pet.createdAt // date parsed from string with format=date-time
```

### String union as enums

You can generate string literal union instead of native enums in case you want to run in Node.js environment with [type-stripping](https://nodejs.org/api/typescript.html#type-stripping). To achive this pass `--inline-enums` command line argument or use `inlineEnums: true` in Node.js API.

```sh
yarn apigen-ts ./openapi.json ./api-client.ts --inline-enums
```

This will generate:

```ts
type MyEnum = "OptionA" | "OptionB"

// instead of
enum MyEnum = {
OptionA = "OptionA",
OptionB = "OptionB"
}
```

### Errors handling

An exception will be thrown for all unsuccessful return codes.
Expand Down Expand Up @@ -156,6 +176,7 @@ await apigen({
// everything below is optional
name: "MyApiClient", // default "ApiClient"
parseDates: true, // default false
inlineEnums: false, // default false, use string literal union instead of enum
resolveName(ctx, op, proposal) {
// proposal is [string, string] which represents module.funcName
if (proposal[0] === "users") return // will use default proposal
Expand Down
10 changes: 9 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type Config = {
output: string | null
name: string
parseDates: boolean
inlineEnums: boolean
resolveName?: (ctx: Context, op: OpConfig, proposal: OpName) => OpName | undefined
}

Expand All @@ -21,8 +22,9 @@ export const initCtx = (config?: Partial<Context>): Context => {
source: "",
output: "",
name: "ApiClient",
parseDates: false,
doc: { openapi: "3.1.0" },
parseDates: false,
inlineEnums: false,
...config,
logTag: "",
usedNames: new Set(),
Expand All @@ -45,6 +47,11 @@ export const getCliConfig = () => {
description: "parse dates as Date objects",
default: false,
},
inlineEnums: {
type: Boolean,
description: "use inline enums instead of enum types",
default: false,
},
},
})

Expand All @@ -53,6 +60,7 @@ export const getCliConfig = () => {
output: argv._.output ?? null,
name: argv.flags.name,
parseDates: argv.flags.parseDates,
inlineEnums: argv.flags.inlineEnums,
}

return config
Expand Down
2 changes: 1 addition & 1 deletion src/type-gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ const isStringEnum = (s: Referenced<Oas3Schema>): s is Oas3Schema & { enum: stri
}

export const makeTypeAlias = (ctx: Context, name: string, s: Referenced<Oas3Schema>) => {
if (isStringEnum(s)) {
if (isStringEnum(s) && !ctx.inlineEnums) {
const tokens1 = uniq(s.enum)
const tokens2 = filterEmpty(tokens1)

Expand Down
25 changes: 20 additions & 5 deletions test/type-gen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import { initCtx } from "../src/config"
import { printCode } from "../src/printer"
import { makeType, makeTypeAlias } from "../src/type-gen"

type Cfg = {
parseDates?: boolean
inlineEnums?: boolean
}

test("type inline", async () => {
const t = (l: Oas3_1Schema, r: string, parseDates = false) => {
const ctx = initCtx({ parseDates })
const t = (l: Oas3_1Schema, r: string, cfg?: Cfg) => {
const ctx = initCtx({ ...cfg })
const res = makeType(ctx, l)
const txt = printCode([res as unknown as ts.Statement])
.replace(/"(\w+)"(\??):/g, "$1$2:")
Expand Down Expand Up @@ -78,7 +83,7 @@ test("type inline", async () => {
// custom types
t({ type: "string", format: "binary" }, "File")
t({ type: "string", format: "date-time" }, "string")
t({ type: "string", format: "date-time" }, "Date", true)
t({ type: "string", format: "date-time" }, "Date", { parseDates: true })

// object
t({ type: "object", properties: { a: { type: "string" } } }, "{ a?: string }")
Expand Down Expand Up @@ -133,8 +138,8 @@ test("type inline", async () => {
})

test("type alias", async () => {
const t = (l: Oas3Schema & { name?: string }, r: string, parseDates = false) => {
const ctx = initCtx({ parseDates })
const t = (l: Oas3Schema & { name?: string }, r: string, cfg?: Cfg) => {
const ctx = initCtx({ ...cfg })
const res = makeTypeAlias(ctx, l.name ?? "t", l)
const txt = printCode([res as unknown as ts.Statement])
.replace(";", "")
Expand Down Expand Up @@ -162,6 +167,8 @@ test("type alias", async () => {
// enums
t({ type: "string", enum: ["a", "b"] }, `enum t { A = "a", B = "b" }`)
t({ type: "string", enum: ["a", "b", "b"] }, `enum t { A = "a", B = "b" }`)
t({ type: "string", enum: ["Aaa", "bBB"] }, `enum t { Aaa = "Aaa", BBB = "bBB" }`)
t({ type: "string", enum: ["_aA", "_bB"] }, `enum t { _aA = "_aA", _bB = "_bB" }`)
// t({ type: "number", enum: [1, 2] }, `enum t { A = 1, B = 2 }`) // no number enum support
t({ type: "number", enum: [1, 2] }, `type t = 1 | 2`)
t({ type: "boolean", enum: [true] }, `type t = true`)
Expand All @@ -173,6 +180,14 @@ test("type alias", async () => {
// equal(await t({ type: "string", enum: ["a", "b"] }), `enum t { A = "a", B = "b", }`)
// equal(await t({ type: "number", enum: ["a", "b"] }), `enum t { A = "a", B = "b", }`)
// equal(await t({ type: "number", enum: [1, 2] }), `enum t { A = "a", B = "b", }`)

// inline enums
t({ type: "string", enum: ["a", "b"] }, `type t = "a" | "b"`, { inlineEnums: true })
t({ type: "string", enum: ["a", "b", "b"] }, `type t = "a" | "b"`, { inlineEnums: true })
t({ type: "string", enum: ["a", "b", ""] }, `type t = "a" | "b"`, { inlineEnums: true })
t({ type: "string", enum: ["Aaa", "bBB"] }, `type t = "Aaa" | "bBB"`, { inlineEnums: true })
t({ type: "string", enum: ["Aaa", "bBB"] }, `type t = "Aaa" | "bBB"`, { inlineEnums: true })
t({ type: "string", enum: ["_aA", "_bB"] }, `type t = "_aA" | "_bB"`, { inlineEnums: true })
})

test.run()

0 comments on commit da3e3c7

Please sign in to comment.