diff --git a/.changeset/dirty-seahorses-move.md b/.changeset/dirty-seahorses-move.md new file mode 100644 index 000000000000..b9eeb87eef06 --- /dev/null +++ b/.changeset/dirty-seahorses-move.md @@ -0,0 +1,5 @@ +--- +'astro': minor +--- + +Add a new `image.endpoint` setting to allow using a custom endpoint in dev and SSR diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 67eed4a9fd37..10e15b6ffd74 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -974,6 +974,28 @@ export interface AstroUserConfig { * @name Image Options */ image?: { + /** + * @docs + * @name image.endpoint + * @type {string} + * @default `undefined` + * @version 3.1.0 + * @description + * Set the endpoint to use for image optimization in dev and SSR. Set to `undefined` to use the default endpoint. + * + * The endpoint will always be injected at `/_image`. + * + * ```js + * { + * image: { + * // Example: Use a custom image endpoint + * endpoint: './src/image-endpoint.ts', + * }, + * } + * ``` + */ + endpoint?: string; + /** * @docs * @name image.service diff --git a/packages/astro/src/assets/internal.ts b/packages/astro/src/assets/internal.ts index 1a266c07a4f0..9d2c9eb0e1d8 100644 --- a/packages/astro/src/assets/internal.ts +++ b/packages/astro/src/assets/internal.ts @@ -11,10 +11,12 @@ import type { import { matchHostname, matchPattern } from './utils/remotePattern.js'; export function injectImageEndpoint(settings: AstroSettings) { + const endpointEntrypoint = settings.config.image.endpoint ?? 'astro/assets/image-endpoint'; + // TODO: Add a setting to disable the image endpoint settings.injectedRoutes.push({ pattern: '/_image', - entryPoint: 'astro/assets/image-endpoint', + entryPoint: endpointEntrypoint, prerender: false, }); diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index 3854f6d22633..b36017c22abb 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -183,6 +183,7 @@ export const AstroConfigSchema = z.object({ .default(ASTRO_CONFIG_DEFAULTS.redirects), image: z .object({ + endpoint: z.string().optional(), service: z .object({ entrypoint: z diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js index 666739539a21..7a0a468225bf 100644 --- a/packages/astro/test/core-image.test.js +++ b/packages/astro/test/core-image.test.js @@ -462,6 +462,47 @@ describe('astro:image', () => { expect($('#local img').attr('data-service-config')).to.equal('bar'); }); }); + + describe('custom endpoint', async () => { + /** @type {import('./test-utils').DevServer} */ + let customEndpointDevServer; + + /** @type {import('./test-utils.js').Fixture} */ + let customEndpointFixture; + + before(async () => { + customEndpointFixture = await loadFixture({ + root: './fixtures/core-image/', + image: { + endpoint: './src/custom-endpoint.ts', + service: testImageService({ foo: 'bar' }), + domains: ['avatars.githubusercontent.com'], + }, + }); + + customEndpointDevServer = await customEndpointFixture.startDevServer({ + server: { port: 4324 }, + }); + }); + + it('custom endpoint works', async () => { + const response = await customEndpointFixture.fetch('/'); + const html = await response.text(); + + const $ = cheerio.load(html); + const src = $('#local img').attr('src'); + + let res = await customEndpointFixture.fetch(src); + expect(res.status).to.equal(200); + expect(await res.text()).to.equal( + "You fool! I'm not a image endpoint at all, I just return this!" + ); + }); + + after(async () => { + await customEndpointDevServer.stop(); + }); + }); }); describe('proper errors', () => { diff --git a/packages/astro/test/fixtures/core-image/src/custom-endpoint.ts b/packages/astro/test/fixtures/core-image/src/custom-endpoint.ts new file mode 100644 index 000000000000..22c32497b009 --- /dev/null +++ b/packages/astro/test/fixtures/core-image/src/custom-endpoint.ts @@ -0,0 +1,3 @@ +export const GET = async () => { + return new Response("You fool! I'm not a image endpoint at all, I just return this!", { status: 200 }); +};