Skip to content

Commit e0a7c9c

Browse files
committed
replace legacy mechanism for looking up key name from definitions
1 parent 44793a4 commit e0a7c9c

File tree

5 files changed

+58284
-85329
lines changed

5 files changed

+58284
-85329
lines changed

src/annotator.ts

+61-24
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,78 @@
11
import {isPlainObject} from 'lodash'
22
import {DereferencedPaths} from './resolver'
3-
import {AnnotatedJSONSchema, JSONSchema, Parent, isAnnotated} from './types/JSONSchema'
3+
import {AnnotatedJSONSchema, JSONSchema, Parent, KeyNameFromDefinition, isAnnotated} from './types/JSONSchema'
44

5-
/**
6-
* Traverses over the schema, assigning to each
7-
* node metadata that will be used downstream.
8-
*/
9-
export function annotate(
10-
schema: JSONSchema,
11-
dereferencedPaths: DereferencedPaths,
12-
parent: JSONSchema | null = null,
13-
): AnnotatedJSONSchema {
14-
if (!Array.isArray(schema) && !isPlainObject(schema)) {
15-
return schema as AnnotatedJSONSchema
5+
const annotators = new Map<
6+
string,
7+
(schema: JSONSchema, parent: JSONSchema | null, dereferencedPaths: DereferencedPaths) => void
8+
>()
9+
10+
annotators.set('Add Parent to each node', (schema, parent) => {
11+
Object.defineProperty(schema, Parent, {
12+
enumerable: false,
13+
value: parent,
14+
writable: false,
15+
})
16+
})
17+
18+
annotators.set('Add key name from the original definition, if there was one', (schema, _, dereferencedPaths) => {
19+
const ref = dereferencedPaths.get(schema)
20+
if (!ref) {
21+
return
1622
}
1723

18-
// Handle cycles
19-
if (isAnnotated(schema)) {
20-
return schema
24+
const keyName = getDefinitionKeyNameFromRef(ref)
25+
if (!keyName) {
26+
return
2127
}
2228

23-
// Add a reference to this schema's parent
24-
Object.defineProperty(schema, Parent, {
29+
Object.defineProperty(schema, KeyNameFromDefinition, {
2530
enumerable: false,
26-
value: parent,
31+
value: keyName,
2732
writable: false,
2833
})
34+
})
2935

30-
// Arrays
31-
if (Array.isArray(schema)) {
32-
schema.forEach(child => annotate(child, dereferencedPaths, schema))
36+
function getDefinitionKeyNameFromRef(ref: string): string | undefined {
37+
const parts = ref.split('/')
38+
if (parts[parts.length - 2] !== 'definitions') {
39+
return
3340
}
41+
return parts[parts.length - 1]
42+
}
3443

35-
// Objects
36-
for (const key in schema) {
37-
annotate(schema[key], dereferencedPaths, schema)
44+
/**
45+
* Traverses over the schema, assigning to each
46+
* node metadata that will be used downstream.
47+
*/
48+
export function annotate(schema: JSONSchema, dereferencedPaths: DereferencedPaths): AnnotatedJSONSchema {
49+
function go(s: JSONSchema, parent: JSONSchema | null): void {
50+
if (!Array.isArray(s) && !isPlainObject(s)) {
51+
return
52+
}
53+
54+
// Handle cycles
55+
if (isAnnotated(s)) {
56+
return
57+
}
58+
59+
// Run annotators
60+
annotators.forEach(f => {
61+
f(s, parent, dereferencedPaths)
62+
})
63+
64+
// Handle arrays
65+
if (Array.isArray(s)) {
66+
s.forEach(_ => go(_, s))
67+
}
68+
69+
// Handle objects
70+
for (const key in s) {
71+
go(s[key], s)
72+
}
3873
}
3974

75+
go(schema, null)
76+
4077
return schema as AnnotatedJSONSchema
4178
}

src/parser.ts

+3-49
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {JSONSchema4Type, JSONSchema4TypeName} from 'json-schema'
2-
import {findKey, includes, isPlainObject, map, memoize, omit} from 'lodash'
2+
import {includes, map, omit} from 'lodash'
33
import {format} from 'util'
44
import {Options} from './'
55
import {typesOfSchema} from './typesOfSchema'
@@ -17,10 +17,9 @@ import {
1717
} from './types/AST'
1818
import {
1919
EnumJSONSchema,
20-
getRootSchema,
2120
isBoolean,
2221
isPrimitive,
23-
JSONSchemaWithDefinitions,
22+
KeyNameFromDefinition,
2423
NormalizedJSONSchema,
2524
Parent,
2625
SchemaSchema,
@@ -143,8 +142,7 @@ function parseNonLiteral(
143142
processed: Processed,
144143
usedNames: UsedNames,
145144
): AST {
146-
const definitions = getDefinitionsMemoized(getRootSchema(schema as any)) // TODO
147-
const keyNameFromDefinition = findKey(definitions, _ => _ === schema)
145+
const keyNameFromDefinition = schema[KeyNameFromDefinition]
148146

149147
switch (type) {
150148
case 'ALL_OF':
@@ -487,47 +485,3 @@ via the \`definition\` "${key}".`
487485
})
488486
}
489487
}
490-
491-
type Definitions = {[k: string]: NormalizedJSONSchema}
492-
493-
function getDefinitions(
494-
schema: NormalizedJSONSchema,
495-
isSchema = true,
496-
processed = new Set<NormalizedJSONSchema>(),
497-
): Definitions {
498-
if (processed.has(schema)) {
499-
return {}
500-
}
501-
processed.add(schema)
502-
if (Array.isArray(schema)) {
503-
return schema.reduce(
504-
(prev, cur) => ({
505-
...prev,
506-
...getDefinitions(cur, false, processed),
507-
}),
508-
{},
509-
)
510-
}
511-
if (isPlainObject(schema)) {
512-
return {
513-
...(isSchema && hasDefinitions(schema) ? schema.$defs : {}),
514-
...Object.keys(schema).reduce<Definitions>(
515-
(prev, cur) => ({
516-
...prev,
517-
...getDefinitions(schema[cur], false, processed),
518-
}),
519-
{},
520-
),
521-
}
522-
}
523-
return {}
524-
}
525-
526-
const getDefinitionsMemoized = memoize(getDefinitions)
527-
528-
/**
529-
* TODO: Reduce rate of false positives
530-
*/
531-
function hasDefinitions(schema: NormalizedJSONSchema): schema is JSONSchemaWithDefinitions {
532-
return '$defs' in schema
533-
}

src/types/JSONSchema.ts

+6-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {JSONSchema4, JSONSchema4Type, JSONSchema4TypeName} from 'json-schema'
2-
import {isPlainObject, memoize} from 'lodash'
2+
import {isPlainObject} from 'lodash'
33

44
export type SchemaType =
55
| 'ALL_OF'
@@ -40,10 +40,15 @@ export interface JSONSchema extends JSONSchema4 {
4040
deprecated?: boolean
4141
}
4242

43+
export const KeyNameFromDefinition = Symbol('KeyNameFromDefinition')
4344
export const Parent = Symbol('Parent')
4445

4546
export interface AnnotatedJSONSchema extends JSONSchema {
4647
[Parent]: AnnotatedJSONSchema
48+
/**
49+
* The name of the definition from the original $ref that was dereferenced, if there was one.
50+
*/
51+
[KeyNameFromDefinition]?: string
4752

4853
additionalItems?: boolean | AnnotatedJSONSchema
4954
additionalProperties?: boolean | AnnotatedJSONSchema
@@ -112,24 +117,10 @@ export interface SchemaSchema extends NormalizedJSONSchema {
112117
required: string[]
113118
}
114119

115-
export interface JSONSchemaWithDefinitions extends NormalizedJSONSchema {
116-
$defs: {
117-
[k: string]: NormalizedJSONSchema
118-
}
119-
}
120-
121120
export interface CustomTypeJSONSchema extends NormalizedJSONSchema {
122121
tsType: string
123122
}
124123

125-
export const getRootSchema = memoize((schema: NormalizedJSONSchema): NormalizedJSONSchema => {
126-
const parent = schema[Parent]
127-
if (!parent) {
128-
return schema
129-
}
130-
return getRootSchema(parent)
131-
})
132-
133124
export function isBoolean(schema: AnnotatedJSONSchema | JSONSchemaType): schema is boolean {
134125
return schema === true || schema === false
135126
}

test/__snapshots__/test/test.ts.md

+58,214-85,241
Large diffs are not rendered by default.

test/__snapshots__/test/test.ts.snap

-73.7 KB
Binary file not shown.

0 commit comments

Comments
 (0)