@portabletext/block-tools 3.3.3 → 3.4.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,20 +1,9 @@
1
1
  import { sanitySchemaToPortableTextSchema } from "@portabletext/sanity-bridge";
2
+ import { isTextBlock, isSpan } from "@portabletext/schema";
2
3
  import flatten from "lodash/flatten.js";
3
4
  import getRandomValues from "get-random-values-esm";
4
5
  import isEqual from "lodash/isEqual.js";
5
6
  import uniq from "lodash/uniq.js";
6
- function isArbitraryTypedObject(object) {
7
- return isRecord(object) && typeof object._type == "string";
8
- }
9
- function isRecord(value) {
10
- return !!value && (typeof value == "object" || typeof value == "function");
11
- }
12
- function isTextBlock(schema, block) {
13
- return !(!isArbitraryTypedObject(block) || block._type !== schema.block.name || !Array.isArray(block.children));
14
- }
15
- function isSpan(schema, child) {
16
- return !(!isArbitraryTypedObject(child) || child._type !== schema.span.name || typeof child.text != "string");
17
- }
18
7
  function keyGenerator() {
19
8
  return randomKey(12);
20
9
  }
@@ -46,6 +35,81 @@ function resolveJsType(val) {
46
35
  }
47
36
  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;
48
37
  }
38
+ function isArbitraryTypedObject(object) {
39
+ return isRecord(object) && typeof object._type == "string";
40
+ }
41
+ function isRecord(value) {
42
+ return !!value && (typeof value == "object" || typeof value == "function");
43
+ }
44
+ function flattenNestedBlocks(context, blocks2) {
45
+ return blocks2.flatMap((block) => {
46
+ if (isBlockContainer(block))
47
+ return flattenNestedBlocks(context, [block.block]);
48
+ if (isTextBlock(context, block)) {
49
+ const hasBlockObjects = block.children.some((child) => context.schema.blockObjects.some(
50
+ (blockObject) => blockObject.name === child._type
51
+ )), hasBlocks = block.children.some(
52
+ (child) => child._type === "__block" || child._type === "block"
53
+ );
54
+ if (hasBlockObjects || hasBlocks) {
55
+ const splitChildren = getSplitChildren(context, block);
56
+ return splitChildren.length === 1 && splitChildren[0].type === "children" && isEqual(splitChildren[0].children, block.children) ? [block] : splitChildren.flatMap((slice) => slice.type === "block object" ? [slice.block] : slice.type === "block" ? flattenNestedBlocks(context, [
57
+ slice.block
58
+ ]) : slice.children.length > 0 ? slice.children.every(
59
+ (child) => isSpan(context, child) && child.text.trim() === ""
60
+ ) ? [] : flattenNestedBlocks(context, [
61
+ {
62
+ ...block,
63
+ children: slice.children
64
+ }
65
+ ]) : []);
66
+ }
67
+ return [block];
68
+ }
69
+ return [block];
70
+ });
71
+ }
72
+ function isBlockContainer(block) {
73
+ return block._type === "__block" && isArbitraryTypedObject(block.block);
74
+ }
75
+ function getSplitChildren(context, block) {
76
+ return block.children.reduce(
77
+ (slices, child) => {
78
+ const knownInlineObject = context.schema.inlineObjects.some(
79
+ (inlineObject) => inlineObject.name === child._type
80
+ ), knownBlockObject = context.schema.blockObjects.some(
81
+ (blockObject) => blockObject.name === child._type
82
+ ), lastSlice = slices.pop();
83
+ return !isSpan(context, child) && !knownInlineObject && knownBlockObject ? [
84
+ ...slices,
85
+ ...lastSlice ? [lastSlice] : [],
86
+ { type: "block object", block: child }
87
+ ] : child._type === "__block" ? [
88
+ ...slices,
89
+ ...lastSlice ? [lastSlice] : [],
90
+ {
91
+ type: "block object",
92
+ block: child.block
93
+ }
94
+ ] : child._type === "block" ? [
95
+ ...slices,
96
+ ...lastSlice ? [lastSlice] : [],
97
+ { type: "block", block: child }
98
+ ] : lastSlice && lastSlice.type === "children" ? [
99
+ ...slices,
100
+ {
101
+ type: "children",
102
+ children: [...lastSlice.children, child]
103
+ }
104
+ ] : [
105
+ ...slices,
106
+ ...lastSlice ? [lastSlice] : [],
107
+ { type: "children", children: [child] }
108
+ ];
109
+ },
110
+ []
111
+ );
112
+ }
49
113
  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 };
50
114
  new Array(4).fill(String.fromCodePoint(c[0])).join("");
51
115
  Object.fromEntries(Object.entries(c).map((t) => t.reverse()));
@@ -309,73 +373,6 @@ function defaultParseHtml() {
309
373
  );
310
374
  return (html) => new DOMParser().parseFromString(html, "text/html");
311
375
  }
312
- function flattenNestedBlocks(context, blocks2) {
313
- let depth = 0;
314
- const flattened = [], traverse = (nodes) => {
315
- const toRemove = [];
316
- nodes.forEach((node) => {
317
- if (depth === 0)
318
- if (context.schema.blockObjects.length > 0 && isTextBlock(context.schema, node)) {
319
- const hasBlockObjects = node.children.some((child) => context.schema.blockObjects.some(
320
- (blockObject) => blockObject.name === child._type
321
- )), hasBlocks = node.children.some(
322
- (child) => child._type === "__block"
323
- );
324
- if (hasBlockObjects || hasBlocks) {
325
- node.children.reduce(
326
- (slices, child) => {
327
- const knownInlineObject = context.schema.inlineObjects.some(
328
- (inlineObject) => inlineObject.name === child._type
329
- ), knownBlockObject = context.schema.blockObjects.some(
330
- (blockObject) => blockObject.name === child._type
331
- ), lastSlice = slices.pop();
332
- return !isSpan(context.schema, child) && !knownInlineObject && knownBlockObject ? [
333
- ...slices,
334
- ...lastSlice ? [lastSlice] : [],
335
- { type: "block object", block: child }
336
- ] : child._type === "__block" ? [
337
- ...slices,
338
- ...lastSlice ? [lastSlice] : [],
339
- {
340
- type: "block object",
341
- block: child.block
342
- }
343
- ] : lastSlice && lastSlice.type === "children" ? [
344
- ...slices,
345
- {
346
- type: "children",
347
- children: [...lastSlice.children, child]
348
- }
349
- ] : [
350
- ...slices,
351
- ...lastSlice ? [lastSlice] : [],
352
- { type: "children", children: [child] }
353
- ];
354
- },
355
- []
356
- ).forEach((slice) => {
357
- if (slice.type === "block object")
358
- flattened.push(slice.block);
359
- else if (slice.children.length > 0) {
360
- const newBlock = {
361
- ...node,
362
- children: slice.children
363
- };
364
- flattened.push(newBlock);
365
- }
366
- });
367
- return;
368
- } else
369
- flattened.push(node);
370
- } else
371
- flattened.push(node);
372
- isTextBlock(context.schema, node) && (depth > 0 && (toRemove.push(node), flattened.push(node)), depth++, traverse(node.children)), node._type === "__block" && (toRemove.push(node), flattened.push(node.block));
373
- }), toRemove.forEach((node) => {
374
- nodes.splice(nodes.indexOf(node), 1);
375
- }), depth--;
376
- };
377
- return traverse(blocks2), flattened;
378
- }
379
376
  function nextSpan(block, index) {
380
377
  const next = block.children[index + 1];
381
378
  return next && next._type === "span" ? next : null;
@@ -389,7 +386,7 @@ function isWhiteSpaceChar(text) {
389
386
  }
390
387
  function trimWhitespace(schema, blocks2) {
391
388
  return blocks2.forEach((block) => {
392
- isTextBlock(schema, block) && block.children.forEach((child, index) => {
389
+ isTextBlock({ schema }, block) && block.children.forEach((child, index) => {
393
390
  if (!isMinimalSpan(child))
394
391
  return;
395
392
  const nextChild = nextSpan(block, index), prevChild = prevSpan(block, index);
@@ -397,20 +394,20 @@ function trimWhitespace(schema, blocks2) {
397
394
  });
398
395
  }), blocks2;
399
396
  }
400
- function ensureRootIsBlocks(schema, blocks2) {
401
- return blocks2.reduce((memo, node, i, original) => {
397
+ function ensureRootIsBlocks(schema, objects) {
398
+ return objects.reduce((blocks2, node, i, original) => {
402
399
  if (node._type === "block")
403
- return memo.push(node), memo;
400
+ return blocks2.push(node), blocks2;
404
401
  if (node._type === "__block")
405
- return memo.push(node.block), memo;
406
- const lastBlock = memo[memo.length - 1];
407
- if (i > 0 && !isTextBlock(schema, original[i - 1]) && isTextBlock(schema, lastBlock))
408
- return lastBlock.children.push(node), memo;
402
+ return blocks2.push(node.block), blocks2;
403
+ const lastBlock = blocks2[blocks2.length - 1];
404
+ if (i > 0 && !isTextBlock({ schema }, original[i - 1]) && isTextBlock({ schema }, lastBlock))
405
+ return lastBlock.children.push(node), blocks2;
409
406
  const block = {
410
407
  ...DEFAULT_BLOCK,
411
408
  children: [node]
412
409
  };
413
- return memo.push(block), memo;
410
+ return blocks2.push(block), blocks2;
414
411
  }, []);
415
412
  }
416
413
  function isNodeList(node) {
@@ -442,7 +439,9 @@ function normalizeWhitespace(rootNode) {
442
439
  const elm = child;
443
440
  isWhitespaceBlock(elm) ? (lastParent && elm.parentElement === lastParent ? (emptyBlockCount++, emptyBlockCount > 1 && nodesToRemove.push(elm)) : emptyBlockCount = 1, lastParent = elm.parentElement) : (normalizeWhitespace(child), emptyBlockCount = 0);
444
441
  }
445
- nodesToRemove.forEach((node) => node.parentElement?.removeChild(node));
442
+ nodesToRemove.forEach((node) => {
443
+ node.parentElement?.removeChild(node);
444
+ });
446
445
  }
447
446
  function removeAllWhitespace(rootNode) {
448
447
  const nodesToRemove = [];
@@ -461,7 +460,9 @@ function removeAllWhitespace(rootNode) {
461
460
  collectNodesToRemove(child);
462
461
  }
463
462
  }
464
- collectNodesToRemove(rootNode), nodesToRemove.forEach((node) => node.parentElement?.removeChild(node));
463
+ collectNodesToRemove(rootNode), nodesToRemove.forEach((node) => {
464
+ node.parentElement?.removeChild(node);
465
+ });
465
466
  }
466
467
  function isWhitespaceBlock(elm) {
467
468
  return ["p", "br"].includes(tagName(elm) || "") && !elm.textContent?.trim();
@@ -738,6 +739,15 @@ function createHTMLRules(schema, options) {
738
739
  } : el.appendChild(el.ownerDocument.createTextNode(` (${href})`)) && next(el.childNodes) : next(el.childNodes);
739
740
  }
740
741
  },
742
+ {
743
+ deserialize(el, next) {
744
+ if (isElement(el) && (tagName(el) === "td" || tagName(el) === "th"))
745
+ return {
746
+ ...DEFAULT_BLOCK,
747
+ children: next(el.childNodes)
748
+ };
749
+ }
750
+ },
741
751
  {
742
752
  deserialize(el) {
743
753
  if (isElement(el) && tagName(el) === "img") {
@@ -901,14 +911,14 @@ class HtmlDeserializer {
901
911
  const { parseHtml } = this, fragment = parseHtml(html), children = Array.from(fragment.childNodes), blocks2 = trimWhitespace(
902
912
  this.schema,
903
913
  flattenNestedBlocks(
904
- { schema: this.schema },
914
+ { schema: this.schema, keyGenerator: this.keyGenerator },
905
915
  ensureRootIsBlocks(
906
916
  this.schema,
907
917
  this.deserializeElements(children)
908
918
  )
909
919
  )
910
920
  );
911
- return this._markDefs.length > 0 && blocks2.filter((block) => isTextBlock(this.schema, block)).forEach((block) => {
921
+ return this._markDefs.length > 0 && blocks2.filter((block) => isTextBlock({ schema: this.schema }, block)).forEach((block) => {
912
922
  block.markDefs = block.markDefs || [], block.markDefs = block.markDefs.concat(
913
923
  this._markDefs.filter((def) => flatten(
914
924
  block.children.map((child) => child.marks || [])
@@ -1029,9 +1039,18 @@ class HtmlDeserializer {
1029
1039
  }
1030
1040
  function normalizeBlock(node, options = {}) {
1031
1041
  const schema = {
1042
+ block: {
1043
+ name: options.blockTypeName || "block"
1044
+ },
1032
1045
  span: {
1033
1046
  name: "span"
1034
- }
1047
+ },
1048
+ styles: [],
1049
+ lists: [],
1050
+ decorators: [],
1051
+ annotations: [],
1052
+ blockObjects: [],
1053
+ inlineObjects: []
1035
1054
  };
1036
1055
  if (node._type !== (options.blockTypeName || "block"))
1037
1056
  return "_key" in node ? node : {
@@ -1057,13 +1076,13 @@ function normalizeBlock(node, options = {}) {
1057
1076
  return block.children = block.children.reduce(
1058
1077
  (acc, child) => {
1059
1078
  const previousChild = acc[acc.length - 1];
1060
- return previousChild && isSpan(schema, child) && isSpan(schema, previousChild) && isEqual(previousChild.marks, child.marks) ? (lastChild && lastChild === child && child.text === "" && block.children.length > 1 || (previousChild.text += child.text), acc) : (acc.push(child), acc);
1079
+ return previousChild && isSpan({ schema }, child) && isSpan({ schema }, previousChild) && isEqual(previousChild.marks, child.marks) ? (lastChild && lastChild === child && child.text === "" && block.children.length > 1 || (previousChild.text += child.text), acc) : (acc.push(child), acc);
1061
1080
  },
1062
1081
  []
1063
1082
  ).map((child) => {
1064
1083
  if (!child)
1065
1084
  throw new Error("missing child");
1066
- return child._key = options.keyGenerator ? options.keyGenerator() : keyGenerator(), isSpan(schema, child) && (child.marks ? allowedDecorators && (child.marks = child.marks.filter((mark) => {
1085
+ return child._key = options.keyGenerator ? options.keyGenerator() : keyGenerator(), isSpan({ schema }, child) && (child.marks ? allowedDecorators && (child.marks = child.marks.filter((mark) => {
1067
1086
  const isAllowed = allowedDecorators.includes(mark), isUsed = block.markDefs?.some((def) => def._key === mark);
1068
1087
  return isAllowed || isUsed;
1069
1088
  })) : child.marks = [], usedMarkDefs.push(...child.marks)), child;