@portabletext/block-tools 1.0.1 → 1.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.mjs → index.cjs} +45 -39
- package/lib/index.cjs.map +1 -0
- package/lib/{index.d.mts → index.d.cts} +5 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +44 -38
- package/lib/index.js.map +1 -1
- package/package.json +12 -11
- package/src/HtmlDeserializer/index.ts +4 -4
- package/src/HtmlDeserializer/rules/html.ts +5 -3
- package/src/HtmlDeserializer/rules/index.ts +1 -1
- package/src/index.ts +6 -4
- package/src/types.ts +1 -0
- package/src/util/normalizeBlock.ts +19 -6
- package/src/util/randomKey.ts +4 -0
- package/lib/index.mjs.map +0 -1
package/lib/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
var flatten__default = /* @__PURE__ */ _interopDefaultCompat(flatten), isEqual__default = /* @__PURE__ */ _interopDefaultCompat(isEqual), uniq__default = /* @__PURE__ */ _interopDefaultCompat(uniq), getRandomValues__default = /* @__PURE__ */ _interopDefaultCompat(getRandomValues);
|
|
1
|
+
import flatten from "lodash/flatten.js";
|
|
2
|
+
import { isBlockSchemaType, isBlockChildrenObjectField, isObjectSchemaType, isBlockStyleObjectField, isBlockListObjectField, isTitledListValue, isPortableTextTextBlock, isPortableTextSpan } from "@sanity/types";
|
|
3
|
+
import isEqual from "lodash/isEqual.js";
|
|
4
|
+
import uniq from "lodash/uniq.js";
|
|
5
|
+
import getRandomValues from "get-random-values-esm";
|
|
8
6
|
function findBlockType(type) {
|
|
9
7
|
return type.type ? findBlockType(type.type) : type.name === "block";
|
|
10
8
|
}
|
|
@@ -92,19 +90,19 @@ const PRESERVE_WHITESPACE_TAGS = ["pre", "textarea", "code"], BLOCK_DEFAULT_STYL
|
|
|
92
90
|
...HTML_HEADER_TAGS,
|
|
93
91
|
...HTML_MISC_TAGS
|
|
94
92
|
};
|
|
95
|
-
|
|
93
|
+
uniq(
|
|
96
94
|
Object.values(ELEMENT_MAP).filter((tag) => "style" in tag).map((tag) => tag.style)
|
|
97
95
|
);
|
|
98
|
-
|
|
96
|
+
uniq(
|
|
99
97
|
Object.values(HTML_DECORATOR_TAGS)
|
|
100
98
|
);
|
|
101
99
|
function blockContentFeatures(blockContentType) {
|
|
102
100
|
if (!blockContentType)
|
|
103
101
|
throw new Error("Parameter 'blockContentType' required");
|
|
104
102
|
const blockType = blockContentType.of.find(findBlockType);
|
|
105
|
-
if (!
|
|
103
|
+
if (!isBlockSchemaType(blockType))
|
|
106
104
|
throw new Error("'block' type is not defined in this schema (required).");
|
|
107
|
-
const ofType = blockType.fields.find(
|
|
105
|
+
const ofType = blockType.fields.find(isBlockChildrenObjectField)?.type?.of;
|
|
108
106
|
if (!ofType)
|
|
109
107
|
throw new Error("No `of` declaration found for blocks `children` field");
|
|
110
108
|
const spanType = ofType.find(
|
|
@@ -115,9 +113,9 @@ function blockContentFeatures(blockContentType) {
|
|
|
115
113
|
"No `span` type found in `block` schema type `children` definition"
|
|
116
114
|
);
|
|
117
115
|
const inlineObjectTypes = ofType.filter(
|
|
118
|
-
(inlineType) => inlineType.name !== "span" &&
|
|
116
|
+
(inlineType) => inlineType.name !== "span" && isObjectSchemaType(inlineType)
|
|
119
117
|
), blockObjectTypes = blockContentType.of.filter(
|
|
120
|
-
(memberType) => memberType.name !== blockType.name &&
|
|
118
|
+
(memberType) => memberType.name !== blockType.name && isObjectSchemaType(memberType)
|
|
121
119
|
);
|
|
122
120
|
return {
|
|
123
121
|
styles: resolveEnabledStyles(blockType),
|
|
@@ -133,7 +131,7 @@ function blockContentFeatures(blockContentType) {
|
|
|
133
131
|
};
|
|
134
132
|
}
|
|
135
133
|
function resolveEnabledStyles(blockType) {
|
|
136
|
-
const styleField = blockType.fields.find(
|
|
134
|
+
const styleField = blockType.fields.find(isBlockStyleObjectField);
|
|
137
135
|
if (!styleField)
|
|
138
136
|
throw new Error(
|
|
139
137
|
"A field with name 'style' is not defined in the block type (required)."
|
|
@@ -159,7 +157,7 @@ function resolveEnabledDecorators(spanType) {
|
|
|
159
157
|
return spanType.decorators;
|
|
160
158
|
}
|
|
161
159
|
function resolveEnabledListItems(blockType) {
|
|
162
|
-
const listField = blockType.fields.find(
|
|
160
|
+
const listField = blockType.fields.find(isBlockListObjectField);
|
|
163
161
|
if (!listField)
|
|
164
162
|
throw new Error(
|
|
165
163
|
"A field with name 'list' is not defined in the block type (required)."
|
|
@@ -174,7 +172,7 @@ function resolveEnabledListItems(blockType) {
|
|
|
174
172
|
function getTitledListValuesFromEnumListOptions(options) {
|
|
175
173
|
const list = options ? options.list : void 0;
|
|
176
174
|
return Array.isArray(list) ? list.map(
|
|
177
|
-
(item) =>
|
|
175
|
+
(item) => isTitledListValue(item) ? item : { title: item, value: item }
|
|
178
176
|
) : [];
|
|
179
177
|
}
|
|
180
178
|
const _XPathResult = {
|
|
@@ -397,7 +395,7 @@ function flattenNestedBlocks(blocks2) {
|
|
|
397
395
|
const flattened = [], traverse = (nodes) => {
|
|
398
396
|
const toRemove = [];
|
|
399
397
|
nodes.forEach((node) => {
|
|
400
|
-
depth === 0 && flattened.push(node),
|
|
398
|
+
depth === 0 && flattened.push(node), 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
399
|
}), toRemove.forEach((node) => {
|
|
402
400
|
nodes.splice(nodes.indexOf(node), 1);
|
|
403
401
|
}), depth--;
|
|
@@ -417,11 +415,11 @@ function isWhiteSpaceChar(text) {
|
|
|
417
415
|
}
|
|
418
416
|
function trimWhitespace(blocks2) {
|
|
419
417
|
return blocks2.forEach((block) => {
|
|
420
|
-
|
|
418
|
+
isPortableTextTextBlock(block) && block.children.forEach((child, index) => {
|
|
421
419
|
if (!isMinimalSpan(child))
|
|
422
420
|
return;
|
|
423
421
|
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 &&
|
|
422
|
+
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(prevChild.marks, child.marks) && isWhiteSpaceChar(child.text) ? (prevChild.text += " ", block.children.splice(index, 1)) : nextChild && isEqual(nextChild.marks, child.marks) && isWhiteSpaceChar(child.text) && (nextChild.text = ` ${nextChild.text}`, block.children.splice(index, 1));
|
|
425
423
|
});
|
|
426
424
|
}), blocks2;
|
|
427
425
|
}
|
|
@@ -432,7 +430,7 @@ function ensureRootIsBlocks(blocks2) {
|
|
|
432
430
|
if (node._type === "__block")
|
|
433
431
|
return memo.push(node.block), memo;
|
|
434
432
|
const lastBlock = memo[memo.length - 1];
|
|
435
|
-
if (i > 0 && !
|
|
433
|
+
if (i > 0 && !isPortableTextTextBlock(original[i - 1]) && isPortableTextTextBlock(lastBlock))
|
|
436
434
|
return lastBlock.children.push(node), memo;
|
|
437
435
|
const block = {
|
|
438
436
|
...DEFAULT_BLOCK,
|
|
@@ -591,9 +589,12 @@ function createGDocsRules(_blockContentType, options) {
|
|
|
591
589
|
}
|
|
592
590
|
];
|
|
593
591
|
}
|
|
592
|
+
function keyGenerator() {
|
|
593
|
+
return randomKey(12);
|
|
594
|
+
}
|
|
594
595
|
function whatwgRNG(length = 16) {
|
|
595
596
|
const rnds8 = new Uint8Array(length);
|
|
596
|
-
return
|
|
597
|
+
return getRandomValues(rnds8), rnds8;
|
|
597
598
|
}
|
|
598
599
|
const byteToHex = [];
|
|
599
600
|
for (let i = 0; i < 256; ++i)
|
|
@@ -762,7 +763,7 @@ function createHTMLRules(_blockContentType, options) {
|
|
|
762
763
|
return next(el.childNodes);
|
|
763
764
|
let markDef;
|
|
764
765
|
return linkEnabled ? (markDef = {
|
|
765
|
-
_key:
|
|
766
|
+
_key: options.keyGenerator ? options.keyGenerator() : keyGenerator(),
|
|
766
767
|
_type: "link",
|
|
767
768
|
href
|
|
768
769
|
}, {
|
|
@@ -862,10 +863,10 @@ class HtmlDeserializer {
|
|
|
862
863
|
const { rules = [], unstable_whitespaceOnPasteMode = "preserve" } = options;
|
|
863
864
|
if (!blockContentType)
|
|
864
865
|
throw new Error("Parameter 'blockContentType' is required");
|
|
865
|
-
const standardRules = createRules(
|
|
866
|
-
blockContentType,
|
|
867
|
-
|
|
868
|
-
);
|
|
866
|
+
const standardRules = createRules(blockContentType, {
|
|
867
|
+
...createRuleOptions(blockContentType),
|
|
868
|
+
keyGenerator: options.keyGenerator
|
|
869
|
+
});
|
|
869
870
|
this.rules = [...rules, ...standardRules];
|
|
870
871
|
const parseHtml = options.parseHtml || defaultParseHtml();
|
|
871
872
|
this.blockContentType = blockContentType, this.parseHtml = (html) => preprocess(html, parseHtml, { unstable_whitespaceOnPasteMode }).body;
|
|
@@ -887,7 +888,7 @@ class HtmlDeserializer {
|
|
|
887
888
|
(block) => block._type === "block"
|
|
888
889
|
).forEach((block) => {
|
|
889
890
|
block.markDefs = block.markDefs || [], block.markDefs = block.markDefs.concat(
|
|
890
|
-
this._markDefs.filter((def) =>
|
|
891
|
+
this._markDefs.filter((def) => flatten(
|
|
891
892
|
block.children.map((child) => child.marks || [])
|
|
892
893
|
).includes(def._key))
|
|
893
894
|
);
|
|
@@ -1009,9 +1010,12 @@ class HtmlDeserializer {
|
|
|
1009
1010
|
}
|
|
1010
1011
|
function normalizeBlock(node, options = {}) {
|
|
1011
1012
|
if (node._type !== (options.blockTypeName || "block"))
|
|
1012
|
-
return "_key" in node ? node : {
|
|
1013
|
+
return "_key" in node ? node : {
|
|
1014
|
+
...node,
|
|
1015
|
+
_key: options.keyGenerator ? options.keyGenerator() : keyGenerator()
|
|
1016
|
+
};
|
|
1013
1017
|
const block = {
|
|
1014
|
-
_key:
|
|
1018
|
+
_key: options.keyGenerator ? options.keyGenerator() : keyGenerator(),
|
|
1015
1019
|
children: [],
|
|
1016
1020
|
markDefs: [],
|
|
1017
1021
|
...node
|
|
@@ -1020,7 +1024,7 @@ function normalizeBlock(node, options = {}) {
|
|
|
1020
1024
|
return block.children = [
|
|
1021
1025
|
{
|
|
1022
1026
|
_type: "span",
|
|
1023
|
-
_key:
|
|
1027
|
+
_key: options.keyGenerator ? options.keyGenerator() : keyGenerator(),
|
|
1024
1028
|
text: "",
|
|
1025
1029
|
marks: []
|
|
1026
1030
|
}
|
|
@@ -1029,13 +1033,13 @@ function normalizeBlock(node, options = {}) {
|
|
|
1029
1033
|
return block.children = block.children.reduce(
|
|
1030
1034
|
(acc, child) => {
|
|
1031
1035
|
const previousChild = acc[acc.length - 1];
|
|
1032
|
-
return previousChild &&
|
|
1036
|
+
return previousChild && isPortableTextSpan(child) && isPortableTextSpan(previousChild) && isEqual(previousChild.marks, child.marks) ? (lastChild && lastChild === child && child.text === "" && block.children.length > 1 || (previousChild.text += child.text), acc) : (acc.push(child), acc);
|
|
1033
1037
|
},
|
|
1034
1038
|
[]
|
|
1035
|
-
).map((child
|
|
1039
|
+
).map((child) => {
|
|
1036
1040
|
if (!child)
|
|
1037
1041
|
throw new Error("missing child");
|
|
1038
|
-
return child._key =
|
|
1042
|
+
return child._key = options.keyGenerator ? options.keyGenerator() : keyGenerator(), isPortableTextSpan(child) && (child.marks ? allowedDecorators && (child.marks = child.marks.filter((mark) => {
|
|
1039
1043
|
const isAllowed = allowedDecorators.includes(mark), isUsed = block.markDefs?.some((def) => def._key === mark);
|
|
1040
1044
|
return isAllowed || isUsed;
|
|
1041
1045
|
})) : child.marks = [], usedMarkDefs.push(...child.marks)), child;
|
|
@@ -1044,13 +1048,15 @@ function normalizeBlock(node, options = {}) {
|
|
|
1044
1048
|
), block;
|
|
1045
1049
|
}
|
|
1046
1050
|
function htmlToBlocks(html, blockContentType, options = {}) {
|
|
1047
|
-
return new HtmlDeserializer(blockContentType, options).deserialize(html).map((block) => normalizeBlock(block));
|
|
1051
|
+
return new HtmlDeserializer(blockContentType, options).deserialize(html).map((block) => normalizeBlock(block, { keyGenerator: options.keyGenerator }));
|
|
1048
1052
|
}
|
|
1049
1053
|
function getBlockContentFeatures(blockContentType) {
|
|
1050
1054
|
return blockContentFeatures(blockContentType);
|
|
1051
1055
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
+
export {
|
|
1057
|
+
getBlockContentFeatures,
|
|
1058
|
+
htmlToBlocks,
|
|
1059
|
+
normalizeBlock,
|
|
1060
|
+
randomKey
|
|
1061
|
+
};
|
|
1056
1062
|
//# sourceMappingURL=index.js.map
|