@@ -9,6 +9,7 @@ import fs from "fs-extra";
99import mime from "mime-types" ;
1010import path from "path" ;
1111import sharp from "sharp" ;
12+ import { nanoid } from "nanoid" ;
1213
1314const convertAsync = promisify ( convert ) ;
1415
@@ -311,3 +312,133 @@ export const convertKeysToSnakeCase = (
311312 Object . entries ( obj ) . map ( ( [ key , value ] ) => [ camelToSnakeCase ( key ) , value ] )
312313 ) ;
313314} ;
315+
316+ interface ProcessedNode {
317+ id : string ;
318+ parentId : string | undefined ;
319+ type : string ;
320+ value : any ;
321+ }
322+ interface parentId {
323+ id : string ;
324+ depth : number ;
325+ }
326+
327+ export const markdownToJson = async ( markdownString : string ) => {
328+ /**
329+ * Bypassing typescript transpiler using eval to use dynamic imports
330+ *
331+ * Source: https://stackoverflow.com/a/70546326
332+ */
333+ const { unified } = await eval ( `import('unified')` ) ;
334+ const { default : remarkParse } = await eval ( `import('remark-parse')` ) ;
335+ const { remarkGfm } = await eval ( `import('remark-gfm')` ) ;
336+
337+ const parsedMd = unified ( )
338+ . use ( remarkParse ) // Parse Markdown to AST
339+ . use ( remarkGfm )
340+ . parse ( markdownString ) ;
341+
342+ console . log ( JSON . stringify ( parsedMd ) ) ;
343+
344+ const parentIdManager : parentId [ ] = [ ] ;
345+
346+ const jsonObj : ProcessedNode [ ] = [ ] ;
347+ parsedMd . children . forEach ( ( node : any ) => {
348+ const isHeading = node . type === "heading" ;
349+
350+ if ( isHeading && node . depth <= ( parentIdManager . at ( - 1 ) ?. depth || 0 ) ) {
351+ for ( let i = parentIdManager . length ; i > 0 ; i -- ) {
352+ parentIdManager . pop ( ) ;
353+ if ( node . depth > ( parentIdManager . at ( - 1 ) ?. depth || 0 ) ) {
354+ break ;
355+ }
356+ }
357+ }
358+ const processedNode = processNode ( node , parentIdManager . at ( - 1 ) ?. id ) ;
359+
360+ if ( isHeading ) {
361+ parentIdManager . push ( { id : processedNode [ 0 ] . id , depth : node . depth } ) ;
362+ }
363+
364+ jsonObj . push ( ...processedNode ) ;
365+ } ) ;
366+
367+ return jsonObj ;
368+ } ;
369+
370+ const type : Record < string , string > = {
371+ heading : "heading" ,
372+ text : "text" ,
373+ list : "list" ,
374+ } ;
375+
376+ const processNode = ( node : any , parentId ?: string ) : ProcessedNode [ ] => {
377+ let value : any ;
378+ let siblingNodes : ProcessedNode [ ] = [ ] ;
379+
380+ if ( node . type === "heading" ) {
381+ value = node . children
382+ . map ( ( childNode : any ) => processText ( childNode ) )
383+ . join ( " " ) ;
384+ } else if ( node . type === "paragraph" ) {
385+ value = node . children
386+ . map ( ( childNode : any ) => processText ( childNode ) )
387+ . join ( " " ) ;
388+ } else if ( node . type === "list" ) {
389+ const processedNodes = node . children . map ( ( childNode : any ) =>
390+ processListItem ( childNode )
391+ ) ;
392+ value = [ ] ;
393+ processedNodes . forEach ( ( pn : any ) => {
394+ value . push ( ...pn . node ) ;
395+ siblingNodes . push ( ...pn . siblings ) ;
396+ } ) ;
397+ }
398+
399+ return [
400+ {
401+ id : nanoid ( ) ,
402+ parentId,
403+ type : type [ node . type as string ] || type . text ,
404+ value,
405+ } ,
406+ ...( siblingNodes || [ ] ) ,
407+ ] ;
408+ } ;
409+
410+ const processText = ( node : any ) => {
411+ return node . value ;
412+ } ;
413+
414+ const processListItem = ( node : any ) => {
415+ let newNode : ProcessedNode [ ] = [ ] ;
416+ let siblings : ProcessedNode [ ] = [ ] ;
417+
418+ node . children . forEach ( ( childNode : any ) => {
419+ if ( childNode . type !== "list" ) {
420+ const processedNode = processNode ( childNode ) ;
421+ if ( newNode . length > 0 ) {
422+ newNode [ 0 ] . value += processedNode . map ( ( { value } ) => value ) . join ( ", " ) ;
423+ } else {
424+ newNode [ 0 ] = processedNode [ 0 ] ;
425+ }
426+ siblings . push ( ...processedNode . slice ( 1 ) ) ;
427+ } else {
428+ if ( newNode . length == 0 ) {
429+ newNode = [
430+ {
431+ id : nanoid ( ) ,
432+ type : "text" ,
433+ value : "" ,
434+ parentId : undefined ,
435+ } ,
436+ ] ;
437+ }
438+ const processedNode = processNode ( childNode , newNode [ 0 ] . id ) ;
439+ siblings . push ( ...processedNode ) ;
440+ }
441+ } ) ;
442+
443+ return { node : newNode , siblings } ;
444+ } ;
0 commit comments