Skip to content

Commit

Permalink
Add new Bun CLI script scripts/bun/optimizeImages.mts to auto-optim…
Browse files Browse the repository at this point in the history
…ize images (#100)
  • Loading branch information
mangs authored Sep 6, 2024
1 parent 6e9dd23 commit 7977229
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publishWorkflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.1.17"
bun-version: "1.1.26"
- uses: mangs/simple-release-notes-action@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pullRequestWorkflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.1.17"
bun-version: "1.1.26"
- run: bun install --frozen-lockfile
- run: bun run check:environment
- run: bun run check:package-version
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 2.31.0

- Add new Bun CLI script `scripts/bun/optimizeImages.mts` to auto-optimize images
- Accessible via convenient `optimize-images` CLI command
- Upgrade target Bun version from `1.1.17` to `1.1.26`
- Update dependency versions to latest

## 2.30.1

- Enable sorting when displaying any request body data in `startDevelopmentServer()`
Expand Down
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions config/eslint/eslintConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"scripts/bun/checkEnvironmentSymlinks.mts",
"scripts/bun/checkEnvironmentVersions.mts",
"scripts/bun/checkPackageVersion.mts",
"scripts/bun/optimizeImages.mts",
"scripts/bun/startDevelopmentServer.mts",
"scripts/git/installGitHooks.mts",
"scripts/git/removeGitHooks.mts"
Expand Down
24 changes: 14 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@mangs/bun-utils",
"version": "2.30.1",
"version": "2.31.0",
"author": "Eric L. Goldstein",
"description": "Useful utils for your Bun projects",
"engines": {
"bun": "1.1.17"
"bun": "1.1.26"
},
"packageManager": "[email protected].17",
"packageManager": "[email protected].26",
"homepage": "https://github.com/mangs/bun-utils#readme",
"repository": {
"type": "git",
Expand All @@ -26,6 +26,7 @@
"check-environment-versions": "scripts/bun/checkEnvironmentVersions.mts",
"devserver": "scripts/bun/startDevelopmentServer.mts",
"install-git-hooks": "scripts/git/installGitHooks.mts",
"optimize-images": "scripts/bun/optimizeImages.mts",
"remove-git-hooks": "scripts/git/removeGitHooks.mts"
},
"exports": {
Expand Down Expand Up @@ -60,19 +61,22 @@
"reinstall:use-lock-file": "bun run --silent delete:node-modules && bun install --frozen-lockfile"
},
"dependencies": {
"type-fest": "4.23.0"
"type-fest": "4.26.0"
},
"devDependencies": {
"@babbel/eslint-config": "2.0.1",
"@types/bun": "1.1.6",
"@babbel/eslint-config": "2.0.3",
"@types/bun": "1.1.8",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-jsdoc": "48.8.3",
"marked": "13.0.2",
"eslint-plugin-jsdoc": "50.2.2",
"marked": "14.1.1",
"prettier": "3.3.3",
"typedoc": "0.26.5",
"typedoc": "0.26.6",
"typedoc-github-wiki-theme": "2.0.0",
"typedoc-plugin-markdown": "4.2.3",
"typedoc-plugin-markdown": "4.2.7",
"typescript": "5.5.4"
},
"optionalDependencies": {
"sharp": "0.33.5"
}
}
87 changes: 87 additions & 0 deletions scripts/bun/optimizeImages.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env bun

// External Imports
import { argv, file, Glob, nanoseconds, write } from 'bun';
import { parseArgs } from 'node:util';
import nodePath from 'node:path';
import sharp from 'sharp'; // eslint-disable-line import/no-extraneous-dependencies -- used only for development

// Internal Imports
import { getHumanReadableFilesize } from '../../src/filesystemUtils.mts';
import { getPerformanceLabel } from '../../src/consoleUtils.mts';

// Type Imports
import type { AvifOptions, JpegOptions, PngOptions, WebpOptions } from 'sharp';

// Local Variables
const imageFormats = new Set(['avif', 'jpeg', 'jpg', 'png', 'webp']);
const compressionOptions = {
// The "quality" options are the default values
avif: { quality: 50 } satisfies AvifOptions,
jpeg: { quality: 80 } satisfies JpegOptions,
jpg: { quality: 80 } satisfies JpegOptions,
png: { quality: 100 } satisfies PngOptions,
webp: { quality: 80 } satisfies WebpOptions,
};

// Local Functions
/**
* Script entrypoint.
*/
async function main() {
const { values } = parseArgs({
allowPositionals: true,
args: argv,
options: {
'convert-to': { type: 'string' },
'root-path': { default: '.', type: 'string' },
},
});

const startTimeTotal = nanoseconds();
const conversionFormat = values['convert-to'];
if (conversionFormat && !imageFormats.has(conversionFormat)) {
throw new Error(`Invalid image conversion format: ${conversionFormat}`);
}

const rootPath = values['root-path']!;
const glob = new Glob('**/*.{avif,jpeg,jpg,png,webp}');
let fileCount = 0;
for await (const filePath of glob.scan(rootPath)) {
fileCount += 1;
const startTime = nanoseconds();
const fullFilePath = nodePath.join(rootPath, filePath);
const sizeBefore = file(fullFilePath).size;
const imageMetadata = await sharp(fullFilePath).metadata();
const { format } = imageMetadata;
if (!format) {
throw new TypeError(`Invalid image format encountered: ${format}`, { cause: { filePath } });
}
const formatKeyed =
(conversionFormat as keyof typeof compressionOptions) ??
(format as keyof typeof compressionOptions);
const formatOptions = compressionOptions[formatKeyed];
const fileArrayBuffer = await sharp(fullFilePath)
[formatKeyed === 'jpg' ? 'jpeg' : formatKeyed](formatOptions)
.toBuffer();
const sizeAfter = fileArrayBuffer.length;
const percentage = new Intl.NumberFormat(undefined, {
maximumFractionDigits: 2,
minimumFractionDigits: 0,
style: 'percent',
}).format(sizeAfter / sizeBefore);
const newFilePath = fullFilePath.replace(/\.\w+$/, `.${formatKeyed}`);
await write(newFilePath, fileArrayBuffer);

console.log(
`${newFilePath}: ${getHumanReadableFilesize(sizeBefore)} to ${getHumanReadableFilesize(sizeAfter)} (${percentage}) ${getPerformanceLabel(startTime)}`,
);
}

console.log(
`\n${fileCount} file${fileCount === 1 ? '' : 's'} processed ${getPerformanceLabel(startTimeTotal)}`,
);
}

// Begin Execution
await main();
1 change: 0 additions & 1 deletion src/consoleUtils.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/

// External Imports
// eslint-disable-next-line n/no-unsupported-features/node-builtins -- ignore until lint library is fixed
import { styleText } from 'node:util';

// Internal Imports
Expand Down

0 comments on commit 7977229

Please sign in to comment.