0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

feat(assets): Allow customising the route of the image endpoint (#11908)

* feat(assets): Allow customizing the route of the image endpoint

* Update packages/astro/src/core/config/schema.ts

Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>

* feat: use undefined instead of default

* fix: strip param correctly on custom endpoint route

* chore: changeset

* nit: unused import

* fix: other unused import

---------

Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>
This commit is contained in:
Erika 2024-09-11 01:04:16 +02:00 committed by GitHub
parent 40760a8ace
commit 518433e433
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 72 additions and 23 deletions

View file

@ -0,0 +1,18 @@
---
'astro': major
---
The `image.endpoint` config now allow customizing the route of the image endpoint in addition to the entrypoint. This can be useful in niche situations where the default route `/_image` conflicts with an existing route or your local server setup.
```js
import { defineConfig } from "astro/config";
defineConfig({
image: {
endpoint: {
route: "/image",
entrypoint: "./src/image_endpoint.ts"
}
},
})
```

View file

@ -1,4 +1,9 @@
import {
removeLeadingForwardSlash,
removeTrailingForwardSlash,
} from '@astrojs/internal-helpers/path';
import { resolveInjectedRoute } from '../../core/routing/manifest/create.js';
import { getPattern } from '../../core/routing/manifest/pattern.js';
import type { AstroSettings, ManifestData } from '../../types/astro.js';
import type { RouteData } from '../../types/public/internal.js';
@ -28,19 +33,34 @@ function getImageEndpointData(
cwd?: string,
): RouteData {
const endpointEntrypoint =
settings.config.image.endpoint ??
(mode === 'dev' ? 'astro/assets/endpoint/node' : 'astro/assets/endpoint/generic');
settings.config.image.endpoint.entrypoint === undefined // If not set, use default endpoint
? mode === 'dev'
? 'astro/assets/endpoint/node'
: 'astro/assets/endpoint/generic'
: settings.config.image.endpoint.entrypoint;
const segments = [
[
{
content: removeTrailingForwardSlash(
removeLeadingForwardSlash(settings.config.image.endpoint.route),
),
dynamic: false,
spread: false,
},
],
];
return {
type: 'endpoint',
isIndex: false,
route: '/_image',
pattern: /^\/_image$/,
segments: [[{ content: '_image', dynamic: false, spread: false }]],
route: settings.config.image.endpoint.route,
pattern: getPattern(segments, settings.config.base, settings.config.trailingSlash),
segments,
params: [],
component: resolveInjectedRoute(endpointEntrypoint, settings.config.root, cwd).component,
generate: () => '',
pathname: '/_image',
pathname: settings.config.image.endpoint.route,
prerender: false,
fallbackRoutes: [],
};

View file

@ -33,7 +33,7 @@ interface SharedServiceProps<T extends Record<string, any> = Record<string, any>
/**
* Return the URL to the endpoint or URL your images are generated from.
*
* For a local service, your service should expose an endpoint handling the image requests, or use Astro's at `/_image`.
* For a local service, your service should expose an endpoint handling the image requests, or use Astro's which by default, is located at `/_image`.
*
* For external services, this should point to the URL your images are coming from, for instance, `/_vercel/image`
*
@ -343,7 +343,7 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
options[key] && searchParams.append(param, options[key].toString());
});
const imageEndpoint = joinPaths(import.meta.env.BASE_URL, '/_image');
const imageEndpoint = joinPaths(import.meta.env.BASE_URL, imageConfig.endpoint.route);
return `${imageEndpoint}?${searchParams}`;
},
parseURL(url) {

View file

@ -101,7 +101,7 @@ export default function assets({
};
return [
// Expose the components and different utilities from `astro:assets` and handle serving images from `/_image` in dev
// Expose the components and different utilities from `astro:assets`
{
name: 'astro:assets',
async resolveId(id) {

View file

@ -412,7 +412,6 @@ async function generatePath(
);
const request = createRequest({
base: config.base,
url,
headers: new Headers(),
logger,

View file

@ -65,6 +65,7 @@ export const ASTRO_CONFIG_DEFAULTS = {
inlineStylesheets: 'auto',
},
image: {
endpoint: { entrypoint: undefined, route: '/_image' },
service: { entrypoint: 'astro/assets/services/sharp', config: {} },
},
devToolbar: {
@ -240,7 +241,15 @@ export const AstroConfigSchema = z.object({
.optional(),
image: z
.object({
endpoint: z.string().optional(),
endpoint: z
.object({
route: z
.literal('/_image')
.or(z.string())
.default(ASTRO_CONFIG_DEFAULTS.image.endpoint.route),
entrypoint: z.string().optional(),
})
.default(ASTRO_CONFIG_DEFAULTS.image.endpoint),
service: z
.object({
entrypoint: z

View file

@ -5,7 +5,6 @@ type HeaderType = Headers | Record<string, any> | IncomingHttpHeaders;
type RequestBody = ArrayBuffer | Blob | ReadableStream | URLSearchParams | FormData;
export interface CreateRequestOptions {
base: string;
url: URL | string;
clientAddress?: string | undefined;
headers: HeaderType;

View file

@ -904,24 +904,28 @@ export interface AstroUserConfig {
/**
* @docs
* @name image.endpoint
* @type {string}
* @default `undefined`
* @type {{route: string, entrypoint: undefined | string}}
* @default `{route: '/_image', entrypoint: 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`.
* Set the endpoint to use for image optimization in dev and SSR. The `entrypoint` property can be set to `undefined` to use the default image endpoint.
*
* ```js
* {
* image: {
* // Example: Use a custom image endpoint
* endpoint: './src/image-endpoint.ts',
* // Example: Use a custom image endpoint at `/custom_endpoint`
* endpoint: {
* route: '/custom_endpoint',
* entrypoint: 'src/my_endpoint.ts',
* },
* },
* }
* ```
*/
endpoint?: string;
endpoint?: {
route: '/_image' | (string & {});
entrypoint: undefined | string;
};
/**
* @docs

View file

@ -166,9 +166,9 @@ export async function handleRoute({
const filePath: URL | undefined = matchedRoute.filePath;
const { preloadedComponent } = matchedRoute;
route = matchedRoute.route;
// Allows adapters to pass in locals in dev mode.
request = createRequest({
base: config.base,
url,
headers: incomingRequest.headers,
method: incomingRequest.method,

View file

@ -646,7 +646,7 @@ describe('astro:image', () => {
customEndpointFixture = await loadFixture({
root: './fixtures/core-image/',
image: {
endpoint: './src/custom-endpoint.ts',
endpoint: { entrypoint: './src/custom-endpoint.ts' },
service: testImageService({ foo: 'bar' }),
domains: ['avatars.githubusercontent.com'],
},
@ -1133,7 +1133,7 @@ describe('astro:image', () => {
outDir: './dist/server-prod',
adapter: testAdapter(),
image: {
endpoint: 'astro/assets/endpoint/node',
endpoint: { entrypoint: 'astro/assets/endpoint/node' },
service: testImageService(),
},
});