micropng-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# MicroPng CLI
|
|
2
|
+
|
|
3
|
+
A high-performance, local-first CLI image compressor built with Node.js and libvips (via `sharp`). Supports recursive processing, atomic overwrites, and format conversion.
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/ISC)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🚀 **Fast**: Parallel processing with smart concurrency control.
|
|
10
|
+
- 📂 **Recursive**: Deeply scans folders and subfolders.
|
|
11
|
+
- 🔄 **Safe Overwrite**: Atomic replacements to prevent data loss.
|
|
12
|
+
- 🖼️ **Multi-Format**: Supports JPEG, PNG, WebP, AVIF.
|
|
13
|
+
- 🔒 **Local-First**: No data leaves your machine.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### For Users
|
|
18
|
+
|
|
19
|
+
You can run **MicroPng** directly without installation using `npx`:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx micropng-cli --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or install it globally to use the `micropng` command anywhere:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g micropng-cli
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### For Developers
|
|
32
|
+
|
|
33
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for local development setup.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Basic Usage
|
|
38
|
+
|
|
39
|
+
Compress a single file:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
micropng-cli input.png --output compressed.png
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Compress a directory of images:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
micropng-cli ./images --output ./compressed-images
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Recursive Processing
|
|
52
|
+
|
|
53
|
+
Process all images in a folder and its subfolders, maintaining the directory structure:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
micropng-cli ./photos --recursive --output ./optimized-photos
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### In-Place Replacement (Overwrite)
|
|
60
|
+
|
|
61
|
+
⚠️ **Warning**: This will replace your original files!
|
|
62
|
+
|
|
63
|
+
Compress and overwrite images in-place safely:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
micropng-cli ./project-assets --recursive --replace
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Format Conversion
|
|
70
|
+
|
|
71
|
+
Convert all PNGs to WebP:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
micropng-cli ./images --format webp --output ./webp-images --recursive
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Options
|
|
78
|
+
|
|
79
|
+
| Option | Alias | Description | Default |
|
|
80
|
+
| :--- | :--- | :--- | :--- |
|
|
81
|
+
| `--output <dir>` | `-o` | Output directory | (Current dir) |
|
|
82
|
+
| `--recursive` | `-r` | Process subfolders deeply | `false` |
|
|
83
|
+
| `--replace` | | Overwrite original files | `false` |
|
|
84
|
+
| `--quality <number>` | `-q` | Compression quality (1-100) | `80` |
|
|
85
|
+
| `--width <number>` | `-w` | Resize width in pixels | (Original) |
|
|
86
|
+
| `--format <type>` | `-f` | Output format (jpeg, png, webp, avif) | (Original) |
|
|
87
|
+
| `--concurrency <number>` | `-c` | Number of concurrent tasks | `5` |
|
|
88
|
+
|
|
89
|
+
## Documentation
|
|
90
|
+
|
|
91
|
+
- [Contributing Guide](./CONTRIBUTING.md): Setup local dev environment and run tests.
|
|
92
|
+
- [Distribution Guide](./DISTRIBUTION.md): How to publish and release this package.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
ISC
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";var k=Object.create;var h=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var z=Object.getPrototypeOf,R=Object.prototype.hasOwnProperty;var N=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of L(e))!R.call(r,s)&&s!==t&&h(r,s,{get:()=>e[s],enumerable:!(i=F(e,s))||i.enumerable});return r};var c=(r,e,t)=>(t=r!=null?k(z(r)):{},N(e||!r||!r.__esModule?h(t,"default",{value:r,enumerable:!0}):t,r));var I=require("commander"),C=c(require("fast-glob"),1),q=c(require("p-limit"),1),a=c(require("path"),1),l=c(require("chalk"),1),E=c(require("fs-extra"),1);var x=c(require("sharp"),1),d=c(require("fs-extra"),1),$=c(require("path"),1);async function j(r,e,t){let{quality:i=80,width:s,format:f}=t,n=(0,x.default)(r);s&&(n=n.resize(s));let y=$.default.extname(r).toLowerCase(),m=f||y.slice(1);m==="jpeg"||m==="jpg"?n=n.jpeg({quality:i}):m==="png"?n=n.png({quality:i,compressionLevel:9}):m==="webp"?n=n.webp({quality:i}):m==="avif"&&(n=n.avif({quality:i}));let p=t.replace?`${r}.tmp_${Date.now()}`:e;try{await d.default.ensureDir($.default.dirname(p)),await n.toFile(p),t.replace&&await d.default.move(p,r,{overwrite:!0})}catch(o){throw t.replace&&await d.default.pathExists(p)&&await d.default.remove(p),o}}var O=new I.Command;O.name("micropng-cli").description("High-performance CLI image compressor").version("0.1.0").argument("<input>","Input file or directory").option("-o, --output <dir>","Output directory").option("-r, --recursive","Process subfolders deeply").option("--replace","OVERWRITE original files (Caution!)").option("-q, --quality <number>","Compression quality (1-100)","80").option("-w, --width <number>","Resize width in pixels").option("-f, --format <type>","Output format (jpeg, png, webp, avif)").option("-c, --concurrency <number>","Number of concurrent tasks","5").action(async(r,e)=>{try{let t=a.default.resolve(r),i=(await E.default.stat(t)).isDirectory(),s;if(i){let o=t.replace(/\\/g,"/");s=e.recursive?`${o}/**/*.{jpg,jpeg,png,webp}`:`${o}/*.{jpg,jpeg,png,webp}`}else s=t.replace(/\\/g,"/");let f=await(0,C.default)(s,{absolute:!0});if(f.length===0){console.log(l.default.yellow("No images found to process."));return}console.log(l.default.blue(`Found ${f.length} images. Processing...`));let n=(0,q.default)(parseInt(e.concurrency)),y=parseInt(e.quality),m=e.width?parseInt(e.width):void 0,p=f.map(o=>n(async()=>{try{let g;if(e.replace)g=o;else if(e.output){let u=i?a.default.relative(t,o):a.default.basename(o),b=a.default.dirname(u),w=a.default.extname(u),v=a.default.basename(u,w),D=e.format?`.${e.format}`:w;g=a.default.join(a.default.resolve(e.output),b,`${v}${D}`)}else{let u=a.default.extname(o),b=a.default.basename(o,u),w=a.default.dirname(o),v=e.format?`.${e.format}`:u;g=a.default.join(w,`${b}_compressed${v}`)}await j(o,g,{quality:y,width:m,format:e.format,replace:e.replace}),console.log(l.default.green(`\u2713 Processed: ${a.default.relative(process.cwd(),o)}`))}catch(g){console.error(l.default.red(`\u2717 Failed: ${o} - ${g.message}`))}}));await Promise.all(p),console.log(l.default.bold.cyan(`
|
|
3
|
+
Compression complete!`))}catch(t){console.error(l.default.red.bold(`Error: ${t.message}`)),process.exit(1)}});O.parse(process.argv);
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{Command as q}from"commander";import E from"fast-glob";import O from"p-limit";import o from"path";import g from"chalk";import D from"fs-extra";import C from"sharp";import d from"fs-extra";import v from"path";async function $(c,e,a){let{quality:n=80,width:m,format:u}=a,t=C(c);m&&(t=t.resize(m));let w=v.extname(c).toLowerCase(),s=u||w.slice(1);s==="jpeg"||s==="jpg"?t=t.jpeg({quality:n}):s==="png"?t=t.png({quality:n,compressionLevel:9}):s==="webp"?t=t.webp({quality:n}):s==="avif"&&(t=t.avif({quality:n}));let i=a.replace?`${c}.tmp_${Date.now()}`:e;try{await d.ensureDir(v.dirname(i)),await t.toFile(i),a.replace&&await d.move(i,c,{overwrite:!0})}catch(r){throw a.replace&&await d.pathExists(i)&&await d.remove(i),r}}var h=new q;h.name("micropng-cli").description("High-performance CLI image compressor").version("0.1.0").argument("<input>","Input file or directory").option("-o, --output <dir>","Output directory").option("-r, --recursive","Process subfolders deeply").option("--replace","OVERWRITE original files (Caution!)").option("-q, --quality <number>","Compression quality (1-100)","80").option("-w, --width <number>","Resize width in pixels").option("-f, --format <type>","Output format (jpeg, png, webp, avif)").option("-c, --concurrency <number>","Number of concurrent tasks","5").action(async(c,e)=>{try{let a=o.resolve(c),n=(await D.stat(a)).isDirectory(),m;if(n){let r=a.replace(/\\/g,"/");m=e.recursive?`${r}/**/*.{jpg,jpeg,png,webp}`:`${r}/*.{jpg,jpeg,png,webp}`}else m=a.replace(/\\/g,"/");let u=await E(m,{absolute:!0});if(u.length===0){console.log(g.yellow("No images found to process."));return}console.log(g.blue(`Found ${u.length} images. Processing...`));let t=O(parseInt(e.concurrency)),w=parseInt(e.quality),s=e.width?parseInt(e.width):void 0,i=u.map(r=>t(async()=>{try{let p;if(e.replace)p=r;else if(e.output){let l=n?o.relative(a,r):o.basename(r),y=o.dirname(l),f=o.extname(l),b=o.basename(l,f),x=e.format?`.${e.format}`:f;p=o.join(o.resolve(e.output),y,`${b}${x}`)}else{let l=o.extname(r),y=o.basename(r,l),f=o.dirname(r),b=e.format?`.${e.format}`:l;p=o.join(f,`${y}_compressed${b}`)}await $(r,p,{quality:w,width:s,format:e.format,replace:e.replace}),console.log(g.green(`\u2713 Processed: ${o.relative(process.cwd(),r)}`))}catch(p){console.error(g.red(`\u2717 Failed: ${r} - ${p.message}`))}}));await Promise.all(i),console.log(g.bold.cyan(`
|
|
3
|
+
Compression complete!`))}catch(a){console.error(g.red.bold(`Error: ${a.message}`)),process.exit(1)}});h.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "micropng-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "High-performance CLI image compressor using sharp and libvips",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist",
|
|
7
|
+
"README.md"
|
|
8
|
+
],
|
|
9
|
+
"main": "index.js",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"bin": {
|
|
12
|
+
"micropng-cli": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"dev": "tsup --watch",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:coverage": "vitest run --coverage",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"image-compression",
|
|
23
|
+
"cli",
|
|
24
|
+
"sharp",
|
|
25
|
+
"recursive",
|
|
26
|
+
"optimization"
|
|
27
|
+
],
|
|
28
|
+
"author": "Sahil",
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"chalk": "^5.6.2",
|
|
32
|
+
"commander": "^14.0.2",
|
|
33
|
+
"fast-glob": "^3.3.3",
|
|
34
|
+
"fs-extra": "^11.3.3",
|
|
35
|
+
"p-limit": "^7.2.0",
|
|
36
|
+
"sharp": "^0.34.5"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/fs-extra": "^11.0.4",
|
|
40
|
+
"@types/node": "^25.1.0",
|
|
41
|
+
"@types/sharp": "^0.31.1",
|
|
42
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
43
|
+
"pkg": "^5.8.1",
|
|
44
|
+
"tsup": "^8.5.1",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vitest": "^4.0.18"
|
|
47
|
+
}
|
|
48
|
+
}
|