@storyblok/richtext
Version:
Storyblok RichText Resolver
1 lines • 49.1 kB
Source Map (JSON)
{"version":3,"file":"markdown-parser.mjs","names":[],"sources":["../src/images-optimization.ts","../src/utils/index.ts","../src/types/index.ts","../src/extensions/utils.ts","../src/extensions/nodes.ts","../src/extensions/marks.ts","../src/extensions/index.ts","../src/html-parser.ts","../src/markdown-parser.ts"],"sourcesContent":["import type { StoryblokRichTextImageOptimizationOptions } from './types';\n\nexport function optimizeImage(src: string, options?: boolean | Partial<StoryblokRichTextImageOptimizationOptions>): { src: string; attrs: Record<string, any> } {\n if (!options) {\n return { src, attrs: {} };\n }\n let w = 0;\n let h = 0;\n const attrs: Record<string, unknown> = {};\n const filterParams: string[] = [];\n\n function validateAndPushFilterParam(value: number, min: number, max: number, filter: string, filterParams: string[]) {\n if (typeof value !== 'number' || value <= min || value >= max) {\n console.warn(`[StoryblokRichText] - ${filter.charAt(0).toUpperCase() + filter.slice(1)} value must be a number between ${min} and ${max} (inclusive)`);\n }\n else {\n filterParams.push(`${filter}(${value})`);\n }\n }\n\n if (typeof options === 'object') {\n if (options.width !== undefined) {\n if (typeof options.width === 'number' && options.width >= 0) {\n attrs.width = options.width;\n w = options.width;\n }\n else {\n console.warn('[StoryblokRichText] - Width value must be a number greater than or equal to 0');\n }\n }\n if (options.height !== undefined) {\n if (typeof options.height === 'number' && options.height >= 0) {\n attrs.height = options.height;\n h = options.height;\n }\n else {\n console.warn('[StoryblokRichText] - Height value must be a number greater than or equal to 0');\n }\n }\n if (options.height === 0 && options.width === 0) {\n delete attrs.width;\n delete attrs.height;\n console.warn('[StoryblokRichText] - Width and height values cannot both be 0');\n }\n if (options.loading && ['lazy', 'eager'].includes(options.loading)) {\n attrs.loading = options.loading;\n }\n if (options.class) {\n attrs.class = options.class;\n }\n\n if (options.filters) {\n const { filters } = options || {};\n const { blur, brightness, fill, format, grayscale, quality, rotate } = filters || {};\n\n if (blur) {\n validateAndPushFilterParam(blur, 0, 100, 'blur', filterParams);\n }\n if (quality) {\n validateAndPushFilterParam(quality, 0, 100, 'quality', filterParams);\n }\n if (brightness) {\n validateAndPushFilterParam(brightness, 0, 100, 'brightness', filterParams);\n }\n if (fill) {\n filterParams.push(`fill(${fill})`);\n }\n if (grayscale) {\n filterParams.push(`grayscale()`);\n }\n if (rotate && [0, 90, 180, 270].includes(options.filters.rotate || 0)) {\n filterParams.push(`rotate(${rotate})`);\n }\n if (format && ['webp', 'png', 'jpeg'].includes(format)) {\n filterParams.push(`format(${format})`);\n }\n }\n\n // Construct srcset attribute\n if (options.srcset) {\n attrs.srcset = options.srcset.map((entry): string | undefined => {\n if (typeof entry === 'number') {\n return `${src}/m/${entry}x0/${filterParams.length > 0 ? `filters:${filterParams.join(':')}` : ''} ${entry}w`;\n }\n if (Array.isArray(entry) && entry.length === 2) {\n const [entryWidth, entryHeight] = entry;\n return `${src}/m/${entryWidth}x${entryHeight}/${filterParams.length > 0 ? `filters:${filterParams.join(':')}` : ''} ${entryWidth}w`;\n }\n else {\n console.warn('[StoryblokRichText] - srcset entry must be a number or a tuple of two numbers');\n return undefined;\n }\n }).join(', ');\n }\n\n // Construct sizes attribute\n if (options.sizes) {\n attrs.sizes = options.sizes.join(', ');\n }\n }\n\n // server-side WebP support detection https://www.storyblok.com/docs/image-service/#optimize\n // https://a.storyblok.com/f/39898/3310x2192/e4ec08624e/demo-image.jpeg/m/\n let resultSrc = `${src}/m/`;\n if (w > 0 || h > 0) {\n resultSrc = `${resultSrc}${w}x${h}/`;\n }\n if (filterParams.length > 0) {\n resultSrc = `${resultSrc}filters:${filterParams.join(':')}`;\n }\n\n return {\n src: resultSrc,\n attrs,\n };\n}\n","import type { BlockAttributes, MarkNode, StoryblokRichTextNode, TextNode } from '../types';\n\n/**\n * Deep equality comparison for plain objects, arrays, and primitives.\n */\nexport function deepEqual(a: any, b: any): boolean {\n if (a === b) {\n return true;\n }\n if (a === null || a === undefined || b === null || b === undefined) {\n return a === b;\n }\n if (typeof a !== typeof b) {\n return false;\n }\n if (typeof a !== 'object') {\n return false;\n }\n if (Array.isArray(a) !== Array.isArray(b)) {\n return false;\n }\n if (Array.isArray(a)) {\n if (a.length !== (b as any[]).length) {\n return false;\n }\n return a.every((v: any, i: number) => deepEqual(v, (b as any[])[i]));\n }\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) {\n return false;\n }\n return aKeys.every(k => Object.prototype.hasOwnProperty.call(b, k) && deepEqual(a[k], b[k]));\n}\n\n/** Checks if two marks are equal by comparing their type and attrs. */\nexport function markEquals<T>(a: MarkNode<T>, b: MarkNode<T>): boolean {\n return a.type === b.type && deepEqual(a.attrs, b.attrs);\n}\n\n/** Type guard: checks if a node is a text node with at least one mark. */\nexport function isMarkedTextNode<T>(node: StoryblokRichTextNode<T>): node is TextNode<T> {\n return node.type === 'text' && !!(node as TextNode<T>).marks?.length;\n}\n\n/** Returns marks unique to a node (not in the shared set), or undefined if all marks are shared. */\nexport function getUniqueMarks<T>(marks: MarkNode<T>[], shared: MarkNode<T>[]): MarkNode<T>[] | undefined {\n const unique = marks.filter(m => !shared.some(s => markEquals(s, m)));\n return unique.length ? unique : undefined;\n}\n\nexport interface MarkedTextGroup<T> {\n group: TextNode<T>[];\n shared: MarkNode<T>[];\n endIndex: number;\n}\n\n/**\n * Starting at `fromIndex`, collects adjacent marked text nodes that share at least one common mark.\n * Returns null if the node at `fromIndex` is not a marked text node.\n */\nexport function collectMarkedTextGroup<T>(\n children: StoryblokRichTextNode<T>[],\n fromIndex: number,\n): MarkedTextGroup<T> | null {\n const child = children[fromIndex];\n if (!isMarkedTextNode(child)) {\n return null;\n }\n\n const group: TextNode<T>[] = [child];\n let shared: MarkNode<T>[] = child.marks!;\n let j = fromIndex + 1;\n while (j < children.length) {\n const next = children[j];\n if (!isMarkedTextNode(next)) {\n break;\n }\n const nextShared = shared.filter(m =>\n next.marks!.some(n => markEquals(m, n)),\n );\n if (nextShared.length === 0) {\n break;\n }\n shared = nextShared;\n group.push(next);\n j++;\n }\n\n return { group, shared, endIndex: j };\n}\n\nexport const SELF_CLOSING_TAGS = [\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n];\n\nexport const BLOCK_LEVEL_TAGS = [\n 'address',\n 'article',\n 'aside',\n 'blockquote',\n 'canvas',\n 'dd',\n 'div',\n 'dl',\n 'dt',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'header',\n 'hgroup',\n 'hr',\n 'li',\n 'main',\n 'nav',\n 'noscript',\n 'ol',\n 'output',\n 'p',\n 'pre',\n 'section',\n 'table',\n 'tfoot',\n 'ul',\n 'video',\n];\n\n/**\n * Converts an object of attributes to a string.\n *\n * @param {Record<string, string>} [attrs]\n *\n * @returns {string} The string representation of the attributes.\n *\n * @example\n *\n * ```typescript\n * const attrs = {\n * class: 'text-red',\n * style: 'color: red',\n * }\n *\n * const attrsString = attrsToString(attrs)\n *\n * console.log(attrsString) // 'class=\"text-red\" style=\"color: red\"'\n *\n * ```\n *\n */\nexport const attrsToString = (attrs: BlockAttributes = {}) => {\n const { custom, ...attrsWithoutCustom } = attrs;\n const normalizedAttrs = { ...attrsWithoutCustom, ...custom };\n return Object.keys(normalizedAttrs)\n .filter(key => normalizedAttrs[key] != null)\n .map(key => `${key}=\"${String(normalizedAttrs[key]).replace(/&/g, '&').replace(/\"/g, '"')}\"`)\n .join(' ');\n};\n\n/**\n * Converts an object of attributes to a CSS style string.\n *\n * @param {Record<string, string>} [attrs]\n *\n * @returns {string} The string representation of the CSS styles.\n *\n * @example\n *\n * ```typescript\n * const attrs = {\n * color: 'red',\n * fontSize: '16px',\n * }\n *\n * const styleString = attrsToStyle(attrs)\n *\n * console.log(styleString) // 'color: red; font-size: 16px'\n * ```\n */\nexport const attrsToStyle = (attrs: Record<string, string> = {}) => Object.keys(attrs)\n .map(key => `${key}: ${attrs[key]}`)\n .join('; ');\n\n/**\n * Escapes HTML entities in a string.\n *\n * @param {string} unsafeText\n * @return {*} {string}\n *\n * @example\n *\n * ```typescript\n * const unsafeText = '<script>alert(\"Hello\")</script>'\n *\n * const safeText = escapeHtml(unsafeText)\n *\n * console.log(safeText) // '<script>alert(\"Hello\")</script>'\n * ```\n */\nexport function escapeHtml(unsafeText: string): string {\n return unsafeText\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Removes undefined values from an object.\n *\n * @param {Record<string, any>} obj\n * @return {*} {Record<string, any>}\n *\n * @example\n *\n * ```typescript\n * const obj = {\n * name: 'John',\n * age: undefined,\n * }\n *\n * const cleanedObj = cleanObject(obj)\n *\n * console.log(cleanedObj) // { name: 'John' }\n * ```\n *\n */\nexport const cleanObject = (obj: Record<string, any>) => {\n return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));\n};\n","export enum BlockTypes {\n DOCUMENT = 'doc',\n HEADING = 'heading',\n PARAGRAPH = 'paragraph',\n QUOTE = 'blockquote',\n OL_LIST = 'ordered_list',\n UL_LIST = 'bullet_list',\n LIST_ITEM = 'list_item',\n CODE_BLOCK = 'code_block',\n HR = 'horizontal_rule',\n BR = 'hard_break',\n IMAGE = 'image',\n EMOJI = 'emoji',\n COMPONENT = 'blok',\n TABLE = 'table',\n TABLE_ROW = 'tableRow',\n TABLE_CELL = 'tableCell',\n TABLE_HEADER = 'tableHeader',\n}\n\nexport enum MarkTypes {\n BOLD = 'bold',\n STRONG = 'strong',\n STRIKE = 'strike',\n UNDERLINE = 'underline',\n ITALIC = 'italic',\n CODE = 'code',\n LINK = 'link',\n ANCHOR = 'anchor',\n STYLED = 'styled',\n SUPERSCRIPT = 'superscript',\n SUBSCRIPT = 'subscript',\n TEXT_STYLE = 'textStyle',\n HIGHLIGHT = 'highlight',\n}\n\nexport enum TextTypes {\n TEXT = 'text',\n}\n\nexport enum LinkTargets {\n SELF = '_self',\n BLANK = '_blank',\n}\n\nexport enum LinkTypes {\n URL = 'url',\n STORY = 'story',\n ASSET = 'asset',\n EMAIL = 'email',\n}\n\n/**\n * Represents text alignment attributes that can be applied to block-level elements.\n */\nexport interface TextAlignmentAttrs {\n textAlign?: 'left' | 'center' | 'right' | 'justify';\n}\n\n/**\n * Represents common attributes that can be applied to block-level elements.\n */\nexport interface BlockAttributes extends TextAlignmentAttrs {\n class?: string;\n id?: string;\n [key: string]: any;\n}\n\nexport interface StoryblokRichTextDocumentNode {\n type: string;\n content?: StoryblokRichTextDocumentNode[];\n attrs?: BlockAttributes;\n text?: string;\n marks?: StoryblokRichTextDocumentNode[];\n}\n\nexport type StoryblokRichTextNodeTypes = BlockTypes | MarkTypes | TextTypes;\n\nexport interface StoryblokRichTextNode<T = string> {\n type: StoryblokRichTextNodeTypes;\n content: StoryblokRichTextNode<T>[];\n children?: T;\n attrs?: BlockAttributes;\n text?: string;\n}\n\nexport interface LinkNode<T = string> extends StoryblokRichTextNode<T> {\n type: MarkTypes.LINK | MarkTypes.ANCHOR;\n linktype: LinkTypes;\n attrs: BlockAttributes;\n}\n\nexport interface MarkNode<T = string> extends StoryblokRichTextNode<T> {\n type: MarkTypes.BOLD |\n MarkTypes.ITALIC |\n MarkTypes.UNDERLINE |\n MarkTypes.STRIKE |\n MarkTypes.CODE |\n MarkTypes.LINK |\n MarkTypes.ANCHOR |\n MarkTypes.STYLED |\n MarkTypes.SUPERSCRIPT |\n MarkTypes.SUBSCRIPT |\n MarkTypes.TEXT_STYLE |\n MarkTypes.HIGHLIGHT;\n attrs?: BlockAttributes;\n}\n\nexport interface TextNode<T = string> extends StoryblokRichTextNode<T> {\n type: TextTypes.TEXT;\n text: string;\n marks?: MarkNode<T>[];\n}\n\n/**\n * Represents the configuration options for optimizing images in rich text content.\n */\nexport interface StoryblokRichTextImageOptimizationOptions {\n /**\n * CSS class to be applied to the image.\n */\n class: string;\n\n /**\n * Width of the image in pixels.\n */\n width: number;\n\n /**\n * Height of the image in pixels.\n */\n height: number;\n\n /**\n * Loading strategy for the image. 'lazy' loads the image when it enters the viewport. 'eager' loads the image immediately.\n */\n loading: 'lazy' | 'eager';\n\n /**\n * Optional filters that can be applied to the image to adjust its appearance.\n *\n * @example\n *\n * ```typescript\n * const filters: Partial<StoryblokRichTextImageOptimizationOptions['filters']> = {\n * blur: 5,\n * brightness: 150,\n * grayscale: true\n * }\n * ```\n */\n filters: Partial<{\n blur: number;\n brightness: number;\n fill: 'transparent';\n format: 'webp' | 'png' | 'jpg';\n grayscale: boolean;\n quality: number;\n rotate: 0 | 90 | 180 | 270;\n }>;\n\n /**\n * Defines a set of source set values that tell the browser different image sizes to load based on screen conditions.\n * The entries can be just the width in pixels or a tuple of width and pixel density.\n *\n * @example\n *\n * ```typescript\n * const srcset: (number | [number, number])[] = [\n * 320,\n * [640, 2]\n * ]\n * ```\n */\n srcset: (number | [number, number])[];\n\n /**\n * A list of sizes that correspond to different viewport widths, instructing the browser on which srcset source to use.\n *\n * @example\n *\n * ```typescript\n * const sizes: string[] = [\n * '(max-width: 320px) 280px',\n * '(max-width: 480px) 440px',\n * '800px'\n * ]\n * ```\n */\n sizes: string[];\n}\n\n/**\n * Represents the options for rendering rich text.\n */\nexport interface StoryblokRichTextOptions<T = string, S = (tag: string, attrs: BlockAttributes, children?: T) => T> {\n /**\n * Defines the function that will be used to render the final HTML string (vanilla) or Framework component (React, Vue).\n *\n * @example\n *\n * ```typescript\n * const renderFn = (tag: string, attrs: Record<string, any>, text?: string) => {\n * return `<${tag} ${Object.keys(attrs).map(key => `${key}=\"${attrs[key]}\"`).join(' ')}>${text}</${tag}>`\n * }\n *\n * const options: StoryblokRichTextOptions = {\n * renderFn\n * }\n * ```\n */\n renderFn?: S;\n\n /**\n * Defines the function that will be used to render HTML text.\n *\n * @example\n *\n * ```typescript\n * import { h, createTextVNode } from 'vue'\n *\n * const options: StoryblokRichTextOptions = {\n * renderFn: h,\n * textFn: createTextVNode\n * }\n * ```\n */\n textFn?: (text: string, attrs?: BlockAttributes) => T;\n\n /**\n * Defines opt-out image optimization options.\n *\n * @example\n *\n * ```typescript\n * const options: StoryblokRichTextOptions = {\n * optimizeImages: true\n * }\n * ```\n *\n * @example\n *\n * ```typescript\n * const options: StoryblokRichTextOptions = {\n * optimizeImages: {\n * class: 'my-image',\n * width: 800,\n * height: 600,\n * loading: 'lazy',\n * }\n * ```\n */\n optimizeImages?: boolean | Partial<StoryblokRichTextImageOptimizationOptions>;\n /**\n * Defines whether to use the key attribute in the resolvers for framework use cases.\n * @default false\n * @example\n *\n * ```typescript\n *\n * const options: StoryblokRichTextOptions = {\n * renderFn: h,\n * keyedResolvers: true\n * }\n * ```\n */\n keyedResolvers?: boolean;\n /**\n * Custom tiptap extensions to override or add node/mark rendering.\n * Extensions are merged with the built-in defaults, overriding by key.\n */\n tiptapExtensions?: Record<string, any>;\n}\n","import type { BlockAttributes } from '../types';\nimport { LinkTypes } from '../types';\nimport { cleanObject } from '../utils';\n\n/**\n * Processes block-level attributes, converting textAlign to inline style\n * and preserving class/id/existing style.\n */\nexport function processBlockAttrs(attrs: BlockAttributes = {}): BlockAttributes {\n const { textAlign, class: className, id: idName, style: existingStyle, ...rest } = attrs;\n const styles: string[] = [];\n\n if (existingStyle) {\n styles.push(existingStyle.endsWith(';') ? existingStyle : `${existingStyle};`);\n }\n\n if (textAlign) {\n styles.push(`text-align: ${textAlign};`);\n }\n\n return cleanObject({\n ...rest,\n class: className,\n id: idName,\n ...(styles.length > 0 ? { style: styles.join(' ') } : {}),\n });\n}\n\n/**\n * Resolves a Storyblok link's attributes into a final href and remaining attrs.\n */\nexport function resolveStoryblokLink(attrs: Record<string, any> = {}): { href: string; rest: Record<string, any> } {\n const { linktype, href, anchor, uuid, custom, ...rest } = attrs;\n\n let finalHref = href ?? '';\n switch (linktype) {\n case LinkTypes.ASSET:\n case LinkTypes.URL:\n break;\n case LinkTypes.EMAIL:\n if (finalHref && !finalHref.startsWith('mailto:')) {\n finalHref = `mailto:${finalHref}`;\n }\n break;\n case LinkTypes.STORY:\n if (anchor) {\n finalHref = `${finalHref}#${anchor}`;\n }\n break;\n default:\n break;\n }\n\n return { href: finalHref, rest: { ...rest, ...(custom || {}) } };\n}\n\n/**\n * Computes table cell attributes, converting colwidth/backgroundColor/textAlign to CSS styles.\n */\nexport function computeTableCellAttrs(attrs: Record<string, any> = {}): BlockAttributes {\n const { colspan, rowspan, colwidth, backgroundColor, textAlign, ...rest } = attrs;\n const styles: string[] = [];\n\n if (colwidth) {\n styles.push(`width: ${colwidth}px;`);\n }\n\n if (backgroundColor) {\n styles.push(`background-color: ${backgroundColor};`);\n }\n\n if (textAlign) {\n styles.push(`text-align: ${textAlign};`);\n }\n\n return cleanObject({\n ...rest,\n ...(colspan > 1 ? { colspan } : {}),\n ...(rowspan > 1 ? { rowspan } : {}),\n ...(styles.length > 0 ? { style: styles.join(' ') } : {}),\n });\n}\n\n/**\n * List of supported HTML attributes by tag name, used by the Reporter mark.\n */\nexport const supportedAttributesByTagName: Record<string, string[]> = {\n a: ['href', 'target', 'data-uuid', 'data-anchor', 'data-linktype'],\n img: ['alt', 'src', 'title'],\n span: ['class'],\n} as const;\n\n/**\n * Gets allowed style classes for an element, warning on invalid ones.\n */\nexport function getAllowedStylesForElement(element: HTMLElement, { allowedStyles }: { allowedStyles: string[] }): string[] {\n const classString = element.getAttribute('class') || '';\n const classes = classString.split(' ').filter(Boolean);\n if (!classes.length) {\n return [];\n }\n\n const invalidStyles = classes.filter(x => !allowedStyles.includes(x));\n for (const invalidStyle of invalidStyles) {\n console.warn(`[StoryblokRichText] - \\`class\\` \"${invalidStyle}\" on \\`<${element.tagName.toLowerCase()}>\\` can not be transformed to rich text.`);\n }\n\n return allowedStyles.filter(x => classes.includes(x));\n}\n","import { Node } from '@tiptap/core';\nimport { BulletList, ListItem, OrderedList } from '@tiptap/extension-list';\nimport { Details, DetailsContent, DetailsSummary } from '@tiptap/extension-details';\nimport { Table, TableCell, TableHeader, TableRow } from '@tiptap/extension-table';\nimport Blockquote from '@tiptap/extension-blockquote';\nimport CodeBlock from '@tiptap/extension-code-block';\nimport Document from '@tiptap/extension-document';\nimport Emoji from '@tiptap/extension-emoji';\nimport HardBreak from '@tiptap/extension-hard-break';\nimport Heading from '@tiptap/extension-heading';\nimport HorizontalRule from '@tiptap/extension-horizontal-rule';\nimport Image from '@tiptap/extension-image';\nimport Paragraph from '@tiptap/extension-paragraph';\nimport Text from '@tiptap/extension-text';\nimport { optimizeImage } from '../images-optimization';\nimport type { StoryblokRichTextImageOptimizationOptions } from '../types';\nimport { cleanObject } from '../utils';\nimport { computeTableCellAttrs, processBlockAttrs } from './utils';\nimport TextAlign from '@tiptap/extension-text-align';\n\n// Re-export unmodified extensions\nexport { Details, DetailsContent, DetailsSummary, Document, Text };\n\nexport const StoryblokTextAlign = TextAlign.configure({\n types: ['heading', 'paragraph'],\n});\n// Blockquote, Paragraph, Heading need processBlockAttrs for textAlign support\nexport const StoryblokBlockquote = Blockquote.extend({\n renderHTML({ HTMLAttributes }) {\n return ['blockquote', processBlockAttrs(HTMLAttributes), 0];\n },\n});\n\nexport const StoryblokParagraph = Paragraph.extend({\n renderHTML({ HTMLAttributes }) {\n return ['p', processBlockAttrs(HTMLAttributes), 0];\n },\n});\n\nexport const StoryblokHeading = Heading.extend({\n renderHTML({ node, HTMLAttributes }) {\n const { level, ...rest } = HTMLAttributes;\n return [`h${node.attrs.level}`, processBlockAttrs(rest), 0];\n },\n});\n\nexport const StoryblokTableRow = TableRow.extend({\n renderHTML({ HTMLAttributes }) {\n return ['tr', processBlockAttrs(HTMLAttributes), 0];\n },\n});\n\n// Storyblok uses snake_case names for some extensions\nexport const StoryblokBulletList = BulletList.extend({\n name: 'bullet_list',\n addOptions() {\n return { ...this.parent!(), itemTypeName: 'list_item' };\n },\n renderHTML({ HTMLAttributes }) {\n return ['ul', processBlockAttrs(HTMLAttributes), 0];\n },\n});\n\nexport const StoryblokOrderedList = OrderedList.extend({\n name: 'ordered_list',\n addAttributes() {\n return {\n order: {\n default: 1,\n },\n };\n },\n addOptions() {\n return { ...this.parent!(), itemTypeName: 'list_item' };\n },\n renderHTML({ HTMLAttributes }) {\n return ['ol', processBlockAttrs(HTMLAttributes), 0];\n },\n});\n\nexport const StoryblokListItem = ListItem.extend({\n name: 'list_item',\n addOptions() {\n return { ...this.parent!(), bulletListTypeName: 'bullet_list', orderedListTypeName: 'ordered_list' };\n },\n renderHTML({ HTMLAttributes }) {\n return ['li', processBlockAttrs(HTMLAttributes), 0];\n },\n});\n\nexport const StoryblokCodeBlock = CodeBlock.extend({\n name: 'code_block',\n addAttributes() {\n return {\n class: {\n default: null,\n },\n };\n },\n renderHTML({ node, HTMLAttributes }) {\n const { language: _, ...rest } = HTMLAttributes;\n const attrs = processBlockAttrs(rest);\n const lang = node.attrs.language;\n const codeAttrs = lang ? { class: `language-${lang}` } : {};\n return ['pre', attrs, ['code', codeAttrs, 0]];\n },\n});\nexport const StoryblokHardBreak = HardBreak.extend({ name: 'hard_break' });\nexport const StoryblokHorizontalRule = HorizontalRule.extend({ name: 'horizontal_rule' });\n\n// Table with custom renderHTML\n// Note: thead/tbody grouping is handled by the richtext renderer,\n// which inspects child rows to detect header vs body rows.\nexport const StoryblokTable = Table.extend({\n renderHTML({ HTMLAttributes }) {\n const attrs = processBlockAttrs(HTMLAttributes);\n return ['table', attrs, 0];\n },\n});\n\n// Table cell with custom style handling\nexport const StoryblokTableCell = TableCell.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n colspan: {\n default: 1,\n },\n rowspan: {\n default: 1,\n },\n colwidth: {\n default: null,\n parseHTML: (element: HTMLElement) => {\n const colwidth = element.getAttribute('colwidth');\n return colwidth ? colwidth.split(',').map(Number) : null;\n },\n },\n backgroundColor: {\n default: null,\n },\n };\n },\n renderHTML({ HTMLAttributes }) {\n return ['td', computeTableCellAttrs(HTMLAttributes), 0];\n },\n});\n\n// Table header with custom style handling\nexport const StoryblokTableHeader = TableHeader.extend({\n renderHTML({ HTMLAttributes }) {\n return ['th', computeTableCellAttrs(HTMLAttributes), 0];\n },\n});\n\n// Image with optimizeImages support\nexport const StoryblokImage = Image.extend<{ optimizeImages: boolean | Partial<StoryblokRichTextImageOptimizationOptions> }>({\n addOptions() {\n return { ...this.parent?.(), optimizeImages: false };\n },\n renderHTML({ HTMLAttributes }) {\n const { src, alt, title, srcset, sizes } = HTMLAttributes;\n let finalSrc = src;\n let extraAttrs = {};\n\n if (this.options.optimizeImages) {\n const result = optimizeImage(src, this.options.optimizeImages);\n finalSrc = result.src;\n extraAttrs = result.attrs;\n }\n\n return ['img', cleanObject({ src: finalSrc, alt, title, srcset, sizes, ...extraAttrs })];\n },\n});\n\n// Emoji with custom renderHTML\nexport const StoryblokEmoji = Emoji.extend({\n renderHTML({ HTMLAttributes }) {\n return ['img', {\n 'data-emoji': HTMLAttributes.emoji,\n 'data-name': HTMLAttributes.name,\n 'src': HTMLAttributes.fallbackImage,\n 'alt': HTMLAttributes.alt,\n 'style': 'width: 1.25em; height: 1.25em; vertical-align: text-top',\n 'draggable': 'false',\n 'loading': 'lazy',\n }];\n },\n});\n\n// Blok node (component placeholder for vanilla usage)\n// Configure `renderComponent` option to render blok components in framework SDKs.\n// Similar to PHP Tiptap extension's `renderer` callback:\n// https://github.com/storyblok/php-tiptap-extension/blob/main/src/Node/Blok.php\nexport const ComponentBlok = Node.create<{ renderComponent: ((blok: Record<string, unknown>, id?: string) => unknown) | null }>({\n name: 'blok',\n group: 'block',\n atom: true,\n addOptions() {\n return {\n renderComponent: null,\n };\n },\n addAttributes() {\n return {\n id: { default: null },\n body: { default: [] },\n };\n },\n parseHTML() {\n return [{ tag: 'div[data-blok]' }];\n },\n renderHTML({ HTMLAttributes }) {\n console.warn('[StoryblokRichText] - BLOK resolver is not available for vanilla usage. Configure `renderComponent` option on the blok tiptapExtension.');\n return ['span', cleanObject({\n 'data-blok': JSON.stringify(HTMLAttributes?.body?.[0] ?? null),\n 'data-blok-id': HTMLAttributes?.id,\n 'style': 'display: none',\n })];\n },\n});\n","import { Mark } from '@tiptap/core';\nimport Bold from '@tiptap/extension-bold';\nimport Code from '@tiptap/extension-code';\nimport Highlight from '@tiptap/extension-highlight';\nimport Italic from '@tiptap/extension-italic';\nimport LinkOriginal from '@tiptap/extension-link';\nimport Strike from '@tiptap/extension-strike';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport { TextStyleKit } from '@tiptap/extension-text-style';\nimport Underline from '@tiptap/extension-underline';\nimport { attrsToStyle, cleanObject } from '../utils';\nimport { getAllowedStylesForElement, resolveStoryblokLink, supportedAttributesByTagName } from './utils';\n\n// Unmodified mark extensions\nexport { Bold, Code, Italic, Strike, Subscript, Superscript, TextStyleKit, Underline };\n\n// Highlight\nexport const StoryblokHighlight = Highlight.extend({\n addAttributes() {\n return {\n color: {\n default: null,\n },\n };\n },\n});\n\n// Link with Storyblok-specific attributes and renderHTML\nexport const StoryblokLink = LinkOriginal.extend({\n addAttributes() {\n return {\n href: {\n parseHTML: (element: HTMLElement) => element.getAttribute('href'),\n },\n uuid: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute('data-uuid') || null,\n },\n anchor: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute('data-anchor') || null,\n },\n target: {\n parseHTML: (element: HTMLElement) => element.getAttribute('target') || null,\n },\n linktype: {\n default: 'url',\n parseHTML: (element: HTMLElement) => element.getAttribute('data-linktype') || 'url',\n },\n };\n },\n renderHTML({ HTMLAttributes }) {\n const { href, rest } = resolveStoryblokLink(HTMLAttributes);\n return ['a', cleanObject({ ...(href ? { href } : {}), ...rest }), 0];\n },\n});\n\n// Link with custom attributes support\nexport const StoryblokLinkWithCustomAttributes = StoryblokLink.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n custom: {\n default: null,\n parseHTML: (element: HTMLElement) => {\n const defaultLinkAttributes = supportedAttributesByTagName.a;\n const customAttributeNames = element.getAttributeNames().filter(n => !defaultLinkAttributes.includes(n));\n const customAttributes: Record<string, string | null> = {};\n for (const attributeName of customAttributeNames) {\n customAttributes[attributeName] = element.getAttribute(attributeName);\n }\n return Object.keys(customAttributes).length ? customAttributes : null;\n },\n },\n };\n },\n});\n\n// Anchor mark (renders as span with id)\nexport const StoryblokAnchor = Mark.create({\n name: 'anchor',\n addAttributes() {\n return {\n id: { default: null },\n };\n },\n parseHTML() {\n return [{ tag: 'span[id]' }];\n },\n renderHTML({ HTMLAttributes }) {\n return ['span', { id: HTMLAttributes.id }, 0];\n },\n});\n\nexport interface StyledOptions {\n allowedStyles?: string[];\n}\n\n// Styled mark with whitelisted CSS classes\nexport const StoryblokStyled = Mark.create<StyledOptions>({\n name: 'styled',\n addAttributes() {\n return {\n class: {\n parseHTML: (element: HTMLElement) => {\n const styles = getAllowedStylesForElement(element, { allowedStyles: this.options.allowedStyles || [] });\n return styles.length ? styles.join(' ') : null;\n },\n },\n };\n },\n parseHTML() {\n return [\n {\n tag: 'span',\n consuming: false,\n getAttrs: (element: HTMLElement) => {\n const styles = getAllowedStylesForElement(element, { allowedStyles: this.options.allowedStyles || [] });\n return styles.length ? null : false;\n },\n },\n ];\n },\n renderHTML({ HTMLAttributes }) {\n const { class: className, ...rest } = HTMLAttributes;\n return ['span', cleanObject({ class: className, style: attrsToStyle(rest) || undefined }), 0];\n },\n});\n\n// TextStyle mark\nexport const StoryblokTextStyle = Mark.create({\n name: 'textStyle',\n addAttributes() {\n return {\n class: { default: null },\n id: { default: null },\n color: { default: null },\n };\n },\n parseHTML() {\n return [{\n tag: 'span',\n consuming: false,\n getAttrs: (element: HTMLElement) => {\n // Only match spans with inline style containing color\n const style = element.getAttribute('style');\n if (style && /color/i.test(style)) {\n return null;\n }\n return false;\n },\n }];\n },\n renderHTML({ HTMLAttributes }) {\n const { class: className, id: idName, ...styleAttrs } = HTMLAttributes;\n return ['span', cleanObject({\n class: className,\n id: idName,\n style: attrsToStyle(styleAttrs) || undefined,\n }), 0];\n },\n});\n\n// Reporter mark: parse-only diagnostic, no renderHTML needed\nexport const Reporter = Mark.create({\n name: 'reporter',\n priority: 0,\n addOptions() {\n return {\n allowCustomAttributes: false,\n };\n },\n parseHTML() {\n return [\n {\n tag: '*',\n consuming: false,\n getAttrs: (element: HTMLElement) => {\n const tagName = element.tagName.toLowerCase();\n if (tagName === 'a' && this.options.allowCustomAttributes) {\n return false;\n }\n\n const unsupportedAttributes = element.getAttributeNames().filter((attr) => {\n const supportedAttrs = tagName in supportedAttributesByTagName ? supportedAttributesByTagName[tagName] : [];\n return !supportedAttrs.includes(attr);\n });\n for (const attr of unsupportedAttributes) {\n console.warn(`[StoryblokRichText] - \\`${attr}\\` \"${element.getAttribute(attr)}\" on \\`<${tagName}>\\` can not be transformed to rich text.`);\n }\n\n return false;\n },\n },\n ];\n },\n});\n","import type { Extension, Mark, Node } from '@tiptap/core';\nimport type { StoryblokRichTextImageOptimizationOptions } from '../types';\nimport {\n ComponentBlok,\n Details,\n DetailsContent,\n DetailsSummary,\n Document,\n StoryblokBlockquote,\n StoryblokBulletList,\n StoryblokCodeBlock,\n StoryblokEmoji,\n StoryblokHardBreak,\n StoryblokHeading,\n StoryblokHorizontalRule,\n StoryblokImage,\n StoryblokListItem,\n StoryblokOrderedList,\n StoryblokParagraph,\n StoryblokTable,\n StoryblokTableCell,\n StoryblokTableHeader,\n StoryblokTableRow,\n StoryblokTextAlign,\n Text,\n} from './nodes';\nimport {\n Bold,\n Code,\n Italic,\n Reporter,\n StoryblokAnchor,\n StoryblokHighlight,\n StoryblokLink,\n StoryblokLinkWithCustomAttributes,\n StoryblokStyled,\n StoryblokTextStyle,\n Strike,\n Subscript,\n Superscript,\n Underline,\n} from './marks';\n\nexport interface StyleOption {\n name: string;\n value: string;\n}\n\nexport interface HTMLParserOptions {\n allowCustomAttributes?: boolean;\n preserveWhitespace?: boolean | 'full';\n tiptapExtensions?: Partial<typeof defaultExtensions & Record<string, Extension | Mark | Node>>;\n styleOptions?: StyleOption[];\n}\n\nexport interface StoryblokExtensionOptions {\n optimizeImages?: boolean | Partial<StoryblokRichTextImageOptimizationOptions>;\n allowCustomAttributes?: boolean;\n styleOptions?: StyleOption[];\n}\n\nconst defaultExtensions = {\n document: Document,\n text: Text,\n paragraph: StoryblokParagraph,\n blockquote: StoryblokBlockquote,\n heading: StoryblokHeading,\n bulletList: StoryblokBulletList,\n orderedList: StoryblokOrderedList,\n listItem: StoryblokListItem,\n codeBlock: StoryblokCodeBlock,\n hardBreak: StoryblokHardBreak,\n horizontalRule: StoryblokHorizontalRule,\n image: StoryblokImage,\n emoji: StoryblokEmoji,\n table: StoryblokTable,\n tableRow: StoryblokTableRow,\n tableCell: StoryblokTableCell,\n tableHeader: StoryblokTableHeader,\n blok: ComponentBlok,\n details: Details,\n detailsContent: DetailsContent,\n detailsSummary: DetailsSummary,\n bold: Bold,\n italic: Italic,\n strike: Strike,\n underline: Underline,\n code: Code,\n superscript: Superscript,\n subscript: Subscript,\n highlight: StoryblokHighlight,\n textStyle: StoryblokTextStyle,\n link: StoryblokLink as typeof StoryblokLink,\n anchor: StoryblokAnchor,\n styled: StoryblokStyled,\n reporter: Reporter,\n textAlign: StoryblokTextAlign,\n};\n\nexport { defaultExtensions };\n\nexport function getStoryblokExtensions(options: StoryblokExtensionOptions = {}) {\n const Link = options.allowCustomAttributes ? StoryblokLinkWithCustomAttributes : StoryblokLink;\n\n return {\n ...defaultExtensions,\n image: StoryblokImage.configure({ optimizeImages: options.optimizeImages || false }),\n link: Link,\n styled: StoryblokStyled.configure({ allowedStyles: options.styleOptions?.map(o => o.value) }),\n reporter: Reporter.configure({ allowCustomAttributes: options.allowCustomAttributes }),\n };\n}\n\nexport * from './marks';\nexport * from './nodes';\nexport { computeTableCellAttrs, processBlockAttrs, resolveStoryblokLink } from './utils';\n","import { generateJSON } from '@tiptap/html';\nimport type { StoryblokRichTextDocumentNode } from './types';\nimport { getStoryblokExtensions } from './extensions';\nimport type { HTMLParserOptions } from './extensions';\n\nexport type { HTMLParserOptions, StyleOption } from './extensions';\nexport type { StyledOptions } from './extensions/marks';\n\nexport function htmlToStoryblokRichtext(\n html: string,\n options: HTMLParserOptions = {},\n) {\n const { preserveWhitespace, tiptapExtensions, ...extensionOptions } = options;\n const allExtensions = getStoryblokExtensions(extensionOptions);\n\n const finalExtensions = tiptapExtensions\n ? { ...allExtensions, ...tiptapExtensions }\n : allExtensions;\n\n return generateJSON(html, Object.values(finalExtensions), {\n preserveWhitespace: preserveWhitespace || false,\n }) as StoryblokRichTextDocumentNode;\n}\n","import MarkdownIt from 'markdown-it';\nimport { type HTMLParserOptions, htmlToStoryblokRichtext } from './html-parser';\n\nexport interface MarkdownParserOptions {\n tiptapExtensions?: HTMLParserOptions['tiptapExtensions'];\n}\n\nexport function markdownToStoryblokRichtext(\n md: string,\n options: MarkdownParserOptions = {},\n) {\n const html = new MarkdownIt({ html: false, linkify: true, typographer: true, breaks: true }).render(md);\n return htmlToStoryblokRichtext(html, options);\n}\n"],"mappings":"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"}