@portabletext/block-tools 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import type {Schema} from '@portabletext/schema'
1
2
  import {vercelStegaClean} from '@vercel/stega'
2
3
  import {isEqual} from 'lodash'
3
4
  import {DEFAULT_BLOCK} from '../constants'
@@ -15,7 +16,6 @@ import {
15
16
  type PortableTextObject,
16
17
  type PortableTextTextBlock,
17
18
  } from '../types.portable-text'
18
- import type {PortableTextSchema} from '../util/portable-text-schema'
19
19
  import {resolveJsType} from '../util/resolveJsType'
20
20
  import preprocessors from './preprocessors'
21
21
 
@@ -70,7 +70,7 @@ export function defaultParseHtml(): HtmlParser {
70
70
  }
71
71
 
72
72
  export function flattenNestedBlocks(
73
- schema: PortableTextSchema,
73
+ schema: Schema,
74
74
  blocks: TypedObject[],
75
75
  ): TypedObject[] {
76
76
  let depth = 0
@@ -124,7 +124,7 @@ function isWhiteSpaceChar(text: string) {
124
124
  * @returns
125
125
  */
126
126
  export function trimWhitespace(
127
- schema: PortableTextSchema,
127
+ schema: Schema,
128
128
  blocks: TypedObject[],
129
129
  ): TypedObject[] {
130
130
  blocks.forEach((block) => {
@@ -186,7 +186,7 @@ export function trimWhitespace(
186
186
  }
187
187
 
188
188
  export function ensureRootIsBlocks(
189
- schema: PortableTextSchema,
189
+ schema: Schema,
190
190
  blocks: TypedObject[],
191
191
  ): TypedObject[] {
192
192
  return blocks.reduce((memo, node, i, original) => {
@@ -1,3 +1,4 @@
1
+ import type {Schema} from '@portabletext/schema'
1
2
  import {flatten} from 'lodash'
2
3
  import type {
3
4
  ArbitraryTypedObject,
@@ -12,7 +13,6 @@ import {
12
13
  type PortableTextBlock,
13
14
  type PortableTextObject,
14
15
  } from '../types.portable-text'
15
- import type {PortableTextSchema} from '../util/portable-text-schema'
16
16
  import {resolveJsType} from '../util/resolveJsType'
17
17
  import {
18
18
  defaultParseHtml,
@@ -34,7 +34,7 @@ import {createRules} from './rules'
34
34
  *
35
35
  */
36
36
  export default class HtmlDeserializer {
37
- schema: PortableTextSchema
37
+ schema: Schema
38
38
  rules: DeserializerRule[]
39
39
  parseHtml: (html: string) => HTMLElement
40
40
  _markDefs: PortableTextObject[] = []
@@ -45,10 +45,7 @@ export default class HtmlDeserializer {
45
45
  * @param blockContentType - Schema type for array containing _at least_ a block child type
46
46
  * @param options - Options for the deserialization process
47
47
  */
48
- constructor(
49
- schema: PortableTextSchema,
50
- options: HtmlDeserializerOptions = {},
51
- ) {
48
+ constructor(schema: Schema, options: HtmlDeserializerOptions = {}) {
52
49
  const {rules = [], unstable_whitespaceOnPasteMode = 'preserve'} = options
53
50
  const standardRules = createRules(schema, {
54
51
  keyGenerator: options.keyGenerator,
@@ -1,3 +1,4 @@
1
+ import type {Schema} from '@portabletext/schema'
1
2
  import {
2
3
  BLOCK_DEFAULT_STYLE,
3
4
  DEFAULT_BLOCK,
@@ -7,7 +8,6 @@ import {
7
8
  HTML_LIST_CONTAINER_TAGS,
8
9
  } from '../../constants'
9
10
  import type {DeserializerRule} from '../../types'
10
- import type {PortableTextSchema} from '../../util/portable-text-schema'
11
11
  import {isElement, tagName} from '../helpers'
12
12
 
13
13
  const LIST_CONTAINER_TAGS = Object.keys(HTML_LIST_CONTAINER_TAGS)
@@ -81,7 +81,7 @@ const blocks: Record<string, {style: string} | undefined> = {
81
81
  ...HTML_HEADER_TAGS,
82
82
  }
83
83
 
84
- function getBlockStyle(schema: PortableTextSchema, el: Node): string {
84
+ function getBlockStyle(schema: Schema, el: Node): string {
85
85
  const childTag = tagName(el.firstChild)
86
86
  const block = childTag && blocks[childTag]
87
87
  if (!block) {
@@ -93,9 +93,7 @@ function getBlockStyle(schema: PortableTextSchema, el: Node): string {
93
93
  return block.style
94
94
  }
95
95
 
96
- export default function createGDocsRules(
97
- schema: PortableTextSchema,
98
- ): DeserializerRule[] {
96
+ export default function createGDocsRules(schema: Schema): DeserializerRule[] {
99
97
  return [
100
98
  {
101
99
  deserialize(el) {
@@ -1,3 +1,4 @@
1
+ import type {Schema} from '@portabletext/schema'
1
2
  import {
2
3
  DEFAULT_BLOCK,
3
4
  DEFAULT_SPAN,
@@ -10,13 +11,12 @@ import {
10
11
  type PartialBlock,
11
12
  } from '../../constants'
12
13
  import type {DeserializerRule} from '../../types'
13
- import type {PortableTextSchema} from '../../util/portable-text-schema'
14
14
  import {keyGenerator} from '../../util/randomKey'
15
15
  import {isElement, tagName} from '../helpers'
16
16
  import {whitespaceTextNodeRule} from './whitespace-text-node'
17
17
 
18
18
  export function resolveListItem(
19
- schema: PortableTextSchema,
19
+ schema: Schema,
20
20
  listNodeTagName: string,
21
21
  ): string | undefined {
22
22
  if (
@@ -35,7 +35,7 @@ export function resolveListItem(
35
35
  }
36
36
 
37
37
  export default function createHTMLRules(
38
- schema: PortableTextSchema,
38
+ schema: Schema,
39
39
  options: {keyGenerator?: () => string},
40
40
  ): DeserializerRule[] {
41
41
  return [
@@ -1,12 +1,12 @@
1
+ import type {Schema} from '@portabletext/schema'
1
2
  import type {DeserializerRule} from '../../types'
2
- import type {PortableTextSchema} from '../../util/portable-text-schema'
3
3
  import createGDocsRules from './gdocs'
4
4
  import createHTMLRules from './html'
5
5
  import createNotionRules from './notion'
6
6
  import createWordRules from './word'
7
7
 
8
8
  export function createRules(
9
- schema: PortableTextSchema,
9
+ schema: Schema,
10
10
  options: {keyGenerator?: () => string},
11
11
  ): DeserializerRule[] {
12
12
  return [
package/src/index.ts CHANGED
@@ -1,25 +1,29 @@
1
+ import {sanitySchemaToPortableTextSchema} from '@portabletext/sanity-bridge'
2
+ import type {Schema} from '@portabletext/schema'
1
3
  import type {ArraySchemaType} from '@sanity/types'
2
4
  import HtmlDeserializer from './HtmlDeserializer'
3
5
  import type {HtmlDeserializerOptions, TypedObject} from './types'
4
6
  import type {PortableTextTextBlock} from './types.portable-text'
5
7
  import {normalizeBlock} from './util/normalizeBlock'
6
- import {getPortableTextSchema} from './util/portable-text-schema'
7
8
 
8
9
  /**
9
10
  * Convert HTML to blocks respecting the block content type's schema
10
11
  *
11
12
  * @param html - The HTML to convert to blocks
12
- * @param blockContentType - A compiled version of the schema type for the block content
13
+ * @param schemaType - A compiled version of the schema type for the block content
13
14
  * @param options - Options for deserializing HTML to blocks
14
15
  * @returns Array of blocks
15
16
  * @public
16
17
  */
17
18
  export function htmlToBlocks(
18
19
  html: string,
19
- blockContentType: ArraySchemaType,
20
+ schemaType: ArraySchemaType | Schema,
20
21
  options: HtmlDeserializerOptions = {},
21
22
  ): (TypedObject | PortableTextTextBlock)[] {
22
- const schema = getPortableTextSchema(blockContentType)
23
+ const schema = isSanitySchema(schemaType)
24
+ ? sanitySchemaToPortableTextSchema(schemaType)
25
+ : schemaType
26
+
23
27
  const deserializer = new HtmlDeserializer(schema, options)
24
28
  return deserializer
25
29
  .deserialize(html)
@@ -37,3 +41,9 @@ export type {BlockNormalizationOptions} from './util/normalizeBlock'
37
41
  export {randomKey} from './util/randomKey'
38
42
  export {normalizeBlock}
39
43
  export type {HtmlDeserializerOptions, TypedObject}
44
+
45
+ function isSanitySchema(
46
+ schema: ArraySchemaType | Schema,
47
+ ): schema is ArraySchemaType {
48
+ return schema.hasOwnProperty('jsonType')
49
+ }
@@ -1,5 +1,5 @@
1
+ import type {Schema} from '@portabletext/schema'
1
2
  import {isArbitraryTypedObject} from './types'
2
- import type {PortableTextSchema} from './util/portable-text-schema'
3
3
 
4
4
  /**
5
5
  * @public
@@ -22,7 +22,7 @@ export interface PortableTextTextBlock<
22
22
  }
23
23
 
24
24
  export function isTextBlock(
25
- schema: PortableTextSchema,
25
+ schema: Schema,
26
26
  block: unknown,
27
27
  ): block is PortableTextTextBlock {
28
28
  if (!isArbitraryTypedObject(block)) {
@@ -51,7 +51,7 @@ export interface PortableTextSpan {
51
51
  }
52
52
 
53
53
  export function isSpan(
54
- schema: PortableTextSchema,
54
+ schema: Schema,
55
55
  child: unknown,
56
56
  ): child is PortableTextSpan {
57
57
  if (!isArbitraryTypedObject(child)) {
@@ -1,3 +1,4 @@
1
+ import type {Schema} from '@portabletext/schema'
1
2
  import {isEqual} from 'lodash'
2
3
  import type {TypedObject} from '../types'
3
4
  import {
@@ -5,7 +6,6 @@ import {
5
6
  type PortableTextSpan,
6
7
  type PortableTextTextBlock,
7
8
  } from '../types.portable-text'
8
- import type {PortableTextSchema} from './portable-text-schema'
9
9
  import {keyGenerator} from './randomKey'
10
10
 
11
11
  /**
@@ -54,7 +54,7 @@ export function normalizeBlock(
54
54
  > & {
55
55
  _key: string
56
56
  } {
57
- const schema: PortableTextSchema = {
57
+ const schema: Schema = {
58
58
  block: {
59
59
  name: options.blockTypeName || 'block',
60
60
  },
@@ -65,6 +65,8 @@ export function normalizeBlock(
65
65
  lists: [],
66
66
  decorators: [],
67
67
  annotations: [],
68
+ blockObjects: [],
69
+ inlineObjects: [],
68
70
  }
69
71
 
70
72
  if (node._type !== (options.blockTypeName || 'block')) {
@@ -1,174 +0,0 @@
1
- import {
2
- isBlockChildrenObjectField,
3
- isBlockListObjectField,
4
- isBlockSchemaType,
5
- isBlockStyleObjectField,
6
- isTitledListValue,
7
- type ArraySchemaType,
8
- type BlockSchemaType,
9
- type EnumListProps,
10
- type SpanSchemaType,
11
- } from '@sanity/types'
12
- import {findBlockType} from './findBlockType'
13
-
14
- export type PortableTextSchema = {
15
- block: {
16
- name: string
17
- }
18
- span: {
19
- name: string
20
- }
21
- styles: ReadonlyArray<{
22
- name: string
23
- title?: string
24
- }>
25
- lists: ReadonlyArray<{
26
- name: string
27
- title?: string
28
- }>
29
- decorators: ReadonlyArray<{
30
- name: string
31
- title?: string
32
- }>
33
- annotations: ReadonlyArray<{
34
- name: string
35
- title?: string
36
- }>
37
- }
38
-
39
- export function getPortableTextSchema(
40
- blockContentType: ArraySchemaType,
41
- ): PortableTextSchema {
42
- if (!blockContentType) {
43
- throw new Error("Parameter 'blockContentType' required")
44
- }
45
-
46
- const blockType = blockContentType.of.find(findBlockType)
47
- if (!isBlockSchemaType(blockType)) {
48
- throw new Error("'block' type is not defined in this schema (required).")
49
- }
50
-
51
- const ofType = blockType.fields.find(isBlockChildrenObjectField)?.type?.of
52
- if (!ofType) {
53
- throw new Error('No `of` declaration found for blocks `children` field')
54
- }
55
-
56
- const spanType = ofType.find(
57
- (member): member is SpanSchemaType => member.name === 'span',
58
- )
59
- if (!spanType) {
60
- throw new Error(
61
- 'No `span` type found in `block` schema type `children` definition',
62
- )
63
- }
64
-
65
- const blockName = blockContentType.of.find(findBlockType)?.name
66
-
67
- if (!blockName) {
68
- throw new Error('No `block` type found in schema type')
69
- }
70
-
71
- return {
72
- styles: resolveEnabledStyles(blockType),
73
- decorators: resolveEnabledDecorators(spanType),
74
- annotations: resolveEnabledAnnotationTypes(spanType),
75
- lists: resolveEnabledListItems(blockType),
76
- block: {
77
- name: blockName,
78
- },
79
- span: {
80
- name: spanType.name,
81
- },
82
- }
83
- }
84
-
85
- function resolveEnabledStyles(blockType: BlockSchemaType): ReadonlyArray<{
86
- name: string
87
- title?: string
88
- }> {
89
- const styleField = blockType.fields.find(isBlockStyleObjectField)
90
- if (!styleField) {
91
- throw new Error(
92
- "A field with name 'style' is not defined in the block type (required).",
93
- )
94
- }
95
-
96
- const textStyles = getTitledListValuesFromEnumListOptions(
97
- styleField.type.options,
98
- )
99
- if (textStyles.length === 0) {
100
- throw new Error(
101
- 'The style fields need at least one style ' +
102
- "defined. I.e: {title: 'Normal', value: 'normal'}.",
103
- )
104
- }
105
-
106
- return textStyles
107
- }
108
-
109
- function resolveEnabledAnnotationTypes(
110
- spanType: SpanSchemaType,
111
- ): ReadonlyArray<{
112
- name: string
113
- title?: string
114
- }> {
115
- return spanType.annotations.map((annotation) => ({
116
- name: annotation.name,
117
- title: annotation.title,
118
- }))
119
- }
120
-
121
- function resolveEnabledDecorators(spanType: SpanSchemaType): ReadonlyArray<{
122
- name: string
123
- title?: string
124
- }> {
125
- return spanType.decorators.map((decorator) => ({
126
- name: decorator.value,
127
- title: decorator.title,
128
- }))
129
- }
130
-
131
- function resolveEnabledListItems(blockType: BlockSchemaType): ReadonlyArray<{
132
- name: string
133
- title?: string
134
- }> {
135
- const listField = blockType.fields.find(isBlockListObjectField)
136
- if (!listField) {
137
- throw new Error(
138
- "A field with name 'list' is not defined in the block type (required).",
139
- )
140
- }
141
-
142
- const listItems = getTitledListValuesFromEnumListOptions(
143
- listField.type.options,
144
- )
145
- if (!listItems) {
146
- throw new Error('The list field need at least to be an empty array')
147
- }
148
-
149
- return listItems
150
- }
151
-
152
- function getTitledListValuesFromEnumListOptions(
153
- options: EnumListProps<string> | undefined,
154
- ): ReadonlyArray<{
155
- name: string
156
- title?: string
157
- }> {
158
- const list = options ? options.list : undefined
159
- if (!Array.isArray(list)) {
160
- return []
161
- }
162
-
163
- return list.map((item) => {
164
- if (isTitledListValue(item)) {
165
- return {
166
- name: item.value ?? item.title,
167
- title: item.title,
168
- }
169
- }
170
- return {
171
- name: item,
172
- }
173
- })
174
- }