@@ -73,6 +73,174 @@ export namespace File {
7373 } )
7474 export type Content = z . infer < typeof Content >
7575
76+ const binaryExtensions = new Set ( [
77+ "exe" ,
78+ "dll" ,
79+ "pdb" ,
80+ "bin" ,
81+ "so" ,
82+ "dylib" ,
83+ "o" ,
84+ "a" ,
85+ "lib" ,
86+ "wav" ,
87+ "mp3" ,
88+ "ogg" ,
89+ "oga" ,
90+ "ogv" ,
91+ "ogx" ,
92+ "flac" ,
93+ "aac" ,
94+ "wma" ,
95+ "m4a" ,
96+ "weba" ,
97+ "mp4" ,
98+ "avi" ,
99+ "mov" ,
100+ "wmv" ,
101+ "flv" ,
102+ "webm" ,
103+ "mkv" ,
104+ "zip" ,
105+ "tar" ,
106+ "gz" ,
107+ "gzip" ,
108+ "bz" ,
109+ "bz2" ,
110+ "bzip" ,
111+ "bzip2" ,
112+ "7z" ,
113+ "rar" ,
114+ "xz" ,
115+ "lz" ,
116+ "z" ,
117+ "pdf" ,
118+ "doc" ,
119+ "docx" ,
120+ "ppt" ,
121+ "pptx" ,
122+ "xls" ,
123+ "xlsx" ,
124+ "dmg" ,
125+ "iso" ,
126+ "img" ,
127+ "vmdk" ,
128+ "ttf" ,
129+ "otf" ,
130+ "woff" ,
131+ "woff2" ,
132+ "eot" ,
133+ "sqlite" ,
134+ "db" ,
135+ "mdb" ,
136+ "apk" ,
137+ "ipa" ,
138+ "aab" ,
139+ "xapk" ,
140+ "app" ,
141+ "pkg" ,
142+ "deb" ,
143+ "rpm" ,
144+ "snap" ,
145+ "flatpak" ,
146+ "appimage" ,
147+ "msi" ,
148+ "msp" ,
149+ "jar" ,
150+ "war" ,
151+ "ear" ,
152+ "class" ,
153+ "kotlin_module" ,
154+ "dex" ,
155+ "vdex" ,
156+ "odex" ,
157+ "oat" ,
158+ "art" ,
159+ "wasm" ,
160+ "wat" ,
161+ "bc" ,
162+ "ll" ,
163+ "s" ,
164+ "ko" ,
165+ "sys" ,
166+ "drv" ,
167+ "efi" ,
168+ "rom" ,
169+ "com" ,
170+ "bat" ,
171+ "cmd" ,
172+ "ps1" ,
173+ "sh" ,
174+ "bash" ,
175+ "zsh" ,
176+ "fish" ,
177+ ] )
178+
179+ const imageExtensions = new Set ( [
180+ "png" ,
181+ "jpg" ,
182+ "jpeg" ,
183+ "gif" ,
184+ "bmp" ,
185+ "webp" ,
186+ "ico" ,
187+ "tif" ,
188+ "tiff" ,
189+ "svg" ,
190+ "svgz" ,
191+ "avif" ,
192+ "apng" ,
193+ "jxl" ,
194+ "heic" ,
195+ "heif" ,
196+ "raw" ,
197+ "cr2" ,
198+ "nef" ,
199+ "arw" ,
200+ "dng" ,
201+ "orf" ,
202+ "raf" ,
203+ "pef" ,
204+ "x3f" ,
205+ ] )
206+
207+ function isImageByExtension ( filepath : string ) : boolean {
208+ const ext = path . extname ( filepath ) . toLowerCase ( ) . slice ( 1 )
209+ return imageExtensions . has ( ext )
210+ }
211+
212+ function getImageMimeType ( filepath : string ) : string {
213+ const ext = path . extname ( filepath ) . toLowerCase ( ) . slice ( 1 )
214+ const mimeTypes : Record < string , string > = {
215+ png : "image/png" ,
216+ jpg : "image/jpeg" ,
217+ jpeg : "image/jpeg" ,
218+ gif : "image/gif" ,
219+ bmp : "image/bmp" ,
220+ webp : "image/webp" ,
221+ ico : "image/x-icon" ,
222+ tif : "image/tiff" ,
223+ tiff : "image/tiff" ,
224+ svg : "image/svg+xml" ,
225+ svgz : "image/svg+xml" ,
226+ avif : "image/avif" ,
227+ apng : "image/apng" ,
228+ jxl : "image/jxl" ,
229+ heic : "image/heic" ,
230+ heif : "image/heif" ,
231+ }
232+ return mimeTypes [ ext ] || "image/" + ext
233+ }
234+
235+ function isBinaryByExtension ( filepath : string ) : boolean {
236+ const ext = path . extname ( filepath ) . toLowerCase ( ) . slice ( 1 )
237+ return binaryExtensions . has ( ext )
238+ }
239+
240+ function isImage ( mimeType : string ) : boolean {
241+ return mimeType . startsWith ( "image/" )
242+ }
243+
76244 async function shouldEncode ( file : BunFile ) : Promise < boolean > {
77245 const type = file . type ?. toLowerCase ( )
78246 log . info ( "shouldEncode" , { type } )
@@ -83,37 +251,13 @@ export namespace File {
83251
84252 const parts = type . split ( "/" , 2 )
85253 const top = parts [ 0 ]
86- const rest = parts [ 1 ] ?? ""
87- const sub = rest . split ( ";" , 1 ) [ 0 ]
88254
89255 const tops = [ "image" , "audio" , "video" , "font" , "model" , "multipart" ]
90256 if ( tops . includes ( top ) ) return true
91257
92- const bins = [
93- "zip" ,
94- "gzip" ,
95- "bzip" ,
96- "compressed" ,
97- "binary" ,
98- "pdf" ,
99- "msword" ,
100- "powerpoint" ,
101- "excel" ,
102- "ogg" ,
103- "exe" ,
104- "dmg" ,
105- "iso" ,
106- "rar" ,
107- ]
108- if ( bins . some ( ( mark ) => sub . includes ( mark ) ) ) return true
109-
110258 return false
111259 }
112260
113- function isImage ( mimeType : string ) : boolean {
114- return mimeType . startsWith ( "image/" )
115- }
116-
117261 export const Event = {
118262 Edited : BusEvent . define (
119263 "file.edited" ,
@@ -280,64 +424,6 @@ export namespace File {
280424 } ) )
281425 }
282426
283- const binaryExtensions = new Set ( [
284- "exe" ,
285- "dll" ,
286- "pdb" ,
287- "bin" ,
288- "so" ,
289- "dylib" ,
290- "o" ,
291- "a" ,
292- "lib" ,
293- "wav" ,
294- "mp3" ,
295- "ogg" ,
296- "flac" ,
297- "aac" ,
298- "wma" ,
299- "m4a" ,
300- "mp4" ,
301- "avi" ,
302- "mov" ,
303- "wmv" ,
304- "flv" ,
305- "webm" ,
306- "mkv" ,
307- "zip" ,
308- "tar" ,
309- "gz" ,
310- "bz2" ,
311- "7z" ,
312- "rar" ,
313- "xz" ,
314- "lz" ,
315- "pdf" ,
316- "doc" ,
317- "docx" ,
318- "ppt" ,
319- "pptx" ,
320- "xls" ,
321- "xlsx" ,
322- "dmg" ,
323- "iso" ,
324- "img" ,
325- "vmdk" ,
326- "ttf" ,
327- "otf" ,
328- "woff" ,
329- "woff2" ,
330- "eot" ,
331- "sqlite" ,
332- "db" ,
333- "mdb" ,
334- ] )
335-
336- function isBinaryByExtension ( filepath : string ) : boolean {
337- const ext = path . extname ( filepath ) . toLowerCase ( ) . slice ( 1 )
338- return binaryExtensions . has ( ext )
339- }
340-
341427 export async function read ( file : string ) : Promise < Content > {
342428 using _ = log . time ( "read" , { file } )
343429 const project = Instance . project
@@ -350,6 +436,17 @@ export namespace File {
350436 }
351437
352438 // Fast path: check extension before any filesystem operations
439+ if ( isImageByExtension ( file ) ) {
440+ const bunFile = Bun . file ( full )
441+ if ( await bunFile . exists ( ) ) {
442+ const buffer = await bunFile . arrayBuffer ( ) . catch ( ( ) => new ArrayBuffer ( 0 ) )
443+ const content = Buffer . from ( buffer ) . toString ( "base64" )
444+ const mimeType = getImageMimeType ( file )
445+ return { type : "text" , content, mimeType, encoding : "base64" }
446+ }
447+ return { type : "text" , content : "" }
448+ }
449+
353450 if ( isBinaryByExtension ( file ) ) {
354451 return { type : "binary" , content : "" }
355452 }
0 commit comments