11import type MarkdownIt from "markdown-it" ;
22import type { RenderRule } from "markdown-it/lib/renderer.mjs" ;
3+ import { existsSync } from "fs" ;
4+ import { join , dirname } from "path" ;
35
46/**
57 * 이미지 경로에서 확장자를 변경합니다
@@ -10,6 +12,36 @@ function changeExtension(src: string, newExt: string): string {
1012 return src . substring ( 0 , lastDotIndex ) + "." + newExt ;
1113}
1214
15+ /**
16+ * 절대 경로로 변환하여 파일 존재 여부 확인
17+ */
18+ function fileExists ( relativePath : string , markdownFilePath : string ) : boolean {
19+ try {
20+ // VitePress rewrites로 인해 env.path가 /posts/... 형태로 오지만
21+ // 실제 파일은 /contents/posts/... 에 있음
22+ // markdownFilePath를 실제 파일 시스템 경로로 변환
23+ let actualPath = markdownFilePath ;
24+ if (
25+ markdownFilePath . includes ( "/posts/" ) &&
26+ ! markdownFilePath . includes ( "/contents/posts/" )
27+ ) {
28+ actualPath = markdownFilePath . replace ( "/posts/" , "/contents/posts/" ) ;
29+ }
30+ if (
31+ markdownFilePath . includes ( "/projects/" ) &&
32+ ! markdownFilePath . includes ( "/contents/projects/" )
33+ ) {
34+ actualPath = markdownFilePath . replace ( "/projects/" , "/contents/projects/" ) ;
35+ }
36+
37+ const dir = dirname ( actualPath ) ;
38+ const absolutePath = join ( dir , relativePath ) ;
39+ return existsSync ( absolutePath ) ;
40+ } catch {
41+ return false ;
42+ }
43+ }
44+
1345/**
1446 * 마크다운의 이미지를 picture 태그로 변환하는 플러그인
1547 */
@@ -38,12 +70,43 @@ export function markdownPicturePlugin(md: MarkdownIt) {
3870
3971 // 이미지 포맷별 경로 생성
4072 const srcWebp = changeExtension ( src , "webp" ) ;
73+ const srcAvif = changeExtension ( src , "avif" ) ;
4174 const srcJpeg = changeExtension ( src , "jpeg" ) ;
4275
76+ // 실제로 존재하는 파일만 source로 추가
77+ const sources : string [ ] = [ ] ;
78+ const markdownPath = env . path || "" ;
79+
80+ console . log ( `\n🔍 이미지 처리 중:` ) ;
81+ console . log ( ` - 원본: ${ src } ` ) ;
82+ console . log ( ` - MD 파일: ${ markdownPath } ` ) ;
83+ console . log ( ` - WebP: ${ srcWebp } (존재: ${ fileExists ( srcWebp , markdownPath ) } )` ) ;
84+ console . log ( ` - AVIF: ${ srcAvif } (존재: ${ fileExists ( srcAvif , markdownPath ) } )` ) ;
85+ console . log ( ` - JPEG: ${ srcJpeg } (존재: ${ fileExists ( srcJpeg , markdownPath ) } )` ) ;
86+
87+ // AVIF (최우선)
88+ if ( fileExists ( srcAvif , markdownPath ) ) {
89+ sources . push ( `<source srcset="${ srcAvif } " type="image/avif" />` ) ;
90+ }
91+ // WebP (차선)
92+ if ( fileExists ( srcWebp , markdownPath ) ) {
93+ sources . push ( `<source srcset="${ srcWebp } " type="image/webp" />` ) ;
94+ }
95+ // JPEG (폴백)
96+ if ( fileExists ( srcJpeg , markdownPath ) ) {
97+ sources . push ( `<source srcset="${ srcJpeg } " type="image/jpeg" />` ) ;
98+ }
99+
100+ console . log ( ` - Sources 개수: ${ sources . length } ` ) ;
101+
102+ // 변환된 이미지가 없으면 원본만 사용
103+ if ( sources . length === 0 ) {
104+ return `<img src="${ src } " alt="${ alt } " loading="lazy" />` ;
105+ }
106+
43107 // picture 태그 생성 (원본을 최종 fallback으로 사용)
44108 return `<picture>
45- <source srcset="${ srcWebp } " type="image/webp" />
46- <source srcset="${ srcJpeg } " type="image/jpeg" />
109+ ${ sources . join ( "\n " ) }
47110 <img src="${ src } " alt="${ alt } " loading="lazy" />
48111</picture>` ;
49112 } ;
0 commit comments