jekyll-compress-images 1.3 → 1.4
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 +4 -4
- data/README.md +56 -3
- data/lib/jekyll-compress-images.rb +252 -19
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 704a828325e4c1d9396206750da62503785ac90033f86d8d36429d3a5ab04a59
|
|
4
|
+
data.tar.gz: 657104fb5242f0a3c5948278b4718762d41cc2d73c5423414205ade8e3d7eb2a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 86ae385b510546688f9e72709c9f8f10b1f60c0e4b09bad40cc1fca06c5b3a39efc95262f704b2c0a98880225444a10a049a24228c76360766d0acf56099f65a
|
|
7
|
+
data.tar.gz: 29e08242a343da1a4476324336700ab579a20e039de62db5241a39740c04636c282d4988e0e11d8c860ded80709c3b9900aa864987f9cf88f6cb8f83310264e6
|
data/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# jekyll-compress-images
|
|
5
5
|
|
|
6
|
-
Plugin for compressing/optimizing images (jpg, png, gif, svg).
|
|
6
|
+
Plugin for compressing/optimizing images (jpg, png, gif, svg), and optionally creating WebP and AVIF versions.
|
|
7
7
|
|
|
8
8
|
# Installation
|
|
9
9
|
|
|
@@ -40,8 +40,60 @@ compress_images:
|
|
|
40
40
|
images_path: "assets/img/**/*.{gif,png,jpg,jpeg,svg}" # relative to your site source
|
|
41
41
|
cache_file: "_compress_images_cache.yml" # remembers which images are already optimized
|
|
42
42
|
threads: 8 # defaults to the number of CPU cores
|
|
43
|
+
overwrite_originals: true # false = only the published images in _site are compressed
|
|
44
|
+
webp: false # create .webp versions of jpg/png images
|
|
45
|
+
webp_quality: 80
|
|
46
|
+
avif: false # create .avif versions of jpg/png images
|
|
47
|
+
avif_quality: 60
|
|
43
48
|
```
|
|
44
49
|
|
|
50
|
+
## Keep your original images
|
|
51
|
+
|
|
52
|
+
By default, images are compressed **in place**, in your source folder. If you'd rather keep high-quality originals in your repo and only publish compressed ones, set:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
compress_images:
|
|
56
|
+
overwrite_originals: false
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Compressed copies are kept in `.jekyll-cache/compress-images/`, so only the first build is slow. Your source images are never touched, and `_compress_images_cache.yml` isn't needed.
|
|
60
|
+
|
|
61
|
+
## WebP and AVIF
|
|
62
|
+
|
|
63
|
+
WebP and AVIF are usually much smaller than JPG/PNG. Turn them on with:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
compress_images:
|
|
67
|
+
webp: true
|
|
68
|
+
avif: true
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This needs `cwebp` and `avifenc` (version 1.0 or newer) installed:
|
|
72
|
+
|
|
73
|
+
- macOS: `brew install webp libavif`
|
|
74
|
+
- Ubuntu 24.04+/Debian 13+ (and GitHub Actions `ubuntu-latest`): `sudo apt-get install webp libavif-bin`
|
|
75
|
+
|
|
76
|
+
For every jpg/png, a `.webp` and `.avif` file is added next to it in `_site`, e.g. `hero.jpg.webp`. If one comes out bigger than the original, it's skipped. Your source folder isn't touched.
|
|
77
|
+
|
|
78
|
+
Browsers only use these files if your HTML asks for them, so use the `picture` tag instead of `<img>`:
|
|
79
|
+
|
|
80
|
+
```liquid
|
|
81
|
+
{% picture assets/img/hero.jpg alt="Hero" class="cover" loading="lazy" %}
|
|
82
|
+
{% picture {{ page.image }} alt="{{ page.title }}" %}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
which becomes:
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<picture>
|
|
89
|
+
<source srcset="/assets/img/hero.jpg.avif" type="image/avif">
|
|
90
|
+
<source srcset="/assets/img/hero.jpg.webp" type="image/webp">
|
|
91
|
+
<img src="/assets/img/hero.jpg" alt="Hero" class="cover" loading="lazy">
|
|
92
|
+
</picture>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Anything after the image path is added to the `<img>`. Browsers that don't support AVIF/WebP use the original image. If you already use another plugin with a `picture` tag (like jekyll_picture_tag), that one is kept.
|
|
96
|
+
|
|
45
97
|
## image_optim options
|
|
46
98
|
|
|
47
99
|
You can pass [image_optim](https://github.com/toy/image_optim) options by using:
|
|
@@ -53,7 +105,7 @@ imageoptim:
|
|
|
53
105
|
verbose: false
|
|
54
106
|
```
|
|
55
107
|
|
|
56
|
-
SVG optimization uses [svgo](https://github.com/svg/svgo), which isn't bundled.
|
|
108
|
+
SVG optimization uses [svgo](https://github.com/svg/svgo), which isn't bundled. It's used automatically when it's installed (`npm install -g svgo`).
|
|
57
109
|
|
|
58
110
|
# Usage
|
|
59
111
|
|
|
@@ -61,8 +113,9 @@ On `jekyll serve` or `jekyll build`, compression will run. If your images are al
|
|
|
61
113
|
|
|
62
114
|
Good to know:
|
|
63
115
|
|
|
64
|
-
- Images are optimized **in place**, in your source folder. Commit them afterwards.
|
|
116
|
+
- Images are optimized **in place**, in your source folder (unless you set `overwrite_originals: false`). Commit them afterwards.
|
|
65
117
|
- Commit `_compress_images_cache.yml` too, so other machines (and CI) know which images are already done.
|
|
118
|
+
- Each build prints a summary like `Optimized 12 images, saved 3.4 MB`. Run with `--verbose` to see every image.
|
|
66
119
|
- If you replace an image with a new file, it will be optimized again automatically.
|
|
67
120
|
|
|
68
121
|
# Development
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
require "cgi"
|
|
1
2
|
require "digest"
|
|
2
3
|
require "etc"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "open3"
|
|
3
6
|
require "pathname"
|
|
7
|
+
require "set"
|
|
4
8
|
require "yaml"
|
|
5
9
|
require "image_optim"
|
|
6
10
|
require "image_optim_pack"
|
|
@@ -13,52 +17,248 @@ module Jekyll
|
|
|
13
17
|
LOG_TOPIC = "CompressImages:".freeze
|
|
14
18
|
|
|
15
19
|
DEFAULT_OPTIONS = {
|
|
16
|
-
"cache_file"
|
|
17
|
-
"images_path"
|
|
18
|
-
"threads"
|
|
20
|
+
"cache_file" => "_compress_images_cache.yml",
|
|
21
|
+
"images_path" => "assets/img/**/*.{gif,png,jpg,jpeg,svg}",
|
|
22
|
+
"threads" => Etc.nprocessors,
|
|
23
|
+
"overwrite_originals" => true,
|
|
24
|
+
"webp" => false,
|
|
25
|
+
"webp_quality" => 80,
|
|
26
|
+
"avif" => false,
|
|
27
|
+
"avif_quality" => 60
|
|
19
28
|
}.freeze
|
|
20
29
|
|
|
21
30
|
DEFAULT_IMAGEOPTIM_OPTIONS = {
|
|
22
31
|
"pngout" => false,
|
|
23
|
-
"svgo" => true,
|
|
24
32
|
"verbose" => false
|
|
25
33
|
}.freeze
|
|
26
34
|
|
|
35
|
+
# Extra formats we can create, the tool that makes them and how to install it
|
|
36
|
+
VARIANTS = {
|
|
37
|
+
"webp" => { "tool" => "cwebp", "install" => "brew install webp / apt install webp" },
|
|
38
|
+
"avif" => { "tool" => "avifenc", "install" => "brew install libavif / apt install libavif-bin" }
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
# Formats cwebp and avifenc can read
|
|
42
|
+
CONVERTIBLE = %w[.jpg .jpeg .png].freeze
|
|
43
|
+
|
|
27
44
|
def generate(site)
|
|
28
45
|
@site = site
|
|
29
46
|
@config = DEFAULT_OPTIONS.merge(site.config["compress_images"] || {})
|
|
30
|
-
@
|
|
47
|
+
@mutex = Mutex.new
|
|
48
|
+
@stats = { "optimized" => 0, "saved" => 0, "created" => 0 }
|
|
49
|
+
@used_cache_files = Set.new
|
|
50
|
+
@digests = {}
|
|
31
51
|
|
|
32
52
|
images = Dir.glob(File.expand_path(@config["images_path"], site.source)).select { |path| File.file?(path) }.sort
|
|
53
|
+
|
|
54
|
+
# Look these up before optimize_copies points them at the cache folder
|
|
55
|
+
files = published_files(images)
|
|
56
|
+
|
|
57
|
+
if @config["overwrite_originals"]
|
|
58
|
+
optimize_originals(images)
|
|
59
|
+
else
|
|
60
|
+
optimize_copies(files)
|
|
61
|
+
end
|
|
62
|
+
create_variants(files)
|
|
63
|
+
clean_cache_dir
|
|
64
|
+
log_summary
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# Optimizes images in the source folder and remembers them in the cache file
|
|
70
|
+
def optimize_originals(images)
|
|
71
|
+
@cache_path = File.expand_path(@config["cache_file"], site.source)
|
|
33
72
|
@original_cache = load_cache
|
|
34
73
|
# Only keep entries for images that still exist, so deleted images drop out of the cache
|
|
35
74
|
@cache = @original_cache.select { |key, _| images.any? { |path| relative(path) == key } }
|
|
36
|
-
@mutex = Mutex.new
|
|
37
75
|
|
|
38
76
|
pending = images.reject { |path| @cache[relative(path)] == digest(path) }
|
|
39
|
-
pending
|
|
77
|
+
in_parallel(pending) do |path|
|
|
78
|
+
safely("optimize", path) do
|
|
79
|
+
optimize(path)
|
|
80
|
+
# Store the digest of the optimized file, so we only run again when the image is replaced
|
|
81
|
+
new_digest = digest(path)
|
|
82
|
+
@mutex.synchronize { @cache[relative(path)] = new_digest }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
40
85
|
ensure
|
|
41
86
|
save_cache if @cache
|
|
42
87
|
end
|
|
43
88
|
|
|
44
|
-
|
|
89
|
+
# Leaves the originals untouched. Optimized copies live in the cache folder
|
|
90
|
+
# and Jekyll publishes them instead of the originals.
|
|
91
|
+
def optimize_copies(files)
|
|
92
|
+
pending = files.keys.reject { |path| File.file?(cached_path(path, File.extname(path))) }
|
|
93
|
+
pending = pending.uniq { |path| cached_path(path, File.extname(path)) }
|
|
45
94
|
|
|
46
|
-
|
|
47
|
-
|
|
95
|
+
in_parallel(pending) do |path|
|
|
96
|
+
safely("optimize", path) do
|
|
97
|
+
write_atomically(cached_path(path, File.extname(path))) do |tmp|
|
|
98
|
+
FileUtils.cp(path, tmp)
|
|
99
|
+
optimize(tmp, path)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
files.each do |path, file|
|
|
105
|
+
copy = cached_path(path, File.extname(path))
|
|
106
|
+
file.define_singleton_method(:path) { copy } if File.file?(copy)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Creates .webp/.avif files next to the images, e.g. hero.jpg -> hero.jpg.webp
|
|
111
|
+
def create_variants(files)
|
|
112
|
+
formats = VARIANTS.keys.select { |format| @config[format] && tool_installed?(format) }
|
|
113
|
+
return if formats.empty?
|
|
114
|
+
|
|
115
|
+
images = files.keys.select { |path| CONVERTIBLE.include?(File.extname(path).downcase) }
|
|
116
|
+
jobs = images.product(formats)
|
|
117
|
+
pending = jobs.reject { |path, format| File.file?(cached_path(path, ".#{format}")) || File.file?(skip_marker(path, format)) }
|
|
118
|
+
pending = pending.uniq { |path, format| cached_path(path, ".#{format}") }
|
|
119
|
+
|
|
120
|
+
in_parallel(pending) do |path, format|
|
|
121
|
+
safely("create #{format} for", path) { convert(path, format) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
jobs.each do |path, format|
|
|
125
|
+
variant = cached_path(path, ".#{format}")
|
|
126
|
+
next unless File.file?(variant)
|
|
127
|
+
|
|
128
|
+
file = StaticFile.new(site, site.source, File.dirname("/#{relative(path)}"), "#{File.basename(path)}.#{format}")
|
|
129
|
+
file.define_singleton_method(:path) { variant }
|
|
130
|
+
site.static_files << file
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def convert(path, format)
|
|
135
|
+
target = cached_path(path, ".#{format}")
|
|
136
|
+
write_atomically(target) do |tmp|
|
|
137
|
+
output, status = Open3.capture2e(*convert_command(format, path, tmp))
|
|
138
|
+
raise output.strip unless status.success?
|
|
139
|
+
|
|
140
|
+
# Not worth publishing if it's bigger than the original, the picture tag then falls back to it
|
|
141
|
+
if File.size(tmp) >= File.size(path)
|
|
142
|
+
FileUtils.touch(skip_marker(path, format))
|
|
143
|
+
FileUtils.rm_f(tmp)
|
|
144
|
+
else
|
|
145
|
+
Jekyll.logger.debug LOG_TOPIC, "Created #{relative(path)}.#{format}"
|
|
146
|
+
@mutex.synchronize { @stats["created"] += 1 }
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def convert_command(format, input, output)
|
|
152
|
+
quality = @config["#{format}_quality"].to_s
|
|
153
|
+
case format
|
|
154
|
+
when "webp" then ["cwebp", "-quiet", "-q", quality, "-metadata", "icc", input, "-o", output]
|
|
155
|
+
when "avif" then ["avifenc", "-q", quality, "-s", "6", "-j", "all", input, output]
|
|
156
|
+
end
|
|
48
157
|
end
|
|
49
158
|
|
|
50
|
-
def optimize(path)
|
|
159
|
+
def optimize(path, original = path)
|
|
51
160
|
size_before = File.size(path)
|
|
52
161
|
image_optim.optimize_image!(path)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
162
|
+
saved = size_before - File.size(path)
|
|
163
|
+
Jekyll.logger.debug LOG_TOPIC, "Optimized #{relative(original)} (-#{percent(saved, size_before)}%)"
|
|
164
|
+
@mutex.synchronize do
|
|
165
|
+
@stats["optimized"] += 1
|
|
166
|
+
@stats["saved"] += saved
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def site
|
|
171
|
+
@site
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def image_optim
|
|
175
|
+
@image_optim ||= ImageOptim.new(imageoptim_options)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# svgo isn't bundled, so only turn it on when it's installed (unless the site config says otherwise)
|
|
179
|
+
def imageoptim_options
|
|
180
|
+
DEFAULT_IMAGEOPTIM_OPTIONS.merge("svgo" => executable?("svgo") || ENV.key?("SVGO_BIN"))
|
|
181
|
+
.merge(site.config["imageoptim"] || {})
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Static files Jekyll will publish at their usual URL, by source path
|
|
185
|
+
def published_files(images)
|
|
186
|
+
by_path = images.to_h { |path| [path, nil] }
|
|
187
|
+
site.static_files.each_with_object({}) do |file, found|
|
|
188
|
+
path = File.expand_path(file.path)
|
|
189
|
+
found[path] = file if by_path.key?(path) && file.url == "/#{relative(path)}"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def in_parallel(items, &block)
|
|
194
|
+
items.in_threads([@config["threads"].to_i, 1].max).each(&block)
|
|
195
|
+
end
|
|
56
196
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@mutex.synchronize { @cache[relative(path)] = new_digest }
|
|
197
|
+
def safely(action, path)
|
|
198
|
+
yield
|
|
60
199
|
rescue StandardError => e
|
|
61
|
-
Jekyll.logger.warn LOG_TOPIC, "Could not
|
|
200
|
+
Jekyll.logger.warn LOG_TOPIC, "Could not #{action} #{relative(path)}: #{e.message}"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Writes to a temporary file first, so an interrupted build never leaves a half-done file in the cache
|
|
204
|
+
def write_atomically(target)
|
|
205
|
+
FileUtils.mkdir_p(File.dirname(target))
|
|
206
|
+
tmp = "#{target}.tmp#{File.extname(target)}"
|
|
207
|
+
yield tmp
|
|
208
|
+
File.rename(tmp, target) if File.file?(tmp)
|
|
209
|
+
ensure
|
|
210
|
+
FileUtils.rm_f(tmp) if tmp
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def cache_dir
|
|
214
|
+
@cache_dir ||= File.join(File.expand_path(site.config["cache_dir"] || ".jekyll-cache", site.source), "compress-images")
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def cached_path(path, extension)
|
|
218
|
+
@digests[path] ||= digest(path)
|
|
219
|
+
File.join(cache_dir, "#{@digests[path]}#{extension}").tap { |file| @used_cache_files << file }
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def skip_marker(path, format)
|
|
223
|
+
"#{cached_path(path, ".#{format}")}.skip".tap { |file| @used_cache_files << file }
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def clean_cache_dir
|
|
227
|
+
return unless File.directory?(cache_dir)
|
|
228
|
+
|
|
229
|
+
(Dir.glob(File.join(cache_dir, "*")).to_set - @used_cache_files).each { |file| FileUtils.rm_f(file) }
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def tool_installed?(format)
|
|
233
|
+
tool = VARIANTS[format]["tool"]
|
|
234
|
+
return true if executable?(tool)
|
|
235
|
+
|
|
236
|
+
Jekyll.logger.warn LOG_TOPIC, "#{format} is enabled but `#{tool}` isn't installed (#{VARIANTS[format]["install"]})"
|
|
237
|
+
false
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def executable?(name)
|
|
241
|
+
ENV["PATH"].to_s.split(File::PATH_SEPARATOR).any? { |dir| File.executable?(File.join(dir, name)) }
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def log_summary
|
|
245
|
+
if @stats["optimized"].positive?
|
|
246
|
+
Jekyll.logger.info LOG_TOPIC, "Optimized #{@stats["optimized"]} #{@stats["optimized"] == 1 ? "image" : "images"}, saved #{human_size(@stats["saved"])}"
|
|
247
|
+
end
|
|
248
|
+
return unless @stats["created"].positive?
|
|
249
|
+
|
|
250
|
+
Jekyll.logger.info LOG_TOPIC, "Created #{@stats["created"]} WebP/AVIF #{@stats["created"] == 1 ? "file" : "files"}"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def human_size(bytes)
|
|
254
|
+
return "#{bytes} B" if bytes < 1024
|
|
255
|
+
return "#{(bytes / 1024.0).round(1)} KB" if bytes < 1024 * 1024
|
|
256
|
+
|
|
257
|
+
"#{(bytes / 1024.0 / 1024).round(1)} MB"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def percent(part, whole)
|
|
261
|
+
whole.zero? ? 0 : (part * 100.0 / whole).round(1)
|
|
62
262
|
end
|
|
63
263
|
|
|
64
264
|
def digest(path)
|
|
@@ -66,7 +266,7 @@ module Jekyll
|
|
|
66
266
|
end
|
|
67
267
|
|
|
68
268
|
def relative(path)
|
|
69
|
-
Pathname.new(path).relative_path_from(Pathname.new(
|
|
269
|
+
Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
|
|
70
270
|
end
|
|
71
271
|
|
|
72
272
|
def load_cache
|
|
@@ -86,4 +286,37 @@ module Jekyll
|
|
|
86
286
|
File.write(@cache_path, @cache.sort.to_h.to_yaml)
|
|
87
287
|
end
|
|
88
288
|
end
|
|
289
|
+
|
|
290
|
+
# {% picture assets/img/hero.jpg alt="Hero" class="cover" %}
|
|
291
|
+
# Uses the AVIF/WebP versions when they exist, and falls back to the original image
|
|
292
|
+
class CompressImagesPictureTag < Liquid::Tag
|
|
293
|
+
def initialize(tag_name, markup, tokens)
|
|
294
|
+
super
|
|
295
|
+
@markup = markup
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def render(context)
|
|
299
|
+
site = context.registers[:site]
|
|
300
|
+
src, attributes = Liquid::Template.parse(@markup).render(context).strip.split(/\s+/, 2)
|
|
301
|
+
raise ArgumentError, "{% picture %} needs an image path, e.g. {% picture assets/img/hero.jpg alt=\"Hero\" %}" if src.to_s.empty?
|
|
302
|
+
|
|
303
|
+
url = "/#{src.sub(%r!\A/+!, "")}"
|
|
304
|
+
published = context.registers[:compress_images_urls] ||= site.static_files.map(&:url).to_set
|
|
305
|
+
sources = %w[avif webp].select { |format| published.include?("#{url}.#{format}") }.map do |format|
|
|
306
|
+
%(<source srcset="#{html_url(site, "#{url}.#{format}")}" type="image/#{format}">)
|
|
307
|
+
end
|
|
308
|
+
img = %(<img src="#{html_url(site, url)}"#{" #{attributes}" if attributes}>)
|
|
309
|
+
|
|
310
|
+
sources.empty? ? img : "<picture>#{sources.join}#{img}</picture>"
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
private
|
|
314
|
+
|
|
315
|
+
def html_url(site, url)
|
|
316
|
+
CGI.escapeHTML("#{site.config["baseurl"].to_s.chomp("/")}#{url}".gsub(" ", "%20"))
|
|
317
|
+
end
|
|
318
|
+
end
|
|
89
319
|
end
|
|
320
|
+
|
|
321
|
+
# Don't take over the tag if another plugin (like jekyll_picture_tag) already registered it
|
|
322
|
+
Liquid::Template.register_tag("picture", Jekyll::CompressImagesPictureTag) unless Liquid::Template.tags["picture"]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-compress-images
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.4'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Valerija Spasojevic
|
|
@@ -76,14 +76,14 @@ dependencies:
|
|
|
76
76
|
name: minitest
|
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
|
-
- - "
|
|
79
|
+
- - ">="
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '5.0'
|
|
82
82
|
type: :development
|
|
83
83
|
prerelease: false
|
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
|
-
- - "
|
|
86
|
+
- - ">="
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
88
|
version: '5.0'
|
|
89
89
|
- !ruby/object:Gem::Dependency
|
|
@@ -116,6 +116,7 @@ licenses:
|
|
|
116
116
|
metadata:
|
|
117
117
|
source_code_uri: https://github.com/valerijaspasojevic/jekyll-compress-images
|
|
118
118
|
changelog_uri: https://github.com/valerijaspasojevic/jekyll-compress-images/blob/master/CHANGELOG.md
|
|
119
|
+
rubygems_mfa_required: 'true'
|
|
119
120
|
post_install_message:
|
|
120
121
|
rdoc_options: []
|
|
121
122
|
require_paths:
|
|
@@ -134,5 +135,6 @@ requirements: []
|
|
|
134
135
|
rubygems_version: 3.4.15
|
|
135
136
|
signing_key:
|
|
136
137
|
specification_version: 4
|
|
137
|
-
summary: Jekyll plugin for
|
|
138
|
+
summary: Jekyll plugin for compressing images (jpg, png, gif, svg), with optional
|
|
139
|
+
WebP and AVIF versions
|
|
138
140
|
test_files: []
|