From 6bfce29af61fad344a56d74b862a24fc8cec18ce Mon Sep 17 00:00:00 2001 From: ergunsh Date: Fri, 10 Sep 2021 14:08:21 +0200 Subject: [PATCH 1/3] Improve typing of `Image.encode` Instead of returning `any` we're now returning the whole object. Still from typing perspective, the API is not that great since we don't have any type relation between `encode` calls and `encodedWith` property. Maybe we can think about returning directly from `encode` call with the returned object having properties that is supplied in `encode` calls --- libsquoosh/src/index.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libsquoosh/src/index.ts b/libsquoosh/src/index.ts index bfd3bac1..cc4fe1af 100644 --- a/libsquoosh/src/index.ts +++ b/libsquoosh/src/index.ts @@ -26,6 +26,12 @@ type PreprocessOptions = { quant?: QuantOptions; rotate?: RotateOptions; }; +type EncodeResult = { + optionsUsed: object; + binary: Uint8Array; + extension: string; + size: number; +}; async function decodeFile({ file, @@ -83,7 +89,7 @@ async function encodeImage({ encConfig: any; optimizerButteraugliTarget: number; maxOptimizerRounds: number; -}) { +}): Promise { let binary: Uint8Array; let optionsUsed = encConfig; const encoder = await encoders[encName].enc(); @@ -172,7 +178,7 @@ class Image { public file: ArrayBuffer | ArrayLike; public workerPool: WorkerPool; public decoded: Promise<{ bitmap: ImageData }>; - public encodedWith: { [key: string]: any }; + public encodedWith: { [key in EncoderKey]?: Promise }; constructor( workerPool: WorkerPool, From 914cdea41db9a6e844d7f38c06445b43bcf22df0 Mon Sep 17 00:00:00 2001 From: ergunsh Date: Fri, 10 Sep 2021 14:45:20 +0200 Subject: [PATCH 2/3] Return encode result from `Image.encode` calls Before this, there were one way to use the API: call `await image.encode({ mozjpeg: {} })` then use `encodedWith` by asserting that `mozjpeg` property exists on it After adding return value to encode, people will be able to use it like `const { mozjpeg } = await image.encode({ mozjpeg: {} });` which provides better type safety --- libsquoosh/src/index.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libsquoosh/src/index.ts b/libsquoosh/src/index.ts index cc4fe1af..04792394 100644 --- a/libsquoosh/src/index.ts +++ b/libsquoosh/src/index.ts @@ -32,6 +32,14 @@ type EncodeResult = { extension: string; size: number; }; +type EncoderOptions = { + mozjpeg?: Partial; + webp?: Partial; + avif?: Partial; + jxl?: Partial; + wp2?: Partial; + oxipng?: Partial; +}; async function decodeFile({ file, @@ -221,19 +229,12 @@ class Image { * @param {object} encodeOptions - An object with encoders to use, and their settings. * @returns {Promise} - A promise that resolves when the image has been encoded with all the specified encoders. */ - async encode( + async encode( encodeOptions: { optimizerButteraugliTarget?: number; maxOptimizerRounds?: number; - } & { - mozjpeg?: Partial; - webp?: Partial; - avif?: Partial; - jxl?: Partial; - wp2?: Partial; - oxipng?: Partial; - } = {}, - ): Promise { + } & T, + ): Promise<{ [key in keyof T]: EncodeResult }> { const { bitmap } = await this.decoded; for (const [name, options] of Object.entries(encodeOptions)) { if (!Object.keys(encoders).includes(name)) { @@ -257,6 +258,7 @@ class Image { }); } await Promise.all(Object.values(this.encodedWith)); + return this.encodedWith as { [key in keyof T]: EncodeResult }; } } From 95a1b35c917f964d0419e8105954c09ab22e4aaf Mon Sep 17 00:00:00 2001 From: ergunsh Date: Fri, 10 Sep 2021 15:18:14 +0200 Subject: [PATCH 3/3] Update `encodedWith` to contain resolved values We already await the promises that we set on the `encodedWith` instead of setting already resolved promises to `encodedWith` we can set the resolved values So, the users can use like `const { mozjpeg: { binary } } = await image.encode({ mozjpeg })` or they can first run `await image.encode({ mozjpeg })` and then `image.encodedWith.mozjpeg.binary` --- libsquoosh/src/index.ts | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/libsquoosh/src/index.ts b/libsquoosh/src/index.ts index 04792394..8ed7e01b 100644 --- a/libsquoosh/src/index.ts +++ b/libsquoosh/src/index.ts @@ -186,7 +186,7 @@ class Image { public file: ArrayBuffer | ArrayLike; public workerPool: WorkerPool; public decoded: Promise<{ bitmap: ImageData }>; - public encodedWith: { [key in EncoderKey]?: Promise }; + public encodedWith: { [key in EncoderKey]?: EncodeResult }; constructor( workerPool: WorkerPool, @@ -227,7 +227,7 @@ class Image { /** * Define one or several encoders to use on the image. * @param {object} encodeOptions - An object with encoders to use, and their settings. - * @returns {Promise} - A promise that resolves when the image has been encoded with all the specified encoders. + * @returns {Promise<{ [key in keyof T]: EncodeResult }>} - A promise that resolves when the image has been encoded with all the specified encoders. */ async encode( encodeOptions: { @@ -236,6 +236,7 @@ class Image { } & T, ): Promise<{ [key in keyof T]: EncodeResult }> { const { bitmap } = await this.decoded; + const setEncodedWithPromises = []; for (const [name, options] of Object.entries(encodeOptions)) { if (!Object.keys(encoders).includes(name)) { continue; @@ -246,18 +247,25 @@ class Image { typeof options === 'string' ? options : Object.assign({}, encRef.defaultEncoderOptions, options); - this.encodedWith[encName] = this.workerPool.dispatchJob({ - operation: 'encode', - bitmap, - encName, - encConfig, - optimizerButteraugliTarget: Number( - encodeOptions.optimizerButteraugliTarget ?? 1.4, - ), - maxOptimizerRounds: Number(encodeOptions.maxOptimizerRounds ?? 6), - }); + setEncodedWithPromises.push( + this.workerPool + .dispatchJob({ + operation: 'encode', + bitmap, + encName, + encConfig, + optimizerButteraugliTarget: Number( + encodeOptions.optimizerButteraugliTarget ?? 1.4, + ), + maxOptimizerRounds: Number(encodeOptions.maxOptimizerRounds ?? 6), + }) + .then((encodeResult) => { + this.encodedWith[encName] = encodeResult; + }), + ); } - await Promise.all(Object.values(this.encodedWith)); + + await Promise.all(setEncodedWithPromises); return this.encodedWith as { [key in keyof T]: EncodeResult }; } }