diff --git a/package.json b/package.json index cf3538c7..aaa0b44f 100644 --- a/package.json +++ b/package.json @@ -42,12 +42,14 @@ "react-dom": ">=16.8" }, "dependencies": { + "@types/json-schema": "^7.0.7", "@stoplight/json": "^3.5.1", "@stoplight/json-schema-merge-allof": "^0.7.2", "@stoplight/react-error-boundary": "^1.0.0", "@stoplight/tree-list": "^5.0.3", + "@stoplight/types": "^12.0.0", "classnames": "^2.2.6", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "mobx-react-lite": "^1.4.1", "pluralize": "^8.0.0" }, @@ -60,12 +62,10 @@ "@stoplight/markdown-viewer": "^3.5.5", "@stoplight/scripts": "^8.2.0", "@stoplight/storybook-config": "^2.0.5", - "@stoplight/types": "11.0.0", "@stoplight/ui-kit": "3.0.0-beta.2", "@types/classnames": "^2.2.9", "@types/enzyme": "3.10.3", "@types/jest": "^24.0.18", - "@types/json-schema": "^7.0.3", "@types/lodash": "^4.14.149", "@types/node": "^12.7.2", "@types/pluralize": "^0.0.29", diff --git a/src/components/JsonSchemaViewer.tsx b/src/components/JsonSchemaViewer.tsx index 9df4b5a8..aa10c472 100644 --- a/src/components/JsonSchemaViewer.tsx +++ b/src/components/JsonSchemaViewer.tsx @@ -4,14 +4,13 @@ import cn from 'classnames'; import { action } from 'mobx'; import * as React from 'react'; -import { JSONSchema4 } from 'json-schema'; import { SchemaTree, SchemaTreeOptions, SchemaTreePopulateHandler, SchemaTreeRefDereferenceFn } from '../tree/tree'; -import { GoToRefHandler, RowRenderer, ViewMode } from '../types'; +import { GoToRefHandler, JSONSchema, RowRenderer, ViewMode } from '../types'; import { isSchemaViewerEmpty } from '../utils/isSchemaViewerEmpty'; import { SchemaTree as SchemaTreeComponent } from './SchemaTree'; export interface IJsonSchemaViewer { - schema: JSONSchema4; + schema: JSONSchema; style?: object; emptyText?: string; defaultExpandedDepth?: number; diff --git a/src/components/SchemaRow.tsx b/src/components/SchemaRow.tsx index 1f5a213b..82a58787 100644 --- a/src/components/SchemaRow.tsx +++ b/src/components/SchemaRow.tsx @@ -1,10 +1,9 @@ import { IRowRendererOptions, isParentNode, Tree } from '@stoplight/tree-list'; import cn from 'classnames'; -import { JSONSchema4 } from 'json-schema'; import * as React from 'react'; import { getNodeMetadata, getSchemaNodeMetadata } from '../tree/metadata'; -import { GoToRefHandler, SchemaKind, SchemaTreeListNode } from '../types'; +import { GoToRefHandler, JSONSchema, SchemaKind, SchemaTreeListNode } from '../types'; import { getPrimaryType } from '../utils/getPrimaryType'; import { hasRefItems, isArrayNodeWithItems, isRefNode } from '../utils/guards'; import { Caret, Description, Divider, Format, Property, Validations } from './shared'; @@ -20,7 +19,7 @@ const ICON_SIZE = 12; const ICON_DIMENSION = 20; const ROW_OFFSET = 7; -function getRelevantSchemaForRequireCheck(treeNode: SchemaTreeListNode): JSONSchema4 | JSONSchema4[] | null { +function getRelevantSchemaForRequireCheck(treeNode: SchemaTreeListNode): JSONSchema | JSONSchema[] | null { const metadata = getNodeMetadata(treeNode); if (!('schemaNode' in metadata)) return null; if (isArrayNodeWithItems(metadata.schemaNode)) { diff --git a/src/components/SchemaTree.tsx b/src/components/SchemaTree.tsx index 1928a3a1..d0c5ca7e 100644 --- a/src/components/SchemaTree.tsx +++ b/src/components/SchemaTree.tsx @@ -1,14 +1,13 @@ import { TreeList, TreeListEvents, TreeStore } from '@stoplight/tree-list'; -import { JSONSchema4 } from 'json-schema'; import { observer } from 'mobx-react-lite'; import * as React from 'react'; -import { GoToRefHandler, RowRenderer } from '../types'; +import { GoToRefHandler, JSONSchema, RowRenderer } from '../types'; import { SchemaRow } from './SchemaRow'; export interface ISchemaTree { treeStore: TreeStore; - schema: JSONSchema4; + schema: JSONSchema; name?: string; hideTopBar?: boolean; expanded?: boolean; diff --git a/src/components/shared/Divider.tsx b/src/components/shared/Divider.tsx index e3899607..a0e541f5 100644 --- a/src/components/shared/Divider.tsx +++ b/src/components/shared/Divider.tsx @@ -1,14 +1,14 @@ import { Dictionary } from '@stoplight/types'; import * as React from 'react'; -import { JSONSchema4CombinerName } from '../../types'; +import { JSONSchemaCombinerName } from '../../types'; -const DIVIDERS: Dictionary = { +const DIVIDERS: Dictionary = { allOf: 'and', anyOf: 'and/or', oneOf: 'or', }; -export const Divider: React.FunctionComponent<{ kind: JSONSchema4CombinerName }> = ({ kind }) => ( +export const Divider: React.FunctionComponent<{ kind: JSONSchemaCombinerName }> = ({ kind }) => (
{DIVIDERS[kind]}
diff --git a/src/components/shared/Types.tsx b/src/components/shared/Types.tsx index 2195f104..41bb6873 100644 --- a/src/components/shared/Types.tsx +++ b/src/components/shared/Types.tsx @@ -1,16 +1,15 @@ import { Dictionary, Optional } from '@stoplight/types'; import cn from 'classnames'; -import { JSONSchema4TypeName } from 'json-schema'; import * as React from 'react'; -import { JSONSchema4CombinerName, SchemaKind } from '../../types'; +import { JSONSchema, JSONSchemaCombinerName, JSONSchemaTypeName, SchemaKind } from '../../types'; /** * TYPE */ export interface IType { - type: JSONSchema4TypeName | JSONSchema4CombinerName | 'binary' | '$ref'; - subtype: Optional | '$ref'; + type: JSONSchemaTypeName | JSONSchemaCombinerName | 'binary' | '$ref'; + subtype: JSONSchema['type'] | '$ref'; className?: string; title: Optional; } @@ -61,8 +60,8 @@ Type.displayName = 'JsonSchemaViewer.Type'; */ interface ITypes { className?: string; - type: Optional; - subtype: Optional; + type: Optional; + subtype: Optional; title: Optional; } diff --git a/src/components/shared/Validations.tsx b/src/components/shared/Validations.tsx index 376d714c..36b9a8ba 100644 --- a/src/components/shared/Validations.tsx +++ b/src/components/shared/Validations.tsx @@ -1,8 +1,8 @@ import { safeStringify } from '@stoplight/json'; import { Dictionary } from '@stoplight/types'; import { Popover } from '@stoplight/ui-kit'; -import { JSONSchema4 } from 'json-schema'; import * as React from 'react'; +import { JSONSchema } from '../../types'; import { ViewModeContext } from '../JsonSchemaViewer'; import { PropertyTypeColors } from './Types'; @@ -93,7 +93,7 @@ export const Validations: React.FunctionComponent = ({ ); }; -export const Format: React.FunctionComponent<{ schema: JSONSchema4 }> = ({ schema }) => { +export const Format: React.FunctionComponent<{ schema: JSONSchema }> = ({ schema }) => { return (
Optional; + schema: JSONSchema, +) => Optional; export type SchemaTreePopulateHandler = (tree: SchemaTree, node: TreeListParentNode) => void; @@ -39,7 +38,7 @@ export { TreeState as SchemaTreeState }; export class SchemaTree extends Tree { public treeOptions: SchemaTreeOptions; - constructor(public schema: JSONSchema4, public state: TreeState, opts: SchemaTreeOptions) { + constructor(public schema: JSONSchema, public state: TreeState, opts: SchemaTreeOptions) { super({ expanded: node => (!(node.id in state.expanded) && SchemaTree.getLevel(node) <= opts.expandedDepth) || @@ -51,7 +50,11 @@ export class SchemaTree extends Tree { protected readonly visited = new WeakSet(); - protected isViewModeRespected = (fragment: JSONSchema4) => { + protected isViewModeRespected = (fragment: JSONSchema) => { + if (!('writeOnly' in fragment) && !('readOnly' in fragment)) { + return true; + } + return !( !!fragment.writeOnly !== !!fragment.readOnly && ((this.treeOptions.viewMode === 'read' && fragment.writeOnly) || @@ -112,7 +115,7 @@ export class SchemaTree extends Tree { this.treeOptions.onPopulate?.(this, this.root); } - public populateTreeFragment(parent: TreeListParentNode, schema: JSONSchema4, path: JsonPath, stepIn: boolean) { + public populateTreeFragment(parent: TreeListParentNode, schema: JSONSchema, path: JsonPath, stepIn: boolean) { const initialLevel = Tree.getLevel(parent); const artificialRoot = Tree.createArtificialRoot(); populateTree(schema, artificialRoot, initialLevel, path, { diff --git a/src/tree/utils/__tests__/walk.spec.ts b/src/tree/utils/__tests__/walk.spec.ts index d2561190..5c32af6c 100644 --- a/src/tree/utils/__tests__/walk.spec.ts +++ b/src/tree/utils/__tests__/walk.spec.ts @@ -1,5 +1,5 @@ import { JSONSchema4 } from 'json-schema'; -import { JSONSchema4CombinerName, SchemaKind } from '../../../types'; +import { JSONSchemaCombinerName, SchemaKind } from '../../../types'; import { walk } from '../walk'; describe('Schema Walker', () => { @@ -100,7 +100,7 @@ describe('Schema Walker', () => { }); describe('title', () => { - describe.each(['allOf', 'oneOf', 'anyOf'])('when combiner equals %s', combiner => { + describe.each(['allOf', 'oneOf', 'anyOf'])('when combiner equals %s', combiner => { test.each([null, 2, void 0, false, true, 0, {}, []])('should ignore %s invalid title', title => { const schema = { [combiner]: [], diff --git a/src/tree/utils/canStepIn.ts b/src/tree/utils/canStepIn.ts index 5c1a8bc7..1ee14d84 100644 --- a/src/tree/utils/canStepIn.ts +++ b/src/tree/utils/canStepIn.ts @@ -1,9 +1,8 @@ -import { JSONSchema4 } from 'json-schema'; -import { SchemaKind } from '../../types'; +import { JSONSchema, SchemaKind } from '../../types'; import { getCombiners } from '../../utils/getCombiners'; import { getPrimaryType } from '../../utils/getPrimaryType'; -export const canStepIn = (fragment: JSONSchema4) => { +export const canStepIn = (fragment: JSONSchema) => { if (getCombiners(fragment) !== void 0) { return true; } diff --git a/src/tree/utils/mergeAllOf.ts b/src/tree/utils/mergeAllOf.ts index 01aeffc2..70b033fd 100644 --- a/src/tree/utils/mergeAllOf.ts +++ b/src/tree/utils/mergeAllOf.ts @@ -1,14 +1,14 @@ import { pathToPointer, safeStringify } from '@stoplight/json'; import { JsonPath } from '@stoplight/types'; -import { JSONSchema4 } from 'json-schema'; import { ResolvingError } from '../../errors'; +import { JSONSchema } from '../../types'; import { WalkingOptions } from './populateTree'; const resolveAllOf = require('@stoplight/json-schema-merge-allof'); -const store = new WeakMap>(); +const store = new WeakMap>(); -function _mergeAllOf(schema: JSONSchema4, path: JsonPath, opts: WalkingOptions) { +function _mergeAllOf(schema: JSONSchema, path: JsonPath, opts: WalkingOptions) { return resolveAllOf(schema, { deep: false, resolvers: resolveAllOf.stoplightResolvers, @@ -38,7 +38,7 @@ function _mergeAllOf(schema: JSONSchema4, path: JsonPath, opts: WalkingOptions) if (Array.isArray(resolved.allOf)) { for (const member of resolved.allOf) { - if (typeof member.$ref === 'string' && schemaRefs.includes(member.$ref)) { + if (typeof member !== 'boolean' && typeof member.$ref === 'string' && schemaRefs.includes(member.$ref)) { throw new ResolvingError('Circular reference detected'); } } @@ -49,7 +49,7 @@ function _mergeAllOf(schema: JSONSchema4, path: JsonPath, opts: WalkingOptions) }); } -export const mergeAllOf = (schema: JSONSchema4, path: JsonPath, opts: WalkingOptions) => { +export const mergeAllOf = (schema: JSONSchema, path: JsonPath, opts: WalkingOptions) => { try { if (!store.has(opts)) { store.set(opts, new WeakMap()); diff --git a/src/tree/utils/mergeOneOrAnyOf.ts b/src/tree/utils/mergeOneOrAnyOf.ts index 7a81047d..f0a166aa 100644 --- a/src/tree/utils/mergeOneOrAnyOf.ts +++ b/src/tree/utils/mergeOneOrAnyOf.ts @@ -1,24 +1,25 @@ import { JsonPath } from '@stoplight/types'; -import { JSONSchema4 } from 'json-schema'; +import { isObject } from 'lodash'; +import { JSONSchema } from '../../types'; import { mergeAllOf } from './mergeAllOf'; import { WalkingOptions } from './populateTree'; export function mergeOneOrAnyOf( - schema: JSONSchema4, + schema: JSONSchema, combiner: 'oneOf' | 'anyOf', path: JsonPath, options: WalkingOptions, -): JSONSchema4[] { +): JSONSchema[] { const items = schema[combiner]; if (!Array.isArray(items)) return []; // just in case - const merged: JSONSchema4[] = []; + const merged: JSONSchema[] = []; if (Array.isArray(schema.allOf) && Array.isArray(items)) { for (const item of items) { merged.push({ - allOf: [...schema.allOf, item], + allOf: [...schema.allOf, item].filter(isObject), }); } @@ -31,7 +32,7 @@ export function mergeOneOrAnyOf( merged.push( mergeAllOf( { - allOf: [prunedSchema, item], + allOf: [prunedSchema, item].filter(isObject), }, path, options, diff --git a/src/tree/utils/populateTree.ts b/src/tree/utils/populateTree.ts index 49544165..6aa3d993 100644 --- a/src/tree/utils/populateTree.ts +++ b/src/tree/utils/populateTree.ts @@ -1,9 +1,8 @@ import { TreeListNode, TreeListParentNode } from '@stoplight/tree-list'; import { JsonPath, Optional } from '@stoplight/types'; -import { JSONSchema4 } from 'json-schema'; import { isObject as _isObject } from 'lodash'; -import { IArrayNode, IObjectNode, SchemaKind, SchemaNode, SchemaTreeListNode } from '../../types'; +import { IArrayNode, IObjectNode, JSONSchema, SchemaKind, SchemaNode, SchemaTreeListNode } from '../../types'; import { generateId } from '../../utils/generateId'; import { getPrimaryType } from '../../utils/getPrimaryType'; import { isCombinerNode, isRefNode } from '../../utils/guards'; @@ -13,18 +12,18 @@ import { mergeAllOf } from './mergeAllOf'; import { mergeOneOrAnyOf } from './mergeOneOrAnyOf'; import { processNode, walk } from './walk'; -export type WalkerRefResolver = (path: JsonPath | null, $ref: string) => JSONSchema4; +export type WalkerRefResolver = (path: JsonPath | null, $ref: string) => JSONSchema; export type WalkingOptions = { mergeAllOf: boolean; - onNode?(fragment: JSONSchema4, node: SchemaNode, parentTreeNode: TreeListNode, level: number): boolean | void; + onNode?(fragment: JSONSchema, node: SchemaNode, parentTreeNode: TreeListNode, level: number): boolean | void; stepIn?: boolean; resolveRef: WalkerRefResolver; shouldResolveEagerly: boolean; }; export type Walker = ( - schema: Optional, + schema: Optional, parent: TreeListParentNode, level: number, path: JsonPath, @@ -53,7 +52,7 @@ export const populateTree: Walker = (schema, parent, level, path, options): unde }); if (isRefNode(node) && node.$ref !== null) { - processRef(treeNode, node as JSONSchema4, level, path, options); + processRef(treeNode, node as JSONSchema, level, path, options); } else if (!isCombinerNode(node)) { switch (getPrimaryType(node)) { case SchemaKind.Array: @@ -95,7 +94,7 @@ export const populateTree: Walker = (schema, parent, level, path, options): unde node.properties[i] = { ...property, type: property.type || node.type, - }; + } as JSONSchema; } populateTree( @@ -119,6 +118,10 @@ function processArray( path: JsonPath, options: WalkingOptions, ): SchemaTreeListNode { + if (typeof schema.items === 'boolean') { + return node; + } + const items = prepareSchema(schema.items, node, path, options); if (!_isObject(items)) return node; @@ -222,7 +225,7 @@ function processObject( function processRef( node: TreeListNode, - schema: JSONSchema4, + schema: JSONSchema, level: number, path: JsonPath, options: WalkingOptions | null, @@ -241,19 +244,21 @@ function processRef( function bailAllOf( node: TreeListParentNode, - schema: JSONSchema4, + schema: JSONSchema, level: number, path: JsonPath, options: WalkingOptions, ) { if (Array.isArray(schema.allOf)) { for (const [i, item] of schema.allOf.entries()) { - populateTree(item, node, level, [...path, i], options); + if (typeof item !== 'boolean') { + populateTree(item, node, level, [...path, i], options); + } } } } -function resolveSchema(schema: Optional, path: JsonPath, options: WalkingOptions | null) { +function resolveSchema(schema: Optional, path: JsonPath, options: WalkingOptions | null) { if (!_isObject(schema) || options === null || !('$ref' in schema) || typeof schema.$ref !== 'string') { return schema; } @@ -263,11 +268,11 @@ function resolveSchema(schema: Optional, path: JsonPath, opt } function prepareSchema( - schema: Optional, + schema: Optional, node: TreeListNode, path: JsonPath, options: WalkingOptions | null, -): Optional { +): Optional { if (options === null || !options.shouldResolveEagerly) return schema; try { diff --git a/src/tree/utils/walk.ts b/src/tree/utils/walk.ts index 5e17369c..e8b3937e 100644 --- a/src/tree/utils/walk.ts +++ b/src/tree/utils/walk.ts @@ -1,7 +1,6 @@ import { Optional } from '@stoplight/types'; -import { JSONSchema4 } from 'json-schema'; import { isObject as _isObject } from 'lodash'; -import { IArrayNode, IBaseNode, ICombinerNode, IObjectNode, SchemaKind, SchemaNode } from '../../types'; +import { IArrayNode, IBaseNode, ICombinerNode, IObjectNode, JSONSchema, SchemaKind, SchemaNode } from '../../types'; import { flattenTypes } from '../../utils/flattenTypes'; import { generateId } from '../../utils/generateId'; import { getAnnotations } from '../../utils/getAnnotations'; @@ -11,7 +10,7 @@ import { getValidations } from '../../utils/getValidations'; import { inferType } from '../../utils/inferType'; import { normalizeRequired } from '../../utils/normalizeRequired'; -function assignNodeSpecificFields(base: IBaseNode, node: JSONSchema4) { +function assignNodeSpecificFields(base: IBaseNode, node: JSONSchema) { switch (getPrimaryType(node)) { case SchemaKind.Array: (base as IArrayNode).items = unwrapItemsOrUndefined(node.items); @@ -29,7 +28,7 @@ function assignNodeSpecificFields(base: IBaseNode, node: JSONSchema4) { } } -export function* processNode(node: JSONSchema4): IterableIterator { +export function* processNode(node: JSONSchema): IterableIterator { const combiners = getCombiners(node); const type = node.type || inferType(node); const title = typeof node.title === 'string' ? { title: node.title } : null; @@ -40,7 +39,7 @@ export function* processNode(node: JSONSchema4): IterableIterator { const combinerNode: ICombinerNode = { id: generateId(), combiner, - properties: Array.isArray(properties) ? properties.slice() : properties, + properties: (Array.isArray(properties) ? properties.slice() : properties) as ICombinerNode['properties'], annotations: getAnnotations(node), ...(type !== void 0 && { type: flattenTypes(type) }), ...title, @@ -50,7 +49,7 @@ export function* processNode(node: JSONSchema4): IterableIterator { } } else if (type) { const primaryType = getPrimaryType(node); - let validationNode: JSONSchema4 = node; + let validationNode: JSONSchema = node; if ( primaryType === SchemaKind.Array && _isObject(node.items) && @@ -103,10 +102,10 @@ export function* processNode(node: JSONSchema4): IterableIterator { export type WalkerValue = { node: SchemaNode; - fragment: JSONSchema4; + fragment: JSONSchema; }; -export function* walk(schema: JSONSchema4[] | JSONSchema4): IterableIterator { +export function* walk(schema: JSONSchema[] | JSONSchema): IterableIterator { if (Array.isArray(schema)) { for (const segment of schema) { yield* walk(segment); diff --git a/src/types.ts b/src/types.ts index 9521bde7..82f26e3c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,13 @@ import { IRowRendererOptions, TreeListNode, TreeStore } from '@stoplight/tree-list'; import { Dictionary } from '@stoplight/types'; -import { JSONSchema4, JSONSchema4TypeName } from 'json-schema'; +import { + JSONSchema4, + JSONSchema4TypeName, + JSONSchema6, + JSONSchema6TypeName, + JSONSchema7, + JSONSchema7TypeName, +} from 'json-schema'; import * as React from 'react'; export enum SchemaKind { @@ -14,25 +21,23 @@ export enum SchemaKind { Object = 'object', } -export type JSONSchema4CombinerName = 'allOf' | 'anyOf' | 'oneOf'; +export type JSONSchemaCombinerName = 'allOf' | 'anyOf' | 'oneOf'; -export type JSONSchema4Annotations = 'title' | 'description' | 'default' | 'examples'; - -export type JSONSchema4Metadata = 'id' | '$schema'; +export type JSONSchemaAnnotations = 'title' | 'description' | 'default' | 'examples'; export interface ICombinerNode { id: string; - readonly combiner: JSONSchema4CombinerName; - properties?: JSONSchema4[]; - annotations: Pick; - readonly type?: JSONSchema4TypeName | JSONSchema4TypeName[]; + readonly combiner: JSONSchemaCombinerName; + properties?: JSONSchema[]; + annotations: Pick; + readonly type?: JSONSchema['type']; title?: string; } -export interface IBaseNode extends Pick { +export interface IBaseNode extends Pick { id: string; - readonly type?: JSONSchema4TypeName | JSONSchema4TypeName[]; - annotations: Partial>; + readonly type?: JSONSchema['type']; + annotations: Partial>; validations: Dictionary; required?: string[]; title?: string; @@ -44,11 +49,11 @@ export interface IRefNode { title?: string; } -export interface IArrayNode extends IBaseNode, Pick {} +export interface IArrayNode extends IBaseNode, Pick {} export interface IObjectNode extends IBaseNode, - Pick {} + Pick {} export interface IObjectPropertyNode extends IBaseNode { name: string; @@ -67,3 +72,7 @@ export type RowRenderer = ( ) => React.ReactNode; export type ViewMode = 'read' | 'write' | 'standalone'; + +export type JSONSchema = JSONSchema4 | JSONSchema6 | JSONSchema7; + +export type JSONSchemaTypeName = JSONSchema4TypeName | JSONSchema6TypeName | JSONSchema7TypeName; diff --git a/src/utils/flattenTypes.ts b/src/utils/flattenTypes.ts index cf3d328a..c4544a59 100644 --- a/src/utils/flattenTypes.ts +++ b/src/utils/flattenTypes.ts @@ -1,8 +1,8 @@ import { Optional } from '@stoplight/types'; -import { JSONSchema4TypeName } from 'json-schema'; +import { JSONSchema, JSONSchemaTypeName } from '../types'; import { isValidType } from './isValidType'; -function getTypeFromObject(obj: object): Optional { +function getTypeFromObject(obj: object): Optional { const size = Object.keys(obj).length; if (size > 1 || !('type' in obj)) { @@ -12,7 +12,7 @@ function getTypeFromObject(obj: object): Optional { } if ('type' in obj && isValidType((obj as { type: string }).type)) { - return (obj as { type: JSONSchema4TypeName }).type; + return (obj as { type: JSONSchemaTypeName }).type; } return; @@ -30,7 +30,7 @@ function flattenType(type: unknown) { return getTypeFromObject(type); } -export const flattenTypes = (types: unknown): Optional => { +export const flattenTypes = (types: unknown): Optional => { if (typeof types === 'string' && isValidType(types)) { return types; } @@ -40,7 +40,7 @@ export const flattenTypes = (types: unknown): Optional { - let combiners: JSONSchema4CombinerName[] | void; +export const getCombiners = (node: JSONSchema): JSONSchemaCombinerName[] | void => { + let combiners: JSONSchemaCombinerName[] | void; if ('anyOf' in node) { // tslint:disable-next-line:prettier diff --git a/src/utils/getMetadata.ts b/src/utils/getMetadata.ts index 639babf7..fb465ef8 100644 --- a/src/utils/getMetadata.ts +++ b/src/utils/getMetadata.ts @@ -1,9 +1,11 @@ -import { JSONSchema4 } from 'json-schema'; +import { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'; import { pick as _pick } from 'lodash'; -import { JSONSchema4Metadata } from '../types'; +import { JSONSchema } from '../types'; -const METADATA: JSONSchema4Metadata[] = ['id', '$schema']; +const METADATA = ['id', '$id', '$schema']; -export function getMetadata(node: JSONSchema4): Pick { +export function getMetadata( + node: JSONSchema, +): Pick & (Pick | Pick) { return _pick(node, METADATA); } diff --git a/src/utils/getPrimaryType.ts b/src/utils/getPrimaryType.ts index 5c4a6aec..f6e08743 100644 --- a/src/utils/getPrimaryType.ts +++ b/src/utils/getPrimaryType.ts @@ -1,8 +1,7 @@ -import { JSONSchema4 } from 'json-schema'; -import { SchemaKind, SchemaNode } from '../types'; +import { JSONSchema, SchemaKind, SchemaNode } from '../types'; import { inferType } from './inferType'; -export function getPrimaryType(node: JSONSchema4 | SchemaNode) { +export function getPrimaryType(node: JSONSchema | SchemaNode) { if ('type' in node && node.type !== undefined) { if (Array.isArray(node.type)) { if (node.type.includes(SchemaKind.Object)) { diff --git a/src/utils/getValidations.ts b/src/utils/getValidations.ts index 4f3c4c41..d21ff00c 100644 --- a/src/utils/getValidations.ts +++ b/src/utils/getValidations.ts @@ -1,6 +1,6 @@ import { Dictionary, Optional } from '@stoplight/types'; -import { JSONSchema4, JSONSchema4TypeName } from 'json-schema'; import { flatMap as _flatMap, pick as _pick } from 'lodash'; +import { JSONSchema } from '../types'; export const COMMON_VALIDATION_TYPES = [ 'enum', // https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1 @@ -63,7 +63,7 @@ function filterOutFormatValidations(values: Dictionary) { return newValues; } -function getDeprecatedValue(node: JSONSchema4): Optional { +function getDeprecatedValue(node: JSONSchema): Optional { if ('x-deprecated' in node) { return !!node['x-deprecated']; } @@ -75,7 +75,7 @@ function getDeprecatedValue(node: JSONSchema4): Optional { return; } -function getTypeValidations(type: JSONSchema4TypeName | JSONSchema4TypeName[]): string[] { +function getTypeValidations(type: NonNullable): string[] { if (Array.isArray(type)) { return _flatMap(type, getTypeValidations); } @@ -83,7 +83,7 @@ function getTypeValidations(type: JSONSchema4TypeName | JSONSchema4TypeName[]): return VALIDATION_TYPES[type] || []; } -export const getValidations = (node: JSONSchema4): Dictionary => { +export const getValidations = (node: JSONSchema): Dictionary => { const extraValidations = node.type && getTypeValidations(node.type); const deprecated = getDeprecatedValue(node); return filterOutFormatValidations({ diff --git a/src/utils/guards.ts b/src/utils/guards.ts index 6b68785c..a354512a 100644 --- a/src/utils/guards.ts +++ b/src/utils/guards.ts @@ -1,10 +1,9 @@ -import { JSONSchema4 } from 'json-schema'; import { isObjectLike as _isObjectLike } from 'lodash'; -import { IArrayNode, ICombinerNode, IRefNode, SchemaKind, SchemaNode } from '../types'; +import { IArrayNode, ICombinerNode, IRefNode, JSONSchema, SchemaKind, SchemaNode } from '../types'; export const isArrayNodeWithItems = ( node: SchemaNode, -): node is Omit & { items: JSONSchema4 | JSONSchema4[] } => +): node is Omit & { items: JSONSchema | JSONSchema[] } => 'type' in node && 'items' in node && (node.type === SchemaKind.Array || (Array.isArray(node.type) && node.type.includes(SchemaKind.Array))) && diff --git a/src/utils/inferType.ts b/src/utils/inferType.ts index f0518ee6..a555f7e0 100644 --- a/src/utils/inferType.ts +++ b/src/utils/inferType.ts @@ -1,8 +1,7 @@ import { Optional } from '@stoplight/types'; -import { JSONSchema4, JSONSchema4TypeName } from 'json-schema'; -import { SchemaKind, SchemaNode } from '../types'; +import { JSONSchema, SchemaKind, SchemaNode } from '../types'; -export function inferType(node: SchemaNode | JSONSchema4): Optional { +export function inferType(node: SchemaNode | JSONSchema): Optional { if ('type' in node) { return node.type; } diff --git a/src/utils/isCombiner.ts b/src/utils/isCombiner.ts index 0d99b825..4ba33e77 100644 --- a/src/utils/isCombiner.ts +++ b/src/utils/isCombiner.ts @@ -1,5 +1,5 @@ -import { JSONSchema4CombinerName } from '../types'; +import { JSONSchemaCombinerName } from '../types'; const combinerTypes = ['allOf', 'oneOf', 'anyOf']; -export const isCombiner = (type: string): type is JSONSchema4CombinerName => combinerTypes.includes(type); +export const isCombiner = (type: string): type is JSONSchemaCombinerName => combinerTypes.includes(type); diff --git a/src/utils/isValidType.ts b/src/utils/isValidType.ts index 7c86e93e..05a2e601 100644 --- a/src/utils/isValidType.ts +++ b/src/utils/isValidType.ts @@ -1,5 +1,5 @@ -import { JSONSchema4TypeName } from 'json-schema'; +import { JSONSchema4TypeName, JSONSchema6TypeName } from 'json-schema'; import { SchemaKind } from '../types'; -export const isValidType = (maybeType: unknown): maybeType is JSONSchema4TypeName => +export const isValidType = (maybeType: unknown): maybeType is JSONSchema4TypeName | JSONSchema6TypeName => typeof maybeType === 'string' && Object.values(SchemaKind).includes(maybeType as SchemaKind); diff --git a/yarn.lock b/yarn.lock index 55add33a..c8239220 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1677,13 +1677,6 @@ mobx-react-lite "^1.5.2" strict-event-emitter-types "^2.0.0" -"@stoplight/types@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-11.0.0.tgz#fd234cfcb1a1cee28ef9642735d9206e009a8fa6" - integrity sha512-Tt8doUE2E5HACigjBHyBpMHS+Td+9tBmGvf4giDH9jK7mS6Q7dyLOeE2QSKyMrslmzd1mRuKBvPhS18g9EEjog== - dependencies: - "@types/json-schema" "^7.0.3" - "@stoplight/types@^10.0.1": version "10.0.1" resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-10.0.1.tgz#aad7ddf667fc4a86b71b4813a22285e959300595" @@ -1699,6 +1692,14 @@ "@types/json-schema" "^7.0.4" utility-types "^3.10.0" +"@stoplight/types@^12.0.0": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.0.0.tgz#3fc68ffa954e7c0a0e6261a982d5c0cb52d5d969" + integrity sha512-BbL0MIbcCwrU4RidcVyMKnRwACnhr7WAEgPH8d6rwhg/j2DegdLadnXCnq8UJXUWW9Apbntgvic3yVGvLtVmxA== + dependencies: + "@types/json-schema" "^7.0.4" + utility-types "^3.10.0" + "@stoplight/ui-kit@3.0.0-beta.2": version "3.0.0-beta.2" resolved "https://registry.yarnpkg.com/@stoplight/ui-kit/-/ui-kit-3.0.0-beta.2.tgz#90492ce72373a4120cb5304a9121bcb82a812dd5" @@ -2334,6 +2335,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/json-schema@^7.0.7": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + "@types/lodash@^4.14.149": version "4.14.149" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440" @@ -9319,6 +9325,11 @@ lodash@4.17.15, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.15.0, lodash@^4.17.11, l resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"