diff --git a/.changeset/breezy-beers-peel.md b/.changeset/breezy-beers-peel.md new file mode 100644 index 000000000000..dc8739fe74d1 --- /dev/null +++ b/.changeset/breezy-beers-peel.md @@ -0,0 +1,5 @@ +--- +'@astrojs/image': patch +--- + +Fixes use of image worker pool in SSR environments diff --git a/packages/integrations/image/src/index.ts b/packages/integrations/image/src/index.ts index e3770d5e91e9..80d87fbd130c 100644 --- a/packages/integrations/image/src/index.ts +++ b/packages/integrations/image/src/index.ts @@ -11,6 +11,12 @@ export { getPicture } from './lib/get-picture.js'; const PKG_NAME = '@astrojs/image'; const ROUTE_PATTERN = '/_image'; +const UNSUPPORTED_ADAPTERS = new Set([ + '@astrojs/cloudflare', + '@astrojs/deno', + '@astrojs/netlify/edge-functions', + '@astrojs/vercel/edge' +]); interface BuildConfig { client: URL; @@ -100,6 +106,11 @@ export default function integration(options: IntegrationOptions = {}): AstroInte _buildConfig = config.build; }, 'astro:build:start': ({ buildConfig }) => { + const adapterName = _config.adapter?.name; + if(adapterName && UNSUPPORTED_ADAPTERS.has(adapterName)) { + throw new Error(`@astrojs/image is not supported with the ${adapterName} adapter. Please choose a Node.js compatible adapter.`); + } + // Backwards compat if (needsBuildConfig) { _buildConfig = buildConfig; diff --git a/packages/integrations/image/src/loaders/squoosh.ts b/packages/integrations/image/src/loaders/squoosh.ts index 87d6e26ec1ad..5d71cdb7fbce 100644 --- a/packages/integrations/image/src/loaders/squoosh.ts +++ b/packages/integrations/image/src/loaders/squoosh.ts @@ -3,11 +3,12 @@ import { red } from 'kleur/colors'; import { error } from '../utils/logger.js'; import { metadata } from '../utils/metadata.js'; import { isRemoteImage } from '../utils/paths.js'; -import { processBuffer } from '../vendor/squoosh/image-pool.js'; import type { Operation } from '../vendor/squoosh/image.js'; import type { OutputFormat, TransformOptions } from './index.js'; import { BaseSSRService } from './index.js'; +const imagePoolModulePromise = import('../vendor/squoosh/image-pool.js'); + class SquooshService extends BaseSSRService { async processAvif(image: any, transform: TransformOptions) { const encodeOptions = transform.quality @@ -112,7 +113,7 @@ class SquooshService extends BaseSSRService { }); throw new Error(`Unknown image output: "${transform.format}" used for ${transform.src}`); } - + const { processBuffer } = await imagePoolModulePromise; const data = await processBuffer(inputBuffer, operations, transform.format, transform.quality); return { diff --git a/packages/integrations/image/src/vendor/squoosh/image-pool.ts b/packages/integrations/image/src/vendor/squoosh/image-pool.ts index d29245fb40dc..e19215397c89 100644 --- a/packages/integrations/image/src/vendor/squoosh/image-pool.ts +++ b/packages/integrations/image/src/vendor/squoosh/image-pool.ts @@ -1,5 +1,6 @@ import { isMainThread } from 'node:worker_threads'; import { cpus } from 'os'; +import { fileURLToPath } from 'url'; import type { OutputFormat } from '../../loaders/index.js'; import execOnce from '../../utils/execOnce.js'; import WorkerPool from '../../utils/workerPool.js'; @@ -12,7 +13,7 @@ const getWorker = execOnce( // There will be at most 7 workers needed since each worker will take // at least 1 operation type. Math.max(1, Math.min(cpus().length - 1, 7)), - './node_modules/@astrojs/image/dist/vendor/squoosh/image-pool.js' + fileURLToPath(import.meta.url) ); } ) diff --git a/packages/integrations/image/src/vite-plugin-astro-image.ts b/packages/integrations/image/src/vite-plugin-astro-image.ts index 04f2303414d7..ee06b0d5ca35 100644 --- a/packages/integrations/image/src/vite-plugin-astro-image.ts +++ b/packages/integrations/image/src/vite-plugin-astro-image.ts @@ -113,6 +113,27 @@ export function createPlugin(config: AstroConfig, options: Required { + for(const name of Object.keys(chunk.modules)) { + if(name.endsWith('vendor/squoosh/image-pool.js')) { + return '[name].[hash].mjs'; + } + } + + if(typeof chunkFileNames === 'function') { + return chunkFileNames.call(this, chunk); + } + + return chunkFileNames!; + }; + } + }, async renderChunk(code) { const assetUrlRE = /__ASTRO_IMAGE_ASSET__([a-z\d]{8})__(?:_(.*?)__)?/g;