@portabletext/toolkit
Version:
Toolkit of handy utility functions for dealing with Portable Text
1 lines • 35.5 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../src/asserters.ts","../src/sortMarksByOccurences.ts","../src/buildMarksTree.ts","../src/nestLists.ts","../src/spanToPlainText.ts","../src/toPlainText.ts","../src/types.ts"],"sourcesContent":["import type {\n ArbitraryTypedObject,\n PortableTextBlock,\n PortableTextListItemBlock,\n PortableTextSpan,\n TypedObject,\n} from '@portabletext/types'\n\nimport type {ToolkitNestedPortableTextSpan, ToolkitPortableTextList, ToolkitTextNode} from './types'\n\n/**\n * Strict check to determine if node is a correctly formatted Portable Text span.\n *\n * @param node - Node to check\n * @returns True if valid Portable Text span, otherwise false\n */\nexport function isPortableTextSpan(\n node: ArbitraryTypedObject | PortableTextSpan,\n): node is PortableTextSpan {\n return (\n node._type === 'span' &&\n 'text' in node &&\n typeof node.text === 'string' &&\n (typeof node.marks === 'undefined' ||\n (Array.isArray(node.marks) && node.marks.every((mark) => typeof mark === 'string')))\n )\n}\n\n/**\n * Strict check to determine if node is a correctly formatted Portable Text block.\n *\n * @param node - Node to check\n * @returns True if valid Portable Text block, otherwise false\n */\nexport function isPortableTextBlock(\n node: PortableTextBlock | TypedObject,\n): node is PortableTextBlock {\n return (\n // A block doesn't _have_ to be named 'block' - to differentiate between\n // allowed child types and marks, one might name them differently\n typeof node._type === 'string' &&\n // Toolkit-types like nested spans are @-prefixed\n node._type[0] !== '@' &&\n // `markDefs` isn't _required_ per say, but if it's there, it needs to be an array\n (!('markDefs' in node) ||\n !node.markDefs ||\n (Array.isArray(node.markDefs) &&\n // Every mark definition needs to have an `_key` to be mappable in child spans\n node.markDefs.every((def) => typeof def._key === 'string'))) &&\n // `children` is required and needs to be an array\n 'children' in node &&\n Array.isArray(node.children) &&\n // All children are objects with `_type` (usually spans, but can contain other stuff)\n node.children.every((child) => typeof child === 'object' && '_type' in child)\n )\n}\n\n/**\n * Strict check to determine if node is a correctly formatted portable list item block.\n *\n * @param block - Block to check\n * @returns True if valid Portable Text list item block, otherwise false\n */\nexport function isPortableTextListItemBlock(\n block: PortableTextBlock | TypedObject,\n): block is PortableTextListItemBlock {\n return (\n isPortableTextBlock(block) &&\n 'listItem' in block &&\n typeof block.listItem === 'string' &&\n (typeof block.level === 'undefined' || typeof block.level === 'number')\n )\n}\n\n/**\n * Loose check to determine if block is a toolkit list node.\n * Only checks `_type`, assumes correct structure.\n *\n * @param block - Block to check\n * @returns True if toolkit list, otherwise false\n */\nexport function isPortableTextToolkitList(\n block: TypedObject | ToolkitPortableTextList,\n): block is ToolkitPortableTextList {\n return block._type === '@list'\n}\n\n/**\n * Loose check to determine if span is a toolkit span node.\n * Only checks `_type`, assumes correct structure.\n *\n * @param span - Span to check\n * @returns True if toolkit span, otherwise false\n */\nexport function isPortableTextToolkitSpan(\n span: TypedObject | ToolkitNestedPortableTextSpan,\n): span is ToolkitNestedPortableTextSpan {\n return span._type === '@span'\n}\n\n/**\n * Loose check to determine if node is a toolkit text node.\n * Only checks `_type`, assumes correct structure.\n *\n * @param node - Node to check\n * @returns True if toolkit text node, otherwise false\n */\nexport function isPortableTextToolkitTextNode(\n node: TypedObject | ToolkitTextNode,\n): node is ToolkitTextNode {\n return node._type === '@text'\n}\n","import type {PortableTextSpan, TypedObject} from '@portabletext/types'\n\nimport {isPortableTextSpan} from './asserters'\n\nconst knownDecorators = ['strong', 'em', 'code', 'underline', 'strike-through']\n\n/**\n * Figures out the optimal order of marks, in order to minimize the amount of\n * nesting/repeated elements in environments such as HTML. For instance, a naive\n * implementation might render something like:\n *\n * ```html\n * <strong>This block contains </strong>\n * <strong><a href=\"https://some.url/\">a link</a></strong>\n * <strong> and some bolded text</strong>\n * ```\n *\n * ...whereas an optimal order would be:\n *\n * ```html\n * <strong>\n * This block contains <a href=\"https://some.url/\">a link</a> and some bolded text\n * </strong>\n * ```\n *\n * This is particularly necessary for cases like links, where you don't want multiple\n * individual links for different segments of the link text, even if parts of it are\n * bolded/italicized.\n *\n * This function is meant to be used like: `block.children.map(sortMarksByOccurences)`,\n * and is used internally in {@link buildMarksTree | `buildMarksTree()`}.\n *\n * The marks are sorted in the following order:\n *\n * 1. Marks that are shared amongst the most adjacent siblings\n * 2. Non-default marks (links, custom metadata)\n * 3. Decorators (bold, emphasis, code etc), in a predefined, preferred order\n *\n * @param span - The current span to sort\n * @param index - The index of the current span within the block\n * @param blockChildren - All children of the block being sorted\n * @returns Array of decorators and annotations, sorted by \"most adjacent siblings\"\n */\nexport function sortMarksByOccurences(\n span: PortableTextSpan | TypedObject,\n index: number,\n blockChildren: (PortableTextSpan | TypedObject)[],\n): string[] {\n if (!isPortableTextSpan(span) || !span.marks) {\n return []\n }\n\n if (!span.marks.length) {\n return []\n }\n\n // Slicing because we'll be sorting with `sort()`, which mutates\n const marks = span.marks.slice()\n const occurences: Record<string, number> = {}\n marks.forEach((mark) => {\n occurences[mark] = 1\n\n for (let siblingIndex = index + 1; siblingIndex < blockChildren.length; siblingIndex++) {\n const sibling = blockChildren[siblingIndex]\n\n if (\n sibling &&\n isPortableTextSpan(sibling) &&\n Array.isArray(sibling.marks) &&\n sibling.marks.indexOf(mark) !== -1\n ) {\n occurences[mark]++\n } else {\n break\n }\n }\n })\n\n return marks.sort((markA, markB) => sortMarks(occurences, markA, markB))\n}\n\nfunction sortMarks<U extends string, T extends Record<U, number>>(\n occurences: T,\n markA: U,\n markB: U,\n): number {\n const aOccurences = occurences[markA]\n const bOccurences = occurences[markB]\n\n if (aOccurences !== bOccurences) {\n return bOccurences - aOccurences\n }\n\n const aKnownPos = knownDecorators.indexOf(markA)\n const bKnownPos = knownDecorators.indexOf(markB)\n\n // Sort known decorators last\n if (aKnownPos !== bKnownPos) {\n return aKnownPos - bKnownPos\n }\n\n // Sort other marks simply by key\n return markA.localeCompare(markB)\n}\n","import type {\n ArbitraryTypedObject,\n PortableTextBlock,\n PortableTextMarkDefinition,\n} from '@portabletext/types'\n\nimport {isPortableTextSpan} from './asserters'\nimport {sortMarksByOccurences} from './sortMarksByOccurences'\nimport type {ToolkitNestedPortableTextSpan, ToolkitTextNode} from './types'\n\n/**\n * Takes a Portable Text block and returns a nested tree of nodes optimized for rendering\n * in HTML-like environments where you want marks/annotations to be nested inside of eachother.\n * For instance, a naive span-by-span rendering might yield:\n *\n * ```html\n * <strong>This block contains </strong>\n * <strong><a href=\"https://some.url/\">a link</a></strong>\n * <strong> and some bolded and </strong>\n * <em><strong>italicized text</strong></em>\n * ```\n *\n * ...whereas an optimal order would be:\n *\n * ```html\n * <strong>\n * This block contains <a href=\"https://some.url/\">a link</a>\n * and some bolded and <em>italicized text</em>\n * </strong>\n * ```\n *\n * Note that since \"native\" Portable Text spans cannot be nested,\n * this function returns an array of \"toolkit specific\" types:\n * {@link ToolkitTextNode | `@text`} and {@link ToolkitNestedPortableTextSpan | `@span` }.\n *\n * The toolkit-specific type can hold both types, as well as any arbitrary inline objects,\n * creating an actual tree.\n *\n * @param block - The Portable Text block to create a tree of nodes from\n * @returns Array of (potentially) nested spans, text nodes and/or arbitrary inline objects\n */\nexport function buildMarksTree<M extends PortableTextMarkDefinition = PortableTextMarkDefinition>(\n block: PortableTextBlock<M>,\n): (ToolkitNestedPortableTextSpan<M> | ToolkitTextNode | ArbitraryTypedObject)[] {\n const {children} = block\n const markDefs = block.markDefs ?? []\n if (!children || !children.length) {\n return []\n }\n\n const sortedMarks = children.map(sortMarksByOccurences)\n\n const rootNode: ToolkitNestedPortableTextSpan<M> = {\n _type: '@span',\n children: [],\n markType: '<unknown>',\n }\n\n let nodeStack: ToolkitNestedPortableTextSpan<M>[] = [rootNode]\n\n for (let i = 0; i < children.length; i++) {\n const span = children[i]\n if (!span) {\n continue\n }\n\n const marksNeeded = sortedMarks[i] || []\n let pos = 1\n\n // Start at position one. Root is always plain and should never be removed\n if (nodeStack.length > 1) {\n for (pos; pos < nodeStack.length; pos++) {\n const mark = nodeStack[pos]?.markKey || ''\n const index = marksNeeded.indexOf(mark)\n\n if (index === -1) {\n break\n }\n\n marksNeeded.splice(index, 1)\n }\n }\n\n // Keep from beginning to first miss\n nodeStack = nodeStack.slice(0, pos)\n\n // Add needed nodes\n let currentNode = nodeStack[nodeStack.length - 1]\n if (!currentNode) {\n continue\n }\n\n for (const markKey of marksNeeded) {\n const markDef = markDefs?.find((def) => def._key === markKey)\n const markType = markDef ? markDef._type : markKey\n const node: ToolkitNestedPortableTextSpan<M> = {\n _type: '@span',\n _key: span._key,\n children: [],\n markDef,\n markType,\n markKey,\n }\n\n currentNode.children.push(node)\n nodeStack.push(node)\n currentNode = node\n }\n\n // Split at newlines to make individual line chunks, but keep newline\n // characters as individual elements in the array. We use these characters\n // in the span serializer to trigger hard-break rendering\n if (isPortableTextSpan(span)) {\n const lines = span.text.split('\\n')\n for (let line = lines.length; line-- > 1; ) {\n lines.splice(line, 0, '\\n')\n }\n\n currentNode.children = currentNode.children.concat(\n lines.map((text) => ({_type: '@text', text})),\n )\n } else {\n // This is some other inline object, not a text span\n currentNode.children = currentNode.children.concat(span)\n }\n }\n\n return rootNode.children\n}\n","import type {PortableTextBlock, PortableTextListItemBlock, TypedObject} from '@portabletext/types'\n\nimport {\n isPortableTextListItemBlock,\n isPortableTextSpan,\n isPortableTextToolkitList,\n} from './asserters'\nimport type {\n ToolkitListNestMode,\n ToolkitPortableTextDirectList,\n ToolkitPortableTextHtmlList,\n ToolkitPortableTextList,\n ToolkitPortableTextListItem,\n} from './types'\n\nexport type ToolkitNestListsOutputNode<T> =\n | T\n | ToolkitPortableTextHtmlList\n | ToolkitPortableTextDirectList\n\n/**\n * Takes an array of blocks and returns an array of nodes optimized for rendering in HTML-like\n * environment, where lists are nested inside of eachother instead of appearing \"flat\" as in\n * native Portable Text data structures.\n *\n * Note that the list node is not a native Portable Text node type, and thus is represented\n * using the {@link ToolkitPortableTextList | `@list`} type name (`{_type: '@list'}`).\n *\n * The nesting can be configured in two modes:\n *\n * - `direct`: deeper list nodes will appear as a direct child of the parent list\n * - `html`, deeper list nodes will appear as a child of the last _list item_ in the parent list\n *\n * When using `direct`, all list nodes will be of type {@link ToolkitPortableTextDirectList},\n * while with `html` they will be of type {@link ToolkitPortableTextHtmlList}\n *\n * These modes are available as {@link LIST_NEST_MODE_HTML} and {@link LIST_NEST_MODE_DIRECT}.\n *\n * @param blocks - Array of Portable Text blocks and other arbitrary types\n * @param mode - Mode to use for nesting, `direct` or `html`\n * @returns Array of potentially nested nodes optimized for rendering\n */\nexport function nestLists<T extends TypedObject = PortableTextBlock | TypedObject>(\n blocks: T[],\n mode: 'direct',\n): (T | ToolkitPortableTextDirectList)[]\nexport function nestLists<T extends TypedObject = PortableTextBlock | TypedObject>(\n blocks: T[],\n mode: 'html',\n): (T | ToolkitPortableTextHtmlList)[]\nexport function nestLists<T extends TypedObject = PortableTextBlock | TypedObject>(\n blocks: T[],\n mode: 'direct' | 'html',\n): (T | ToolkitPortableTextHtmlList | ToolkitPortableTextDirectList)[]\nexport function nestLists<T extends TypedObject = PortableTextBlock | TypedObject>(\n blocks: T[],\n mode: ToolkitListNestMode,\n): ToolkitNestListsOutputNode<T>[] {\n const tree: ToolkitNestListsOutputNode<T>[] = []\n let currentList: ToolkitPortableTextList | undefined\n\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i]\n if (!block) {\n continue\n }\n\n if (!isPortableTextListItemBlock(block)) {\n tree.push(block)\n currentList = undefined\n continue\n }\n\n // Start of a new list?\n if (!currentList) {\n currentList = listFromBlock(block, i, mode)\n tree.push(currentList)\n continue\n }\n\n // New list item within same list?\n if (blockMatchesList(block, currentList)) {\n currentList.children.push(block)\n continue\n }\n\n // Different list props, are we going deeper?\n if ((block.level || 1) > currentList.level) {\n const newList = listFromBlock(block, i, mode)\n\n if (mode === 'html') {\n // Because HTML is kinda weird, nested lists needs to be nested within list items.\n // So while you would think that we could populate the parent list with a new sub-list,\n // we actually have to target the last list element (child) of the parent.\n // However, at this point we need to be very careful - simply pushing to the list of children\n // will mutate the input, and we don't want to blindly clone the entire tree.\n\n // Clone the last child while adding our new list as the last child of it\n const lastListItem = currentList.children[\n currentList.children.length - 1\n ] as ToolkitPortableTextListItem\n\n const newLastChild: ToolkitPortableTextListItem = {\n ...lastListItem,\n children: [...lastListItem.children, newList],\n }\n\n // Swap the last child\n currentList.children[currentList.children.length - 1] = newLastChild\n } else {\n ;(currentList as ToolkitPortableTextDirectList).children.push(\n newList as ToolkitPortableTextDirectList,\n )\n }\n\n // Set the newly created, deeper list as the current\n currentList = newList\n continue\n }\n\n // Different list props, are we going back up the tree?\n if ((block.level || 1) < currentList.level) {\n // Current list has ended, and we need to hook up with a parent of the same level and type\n const matchingBranch = tree[tree.length - 1]\n const match = matchingBranch && findListMatching(matchingBranch, block)\n if (match) {\n currentList = match\n currentList.children.push(block)\n continue\n }\n\n // Similar parent can't be found, assume new list\n currentList = listFromBlock(block, i, mode)\n tree.push(currentList)\n continue\n }\n\n // Different list props, different list style?\n if (block.listItem !== currentList.listItem) {\n const matchingBranch = tree[tree.length - 1]\n const match = matchingBranch && findListMatching(matchingBranch, {level: block.level || 1})\n if (match && match.listItem === block.listItem) {\n currentList = match\n currentList.children.push(block)\n continue\n } else {\n currentList = listFromBlock(block, i, mode)\n tree.push(currentList)\n continue\n }\n }\n\n // eslint-disable-next-line no-console\n console.warn('Unknown state encountered for block', block)\n tree.push(block)\n }\n\n return tree\n}\n\nfunction blockMatchesList(block: PortableTextBlock, list: ToolkitPortableTextList) {\n return (block.level || 1) === list.level && block.listItem === list.listItem\n}\n\nfunction listFromBlock(\n block: PortableTextListItemBlock,\n index: number,\n mode: ToolkitListNestMode,\n): ToolkitPortableTextList {\n return {\n _type: '@list',\n _key: `${block._key || `${index}`}-parent`,\n mode,\n level: block.level || 1,\n listItem: block.listItem,\n children: [block],\n }\n}\n\nfunction findListMatching<T extends TypedObject | PortableTextBlock>(\n rootNode: T,\n matching: Partial<PortableTextListItemBlock>,\n): ToolkitPortableTextList | undefined {\n const level = matching.level || 1\n const style = matching.listItem || 'normal'\n const filterOnType = typeof matching.listItem === 'string'\n if (\n isPortableTextToolkitList(rootNode) &&\n (rootNode.level || 1) === level &&\n filterOnType &&\n (rootNode.listItem || 'normal') === style\n ) {\n return rootNode\n }\n\n if (!('children' in rootNode)) {\n return undefined\n }\n\n const node = rootNode.children[rootNode.children.length - 1]\n return node && !isPortableTextSpan(node) ? findListMatching(node, matching) : undefined\n}\n","import {isPortableTextToolkitSpan, isPortableTextToolkitTextNode} from './asserters'\nimport type {ToolkitNestedPortableTextSpan} from './types'\n\n/**\n * Returns the plain-text representation of a\n * {@link ToolkitNestedPortableTextSpan | toolkit-specific Portable Text span}.\n *\n * Useful if you have a subset of nested nodes and want the text from just those,\n * instead of for the entire Portable Text block.\n *\n * @param span - Span node to get text from (Portable Text toolkit specific type)\n * @returns The plain-text version of the span\n */\nexport function spanToPlainText(span: ToolkitNestedPortableTextSpan): string {\n let text = ''\n span.children.forEach((current) => {\n if (isPortableTextToolkitTextNode(current)) {\n text += current.text\n } else if (isPortableTextToolkitSpan(current)) {\n text += spanToPlainText(current)\n }\n })\n return text\n}\n","import type {ArbitraryTypedObject, PortableTextBlock} from '@portabletext/types'\n\nimport {isPortableTextBlock, isPortableTextSpan} from './asserters'\n\nconst leadingSpace = /^\\s/\nconst trailingSpace = /\\s$/\n\n/**\n * Takes a Portable Text block (or an array of them) and returns the text value\n * of all the Portable Text span nodes. Adds whitespace when encountering inline,\n * non-span nodes to ensure text flow is optimal.\n *\n * Note that this only accounts for regular Portable Text blocks - any text inside\n * custom content types are not included in the output.\n *\n * @param block - Single block or an array of blocks to extract text from\n * @returns The plain-text content of the blocks\n */\nexport function toPlainText(\n block: PortableTextBlock | ArbitraryTypedObject[] | PortableTextBlock[],\n): string {\n const blocks = Array.isArray(block) ? block : [block]\n let text = ''\n\n blocks.forEach((current, index) => {\n if (!isPortableTextBlock(current)) {\n return\n }\n\n let pad = false\n current.children.forEach((span) => {\n if (isPortableTextSpan(span)) {\n // If the previous element was a non-span, and we have no natural whitespace\n // between the previous and the next span, insert it to give the spans some\n // room to breathe. However, don't do so if this is the first span.\n text += pad && text && !trailingSpace.test(text) && !leadingSpace.test(span.text) ? ' ' : ''\n text += span.text\n pad = false\n } else {\n pad = true\n }\n })\n\n if (index !== blocks.length - 1) {\n text += '\\n\\n'\n }\n })\n\n return text\n}\n","import type {\n ArbitraryTypedObject,\n PortableTextListItemBlock,\n PortableTextMarkDefinition,\n PortableTextSpan,\n} from '@portabletext/types'\n\n/**\n * List nesting mode for HTML, see the {@link nestLists | `nestLists()` function}\n */\nexport const LIST_NEST_MODE_HTML = 'html'\n\n/**\n * List nesting mode for direct, nested lists, see the {@link nestLists | `nestLists()` function}\n */\nexport const LIST_NEST_MODE_DIRECT = 'direct'\n\n/**\n * List nesting mode, see the {@link nestLists | `nestLists()` function}\n */\nexport type ToolkitListNestMode = 'html' | 'direct'\n\n/**\n * Toolkit-specific type representing a nested list\n *\n * See the `nestLists()` function for more info\n */\nexport type ToolkitPortableTextList = ToolkitPortableTextHtmlList | ToolkitPortableTextDirectList\n\n/**\n * Toolkit-specific type representing a nested list in HTML mode, where deeper lists are nested\n * inside of the _list items_, eg `<ul><li>Some text<ul><li>Deeper</li></ul></li></ul>`\n */\nexport interface ToolkitPortableTextHtmlList {\n /**\n * Type name, prefixed with `@` to signal that this is a toolkit-specific node.\n */\n _type: '@list'\n\n /**\n * Unique key for this list (within its parent)\n */\n _key: string\n\n /**\n * List mode, signaling that list nodes will appear as children of the _list items_\n */\n mode: 'html'\n\n /**\n * Level/depth of this list node (starts at `1`)\n */\n level: number\n\n /**\n * Style of this list item (`bullet`, `number` are common values, but can be customized)\n */\n listItem: string\n\n /**\n * Child nodes of this list - toolkit-specific list items which can themselves hold deeper lists\n */\n children: ToolkitPortableTextListItem[]\n}\n\n/**\n * Toolkit-specific type representing a nested list in \"direct\" mode, where deeper lists are nested\n * inside of the lists children, alongside other blocks.\n */\nexport interface ToolkitPortableTextDirectList {\n /**\n * Type name, prefixed with `@` to signal that this is a toolkit-specific node.\n */\n _type: '@list'\n\n /**\n * Unique key for this list (within its parent)\n */\n _key: string\n\n /**\n * List mode, signaling that list nodes can appear as direct children\n */\n mode: 'direct'\n\n /**\n * Level/depth of this list node (starts at `1`)\n */\n level: number\n\n /**\n * Style of this list item (`bullet`, `number` are common values, but can be customized)\n */\n listItem: string\n\n /**\n * Child nodes of this list - either portable text list items, or another, deeper list\n */\n children: (PortableTextListItemBlock | ToolkitPortableTextDirectList)[]\n}\n\n/**\n * Toolkit-specific type representing a list item block, but where the children can be another list\n */\nexport interface ToolkitPortableTextListItem\n extends PortableTextListItemBlock<\n PortableTextMarkDefinition,\n PortableTextSpan | ToolkitPortableTextList\n > {}\n\n/**\n * Toolkit-specific type representing a text node, used when nesting spans.\n *\n * See the {@link buildMarksTree | `buildMarksTree()` function}\n */\nexport interface ToolkitTextNode {\n /**\n * Type name, prefixed with `@` to signal that this is a toolkit-specific node.\n */\n _type: '@text'\n\n /**\n * The actual string value of the text node\n */\n text: string\n}\n\n/**\n * Toolkit-specific type representing a portable text span that can hold other spans.\n * In this type, each span only has a single mark, instead of an array of them.\n */\nexport interface ToolkitNestedPortableTextSpan<\n M extends PortableTextMarkDefinition = PortableTextMarkDefinition,\n> {\n /**\n * Type name, prefixed with `@` to signal that this is a toolkit-specific node.\n */\n _type: '@span'\n\n /**\n * Unique key for this span\n */\n _key?: string\n\n /**\n * Holds the value (definition) of the mark in the case of annotations.\n * `undefined` if the mark is a decorator (strong, em or similar).\n */\n markDef?: M\n\n /**\n * The key of the mark definition (in the case of annotations).\n * `undefined` if the mark is a decorator (strong, em or similar).\n */\n markKey?: string\n\n /**\n * Type of the mark. For annotations, this is the `_type` property of the value.\n * For decorators, it will hold the name of the decorator (strong, em or similar).\n */\n markType: string\n\n /**\n * Child nodes of this span. Can be toolkit-specific text nodes, nested spans\n * or any inline object type.\n */\n children: (\n | ToolkitTextNode\n | ToolkitNestedPortableTextSpan<PortableTextMarkDefinition>\n | ArbitraryTypedObject\n )[]\n}\n"],"names":["isPortableTextSpan","node","_type","text","marks","Array","isArray","every","mark","isPortableTextBlock","markDefs","def","_key","children","child","isPortableTextListItemBlock","block","listItem","level","isPortableTextToolkitList","isPortableTextToolkitSpan","span","isPortableTextToolkitTextNode","knownDecorators","sortMarksByOccurences","index","blockChildren","length","slice","occurences","forEach","siblingIndex","sibling","indexOf","sort","markA","markB","sortMarks","aOccurences","bOccurences","aKnownPos","bKnownPos","localeCompare","buildMarksTree","_a","_b","sortedMarks","map","rootNode","markType","nodeStack","i","marksNeeded","pos","markKey","splice","currentNode","markDef","find","push","lines","split","line","concat","nestLists","blocks","mode","tree","currentList","listFromBlock","blockMatchesList","newList","lastListItem","newLastChild","_objectSpread","matchingBranch","match","findListMatching","console","warn","list","matching","style","filterOnType","spanToPlainText","current","leadingSpace","trailingSpace","toPlainText","pad","test","LIST_NEST_MODE_HTML","LIST_NEST_MODE_DIRECT"],"mappings":"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"}