Skip to content

Commit

Permalink
refactor(types): refactor portable text types used internally in the …
Browse files Browse the repository at this point in the history
…studio
  • Loading branch information
skogsmaskin committed Dec 20, 2022
1 parent dfd33ce commit 48c03a9
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 21 deletions.
60 changes: 52 additions & 8 deletions packages/@sanity/types/src/portableText/asserters.ts
@@ -1,26 +1,70 @@
import type {Block, Span} from './types'
import type {PortableTextTextBlock, PortableTextSpan, PortableTextObject} from './types'

function isRecord(value: unknown): value is Record<string, unknown> {
return !!value && (typeof value == 'object' || typeof value == 'function')
}

/** @internal */
export function isBlock<T = Span>(value: unknown): value is Block<T> {
/**
* Assert that a given object is a portable-text text-block type object
*
* @remarks
* * The `markDefs` and `style` property of a block is optional.
* * Block types can be named, so expect anything of the _type property.
*
* @alpha
*/
export function isPortableTextTextBlock<T = PortableTextSpan | PortableTextObject>(
value: unknown
): value is PortableTextTextBlock<T> {
return (
isRecord(value) &&
typeof value._type === 'string' && // block types can be named, so expect anything here.
typeof value.style === 'string' &&
Array.isArray(value.children) &&
Array.isArray(value.markDefs)
value.children.every((child) => isRecord(child)) &&
('markDefs' in value // optional property
? Array.isArray(value.markDefs) && value.markDefs.every((def) => isRecord(def))
: false) &&
('style' in value ? typeof value.style === 'string' : true) // optional property
)
}

/** @internal */
export function isSpan(value: unknown): value is Span {
/**
* Assert that a given object is a portable-text span-type object
*
* @remarks
* The `marks` property of a block is optional.
*
* @alpha
*/
export function isPortableTextSpan(value: unknown): value is PortableTextSpan {
return (
isRecord(value) &&
value._type === 'span' &&
typeof value.text === 'string' &&
Array.isArray(value.marks)
('marks' in value // optional property
? Array.isArray(value.marks) && value.marks.every((mark) => typeof mark === 'string')
: true)
)
}

/**
* Assert that a given object is a portable-text list-text-block-type object
*
* @remarks
* Uses `isPortableTextTextBlock` and checks for `listItem` and `level`
*
* @see isPortableTextTextBlock
*
* @alpha
*/
export function isPortableTextListBlock<T = PortableTextSpan | PortableTextObject>(
value: unknown
): value is PortableTextTextBlock<T> {
return (
isPortableTextTextBlock(value) &&
'listItem' in value &&
typeof value.listItem === 'string' &&
'level' in value &&
Number.isInteger(value.level)
)
}
41 changes: 28 additions & 13 deletions packages/@sanity/types/src/portableText/types.ts
@@ -1,23 +1,38 @@
/** @public */
export interface Block<TChild = Span> {
/** @alpha */

export type PortableTextBlock = PortableTextTextBlock | PortableTextObject

/** @alpha */
export interface PortableTextTextBlock<TChild = PortableTextSpan | PortableTextObject> {
_type: string
_key: string
style: string
children: TChild[]
markDefs: MarkDefinition[]
markDefs?: PortableTextObject[]
listItem?: string
style?: string
level?: number
}

/** @public */
export interface Span {
_type: 'span'
/** @alpha */
export interface PortableTextObject {
_type: string
_key: string
marks: string[]
text: string
[other: string]: unknown
}

/** @public */
export interface MarkDefinition {
[key: string]: unknown
_type: string
/** @alpha */
export interface PortableTextSpan {
_key: string
_type: 'span'
text: string
marks?: string[]
}

/** @alpha */
export type PortableTextChild = PortableTextObject | PortableTextSpan

/** @alpha */
export interface PortableTextListBlock extends PortableTextTextBlock {
listItem: string
level: number
}

0 comments on commit 48c03a9

Please sign in to comment.