Skip to content

Commit

Permalink
feat: gatsbyImageData: add support for "quality" argument and default…
Browse files Browse the repository at this point in the history
… value for "layout"
  • Loading branch information
veryspry authored and wardpeet committed Feb 23, 2022
1 parent 2498682 commit 24fdfca
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ describe(`gatsbyImageData`, () => {
expect(parsedSrcSet[0].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=300&h=481&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=300&h=481&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedSrcSet[0].viewport).toEqual(`1x`)
expect(parsedSrcSet[1].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=600&h=962&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=600&h=962&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
Expand Down Expand Up @@ -172,31 +172,31 @@ describe(`gatsbyImageData`, () => {
expect(parsedSrcSet[0].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=75&h=120&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=75&h=120&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedSrcSet[0].viewport).toEqual(`75w`)
expect(parsedSrcSet[1].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=150&h=241&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=150&h=241&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedSrcSet[1].viewport).toEqual(`150w`)
expect(parsedSrcSet[2].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=300&h=481&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=300&h=481&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedSrcSet[2].viewport).toEqual(`300w`)
expect(parsedSrcSet[3].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=600&h=962&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=600&h=962&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
Expand Down Expand Up @@ -250,31 +250,31 @@ describe(`gatsbyImageData`, () => {
expect(parsedSrcSet[0].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=750&h=1202&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=750&h=1202&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedSrcSet[0].viewport).toEqual(`750w`)
expect(parsedSrcSet[1].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=1080&h=1731&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=1080&h=1731&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedSrcSet[1].viewport).toEqual(`1080w`)
expect(parsedSrcSet[2].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=1366&h=2190&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=1366&h=2190&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedSrcSet[2].viewport).toEqual(`1366w`)
expect(parsedSrcSet[3].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=1920&h=3078&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=1920&h=3078&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
Expand Down Expand Up @@ -349,14 +349,14 @@ describe(`gatsbyImageData`, () => {
expect(parsedFixedSrcSet[0].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=300&h=481&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=300&h=481&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedFixedSrcSet[1].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=600&h=962&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=600&h=962&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
Expand All @@ -368,14 +368,14 @@ describe(`gatsbyImageData`, () => {
expect(parsedConstrainedSrcSet[0].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=300&h=481&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=300&h=481&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
expect(parsedConstrainedSrcSet[1].src).toEqual(
`/_gatsby/image/${Buffer.from(portraitSource.url).toString(
`base64`
)}/${Buffer.from(`w=600&h=962&fm=webp`).toString(`base64`)}/${
)}/${Buffer.from(`w=600&h=962&fm=webp&q=75`).toString(`base64`)}/${
portraitSource.basename
}.webp`
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ describe(`resizeResolver`, () => {
const [transformArgs] = args.split(`.`)
expect(Buffer.from(url, `base64`).toString()).toBe(source.url)
expect(Buffer.from(transformArgs, `base64`).toString()).toBe(
`w=${expected.widthOnly[0]}&h=${expected.widthOnly[1]}&fm=jpg`
`w=${expected.widthOnly[0]}&h=${expected.widthOnly[1]}&fm=jpg&q=75`
)
expect(result?.width).toBe(expected.widthOnly[0])
expect(result?.height).toBe(expected.widthOnly[1])
Expand All @@ -223,7 +223,7 @@ describe(`resizeResolver`, () => {
const [transformArgs] = args.split(`.`)
expect(Buffer.from(url, `base64`).toString()).toBe(source.url)
expect(Buffer.from(transformArgs, `base64`).toString()).toBe(
`w=${expected.heightOnly[0]}&h=${expected.heightOnly[1]}&fm=jpg`
`w=${expected.heightOnly[0]}&h=${expected.heightOnly[1]}&fm=jpg&q=75`
)
expect(result?.width).toBe(expected.heightOnly[0])
expect(result?.height).toBe(expected.heightOnly[1])
Expand Down Expand Up @@ -295,12 +295,11 @@ describe(`resizeResolver`, () => {
args: {
contentDigest: `1`,
url: portraitSource.url,
filename: `${Buffer.from(`w=100&h=160&fm=jpg`).toString(
`base64`
)}.jpg`,
filename: `dog-portrait.jpg`,
format: `jpg`,
width: 100,
height: expect.any(Number),
quality: 75,
},
inputPaths: [],
name: `IMAGE_CDN`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type IGatsbyImageDataArgs = Omit<
cropFocus?: Array<ImageCropFocus>
fit?: CalculateImageSizesArgs["fit"]
outputPixelDensities?: CalculateImageSizesArgs["outputPixelDensities"]
quality?: number
}

type ImageSizeArgs = CalculateImageSizesArgs & {
Expand All @@ -69,6 +70,7 @@ interface IImageSizes {

const DEFAULT_PIXEL_DENSITIES = [0.25, 0.5, 1, 2]
const DEFAULT_BREAKPOINTS = [750, 1080, 1366, 1920]
const DEFAULT_QUALITY = 75

export async function gatsbyImageDataResolver(
source: IRemoteFileNode,
Expand Down Expand Up @@ -114,6 +116,10 @@ export async function gatsbyImageDataResolver(
args.placeholder = PlaceholderType.DOMINANT_COLOR
}

if (!args.quality) {
args.quality = DEFAULT_QUALITY
}

let backgroundColor = args.backgroundColor
const sourceMetadata: ISourceMetadata = {
width: source.width,
Expand Down Expand Up @@ -148,11 +154,16 @@ export async function gatsbyImageDataResolver(
{
url: source.url,
extension: format,
basename: path.basename(
source.filename,
path.extname(source.filename)
),
width,
height: Math.round(width / imageSizes.aspectRatio),
format,
fit: args.fit as ImageFit,
contentDigest: source.internal.contentDigest,
quality: args.quality as number,
},
store
)
Expand All @@ -163,6 +174,7 @@ export async function gatsbyImageDataResolver(
height: Math.round(width / imageSizes.aspectRatio),
format,
cropFocus: args.cropFocus,
quality: args.quality as number,
})}/${path.basename(
source.filename,
path.extname(source.filename)
Expand Down Expand Up @@ -233,13 +245,14 @@ export function generateGatsbyImageDataFieldConfig(
type: `JSON`,
args: {
layout: {
type: enums.layout.NonNull.getTypeName(),
type: enums.layout.getTypeName(),
description: stripIndent`
The layout for the image.
FIXED: A static image sized, that does not resize according to the screen width
FULL_WIDTH: The image resizes to fit its container. Pass a "sizes" option if it isn't going to be the full width of the screen.
CONSTRAINED: Resizes to fit its container, up to a maximum width, at which point it will remain fixed in size.
`,
defaultValue: enums.layout.getField(`CONSTRAINED`).value,
},
width: {
type: `Int`,
Expand Down Expand Up @@ -321,6 +334,10 @@ export function generateGatsbyImageDataFieldConfig(
cropFocus: {
type: enums.cropFocus.List.getTypeName(),
},
quality: {
type: `Int`,
defaultValue: DEFAULT_QUALITY,
},
},
resolve(source, args): ReturnType<typeof gatsbyImageDataResolver> {
return gatsbyImageDataResolver(source, args, store)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ interface IResizeArgs {
fit: ImageFit
format: ImageFormat
cropFocus: Array<ImageCropFocus>
quality: number
}

const DEFAULT_QUALITY = 75

const allowedFormats: Array<ImageFormat> = [
`jpg`,
`png`,
Expand All @@ -51,6 +54,10 @@ export async function resizeResolver(
args.format = `auto`
}

if (!args.quality) {
args.quality = DEFAULT_QUALITY
}

if (!allowedFormats.includes(args.format)) {
throw new Error(
`Unknown format "${args.format}" was given to resize ${source.url}`
Expand All @@ -76,6 +83,7 @@ export async function resizeResolver(
{
url: source.url,
extension: format,
basename: path.basename(source.filename, path.extname(source.filename)),
...(args as IResizeArgs),
width,
height,
Expand Down Expand Up @@ -132,6 +140,10 @@ export function generateResizeFieldConfig(
cropFocus: {
type: enums.cropFocus.List.getTypeName(),
},
quality: {
type: `Int`,
defaultValue: DEFAULT_QUALITY,
},
},
resolve(source, args): ReturnType<typeof resizeResolver> {
return resizeResolver(source, args, store)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,23 @@ export function dispatchLocalImageServiceJob(
{
url,
extension,
basename,
width,
height,
format,
fit,
contentDigest,
quality,
}: {
url: string
extension: string
basename: string
width: number
height: number
format: string
fit: ImageFit
contentDigest: string
quality: number
},
store: Store
): void {
Expand All @@ -102,16 +106,17 @@ export function dispatchLocalImageServiceJob(
inputPaths: [],
outputDir: path.join(
global.__GATSBY?.root || process.cwd(),
publicUrl.filter(Boolean).join(`/`)
publicUrl.filter(Boolean).join(`/`),
generateImageArgs({ width, height, format, quality })
),
args: {
url,
filename:
generateImageArgs({ width, height, format }) + `.${extension}`,
filename: `${basename}.${extension}`,
width,
height,
format,
fit,
quality,
contentDigest,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ for (const f of fit) {

pipeline.toFile(path.join(__dirname, `./__fixtures__/dog-300-${f}.jpg`))
}

// pipeline.resize(300, 300).jpeg({ quality: 75 }).png({ quality: 75 })
// pipeline.toFormat(`webp`).toFile(path.join(__dirname, `./__fixtures__/dog-300.webp`))
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface IResizeArgs {
height: number
format: string
outputPath?: string
quality: number
}

// Lots of work to get the sharp instance
Expand Down Expand Up @@ -101,7 +102,7 @@ export async function transformImage({

async function resizeImageWithSharp(
pipeline: Pipeline | Buffer,
{ width, height, format, outputPath }: IResizeArgs
{ width, height, format, outputPath, quality }: IResizeArgs
): Promise<Buffer | void> {
if (pipeline instanceof Buffer) {
if (!outputPath) {
Expand All @@ -113,6 +114,10 @@ async function resizeImageWithSharp(

const resizedImage = pipeline
.resize(width, height, {})
.jpeg({ quality })
.png({ quality })
.webp({ quality })
.avif({ quality })
.toFormat(
format as unknown as keyof Awaited<
ReturnType<typeof getSharpInstance>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ export function generateImageArgs({
height,
format,
cropFocus,
quality,
}: WidthOrHeight & {
format: string
cropFocus?: ImageCropFocus | Array<ImageCropFocus>
quality: number
}): string {
const args: Array<string> = []
if (width) {
Expand All @@ -38,9 +40,8 @@ export function generateImageArgs({
`crop=${Array.isArray(cropFocus) ? cropFocus.join(`,`) : cropFocus}`
)
}
if (format) {
args.push(`fm=${format}`)
}
args.push(`fm=${format}`)
args.push(`q=${quality}`)

return Buffer.from(args.join(`&`)).toString(`base64`)
}

0 comments on commit 24fdfca

Please sign in to comment.