@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 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)
@@ -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
+ }