@portabletext/block-tools 3.2.1 → 3.3.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 +140 -22
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +48 -2
- package/lib/index.d.ts +48 -2
- package/lib/index.js +139 -21
- package/lib/index.js.map +1 -1
- package/package.json +4 -4
- package/src/HtmlDeserializer/flatten-nested-blocks.test.ts +140 -0
- package/src/HtmlDeserializer/helpers.ts +113 -7
- package/src/HtmlDeserializer/index.ts +9 -2
- package/src/HtmlDeserializer/rules/gdocs.ts +9 -1
- package/src/HtmlDeserializer/rules/html.ts +76 -1
- package/src/HtmlDeserializer/rules/index.ts +2 -1
- package/src/index.ts +2 -2
- package/src/schema-matchers.ts +41 -0
- package/src/types.ts +6 -0
package/lib/index.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: !0 });
|
|
3
|
-
var sanityBridge = require("@portabletext/sanity-bridge"), flatten = require("lodash/flatten.js"),
|
|
3
|
+
var sanityBridge = require("@portabletext/sanity-bridge"), flatten = require("lodash/flatten.js"), getRandomValues = require("get-random-values-esm"), isEqual = require("lodash/isEqual.js"), uniq = require("lodash/uniq.js");
|
|
4
4
|
function _interopDefaultCompat(e) {
|
|
5
5
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
6
6
|
}
|
|
7
|
-
var flatten__default = /* @__PURE__ */ _interopDefaultCompat(flatten),
|
|
7
|
+
var flatten__default = /* @__PURE__ */ _interopDefaultCompat(flatten), getRandomValues__default = /* @__PURE__ */ _interopDefaultCompat(getRandomValues), isEqual__default = /* @__PURE__ */ _interopDefaultCompat(isEqual), uniq__default = /* @__PURE__ */ _interopDefaultCompat(uniq);
|
|
8
8
|
function isArbitraryTypedObject(object) {
|
|
9
9
|
return isRecord(object) && typeof object._type == "string";
|
|
10
10
|
}
|
|
@@ -17,6 +17,19 @@ function isTextBlock(schema, block) {
|
|
|
17
17
|
function isSpan(schema, child) {
|
|
18
18
|
return !(!isArbitraryTypedObject(child) || child._type !== schema.span.name || typeof child.text != "string");
|
|
19
19
|
}
|
|
20
|
+
function keyGenerator() {
|
|
21
|
+
return randomKey(12);
|
|
22
|
+
}
|
|
23
|
+
function whatwgRNG(length = 16) {
|
|
24
|
+
const rnds8 = new Uint8Array(length);
|
|
25
|
+
return getRandomValues__default.default(rnds8), rnds8;
|
|
26
|
+
}
|
|
27
|
+
const byteToHex = [];
|
|
28
|
+
for (let i = 0; i < 256; ++i)
|
|
29
|
+
byteToHex[i] = (i + 256).toString(16).slice(1);
|
|
30
|
+
function randomKey(length) {
|
|
31
|
+
return whatwgRNG(length).reduce((str, n) => str + byteToHex[n], "").slice(0, length);
|
|
32
|
+
}
|
|
20
33
|
const objectToString = Object.prototype.toString;
|
|
21
34
|
function resolveJsType(val) {
|
|
22
35
|
switch (objectToString.call(val)) {
|
|
@@ -298,12 +311,67 @@ function defaultParseHtml() {
|
|
|
298
311
|
);
|
|
299
312
|
return (html) => new DOMParser().parseFromString(html, "text/html");
|
|
300
313
|
}
|
|
301
|
-
function flattenNestedBlocks(
|
|
314
|
+
function flattenNestedBlocks(context, blocks2) {
|
|
302
315
|
let depth = 0;
|
|
303
316
|
const flattened = [], traverse = (nodes) => {
|
|
304
317
|
const toRemove = [];
|
|
305
318
|
nodes.forEach((node) => {
|
|
306
|
-
|
|
319
|
+
if (depth === 0)
|
|
320
|
+
if (context.schema.blockObjects.length > 0 && isTextBlock(context.schema, node)) {
|
|
321
|
+
const hasBlockObjects = node.children.some((child) => context.schema.blockObjects.some(
|
|
322
|
+
(blockObject) => blockObject.name === child._type
|
|
323
|
+
)), hasBlocks = node.children.some(
|
|
324
|
+
(child) => child._type === "__block"
|
|
325
|
+
);
|
|
326
|
+
if (hasBlockObjects || hasBlocks) {
|
|
327
|
+
node.children.reduce(
|
|
328
|
+
(slices, child) => {
|
|
329
|
+
const knownInlineObject = context.schema.inlineObjects.some(
|
|
330
|
+
(inlineObject) => inlineObject.name === child._type
|
|
331
|
+
), knownBlockObject = context.schema.blockObjects.some(
|
|
332
|
+
(blockObject) => blockObject.name === child._type
|
|
333
|
+
), lastSlice = slices.pop();
|
|
334
|
+
return !isSpan(context.schema, child) && !knownInlineObject && knownBlockObject ? [
|
|
335
|
+
...slices,
|
|
336
|
+
...lastSlice ? [lastSlice] : [],
|
|
337
|
+
{ type: "block object", block: child }
|
|
338
|
+
] : child._type === "__block" ? [
|
|
339
|
+
...slices,
|
|
340
|
+
...lastSlice ? [lastSlice] : [],
|
|
341
|
+
{
|
|
342
|
+
type: "block object",
|
|
343
|
+
block: child.block
|
|
344
|
+
}
|
|
345
|
+
] : lastSlice && lastSlice.type === "children" ? [
|
|
346
|
+
...slices,
|
|
347
|
+
{
|
|
348
|
+
type: "children",
|
|
349
|
+
children: [...lastSlice.children, child]
|
|
350
|
+
}
|
|
351
|
+
] : [
|
|
352
|
+
...slices,
|
|
353
|
+
...lastSlice ? [lastSlice] : [],
|
|
354
|
+
{ type: "children", children: [child] }
|
|
355
|
+
];
|
|
356
|
+
},
|
|
357
|
+
[]
|
|
358
|
+
).forEach((slice) => {
|
|
359
|
+
if (slice.type === "block object")
|
|
360
|
+
flattened.push(slice.block);
|
|
361
|
+
else if (slice.children.length > 0) {
|
|
362
|
+
const newBlock = {
|
|
363
|
+
...node,
|
|
364
|
+
children: slice.children
|
|
365
|
+
};
|
|
366
|
+
flattened.push(newBlock);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
return;
|
|
370
|
+
} else
|
|
371
|
+
flattened.push(node);
|
|
372
|
+
} else
|
|
373
|
+
flattened.push(node);
|
|
374
|
+
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));
|
|
307
375
|
}), toRemove.forEach((node) => {
|
|
308
376
|
nodes.splice(nodes.indexOf(node), 1);
|
|
309
377
|
}), depth--;
|
|
@@ -453,8 +521,10 @@ function getBlockStyle(schema, el) {
|
|
|
453
521
|
function createGDocsRules(schema) {
|
|
454
522
|
return [
|
|
455
523
|
{
|
|
456
|
-
deserialize(el) {
|
|
524
|
+
deserialize(el, next) {
|
|
457
525
|
if (isElement(el) && tagName(el) === "span" && isGoogleDocs(el)) {
|
|
526
|
+
if (!el.textContent)
|
|
527
|
+
return !el.previousSibling && !el.nextSibling && el.setAttribute("data-lonely-child", "true"), next(el.childNodes);
|
|
458
528
|
const span = {
|
|
459
529
|
...DEFAULT_SPAN,
|
|
460
530
|
marks: [],
|
|
@@ -497,19 +567,6 @@ function createGDocsRules(schema) {
|
|
|
497
567
|
}
|
|
498
568
|
];
|
|
499
569
|
}
|
|
500
|
-
function keyGenerator() {
|
|
501
|
-
return randomKey(12);
|
|
502
|
-
}
|
|
503
|
-
function whatwgRNG(length = 16) {
|
|
504
|
-
const rnds8 = new Uint8Array(length);
|
|
505
|
-
return getRandomValues__default.default(rnds8), rnds8;
|
|
506
|
-
}
|
|
507
|
-
const byteToHex = [];
|
|
508
|
-
for (let i = 0; i < 256; ++i)
|
|
509
|
-
byteToHex[i] = (i + 256).toString(16).slice(1);
|
|
510
|
-
function randomKey(length) {
|
|
511
|
-
return whatwgRNG(length).reduce((str, n) => str + byteToHex[n], "").slice(0, length);
|
|
512
|
-
}
|
|
513
570
|
const whitespaceTextNodeRule = {
|
|
514
571
|
deserialize(node) {
|
|
515
572
|
return node.nodeName === "#text" && isWhitespaceTextNode(node) ? {
|
|
@@ -682,6 +739,62 @@ function createHTMLRules(schema, options) {
|
|
|
682
739
|
children: next(el.childNodes)
|
|
683
740
|
} : el.appendChild(el.ownerDocument.createTextNode(` (${href})`)) && next(el.childNodes) : next(el.childNodes);
|
|
684
741
|
}
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
deserialize(el) {
|
|
745
|
+
if (isElement(el) && tagName(el) === "img") {
|
|
746
|
+
const src = el.getAttribute("src") ?? void 0, alt = el.getAttribute("alt") ?? void 0, props = Object.fromEntries(
|
|
747
|
+
Array.from(el.attributes).map((attr) => [attr.name, attr.value])
|
|
748
|
+
), ancestorOfLonelyChild = el?.parentElement?.parentElement?.getAttribute("data-lonely-child"), ancestorOfListItem = el.closest("li") !== null;
|
|
749
|
+
if (ancestorOfLonelyChild && !ancestorOfListItem) {
|
|
750
|
+
const image2 = options.matchers?.image?.({
|
|
751
|
+
context: {
|
|
752
|
+
schema,
|
|
753
|
+
keyGenerator: options.keyGenerator ?? keyGenerator
|
|
754
|
+
},
|
|
755
|
+
props: {
|
|
756
|
+
...props,
|
|
757
|
+
...src ? { src } : {},
|
|
758
|
+
...alt ? { alt } : {}
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
if (image2)
|
|
762
|
+
return {
|
|
763
|
+
_type: "__block",
|
|
764
|
+
block: image2
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
const inlineImage = options.matchers?.inlineImage?.({
|
|
768
|
+
context: {
|
|
769
|
+
schema,
|
|
770
|
+
keyGenerator: options.keyGenerator ?? keyGenerator
|
|
771
|
+
},
|
|
772
|
+
props: {
|
|
773
|
+
...props,
|
|
774
|
+
...src ? { src } : {},
|
|
775
|
+
...alt ? { alt } : {}
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
if (inlineImage)
|
|
779
|
+
return inlineImage;
|
|
780
|
+
const image = options.matchers?.image?.({
|
|
781
|
+
context: {
|
|
782
|
+
schema,
|
|
783
|
+
keyGenerator: options.keyGenerator ?? keyGenerator
|
|
784
|
+
},
|
|
785
|
+
props: {
|
|
786
|
+
...props,
|
|
787
|
+
...src ? { src } : {},
|
|
788
|
+
...alt ? { alt } : {}
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
if (image)
|
|
792
|
+
return {
|
|
793
|
+
_type: "__block",
|
|
794
|
+
block: image
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
}
|
|
685
798
|
}
|
|
686
799
|
];
|
|
687
800
|
}
|
|
@@ -759,6 +872,7 @@ function createRules(schema, options) {
|
|
|
759
872
|
];
|
|
760
873
|
}
|
|
761
874
|
class HtmlDeserializer {
|
|
875
|
+
keyGenerator;
|
|
762
876
|
schema;
|
|
763
877
|
rules;
|
|
764
878
|
parseHtml;
|
|
@@ -771,9 +885,10 @@ class HtmlDeserializer {
|
|
|
771
885
|
*/
|
|
772
886
|
constructor(schema, options = {}) {
|
|
773
887
|
const { rules = [], unstable_whitespaceOnPasteMode = "preserve" } = options, standardRules = createRules(schema, {
|
|
774
|
-
keyGenerator: options.keyGenerator
|
|
888
|
+
keyGenerator: options.keyGenerator,
|
|
889
|
+
matchers: options.matchers
|
|
775
890
|
});
|
|
776
|
-
this.schema = schema, this.rules = [...rules, ...standardRules];
|
|
891
|
+
this.schema = schema, this.keyGenerator = options.keyGenerator ?? keyGenerator, this.rules = [...rules, ...standardRules];
|
|
777
892
|
const parseHtml = options.parseHtml || defaultParseHtml();
|
|
778
893
|
this.parseHtml = (html) => preprocess(html, parseHtml, { unstable_whitespaceOnPasteMode }).body;
|
|
779
894
|
}
|
|
@@ -788,8 +903,11 @@ class HtmlDeserializer {
|
|
|
788
903
|
const { parseHtml } = this, fragment = parseHtml(html), children = Array.from(fragment.childNodes), blocks2 = trimWhitespace(
|
|
789
904
|
this.schema,
|
|
790
905
|
flattenNestedBlocks(
|
|
791
|
-
this.schema,
|
|
792
|
-
ensureRootIsBlocks(
|
|
906
|
+
{ schema: this.schema },
|
|
907
|
+
ensureRootIsBlocks(
|
|
908
|
+
this.schema,
|
|
909
|
+
this.deserializeElements(children)
|
|
910
|
+
)
|
|
793
911
|
)
|
|
794
912
|
);
|
|
795
913
|
return this._markDefs.length > 0 && blocks2.filter((block) => isTextBlock(this.schema, block)).forEach((block) => {
|