Skip to content

Commit bbdc17e

Browse files
committed
feat: MirrorNode is almost like RegularNode
1 parent 19ece85 commit bbdc17e

File tree

9 files changed

+808
-565
lines changed

9 files changed

+808
-565
lines changed

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

Lines changed: 301 additions & 190 deletions
Large diffs are not rendered by default.

src/__tests__/tree.spec.ts

Lines changed: 426 additions & 352 deletions
Large diffs are not rendered by default.

src/__tests__/utils/printTree.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { pathToPointer } from '@stoplight/json';
2+
import type { Dictionary } from '@stoplight/types';
23
import * as treeify from 'treeify';
34

45
import { MirrorNode, ReferenceNode, RegularNode, SchemaNode } from '../../nodes';
@@ -12,13 +13,13 @@ export function printTree(schema: SchemaFragment, opts?: Partial<SchemaTreeOptio
1213

1314
const root: unknown =
1415
tree.root.children.length > 1
15-
? tree.root.children.map(prepareTree, new WeakSet())
16+
? tree.root.children.map(child => prepareTree.call(new WeakSet(), child))
1617
: prepareTree.call(new WeakSet(), tree.root.children[0]);
1718

1819
return treeify.asTree(root as treeify.TreeObject, true, true);
1920
}
2021

21-
function printRegularNode(this: WeakSet<SchemaNode>, node: RegularNode): any {
22+
function printRegularNode(this: WeakSet<SchemaFragment>, node: RegularNode): Dictionary<unknown> {
2223
return {
2324
...(node.types !== null ? { types: node.types } : null),
2425
...(node.primaryType !== null ? { primaryType: node.primaryType } : null),
@@ -36,30 +37,30 @@ function printReferenceNode(node: ReferenceNode) {
3637
};
3738
}
3839

39-
function printMirrorNode(this: WeakSet<SchemaNode>, node: MirrorNode): any {
40-
if (this.has(node.mirrors)) {
40+
function printMirrorNode(this: WeakSet<SchemaFragment>, node: MirrorNode): any {
41+
if (this.has(node.fragment)) {
4142
return {
42-
mirrors: pathToPointer(node.path as string[]),
43+
mirrors: pathToPointer(node.mirroredNode.path as string[]),
4344
};
4445
}
4546

46-
this.add(node.mirrors);
47-
return printNode.call(this, node.mirrors);
47+
this.add(node.fragment);
48+
return printRegularNode.call(this, node as RegularNode);
4849
}
4950

50-
function printNode(this: WeakSet<SchemaNode>, node: SchemaNode) {
51-
return node instanceof RegularNode
51+
function printNode(this: WeakSet<SchemaFragment>, node: SchemaNode) {
52+
return node instanceof MirrorNode
53+
? printMirrorNode.call(this, node)
54+
: node instanceof RegularNode
5255
? printRegularNode.call(this, node)
5356
: node instanceof ReferenceNode
5457
? printReferenceNode.call(this, node)
55-
: node instanceof MirrorNode
56-
? printMirrorNode.call(this, node)
5758
: {
5859
kind: 'unknown node',
5960
};
6061
}
6162

62-
function prepareTree(this: WeakSet<SchemaNode>, node: SchemaNode) {
63+
function prepareTree(this: WeakSet<SchemaFragment>, node: SchemaNode) {
6364
return {
6465
[pathToPointer(node.path as string[])]: printNode.call(this, node),
6566
};

src/guards/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './nodes';

src/guards/nodes.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { MirrorNode, ReferenceNode, RegularNode, SchemaNode } from '../nodes';
2+
3+
export function isRegularNode(node: SchemaNode): node is RegularNode {
4+
return 'types' in node && 'primaryType' in node && 'combiners' in node;
5+
}
6+
7+
export function isMirrorNode(node: SchemaNode): node is MirrorNode {
8+
return 'mirroredNode' in node;
9+
}
10+
11+
export function isReferenceNode(node: SchemaNode): node is ReferenceNode {
12+
return 'external' in node && 'value' in node;
13+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './guards';
12
export * from './nodes';
23
export * from './resolver';
34
export * from './tree';

src/nodes/MirrorNode.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,59 @@
1+
import type { Dictionary } from '@stoplight/types';
2+
13
import { BaseNode } from './BaseNode';
2-
import type { SchemaNode } from './types';
4+
import type { RegularNode } from './RegularNode';
5+
import type { SchemaAnnotations, SchemaCombinerName, SchemaMeta, SchemaNode, SchemaNodeKind } from './types';
6+
7+
export class MirrorNode extends BaseNode implements RegularNode {
8+
public readonly types!: SchemaNodeKind[] | null;
9+
public readonly primaryType!: SchemaNodeKind | null;
10+
public readonly combiners!: SchemaCombinerName[] | null;
11+
12+
public readonly required!: string[] | null;
13+
public readonly enum!: unknown[] | null;
14+
public readonly format!: string | null;
15+
public readonly title!: string | null;
16+
public readonly deprecated!: boolean;
17+
18+
public readonly meta!: Readonly<Partial<Dictionary<unknown, SchemaMeta>>>;
19+
public readonly annotations!: Readonly<Partial<Dictionary<unknown, SchemaAnnotations>>>;
20+
public readonly validations!: Readonly<Dictionary<unknown>>;
21+
22+
public readonly simple!: boolean;
23+
24+
constructor(public readonly mirroredNode: SchemaNode) {
25+
super(mirroredNode.fragment);
26+
27+
return new Proxy(this, {
28+
get(target, key) {
29+
if (key in target) {
30+
return target[key];
31+
}
32+
33+
if (key in mirroredNode) {
34+
return Reflect.get(mirroredNode, key, mirroredNode);
35+
}
36+
37+
return;
38+
},
339

4-
export class MirrorNode extends BaseNode {
5-
constructor(public readonly mirrors: SchemaNode) {
6-
super(mirrors.fragment);
40+
has(target, key) {
41+
return key in target || key in mirroredNode;
42+
},
43+
});
744
}
845

946
public get children(): MirrorNode[] | null {
10-
if (!('children' in this.mirrors)) return null;
47+
if (!('children' in this.mirroredNode)) return null;
1148

12-
const referencedChildren = this.mirrors.children;
49+
const referencedChildren = this.mirroredNode.children;
1350
if (referencedChildren === null) return null;
1451

1552
const children: MirrorNode[] = [];
1653
for (const child of referencedChildren) {
1754
const mirroredChild = new MirrorNode(child);
1855
mirroredChild.parent = this;
56+
mirroredChild.subpath = child.subpath;
1957
children.push(mirroredChild);
2058
}
2159

src/nodes/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { MirrorNode } from './MirrorNode';
22
import type { ReferenceNode } from './ReferenceNode';
33
import type { RegularNode } from './RegularNode';
4+
import type { RootNode } from './RootNode';
45

5-
export type SchemaNode = RegularNode | ReferenceNode | MirrorNode;
6+
export type SchemaNode = RootNode | RegularNode | ReferenceNode | MirrorNode;
67

78
export enum SchemaNodeKind {
89
Any = 'any',

src/walker/walker.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { EventEmitter } from '@stoplight/lifecycle';
22
import type { Dictionary } from '@stoplight/types';
33

4+
import { isRegularNode } from '../guards';
45
import { mergeAllOf } from '../mergers/mergeAllOf';
56
import { mergeOneOrAnyOf } from '../mergers/mergeOneOrAnyOf';
67
import { ReferenceNode, RegularNode } from '../nodes';
78
import { MirrorNode } from '../nodes/MirrorNode';
8-
import type { RootNode } from '../nodes/RootNode';
9+
import { RootNode } from '../nodes/RootNode';
910
import { SchemaCombinerName, SchemaNode, SchemaNodeKind } from '../nodes/types';
1011
import type { SchemaFragment } from '../types';
1112
import { isObjectLiteral } from '../utils/guards';
@@ -91,8 +92,10 @@ export class Walker extends EventEmitter<Dictionary<WalkerEventHandler, WalkerEv
9192
continue;
9293
}
9394

94-
schemaNode.parent = initialSchemaNode;
95-
schemaNode.subpath = this.path.slice(initialSchemaNode.path.length);
95+
if (!(schemaNode instanceof RootNode)) {
96+
schemaNode.parent = initialSchemaNode;
97+
schemaNode.subpath = this.path.slice(initialSchemaNode.path.length);
98+
}
9699

97100
if ('children' in initialSchemaNode) {
98101
if (initialSchemaNode.children === null) {
@@ -124,7 +127,7 @@ export class Walker extends EventEmitter<Dictionary<WalkerEventHandler, WalkerEv
124127
protected *walkNodeChildren() {
125128
const { fragment, schemaNode } = this;
126129

127-
if (!(schemaNode instanceof RegularNode)) return;
130+
if (!isRegularNode(schemaNode)) return;
128131

129132
const {
130133
depth: initialDepth,

0 commit comments

Comments
 (0)