1+ import type { Element , Node } from 'hast'
12import { visit , SKIP } from 'unist-util-visit'
23import { IMAGE_DENSITY } from '../../assets/lib/image-density'
34
@@ -9,12 +10,16 @@ export const MAX_WIDTH = 1440
910const DEFAULT_IMAGE_DENSITY = '2x'
1011
1112// Matches any <img> tags with an href that starts with `/assets/`
12- const matcher = ( node ) =>
13- node . type === 'element' &&
14- node . tagName === 'img' &&
15- node . properties &&
16- node . properties . src &&
17- node . properties . src . startsWith ( '/assets/' )
13+ function isAssetImg ( node : Node ) : node is Element {
14+ return (
15+ node . type === 'element' &&
16+ ( node as Element ) . tagName === 'img' &&
17+ ! ! ( node as Element ) . properties &&
18+ ! ! ( node as Element ) . properties ?. src &&
19+ typeof ( node as Element ) . properties ?. src === 'string' &&
20+ ( ( node as Element ) . properties ?. src as string ) . startsWith ( '/assets/' )
21+ )
22+ }
1823
1924/**
2025 * Where it can mutate the AST to swap from:
@@ -30,17 +35,20 @@ const matcher = (node) =>
3035 *
3136 * */
3237export default function rewriteAssetImgTags ( ) {
33- return ( tree ) => {
34- visit ( tree , matcher , ( node ) => {
35- if ( node . properties . src . endsWith ( '.png' ) ) {
38+ return ( tree : Node ) => {
39+ visit ( tree , 'element' , ( node : Node ) => {
40+ if ( ! isAssetImg ( node ) ) return
41+
42+ const src = node . properties ?. src as string
43+ if ( src . endsWith ( '.png' ) ) {
3644 const copyPNG = structuredClone ( node )
3745
38- const originalSrc = node . properties . src
46+ const originalSrc = src
3947 const originalSrcWithoutCb = originalSrc . replace ( / c b - \w + \/ / , '' )
40- const webpSrc = injectMaxWidth ( node . properties . src . replace ( / \. p n g $ / , '.webp' ) , MAX_WIDTH )
48+ const webpSrc = injectMaxWidth ( src . replace ( / \. p n g $ / , '.webp' ) , MAX_WIDTH )
4149 const srcset = `${ webpSrc } ${ IMAGE_DENSITY [ originalSrcWithoutCb ] || DEFAULT_IMAGE_DENSITY } `
4250
43- const sourceWEBP = {
51+ const sourceWEBP : Element = {
4452 type : 'element' ,
4553 tagName : 'source' ,
4654 properties : {
@@ -49,12 +57,15 @@ export default function rewriteAssetImgTags() {
4957 } ,
5058 children : [ ] ,
5159 }
52- node . children . push ( sourceWEBP )
5360
61+ node . children = node . children || [ ]
62+ node . children . push ( sourceWEBP )
5463 node . children . push ( copyPNG )
5564 node . tagName = 'picture'
65+
5666 delete node . properties . alt
5767 delete node . properties . src
68+
5869 // Don't go further or else you end up in an infinite recursion
5970 return SKIP
6071 }
@@ -68,7 +79,7 @@ export default function rewriteAssetImgTags() {
6879 * For example, if the pathname is `/assets/cb-1234/images/foo.png`
6980 * return `/assets/cb-1234/_mw-1440/images/foo.png`
7081 */
71- function injectMaxWidth ( pathname , maxWidth ) {
82+ function injectMaxWidth ( pathname : string , maxWidth : number ) : string {
7283 const split = pathname . split ( '/' )
7384 // This prefix needs to match what's possibly expected in dynamic-assets.js
7485 const inject = `mw-${ maxWidth } `
0 commit comments