Skip to content

Commit

Permalink
feat: support customName options (#585)
Browse files Browse the repository at this point in the history
* feat: support `customName` options

* feat: support nullish name for `customName`
  • Loading branch information
antfu committed Apr 21, 2024
1 parent 11879fa commit 2f29f19
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 20 deletions.
5 changes: 5 additions & 0 deletions src/index.ts
Expand Up @@ -15,6 +15,7 @@ import {validate} from './validator'
import {isDeepStrictEqual} from 'util'
import {link} from './linker'
import {validateOptions} from './optionValidator'
import {JSONSchema as LinkedJSONSchema} from './types/JSONSchema'

export {EnumJSONSchema, JSONSchema, NamedEnumJSONSchema, CustomTypeJSONSchema} from './types/JSONSchema'

Expand Down Expand Up @@ -76,6 +77,10 @@ export interface Options {
* Generate unknown type instead of any
*/
unknownAny: boolean
/**
* Custom function to provide a type name for a given schema
*/
customName?: (schema: LinkedJSONSchema, keyNameFromDefinition: string | undefined) => string | undefined
}

export const DEFAULT_OPTIONS: Options = {
Expand Down
42 changes: 22 additions & 20 deletions src/parser.ts
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),
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,10 @@ function standaloneName(
schema: LinkedJSONSchema,
keyNameFromDefinition: string | undefined,
usedNames: UsedNames,
options: Options,
): string | undefined {
const name = schema.title || schema.$id || keyNameFromDefinition
const name =
options.customName?.(schema, keyNameFromDefinition) || schema.title || schema.$id || keyNameFromDefinition
if (name) {
return generateName(name, usedNames)
}
Expand All @@ -358,7 +360,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
25 changes: 25 additions & 0 deletions test/__snapshots__/test/test.ts.md
Expand Up @@ -748,6 +748,31 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## customName.1.js

> Expected output to match snapshot for e2e test: customName.1.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 FooTitle {␊
propaaa?: CustomPrefixDefaa;␊
propaab?: CustomPrefixDefaa;␊
propbbb?: PropbbbTitle;␊
[k: string]: unknown;␊
}␊
export interface CustomPrefixDefaa {␊
[k: string]: unknown;␊
}␊
export interface PropbbbTitle {␊
[k: string]: unknown;␊
}␊
`

## customType.js

> Expected output to match snapshot for e2e test: customType.js
Expand Down
Binary file modified test/__snapshots__/test/test.ts.snap
Binary file not shown.
24 changes: 24 additions & 0 deletions test/e2e/customName.1.ts
@@ -0,0 +1,24 @@
import {Options} from '../../src/'

export const input = {
type: 'object',
id: 'FooId',
title: 'FooTitle',
definitions: {
defaa: {id: 'defaa-id'},
defab: {id: 'defab-id'},
},
properties: {
propaaa: {$ref: '#/definitions/defaa'},
propaab: {$ref: '#/definitions/defaa'},
propbbb: {id: 'propbbb-id', title: 'propbbb-title'},
},
}

export const options: Partial<Options> = {
customName: (_schema, keyName) => {
if (!keyName)
return undefined // Fallback to default naming
return 'CustomPrefix_' + keyName
},
}

0 comments on commit 2f29f19

Please sign in to comment.