@portabletext/block-tools 2.0.8 → 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/README.md +1 -31
- package/lib/index.cjs +159 -162
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +24 -39
- package/lib/index.d.ts +24 -39
- package/lib/index.js +159 -162
- package/lib/index.js.map +1 -1
- package/package.json +3 -2
- package/src/HtmlDeserializer/helpers.ts +23 -46
- package/src/HtmlDeserializer/index.ts +15 -27
- package/src/HtmlDeserializer/rules/gdocs.ts +6 -7
- package/src/HtmlDeserializer/rules/html.ts +35 -24
- package/src/HtmlDeserializer/rules/index.ts +7 -7
- package/src/HtmlDeserializer/rules/notion.ts +1 -4
- package/src/index.ts +13 -28
- package/src/types.portable-text.ts +79 -0
- package/src/types.ts +11 -57
- package/src/util/normalizeBlock.ts +21 -7
- package/src/util/{blockContentTypeFeatures.ts → portable-text-schema.ts} +72 -39
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
Let's start with a complete example:
|
|
10
10
|
|
|
11
11
|
```js
|
|
12
|
-
import {
|
|
12
|
+
import {htmlToBlocks} from '@portabletext/block-tools'
|
|
13
13
|
import {Schema} from '@sanity/schema'
|
|
14
14
|
|
|
15
15
|
// Start with compiling a schema we can work against
|
|
@@ -58,9 +58,6 @@ const blocks = htmlToBlocks(
|
|
|
58
58
|
// }
|
|
59
59
|
// ]
|
|
60
60
|
// }
|
|
61
|
-
|
|
62
|
-
// Get the feature-set of a blockContentType
|
|
63
|
-
const features = getBlockContentFeatures(blockContentType)
|
|
64
61
|
```
|
|
65
62
|
|
|
66
63
|
## Methods
|
|
@@ -197,30 +194,3 @@ Will produce
|
|
|
197
194
|
markDefs: []
|
|
198
195
|
}
|
|
199
196
|
```
|
|
200
|
-
|
|
201
|
-
### `getBlockContentFeatures(blockContentType)`
|
|
202
|
-
|
|
203
|
-
Will return an object with the features enabled for the input block content type.
|
|
204
|
-
|
|
205
|
-
```js
|
|
206
|
-
{
|
|
207
|
-
annotations: [{title: 'Link', value: 'link'}],
|
|
208
|
-
decorators: [
|
|
209
|
-
{title: 'Strong', value: 'strong'},
|
|
210
|
-
{title: 'Emphasis', value: 'em'},
|
|
211
|
-
{title: 'Code', value: 'code'},
|
|
212
|
-
{title: 'Underline', value: 'underline'},
|
|
213
|
-
{title: 'Strike', value: 'strike-through'}
|
|
214
|
-
],
|
|
215
|
-
styles: [
|
|
216
|
-
{title: 'Normal', value: 'normal'},
|
|
217
|
-
{title: 'Heading 1', value: 'h1'},
|
|
218
|
-
{title: 'H2', value: 'h2'},
|
|
219
|
-
{title: 'H3', value: 'h3'},
|
|
220
|
-
{title: 'H4', value: 'h4'},
|
|
221
|
-
{title: 'H5', value: 'h5'},
|
|
222
|
-
{title: 'H6', value: 'h6'},
|
|
223
|
-
{title: 'Quote', value: 'blockquote'}
|
|
224
|
-
]
|
|
225
|
-
}
|
|
226
|
-
```
|
package/lib/index.cjs
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: !0 });
|
|
3
|
-
var flatten = require("lodash/flatten.js"),
|
|
3
|
+
var flatten = require("lodash/flatten.js"), isEqual = require("lodash/isEqual.js"), uniq = require("lodash/uniq.js"), getRandomValues = require("get-random-values-esm"), types = require("@sanity/types");
|
|
4
4
|
function _interopDefaultCompat(e) {
|
|
5
5
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
6
6
|
}
|
|
7
7
|
var flatten__default = /* @__PURE__ */ _interopDefaultCompat(flatten), isEqual__default = /* @__PURE__ */ _interopDefaultCompat(isEqual), uniq__default = /* @__PURE__ */ _interopDefaultCompat(uniq), getRandomValues__default = /* @__PURE__ */ _interopDefaultCompat(getRandomValues);
|
|
8
|
-
function
|
|
9
|
-
return
|
|
8
|
+
function isArbitraryTypedObject(object) {
|
|
9
|
+
return isRecord(object) && typeof object._type == "string";
|
|
10
|
+
}
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return !!value && (typeof value == "object" || typeof value == "function");
|
|
13
|
+
}
|
|
14
|
+
function isTextBlock(schema, block) {
|
|
15
|
+
return !(!isArbitraryTypedObject(block) || block._type !== schema.block.name || !Array.isArray(block.children));
|
|
16
|
+
}
|
|
17
|
+
function isSpan(schema, child) {
|
|
18
|
+
return !(!isArbitraryTypedObject(child) || child._type !== schema.span.name || typeof child.text != "string");
|
|
10
19
|
}
|
|
11
20
|
const objectToString = Object.prototype.toString;
|
|
12
21
|
function resolveJsType(val) {
|
|
@@ -98,85 +107,6 @@ uniq__default.default(
|
|
|
98
107
|
uniq__default.default(
|
|
99
108
|
Object.values(HTML_DECORATOR_TAGS)
|
|
100
109
|
);
|
|
101
|
-
function blockContentFeatures(blockContentType) {
|
|
102
|
-
if (!blockContentType)
|
|
103
|
-
throw new Error("Parameter 'blockContentType' required");
|
|
104
|
-
const blockType = blockContentType.of.find(findBlockType);
|
|
105
|
-
if (!types.isBlockSchemaType(blockType))
|
|
106
|
-
throw new Error("'block' type is not defined in this schema (required).");
|
|
107
|
-
const ofType = blockType.fields.find(types.isBlockChildrenObjectField)?.type?.of;
|
|
108
|
-
if (!ofType)
|
|
109
|
-
throw new Error("No `of` declaration found for blocks `children` field");
|
|
110
|
-
const spanType = ofType.find(
|
|
111
|
-
(member) => member.name === "span"
|
|
112
|
-
);
|
|
113
|
-
if (!spanType)
|
|
114
|
-
throw new Error(
|
|
115
|
-
"No `span` type found in `block` schema type `children` definition"
|
|
116
|
-
);
|
|
117
|
-
const inlineObjectTypes = ofType.filter(
|
|
118
|
-
(inlineType) => inlineType.name !== "span" && types.isObjectSchemaType(inlineType)
|
|
119
|
-
), blockObjectTypes = blockContentType.of.filter(
|
|
120
|
-
(memberType) => memberType.name !== blockType.name && types.isObjectSchemaType(memberType)
|
|
121
|
-
);
|
|
122
|
-
return {
|
|
123
|
-
styles: resolveEnabledStyles(blockType),
|
|
124
|
-
decorators: resolveEnabledDecorators(spanType),
|
|
125
|
-
annotations: resolveEnabledAnnotationTypes(spanType),
|
|
126
|
-
lists: resolveEnabledListItems(blockType),
|
|
127
|
-
types: {
|
|
128
|
-
block: blockContentType,
|
|
129
|
-
span: spanType,
|
|
130
|
-
inlineObjects: inlineObjectTypes,
|
|
131
|
-
blockObjects: blockObjectTypes
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
function resolveEnabledStyles(blockType) {
|
|
136
|
-
const styleField = blockType.fields.find(types.isBlockStyleObjectField);
|
|
137
|
-
if (!styleField)
|
|
138
|
-
throw new Error(
|
|
139
|
-
"A field with name 'style' is not defined in the block type (required)."
|
|
140
|
-
);
|
|
141
|
-
const textStyles = getTitledListValuesFromEnumListOptions(
|
|
142
|
-
styleField.type.options
|
|
143
|
-
);
|
|
144
|
-
if (textStyles.length === 0)
|
|
145
|
-
throw new Error(
|
|
146
|
-
"The style fields need at least one style defined. I.e: {title: 'Normal', value: 'normal'}."
|
|
147
|
-
);
|
|
148
|
-
return textStyles;
|
|
149
|
-
}
|
|
150
|
-
function resolveEnabledAnnotationTypes(spanType) {
|
|
151
|
-
return spanType.annotations.map((annotation) => ({
|
|
152
|
-
title: annotation.title,
|
|
153
|
-
type: annotation,
|
|
154
|
-
value: annotation.name,
|
|
155
|
-
icon: annotation.icon
|
|
156
|
-
}));
|
|
157
|
-
}
|
|
158
|
-
function resolveEnabledDecorators(spanType) {
|
|
159
|
-
return spanType.decorators;
|
|
160
|
-
}
|
|
161
|
-
function resolveEnabledListItems(blockType) {
|
|
162
|
-
const listField = blockType.fields.find(types.isBlockListObjectField);
|
|
163
|
-
if (!listField)
|
|
164
|
-
throw new Error(
|
|
165
|
-
"A field with name 'list' is not defined in the block type (required)."
|
|
166
|
-
);
|
|
167
|
-
const listItems = getTitledListValuesFromEnumListOptions(
|
|
168
|
-
listField.type.options
|
|
169
|
-
);
|
|
170
|
-
if (!listItems)
|
|
171
|
-
throw new Error("The list field need at least to be an empty array");
|
|
172
|
-
return listItems;
|
|
173
|
-
}
|
|
174
|
-
function getTitledListValuesFromEnumListOptions(options) {
|
|
175
|
-
const list = options ? options.list : void 0;
|
|
176
|
-
return Array.isArray(list) ? list.map(
|
|
177
|
-
(item) => types.isTitledListValue(item) ? item : { title: item, value: item }
|
|
178
|
-
) : [];
|
|
179
|
-
}
|
|
180
110
|
const _XPathResult = {
|
|
181
111
|
BOOLEAN_TYPE: 3,
|
|
182
112
|
ORDERED_NODE_ITERATOR_TYPE: 5,
|
|
@@ -348,23 +278,6 @@ var preprocessWord = (html, doc) => {
|
|
|
348
278
|
preprocessGDocs,
|
|
349
279
|
preprocessHTML
|
|
350
280
|
];
|
|
351
|
-
function createRuleOptions(blockContentType) {
|
|
352
|
-
const features = blockContentFeatures(blockContentType), enabledBlockStyles = features.styles.map(
|
|
353
|
-
(item) => item.value || item.title
|
|
354
|
-
), enabledSpanDecorators = features.decorators.map(
|
|
355
|
-
(item) => item.value || item.title
|
|
356
|
-
), enabledBlockAnnotations = features.annotations.map(
|
|
357
|
-
(item) => item.value || item.title || ""
|
|
358
|
-
), enabledListTypes = features.lists.map(
|
|
359
|
-
(item) => item.value || item.title || ""
|
|
360
|
-
);
|
|
361
|
-
return {
|
|
362
|
-
enabledBlockStyles,
|
|
363
|
-
enabledSpanDecorators,
|
|
364
|
-
enabledBlockAnnotations,
|
|
365
|
-
enabledListTypes
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
281
|
function tagName(el) {
|
|
369
282
|
if (el && "tagName" in el)
|
|
370
283
|
return el.tagName.toLowerCase();
|
|
@@ -385,12 +298,12 @@ function defaultParseHtml() {
|
|
|
385
298
|
);
|
|
386
299
|
return (html) => new DOMParser().parseFromString(html, "text/html");
|
|
387
300
|
}
|
|
388
|
-
function flattenNestedBlocks(blocks2) {
|
|
301
|
+
function flattenNestedBlocks(schema, blocks2) {
|
|
389
302
|
let depth = 0;
|
|
390
303
|
const flattened = [], traverse = (nodes) => {
|
|
391
304
|
const toRemove = [];
|
|
392
305
|
nodes.forEach((node) => {
|
|
393
|
-
depth === 0 && flattened.push(node),
|
|
306
|
+
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));
|
|
394
307
|
}), toRemove.forEach((node) => {
|
|
395
308
|
nodes.splice(nodes.indexOf(node), 1);
|
|
396
309
|
}), depth--;
|
|
@@ -408,9 +321,9 @@ function prevSpan(block, index) {
|
|
|
408
321
|
function isWhiteSpaceChar(text) {
|
|
409
322
|
return ["\xA0", " "].includes(text);
|
|
410
323
|
}
|
|
411
|
-
function trimWhitespace(blocks2) {
|
|
324
|
+
function trimWhitespace(schema, blocks2) {
|
|
412
325
|
return blocks2.forEach((block) => {
|
|
413
|
-
|
|
326
|
+
isTextBlock(schema, block) && block.children.forEach((child, index) => {
|
|
414
327
|
if (!isMinimalSpan(child))
|
|
415
328
|
return;
|
|
416
329
|
const nextChild = nextSpan(block, index), prevChild = prevSpan(block, index);
|
|
@@ -418,14 +331,14 @@ function trimWhitespace(blocks2) {
|
|
|
418
331
|
});
|
|
419
332
|
}), blocks2;
|
|
420
333
|
}
|
|
421
|
-
function ensureRootIsBlocks(blocks2) {
|
|
334
|
+
function ensureRootIsBlocks(schema, blocks2) {
|
|
422
335
|
return blocks2.reduce((memo, node, i, original) => {
|
|
423
336
|
if (node._type === "block")
|
|
424
337
|
return memo.push(node), memo;
|
|
425
338
|
if (node._type === "__block")
|
|
426
339
|
return memo.push(node.block), memo;
|
|
427
340
|
const lastBlock = memo[memo.length - 1];
|
|
428
|
-
if (i > 0 && !
|
|
341
|
+
if (i > 0 && !isTextBlock(schema, original[i - 1]) && isTextBlock(schema, lastBlock))
|
|
429
342
|
return lastBlock.children.push(node), memo;
|
|
430
343
|
const block = {
|
|
431
344
|
...DEFAULT_BLOCK,
|
|
@@ -533,11 +446,11 @@ const blocks = {
|
|
|
533
446
|
...HTML_BLOCK_TAGS,
|
|
534
447
|
...HTML_HEADER_TAGS
|
|
535
448
|
};
|
|
536
|
-
function getBlockStyle(
|
|
449
|
+
function getBlockStyle(schema, el) {
|
|
537
450
|
const childTag = tagName(el.firstChild), block = childTag && blocks[childTag];
|
|
538
|
-
return block &&
|
|
451
|
+
return block && schema.styles.some((style) => style.name === block.style) ? block.style : BLOCK_DEFAULT_STYLE;
|
|
539
452
|
}
|
|
540
|
-
function createGDocsRules(
|
|
453
|
+
function createGDocsRules(schema) {
|
|
541
454
|
return [
|
|
542
455
|
{
|
|
543
456
|
deserialize(el) {
|
|
@@ -558,7 +471,7 @@ function createGDocsRules(_blockContentType, options) {
|
|
|
558
471
|
...DEFAULT_BLOCK,
|
|
559
472
|
listItem: getListItemStyle$1(el),
|
|
560
473
|
level: getListItemLevel$1(el),
|
|
561
|
-
style: getBlockStyle(
|
|
474
|
+
style: getBlockStyle(schema, el),
|
|
562
475
|
children: next(el.firstChild?.childNodes || [])
|
|
563
476
|
};
|
|
564
477
|
}
|
|
@@ -609,13 +522,13 @@ const whitespaceTextNodeRule = {
|
|
|
609
522
|
function isWhitespaceTextNode(node) {
|
|
610
523
|
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";
|
|
611
524
|
}
|
|
612
|
-
function resolveListItem(
|
|
613
|
-
if (listNodeTagName === "ul" &&
|
|
525
|
+
function resolveListItem(schema, listNodeTagName) {
|
|
526
|
+
if (listNodeTagName === "ul" && schema.lists.some((list) => list.name === "bullet"))
|
|
614
527
|
return "bullet";
|
|
615
|
-
if (listNodeTagName === "ol" &&
|
|
528
|
+
if (listNodeTagName === "ol" && schema.lists.some((list) => list.name === "number"))
|
|
616
529
|
return "number";
|
|
617
530
|
}
|
|
618
|
-
function createHTMLRules(
|
|
531
|
+
function createHTMLRules(schema, options) {
|
|
619
532
|
return [
|
|
620
533
|
whitespaceTextNodeRule,
|
|
621
534
|
{
|
|
@@ -623,7 +536,9 @@ function createHTMLRules(_blockContentType, options) {
|
|
|
623
536
|
deserialize(el) {
|
|
624
537
|
if (tagName(el) !== "pre")
|
|
625
538
|
return;
|
|
626
|
-
const isCodeEnabled =
|
|
539
|
+
const isCodeEnabled = schema.styles.some(
|
|
540
|
+
(style) => style.name === "code"
|
|
541
|
+
);
|
|
627
542
|
return {
|
|
628
543
|
_type: "block",
|
|
629
544
|
style: "normal",
|
|
@@ -676,11 +591,15 @@ function createHTMLRules(_blockContentType, options) {
|
|
|
676
591
|
...HTML_HEADER_TAGS
|
|
677
592
|
}, tag = tagName(el);
|
|
678
593
|
let block = tag ? blocks2[tag] : void 0;
|
|
679
|
-
if (block)
|
|
680
|
-
return
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
594
|
+
if (!block)
|
|
595
|
+
return;
|
|
596
|
+
if (el.parentNode && tagName(el.parentNode) === "li")
|
|
597
|
+
return next(el.childNodes);
|
|
598
|
+
const blockStyle = block.style;
|
|
599
|
+
return schema.styles.some((style) => style.name === blockStyle) || (block = DEFAULT_BLOCK), {
|
|
600
|
+
...block,
|
|
601
|
+
children: next(el.childNodes)
|
|
602
|
+
};
|
|
684
603
|
}
|
|
685
604
|
},
|
|
686
605
|
// Ignore span tags
|
|
@@ -723,10 +642,7 @@ function createHTMLRules(_blockContentType, options) {
|
|
|
723
642
|
const tag = tagName(el), listItem = tag ? HTML_LIST_ITEM_TAGS[tag] : void 0, parentTag = tagName(el.parentNode) || "";
|
|
724
643
|
if (!listItem || !el.parentNode || !HTML_LIST_CONTAINER_TAGS[parentTag])
|
|
725
644
|
return;
|
|
726
|
-
const enabledListItem = resolveListItem(
|
|
727
|
-
parentTag,
|
|
728
|
-
options.enabledListTypes
|
|
729
|
-
);
|
|
645
|
+
const enabledListItem = resolveListItem(schema, parentTag);
|
|
730
646
|
return enabledListItem ? (listItem.listItem = enabledListItem, {
|
|
731
647
|
...listItem,
|
|
732
648
|
children: next(el.childNodes)
|
|
@@ -737,7 +653,9 @@ function createHTMLRules(_blockContentType, options) {
|
|
|
737
653
|
{
|
|
738
654
|
deserialize(el, next) {
|
|
739
655
|
const decorator = HTML_DECORATOR_TAGS[tagName(el) || ""];
|
|
740
|
-
if (!(!decorator || !
|
|
656
|
+
if (!(!decorator || !schema.decorators.some(
|
|
657
|
+
(decoratorType) => decoratorType.name === decorator
|
|
658
|
+
)))
|
|
741
659
|
return {
|
|
742
660
|
_type: "__decorator",
|
|
743
661
|
name: decorator,
|
|
@@ -751,19 +669,18 @@ function createHTMLRules(_blockContentType, options) {
|
|
|
751
669
|
deserialize(el, next) {
|
|
752
670
|
if (tagName(el) !== "a")
|
|
753
671
|
return;
|
|
754
|
-
const linkEnabled =
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
return linkEnabled ? (markDef = {
|
|
759
|
-
_key: options.keyGenerator ? options.keyGenerator() : keyGenerator(),
|
|
760
|
-
_type: "link",
|
|
761
|
-
href
|
|
762
|
-
}, {
|
|
672
|
+
const linkEnabled = schema.annotations.some(
|
|
673
|
+
(annotation) => annotation.name === "link"
|
|
674
|
+
), href = isElement(el) && el.getAttribute("href");
|
|
675
|
+
return href ? linkEnabled ? {
|
|
763
676
|
_type: "__annotation",
|
|
764
|
-
markDef
|
|
677
|
+
markDef: {
|
|
678
|
+
_key: options.keyGenerator ? options.keyGenerator() : keyGenerator(),
|
|
679
|
+
_type: "link",
|
|
680
|
+
href
|
|
681
|
+
},
|
|
765
682
|
children: next(el.childNodes)
|
|
766
|
-
}
|
|
683
|
+
} : el.appendChild(el.ownerDocument.createTextNode(` (${href})`)) && next(el.childNodes) : next(el.childNodes);
|
|
767
684
|
}
|
|
768
685
|
}
|
|
769
686
|
];
|
|
@@ -783,7 +700,7 @@ function isUnderline(el) {
|
|
|
783
700
|
function isNotion(el) {
|
|
784
701
|
return isElement(el) && !!el.getAttribute("data-is-notion");
|
|
785
702
|
}
|
|
786
|
-
function createNotionRules(
|
|
703
|
+
function createNotionRules() {
|
|
787
704
|
return [
|
|
788
705
|
{
|
|
789
706
|
deserialize(el) {
|
|
@@ -833,16 +750,16 @@ function createWordRules() {
|
|
|
833
750
|
}
|
|
834
751
|
];
|
|
835
752
|
}
|
|
836
|
-
function createRules(
|
|
753
|
+
function createRules(schema, options) {
|
|
837
754
|
return [
|
|
838
755
|
...createWordRules(),
|
|
839
756
|
...createNotionRules(),
|
|
840
|
-
...createGDocsRules(
|
|
841
|
-
...createHTMLRules(
|
|
757
|
+
...createGDocsRules(schema),
|
|
758
|
+
...createHTMLRules(schema, options)
|
|
842
759
|
];
|
|
843
760
|
}
|
|
844
761
|
class HtmlDeserializer {
|
|
845
|
-
|
|
762
|
+
schema;
|
|
846
763
|
rules;
|
|
847
764
|
parseHtml;
|
|
848
765
|
_markDefs = [];
|
|
@@ -852,17 +769,13 @@ class HtmlDeserializer {
|
|
|
852
769
|
* @param blockContentType - Schema type for array containing _at least_ a block child type
|
|
853
770
|
* @param options - Options for the deserialization process
|
|
854
771
|
*/
|
|
855
|
-
constructor(
|
|
856
|
-
const { rules = [], unstable_whitespaceOnPasteMode = "preserve" } = options
|
|
857
|
-
if (!blockContentType)
|
|
858
|
-
throw new Error("Parameter 'blockContentType' is required");
|
|
859
|
-
const standardRules = createRules(blockContentType, {
|
|
860
|
-
...createRuleOptions(blockContentType),
|
|
772
|
+
constructor(schema, options = {}) {
|
|
773
|
+
const { rules = [], unstable_whitespaceOnPasteMode = "preserve" } = options, standardRules = createRules(schema, {
|
|
861
774
|
keyGenerator: options.keyGenerator
|
|
862
775
|
});
|
|
863
|
-
this.rules = [...rules, ...standardRules];
|
|
776
|
+
this.schema = schema, this.rules = [...rules, ...standardRules];
|
|
864
777
|
const parseHtml = options.parseHtml || defaultParseHtml();
|
|
865
|
-
this.
|
|
778
|
+
this.parseHtml = (html) => preprocess(html, parseHtml, { unstable_whitespaceOnPasteMode }).body;
|
|
866
779
|
}
|
|
867
780
|
/**
|
|
868
781
|
* Deserialize HTML.
|
|
@@ -873,21 +786,19 @@ class HtmlDeserializer {
|
|
|
873
786
|
deserialize = (html) => {
|
|
874
787
|
this._markDefs = [];
|
|
875
788
|
const { parseHtml } = this, fragment = parseHtml(html), children = Array.from(fragment.childNodes), blocks2 = trimWhitespace(
|
|
789
|
+
this.schema,
|
|
876
790
|
flattenNestedBlocks(
|
|
877
|
-
|
|
791
|
+
this.schema,
|
|
792
|
+
ensureRootIsBlocks(this.schema, this.deserializeElements(children))
|
|
878
793
|
)
|
|
879
794
|
);
|
|
880
|
-
this._markDefs.length > 0 && blocks2.filter(
|
|
881
|
-
(block) => block._type === "block"
|
|
882
|
-
).forEach((block) => {
|
|
795
|
+
return this._markDefs.length > 0 && blocks2.filter((block) => isTextBlock(this.schema, block)).forEach((block) => {
|
|
883
796
|
block.markDefs = block.markDefs || [], block.markDefs = block.markDefs.concat(
|
|
884
797
|
this._markDefs.filter((def) => flatten__default.default(
|
|
885
798
|
block.children.map((child) => child.marks || [])
|
|
886
799
|
).includes(def._key))
|
|
887
800
|
);
|
|
888
|
-
});
|
|
889
|
-
const type = this.blockContentType.of.find(findBlockType);
|
|
890
|
-
return type ? blocks2.map((block) => (block._type === "block" && (block._type = type.name), block)) : blocks2;
|
|
801
|
+
}), blocks2.map((block) => (block._type === "block" && (block._type = this.schema.block.name), block));
|
|
891
802
|
};
|
|
892
803
|
/**
|
|
893
804
|
* Deserialize an array of DOM elements.
|
|
@@ -1001,6 +912,11 @@ class HtmlDeserializer {
|
|
|
1001
912
|
};
|
|
1002
913
|
}
|
|
1003
914
|
function normalizeBlock(node, options = {}) {
|
|
915
|
+
const schema = {
|
|
916
|
+
span: {
|
|
917
|
+
name: "span"
|
|
918
|
+
}
|
|
919
|
+
};
|
|
1004
920
|
if (node._type !== (options.blockTypeName || "block"))
|
|
1005
921
|
return "_key" in node ? node : {
|
|
1006
922
|
...node,
|
|
@@ -1025,13 +941,13 @@ function normalizeBlock(node, options = {}) {
|
|
|
1025
941
|
return block.children = block.children.reduce(
|
|
1026
942
|
(acc, child) => {
|
|
1027
943
|
const previousChild = acc[acc.length - 1];
|
|
1028
|
-
return previousChild &&
|
|
944
|
+
return previousChild && isSpan(schema, child) && isSpan(schema, previousChild) && isEqual__default.default(previousChild.marks, child.marks) ? (lastChild && lastChild === child && child.text === "" && block.children.length > 1 || (previousChild.text += child.text), acc) : (acc.push(child), acc);
|
|
1029
945
|
},
|
|
1030
946
|
[]
|
|
1031
947
|
).map((child) => {
|
|
1032
948
|
if (!child)
|
|
1033
949
|
throw new Error("missing child");
|
|
1034
|
-
return child._key = options.keyGenerator ? options.keyGenerator() : keyGenerator(),
|
|
950
|
+
return child._key = options.keyGenerator ? options.keyGenerator() : keyGenerator(), isSpan(schema, child) && (child.marks ? allowedDecorators && (child.marks = child.marks.filter((mark) => {
|
|
1035
951
|
const isAllowed = allowedDecorators.includes(mark), isUsed = block.markDefs?.some((def) => def._key === mark);
|
|
1036
952
|
return isAllowed || isUsed;
|
|
1037
953
|
})) : child.marks = [], usedMarkDefs.push(...child.marks)), child;
|
|
@@ -1039,13 +955,94 @@ function normalizeBlock(node, options = {}) {
|
|
|
1039
955
|
(markDef) => usedMarkDefs.includes(markDef._key)
|
|
1040
956
|
), block;
|
|
1041
957
|
}
|
|
1042
|
-
function
|
|
1043
|
-
return
|
|
958
|
+
function findBlockType(type) {
|
|
959
|
+
return type.type ? findBlockType(type.type) : type.name === "block";
|
|
1044
960
|
}
|
|
1045
|
-
function
|
|
1046
|
-
|
|
961
|
+
function getPortableTextSchema(blockContentType) {
|
|
962
|
+
if (!blockContentType)
|
|
963
|
+
throw new Error("Parameter 'blockContentType' required");
|
|
964
|
+
const blockType = blockContentType.of.find(findBlockType);
|
|
965
|
+
if (!types.isBlockSchemaType(blockType))
|
|
966
|
+
throw new Error("'block' type is not defined in this schema (required).");
|
|
967
|
+
const ofType = blockType.fields.find(types.isBlockChildrenObjectField)?.type?.of;
|
|
968
|
+
if (!ofType)
|
|
969
|
+
throw new Error("No `of` declaration found for blocks `children` field");
|
|
970
|
+
const spanType = ofType.find(
|
|
971
|
+
(member) => member.name === "span"
|
|
972
|
+
);
|
|
973
|
+
if (!spanType)
|
|
974
|
+
throw new Error(
|
|
975
|
+
"No `span` type found in `block` schema type `children` definition"
|
|
976
|
+
);
|
|
977
|
+
const blockName = blockContentType.of.find(findBlockType)?.name;
|
|
978
|
+
if (!blockName)
|
|
979
|
+
throw new Error("No `block` type found in schema type");
|
|
980
|
+
return {
|
|
981
|
+
styles: resolveEnabledStyles(blockType),
|
|
982
|
+
decorators: resolveEnabledDecorators(spanType),
|
|
983
|
+
annotations: resolveEnabledAnnotationTypes(spanType),
|
|
984
|
+
lists: resolveEnabledListItems(blockType),
|
|
985
|
+
block: {
|
|
986
|
+
name: blockName
|
|
987
|
+
},
|
|
988
|
+
span: {
|
|
989
|
+
name: spanType.name
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
function resolveEnabledStyles(blockType) {
|
|
994
|
+
const styleField = blockType.fields.find(types.isBlockStyleObjectField);
|
|
995
|
+
if (!styleField)
|
|
996
|
+
throw new Error(
|
|
997
|
+
"A field with name 'style' is not defined in the block type (required)."
|
|
998
|
+
);
|
|
999
|
+
const textStyles = getTitledListValuesFromEnumListOptions(
|
|
1000
|
+
styleField.type.options
|
|
1001
|
+
);
|
|
1002
|
+
if (textStyles.length === 0)
|
|
1003
|
+
throw new Error(
|
|
1004
|
+
"The style fields need at least one style defined. I.e: {title: 'Normal', value: 'normal'}."
|
|
1005
|
+
);
|
|
1006
|
+
return textStyles;
|
|
1007
|
+
}
|
|
1008
|
+
function resolveEnabledAnnotationTypes(spanType) {
|
|
1009
|
+
return spanType.annotations.map((annotation) => ({
|
|
1010
|
+
name: annotation.name,
|
|
1011
|
+
title: annotation.title
|
|
1012
|
+
}));
|
|
1013
|
+
}
|
|
1014
|
+
function resolveEnabledDecorators(spanType) {
|
|
1015
|
+
return spanType.decorators.map((decorator) => ({
|
|
1016
|
+
name: decorator.value,
|
|
1017
|
+
title: decorator.title
|
|
1018
|
+
}));
|
|
1019
|
+
}
|
|
1020
|
+
function resolveEnabledListItems(blockType) {
|
|
1021
|
+
const listField = blockType.fields.find(types.isBlockListObjectField);
|
|
1022
|
+
if (!listField)
|
|
1023
|
+
throw new Error(
|
|
1024
|
+
"A field with name 'list' is not defined in the block type (required)."
|
|
1025
|
+
);
|
|
1026
|
+
const listItems = getTitledListValuesFromEnumListOptions(
|
|
1027
|
+
listField.type.options
|
|
1028
|
+
);
|
|
1029
|
+
if (!listItems)
|
|
1030
|
+
throw new Error("The list field need at least to be an empty array");
|
|
1031
|
+
return listItems;
|
|
1032
|
+
}
|
|
1033
|
+
function getTitledListValuesFromEnumListOptions(options) {
|
|
1034
|
+
const list = options ? options.list : void 0;
|
|
1035
|
+
return Array.isArray(list) ? list.map((item) => types.isTitledListValue(item) ? {
|
|
1036
|
+
name: item.value ?? item.title,
|
|
1037
|
+
title: item.title
|
|
1038
|
+
} : {
|
|
1039
|
+
name: item
|
|
1040
|
+
}) : [];
|
|
1041
|
+
}
|
|
1042
|
+
function htmlToBlocks(html, blockContentType, options = {}) {
|
|
1043
|
+
const schema = getPortableTextSchema(blockContentType);
|
|
1044
|
+
return new HtmlDeserializer(schema, options).deserialize(html).map((block) => normalizeBlock(block, { keyGenerator: options.keyGenerator }));
|
|
1047
1045
|
}
|
|
1048
|
-
exports.getBlockContentFeatures = getBlockContentFeatures;
|
|
1049
1046
|
exports.htmlToBlocks = htmlToBlocks;
|
|
1050
1047
|
exports.normalizeBlock = normalizeBlock;
|
|
1051
1048
|
exports.randomKey = randomKey;
|