Created
July 22, 2021 15:41
-
-
Save Defite/7439f77d8269d328a6bf82ff620018d6 to your computer and use it in GitHub Desktop.
Ghost.js, prepend image urls with Cloudinary links. Works for Ghost 4.9.4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
... | |
"imageOptimization": { | |
"cloudinary": { | |
"baseUrl": "https://res.cloudinary.com/summertimesadness/image/fetch/f_auto,q_auto" | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//versions/4.9.4/node_modules/@tryghost/kg-default-cards/lib/cards/image.js | |
const { | |
isLocalContentImage, | |
isUnsplashImage, | |
getAvailableImageWidths, | |
setSrcsetAttribute, | |
resizeImage | |
} = require('../utils'); | |
const { | |
absoluteToRelative, | |
relativeToAbsolute, | |
htmlAbsoluteToRelative, | |
htmlRelativeToAbsolute, | |
htmlToTransformReady, | |
toTransformReady | |
} = require('@tryghost/url-utils/lib/utils'); | |
module.exports = { | |
name: 'image', | |
type: 'dom', | |
render({ payload, env: { dom }, options = {} }) { | |
if (!payload.src) { | |
return dom.createTextNode(''); | |
} | |
const figure = dom.createElement('figure'); | |
let figureClass = 'kg-card kg-image-card'; | |
if (payload.cardWidth) { | |
figureClass = `${figureClass} kg-width-${payload.cardWidth}`; | |
} | |
figure.setAttribute('class', figureClass); | |
const img = dom.createElement('img'); | |
const { cloudinary } = options.imageOptimization; | |
const payloadSrc = cloudinary ? `${cloudinary.baseUrl}/${payload.src}` : payload.src; | |
img.setAttribute('src', payloadSrc); | |
img.setAttribute('class', 'kg-image'); | |
img.setAttribute('alt', payload.alt || ''); | |
img.setAttribute('loading', 'lazy'); | |
if (payload.title) { | |
img.setAttribute('title', payload.title); | |
} | |
if (payload.width && payload.height) { | |
img.setAttribute('width', payload.width); | |
img.setAttribute('height', payload.height); | |
} | |
// images can be resized to max width, if that's the case output | |
// the resized width/height attrs to ensure 3rd party gallery plugins | |
// aren't affected by differing sizes | |
const { canTransformImage } = options; | |
const { defaultMaxWidth } = options.imageOptimization || {}; | |
if ( | |
defaultMaxWidth && | |
payload.width > defaultMaxWidth && | |
isLocalContentImage(payload.src, options.siteUrl) && | |
canTransformImage && | |
canTransformImage(payload.src) | |
) { | |
const { width, height } = resizeImage(payload, { width: defaultMaxWidth }); | |
img.setAttribute('width', width); | |
img.setAttribute('height', height); | |
} | |
// add srcset unless it's an email, email clients do not have good support for srcset or sizes | |
if (options.target !== 'email') { | |
setSrcsetAttribute(img, payload, options); | |
if (img.getAttribute('srcset') && payload.width && payload.width >= 720) { | |
// standard size | |
if (!payload.cardWidth) { | |
img.setAttribute('sizes', '(min-width: 720px) 720px'); | |
} | |
if (payload.cardWidth === 'wide' && payload.width >= 1200) { | |
img.setAttribute('sizes', '(min-width: 1200px) 1200px'); | |
} | |
} | |
} | |
// Outlook is unable to properly resize images without a width/height | |
// so we add that at the expected size in emails (600px) and use a higher | |
// resolution image to keep images looking good on retina screens | |
if (options.target === 'email' && payload.width && payload.height) { | |
let imageDimensions = { | |
width: payload.width, | |
height: payload.height | |
}; | |
if (payload.width >= 600) { | |
imageDimensions = resizeImage(imageDimensions, { width: 600 }); | |
} | |
img.setAttribute('width', imageDimensions.width); | |
img.setAttribute('height', imageDimensions.height); | |
if (isLocalContentImage(payload.src, options.siteUrl) && options.canTransformImage && options.canTransformImage(payload.src)) { | |
// find available image size next up from 2x600 so we can use it for the "retina" src | |
const availableImageWidths = getAvailableImageWidths(payload, options.imageOptimization.contentImageSizes); | |
const srcWidth = availableImageWidths.find(width => width >= 1200); | |
if (!srcWidth || srcWidth === payload.width) { | |
// do nothing, width is smaller than retina or matches the original payload src | |
} else { | |
const [, imagesPath, filename] = payload.src.match(/(.*\/content\/images)\/(.*)/); | |
img.setAttribute('src', `${imagesPath}/size/w${srcWidth}/${filename}`); | |
} | |
} | |
if (isUnsplashImage(payload.src)) { | |
const unsplashUrl = new URL(payload.src); | |
unsplashUrl.searchParams.set('w', 1200); | |
img.setAttribute('src', unsplashUrl.href); | |
} | |
} | |
if (payload.href) { | |
const a = dom.createElement('a'); | |
a.setAttribute('href', payload.href); | |
a.appendChild(img); | |
figure.appendChild(a); | |
} else { | |
figure.appendChild(img); | |
} | |
if (payload.caption) { | |
const figcaption = dom.createElement('figcaption'); | |
figcaption.appendChild(dom.createRawHTMLSection(payload.caption)); | |
figure.appendChild(figcaption); | |
figure.setAttribute('class', `${figure.getAttribute('class')} kg-card-hascaption`); | |
} | |
return figure; | |
}, | |
absoluteToRelative(payload, options) { | |
payload.src = payload.src && absoluteToRelative(payload.src, options.siteUrl, options); | |
payload.caption = payload.caption && htmlAbsoluteToRelative(payload.caption, options.siteUrl, options); | |
return payload; | |
}, | |
relativeToAbsolute(payload, options) { | |
payload.src = payload.src && relativeToAbsolute(payload.src, options.siteUrl, options.itemUrl, options); | |
payload.caption = payload.caption && htmlRelativeToAbsolute(payload.caption, options.siteUrl, options.itemUrl, options); | |
return payload; | |
}, | |
toTransformReady(payload, options) { | |
payload.src = payload.src && toTransformReady(payload.src, options.siteUrl, options); | |
payload.caption = payload.caption && htmlToTransformReady(payload.caption, options.siteUrl, options); | |
return payload; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// versions/4.9.4/node_modules/@tryghost/kg-default-cards/lib/utils/set-srcset-attribute.js | |
const isLocalContentImage = require('./is-local-content-image'); | |
const getAvailableImageWidths = require('./get-available-image-widths'); | |
const isUnsplashImage = require('./is-unsplash-image'); | |
// default content sizes: [600, 1000, 1600, 2400] | |
module.exports = function setSrcsetAttribute(elem, image, options) { | |
if (!elem || !['IMG', 'SOURCE'].includes(elem.tagName) || !elem.getAttribute('src') || !image) { | |
return; | |
} | |
if (!options.imageOptimization || options.imageOptimization.srcsets === false || !image.width || !options.imageOptimization.contentImageSizes) { | |
return; | |
} | |
if (isLocalContentImage(image.src, options.siteUrl) && options.canTransformImage && !options.canTransformImage(image.src)) { | |
return; | |
} | |
const srcsetWidths = getAvailableImageWidths(image, options.imageOptimization.contentImageSizes); | |
// apply srcset if this is a relative image that matches Ghost's image url structure | |
if (isLocalContentImage(image.src, options.siteUrl)) { | |
const [, imagesPath, filename] = image.src.match(/(.*\/content\/images)\/(.*)/); | |
const srcs = []; | |
srcsetWidths.forEach((width) => { | |
if (width === image.width) { | |
// use original image path if width matches exactly (avoids 302s from size->original) | |
srcs.push(`${image.src} ${width}w`); | |
} else if (width <= image.width) { | |
// avoid creating srcset sizes larger than intrinsic image width | |
if (options.imageOptimization.cloudinary) { | |
srcs.push(`${options.imageOptimization.cloudinary.baseUrl},w_${width}/${imagesPath}/${filename} ${width}w`); | |
} else { | |
srcs.push(`${imagesPath}/size/w${width}/${filename} ${width}w`); | |
} | |
} | |
}); | |
if (srcs.length) { | |
elem.setAttribute('srcset', srcs.join(', ')); | |
} | |
} | |
// apply srcset if this is an Unsplash image | |
if (isUnsplashImage(image.src)) { | |
const unsplashUrl = new URL(image.src); | |
const srcs = []; | |
srcsetWidths.forEach((width) => { | |
unsplashUrl.searchParams.set('w', width); | |
srcs.push(`${unsplashUrl.href} ${width}w`); | |
}); | |
elem.setAttribute('srcset', srcs.join(', ')); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment