bridgetown-image-pipeline 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4b506abefc78cfff32c2485ca39d6cd789486ffde30b52060e9aee52a2333510
4
+ data.tar.gz: 5bf51dd415078908778245b62003e4e36417519bb134e174e470bb77573f2f8c
5
+ SHA512:
6
+ metadata.gz: 8bd6e9145bf27b2ca0156ee67ce008bae9b3648d458baf7ec46adcbbddd34cc2e777c7436fee2a8882f8c90bb77954574e12149a937cd4d00290d5a7ab9001e3
7
+ data.tar.gz: 3d2cc00f6657d78ebf7c5777d6d28b8e1e46e116a500e991373c324d140c1c55f4b4ea9e28bfbace1d8d2f5b9c23249b4aa0e02e96633b4a2a1f1cbe08385f21
data/CHANGELOG.md ADDED
@@ -0,0 +1,49 @@
1
+ # Changelog
2
+
3
+ All notable changes to this gem are recorded in this file. The format is
4
+ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
5
+ project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-05-15
10
+
11
+ Initial release. Extracted from
12
+ [rubycentral/rubyconf-2026](https://github.com/rubycentral/rubyconf-2026)
13
+ after six PRs of in-repo iteration (#97, #105, #106, #109, #110, #112).
14
+
15
+ ### Added
16
+
17
+ - Build-time AVIF and WebP derivative generation via libvips at configurable
18
+ widths (defaults: 400, 600, 800, 1200, 1600).
19
+ - `picture_tag(src, alt:, sizes:, priority:, **attrs)` ERB helper for
20
+ responsive `<picture>` elements.
21
+ - `bg_image_block(src, breakpoint_only:, class_suffix:)` ERB helper that
22
+ emits an inline `<style>` block with `image-set(avif, webp)` declarations
23
+ plus Tailwind-breakpoint `@media` overrides.
24
+ - `bg_image_class(src, class_suffix:)` ERB helper that returns the derived
25
+ CSS class name without a `<style>` block, for cases that need the class
26
+ in multiple places after emitting the block once.
27
+ - `Inspector` (off by default) that walks rendered HTML and rewrites bare
28
+ `<img src="/images/...">` references into responsive `<picture>` tags.
29
+ - Per-derivative cache under `.bridgetown-cache/image_pipeline/`, keyed by
30
+ source SHA1 + gem version + config fingerprint.
31
+ - Configurable via initializer block:
32
+ ```ruby
33
+ init "bridgetown-image-pipeline" do
34
+ widths [400, 800, 1600]
35
+ auto_rewrite true
36
+ end
37
+ ```
38
+
39
+ ### Notes for adopters
40
+
41
+ - `output_dir` defaults to `_bridgetown/image_pipeline` (under Bridgetown's
42
+ framework-reserved prefix). Override via `output_dir: "your/path"` if you
43
+ need to preserve URLs from a prior in-repo plugin layout.
44
+ - `auto_rewrite` and `fail_on_missing` both default to `false` (conservative
45
+ defaults). Enable explicitly if you want the Inspector or strict
46
+ missing-manifest behaviour.
47
+
48
+ [Unreleased]: https://github.com/beflagrant/bridgetown-image-pipeline/compare/v0.1.0...HEAD
49
+ [0.1.0]: https://github.com/beflagrant/bridgetown-image-pipeline/releases/tag/v0.1.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Flagrant LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,241 @@
1
+ # bridgetown-image-pipeline
2
+
3
+ A Bridgetown 2.0+ plugin that pre-generates responsive **AVIF** and **WebP**
4
+ image derivatives at multiple widths, plus ERB helpers for `<picture>`
5
+ elements and CSS `image-set()` backgrounds.
6
+
7
+ Used in production at [rubyconf.org](https://rubyconf.org) — homepage mobile
8
+ Lighthouse perf score went from **0.47 → 0.93** after the bg-image migration.
9
+
10
+ ## Features
11
+
12
+ - **Build-time derivative generation** via libvips. AVIF + WebP at configurable
13
+ widths (default: 400, 600, 800, 1200, 1600). Source-width-aware (no upscaling).
14
+ - **`picture_tag` helper** — emits `<picture>` with `<source>` per format and
15
+ an `<img>` fallback with `srcset` + `sizes`.
16
+ - **`bg_image_block` helper** — emits an inline `<style>` block with
17
+ `background-image: image-set(...)` rules for backgrounds. Tailwind-breakpoint
18
+ aware. Drop-in replacement for `bg-[url(...)]` utilities.
19
+ - **`Inspector`** (optional, off by default) — rewrites bare `<img>` tags in
20
+ rendered HTML to wrap them in `<picture>` with the appropriate sources.
21
+ - **Per-derivative cache** keyed by source SHA1 + gem version + config
22
+ fingerprint. Rebuilds skip unchanged sources.
23
+
24
+ ## Requirements
25
+
26
+ - Ruby >= 3.2
27
+ - Bridgetown >= 2.0, < 3.0
28
+ - libvips with **HEIF/AVIF plugin** installed (see Installation below)
29
+
30
+ ## Installation
31
+
32
+ Add to your site's `Gemfile`:
33
+
34
+ ```ruby
35
+ gem "bridgetown-image-pipeline"
36
+ ```
37
+
38
+ Then `bundle install`.
39
+
40
+ ### libvips with HEIF/AVIF support
41
+
42
+ The plugin needs libvips compiled with HEIF support so it can encode AVIF.
43
+
44
+ **macOS (Homebrew):**
45
+
46
+ ```sh
47
+ brew install vips
48
+ ```
49
+
50
+ **Ubuntu / Debian:**
51
+
52
+ ```sh
53
+ sudo apt-get install libvips libvips-tools \
54
+ libheif1 libheif-dev libheif-plugin-aomenc libheif-plugin-libde265
55
+ ```
56
+
57
+ **Verify the encoder works:**
58
+
59
+ ```sh
60
+ vips --vips-config | tr ',' '\n' | grep -i heif
61
+ vips black /tmp/_check.avif 16 16 && rm /tmp/_check.avif
62
+ ```
63
+
64
+ Both lines should succeed. If the second fails with "cannot encode AVIF", the
65
+ libheif AV1 encoder plugin (`libheif-plugin-aomenc` on Ubuntu) is missing.
66
+
67
+ ### Activate the plugin
68
+
69
+ In `config/initializers.rb`:
70
+
71
+ ```ruby
72
+ Bridgetown.configure do |config|
73
+ init "bridgetown-image-pipeline"
74
+ end
75
+ ```
76
+
77
+ Or with overrides:
78
+
79
+ ```ruby
80
+ Bridgetown.configure do |config|
81
+ init "bridgetown-image-pipeline" do
82
+ widths [400, 800, 1200, 1600]
83
+ auto_rewrite true # enable the Inspector
84
+ output_dir "_bridgetown/image_pipeline" # default
85
+ end
86
+ end
87
+ ```
88
+
89
+ ## Usage
90
+
91
+ ### `picture_tag` — responsive `<picture>` elements
92
+
93
+ ```erb
94
+ <%= picture_tag "/images/hero.jpg",
95
+ alt: "Sandstone formations at sunset",
96
+ sizes: "(min-width: 1024px) 50vw, 100vw",
97
+ priority: true,
98
+ class: "w-full h-auto" %>
99
+ ```
100
+
101
+ Renders:
102
+
103
+ ```html
104
+ <picture>
105
+ <source type="image/avif" srcset="/_bridgetown/image_pipeline/hero-400.avif 400w, ..." sizes="...">
106
+ <source type="image/webp" srcset="/_bridgetown/image_pipeline/hero-400.webp 400w, ..." sizes="...">
107
+ <img src="/_bridgetown/image_pipeline/hero-1600.jpg"
108
+ srcset="..." sizes="..."
109
+ width="2400" height="1600"
110
+ alt="Sandstone formations at sunset"
111
+ loading="eager" decoding="async" fetchpriority="high"
112
+ class="w-full h-auto">
113
+ </picture>
114
+ ```
115
+
116
+ `priority: true` adds `loading="eager"` and `fetchpriority="high"`. Use for
117
+ LCP candidates only.
118
+
119
+ ### `bg_image_block` — responsive CSS backgrounds
120
+
121
+ For decorative `background-image` use cases. Emits an inline `<style>`
122
+ block; the caller uses the returned class on the element:
123
+
124
+ ```erb
125
+ <%= bg_image_block "/images/all-flora.jpg" %>
126
+ <section class="bg-img-all-flora bg-cover p-6 lg:p-20">
127
+ ...
128
+ </section>
129
+ ```
130
+
131
+ Class names are derived from the source basename:
132
+ `/images/all-flora.jpg` → `bg-img-all-flora`.
133
+
134
+ **Breakpoint-only backgrounds** (replicates Tailwind's `lg:bg-[url(...)]`):
135
+
136
+ ```erb
137
+ <%= bg_image_block "/images/hero.jpg", breakpoint_only: 1024 %>
138
+ <section class="bg-img-hero bg-cover"> <!-- bg only shows >=1024px -->
139
+ ```
140
+
141
+ **Same source in two contexts** (e.g. hero + decorative loop):
142
+
143
+ ```erb
144
+ <%= bg_image_block "/images/flowers.png", class_suffix: "hero" %>
145
+ <section class="bg-img-flowers-hero ...">
146
+
147
+ <%= bg_image_block "/images/flowers.png" %>
148
+ <div class="bg-img-flowers ...">
149
+ ```
150
+
151
+ The `class_suffix:` kwarg keeps the two `<style>` blocks from colliding.
152
+
153
+ ### `bg_image_class` — class name only, no style block
154
+
155
+ When you need to reference the class in multiple places after emitting the
156
+ block once:
157
+
158
+ ```erb
159
+ <%= bg_image_block "/images/flowers.png" %>
160
+ <% klass = bg_image_class("/images/flowers.png") %>
161
+ <section class="<%= klass %> ...">...</section>
162
+ <aside class="<%= klass %> ...">...</aside>
163
+ ```
164
+
165
+ ### `Inspector` — auto-wrap bare `<img>` tags
166
+
167
+ Off by default. Enable in your initializer:
168
+
169
+ ```ruby
170
+ init "bridgetown-image-pipeline" do
171
+ auto_rewrite true
172
+ end
173
+ ```
174
+
175
+ When on, any rendered `<img src="/images/foo.jpg">` whose source is in the
176
+ manifest is rewritten as a `<picture>` with the appropriate sources.
177
+ Opt-out on a per-tag basis with `data-no-pipeline`:
178
+
179
+ ```html
180
+ <img src="/images/exact-bytes-required.jpg" data-no-pipeline>
181
+ ```
182
+
183
+ ## Configuration
184
+
185
+ All options, with defaults:
186
+
187
+ | Option | Default | Description |
188
+ |--------|---------|-------------|
189
+ | `source_globs` | `["src/images/**/*.{jpg,jpeg,png}"]` | What to process. **Must live under `src/`** — public URLs are derived by stripping the `src/` prefix, so `src/images/foo.jpg` becomes `/images/foo.jpg` in `picture_tag` lookups. Sources outside `src/` are processed but unreachable from templates. |
190
+ | `exclude` | `[]` | Glob patterns to skip |
191
+ | `widths` | `[400, 600, 800, 1200, 1600]` | Derivative widths |
192
+ | `formats` | `[:avif, :webp]` | Output formats (plus original) |
193
+ | `output_dir` | `"_bridgetown/image_pipeline"` | Output path under `output/` |
194
+ | `quality` | `{ avif: 65, webp: 88, jpeg: 88 }` | Per-format quality |
195
+ | `auto_rewrite` | `false` | Enable the Inspector |
196
+ | `fail_on_missing` | `false` | Raise vs. warn on missing manifest |
197
+ | `breakpoints` | `{ 640 => 400, 768 => 600, 1024 => 800, 1280 => 1200 }` | Tailwind-style breakpoints for `bg_image_block` |
198
+ | `default_width` | `1600` | Default tier for `bg_image_block`'s un-prefixed rule |
199
+
200
+ ## Gotcha: Tailwind v4 and `bg-[url(...)]` in docs
201
+
202
+ Tailwind v4 auto-scans every file from the CSS entrypoint's parent dir up to
203
+ the git root. If your repo has Markdown documentation that quotes raw
204
+ Tailwind classes like `bg-[url(...)]` (e.g. in a README that explains a
205
+ migration from the old utility to this plugin), Tailwind will pick those
206
+ literal strings up and try to emit CSS rules for them. esbuild will then
207
+ fail to resolve the `...` placeholder URL, breaking your build.
208
+
209
+ Fix: add `@source not` directives to your Tailwind CSS:
210
+
211
+ ```css
212
+ @import "tailwindcss";
213
+ @source not "../../docs/**";
214
+ @source not "../../test/**";
215
+ ```
216
+
217
+ ## Architecture
218
+
219
+ See [`docs/INTERNALS.md`](docs/INTERNALS.md) for the design spec and
220
+ [`docs/adr/`](docs/adr/) for the architecture decision records covering
221
+ the helper API, output paths, defaults, and cache-key invariants.
222
+
223
+ ## Development
224
+
225
+ ```sh
226
+ bin/setup
227
+ bundle exec rake test
228
+ bundle exec rake rubocop
229
+ ```
230
+
231
+ CI runs against Ruby 3.2/3.3/3.4 × Bridgetown 2.0/edge.
232
+
233
+ ## License
234
+
235
+ MIT — see [`LICENSE.txt`](LICENSE.txt).
236
+
237
+ ## Status
238
+
239
+ Maintained by [Flagrant](https://beflagrant.com) for use in production at
240
+ [rubyconf.org](https://rubyconf.org). No SLA. Issues and PRs welcome at
241
+ [github.com/beflagrant/bridgetown-image-pipeline](https://github.com/beflagrant/bridgetown-image-pipeline).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]