From f78de08f2f319b00d09d60afa0c443f070f128e3 Mon Sep 17 00:00:00 2001 From: "Kai J." Date: Wed, 28 Aug 2024 10:00:01 +0200 Subject: [PATCH 1/4] Major update --- .github/workflows/test.yml | 6 ++-- eslint.config.js | 21 +++++++++++++ index.js | 64 ++++++++++++++++++++------------------ package.json | 18 ++++++----- test.js | 51 ++++++++++++++++-------------- 5 files changed, 96 insertions(+), 64 deletions(-) create mode 100644 eslint.config.js diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 76c979b..3349b51 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v2 @@ -32,7 +32,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v2 @@ -50,7 +50,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v2 diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..71fc9a2 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,21 @@ +import globals from "globals"; +import js from "@eslint/js"; + +export default [ + js.configs.recommended, + { + languageOptions: { + globals: { + ...globals.browser + } + }, + rules: { + "no-const-assign": "error", + "no-unused-vars": "warn", + "no-console": "error", + "prefer-const": "error", + "no-eq-null": "error", + "no-var": "error" + } + }, +]; diff --git a/index.js b/index.js index c2fd802..7a353fa 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,15 @@ -'use strict'; -const execBuffer = require('exec-buffer'); -const isPng = require('is-png'); -const optipng = require('optipng-bin'); +import { execa } from "execa"; +import { Buffer } from "buffer"; +import { fileTypeFromBuffer } from "file-type"; +import tmp from 'tmp'; +import fs from 'fs' +import optipng from "optipng-bin"; -module.exports = options => async buffer => { +export async function isPng (buffer) { + return (await fileTypeFromBuffer(buffer)).mime == 'image/png' +} + +export default (options) => async (buffer) => { options = { optimizationLevel: 3, bitDepthReduction: true, @@ -11,52 +17,50 @@ module.exports = options => async buffer => { paletteReduction: true, interlaced: false, errorRecovery: true, - ...options + ...options, }; if (!Buffer.isBuffer(buffer)) { - throw new TypeError('Expected a buffer'); + throw new TypeError("Expected a buffer"); } - if (!isPng(buffer)) { + if (!await isPng(buffer)) { return buffer; } - const arguments_ = [ - '-strip', - 'all', - '-clobber', - '-o', - options.optimizationLevel, - '-out', - execBuffer.output - ]; + const inputFile = tmp.fileSync(); + const outputFile = tmp.fileSync(); + + fs.writeSync(inputFile.fd, buffer); + + const args = [ + "-strip", "all", + "-clobber", + "-o", options.optimizationLevel, + inputFile.name, + "-out", outputFile.name + ] if (options.errorRecovery) { - arguments_.push('-fix'); + args.push("-fix"); } if (!options.bitDepthReduction) { - arguments_.push('-nb'); + args.push("-nb"); } - if (typeof options.interlaced === 'boolean') { - arguments_.push('-i', options.interlaced ? '1' : '0'); + if (typeof options.interlaced === "boolean") { + args.push("-i", options.interlaced ? "1" : "0"); } if (!options.colorTypeReduction) { - arguments_.push('-nc'); + args.push("-nc"); } if (!options.paletteReduction) { - arguments_.push('-np'); + args.push("-np"); } - arguments_.push(execBuffer.input); - - return execBuffer({ - input: buffer, - bin: optipng, - args: arguments_ - }); + await execa(optipng, args); + return fs.readFileSync(outputFile.name); }; diff --git a/package.json b/package.json index 2678f22..47a2e75 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,13 @@ "version": "8.0.0", "description": "Imagemin plugin for OptiPNG", "license": "MIT", + "type": "module", "repository": "imagemin/imagemin-optipng", "engines": { - "node": ">=10" + "node": ">=18" }, "scripts": { - "test": "xo && ava" + "test": "eslint && ava" }, "files": [ "index.js" @@ -23,12 +24,15 @@ "png" ], "dependencies": { - "exec-buffer": "^3.0.0", - "is-png": "^2.0.0", - "optipng-bin": "^7.0.0" + "execa": "^9.3.1", + "file-type": "^19.4.1", + "optipng-bin": "^9.0.0", + "tmp": "^0.2.3" }, "devDependencies": { - "ava": "^3.8.0", - "xo": "^0.30.0" + "@eslint/js": "^9.9.0", + "ava": "^6.1.3", + "eslint": "^9.9.0", + "globals": "^15.9.0" } } diff --git a/test.js b/test.js index 4a53ce7..fa7d344 100644 --- a/test.js +++ b/test.js @@ -1,65 +1,68 @@ -const fs = require('fs'); -const path = require('path'); -const isPng = require('is-png'); -const test = require('ava'); -const optipng = require('.'); +import fs from 'fs'; +import path from 'path'; +import test from 'ava'; +import optipng, { isPng } from './index.js'; -const fixture = fs.readFileSync(path.join(__dirname, 'fixture.png')); -const fixtureBroken = fs.readFileSync(path.join(__dirname, 'fixture_broken.png')); + +const fixture = fs.readFileSync(path.resolve('fixture.png')); +const fixtureBroken = fs.readFileSync(path.resolve('fixture_broken.png')); test('optimize a PNG', async t => { const data = await optipng()(fixture); t.true(data.length < fixture.length); - t.true(isPng(data)); + t.true(await isPng(data)); }); test('throw on empty input', async t => { - await t.throwsAsync(optipng()(), {message: /Expected a buffer/}); + await t.throwsAsync(optipng()(), { message: /Expected a buffer/ }); }); test('bitDepthReduction option', async t => { - await t.notThrowsAsync(optipng({bitDepthReduction: true})(fixture)); + await t.notThrowsAsync(optipng({ bitDepthReduction: true })(fixture)); }); test('colorTypeReduction option', async t => { - await t.notThrowsAsync(optipng({colorTypeReduction: true})(fixture)); + await t.notThrowsAsync(optipng({ colorTypeReduction: true })(fixture)); }); test('paletteReduction option', async t => { - await t.notThrowsAsync(optipng({paletteReduction: true})(fixture)); + await t.notThrowsAsync(optipng({ paletteReduction: true })(fixture)); }); test('errorRecovery default', async t => { const data = await optipng()(fixtureBroken); - t.true(isPng(data)); + t.true(await isPng(data), "return data is not a PNG."); }); test('errorRecovery explicit', async t => { - const data = await optipng({errorRecovery: true})(fixtureBroken); - t.true(isPng(data)); + const data = await optipng({ errorRecovery: true })(fixtureBroken); + t.true(await isPng(data), "return data is not a PNG."); }); test('errorRecovery is set to false', async t => { - await t.throwsAsync(optipng({errorRecovery: false})(fixtureBroken)); + await t.throwsAsync(optipng({ errorRecovery: false })(fixtureBroken)); }); test('interlaced is set to true', async t => { const [data1, data2] = await Promise.all([ - optipng({interlaced: true})(fixture), + optipng({ interlaced: true })(fixture), optipng()(fixture) ]); - t.true(isPng(data1)); - t.true(data1.length > data2.length); + t.true(await isPng(data1), "return data is not a PNG."); + t.true(data1.length > data2.length, "interlaced image should be bigger"); }); test('interlaced is set to undefined and null', async t => { const [data1, data2, data3] = await Promise.all([ - optipng({interlaced: undefined})(fixture), - optipng({interlaced: null})(fixture), - optipng({interlaced: true})(fixture) + optipng({ interlaced: undefined })(fixture), + optipng({ interlaced: null })(fixture), + optipng({ interlaced: true })(fixture) ]); - t.true(isPng(data1) && isPng(data2)); - t.true(data1.length === data2.length && data1.length < data3.length); + t.true(await isPng(data1), "return data1 is not a PNG."); + t.true(await isPng(data2), "return data2 is not a PNG."); + t.true(await isPng(data3), "return data3 is not a PNG."); + t.true(data1.length < data3.length, "interlaced image should be bigger"); + t.true(data1.length === data2.length, "interlacing options 'undefined' and 'null' should be equal"); }); From 61552a01319e048a0ef62a11b396190827bee248 Mon Sep 17 00:00:00 2001 From: "Kai J." Date: Wed, 28 Aug 2024 10:06:58 +0200 Subject: [PATCH 2/4] revert to is-png --- index.js | 8 ++------ package.json | 2 +- test.js | 18 +++++++++--------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 7a353fa..59d0bb2 100644 --- a/index.js +++ b/index.js @@ -1,14 +1,10 @@ import { execa } from "execa"; import { Buffer } from "buffer"; -import { fileTypeFromBuffer } from "file-type"; +import isPng from "is-png"; import tmp from 'tmp'; import fs from 'fs' import optipng from "optipng-bin"; -export async function isPng (buffer) { - return (await fileTypeFromBuffer(buffer)).mime == 'image/png' -} - export default (options) => async (buffer) => { options = { optimizationLevel: 3, @@ -24,7 +20,7 @@ export default (options) => async (buffer) => { throw new TypeError("Expected a buffer"); } - if (!await isPng(buffer)) { + if (!isPng(buffer)) { return buffer; } diff --git a/package.json b/package.json index 47a2e75..953788b 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ ], "dependencies": { "execa": "^9.3.1", - "file-type": "^19.4.1", + "is-png": "3.0.1", "optipng-bin": "^9.0.0", "tmp": "^0.2.3" }, diff --git a/test.js b/test.js index fa7d344..94fc4ae 100644 --- a/test.js +++ b/test.js @@ -1,8 +1,8 @@ import fs from 'fs'; import path from 'path'; import test from 'ava'; -import optipng, { isPng } from './index.js'; - +import optipng from './index.js'; +import isPng from 'is-png'; const fixture = fs.readFileSync(path.resolve('fixture.png')); const fixtureBroken = fs.readFileSync(path.resolve('fixture_broken.png')); @@ -10,7 +10,7 @@ const fixtureBroken = fs.readFileSync(path.resolve('fixture_broken.png')); test('optimize a PNG', async t => { const data = await optipng()(fixture); t.true(data.length < fixture.length); - t.true(await isPng(data)); + t.true(isPng(data)); }); test('throw on empty input', async t => { @@ -31,12 +31,12 @@ test('paletteReduction option', async t => { test('errorRecovery default', async t => { const data = await optipng()(fixtureBroken); - t.true(await isPng(data), "return data is not a PNG."); + t.true(isPng(data), "return data is not a PNG."); }); test('errorRecovery explicit', async t => { const data = await optipng({ errorRecovery: true })(fixtureBroken); - t.true(await isPng(data), "return data is not a PNG."); + t.true(isPng(data), "return data is not a PNG."); }); test('errorRecovery is set to false', async t => { @@ -49,7 +49,7 @@ test('interlaced is set to true', async t => { optipng()(fixture) ]); - t.true(await isPng(data1), "return data is not a PNG."); + t.true(isPng(data1), "return data is not a PNG."); t.true(data1.length > data2.length, "interlaced image should be bigger"); }); @@ -60,9 +60,9 @@ test('interlaced is set to undefined and null', async t => { optipng({ interlaced: true })(fixture) ]); - t.true(await isPng(data1), "return data1 is not a PNG."); - t.true(await isPng(data2), "return data2 is not a PNG."); - t.true(await isPng(data3), "return data3 is not a PNG."); + t.true(isPng(data1), "return data1 is not a PNG."); + t.true(isPng(data2), "return data2 is not a PNG."); + t.true(isPng(data3), "return data3 is not a PNG."); t.true(data1.length < data3.length, "interlaced image should be bigger"); t.true(data1.length === data2.length, "interlacing options 'undefined' and 'null' should be equal"); }); From d98af6454f1427f0219851a7535071d60a240070 Mon Sep 17 00:00:00 2001 From: "Kai J." Date: Wed, 28 Aug 2024 10:12:05 +0200 Subject: [PATCH 3/4] add strip Metadata option --- index.js | 6 +++++- readme.md | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 59d0bb2..3643d2e 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,7 @@ import optipng from "optipng-bin"; export default (options) => async (buffer) => { options = { optimizationLevel: 3, + stripMetadata: true, bitDepthReduction: true, colorTypeReduction: true, paletteReduction: true, @@ -30,13 +31,16 @@ export default (options) => async (buffer) => { fs.writeSync(inputFile.fd, buffer); const args = [ - "-strip", "all", "-clobber", "-o", options.optimizationLevel, inputFile.name, "-out", outputFile.name ] + if (options.stripMetadata) { + args.push("-strip", "all") + } + if (options.errorRecovery) { args.push("-fix"); } diff --git a/readme.md b/readme.md index 1c62710..b798b11 100644 --- a/readme.md +++ b/readme.md @@ -62,6 +62,13 @@ Default: `true` Apply bit depth reduction. +##### stripMetadata + +Type: `boolean`\ +Default: `true` + +Strip Metadata. + ##### colorTypeReduction Type: `boolean`\ From 76b131bc7f2a53c8204e847fb6b6d18955ecc89e Mon Sep 17 00:00:00 2001 From: "Kai J." Date: Wed, 28 Aug 2024 10:18:27 +0200 Subject: [PATCH 4/4] update actions --- .github/workflows/test.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3349b51..087252a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,9 +17,9 @@ jobs: node-version: [18.x, 20.x, 22.x] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install @@ -35,9 +35,9 @@ jobs: node-version: [18.x, 20.x, 22.x] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install @@ -53,9 +53,9 @@ jobs: node-version: [18.x, 20.x, 22.x] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install