diff options
author | 2022-07-18 19:43:40 +0000 | |
---|---|---|
committer | 2022-07-18 19:43:40 +0000 | |
commit | 2a7dd040e8a65d62fbb3bbd7308f523bd48deda5 (patch) | |
tree | 3b091a486ae32977adb632ad34557d428720029c | |
parent | eee14b5e5ff4221877d58e3fddbe26593f7a419e (diff) | |
download | astro-2a7dd040e8a65d62fbb3bbd7308f523bd48deda5.tar.gz astro-2a7dd040e8a65d62fbb3bbd7308f523bd48deda5.tar.zst astro-2a7dd040e8a65d62fbb3bbd7308f523bd48deda5.zip |
Improving support for third-party hosted image services (#3957)
* WIP: always use the built-in sharp service for local images in `dev`
* adding type definitions for the integration's use of globalThis
* simplifying the globalThis type checking
* chore: adding changeset
* removing temp hosted service used for testing
-rw-r--r-- | .changeset/purple-vans-bake.md | 5 | ||||
-rw-r--r-- | packages/integrations/image/README.md | 2 | ||||
-rw-r--r-- | packages/integrations/image/src/endpoints/dev.ts | 4 | ||||
-rw-r--r-- | packages/integrations/image/src/get-image.ts | 27 | ||||
-rw-r--r-- | packages/integrations/image/src/index.ts | 24 | ||||
-rw-r--r-- | packages/integrations/image/src/types.ts | 12 |
6 files changed, 59 insertions, 15 deletions
diff --git a/.changeset/purple-vans-bake.md b/.changeset/purple-vans-bake.md new file mode 100644 index 000000000..3af99ba74 --- /dev/null +++ b/.changeset/purple-vans-bake.md @@ -0,0 +1,5 @@ +--- +'@astrojs/image': patch +--- + +Improves the `astro dev` experience when using a third-party hosted image service diff --git a/packages/integrations/image/README.md b/packages/integrations/image/README.md index b14587d71..ee9fdf3c9 100644 --- a/packages/integrations/image/README.md +++ b/packages/integrations/image/README.md @@ -73,6 +73,8 @@ The included `sharp` transformer supports resizing images and encoding them to d The intergration can be configured to run with a different image service, either a hosted image service or a full image transformer that runs locally in your build or SSR deployment. +> During development, local images may not have been published yet and would not be available to hosted image services. Local images will always use the built-in `sharp` service when using `astro dev`. + There are currently no other configuration options for the `@astrojs/image` integration. Please [open an issue](https://github.com/withastro/astro/issues/new/choose) if you have a compelling use case to share. <details> diff --git a/packages/integrations/image/src/endpoints/dev.ts b/packages/integrations/image/src/endpoints/dev.ts index 3d8b28993..67b37b177 100644 --- a/packages/integrations/image/src/endpoints/dev.ts +++ b/packages/integrations/image/src/endpoints/dev.ts @@ -1,10 +1,10 @@ import type { APIRoute } from 'astro'; import { lookup } from 'mrmime'; -// @ts-ignore -import loader from 'virtual:image-loader'; import { loadImage } from '../utils.js'; export const get: APIRoute = async ({ request }) => { + const loader = globalThis.astroImage.ssrLoader; + try { const url = new URL(request.url); const transform = loader.parseTransform(url.searchParams); diff --git a/packages/integrations/image/src/get-image.ts b/packages/integrations/image/src/get-image.ts index 5eb41cf73..83ac2e5ba 100644 --- a/packages/integrations/image/src/get-image.ts +++ b/packages/integrations/image/src/get-image.ts @@ -8,7 +8,7 @@ import { OutputFormat, TransformOptions, } from './types.js'; -import { parseAspectRatio } from './utils.js'; +import { isRemoteImage, parseAspectRatio } from './utils.js'; export interface GetImageTransform extends Omit<TransformOptions, 'src'> { src: string | ImageMetadata | Promise<{ default: ImageMetadata }>; @@ -105,23 +105,32 @@ export async function getImage( loader: ImageService, transform: GetImageTransform ): Promise<ImageAttributes> { - (globalThis as any).loader = loader; + globalThis.astroImage.loader = loader; const resolved = await resolveTransform(transform); + const attributes = await loader.getImageAttributes(resolved); + const isDev = globalThis.astroImage.command === 'dev'; + const isLocalImage = !isRemoteImage(resolved.src); + + const _loader = isDev && isLocalImage ? globalThis.astroImage.ssrLoader : loader; + + if (!_loader) { + throw new Error('@astrojs/image: loader not found!'); + } + // For SSR services, build URLs for the injected route - if (isSSRService(loader)) { - const { searchParams } = loader.serializeTransform(resolved); + if (isSSRService(_loader)) { + const { searchParams } = _loader.serializeTransform(resolved); // cache all images rendered to HTML - if (globalThis && (globalThis as any).addStaticImage) { - (globalThis as any)?.addStaticImage(resolved); + if (globalThis?.astroImage) { + globalThis.astroImage.addStaticImage(resolved); } - const src = - globalThis && (globalThis as any).filenameFormat - ? (globalThis as any).filenameFormat(resolved, searchParams) + const src = globalThis?.astroImage + ? globalThis.astroImage.filenameFormat(resolved, searchParams) : `${ROUTE_PATTERN}?${searchParams.toString()}`; return { diff --git a/packages/integrations/image/src/index.ts b/packages/integrations/image/src/index.ts index 3721f9667..da1ec1d4b 100644 --- a/packages/integrations/image/src/index.ts +++ b/packages/integrations/image/src/index.ts @@ -3,6 +3,7 @@ import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; import { OUTPUT_DIR, PKG_NAME, ROUTE_PATTERN } from './constants.js'; +import sharp from './loaders/sharp.js'; import { IntegrationOptions, TransformOptions } from './types.js'; import { ensureDir, @@ -51,15 +52,15 @@ const createIntegration = (options: IntegrationOptions = {}): AstroIntegration = // Used to cache all images rendered to HTML // Added to globalThis to share the same map in Node and Vite - (globalThis as any).addStaticImage = (transform: TransformOptions) => { + function addStaticImage(transform: TransformOptions) { staticImages.set(propsToFilename(transform), transform); }; // TODO: Add support for custom, user-provided filename format functions - (globalThis as any).filenameFormat = ( + function filenameFormat( transform: TransformOptions, searchParams: URLSearchParams - ) => { + ) { if (mode === 'ssg') { return isRemoteImage(transform.src) ? path.join(OUTPUT_DIR, path.basename(propsToFilename(transform))) @@ -73,6 +74,16 @@ const createIntegration = (options: IntegrationOptions = {}): AstroIntegration = } }; + // Initialize the integration's globalThis namespace + // This is needed to share scope between Node and Vite + globalThis.astroImage = { + loader: undefined, // initialized in first getImage() call + ssrLoader: sharp, + command, + addStaticImage, + filenameFormat, + } + if (mode === 'ssr') { injectRoute({ pattern: ROUTE_PATTERN, @@ -83,7 +94,12 @@ const createIntegration = (options: IntegrationOptions = {}): AstroIntegration = }, 'astro:build:done': async ({ dir }) => { for await (const [filename, transform] of staticImages) { - const loader = (globalThis as any).loader; + const loader = globalThis.astroImage.loader; + + if (!loader || !('transform' in loader)) { + // this should never be hit, how was a staticImage added without an SSR service? + return; + } let inputBuffer: Buffer | undefined = undefined; let outputFile: string; diff --git a/packages/integrations/image/src/types.ts b/packages/integrations/image/src/types.ts index 96067e828..e6c315c23 100644 --- a/packages/integrations/image/src/types.ts +++ b/packages/integrations/image/src/types.ts @@ -2,6 +2,18 @@ export type { Image, Picture } from '../components/index.js'; export * from './index.js'; +interface ImageIntegration { + loader?: ImageService; + ssrLoader: SSRImageService; + command: 'dev' | 'build'; + addStaticImage: (transform: TransformOptions) => void; + filenameFormat: (transform: TransformOptions, searchParams: URLSearchParams) => string; +} + +declare global { + var astroImage: ImageIntegration; +} + export type InputFormat = | 'heic' | 'heif' |