Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to generate TS string enums #578

Merged
merged 2 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ See [server demo](example) and [browser demo](https://github.com/bcherny/json-sc
| cwd | string | `process.cwd()` | Root directory for resolving [`$ref`](https://tools.ietf.org/id/draft-pbryan-zyp-json-ref-03.html)s |
| declareExternallyReferenced | boolean | `true` | Declare external schemas referenced via `$ref`? |
| enableConstEnums | boolean | `true` | Prepend enums with [`const`](https://www.typescriptlang.org/docs/handbook/enums.html#computed-and-constant-members)? |
| inferStringEnumKeysFromValues | boolean | `false` | Create enums from JSON enums with eponymous keys |
| format | boolean | `true` | Format code? Set this to `false` to improve performance. |
| ignoreMinAndMaxItems | boolean | `false` | Ignore maxItems and minItems for `array` types, preventing tuples being generated. |
| maxItems | number | `20` | Maximum number of unioned tuples to emit when representing bounded-size array types, before falling back to emitting unbounded arrays. Increase this to improve precision of emitted types, decrease it to improve performance, or set it to `-1` to ignore `maxItems`.
Expand Down
2 changes: 2 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ Boolean values can be set to false using the 'no-' prefix.
Declare external schemas referenced via '$ref'?
--enableConstEnums
Prepend enums with 'const'?
--inferStringEnumKeysFromValues
Create enums from JSON enums instead of union types
--format
Format code? Set this to false to improve performance.
--maxItems
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ export interface Options {
* Prepend enums with [`const`](https://www.typescriptlang.org/docs/handbook/enums.html#computed-and-constant-members)?
*/
enableConstEnums: boolean
/**
* Create enums from JSON enums with eponymous keys
*/
inferStringEnumKeysFromValues: boolean
/**
* Format code? Set this to `false` to improve performance.
*/
Expand Down Expand Up @@ -95,6 +99,7 @@ export const DEFAULT_OPTIONS: Options = {
cwd: process.cwd(),
declareExternallyReferenced: true,
enableConstEnums: true,
inferStringEnumKeysFromValues: false,
format: true,
ignoreMinAndMaxItems: false,
maxItems: 20,
Expand Down
9 changes: 9 additions & 0 deletions src/normalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ function isObjectType(schema: LinkedJSONSchema) {
function isArrayType(schema: LinkedJSONSchema) {
return schema.items !== undefined || hasType(schema, 'array') || hasType(schema, 'any')
}
function isEnumTypeWithoutTsEnumNames(schema: LinkedJSONSchema) {
return schema.type === 'string' && schema.enum !== undefined && schema.tsEnumNames === undefined
}

rules.set('Remove `type=["null"]` if `enum=[null]`', schema => {
if (
Expand Down Expand Up @@ -222,6 +225,12 @@ rules.set('Transform const to singleton enum', schema => {
}
})

rules.set('Add tsEnumNames to enum types', (schema, _, options) => {
if (isEnumTypeWithoutTsEnumNames(schema) && options.inferStringEnumKeysFromValues) {
schema.tsEnumNames = schema.enum?.map(String)
}
})

export function normalize(
rootSchema: LinkedJSONSchema,
dereferencedPaths: DereferencedPaths,
Expand Down
47 changes: 27 additions & 20 deletions test/__snapshots__/test/test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,33 @@ Generated by [AVA](https://avajs.dev).
| [{foo: [4, 6]}];␊
`

## enum.5.js

> Expected output to match snapshot for e2e test: enum.5.js

`/* eslint-disable */␊
/**␊
* This file was automatically generated by json-schema-to-typescript.␊
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,␊
* and run json-schema-to-typescript to regenerate this file.␊
*/␊
export interface Enum {␊
stringEnum: StringEnum;␊
impliedStringEnum: "a" | "b" | "c";␊
booleanEnum: true;␊
impliedBooleanEnum: true;␊
integerEnum: -1 | 0 | 1;␊
impliedIntegerEnum: -1 | 0 | 1;␊
}␊
export const enum StringEnum {␊
a = "a",␊
b = "b",␊
c = "c"␊
}␊
`

## enum.js

> Expected output to match snapshot for e2e test: enum.js
Expand Down Expand Up @@ -449547,26 +449574,6 @@ Generated by [AVA](https://avajs.dev).
}␊
`

> Snapshot 5

'./test/resources/MultiSchema/out/b.yaml.d.ts'

> Snapshot 6

`/* eslint-disable */␊
/**␊
* This file was automatically generated by json-schema-to-typescript.␊
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,␊
* and run json-schema-to-typescript to regenerate this file.␊
*/␊
export interface BSchema {␊
x?: string;␊
y: number;␊
[k: string]: unknown;␊
}␊
`

## files in (-i), pipe out

> Snapshot 1
Expand Down
Binary file modified test/__snapshots__/test/test.ts.snap
Binary file not shown.
46 changes: 46 additions & 0 deletions test/e2e/enum.5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export const input = {
title: 'Enum',
type: 'object',
definitions: {
enumFromDefinition: {
type: 'string',
enum: ['a', 'b', 'c'],
},
},
properties: {
stringEnum: {
type: 'string',
enum: ['a', 'b', 'c'],
},
impliedStringEnum: {
enum: ['a', 'b', 'c'],
},
booleanEnum: {
type: 'boolean',
enum: [true],
},
impliedBooleanEnum: {
enum: [true],
},
integerEnum: {
type: 'integer',
enum: [-1, 0, 1],
},
impliedIntegerEnum: {
enum: [-1, 0, 1],
}
},
required: [
'stringEnum',
'impliedStringEnum',
'booleanEnum',
'impliedBooleanEnum',
'integerEnum',
'impliedIntegerEnum'
],
additionalProperties: false,
}

export const options = {
inferStringEnumKeysFromValues: true
}
17 changes: 17 additions & 0 deletions test/normalizer/addTsEnumNames.1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "Add tsEnumNames to enum types (1)",
"in": {
"$id": "foo",
"type": "string",
"enum": ["foo", "bar"]
},
"options": {
"inferStringEnumKeysFromValues": true
},
"out": {
"$id": "foo",
"type": "string",
"enum": [ "foo", "bar" ],
"tsEnumNames": ["foo", "bar"]
}
}
16 changes: 16 additions & 0 deletions test/normalizer/addTsEnumNames.2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Add tsEnumNames to enum types (2)",
"in": {
"$id": "foo",
"type": "string",
"enum": ["foo", "bar"]
},
"options": {
"inferStringEnumKeysFromValues": false
},
"out": {
"$id": "foo",
"type": "string",
"enum": [ "foo", "bar" ]
}
}
18 changes: 18 additions & 0 deletions test/normalizer/addTsEnumNames.3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Add tsEnumNames to enum types (3)",
"in": {
"$id": "foo",
"type": "string",
"enum": ["foo", "bar"],
"tsEnumNames": ["FOO", "BAR"]
},
"options": {
"inferStringEnumKeysFromValues": true
},
"out": {
"$id": "foo",
"type": "string",
"enum": [ "foo", "bar" ],
"tsEnumNames": ["FOO", "BAR"]
}
}