import escapeHtml from 'escape-html' ; import { BlockType , LeafType , NodeTypes } from '../types/types' ; import { DEFAULT_NODE_TYPES , BREAK_TAG , LIST_TYPES } from './constants' ; interface Options { nodeTypes : NodeTypes ; listDepth ? : number ; olListItemIndex ? : number ; ignoreParagraphNewline ? : boolean ; } const reverseStr = ( string ) => string . split ( '' ) . reverse ( ) . join ( '' ) ; const isLeafNode = ( node ) : node is LeafType => { return typeof ( node as LeafType ) . text === 'string' ; } ; const retainWhitespaceAndFormat = ( string , format ) => { const frozenString = string . trim ( ) ; let children = frozenString ; const fullFormat = `${ format }${ children }${ reverseStr ( format ) }` ; if ( children . length === string . length ) { return fullFormat ; } const formattedString = format + children + reverseStr ( format ) ; return string . replace ( frozenString , formattedString ) ; } ; /**
 * Most of the code is coming from https://github.com/hanford/remark-slate.
 * However it was working until Slate < 0.51.0. They changed the way how
 * each nodes were represented in the next versions. This supports Slate 0.59
 * and fixes a couple of issues that were in the current npm library.
 */ export const serialize = ( data , opts = { nodeTypes : DEFAULT_NODE_TYPES } ) => data . map ( ( chunk ) => { const { nodeTypes : userNodeTypes = DEFAULT_NODE_TYPES , ignoreParagraphNewline = false , listDepth = 0 , } = opts ; let text = ( chunk as LeafType ) . text || '' ; let type = ( chunk as BlockType ) . type || '' ; const nodeTypes = { ... DEFAULT_NODE_TYPES , ... userNodeTypes , heading : { ... DEFAULT_NODE_TYPES . heading , ... userNodeTypes . heading , } , } ; let children = text ; if ( ! isLeafNode ( chunk ) ) { children = chunk . children . map ( ( c , index ) => { const isList = ! isLeafNode ( c ) ? LIST_TYPES . includes ( c . type || '' ) : false ; const selfIsList = LIST_TYPES . includes ( chunk . type || '' ) ; let childrenHasLink = false ; if ( ! isLeafNode ( chunk ) && Array . isArray ( chunk . children ) ) { childrenHasLink = chunk . children . some ( ( f ) => ! isLeafNode ( f ) && f . type === nodeTypes . link ) ; } return serialize ( [ { ... c , parentType : type } ] , { nodeTypes , ignoreParagraphNewline : ( ignoreParagraphNewline || isList || selfIsList || childrenHasLink ) && ! ( c as BlockType ) . break , listDepth : LIST_TYPES . includes ( ( c as BlockType ) . type || '' ) ? listDepth + 1 : listDepth , olListItemIndex : type === nodeTypes . ol_list ? index + 1 : undefined , } ) ; } ) . join ( '' ) ; } if ( ! ignoreParagraphNewline && ( text === '' || text === '\n' ) && chunk . parentType === nodeTypes . paragraph ) { type = nodeTypes . paragraph ; children = BREAK_TAG ; } if ( children === '' ) { return ; } if ( children !== BREAK_TAG && isLeafNode ( chunk ) ) { if ( chunk . strikeThrough && chunk . bold && chunk . italic ) { children = retainWhitespaceAndFormat ( children , '~~***' ) ; } else if ( chunk . bold && chunk . italic ) { children = retainWhitespaceAndFormat ( children , '***' ) ; } else { if ( chunk . bold ) { children = retainWhitespaceAndFormat ( children , '**' ) ; } if ( chunk . italic ) { children = retainWhitespaceAndFormat ( children , '_' ) ; } if ( chunk . strikeThrough ) { children = retainWhitespaceAndFormat ( children , '~~' ) ; } } } switch ( type ) { case nodeTypes . heading [ 1 ] : return `# ${ children }\n\n` ; case nodeTypes . heading [ 2 ] : return `## ${ children }\n\n` ; case nodeTypes . heading [ 3 ] : return `### ${ children }\n\n` ; case nodeTypes . heading [ 4 ] : return `#### ${ children }\n\n` ; case nodeTypes . heading [ 5 ] : return `##### ${ children }\n\n` ; case nodeTypes . heading [ 6 ] : return `###### ${ children }\n\n` ; case nodeTypes . block_quote : return `> ${ children }\n\n` ; case nodeTypes . code_block : return `\`\`\`${ ( chunk as BlockType ) . language || '' }\n${ children }\n\`\`\`\n` ; case nodeTypes . link : const url = ( chunk as { url ? : string ; } ) ?. url ; return url ? `[${ children }](${ url })` : children ; case nodeTypes . ul_list : case nodeTypes . ol_list : return `\n${ children }\n` ; case nodeTypes . listItem : const isOL = chunk && chunk . parentType === nodeTypes . ol_list ; let spacer = '' ; for ( let k = 0 ; listDepth > k ; k ++ ) { if ( isOL ) { spacer += '   ' ; } else { spacer += '  ' ; } } return `${ spacer }${ isOL ? `${ opts . olListItemIndex }.` : '-' } ${ children }\n` ; case nodeTypes . paragraph : return `${ children }\n\n` ; default : return escapeHtml ( children ) ; } } ) ; export const serializeAndFormat = ( data ) => { const firstLineBreak = /^[\n]/g ; const lastItemLineBreak = /[\n]{2}$/g ; return serialize ( data ) . map ( ( line , index , arr ) => { if ( index === arr . length - 1 ) { return line ?. replace ( lastItemLineBreak , '' ) ; } return line ?. replace ( firstLineBreak , '' ) ; } ) . join ( '' ) ; } ;