Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: image and file cdn url generator adapter implementation #38685

Merged
merged 33 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3ff1518
alternate image url construction
kathmbeck Nov 2, 2023
8c4b42a
Merge branch 'master' into alternate-image-url
kathmbeck Nov 2, 2023
aee50e6
try using image cdn in e2e site
pieh Nov 3, 2023
6b3bdbe
Update netlify.toml
kathmbeck Nov 3, 2023
3f8efd2
have separate check for dispatching image and file service
pieh Nov 7, 2023
40d2add
fix tests?
pieh Nov 7, 2023
9ebede5
try to use images from deploy (so we can avoid using ones hosted exte…
pieh Nov 8, 2023
599f34d
replicate prod-runtime imagecdn tests in adapters
pieh Nov 8, 2023
011228a
fix import
pieh Nov 8, 2023
d9dea3f
adjusting remote-file tests
pieh Nov 8, 2023
f89b551
adjusting remote-file tests 2
pieh Nov 8, 2023
57fdd3a
cleanup/test
kathmbeck Nov 8, 2023
1a8df77
assert naturalWidth/height in image-cdn tests (both adapters and prod…
pieh Nov 9, 2023
36b918a
remove unused
pieh Nov 9, 2023
5b81ecd
don't use path prefix for alternate image cdn url
pieh Nov 13, 2023
d324338
Merge remote-tracking branch 'origin/master' into alternate-image-url
pieh Nov 13, 2023
0975496
_gatsby/file is prefixed
pieh Nov 13, 2023
d31412a
feat: move custom image cdn url generator implementation to adapter (…
pieh Nov 30, 2023
ed5f3bf
Merge branch 'master' into alternate-image-url
kathmbeck Nov 30, 2023
370d2b4
use position/cover
kathmbeck Nov 30, 2023
3527298
update comment
kathmbeck Nov 30, 2023
a175d3b
update docs
kathmbeck Nov 30, 2023
4ba752e
Merge remote-tracking branch 'origin/master' into alternate-image-url
pieh Dec 4, 2023
4514117
chore: types/jsdocs shuffle
pieh Dec 4, 2023
c7b3da3
apply suggestion from https://github.com/gatsbyjs/gatsby/pull/38685\#…
pieh Dec 6, 2023
78b108c
remove docs from feature branch
pieh Dec 6, 2023
d82f88f
feat: provide custom FILE_CDN url generator from adapter (#38735)
pieh Dec 6, 2023
53ce56d
add note that generated urls ideally are relative, but can be absolut…
pieh Dec 6, 2023
7de5336
feat: allow adding remote file allowed url patterns (#38719)
pieh Dec 11, 2023
8f1bc5d
chore: update adapter README about imageCDN
pieh Dec 11, 2023
434a946
Merge remote-tracking branch 'origin/master' into alternate-image-url
pieh Dec 14, 2023
1bb2e01
use correct remote_images for adapters e2e site
pieh Dec 14, 2023
1c79840
Merge remote-tracking branch 'origin/master' into alternate-image-url
pieh Dec 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export function shouldDispatch(): boolean {
return (
!(
process.env.GATSBY_CLOUD_IMAGE_CDN === `1` ||
process.env.GATSBY_CLOUD_IMAGE_CDN === `true`
process.env.GATSBY_CLOUD_IMAGE_CDN === `true` ||
process.env.NETLIFY_IMAGE_CDN === `true`
) && process.env.NODE_ENV === `production`
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import url from "url"
import {
generateFileUrl,
generateImageUrl,
generateImageUrlAlt,
ImageCDNUrlKeys,
} from "../url-generator"

Expand Down Expand Up @@ -343,3 +344,126 @@ describe(`url-generator`, () => {
)
})
})

describe(`generateImageUrlAlt`, () => {
beforeEach(() => {
process.env.NETLIFY_IMAGE_CDN = `true`
})

afterEach(() => {
delete process.env.NETLIFY_IMAGE_CDN
})

const source = {
url: `https://example.com/image.jpg`,
filename: `image.jpg`,
mimeType: `image/jpeg`,
internal: {
contentDigest: `1234`,
},
}

it(`should return an image based url`, () => {
expect(
generateImageUrlAlt(source, {
width: 100,
height: 100,
cropFocus: `top`,
format: `webp`,
quality: 80,
})
).toMatchInlineSnapshot(
`"/.netlify/images?w=100&h=100&fit=crop&crop=top&fm=webp&q=80&url=https%3A%2F%2Fexample.com%2Fimage.jpg&cd=1234"`
)
})

it(`should handle special characters`, () => {
const source = {
url: `https://example.com/image-éà.jpg`,
filename: `image-éà.jpg`,
mimeType: `image/jpeg`,
internal: {
contentDigest: `1234`,
},
}

expect(
generateImageUrlAlt(source, {
width: 100,
height: 100,
cropFocus: `top`,
format: `webp`,
quality: 80,
})
).toMatchInlineSnapshot(
`"/.netlify/images?w=100&h=100&fit=crop&crop=top&fm=webp&q=80&url=https%3A%2F%2Fexample.com%2Fimage-%C3%A9%C3%A0.jpg&cd=1234"`
)
})

it(`should handle spaces`, () => {
const source = {
url: `https://example.com/image test.jpg`,
filename: `image test.jpg`,
mimeType: `image/jpeg`,
internal: {
contentDigest: `1234`,
},
}

expect(
generateImageUrlAlt(source, {
width: 100,
height: 100,
cropFocus: `top`,
format: `webp`,
quality: 80,
})
).toMatchInlineSnapshot(
`"/.netlify/images?w=100&h=100&fit=crop&crop=top&fm=webp&q=80&url=https%3A%2F%2Fexample.com%2Fimage+test.jpg&cd=1234"`
)
})

it(`should handle encoded urls`, () => {
const source = {
url: `https://example.com/image%20test.jpg`,
filename: `image test.jpg`,
mimeType: `image/jpeg`,
internal: {
contentDigest: `1234`,
},
}

expect(
generateImageUrlAlt(source, {
width: 100,
height: 100,
cropFocus: `top`,
format: `webp`,
quality: 80,
})
).toMatchInlineSnapshot(
`"/.netlify/images?w=100&h=100&fit=crop&crop=top&fm=webp&q=80&url=https%3A%2F%2Fexample.com%2Fimage%2520test.jpg&cd=1234"`
)
})

it.each([
[`width`, `w`, 100],
[`height`, `h`, 50],
[`cropFocus`, `crop`, `center,right`],
[`format`, `fm`, `webp`],
[`quality`, `q`, 60],
] as Array<[keyof ImageArgs, string, ImageArgs[keyof ImageArgs]]>)(
`should set %s in image args`,
(key, queryKey, value) => {
const url = new URL(
// @ts-ignore remove typings
`https://netlify.com${generateImageUrlAlt(source, {
format: `webp`,
[key]: value,
})}`
)

expect(url.searchParams.get(queryKey)).toEqual(value.toString())
}
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ export function generateImageUrl(
imageArgs: Parameters<typeof generateImageArgs>[0],
store?: Store
): string {
if (process.env.NETLIFY_IMAGE_CDN) {
return generateImageUrlAlt(source, imageArgs, store)
}
const filenameWithoutExt = basename(source.filename, extname(source.filename))
const queryStr = generateImageArgs(imageArgs)

Expand Down Expand Up @@ -172,3 +175,75 @@ function generateImageArgs({

return args.join(`&`)
}

export function generateImageUrlAlt(
source: {
url: string
filename: string
mimeType: string
internal: { contentDigest: string }
},
imageArgs: Parameters<typeof generateImageArgs>[0],
store?: Store
): string {
const placeholderOrigin = `http://netlify.com`
const imageParams = generateImageArgsAlt(imageArgs)

const baseURL = new URL(`${placeholderOrigin}/${generateRoutePrefix(store)}`)

baseURL.search = imageParams.toString()
baseURL.searchParams.append(`url`, source.url)
baseURL.searchParams.append(`cd`, source.internal.contentDigest)

return `${baseURL.pathname}${baseURL.search}`
}

function generateRoutePrefix(store?: Store): string {
const state = store?.getState()

const pathPrefix = state?.program?.prefixPaths
? state?.config?.pathPrefix
: ``

return pathPrefix + `.netlify/images`
pieh marked this conversation as resolved.
Show resolved Hide resolved
}

export function generateImageArgsAlt({
width,
height,
format,
cropFocus,
quality,
}: WidthOrHeight & {
format: string
cropFocus?: ImageCropFocus | Array<ImageCropFocus>
quality: number
}): URLSearchParams {
const params = new URLSearchParams()

if (width) {
params.append(`w`, width.toString())
}
if (height) {
params.append(`h`, height.toString())
}
if (cropFocus) {
params.append(`fit`, `crop`)
if (Array.isArray(cropFocus)) {
// For array of cropFocus values, append them as comma-separated string
params.append(`crop`, cropFocus.join(`,`))
} else {
params.append(`crop`, cropFocus)
}
}

if (format) {
params.append(`fm`, format)
}

if (quality) {
params.append(`q`, quality.toString())
}

return params
}