module.exports = ({ type, pathPrefix, getNodeAndSavePathDependency }) => { if (type.name !== `ImageSharp`) { return {} } return { original: { type: new GraphQLObjectType({ name: `ImageSharpOriginal`, fields: { width: { type: GraphQLFloat }, height: { type: GraphQLFloat }, src: { type: GraphQLString }, }, }), args: {}, async resolve(image, fieldArgs, context) { const details = getNodeAndSavePathDependency(image.parent, context.path) const dimensions = sizeOf(details.absolutePath) const imageName = `${image.internal.contentDigest}${details.ext}` const publicPath = path.join( process.cwd(), `public`, `static/${imageName}` ) if (!fsExtra.existsSync(publicPath)) { fsExtra.copy(details.absolutePath, publicPath, err => { if (err) { console.error(`error copying file`, err) } }) } return { width: dimensions.width, height: dimensions.height, src: `/static/` + imageName, } }, }, resolutions: { type: new GraphQLObjectType({ name: `ImageSharpResolutions`, fields: { base64: { type: GraphQLString }, aspectRatio: { type: GraphQLFloat }, width: { type: GraphQLFloat }, height: { type: GraphQLFloat }, src: { type: GraphQLString }, srcSet: { type: GraphQLString }, originalName: { type: GraphQLString }, }, }), args: { width: { type: GraphQLInt, defaultValue: 400, }, height: { type: GraphQLInt, }, jpegProgressive: { type: GraphQLBoolean, defaultValue: true, }, grayscale: { type: GraphQLBoolean, defaultValue: false, }, duotone: { type: DuotoneGradientType, defaultValue: false, }, quality: { type: GraphQLInt, defaultValue: 50, }, toFormat: { type: ImageFormatType, defaultValue: ``, }, cropFocus: { type: ImageCropFocusType, defaultValue: sharp.strategy.attention, }, rotate: { type: GraphQLInt, defaultValue: 0, }, }, resolve(image, fieldArgs, context) { const promise = resolutions({ file: getNodeAndSavePathDependency(image.parent, context.path), args: { ...fieldArgs, pathPrefix }, }) return promise }, }, sizes: { type: new GraphQLObjectType({ name: `ImageSharpSizes`, fields: { base64: { type: GraphQLString }, aspectRatio: { type: GraphQLFloat }, src: { type: GraphQLString }, srcSet: { type: GraphQLString }, sizes: { type: GraphQLString }, originalImg: { type: GraphQLString }, originalName: { type: GraphQLString }, }, }), args: { maxWidth: { type: GraphQLInt, defaultValue: 800, }, maxHeight: { type: GraphQLInt, }, grayscale: { type: GraphQLBoolean, defaultValue: false, }, jpegProgressive: { type: GraphQLBoolean, defaultValue: true, }, duotone: { type: DuotoneGradientType, defaultValue: false, }, quality: { type: GraphQLInt, defaultValue: 50, }, toFormat: { type: ImageFormatType, defaultValue: ``, }, cropFocus: { type: ImageCropFocusType, defaultValue: sharp.strategy.attention, }, rotate: { type: GraphQLInt, defaultValue: 0, }, }, resolve(image, fieldArgs, context) { return sizes({ file: getNodeAndSavePathDependency(image.parent, context.path), args: { ...fieldArgs, pathPrefix }, }) }, }, responsiveResolution: { deprecationReason: `We dropped the "responsive" part of the name to make it shorter https://github.com/gatsbyjs/gatsby/pull/2320/`, type: new GraphQLObjectType({ name: `ImageSharpResponsiveResolution`, fields: { base64: { type: GraphQLString }, aspectRatio: { type: GraphQLFloat }, width: { type: GraphQLFloat }, height: { type: GraphQLFloat }, src: { type: GraphQLString }, srcSet: { type: GraphQLString }, originalName: { type: GraphQLString }, }, }), args: { width: { type: GraphQLInt, defaultValue: 400, }, height: { type: GraphQLInt, }, jpegProgressive: { type: GraphQLBoolean, defaultValue: true, }, grayscale: { type: GraphQLBoolean, defaultValue: false, }, duotone: { type: DuotoneGradientType, defaultValue: false, }, quality: { type: GraphQLInt, defaultValue: 50, }, toFormat: { type: ImageFormatType, defaultValue: ``, }, cropFocus: { type: ImageCropFocusType, defaultValue: sharp.strategy.attention, }, rotate: { type: GraphQLInt, defaultValue: 0, }, }, resolve(image, fieldArgs, context) { const promise = resolutions({ file: getNodeAndSavePathDependency(image.parent, context.path), args: { ...fieldArgs, pathPrefix }, }) return promise }, }, responsiveSizes: { deprecationReason: `We dropped the "responsive" part of the name to make it shorter https://github.com/gatsbyjs/gatsby/pull/2320/`, type: new GraphQLObjectType({ name: `ImageSharpResponsiveSizes`, fields: { base64: { type: GraphQLString }, aspectRatio: { type: GraphQLFloat }, src: { type: GraphQLString }, srcSet: { type: GraphQLString }, sizes: { type: GraphQLString }, originalImg: { type: GraphQLString }, originalName: { type: GraphQLString }, }, }), args: { maxWidth: { type: GraphQLInt, defaultValue: 800, }, maxHeight: { type: GraphQLInt, }, grayscale: { type: GraphQLBoolean, defaultValue: false, }, jpegProgressive: { type: GraphQLBoolean, defaultValue: true, }, duotone: { type: DuotoneGradientType, defaultValue: false, }, quality: { type: GraphQLInt, defaultValue: 50, }, toFormat: { type: ImageFormatType, defaultValue: ``, }, cropFocus: { type: ImageCropFocusType, defaultValue: sharp.strategy.attention, }, rotate: { type: GraphQLInt, defaultValue: 0, }, }, resolve(image, fieldArgs, context) { return sizes({ file: getNodeAndSavePathDependency(image.parent, context.path), args: { ...fieldArgs, pathPrefix }, }) }, }, resize: { type: new GraphQLObjectType({ name: `ImageSharpResize`, fields: { src: { type: GraphQLString }, width: { type: GraphQLInt }, height: { type: GraphQLInt }, aspectRatio: { type: GraphQLFloat }, originalName: { type: GraphQLString }, }, }), args: { width: { type: GraphQLInt, defaultValue: 400, }, height: { type: GraphQLInt, }, quality: { type: GraphQLInt, defaultValue: 50, }, jpegProgressive: { type: GraphQLBoolean, defaultValue: true, }, pngCompressionLevel: { type: GraphQLInt, defaultValue: 9, }, grayscale: { type: GraphQLBoolean, defaultValue: false, }, duotone: { type: DuotoneGradientType, defaultValue: false, }, base64: { type: GraphQLBoolean, defaultValue: false, }, toFormat: { type: ImageFormatType, defaultValue: ``, }, cropFocus: { type: ImageCropFocusType, defaultValue: sharp.strategy.attention, }, rotate: { type: GraphQLInt, defaultValue: 0, }, }, resolve(image, fieldArgs, context) { return new Promise(resolve => { const file = getNodeAndSavePathDependency(image.parent, context.path) if (fieldArgs.base64) { resolve( base64({ file, }) ) } else { resolve( queueImageResizing({ file, args: { ...fieldArgs, pathPrefix }, }) ) } }) }, }, } }
const generateImagesAndUpdateNode = async function(node, resolve) { // Check if this markdownNode has a File parent. This plugin // won't work if the image isn't hosted locally. const parentNode = getNode(markdownNode.parent) let imagePath if (parentNode && parentNode.dir) { imagePath = slash(path.join(parentNode.dir, node.url)) } else { return null } const imageNode = _.find(files, file => { if (file && file.absolutePath) { return file.absolutePath === imagePath } return null }) if (!imageNode || !imageNode.absolutePath) { return resolve() } let responsiveSizesResult = await sizes({ file: imageNode, args: options, }) // Calculate the paddingBottom % const ratio = `${1 / responsiveSizesResult.aspectRatio * 100}%` const originalImg = responsiveSizesResult.originalImg const fallbackSrc = responsiveSizesResult.src const srcSet = responsiveSizesResult.srcSet const presentationWidth = responsiveSizesResult.presentationWidth // Generate default alt tag const srcSplit = node.url.split(`/`) const fileName = srcSplit[srcSplit.length - 1] const fileNameNoExt = fileName.replace(/\.[^/.]+$/, ``) const defaultAlt = fileNameNoExt.replace(/[^A-Z0-9]/gi, ` `) // TODO // Fade in images on load. // https://www.perpetual-beta.org/weblog/silky-smooth-image-loading.html // Construct new image node w/ aspect ratio placeholder let rawHTML = ` <span class="gatsby-resp-image-wrapper" style="position: relative; display: block; ${options.wrapperStyle}; max-width: ${presentationWidth}px; margin-left: auto; margin-right: auto;" > <span class="gatsby-resp-image-background-image" style="padding-bottom: ${ratio}; position: relative; bottom: 0; left: 0; background-image: url('${responsiveSizesResult.base64}'); background-size: cover; display: block;" > <img class="gatsby-resp-image-image" style="width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px ${options.backgroundColor};" alt="${node.alt ? node.alt : defaultAlt}" title="${node.title ? node.title : ``}" src="${fallbackSrc}" srcset="${srcSet}" sizes="${responsiveSizesResult.sizes}" /> </span> </span> ` // Make linking to original image optional. if (options.linkImagesToOriginal) { rawHTML = ` <a class="gatsby-resp-image-link" href="${originalImg}" style="display: block" target="_blank" rel="noopener" > ${rawHTML} </a> ` } return rawHTML }