@portabletext/block-tools 5.0.4 → 5.1.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.js CHANGED
@@ -1,17 +1,15 @@
1
+ import { htmlToPortableText } from "@portabletext/html";
1
2
  import { sanitySchemaToPortableTextSchema } from "@portabletext/sanity-bridge";
2
- import { isTextBlock, isSpan } from "@portabletext/schema";
3
- import { isElement, tagName, PRESERVE_WHITESPACE_TAGS, HTML_BLOCK_TAGS, HTML_HEADER_TAGS, DEFAULT_SPAN, DEFAULT_BLOCK, BLOCK_DEFAULT_STYLE, HTML_LIST_CONTAINER_TAGS, HTML_SPAN_TAGS, HTML_LIST_ITEM_TAGS, HTML_DECORATOR_TAGS, isMinimalSpan, isEqualMarks, defaultParseHtml, flattenNestedBlocks, ensureRootIsBlocks, resolveJsType, isPlaceholderDecorator, isPlaceholderAnnotation, isMinimalBlock, isNodeList } from "./_chunks-es/helpers.js";
4
- var l = { 0: 8203, 1: 8204, 2: 8205, 3: 8290, 4: 8291, 5: 8288, 6: 65279, 7: 8289, 8: 119155, 9: 119156, a: 119157, b: 119158, c: 119159, d: 119160, e: 119161, f: 119162 }, d = { 0: 8203, 1: 8204, 2: 8205, 3: 65279 }, a = { 0: String.fromCodePoint(d[0]), 1: String.fromCodePoint(d[1]), 2: String.fromCodePoint(d[2]), 3: String.fromCodePoint(d[3]) };
5
- new Array(4).fill(String.fromCodePoint(d[0])).join("");
6
- Object.fromEntries(Object.entries(a).map((t) => [t[1], +t[0]]));
7
- Object.fromEntries(Object.entries(l).map((t) => t.reverse()));
8
- var _ = `${Object.values(l).map((t) => `\\u{${t.toString(16)}}`).join("")}`, u = new RegExp(`[${_}]{4,}`, "gu");
9
- function D(t) {
10
- var e;
11
- return { cleaned: t.replace(u, ""), encoded: ((e = t.match(u)) == null ? void 0 : e[0]) || "" };
12
- }
13
- function M(t) {
14
- return t && JSON.parse(D(JSON.stringify(t)).cleaned);
3
+ import { isSpan } from "@portabletext/schema";
4
+ function isEqualMarks(a, b) {
5
+ if (!a || !b)
6
+ return a === b;
7
+ if (a.length !== b.length)
8
+ return !1;
9
+ for (let index = 0; index < a.length; index++)
10
+ if (a[index] !== b[index])
11
+ return !1;
12
+ return !0;
15
13
  }
16
14
  function keyGenerator() {
17
15
  return randomKey(12);
@@ -26,1204 +24,6 @@ for (let i = 0; i < 256; ++i)
26
24
  function randomKey(length) {
27
25
  return whatwgRNG(length).reduce((str, n) => str + byteToHex[n], "").slice(0, length);
28
26
  }
29
- function isWordOnlineHtml(html) {
30
- return /class="(?:TextRun|NormalTextRun)[^"]*SCXW\d+[^"]*BCX\d+/.test(html) || /class="EOP[^"]*SCXW\d+/.test(html);
31
- }
32
- function isWordOnlineTextRun(el) {
33
- return !isElement(el) || tagName(el) !== "span" ? !1 : el.classList.contains("TextRun") && !el.classList.contains("EOP");
34
- }
35
- function isNormalTextRun(el) {
36
- return !isElement(el) || tagName(el) !== "span" ? !1 : el.classList.contains("NormalTextRun");
37
- }
38
- function isTextRunSpan(el) {
39
- return !isElement(el) || tagName(el) !== "span" ? !1 : el.classList.contains("TextRun") && !el.classList.contains("NormalTextRun") && !el.classList.contains("EOP");
40
- }
41
- function isFindHit(el) {
42
- return !isElement(el) || tagName(el) !== "span" ? !1 : el.classList.contains("FindHit");
43
- }
44
- function isInHeading(el) {
45
- let current = el;
46
- for (; current; ) {
47
- if (isElement(current) && tagName(current) === "word-online-block" && /^heading \d$/.test(current.getAttribute("data-parastyle") ?? ""))
48
- return !0;
49
- current = current.parentNode;
50
- }
51
- return !1;
52
- }
53
- function isInBlockquote(el) {
54
- let current = el;
55
- for (; current; ) {
56
- if (isElement(current) && tagName(current) === "word-online-block" && current.getAttribute("data-parastyle") === "Quote")
57
- return !0;
58
- current = current.parentNode;
59
- }
60
- return !1;
61
- }
62
- function hasStrongFormatting(el) {
63
- const style = el.getAttribute("style") ?? "";
64
- return el.classList.contains("MacChromeBold") || /font-weight\s*:\s*bold/.test(style);
65
- }
66
- function hasEmphasisFormatting(el) {
67
- const style = el.getAttribute("style") ?? "";
68
- return /font-style\s*:\s*italic/.test(style);
69
- }
70
- function hasUnderlineFormatting(el) {
71
- const style = el.getAttribute("style") ?? "";
72
- return el.classList.contains("Underlined") || /text-decoration\s*:\s*underline/.test(style);
73
- }
74
- function hasStrikethroughFormatting(el) {
75
- const style = el.getAttribute("style") ?? "";
76
- return el.classList.contains("Strikethrough") || /text-decoration\s*:\s*line-through/.test(style);
77
- }
78
- function hasFormatting(el) {
79
- return hasStrongFormatting(el) || hasEmphasisFormatting(el) || hasUnderlineFormatting(el) || hasStrikethroughFormatting(el);
80
- }
81
- function preprocessWordOnline(html, doc) {
82
- if (!isWordOnlineHtml(html))
83
- return doc;
84
- const paragraphs = Array.from(
85
- doc.querySelectorAll('p.Paragraph[role="heading"]')
86
- );
87
- for (const paragraph of paragraphs) {
88
- const ariaLevel = paragraph.getAttribute("aria-level");
89
- if (ariaLevel) {
90
- const wrapper = doc.createElement("word-online-block");
91
- wrapper.setAttribute("data-parastyle", `heading ${ariaLevel}`);
92
- const parent = paragraph.parentNode;
93
- if (parent) {
94
- for (parent.insertBefore(wrapper, paragraph); paragraph.firstChild; )
95
- wrapper.appendChild(paragraph.firstChild);
96
- parent.removeChild(paragraph);
97
- }
98
- }
99
- }
100
- let child = doc.body.firstChild;
101
- for (; child; ) {
102
- const next = child.nextSibling;
103
- if (!isElement(child) || !tagName(child)?.includes("span")) {
104
- child = next;
105
- continue;
106
- }
107
- const paraStyle = getParaStyle(child);
108
- if (!paraStyle) {
109
- child = next;
110
- continue;
111
- }
112
- const group = [child];
113
- let sibling = next;
114
- for (; sibling && !(!isElement(sibling) || getParaStyle(sibling) !== paraStyle); )
115
- group.push(sibling), sibling = sibling.nextSibling;
116
- const wrapper = doc.createElement("word-online-block");
117
- wrapper.setAttribute("data-parastyle", paraStyle), doc.body.insertBefore(wrapper, child);
118
- for (const span of group)
119
- wrapper.appendChild(span);
120
- child = sibling;
121
- }
122
- const textRunSpans = Array.from(doc.body.querySelectorAll("span")).filter(
123
- isTextRunSpan
124
- );
125
- for (const textRunSpan of textRunSpans) {
126
- const normalTextRuns = Array.from(textRunSpan.childNodes).filter(
127
- isNormalTextRun
128
- );
129
- for (const normalTextRun of normalTextRuns) {
130
- let foundNestedSpan = !0;
131
- for (; foundNestedSpan; ) {
132
- const children = Array.from(normalTextRun.childNodes), nestedSpanIndex = children.findIndex(
133
- (node) => isElement(node) && tagName(node) === "span" && node.textContent.trim() === ""
134
- );
135
- if (nestedSpanIndex === -1) {
136
- foundNestedSpan = !1;
137
- break;
138
- }
139
- const nestedSpan = children.at(nestedSpanIndex);
140
- if (!nestedSpan) {
141
- foundNestedSpan = !1;
142
- break;
143
- }
144
- const spaceText = nestedSpan.textContent?.replace(/\u00a0/g, " ") ?? "", isSpaceAtBeginning = !children.slice(0, nestedSpanIndex).some((n) => n.nodeType === 3);
145
- if (normalTextRun.removeChild(nestedSpan), isSpaceAtBeginning) {
146
- const firstTextNode = Array.from(normalTextRun.childNodes).find(
147
- (n) => n.nodeType === 3
148
- );
149
- if (firstTextNode)
150
- firstTextNode.textContent = spaceText + (firstTextNode.textContent || "");
151
- else {
152
- const spaceNode = doc.createTextNode(spaceText);
153
- normalTextRun.insertBefore(spaceNode, normalTextRun.firstChild);
154
- }
155
- } else {
156
- const nextSibling = textRunSpan.nextSibling, currentHasFormatting = hasFormatting(textRunSpan);
157
- if (nextSibling && isElement(nextSibling) && isTextRunSpan(nextSibling)) {
158
- const nextHasFormatting = hasFormatting(nextSibling);
159
- if (currentHasFormatting && !nextHasFormatting) {
160
- const nextNormalTextRun = Array.from(nextSibling.childNodes).find(
161
- isNormalTextRun
162
- );
163
- if (nextNormalTextRun && isElement(nextNormalTextRun)) {
164
- const firstChild = nextNormalTextRun.firstChild;
165
- if (firstChild && firstChild.nodeType === 3)
166
- firstChild.textContent = spaceText + (firstChild.textContent ?? "");
167
- else {
168
- const spaceNode = doc.createTextNode(spaceText);
169
- nextNormalTextRun.insertBefore(
170
- spaceNode,
171
- nextNormalTextRun.firstChild
172
- );
173
- }
174
- }
175
- } else {
176
- const lastTextNode = Array.from(normalTextRun.childNodes).find(
177
- (n) => n.nodeType === 3
178
- );
179
- if (lastTextNode)
180
- lastTextNode.textContent = (lastTextNode.textContent ?? "") + spaceText;
181
- else {
182
- const spaceNode = doc.createTextNode(spaceText);
183
- normalTextRun.appendChild(spaceNode);
184
- }
185
- }
186
- } else {
187
- const lastTextNode = Array.from(normalTextRun.childNodes).find(
188
- (n) => n.nodeType === 3
189
- );
190
- if (lastTextNode)
191
- lastTextNode.textContent = (lastTextNode.textContent ?? "") + spaceText;
192
- else {
193
- const spaceNode = doc.createTextNode(spaceText);
194
- normalTextRun.appendChild(spaceNode);
195
- }
196
- }
197
- }
198
- }
199
- }
200
- }
201
- return doc;
202
- }
203
- function getParaStyle(element) {
204
- const directStyle = element.getAttribute("data-ccp-parastyle");
205
- if (directStyle)
206
- return directStyle;
207
- if (tagName(element) === "span" && element.classList.contains("TextRun")) {
208
- const normalTextRuns = Array.from(
209
- element.querySelectorAll(".NormalTextRun")
210
- );
211
- if (normalTextRuns.length > 0) {
212
- const firstStyle = normalTextRuns[0].getAttribute("data-ccp-parastyle");
213
- if (firstStyle && normalTextRuns.every(
214
- (normalTextRun) => normalTextRun.getAttribute("data-ccp-parastyle") === firstStyle
215
- ))
216
- return firstStyle;
217
- }
218
- }
219
- }
220
- const _XPathResult = {
221
- BOOLEAN_TYPE: 3,
222
- ORDERED_NODE_ITERATOR_TYPE: 5,
223
- UNORDERED_NODE_SNAPSHOT_TYPE: 6
224
- };
225
- function preprocessGDocs(_html, doc) {
226
- let gDocsRootOrSiblingNode = doc.evaluate(
227
- '//*[@id and contains(@id, "docs-internal-guid")]',
228
- doc,
229
- null,
230
- _XPathResult.ORDERED_NODE_ITERATOR_TYPE,
231
- null
232
- ).iterateNext();
233
- if (gDocsRootOrSiblingNode) {
234
- const isWrappedRootTag = tagName(gDocsRootOrSiblingNode) === "b";
235
- isWrappedRootTag || (gDocsRootOrSiblingNode = doc.body);
236
- const childNodes = doc.evaluate(
237
- "//*",
238
- doc,
239
- null,
240
- _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
241
- null
242
- );
243
- for (let i = childNodes.snapshotLength - 1; i >= 0; i--) {
244
- const elm = childNodes.snapshotItem(i);
245
- elm?.setAttribute("data-is-google-docs", "true"), (elm?.parentElement === gDocsRootOrSiblingNode || !isWrappedRootTag && elm.parentElement === doc.body) && (elm?.setAttribute("data-is-root-node", "true"), tagName(elm)), tagName(elm) === "li" && elm.firstChild && tagName(elm?.firstChild) === "img" && elm.removeChild(elm.firstChild);
246
- }
247
- return isWrappedRootTag && doc.body.firstElementChild?.replaceWith(
248
- ...Array.from(gDocsRootOrSiblingNode.childNodes)
249
- ), doc;
250
- }
251
- return doc;
252
- }
253
- const unwantedWordDocumentPaths = [
254
- "/html/text()",
255
- "/html/head/text()",
256
- "/html/body/text()",
257
- "/html/body/ul/text()",
258
- "/html/body/ol/text()",
259
- "//comment()",
260
- "//style",
261
- "//xml",
262
- "//script",
263
- "//meta",
264
- "//link"
265
- ];
266
- function preprocessHTML(_html, doc) {
267
- const bodyTextNodes = doc.evaluate(
268
- "/html/body/text()",
269
- doc,
270
- null,
271
- _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
272
- null
273
- );
274
- for (let i = bodyTextNodes.snapshotLength - 1; i >= 0; i--) {
275
- const node = bodyTextNodes.snapshotItem(i), text = node.textContent || "";
276
- if (text.replace(/[^\S\n]+$/g, "")) {
277
- const newNode = doc.createElement("span");
278
- newNode.appendChild(doc.createTextNode(text)), node.parentNode?.replaceChild(newNode, node);
279
- } else
280
- node.parentNode?.removeChild(node);
281
- }
282
- const unwantedNodes = doc.evaluate(
283
- unwantedWordDocumentPaths.join("|"),
284
- doc,
285
- null,
286
- _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
287
- null
288
- );
289
- for (let i = unwantedNodes.snapshotLength - 1; i >= 0; i--) {
290
- const unwanted = unwantedNodes.snapshotItem(i);
291
- unwanted && unwanted.parentNode?.removeChild(unwanted);
292
- }
293
- return doc;
294
- }
295
- function preprocessNotion(html, doc) {
296
- const NOTION_REGEX = /<!-- notionvc:.*?-->/g;
297
- if (html.match(NOTION_REGEX)) {
298
- const childNodes = doc.evaluate(
299
- "//*",
300
- doc,
301
- null,
302
- _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
303
- null
304
- );
305
- for (let i = childNodes.snapshotLength - 1; i >= 0; i--)
306
- childNodes.snapshotItem(i)?.setAttribute("data-is-notion", "true");
307
- return doc;
308
- }
309
- return doc;
310
- }
311
- const BLOCK_CONTAINER_ELEMENTS = [
312
- "body",
313
- "table",
314
- "tbody",
315
- "thead",
316
- "tfoot",
317
- "tr",
318
- "ul",
319
- "ol"
320
- ];
321
- function preprocessWhitespace(_2, doc) {
322
- function processNode(node) {
323
- if (node.nodeType === _XPathResult.BOOLEAN_TYPE && !PRESERVE_WHITESPACE_TAGS.includes(
324
- node.parentElement?.tagName.toLowerCase() || ""
325
- )) {
326
- const normalized = node.textContent?.replace(/\s\s+/g, " ").replace(/[\r\n]+/g, " ") || "", parentTag = node.parentElement?.tagName.toLowerCase();
327
- parentTag && BLOCK_CONTAINER_ELEMENTS.includes(parentTag) && normalized.trim() === "" ? node.parentNode?.removeChild(node) : node.textContent = normalized;
328
- } else
329
- for (let i = node.childNodes.length - 1; i >= 0; i--)
330
- processNode(node.childNodes[i]);
331
- }
332
- return processNode(doc.body), doc;
333
- }
334
- const WORD_HTML_REGEX = /(class="?Mso|style=(?:"|')[^"]*?\bmso-|w:WordDocument|<o:\w+>|<\/font>)/, unwantedPaths = [
335
- "//o:p",
336
- "//span[@style='mso-list:Ignore']",
337
- "//span[@style='mso-list: Ignore']"
338
- ], mappedPaths = [
339
- "//p[@class='MsoTocHeading']",
340
- "//p[@class='MsoTitle']",
341
- "//p[@class='MsoToaHeading']",
342
- "//p[@class='MsoSubtitle']",
343
- "//span[@class='MsoSubtleEmphasis']",
344
- "//span[@class='MsoIntenseEmphasis']"
345
- ], elementMap = {
346
- MsoTocHeading: ["h3"],
347
- MsoTitle: ["h1"],
348
- MsoToaHeading: ["h2"],
349
- MsoSubtitle: ["h5"],
350
- MsoSubtleEmphasis: ["span", "em"],
351
- MsoIntenseEmphasis: ["span", "em", "strong"]
352
- // Remove cruft
353
- };
354
- function isWordHtml(html) {
355
- return WORD_HTML_REGEX.test(html);
356
- }
357
- function preprocessWord(html, doc) {
358
- if (!isWordHtml(html))
359
- return doc;
360
- const unwantedNodes = doc.evaluate(
361
- unwantedPaths.join("|"),
362
- doc,
363
- (prefix) => prefix === "o" ? "urn:schemas-microsoft-com:office:office" : null,
364
- _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
365
- null
366
- );
367
- for (let i = unwantedNodes.snapshotLength - 1; i >= 0; i--) {
368
- const unwanted = unwantedNodes.snapshotItem(i);
369
- unwanted?.parentNode && unwanted.parentNode.removeChild(unwanted);
370
- }
371
- const mappedElements = doc.evaluate(
372
- mappedPaths.join("|"),
373
- doc,
374
- null,
375
- _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
376
- null
377
- );
378
- for (let i = mappedElements.snapshotLength - 1; i >= 0; i--) {
379
- const mappedElm = mappedElements.snapshotItem(i), tags = elementMap[mappedElm.className], text = doc.createTextNode(mappedElm.textContent || "");
380
- if (!tags)
381
- continue;
382
- const parentElement = doc.createElement(tags[0]);
383
- let parent = parentElement, child = parentElement;
384
- tags.slice(1).forEach((tag) => {
385
- child = doc.createElement(tag), parent.appendChild(child), parent = child;
386
- }), child.appendChild(text), mappedElm?.parentNode?.replaceChild(parentElement, mappedElm);
387
- }
388
- return doc;
389
- }
390
- const preprocessors = [
391
- preprocessWhitespace,
392
- preprocessNotion,
393
- preprocessWord,
394
- preprocessWordOnline,
395
- preprocessGDocs,
396
- preprocessHTML
397
- ];
398
- function mapParaStyleToBlockStyle(schema, paraStyle) {
399
- const blockStyle = {
400
- "heading 1": "h1",
401
- "heading 2": "h2",
402
- "heading 3": "h3",
403
- "heading 4": "h4",
404
- "heading 5": "h5",
405
- "heading 6": "h6",
406
- Quote: "blockquote"
407
- }[paraStyle] ?? "normal";
408
- return schema.styles.find((style) => style.name === blockStyle)?.name;
409
- }
410
- function createWordOnlineRules(schema, options) {
411
- return [
412
- // Image rule - handles bare Word Online <img> tags with WACImage class
413
- {
414
- deserialize(el) {
415
- if (!isElement(el) || tagName(el) !== "img")
416
- return;
417
- const classNameRaw = el.className;
418
- let className = "";
419
- if (typeof classNameRaw == "string" ? className = classNameRaw : classNameRaw && typeof classNameRaw == "object" && (className = classNameRaw.baseVal || ""), !className.includes("WACImage"))
420
- return;
421
- const src = el.getAttribute("src") ?? void 0, alt = el.getAttribute("alt") ?? void 0, props = Object.fromEntries(
422
- Array.from(el.attributes).map((attr) => [attr.name, attr.value])
423
- ), image = options.matchers?.image?.({
424
- context: {
425
- schema,
426
- keyGenerator: options.keyGenerator ?? keyGenerator
427
- },
428
- props: {
429
- ...props,
430
- ...src ? { src } : {},
431
- ...alt ? { alt } : {}
432
- }
433
- });
434
- if (image)
435
- return {
436
- _type: "__block",
437
- block: image
438
- };
439
- }
440
- },
441
- // Image rule - handles Word Online images wrapped in WACImageContainer
442
- {
443
- deserialize(el) {
444
- if (!isElement(el))
445
- return;
446
- const classNameRaw = el.className;
447
- let className = "";
448
- if (typeof classNameRaw == "string" ? className = classNameRaw : classNameRaw && typeof classNameRaw == "object" && (className = classNameRaw.baseVal || ""), !className.includes("WACImageContainer"))
449
- return;
450
- const img = el.querySelector("img");
451
- if (!img)
452
- return;
453
- const src = img.getAttribute("src") ?? void 0, alt = img.getAttribute("alt") ?? void 0, props = Object.fromEntries(
454
- Array.from(img.attributes).map((attr) => [attr.name, attr.value])
455
- ), isInsideListItem = el.closest("li") !== null;
456
- if (el.closest("p") === null || isInsideListItem) {
457
- const inlineImage = options.matchers?.inlineImage?.({
458
- context: {
459
- schema,
460
- keyGenerator: options.keyGenerator ?? keyGenerator
461
- },
462
- props: {
463
- ...props,
464
- ...src ? { src } : {},
465
- ...alt ? { alt } : {}
466
- }
467
- });
468
- if (inlineImage)
469
- return inlineImage;
470
- }
471
- const image = options.matchers?.image?.({
472
- context: {
473
- schema,
474
- keyGenerator: options.keyGenerator ?? keyGenerator
475
- },
476
- props: {
477
- ...props,
478
- ...src ? { src } : {},
479
- ...alt ? { alt } : {}
480
- }
481
- });
482
- if (image)
483
- return {
484
- _type: "__block",
485
- block: image
486
- };
487
- }
488
- },
489
- // List item rule - handles <li> elements with aria-level
490
- {
491
- deserialize(el, next) {
492
- if (!isElement(el) || tagName(el) !== "li")
493
- return;
494
- const ariaLevel = el.getAttribute("data-aria-level");
495
- if (!ariaLevel)
496
- return;
497
- const listItem = tagName(el.parentNode) === "ol" ? "number" : "bullet";
498
- let childNodesToProcess = el.childNodes, blockStyle = "normal";
499
- if (el.childNodes.length === 1 && el.firstChild && isElement(el.firstChild)) {
500
- const childTag = tagName(el.firstChild);
501
- if (childTag && (HTML_BLOCK_TAGS[childTag] || HTML_HEADER_TAGS[childTag] || childTag === "word-online-block")) {
502
- if (childTag === "word-online-block") {
503
- const paraStyle = el.firstChild.getAttribute("data-parastyle"), foundBlockStyle = paraStyle ? mapParaStyleToBlockStyle(schema, paraStyle) : void 0;
504
- foundBlockStyle && (blockStyle = foundBlockStyle);
505
- }
506
- childNodesToProcess = el.firstChild.childNodes;
507
- }
508
- }
509
- const children = next(childNodesToProcess);
510
- let childArray = Array.isArray(children) ? children : [children].filter(Boolean);
511
- for (; childArray.length > 0; ) {
512
- const lastChild = childArray[childArray.length - 1];
513
- if (lastChild && typeof lastChild == "object" && "text" in lastChild) {
514
- const text = lastChild.text.trimEnd();
515
- if (text === "")
516
- childArray = childArray.slice(0, -1);
517
- else if (text !== lastChild.text) {
518
- lastChild.text = text;
519
- break;
520
- } else
521
- break;
522
- } else
523
- break;
524
- }
525
- return {
526
- _type: schema.block.name,
527
- children: childArray,
528
- markDefs: [],
529
- style: blockStyle,
530
- listItem,
531
- level: parseInt(ariaLevel, 10)
532
- };
533
- }
534
- },
535
- // Block style rule - handles paragraph styles like Quote
536
- // The preprocessor wraps grouped NormalTextRun spans in a word-online-block element
537
- {
538
- deserialize(el, next) {
539
- if (!isElement(el))
540
- return;
541
- const paraStyle = el.getAttribute("data-parastyle"), blockStyle = paraStyle ? mapParaStyleToBlockStyle(schema, paraStyle) : void 0;
542
- if (!blockStyle)
543
- return;
544
- const children = next(el.childNodes);
545
- return {
546
- _type: schema.block.name,
547
- style: blockStyle,
548
- markDefs: [],
549
- children: Array.isArray(children) ? children : children ? [children] : []
550
- };
551
- }
552
- },
553
- // TextRun rule
554
- {
555
- deserialize(el) {
556
- if (isWordOnlineTextRun(el)) {
557
- if (!isElement(el) || !el.textContent)
558
- return;
559
- const text = Array.from(el.childNodes).filter(
560
- (node) => isNormalTextRun(node) || isFindHit(node)
561
- ).map((span2) => isElement(span2) ? span2.textContent ?? "" : "").join("");
562
- if (!text)
563
- return;
564
- const span = {
565
- ...DEFAULT_SPAN,
566
- marks: [],
567
- text
568
- };
569
- if (hasStrongFormatting(el) && span.marks.push("strong"), hasEmphasisFormatting(el) && !isInHeading(el) && !isInBlockquote(el) && span.marks.push("em"), hasUnderlineFormatting(el))
570
- if (isElement(el) && el.parentElement && tagName(el.parentElement) === "a") {
571
- const linkElement = el.parentElement;
572
- if (linkElement) {
573
- const prevSibling = linkElement.previousSibling, nextSibling = linkElement.nextSibling, hasPrevUnderline = prevSibling && isElement(prevSibling) && hasUnderlineFormatting(prevSibling), hasNextUnderline = nextSibling && isElement(nextSibling) && hasUnderlineFormatting(nextSibling);
574
- (hasPrevUnderline || hasNextUnderline) && span.marks.push("underline");
575
- }
576
- } else
577
- span.marks.push("underline");
578
- return hasStrikethroughFormatting(el) && span.marks.push("strike-through"), span;
579
- }
580
- }
581
- }
582
- ];
583
- }
584
- const LIST_CONTAINER_TAGS = Object.keys(HTML_LIST_CONTAINER_TAGS);
585
- function isEmphasis$1(el) {
586
- const style = isElement(el) && el.getAttribute("style");
587
- return /font-style\s*:\s*italic/.test(style || "");
588
- }
589
- function isStrong$1(el) {
590
- const style = isElement(el) && el.getAttribute("style");
591
- return /font-weight\s*:\s*700/.test(style || "");
592
- }
593
- function isUnderline$1(el) {
594
- if (!isElement(el) || tagName(el.parentNode) === "a")
595
- return !1;
596
- const style = isElement(el) && el.getAttribute("style");
597
- return /text-decoration\s*:\s*underline/.test(style || "");
598
- }
599
- function isStrikethrough(el) {
600
- const style = isElement(el) && el.getAttribute("style");
601
- return /text-decoration\s*:\s*(?:.*line-through.*;)/.test(style || "");
602
- }
603
- function isGoogleDocs(el) {
604
- return isElement(el) && !!el.getAttribute("data-is-google-docs");
605
- }
606
- function isRootNode(el) {
607
- return isElement(el) && !!el.getAttribute("data-is-root-node");
608
- }
609
- function getListItemStyle$1(el) {
610
- const parentTag = tagName(el.parentNode);
611
- if (!(parentTag && !LIST_CONTAINER_TAGS.includes(parentTag)))
612
- return tagName(el.parentNode) === "ul" ? "bullet" : "number";
613
- }
614
- function getListItemLevel$1(el) {
615
- let level = 0;
616
- if (tagName(el) === "li") {
617
- let parentNode = el.parentNode;
618
- for (; parentNode; ) {
619
- const parentTag = tagName(parentNode);
620
- parentTag && LIST_CONTAINER_TAGS.includes(parentTag) && level++, parentNode = parentNode.parentNode;
621
- }
622
- } else
623
- level = 1;
624
- return level;
625
- }
626
- const blocks = {
627
- ...HTML_BLOCK_TAGS,
628
- ...HTML_HEADER_TAGS
629
- };
630
- function getBlockStyle(schema, el) {
631
- const childTag = tagName(el.firstChild), block = childTag && blocks[childTag];
632
- return block ? schema.styles.some((style) => style.name === block.style) ? block.style : BLOCK_DEFAULT_STYLE : BLOCK_DEFAULT_STYLE;
633
- }
634
- function createGDocsRules(schema) {
635
- return [
636
- {
637
- deserialize(el, next) {
638
- if (isElement(el) && tagName(el) === "span" && isGoogleDocs(el)) {
639
- if (!el.textContent)
640
- return !el.previousSibling && !el.nextSibling && el.setAttribute("data-lonely-child", "true"), next(el.childNodes);
641
- const span = {
642
- ...DEFAULT_SPAN,
643
- marks: [],
644
- text: el.textContent
645
- };
646
- return isStrong$1(el) && span.marks.push("strong"), isUnderline$1(el) && span.marks.push("underline"), isStrikethrough(el) && span.marks.push("strike-through"), isEmphasis$1(el) && span.marks.push("em"), span;
647
- }
648
- }
649
- },
650
- {
651
- deserialize(el, next) {
652
- if (tagName(el) === "li" && isGoogleDocs(el))
653
- return {
654
- ...DEFAULT_BLOCK,
655
- listItem: getListItemStyle$1(el),
656
- level: getListItemLevel$1(el),
657
- style: getBlockStyle(schema, el),
658
- children: next(el.firstChild?.childNodes || [])
659
- };
660
- }
661
- },
662
- {
663
- deserialize(el) {
664
- if (tagName(el) === "br" && isGoogleDocs(el) && isElement(el) && el.classList.contains("apple-interchange-newline"))
665
- return {
666
- ...DEFAULT_SPAN,
667
- text: ""
668
- };
669
- if (tagName(el) === "br" && isGoogleDocs(el) && isElement(el) && el?.parentNode?.textContent === "")
670
- return {
671
- ...DEFAULT_SPAN,
672
- text: ""
673
- };
674
- if (tagName(el) === "br" && isGoogleDocs(el) && isElement(el) && isRootNode(el))
675
- return {
676
- ...DEFAULT_SPAN,
677
- text: ""
678
- };
679
- }
680
- }
681
- ];
682
- }
683
- const whitespaceTextNodeRule = {
684
- deserialize(node) {
685
- return node.nodeName === "#text" && isWhitespaceTextNode(node) ? {
686
- ...DEFAULT_SPAN,
687
- marks: [],
688
- text: (node.textContent ?? "").replace(/\s\s+/g, " ")
689
- } : void 0;
690
- }
691
- };
692
- function isWhitespaceTextNode(node) {
693
- const isWhitespaceOnly = node.nodeType === 3 && (node.textContent || "").replace(/[\r\n]/g, " ").replace(/\s\s+/g, " ") === " ", hasSiblingContext = node.nextSibling && node.nextSibling.nodeType !== 3 && node.previousSibling && node.previousSibling.nodeType !== 3, hasParentSiblingContext = node.parentNode && tagName(node.parentNode) === "span" && !node.nextSibling && !node.previousSibling && node.parentNode.previousSibling && node.parentNode.previousSibling.nodeType !== 3 && node.parentNode.nextSibling && node.parentNode.nextSibling.nodeType !== 3;
694
- return (isWhitespaceOnly && (hasSiblingContext || hasParentSiblingContext) || node.textContent !== " ") && tagName(node.parentNode) !== "body";
695
- }
696
- function resolveListItem(schema, listNodeTagName) {
697
- if (listNodeTagName === "ul" && schema.lists.some((list) => list.name === "bullet"))
698
- return "bullet";
699
- if (listNodeTagName === "ol" && schema.lists.some((list) => list.name === "number"))
700
- return "number";
701
- }
702
- function createHTMLRules(schema, options) {
703
- return [
704
- whitespaceTextNodeRule,
705
- {
706
- // Pre element
707
- deserialize(el) {
708
- if (tagName(el) !== "pre")
709
- return;
710
- const isCodeEnabled = schema.styles.some(
711
- (style) => style.name === "code"
712
- );
713
- return {
714
- _type: "block",
715
- style: "normal",
716
- markDefs: [],
717
- children: [
718
- {
719
- ...DEFAULT_SPAN,
720
- marks: isCodeEnabled ? ["code"] : [],
721
- text: el.textContent || ""
722
- }
723
- ]
724
- };
725
- }
726
- },
727
- // Blockquote element
728
- {
729
- deserialize(el, next) {
730
- if (tagName(el) !== "blockquote")
731
- return;
732
- const blocks2 = {
733
- ...HTML_BLOCK_TAGS,
734
- ...HTML_HEADER_TAGS
735
- };
736
- delete blocks2.blockquote;
737
- const nonBlockquoteBlocks = Object.keys(blocks2), children = [];
738
- return el.childNodes.forEach((node, index) => {
739
- if (el.ownerDocument)
740
- if (node.nodeType === 1 && nonBlockquoteBlocks.includes(
741
- node.localName.toLowerCase()
742
- )) {
743
- const span = el.ownerDocument.createElement("span"), previousChild = children[children.length - 1];
744
- previousChild && previousChild.nodeType === 3 && previousChild.textContent?.trim() && span.appendChild(el.ownerDocument.createTextNode("\r")), node.childNodes.forEach((cn) => {
745
- span.appendChild(cn.cloneNode(!0));
746
- }), index !== el.childNodes.length && span.appendChild(el.ownerDocument.createTextNode("\r")), children.push(span);
747
- } else
748
- children.push(node);
749
- }), {
750
- _type: "block",
751
- style: "blockquote",
752
- markDefs: [],
753
- children: next(children)
754
- };
755
- }
756
- },
757
- // Block elements
758
- {
759
- deserialize(el, next) {
760
- const blocks2 = {
761
- ...HTML_BLOCK_TAGS,
762
- ...HTML_HEADER_TAGS
763
- }, tag = tagName(el);
764
- let block = tag ? blocks2[tag] : void 0;
765
- if (!block)
766
- return;
767
- if (el.parentNode && tagName(el.parentNode) === "li")
768
- return next(el.childNodes);
769
- const blockStyle = block.style;
770
- return schema.styles.some((style) => style.name === blockStyle) || (block = DEFAULT_BLOCK), {
771
- ...block,
772
- children: next(el.childNodes)
773
- };
774
- }
775
- },
776
- // Ignore span tags
777
- {
778
- deserialize(el, next) {
779
- const tag = tagName(el);
780
- if (!(!tag || !(tag in HTML_SPAN_TAGS)))
781
- return next(el.childNodes);
782
- }
783
- },
784
- // Ignore div tags
785
- {
786
- deserialize(el, next) {
787
- if (tagName(el) === "div")
788
- return next(el.childNodes);
789
- }
790
- },
791
- // Ignore list containers
792
- {
793
- deserialize(el, next) {
794
- const tag = tagName(el);
795
- if (!(!tag || !(tag in HTML_LIST_CONTAINER_TAGS)))
796
- return next(el.childNodes);
797
- }
798
- },
799
- // Deal with br's
800
- {
801
- deserialize(el) {
802
- if (tagName(el) === "br")
803
- return {
804
- ...DEFAULT_SPAN,
805
- text: `
806
- `
807
- };
808
- }
809
- },
810
- // Deal with list items
811
- {
812
- deserialize(el, next, block) {
813
- const tag = tagName(el), listItem = tag ? HTML_LIST_ITEM_TAGS[tag] : void 0;
814
- if (!listItem)
815
- return;
816
- const parentTag = tagName(el.parentNode) || "", listTag = HTML_LIST_CONTAINER_TAGS[parentTag] ? parentTag : "ul", enabledListItem = resolveListItem(schema, listTag);
817
- return enabledListItem ? (listItem.listItem = enabledListItem, {
818
- ...listItem,
819
- children: next(el.childNodes)
820
- }) : block({ _type: "block", children: next(el.childNodes) });
821
- }
822
- },
823
- // Deal with decorators - this is a limited set of known html elements that we know how to deserialize
824
- {
825
- deserialize(el, next) {
826
- const decorator = HTML_DECORATOR_TAGS[tagName(el) || ""];
827
- if (!(!decorator || !schema.decorators.some(
828
- (decoratorType) => decoratorType.name === decorator
829
- )))
830
- return {
831
- _type: "__decorator",
832
- name: decorator,
833
- children: next(el.childNodes)
834
- };
835
- }
836
- },
837
- // Special case for hyperlinks, add annotation (if allowed by schema),
838
- // If not supported just write out the link text and href in plain text.
839
- {
840
- deserialize(el, next) {
841
- if (tagName(el) !== "a")
842
- return;
843
- const linkEnabled = schema.annotations.some(
844
- (annotation) => annotation.name === "link"
845
- ), href = isElement(el) && el.getAttribute("href");
846
- return href ? linkEnabled ? {
847
- _type: "__annotation",
848
- markDef: {
849
- _key: options.keyGenerator ? options.keyGenerator() : keyGenerator(),
850
- _type: "link",
851
- href
852
- },
853
- children: next(el.childNodes)
854
- } : el.appendChild(el.ownerDocument.createTextNode(` (${href})`)) && next(el.childNodes) : next(el.childNodes);
855
- }
856
- },
857
- {
858
- deserialize(el, next) {
859
- if (isElement(el) && (tagName(el) === "td" || tagName(el) === "th"))
860
- return {
861
- ...DEFAULT_BLOCK,
862
- children: next(el.childNodes)
863
- };
864
- }
865
- },
866
- {
867
- deserialize(el) {
868
- if (isElement(el) && tagName(el) === "img") {
869
- const src = el.getAttribute("src") ?? void 0, alt = el.getAttribute("alt") ?? void 0, props = Object.fromEntries(
870
- Array.from(el.attributes).map((attr) => [attr.name, attr.value])
871
- ), ancestorOfLonelyChild = el?.parentElement?.parentElement?.getAttribute("data-lonely-child"), ancestorOfListItem = el.closest("li") !== null;
872
- if (ancestorOfLonelyChild && !ancestorOfListItem) {
873
- const image2 = options.matchers?.image?.({
874
- context: {
875
- schema,
876
- keyGenerator: options.keyGenerator ?? keyGenerator
877
- },
878
- props: {
879
- ...props,
880
- ...src ? { src } : {},
881
- ...alt ? { alt } : {}
882
- }
883
- });
884
- if (image2)
885
- return {
886
- _type: "__block",
887
- block: image2
888
- };
889
- }
890
- const inlineImage = options.matchers?.inlineImage?.({
891
- context: {
892
- schema,
893
- keyGenerator: options.keyGenerator ?? keyGenerator
894
- },
895
- props: {
896
- ...props,
897
- ...src ? { src } : {},
898
- ...alt ? { alt } : {}
899
- }
900
- });
901
- if (inlineImage)
902
- return inlineImage;
903
- const image = options.matchers?.image?.({
904
- context: {
905
- schema,
906
- keyGenerator: options.keyGenerator ?? keyGenerator
907
- },
908
- props: {
909
- ...props,
910
- ...src ? { src } : {},
911
- ...alt ? { alt } : {}
912
- }
913
- });
914
- if (image)
915
- return {
916
- _type: "__block",
917
- block: image
918
- };
919
- }
920
- }
921
- }
922
- ];
923
- }
924
- function isEmphasis(el) {
925
- const style = isElement(el) && el.getAttribute("style");
926
- return /font-style:italic/.test(style || "");
927
- }
928
- function isStrong(el) {
929
- const style = isElement(el) && el.getAttribute("style");
930
- return /font-weight:700/.test(style || "") || /font-weight:600/.test(style || "");
931
- }
932
- function isUnderline(el) {
933
- const style = isElement(el) && el.getAttribute("style");
934
- return /text-decoration:underline/.test(style || "");
935
- }
936
- function isNotion(el) {
937
- return isElement(el) && !!el.getAttribute("data-is-notion");
938
- }
939
- function createNotionRules() {
940
- return [
941
- {
942
- deserialize(el) {
943
- if (isElement(el) && tagName(el) === "span" && isNotion(el)) {
944
- const span = {
945
- ...DEFAULT_SPAN,
946
- marks: [],
947
- text: el.textContent
948
- };
949
- return isStrong(el) && span.marks.push("strong"), isUnderline(el) && span.marks.push("underline"), isEmphasis(el) && span.marks.push("em"), span;
950
- }
951
- }
952
- }
953
- ];
954
- }
955
- function getListItemStyle(el) {
956
- const style = isElement(el) && el.getAttribute("style");
957
- if (style && style.match(/lfo\d+/))
958
- return style.match("lfo1") ? "number" : "bullet";
959
- }
960
- function getListItemLevel(el) {
961
- const style = isElement(el) && el.getAttribute("style");
962
- if (!style)
963
- return;
964
- const levelMatch = style.match(/level\d+/);
965
- if (!levelMatch)
966
- return;
967
- const [level] = levelMatch[0].match(/\d/) || [];
968
- return (level ? Number.parseInt(level, 10) : 1) || 1;
969
- }
970
- function isWordListElement(el) {
971
- if (!isElement(el))
972
- return !1;
973
- if (el.className && (el.className === "MsoListParagraphCxSpFirst" || el.className === "MsoListParagraphCxSpMiddle" || el.className === "MsoListParagraphCxSpLast"))
974
- return !0;
975
- const style = el.getAttribute("style");
976
- return !!(style && /mso-list:\s*l\d+\s+level\d+\s+lfo\d+/.test(style));
977
- }
978
- function getHeadingStyle(el) {
979
- const tag = tagName(el);
980
- if (tag && HTML_HEADER_TAGS[tag])
981
- return HTML_HEADER_TAGS[tag]?.style;
982
- }
983
- function createWordRules() {
984
- return [
985
- {
986
- deserialize(el, next) {
987
- const tag = tagName(el);
988
- if ((tag === "p" || HTML_HEADER_TAGS[tag || ""]) && isWordListElement(el)) {
989
- const headingStyle = getHeadingStyle(el);
990
- return {
991
- ...DEFAULT_BLOCK,
992
- listItem: getListItemStyle(el),
993
- level: getListItemLevel(el),
994
- style: headingStyle || BLOCK_DEFAULT_STYLE,
995
- children: next(el.childNodes)
996
- };
997
- }
998
- }
999
- }
1000
- ];
1001
- }
1002
- function createRules(schema, options) {
1003
- return [
1004
- ...createWordRules(),
1005
- ...createWordOnlineRules(schema, options),
1006
- ...createNotionRules(),
1007
- ...createGDocsRules(schema),
1008
- ...createHTMLRules(schema, options)
1009
- ];
1010
- }
1011
- function trimWhitespace(context, mode, blocks2) {
1012
- const trimmedBlocks = [];
1013
- let consecutiveEmptyCount = 0;
1014
- for (const block of blocks2) {
1015
- const trimmedBlock = isTextBlock(context, block) ? trimTextBlockWhitespace(block) : block;
1016
- if (mode === "preserve") {
1017
- trimmedBlocks.push(trimmedBlock);
1018
- continue;
1019
- }
1020
- if (mode === "remove") {
1021
- if (isEmptyTextBlock(context, trimmedBlock))
1022
- continue;
1023
- trimmedBlocks.push(trimmedBlock);
1024
- continue;
1025
- }
1026
- if (mode === "normalize") {
1027
- if (isEmptyTextBlock(context, trimmedBlock)) {
1028
- consecutiveEmptyCount++, consecutiveEmptyCount === 1 && trimmedBlocks.push(trimmedBlock);
1029
- continue;
1030
- }
1031
- trimmedBlocks.push(trimmedBlock), consecutiveEmptyCount = 0;
1032
- }
1033
- }
1034
- return trimmedBlocks;
1035
- }
1036
- function isEmptyTextBlock(context, block) {
1037
- return !(!isTextBlock(context, block) || block.children.some(
1038
- (child) => !isSpan(context, child) || child.text.trim() !== ""
1039
- ));
1040
- }
1041
- function trimTextBlockWhitespace(block) {
1042
- let index = 0;
1043
- for (const child of block.children) {
1044
- if (!isMinimalSpan(child)) {
1045
- index++;
1046
- continue;
1047
- }
1048
- const nextChild = nextSpan(block, index), prevChild = prevSpan(block, index);
1049
- index === 0 && (child.text = child.text.replace(/^[^\S\n]+/g, "")), index === block.children.length - 1 && (child.text = child.text.replace(/[^\S\n]+$/g, "")), /\s/.test(child.text.slice(Math.max(0, child.text.length - 1))) && nextChild && isMinimalSpan(nextChild) && /\s/.test(nextChild.text.slice(0, 1)) && (child.text = child.text.replace(/[^\S\n]+$/g, "")), /\s/.test(child.text.slice(0, 1)) && prevChild && isMinimalSpan(prevChild) && /\s/.test(prevChild.text.slice(Math.max(0, prevChild.text.length - 1))) && (child.text = child.text.replace(/^[^\S\n]+/g, "")), child.text || block.children.splice(index, 1), prevChild && Array.isArray(prevChild.marks) && isEqualMarks(prevChild.marks, child.marks) && isWhiteSpaceChar(child.text) ? (prevChild.text += " ", block.children.splice(index, 1)) : nextChild && Array.isArray(nextChild.marks) && isEqualMarks(nextChild.marks, child.marks) && isWhiteSpaceChar(child.text) && (nextChild.text = ` ${nextChild.text}`, block.children.splice(index, 1)), index++;
1050
- }
1051
- return block;
1052
- }
1053
- function nextSpan(block, index) {
1054
- const next = block.children[index + 1];
1055
- return next && next._type === "span" ? next : null;
1056
- }
1057
- function prevSpan(block, index) {
1058
- const prev = block.children[index - 1];
1059
- return prev && prev._type === "span" ? prev : null;
1060
- }
1061
- function isWhiteSpaceChar(text) {
1062
- return ["\xA0", " "].includes(text);
1063
- }
1064
- class HtmlDeserializer {
1065
- keyGenerator;
1066
- schema;
1067
- rules;
1068
- parseHtml;
1069
- whitespaceMode;
1070
- _markDefs = [];
1071
- /**
1072
- * Create a new serializer respecting a Sanity block content type's schema
1073
- *
1074
- * @param blockContentType - Schema type for array containing _at least_ a block child type
1075
- * @param options - Options for the deserialization process
1076
- */
1077
- constructor(schema, options = {}) {
1078
- const { rules = [], unstable_whitespaceOnPasteMode = "preserve" } = options, standardRules = createRules(schema, {
1079
- keyGenerator: options.keyGenerator,
1080
- matchers: options.matchers
1081
- });
1082
- this.schema = schema, this.keyGenerator = options.keyGenerator ?? keyGenerator, this.rules = [...rules, ...standardRules], this.whitespaceMode = unstable_whitespaceOnPasteMode;
1083
- const parseHtml = options.parseHtml || defaultParseHtml();
1084
- this.parseHtml = (html) => {
1085
- const cleanHTML = M(html), doc = parseHtml(cleanHTML);
1086
- for (const processor of preprocessors)
1087
- processor(cleanHTML, doc);
1088
- return doc.body;
1089
- };
1090
- }
1091
- /**
1092
- * Deserialize HTML.
1093
- *
1094
- * @param html - The HTML to deserialize, as a string
1095
- * @returns Array of blocks - either portable text blocks or other allowed blocks
1096
- */
1097
- deserialize = (html) => {
1098
- this._markDefs = [];
1099
- const { parseHtml } = this, fragment = parseHtml(html), children = Array.from(fragment.childNodes), blocks2 = trimWhitespace(
1100
- { schema: this.schema },
1101
- this.whitespaceMode,
1102
- flattenNestedBlocks(
1103
- { schema: this.schema },
1104
- ensureRootIsBlocks(
1105
- this.schema,
1106
- this.deserializeElements(children)
1107
- )
1108
- )
1109
- );
1110
- return this._markDefs.length > 0 && blocks2.filter((block) => isTextBlock({ schema: this.schema }, block)).forEach((block) => {
1111
- block.markDefs = block.markDefs || [], block.markDefs = block.markDefs.concat(
1112
- this._markDefs.filter((def) => block.children.flatMap((child) => child.marks || []).includes(def._key))
1113
- );
1114
- }), blocks2.map((block) => (block._type === "block" && (block._type = this.schema.block.name), block));
1115
- };
1116
- /**
1117
- * Deserialize an array of DOM elements.
1118
- *
1119
- * @param elements - Array of DOM elements to deserialize
1120
- * @returns
1121
- */
1122
- deserializeElements = (elements = []) => {
1123
- let nodes = [];
1124
- return elements.forEach((element) => {
1125
- nodes = nodes.concat(this.deserializeElement(element));
1126
- }), nodes;
1127
- };
1128
- /**
1129
- * Deserialize a DOM element
1130
- *
1131
- * @param element - Deserialize a DOM element
1132
- * @returns
1133
- */
1134
- deserializeElement = (element) => {
1135
- const next = (elements) => {
1136
- if (isNodeList(elements))
1137
- return this.deserializeElements(Array.from(elements));
1138
- if (Array.isArray(elements))
1139
- return this.deserializeElements(elements);
1140
- if (elements)
1141
- return this.deserializeElement(elements);
1142
- }, block = (props) => ({
1143
- _type: "__block",
1144
- block: props
1145
- });
1146
- let node;
1147
- for (let i = 0; i < this.rules.length; i++) {
1148
- const rule = this.rules[i];
1149
- if (!rule.deserialize)
1150
- continue;
1151
- const ret = rule.deserialize(element, next, block), type = resolveJsType(ret);
1152
- if (type !== "array" && type !== "object" && type !== "null" && type !== "undefined")
1153
- throw new Error(
1154
- `A rule returned an invalid deserialized representation: "${node}".`
1155
- );
1156
- if (ret !== void 0) {
1157
- {
1158
- if (ret === null)
1159
- throw new Error("Deserializer rule returned `null`");
1160
- Array.isArray(ret) ? node = ret : isPlaceholderDecorator(ret) ? node = this.deserializeDecorator(ret) : isPlaceholderAnnotation(ret) ? node = this.deserializeAnnotation(ret) : node = ret;
1161
- }
1162
- if (ret && !Array.isArray(ret) && isMinimalBlock(ret) && "listItem" in ret) {
1163
- let parent = element.parentNode?.parentNode;
1164
- for (; parent && tagName(parent) === "li"; )
1165
- parent = parent.parentNode?.parentNode, ret.level = ret.level ? ret.level + 1 : 1;
1166
- }
1167
- ret && !Array.isArray(ret) && isMinimalBlock(ret) && ret.style === "blockquote" && ret.children.forEach((child, index) => {
1168
- isMinimalSpan(child) && child.text === "\r" && (child.text = `
1169
- `, (index === 0 || index === ret.children.length - 1) && ret.children.splice(index, 1));
1170
- });
1171
- break;
1172
- }
1173
- }
1174
- return node || next(element.childNodes) || [];
1175
- };
1176
- /**
1177
- * Deserialize a `__decorator` type
1178
- * (an internal made up type to process decorators exclusively)
1179
- *
1180
- * @param decorator -
1181
- * @returns array of ...
1182
- */
1183
- deserializeDecorator = (decorator) => {
1184
- const { name } = decorator, applyDecorator = (node) => {
1185
- if (isPlaceholderDecorator(node))
1186
- return this.deserializeDecorator(node);
1187
- if (isMinimalSpan(node))
1188
- node.marks = node.marks || [], node.text.trim() && node.marks.unshift(name);
1189
- else if ("children" in node && Array.isArray(node.children)) {
1190
- const block = node;
1191
- block.children = block.children.map(applyDecorator);
1192
- }
1193
- return node;
1194
- };
1195
- return decorator.children.reduce((children, node) => {
1196
- const ret = applyDecorator(node);
1197
- return Array.isArray(ret) ? children.concat(ret) : (children.push(ret), children);
1198
- }, []);
1199
- };
1200
- /**
1201
- * Deserialize a `__annotation` object.
1202
- * (an internal made up type to process annotations exclusively)
1203
- *
1204
- * @param annotation -
1205
- * @returns Array of...
1206
- */
1207
- deserializeAnnotation = (annotation) => {
1208
- const { markDef } = annotation;
1209
- this._markDefs.push(markDef);
1210
- const applyAnnotation = (node) => {
1211
- if (isPlaceholderAnnotation(node))
1212
- return this.deserializeAnnotation(node);
1213
- if (isMinimalSpan(node))
1214
- node.marks = node.marks || [], node.text.trim() && node.marks.unshift(markDef._key);
1215
- else if ("children" in node && Array.isArray(node.children)) {
1216
- const block = node;
1217
- block.children = block.children.map(applyAnnotation);
1218
- }
1219
- return node;
1220
- };
1221
- return annotation.children.reduce((children, node) => {
1222
- const ret = applyAnnotation(node);
1223
- return Array.isArray(ret) ? children.concat(ret) : (children.push(ret), children);
1224
- }, []);
1225
- };
1226
- }
1227
27
  function normalizeBlock(node, options = {}) {
1228
28
  const schema = {
1229
29
  block: {
@@ -1279,7 +79,27 @@ function normalizeBlock(node, options = {}) {
1279
79
  }
1280
80
  function htmlToBlocks(html, schemaType, options = {}) {
1281
81
  const schema = isSanitySchema(schemaType) ? sanitySchemaToPortableTextSchema(schemaType) : schemaType;
1282
- return new HtmlDeserializer(schema, options).deserialize(html).map((block) => normalizeBlock(block, { keyGenerator: options.keyGenerator }));
82
+ return htmlToPortableText(html, {
83
+ schema,
84
+ keyGenerator: options.keyGenerator,
85
+ parseHtml: options.parseHtml,
86
+ rules: options.rules,
87
+ whitespaceMode: options.unstable_whitespaceOnPasteMode,
88
+ types: adaptMatchers(options.matchers)
89
+ });
90
+ }
91
+ function adaptMatchers(matchers) {
92
+ if (!(!matchers?.image && !matchers?.inlineImage))
93
+ return {
94
+ image: ({ context, value, isInline }) => {
95
+ const matcher = isInline ? matchers.inlineImage : matchers.image;
96
+ if (!matcher)
97
+ return;
98
+ const result = matcher({ context, props: value });
99
+ if (result)
100
+ return result;
101
+ }
102
+ };
1283
103
  }
1284
104
  function isSanitySchema(schema) {
1285
105
  return schema.hasOwnProperty("jsonType");