middleman-brotli 0.0.1

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: 6aac27e574c55f66b3d57c8ddefd04222991625f49deb95e280edc5286891112
4
+ data.tar.gz: 500d8731091523ed5c6a68e61849132ed9922de8a11af935eae632cdc8253e5e
5
+ SHA512:
6
+ metadata.gz: cb0227c4b028db975deec31335b028ba9ee0c9bb2b01275628aa6b678153d183b1af2befd56a7e329f5430dcc68b75eefd4f07c6689c73d7f7212ca4e844af25
7
+ data.tar.gz: e517f3a068e0178654b8125aa16723ea9476366a38ddb53a813ff3d7dbf493297ad2a164f181ba134f1318f52de2d6114a2cfa1910ba2ab39c320e8b8819a7b7
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ # Ignore bundler lock file
2
+ /Gemfile.lock
3
+
4
+ # Ignore pkg folder
5
+ /pkg
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # If you do not have OpenSSL installed, update
2
+ # the following line to use "http://" instead
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in middleman-brotli.gemspec
6
+ gemspec
7
+
8
+ group :development do
9
+ gem 'rake'
10
+ gem 'rdoc'
11
+ gem 'yard'
12
+ end
13
+
14
+ group :test do
15
+ gem 'cucumber'
16
+ gem 'aruba'
17
+ gem 'rspec'
18
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Daan van Vugt
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,46 @@
1
+ # Middleman-brotli extension
2
+
3
+ This extension compresses assets with brotli for maximum shrinkage.
4
+ Based on the gzip compressor from middleman core.
5
+
6
+ ## Installation
7
+ Add the gem to your `.gemrc`
8
+
9
+ ```ruby
10
+ gem 'middleman-brotli', git: 'https://github.com/exteris/middleman-brotli', branch: 'master'
11
+ ```
12
+
13
+ and activate it in your `config.rb`:
14
+
15
+ ```ruby
16
+ configure :build do
17
+ activate :brotli
18
+ end
19
+ ```
20
+
21
+ You might need to apply some other settings to your web server and CDN.
22
+ See for instance [This page](https://afasterweb.com/2016/03/15/serving-up-brotli-with-nginx-and-jekyll/).
23
+
24
+ ## Options
25
+ The files to compress can be selected by extension, with an optional ignore list.
26
+ Default options are:
27
+
28
+ ```ruby
29
+ option :exts, %w(.css .htm .html .js .svg .xhtml .otf .woff .ttf .woff2), 'File extensions to compress when building.'
30
+ option :ignore, [], 'Patterns to avoid compressing'
31
+ option :overwrite, false, 'Overwrite original files instead of adding .br extension.'
32
+ ```
33
+
34
+ They can be set as:
35
+ ```ruby
36
+ configure :build do
37
+ activate :brotli, ignore: 'tinyfile.js'
38
+ end
39
+ ```
40
+
41
+ ## Improvements
42
+ * Detect filetype and apply font, text or generic compression types (Brotli comes with different default dictionaries for these types of files)
43
+ * We could generalize the gzip plugin to just use multiple compression commands
44
+
45
+ # LICENSE
46
+ This project is released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'cucumber/rake/task'
5
+
6
+ Cucumber::Rake::Task.new(:cucumber, 'Run features that should pass') do |t|
7
+ t.cucumber_opts = '--color --tags ~@wip --strict'
8
+ end
9
+
10
+ require 'rake/clean'
11
+
12
+ task test: ['cucumber']
13
+
14
+ task default: :test
@@ -0,0 +1,4 @@
1
+ PROJECT_ROOT_PATH = File.dirname(File.dirname(File.dirname(__FILE__)))
2
+ require 'middleman-core'
3
+ require 'middleman-core/step_definitions'
4
+ require File.join(PROJECT_ROOT_PATH, 'lib', 'middleman-brotli')
@@ -0,0 +1,104 @@
1
+ # Require core library
2
+ require 'middleman-core'
3
+
4
+ # Extension namespace
5
+ class Middleman::Brotli < ::Middleman::Extension
6
+ option :exts, %w(.css .htm .html .js .svg .xhtml .otf .woff .ttf .woff2), 'File extensions to compress when building.'
7
+ option :ignore, [], 'Patterns to avoid compressing'
8
+ option :overwrite, false, 'Overwrite original files instead of adding .br extension.'
9
+
10
+ class NumberHelpers
11
+ include ::Padrino::Helpers::NumberHelpers
12
+ end
13
+
14
+ def initialize(app, options_hash={})
15
+ super
16
+
17
+ require 'brotli'
18
+ require 'stringio'
19
+ require 'find'
20
+ require 'thread'
21
+ end
22
+
23
+ def after_build(builder)
24
+ num_threads = 4
25
+ paths = ::Middleman::Util.all_files_under(app.config[:build_dir])
26
+ total_savings = 0
27
+
28
+ # Fill a queue with inputs
29
+ in_queue = Queue.new
30
+ paths.each do |path|
31
+ in_queue << path if should_brotli?(path)
32
+ end
33
+ num_paths = in_queue.size
34
+
35
+ # Farm out compression tasks to threads and put the results in in_queue
36
+ out_queue = Queue.new
37
+ num_threads.times.each do
38
+ Thread.new do
39
+ while path = in_queue.pop
40
+ out_queue << brotli_file(path.to_s)
41
+ end
42
+ end
43
+ end
44
+
45
+ # Insert a nil for each thread to stop it
46
+ num_threads.times do
47
+ in_queue << nil
48
+ end
49
+
50
+ old_locale = I18n.locale
51
+ I18n.locale = :en # use the english localizations for printing out file sizes to make sure the localizations exist
52
+
53
+ num_paths.times do
54
+ output_filename, old_size, new_size = out_queue.pop
55
+
56
+ next unless output_filename
57
+
58
+ total_savings += (old_size - new_size)
59
+ size_change_word = (old_size - new_size) > 0 ? 'smaller' : 'larger'
60
+ builder.trigger :created, "#{output_filename} (#{NumberHelpers.new.number_to_human_size((old_size - new_size).abs)} #{size_change_word})"
61
+ end
62
+
63
+ builder.trigger :brotli, '', "Total brotli savings: #{NumberHelpers.new.number_to_human_size(total_savings)}"
64
+ I18n.locale = old_locale
65
+ end
66
+
67
+ Contract String => [Maybe[String], Maybe[Num], Maybe[Num]]
68
+ def brotli_file(path)
69
+ input_file = File.open(path, 'rb').read
70
+ output_filename = options.overwrite ? path : path + '.br'
71
+ input_file_time = File.mtime(path)
72
+
73
+ # Check if the right file's already there
74
+ if !options.overwrite && File.exist?(output_filename) && File.mtime(output_filename) == input_file_time
75
+ return [nil, nil, nil]
76
+ end
77
+
78
+ File.open(output_filename, 'wb') do |f|
79
+ f.write(Brotli.deflate(input_file, quality: 11))
80
+ end
81
+
82
+ # Make the file times match, both for Nginx's brotli_static extension
83
+ # and so we can ID existing files. Also, so even if the br files are
84
+ # wiped out by build --clean and recreated, we won't rsync them over
85
+ # again because they'll end up with the same mtime.
86
+ File.utime(File.atime(output_filename), input_file_time, output_filename)
87
+
88
+ old_size = File.size(path)
89
+ new_size = File.size(output_filename)
90
+
91
+ [output_filename, old_size, new_size]
92
+ end
93
+
94
+ private
95
+
96
+ # Whether a path should be brotli'd
97
+ # @param [Pathname] path A destination path
98
+ # @return [Boolean]
99
+ Contract Pathname => Bool
100
+ def should_brotli?(path)
101
+ path = path.sub app.config[:build_dir] + '/', ''
102
+ options.exts.include?(path.extname) && options.ignore.none? { |ignore| Middleman::Util.path_match(ignore, path.to_s) }
103
+ end
104
+ end
@@ -0,0 +1,6 @@
1
+ require "middleman-core"
2
+
3
+ Middleman::Extensions.register :brotli do
4
+ require "middleman-brotli/extension.rb"
5
+ Middleman::Brotli
6
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "middleman-brotli"
6
+ s.version = "0.0.1"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Daan van Vugt"]
9
+ s.email = ["daanvanvugt@gmail.com"]
10
+ # s.homepage = "http://example.com"
11
+ s.summary = %q{Compresses middleman outputs with brotli}
12
+ s.description = %q{This Middleman extension writes brotli-compressed output files (.br) for your pages, js and css.}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ # The version of middleman-core your extension depends on
20
+ s.add_runtime_dependency("middleman-core", [">= 4.2.1"])
21
+
22
+ # Additional dependencies
23
+ s.add_runtime_dependency("brotli", ">= 0.2.0")
24
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: middleman-brotli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Daan van Vugt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-09-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: middleman-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: brotli
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.0
41
+ description: This Middleman extension writes brotli-compressed output files (.br)
42
+ for your pages, js and css.
43
+ email:
44
+ - daanvanvugt@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE
52
+ - README.md
53
+ - Rakefile
54
+ - features/support/env.rb
55
+ - lib/middleman-brotli.rb
56
+ - lib/middleman-brotli/extension.rb
57
+ - middleman-brotli.gemspec
58
+ homepage:
59
+ licenses: []
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubygems_version: 3.2.22
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Compresses middleman outputs with brotli
80
+ test_files:
81
+ - features/support/env.rb