Skip to content

Commit

Permalink
refactor(contentful): add function to download & cache contentful images
Browse files Browse the repository at this point in the history
  • Loading branch information
Benedikt Rötsch committed Jun 12, 2018
1 parent 3a79ecd commit 7d6b311
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 128 deletions.
61 changes: 61 additions & 0 deletions packages/gatsby-source-contentful/src/cache-image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const crypto = require(`crypto`)
const { resolve, parse } = require(`path`)

const axios = require(`axios`)
const { pathExists, createWriteStream } = require(`fs-extra`)

module.exports = async function cacheImage (store, image, options) {
const program = store.getState().program
const CACHE_DIR = resolve(`${program.directory}/.cache/contentful/assets/`)
const { file: { url, fileName, details } } = image
const { width, height, maxWidth, maxHeight, resizingBehavior, cropFocus, background } = options
const userWidth = maxWidth || width
const userHeight = maxHeight || height

const aspectRatio = details.image.height / details.image.width
const resultingWidth = Math.round(userWidth || 800)
const resultingHeight = Math.round(userHeight || resultingWidth * aspectRatio)

const params = [`w=${resultingWidth}`, `h=${resultingHeight}`]
if (resizingBehavior) {
params.push(`fit=${resizingBehavior}`)
}
if (cropFocus) {
params.push(`crop=${cropFocus}`)
}
if (background) {
params.push(`bg=${background}`)
}

const optionsHash = crypto
.createHash(`md5`)
.update(JSON.stringify([
url,
...params,
]))
.digest(`hex`)

const { name, ext } = parse(fileName)
const absolutePath = resolve(CACHE_DIR, `${name}-${optionsHash}${ext}`)

const alreadyExists = await pathExists(absolutePath)

if (!alreadyExists) {
const previewUrl = `http:${url}?${params.join(`&`)}`

const response = await axios({
method: `get`,
url: previewUrl,
responseType: `stream`,
})

await new Promise((resolve, reject) => {
const file = createWriteStream(absolutePath)
response.data.pipe(file)
file.on(`finish`, resolve)
file.on(`error`, reject)
})
}

return absolutePath
}
77 changes: 11 additions & 66 deletions packages/gatsby-source-contentful/src/extend-node-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,16 @@ const {
const qs = require(`qs`)
const base64Img = require(`base64-img`)
const _ = require(`lodash`)
const Debug = require(`debug`)
const { tmpdir } = require(`os`)
const path = require(`path`)

const cacheImage = require(`./cache-image`)

const {
ImageFormatType,
ImageResizingBehavior,
ImageCropFocusType,
} = require(`./schemes`)


const TMP_DIR = tmpdir()
const debug = Debug(`gatsby-source-contentful`)

const isImage = image =>
_.includes(
[`image/jpeg`, `image/jpg`, `image/png`, `image/webp`, `image/gif`],
Expand Down Expand Up @@ -267,80 +263,29 @@ const resolveResize = (image, options) => {

exports.resolveResize = resolveResize

exports.extendNodeType = ({ type }) => {
exports.extendNodeType = ({ type, store }) => {
if (type.name !== `ContentfulAsset`) {
return {}
}

const getTracedSVG = async (args) =>
{
const { extname, resolve } = require(`path`)
const axios = require(`axios`)
const { pathExists, createWriteStream } = require(`fs-extra`)
const {
traceSVG,
} = require(`gatsby-plugin-sharp`)

const { image, options } = args
const { id, file: { url, fileName } } = image
const { maxWidth, maxHeight, width, height, resizingBehavior, cropFocus, background } = options

// Downloading small version of the image with same aspect ratio
const assetWidth = maxWidth || width
const assetHeight = maxHeight || height
let aspectRatio = image.aspectRatio
if (assetWidth && assetHeight) {
aspectRatio = assetHeight / assetWidth
}

const uniqueId = [
id,
aspectRatio,
resizingBehavior,
cropFocus,
background,
]
.filter(Boolean)
.join(`-`)

const extension = extname(fileName)
const absolutePath = resolve(TMP_DIR, `${uniqueId}${extension}`)

const alreadyExists = await pathExists(absolutePath)

if (!alreadyExists) {
const previewWidth = 500
const previewHeight = Math.floor(previewWidth * aspectRatio)

const params = [`w=${previewWidth}`, `h=${previewHeight}`]
if (resizingBehavior) {
params.push(`fit=${resizingBehavior}`)
}
if (cropFocus) {
params.push(`crop=${cropFocus}`)
}
if (background) {
params.push(`bg=${background}`)
}

const previewUrl = `http:${url}?${params.join(`&`)}`

debug(`Downloading: ${previewUrl}`)

const response = await axios({
method: `get`,
url: previewUrl,
responseType: `stream`,
})
const {
file: { contentType },
} = image

await new Promise((resolve, reject) => {
const file = createWriteStream(absolutePath)
response.data.pipe(file)
file.on(`finish`, resolve)
file.on(`error`, reject)
})
if (contentType.indexOf(`image/`) !== 0) {
return null
}

const absolutePath = await cacheImage(store, image, options)
const extension = path.extname(absolutePath)

return traceSVG({
file: {
internal: image.internal,
Expand Down
8 changes: 8 additions & 0 deletions packages/gatsby-source-contentful/src/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const path = require(`path`)

const _ = require(`lodash`)
const fs = require(`fs-extra`)

Expand Down Expand Up @@ -181,6 +183,12 @@ exports.sourceNodes = async (
return
}

exports.onPreBootstrap = async ({ store }) => {
const program = store.getState().program
const CACHE_DIR = path.resolve(`${program.directory}/.cache/contentful/assets/`)
await fs.ensureDir(CACHE_DIR)
}

// Check if there are any ContentfulAsset nodes and if gatsby-image is installed. If so,
// add fragments for ContentfulAsset and gatsby-image. The fragment will cause an error
// if there's not ContentfulAsset nodes and without gatsby-image, the fragment is useless.
Expand Down
99 changes: 37 additions & 62 deletions packages/gatsby-transformer-sqip/src/extend-node-type.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { extname, resolve } = require(`path`)
const { resolve } = require(`path`)

const {
DuotoneGradientType,
Expand All @@ -15,13 +15,12 @@ const {
GraphQLBoolean,
} = require(`graphql`)
const sharp = require(`sharp`)
const { ensureDir } = require(`fs-extra`)

const generateSqip = require(`./generate-sqip`)

const debug = Debug(`gatsby-transformer-sqip`)
const SUPPORTED_NODES = [`ImageSharp`, `ContentfulAsset`]
const buildDirectory = process.env.GATSBY_BUILD_DIR || `public`
const CACHE_DIR = resolve(process.cwd(), buildDirectory, `static`)

module.exports = async args => {
const {
Expand All @@ -42,7 +41,12 @@ module.exports = async args => {
return {}
}

async function sqipSharp({ type, cache, getNodeAndSavePathDependency }) {
async function sqipSharp({ type, cache, getNodeAndSavePathDependency, store }) {
const program = store.getState().program
const cacheDir = resolve(`${program.directory}/.cache/sqip/`)

await ensureDir(cacheDir)

return {
sqip: {
type: new GraphQLObjectType({
Expand Down Expand Up @@ -115,7 +119,7 @@ async function sqipSharp({ type, cache, getNodeAndSavePathDependency }) {

return generateSqip({
cache,
cacheDir: CACHE_DIR,
cacheDir,
absolutePath,
numberOfPrimitives,
blur,
Expand All @@ -126,14 +130,18 @@ async function sqipSharp({ type, cache, getNodeAndSavePathDependency }) {
}
}

async function sqipContentful({ type, cache }) {
const { createWriteStream } = require(`fs`)
const axios = require(`axios`)

async function sqipContentful({ type, cache, store }) {
const {
schemes: { ImageResizingBehavior, ImageCropFocusType },
} = require(`gatsby-source-contentful`)

const cacheImage = require(`gatsby-source-contentful/cache-image`)

const program = store.getState().program
const cacheDir = resolve(`${program.directory}/.cache/sqip/`)

await ensureDir(cacheDir)

return {
sqip: {
type: new GraphQLObjectType({
Expand Down Expand Up @@ -177,79 +185,46 @@ async function sqipContentful({ type, cache }) {
},
async resolve(asset, fieldArgs, context) {
const {
id,
file: { url, fileName, details, contentType },
file: { contentType },
} = asset

if (contentType.indexOf(`image/`) !== 0) {
return null
}

const {
blur,
numberOfPrimitives,
mode,
width,
height,
resizingBehavior,
cropFocus,
background,
} = fieldArgs

if (contentType.indexOf(`image/`) !== 0) {
return null
}

// Downloading small version of the image with same aspect ratio
const assetWidth = width || details.image.width
const assetHeight = height || details.image.height
const aspectRatio = assetHeight / assetWidth
const previewWidth = 256
const previewHeight = Math.floor(previewWidth * aspectRatio)
let {
width,
height,
} = fieldArgs

const params = [`w=${previewWidth}`, `h=${previewHeight}`]
if (resizingBehavior) {
params.push(`fit=${resizingBehavior}`)
}
if (cropFocus) {
params.push(`crop=${cropFocus}`)
}
if (background) {
params.push(`bg=${background}`)
if (width && height) {
const aspectRatio = height / width
width = 256
height = height * aspectRatio
}

const uniqueId = [
id,
aspectRatio,
const options = {
width: 256,
height,
resizingBehavior,
cropFocus,
background,
]
.filter(Boolean)
.join(`-`)

const extension = extname(fileName)
const absolutePath = resolve(CACHE_DIR, `${uniqueId}${extension}`)

const alreadyExists = await fs.pathExists(absolutePath)

if (!alreadyExists) {
const previewUrl = `http:${url}?${params.join(`&`)}`

debug(`Downloading: ${previewUrl}`)

const response = await axios({
method: `get`,
url: previewUrl,
responseType: `stream`,
})

await new Promise((resolve, reject) => {
const file = createWriteStream(absolutePath)
response.data.pipe(file)
file.on(`finish`, resolve)
file.on(`error`, reject)
})
}

const absolutePath = await cacheImage(store, asset, options)

return generateSqip({
cache,
cacheDir: CACHE_DIR,
cacheDir,
absolutePath,
numberOfPrimitives,
blur,
Expand Down

0 comments on commit 7d6b311

Please sign in to comment.