fontawesome_subsetter 0.1.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +113 -0
- data/lib/fontawesome_subsetter/engine.rb +17 -0
- data/lib/fontawesome_subsetter/icon_helper.rb +21 -0
- data/lib/fontawesome_subsetter/subsetter.rb +210 -0
- data/lib/fontawesome_subsetter/version.rb +5 -0
- data/lib/fontawesome_subsetter.rb +35 -0
- data/lib/puma/plugin/fontawesome.rb +93 -0
- data/lib/tasks/fontawesome_subsetter.rake +15 -0
- metadata +126 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8d07501e79546fbe88ff5c7130cb5b0a5e90c2bc0dedc50bebf97989a966cf33
|
|
4
|
+
data.tar.gz: cbbd58e800778f1098353a673cbf11348b656cc13d9b832c5b53980dbb588ab9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d5dbc3c8c0109591d830a15f7828c4976239443bada05930b15d2c99a70a45159cd80c68b080c17665db2f6798cfc8739622dd62980436c138ad19c90d5bc1ef
|
|
7
|
+
data.tar.gz: 64d4f1265db3642169657c29008917a462ba0cec6bd92113766a109df9ebb7afcdd79c3c25f2253f2d1f4337f97a5af86216722f2aa6026d266d5ec57bfe8e58
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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,113 @@
|
|
|
1
|
+
# FontAwesome Subsetter
|
|
2
|
+
|
|
3
|
+
A Rails gem that tree-shakes FontAwesome — scans your views and helpers for `icon()` calls, subsets WOFF2 font files to include only used glyphs, and generates minimal CSS.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- **FontAwesome** files in `vendor/fontawesome/` (metadata, scss, webfonts)
|
|
8
|
+
- **pyftsubset** (from [fonttools](https://github.com/fonttools/fonttools)): `pip install fonttools brotli`
|
|
9
|
+
- **sass-embedded** gem for SCSS compilation
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add to your Gemfile:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem "fontawesome_subsetter"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then run `bundle install`.
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
### Directory Structure
|
|
24
|
+
|
|
25
|
+
The gem expects FontAwesome Pro files in your project at:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
vendor/
|
|
29
|
+
fontawesome/
|
|
30
|
+
metadata/
|
|
31
|
+
icons.yml
|
|
32
|
+
scss/
|
|
33
|
+
variables.scss
|
|
34
|
+
functions.scss
|
|
35
|
+
mixins.scss
|
|
36
|
+
core.scss
|
|
37
|
+
animated.scss
|
|
38
|
+
icons.scss
|
|
39
|
+
solid.scss
|
|
40
|
+
regular.scss
|
|
41
|
+
...
|
|
42
|
+
webfonts/
|
|
43
|
+
fa-solid-900.woff2
|
|
44
|
+
fa-regular-400.woff2
|
|
45
|
+
fa-brands-400.woff2
|
|
46
|
+
fa-duotone-900.woff2
|
|
47
|
+
fa-thin-100.woff2
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> **Note:** In `vendor/fontawesome/scss/variables.scss`, change `$icons` and `$brand-icons` to empty maps with `!default` so the subsetter can inject only the used icons.
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
### 1. Icon Helper
|
|
55
|
+
|
|
56
|
+
The gem provides an `icon` helper available in all views:
|
|
57
|
+
|
|
58
|
+
```slim
|
|
59
|
+
= icon(:fas, :download, "Download", class: "mr-1")
|
|
60
|
+
= icon(:far, :check)
|
|
61
|
+
= icon(:fab, :github)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2. Puma Plugin (Development Watch Mode)
|
|
65
|
+
|
|
66
|
+
Add to your `config/puma.rb`:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
if ENV.fetch("RAILS_ENV", "development") == "development"
|
|
70
|
+
plugin :fontawesome
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This watches `app/views/`, `app/helpers/`, and `app/components/` for changes and automatically rebuilds subsetted fonts.
|
|
75
|
+
|
|
76
|
+
### 3. Rake Task (Deploy)
|
|
77
|
+
|
|
78
|
+
The gem automatically hooks into `assets:precompile`:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
rake assets:precompile # fontawesome:subset runs first
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
You can also run it manually:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
rake fontawesome:subset
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
# config/initializers/fontawesome_subsetter.rb
|
|
94
|
+
FontawesomeSubsetter.configure do | config |
|
|
95
|
+
# Paths to scan for icon() calls (defaults shown)
|
|
96
|
+
config.scan_globs = ["app/views/**/*.slim", "app/helpers/**/*.rb"]
|
|
97
|
+
|
|
98
|
+
# Regex to match icon() calls (default shown)
|
|
99
|
+
config.icon_regex = /icon\(\s*:(?<prefix>fas|far|fab|fad|fal|fat)\s*,\s*:(?<icon>[\w_\-]+)\b/
|
|
100
|
+
|
|
101
|
+
# Override default paths (optional — defaults use Rails.root)
|
|
102
|
+
# config.meta_path = Rails.root.join("vendor", "fontawesome", "metadata", "icons.yml")
|
|
103
|
+
# config.fonts_dir = Rails.root.join("vendor", "fontawesome", "webfonts")
|
|
104
|
+
# config.build_dir = Rails.root.join("app", "assets", "builds")
|
|
105
|
+
|
|
106
|
+
# Custom SCSS template (optional — receives @styles hash, must return SCSS string)
|
|
107
|
+
# config.scss_template = ->(styles) { "..." }
|
|
108
|
+
end
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
[MIT](LICENSE.txt) — Copyright (c) 2026
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module FontawesomeSubsetter
|
|
2
|
+
|
|
3
|
+
class Engine < ::Rails::Engine
|
|
4
|
+
|
|
5
|
+
initializer "fontawesome_subsetter.helpers" do
|
|
6
|
+
ActiveSupport.on_load(:action_view) do
|
|
7
|
+
include FontawesomeSubsetter::IconHelper
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
rake_tasks do
|
|
12
|
+
load File.expand_path("../tasks/fontawesome_subsetter.rake", __dir__)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module FontawesomeSubsetter
|
|
2
|
+
|
|
3
|
+
module IconHelper
|
|
4
|
+
|
|
5
|
+
def icon(style, name, text = nil, html_options = {}, &block)
|
|
6
|
+
text, html_options = nil, text if text.is_a?(Hash)
|
|
7
|
+
|
|
8
|
+
html_options[:class] = "#{ style } fa-#{ name.to_s.dasherize }#{ " #{ html_options[:class] }" if html_options.key? :class }"
|
|
9
|
+
|
|
10
|
+
html = content_tag :i, nil, html_options
|
|
11
|
+
|
|
12
|
+
html = "#{ html } #{ text }" if text.present?
|
|
13
|
+
|
|
14
|
+
html = "#{ html } #{ capture block }" if block_given?
|
|
15
|
+
|
|
16
|
+
html.html_safe
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "open3"
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "yaml"
|
|
5
|
+
require "set"
|
|
6
|
+
|
|
7
|
+
module FontawesomeSubsetter
|
|
8
|
+
|
|
9
|
+
class Subsetter
|
|
10
|
+
|
|
11
|
+
def initialize(root: nil)
|
|
12
|
+
@file_icon_map = {}
|
|
13
|
+
@root = root || defined?(Rails) && Rails.root || Pathname.new(Dir.pwd)
|
|
14
|
+
@root = Pathname.new(@root) unless @root.is_a?(Pathname)
|
|
15
|
+
|
|
16
|
+
config = FontawesomeSubsetter.configuration
|
|
17
|
+
|
|
18
|
+
@meta_path = config.meta_path || @root.join("vendor", "fontawesome", "metadata", "icons.yml")
|
|
19
|
+
@fonts_dir = config.fonts_dir || @root.join("vendor", "fontawesome", "webfonts")
|
|
20
|
+
@build_dir = config.build_dir || @root.join("app", "assets", "builds")
|
|
21
|
+
@webfonts_dir = @build_dir.join("webfonts")
|
|
22
|
+
@scan_globs = config.scan_globs
|
|
23
|
+
@icon_regex = config.icon_regex
|
|
24
|
+
|
|
25
|
+
@styles = {
|
|
26
|
+
"solid" => { name: "solid", prefix: "fas", unicode_cache: Set.new, icon_cache: Set.new, sass_icon_cache: Set.new, changed: false, file: "fa-solid-900.woff2" },
|
|
27
|
+
"regular" => { name: "regular", prefix: "far", unicode_cache: Set.new, icon_cache: Set.new, sass_icon_cache: Set.new, changed: false, file: "fa-regular-400.woff2" },
|
|
28
|
+
"light" => { name: "light", prefix: "fal", unicode_cache: Set.new, icon_cache: Set.new, sass_icon_cache: Set.new, changed: false, file: "fa-thin-100.woff2" },
|
|
29
|
+
"thin" => { name: "thin", prefix: "fat", unicode_cache: Set.new, icon_cache: Set.new, sass_icon_cache: Set.new, changed: false, file: "fa-thin-100.woff2" },
|
|
30
|
+
"duotone" => { name: "duotone", prefix: "fad", unicode_cache: Set.new, icon_cache: Set.new, sass_icon_cache: Set.new, changed: false, file: "fa-duotone-900.woff2" },
|
|
31
|
+
"brands" => { name: "brands", prefix: "fab", unicode_cache: Set.new, icon_cache: Set.new, sass_icon_cache: Set.new, changed: false, file: "fa-brands-400.woff2" }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@watch_paths = [
|
|
35
|
+
@root.join("app", "views"),
|
|
36
|
+
@root.join("app", "helpers"),
|
|
37
|
+
@root.join("app", "components")
|
|
38
|
+
].select(&:exist?)
|
|
39
|
+
|
|
40
|
+
@watch_file_type_regex = config.watch_file_type_regex
|
|
41
|
+
|
|
42
|
+
FileUtils.rm_rf(@webfonts_dir)
|
|
43
|
+
FileUtils.mkdir_p(@webfonts_dir)
|
|
44
|
+
|
|
45
|
+
# Load FontAwesome metadata
|
|
46
|
+
@metadata = YAML.safe_load(File.read(@meta_path))
|
|
47
|
+
|
|
48
|
+
# Build unicode map, set entry to an array of all codepoints (main + secondary)
|
|
49
|
+
@metadata.each { | key, entry | @metadata[key]["unicodes"] = Set.new([ "U+#{ entry['unicode'].upcase }", *entry.dig("aliases", "unicodes", "secondary")&.map { | s | "U+#{ s.upcase }" } ]) }
|
|
50
|
+
|
|
51
|
+
# Build alias map
|
|
52
|
+
@metadata.merge!(@metadata.values.flat_map { | entry | (entry.dig("aliases", "names") || []).map { [ it, entry ] } }.to_h)
|
|
53
|
+
|
|
54
|
+
# Strip useless metadata
|
|
55
|
+
@metadata.each_value { it.except!("changes", "label", "voted", "search", "styles", "aliases") }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Updates the cache for each style, returns the styles that have changed.
|
|
59
|
+
def scan_files_and_update_caches(files)
|
|
60
|
+
files.each do | path |
|
|
61
|
+
|
|
62
|
+
text = File.read(path)
|
|
63
|
+
text.scan(@icon_regex).each do | prefix, icon |
|
|
64
|
+
|
|
65
|
+
style = @styles.find { it&.last[:prefix] == prefix }&.last
|
|
66
|
+
next unless style
|
|
67
|
+
|
|
68
|
+
normalized = icon.tr("_", "-")
|
|
69
|
+
entry = @metadata[normalized]
|
|
70
|
+
next unless entry
|
|
71
|
+
|
|
72
|
+
# If the icon is new for this style, update the caches
|
|
73
|
+
if style[:icon_cache].add?(normalized)
|
|
74
|
+
style[:unicode_cache].merge(entry["unicodes"])
|
|
75
|
+
style[:sass_icon_cache].add("\"#{ normalized }\": \\#{ entry['unicode'] }")
|
|
76
|
+
style[:changed] = true
|
|
77
|
+
else
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Takes an array of styles and creates a thread to subset each font file
|
|
87
|
+
def subset_webfonts(styles)
|
|
88
|
+
styles.map { | style | Thread.new(style) { subset_font(style) } }.each(&:join)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def subset_font(style)
|
|
92
|
+
file = style[:file]
|
|
93
|
+
out_file = @webfonts_dir.join(file)
|
|
94
|
+
cmd = "pyftsubset #{ @fonts_dir.join(file) } --flavor=woff2 --unicodes=#{ style[:unicode_cache].to_a.join(",") } --output-file=#{ out_file } --layout-features=* --no-hinting"
|
|
95
|
+
|
|
96
|
+
puts "[FontSubsetter][#{ style[:name] }] #{ cmd }"
|
|
97
|
+
stdout, stderr, status = Open3.capture3(cmd)
|
|
98
|
+
if status.success?
|
|
99
|
+
puts "[FontSubsetter][#{ style[:name] }] wrote #{ out_file }"
|
|
100
|
+
else
|
|
101
|
+
warn "[FontSubsetter][#{ style[:name] }] subset failed:\n#{ stderr }"
|
|
102
|
+
raise "pyftsubset failed (exit #{ status.exitstatus })"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def subset_stylesheets
|
|
107
|
+
styles = @styles.values.select { it[:sass_icon_cache].any? }
|
|
108
|
+
|
|
109
|
+
scss_template = FontawesomeSubsetter.configuration.scss_template
|
|
110
|
+
|
|
111
|
+
scss_string = if scss_template
|
|
112
|
+
scss_template.call(@styles)
|
|
113
|
+
else
|
|
114
|
+
default_scss_template(styles)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Compile the dynamic SCSS. `load_paths` tells Sass where to find the imported files.
|
|
118
|
+
compiled_css = Sass.compile_string(scss_string, style: :compressed, load_paths: [ @root ]).css
|
|
119
|
+
|
|
120
|
+
# Forcefully remove all comments, including "loud" /*! ... */ comments for licenses.
|
|
121
|
+
compiled_css = compiled_css.gsub(/\/\*!.*?\*\//m, "")
|
|
122
|
+
|
|
123
|
+
# Output to builds folder, which is the standard for Propshaft
|
|
124
|
+
out_file = @build_dir.join("fontawesome.css")
|
|
125
|
+
File.write(out_file, compiled_css)
|
|
126
|
+
puts "[FontSubsetter] wrote #{ out_file } with styles: #{ styles.map { it[:name] }.join(', ') }"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def build(files_to_scan = nil)
|
|
130
|
+
files_to_scan ||= @scan_globs.flat_map { Dir.glob it }
|
|
131
|
+
|
|
132
|
+
scan_files_and_update_caches(files_to_scan)
|
|
133
|
+
|
|
134
|
+
styles = @styles.values.select { it[:changed] }
|
|
135
|
+
|
|
136
|
+
# Rebuild assets only if caches have changed
|
|
137
|
+
unless styles.empty?
|
|
138
|
+
subset_webfonts(styles)
|
|
139
|
+
subset_stylesheets
|
|
140
|
+
@styles.values.each { it[:changed] = false }
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def build_watch
|
|
145
|
+
require "listen"
|
|
146
|
+
|
|
147
|
+
# Initial full scan and build
|
|
148
|
+
build()
|
|
149
|
+
|
|
150
|
+
puts "Starting FontAwesome watch mode..."
|
|
151
|
+
|
|
152
|
+
listener = Listen.to(*@watch_paths) do | modified, added, removed |
|
|
153
|
+
|
|
154
|
+
# NOTE: At most, it seems to take around 1.7 seconds to subset
|
|
155
|
+
start_time = Time.now
|
|
156
|
+
|
|
157
|
+
puts "Detected changes: modified=#{ modified }, added=#{ added }, removed=#{ removed }"
|
|
158
|
+
|
|
159
|
+
# Scan only modified and added files
|
|
160
|
+
build(modified + added)
|
|
161
|
+
|
|
162
|
+
elapsed = Time.now - start_time
|
|
163
|
+
puts "[FontSubsetter] Total subsetting time: #{ format('%.2f', elapsed) } seconds"
|
|
164
|
+
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
listener.start
|
|
168
|
+
|
|
169
|
+
# Keep the process alive
|
|
170
|
+
loop do
|
|
171
|
+
|
|
172
|
+
sleep 1
|
|
173
|
+
|
|
174
|
+
end
|
|
175
|
+
rescue Interrupt
|
|
176
|
+
puts "FontAwesome watch stopped"
|
|
177
|
+
listener&.stop
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
def default_scss_template(styles)
|
|
183
|
+
<<-SCSS
|
|
184
|
+
|
|
185
|
+
// NOTE: $icons and $brand-icons were changed in variables to just be empty arrays with !default.
|
|
186
|
+
@use "./vendor/fontawesome/scss/variables" with (
|
|
187
|
+
$font-path: "webfonts",
|
|
188
|
+
$icons: (#{ @styles.except("brands").values.reduce(Set.new) { | all_icons, style | all_icons.merge(style[:sass_icon_cache]) }.join(", ") }),
|
|
189
|
+
$brand-icons: (#{ @styles["brands"][:sass_icon_cache].to_a.join(", ") }));
|
|
190
|
+
|
|
191
|
+
@use "./vendor/fontawesome/scss/functions";
|
|
192
|
+
@use "./vendor/fontawesome/scss/mixins";
|
|
193
|
+
@use "./vendor/fontawesome/scss/core";
|
|
194
|
+
// @use "./vendor/fontawesome/scss/sizing";
|
|
195
|
+
// @use "./vendor/fontawesome/scss/widths";
|
|
196
|
+
// @use "./vendor/fontawesome/scss/list";
|
|
197
|
+
// @use "./vendor/fontawesome/scss/bordered";
|
|
198
|
+
@use "./vendor/fontawesome/scss/animated";
|
|
199
|
+
// @use "./vendor/fontawesome/scss/rotated-flipped";
|
|
200
|
+
// @use "./vendor/fontawesome/scss/stacked";
|
|
201
|
+
@use "./vendor/fontawesome/scss/icons";
|
|
202
|
+
|
|
203
|
+
#{ styles.map { "@use \"./vendor/fontawesome/scss/#{ it[:name] }.scss\";" }.join("\n") }
|
|
204
|
+
|
|
205
|
+
SCSS
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require_relative "fontawesome_subsetter/version"
|
|
2
|
+
require_relative "fontawesome_subsetter/icon_helper"
|
|
3
|
+
require_relative "fontawesome_subsetter/subsetter"
|
|
4
|
+
require_relative "fontawesome_subsetter/engine" if defined?(Rails)
|
|
5
|
+
|
|
6
|
+
module FontawesomeSubsetter
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
|
|
10
|
+
attr_writer :configuration
|
|
11
|
+
|
|
12
|
+
def configuration
|
|
13
|
+
@configuration ||= Configuration.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def configure
|
|
17
|
+
yield(configuration)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Configuration
|
|
23
|
+
|
|
24
|
+
attr_accessor :meta_path, :fonts_dir, :build_dir, :scan_globs, :watch_paths,
|
|
25
|
+
:watch_file_type_regex, :icon_regex, :scss_template, :styles
|
|
26
|
+
|
|
27
|
+
def initialize
|
|
28
|
+
@scan_globs = ["app/views/**/*.slim", "app/helpers/**/*.rb"]
|
|
29
|
+
@watch_file_type_regex = /\.(slim|rb)$/
|
|
30
|
+
@icon_regex = /icon\(\s*:(?<prefix>fas|far|fab|fad|fal|fat)\s*,\s*:(?<icon>[\w_\-]+)\b/
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require "puma/plugin"
|
|
2
|
+
|
|
3
|
+
class Fontawesome
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
Puma::Plugin.create do
|
|
7
|
+
|
|
8
|
+
attr_reader :puma_pid, :fontawesome_pid, :log_writer
|
|
9
|
+
|
|
10
|
+
def start(launcher)
|
|
11
|
+
@log_writer = launcher.log_writer
|
|
12
|
+
@puma_pid = $PROCESS_ID
|
|
13
|
+
|
|
14
|
+
launcher.events.on_booted do
|
|
15
|
+
|
|
16
|
+
@fontawesome_pid = fork do
|
|
17
|
+
|
|
18
|
+
Thread.new { monitor_puma }
|
|
19
|
+
|
|
20
|
+
begin
|
|
21
|
+
# Set up signal handling for clean shutdown
|
|
22
|
+
trap("INT") { exit 0 }
|
|
23
|
+
trap("TERM") { exit 0 }
|
|
24
|
+
|
|
25
|
+
require "fontawesome_subsetter"
|
|
26
|
+
subsetter = FontawesomeSubsetter::Subsetter.new
|
|
27
|
+
subsetter.build_watch
|
|
28
|
+
rescue => e
|
|
29
|
+
@log_writer.log "FontAwesome subsetter error: #{ e.message }"
|
|
30
|
+
@log_writer.log e.backtrace.join("\n")
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
in_background do
|
|
37
|
+
|
|
38
|
+
monitor_fontawesome
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
launcher.events.on_stopped { stop_fontawesome }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def stop_fontawesome
|
|
50
|
+
Process.waitpid(fontawesome_pid, Process::WNOHANG)
|
|
51
|
+
log "Stopping FontAwesome..."
|
|
52
|
+
Process.kill(:INT, fontawesome_pid) if fontawesome_pid
|
|
53
|
+
Process.wait(fontawesome_pid)
|
|
54
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def monitor_puma
|
|
58
|
+
monitor(:puma_dead?, "Detected Puma has gone away, stopping FontAwesome...")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def monitor_fontawesome
|
|
62
|
+
monitor(:fontawesome_dead?, "Detected FontAwesome has gone away, stopping Puma...")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def monitor(process_dead, message)
|
|
66
|
+
loop do
|
|
67
|
+
|
|
68
|
+
if send(process_dead)
|
|
69
|
+
log message
|
|
70
|
+
Process.kill(:INT, $PROCESS_ID)
|
|
71
|
+
break
|
|
72
|
+
end
|
|
73
|
+
sleep 2
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def fontawesome_dead?
|
|
79
|
+
Process.waitpid(fontawesome_pid, Process::WNOHANG)
|
|
80
|
+
false
|
|
81
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
|
82
|
+
true
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def puma_dead?
|
|
86
|
+
Process.ppid != puma_pid
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def log(...)
|
|
90
|
+
log_writer.log(...)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
namespace :fontawesome do
|
|
2
|
+
|
|
3
|
+
desc "Build FontAwesome, subsetting icons and shaking out unused CSS."
|
|
4
|
+
task :subset do
|
|
5
|
+
require "fontawesome_subsetter"
|
|
6
|
+
|
|
7
|
+
subsetter = FontawesomeSubsetter::Subsetter.new
|
|
8
|
+
subsetter.build()
|
|
9
|
+
|
|
10
|
+
puts "FontAwesome subset build completed successfully!"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Rake::Task["assets:precompile"].enhance(["fontawesome:subset"])
|
metadata
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fontawesome_subsetter
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- 16554289+optimuspwnius@users.noreply.github.com
|
|
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: railties
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: actionview
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '7.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '7.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: sass-embedded
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: listen
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '3.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: puma
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '5.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '5.0'
|
|
82
|
+
description: Scans your views and helpers for icon() calls, subsets FontAwesome Pro
|
|
83
|
+
WOFF2 fonts via pyftsubset, and generates minimal CSS. Includes a Puma plugin for
|
|
84
|
+
development watch mode, a Rails view helper, and a rake task that hooks into assets:precompile.
|
|
85
|
+
email:
|
|
86
|
+
- 16554289+optimuspwnius@users.noreply.github.com
|
|
87
|
+
executables: []
|
|
88
|
+
extensions: []
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
files:
|
|
91
|
+
- LICENSE.txt
|
|
92
|
+
- README.md
|
|
93
|
+
- lib/fontawesome_subsetter.rb
|
|
94
|
+
- lib/fontawesome_subsetter/engine.rb
|
|
95
|
+
- lib/fontawesome_subsetter/icon_helper.rb
|
|
96
|
+
- lib/fontawesome_subsetter/subsetter.rb
|
|
97
|
+
- lib/fontawesome_subsetter/version.rb
|
|
98
|
+
- lib/puma/plugin/fontawesome.rb
|
|
99
|
+
- lib/tasks/fontawesome_subsetter.rake
|
|
100
|
+
homepage: https://github.com/optimuspwnius/fontawesome-subsetter
|
|
101
|
+
licenses:
|
|
102
|
+
- MIT
|
|
103
|
+
metadata:
|
|
104
|
+
homepage_uri: https://github.com/optimuspwnius/fontawesome-subsetter
|
|
105
|
+
source_code_uri: https://github.com/optimuspwnius/fontawesome-subsetter
|
|
106
|
+
changelog_uri: https://github.com/optimuspwnius/fontawesome-subsetter/blob/main/CHANGELOG.md
|
|
107
|
+
allowed_push_host: https://rubygems.org
|
|
108
|
+
rdoc_options: []
|
|
109
|
+
require_paths:
|
|
110
|
+
- lib
|
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - ">="
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '3.2'
|
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - ">="
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: '0'
|
|
121
|
+
requirements: []
|
|
122
|
+
rubygems_version: 4.0.7
|
|
123
|
+
specification_version: 4
|
|
124
|
+
summary: FontAwesome Pro subsetter for Rails — tree-shakes unused icons from font
|
|
125
|
+
files and CSS.
|
|
126
|
+
test_files: []
|