Skip to content

Conversation

@pieh
Copy link
Contributor

@pieh pieh commented May 28, 2025

https://linear.app/netlify/issue/FRB-1838/support-image-optimization

Adds Image CDN support. Should have parity with handling we have in CLI:

  • supports both local and remote images
  • checks if remote image is allowed
  • applies image transformations with ipx (there is transformation from our syntax to ipx syntax which was copied and adjusted from CLI)

@pieh pieh force-pushed the michalpiechowiak/frb-1858-add-passthrough-image-optimization-for-netlifyimages branch from 97cf088 to 655159d Compare May 30, 2025 11:19
@pieh pieh force-pushed the michalpiechowiak/frb-1858-add-passthrough-image-optimization-for-netlifyimages branch from 655159d to 3541d01 Compare May 30, 2025 11:49
@pieh pieh changed the title feat: initial image cdn handling feat: image cdn support Jun 4, 2025
/**
* Returns Buffer of a generated random noise jpeg image with the specified width and height.
*/
export async function generateImage(width: number, height: number): Promise<Buffer> {
Copy link
Contributor Author

@pieh pieh Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid adding fixture style images to repo, this will generate images on-demand (random noise, so they don't mean anything, but can be used to assert resizing correctness)

In this PR this is used in ~unit tests for ImageHandler AND e2e for vite plugin

Additionally minor adjustments to Fixture to allow return of this image to feed into Fixture.withFile builder

Comment on lines 12 to 13
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const imageBuffer = image.data as Buffer
Copy link
Contributor Author

@pieh pieh Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TS type for the lib that generates random images is ... not working (image is treated as error type) :/

| Image CDN | ❌ No |
| Image CDN | ✅ Yes |

> Note: Missing features will be added incrementally. This module is **not** intended to be a full replacement for the
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First sentence of note right now doesn't make much sense because feature support matrix with this PR is "everything is supported".

Given our previous chats I was thinking about Extensions item and marking as not supported in which case Note itself might not need any changes

}

export type ResponseType = 'edge-function' | 'function' | 'redirect' | 'static'
export type ResponseType = 'edge-function' | 'function' | 'image' | 'redirect' | 'static'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be considered a breaking change? There is new possible response type for public handleAndIntrospect(NodeRequest) methods

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that's worth a major bump, but happy to defer to you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should consider widening a type as a breaking change

@pieh pieh requested a review from a team as a code owner June 4, 2025 11:55
"vitest": "^3.1.4"
},
"dependencies": {
"ipx": "^3.0.3"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👯 👯 👯 netlify/cli#6308

Copy link
Contributor Author

@pieh pieh Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good shout, but hopefully this would be addressed by sharp bump that is done in ipx@3 - https://github.com/unjs/ipx/releases/tag/v3.0.0 / lovell/sharp#3750 (vs ipx@2 using older sharp installation that is problematic)

@pieh pieh force-pushed the michalpiechowiak/frb-1858-add-passthrough-image-optimization-for-netlifyimages branch from 8a9a46b to 7621a4d Compare June 4, 2025 16:50
const dev = new NetlifyDev({
projectRoot: directory,
edgeFunctions: {
// disable edge functions to avoid relying on edge functions handling spinning up internal server
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently in main:

// If a custom server has been provided, use it. If not, we must stand up
// a new one, since it's required for communication with edge functions.
if (typeof this.#server === 'string') {
serverAddress = this.#server
} else if (this.#features.edgeFunctions) {
const passthroughServer = new HTTPServer(async (req) => {
const res = await this.handle(req)
return res ?? new Response(null, { status: 404 })
})
this.#cleanupJobs.push(() => passthroughServer.stop())
serverAddress = await passthroughServer.start()
}

Handling of local images does rely on origin server, so for the case of ~standalone @netlify/dev without ~external origin server I wanted to have a test that doesn't rely on any side-effects other features might have to make sure it's all self-reliant.

Maybe this could be adjusted to just disable ALL features except for images, because disabling only edge-functions specifically might leak too much of current implementation detail (or have 2 cases - one with default and everything enabled to make sure it all works together and one with just images enabled to test that images handling deal with all its requirements and doesn't rely on other features)

}

export type ResponseType = 'edge-function' | 'function' | 'redirect' | 'static'
export type ResponseType = 'edge-function' | 'function' | 'image' | 'redirect' | 'static'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that's worth a major bump, but happy to defer to you.

eduardoboucas
eduardoboucas previously approved these changes Jun 5, 2025
/**
* Helper to create a server handler that responds with a random noise image.
*/
export function createImageServerHandler(imageConfigFromURL: (url: URL) => { width: number; height: number } | null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker, but I'm curious why you choose to pass a function here as opposed to something like Record<URL, { width: number, height: number }>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This felt customizable enough to support potential future test setups that respond to /random-image/:width/:height without having to declare all allowed size combinations ahead of time in tests.

But because nothing like that is actually being done and I could get away with alternative you mentioned (or probably anything heh) for the current tests - I'm happy to adjust, if that would be preferred. I don't have strong feelings here one way or the other.

…1858-add-passthrough-image-optimization-for-netlifyimages
@pieh pieh merged commit 01c844d into main Jun 6, 2025
10 checks passed
@pieh pieh deleted the michalpiechowiak/frb-1858-add-passthrough-image-optimization-for-netlifyimages branch June 6, 2025 09:58
@token-generator-app token-generator-app bot mentioned this pull request Jun 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants