@@ -218,24 +218,57 @@ async function checkMeta() {
218218
219219// === 4. no-image fallback ===
220220async function checkNoImage ( ) {
221- // 旧 28 件のうち代表サンプル
221+ // 旧 28 件のうち代表サンプル。
222+ // SvelteKit の hydration payload には生データ (`coverUrl: "+/images/..."`) が含まれることがあるが、
223+ // それは描画には使われない。実際にレンダリングされる属性 (img[src], meta[content], JSON-LD) のみ検査する。
222224 const samples = [
223225 "/articles/2019-11-03-utcode-lectures-01" ,
224226 "/articles/2021-09-24-tuk-programming-workshop" ,
225227 "/articles/2022-07-15-summer-events" ,
226228 "/articles/2019-09-30-2019a-schedule" ,
227229 ] ;
230+ const brokenPattern = / \+ \/ i m a g e s \/ n o - i m a g e \. s v g / ;
231+ const renderingChecks : Array < { name : string ; re : RegExp } > = [
232+ { name : "img[src]" , re : / < i m g \b [ ^ > ] * \b s r c \s * = \s * [ " ' ] [ ^ " ' ] * \+ \/ i m a g e s \/ n o - i m a g e \. s v g / i } ,
233+ {
234+ name : "og:image" ,
235+ re : / < m e t a \b [ ^ > ] * \b p r o p e r t y \s * = \s * [ " ' ] o g : i m a g e [ " ' ] [ ^ > ] * \b c o n t e n t \s * = \s * [ " ' ] [ ^ " ' ] * \+ \/ i m a g e s \/ n o - i m a g e \. s v g / i,
236+ } ,
237+ {
238+ name : "og:image (reverse)" ,
239+ re : / < m e t a \b [ ^ > ] * \b c o n t e n t \s * = \s * [ " ' ] [ ^ " ' ] * \+ \/ i m a g e s \/ n o - i m a g e \. s v g [ " ' ] [ ^ > ] * \b p r o p e r t y \s * = \s * [ " ' ] o g : i m a g e [ " ' ] / i,
240+ } ,
241+ {
242+ name : "twitter:image" ,
243+ re : / < m e t a \b [ ^ > ] * \b n a m e \s * = \s * [ " ' ] t w i t t e r : i m a g e [ " ' ] [ ^ > ] * \b c o n t e n t \s * = \s * [ " ' ] [ ^ " ' ] * \+ \/ i m a g e s \/ n o - i m a g e \. s v g / i,
244+ } ,
245+ {
246+ name : "twitter:image (reverse)" ,
247+ re : / < m e t a \b [ ^ > ] * \b c o n t e n t \s * = \s * [ " ' ] [ ^ " ' ] * \+ \/ i m a g e s \/ n o - i m a g e \. s v g [ " ' ] [ ^ > ] * \b n a m e \s * = \s * [ " ' ] t w i t t e r : i m a g e [ " ' ] / i,
248+ } ,
249+ ] ;
228250 for ( const path of samples ) {
229251 const r = await fetchFollow ( path ) ;
230252 if ( r . status !== 200 ) {
231253 record ( "NO_IMAGE" , path , false , `article not reachable (status=${ r . status } )` ) ;
232254 continue ;
233255 }
234- const broken =
235- r . body . includes ( "+/images/no-image.svg" ) ||
236- r . body . includes ( "%2B/images/no-image.svg" ) ||
237- r . body . includes ( "+%2Fimages%2Fno-image.svg" ) ;
238- record ( "NO_IMAGE" , path , ! broken , broken ? "found broken `+/images/no-image.svg`" : "ok" ) ;
256+ const failures = renderingChecks . filter ( ( c ) => c . re . test ( r . body ) ) . map ( ( c ) => c . name ) ;
257+ // JSON-LD inside <script type="application/ld+json"> の image フィールドも検査
258+ const ldJsonMatch = r . body . match (
259+ / < s c r i p t [ ^ > ] * t y p e = [ " ' ] a p p l i c a t i o n \/ l d \+ j s o n [ " ' ] [ ^ > ] * > ( [ \s \S ] * ?) < \/ s c r i p t > / i,
260+ ) ;
261+ if ( ldJsonMatch ?. [ 1 ] ) {
262+ const imageMatch = ldJsonMatch [ 1 ] . match ( / " i m a g e " \s * : \s * " ( [ ^ " ] + ) " / ) ;
263+ if ( imageMatch ?. [ 1 ] && brokenPattern . test ( imageMatch [ 1 ] ) ) {
264+ failures . push ( "ld+json image" ) ;
265+ }
266+ }
267+ if ( failures . length > 0 ) {
268+ record ( "NO_IMAGE" , path , false , `broken in: ${ failures . join ( ", " ) } ` ) ;
269+ } else {
270+ record ( "NO_IMAGE" , path , true , "ok" ) ;
271+ }
239272 }
240273}
241274
0 commit comments