jekyll-minify 1.0.5
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +12 -0
- data/lib/jekyll-minify.rb +488 -0
- metadata +152 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 409c97918c1a1c325caa714ec5e6cd1140ed77acdf46489b40b9e58811ce1ce8
|
|
4
|
+
data.tar.gz: 61fdea389bd22b6e3df9e9164fc0fe3dad957dd99926cb786b2af49b41a5d3ce
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 590d7cfae1b729be179d2eacfa9e55322bcdb1c8c31c93abd57bb15ea41759620895e6994cf506bd12799374a6c118f5c22af8571fbd34e798bcdb9c6fe5147d
|
|
7
|
+
data.tar.gz: 4aaf03d60659ec0f7540dcf33041dacd5dbbc473ae4f085514febe3702e5979513d669bfb9db4dc6642c25e4078e6277b54d37b4c2670508f3e7971cacb5b993
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alain Reguera Delgado
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# jekyll-minify
|
|
2
|
+
|
|
3
|
+
Automatically minifies HTML, CSS, and JavaScript files for Jekyll sites using
|
|
4
|
+
the Rust-based `minify-html` gem.
|
|
5
|
+
|
|
6
|
+
## Documentation
|
|
7
|
+
|
|
8
|
+
For complete documentation, configuration options, and examples, visit the [documentation site](https://centos.gitlab.io/artwork/centos-web/jekyll-minify/).
|
|
9
|
+
|
|
10
|
+
## License
|
|
11
|
+
|
|
12
|
+
MIT License - see LICENSE file for details.
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Jekyll plugin, `jekyll-minify`, automatically minifies HTML, CSS, and
|
|
4
|
+
# JavaScript files generated by Jekyll to reduce their file size and improve page
|
|
5
|
+
# load performance. It runs after the entire site is built and processes all output
|
|
6
|
+
# files in the destination directory.
|
|
7
|
+
#
|
|
8
|
+
# === How It Works ===
|
|
9
|
+
#
|
|
10
|
+
# The plugin uses a post-write hook that runs after Jekyll finishes writing all
|
|
11
|
+
# files to the output directory (public/). It then traverses the directory tree
|
|
12
|
+
# and minifies files using the minify-html gem (Rust-based minifier):
|
|
13
|
+
#
|
|
14
|
+
# - **HTML Minification:** Removes comments, whitespace, and optional tags using
|
|
15
|
+
# proper HTML parsing. Expected reduction: 10-25%
|
|
16
|
+
#
|
|
17
|
+
# - **CSS Minification:** Removes comments and whitespace from inline and standalone
|
|
18
|
+
# CSS files. Skips pre-minified files (*.min.css).
|
|
19
|
+
# Expected reduction: 10-20%
|
|
20
|
+
#
|
|
21
|
+
# - **JavaScript Minification:** Removes comments and unnecessary whitespace from
|
|
22
|
+
# inline and standalone JavaScript files using proper parsing. Skips pre-minified
|
|
23
|
+
# files (*.min.js). Renames minified files to *.min.js. Expected reduction: 20-30%
|
|
24
|
+
#
|
|
25
|
+
# === Error Handling ===
|
|
26
|
+
#
|
|
27
|
+
# The plugin is designed to fail gracefully:
|
|
28
|
+
# - If minification fails for any file, the original file is preserved
|
|
29
|
+
# - Errors are logged as warnings but do not break the build
|
|
30
|
+
# - Statistics are still displayed for successfully minified files
|
|
31
|
+
#
|
|
32
|
+
# === Performance ===
|
|
33
|
+
#
|
|
34
|
+
# Processing typically adds less than 1 second to the total build time. The
|
|
35
|
+
# minify-html gem (Rust-based) provides efficient processing.
|
|
36
|
+
#
|
|
37
|
+
# === Requirements ===
|
|
38
|
+
#
|
|
39
|
+
# This plugin requires the 'minify_html' gem. Install with:
|
|
40
|
+
# gem install minify_html
|
|
41
|
+
#
|
|
42
|
+
# === Configuration ===
|
|
43
|
+
#
|
|
44
|
+
# The plugin is enabled by default. You can control it via _config.yml:
|
|
45
|
+
#
|
|
46
|
+
# with_minify: false # disable minification (defaults to true)
|
|
47
|
+
# with_minify_data: # override minify-html options
|
|
48
|
+
# remove_bangs: false # preserve IE conditional comments
|
|
49
|
+
# minify_css: false # disable CSS minification
|
|
50
|
+
# minify_js: false # disable JS minification
|
|
51
|
+
#
|
|
52
|
+
# Defaults to all 7 minify-html options enabled. YAML string keys are automatically
|
|
53
|
+
# converted to symbols for the minify-html gem.
|
|
54
|
+
#
|
|
55
|
+
# === W3C HTML Validation Compatibility ===
|
|
56
|
+
#
|
|
57
|
+
# This plugin uses minify-html's built-in `ensure_spec_compliant_unquoted_attribute_values`
|
|
58
|
+
# option to maintain W3C validation compliance. This preserves attribute quotes on values
|
|
59
|
+
# containing special characters, preventing unquoting issues with attributes like:
|
|
60
|
+
# prefix="og: https://ogp.me/ns#"
|
|
61
|
+
#
|
|
62
|
+
# === CSS/JavaScript Minification Note ===
|
|
63
|
+
#
|
|
64
|
+
# The minify-html Ruby gem only exposes HTML minification through its public API.
|
|
65
|
+
# CSS and JavaScript minification are supported internally through wrapper tags:
|
|
66
|
+
# CSS is wrapped in `<style>` tags and JS in `<script>` tags for minification,
|
|
67
|
+
# then extracted. This is the only approach supported by the gem's design.
|
|
68
|
+
|
|
69
|
+
require 'jekyll'
|
|
70
|
+
require 'pathname'
|
|
71
|
+
|
|
72
|
+
begin
|
|
73
|
+
require 'minify_html'
|
|
74
|
+
rescue LoadError
|
|
75
|
+
raise Jekyll::Errors::FatalException, "Missing required gem 'minify_html'. " \
|
|
76
|
+
'Install with: gem install minify_html'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
module Jekyll
|
|
80
|
+
# Jekyll plugin for automatic minification of HTML, CSS, and JavaScript.
|
|
81
|
+
module Minify
|
|
82
|
+
# Minifies HTML, CSS, and JS files in the Jekyll destination directory.
|
|
83
|
+
#
|
|
84
|
+
# Runs as a Jekyll post-write hook. Processes all eligible files after the
|
|
85
|
+
# site build completes. Files that fail minification are preserved unchanged.
|
|
86
|
+
#
|
|
87
|
+
# @example Disable minification in development
|
|
88
|
+
# # _config-dev.yml
|
|
89
|
+
# with_minify: false
|
|
90
|
+
#
|
|
91
|
+
# @example Override minify-html options for production
|
|
92
|
+
# # _config.yml
|
|
93
|
+
# with_minify: true
|
|
94
|
+
# with_minify_data:
|
|
95
|
+
# remove_bangs: false
|
|
96
|
+
# minify_css: false
|
|
97
|
+
class Minifier
|
|
98
|
+
# Default options passed to the minify-html gem.
|
|
99
|
+
# User overrides from `with_minify_data` in _config.yml are merged over these.
|
|
100
|
+
#
|
|
101
|
+
# @return [Hash{Symbol => Boolean}]
|
|
102
|
+
DEFAULT_MINIFY_OPTIONS = {
|
|
103
|
+
keep_html_and_head_opening_tags: true,
|
|
104
|
+
keep_closing_tags: true,
|
|
105
|
+
minify_css: true,
|
|
106
|
+
minify_js: true,
|
|
107
|
+
remove_bangs: true,
|
|
108
|
+
remove_processing_instructions: true,
|
|
109
|
+
ensure_spec_compliant_unquoted_attribute_values: true
|
|
110
|
+
}.freeze
|
|
111
|
+
|
|
112
|
+
# Asset types processed by the minifier, in order.
|
|
113
|
+
# Each entry has :type (Symbol) and :display_name (String) for logging.
|
|
114
|
+
# @return [Array<Hash>]
|
|
115
|
+
ASSET_TYPES = [
|
|
116
|
+
{ type: :html, display_name: 'HTML' },
|
|
117
|
+
{ type: :css, display_name: 'CSS' },
|
|
118
|
+
{ type: :js, display_name: 'JS' }
|
|
119
|
+
].freeze
|
|
120
|
+
|
|
121
|
+
# Initializes a new Minifier with empty statistics and a monotonic start time.
|
|
122
|
+
def initialize
|
|
123
|
+
@minify_options = DEFAULT_MINIFY_OPTIONS
|
|
124
|
+
@stats = {
|
|
125
|
+
html: { count: 0, original_bytes: 0, minified_bytes: 0 },
|
|
126
|
+
css: { count: 0, original_bytes: 0, minified_bytes: 0 },
|
|
127
|
+
js: { count: 0, original_bytes: 0, minified_bytes: 0 },
|
|
128
|
+
errors: []
|
|
129
|
+
}
|
|
130
|
+
@start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Minifies all eligible files in the site destination directory.
|
|
134
|
+
#
|
|
135
|
+
# @param site [Jekyll::Site] the Jekyll site object
|
|
136
|
+
# @return [void]
|
|
137
|
+
def self.minify_site(site)
|
|
138
|
+
new.run(site)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Runs the minification pipeline for the given site.
|
|
142
|
+
# Reads configuration from site.config:
|
|
143
|
+
# - with_minify (Boolean, defaults to true): enable/disable the plugin
|
|
144
|
+
# - with_minify_data (Hash, optional): override minify-html options
|
|
145
|
+
#
|
|
146
|
+
# Returns immediately if with_minify is false or the destination directory does not exist.
|
|
147
|
+
#
|
|
148
|
+
# @param site [Jekyll::Site] the Jekyll site object providing dest and config
|
|
149
|
+
# @return [void]
|
|
150
|
+
def run(site)
|
|
151
|
+
unless plugin_enabled?(site.config)
|
|
152
|
+
Jekyll.logger.info 'jekyll-minify:', 'Minification disabled (with_minify: false).'
|
|
153
|
+
return
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
@minify_options = build_minify_options(site.config)
|
|
157
|
+
|
|
158
|
+
dest = Pathname.new(site.dest)
|
|
159
|
+
return unless dest.directory?
|
|
160
|
+
|
|
161
|
+
Jekyll.logger.info 'jekyll-minify:', 'Starting minification...'
|
|
162
|
+
|
|
163
|
+
minify_each_file(dest)
|
|
164
|
+
log_statistics
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
# Returns false if the site config explicitly disables the plugin.
|
|
170
|
+
# Defaults to true when the key is absent (matches with_link_decorator convention).
|
|
171
|
+
#
|
|
172
|
+
# @param site_config [Hash] Jekyll site configuration hash (site.config)
|
|
173
|
+
# @return [Boolean]
|
|
174
|
+
def plugin_enabled?(site_config)
|
|
175
|
+
site_config.key?('with_minify') ? site_config['with_minify'] : true
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Builds effective minify-html options by merging with_minify_data over defaults.
|
|
179
|
+
# YAML string keys from _config.yml are converted to symbols for minify-html.
|
|
180
|
+
# Unknown keys are passed through unchanged.
|
|
181
|
+
#
|
|
182
|
+
# @param site_config [Hash] Jekyll site configuration hash (site.config)
|
|
183
|
+
# @return [Hash{Symbol => Boolean}]
|
|
184
|
+
def build_minify_options(site_config)
|
|
185
|
+
overrides = (site_config['with_minify_data'] || {}).transform_keys(&:to_sym)
|
|
186
|
+
DEFAULT_MINIFY_OPTIONS.merge(overrides)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Walks the destination directory and dispatches each eligible file.
|
|
190
|
+
#
|
|
191
|
+
# @param dest [Pathname] the Jekyll site destination directory
|
|
192
|
+
# @return [void]
|
|
193
|
+
def minify_each_file(dest)
|
|
194
|
+
dest.glob('**/*.{html,css,js}').each do |file_path|
|
|
195
|
+
minify_file(file_path)
|
|
196
|
+
end
|
|
197
|
+
rescue StandardError => e
|
|
198
|
+
Jekyll.logger.error 'jekyll-minify:', "Error during file discovery: #{e.message}"
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Dispatches a single file to the appropriate minifier based on extension.
|
|
202
|
+
# Skips files already minified (*.min.css, *.min.js).
|
|
203
|
+
#
|
|
204
|
+
# @param file_path [Pathname] path to the file to process
|
|
205
|
+
# @return [void]
|
|
206
|
+
def minify_file(file_path)
|
|
207
|
+
# Skip already-minified files
|
|
208
|
+
return if file_path.to_s.end_with?('.min.css', '.min.js')
|
|
209
|
+
|
|
210
|
+
case file_path.extname.downcase
|
|
211
|
+
when '.html'
|
|
212
|
+
minify_with_fallback(file_path, :html)
|
|
213
|
+
when '.css'
|
|
214
|
+
minify_with_fallback(file_path, :css)
|
|
215
|
+
when '.js'
|
|
216
|
+
minify_with_fallback(file_path, :js, rename_to_min: true)
|
|
217
|
+
end
|
|
218
|
+
rescue StandardError => e
|
|
219
|
+
Jekyll.logger.warn 'jekyll-minify:', "Unexpected error processing #{file_path}: #{e.message}"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Minifies a file, restoring the original if anything goes wrong.
|
|
223
|
+
# Skips non-UTF-8 files with a targeted warning.
|
|
224
|
+
# Skips empty files.
|
|
225
|
+
#
|
|
226
|
+
# @param file_path [Pathname] path to the file to minify
|
|
227
|
+
# @param type [Symbol] asset type: :html, :css, or :js
|
|
228
|
+
# @param rename_to_min [Boolean] whether to rename .js output to .min.js
|
|
229
|
+
# @return [void]
|
|
230
|
+
def minify_with_fallback(file_path, type, rename_to_min: false)
|
|
231
|
+
begin
|
|
232
|
+
original_content = file_path.read(encoding: 'UTF-8')
|
|
233
|
+
rescue Encoding::InvalidByteSequenceError
|
|
234
|
+
Jekyll.logger.warn 'jekyll-minify:', "Skipping #{file_path}: not valid UTF-8"
|
|
235
|
+
return
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
original_bytes = original_content.bytesize
|
|
239
|
+
|
|
240
|
+
# Skip empty files
|
|
241
|
+
return if original_bytes.zero?
|
|
242
|
+
|
|
243
|
+
begin
|
|
244
|
+
minified = minify_by_type(original_content, type)
|
|
245
|
+
validate_minified_content(minified)
|
|
246
|
+
minified_bytes = minified.bytesize
|
|
247
|
+
write_minified_file(file_path, minified, original_content,
|
|
248
|
+
rename_to_min: rename_to_min)
|
|
249
|
+
record_minification_result(type, original_bytes, minified_bytes)
|
|
250
|
+
rescue StandardError => e
|
|
251
|
+
restore_on_error(file_path, original_content, e)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Validates that minification produced non-empty content.
|
|
256
|
+
#
|
|
257
|
+
# @param minified [String, nil] the minification output to validate
|
|
258
|
+
# @raise [RuntimeError] if minified is nil, empty, or only whitespace
|
|
259
|
+
# @return [void]
|
|
260
|
+
def validate_minified_content(minified)
|
|
261
|
+
raise 'Minification resulted in empty content' if minified.nil? || minified.strip.empty?
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Records byte counts and file count for the given asset type.
|
|
265
|
+
#
|
|
266
|
+
# @param type [Symbol] asset type: :html, :css, or :js
|
|
267
|
+
# @param original_bytes [Integer] size before minification
|
|
268
|
+
# @param minified_bytes [Integer] size after minification
|
|
269
|
+
# @return [void]
|
|
270
|
+
def record_minification_result(type, original_bytes, minified_bytes)
|
|
271
|
+
@stats[type][:count] += 1
|
|
272
|
+
@stats[type][:original_bytes] += original_bytes
|
|
273
|
+
@stats[type][:minified_bytes] += minified_bytes
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Logs a minification failure and restores the original file content.
|
|
277
|
+
#
|
|
278
|
+
# @param file_path [Pathname] path to the file being restored
|
|
279
|
+
# @param original_content [String] the unmodified file content to write back
|
|
280
|
+
# @param error [StandardError] the error that triggered the restore
|
|
281
|
+
# @return [void]
|
|
282
|
+
def restore_on_error(file_path, original_content, error)
|
|
283
|
+
error_msg = "Failed to minify #{file_path}: #{error.message}"
|
|
284
|
+
Jekyll.logger.warn 'jekyll-minify:', error_msg
|
|
285
|
+
@stats[:errors] << error_msg
|
|
286
|
+
file_path.write(original_content, encoding: 'UTF-8')
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Writes minified content to disk, optionally renaming .js to .min.js.
|
|
290
|
+
#
|
|
291
|
+
# @param file_path [Pathname] path to write minified content
|
|
292
|
+
# @param minified_content [String] minified content
|
|
293
|
+
# @param original_content [String] fallback content if write or rename fails
|
|
294
|
+
# @param rename_to_min [Boolean] if true, renames .js to .min.js
|
|
295
|
+
# @raise [RuntimeError] if the write or rename operation fails
|
|
296
|
+
# @return [void]
|
|
297
|
+
def write_minified_file(file_path, minified_content, original_content, rename_to_min: false)
|
|
298
|
+
file_path.write(minified_content, encoding: 'UTF-8')
|
|
299
|
+
|
|
300
|
+
if rename_to_min && file_path.extname == '.js'
|
|
301
|
+
new_path = file_path.sub_ext('.min.js')
|
|
302
|
+
file_path.rename(new_path)
|
|
303
|
+
end
|
|
304
|
+
rescue StandardError => e
|
|
305
|
+
# Restore original if write or rename fails
|
|
306
|
+
file_path.write(original_content, encoding: 'UTF-8')
|
|
307
|
+
raise "Failed to write minified file: #{e.message}"
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Routes content to the appropriate minifier by asset type.
|
|
311
|
+
#
|
|
312
|
+
# @param content [String] raw file content
|
|
313
|
+
# @param type [Symbol] asset type: :html, :css, or :js
|
|
314
|
+
# @return [String] minified content
|
|
315
|
+
# @raise [RuntimeError] if the type is not recognized
|
|
316
|
+
def minify_by_type(content, type)
|
|
317
|
+
case type
|
|
318
|
+
when :html then minify_html(content)
|
|
319
|
+
when :css then minify_css(content)
|
|
320
|
+
when :js then minify_js(content)
|
|
321
|
+
else raise "Unknown asset type: #{type}"
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# =========================================================================
|
|
326
|
+
# Minification via minify-html gem
|
|
327
|
+
# =========================================================================
|
|
328
|
+
|
|
329
|
+
# Minifies HTML content using the minify-html gem.
|
|
330
|
+
# Uses @minify_options which defaults to DEFAULT_MINIFY_OPTIONS but may be
|
|
331
|
+
# overridden via with_minify_data in _config.yml.
|
|
332
|
+
#
|
|
333
|
+
# @param content [String] raw HTML string
|
|
334
|
+
# @return [String] minified HTML
|
|
335
|
+
# @raise [RuntimeError] if the minify-html gem raises an error
|
|
336
|
+
def minify_html(content)
|
|
337
|
+
Kernel.minify_html(content, @minify_options)
|
|
338
|
+
rescue StandardError => e
|
|
339
|
+
raise "HTML minification failed: #{e.message}"
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Minifies CSS or JS content by wrapping it in a temporary HTML tag.
|
|
343
|
+
# Extracts the minified content between the first pair of wrapper tags.
|
|
344
|
+
#
|
|
345
|
+
# @param content [String] raw CSS or JS string
|
|
346
|
+
# @param wrapper_tag [String] HTML tag name ("style" or "script")
|
|
347
|
+
# @param minify_option [Symbol] minify-html option key (:minify_css or :minify_js)
|
|
348
|
+
# @return [String] minified content without wrapper tags
|
|
349
|
+
# @raise [RuntimeError] if the minification result is empty or the tag is not found
|
|
350
|
+
def minify_wrapped_content(content, wrapper_tag, minify_option)
|
|
351
|
+
wrapped = "<#{wrapper_tag}>#{content}</#{wrapper_tag}>"
|
|
352
|
+
minified = Kernel.minify_html(wrapped, { minify_option => true })
|
|
353
|
+
match = minified.match(%r{<#{wrapper_tag}>(.*?)</#{wrapper_tag}>}m)
|
|
354
|
+
raise "#{wrapper_tag.upcase} minification resulted in empty content" unless match
|
|
355
|
+
|
|
356
|
+
match[1]
|
|
357
|
+
rescue StandardError => e
|
|
358
|
+
raise "#{wrapper_tag.upcase} minification failed: #{e.message}"
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Minifies a CSS string by wrapping it in a <style> tag.
|
|
362
|
+
#
|
|
363
|
+
# @param content [String] raw CSS string
|
|
364
|
+
# @return [String] minified CSS
|
|
365
|
+
def minify_css(content)
|
|
366
|
+
minify_wrapped_content(content, 'style', :minify_css)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Minifies a JavaScript string by wrapping it in a <script> tag.
|
|
370
|
+
#
|
|
371
|
+
# @param content [String] raw JavaScript string
|
|
372
|
+
# @return [String] minified JavaScript
|
|
373
|
+
def minify_js(content)
|
|
374
|
+
minify_wrapped_content(content, 'script', :minify_js)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# =========================================================================
|
|
378
|
+
# Logging and Statistics
|
|
379
|
+
# =========================================================================
|
|
380
|
+
|
|
381
|
+
# Formats a byte count as a human-readable string.
|
|
382
|
+
#
|
|
383
|
+
# @param bytes [Integer] number of bytes
|
|
384
|
+
# @return [String] formatted size (e.g. "512B", "1.0KB", "2.5MB")
|
|
385
|
+
def format_size(bytes)
|
|
386
|
+
if bytes < 1024
|
|
387
|
+
"#{bytes}B"
|
|
388
|
+
elsif bytes < 1_048_576
|
|
389
|
+
"#{(bytes / 1024.0).round(1)}KB"
|
|
390
|
+
else
|
|
391
|
+
"#{(bytes / 1_048_576.0).round(1)}MB"
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Calculates the percentage reduction from original to minified size.
|
|
396
|
+
# Returns 0 if original_bytes is zero (division guard).
|
|
397
|
+
#
|
|
398
|
+
# @param original_bytes [Integer] size before minification
|
|
399
|
+
# @param minified_bytes [Integer] size after minification
|
|
400
|
+
# @return [Float] percentage reduction; negative if the file grew
|
|
401
|
+
def reduction_percent(original_bytes, minified_bytes)
|
|
402
|
+
return 0 if original_bytes.zero?
|
|
403
|
+
|
|
404
|
+
(((original_bytes - minified_bytes) / original_bytes.to_f) * 100).round(1)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# Logs per-type and overall minification statistics, plus any errors.
|
|
408
|
+
#
|
|
409
|
+
# @return [void]
|
|
410
|
+
def log_statistics
|
|
411
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start_time
|
|
412
|
+
|
|
413
|
+
Jekyll.logger.info 'jekyll-minify:', 'Minification complete using minify-html gem'
|
|
414
|
+
|
|
415
|
+
totals = log_per_type_statistics
|
|
416
|
+
log_overall_statistics(totals, elapsed) if totals[:original_bytes].positive?
|
|
417
|
+
log_minification_errors
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Logs per-type statistics and returns combined byte totals.
|
|
421
|
+
#
|
|
422
|
+
# @return [Hash{Symbol => Integer}] totals with keys :original_bytes, :minified_bytes
|
|
423
|
+
def log_per_type_statistics
|
|
424
|
+
totals = { original_bytes: 0, minified_bytes: 0 }
|
|
425
|
+
|
|
426
|
+
ASSET_TYPES.each do |asset|
|
|
427
|
+
type = asset[:type]
|
|
428
|
+
data = @stats[type]
|
|
429
|
+
|
|
430
|
+
next if data[:count].zero?
|
|
431
|
+
|
|
432
|
+
log_type_statistics(asset[:display_name], data)
|
|
433
|
+
totals[:original_bytes] += data[:original_bytes]
|
|
434
|
+
totals[:minified_bytes] += data[:minified_bytes]
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
totals
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Logs statistics for a single asset type.
|
|
441
|
+
#
|
|
442
|
+
# @param display_name [String] human-readable type label (e.g. "HTML", "CSS")
|
|
443
|
+
# @param data [Hash] stats hash with :count, :original_bytes, :minified_bytes
|
|
444
|
+
# @return [void]
|
|
445
|
+
def log_type_statistics(display_name, data)
|
|
446
|
+
percent = reduction_percent(data[:original_bytes], data[:minified_bytes])
|
|
447
|
+
before_fmt = format_size(data[:original_bytes])
|
|
448
|
+
after_fmt = format_size(data[:minified_bytes])
|
|
449
|
+
|
|
450
|
+
Jekyll.logger.info 'jekyll-minify:',
|
|
451
|
+
"#{display_name}: #{data[:count]} files (#{before_fmt} → " \
|
|
452
|
+
"#{after_fmt}, #{percent}% reduction)"
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Logs overall (cross-type) minification statistics.
|
|
456
|
+
#
|
|
457
|
+
# @param totals [Hash{Symbol => Integer}] combined :original_bytes and :minified_bytes
|
|
458
|
+
# @param elapsed [Float] elapsed time in seconds
|
|
459
|
+
# @return [void]
|
|
460
|
+
def log_overall_statistics(totals, elapsed)
|
|
461
|
+
percent = reduction_percent(totals[:original_bytes], totals[:minified_bytes])
|
|
462
|
+
before_fmt = format_size(totals[:original_bytes])
|
|
463
|
+
after_fmt = format_size(totals[:minified_bytes])
|
|
464
|
+
|
|
465
|
+
Jekyll.logger.info 'jekyll-minify:',
|
|
466
|
+
"Total: #{before_fmt} → #{after_fmt} " \
|
|
467
|
+
"(#{percent}% reduction) in #{elapsed.round(2)}s"
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Logs a summary of minification errors, if any occurred.
|
|
471
|
+
# No output is produced when @stats[:errors] is empty.
|
|
472
|
+
#
|
|
473
|
+
# @return [void]
|
|
474
|
+
def log_minification_errors
|
|
475
|
+
return if @stats[:errors].empty?
|
|
476
|
+
|
|
477
|
+
Jekyll.logger.warn 'jekyll-minify:',
|
|
478
|
+
"#{@stats[:errors].length} minification errors (files preserved):"
|
|
479
|
+
@stats[:errors].each { |error| Jekyll.logger.warn 'jekyll-minify:', " - #{error}" }
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# Register the post_write hook
|
|
486
|
+
Jekyll::Hooks.register :site, :post_write do |site|
|
|
487
|
+
Jekyll::Minify::Minifier.minify_site(site)
|
|
488
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: jekyll-minify
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.5
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- ReleaseBot
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: jekyll
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '4.0'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '5.0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '4.0'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '5.0'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: minify_html
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - "~>"
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0.15'
|
|
39
|
+
type: :runtime
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - "~>"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0.15'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: rake
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - "~>"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '13.0'
|
|
53
|
+
type: :development
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '13.0'
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: rspec
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '3.0'
|
|
67
|
+
type: :development
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '3.0'
|
|
74
|
+
- !ruby/object:Gem::Dependency
|
|
75
|
+
name: rubocop
|
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - "~>"
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '1.57'
|
|
81
|
+
type: :development
|
|
82
|
+
prerelease: false
|
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - "~>"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '1.57'
|
|
88
|
+
- !ruby/object:Gem::Dependency
|
|
89
|
+
name: rubocop-jekyll
|
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0.14'
|
|
95
|
+
type: :development
|
|
96
|
+
prerelease: false
|
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - "~>"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '0.14'
|
|
102
|
+
- !ruby/object:Gem::Dependency
|
|
103
|
+
name: yard
|
|
104
|
+
requirement: !ruby/object:Gem::Requirement
|
|
105
|
+
requirements:
|
|
106
|
+
- - "~>"
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: '0.9'
|
|
109
|
+
type: :development
|
|
110
|
+
prerelease: false
|
|
111
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - "~>"
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '0.9'
|
|
116
|
+
description: Jekyll plugin to minify HTML, CSS, and JavaScript files.
|
|
117
|
+
email:
|
|
118
|
+
- group_58921183_bot_c5520aeba366578e2e444dd181ff3e23@noreply.gitlab.com
|
|
119
|
+
executables: []
|
|
120
|
+
extensions: []
|
|
121
|
+
extra_rdoc_files: []
|
|
122
|
+
files:
|
|
123
|
+
- LICENSE
|
|
124
|
+
- README.md
|
|
125
|
+
- lib/jekyll-minify.rb
|
|
126
|
+
homepage: https://gitlab.com/CentOS/artwork/centos-web/jekyll-minify
|
|
127
|
+
licenses:
|
|
128
|
+
- MIT
|
|
129
|
+
metadata:
|
|
130
|
+
rubygems_mfa_required: 'true'
|
|
131
|
+
homepage_uri: https://gitlab.com/CentOS/artwork/centos-web/jekyll-minify
|
|
132
|
+
source_code_uri: https://gitlab.com/CentOS/artwork/centos-web/jekyll-minify/tree/main/website/jekyll-minify
|
|
133
|
+
changelog_uri: https://gitlab.com/CentOS/artwork/centos-web/jekyll-minify/releases
|
|
134
|
+
bug_tracker_uri: https://gitlab.com/CentOS/artwork/centos-web/jekyll-minify/issues
|
|
135
|
+
rdoc_options: []
|
|
136
|
+
require_paths:
|
|
137
|
+
- lib
|
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - ">="
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '2.7'
|
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
|
+
requirements:
|
|
145
|
+
- - ">="
|
|
146
|
+
- !ruby/object:Gem::Version
|
|
147
|
+
version: '0'
|
|
148
|
+
requirements: []
|
|
149
|
+
rubygems_version: 3.6.9
|
|
150
|
+
specification_version: 4
|
|
151
|
+
summary: Jekyll plugin to minify HTML, CSS, and JavaScript files.
|
|
152
|
+
test_files: []
|