Skip to content

Commit 29fa570

Browse files
authored
fix(walker): reuse allOfs correctly (#7)
* fix(walker): reuse allOfs correctly * chore(walker): oneOf/anyOf should also reuse initialFragment
1 parent 4c95bda commit 29fa570

File tree

4 files changed

+125
-16
lines changed

4 files changed

+125
-16
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"properties": {
3+
"someProp": {
4+
"$ref": "#/__bundled__/repo"
5+
}
6+
},
7+
"__bundled__": {
8+
"repo": {
9+
"properties": {
10+
"parent": {
11+
"allOf": [
12+
{
13+
"$ref": "#/__bundled__/repo"
14+
},
15+
{
16+
"type": "object",
17+
"properties": {
18+
"foo": {
19+
"type": "string"
20+
}
21+
}
22+
}
23+
]
24+
}
25+
},
26+
"type": "object"
27+
}
28+
}
29+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$ref": "#/__bundled__/repo",
3+
"__bundled__": {
4+
"repo": {
5+
"properties": {
6+
"parent": {
7+
"allOf": [
8+
{
9+
"$ref": "#/__bundled__/repo"
10+
},
11+
{
12+
"type": "object"
13+
}
14+
]
15+
}
16+
},
17+
"type": "object"
18+
}
19+
}
20+
}

src/__tests__/__snapshots__/tree.spec.ts.snap

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,53 @@ exports[`SchemaTree output should generate valid tree for combiners/allOfs/base.
554554
"
555555
`;
556556

557+
exports[`SchemaTree output should generate valid tree for combiners/allOfs/circular-regular-level.json 1`] = `
558+
"└─ #
559+
├─ types
560+
│ └─ 0: object
561+
├─ primaryType: object
562+
└─ children
563+
└─ 0
564+
└─ #/properties/someProp
565+
├─ types
566+
│ └─ 0: object
567+
├─ primaryType: object
568+
└─ children
569+
└─ 0
570+
└─ #/properties/someProp/properties/parent
571+
├─ types
572+
│ └─ 0: object
573+
├─ primaryType: object
574+
└─ children
575+
├─ 0
576+
│ └─ #/properties/someProp/properties/parent/properties/parent
577+
│ └─ mirrors: #/properties/someProp/properties/parent
578+
└─ 1
579+
└─ #/properties/someProp/properties/parent/properties/foo
580+
├─ types
581+
│ └─ 0: string
582+
└─ primaryType: string
583+
"
584+
`;
585+
586+
exports[`SchemaTree output should generate valid tree for combiners/allOfs/circular-top-level.json 1`] = `
587+
"└─ #
588+
├─ types
589+
│ └─ 0: object
590+
├─ primaryType: object
591+
└─ children
592+
└─ 0
593+
└─ #/properties/parent
594+
├─ types
595+
│ └─ 0: object
596+
├─ primaryType: object
597+
└─ children
598+
└─ 0
599+
└─ #/properties/parent/properties/parent
600+
└─ mirrors: #/properties/parent
601+
"
602+
`;
603+
557604
exports[`SchemaTree output should generate valid tree for combiners/allOfs/complex.json 1`] = `
558605
"└─ #
559606
├─ types

src/walker/walker.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ type InternalWalkerState = {
1919
schemaNode: RegularNode | RootNode;
2020
};
2121

22+
type ProcessedFragment = SchemaFragment | SchemaFragment[];
23+
2224
export class Walker extends EventEmitter<WalkerEmitter> {
2325
public readonly path: string[];
2426
public depth: number;
2527

2628
protected fragment: SchemaFragment;
2729
protected schemaNode: RegularNode | RootNode;
2830

29-
private processedFragments: WeakMap<SchemaFragment, SchemaNode>;
31+
private processedFragments: WeakMap<ProcessedFragment, SchemaNode>;
3032
private readonly hooks: Partial<Dictionary<WalkerHookHandler, WalkerHookAction>>;
3133

3234
constructor(protected readonly root: RootNode, protected readonly walkingOptions: WalkingOptions) {
@@ -103,10 +105,12 @@ export class Walker extends EventEmitter<WalkerEmitter> {
103105
const state = this.dumpInternalWalkerState();
104106

105107
super.emit('enterFragment', fragment);
106-
const schemaNode = this.processFragment();
108+
const [schemaNode, initialFragment] = this.processFragment();
107109
super.emit('enterNode', schemaNode);
108110

109-
this.processedFragments.set(schemaNode.fragment, isMirroredNode(schemaNode) ? schemaNode.mirroredNode : schemaNode);
111+
const actualNode = isMirroredNode(schemaNode) ? schemaNode.mirroredNode : schemaNode;
112+
this.processedFragments.set(schemaNode.fragment, actualNode);
113+
this.processedFragments.set(initialFragment, actualNode);
110114

111115
this.fragment = schemaNode.fragment;
112116
this.depth = initialDepth + 1;
@@ -237,23 +241,23 @@ export class Walker extends EventEmitter<WalkerEmitter> {
237241
this.schemaNode = schemaNode;
238242
}
239243

240-
protected retrieveFromFragment(fragment: SchemaFragment): MirroredSchemaNode | void {
244+
protected retrieveFromFragment(fragment: ProcessedFragment): [MirroredSchemaNode, ProcessedFragment] | void {
241245
const processedSchemaNode = this.processedFragments.get(fragment);
242246
if (processedSchemaNode !== void 0) {
243247
if (isRegularNode(processedSchemaNode)) {
244-
return new MirroredRegularNode(processedSchemaNode);
248+
return [new MirroredRegularNode(processedSchemaNode), fragment];
245249
}
246250

247251
if (isReferenceNode(processedSchemaNode)) {
248-
return new MirroredReferenceNode(processedSchemaNode);
252+
return [new MirroredReferenceNode(processedSchemaNode), fragment];
249253
}
250254

251255
// whoops, we don't know what to do with it
252256
throw new TypeError('Cannot mirror the node');
253257
}
254258
}
255259

256-
protected processFragment(): SchemaNode {
260+
protected processFragment(): [SchemaNode, ProcessedFragment] {
257261
const { walkingOptions, path } = this;
258262
let { fragment } = this;
259263

@@ -265,23 +269,29 @@ export class Walker extends EventEmitter<WalkerEmitter> {
265269

266270
if ('$ref' in fragment) {
267271
if (typeof fragment.$ref !== 'string') {
268-
return new ReferenceNode(fragment, '$ref is not a string');
272+
return [new ReferenceNode(fragment, '$ref is not a string'), fragment];
269273
} else if (walkingOptions.resolveRef !== null) {
270274
try {
271275
fragment = walkingOptions.resolveRef(path, fragment.$ref);
272276
} catch (ex) {
273277
super.emit('error', createMagicError(ex));
274-
return new ReferenceNode(fragment, ex?.message ?? 'Unknown resolving error');
278+
return [new ReferenceNode(fragment, ex?.message ?? 'Unknown resolving error'), fragment];
275279
}
276280
} else {
277-
return new ReferenceNode(fragment, null);
281+
return [new ReferenceNode(fragment, null), fragment];
278282
}
279283
}
280284

285+
let initialFragment: ProcessedFragment = fragment;
281286
if (walkingOptions.mergeAllOf && SchemaCombinerName.AllOf in fragment) {
282287
try {
288+
if (Array.isArray(fragment.allOf)) {
289+
initialFragment = fragment.allOf;
290+
}
291+
283292
fragment = mergeAllOf(fragment, path, walkingOptions);
284293
} catch (ex) {
294+
initialFragment = fragment;
285295
super.emit('error', createMagicError(new MergingError(ex?.message ?? 'Unknown merging error')));
286296
// no the end of the world - we will render raw unprocessed fragment
287297
}
@@ -291,25 +301,28 @@ export class Walker extends EventEmitter<WalkerEmitter> {
291301
try {
292302
const merged = mergeOneOrAnyOf(fragment, path, walkingOptions);
293303
if (merged.length === 1) {
294-
return new RegularNode(merged[0]);
304+
return [new RegularNode(merged[0]), initialFragment];
295305
} else {
296306
const combiner = SchemaCombinerName.OneOf in fragment ? SchemaCombinerName.OneOf : SchemaCombinerName.AnyOf;
297-
return new RegularNode({
298-
[combiner]: merged,
299-
});
307+
return [
308+
new RegularNode({
309+
[combiner]: merged,
310+
}),
311+
initialFragment,
312+
];
300313
}
301314
} catch (ex) {
302315
super.emit('error', createMagicError(new MergingError(ex?.message ?? 'Unknown merging error')));
303316
// no the end of the world - we will render raw unprocessed fragment
304317
}
305318
}
306319

307-
retrieved = isNonNullable(fragment) ? this.retrieveFromFragment(fragment) : null;
320+
retrieved = isNonNullable(fragment) ? this.retrieveFromFragment(initialFragment) : null;
308321

309322
if (retrieved) {
310323
return retrieved;
311324
}
312325

313-
return new RegularNode(fragment);
326+
return [new RegularNode(fragment), initialFragment];
314327
}
315328
}

0 commit comments

Comments
 (0)