@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/LICENSE +21 -0
- package/README.md +226 -0
- package/lib/index.cjs +1056 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +172 -0
- package/lib/index.d.ts +172 -0
- package/lib/index.js +1056 -0
- package/lib/index.js.map +1 -0
- package/package.json +71 -0
- package/src/HtmlDeserializer/helpers.ts +363 -0
- package/src/HtmlDeserializer/index.ts +313 -0
- package/src/HtmlDeserializer/preprocessors/gdocs.ts +86 -0
- package/src/HtmlDeserializer/preprocessors/html.ts +57 -0
- package/src/HtmlDeserializer/preprocessors/index.ts +13 -0
- package/src/HtmlDeserializer/preprocessors/notion.ts +25 -0
- package/src/HtmlDeserializer/preprocessors/whitespace.ts +31 -0
- package/src/HtmlDeserializer/preprocessors/word.ts +92 -0
- package/src/HtmlDeserializer/preprocessors/xpathResult.ts +13 -0
- package/src/HtmlDeserializer/rules/gdocs.ts +183 -0
- package/src/HtmlDeserializer/rules/html.ts +264 -0
- package/src/HtmlDeserializer/rules/index.ts +18 -0
- package/src/HtmlDeserializer/rules/notion.ts +60 -0
- package/src/HtmlDeserializer/rules/word.ts +59 -0
- package/src/constants.ts +104 -0
- package/src/index.ts +52 -0
- package/src/types.ts +139 -0
- package/src/util/blockContentTypeFeatures.ts +141 -0
- package/src/util/findBlockType.ts +13 -0
- package/src/util/normalizeBlock.ts +142 -0
- package/src/util/randomKey.ts +26 -0
- package/src/util/resolveJsType.ts +44 -0
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
|