@portabletext/block-tools 2.0.7 → 3.0.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.
package/lib/index.d.ts CHANGED
@@ -1,36 +1,36 @@
1
- import { ArraySchemaType, I18nTitledListValue, ObjectSchemaType, PortableTextSpan, PortableTextTextBlock, SpanSchemaType, TitledListValue } from "@sanity/types";
2
- import { ComponentType } from "react";
1
+ import { ArraySchemaType } from "@sanity/types";
3
2
  /**
4
3
  * @public
5
4
  */
6
- interface BlockContentFeatures {
7
- styles: TitledListValue<string>[];
8
- decorators: TitledListValue<string>[];
9
- annotations: ResolvedAnnotationType[];
10
- lists: I18nTitledListValue<string>[];
11
- types: {
12
- block: ArraySchemaType;
13
- span: SpanSchemaType;
14
- inlineObjects: ObjectSchemaType[];
15
- blockObjects: ObjectSchemaType[];
16
- };
5
+ type PortableTextBlock = PortableTextTextBlock | PortableTextObject;
6
+ /**
7
+ * @public
8
+ */
9
+ interface PortableTextTextBlock<TChild = PortableTextSpan | PortableTextObject> {
10
+ _type: string;
11
+ _key: string;
12
+ children: TChild[];
13
+ markDefs?: PortableTextObject[];
14
+ listItem?: string;
15
+ style?: string;
16
+ level?: number;
17
17
  }
18
18
  /**
19
- * @beta
19
+ * @public
20
20
  */
21
- interface BlockEditorSchemaProps {
22
- icon?: string | ComponentType;
23
- render?: ComponentType;
21
+ interface PortableTextSpan {
22
+ _key: string;
23
+ _type: 'span';
24
+ text: string;
25
+ marks?: string[];
24
26
  }
25
27
  /**
26
28
  * @public
27
29
  */
28
- interface ResolvedAnnotationType {
29
- blockEditor?: BlockEditorSchemaProps;
30
- title: string | undefined;
31
- value: string;
32
- type: ObjectSchemaType;
33
- icon: ComponentType | undefined;
30
+ interface PortableTextObject {
31
+ _type: string;
32
+ _key: string;
33
+ [other: string]: unknown;
34
34
  }
35
35
  /**
36
36
  * @public
@@ -62,10 +62,6 @@ interface HtmlDeserializerOptions {
62
62
  parseHtml?: HtmlParser;
63
63
  unstable_whitespaceOnPasteMode?: WhiteSpacePasteMode;
64
64
  }
65
- /**
66
- * @public
67
- */
68
-
69
65
  /**
70
66
  * @public
71
67
  */
@@ -75,9 +71,6 @@ interface DeserializerRule {
75
71
  block: ArbitraryTypedObject;
76
72
  }) => TypedObject | TypedObject[] | undefined;
77
73
  }
78
- /**
79
- * @public
80
- */
81
74
  /**
82
75
  * Block normalization options
83
76
  *
@@ -133,12 +126,4 @@ declare function randomKey(length: number): string;
133
126
  * @public
134
127
  */
135
128
  declare function htmlToBlocks(html: string, blockContentType: ArraySchemaType, options?: HtmlDeserializerOptions): (TypedObject | PortableTextTextBlock)[];
136
- /**
137
- * Normalize and extract features of an schema type containing a block type
138
- *
139
- * @param blockContentType - Schema type for the block type
140
- * @returns Returns the featureset of a compiled block content type.
141
- * @public
142
- */
143
- declare function getBlockContentFeatures(blockContentType: ArraySchemaType): BlockContentFeatures;
144
- export { type ArbitraryTypedObject, type BlockContentFeatures, type BlockEditorSchemaProps, type BlockNormalizationOptions, type DeserializerRule, type HtmlDeserializerOptions, type HtmlParser, type ResolvedAnnotationType, type TypedObject, getBlockContentFeatures, htmlToBlocks, normalizeBlock, randomKey };
129
+ export { type ArbitraryTypedObject, type BlockNormalizationOptions, type DeserializerRule, type HtmlDeserializerOptions, type HtmlParser, type PortableTextBlock, type PortableTextObject, type PortableTextSpan, type PortableTextTextBlock, type TypedObject, htmlToBlocks, normalizeBlock, randomKey };
package/lib/index.js CHANGED
@@ -1,10 +1,19 @@
1
1
  import flatten from "lodash/flatten.js";
2
- import { isBlockSchemaType, isBlockChildrenObjectField, isObjectSchemaType, isBlockListObjectField, isBlockStyleObjectField, isTitledListValue, isPortableTextTextBlock, isPortableTextSpan } from "@sanity/types";
3
2
  import isEqual from "lodash/isEqual.js";
4
3
  import uniq from "lodash/uniq.js";
5
4
  import getRandomValues from "get-random-values-esm";
6
- function findBlockType(type) {
7
- return type.type ? findBlockType(type.type) : type.name === "block";
5
+ import { isBlockSchemaType, isBlockChildrenObjectField, isBlockListObjectField, isBlockStyleObjectField, isTitledListValue } from "@sanity/types";
6
+ function isArbitraryTypedObject(object) {
7
+ return isRecord(object) && typeof object._type == "string";
8
+ }
9
+ function isRecord(value) {
10
+ return !!value && (typeof value == "object" || typeof value == "function");
11
+ }
12
+ function isTextBlock(schema, block) {
13
+ return !(!isArbitraryTypedObject(block) || block._type !== schema.block.name || !Array.isArray(block.children));
14
+ }
15
+ function isSpan(schema, child) {
16
+ return !(!isArbitraryTypedObject(child) || child._type !== schema.span.name || typeof child.text != "string");
8
17
  }
9
18
  const objectToString = Object.prototype.toString;
10
19
  function resolveJsType(val) {
@@ -96,85 +105,6 @@ uniq(
96
105
  uniq(
97
106
  Object.values(HTML_DECORATOR_TAGS)
98
107
  );
99
- function blockContentFeatures(blockContentType) {
100
- if (!blockContentType)
101
- throw new Error("Parameter 'blockContentType' required");
102
- const blockType = blockContentType.of.find(findBlockType);
103
- if (!isBlockSchemaType(blockType))
104
- throw new Error("'block' type is not defined in this schema (required).");
105
- const ofType = blockType.fields.find(isBlockChildrenObjectField)?.type?.of;
106
- if (!ofType)
107
- throw new Error("No `of` declaration found for blocks `children` field");
108
- const spanType = ofType.find(
109
- (member) => member.name === "span"
110
- );
111
- if (!spanType)
112
- throw new Error(
113
- "No `span` type found in `block` schema type `children` definition"
114
- );
115
- const inlineObjectTypes = ofType.filter(
116
- (inlineType) => inlineType.name !== "span" && isObjectSchemaType(inlineType)
117
- ), blockObjectTypes = blockContentType.of.filter(
118
- (memberType) => memberType.name !== blockType.name && isObjectSchemaType(memberType)
119
- );
120
- return {
121
- styles: resolveEnabledStyles(blockType),
122
- decorators: resolveEnabledDecorators(spanType),
123
- annotations: resolveEnabledAnnotationTypes(spanType),
124
- lists: resolveEnabledListItems(blockType),
125
- types: {
126
- block: blockContentType,
127
- span: spanType,
128
- inlineObjects: inlineObjectTypes,
129
- blockObjects: blockObjectTypes
130
- }
131
- };
132
- }
133
- function resolveEnabledStyles(blockType) {
134
- const styleField = blockType.fields.find(isBlockStyleObjectField);
135
- if (!styleField)
136
- throw new Error(
137
- "A field with name 'style' is not defined in the block type (required)."
138
- );
139
- const textStyles = getTitledListValuesFromEnumListOptions(
140
- styleField.type.options
141
- );
142
- if (textStyles.length === 0)
143
- throw new Error(
144
- "The style fields need at least one style defined. I.e: {title: 'Normal', value: 'normal'}."
145
- );
146
- return textStyles;
147
- }
148
- function resolveEnabledAnnotationTypes(spanType) {
149
- return spanType.annotations.map((annotation) => ({
150
- title: annotation.title,
151
- type: annotation,
152
- value: annotation.name,
153
- icon: annotation.icon
154
- }));
155
- }
156
- function resolveEnabledDecorators(spanType) {
157
- return spanType.decorators;
158
- }
159
- function resolveEnabledListItems(blockType) {
160
- const listField = blockType.fields.find(isBlockListObjectField);
161
- if (!listField)
162
- throw new Error(
163
- "A field with name 'list' is not defined in the block type (required)."
164
- );
165
- const listItems = getTitledListValuesFromEnumListOptions(
166
- listField.type.options
167
- );
168
- if (!listItems)
169
- throw new Error("The list field need at least to be an empty array");
170
- return listItems;
171
- }
172
- function getTitledListValuesFromEnumListOptions(options) {
173
- const list = options ? options.list : void 0;
174
- return Array.isArray(list) ? list.map(
175
- (item) => isTitledListValue(item) ? item : { title: item, value: item }
176
- ) : [];
177
- }
178
108
  const _XPathResult = {
179
109
  BOOLEAN_TYPE: 3,
180
110
  ORDERED_NODE_ITERATOR_TYPE: 5,
@@ -346,23 +276,6 @@ var preprocessWord = (html, doc) => {
346
276
  preprocessGDocs,
347
277
  preprocessHTML
348
278
  ];
349
- function createRuleOptions(blockContentType) {
350
- const features = blockContentFeatures(blockContentType), enabledBlockStyles = features.styles.map(
351
- (item) => item.value || item.title
352
- ), enabledSpanDecorators = features.decorators.map(
353
- (item) => item.value || item.title
354
- ), enabledBlockAnnotations = features.annotations.map(
355
- (item) => item.value || item.title || ""
356
- ), enabledListTypes = features.lists.map(
357
- (item) => item.value || item.title || ""
358
- );
359
- return {
360
- enabledBlockStyles,
361
- enabledSpanDecorators,
362
- enabledBlockAnnotations,
363
- enabledListTypes
364
- };
365
- }
366
279
  function tagName(el) {
367
280
  if (el && "tagName" in el)
368
281
  return el.tagName.toLowerCase();
@@ -383,12 +296,12 @@ function defaultParseHtml() {
383
296
  );
384
297
  return (html) => new DOMParser().parseFromString(html, "text/html");
385
298
  }
386
- function flattenNestedBlocks(blocks2) {
299
+ function flattenNestedBlocks(schema, blocks2) {
387
300
  let depth = 0;
388
301
  const flattened = [], traverse = (nodes) => {
389
302
  const toRemove = [];
390
303
  nodes.forEach((node) => {
391
- depth === 0 && flattened.push(node), isPortableTextTextBlock(node) && (depth > 0 && (toRemove.push(node), flattened.push(node)), depth++, traverse(node.children)), node._type === "__block" && (toRemove.push(node), flattened.push(node.block));
304
+ depth === 0 && flattened.push(node), isTextBlock(schema, node) && (depth > 0 && (toRemove.push(node), flattened.push(node)), depth++, traverse(node.children)), node._type === "__block" && (toRemove.push(node), flattened.push(node.block));
392
305
  }), toRemove.forEach((node) => {
393
306
  nodes.splice(nodes.indexOf(node), 1);
394
307
  }), depth--;
@@ -406,9 +319,9 @@ function prevSpan(block, index) {
406
319
  function isWhiteSpaceChar(text) {
407
320
  return ["\xA0", " "].includes(text);
408
321
  }
409
- function trimWhitespace(blocks2) {
322
+ function trimWhitespace(schema, blocks2) {
410
323
  return blocks2.forEach((block) => {
411
- isPortableTextTextBlock(block) && block.children.forEach((child, index) => {
324
+ isTextBlock(schema, block) && block.children.forEach((child, index) => {
412
325
  if (!isMinimalSpan(child))
413
326
  return;
414
327
  const nextChild = nextSpan(block, index), prevChild = prevSpan(block, index);
@@ -416,14 +329,14 @@ function trimWhitespace(blocks2) {
416
329
  });
417
330
  }), blocks2;
418
331
  }
419
- function ensureRootIsBlocks(blocks2) {
332
+ function ensureRootIsBlocks(schema, blocks2) {
420
333
  return blocks2.reduce((memo, node, i, original) => {
421
334
  if (node._type === "block")
422
335
  return memo.push(node), memo;
423
336
  if (node._type === "__block")
424
337
  return memo.push(node.block), memo;
425
338
  const lastBlock = memo[memo.length - 1];
426
- if (i > 0 && !isPortableTextTextBlock(original[i - 1]) && isPortableTextTextBlock(lastBlock))
339
+ if (i > 0 && !isTextBlock(schema, original[i - 1]) && isTextBlock(schema, lastBlock))
427
340
  return lastBlock.children.push(node), memo;
428
341
  const block = {
429
342
  ...DEFAULT_BLOCK,
@@ -531,11 +444,11 @@ const blocks = {
531
444
  ...HTML_BLOCK_TAGS,
532
445
  ...HTML_HEADER_TAGS
533
446
  };
534
- function getBlockStyle(el, enabledBlockStyles) {
447
+ function getBlockStyle(schema, el) {
535
448
  const childTag = tagName(el.firstChild), block = childTag && blocks[childTag];
536
- return block && enabledBlockStyles.includes(block.style) ? block.style : BLOCK_DEFAULT_STYLE;
449
+ return block && schema.styles.some((style) => style.name === block.style) ? block.style : BLOCK_DEFAULT_STYLE;
537
450
  }
538
- function createGDocsRules(_blockContentType, options) {
451
+ function createGDocsRules(schema) {
539
452
  return [
540
453
  {
541
454
  deserialize(el) {
@@ -556,7 +469,7 @@ function createGDocsRules(_blockContentType, options) {
556
469
  ...DEFAULT_BLOCK,
557
470
  listItem: getListItemStyle$1(el),
558
471
  level: getListItemLevel$1(el),
559
- style: getBlockStyle(el, options.enabledBlockStyles),
472
+ style: getBlockStyle(schema, el),
560
473
  children: next(el.firstChild?.childNodes || [])
561
474
  };
562
475
  }
@@ -607,13 +520,13 @@ const whitespaceTextNodeRule = {
607
520
  function isWhitespaceTextNode(node) {
608
521
  return (node.nodeType === 3 && (node.textContent || "").replace(/[\r\n]/g, " ").replace(/\s\s+/g, " ") === " " && node.nextSibling && node.nextSibling.nodeType !== 3 && node.previousSibling && node.previousSibling.nodeType !== 3 || node.textContent !== " ") && tagName(node.parentNode) !== "body";
609
522
  }
610
- function resolveListItem(listNodeTagName, enabledListTypes) {
611
- if (listNodeTagName === "ul" && enabledListTypes.includes("bullet"))
523
+ function resolveListItem(schema, listNodeTagName) {
524
+ if (listNodeTagName === "ul" && schema.lists.some((list) => list.name === "bullet"))
612
525
  return "bullet";
613
- if (listNodeTagName === "ol" && enabledListTypes.includes("number"))
526
+ if (listNodeTagName === "ol" && schema.lists.some((list) => list.name === "number"))
614
527
  return "number";
615
528
  }
616
- function createHTMLRules(_blockContentType, options) {
529
+ function createHTMLRules(schema, options) {
617
530
  return [
618
531
  whitespaceTextNodeRule,
619
532
  {
@@ -621,7 +534,9 @@ function createHTMLRules(_blockContentType, options) {
621
534
  deserialize(el) {
622
535
  if (tagName(el) !== "pre")
623
536
  return;
624
- const isCodeEnabled = options.enabledBlockStyles.includes("code");
537
+ const isCodeEnabled = schema.styles.some(
538
+ (style) => style.name === "code"
539
+ );
625
540
  return {
626
541
  _type: "block",
627
542
  style: "normal",
@@ -674,11 +589,15 @@ function createHTMLRules(_blockContentType, options) {
674
589
  ...HTML_HEADER_TAGS
675
590
  }, tag = tagName(el);
676
591
  let block = tag ? blocks2[tag] : void 0;
677
- if (block)
678
- return el.parentNode && tagName(el.parentNode) === "li" ? next(el.childNodes) : (options.enabledBlockStyles.includes(block.style) || (block = DEFAULT_BLOCK), {
679
- ...block,
680
- children: next(el.childNodes)
681
- });
592
+ if (!block)
593
+ return;
594
+ if (el.parentNode && tagName(el.parentNode) === "li")
595
+ return next(el.childNodes);
596
+ const blockStyle = block.style;
597
+ return schema.styles.some((style) => style.name === blockStyle) || (block = DEFAULT_BLOCK), {
598
+ ...block,
599
+ children: next(el.childNodes)
600
+ };
682
601
  }
683
602
  },
684
603
  // Ignore span tags
@@ -721,10 +640,7 @@ function createHTMLRules(_blockContentType, options) {
721
640
  const tag = tagName(el), listItem = tag ? HTML_LIST_ITEM_TAGS[tag] : void 0, parentTag = tagName(el.parentNode) || "";
722
641
  if (!listItem || !el.parentNode || !HTML_LIST_CONTAINER_TAGS[parentTag])
723
642
  return;
724
- const enabledListItem = resolveListItem(
725
- parentTag,
726
- options.enabledListTypes
727
- );
643
+ const enabledListItem = resolveListItem(schema, parentTag);
728
644
  return enabledListItem ? (listItem.listItem = enabledListItem, {
729
645
  ...listItem,
730
646
  children: next(el.childNodes)
@@ -735,7 +651,9 @@ function createHTMLRules(_blockContentType, options) {
735
651
  {
736
652
  deserialize(el, next) {
737
653
  const decorator = HTML_DECORATOR_TAGS[tagName(el) || ""];
738
- if (!(!decorator || !options.enabledSpanDecorators.includes(decorator)))
654
+ if (!(!decorator || !schema.decorators.some(
655
+ (decoratorType) => decoratorType.name === decorator
656
+ )))
739
657
  return {
740
658
  _type: "__decorator",
741
659
  name: decorator,
@@ -749,19 +667,18 @@ function createHTMLRules(_blockContentType, options) {
749
667
  deserialize(el, next) {
750
668
  if (tagName(el) !== "a")
751
669
  return;
752
- const linkEnabled = options.enabledBlockAnnotations.includes("link"), href = isElement(el) && el.getAttribute("href");
753
- if (!href)
754
- return next(el.childNodes);
755
- let markDef;
756
- return linkEnabled ? (markDef = {
757
- _key: options.keyGenerator ? options.keyGenerator() : keyGenerator(),
758
- _type: "link",
759
- href
760
- }, {
670
+ const linkEnabled = schema.annotations.some(
671
+ (annotation) => annotation.name === "link"
672
+ ), href = isElement(el) && el.getAttribute("href");
673
+ return href ? linkEnabled ? {
761
674
  _type: "__annotation",
762
- markDef,
675
+ markDef: {
676
+ _key: options.keyGenerator ? options.keyGenerator() : keyGenerator(),
677
+ _type: "link",
678
+ href
679
+ },
763
680
  children: next(el.childNodes)
764
- }) : el.appendChild(el.ownerDocument.createTextNode(` (${href})`)) && next(el.childNodes);
681
+ } : el.appendChild(el.ownerDocument.createTextNode(` (${href})`)) && next(el.childNodes) : next(el.childNodes);
765
682
  }
766
683
  }
767
684
  ];
@@ -781,7 +698,7 @@ function isUnderline(el) {
781
698
  function isNotion(el) {
782
699
  return isElement(el) && !!el.getAttribute("data-is-notion");
783
700
  }
784
- function createNotionRules(_blockContentType) {
701
+ function createNotionRules() {
785
702
  return [
786
703
  {
787
704
  deserialize(el) {
@@ -831,16 +748,16 @@ function createWordRules() {
831
748
  }
832
749
  ];
833
750
  }
834
- function createRules(blockContentType, options) {
751
+ function createRules(schema, options) {
835
752
  return [
836
753
  ...createWordRules(),
837
754
  ...createNotionRules(),
838
- ...createGDocsRules(blockContentType, options),
839
- ...createHTMLRules(blockContentType, options)
755
+ ...createGDocsRules(schema),
756
+ ...createHTMLRules(schema, options)
840
757
  ];
841
758
  }
842
759
  class HtmlDeserializer {
843
- blockContentType;
760
+ schema;
844
761
  rules;
845
762
  parseHtml;
846
763
  _markDefs = [];
@@ -850,17 +767,13 @@ class HtmlDeserializer {
850
767
  * @param blockContentType - Schema type for array containing _at least_ a block child type
851
768
  * @param options - Options for the deserialization process
852
769
  */
853
- constructor(blockContentType, options = {}) {
854
- const { rules = [], unstable_whitespaceOnPasteMode = "preserve" } = options;
855
- if (!blockContentType)
856
- throw new Error("Parameter 'blockContentType' is required");
857
- const standardRules = createRules(blockContentType, {
858
- ...createRuleOptions(blockContentType),
770
+ constructor(schema, options = {}) {
771
+ const { rules = [], unstable_whitespaceOnPasteMode = "preserve" } = options, standardRules = createRules(schema, {
859
772
  keyGenerator: options.keyGenerator
860
773
  });
861
- this.rules = [...rules, ...standardRules];
774
+ this.schema = schema, this.rules = [...rules, ...standardRules];
862
775
  const parseHtml = options.parseHtml || defaultParseHtml();
863
- this.blockContentType = blockContentType, this.parseHtml = (html) => preprocess(html, parseHtml, { unstable_whitespaceOnPasteMode }).body;
776
+ this.parseHtml = (html) => preprocess(html, parseHtml, { unstable_whitespaceOnPasteMode }).body;
864
777
  }
865
778
  /**
866
779
  * Deserialize HTML.
@@ -871,21 +784,19 @@ class HtmlDeserializer {
871
784
  deserialize = (html) => {
872
785
  this._markDefs = [];
873
786
  const { parseHtml } = this, fragment = parseHtml(html), children = Array.from(fragment.childNodes), blocks2 = trimWhitespace(
787
+ this.schema,
874
788
  flattenNestedBlocks(
875
- ensureRootIsBlocks(this.deserializeElements(children))
789
+ this.schema,
790
+ ensureRootIsBlocks(this.schema, this.deserializeElements(children))
876
791
  )
877
792
  );
878
- this._markDefs.length > 0 && blocks2.filter(
879
- (block) => block._type === "block"
880
- ).forEach((block) => {
793
+ return this._markDefs.length > 0 && blocks2.filter((block) => isTextBlock(this.schema, block)).forEach((block) => {
881
794
  block.markDefs = block.markDefs || [], block.markDefs = block.markDefs.concat(
882
795
  this._markDefs.filter((def) => flatten(
883
796
  block.children.map((child) => child.marks || [])
884
797
  ).includes(def._key))
885
798
  );
886
- });
887
- const type = this.blockContentType.of.find(findBlockType);
888
- return type ? blocks2.map((block) => (block._type === "block" && (block._type = type.name), block)) : blocks2;
799
+ }), blocks2.map((block) => (block._type === "block" && (block._type = this.schema.block.name), block));
889
800
  };
890
801
  /**
891
802
  * Deserialize an array of DOM elements.
@@ -999,6 +910,11 @@ class HtmlDeserializer {
999
910
  };
1000
911
  }
1001
912
  function normalizeBlock(node, options = {}) {
913
+ const schema = {
914
+ span: {
915
+ name: "span"
916
+ }
917
+ };
1002
918
  if (node._type !== (options.blockTypeName || "block"))
1003
919
  return "_key" in node ? node : {
1004
920
  ...node,
@@ -1023,13 +939,13 @@ function normalizeBlock(node, options = {}) {
1023
939
  return block.children = block.children.reduce(
1024
940
  (acc, child) => {
1025
941
  const previousChild = acc[acc.length - 1];
1026
- return previousChild && isPortableTextSpan(child) && isPortableTextSpan(previousChild) && isEqual(previousChild.marks, child.marks) ? (lastChild && lastChild === child && child.text === "" && block.children.length > 1 || (previousChild.text += child.text), acc) : (acc.push(child), acc);
942
+ return previousChild && isSpan(schema, child) && isSpan(schema, previousChild) && isEqual(previousChild.marks, child.marks) ? (lastChild && lastChild === child && child.text === "" && block.children.length > 1 || (previousChild.text += child.text), acc) : (acc.push(child), acc);
1027
943
  },
1028
944
  []
1029
945
  ).map((child) => {
1030
946
  if (!child)
1031
947
  throw new Error("missing child");
1032
- return child._key = options.keyGenerator ? options.keyGenerator() : keyGenerator(), isPortableTextSpan(child) && (child.marks ? allowedDecorators && (child.marks = child.marks.filter((mark) => {
948
+ return child._key = options.keyGenerator ? options.keyGenerator() : keyGenerator(), isSpan(schema, child) && (child.marks ? allowedDecorators && (child.marks = child.marks.filter((mark) => {
1033
949
  const isAllowed = allowedDecorators.includes(mark), isUsed = block.markDefs?.some((def) => def._key === mark);
1034
950
  return isAllowed || isUsed;
1035
951
  })) : child.marks = [], usedMarkDefs.push(...child.marks)), child;
@@ -1037,14 +953,95 @@ function normalizeBlock(node, options = {}) {
1037
953
  (markDef) => usedMarkDefs.includes(markDef._key)
1038
954
  ), block;
1039
955
  }
1040
- function htmlToBlocks(html, blockContentType, options = {}) {
1041
- return new HtmlDeserializer(blockContentType, options).deserialize(html).map((block) => normalizeBlock(block, { keyGenerator: options.keyGenerator }));
956
+ function findBlockType(type) {
957
+ return type.type ? findBlockType(type.type) : type.name === "block";
1042
958
  }
1043
- function getBlockContentFeatures(blockContentType) {
1044
- return blockContentFeatures(blockContentType);
959
+ function getPortableTextSchema(blockContentType) {
960
+ if (!blockContentType)
961
+ throw new Error("Parameter 'blockContentType' required");
962
+ const blockType = blockContentType.of.find(findBlockType);
963
+ if (!isBlockSchemaType(blockType))
964
+ throw new Error("'block' type is not defined in this schema (required).");
965
+ const ofType = blockType.fields.find(isBlockChildrenObjectField)?.type?.of;
966
+ if (!ofType)
967
+ throw new Error("No `of` declaration found for blocks `children` field");
968
+ const spanType = ofType.find(
969
+ (member) => member.name === "span"
970
+ );
971
+ if (!spanType)
972
+ throw new Error(
973
+ "No `span` type found in `block` schema type `children` definition"
974
+ );
975
+ const blockName = blockContentType.of.find(findBlockType)?.name;
976
+ if (!blockName)
977
+ throw new Error("No `block` type found in schema type");
978
+ return {
979
+ styles: resolveEnabledStyles(blockType),
980
+ decorators: resolveEnabledDecorators(spanType),
981
+ annotations: resolveEnabledAnnotationTypes(spanType),
982
+ lists: resolveEnabledListItems(blockType),
983
+ block: {
984
+ name: blockName
985
+ },
986
+ span: {
987
+ name: spanType.name
988
+ }
989
+ };
990
+ }
991
+ function resolveEnabledStyles(blockType) {
992
+ const styleField = blockType.fields.find(isBlockStyleObjectField);
993
+ if (!styleField)
994
+ throw new Error(
995
+ "A field with name 'style' is not defined in the block type (required)."
996
+ );
997
+ const textStyles = getTitledListValuesFromEnumListOptions(
998
+ styleField.type.options
999
+ );
1000
+ if (textStyles.length === 0)
1001
+ throw new Error(
1002
+ "The style fields need at least one style defined. I.e: {title: 'Normal', value: 'normal'}."
1003
+ );
1004
+ return textStyles;
1005
+ }
1006
+ function resolveEnabledAnnotationTypes(spanType) {
1007
+ return spanType.annotations.map((annotation) => ({
1008
+ name: annotation.name,
1009
+ title: annotation.title
1010
+ }));
1011
+ }
1012
+ function resolveEnabledDecorators(spanType) {
1013
+ return spanType.decorators.map((decorator) => ({
1014
+ name: decorator.value,
1015
+ title: decorator.title
1016
+ }));
1017
+ }
1018
+ function resolveEnabledListItems(blockType) {
1019
+ const listField = blockType.fields.find(isBlockListObjectField);
1020
+ if (!listField)
1021
+ throw new Error(
1022
+ "A field with name 'list' is not defined in the block type (required)."
1023
+ );
1024
+ const listItems = getTitledListValuesFromEnumListOptions(
1025
+ listField.type.options
1026
+ );
1027
+ if (!listItems)
1028
+ throw new Error("The list field need at least to be an empty array");
1029
+ return listItems;
1030
+ }
1031
+ function getTitledListValuesFromEnumListOptions(options) {
1032
+ const list = options ? options.list : void 0;
1033
+ return Array.isArray(list) ? list.map((item) => isTitledListValue(item) ? {
1034
+ name: item.value ?? item.title,
1035
+ title: item.title
1036
+ } : {
1037
+ name: item
1038
+ }) : [];
1039
+ }
1040
+ function htmlToBlocks(html, blockContentType, options = {}) {
1041
+ const schema = getPortableTextSchema(blockContentType);
1042
+ return new HtmlDeserializer(schema, options).deserialize(html).map((block) => normalizeBlock(block, { keyGenerator: options.keyGenerator }));
1045
1043
  }
1046
1044
  export {
1047
- getBlockContentFeatures,
1048
1045
  htmlToBlocks,
1049
1046
  normalizeBlock,
1050
1047
  randomKey