@nolly-cafe/distill 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/LICENSE +19 -0
- package/README.md +187 -0
- package/bin/distfill.js +2 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2025 Nolly
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# @nolly-cafe/distill
|
|
2
|
+
|
|
3
|
+
`distill` is a zero-config, Vite-powered build and optimization CLI designed to be run anywhere, without requiring a local npm project, configuration files, or installed dependencies.
|
|
4
|
+
|
|
5
|
+
It is intended for static sites, simple projects, and quick builds where you want a production-ready `dist/` output with aggressive minification and image optimization — immediately.
|
|
6
|
+
|
|
7
|
+
The tool is executed via `pnpm dlx`, meaning nothing is installed permanently and no setup is required.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Zero configuration by default
|
|
14
|
+
- Optional, composable configuration via CLI flags
|
|
15
|
+
- No `package.json` required in the target directory
|
|
16
|
+
- No local Vite installation required
|
|
17
|
+
- Works with or without a `src/` directory
|
|
18
|
+
- Always outputs a production-ready directory
|
|
19
|
+
- HTML, JS, and CSS minification
|
|
20
|
+
- Image optimization and conversion pipeline
|
|
21
|
+
- Designed for ephemeral execution (`pnpm dlx`)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Node.js 18 or newer
|
|
28
|
+
- `pnpm` (recommended)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
From any directory containing a static site:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
pnpm dlx @nolly-cafe/distill
|
|
38
|
+
````
|
|
39
|
+
|
|
40
|
+
You may also specify a path explicitly:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
pnpm dlx @nolly-cafe/distill ./path/to/site
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## CLI options
|
|
49
|
+
|
|
50
|
+
The table below documents all supported CLI parameters, their purpose, defaults, and accepted values.
|
|
51
|
+
All options are optional; running `distill` with no flags uses opinionated defaults.
|
|
52
|
+
| Option | Description | Default | Allowed values |
|
|
53
|
+
| ------------------------- | ------------------------------- | ----------------------- | -------------------------------- |
|
|
54
|
+
| `path` (positional) | Source directory to build | `.` (current directory) | Any valid directory path |
|
|
55
|
+
| `--out <dir>` | Output directory | `<source>/dist` | Any valid directory path |
|
|
56
|
+
| `--images <dir>` | Image source directory | `<source>/img` | Any valid directory path |
|
|
57
|
+
| `--images-out <dir>` | Image output directory | `<out>/img` | Any valid directory path |
|
|
58
|
+
| `--no-images` | Disable all image processing | `false` | flag |
|
|
59
|
+
| `--image-preset <preset>` | Image optimization preset | `balanced` | `fast`, `balanced`, `aggressive` |
|
|
60
|
+
| `--no-webp` | Disable WebP generation | `false` | flag |
|
|
61
|
+
| `--no-avif` | Disable AVIF generation | `false` | flag |
|
|
62
|
+
| `--resize <WxH>` | Resize images before conversion | unset | e.g. `1200x630`, `800x` |
|
|
63
|
+
| `--jpeg-quality <number>` | JPEG quality setting | `75` | `1–100` |
|
|
64
|
+
| `--svg-preset <preset>` | SVG optimization level | `safe` | `safe`, `aggressive` |
|
|
65
|
+
| `--about` | Print tool information and exit | — | flag |
|
|
66
|
+
| `--help` | Print usage and exit | — | flag |
|
|
67
|
+
|
|
68
|
+
### Notes
|
|
69
|
+
- Presets affect **only image processing**, not HTML/CSS/JS minification.
|
|
70
|
+
- When both `--resize` and format conversion are enabled, resizing occurs before format generation.
|
|
71
|
+
- Explicit paths always take priority over inferred defaults.
|
|
72
|
+
- Unsupported image formats are copied verbatim.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Source resolution rules
|
|
77
|
+
|
|
78
|
+
* If a `src/` directory exists and no explicit path is provided, it is used as the build root
|
|
79
|
+
* If a path is explicitly provided, that path is always used
|
|
80
|
+
* The output directory defaults to `dist/` in the current working directory, unless overridden
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Directory example
|
|
85
|
+
|
|
86
|
+
Without `src/`:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
my-site/
|
|
90
|
+
├── index.html
|
|
91
|
+
├── img/
|
|
92
|
+
│ ├── hero.jpg
|
|
93
|
+
│ └── logo.png
|
|
94
|
+
└── dist/
|
|
95
|
+
├── index.html
|
|
96
|
+
├── assets/
|
|
97
|
+
└── img/
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
With `src/`:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
my-site/
|
|
104
|
+
├── src/
|
|
105
|
+
│ ├── index.html
|
|
106
|
+
│ └── img/
|
|
107
|
+
└── dist/
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Image processing
|
|
113
|
+
|
|
114
|
+
If an `img/` directory exists inside the source root, images are automatically processed and emitted into the output directory.
|
|
115
|
+
|
|
116
|
+
### Supported formats
|
|
117
|
+
|
|
118
|
+
* PNG
|
|
119
|
+
* JPEG
|
|
120
|
+
* GIF
|
|
121
|
+
* SVG
|
|
122
|
+
* WebP
|
|
123
|
+
* AVIF
|
|
124
|
+
|
|
125
|
+
### Default behavior
|
|
126
|
+
|
|
127
|
+
* PNG, JPEG, GIF, and SVG are optimized using imagemin
|
|
128
|
+
* WebP and AVIF versions are generated using sharp
|
|
129
|
+
* Unsupported formats are copied verbatim
|
|
130
|
+
* Output images are written to `<dist>/img`
|
|
131
|
+
|
|
132
|
+
### Customization (optional)
|
|
133
|
+
|
|
134
|
+
All image behavior can be controlled via flags, without configuration files:
|
|
135
|
+
|
|
136
|
+
* Presets for speed vs compression
|
|
137
|
+
* Format enable/disable
|
|
138
|
+
* Quality tuning
|
|
139
|
+
* Resize operations
|
|
140
|
+
* SVG optimization modes
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
pnpm dlx @nolly-cafe/distill ./site \
|
|
146
|
+
--image-preset aggressive \
|
|
147
|
+
--resize 1200x630 \
|
|
148
|
+
--no-avif
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Commands
|
|
154
|
+
|
|
155
|
+
### `--about`
|
|
156
|
+
|
|
157
|
+
Prints information about the tool and exits.
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
pnpm dlx @nolly-cafe/distill --about
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `--help`
|
|
164
|
+
|
|
165
|
+
Prints usage and available options.
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
pnpm dlx @nolly-cafe/distill --help
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Philosophy
|
|
174
|
+
|
|
175
|
+
`distill` is intentionally opinionated.
|
|
176
|
+
|
|
177
|
+
It does not aim to replace frameworks or bundlers. It exists to solve one problem well:
|
|
178
|
+
|
|
179
|
+
> “Take this folder and give me a clean, production-ready `dist/` in my current directory
|
|
180
|
+
|
|
181
|
+
The internal architecture separates argument parsing, path resolution, build orchestration, and asset processing. This allows new behavior to be added as configuration rather than refactors.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
[MIT License © 2025 NollyCafe LLC](./LICENSE)
|
package/bin/distfill.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import f from"process";import V from"yargs";import{hideBin as N}from"yargs/helpers";import{build as F,defineConfig as H}from"vite";import{createHtmlPlugin as M}from"vite-plugin-html";import u from"fs";import m from"path";import $ from"imagemin";import B from"imagemin-pngquant";import E from"imagemin-mozjpeg";import W from"imagemin-gifsicle";import Q from"imagemin-svgo";import C from"sharp";var y=e=>t=>`\x1B[${e}m${t}\x1B[0m`;var s={info:e=>console.log(y(36)("[distill]"),e),success:e=>console.log(y(32)("[distill]"),e),warn:e=>console.warn(y(33)("[distill]"),e),error:e=>console.error(y(31)("[distill]"),e)};var G=Q;var L=/\.(png|jpe?g|gif|svg|webp|avif|bmp|tiff?|ico)$/i;async function x(e){if(!u.existsSync(e.src)){s.warn(`image directory not found: ${e.src}`);return}const t=u.readdirSync(e.src).filter(i=>L.test(i));if(!t.length){s.warn("no images found");return}u.mkdirSync(e.out,{recursive:true});const r=[];if(e.optimize.png)r.push(B({quality:[.65,.8]}));if(e.optimize.jpeg)r.push(E({quality:e.jpegQuality}));if(e.optimize.gif)r.push(W({optimizationLevel:3}));if(e.optimize.svg){r.push(G({plugins:e.svgPreset==="aggressive"?[]:[{name:"removeViewBox",active:false}]}))}const n=t.filter(i=>/\.(png|jpe?g|gif|svg)$/i.test(i));if(n.length&&r.length){await $(n.map(i=>m.join(e.src,i)),{destination:e.out,plugins:r})}for(const i of t){const l=m.join(e.src,i);const d=m.parse(i).name;let p=C(l);if(e.resize){p=p.resize(e.resize.width,e.resize.height)}if(e.convert.webp){await p.webp({quality:e.convert.webp.quality}).toFile(m.join(e.out,`${d}.webp`))}if(e.convert.avif){await p.avif({quality:e.convert.avif.quality,effort:e.convert.avif.effort}).toFile(m.join(e.out,`${d}.avif`))}}for(const i of t){const l=m.join(e.out,i);if(!u.existsSync(l)){u.copyFileSync(m.join(e.src,i),l)}}s.success("image processing complete")}import k from"fs";import g from"path";function I(e){const{cwd:t,sourceArg:r,outArg:n,imagesArg:i,imagesOutArg:l,noImages:d,imagePreset:p="balanced",noWebp:A,noAvif:O,resize:h,svgPreset:S="safe",jpegQuality:q}=e;let c;if(r){c=g.resolve(t,r)}else{const a=g.join(t,"src");c=k.existsSync(a)?a:t}const b=n?g.resolve(t,n):g.join(t,"dist");if(d){return{sourceRoot:c,outDir:b}}const D={fast:{webp:{quality:60},avif:{quality:45,effort:3}},balanced:{webp:{quality:75},avif:{quality:50,effort:5}},aggressive:{webp:{quality:85},avif:{quality:60,effort:8}}};let w;if(h){const a=h.match(/^(\d+)?x(\d+)?$/);if(a){w={width:a[1]?Number(a[1]):void 0,height:a[2]?Number(a[2]):void 0}}}const R=i?g.resolve(t,i):g.join(c,"img");const T=l?g.resolve(t,l):g.join(b,"img");const v={...D[p]};if(A)delete v.webp;if(O)delete v.avif;return{sourceRoot:c,outDir:b,images:{enabled:true,src:R,out:T,optimize:{png:true,jpeg:true,gif:true,svg:true},jpegQuality:q??75,svgPreset:S,convert:v,resize:w}}}async function P(e){const t=I(e);const{sourceRoot:r,outDir:n,images:i}=t;s.info(`building from ${r}`);s.info(`output directory ${n}`);await F(H({base:"./",root:r,build:{outDir:n,emptyOutDir:true,assetsDir:"assets",minify:"terser"},plugins:[M({minify:true}),...i?[{name:"distill-images",apply:"build",async closeBundle(){await x(i)}}]:[]]}));if(!i){s.warn("image processing disabled")}s.success("build complete")}var j=["distill \u2014 zero-config static site distiller","","distill is a Vite-powered CLI designed for people who want production-ready","static builds without maintaining tooling, configuration files, or local","dependencies.","","It is built for ephemeral execution (pnpm dlx) and opinionated output.","","Author"," Nolly \u2014 independent developer"," https://github.com/thenolly","","Philosophy",' "Take this folder and give me a clean, production-ready dist/ in my current directory."',"","License"," MIT License \xA9 2025 NollyCafe LLC"].join("\n");var z=["Usage:"," distill [path] [options]","","Arguments:"," path Source directory (defaults to current directory)","","Options:"," --out <dir> Output directory"," --images <dir> Image source directory"," --images-out <dir> Image output directory"," --no-images Disable all image processing","","Image optimization:"," --image-preset <name> fast | balanced | aggressive"," --jpeg-quality <num> JPEG quality (0\u2013100)"," --svg-preset <name> safe | aggressive"," --resize <WxH> Resize images (e.g. 1200x630)"," --no-webp Disable WebP generation"," --no-avif Disable AVIF generation","","Other:"," --about Show information about distill"," --help Show this help message","","Behavior:"," - Explicit path disables automatic ./src detection"," - --out always takes priority"," - Output defaults to ./dist relative to the current working directory"," - Images default to <source>/img"," - Image output defaults to <dist>/img"," - Unsupported formats are copied verbatim","","Examples:"," pnpm dlx @nolly-cafe/distill"," pnpm dlx @nolly-cafe/distill ./site"," pnpm dlx @nolly-cafe/distill ./site --out ./build"," pnpm dlx @nolly-cafe/distill ./site --image-preset aggressive"," pnpm dlx @nolly-cafe/distill ./site --resize 1200x630 --no-avif"].join("\n");var o=V(N(f.argv)).help(false).version(false).option("about",{type:"boolean"}).option("help",{type:"boolean"}).option("out",{type:"string",describe:"Output directory"}).option("images",{type:"string",describe:"Image source directory"}).option("images-out",{type:"string",describe:"Image output directory"}).option("no-images",{type:"boolean",describe:"Disable image processing"}).option("image-preset",{choices:["fast","balanced","aggressive"],describe:"Image optimization preset"}).option("no-webp",{type:"boolean",describe:"Disable WebP output"}).option("no-avif",{type:"boolean",describe:"Disable AVIF output"}).option("resize",{type:"string",describe:"Resize images (WIDTHxHEIGHT)"}).option("svg-preset",{choices:["safe","aggressive"],describe:"SVG optimization preset"}).option("jpeg-quality",{type:"number",describe:"JPEG quality (1\u2013100)"}).parseSync();if(o.help){console.log(z);f.exit(0)}if(o.about){console.log(j);f.exit(0)}P({cwd:f.cwd(),sourceArg:o._[0]?String(o._[0]):void 0,outArg:o.out,imagesArg:o.images,imagesOutArg:o["images-out"],noImages:o["no-images"],imagePreset:o["image-preset"],noWebp:o["no-webp"],noAvif:o["no-avif"],resize:o.resize,svgPreset:o["svg-preset"],jpegQuality:o["jpeg-quality"]}).catch(e=>{s.error(e instanceof Error?e.message:String(e));f.exit(1)});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nolly-cafe/distill",
|
|
3
|
+
"description": "Zero-config, ephemeral CLI for building and optimizing static sites with Vite.",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"cli",
|
|
6
|
+
"static-site",
|
|
7
|
+
"vite",
|
|
8
|
+
"build",
|
|
9
|
+
"image-optimization",
|
|
10
|
+
"pnpm-dlx",
|
|
11
|
+
"zero-config"
|
|
12
|
+
],
|
|
13
|
+
"version": "0.1.0",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"distill": "./bin/distill.js"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"packageManager": "pnpm@10.18.1",
|
|
23
|
+
"files": [
|
|
24
|
+
"bin"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"imagemin": "^9.0.1",
|
|
31
|
+
"imagemin-gifsicle": "^7.0.0",
|
|
32
|
+
"imagemin-mozjpeg": "^10.0.0",
|
|
33
|
+
"imagemin-pngquant": "^10.0.0",
|
|
34
|
+
"imagemin-svgo": "^11.0.1",
|
|
35
|
+
"imagemin-webp": "^8.0.0",
|
|
36
|
+
"sharp": "^0.34.5",
|
|
37
|
+
"terser": "^5.44.1",
|
|
38
|
+
"vite": "^7.3.0",
|
|
39
|
+
"vite-plugin-html": "^3.2.2",
|
|
40
|
+
"yargs": "^18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/imagemin": "^9.0.1",
|
|
44
|
+
"@types/imagemin-gifsicle": "^7.0.4",
|
|
45
|
+
"@types/imagemin-mozjpeg": "^8.0.4",
|
|
46
|
+
"@types/imagemin-svgo": "^11.0.0",
|
|
47
|
+
"@types/imagemin-webp": "^7.0.3",
|
|
48
|
+
"@types/node": "^25.0.3",
|
|
49
|
+
"@types/yargs": "^17.0.35",
|
|
50
|
+
"tsup": "^8.5.1",
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
}
|
|
53
|
+
}
|