1- import { JSONSchemaTypeName , AnnotatedJSONSchema , NormalizedJSONSchema , JSONSchema , Parent } from './types/JSONSchema'
2- import { appendToDescription , escapeBlockComment , isSchemaLike , justName , toSafeString , traverse } from './utils'
1+ import {
2+ JSONSchemaTypeName ,
3+ AnnotatedJSONSchema ,
4+ NormalizedJSONSchema ,
5+ JSONSchema ,
6+ Parent ,
7+ Ref ,
8+ IsSchema ,
9+ } from './types/JSONSchema'
10+ import { appendToDescription , escapeBlockComment , justName , toSafeString , traverse } from './utils'
311import { Options } from './'
4- import { DereferencedPaths } from './resolver'
512import { isDeepStrictEqual } from 'util'
13+ import { typesOfSchema } from './typesOfSchema'
614
7- type Rule = (
8- schema : AnnotatedJSONSchema ,
9- fileName : string ,
10- options : Options ,
11- key : string | null ,
12- dereferencedPaths : DereferencedPaths ,
13- ) => void
15+ type Rule = ( schema : AnnotatedJSONSchema , fileName : string , options : Options , key : string | null ) => void
1416const rules = new Map < string , Rule > ( )
1517
1618function hasType ( schema : JSONSchema , type : JSONSchemaTypeName ) {
@@ -62,9 +64,10 @@ rules.set('Default additionalProperties', (schema, _, options) => {
6264} )
6365
6466rules . set ( 'Transform id to $id' , ( schema , fileName ) => {
65- if ( ! isSchemaLike ( schema ) ) {
67+ if ( ! schema [ IsSchema ] ) {
6668 return
6769 }
70+
6871 if ( schema . id && schema . $id && schema . id !== schema . $id ) {
6972 throw ReferenceError (
7073 `Schema must define either id or $id, not both. Given id=${ schema . id } , $id=${ schema . $id } in ${ fileName } ` ,
@@ -76,32 +79,28 @@ rules.set('Transform id to $id', (schema, fileName) => {
7679 }
7780} )
7881
79- rules . set ( 'Add an $id to anything that needs it ' , ( schema , fileName , _options , _key , dereferencedPaths ) => {
80- if ( ! isSchemaLike ( schema ) ) {
82+ rules . set ( 'Add an $id to each top-level schema ' , ( schema , fileName ) => {
83+ if ( schema . $id || schema [ Parent ] ) {
8184 return
8285 }
8386
84- // Top-level schema
85- if ( ! schema . $id && ! schema [ Parent ] ) {
86- schema . $id = toSafeString ( justName ( fileName ) )
87+ if ( ! schema [ IsSchema ] ) {
8788 return
8889 }
8990
90- // Sub-schemas with references
91- if ( ! isArrayType ( schema ) && ! isObjectType ( schema ) ) {
91+ schema . $id = toSafeString ( justName ( fileName ) )
92+ } )
93+
94+ rules . set ( 'Add an $id to each referenced schema' , schema => {
95+ if ( schema . $id ) {
9296 return
9397 }
9498
95- // We'll infer from $id and title downstream
96- // TODO: Normalize upstream
97- const dereferencedName = dereferencedPaths . get ( schema )
98- if ( ! schema . $id && ! schema . title && dereferencedName ) {
99- schema . $id = toSafeString ( justName ( dereferencedName ) )
99+ if ( ! schema [ Ref ] ) {
100+ return
100101 }
101102
102- if ( dereferencedName ) {
103- dereferencedPaths . delete ( schema )
104- }
103+ schema . $id = toSafeString ( justName ( schema [ Ref ] ) )
105104} )
106105
107106rules . set ( 'Escape closing JSDoc comment' , schema => {
@@ -218,6 +217,35 @@ rules.set('Transform definitions to $defs', (schema, fileName) => {
218217 }
219218} )
220219
220+ rules . set (
221+ "Add an $id to each $def that doesn't have one, if unreachableDefinitions is enabled" ,
222+ ( schema , _ , options ) => {
223+ if ( ! options . unreachableDefinitions ) {
224+ return
225+ }
226+
227+ if ( schema . $id ) {
228+ return
229+ }
230+
231+ const parent = schema [ Parent ]
232+ if ( ! parent ) {
233+ return
234+ }
235+
236+ const grandparent = parent [ Parent ]
237+ if ( ! grandparent ) {
238+ return
239+ }
240+
241+ if ( Object . keys ( grandparent ) . find ( _ => grandparent [ _ ] === parent ) !== '$defs' ) {
242+ return
243+ }
244+
245+ schema . $id = toSafeString ( Object . keys ( parent ) . find ( _ => parent [ _ ] === schema ) ! )
246+ } ,
247+ )
248+
221249rules . set ( 'Transform const to singleton enum' , schema => {
222250 if ( schema . const !== undefined ) {
223251 schema . enum = [ schema . const ]
@@ -231,12 +259,34 @@ rules.set('Add tsEnumNames to enum types', (schema, _, options) => {
231259 }
232260} )
233261
234- export function normalize (
235- rootSchema : AnnotatedJSONSchema ,
236- dereferencedPaths : DereferencedPaths ,
237- filename : string ,
238- options : Options ,
239- ) : NormalizedJSONSchema {
240- rules . forEach ( rule => traverse ( rootSchema , ( schema , key ) => rule ( schema , filename , options , key , dereferencedPaths ) ) )
262+ rules . set ( 'Add an $id to each named enum' , schema => {
263+ if ( ! schema [ IsSchema ] ) {
264+ return
265+ }
266+
267+ if ( schema . $id ) {
268+ return
269+ }
270+
271+ if ( ! typesOfSchema ( schema ) . includes ( 'NAMED_ENUM' ) ) {
272+ return
273+ }
274+
275+ const parent = schema [ Parent ]
276+ const keyName = Object . keys ( parent ) . find ( _ => parent [ _ ] === schema )
277+
278+ // Special case: generate nicer names for additionalProperties enums
279+ if ( parent [ IsSchema ] && keyName === 'additionalProperties' ) {
280+ const grandparent = parent [ Parent ]
281+ const parentKeyName = Object . keys ( grandparent ) . find ( _ => grandparent [ _ ] === parent ) !
282+ schema . $id = toSafeString ( parentKeyName ) + toSafeString ( keyName )
283+ return
284+ }
285+
286+ schema . $id = toSafeString ( justName ( keyName ) )
287+ } )
288+
289+ export function normalize ( rootSchema : AnnotatedJSONSchema , filename : string , options : Options ) : NormalizedJSONSchema {
290+ rules . forEach ( rule => traverse ( rootSchema , ( schema , key ) => rule ( schema , filename , options , key ) ) )
241291 return rootSchema as NormalizedJSONSchema
242292}
0 commit comments