From 8389b2237fce2dc70af3a2f6ed5cc6505a45e951 Mon Sep 17 00:00:00 2001 From: 343dev <343dev@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:40:23 +0700 Subject: [PATCH 1/5] Remove "exec-buffer" from GIF optimization process --- optimize.js | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/optimize.js b/optimize.js index dc201d4..13d0ddb 100644 --- a/optimize.js +++ b/optimize.js @@ -1,3 +1,4 @@ +import { spawn } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; @@ -195,20 +196,39 @@ function processPng({ fileBuffer, config, isLossless }) { } function processGif({ fileBuffer, config, isLossless }) { - return execBuffer({ - bin: gifsicle, - args: [ + return new Promise((resolve, reject) => { + const processArguments = [ ...optionsToArguments({ options: (isLossless ? config?.gif?.lossless : config?.gif?.lossy) || {}, concat: true, }), `--threads=${os.cpus().length}`, '--no-warnings', - '--output', - execBuffer.output, - execBuffer.input, - ], - input: fileBuffer, + '-', + ]; + const process = spawn(gifsicle, processArguments); + + process.stdin.write(fileBuffer); + process.stdin.end(); + + const stdoutChunks = []; + process.stdout.on('data', chunk => { + stdoutChunks.push(chunk); + }); + + process.on('error', error => { + reject(new Error(`Error processing GIF: ${error.message}`)); + }); + + process.on('close', code => { + if (code !== 0) { + reject(new Error(`GIF optimization process exited with code ${code}`)); + return; + } + + const processedFileBuffer = Buffer.concat(stdoutChunks); + resolve(processedFileBuffer); + }); }); } From 755e7b597474cba523d0904b7139db4af9ebeded Mon Sep 17 00:00:00 2001 From: 343dev <343dev@users.noreply.github.com> Date: Thu, 10 Apr 2025 17:05:13 +0700 Subject: [PATCH 2/5] Bump @343dev/guetzli from 1.0.1 to 1.1.0 --- package-lock.json | 8 ++++---- package.json | 2 +- tests/cli.test.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4837dec..c2058ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.0.0", "license": "MIT", "dependencies": { - "@343dev/guetzli": "^1.0.1", + "@343dev/guetzli": "^1.1.0", "cli-progress": "^3.11.0", "commander": "^12.1.0", "exec-buffer": "^3.2.0", @@ -56,9 +56,9 @@ } }, "node_modules/@343dev/guetzli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@343dev/guetzli/-/guetzli-1.0.1.tgz", - "integrity": "sha512-M+hjr0IIuXrmAlUO/Qz92jE9ZkPUSMg28J8hu2JscCEH7SUDUL4G1uiWlNVGZwh7K3G+9Ln0nLctF9WGWy/D8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@343dev/guetzli/-/guetzli-1.1.0.tgz", + "integrity": "sha512-mcarbcAG2RnguYhQsbKmjQpvsRsCmvcjukqfy5MDIGVepj5BNOd6maFjoR45ePcyO7eqVYt2S6h34XGxc09DUA==", "hasInstallScript": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 68e4884..74509c7 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "node": ">=18.18" }, "dependencies": { - "@343dev/guetzli": "^1.0.1", + "@343dev/guetzli": "^1.1.0", "cli-progress": "^3.11.0", "commander": "^12.1.0", "exec-buffer": "^3.2.0", diff --git a/tests/cli.test.js b/tests/cli.test.js index d3818cd..d9f3608 100644 --- a/tests/cli.test.js +++ b/tests/cli.test.js @@ -88,7 +88,7 @@ describe('CLI', () => { const stdout = runCliWithParameters(`--lossless ${workDirectory}${file}`); expectFileRatio({ - stdout, file, maxRatio: 50, minRatio: 45, + stdout, file, maxRatio: 55, minRatio: 45, }); }); From fa2ea9210e3698d3c20892671bd6da212f4f8a62 Mon Sep 17 00:00:00 2001 From: 343dev <343dev@users.noreply.github.com> Date: Thu, 10 Apr 2025 17:14:27 +0700 Subject: [PATCH 3/5] Remove "exec-buffer" from JPG optimization process --- optimize.js | 58 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/optimize.js b/optimize.js index 13d0ddb..22b5a99 100644 --- a/optimize.js +++ b/optimize.js @@ -4,7 +4,6 @@ import os from 'node:os'; import path from 'node:path'; import guetzli from '@343dev/guetzli'; -import execBuffer from 'exec-buffer'; import gifsicle from 'gifsicle'; import pLimit from 'p-limit'; import sharp from 'sharp'; @@ -165,28 +164,49 @@ async function processJpeg({ fileBuffer, config, isLossless }) { const sharpImage = sharp(fileBuffer) .rotate(); // Rotate image using information from EXIF Orientation tag - if (isLossless) { - const inputBuffer = await sharpImage - .toColorspace('srgb') // Replace colorspace (guetzli works only with sRGB) - .jpeg({ quality: 100, optimizeCoding: false }) // Applying maximum quality to minimize losses during image processing with sharp + if (!isLossless) { + return sharpImage + .jpeg(config?.jpeg?.lossy || {}) .toBuffer(); - - return execBuffer({ - bin: guetzli, - args: [ - ...optionsToArguments({ - options: config?.jpeg?.lossless || {}, - }), - execBuffer.input, - execBuffer.output, - ], - input: inputBuffer, - }); } - return sharpImage - .jpeg(config?.jpeg?.lossy || {}) + const inputBuffer = await sharpImage + .toColorspace('srgb') // Replace colorspace (guetzli works only with sRGB) + .jpeg({ quality: 100, optimizeCoding: false }) // Applying maximum quality to minimize losses during image processing with sharp .toBuffer(); + + return new Promise((resolve, reject) => { + const processArguments = [ + ...optionsToArguments({ + options: config?.jpeg?.lossless || {}, + }), + '-', + '-', + ]; + const process = spawn(guetzli, processArguments); + + process.stdin.write(inputBuffer); + process.stdin.end(); + + const stdoutChunks = []; + process.stdout.on('data', chunk => { + stdoutChunks.push(chunk); + }); + + process.on('error', error => { + reject(new Error(`Error processing JPG: ${error.message}`)); + }); + + process.on('close', code => { + if (code !== 0) { + reject(new Error(`JPG optimization process exited with code ${code}`)); + return; + } + + const processedFileBuffer = Buffer.concat(stdoutChunks); + resolve(processedFileBuffer); + }); + }); } function processPng({ fileBuffer, config, isLossless }) { From b5bc327375e175d5cabbfe19254c888915e2237f Mon Sep 17 00:00:00 2001 From: 343dev <343dev@users.noreply.github.com> Date: Thu, 10 Apr 2025 17:16:28 +0700 Subject: [PATCH 4/5] Uninstall exec-buffer --- package-lock.json | 38 ++++++++------------------------------ package.json | 1 - 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2058ee..909a4cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@343dev/guetzli": "^1.1.0", "cli-progress": "^3.11.0", "commander": "^12.1.0", - "exec-buffer": "^3.2.0", "fdir": "^6.4.0", "gifsicle": "^7.0.1", "p-limit": "^6.1.0", @@ -2453,6 +2452,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -2815,6 +2815,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -3360,6 +3361,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, "license": "MIT" }, "node_modules/config-chain": { @@ -4509,22 +4511,6 @@ "dev": true, "license": "MIT" }, - "node_modules/exec-buffer": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", - "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", - "license": "MIT", - "dependencies": { - "execa": "^0.7.0", - "p-finally": "^1.0.0", - "pify": "^3.0.0", - "rimraf": "^2.5.4", - "tempfile": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -4907,6 +4893,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -5180,6 +5167,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -5522,6 +5510,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -7453,6 +7442,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -7965,6 +7955,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8627,19 +8618,6 @@ "dev": true, "license": "MIT" }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", diff --git a/package.json b/package.json index 74509c7..9a43925 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "@343dev/guetzli": "^1.1.0", "cli-progress": "^3.11.0", "commander": "^12.1.0", - "exec-buffer": "^3.2.0", "fdir": "^6.4.0", "gifsicle": "^7.0.1", "p-limit": "^6.1.0", From b18d77f829d92347894f2946cbf9f81cebde1286 Mon Sep 17 00:00:00 2001 From: 343dev <343dev@users.noreply.github.com> Date: Thu, 10 Apr 2025 17:33:49 +0700 Subject: [PATCH 5/5] Optimize processGif & processJpeg functions --- optimize.js | 98 ++++++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 54 deletions(-) diff --git a/optimize.js b/optimize.js index 22b5a99..8d2042f 100644 --- a/optimize.js +++ b/optimize.js @@ -175,37 +175,18 @@ async function processJpeg({ fileBuffer, config, isLossless }) { .jpeg({ quality: 100, optimizeCoding: false }) // Applying maximum quality to minimize losses during image processing with sharp .toBuffer(); - return new Promise((resolve, reject) => { - const processArguments = [ - ...optionsToArguments({ - options: config?.jpeg?.lossless || {}, - }), - '-', - '-', - ]; - const process = spawn(guetzli, processArguments); - - process.stdin.write(inputBuffer); - process.stdin.end(); - - const stdoutChunks = []; - process.stdout.on('data', chunk => { - stdoutChunks.push(chunk); - }); - - process.on('error', error => { - reject(new Error(`Error processing JPG: ${error.message}`)); - }); - - process.on('close', code => { - if (code !== 0) { - reject(new Error(`JPG optimization process exited with code ${code}`)); - return; - } - - const processedFileBuffer = Buffer.concat(stdoutChunks); - resolve(processedFileBuffer); - }); + const commandOptions = [ + ...optionsToArguments({ + options: config?.jpeg?.lossless || {}, + }), + '-', + '-', + ]; + + return pipe({ + command: guetzli, + commandOptions, + inputBuffer, }); } @@ -216,19 +197,37 @@ function processPng({ fileBuffer, config, isLossless }) { } function processGif({ fileBuffer, config, isLossless }) { + const commandOptions = [ + ...optionsToArguments({ + options: (isLossless ? config?.gif?.lossless : config?.gif?.lossy) || {}, + concat: true, + }), + `--threads=${os.cpus().length}`, + '--no-warnings', + '-', + ]; + + return pipe({ + command: gifsicle, + commandOptions, + inputBuffer: fileBuffer, + }); +} + +function processSvg({ fileBuffer, config }) { + return Buffer.from( + svgoOptimize( + fileBuffer, + config.svg, + ).data, + ); +} + +function pipe({ command, commandOptions, inputBuffer }) { return new Promise((resolve, reject) => { - const processArguments = [ - ...optionsToArguments({ - options: (isLossless ? config?.gif?.lossless : config?.gif?.lossy) || {}, - concat: true, - }), - `--threads=${os.cpus().length}`, - '--no-warnings', - '-', - ]; - const process = spawn(gifsicle, processArguments); - - process.stdin.write(fileBuffer); + const process = spawn(command, commandOptions); + + process.stdin.write(inputBuffer); process.stdin.end(); const stdoutChunks = []; @@ -237,12 +236,12 @@ function processGif({ fileBuffer, config, isLossless }) { }); process.on('error', error => { - reject(new Error(`Error processing GIF: ${error.message}`)); + reject(new Error(`Error processing image: ${error.message}`)); }); process.on('close', code => { if (code !== 0) { - reject(new Error(`GIF optimization process exited with code ${code}`)); + reject(new Error(`Image optimization process exited with code ${code}`)); return; } @@ -251,12 +250,3 @@ function processGif({ fileBuffer, config, isLossless }) { }); }); } - -function processSvg({ fileBuffer, config }) { - return Buffer.from( - svgoOptimize( - fileBuffer, - config.svg, - ).data, - ); -}