-
Notifications
You must be signed in to change notification settings - Fork 4
/
asserters.ts
110 lines (104 loc) · 3.52 KB
/
asserters.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import type {
ArbitraryTypedObject,
PortableTextBlock,
PortableTextListItemBlock,
PortableTextSpan,
TypedObject,
} from '@portabletext/types'
import type {ToolkitNestedPortableTextSpan, ToolkitPortableTextList, ToolkitTextNode} from './types'
/**
* Strict check to determine if node is a correctly formatted Portable Text span.
*
* @param node - Node to check
* @returns True if valid Portable Text span, otherwise false
*/
export function isPortableTextSpan(
node: ArbitraryTypedObject | PortableTextSpan
): node is PortableTextSpan {
return (
node._type === 'span' &&
'text' in node &&
typeof node.text === 'string' &&
(typeof node.marks === 'undefined' ||
(Array.isArray(node.marks) && node.marks.every((mark) => typeof mark === 'string')))
)
}
/**
* Strict check to determine if node is a correctly formatted Portable Text block.
*
* @param node - Node to check
* @returns True if valid Portable Text block, otherwise false
*/
export function isPortableTextBlock(
node: PortableTextBlock | TypedObject
): node is PortableTextBlock {
return (
// A block doesn't _have_ to be named 'block' - to differentiate between
// allowed child types and marks, one might name them differently
typeof node._type === 'string' &&
// Toolkit-types like nested spans are @-prefixed
node._type[0] !== '@' &&
// `markDefs` isn't _required_ per say, but if it's there, it needs to be an array
(!('markDefs' in node) ||
(Array.isArray(node.markDefs) &&
// Every mark definition needs to have an `_key` to be mappable in child spans
node.markDefs.every((def) => typeof def._key === 'string'))) &&
// `children` is required and needs to be an array
'children' in node &&
Array.isArray(node.children) &&
// All children are objects with `_type` (usually spans, but can contain other stuff)
node.children.every((child) => typeof child === 'object' && '_type' in child)
)
}
/**
* Strict check to determine if node is a correctly formatted portable list item block.
*
* @param block - Block to check
* @returns True if valid Portable Text list item block, otherwise false
*/
export function isPortableTextListItemBlock(
block: PortableTextBlock | TypedObject
): block is PortableTextListItemBlock {
return (
isPortableTextBlock(block) &&
'listItem' in block &&
typeof block.listItem === 'string' &&
(typeof block.level === 'undefined' || typeof block.level === 'number')
)
}
/**
* Loose check to determine if block is a toolkit list node.
* Only checks `_type`, assumes correct structure.
*
* @param block - Block to check
* @returns True if toolkit list, otherwise false
*/
export function isPortableTextToolkitList(
block: TypedObject | ToolkitPortableTextList
): block is ToolkitPortableTextList {
return block._type === '@list'
}
/**
* Loose check to determine if span is a toolkit span node.
* Only checks `_type`, assumes correct structure.
*
* @param span - Span to check
* @returns True if toolkit span, otherwise false
*/
export function isPortableTextToolkitSpan(
span: TypedObject | ToolkitNestedPortableTextSpan
): span is ToolkitNestedPortableTextSpan {
return span._type === '@span'
}
/**
* Loose check to determine if node is a toolkit text node.
* Only checks `_type`, assumes correct structure.
*
* @param node - Node to check
* @returns True if toolkit text node, otherwise false
*/
export function isPortableTextToolkitTextNode(
node: TypedObject | ToolkitTextNode
): node is ToolkitTextNode {
return node._type === '@text'
}