diff --git a/jest.config.js b/jest.config.js index 58f0a45..5346809 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,7 @@ module.exports = { rootDir: process.cwd(), testEnvironment: 'node', + roots: ['/src'], setupFilesAfterEnv: ['./setupTests.ts'], testMatch: ['/src/**/__tests__/*.(ts|js)?(x)'], transform: { diff --git a/src/__tests__/__fixtures__/arrays/additional-empty.json b/src/__tests__/__fixtures__/arrays/additional-empty.json new file mode 100644 index 0000000..b968368 --- /dev/null +++ b/src/__tests__/__fixtures__/arrays/additional-empty.json @@ -0,0 +1,4 @@ +{ + "type": "array", + "additionalItems": {} +} diff --git a/src/__tests__/__fixtures__/arrays/additional-false.json b/src/__tests__/__fixtures__/arrays/additional-false.json new file mode 100644 index 0000000..024923c --- /dev/null +++ b/src/__tests__/__fixtures__/arrays/additional-false.json @@ -0,0 +1,4 @@ +{ + "type": "array", + "additionalItems": false +} diff --git a/src/__tests__/__fixtures__/arrays/additional-schema.json b/src/__tests__/__fixtures__/arrays/additional-schema.json new file mode 100644 index 0000000..d795cb4 --- /dev/null +++ b/src/__tests__/__fixtures__/arrays/additional-schema.json @@ -0,0 +1,11 @@ +{ + "type": "array", + "additionalItems": { + "type": "object", + "properties": { + "baz": { + "type": "number" + } + } + } +} diff --git a/src/__tests__/__fixtures__/arrays/additional-true.json b/src/__tests__/__fixtures__/arrays/additional-true.json new file mode 100644 index 0000000..01777c1 --- /dev/null +++ b/src/__tests__/__fixtures__/arrays/additional-true.json @@ -0,0 +1,4 @@ +{ + "type": "array", + "additionalItems": true +} diff --git a/src/__tests__/__fixtures__/arrays/with-multiple-arrayish-items.json b/src/__tests__/__fixtures__/arrays/with-multiple-arrayish-items.json index e45d26d..6a9e8ef 100644 --- a/src/__tests__/__fixtures__/arrays/with-multiple-arrayish-items.json +++ b/src/__tests__/__fixtures__/arrays/with-multiple-arrayish-items.json @@ -17,10 +17,7 @@ "type": "string" } }, - "required": [ - "code", - "msg" - ] + "required": ["code", "msg"] } ] } diff --git a/src/__tests__/__fixtures__/arrays/with-single-arrayish-items.json b/src/__tests__/__fixtures__/arrays/with-single-arrayish-items.json index f15e8d3..b5b53c5 100644 --- a/src/__tests__/__fixtures__/arrays/with-single-arrayish-items.json +++ b/src/__tests__/__fixtures__/arrays/with-single-arrayish-items.json @@ -14,10 +14,7 @@ "type": "string" } }, - "required": [ - "code", - "msg" - ] + "required": ["code", "msg"] } ] } diff --git a/src/__tests__/__fixtures__/objects/additional-empty.json b/src/__tests__/__fixtures__/objects/additional-empty.json new file mode 100644 index 0000000..08c87bb --- /dev/null +++ b/src/__tests__/__fixtures__/objects/additional-empty.json @@ -0,0 +1,4 @@ +{ + "type": "object", + "additionalProperties": {} +} diff --git a/src/__tests__/__fixtures__/objects/additional-false.json b/src/__tests__/__fixtures__/objects/additional-false.json new file mode 100644 index 0000000..97ea2a4 --- /dev/null +++ b/src/__tests__/__fixtures__/objects/additional-false.json @@ -0,0 +1,4 @@ +{ + "type": "object", + "additionalProperties": false +} diff --git a/src/__tests__/__fixtures__/objects/additional-schema.json b/src/__tests__/__fixtures__/objects/additional-schema.json new file mode 100644 index 0000000..66de7a3 --- /dev/null +++ b/src/__tests__/__fixtures__/objects/additional-schema.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "baz": { + "type": "number" + } + } + } +} diff --git a/src/__tests__/__fixtures__/objects/additional-true.json b/src/__tests__/__fixtures__/objects/additional-true.json new file mode 100644 index 0000000..9bc2c95 --- /dev/null +++ b/src/__tests__/__fixtures__/objects/additional-true.json @@ -0,0 +1,4 @@ +{ + "type": "object", + "additionalProperties": true +} diff --git a/src/__tests__/__snapshots__/tree.spec.ts.snap b/src/__tests__/__snapshots__/tree.spec.ts.snap index 15893f9..28d8a9e 100644 --- a/src/__tests__/__snapshots__/tree.spec.ts.snap +++ b/src/__tests__/__snapshots__/tree.spec.ts.snap @@ -296,6 +296,61 @@ exports[`SchemaTree output compound keywords given oneOf combiner placed next to " `; +exports[`SchemaTree output should generate valid tree for arrays/additional-empty.json 1`] = ` +"└─ # + ├─ types + │ └─ 0: array + ├─ primaryType: array + └─ children + └─ 0 + └─ #/additionalItems +" +`; + +exports[`SchemaTree output should generate valid tree for arrays/additional-false.json 1`] = ` +"└─ # + ├─ types + │ └─ 0: array + ├─ primaryType: array + └─ children + └─ 0 + └─ #/additionalItems + └─ value: false +" +`; + +exports[`SchemaTree output should generate valid tree for arrays/additional-schema.json 1`] = ` +"└─ # + ├─ types + │ └─ 0: array + ├─ primaryType: array + └─ children + └─ 0 + └─ #/additionalItems + ├─ types + │ └─ 0: object + ├─ primaryType: object + └─ children + └─ 0 + └─ #/additionalItems/properties/baz + ├─ types + │ └─ 0: number + └─ primaryType: number +" +`; + +exports[`SchemaTree output should generate valid tree for arrays/additional-true.json 1`] = ` +"└─ # + ├─ types + │ └─ 0: array + ├─ primaryType: array + └─ children + └─ 0 + └─ #/additionalItems + └─ value: true +" +`; + exports[`SchemaTree output should generate valid tree for arrays/of-allofs.json 1`] = ` "└─ # ├─ types @@ -823,7 +878,16 @@ exports[`SchemaTree output should generate valid tree for combiners/allOfs/neste │ └─ #/properties/order │ ├─ types │ │ └─ 0: object - │ └─ primaryType: object + │ ├─ primaryType: object + │ └─ children + │ └─ 0 + │ └─ #/properties/order/additionalProperties + │ ├─ types + │ │ └─ 0: string + │ ├─ primaryType: string + │ └─ enum + │ ├─ 0: ASC + │ └─ 1: DESC └─ 7 └─ #/properties/nextToken ├─ types @@ -1246,6 +1310,61 @@ exports[`SchemaTree output should generate valid tree for formats-schema.json 1` " `; +exports[`SchemaTree output should generate valid tree for objects/additional-empty.json 1`] = ` +"└─ # + ├─ types + │ └─ 0: object + ├─ primaryType: object + └─ children + └─ 0 + └─ #/additionalProperties +" +`; + +exports[`SchemaTree output should generate valid tree for objects/additional-false.json 1`] = ` +"└─ # + ├─ types + │ └─ 0: object + ├─ primaryType: object + └─ children + └─ 0 + └─ #/additionalProperties + └─ value: false +" +`; + +exports[`SchemaTree output should generate valid tree for objects/additional-schema.json 1`] = ` +"└─ # + ├─ types + │ └─ 0: object + ├─ primaryType: object + └─ children + └─ 0 + └─ #/additionalProperties + ├─ types + │ └─ 0: object + ├─ primaryType: object + └─ children + └─ 0 + └─ #/additionalProperties/properties/baz + ├─ types + │ └─ 0: number + └─ primaryType: number +" +`; + +exports[`SchemaTree output should generate valid tree for objects/additional-true.json 1`] = ` +"└─ # + ├─ types + │ └─ 0: object + ├─ primaryType: object + └─ children + └─ 0 + └─ #/additionalProperties + └─ value: true +" +`; + exports[`SchemaTree output should generate valid tree for references/base.json 1`] = ` "└─ # ├─ types diff --git a/src/__tests__/tree.spec.ts b/src/__tests__/tree.spec.ts index 721cf98..0f88231 100644 --- a/src/__tests__/tree.spec.ts +++ b/src/__tests__/tree.spec.ts @@ -907,6 +907,34 @@ describe('SchemaTree', () => { tree.root.children[0].children[1].annotations.description, ).toEqual('_Everyone_ ~hates~ loves caves'); }); + + it('should render true/false schemas', () => { + const schema = { + type: 'object', + properties: { + bear: true, + cave: false, + }, + }; + + const tree = new SchemaTree(schema); + tree.populate(); + + expect(printTree(schema)).toMatchInlineSnapshot(` + "└─ # + ├─ types + │ └─ 0: object + ├─ primaryType: object + └─ children + ├─ 0 + │ └─ #/properties/bear + │ └─ value: true + └─ 1 + └─ #/properties/cave + └─ value: false + " + `); + }); }); describe('position', () => { diff --git a/src/__tests__/utils/printTree.ts b/src/__tests__/utils/printTree.ts index 18d7cbb..f329a91 100644 --- a/src/__tests__/utils/printTree.ts +++ b/src/__tests__/utils/printTree.ts @@ -2,8 +2,9 @@ import { pathToPointer } from '@stoplight/json'; import type { Dictionary } from '@stoplight/types'; import * as treeify from 'treeify'; -import { isMirroredNode, isReferenceNode, isRegularNode } from '../../guards'; +import { isBooleanishNode, isMirroredNode, isReferenceNode, isRegularNode, isRootNode } from '../../guards'; import type { MirroredSchemaNode, ReferenceNode, RegularNode, SchemaNode } from '../../nodes'; +import type { BooleanishNode } from '../../nodes/BooleanishNode'; import type { SchemaTreeOptions } from '../../tree'; import { SchemaTree } from '../../tree'; import type { SchemaFragment } from '../../types'; @@ -41,6 +42,12 @@ function printReferenceNode(node: ReferenceNode) { }; } +function printBooleanishNode(node: BooleanishNode) { + return { + value: node.fragment, + }; +} + function printMirrorNode(node: MirroredSchemaNode): any { return { mirrors: pathToPointer(node.mirroredNode.path as string[]), @@ -48,15 +55,17 @@ function printMirrorNode(node: MirroredSchemaNode): any { } function printNode(node: SchemaNode) { - return isMirroredNode(node) - ? printMirrorNode(node) - : isRegularNode(node) - ? printRegularNode(node) - : isReferenceNode(node) - ? printReferenceNode(node) - : { - kind: 'unknown node', - }; + if (isMirroredNode(node)) { + return printMirrorNode(node); + } else if (isRegularNode(node)) { + return printRegularNode(node); + } else if (isReferenceNode(node)) { + return printReferenceNode(node); + } else if (isBooleanishNode(node)) { + return printBooleanishNode(node); + } else if (isRootNode(node)) { + return {}; + } } function prepareTree(node: SchemaNode) { diff --git a/src/accessors/getValidations.ts b/src/accessors/getValidations.ts index 2d5394d..6425248 100644 --- a/src/accessors/getValidations.ts +++ b/src/accessors/getValidations.ts @@ -12,8 +12,8 @@ const VALIDATION_TYPES: Partial>>; public readonly validations: Readonly>; public readonly originalFragment: SchemaFragment; constructor(public readonly fragment: SchemaFragment, context?: { originalFragment?: SchemaFragment }) { - super(fragment); + super(); this.$id = unwrapStringOrNull('id' in fragment ? fragment.id : fragment.$id); this.types = getTypes(fragment); diff --git a/src/nodes/RootNode.ts b/src/nodes/RootNode.ts index 6c744b4..9d7f8f1 100644 --- a/src/nodes/RootNode.ts +++ b/src/nodes/RootNode.ts @@ -7,7 +7,7 @@ export class RootNode extends BaseNode { public readonly children: SchemaNode[]; constructor(public readonly fragment: SchemaFragment) { - super(fragment); + super(); this.children = []; } } diff --git a/src/nodes/index.ts b/src/nodes/index.ts index 4bd4715..b60c970 100644 --- a/src/nodes/index.ts +++ b/src/nodes/index.ts @@ -1,4 +1,5 @@ export { BaseNode } from './BaseNode'; +export { BooleanishNode } from './BooleanishNode'; export * from './mirrored'; export { ReferenceNode } from './ReferenceNode'; export { RegularNode } from './RegularNode'; diff --git a/src/nodes/mirrored/MirroredReferenceNode.ts b/src/nodes/mirrored/MirroredReferenceNode.ts index d1404c8..89cccf4 100644 --- a/src/nodes/mirrored/MirroredReferenceNode.ts +++ b/src/nodes/mirrored/MirroredReferenceNode.ts @@ -1,9 +1,13 @@ +import type { SchemaFragment } from '../../types'; import { BaseNode } from '../BaseNode'; import type { ReferenceNode } from '../ReferenceNode'; export class MirroredReferenceNode extends BaseNode implements ReferenceNode { + public readonly fragment: SchemaFragment; + constructor(public readonly mirroredNode: ReferenceNode) { - super(mirroredNode.fragment); + super(); + this.fragment = mirroredNode.fragment; } get error() { diff --git a/src/nodes/mirrored/MirroredRegularNode.ts b/src/nodes/mirrored/MirroredRegularNode.ts index 7eb07b7..873c15f 100644 --- a/src/nodes/mirrored/MirroredRegularNode.ts +++ b/src/nodes/mirrored/MirroredRegularNode.ts @@ -1,15 +1,17 @@ import type { Dictionary } from '@stoplight/types'; -import { isRegularNode } from '../../guards'; +import { isReferenceNode, isRegularNode } from '../../guards'; import type { SchemaFragment } from '../../types'; import { isNonNullable } from '../../utils'; import { BaseNode } from '../BaseNode'; +import { BooleanishNode } from '../BooleanishNode'; import type { ReferenceNode } from '../ReferenceNode'; import type { RegularNode } from '../RegularNode'; import type { SchemaAnnotations, SchemaCombinerName, SchemaNodeKind } from '../types'; import { MirroredReferenceNode } from './MirroredReferenceNode'; export class MirroredRegularNode extends BaseNode implements RegularNode { + public readonly fragment: SchemaFragment; public readonly $id!: string | null; public readonly types!: SchemaNodeKind[] | null; public readonly primaryType!: SchemaNodeKind | null; @@ -28,10 +30,14 @@ export class MirroredRegularNode extends BaseNode implements RegularNode { public readonly simple!: boolean; public readonly unknown!: boolean; - private readonly cache: WeakMap; + private readonly cache: WeakMap< + RegularNode | BooleanishNode | ReferenceNode, + MirroredRegularNode | BooleanishNode | MirroredReferenceNode + >; constructor(public readonly mirroredNode: RegularNode, context?: { originalFragment?: SchemaFragment }) { - super(mirroredNode.fragment); + super(); + this.fragment = mirroredNode.fragment; this.originalFragment = context?.originalFragment ?? mirroredNode.originalFragment; this.cache = new WeakMap(); @@ -59,9 +65,9 @@ export class MirroredRegularNode extends BaseNode implements RegularNode { private readonly _this: MirroredRegularNode; - private _children?: (MirroredRegularNode | MirroredReferenceNode)[]; + private _children?: (MirroredRegularNode | BooleanishNode | MirroredReferenceNode)[]; - public get children(): (MirroredRegularNode | MirroredReferenceNode)[] | null | undefined { + public get children(): (MirroredRegularNode | BooleanishNode | MirroredReferenceNode)[] | null | undefined { const referencedChildren = this.mirroredNode.children; if (!isNonNullable(referencedChildren)) { @@ -74,7 +80,7 @@ export class MirroredRegularNode extends BaseNode implements RegularNode { this._children.length = 0; } - const children: (MirroredRegularNode | MirroredReferenceNode)[] = this._children; + const children: (MirroredRegularNode | BooleanishNode | MirroredReferenceNode)[] = this._children; for (const child of referencedChildren) { // this is to avoid pointing at nested mirroring const cached = this.cache.get(child); @@ -84,7 +90,11 @@ export class MirroredRegularNode extends BaseNode implements RegularNode { continue; } - const mirroredChild = isRegularNode(child) ? new MirroredRegularNode(child) : new MirroredReferenceNode(child); + const mirroredChild = isRegularNode(child) + ? new MirroredRegularNode(child) + : isReferenceNode(child) + ? new MirroredReferenceNode(child) + : new BooleanishNode(child.fragment); mirroredChild.parent = this._this; mirroredChild.subpath = child.subpath; diff --git a/src/nodes/types.ts b/src/nodes/types.ts index 5d69517..2723d4a 100644 --- a/src/nodes/types.ts +++ b/src/nodes/types.ts @@ -1,3 +1,4 @@ +import type { BooleanishNode } from './BooleanishNode'; import type { MirroredReferenceNode } from './mirrored/MirroredReferenceNode'; import type { MirroredRegularNode } from './mirrored/MirroredRegularNode'; import type { ReferenceNode } from './ReferenceNode'; @@ -6,7 +7,7 @@ import type { RootNode } from './RootNode'; export type MirroredSchemaNode = MirroredRegularNode | MirroredReferenceNode; -export type SchemaNode = RootNode | RegularNode | ReferenceNode | MirroredSchemaNode; +export type SchemaNode = RootNode | RegularNode | BooleanishNode | ReferenceNode | MirroredSchemaNode; export enum SchemaNodeKind { Any = 'any', diff --git a/src/utils/guards.ts b/src/utils/guards.ts index 085f14a..778deae 100644 --- a/src/utils/guards.ts +++ b/src/utils/guards.ts @@ -1,5 +1,7 @@ import type { Dictionary } from '@stoplight/types'; +import type { SchemaFragment } from '../types'; + export function isStringOrNumber(value: unknown): value is number | string { return typeof value === 'string' || typeof value === 'number'; } @@ -23,3 +25,7 @@ export function isObjectLiteral(maybeObj: unknown): maybeObj is Dictionary(maybeNullable: T): maybeNullable is NonNullable { return maybeNullable !== void 0 && maybeNullable !== null; } + +export function isValidSchemaFragment(maybeSchemaFragment: unknown): maybeSchemaFragment is SchemaFragment { + return typeof maybeSchemaFragment === 'boolean' || isObjectLiteral(maybeSchemaFragment); +} diff --git a/src/walker/types.ts b/src/walker/types.ts index 34f7b74..284f73d 100644 --- a/src/walker/types.ts +++ b/src/walker/types.ts @@ -13,7 +13,7 @@ export type WalkingOptions = { }; export type WalkerSnapshot = { - readonly fragment: SchemaFragment; + readonly fragment: SchemaFragment | boolean; readonly depth: number; readonly schemaNode: RegularNode | RootNode; readonly path: string[]; @@ -23,7 +23,7 @@ export type WalkerHookAction = 'filter' | 'stepIn'; export type WalkerHookHandler = (node: SchemaNode) => boolean; export type WalkerNodeEventHandler = (node: SchemaNode) => void; -export type WalkerFragmentEventHandler = (node: SchemaFragment) => void; +export type WalkerFragmentEventHandler = (node: SchemaFragment | boolean) => void; export type WalkerErrorEventHandler = (ex: Error) => void; export type WalkerEmitter = { diff --git a/src/walker/walker.ts b/src/walker/walker.ts index 7989d90..02efbd5 100644 --- a/src/walker/walker.ts +++ b/src/walker/walker.ts @@ -7,10 +7,11 @@ import { isMirroredNode, isReferenceNode, isRegularNode, isRootNode } from '../g import { mergeAllOf } from '../mergers/mergeAllOf'; import { mergeOneOrAnyOf } from '../mergers/mergeOneOrAnyOf'; import { MirroredReferenceNode, MirroredRegularNode, MirroredSchemaNode, ReferenceNode, RegularNode } from '../nodes'; +import { BooleanishNode } from '../nodes/BooleanishNode'; import type { RootNode } from '../nodes/RootNode'; import { SchemaCombinerName, SchemaNode, SchemaNodeKind } from '../nodes/types'; import type { SchemaFragment } from '../types'; -import { isNonNullable, isObjectLiteral } from '../utils/guards'; +import { isNonNullable, isObjectLiteral, isValidSchemaFragment } from '../utils/guards'; import type { WalkerEmitter, WalkerHookAction, WalkerHookHandler, WalkerSnapshot, WalkingOptions } from './types'; type InternalWalkerState = { @@ -25,7 +26,7 @@ export class Walker extends EventEmitter { public readonly path: string[]; public depth: number; - protected fragment: SchemaFragment; + protected fragment: SchemaFragment | boolean; protected schemaNode: RegularNode | RootNode; private mergedAllOfs: WeakMap; @@ -124,12 +125,19 @@ export class Walker extends EventEmitter { super.emit('enterNode', schemaNode); const actualNode = isMirroredNode(schemaNode) ? schemaNode.mirroredNode : schemaNode; - this.processedFragments.set(schemaNode.fragment, actualNode); - this.processedFragments.set(initialFragment, actualNode); + if (typeof schemaNode.fragment !== 'boolean' && initialFragment !== null) { + this.processedFragments.set(schemaNode.fragment, actualNode); + this.processedFragments.set(initialFragment, actualNode); + } this.fragment = schemaNode.fragment; this.depth = initialDepth + 1; + if (!isRootNode(schemaNode)) { + schemaNode.parent = initialSchemaNode; + schemaNode.subpath = this.path.slice(initialSchemaNode.path.length); + } + const isIncluded = this.hooks.filter?.(schemaNode); if (isIncluded === false) { @@ -137,11 +145,6 @@ export class Walker extends EventEmitter { return; } - if (!isRootNode(schemaNode)) { - schemaNode.parent = initialSchemaNode; - schemaNode.subpath = this.path.slice(initialSchemaNode.path.length); - } - if ('children' in initialSchemaNode && !isRootNode(schemaNode)) { if (initialSchemaNode.children === void 0) { (initialSchemaNode as RegularNode).children = [schemaNode]; @@ -186,7 +189,7 @@ export class Walker extends EventEmitter { protected walkNodeChildren(): void { const { fragment, schemaNode } = this; - if (!isRegularNode(schemaNode)) return; + if (!isRegularNode(schemaNode) || typeof fragment === 'boolean') return; const state = this.dumpInternalWalkerState(); @@ -213,17 +216,26 @@ export class Walker extends EventEmitter { let i = -1; for (const item of fragment.items) { i++; - if (!isObjectLiteral(item)) continue; + if (!isValidSchemaFragment(item)) continue; this.fragment = item; this.restoreInternalWalkerState(state); this.path.push('items', String(i)); this.walk(); } - } else if (isObjectLiteral(fragment.items)) { - this.fragment = fragment.items; - this.restoreInternalWalkerState(state); - this.path.push('items'); - this.walk(); + } else { + if (isObjectLiteral(fragment.items)) { + this.fragment = fragment.items; + this.restoreInternalWalkerState(state); + this.path.push('items'); + this.walk(); + } + + if (isValidSchemaFragment(fragment.additionalItems)) { + this.fragment = fragment.additionalItems; + this.restoreInternalWalkerState(state); + this.path.push('additionalItems'); + this.walk(); + } } break; @@ -231,7 +243,7 @@ export class Walker extends EventEmitter { if (isObjectLiteral(fragment.properties)) { for (const key of Object.keys(fragment.properties)) { const value = fragment.properties[key]; - if (!isObjectLiteral(value)) continue; + if (!isValidSchemaFragment(value)) continue; this.fragment = value; this.restoreInternalWalkerState(state); this.path.push('properties', key); @@ -242,7 +254,7 @@ export class Walker extends EventEmitter { if (isObjectLiteral(fragment.patternProperties)) { for (const key of Object.keys(fragment.patternProperties)) { const value = fragment.patternProperties[key]; - if (!isObjectLiteral(value)) continue; + if (!isValidSchemaFragment(value)) continue; this.fragment = value; this.restoreInternalWalkerState(state); this.path.push('patternProperties', key); @@ -250,6 +262,13 @@ export class Walker extends EventEmitter { } } + if (isValidSchemaFragment(fragment.additionalProperties)) { + this.fragment = fragment.additionalProperties; + this.restoreInternalWalkerState(state); + this.path.push('additionalProperties'); + this.walk(); + } + break; } @@ -275,11 +294,19 @@ export class Walker extends EventEmitter { } } - protected processFragment(): [SchemaNode, ProcessedFragment] { + protected processFragment(): [SchemaNode, ProcessedFragment | null] { const { walkingOptions, path, fragment: originalFragment, depth } = this; let { fragment } = this; - let retrieved = isNonNullable(fragment) ? this.retrieveFromFragment(fragment, originalFragment) : null; + if (typeof fragment === 'boolean') { + return [new BooleanishNode(fragment), null]; + } + + if (typeof originalFragment === 'boolean') { + throw new TypeError('Original fragment cannot be a boolean'); + } + + let retrieved = isNonNullable(fragment) ? this.retrieveFromFragment(fragment, fragment) : null; if (retrieved) { return retrieved;