@@ -12,13 +12,13 @@ const maxLimit = limitArg ? Number(limitArg.split("=")[1]) : 200;
1212const EXCLUDE_SLUG_REGEX =
1313 / ( f c m - o n - a n d r o i d | a i - l o g i c - u n i t y - a n d r o i d x r | f p n v - | f i r e b a s e - p n v - | c r a s h l y t i c s - a n d r o i d | c r a s h l y t i c s - m c p | c r a s h l y t i c s - e x p o r t | d a r t - f u n c t i o n s - e x p | a p p l e - f o u n d a t i o n - m o d e l s | b u i l d - i o s - a p p | f i r e b a s e - a n a l y t i c s - o u t a g e | b r a z i l - r o u n d u p | i o s - s d k | a n d r o i d - s d k | c o c o a p o d s | s w i f t p m | g r a d l e | w e a r - o s | t v o s | w a t c h o s | f l u t t e r - o n l y ) / i;
1414
15- function cleanDashes ( text ) {
15+ function cleanDashes ( text , { trim = true } = { } ) {
1616 if ( ! text ) return "" ;
17- return String ( text )
17+ const cleaned = String ( text )
1818 . replace ( / \s * [ — – ] \s * / g, ", " )
1919 . replace ( / \s + - - \s + / g, ", " )
20- . replace ( / \s { 2 , } / g, " " )
21- . trim ( ) ;
20+ . replace ( / \s { 2 , } / g, " " ) ;
21+ return trim ? cleaned . trim ( ) : cleaned ;
2222}
2323
2424function decodeHtmlEntities ( str ) {
@@ -35,14 +35,73 @@ function decodeHtmlEntities(str) {
3535}
3636
3737function stripTags ( html ) {
38- return decodeHtmlEntities ( String ( html || "" ) . replace ( / < [ ^ > ] + > / g, "" ) ) . trim ( ) ;
38+ return decodeHtmlEntities (
39+ String ( html || "" )
40+ . replace ( / < b r \s * \/ ? > / gi, " " )
41+ . replace ( / < [ ^ > ] + > / g, "" )
42+ . replace ( / \s + / g, " " )
43+ ) . trim ( ) ;
3944}
4045
4146function makeKey ( prefix , seed ) {
4247 const hash = crypto . createHash ( "md5" ) . update ( String ( seed ) ) . digest ( "hex" ) . slice ( 0 , 10 ) ;
4348 return `fb_${ prefix } _${ hash } ` ;
4449}
4550
51+ export function shouldInsertSpaceBetweenSpans ( pText , cText ) {
52+ if ( ! pText || ! cText ) return false ;
53+ if ( / \s $ / . test ( pText ) || / ^ \s / . test ( cText ) ) return false ;
54+
55+ const lastChar = pText . slice ( - 1 ) ;
56+ const firstChar = cText . slice ( 0 , 1 ) ;
57+
58+ if ( / ^ [ . , ; : ! ? ) \] } ’ ' ] / . test ( firstChar ) ) return false ;
59+ if ( / ^ [ " ” ] ( \s | [ . , ; : ! ? ) ] | $ ) / . test ( cText ) ) return false ;
60+ if ( / [ ( \[ { ] $ / . test ( lastChar ) && pText . length === 1 ) return false ;
61+ if ( / ( ^ | \s ) [ ( \[ { " “ ‘ ] $ / . test ( pText ) ) return false ;
62+ if ( lastChar === "—" || firstChar === "—" || cText . startsWith ( "---" ) || pText . endsWith ( "---" ) ) return false ;
63+ if ( pText === "-" || cText === "-" ) return false ;
64+
65+ return true ;
66+ }
67+
68+ export function normalizeBlockSpacing ( block ) {
69+ if ( ! block || block . _type !== "block" || ! Array . isArray ( block . children ) || block . children . length < 2 ) {
70+ return block ;
71+ }
72+ const linkMarkKeys = new Set ( ( block . markDefs || [ ] ) . map ( ( m ) => m . _key ) ) ;
73+ const children = block . children . map ( ( c ) => ( {
74+ ...c ,
75+ marks : Array . isArray ( c . marks ) ? [ ...c . marks ] : [ ] ,
76+ } ) ) ;
77+
78+ const out = [ ] ;
79+ for ( let i = 0 ; i < children . length ; i ++ ) {
80+ const curr = children [ i ] ;
81+ if ( out . length > 0 ) {
82+ const prev = out [ out . length - 1 ] ;
83+ if ( shouldInsertSpaceBetweenSpans ( prev . text , curr . text ) ) {
84+ const prevHasLinkOrCode = prev . marks . some ( ( m ) => m === "code" || linkMarkKeys . has ( m ) ) ;
85+ const currHasLinkOrCode = curr . marks . some ( ( m ) => m === "code" || linkMarkKeys . has ( m ) ) ;
86+ if ( ! prevHasLinkOrCode ) {
87+ prev . text = `${ prev . text } ` ;
88+ } else if ( ! currHasLinkOrCode ) {
89+ curr . text = ` ${ curr . text } ` ;
90+ } else {
91+ out . push ( {
92+ _type : "span" ,
93+ _key : `${ prev . _key || "sp" } _sep_${ i } ` ,
94+ marks : [ ] ,
95+ text : " " ,
96+ } ) ;
97+ }
98+ }
99+ }
100+ out . push ( curr ) ;
101+ }
102+ return { ...block , children : out } ;
103+ }
104+
46105function createTextBlock ( text , style = "normal" , keySeed = text ) {
47106 return {
48107 _type : "block" ,
@@ -64,7 +123,8 @@ function createRichBlock(segments, { style = "normal", listItem = undefined, lev
64123 const markDefs = [ ] ;
65124 const children = [ ] ;
66125 segments . forEach ( ( seg , i ) => {
67- const rawText = cleanDashes ( seg . text || "" ) ;
126+ const hasMark = Boolean ( seg . href || seg . code || seg . strong ) ;
127+ const rawText = cleanDashes ( seg . text || "" , { trim : hasMark } ) ;
68128 if ( ! rawText ) return ;
69129 const marks = [ ] ;
70130 if ( seg . strong ) marks . push ( "strong" ) ;
@@ -98,7 +158,7 @@ function createRichBlock(segments, { style = "normal", listItem = undefined, lev
98158 block . listItem = listItem ;
99159 block . level = level || 1 ;
100160 }
101- return block ;
161+ return normalizeBlockSpacing ( block ) ;
102162}
103163
104164/**
@@ -113,7 +173,7 @@ function parseInlineHtmlToSegments(html, baseUrl = "https://firebase.blog") {
113173 while ( ( match = tokenRegex . exec ( html ) ) !== null ) {
114174 if ( match . index > lastIndex ) {
115175 const plain = stripTags ( html . slice ( lastIndex , match . index ) ) ;
116- if ( plain ) segments . push ( { text : ` ${ plain } ` } ) ;
176+ if ( plain ) segments . push ( { text : plain } ) ;
117177 }
118178 const fullToken = match [ 1 ] ;
119179 const hrefAttr = match [ 2 ] ;
@@ -124,19 +184,19 @@ function parseInlineHtmlToSegments(html, baseUrl = "https://firebase.blog") {
124184 try {
125185 resolvedHref = new URL ( hrefAttr , baseUrl ) . toString ( ) ;
126186 } catch { }
127- segments . push ( { text : ` ${ innerText } ` , href : resolvedHref , strong : true } ) ;
187+ segments . push ( { text : innerText , href : resolvedHref , strong : true } ) ;
128188 } else if ( / ^ < (?: s t r o n g | b ) > / i. test ( fullToken ) ) {
129- segments . push ( { text : ` ${ innerText } ` , strong : true } ) ;
189+ segments . push ( { text : innerText , strong : true } ) ;
130190 } else if ( / ^ < c o d e > / i. test ( fullToken ) ) {
131- segments . push ( { text : ` ${ innerText } ` , code : true } ) ;
191+ segments . push ( { text : innerText , code : true } ) ;
132192 }
133193 }
134194 lastIndex = tokenRegex . lastIndex ;
135195 }
136196
137197 if ( lastIndex < html . length ) {
138198 const tail = stripTags ( html . slice ( lastIndex ) ) ;
139- if ( tail ) segments . push ( { text : ` ${ tail } ` } ) ;
199+ if ( tail ) segments . push ( { text : tail } ) ;
140200 }
141201
142202 if ( segments . length === 0 ) {
0 commit comments