@portabletext/block-tools 0.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.cjs ADDED
@@ -0,0 +1,1056 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: !0 });
3
+ var flatten = require("lodash/flatten.js"), types = require("@sanity/types"), isEqual = require("lodash/isEqual.js"), uniq = require("lodash/uniq.js"), getRandomValues = require("get-random-values-esm");
4
+ function _interopDefaultCompat(e) {
5
+ return e && typeof e == "object" && "default" in e ? e : { default: e };
6
+ }
7
+ var flatten__default = /* @__PURE__ */ _interopDefaultCompat(flatten), isEqual__default = /* @__PURE__ */ _interopDefaultCompat(isEqual), uniq__default = /* @__PURE__ */ _interopDefaultCompat(uniq), getRandomValues__default = /* @__PURE__ */ _interopDefaultCompat(getRandomValues);
8
+ function findBlockType(type) {
9
+ return type.type ? findBlockType(type.type) : type.name === "block";
10
+ }
11
+ const objectToString = Object.prototype.toString;
12
+ function resolveJsType(val) {
13
+ switch (objectToString.call(val)) {
14
+ case "[object Function]":
15
+ return "function";
16
+ case "[object Date]":
17
+ return "date";
18
+ case "[object RegExp]":
19
+ return "regexp";
20
+ case "[object Arguments]":
21
+ return "arguments";
22
+ case "[object Array]":
23
+ return "array";
24
+ case "[object String]":
25
+ return "string";
26
+ }
27
+ return val === null ? "null" : val === void 0 ? "undefined" : val && typeof val == "object" && "nodeType" in val && val.nodeType === 1 ? "element" : val === Object(val) ? "object" : typeof val;
28
+ }
29
+ var s = { 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 }, c = { 0: 8203, 1: 8204, 2: 8205, 3: 65279 };
30
+ new Array(4).fill(String.fromCodePoint(c[0])).join("");
31
+ Object.fromEntries(Object.entries(c).map((t) => t.reverse()));
32
+ Object.fromEntries(Object.entries(s).map((t) => t.reverse()));
33
+ var S = `${Object.values(s).map((t) => `\\u{${t.toString(16)}}`).join("")}`, f = new RegExp(`[${S}]{4,}`, "gu");
34
+ function _(t) {
35
+ var e;
36
+ return { cleaned: t.replace(f, ""), encoded: ((e = t.match(f)) == null ? void 0 : e[0]) || "" };
37
+ }
38
+ function O(t) {
39
+ return t && JSON.parse(_(JSON.stringify(t)).cleaned);
40
+ }
41
+ const PRESERVE_WHITESPACE_TAGS = ["pre", "textarea", "code"], BLOCK_DEFAULT_STYLE = "normal", DEFAULT_BLOCK = Object.freeze({
42
+ _type: "block",
43
+ markDefs: [],
44
+ style: BLOCK_DEFAULT_STYLE
45
+ }), DEFAULT_SPAN = Object.freeze({
46
+ _type: "span",
47
+ marks: []
48
+ }), HTML_BLOCK_TAGS = {
49
+ p: DEFAULT_BLOCK,
50
+ blockquote: { ...DEFAULT_BLOCK, style: "blockquote" }
51
+ }, HTML_SPAN_TAGS = {
52
+ span: { object: "text" }
53
+ }, HTML_LIST_CONTAINER_TAGS = {
54
+ ol: { object: null },
55
+ ul: { object: null }
56
+ }, HTML_HEADER_TAGS = {
57
+ h1: { ...DEFAULT_BLOCK, style: "h1" },
58
+ h2: { ...DEFAULT_BLOCK, style: "h2" },
59
+ h3: { ...DEFAULT_BLOCK, style: "h3" },
60
+ h4: { ...DEFAULT_BLOCK, style: "h4" },
61
+ h5: { ...DEFAULT_BLOCK, style: "h5" },
62
+ h6: { ...DEFAULT_BLOCK, style: "h6" }
63
+ }, HTML_MISC_TAGS = {
64
+ br: { ...DEFAULT_BLOCK, style: BLOCK_DEFAULT_STYLE }
65
+ }, HTML_DECORATOR_TAGS = {
66
+ b: "strong",
67
+ strong: "strong",
68
+ i: "em",
69
+ em: "em",
70
+ u: "underline",
71
+ s: "strike-through",
72
+ strike: "strike-through",
73
+ del: "strike-through",
74
+ code: "code",
75
+ sup: "sup",
76
+ sub: "sub",
77
+ ins: "ins",
78
+ mark: "mark",
79
+ small: "small"
80
+ }, HTML_LIST_ITEM_TAGS = {
81
+ li: {
82
+ ...DEFAULT_BLOCK,
83
+ style: BLOCK_DEFAULT_STYLE,
84
+ level: 1,
85
+ listItem: "bullet"
86
+ }
87
+ }, ELEMENT_MAP = {
88
+ ...HTML_BLOCK_TAGS,
89
+ ...HTML_SPAN_TAGS,
90
+ ...HTML_LIST_CONTAINER_TAGS,
91
+ ...HTML_LIST_ITEM_TAGS,
92
+ ...HTML_HEADER_TAGS,
93
+ ...HTML_MISC_TAGS
94
+ };
95
+ uniq__default.default(
96
+ Object.values(ELEMENT_MAP).filter((tag) => "style" in tag).map((tag) => tag.style)
97
+ );
98
+ uniq__default.default(
99
+ Object.values(HTML_DECORATOR_TAGS)
100
+ );
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
+ const _XPathResult = {
181
+ ANY_TYPE: 0,
182
+ NUMBER_TYPE: 1,
183
+ STRING_TYPE: 2,
184
+ BOOLEAN_TYPE: 3,
185
+ UNORDERED_NODE_ITERATOR_TYPE: 4,
186
+ ORDERED_NODE_ITERATOR_TYPE: 5,
187
+ UNORDERED_NODE_SNAPSHOT_TYPE: 6,
188
+ ORDERED_NODE_SNAPSHOT_TYPE: 7,
189
+ ANY_UNORDERED_NODE_TYPE: 8,
190
+ FIRST_ORDERED_NODE_TYPE: 9
191
+ };
192
+ var preprocessGDocs = (_html, doc, options) => {
193
+ const whitespaceOnPasteMode = options?.unstable_whitespaceOnPasteMode || "preserve";
194
+ let gDocsRootOrSiblingNode = doc.evaluate(
195
+ '//*[@id and contains(@id, "docs-internal-guid")]',
196
+ doc,
197
+ null,
198
+ _XPathResult.ORDERED_NODE_ITERATOR_TYPE,
199
+ null
200
+ ).iterateNext();
201
+ if (gDocsRootOrSiblingNode) {
202
+ const isWrappedRootTag = tagName(gDocsRootOrSiblingNode) === "b";
203
+ switch (isWrappedRootTag || (gDocsRootOrSiblingNode = doc.body), whitespaceOnPasteMode) {
204
+ case "normalize":
205
+ normalizeWhitespace(gDocsRootOrSiblingNode);
206
+ break;
207
+ case "remove":
208
+ removeAllWhitespace(gDocsRootOrSiblingNode);
209
+ break;
210
+ }
211
+ const childNodes = doc.evaluate(
212
+ "//*",
213
+ doc,
214
+ null,
215
+ _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
216
+ null
217
+ );
218
+ for (let i = childNodes.snapshotLength - 1; i >= 0; i--) {
219
+ const elm = childNodes.snapshotItem(i);
220
+ 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);
221
+ }
222
+ return isWrappedRootTag && doc.body.firstElementChild?.replaceWith(
223
+ ...Array.from(gDocsRootOrSiblingNode.childNodes)
224
+ ), doc;
225
+ }
226
+ return doc;
227
+ };
228
+ const unwantedWordDocumentPaths = [
229
+ "/html/text()",
230
+ "/html/head/text()",
231
+ "/html/body/text()",
232
+ "/html/body/ul/text()",
233
+ "/html/body/ol/text()",
234
+ "//comment()",
235
+ "//style",
236
+ "//xml",
237
+ "//script",
238
+ "//meta",
239
+ "//link"
240
+ ];
241
+ var preprocessHTML = (_html, doc) => {
242
+ const bodyTextNodes = doc.evaluate(
243
+ "/html/body/text()",
244
+ doc,
245
+ null,
246
+ _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
247
+ null
248
+ );
249
+ for (let i = bodyTextNodes.snapshotLength - 1; i >= 0; i--) {
250
+ const node = bodyTextNodes.snapshotItem(i), text = node.textContent || "";
251
+ if (text.replace(/[^\S\n]+$/g, "")) {
252
+ const newNode = doc.createElement("span");
253
+ newNode.appendChild(doc.createTextNode(text)), node.parentNode?.replaceChild(newNode, node);
254
+ } else
255
+ node.parentNode?.removeChild(node);
256
+ }
257
+ const unwantedNodes = doc.evaluate(
258
+ unwantedWordDocumentPaths.join("|"),
259
+ doc,
260
+ null,
261
+ _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
262
+ null
263
+ );
264
+ for (let i = unwantedNodes.snapshotLength - 1; i >= 0; i--) {
265
+ const unwanted = unwantedNodes.snapshotItem(i);
266
+ unwanted && unwanted.parentNode?.removeChild(unwanted);
267
+ }
268
+ return doc;
269
+ }, preprocessNotion = (html, doc) => {
270
+ const NOTION_REGEX = /<!-- notionvc:.*?-->/g;
271
+ if (html.match(NOTION_REGEX)) {
272
+ const childNodes = doc.evaluate(
273
+ "//*",
274
+ doc,
275
+ null,
276
+ _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
277
+ null
278
+ );
279
+ for (let i = childNodes.snapshotLength - 1; i >= 0; i--)
280
+ childNodes.snapshotItem(i)?.setAttribute("data-is-notion", "true");
281
+ return doc;
282
+ }
283
+ return doc;
284
+ }, preprocessWhitespace = (_2, doc) => {
285
+ function processNode(node) {
286
+ if (node.nodeType === _XPathResult.BOOLEAN_TYPE && !PRESERVE_WHITESPACE_TAGS.includes(
287
+ node.parentElement?.tagName.toLowerCase() || ""
288
+ ))
289
+ node.textContent = node.textContent?.replace(/\s\s+/g, " ").replace(/[\r\n]+/g, " ") || "";
290
+ else
291
+ for (let i = 0; i < node.childNodes.length; i++)
292
+ processNode(node.childNodes[i]);
293
+ }
294
+ return processNode(doc.body), doc;
295
+ };
296
+ const WORD_HTML_REGEX = /(class="?Mso|style=(?:"|')[^"]*?\bmso-|w:WordDocument|<o:\w+>|<\/font>)/, unwantedPaths = [
297
+ "//o:p",
298
+ "//span[@style='mso-list:Ignore']",
299
+ "//span[@style='mso-list: Ignore']"
300
+ ], mappedPaths = [
301
+ "//p[@class='MsoTocHeading']",
302
+ "//p[@class='MsoTitle']",
303
+ "//p[@class='MsoToaHeading']",
304
+ "//p[@class='MsoSubtitle']",
305
+ "//span[@class='MsoSubtleEmphasis']",
306
+ "//span[@class='MsoIntenseEmphasis']"
307
+ ], elementMap = {
308
+ MsoTocHeading: ["h3"],
309
+ MsoTitle: ["h1"],
310
+ MsoToaHeading: ["h2"],
311
+ MsoSubtitle: ["h5"],
312
+ MsoSubtleEmphasis: ["span", "em"],
313
+ MsoIntenseEmphasis: ["span", "em", "strong"]
314
+ // Remove cruft
315
+ };
316
+ function isWordHtml(html) {
317
+ return WORD_HTML_REGEX.test(html);
318
+ }
319
+ var preprocessWord = (html, doc) => {
320
+ if (!isWordHtml(html))
321
+ return doc;
322
+ const unwantedNodes = doc.evaluate(
323
+ unwantedPaths.join("|"),
324
+ doc,
325
+ (prefix) => prefix === "o" ? "urn:schemas-microsoft-com:office:office" : null,
326
+ _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
327
+ null
328
+ );
329
+ for (let i = unwantedNodes.snapshotLength - 1; i >= 0; i--) {
330
+ const unwanted = unwantedNodes.snapshotItem(i);
331
+ unwanted?.parentNode && unwanted.parentNode.removeChild(unwanted);
332
+ }
333
+ const mappedElements = doc.evaluate(
334
+ mappedPaths.join("|"),
335
+ doc,
336
+ null,
337
+ _XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
338
+ null
339
+ );
340
+ for (let i = mappedElements.snapshotLength - 1; i >= 0; i--) {
341
+ const mappedElm = mappedElements.snapshotItem(i), tags = elementMap[mappedElm.className], text = doc.createTextNode(mappedElm.textContent || "");
342
+ if (!tags)
343
+ continue;
344
+ const parentElement = doc.createElement(tags[0]);
345
+ let parent = parentElement, child = parentElement;
346
+ tags.slice(1).forEach((tag) => {
347
+ child = doc.createElement(tag), parent.appendChild(child), parent = child;
348
+ }), child.appendChild(text), mappedElm?.parentNode?.replaceChild(parentElement, mappedElm);
349
+ }
350
+ return doc;
351
+ }, preprocessors = [
352
+ preprocessWhitespace,
353
+ preprocessNotion,
354
+ preprocessWord,
355
+ preprocessGDocs,
356
+ preprocessHTML
357
+ ];
358
+ function createRuleOptions(blockContentType) {
359
+ const features = blockContentFeatures(blockContentType), enabledBlockStyles = features.styles.map(
360
+ (item) => item.value || item.title
361
+ ), enabledSpanDecorators = features.decorators.map(
362
+ (item) => item.value || item.title
363
+ ), enabledBlockAnnotations = features.annotations.map(
364
+ (item) => item.value || item.title || ""
365
+ ), enabledListTypes = features.lists.map(
366
+ (item) => item.value || item.title || ""
367
+ );
368
+ return {
369
+ enabledBlockStyles,
370
+ enabledSpanDecorators,
371
+ enabledBlockAnnotations,
372
+ enabledListTypes
373
+ };
374
+ }
375
+ function tagName(el) {
376
+ if (el && "tagName" in el)
377
+ return el.tagName.toLowerCase();
378
+ }
379
+ function preprocess(html, parseHtml, options) {
380
+ const cleanHTML = O(html), doc = parseHtml(normalizeHtmlBeforePreprocess(cleanHTML));
381
+ return preprocessors.forEach((processor) => {
382
+ processor(cleanHTML, doc, options);
383
+ }), doc;
384
+ }
385
+ function normalizeHtmlBeforePreprocess(html) {
386
+ return html.trim();
387
+ }
388
+ function defaultParseHtml() {
389
+ if (resolveJsType(DOMParser) === "undefined")
390
+ throw new Error(
391
+ "The native `DOMParser` global which the `Html` deserializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead."
392
+ );
393
+ return (html) => new DOMParser().parseFromString(html, "text/html");
394
+ }
395
+ function flattenNestedBlocks(blocks2) {
396
+ let depth = 0;
397
+ const flattened = [], traverse = (nodes) => {
398
+ const toRemove = [];
399
+ nodes.forEach((node) => {
400
+ depth === 0 && flattened.push(node), types.isPortableTextTextBlock(node) && (depth > 0 && (toRemove.push(node), flattened.push(node)), depth++, traverse(node.children)), node._type === "__block" && (toRemove.push(node), flattened.push(node.block));
401
+ }), toRemove.forEach((node) => {
402
+ nodes.splice(nodes.indexOf(node), 1);
403
+ }), depth--;
404
+ };
405
+ return traverse(blocks2), flattened;
406
+ }
407
+ function nextSpan(block, index) {
408
+ const next = block.children[index + 1];
409
+ return next && next._type === "span" ? next : null;
410
+ }
411
+ function prevSpan(block, index) {
412
+ const prev = block.children[index - 1];
413
+ return prev && prev._type === "span" ? prev : null;
414
+ }
415
+ function isWhiteSpaceChar(text) {
416
+ return ["\xA0", " "].includes(text);
417
+ }
418
+ function trimWhitespace(blocks2) {
419
+ return blocks2.forEach((block) => {
420
+ types.isPortableTextTextBlock(block) && block.children.forEach((child, index) => {
421
+ if (!isMinimalSpan(child))
422
+ return;
423
+ const nextChild = nextSpan(block, index), prevChild = prevSpan(block, index);
424
+ 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 && isEqual__default.default(prevChild.marks, child.marks) && isWhiteSpaceChar(child.text) ? (prevChild.text += " ", block.children.splice(index, 1)) : nextChild && isEqual__default.default(nextChild.marks, child.marks) && isWhiteSpaceChar(child.text) && (nextChild.text = ` ${nextChild.text}`, block.children.splice(index, 1));
425
+ });
426
+ }), blocks2;
427
+ }
428
+ function ensureRootIsBlocks(blocks2) {
429
+ return blocks2.reduce((memo, node, i, original) => {
430
+ if (node._type === "block")
431
+ return memo.push(node), memo;
432
+ if (node._type === "__block")
433
+ return memo.push(node.block), memo;
434
+ const lastBlock = memo[memo.length - 1];
435
+ if (i > 0 && !types.isPortableTextTextBlock(original[i - 1]) && types.isPortableTextTextBlock(lastBlock))
436
+ return lastBlock.children.push(node), memo;
437
+ const block = {
438
+ ...DEFAULT_BLOCK,
439
+ children: [node]
440
+ };
441
+ return memo.push(block), memo;
442
+ }, []);
443
+ }
444
+ function isNodeList(node) {
445
+ return Object.prototype.toString.call(node) === "[object NodeList]";
446
+ }
447
+ function isMinimalSpan(node) {
448
+ return node._type === "span";
449
+ }
450
+ function isMinimalBlock(node) {
451
+ return node._type === "block";
452
+ }
453
+ function isPlaceholderDecorator(node) {
454
+ return node._type === "__decorator";
455
+ }
456
+ function isPlaceholderAnnotation(node) {
457
+ return node._type === "__annotation";
458
+ }
459
+ function isElement(node) {
460
+ return node.nodeType === 1;
461
+ }
462
+ function normalizeWhitespace(rootNode) {
463
+ let emptyBlockCount = 0, lastParent = null;
464
+ const nodesToRemove = [];
465
+ for (let child = rootNode.firstChild; child; child = child.nextSibling) {
466
+ if (!isElement(child)) {
467
+ normalizeWhitespace(child), emptyBlockCount = 0;
468
+ continue;
469
+ }
470
+ const elm = child;
471
+ isWhitespaceBlock(elm) ? (lastParent && elm.parentElement === lastParent ? (emptyBlockCount++, emptyBlockCount > 1 && nodesToRemove.push(elm)) : emptyBlockCount = 1, lastParent = elm.parentElement) : (normalizeWhitespace(child), emptyBlockCount = 0);
472
+ }
473
+ nodesToRemove.forEach((node) => node.parentElement?.removeChild(node));
474
+ }
475
+ function removeAllWhitespace(rootNode) {
476
+ const nodesToRemove = [];
477
+ function collectNodesToRemove(currentNode) {
478
+ if (isElement(currentNode)) {
479
+ const elm = currentNode;
480
+ if (tagName(elm) === "br" && (tagName(elm.nextElementSibling) === "p" || tagName(elm.previousElementSibling) === "p")) {
481
+ nodesToRemove.push(elm);
482
+ return;
483
+ }
484
+ if ((tagName(elm) === "p" || tagName(elm) === "br") && elm?.firstChild?.textContent?.trim() === "") {
485
+ nodesToRemove.push(elm);
486
+ return;
487
+ }
488
+ for (let child = elm.firstChild; child; child = child.nextSibling)
489
+ collectNodesToRemove(child);
490
+ }
491
+ }
492
+ collectNodesToRemove(rootNode), nodesToRemove.forEach((node) => node.parentElement?.removeChild(node));
493
+ }
494
+ function isWhitespaceBlock(elm) {
495
+ return ["p", "br"].includes(tagName(elm) || "") && !elm.textContent?.trim();
496
+ }
497
+ const LIST_CONTAINER_TAGS = Object.keys(HTML_LIST_CONTAINER_TAGS);
498
+ function isEmphasis$1(el) {
499
+ const style = isElement(el) && el.getAttribute("style");
500
+ return /font-style\s*:\s*italic/.test(style || "");
501
+ }
502
+ function isStrong$1(el) {
503
+ const style = isElement(el) && el.getAttribute("style");
504
+ return /font-weight\s*:\s*700/.test(style || "");
505
+ }
506
+ function isUnderline$1(el) {
507
+ if (!isElement(el) || tagName(el.parentNode) === "a")
508
+ return !1;
509
+ const style = isElement(el) && el.getAttribute("style");
510
+ return /text-decoration\s*:\s*underline/.test(style || "");
511
+ }
512
+ function isStrikethrough(el) {
513
+ const style = isElement(el) && el.getAttribute("style");
514
+ return /text-decoration\s*:\s*(?:.*line-through.*;)/.test(style || "");
515
+ }
516
+ function isGoogleDocs(el) {
517
+ return isElement(el) && !!el.getAttribute("data-is-google-docs");
518
+ }
519
+ function isRootNode(el) {
520
+ return isElement(el) && !!el.getAttribute("data-is-root-node");
521
+ }
522
+ function getListItemStyle$1(el) {
523
+ const parentTag = tagName(el.parentNode);
524
+ if (!(parentTag && !LIST_CONTAINER_TAGS.includes(parentTag)))
525
+ return tagName(el.parentNode) === "ul" ? "bullet" : "number";
526
+ }
527
+ function getListItemLevel$1(el) {
528
+ let level = 0;
529
+ if (tagName(el) === "li") {
530
+ let parentNode = el.parentNode;
531
+ for (; parentNode; ) {
532
+ const parentTag = tagName(parentNode);
533
+ parentTag && LIST_CONTAINER_TAGS.includes(parentTag) && level++, parentNode = parentNode.parentNode;
534
+ }
535
+ } else
536
+ level = 1;
537
+ return level;
538
+ }
539
+ const blocks = {
540
+ ...HTML_BLOCK_TAGS,
541
+ ...HTML_HEADER_TAGS
542
+ };
543
+ function getBlockStyle(el, enabledBlockStyles) {
544
+ const childTag = tagName(el.firstChild), block = childTag && blocks[childTag];
545
+ return block && enabledBlockStyles.includes(block.style) ? block.style : BLOCK_DEFAULT_STYLE;
546
+ }
547
+ function createGDocsRules(_blockContentType, options) {
548
+ return [
549
+ {
550
+ deserialize(el) {
551
+ if (isElement(el) && tagName(el) === "span" && isGoogleDocs(el)) {
552
+ const span = {
553
+ ...DEFAULT_SPAN,
554
+ marks: [],
555
+ text: el.textContent
556
+ };
557
+ 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;
558
+ }
559
+ }
560
+ },
561
+ {
562
+ deserialize(el, next) {
563
+ if (tagName(el) === "li" && isGoogleDocs(el))
564
+ return {
565
+ ...DEFAULT_BLOCK,
566
+ listItem: getListItemStyle$1(el),
567
+ level: getListItemLevel$1(el),
568
+ style: getBlockStyle(el, options.enabledBlockStyles),
569
+ children: next(el.firstChild?.childNodes || [])
570
+ };
571
+ }
572
+ },
573
+ {
574
+ deserialize(el) {
575
+ if (tagName(el) === "br" && isGoogleDocs(el) && isElement(el) && el.classList.contains("apple-interchange-newline"))
576
+ return {
577
+ ...DEFAULT_SPAN,
578
+ text: ""
579
+ };
580
+ if (tagName(el) === "br" && isGoogleDocs(el) && isElement(el) && el?.parentNode?.textContent === "")
581
+ return {
582
+ ...DEFAULT_SPAN,
583
+ text: ""
584
+ };
585
+ if (tagName(el) === "br" && isGoogleDocs(el) && isElement(el) && isRootNode(el))
586
+ return {
587
+ ...DEFAULT_SPAN,
588
+ text: ""
589
+ };
590
+ }
591
+ }
592
+ ];
593
+ }
594
+ function whatwgRNG(length = 16) {
595
+ const rnds8 = new Uint8Array(length);
596
+ return getRandomValues__default.default(rnds8), rnds8;
597
+ }
598
+ const byteToHex = [];
599
+ for (let i = 0; i < 256; ++i)
600
+ byteToHex[i] = (i + 256).toString(16).slice(1);
601
+ function randomKey(length) {
602
+ return whatwgRNG(length).reduce((str, n) => str + byteToHex[n], "").slice(0, length);
603
+ }
604
+ function resolveListItem(listNodeTagName, enabledListTypes) {
605
+ if (listNodeTagName === "ul" && enabledListTypes.includes("bullet"))
606
+ return "bullet";
607
+ if (listNodeTagName === "ol" && enabledListTypes.includes("number"))
608
+ return "number";
609
+ }
610
+ function createHTMLRules(_blockContentType, options) {
611
+ return [
612
+ // Text nodes
613
+ {
614
+ deserialize(el) {
615
+ if (tagName(el) === "pre")
616
+ return;
617
+ const isValidText = (el.nodeType === 3 && (el.textContent || "").replace(/[\r\n]/g, " ").replace(/\s\s+/g, " ") === " " && el.nextSibling && el.nextSibling.nodeType !== 3 && el.previousSibling && el.previousSibling.nodeType !== 3 || el.textContent !== " ") && tagName(el.parentNode) !== "body";
618
+ if (el.nodeName === "#text" && isValidText)
619
+ return {
620
+ ...DEFAULT_SPAN,
621
+ marks: [],
622
+ text: (el.textContent || "").replace(/\s\s+/g, " ")
623
+ };
624
+ }
625
+ },
626
+ // Pre element
627
+ {
628
+ deserialize(el) {
629
+ if (tagName(el) !== "pre")
630
+ return;
631
+ const isCodeEnabled = options.enabledBlockStyles.includes("code");
632
+ return {
633
+ _type: "block",
634
+ style: "normal",
635
+ markDefs: [],
636
+ children: [
637
+ {
638
+ ...DEFAULT_SPAN,
639
+ marks: isCodeEnabled ? ["code"] : [],
640
+ text: el.textContent || ""
641
+ }
642
+ ]
643
+ };
644
+ }
645
+ },
646
+ // Blockquote element
647
+ {
648
+ deserialize(el, next) {
649
+ if (tagName(el) !== "blockquote")
650
+ return;
651
+ const blocks2 = {
652
+ ...HTML_BLOCK_TAGS,
653
+ ...HTML_HEADER_TAGS
654
+ };
655
+ delete blocks2.blockquote;
656
+ const children = [];
657
+ return el.childNodes.forEach((node, index) => {
658
+ if (node.nodeType === 1 && Object.keys(blocks2).includes(
659
+ node.localName.toLowerCase()
660
+ )) {
661
+ if (!el.ownerDocument)
662
+ return;
663
+ const span = el.ownerDocument.createElement("span");
664
+ span.appendChild(el.ownerDocument.createTextNode("\r")), node.childNodes.forEach((cn) => {
665
+ span.appendChild(cn.cloneNode(!0));
666
+ }), index !== el.childNodes.length && span.appendChild(el.ownerDocument.createTextNode("\r")), children.push(span);
667
+ } else
668
+ children.push(node);
669
+ }), {
670
+ _type: "block",
671
+ style: "blockquote",
672
+ markDefs: [],
673
+ children: next(children)
674
+ };
675
+ }
676
+ },
677
+ // Block elements
678
+ {
679
+ deserialize(el, next) {
680
+ const blocks2 = {
681
+ ...HTML_BLOCK_TAGS,
682
+ ...HTML_HEADER_TAGS
683
+ }, tag = tagName(el);
684
+ let block = tag ? blocks2[tag] : void 0;
685
+ if (block)
686
+ return el.parentNode && tagName(el.parentNode) === "li" ? next(el.childNodes) : (options.enabledBlockStyles.includes(block.style) || (block = DEFAULT_BLOCK), {
687
+ ...block,
688
+ children: next(el.childNodes)
689
+ });
690
+ }
691
+ },
692
+ // Ignore span tags
693
+ {
694
+ deserialize(el, next) {
695
+ const tag = tagName(el);
696
+ if (!(!tag || !(tag in HTML_SPAN_TAGS)))
697
+ return next(el.childNodes);
698
+ }
699
+ },
700
+ // Ignore div tags
701
+ {
702
+ deserialize(el, next) {
703
+ if (tagName(el) === "div")
704
+ return next(el.childNodes);
705
+ }
706
+ },
707
+ // Ignore list containers
708
+ {
709
+ deserialize(el, next) {
710
+ const tag = tagName(el);
711
+ if (!(!tag || !(tag in HTML_LIST_CONTAINER_TAGS)))
712
+ return next(el.childNodes);
713
+ }
714
+ },
715
+ // Deal with br's
716
+ {
717
+ deserialize(el) {
718
+ if (tagName(el) === "br")
719
+ return {
720
+ ...DEFAULT_SPAN,
721
+ text: `
722
+ `
723
+ };
724
+ }
725
+ },
726
+ // Deal with list items
727
+ {
728
+ deserialize(el, next, block) {
729
+ const tag = tagName(el), listItem = tag ? HTML_LIST_ITEM_TAGS[tag] : void 0, parentTag = tagName(el.parentNode) || "";
730
+ if (!listItem || !el.parentNode || !HTML_LIST_CONTAINER_TAGS[parentTag])
731
+ return;
732
+ const enabledListItem = resolveListItem(
733
+ parentTag,
734
+ options.enabledListTypes
735
+ );
736
+ return enabledListItem ? (listItem.listItem = enabledListItem, {
737
+ ...listItem,
738
+ children: next(el.childNodes)
739
+ }) : block({ _type: "block", children: next(el.childNodes) });
740
+ }
741
+ },
742
+ // Deal with decorators - this is a limited set of known html elements that we know how to deserialize
743
+ {
744
+ deserialize(el, next) {
745
+ const decorator = HTML_DECORATOR_TAGS[tagName(el) || ""];
746
+ if (!(!decorator || !options.enabledSpanDecorators.includes(decorator)))
747
+ return {
748
+ _type: "__decorator",
749
+ name: decorator,
750
+ children: next(el.childNodes)
751
+ };
752
+ }
753
+ },
754
+ // Special case for hyperlinks, add annotation (if allowed by schema),
755
+ // If not supported just write out the link text and href in plain text.
756
+ {
757
+ deserialize(el, next) {
758
+ if (tagName(el) !== "a")
759
+ return;
760
+ const linkEnabled = options.enabledBlockAnnotations.includes("link"), href = isElement(el) && el.getAttribute("href");
761
+ if (!href)
762
+ return next(el.childNodes);
763
+ let markDef;
764
+ return linkEnabled ? (markDef = {
765
+ _key: randomKey(12),
766
+ _type: "link",
767
+ href
768
+ }, {
769
+ _type: "__annotation",
770
+ markDef,
771
+ children: next(el.childNodes)
772
+ }) : el.appendChild(el.ownerDocument.createTextNode(` (${href})`)) && next(el.childNodes);
773
+ }
774
+ }
775
+ ];
776
+ }
777
+ function isEmphasis(el) {
778
+ const style = isElement(el) && el.getAttribute("style");
779
+ return /font-style:italic/.test(style || "");
780
+ }
781
+ function isStrong(el) {
782
+ const style = isElement(el) && el.getAttribute("style");
783
+ return /font-weight:700/.test(style || "") || /font-weight:600/.test(style || "");
784
+ }
785
+ function isUnderline(el) {
786
+ const style = isElement(el) && el.getAttribute("style");
787
+ return /text-decoration:underline/.test(style || "");
788
+ }
789
+ function isNotion(el) {
790
+ return isElement(el) && !!el.getAttribute("data-is-notion");
791
+ }
792
+ function createNotionRules(_blockContentType) {
793
+ return [
794
+ {
795
+ deserialize(el) {
796
+ if (isElement(el) && tagName(el) === "span" && isNotion(el)) {
797
+ const span = {
798
+ ...DEFAULT_SPAN,
799
+ marks: [],
800
+ text: el.textContent
801
+ };
802
+ return isStrong(el) && span.marks.push("strong"), isUnderline(el) && span.marks.push("underline"), isEmphasis(el) && span.marks.push("em"), span;
803
+ }
804
+ }
805
+ }
806
+ ];
807
+ }
808
+ function getListItemStyle(el) {
809
+ const style = isElement(el) && el.getAttribute("style");
810
+ if (style && style.match(/lfo\d+/))
811
+ return style.match("lfo1") ? "bullet" : "number";
812
+ }
813
+ function getListItemLevel(el) {
814
+ const style = isElement(el) && el.getAttribute("style");
815
+ if (!style)
816
+ return;
817
+ const levelMatch = style.match(/level\d+/);
818
+ if (!levelMatch)
819
+ return;
820
+ const [level] = levelMatch[0].match(/\d/) || [];
821
+ return (level ? Number.parseInt(level, 10) : 1) || 1;
822
+ }
823
+ function isWordListElement(el) {
824
+ return isElement(el) && el.className ? el.className === "MsoListParagraphCxSpFirst" || el.className === "MsoListParagraphCxSpMiddle" || el.className === "MsoListParagraphCxSpLast" : !1;
825
+ }
826
+ function createWordRules() {
827
+ return [
828
+ {
829
+ deserialize(el, next) {
830
+ if (tagName(el) === "p" && isWordListElement(el))
831
+ return {
832
+ ...DEFAULT_BLOCK,
833
+ listItem: getListItemStyle(el),
834
+ level: getListItemLevel(el),
835
+ style: BLOCK_DEFAULT_STYLE,
836
+ children: next(el.childNodes)
837
+ };
838
+ }
839
+ }
840
+ ];
841
+ }
842
+ function createRules(blockContentType, options) {
843
+ return [
844
+ ...createWordRules(),
845
+ ...createNotionRules(),
846
+ ...createGDocsRules(blockContentType, options),
847
+ ...createHTMLRules(blockContentType, options)
848
+ ];
849
+ }
850
+ class HtmlDeserializer {
851
+ blockContentType;
852
+ rules;
853
+ parseHtml;
854
+ _markDefs = [];
855
+ /**
856
+ * Create a new serializer respecting a Sanity block content type's schema
857
+ *
858
+ * @param blockContentType - Schema type for array containing _at least_ a block child type
859
+ * @param options - Options for the deserialization process
860
+ */
861
+ constructor(blockContentType, options = {}) {
862
+ const { rules = [], unstable_whitespaceOnPasteMode = "preserve" } = options;
863
+ if (!blockContentType)
864
+ throw new Error("Parameter 'blockContentType' is required");
865
+ const standardRules = createRules(
866
+ blockContentType,
867
+ createRuleOptions(blockContentType)
868
+ );
869
+ this.rules = [...rules, ...standardRules];
870
+ const parseHtml = options.parseHtml || defaultParseHtml();
871
+ this.blockContentType = blockContentType, this.parseHtml = (html) => preprocess(html, parseHtml, { unstable_whitespaceOnPasteMode }).body;
872
+ }
873
+ /**
874
+ * Deserialize HTML.
875
+ *
876
+ * @param html - The HTML to deserialize, as a string
877
+ * @returns Array of blocks - either portable text blocks or other allowed blocks
878
+ */
879
+ deserialize = (html) => {
880
+ this._markDefs = [];
881
+ const { parseHtml } = this, fragment = parseHtml(html), children = Array.from(fragment.childNodes), blocks2 = trimWhitespace(
882
+ flattenNestedBlocks(
883
+ ensureRootIsBlocks(this.deserializeElements(children))
884
+ )
885
+ );
886
+ this._markDefs.length > 0 && blocks2.filter(
887
+ (block) => block._type === "block"
888
+ ).forEach((block) => {
889
+ block.markDefs = block.markDefs || [], block.markDefs = block.markDefs.concat(
890
+ this._markDefs.filter((def) => flatten__default.default(
891
+ block.children.map((child) => child.marks || [])
892
+ ).includes(def._key))
893
+ );
894
+ });
895
+ const type = this.blockContentType.of.find(findBlockType);
896
+ return type ? blocks2.map((block) => (block._type === "block" && (block._type = type.name), block)) : blocks2;
897
+ };
898
+ /**
899
+ * Deserialize an array of DOM elements.
900
+ *
901
+ * @param elements - Array of DOM elements to deserialize
902
+ * @returns
903
+ */
904
+ deserializeElements = (elements = []) => {
905
+ let nodes = [];
906
+ return elements.forEach((element) => {
907
+ nodes = nodes.concat(this.deserializeElement(element));
908
+ }), nodes;
909
+ };
910
+ /**
911
+ * Deserialize a DOM element
912
+ *
913
+ * @param element - Deserialize a DOM element
914
+ * @returns
915
+ */
916
+ deserializeElement = (element) => {
917
+ const next = (elements) => {
918
+ if (isNodeList(elements))
919
+ return this.deserializeElements(Array.from(elements));
920
+ if (Array.isArray(elements))
921
+ return this.deserializeElements(elements);
922
+ if (elements)
923
+ return this.deserializeElement(elements);
924
+ }, block = (props) => ({
925
+ _type: "__block",
926
+ block: props
927
+ });
928
+ let node;
929
+ for (let i = 0; i < this.rules.length; i++) {
930
+ const rule = this.rules[i];
931
+ if (!rule.deserialize)
932
+ continue;
933
+ const ret = rule.deserialize(element, next, block), type = resolveJsType(ret);
934
+ if (type !== "array" && type !== "object" && type !== "null" && type !== "undefined")
935
+ throw new Error(
936
+ `A rule returned an invalid deserialized representation: "${node}".`
937
+ );
938
+ if (ret !== void 0) {
939
+ {
940
+ if (ret === null)
941
+ throw new Error("Deserializer rule returned `null`");
942
+ Array.isArray(ret) ? node = ret : isPlaceholderDecorator(ret) ? node = this.deserializeDecorator(ret) : isPlaceholderAnnotation(ret) ? node = this.deserializeAnnotation(ret) : node = ret;
943
+ }
944
+ if (ret && !Array.isArray(ret) && isMinimalBlock(ret) && "listItem" in ret) {
945
+ let parent = element.parentNode?.parentNode;
946
+ for (; parent && tagName(parent) === "li"; )
947
+ parent = parent.parentNode?.parentNode, ret.level = ret.level ? ret.level + 1 : 1;
948
+ }
949
+ ret && !Array.isArray(ret) && isMinimalBlock(ret) && ret.style === "blockquote" && ret.children.forEach((child, index) => {
950
+ isMinimalSpan(child) && child.text === "\r" && (child.text = `
951
+
952
+ `, (index === 0 || index === ret.children.length - 1) && ret.children.splice(index, 1));
953
+ });
954
+ break;
955
+ }
956
+ }
957
+ return node || next(element.childNodes) || [];
958
+ };
959
+ /**
960
+ * Deserialize a `__decorator` type
961
+ * (an internal made up type to process decorators exclusively)
962
+ *
963
+ * @param decorator -
964
+ * @returns array of ...
965
+ */
966
+ deserializeDecorator = (decorator) => {
967
+ const { name } = decorator, applyDecorator = (node) => {
968
+ if (isPlaceholderDecorator(node))
969
+ return this.deserializeDecorator(node);
970
+ if (isMinimalSpan(node))
971
+ node.marks = node.marks || [], node.text.trim() && node.marks.unshift(name);
972
+ else if ("children" in node && Array.isArray(node.children)) {
973
+ const block = node;
974
+ block.children = block.children.map(applyDecorator);
975
+ }
976
+ return node;
977
+ };
978
+ return decorator.children.reduce((children, node) => {
979
+ const ret = applyDecorator(node);
980
+ return Array.isArray(ret) ? children.concat(ret) : (children.push(ret), children);
981
+ }, []);
982
+ };
983
+ /**
984
+ * Deserialize a `__annotation` object.
985
+ * (an internal made up type to process annotations exclusively)
986
+ *
987
+ * @param annotation -
988
+ * @returns Array of...
989
+ */
990
+ deserializeAnnotation = (annotation) => {
991
+ const { markDef } = annotation;
992
+ this._markDefs.push(markDef);
993
+ const applyAnnotation = (node) => {
994
+ if (isPlaceholderAnnotation(node))
995
+ return this.deserializeAnnotation(node);
996
+ if (isMinimalSpan(node))
997
+ node.marks = node.marks || [], node.text.trim() && node.marks.unshift(markDef._key);
998
+ else if ("children" in node && Array.isArray(node.children)) {
999
+ const block = node;
1000
+ block.children = block.children.map(applyAnnotation);
1001
+ }
1002
+ return node;
1003
+ };
1004
+ return annotation.children.reduce((children, node) => {
1005
+ const ret = applyAnnotation(node);
1006
+ return Array.isArray(ret) ? children.concat(ret) : (children.push(ret), children);
1007
+ }, []);
1008
+ };
1009
+ }
1010
+ function normalizeBlock(node, options = {}) {
1011
+ if (node._type !== (options.blockTypeName || "block"))
1012
+ return "_key" in node ? node : { ...node, _key: randomKey(12) };
1013
+ const block = {
1014
+ _key: randomKey(12),
1015
+ children: [],
1016
+ markDefs: [],
1017
+ ...node
1018
+ }, lastChild = block.children[block.children.length - 1];
1019
+ if (!lastChild)
1020
+ return block.children = [
1021
+ {
1022
+ _type: "span",
1023
+ _key: `${block._key}0`,
1024
+ text: "",
1025
+ marks: []
1026
+ }
1027
+ ], block;
1028
+ const usedMarkDefs = [], allowedDecorators = options.allowedDecorators && Array.isArray(options.allowedDecorators) ? options.allowedDecorators : !1;
1029
+ return block.children = block.children.reduce(
1030
+ (acc, child) => {
1031
+ const previousChild = acc[acc.length - 1];
1032
+ return previousChild && types.isPortableTextSpan(child) && types.isPortableTextSpan(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);
1033
+ },
1034
+ []
1035
+ ).map((child, index) => {
1036
+ if (!child)
1037
+ throw new Error("missing child");
1038
+ return child._key = `${block._key}${index}`, types.isPortableTextSpan(child) && (child.marks ? allowedDecorators && (child.marks = child.marks.filter((mark) => {
1039
+ const isAllowed = allowedDecorators.includes(mark), isUsed = block.markDefs?.some((def) => def._key === mark);
1040
+ return isAllowed || isUsed;
1041
+ })) : child.marks = [], usedMarkDefs.push(...child.marks)), child;
1042
+ }), block.markDefs = (block.markDefs || []).filter(
1043
+ (markDef) => usedMarkDefs.includes(markDef._key)
1044
+ ), block;
1045
+ }
1046
+ function htmlToBlocks(html, blockContentType, options = {}) {
1047
+ return new HtmlDeserializer(blockContentType, options).deserialize(html).map((block) => normalizeBlock(block));
1048
+ }
1049
+ function getBlockContentFeatures(blockContentType) {
1050
+ return blockContentFeatures(blockContentType);
1051
+ }
1052
+ exports.getBlockContentFeatures = getBlockContentFeatures;
1053
+ exports.htmlToBlocks = htmlToBlocks;
1054
+ exports.normalizeBlock = normalizeBlock;
1055
+ exports.randomKey = randomKey;
1056
+ //# sourceMappingURL=index.cjs.map