diff --git a/packages/integrations/image/src/index.ts b/packages/integrations/image/src/index.ts index f41b943d2..cc5d0d518 100644 --- a/packages/integrations/image/src/index.ts +++ b/packages/integrations/image/src/index.ts @@ -3,8 +3,8 @@ import { ssgBuild } from './build/ssg.js'; import type { ImageService, SSRImageService, TransformOptions } from './loaders/index.js'; import type { LoggerLevel } from './utils/logger.js'; import { joinPaths, prependForwardSlash, propsToFilename } from './utils/paths.js'; -import { createPlugin } from './vite-plugin-astro-image.js'; import { copyWasmFiles } from './vendor/squoosh/copy-wasm.js'; +import { createPlugin } from './vite-plugin-astro-image.js'; export { getImage } from './lib/get-image.js'; export { getPicture } from './lib/get-picture.js'; @@ -48,19 +48,17 @@ export default function integration(options: IntegrationOptions = {}): AstroInte return { plugins: [createPlugin(_config, resolvedOptions)], optimizeDeps: { - include: [ - 'image-size', - ].filter(Boolean), + include: ['image-size'].filter(Boolean), }, build: { - rollupOptions: { - external: ["sharp"] - } - }, + rollupOptions: { + external: ['sharp'], + }, + }, ssr: { noExternal: ['@astrojs/image', resolvedOptions.serviceEntryPoint], }, - assetsInclude: ['**/*.wasm'] + assetsInclude: ['**/*.wasm'], }; } @@ -78,18 +76,19 @@ export default function integration(options: IntegrationOptions = {}): AstroInte entryPoint: '@astrojs/image/endpoint', }); } - - const { default: defaultLoader } = await import(resolvedOptions.serviceEntryPoint === '@astrojs/image/sharp' - ? './loaders/sharp.js' - : './loaders/squoosh.js' + + const { default: defaultLoader } = await import( + resolvedOptions.serviceEntryPoint === '@astrojs/image/sharp' + ? './loaders/sharp.js' + : './loaders/squoosh.js' ); - + globalThis.astroImage = { - defaultLoader - } + defaultLoader, + }; }, 'astro:build:start': async ({ buildConfig }) => { - _buildConfig = buildConfig + _buildConfig = buildConfig; }, 'astro:build:setup': async () => { // Used to cache all images rendered to HTML @@ -141,7 +140,7 @@ export default function integration(options: IntegrationOptions = {}): AstroInte if (resolvedOptions.serviceEntryPoint === '@astrojs/image/squoosh') { await copyWasmFiles(_buildConfig.server); } - } + }, }, }; } diff --git a/packages/integrations/image/src/lib/get-picture.ts b/packages/integrations/image/src/lib/get-picture.ts index e3b07b505..4149a93a2 100644 --- a/packages/integrations/image/src/lib/get-picture.ts +++ b/packages/integrations/image/src/lib/get-picture.ts @@ -1,9 +1,9 @@ /// import mime from 'mime'; import { OutputFormat, parseAspectRatio, TransformOptions } from '../loaders/index.js'; +import { extname } from '../utils/paths.js'; import { ImageMetadata } from '../vite-plugin-astro-image.js'; import { getImage } from './get-image.js'; -import { extname } from '../utils/paths.js'; export interface GetPictureParams { src: string | ImageMetadata | Promise<{ default: ImageMetadata }>; diff --git a/packages/integrations/image/src/loaders/index.ts b/packages/integrations/image/src/loaders/index.ts index 30ccb879f..e5010c36d 100644 --- a/packages/integrations/image/src/loaders/index.ts +++ b/packages/integrations/image/src/loaders/index.ts @@ -1,4 +1,3 @@ -import { AstroConfig } from 'astro'; import { htmlColorNames, type NamedColor } from '../utils/colornames.js'; /// @@ -299,5 +298,8 @@ export abstract class BaseSSRService implements SSRImageService { return transform; } - abstract transform(inputBuffer: Buffer, transform: TransformOptions): Promise<{ data: Buffer, format: OutputFormat }>; + abstract transform( + inputBuffer: Buffer, + transform: TransformOptions + ): Promise<{ data: Buffer; format: OutputFormat }>; } diff --git a/packages/integrations/image/src/loaders/sharp.ts b/packages/integrations/image/src/loaders/sharp.ts index dd58aacb0..55ea28645 100644 --- a/packages/integrations/image/src/loaders/sharp.ts +++ b/packages/integrations/image/src/loaders/sharp.ts @@ -1,6 +1,6 @@ import sharp from 'sharp'; -import { BaseSSRService, isOutputFormatSupportsAlpha } from '../loaders/index.js'; import type { SSRImageService } from '../loaders/index.js'; +import { BaseSSRService, isOutputFormatSupportsAlpha } from '../loaders/index.js'; import type { OutputFormat, TransformOptions } from './index.js'; class SharpService extends BaseSSRService { diff --git a/packages/integrations/image/src/loaders/squoosh.ts b/packages/integrations/image/src/loaders/squoosh.ts index b4e9d636a..20529c9f8 100644 --- a/packages/integrations/image/src/loaders/squoosh.ts +++ b/packages/integrations/image/src/loaders/squoosh.ts @@ -1,12 +1,12 @@ // @ts-ignore import { red } from 'kleur/colors'; -import { BaseSSRService } from './index.js'; import { error } from '../utils/logger.js'; import { metadata } from '../utils/metadata.js'; import { isRemoteImage } from '../utils/paths.js'; -import type { OutputFormat, TransformOptions } from './index.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'; class SquooshService extends BaseSSRService { async processAvif(image: any, transform: TransformOptions) { @@ -58,7 +58,10 @@ class SquooshService extends BaseSSRService { }; } - async autorotate(transform: TransformOptions, inputBuffer: Buffer): Promise { + async autorotate( + transform: TransformOptions, + inputBuffer: Buffer + ): Promise { // check EXIF orientation data and rotate the image if needed try { const meta = await metadata(transform.src, inputBuffer); @@ -74,15 +77,15 @@ class SquooshService extends BaseSSRService { case 8: return { type: 'rotate', numRotations: 3 }; } - } catch { } + } catch {} } async transform(inputBuffer: Buffer, transform: TransformOptions) { const operations: Operation[] = []; if (!isRemoteImage(transform.src)) { - const autorotate = await this.autorotate(transform, inputBuffer) - + const autorotate = await this.autorotate(transform, inputBuffer); + if (autorotate) { operations.push(autorotate); } @@ -96,7 +99,7 @@ class SquooshService extends BaseSSRService { type: 'resize', width, height, - }) + }); } if (!transform.format) { @@ -108,12 +111,17 @@ class SquooshService extends BaseSSRService { throw new Error(`Unknown image output: "${transform.format}" used for ${transform.src}`); } - const data = await processBuffer(inputBuffer, operations, transform.format, transform.quality || 100); + const data = await processBuffer( + inputBuffer, + operations, + transform.format, + transform.quality || 100 + ); return { data: Buffer.from(data), - format: transform.format - } + format: transform.format, + }; } } diff --git a/packages/integrations/image/src/utils/execOnce.ts b/packages/integrations/image/src/utils/execOnce.ts index 1a9a7ba68..97201e54f 100644 --- a/packages/integrations/image/src/utils/execOnce.ts +++ b/packages/integrations/image/src/utils/execOnce.ts @@ -1,14 +1,12 @@ -export default function execOnce ReturnType>( - fn: T -): T { - let used = false - let result: ReturnType +export default function execOnce ReturnType>(fn: T): T { + let used = false; + let result: ReturnType; - return ((...args: any[]) => { - if (!used) { - used = true - result = fn(...args) - } - return result - }) as T + return ((...args: any[]) => { + if (!used) { + used = true; + result = fn(...args); + } + return result; + }) as T; } diff --git a/packages/integrations/image/src/utils/metadata.ts b/packages/integrations/image/src/utils/metadata.ts index 23e83b614..715323d39 100644 --- a/packages/integrations/image/src/utils/metadata.ts +++ b/packages/integrations/image/src/utils/metadata.ts @@ -9,7 +9,7 @@ export interface Metadata extends ImageMetadata { } export async function metadata(src: URL | string, data?: Buffer): Promise { - const file = data || await fs.readFile(src); + const file = data || (await fs.readFile(src)); const { width, height, type, orientation } = await sizeOf(file); const isPortrait = (orientation || 0) >= 5; diff --git a/packages/integrations/image/src/utils/paths.ts b/packages/integrations/image/src/utils/paths.ts index 1ebf3ece7..cd21da95d 100644 --- a/packages/integrations/image/src/utils/paths.ts +++ b/packages/integrations/image/src/utils/paths.ts @@ -1,4 +1,4 @@ -import { OutputFormat, TransformOptions } from '../loaders/index.js'; +import { TransformOptions } from '../loaders/index.js'; import { shorthash } from './shorthash.js'; export function isRemoteImage(src: string) { diff --git a/packages/integrations/image/src/utils/workerPool.ts b/packages/integrations/image/src/utils/workerPool.ts index 5b6b75829..5c4c45c6e 100644 --- a/packages/integrations/image/src/utils/workerPool.ts +++ b/packages/integrations/image/src/utils/workerPool.ts @@ -1,125 +1,123 @@ /* tslint-disable ban-types */ -import { Worker, parentPort } from 'worker_threads'; import { TransformStream } from 'web-streams-polyfill'; +import { parentPort, Worker } from 'worker_threads'; function uuid() { - return Array.from({ length: 16 }, () => - Math.floor(Math.random() * 256).toString(16), - ).join(''); + return Array.from({ length: 16 }, () => Math.floor(Math.random() * 256).toString(16)).join(''); } interface Job { - msg: I; - resolve: (result: any) => void; - reject: (reason: any) => void; + msg: I; + resolve: (result: any) => void; + reject: (reason: any) => void; } export default class WorkerPool { - public numWorkers: number; - public jobQueue: TransformStream, Job>; - public workerQueue: TransformStream; - public done: Promise; + public numWorkers: number; + public jobQueue: TransformStream, Job>; + public workerQueue: TransformStream; + public done: Promise; - constructor(numWorkers: number, workerFile: string) { - this.numWorkers = numWorkers; - this.jobQueue = new TransformStream(); - this.workerQueue = new TransformStream(); + constructor(numWorkers: number, workerFile: string) { + this.numWorkers = numWorkers; + this.jobQueue = new TransformStream(); + this.workerQueue = new TransformStream(); - const writer = this.workerQueue.writable.getWriter(); - for (let i = 0; i < numWorkers; i++) { - writer.write(new Worker(workerFile)); - } - writer.releaseLock(); + const writer = this.workerQueue.writable.getWriter(); + for (let i = 0; i < numWorkers; i++) { + writer.write(new Worker(workerFile)); + } + writer.releaseLock(); - this.done = this._readLoop(); - } + this.done = this._readLoop(); + } - async _readLoop() { - const reader = this.jobQueue.readable.getReader(); - while (true) { - const { value, done } = await reader.read(); - if (done) { - await this._terminateAll(); - return; - } + async _readLoop() { + const reader = this.jobQueue.readable.getReader(); + while (true) { + const { value, done } = await reader.read(); + if (done) { + await this._terminateAll(); + return; + } - if (!value) { - throw new Error('Reader did not return any value'); - } + if (!value) { + throw new Error('Reader did not return any value'); + } - const { msg, resolve, reject } = value; - const worker = await this._nextWorker(); - this.jobPromise(worker, msg) - .then((result) => resolve(result)) - .catch((reason) => reject(reason)) - .finally(() => { - // Return the worker to the pool - const writer = this.workerQueue.writable.getWriter(); - writer.write(worker); - writer.releaseLock(); - }); - } - } + const { msg, resolve, reject } = value; + const worker = await this._nextWorker(); + this.jobPromise(worker, msg) + .then((result) => resolve(result)) + .catch((reason) => reject(reason)) + .finally(() => { + // Return the worker to the pool + const writer = this.workerQueue.writable.getWriter(); + writer.write(worker); + writer.releaseLock(); + }); + } + } - async _nextWorker() { - const reader = this.workerQueue.readable.getReader(); - const { value } = await reader.read(); - reader.releaseLock(); - if (!value) { - throw new Error('No worker left'); - } + async _nextWorker() { + const reader = this.workerQueue.readable.getReader(); + const { value } = await reader.read(); + reader.releaseLock(); + if (!value) { + throw new Error('No worker left'); + } - return value; - } + return value; + } - async _terminateAll() { - for (let n = 0; n < this.numWorkers; n++) { - const worker = await this._nextWorker(); - worker.terminate(); - } - this.workerQueue.writable.close(); - } + async _terminateAll() { + for (let n = 0; n < this.numWorkers; n++) { + const worker = await this._nextWorker(); + worker.terminate(); + } + this.workerQueue.writable.close(); + } - async join() { - this.jobQueue.writable.getWriter().close(); - await this.done; - } + async join() { + this.jobQueue.writable.getWriter().close(); + await this.done; + } - dispatchJob(msg: I): Promise { - return new Promise((resolve, reject) => { - const writer = this.jobQueue.writable.getWriter(); - writer.write({ msg, resolve, reject }); - writer.releaseLock(); - }); - } + dispatchJob(msg: I): Promise { + return new Promise((resolve, reject) => { + const writer = this.jobQueue.writable.getWriter(); + writer.write({ msg, resolve, reject }); + writer.releaseLock(); + }); + } - private jobPromise(worker: Worker, msg: I) { - return new Promise((resolve, reject) => { - const id = uuid(); - worker.postMessage({ msg, id }); - worker.on('message', function f({ error, result, id: rid }) { - if (rid !== id) { - return; - } - if (error) { - reject(error); - return; - } - worker.off('message', f); - resolve(result); - }); - }); - } + private jobPromise(worker: Worker, msg: I) { + return new Promise((resolve, reject) => { + const id = uuid(); + worker.postMessage({ msg, id }); + worker.on('message', function f({ error, result, id: rid }) { + if (rid !== id) { + return; + } + if (error) { + reject(error); + return; + } + worker.off('message', f); + resolve(result); + }); + }); + } - static useThisThreadAsWorker(cb: (msg: I) => O) { - parentPort!.on('message', async (data) => { - const { msg, id } = data; - try { - const result = await cb(msg); - parentPort!.postMessage({ result, id }); - } catch (e: any) { - parentPort!.postMessage({ error: e.message, id }); - } - }); - } + static useThisThreadAsWorker(cb: (msg: I) => O) { + parentPort!.on('message', async (data) => { + const { msg, id } = data; + try { + const result = await cb(msg); + parentPort!.postMessage({ result, id }); + } catch (e: any) { + parentPort!.postMessage({ error: e.message, id }); + } + }); + } } diff --git a/packages/integrations/image/src/vendor/squoosh/avif/avif_node_dec.ts b/packages/integrations/image/src/vendor/squoosh/avif/avif_node_dec.ts index 44f9ab33d..ee75405bf 100644 --- a/packages/integrations/image/src/vendor/squoosh/avif/avif_node_dec.ts +++ b/packages/integrations/image/src/vendor/squoosh/avif/avif_node_dec.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -const require = createRequire(import.meta.url); import { dirname } from '../emscripten-utils.js'; +const require = createRequire(import.meta.url); var Module = (function () { return function (Module) { diff --git a/packages/integrations/image/src/vendor/squoosh/avif/avif_node_enc.ts b/packages/integrations/image/src/vendor/squoosh/avif/avif_node_enc.ts index d785563d3..476be1219 100644 --- a/packages/integrations/image/src/vendor/squoosh/avif/avif_node_enc.ts +++ b/packages/integrations/image/src/vendor/squoosh/avif/avif_node_enc.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -const require = createRequire(import.meta.url); import { dirname } from '../emscripten-utils.js'; +const require = createRequire(import.meta.url); var Module = (function () { return function (Module) { diff --git a/packages/integrations/image/src/vendor/squoosh/codecs.ts b/packages/integrations/image/src/vendor/squoosh/codecs.ts index f4b231591..48deae27a 100644 --- a/packages/integrations/image/src/vendor/squoosh/codecs.ts +++ b/packages/integrations/image/src/vendor/squoosh/codecs.ts @@ -83,7 +83,7 @@ const resizeInit = () => resize.default(fsp.readFile(pathify(resizeWasm.toString const rotateWasm = new URL('./rotate/rotate.wasm', import.meta.url) // Our decoders currently rely on a `ImageData` global. -import ImageData from './image_data.js'; +import ImageData from './image_data.js' (global as any).ImageData = ImageData function resizeNameToIndex( diff --git a/packages/integrations/image/src/vendor/squoosh/image-pool.ts b/packages/integrations/image/src/vendor/squoosh/image-pool.ts index b390c50b9..bc1b8e2bb 100644 --- a/packages/integrations/image/src/vendor/squoosh/image-pool.ts +++ b/packages/integrations/image/src/vendor/squoosh/image-pool.ts @@ -1,10 +1,10 @@ -import { cpus } from 'os' import { isMainThread } from 'node:worker_threads'; +import { cpus } from 'os'; +import type { OutputFormat } from '../../loaders/index.js'; +import execOnce from '../../utils/execOnce.js'; import WorkerPool from '../../utils/workerPool.js'; import type { Operation } from './image.js'; import * as impl from './impl.js'; -import execOnce from '../../utils/execOnce.js'; -import type { OutputFormat } from '../../loaders/index.js'; const getWorker = execOnce( () => { diff --git a/packages/integrations/image/src/vendor/squoosh/image.ts b/packages/integrations/image/src/vendor/squoosh/image.ts index 2d9394822..fa77efd97 100644 --- a/packages/integrations/image/src/vendor/squoosh/image.ts +++ b/packages/integrations/image/src/vendor/squoosh/image.ts @@ -1,5 +1,5 @@ -import * as impl from './impl.js'; import type { OutputFormat } from '../../loaders/index.js'; +import * as impl from './impl.js'; type RotateOperation = { type: 'rotate' diff --git a/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_dec.ts b/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_dec.ts index b95b293c2..7c9dd4548 100644 --- a/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_dec.ts +++ b/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_dec.ts @@ -1,7 +1,7 @@ /* eslint-disable */ // @ts-nocheck -import { dirname } from '../emscripten-utils.js'; import { createRequire } from 'module'; +import { dirname } from '../emscripten-utils.js'; const require = createRequire(import.meta.url); var Module = (function () { diff --git a/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_enc.ts b/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_enc.ts index a68b08afc..ff1eb6ca0 100644 --- a/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_enc.ts +++ b/packages/integrations/image/src/vendor/squoosh/mozjpeg/mozjpeg_node_enc.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -const require = createRequire(import.meta.url); import { dirname } from '../emscripten-utils.js'; +const require = createRequire(import.meta.url); var Module = (function () { return function (Module) { diff --git a/packages/integrations/image/src/vendor/squoosh/webp/webp_node_dec.ts b/packages/integrations/image/src/vendor/squoosh/webp/webp_node_dec.ts index 6188bc3a9..cc05672bb 100644 --- a/packages/integrations/image/src/vendor/squoosh/webp/webp_node_dec.ts +++ b/packages/integrations/image/src/vendor/squoosh/webp/webp_node_dec.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -const require = createRequire(import.meta.url); import { dirname } from '../emscripten-utils.js'; +const require = createRequire(import.meta.url); var Module = (function () { return function (Module) { diff --git a/packages/integrations/image/src/vendor/squoosh/webp/webp_node_enc.ts b/packages/integrations/image/src/vendor/squoosh/webp/webp_node_enc.ts index 70f98b730..669c2caac 100644 --- a/packages/integrations/image/src/vendor/squoosh/webp/webp_node_enc.ts +++ b/packages/integrations/image/src/vendor/squoosh/webp/webp_node_enc.ts @@ -1,8 +1,8 @@ /* eslint-disable */ // @ts-nocheck import { createRequire } from 'module'; -const require = createRequire(import.meta.url); import { dirname } from '../emscripten-utils.js'; +const require = createRequire(import.meta.url); var Module = (function () { return function (Module) { diff --git a/packages/integrations/image/src/vite-plugin-astro-image.ts b/packages/integrations/image/src/vite-plugin-astro-image.ts index 8aa249e48..51073ee70 100644 --- a/packages/integrations/image/src/vite-plugin-astro-image.ts +++ b/packages/integrations/image/src/vite-plugin-astro-image.ts @@ -89,7 +89,9 @@ export function createPlugin(config: AstroConfig, options: Required