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

Make property title expansion optional #2

Merged
merged 15 commits into from
Feb 5, 2024
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ See [server demo](example) and [browser demo](https://github.com/bcherny/json-sc
| unknownAny | boolean | `true` | Use `unknown` instead of `any` where possible |
| unreachableDefinitions | boolean | `false` | Generates code for `$defs` that aren't referenced by the schema. |
| $refOptions | object | `{}` | [$RefParser](https://github.com/BigstickCarpet/json-schema-ref-parser) Options, used when resolving `$ref`s |
| expandPropertyTitles | boolean | `true` | Use the `"title"` field within each property to expand into a separate type? |
andreyctkn marked this conversation as resolved.
Show resolved Hide resolved
## CLI

A CLI utility is provided with this package.
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export interface Options {
* Generate unknown type instead of any
*/
unknownAny: boolean
/**
* Expand the title field of each property into a separate type
*/
expandPropertyTitles: boolean
}

export const DEFAULT_OPTIONS: Options = {
Expand Down Expand Up @@ -105,6 +109,7 @@ export const DEFAULT_OPTIONS: Options = {
},
unreachableDefinitions: false,
unknownAny: true,
expandPropertyTitles: true,
}

export function compileFromFile(filename: string, options: Partial<Options> = DEFAULT_OPTIONS): Promise<string> {
Expand Down
41 changes: 21 additions & 20 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
ethanpmullen marked this conversation as resolved.
Show resolved Hide resolved
params: schema.allOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
type: 'INTERSECTION',
}
Expand All @@ -157,14 +157,14 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
}
case 'ANY_OF':
return {
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
params: schema.anyOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
type: 'UNION',
}
Expand All @@ -173,7 +173,7 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'BOOLEAN',
}
case 'CUSTOM_TYPE':
Expand All @@ -182,15 +182,15 @@ function parseNonLiteral(
deprecated: schema.deprecated,
keyName,
params: schema.tsType!,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'CUSTOM_TYPE',
}
case 'NAMED_ENUM':
return {
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition ?? keyName, usedNames)!,
standaloneName: standaloneName(schema, keyNameFromDefinition ?? keyName, usedNames, options)!,
params: schema.enum!.map((_, n) => ({
ast: parseLiteral(_, undefined),
keyName: schema.tsEnumNames![n],
Expand All @@ -204,30 +204,30 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'NEVER',
}
case 'NULL':
return {
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'NULL',
}
case 'NUMBER':
return {
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'NUMBER',
}
case 'OBJECT':
return {
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'OBJECT',
deprecated: schema.deprecated,
}
Expand All @@ -236,7 +236,7 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
params: schema.oneOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
type: 'UNION',
}
Expand All @@ -247,7 +247,7 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'STRING',
}
case 'TYPED_ARRAY':
Expand All @@ -261,7 +261,7 @@ function parseNonLiteral(
keyName,
maxItems,
minItems,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
params: schema.items.map(_ => parse(_, options, undefined, processed, usedNames)),
type: 'TUPLE',
}
Expand All @@ -276,7 +276,7 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
params: parse(schema.items!, options, `{keyNameFromDefinition}Items`, processed, usedNames),
type: 'ARRAY',
}
Expand All @@ -286,7 +286,7 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
params: (schema.type as JSONSchema4TypeName[]).map(type => {
const member: LinkedJSONSchema = {...omit(schema, '$id', 'description', 'title'), type}
return parse(maybeStripDefault(member as any), options, undefined, processed, usedNames)
Expand All @@ -298,7 +298,7 @@ function parseNonLiteral(
comment: schema.description,
deprecated: schema.deprecated,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
params: schema.enum!.map(_ => parseLiteral(_, undefined)),
type: 'UNION',
}
Expand All @@ -320,7 +320,7 @@ function parseNonLiteral(
params: Array(Math.max(maxItems, minItems) || 0).fill(params),
// if there is no maximum, then add a spread item to collect the rest
spreadParam: maxItems >= 0 ? undefined : params,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'TUPLE',
}
}
Expand All @@ -330,7 +330,7 @@ function parseNonLiteral(
deprecated: schema.deprecated,
keyName,
params,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
type: 'ARRAY',
}
}
Expand All @@ -343,8 +343,9 @@ function standaloneName(
schema: LinkedJSONSchema,
keyNameFromDefinition: string | undefined,
usedNames: UsedNames,
options: Options,
): string | undefined {
const name = schema.title || schema.$id || keyNameFromDefinition
const name = (options.expandPropertyTitles && schema.title) || schema.$id || keyNameFromDefinition
if (name) {
return generateName(name, usedNames)
}
Expand All @@ -358,7 +359,7 @@ function newInterface(
keyName?: string,
keyNameFromDefinition?: string,
): TInterface {
const name = standaloneName(schema, keyNameFromDefinition, usedNames)!
const name = standaloneName(schema, keyNameFromDefinition, usedNames, options)!
return {
comment: schema.description,
deprecated: schema.deprecated,
Expand Down
54 changes: 54 additions & 0 deletions test/__snapshots__/test/test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,60 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## expandPropertyTitles.js

> Expected output to match snapshot for e2e test: expandPropertyTitles.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 type LastName = string;␊
export type Height = number;␊
export interface ExpandPropertyTitles {␊
firstName: string;␊
lastName: LastName;␊
/**␊
* Age in years␊
*/␊
age?: number;␊
height?: Height;␊
favoriteFoods?: unknown[];␊
likesDogs?: boolean;␊
[k: string]: unknown;␊
}␊
`
## expandPropertyTitles.js

> Expected output to match snapshot for e2e test: expandPropertyTitles.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 type Height = number;␊
export interface ExampleSchema {␊
firstName: string;␊
lastName: string;␊
/**␊
* Age in years␊
*/␊
age?: number;␊
height?: Height;␊
favoriteFoods?: unknown[];␊
likesDogs?: boolean;␊
[k: string]: unknown;␊
}␊
`

## extends.1a.js

> Expected output to match snapshot for e2e test: extends.1a.js
Expand Down
Binary file modified test/__snapshots__/test/test.ts.snap
Binary file not shown.
39 changes: 39 additions & 0 deletions test/e2e/expandPropertyTitles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export const input = {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
title: 'First Name',
type: 'string',
},
lastName: {
title: 'Last Name',
id: 'lastName',
type: 'string',
},
age: {
title: 'The Age of The Person This Represents',
description: 'Age in years',
type: 'integer',
minimum: 0,
},
height: {
title: 'Height in Feet',
$id: 'height',
type: 'number',
},
favoriteFoods: {
title: 'What Foods They Like',
type: 'array',
},
likesDogs: {
title: 'Whether they Like Dogs',
type: 'boolean',
},
},
required: ['firstName', 'lastName'],
}

export const options = {
expandPropertyTitles: false,
}