🚸 Rewrite the markdown deserializer to improve br… (#1198)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Updated markdown handling and serialization libraries for improved performance and accuracy in text formatting. - **New Features** - Enhanced rich text and markdown conversion capabilities, providing users with more reliable and seamless text formatting options. - **Documentation** - Added detailed documentation for markdown to rich text conversion and vice versa, ensuring easier understanding and implementation for developers. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
253
packages/lib/markdown/remark-slate/remarkDefaultElementRules.ts
Normal file
253
packages/lib/markdown/remark-slate/remarkDefaultElementRules.ts
Normal file
@@ -0,0 +1,253 @@
|
||||
import { ELEMENT_BLOCKQUOTE } from '@udecode/plate-block-quote'
|
||||
import {
|
||||
ELEMENT_CODE_BLOCK,
|
||||
ELEMENT_CODE_LINE,
|
||||
} from '@udecode/plate-code-block'
|
||||
import {
|
||||
getPluginType,
|
||||
TDescendant,
|
||||
TElement,
|
||||
TText,
|
||||
Value,
|
||||
} from '@udecode/plate-common'
|
||||
import {
|
||||
ELEMENT_H1,
|
||||
ELEMENT_H2,
|
||||
ELEMENT_H3,
|
||||
ELEMENT_H4,
|
||||
ELEMENT_H5,
|
||||
ELEMENT_H6,
|
||||
} from '@udecode/plate-heading'
|
||||
import { ELEMENT_HR } from '@udecode/plate-horizontal-rule'
|
||||
import { ELEMENT_LINK } from '@udecode/plate-link'
|
||||
import {
|
||||
ELEMENT_LI,
|
||||
ELEMENT_LIC,
|
||||
ELEMENT_OL,
|
||||
ELEMENT_UL,
|
||||
} from '@udecode/plate-list'
|
||||
import { ELEMENT_IMAGE } from '@udecode/plate-media'
|
||||
import { ELEMENT_PARAGRAPH } from '@udecode/plate-paragraph'
|
||||
|
||||
import { remarkTransformElementChildren } from './remarkTransformElementChildren'
|
||||
import { MdastNode, RemarkElementRules, RemarkPluginOptions } from './types'
|
||||
|
||||
export const remarkDefaultElementRules: RemarkElementRules<Value> = {
|
||||
heading: {
|
||||
transform: (node, lastLineNumber, options) => {
|
||||
const headingType = {
|
||||
1: ELEMENT_H1,
|
||||
2: ELEMENT_H2,
|
||||
3: ELEMENT_H3,
|
||||
4: ELEMENT_H4,
|
||||
5: ELEMENT_H5,
|
||||
6: ELEMENT_H6,
|
||||
}[node.depth ?? 1]
|
||||
|
||||
return [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
{
|
||||
type: getPluginType(options.editor, headingType),
|
||||
children: remarkTransformElementChildren(
|
||||
node,
|
||||
lastLineNumber,
|
||||
options
|
||||
),
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
list: {
|
||||
transform: (node, lastLineNumber, options) => {
|
||||
if (options.indentList) {
|
||||
const listStyleType = node.ordered ? 'decimal' : 'disc'
|
||||
|
||||
const parseListItems = (
|
||||
_node: MdastNode,
|
||||
listItems: TElement[] = [],
|
||||
indent = 1
|
||||
) => {
|
||||
_node.children!.forEach((listItem) => {
|
||||
const [paragraph, ...subLists] = listItem.children!
|
||||
|
||||
listItems.push({
|
||||
type: getPluginType(options.editor, ELEMENT_PARAGRAPH),
|
||||
listStyleType,
|
||||
indent,
|
||||
children: remarkTransformElementChildren(
|
||||
paragraph || '',
|
||||
lastLineNumber,
|
||||
options
|
||||
),
|
||||
})
|
||||
|
||||
subLists.forEach((subList) => {
|
||||
parseListItems(subList, listItems, indent + 1)
|
||||
})
|
||||
})
|
||||
|
||||
return listItems
|
||||
}
|
||||
|
||||
return [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
parseListItems(node),
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
{
|
||||
type: getPluginType(
|
||||
options.editor,
|
||||
node.ordered ? ELEMENT_OL : ELEMENT_UL
|
||||
),
|
||||
children: remarkTransformElementChildren(
|
||||
node,
|
||||
lastLineNumber,
|
||||
options
|
||||
),
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
listItem: {
|
||||
transform: (node, lastLineNumber, options) => ({
|
||||
type: getPluginType(options.editor, ELEMENT_LI),
|
||||
children: remarkTransformElementChildren(
|
||||
node,
|
||||
lastLineNumber,
|
||||
options
|
||||
).map(
|
||||
(child) =>
|
||||
({
|
||||
...child,
|
||||
type:
|
||||
child.type === getPluginType(options.editor, ELEMENT_PARAGRAPH)
|
||||
? getPluginType(options.editor, ELEMENT_LIC)
|
||||
: child.type,
|
||||
} as TDescendant)
|
||||
),
|
||||
}),
|
||||
},
|
||||
paragraph: {
|
||||
transform: (node, lastLineNumber, options) => {
|
||||
const children = remarkTransformElementChildren(
|
||||
node,
|
||||
lastLineNumber,
|
||||
options
|
||||
)
|
||||
|
||||
const paragraphType = getPluginType(options.editor, ELEMENT_PARAGRAPH)
|
||||
const splitBlockTypes = new Set([
|
||||
getPluginType(options.editor, ELEMENT_IMAGE),
|
||||
])
|
||||
|
||||
const elements: TElement[] = []
|
||||
let inlineNodes: TDescendant[] = []
|
||||
|
||||
const flushInlineNodes = () => {
|
||||
if (inlineNodes.length > 0) {
|
||||
elements.push({
|
||||
type: paragraphType,
|
||||
children: inlineNodes,
|
||||
})
|
||||
|
||||
inlineNodes = []
|
||||
}
|
||||
}
|
||||
|
||||
children.forEach((child) => {
|
||||
const { type } = child
|
||||
|
||||
if (type && splitBlockTypes.has(type as string)) {
|
||||
flushInlineNodes()
|
||||
elements.push(child as TElement)
|
||||
} else {
|
||||
inlineNodes.push(child)
|
||||
}
|
||||
})
|
||||
|
||||
flushInlineNodes()
|
||||
|
||||
return [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
...elements,
|
||||
]
|
||||
},
|
||||
},
|
||||
link: {
|
||||
transform: (node, lastLineNumber, options) => [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
{
|
||||
type: getPluginType(options.editor, ELEMENT_LINK),
|
||||
url: node.url,
|
||||
children: remarkTransformElementChildren(node, lastLineNumber, options),
|
||||
},
|
||||
],
|
||||
},
|
||||
image: {
|
||||
transform: (node, lastLineNumber, options) => [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
{
|
||||
type: getPluginType(options.editor, ELEMENT_IMAGE),
|
||||
children: [{ text: '' } as TText],
|
||||
url: node.url,
|
||||
caption: [{ text: node.alt } as TText],
|
||||
},
|
||||
],
|
||||
},
|
||||
blockquote: {
|
||||
transform: (node, lastLineNumber, options) => [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
{
|
||||
type: getPluginType(options.editor, ELEMENT_BLOCKQUOTE),
|
||||
children: node.children!.flatMap((paragraph) =>
|
||||
remarkTransformElementChildren(paragraph, lastLineNumber, options)
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
code: {
|
||||
transform: (node, lastLineNumber, options) => [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
{
|
||||
type: getPluginType(options.editor, ELEMENT_CODE_BLOCK),
|
||||
lang: node.lang ?? undefined,
|
||||
children: (node.value || '').split('\n').map((line) => ({
|
||||
type: getPluginType(options.editor, ELEMENT_CODE_LINE),
|
||||
children: [{ text: line } as TText],
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
thematicBreak: {
|
||||
transform: (node, lastLineNumber, options) => [
|
||||
...parseLineBreakNodes(node, lastLineNumber, options),
|
||||
{
|
||||
type: getPluginType(options.editor, ELEMENT_HR),
|
||||
children: [{ text: '' } as TText],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const parseLineBreakNodes = (
|
||||
node: MdastNode,
|
||||
lastLineNumber: number,
|
||||
options: RemarkPluginOptions<Value>
|
||||
) => {
|
||||
const lineBreaks = node.position.start.line - lastLineNumber
|
||||
|
||||
let lineBreakNodes = []
|
||||
|
||||
if (lineBreaks > 1)
|
||||
lineBreakNodes.push(
|
||||
...Array(lineBreaks - 1).fill({
|
||||
type: getPluginType(options.editor, ELEMENT_PARAGRAPH),
|
||||
children: [{ text: '' } as TText],
|
||||
})
|
||||
)
|
||||
|
||||
return lineBreakNodes
|
||||
}
|
||||
Reference in New Issue
Block a user