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:
parent
40760a8ace
commit
518433e433
10 changed files with 72 additions and 23 deletions
18
.changeset/dry-worms-knock.md
Normal file
18
.changeset/dry-worms-knock.md
Normal 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"
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
|
@ -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: [],
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -412,7 +412,6 @@ async function generatePath(
|
|||
);
|
||||
|
||||
const request = createRequest({
|
||||
base: config.base,
|
||||
url,
|
||||
headers: new Headers(),
|
||||
logger,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue