bunki 0.6.0 → 0.7.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 CHANGED
@@ -1,45 +1,60 @@
1
1
  # Bunki
2
2
 
3
- # Bunki
3
+ [![CI](https://github.com/kahwee/bunki/actions/workflows/ci.yml/badge.svg)](https://github.com/kahwee/bunki/actions/workflows/ci.yml)
4
+ [![Coverage Status](https://coveralls.io/repos/github/kahwee/bunki/badge.svg?branch=main)](https://coveralls.io/github/kahwee/bunki?branch=main)
5
+ [![npm version](https://badge.fury.io/js/bunki.svg)](https://badge.fury.io/js/bunki)
4
6
 
5
- Fast static site generator for blogs/docs built with Bun. Core features: Markdown + frontmatter, tags, year archives, pagination, RSS, sitemap, secure sanitized HTML, syntax highlighting, optional PostCSS pipeline, image uploading (S3/R2), tiny Nunjucks templates.
7
+ Fast static site generator for blogs and documentation built with Bun. Supports Markdown + frontmatter, tags, year-based archives, pagination, RSS feeds, sitemaps, secure HTML sanitization, syntax highlighting, PostCSS pipelines, media uploads (images & videos to S3/R2), incremental uploads with year filtering, and Nunjucks templating.
6
8
 
7
9
  ## Install
8
10
 
11
+ Requires **Bun v1.3.0+** (recommended) or Node.js v18+
12
+
9
13
  ```bash
10
- bun install bunki # add locally
11
- bun install -g bunki # or global
12
- npm i bunki # Node (>=18)
13
- ```
14
+ # Install globally with Bun
15
+ bun install -g bunki
14
16
 
15
- Requires Bun >= 1.2.21 (recommended runtime).
17
+ # Or with npm
18
+ npm install -g bunki
19
+
20
+ # Or in your project
21
+ bun install bunki
22
+ ```
16
23
 
17
24
  ## Quick Start
18
25
 
19
26
  ```bash
20
- bunki init
21
- bunki new "Hello World" --tags web,notes
22
- bunki generate
23
- bunki serve # http://localhost:3000
27
+ bunki init # Create new site
28
+ bunki new "My First Post" --tags web,notes # Add content
29
+ bunki generate # Build static site
30
+ bunki serve --port 3000 # Preview locally
24
31
  ```
25
32
 
26
- ## Minimal Config (bunki.config.ts)
33
+ This creates a fully functional site with Markdown content, responsive templates, and all assets in `dist/`.
27
34
 
28
- ```ts
35
+ ## Configuration
36
+
37
+ Create `bunki.config.ts` in your project root:
38
+
39
+ ```typescript
29
40
  import { SiteConfig } from "bunki";
41
+
30
42
  export default (): SiteConfig => ({
31
43
  title: "My Blog",
32
- description: "Thoughts",
44
+ description: "My thoughts and ideas",
33
45
  baseUrl: "https://example.com",
34
46
  domain: "example.com",
47
+
48
+ // Optional: PostCSS/Tailwind CSS support
35
49
  css: {
36
50
  input: "templates/styles/main.css",
37
51
  output: "css/style.css",
38
52
  postcssConfig: "postcss.config.js",
39
53
  enabled: true,
40
- }, // optional
54
+ },
55
+
56
+ // Optional: Image upload to Cloudflare R2 or S3
41
57
  s3: {
42
- // optional image upload
43
58
  accessKeyId: process.env.R2_ACCESS_KEY_ID || "",
44
59
  secretAccessKey: process.env.R2_SECRET_ACCESS_KEY || "",
45
60
  bucket: process.env.R2_BUCKET || "",
@@ -50,37 +65,54 @@ export default (): SiteConfig => ({
50
65
  });
51
66
  ```
52
67
 
53
- ## Frontmatter
68
+ ## Content & Frontmatter
69
+
70
+ Create Markdown files in `content/YYYY/` (e.g., `content/2025/my-post.md`):
54
71
 
55
72
  ```markdown
56
73
  ---
57
74
  title: "Post Title"
58
75
  date: 2025-01-15T09:00:00-07:00
59
76
  tags: [web, performance]
60
- excerpt: Optional summary
77
+ excerpt: "Optional summary for listings"
61
78
  ---
79
+
80
+ # Post Title
81
+
82
+ Your content here with **markdown** support.
83
+
84
+ ![Image alt text](/images/my-image.jpg)
85
+
86
+ <video controls width="640" height="360">
87
+ <source src="video.mp4" type="video/mp4">
88
+ Your browser does not support HTML5 video.
89
+ </video>
62
90
  ```
63
91
 
64
- Optional tag descriptions: `src/tags.toml`:
92
+ Optional: Define tag descriptions in `src/tags.toml`:
65
93
 
66
94
  ```toml
67
- performance = "Speed & optimization"
68
- web = "General web dev"
95
+ performance = "Performance optimization and speed"
96
+ web = "Web development and technology"
69
97
  ```
70
98
 
71
- ## CSS (Tailwind example)
99
+ ## CSS & Tailwind
100
+
101
+ To use Tailwind CSS:
72
102
 
73
103
  ```bash
74
104
  bun add -D tailwindcss @tailwindcss/postcss @tailwindcss/typography
75
105
  ```
76
106
 
77
- `postcss.config.js`:
107
+ Create `postcss.config.js`:
78
108
 
79
- ```js
80
- module.exports = { plugins: [require("@tailwindcss/postcss")] };
109
+ ```javascript
110
+ module.exports = {
111
+ plugins: [require("@tailwindcss/postcss")],
112
+ };
81
113
  ```
82
114
 
83
- `templates/styles/main.css`:
115
+ Create `templates/styles/main.css`:
84
116
 
85
117
  ```css
86
118
  @tailwind base;
@@ -88,541 +120,533 @@ module.exports = { plugins: [require("@tailwindcss/postcss")] };
88
120
  @tailwind utilities;
89
121
  ```
90
122
 
91
- Processed automatically during `bunki generate`.
123
+ CSS is processed automatically during `bunki generate`.
124
+
125
+ ## Image Management
126
+
127
+ ### Overview
128
+
129
+ The `images:push` command uploads local media (images and videos) to Cloudflare R2, AWS S3, or any S3-compatible storage provider. Media files are organized by year in the `images/` directory and uploaded with their full directory structure preserved.
130
+
131
+ **Supported formats:**
92
132
 
93
- ## Images
133
+ - **Images:** JPG, JPEG, PNG, GIF, WebP, SVG
134
+ - **Video:** MP4
94
135
 
95
- Env vars (R2 / S3):
136
+ ### Directory Structure
137
+
138
+ Organize images by year and post slug:
96
139
 
97
140
  ```
98
- R2_ACCESS_KEY_ID=...
99
- R2_SECRET_ACCESS_KEY=...
100
- R2_BUCKET=...
101
- R2_ENDPOINT=...
102
- R2_PUBLIC_URL=https://cdn.example.com
141
+ images/
142
+ ├── 2023/
143
+ │ ├── post-slug-1/
144
+ │ │ ├── image-1.jpg
145
+ │ │ └── image-2.png
146
+ │ └── post-slug-2/
147
+ │ └── photo.webp
148
+ ├── 2024/
149
+ │ └── travel-guide/
150
+ │ ├── paris-1.jpg
151
+ │ ├── london-2.jpg
152
+ │ ├── tokyo-3.png
153
+ │ └── travel-vlog.mp4
154
+ └── 2025/
155
+ └── new-post/
156
+ ├── screenshot.jpg
157
+ └── demo-video.mp4
103
158
  ```
104
159
 
105
- Upload:
160
+ The directory structure is preserved when uploading to cloud storage.
106
161
 
107
- ```bash
108
- bunki images:push --images ./images --output-json image-map.json
109
- ```
162
+ ### Configuration
163
+
164
+ Add S3/R2 configuration to `bunki.config.ts`:
110
165
 
111
- ## Programmatic
166
+ ```typescript
167
+ import { SiteConfig } from "bunki";
168
+
169
+ export default (): SiteConfig => ({
170
+ title: "My Blog",
171
+ // ... other config
112
172
 
113
- ```ts
114
- import { SiteGenerator, loadConfig } from "bunki";
115
- const cfg = await loadConfig("./bunki.config.ts");
116
- const gen = new SiteGenerator({
117
- contentDir: "content",
118
- outputDir: "dist",
119
- templatesDir: "templates",
120
- config: cfg,
173
+ // Image upload configuration
174
+ s3: {
175
+ accessKeyId: process.env.S3_ACCESS_KEY_ID || "",
176
+ secretAccessKey: process.env.S3_SECRET_ACCESS_KEY || "",
177
+ bucket: process.env.S3_BUCKET || "",
178
+ endpoint: process.env.S3_ENDPOINT, // Optional: for R2, etc.
179
+ region: process.env.S3_REGION || "auto",
180
+ publicUrl: process.env.S3_PUBLIC_URL || "",
181
+ },
121
182
  });
122
- await gen.initialize();
123
- await gen.generate();
124
183
  ```
125
184
 
126
- ## CLI
185
+ ### Environment Variables
127
186
 
128
- ```
129
- init | new | generate | serve | images:push | css
130
- ```
187
+ Set these in your `.env` file or export them in your shell:
131
188
 
132
- ## Output
189
+ ```bash
190
+ # Required
191
+ export S3_ACCESS_KEY_ID="your-access-key"
192
+ export S3_SECRET_ACCESS_KEY="your-secret-key"
193
+ export S3_BUCKET="your-bucket-name"
194
+ export S3_PUBLIC_URL="https://cdn.example.com"
133
195
 
134
- ```
135
- - 📱 **Mobile-first** responsive templates
136
- index.html
137
- feed.xml
138
- sitemap.xml
139
- 2025/... per-post dirs
140
- tags/... tag pages
141
- css/style.css (if enabled)
196
+ # Optional (for Cloudflare R2 or custom endpoints)
197
+ export S3_ENDPOINT="https://r2.cloudflarestorage.com"
198
+ export S3_REGION="auto"
199
+
200
+ # Optional (custom domain per bucket)
201
+ export S3_CUSTOM_DOMAIN_YOUR_BUCKET="cdn.example.com"
142
202
  ```
143
203
 
144
- ## Security
204
+ ### Basic Usage
145
205
 
146
- HTML sanitized; external links hardened; unsafe tags stripped.
206
+ Upload all images:
147
207
 
148
- ## Changelog
208
+ ```bash
209
+ bunki images:push
210
+ ```
149
211
 
150
- v0.5.3 (current)
212
+ This command:
151
213
 
152
- - Modularized CLI commands (init, new, generate, serve, css, images:push)
153
- - Dependency-injected command handlers for easier unit testing
154
- - Switched CLI entry to Bun.main/Bun.argv
155
- - Added/updated tests for init/new handlers; all suites green
156
- - Minor CSS command robustness and exit handling
214
+ 1. Scans the `images/` directory recursively
215
+ 2. Uploads all supported image formats
216
+ 3. Preserves the directory structure (year/slug/filename)
217
+ 4. Generates public URLs for each image
157
218
 
158
- v0.3.x
219
+ ### Command Options
159
220
 
160
- - PostCSS pipeline + `css` command
161
- - Export map + sideEffects=false for tree-shaking
162
- - Prepack build cleanup
221
+ #### `--images <dir>`
163
222
 
164
- ## Contribute
223
+ Specify a custom images directory (default: `./images`)
165
224
 
166
225
  ```bash
167
- bun install
168
- bun run build
169
- bun test
226
+ bunki images:push --images ./assets/images
170
227
  ```
171
228
 
172
- ## License
229
+ #### `--domain <domain>`
173
230
 
174
- MIT © KahWee Teng
231
+ Set a custom domain for bucket identification (optional)
175
232
 
176
- - ⚡ **Simple CLI interface** with intuitive commands
233
+ ```bash
234
+ bunki images:push --domain my-blog
235
+ ```
177
236
 
178
- ## 🛠️ PostCSS Integration
237
+ #### `--output-json <file>`
179
238
 
180
- Bunki v0.3.0+ includes built-in PostCSS processing, allowing you to use modern CSS frameworks like Tailwind CSS while keeping framework-specific dependencies in your project:
239
+ Export a JSON mapping of filenames to their public URLs
181
240
 
182
- ### CSS Configuration
241
+ ```bash
242
+ bunki images:push --output-json image-urls.json
243
+ ```
183
244
 
184
- Configure CSS processing in your `bunki.config.ts`:
245
+ This creates a JSON file with the structure:
185
246
 
186
- ```typescript
187
- export default function (): SiteConfig {
188
- return {
189
- title: "My Blog",
190
- description: "A blog built with Bunki",
191
- baseUrl: "https://example.com",
192
- domain: "blog",
193
- // CSS processing configuration
194
- css: {
195
- input: "templates/styles/main.css", // Input CSS file
196
- output: "css/style.css", // Output path in dist
197
- postcssConfig: "postcss.config.js", // PostCSS config file
198
- enabled: true, // Enable CSS processing
199
- watch: false, // Watch for changes (dev mode)
200
- },
201
- // ... other config
202
- };
247
+ ```json
248
+ {
249
+ "2023/post-slug/image.jpg": "https://cdn.example.com/2023/post-slug/image.jpg",
250
+ "2024/travel/paris.jpg": "https://cdn.example.com/2024/travel/paris.jpg"
203
251
  }
204
252
  ```
205
253
 
206
- ### CSS Processing Commands
254
+ #### `--min-year <year>`
255
+
256
+ Upload only images from the specified year onwards
207
257
 
208
258
  ```bash
209
- # Process CSS as part of site generation
210
- bunki generate
259
+ # Upload only 2023 and 2024 images (skip 2021, 2022)
260
+ bunki images:push --min-year 2023
211
261
 
212
- # Process CSS standalone
213
- bunki css
262
+ # Upload only 2024 and newer images
263
+ bunki images:push --min-year 2024
214
264
 
215
- # Watch CSS files for changes (development)
216
- bunki css --watch
265
+ # Upload from 2022 onwards (all images in this example)
266
+ bunki images:push --min-year 2022
217
267
  ```
218
268
 
219
- ### Framework Integration Example (Tailwind CSS)
269
+ This is useful for:
220
270
 
221
- 1. **Install Tailwind in your project** (not in bunki):
271
+ - Incremental uploads (upload only new images)
272
+ - Testing uploads for specific years
273
+ - Managing large image collections across multiple uploads
222
274
 
223
- ```bash
224
- bun add -D tailwindcss @tailwindcss/postcss @tailwindcss/typography
225
- ```
275
+ ### Complete Examples
226
276
 
227
- 2. **Create `postcss.config.js`**:
277
+ #### Cloudflare R2 Setup
228
278
 
229
- ```javascript
230
- module.exports = {
231
- plugins: [require("@tailwindcss/postcss")],
232
- };
233
- ```
279
+ 1. **Create R2 bucket and API token** in Cloudflare dashboard
234
280
 
235
- 3. **Create `tailwind.config.js`**:
281
+ 2. **Set environment variables:**
236
282
 
237
- ```javascript
238
- module.exports = {
239
- content: ["./content/**/*.{md,mdx}", "./templates/**/*.{njk,html}"],
240
- theme: {
241
- extend: {},
242
- },
243
- plugins: [require("@tailwindcss/typography")],
244
- };
283
+ ```bash
284
+ export S3_ACCESS_KEY_ID="your-r2-api-token-id"
285
+ export S3_SECRET_ACCESS_KEY="your-r2-api-token-secret"
286
+ export S3_BUCKET="my-blog-images"
287
+ export S3_ENDPOINT="https://r2.cloudflarestorage.com"
288
+ export S3_REGION="auto"
289
+ export S3_PUBLIC_URL="https://cdn.example.com"
245
290
  ```
246
291
 
247
- 4. **Create your CSS file** (`templates/styles/main.css`):
292
+ 3. **Upload images:**
248
293
 
249
- ```css
250
- @tailwind base;
251
- @tailwind components;
252
- @tailwind utilities;
294
+ ```bash
295
+ bunki images:push --output-json image-urls.json
253
296
  ```
254
297
 
255
- Bunki will process your CSS through PostCSS and Tailwind, generating optimized output in your `dist` directory.
256
-
257
- ## 📦 Installation
298
+ #### AWS S3 Setup
258
299
 
259
- > **IMPORTANT**: Bunki requires Bun v1.2.21 or later as its runtime. It also works with Node.js v18+ but Bun is recommended for optimal performance.
300
+ 1. **Create S3 bucket and IAM user** in AWS Console
260
301
 
261
- ### Prerequisites
302
+ 2. **Set environment variables:**
262
303
 
263
304
  ```bash
264
- # Install Bun if you don't have it
265
- curl -fsSL https://bun.sh/install | bash
266
-
267
- # Verify Bun version (should be 1.2.21 or later)
268
- bun --version
305
+ export S3_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
306
+ export S3_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
307
+ export S3_BUCKET="my-blog-bucket"
308
+ export S3_REGION="us-east-1"
309
+ export S3_PUBLIC_URL="https://my-blog-bucket.s3.amazonaws.com"
269
310
  ```
270
311
 
271
- ### Installation Options
312
+ 3. **Upload images:**
272
313
 
273
314
  ```bash
274
- # Install globally with Bun
275
- bun install -g bunki
276
-
277
- # Or with npm
278
- npm install -g bunki
279
-
280
- # Or install locally in your project
281
- bun install bunki
282
- # or
283
- npm install bunki
284
-
285
- # Or from GitHub for development
286
- git clone git@github.com:kahwee/bunki.git
287
- cd bunki
288
- bun install
289
- bun run build
290
- bun link
315
+ bunki images:push
291
316
  ```
292
317
 
293
- ## 🚀 Quick Start
318
+ #### Incremental Upload (Year-Based)
294
319
 
295
- ### Initialize a New Site
320
+ If you have thousands of images and want to upload them incrementally:
296
321
 
297
322
  ```bash
298
- # Create a new site with default templates and configuration
299
- bunki init
323
+ # First, upload all 2023 images
324
+ bunki images:push --min-year 2023 --max-year 2023
325
+
326
+ # Next, upload 2024 images
327
+ bunki images:push --min-year 2024 --max-year 2024
300
328
 
301
- # This creates:
302
- # - bunki.config.ts (configuration)
303
- # - content/ (for markdown posts)
304
- # - templates/ (Nunjucks templates)
305
- # - public/ (static assets)
329
+ # Finally, upload 2025 images
330
+ bunki images:push --min-year 2025
306
331
  ```
307
332
 
308
- ### Add Content
333
+ ### Using Uploaded Images in Markdown
309
334
 
310
- Create markdown files in the `content` directory with frontmatter:
335
+ After uploading, reference images in your Markdown posts:
311
336
 
312
- ````markdown
337
+ ```markdown
313
338
  ---
314
- title: Your Post Title
315
- date: 2025-01-01T09:00:00-07:00
316
- tags: [web-development, javascript]
339
+ title: "Paris Trip"
340
+ date: 2024-06-15T10:00:00
341
+ tags: [travel, france]
317
342
  ---
318
343
 
319
- # Your Post Title
344
+ # My Trip to Paris
320
345
 
321
- Your post content goes here with **markdown** support!
346
+ ![Eiffel Tower at sunset](https://cdn.example.com/2024/paris-trip/eiffel-tower.jpg)
322
347
 
323
- ```javascript
324
- console.log("Code highlighting works too!");
348
+ ![Louvre Museum](https://cdn.example.com/2024/paris-trip/louvre.jpg)
349
+
350
+ ## Evening Stroll
351
+
352
+ The Parisian streets at night are magical.
353
+
354
+ ![Seine River at night](https://cdn.example.com/2024/paris-trip/seine-night.jpg)
325
355
  ```
326
356
 
327
- You can also embed videos using HTML5 video tags:
357
+ ### Using Uploaded Videos in Markdown
328
358
 
329
- <video src="https://example.com/video.mp4" controls width="640" height="360"></video>
359
+ Upload MP4 videos alongside your images and embed them in your posts:
330
360
 
331
- Or with multiple formats for better browser compatibility:
361
+ ```markdown
362
+ ---
363
+ title: "Travel Vlog"
364
+ date: 2024-06-15T10:00:00
365
+ tags: [travel, video]
366
+ ---
332
367
 
333
- <video controls width="640" height="360" poster="thumbnail.jpg">
334
- <source src="video.mp4" type="video/mp4">
335
- <source src="video.webm" type="video/webm">
336
- Your browser does not support the video tag.
337
- </video>
338
- ````
368
+ # My Paris Adventure
339
369
 
340
- ````
370
+ Watch my trip to Paris:
341
371
 
342
- Or use the CLI to create a new post:
372
+ <video controls width="640" height="360">
373
+ <source src="https://cdn.example.com/2024/paris-trip/travel-vlog.mp4" type="video/mp4">
374
+ Your browser does not support HTML5 video.
375
+ </video>
343
376
 
344
- ```bash
345
- bunki new "Your Post Title" --tags "web-development, javascript"
346
- ````
377
+ ## Behind the Scenes
347
378
 
348
- ### Generate Your Site
379
+ Check out the making of the vlog:
349
380
 
350
- ```bash
351
- # Generate the static site (includes CSS processing)
352
- bunki generate
381
+ <video controls width="640" height="360">
382
+ <source src="https://cdn.example.com/2024/paris-trip/behind-scenes.mp4" type="video/mp4">
383
+ Your browser does not support HTML5 video.
384
+ </video>
353
385
  ```
354
386
 
355
- ### Preview Your Site
387
+ **Video Upload Example:**
356
388
 
357
389
  ```bash
358
- # Start a local development server
359
- bunki serve
390
+ # Upload all images and videos (including MP4 files)
391
+ bunki images:push
360
392
 
361
- # Custom port
362
- bunki serve --port 3000
393
+ # Upload only 2024 videos and images
394
+ bunki images:push --min-year 2024
395
+
396
+ # Preview what would be uploaded without actually uploading
397
+ BUNKI_DRY_RUN=true bunki images:push --min-year 2024
363
398
  ```
364
399
 
365
- ## ⚙️ Configuration
400
+ **Video File Organization:**
366
401
 
367
- The `bunki.config.ts` file contains the configuration for your site:
402
+ Keep videos organized the same way as images for consistency:
368
403
 
369
- ```typescript
370
- import { SiteConfig } from "bunki";
371
- import { config } from "dotenv";
372
-
373
- // Load environment variables from .env file
374
- config();
375
-
376
- export default function (): SiteConfig {
377
- return {
378
- title: "My Blog",
379
- description: "A blog built with Bunki",
380
- baseUrl: "https://example.com",
381
- domain: "blog",
382
-
383
- // CSS processing configuration
384
- css: {
385
- input: "templates/styles/main.css",
386
- output: "css/style.css",
387
- postcssConfig: "postcss.config.js",
388
- enabled: true,
389
- watch: false,
390
- },
391
-
392
- // Cloud storage configuration for images
393
- publicUrl: process.env.R2_PUBLIC_URL,
394
- s3: {
395
- accessKeyId: process.env.R2_ACCESS_KEY_ID || "",
396
- secretAccessKey: process.env.R2_SECRET_ACCESS_KEY || "",
397
- bucket: process.env.R2_BUCKET || "",
398
- endpoint: process.env.R2_ENDPOINT,
399
- region: process.env.R2_REGION || "auto",
400
- },
401
- };
402
- }
404
+ ```
405
+ images/
406
+ ├── 2024/
407
+ │ └── travel-vlog/
408
+ │ ├── intro.mp4
409
+ │ ├── highlights.mp4
410
+ │ ├── thumbnail.jpg
411
+ │ └── poster.jpg
412
+ └── 2025/
413
+ └── tutorial/
414
+ ├── part-1.mp4
415
+ ├── part-2.mp4
416
+ └── preview.jpg
403
417
  ```
404
418
 
405
- ## 🖼️ Image Management
419
+ **Video Tips:**
406
420
 
407
- Bunki uses Bun's native S3 API for efficient image uploads to S3-compatible services like Cloudflare R2.
421
+ 1. **File Size**: Keep MP4 files optimized (under 50MB recommended)
422
+ - Use tools like FFmpeg to compress before uploading
423
+ - Example: `ffmpeg -i input.mp4 -crf 28 output.mp4`
408
424
 
409
- ### Environment Configuration
425
+ 2. **Format & Codec**:
426
+ - Use H.264 video codec for best compatibility
427
+ - Use AAC audio codec
428
+ - Container: MP4 (.mp4 extension)
410
429
 
411
- Create a `.env` file with your storage credentials:
430
+ 3. **Video Dimensions**:
431
+ - Keep 16:9 aspect ratio for web
432
+ - Common resolutions: 640x360, 1280x720, 1920x1080
412
433
 
413
- ```env
414
- R2_ACCOUNT_ID=your-account-id
415
- R2_ACCESS_KEY_ID=your-access-key
416
- R2_SECRET_ACCESS_KEY=your-secret-key
417
- R2_BUCKET=your-bucket
418
- R2_PUBLIC_URL=https://your-cdn-url.com
419
- ```
434
+ 4. **Hosting**:
435
+ - MP4s benefit from CDN caching via S3/R2
436
+ - Cloudflare R2 provides excellent video delivery
437
+ - AWS S3 with CloudFront for additional acceleration
438
+
439
+ ### Dry Run Mode
420
440
 
421
- For domain-specific custom domains:
441
+ Test the upload process without actually uploading:
422
442
 
423
- ```env
424
- R2_CUSTOM_DOMAIN_EXAMPLE_COM=cdn.example.com
443
+ ```bash
444
+ # Preview what would be uploaded (no actual upload)
445
+ BUNKI_DRY_RUN=true bunki images:push
425
446
  ```
426
447
 
427
- ### Upload Commands
448
+ This shows:
428
449
 
429
- ```bash
430
- # Upload all images from the images/ directory
431
- bunki images:push
450
+ - Which images would be uploaded
451
+ - The directory structure that would be created
452
+ - Generated public URLs
432
453
 
433
- # Specify a different images directory
434
- bunki images:push --images path/to/images
454
+ ### Troubleshooting
435
455
 
436
- # Output URL mapping to a JSON file for reference
437
- bunki images:push --output-json image-urls.json
456
+ #### "Missing S3 configuration"
438
457
 
439
- # Upload for a specific domain
440
- bunki images:push --domain example.com
441
- ```
442
-
443
- Supported formats: **JPG**, **PNG**, **GIF**, **WebP**, **SVG**
444
-
445
- ## 📁 Directory Structure
446
-
447
- ```
448
- my-blog/
449
- ├── bunki.config.ts # Site configuration
450
- ├── postcss.config.js # PostCSS configuration (optional)
451
- ├── tailwind.config.js # Tailwind config (if using Tailwind)
452
- ├── .env # Environment variables
453
- ├── content/ # Markdown content
454
- │ └── 2025/ # Year-based organization
455
- │ ├── my-first-post.md
456
- │ └── another-post.md
457
- ├── templates/ # Nunjucks templates
458
- │ ├── base.njk # Base layout
459
- │ ├── index.njk # Homepage
460
- │ ├── post.njk # Post template
461
- │ ├── tag.njk # Tag page
462
- │ ├── tags.njk # Tags index
463
- │ ├── archive.njk # Year archive
464
- │ └── styles/ # CSS source files
465
- │ └── main.css # Main stylesheet
466
- ├── images/ # Local images (uploaded to cloud)
467
- ├── public/ # Static assets (copied to dist)
468
- ├── src/ # Tag descriptions
469
- │ └── tags.toml # Tag metadata
470
- └── dist/ # Generated site (output)
471
- ├── index.html
472
- ├── css/
473
- │ └── style.css # Processed CSS
474
- ├── 2025/
475
- │ └── my-first-post/
476
- │ └── index.html
477
- ├── tags/
478
- ├── feed.xml # RSS feed
479
- └── sitemap.xml # Sitemap
480
- ```
481
-
482
- ## 🚨 CLI Commands
458
+ Ensure all required environment variables are set. Check `bunki.config.ts` and your `.env` file.
483
459
 
484
- ```bash
485
- Usage: bunki [options] [command]
460
+ #### "No image files found"
486
461
 
487
- Commands:
488
- init [options] Initialize a new site with templates
489
- new [options] <title> Create a new blog post
490
- generate [options] Generate static site from content
491
- css [options] Process CSS using PostCSS
492
- serve [options] Start local development server
493
- images:push [options] Upload images to cloud storage
494
- help [command] Display help for command
462
+ - Verify images exist in `images/` directory
463
+ - Check that files have supported extensions (.jpg, .png, .gif, .webp, .svg)
464
+ - Ensure the directory structure is correct (e.g., `images/2024/post-slug/image.jpg`)
495
465
 
496
- Options:
497
- -V, --version Display version number
498
- -h, --help Display help for command
499
- ```
466
+ #### "Unauthorized" or "Access Denied"
500
467
 
501
- ### Detailed Command Options
468
+ - Verify S3 credentials (access key and secret key)
469
+ - Check that the IAM user/API token has S3 permissions
470
+ - Confirm the bucket name is correct
502
471
 
503
- ```bash
504
- # Initialize new site
505
- bunki init --config custom.config.ts
472
+ #### "Invalid bucket name"
506
473
 
507
- # Create new post
508
- bunki new "My Post Title" --tags "javascript, web-dev"
474
+ - S3 bucket names must be globally unique
475
+ - Use only lowercase letters, numbers, and hyphens
476
+ - Bucket names must be 3-63 characters long
509
477
 
510
- # Generate with custom options
511
- bunki generate --config bunki.config.ts --output dist
478
+ ### Advanced Configuration
512
479
 
513
- # CSS processing
514
- bunki css --watch # Watch for changes
515
- bunki css --output dist # Custom output directory
480
+ #### Custom Domain per Bucket
516
481
 
517
- # Development server
518
- bunki serve --port 3000 # Custom port
519
- bunki serve --output dist # Serve from custom directory
482
+ If you have multiple S3 buckets with different custom domains:
520
483
 
521
- # Image upload
522
- bunki images:push --domain example.com
523
- bunki images:push --images custom/path --output-json mapping.json
484
+ ```bash
485
+ export S3_CUSTOM_DOMAIN_MY_BUCKET="cdn1.example.com"
486
+ export S3_CUSTOM_DOMAIN_BACKUP_BUCKET="cdn2.example.com"
524
487
  ```
525
488
 
526
- ## 🏗️ Development
489
+ The bucket name is converted to uppercase and hyphens to underscores for the environment variable name.
527
490
 
528
- ### Prerequisites for Contributors
491
+ #### Direct CDN URLs
529
492
 
530
- ```bash
531
- git clone git@github.com:kahwee/bunki.git
532
- cd bunki
533
- bun install
493
+ Configure public URLs with custom domains:
494
+
495
+ ```typescript
496
+ // bunki.config.ts
497
+ s3: {
498
+ // ... other config
499
+ publicUrl: "https://img.example.com",
500
+ }
534
501
  ```
535
502
 
536
- ### Development Commands
503
+ Or via environment variable:
537
504
 
538
505
  ```bash
539
- # Build the project
540
- bun run build
506
+ export S3_PUBLIC_URL="https://img.example.com"
507
+ ```
508
+
509
+ ### Performance Tips
541
510
 
542
- # Run in development mode with watch
543
- bun run dev
511
+ 1. **Use year-based filtering** for large image collections:
544
512
 
545
- # Run tests
546
- bun test
513
+ ```bash
514
+ bunki images:push --min-year 2024 # Only newest images
515
+ ```
547
516
 
548
- # Run tests with coverage
549
- bun test:coverage
517
+ 2. **Organize by post slug** for better directory structure:
550
518
 
551
- # Watch tests
552
- bun test:watch
519
+ ```
520
+ images/2024/post-title/image.jpg
521
+ images/2024/post-title/photo.jpg
522
+ ```
553
523
 
554
- # Type checking
555
- bun run typecheck
524
+ 3. **Compress images before uploading** to save storage:
525
+ - Use tools like `imagemin` or built-in OS utilities
526
+ - Aim for 500KB or smaller per image
527
+
528
+ 4. **Use modern formats** (WebP) for better compression:
529
+ - JPG/PNG for screenshots
530
+ - WebP for photos
531
+ - SVG for icons/graphics
532
+
533
+ ## CLI Commands
534
+
535
+ ```bash
536
+ bunki init [--config FILE] # Initialize new site
537
+ bunki new <TITLE> [--tags TAG1,TAG2] # Create new post
538
+ bunki generate [--config FILE] # Build static site
539
+ bunki serve [--port 3000] # Start dev server
540
+ bunki css [--watch] # Process CSS
541
+ bunki images:push [--domain DOMAIN] # Upload images to cloud
542
+ ```
543
+
544
+ ## Output Structure
545
+
546
+ ```
547
+ dist/
548
+ ├── index.html # Homepage
549
+ ├── feed.xml # RSS feed
550
+ ├── sitemap.xml # XML sitemap
551
+ ├── css/style.css # Processed stylesheet
552
+ ├── 2025/
553
+ │ └── my-post/
554
+ │ └── index.html # Post page
555
+ ├── tags/
556
+ │ └── web/
557
+ │ └── index.html # Tag page
558
+ └── page/
559
+ └── 2/index.html # Paginated content
560
+ ```
561
+
562
+ ## Features
563
+
564
+ - **Markdown Processing**: Frontmatter extraction, code highlighting, HTML sanitization
565
+ - **Security**: XSS protection, sanitized HTML, link hardening
566
+ - **Performance**: Static files, optional gzip, optimized output
567
+ - **Templating**: Nunjucks with custom filters and macros
568
+ - **Styling**: Built-in PostCSS support for modern CSS frameworks
569
+ - **Media Management**: Direct S3/R2 uploads for images and MP4 videos with URL mapping
570
+ - **Incremental Uploads**: Year-based filtering (`--min-year`) for large media collections
571
+ - **SEO**: Automatic RSS feeds, sitemaps, meta tags
572
+ - **Pagination**: Configurable posts per page
573
+ - **Archives**: Year-based and tag-based organization
574
+
575
+ ## Development
556
576
 
557
- # Format code
558
- bun run format
577
+ ```bash
578
+ git clone git@github.com:kahwee/bunki.git
579
+ cd bunki
580
+ bun install
559
581
 
560
- # Clean build artifacts
561
- bun run clean
582
+ bun run build # Build distribution
583
+ bun test # Run test suite
584
+ bun test:coverage # Test coverage report
585
+ bun run typecheck # TypeScript validation
586
+ bun run format # Prettier formatting
562
587
  ```
563
588
 
564
- ### Project Structure
589
+ ## Project Structure
565
590
 
566
591
  ```
567
592
  bunki/
568
593
  ├── src/
569
- │ ├── cli.ts # CLI interface
570
- │ ├── config.ts # Configuration management
571
- │ ├── site-generator.ts # Core site generation
572
- │ ├── server.ts # Development server
573
- │ ├── parser.ts # Markdown parsing
574
- │ ├── types.ts # TypeScript types
575
- │ └── utils/
576
- ├── css-processor.ts # PostCSS integration
577
- ├── file-utils.ts # File operations
578
- ├── image-uploader.ts # Image cloud upload
579
- │ ├── markdown-utils.ts # Markdown processing
580
- │ └── s3-uploader.ts # S3 API client
581
- ├── test/ # Test files
582
- ├── fixtures/ # Test fixtures
583
- └── dist/ # Built output
594
+ │ ├── cli.ts # CLI interface
595
+ │ ├── config.ts # Configuration management
596
+ │ ├── site-generator.ts # Core generation logic
597
+ │ ├── server.ts # Development server
598
+ │ ├── parser.ts # Markdown parsing
599
+ │ ├── types.ts # TypeScript types
600
+ │ └── utils/ # Utility modules
601
+ ├── test/ # Test suite (mirrors src/)
602
+ ├── templates/ # Example templates
603
+ ├── fixtures/ # Test fixtures
604
+ └── dist/ # Built output
584
605
  ```
585
606
 
586
- ## 📋 Changelog
607
+ ## Changelog
587
608
 
588
- ### v0.3.0 (Latest)
609
+ ### v0.7.0 (Current)
589
610
 
590
- - **NEW**: PostCSS integration with configurable CSS processing
591
- - **NEW**: CSS watch mode for development
592
- - **NEW**: Framework-agnostic CSS support (Tailwind, etc.)
593
- - 🔄 **IMPROVED**: Enhanced CLI with `css` command
594
- - 🔄 **IMPROVED**: Better error handling and fallbacks
595
- - 🐛 **FIXED**: Directory existence validation for development server
596
- - 📚 **DOCS**: Comprehensive PostCSS integration guide
611
+ - **Media uploads**: Added MP4 video support alongside image uploads
612
+ - **Incremental uploads**: Year-based filtering with `--min-year` option
613
+ - **Enhanced documentation**: Comprehensive video upload guide with examples
614
+ - **Test coverage**: Added 10+ tests for image/video uploader functionality
615
+ - **Fixed timestamps**: Stable dates in test fixtures to prevent flipping
597
616
 
598
- ### v0.2.6
617
+ ### v0.6.1
599
618
 
600
- - 🐛 **FIXED**: Server directory existence check
601
- - 🔄 **IMPROVED**: Error handling and logging
602
- - 📚 **DOCS**: Enhanced documentation
619
+ - Version bump and welcome date stabilization
620
+ - Test formatting improvements
621
+ - Code style consistency updates
603
622
 
604
- ## 🤝 Contributing
623
+ ### v0.5.3
605
624
 
606
- Contributions are welcome! Please read our contributing guidelines and feel free to submit issues and pull requests.
625
+ - Modularized CLI commands with dependency injection
626
+ - Enhanced test coverage (130+ tests, 539+ assertions)
627
+ - Fixed CLI entry point detection (Bun.main compatibility)
628
+ - Added comprehensive server tests using Bun.serve()
629
+ - Improved CSS processor with fallback support
607
630
 
608
- ### Areas for Contribution
631
+ ### v0.3.0
609
632
 
610
- - 🐛 Bug fixes and improvements
611
- - 📝 Documentation improvements
612
- - 🧪 Additional test coverage
613
- - New features (CSS plugins, template engines, etc.)
614
- - 🎨 Template improvements
633
+ - PostCSS integration with CSS processing command
634
+ - Framework-agnostic CSS support (Tailwind, etc.)
635
+ - CSS watch mode for development
636
+ - Better error handling and recovery
615
637
 
616
- ## 📄 License
638
+ ## Contributing
617
639
 
618
- MIT © [KahWee Teng](https://github.com/kahwee)
640
+ Contributions welcome! Areas for improvement:
619
641
 
620
- ---
642
+ - Bug fixes and error handling
643
+ - Documentation and examples
644
+ - Test coverage expansion
645
+ - Performance optimizations
646
+ - New features and plugins
621
647
 
622
- ## 🙋‍♂️ Support
648
+ ## License
623
649
 
624
- - 📚 [Documentation](https://github.com/kahwee/bunki)
625
- - 🐛 [Issues](https://github.com/kahwee/bunki/issues)
626
- - 💬 [Discussions](https://github.com/kahwee/bunki/discussions)
650
+ MIT © [KahWee Teng](https://github.com/kahwee)
627
651
 
628
- Built with ❤️ using [Bun](https://bun.sh)
652
+ Built with [Bun](https://bun.sh)