1- import  { 
2-     Block  as  PublicBlock ,  BlockBase ,  Emoji ,  ExternalFile ,  ExternalFileWithCaption ,  File , 
3-     FileWithCaption ,  ImageBlock ,  RichText 
4- }  from  '@notionhq/client/build/src/api-types' ; 
5- 
61import  {  AssetWriter  }  from  './AssetWriter' ; 
2+ import  { 
3+     Block ,  Emoji ,  ExternalFile ,  ExternalFileWithCaption ,  File ,  FileWithCaption ,  ImageBlock 
4+ }  from  './Blocks' ; 
75import  {  DeferredRenderer  }  from  './DeferredRenderer' ; 
86import  {  logger  }  from  './logger' ; 
97import  {  RichTextRenderer  }  from  './RichTextRenderer' ; 
108
119const  debug  =  require ( "debug" ) ( "blocks" ) ; 
1210
13- export  interface  CodeBlock  extends  BlockBase  { 
14-   type : "code" ; 
15-   code : { 
16-     text : RichText [ ] ; 
17-     language : string ; 
18-   } ; 
19- } 
20- 
21- export  interface  QuoteBlock  extends  BlockBase  { 
22-   type : "quote" ; 
23-   code : { 
24-     text : RichText [ ] ; 
25-     language : string ; 
26-   } ; 
27- } 
28- 
29- export  interface  CalloutBlock  extends  BlockBase  { 
30-   type : "callout" ; 
31-   callout : { 
32-     text : RichText [ ] ; 
33-     icon : File  |  ExternalFile  |  Emoji ; 
34-   } ; 
35- } 
36- 
37- export  interface  DividerBlock  extends  BlockBase  { 
38-   type : "divider" ; 
39- } 
40- 
41- export  interface  ChildDatabaseBlock  extends  BlockBase  { 
42-   type : "child_database" ; 
43- } 
44- 
45- // these are blocks that the notion API client code does not have proper typings for 
46- // for unknown reasons they removed types alltogether in v0.4 of the client 
47- // https://github.com/makenotion/notion-sdk-js/pulls?q=is%3Apr+is%3Aclosed#issuecomment-927781781 
48- export  type  Block  = 
49-   |  PublicBlock 
50-   |  CodeBlock 
51-   |  QuoteBlock 
52-   |  CalloutBlock 
53-   |  DividerBlock 
54-   |  ChildDatabaseBlock ; 
55- 
56- 
5711export  interface  BlockRenderResult  { 
5812  lines : string ; 
5913  childIndent ?: number ; 
@@ -62,9 +16,12 @@ export class BlockRenderer {
6216  constructor ( 
6317    private  readonly  richText : RichTextRenderer , 
6418    private  readonly  deferredRenderer : DeferredRenderer 
65-   )  {   } 
19+   )  { } 
6620
67-   async  renderBlock ( block : Block ,  assets : AssetWriter ) : Promise < BlockRenderResult >  { 
21+   async  renderBlock ( 
22+     block : Block , 
23+     assets : AssetWriter 
24+   ) : Promise < BlockRenderResult >  { 
6825    switch  ( block . type )  { 
6926      case  "paragraph" :
7027        return  { 
@@ -73,58 +30,70 @@ export class BlockRenderer {
7330      // note: render headings +1 level, because h1 is reserved for page titles 
7431      case  "heading_1" :
7532        return  { 
76-           lines : "## "  +  ( await  this . richText . renderMarkdown ( block . heading_1 . text ) ) 
33+           lines :
34+             "## "  +  ( await  this . richText . renderMarkdown ( block . heading_1 . text ) ) , 
7735        } ; 
7836      case  "heading_2" :
7937        return  { 
80-           lines : "### "  +  ( await  this . richText . renderMarkdown ( block . heading_2 . text ) ) 
38+           lines :
39+             "### "  +  ( await  this . richText . renderMarkdown ( block . heading_2 . text ) ) , 
8140        } ; 
8241      case  "heading_3" :
8342        return  { 
84-           lines : "#### "  +  ( await  this . richText . renderMarkdown ( block . heading_3 . text ) ) 
43+           lines :
44+             "#### "  + 
45+             ( await  this . richText . renderMarkdown ( block . heading_3 . text ) ) , 
8546        } ; 
8647      case  "bulleted_list_item" :
8748        return  { 
88-           lines : "- "  +  await  this . richText . renderMarkdown ( block . bulleted_list_item . text ) , 
89-           childIndent : 4 
49+           lines :
50+             "- "  + 
51+             ( await  this . richText . renderMarkdown ( block . bulleted_list_item . text ) ) , 
52+           childIndent : 4 , 
9053        } ; 
9154      case  "numbered_list_item" :
9255        return  { 
93-           lines : "1. "  +  await  this . richText . renderMarkdown ( block . numbered_list_item . text ) , 
94-           childIndent : 4 
56+           lines :
57+             "1. "  + 
58+             ( await  this . richText . renderMarkdown ( block . numbered_list_item . text ) ) , 
59+           childIndent : 4 , 
9560        } ; 
9661      case  "to_do" :
9762        return  { 
98-           lines : "[ ] "  +  ( await  this . richText . renderMarkdown ( block . to_do . text ) ) 
63+           lines :
64+             "[ ] "  +  ( await  this . richText . renderMarkdown ( block . to_do . text ) ) , 
9965        } ; 
10066      case  "image" :
10167        return  { 
102-           lines : await  this . renderImage ( block ,  assets ) 
103-         } 
104-       case  "quote" :
105-         block  as  any ; 
106-         return  { 
107-           lines : "> "  +  ( await  this . richText . renderMarkdown ( ( block  as  any ) . quote . text ) ) 
68+           lines : await  this . renderImage ( block ,  assets ) , 
10869        } ; 
109-       case  "code" :
70+       case  "quote" : { 
71+         // it's legal for a notion block to be cmoposed of multiple lines 
72+         // each of them must be prefixed with "> " to be part of the same quote block 
73+         const  content  =  await  this . richText . renderMarkdown ( block . quote . text ) ; 
74+ 
75+         return  {  lines : this . formatAsQuoteBlock ( content )  } ; 
76+       } 
77+       case  "code" : { 
11078        const  code  =  await  this . richText . renderPlainText ( block . code . text ) ; 
11179        if  ( code . startsWith ( "<!--notion-markdown-cms:raw-->" ) )  { 
11280          return  {  lines : code  } ; 
11381        } 
11482
11583        return  { 
116-           lines : 
117-             "```"  + 
118-             block . code . language  + 
119-             "\n"  +  code  + 
120-             "\n```" 
84+           lines : "```"  +  block . code . language  +  "\n"  +  code  +  "\n```" , 
12185        } ; 
122-       case  "callout" :
86+       } 
87+       case  "callout" : { 
88+         // render emoji as bold, this enables css to target it as `blockquote > strong:first-child` 
89+         const  content  = 
90+           `**${ this . renderIcon ( block . callout . icon ) }  ** `  + 
91+           ( await  this . richText . renderMarkdown ( block . callout . text ) ) ; 
92+ 
12393        return  { 
124-           lines :
125-             `> **${ this . renderIcon ( block . callout . icon ) }  ** ` +  // render emoji as bold, this enables css to target it as `blockquote > strong:first-child` 
126-             ( await  this . richText . renderMarkdown ( block . callout . text ) ) , 
94+           lines : this . formatAsQuoteBlock ( content ) , 
12795        } ; 
96+       } 
12897      case  "divider" :
12998        return  {  lines : "---"  } ; 
13099      case  "child_database" :
@@ -142,8 +111,11 @@ export class BlockRenderer {
142111      case  "unsupported" :
143112      default :
144113        return  { 
145-           lines : this . renderUnsupported ( `unsupported block type: ${ block . type }  ` ,  block ) 
146-         } 
114+           lines : this . renderUnsupported ( 
115+             `unsupported block type: ${ block . type }  ` , 
116+             block 
117+           ) , 
118+         } ; 
147119    } 
148120  } 
149121
@@ -179,6 +151,13 @@ export class BlockRenderer {
179151    } 
180152  } 
181153
154+   private  formatAsQuoteBlock ( content : string )  { 
155+     return  content 
156+       . split ( "\n" ) 
157+       . map ( ( x )  =>  "> "  +  x ) 
158+       . join ( "\n" ) ; 
159+   } 
160+ 
182161  private  renderUnsupported ( msg : string ,  obj : any ) : string  { 
183162    logger . warn ( msg ) ; 
184163    debug ( msg  +  "\n%O" ,  obj ) ; 
0 commit comments