@portabletext/html 1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 - 2026 Sanity.io
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,235 @@
1
+ # `@portabletext/html`
2
+
3
+ > Convert HTML to Portable Text
4
+
5
+ > **Using Sanity?** [`@portabletext/block-tools`](../block-tools) wraps this package with Sanity schema support - use that if you already have a compiled Sanity schema.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @portabletext/html
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import {htmlToPortableText} from '@portabletext/html'
17
+
18
+ const blocks = htmlToPortableText('<h1>Hello <strong>world</strong></h1>')
19
+ ```
20
+
21
+ ```json
22
+ [
23
+ {
24
+ "_type": "block",
25
+ "_key": "f4s8k2",
26
+ "style": "h1",
27
+ "children": [
28
+ {"_type": "span", "_key": "a9c3x1", "text": "Hello ", "marks": []},
29
+ {"_type": "span", "_key": "b7d2m5", "text": "world", "marks": ["strong"]}
30
+ ],
31
+ "markDefs": []
32
+ }
33
+ ]
34
+ ```
35
+
36
+ ## Supported features
37
+
38
+ | Feature | HTML to Portable Text |
39
+ | -------------------- | --------------------- |
40
+ | Headings (h1-h6) | Yes |
41
+ | Paragraphs | Yes |
42
+ | Bold | Yes |
43
+ | Italic | Yes |
44
+ | Underline | Yes |
45
+ | Strikethrough | Yes |
46
+ | Inline code | Yes |
47
+ | Links | Yes |
48
+ | Blockquotes | Yes |
49
+ | Ordered lists | Yes |
50
+ | Unordered lists | Yes |
51
+ | Nested lists | Yes |
52
+ | Images | Yes\* |
53
+ | Tables | Yes\* |
54
+ | Google Docs paste | Yes |
55
+ | Microsoft Word paste | Yes |
56
+ | Word Online paste | Yes |
57
+ | Notion paste | Yes |
58
+
59
+ \* Requires custom configuration (see usage below)
60
+
61
+ ## Usage
62
+
63
+ ### `htmlToPortableText`
64
+
65
+ ```ts
66
+ import {htmlToPortableText} from '@portabletext/html'
67
+
68
+ const blocks = htmlToPortableText(`
69
+ <h1>Hello World</h1>
70
+ <p>This is <strong>bold</strong> and <em>italic</em> text with a <a href="https://example.com">link</a>.</p>
71
+ <ul>
72
+ <li>First item</li>
73
+ <li>Second item</li>
74
+ </ul>
75
+ `)
76
+ ```
77
+
78
+ ### Custom schema
79
+
80
+ By default, `htmlToPortableText` uses a schema with common styles, decorators, and annotations. You can provide your own schema to control what types are produced:
81
+
82
+ ```ts
83
+ import {htmlToPortableText} from '@portabletext/html'
84
+ import {compileSchema, defineSchema} from '@portabletext/schema'
85
+
86
+ const schema = compileSchema(
87
+ defineSchema({
88
+ decorators: [{name: 'strong'}, {name: 'em'}],
89
+ styles: [{name: 'normal'}, {name: 'h1'}, {name: 'h2'}],
90
+ annotations: [{name: 'link'}],
91
+ blockObjects: [{name: 'image', fields: [{name: 'src', type: 'string'}]}],
92
+ }),
93
+ )
94
+
95
+ const blocks = htmlToPortableText(html, {schema})
96
+ ```
97
+
98
+ ### Custom rules
99
+
100
+ Rules give you full control over how HTML elements are converted to Portable Text. Custom rules are checked before the built-in rules - the first rule that returns a value wins.
101
+
102
+ ```ts
103
+ import {htmlToPortableText} from '@portabletext/html'
104
+
105
+ const blocks = htmlToPortableText(html, {
106
+ rules: [
107
+ {
108
+ deserialize(el, next, createBlock) {
109
+ // Convert <pre><code> to a custom code block type
110
+ if (el.tagName?.toLowerCase() !== 'pre') return undefined
111
+ const code = el.querySelector('code')
112
+ return createBlock({
113
+ _type: 'code',
114
+ text: (code ?? el).textContent ?? '',
115
+ language: code?.className?.replace('language-', '') ?? undefined,
116
+ })
117
+ },
118
+ },
119
+ ],
120
+ })
121
+ ```
122
+
123
+ ### Whitespace handling
124
+
125
+ The `whitespaceMode` option controls how whitespace is handled during deserialization:
126
+
127
+ ```ts
128
+ const blocks = htmlToPortableText(html, {
129
+ whitespaceMode: 'normalize', // 'preserve' | 'remove' | 'normalize'
130
+ })
131
+ ```
132
+
133
+ - `'preserve'` (default) - keep whitespace as-is
134
+ - `'remove'` - strip extra whitespace
135
+ - `'normalize'` - normalize whitespace (useful for Google Docs paste)
136
+
137
+ ### HTML parsing
138
+
139
+ In the browser, `htmlToPortableText` uses the native `DOMParser`. In Node.js, you need to provide a parser:
140
+
141
+ ```ts
142
+ import {htmlToPortableText} from '@portabletext/html'
143
+ import {JSDOM} from 'jsdom'
144
+
145
+ const blocks = htmlToPortableText(html, {
146
+ parseHtml: (html) => new JSDOM(html).window.document,
147
+ })
148
+ ```
149
+
150
+ ### Image handling
151
+
152
+ Images require a custom matcher since there's no universal way to represent them in Portable Text. The `types.image` option receives an `ObjectMatcher` that's called for every `<img>` element:
153
+
154
+ ```ts
155
+ import {htmlToPortableText, type ObjectMatcher} from '@portabletext/html'
156
+ import {compileSchema, defineSchema} from '@portabletext/schema'
157
+
158
+ const schema = compileSchema(
159
+ defineSchema({
160
+ blockObjects: [{name: 'image', fields: [{name: 'src', type: 'string'}]}],
161
+ inlineObjects: [{name: 'image', fields: [{name: 'src', type: 'string'}]}],
162
+ }),
163
+ )
164
+
165
+ const imageMatcher: ObjectMatcher<{src?: string; alt?: string}> = ({
166
+ context,
167
+ value,
168
+ isInline,
169
+ }) => {
170
+ const collection = isInline
171
+ ? context.schema.inlineObjects
172
+ : context.schema.blockObjects
173
+
174
+ if (!collection.some((obj) => obj.name === 'image')) {
175
+ return undefined
176
+ }
177
+
178
+ return {
179
+ _key: context.keyGenerator(),
180
+ _type: 'image',
181
+ ...(value.src ? {src: value.src} : {}),
182
+ }
183
+ }
184
+
185
+ const blocks = htmlToPortableText(html, {
186
+ schema,
187
+ types: {image: imageMatcher},
188
+ })
189
+ ```
190
+
191
+ The matcher is called with `isInline: false` for block-level images and `isInline: true` for inline images. Return `undefined` to skip the image.
192
+
193
+ ### Table flattening
194
+
195
+ The `createFlattenTableRule` helper converts tables into a flat list of blocks:
196
+
197
+ ```ts
198
+ import {htmlToPortableText} from '@portabletext/html'
199
+ import {createFlattenTableRule} from '@portabletext/html/rules'
200
+
201
+ const blocks = htmlToPortableText(html, {
202
+ rules: [
203
+ createFlattenTableRule({
204
+ schema,
205
+ separator: () => ({_type: 'span', text: ': '}),
206
+ }),
207
+ ],
208
+ })
209
+ ```
210
+
211
+ ### Key generation
212
+
213
+ By default, random keys are generated for each block and span. You can provide your own key generator for deterministic output:
214
+
215
+ ```ts
216
+ let i = 0
217
+ const blocks = htmlToPortableText(html, {
218
+ keyGenerator: () => `key${i++}`,
219
+ })
220
+ ```
221
+
222
+ ## Paste source support
223
+
224
+ The package includes built-in preprocessors that detect and handle paste from:
225
+
226
+ - **Google Docs** - handles inline styles, checkmark lists, and whitespace quirks
227
+ - **Microsoft Word** - handles mso-list CSS, heading styles, and list numbering
228
+ - **Word Online** - handles WACImage containers, TextRun formatting, and paragraph styles
229
+ - **Notion** - handles inline style formatting for single-block copies
230
+
231
+ These preprocessors run automatically - no configuration needed.
232
+
233
+ ## License
234
+
235
+ MIT
@@ -0,0 +1,25 @@
1
+ import { PortableTextObject } from "@portabletext/schema";
2
+ /**
3
+ * @public
4
+ */
5
+ interface TypedObject {
6
+ _type: string;
7
+ _key?: string;
8
+ }
9
+ /**
10
+ * @public
11
+ */
12
+ interface ArbitraryTypedObject extends TypedObject {
13
+ [key: string]: unknown;
14
+ }
15
+ /**
16
+ * @public
17
+ */
18
+ interface DeserializerRule {
19
+ deserialize: (el: Node, next: (elements: Node | Node[] | NodeList) => TypedObject | TypedObject[] | undefined, createBlock: (props: ArbitraryTypedObject) => {
20
+ _type: string;
21
+ block: ArbitraryTypedObject;
22
+ }) => TypedObject | TypedObject[] | undefined;
23
+ }
24
+ export { TypedObject as n, DeserializerRule as t };
25
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types.ts"],"sourcesContent":[],"mappings":";;AAKA;AAQA;AAkDiB,UA1DA,WAAA,CA0DgB;EAEzB,KAAA,EAAA,MAAA;EAEQ,IAAA,CAAA,EAAA,MAAA;;;;;AAES,UAxDR,oBAAA,SAA6B,WAwDrB,CAAA;EAEZ,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;;;;;UARI,gBAAA;oBAET,uBAEQ,OAAO,SAAS,aACvB,cAAc,gDACE;;WAEZ;QAEN,cAAc"}
@@ -0,0 +1,297 @@
1
+ import { isTextBlock, isSpan } from "@portabletext/schema";
2
+ function isArbitraryTypedObject(object) {
3
+ return isRecord(object) && typeof object._type == "string";
4
+ }
5
+ function isRecord(value) {
6
+ return !!value && (typeof value == "object" || typeof value == "function");
7
+ }
8
+ function isEqualMarks(a, b) {
9
+ if (!a || !b)
10
+ return a === b;
11
+ if (a.length !== b.length)
12
+ return !1;
13
+ for (let index = 0; index < a.length; index++)
14
+ if (a[index] !== b[index])
15
+ return !1;
16
+ return !0;
17
+ }
18
+ function isDeepEqual(data, other) {
19
+ return isDeepEqualImplementation(data, other);
20
+ }
21
+ function isDeepEqualImplementation(data, other) {
22
+ if (data === other || Object.is(data, other))
23
+ return !0;
24
+ if (typeof data != "object" || typeof other != "object" || data === null || other === null || Object.getPrototypeOf(data) !== Object.getPrototypeOf(other))
25
+ return !1;
26
+ if (Array.isArray(data))
27
+ return isDeepEqualArrays(data, other);
28
+ if (data instanceof Map)
29
+ return isDeepEqualMaps(data, other);
30
+ if (data instanceof Set)
31
+ return isDeepEqualSets(data, other);
32
+ if (data instanceof Date)
33
+ return data.getTime() === other.getTime();
34
+ if (data instanceof RegExp)
35
+ return data.toString() === other.toString();
36
+ if (Object.keys(data).length !== Object.keys(other).length)
37
+ return !1;
38
+ for (const [key, value] of Object.entries(data))
39
+ if (!(key in other) || !isDeepEqualImplementation(
40
+ value,
41
+ // @ts-expect-error [ts7053] - We already checked that `other` has `key`
42
+ other[key]
43
+ ))
44
+ return !1;
45
+ return !0;
46
+ }
47
+ function isDeepEqualArrays(data, other) {
48
+ if (data.length !== other.length)
49
+ return !1;
50
+ for (const [index, item] of data.entries())
51
+ if (!isDeepEqualImplementation(item, other[index]))
52
+ return !1;
53
+ return !0;
54
+ }
55
+ function isDeepEqualMaps(data, other) {
56
+ if (data.size !== other.size)
57
+ return !1;
58
+ for (const [key, value] of data.entries())
59
+ if (!other.has(key) || !isDeepEqualImplementation(value, other.get(key)))
60
+ return !1;
61
+ return !0;
62
+ }
63
+ function isDeepEqualSets(data, other) {
64
+ if (data.size !== other.size)
65
+ return !1;
66
+ const otherCopy = [...other];
67
+ for (const dataItem of data) {
68
+ let isFound = !1;
69
+ for (const [index, otherItem] of otherCopy.entries())
70
+ if (isDeepEqualImplementation(dataItem, otherItem)) {
71
+ isFound = !0, otherCopy.splice(index, 1);
72
+ break;
73
+ }
74
+ if (!isFound)
75
+ return !1;
76
+ }
77
+ return !0;
78
+ }
79
+ function flattenNestedBlocks(context, blocks) {
80
+ return blocks.flatMap((block) => {
81
+ if (isBlockContainer(block))
82
+ return flattenNestedBlocks(context, [block.block]);
83
+ if (isTextBlock(context, block)) {
84
+ const hasBlockObjects = block.children.some((child) => context.schema.blockObjects.some(
85
+ (blockObject) => blockObject.name === child._type
86
+ )), hasBlocks = block.children.some(
87
+ (child) => child._type === "__block" || child._type === "block"
88
+ );
89
+ if (hasBlockObjects || hasBlocks) {
90
+ const splitChildren = getSplitChildren(context, block), firstSlice = splitChildren[0];
91
+ return splitChildren.length === 1 && firstSlice && firstSlice.type === "children" && isDeepEqual(firstSlice.children, block.children) ? [block] : splitChildren.flatMap((slice) => slice.type === "block object" ? [slice.block] : slice.type === "block" ? flattenNestedBlocks(context, [
92
+ slice.block
93
+ ]) : slice.children.length > 0 ? slice.children.every(
94
+ (child) => isSpan(context, child) && child.text.trim() === ""
95
+ ) ? [] : flattenNestedBlocks(context, [
96
+ {
97
+ ...block,
98
+ children: slice.children
99
+ }
100
+ ]) : []);
101
+ }
102
+ return [block];
103
+ }
104
+ return [block];
105
+ });
106
+ }
107
+ function isBlockContainer(block) {
108
+ return block._type === "__block" && isArbitraryTypedObject(block.block);
109
+ }
110
+ function getSplitChildren(context, block) {
111
+ return block.children.reduce(
112
+ (slices, child) => {
113
+ const knownInlineObject = context.schema.inlineObjects.some(
114
+ (inlineObject) => inlineObject.name === child._type
115
+ ), knownBlockObject = context.schema.blockObjects.some(
116
+ (blockObject) => blockObject.name === child._type
117
+ ), lastSlice = slices.pop();
118
+ return !isSpan(context, child) && !knownInlineObject && knownBlockObject ? [
119
+ ...slices,
120
+ ...lastSlice ? [lastSlice] : [],
121
+ { type: "block object", block: child }
122
+ ] : child._type === "__block" ? [
123
+ ...slices,
124
+ ...lastSlice ? [lastSlice] : [],
125
+ {
126
+ type: "block object",
127
+ block: child.block
128
+ }
129
+ ] : child._type === "block" ? [
130
+ ...slices,
131
+ ...lastSlice ? [lastSlice] : [],
132
+ { type: "block", block: child }
133
+ ] : lastSlice && lastSlice.type === "children" ? [
134
+ ...slices,
135
+ {
136
+ type: "children",
137
+ children: [...lastSlice.children, child]
138
+ }
139
+ ] : [
140
+ ...slices,
141
+ ...lastSlice ? [lastSlice] : [],
142
+ { type: "children", children: [child] }
143
+ ];
144
+ },
145
+ []
146
+ );
147
+ }
148
+ const PRESERVE_WHITESPACE_TAGS = ["pre", "textarea", "code"], BLOCK_DEFAULT_STYLE = "normal", DEFAULT_BLOCK = Object.freeze({
149
+ _type: "block",
150
+ markDefs: [],
151
+ style: BLOCK_DEFAULT_STYLE
152
+ }), DEFAULT_SPAN = Object.freeze({
153
+ _type: "span",
154
+ marks: []
155
+ }), HTML_BLOCK_TAGS = {
156
+ p: DEFAULT_BLOCK,
157
+ blockquote: { ...DEFAULT_BLOCK, style: "blockquote" }
158
+ }, HTML_SPAN_TAGS = {
159
+ span: { object: "text" }
160
+ }, HTML_LIST_CONTAINER_TAGS = {
161
+ ol: { object: null },
162
+ ul: { object: null }
163
+ }, HTML_HEADER_TAGS = {
164
+ h1: { ...DEFAULT_BLOCK, style: "h1" },
165
+ h2: { ...DEFAULT_BLOCK, style: "h2" },
166
+ h3: { ...DEFAULT_BLOCK, style: "h3" },
167
+ h4: { ...DEFAULT_BLOCK, style: "h4" },
168
+ h5: { ...DEFAULT_BLOCK, style: "h5" },
169
+ h6: { ...DEFAULT_BLOCK, style: "h6" }
170
+ }, HTML_MISC_TAGS = {
171
+ br: { ...DEFAULT_BLOCK, style: BLOCK_DEFAULT_STYLE }
172
+ }, HTML_DECORATOR_TAGS = {
173
+ b: "strong",
174
+ strong: "strong",
175
+ i: "em",
176
+ em: "em",
177
+ u: "underline",
178
+ s: "strike-through",
179
+ strike: "strike-through",
180
+ del: "strike-through",
181
+ code: "code",
182
+ sup: "sup",
183
+ sub: "sub",
184
+ ins: "ins",
185
+ mark: "mark",
186
+ small: "small"
187
+ }, HTML_LIST_ITEM_TAGS = {
188
+ li: {
189
+ ...DEFAULT_BLOCK,
190
+ style: BLOCK_DEFAULT_STYLE,
191
+ level: 1,
192
+ listItem: "bullet"
193
+ }
194
+ }, ELEMENT_MAP = {
195
+ ...HTML_BLOCK_TAGS,
196
+ ...HTML_SPAN_TAGS,
197
+ ...HTML_LIST_CONTAINER_TAGS,
198
+ ...HTML_LIST_ITEM_TAGS,
199
+ ...HTML_HEADER_TAGS,
200
+ ...HTML_MISC_TAGS
201
+ };
202
+ [
203
+ ...new Set(
204
+ Object.values(ELEMENT_MAP).filter((tag) => "style" in tag).map((tag) => tag.style)
205
+ )
206
+ ];
207
+ [
208
+ ...new Set(Object.values(HTML_DECORATOR_TAGS))
209
+ ];
210
+ const objectToString = Object.prototype.toString;
211
+ function resolveJsType(val) {
212
+ switch (objectToString.call(val)) {
213
+ case "[object Function]":
214
+ return "function";
215
+ case "[object Date]":
216
+ return "date";
217
+ case "[object RegExp]":
218
+ return "regexp";
219
+ case "[object Arguments]":
220
+ return "arguments";
221
+ case "[object Array]":
222
+ return "array";
223
+ case "[object String]":
224
+ return "string";
225
+ }
226
+ 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;
227
+ }
228
+ function tagName(el) {
229
+ if (el && "tagName" in el)
230
+ return el.tagName.toLowerCase();
231
+ }
232
+ function defaultParseHtml() {
233
+ if (resolveJsType(DOMParser) === "undefined")
234
+ throw new Error(
235
+ "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."
236
+ );
237
+ return (html) => new DOMParser().parseFromString(html, "text/html");
238
+ }
239
+ function ensureRootIsBlocks(schema, objects) {
240
+ return objects.reduce((blocks, node, i, original) => {
241
+ if (node._type === "block")
242
+ return blocks.push(node), blocks;
243
+ if (node._type === "__block")
244
+ return blocks.push(node.block), blocks;
245
+ const lastBlock = blocks[blocks.length - 1];
246
+ if (i > 0 && !isTextBlock({ schema }, original[i - 1]) && isTextBlock({ schema }, lastBlock))
247
+ return lastBlock.children.push(node), blocks;
248
+ const block = {
249
+ ...DEFAULT_BLOCK,
250
+ children: [node]
251
+ };
252
+ return blocks.push(block), blocks;
253
+ }, []);
254
+ }
255
+ function isNodeList(node) {
256
+ return Object.prototype.toString.call(node) === "[object NodeList]";
257
+ }
258
+ function isMinimalSpan(node) {
259
+ return node._type === "span";
260
+ }
261
+ function isMinimalBlock(node) {
262
+ return node._type === "block";
263
+ }
264
+ function isPlaceholderDecorator(node) {
265
+ return node._type === "__decorator";
266
+ }
267
+ function isPlaceholderAnnotation(node) {
268
+ return node._type === "__annotation";
269
+ }
270
+ function isElement(node) {
271
+ return node.nodeType === 1;
272
+ }
273
+ export {
274
+ BLOCK_DEFAULT_STYLE,
275
+ DEFAULT_BLOCK,
276
+ DEFAULT_SPAN,
277
+ HTML_BLOCK_TAGS,
278
+ HTML_DECORATOR_TAGS,
279
+ HTML_HEADER_TAGS,
280
+ HTML_LIST_CONTAINER_TAGS,
281
+ HTML_LIST_ITEM_TAGS,
282
+ HTML_SPAN_TAGS,
283
+ PRESERVE_WHITESPACE_TAGS,
284
+ defaultParseHtml,
285
+ ensureRootIsBlocks,
286
+ flattenNestedBlocks,
287
+ isElement,
288
+ isEqualMarks,
289
+ isMinimalBlock,
290
+ isMinimalSpan,
291
+ isNodeList,
292
+ isPlaceholderAnnotation,
293
+ isPlaceholderDecorator,
294
+ resolveJsType,
295
+ tagName
296
+ };
297
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sources":["../../src/types.ts","../../src/deserializer/equality.ts","../../src/deserializer/flatten-nested-blocks.ts","../../src/deserializer/constants.ts","../../src/deserializer/resolve-js-type.ts","../../src/deserializer/helpers.ts"],"sourcesContent":["import type {PortableTextObject} from '@portabletext/schema'\n\n/**\n * @public\n */\nexport interface TypedObject {\n _type: string\n _key?: string\n}\n\n/**\n * @public\n */\nexport interface ArbitraryTypedObject extends TypedObject {\n [key: string]: unknown\n}\n\nexport function isArbitraryTypedObject(\n object: unknown,\n): object is ArbitraryTypedObject {\n return isRecord(object) && typeof object['_type'] === 'string'\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && (typeof value === 'object' || typeof value === 'function')\n}\n\nexport interface MinimalSpan {\n _type: 'span'\n _key?: string\n text: string\n marks?: string[]\n}\n\nexport interface MinimalBlock extends TypedObject {\n _type: 'block'\n children: TypedObject[]\n markDefs?: TypedObject[]\n style?: string\n level?: number\n listItem?: string\n}\n\nexport interface PlaceholderDecorator {\n _type: '__decorator'\n name: string\n children: TypedObject[]\n}\n\nexport interface PlaceholderAnnotation {\n _type: '__annotation'\n markDef: PortableTextObject\n children: TypedObject[]\n}\n\n/**\n * @public\n */\nexport type HtmlParser = (html: string) => Document\n\n/**\n * @public\n */\nexport interface DeserializerRule {\n deserialize: (\n el: Node,\n next: (\n elements: Node | Node[] | NodeList,\n ) => TypedObject | TypedObject[] | undefined,\n createBlock: (props: ArbitraryTypedObject) => {\n _type: string\n block: ArbitraryTypedObject\n },\n ) => TypedObject | TypedObject[] | undefined\n}\n","export function isEqualMarks(\n a: Array<string> | undefined,\n b: Array<string> | undefined,\n): boolean {\n if (!a || !b) {\n return a === b\n }\n\n if (a.length !== b.length) {\n return false\n }\n\n for (let index = 0; index < a.length; index++) {\n if (a[index] !== b[index]) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * More or less copied from Remeda (https://github.com/remeda/remeda/blob/main/packages/remeda/src/isDeepEqual.ts)\n */\nexport function isDeepEqual<A, B>(data: A, other: B) {\n return isDeepEqualImplementation(data, other)\n}\n\nfunction isDeepEqualImplementation<T>(data: unknown, other: T): data is T {\n if (data === other) {\n return true\n }\n\n if (Object.is(data, other)) {\n // We want to ignore the slight differences between `===` and `Object.is` as\n // both of them largely define equality from a semantic point-of-view.\n return true\n }\n\n if (typeof data !== 'object' || typeof other !== 'object') {\n return false\n }\n\n if (data === null || other === null) {\n return false\n }\n\n if (Object.getPrototypeOf(data) !== Object.getPrototypeOf(other)) {\n // If the objects don't share a prototype it's unlikely that they are\n // semantically equal. It is technically possible to build 2 prototypes that\n // act the same but are not equal (at the reference level, checked via\n // `===`) and then create 2 objects that are equal although we would fail on\n // them. Because this is so unlikely, the optimization we gain here for the\n // rest of the function by assuming that `other` is of the same type as\n // `data` is more than worth it.\n return false\n }\n\n if (Array.isArray(data)) {\n return isDeepEqualArrays(data, other as unknown as ReadonlyArray<unknown>)\n }\n\n if (data instanceof Map) {\n return isDeepEqualMaps(data, other as unknown as Map<unknown, unknown>)\n }\n\n if (data instanceof Set) {\n return isDeepEqualSets(data, other as unknown as Set<unknown>)\n }\n\n if (data instanceof Date) {\n return data.getTime() === (other as unknown as Date).getTime()\n }\n\n if (data instanceof RegExp) {\n return data.toString() === (other as unknown as RegExp).toString()\n }\n\n // At this point we only know that the 2 objects share a prototype and are not\n // any of the previous types. They could be plain objects (Object.prototype),\n // they could be classes, they could be other built-ins, or they could be\n // something weird. We assume that comparing values by keys is enough to judge\n // their equality.\n\n if (Object.keys(data).length !== Object.keys(other).length) {\n return false\n }\n\n for (const [key, value] of Object.entries(data)) {\n if (!(key in other)) {\n return false\n }\n\n if (\n !isDeepEqualImplementation(\n value,\n // @ts-expect-error [ts7053] - We already checked that `other` has `key`\n other[key],\n )\n ) {\n return false\n }\n }\n\n return true\n}\n\nfunction isDeepEqualArrays(\n data: ReadonlyArray<unknown>,\n other: ReadonlyArray<unknown>,\n): boolean {\n if (data.length !== other.length) {\n return false\n }\n\n for (const [index, item] of data.entries()) {\n if (!isDeepEqualImplementation(item, other[index])) {\n return false\n }\n }\n\n return true\n}\n\nfunction isDeepEqualMaps(\n data: ReadonlyMap<unknown, unknown>,\n other: ReadonlyMap<unknown, unknown>,\n): boolean {\n if (data.size !== other.size) {\n return false\n }\n\n for (const [key, value] of data.entries()) {\n if (!other.has(key)) {\n return false\n }\n\n if (!isDeepEqualImplementation(value, other.get(key))) {\n return false\n }\n }\n\n return true\n}\n\nfunction isDeepEqualSets(\n data: ReadonlySet<unknown>,\n other: ReadonlySet<unknown>,\n): boolean {\n if (data.size !== other.size) {\n return false\n }\n\n // To ensure we only count each item once we need to \"remember\" which items of\n // the other set we've already matched against. We do this by creating a copy\n // of the other set and removing items from it as we find them in the data\n // set.\n const otherCopy = [...other]\n\n for (const dataItem of data) {\n let isFound = false\n\n for (const [index, otherItem] of otherCopy.entries()) {\n if (isDeepEqualImplementation(dataItem, otherItem)) {\n isFound = true\n otherCopy.splice(index, 1)\n break\n }\n }\n\n if (!isFound) {\n return false\n }\n }\n\n return true\n}\n","import type {Schema} from '@portabletext/schema'\nimport {\n isSpan,\n isTextBlock,\n type PortableTextBlock,\n type PortableTextObject,\n type PortableTextSpan,\n type PortableTextTextBlock,\n} from '@portabletext/schema'\nimport {\n isArbitraryTypedObject,\n type ArbitraryTypedObject,\n type TypedObject,\n} from '../types'\nimport {isDeepEqual} from './equality'\n\nexport function flattenNestedBlocks(\n context: {\n schema: Schema\n },\n blocks: Array<ArbitraryTypedObject>,\n): TypedObject[] {\n const flattened = blocks.flatMap((block) => {\n if (isBlockContainer(block)) {\n return flattenNestedBlocks(context, [block.block])\n }\n\n if (isTextBlock(context, block)) {\n const hasBlockObjects = block.children.some((child) => {\n const knownBlockObject = context.schema.blockObjects.some(\n (blockObject) => blockObject.name === child._type,\n )\n return knownBlockObject\n })\n const hasBlocks = block.children.some(\n (child) => child._type === '__block' || child._type === 'block',\n )\n\n if (hasBlockObjects || hasBlocks) {\n const splitChildren = getSplitChildren(context, block)\n\n const firstSlice = splitChildren[0]\n if (\n splitChildren.length === 1 &&\n firstSlice &&\n firstSlice.type === 'children' &&\n isDeepEqual(firstSlice.children, block.children)\n ) {\n return [block]\n }\n\n return splitChildren.flatMap((slice) => {\n if (slice.type === 'block object') {\n return [slice.block]\n }\n\n if (slice.type === 'block') {\n return flattenNestedBlocks(context, [\n slice.block as ArbitraryTypedObject,\n ])\n }\n\n if (slice.children.length > 0) {\n if (\n slice.children.every(\n (child) => isSpan(context, child) && child.text.trim() === '',\n )\n ) {\n return []\n }\n\n return flattenNestedBlocks(context, [\n {\n ...block,\n children: slice.children,\n },\n ])\n }\n\n return []\n })\n }\n\n return [block]\n }\n\n return [block]\n })\n\n return flattened\n}\n\nfunction isBlockContainer(\n block: ArbitraryTypedObject,\n): block is BlockContainer {\n return block['_type'] === '__block' && isArbitraryTypedObject(block['block'])\n}\n\ntype BlockContainer = {\n _type: '__block'\n block: ArbitraryTypedObject\n}\n\nfunction getSplitChildren(\n context: {schema: Schema},\n block: PortableTextTextBlock,\n) {\n return block.children.reduce(\n (slices, child) => {\n const knownInlineObject = context.schema.inlineObjects.some(\n (inlineObject) => inlineObject.name === child._type,\n )\n const knownBlockObject = context.schema.blockObjects.some(\n (blockObject) => blockObject.name === child._type,\n )\n\n const lastSlice = slices.pop()\n\n if (!isSpan(context, child) && !knownInlineObject) {\n if (knownBlockObject) {\n return [\n ...slices,\n ...(lastSlice ? [lastSlice] : []),\n {type: 'block object' as const, block: child},\n ]\n }\n }\n\n if (child._type === '__block') {\n return [\n ...slices,\n ...(lastSlice ? [lastSlice] : []),\n {\n type: 'block object' as const,\n block: (child as any).block,\n },\n ]\n }\n\n if (child._type === 'block') {\n return [\n ...slices,\n ...(lastSlice ? [lastSlice] : []),\n {type: 'block' as const, block: child},\n ]\n }\n\n if (lastSlice) {\n if (lastSlice.type === 'children') {\n return [\n ...slices,\n {\n type: 'children' as const,\n children: [...lastSlice.children, child],\n },\n ]\n }\n }\n\n return [\n ...slices,\n ...(lastSlice ? [lastSlice] : []),\n {type: 'children' as const, children: [child]},\n ]\n },\n [] as Array<\n | {\n type: 'children'\n children: Array<PortableTextSpan | PortableTextObject>\n }\n | {type: 'block object'; block: PortableTextObject}\n | {type: 'block'; block: PortableTextBlock}\n >,\n )\n}\n","export interface PartialBlock {\n _type: string\n markDefs: string[]\n style: string\n level?: number\n listItem?: string\n}\n\nexport const PRESERVE_WHITESPACE_TAGS = ['pre', 'textarea', 'code']\n\nexport const BLOCK_DEFAULT_STYLE = 'normal'\n\nexport const DEFAULT_BLOCK: PartialBlock = Object.freeze({\n _type: 'block',\n markDefs: [],\n style: BLOCK_DEFAULT_STYLE,\n})\n\nexport const DEFAULT_SPAN = Object.freeze({\n _type: 'span',\n marks: [] as string[],\n})\n\nexport const HTML_BLOCK_TAGS = {\n p: DEFAULT_BLOCK,\n blockquote: {...DEFAULT_BLOCK, style: 'blockquote'} as PartialBlock,\n}\n\nexport const HTML_SPAN_TAGS = {\n span: {object: 'text'},\n}\n\nexport const HTML_LIST_CONTAINER_TAGS: Record<\n string,\n {object: null} | undefined\n> = {\n ol: {object: null},\n ul: {object: null},\n}\n\nexport const HTML_HEADER_TAGS: Record<string, PartialBlock | undefined> = {\n h1: {...DEFAULT_BLOCK, style: 'h1'},\n h2: {...DEFAULT_BLOCK, style: 'h2'},\n h3: {...DEFAULT_BLOCK, style: 'h3'},\n h4: {...DEFAULT_BLOCK, style: 'h4'},\n h5: {...DEFAULT_BLOCK, style: 'h5'},\n h6: {...DEFAULT_BLOCK, style: 'h6'},\n}\n\nexport const HTML_MISC_TAGS = {\n br: {...DEFAULT_BLOCK, style: BLOCK_DEFAULT_STYLE} as PartialBlock,\n}\n\nexport const HTML_DECORATOR_TAGS: Record<string, string | undefined> = {\n b: 'strong',\n strong: 'strong',\n\n i: 'em',\n em: 'em',\n\n u: 'underline',\n s: 'strike-through',\n strike: 'strike-through',\n del: 'strike-through',\n\n code: 'code',\n sup: 'sup',\n sub: 'sub',\n ins: 'ins',\n mark: 'mark',\n small: 'small',\n}\n\nexport const HTML_LIST_ITEM_TAGS: Record<string, PartialBlock | undefined> = {\n li: {\n ...DEFAULT_BLOCK,\n style: BLOCK_DEFAULT_STYLE,\n level: 1,\n listItem: 'bullet',\n },\n}\n\nexport const ELEMENT_MAP = {\n ...HTML_BLOCK_TAGS,\n ...HTML_SPAN_TAGS,\n ...HTML_LIST_CONTAINER_TAGS,\n ...HTML_LIST_ITEM_TAGS,\n ...HTML_HEADER_TAGS,\n ...HTML_MISC_TAGS,\n}\n\nexport const DEFAULT_SUPPORTED_STYLES = [\n ...new Set(\n Object.values(ELEMENT_MAP)\n .filter((tag): tag is PartialBlock => 'style' in tag)\n .map((tag) => tag.style),\n ),\n]\n\nexport const DEFAULT_SUPPORTED_DECORATORS = [\n ...new Set(Object.values(HTML_DECORATOR_TAGS)),\n]\n\nexport const DEFAULT_SUPPORTED_ANNOTATIONS = ['link']\n","const objectToString = Object.prototype.toString\n\n// Copied from https://github.com/ForbesLindesay/type-of\n// but inlined to have fine grained control\nexport function resolveJsType(val: unknown) {\n switch (objectToString.call(val)) {\n case '[object Function]':\n return 'function'\n case '[object Date]':\n return 'date'\n case '[object RegExp]':\n return 'regexp'\n case '[object Arguments]':\n return 'arguments'\n case '[object Array]':\n return 'array'\n case '[object String]':\n return 'string'\n default:\n }\n\n if (val === null) {\n return 'null'\n }\n\n if (val === undefined) {\n return 'undefined'\n }\n\n if (\n val &&\n typeof val === 'object' &&\n 'nodeType' in val &&\n (val as {nodeType: unknown}).nodeType === 1\n ) {\n return 'element'\n }\n\n if (val === Object(val)) {\n return 'object'\n }\n\n return typeof val\n}\n","import type {Schema} from '@portabletext/schema'\nimport {isTextBlock, type PortableTextObject} from '@portabletext/schema'\nimport type {\n ArbitraryTypedObject,\n HtmlParser,\n MinimalBlock,\n MinimalSpan,\n PlaceholderAnnotation,\n PlaceholderDecorator,\n TypedObject,\n} from '../types'\nimport {DEFAULT_BLOCK} from './constants'\nimport {resolveJsType} from './resolve-js-type'\n\n/**\n * Utility function that always return a lowerCase version of the element.tagName\n *\n * @param el - Element to get tag name for\n * @returns Lowercase tagName for that element, or undefined if not an element\n */\nexport function tagName(el: HTMLElement | Node | null): string | undefined {\n if (el && 'tagName' in el) {\n return el.tagName.toLowerCase()\n }\n\n return undefined\n}\n\n/**\n * A default `parseHtml` function that returns the html using `DOMParser`.\n *\n * @returns HTML Parser based on `DOMParser`\n */\nexport function defaultParseHtml(): HtmlParser {\n if (resolveJsType(DOMParser) === 'undefined') {\n throw new Error(\n 'The native `DOMParser` global which the `Html` deserializer uses by ' +\n 'default is not present in this environment. ' +\n 'You must supply the `options.parseHtml` function instead.',\n )\n }\n return (html) => {\n return new DOMParser().parseFromString(html, 'text/html')\n }\n}\n\nexport function ensureRootIsBlocks(\n schema: Schema,\n objects: Array<ArbitraryTypedObject>,\n): ArbitraryTypedObject[] {\n return objects.reduce((blocks, node, i, original) => {\n if (node._type === 'block') {\n blocks.push(node)\n return blocks\n }\n\n if (node._type === '__block') {\n blocks.push((node as any).block)\n return blocks\n }\n\n const lastBlock = blocks[blocks.length - 1]\n if (\n i > 0 &&\n !isTextBlock({schema}, original[i - 1]) &&\n isTextBlock({schema}, lastBlock)\n ) {\n lastBlock.children.push(node as PortableTextObject)\n return blocks\n }\n\n const block = {\n ...DEFAULT_BLOCK,\n children: [node],\n }\n\n blocks.push(block)\n return blocks\n }, [] as ArbitraryTypedObject[])\n}\n\nexport function isNodeList(node: unknown): node is NodeList {\n return Object.prototype.toString.call(node) === '[object NodeList]'\n}\n\nexport function isMinimalSpan(node: TypedObject): node is MinimalSpan {\n return node._type === 'span'\n}\n\nexport function isMinimalBlock(node: TypedObject): node is MinimalBlock {\n return node._type === 'block'\n}\n\nexport function isPlaceholderDecorator(\n node: TypedObject,\n): node is PlaceholderDecorator {\n return node._type === '__decorator'\n}\n\nexport function isPlaceholderAnnotation(\n node: TypedObject,\n): node is PlaceholderAnnotation {\n return node._type === '__annotation'\n}\n\nexport function isElement(node: Node): node is Element {\n return node.nodeType === 1\n}\n"],"names":[],"mappings":"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"}