@@ -3,8 +3,14 @@ import FileType, { FileTypeResult } from "file-type";
33import fetch from "node-fetch" ;
44import * as Path from "path" ;
55import { makeImagePersistencePlan } from "./MakeImagePersistencePlan" ;
6- import { logDebug , verbose , info } from "./log" ;
7- import { ListBlockChildrenResponse } from "@notionhq/client/build/src/api-endpoints" ;
6+ import { warning , logDebug , verbose , info } from "./log" ;
7+ import { ListBlockChildrenResponseResult } from "notion-to-md/build/types" ;
8+
9+ // We several things here:
10+ // 1) copy images locally instead of leaving them in Notion
11+ // 2) change the links to point here
12+ // 3) read the caption and if there are localized images, get those too
13+ // 4) prepare for localized documents, which need a copy of every image
814
915let existingImagesNotSeenYetInPull : string [ ] = [ ] ;
1016let imageOutputPath = "" ; // default to putting in the same directory as the document referring to it.
@@ -59,22 +65,65 @@ export async function initImageHandling(
5965 }
6066}
6167
62- export async function outputImages (
63- blocks : (
64- | ListBlockChildrenResponse
65- | /* not avail in types: BlockObjectResponse so we use any*/ any
66- ) [ ] ,
68+ // This is a "custom transformer" function passed to notion-to-markdown
69+ // eslint-disable-next-line @typescript-eslint/require-await
70+ export async function markdownToMDImageTransformer (
71+ block : ListBlockChildrenResponseResult ,
6772 fullPathToDirectoryContainingMarkdown : string ,
6873 relativePathToThisPage : string
74+ ) : Promise < string > {
75+ const image = ( block as any ) . image ;
76+
77+ await processImageBlock (
78+ image ,
79+ fullPathToDirectoryContainingMarkdown ,
80+ relativePathToThisPage
81+ ) ;
82+
83+ // just concatenate the caption text parts together
84+ const altText : string = image . caption
85+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
86+ . map ( ( item : any ) => item . plain_text )
87+ . join ( "" ) ;
88+
89+ const href : string =
90+ image . type === "external" ? image . external . url : image . file . url ;
91+ return `` ;
92+ }
93+
94+ async function processImageBlock (
95+ imageBlock : any ,
96+ pathToParentDocument : string ,
97+ relativePathToThisPage : string
6998) : Promise < void > {
70- for ( const b of blocks ) {
71- if ( "image" in b ) {
72- await processImageBlock (
73- b ,
74- fullPathToDirectoryContainingMarkdown ,
75- relativePathToThisPage
76- ) ;
77- }
99+ logDebug ( "processImageBlock" , JSON . stringify ( imageBlock ) ) ;
100+
101+ // this is broken into all these steps to facilitate unit testing without IO
102+ const imageSet = parseImageBlock ( imageBlock ) ;
103+ imageSet . pathToParentDocument = pathToParentDocument ;
104+ imageSet . relativePathToParentDocument = relativePathToThisPage ;
105+
106+ await readPrimaryImage ( imageSet ) ;
107+ makeImagePersistencePlan ( imageSet , imageOutputPath , imagePrefix ) ;
108+ await saveImage ( imageSet ) ;
109+
110+ // change the src to point to our copy of the image
111+ if ( "file" in imageBlock ) {
112+ imageBlock . file . url = imageSet . filePathToUseInMarkdown ;
113+ } else {
114+ imageBlock . external . url = imageSet . filePathToUseInMarkdown ;
115+ }
116+ // put back the simplified caption, stripped of the meta information
117+ if ( imageSet . caption ) {
118+ imageBlock . caption = [
119+ {
120+ type : "text" ,
121+ text : { content : imageSet . caption , link : null } ,
122+ plain_text : imageSet . caption ,
123+ } ,
124+ ] ;
125+ } else {
126+ imageBlock . caption = [ ] ;
78127 }
79128}
80129
@@ -127,20 +176,20 @@ function writeImageIfNew(path: string, buffer: Buffer) {
127176 fs . createWriteStream ( path ) . write ( buffer ) ; // async but we're not waiting
128177}
129178
130- export function parseImageBlock ( b : any ) : ImageSet {
179+ export function parseImageBlock ( image : any ) : ImageSet {
131180 const imageSet : ImageSet = {
132181 primaryUrl : "" ,
133182 caption : "" ,
134183 localizedUrls : locales . map ( l => ( { iso632Code : l , url : "" } ) ) ,
135184 } ;
136185
137- if ( "file" in b . image ) {
138- imageSet . primaryUrl = b . image . file . url ; // image saved on notion (actually AWS)
186+ if ( "file" in image ) {
187+ imageSet . primaryUrl = image . file . url ; // image saved on notion (actually AWS)
139188 } else {
140- imageSet . primaryUrl = b . image . external . url ; // image still pointing somewhere else. I've see this happen when copying a Google Doc into Notion. Notion kep pointing at the google doc.
189+ imageSet . primaryUrl = image . external . url ; // image still pointing somewhere else. I've see this happen when copying a Google Doc into Notion. Notion kep pointing at the google doc.
141190 }
142191
143- const mergedCaption : string = b . image . caption
192+ const mergedCaption : string = image . caption
144193 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
145194 . map ( ( c : any ) => c . plain_text )
146195 . join ( "" ) ;
@@ -169,44 +218,6 @@ export function parseImageBlock(b: any): ImageSet {
169218 return imageSet ;
170219}
171220
172- // Download the image if we don't have it, give it a good name, and
173- // change the src to point to our copy of the image.
174- async function processImageBlock (
175- b : any ,
176- pathToParentDocument : string ,
177- relativePathToThisPage : string
178- ) : Promise < void > {
179- logDebug ( "processImageBlock" , JSON . stringify ( b ) ) ;
180-
181- // this is broken into all these steps to facilitate unit testing without IO
182- const imageSet = parseImageBlock ( b ) ;
183- imageSet . pathToParentDocument = pathToParentDocument ;
184- imageSet . relativePathToParentDocument = relativePathToThisPage ;
185-
186- await readPrimaryImage ( imageSet ) ;
187- makeImagePersistencePlan ( imageSet , imageOutputPath , imagePrefix ) ;
188- await saveImage ( imageSet ) ;
189-
190- // change the src to point to our copy of the image
191- if ( "file" in b . image ) {
192- b . image . file . url = imageSet . filePathToUseInMarkdown ;
193- } else {
194- b . image . external . url = imageSet . filePathToUseInMarkdown ;
195- }
196- // put back the simplified caption, stripped of the meta information
197- if ( imageSet . caption ) {
198- b . image . caption = [
199- {
200- type : "text" ,
201- text : { content : imageSet . caption , link : null } ,
202- plain_text : imageSet . caption ,
203- } ,
204- ] ;
205- } else {
206- b . image . caption = [ ] ;
207- }
208- }
209-
210221function imageWasSeen ( path : string ) {
211222 existingImagesNotSeenYetInPull = existingImagesNotSeenYetInPull . filter (
212223 p => p !== path
0 commit comments