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 support for deprecated keyword (Fixes #441) #542

Merged
merged 9 commits into from
Aug 27, 2023
16 changes: 11 additions & 5 deletions src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ function generateInterface(ast: TInterface, options: Options): string {
)
.map(
([isRequired, keyName, ast, type]) =>
(hasComment(ast) && !ast.standaloneName ? generateComment(ast.comment) + '\n' : '') +
(hasComment(ast) && !ast.standaloneName ? generateComment(ast.comment, ast.deprecated) + '\n' : '') +
escapeKeyName(keyName) +
(isRequired ? '' : '?') +
': ' +
Expand All @@ -316,13 +316,19 @@ function generateInterface(ast: TInterface, options: Options): string {
)
}

function generateComment(comment: string): string {
return ['/**', ...comment.split('\n').map(_ => ' * ' + _), ' */'].join('\n')
function generateComment(comment: string, deprecated?: boolean): string {
const commentLines = ['/**']
if (deprecated) {
commentLines.push(' * @deprecated')
}
commentLines.push(...comment.split('\n').map(_ => ' * ' + _))
commentLines.push(' */')
return commentLines.join('\n')
}

function generateStandaloneEnum(ast: TEnum, options: Options): string {
return (
(hasComment(ast) ? generateComment(ast.comment) + '\n' : '') +
(hasComment(ast) ? generateComment(ast.comment, ast.deprecated) + '\n' : '') +
'export ' +
(options.enableConstEnums ? 'const ' : '') +
`enum ${toSafeString(ast.standaloneName)} {` +
Expand All @@ -335,7 +341,7 @@ function generateStandaloneEnum(ast: TEnum, options: Options): string {

function generateStandaloneInterface(ast: TNamedInterface, options: Options): string {
return (
(hasComment(ast) ? generateComment(ast.comment) + '\n' : '') +
(hasComment(ast) ? generateComment(ast.comment, ast.deprecated) + '\n' : '') +
`export interface ${toSafeString(ast.standaloneName)} ` +
(ast.superTypes.length > 0
? `extends ${ast.superTypes.map(superType => toSafeString(superType.standaloneName)).join(', ')} `
Expand Down
57 changes: 38 additions & 19 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,37 +148,42 @@ function parseNonLiteral(
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
params: schema.allOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
type: 'INTERSECTION'
type: 'INTERSECTION',
deprecated: schema.deprecated
}
case 'ANY':
return {
...(options.unknownAny ? T_UNKNOWN : T_ANY),
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames)
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
deprecated: schema.deprecated
}
case 'ANY_OF':
return {
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
params: schema.anyOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
type: 'UNION'
type: 'UNION',
deprecated: schema.deprecated
}
case 'BOOLEAN':
return {
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'BOOLEAN'
type: 'BOOLEAN',
deprecated: schema.deprecated
}
case 'CUSTOM_TYPE':
return {
comment: schema.description,
keyName,
params: schema.tsType!,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'CUSTOM_TYPE'
type: 'CUSTOM_TYPE',
deprecated: schema.deprecated
}
case 'NAMED_ENUM':
return {
Expand All @@ -189,7 +194,8 @@ function parseNonLiteral(
ast: parseLiteral(_, undefined),
keyName: schema.tsEnumNames![n]
})),
type: 'ENUM'
type: 'ENUM',
deprecated: schema.deprecated
}
case 'NAMED_SCHEMA':
return newInterface(schema as SchemaSchema, options, processed, usedNames, keyName)
Expand All @@ -198,36 +204,41 @@ function parseNonLiteral(
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'NEVER'
type: 'NEVER',
deprecated: schema.deprecated
}
case 'NULL':
return {
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'NULL'
type: 'NULL',
deprecated: schema.deprecated
}
case 'NUMBER':
return {
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'NUMBER'
type: 'NUMBER',
deprecated: schema.deprecated
}
case 'OBJECT':
return {
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'OBJECT'
type: 'OBJECT',
deprecated: schema.deprecated
}
case 'ONE_OF':
return {
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
params: schema.oneOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
type: 'UNION'
type: 'UNION',
deprecated: schema.deprecated
}
case 'REFERENCE':
throw Error(format('Refs should have been resolved by the resolver!', schema))
Expand All @@ -236,7 +247,8 @@ function parseNonLiteral(
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'STRING'
type: 'STRING',
deprecated: schema.deprecated
}
case 'TYPED_ARRAY':
if (Array.isArray(schema.items)) {
Expand All @@ -250,7 +262,8 @@ function parseNonLiteral(
minItems,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
params: schema.items.map(_ => parse(_, options, undefined, processed, usedNames)),
type: 'TUPLE'
type: 'TUPLE',
deprecated: schema.deprecated
}
if (schema.additionalItems === true) {
arrayType.spreadParam = options.unknownAny ? T_UNKNOWN : T_ANY
Expand All @@ -264,7 +277,8 @@ function parseNonLiteral(
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
params: parse(schema.items!, options, `{keyNameFromDefinition}Items`, processed, usedNames),
type: 'ARRAY'
type: 'ARRAY',
deprecated: schema.deprecated
}
}
case 'UNION':
Expand All @@ -276,15 +290,17 @@ function parseNonLiteral(
const member: LinkedJSONSchema = {...omit(schema, '$id', 'description', 'title'), type}
return parse(maybeStripDefault(member as any), options, undefined, processed, usedNames)
}),
type: 'UNION'
type: 'UNION',
deprecated: schema.deprecated
}
case 'UNNAMED_ENUM':
return {
comment: schema.description,
keyName,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
params: schema.enum!.map(_ => parseLiteral(_, undefined)),
type: 'UNION'
type: 'UNION',
deprecated: schema.deprecated
}
case 'UNNAMED_SCHEMA':
return newInterface(schema as SchemaSchema, options, processed, usedNames, keyName, keyNameFromDefinition)
Expand All @@ -304,7 +320,8 @@ function parseNonLiteral(
// if there is no maximum, then add a spread item to collect the rest
spreadParam: maxItems >= 0 ? undefined : params,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'TUPLE'
type: 'TUPLE',
deprecated: schema.deprecated
}
}

Expand All @@ -313,7 +330,8 @@ function parseNonLiteral(
keyName,
params,
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames),
type: 'ARRAY'
type: 'ARRAY',
deprecated: schema.deprecated
}
}
}
Expand Down Expand Up @@ -347,7 +365,8 @@ function newInterface(
params: parseSchema(schema, options, processed, usedNames, name),
standaloneName: name,
superTypes: parseSuperTypes(schema, options, processed, usedNames),
type: 'INTERFACE'
type: 'INTERFACE',
deprecated: schema.deprecated
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/types/AST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ export interface AbstractAST {
keyName?: string
standaloneName?: string
type: AST_TYPE
deprecated?: boolean
}

export type ASTWithComment = AST & {comment: string}
export type ASTWithName = AST & {keyName: string}
export type ASTWithStandaloneName = AST & {standaloneName: string}

export function hasComment(ast: AST): ast is ASTWithComment {
return 'comment' in ast && ast.comment != null && ast.comment !== ''
return ('comment' in ast && ast.comment != null && ast.comment !== '')
// Compare to true because ast.deprecated might be undefined
|| ('deprecated' in ast && ast.deprecated === true)
}

export function hasStandaloneName(ast: AST): ast is ASTWithStandaloneName {
Expand Down
4 changes: 4 additions & 0 deletions src/types/JSONSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export interface JSONSchema extends JSONSchema4 {
* schema extension to support custom types
*/
tsType?: string
/**
* property exists at least in https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.3
*/
deprecated?: boolean
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great to contribute this to the upstream @types/json-schema typings, since this isn't JSTT-specific.

}

export const Parent = Symbol('Parent')
Expand Down
5 changes: 5 additions & 0 deletions src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ rules.set('When minItems exists, minItems >= 0', schema => {
}
})

rules.set('deprecated must be a boolean', schema => {
const typeOfDeprecated = typeof schema.deprecated
return typeOfDeprecated === 'boolean' || typeOfDeprecated === 'undefined'
})

export function validate(schema: LinkedJSONSchema, filename: string): string[] {
const errors: string[] = []
rules.forEach((rule, ruleName) => {
Expand Down
34 changes: 34 additions & 0 deletions test/__snapshots__/test/test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,39 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## deprecated.js

> Expected output to match snapshot for e2e test: deprecated.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.␊
*/␊
/**␊
* @deprecated␊
* /* comment * /␊
*/␊
export interface ExampleSchema {␊
/**␊
* @deprecated␊
* /* nested comment * /␊
*/␊
firstName: string;␊
/**␊
* @deprecated␊
* Hi, my name's Doechii, this will be in a comment␊
*/␊
middleName?: string;␊
/**␊
* /* nested comment * /␊
*/␊
lastName?: string;␊
}␊
`

## disjointType.js

> Expected output to match snapshot for e2e test: disjointType.js
Expand Down Expand Up @@ -442215,6 +442248,7 @@ Generated by [AVA](https://avajs.dev).
*/␊
created_at?: string␊
/**␊
* @deprecated␊
* deprecated. Please refer to 'comment' instead␊
*/␊
email?: string␊
Expand Down
Binary file modified test/__snapshots__/test/test.ts.snap
Binary file not shown.
28 changes: 28 additions & 0 deletions test/e2e/deprecated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const input = {
"$schema": "http://json-schema.org/draft/2019-09/schema#",
"title": "Example Schema",
"type": "object",
"deprecated": true,
description: '/* comment */',
"properties": {
"firstName": {
"type": "string",
"deprecated": true,
description: '/* nested comment */'
},
"middleName":{
"type": "string",
"deprecated": true,
"description": "Hi, my name's Doechii, this will be in a comment"
},
"lastName":{
"type": "string",
"deprecated": false,
description: '/* nested comment */'
},
},
"additionalProperties": false,
"required": [
"firstName"
]
}
Loading