fontico 0.1.1 → 1.2.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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontico
4
- VERSION = "0.1.1"
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/fontico.rb CHANGED
@@ -23,6 +23,8 @@ module Fontico
23
23
  autoload :Sprite, "fontico/emitters/sprite"
24
24
  autoload :Font, "fontico/emitters/font"
25
25
  autoload :Stylesheet, "fontico/emitters/stylesheet"
26
+ autoload :Header, "fontico/emitters/header"
27
+ autoload :GfxFont, "fontico/emitters/gfx_font"
26
28
  end
27
29
 
28
30
  class << self
@@ -34,9 +36,53 @@ module Fontico
34
36
  def root = @root ||= Dir.pwd
35
37
  def output_dir = @output_dir ||= "app/assets/builds"
36
38
  def manifest_path = @manifest_path ||= File.join(root, "icons.yml")
37
- def manifest = @manifest ||= Manifest.load(manifest_path)
39
+ # Same shape as I18n.load_path: engines first, the app last, later wins
40
+ # a name. Unset, it is just the app's icons.yml (non-Rails, tests).
41
+ def load_path = @load_path || [manifest_path].select { File.file?(_1) }
42
+
43
+ def load_path=(paths)
44
+ @load_path = paths.nil? ? nil : Array(paths)
45
+ reset!
46
+ end
47
+
48
+ def manifest = @manifest ||= Manifest.load(*load_path)
38
49
  def sprite_file = File.join(root, output_dir, "icons.svg")
39
50
 
51
+ # Rendered markup for bare icon() calls, minus the sprite path. Keyed by
52
+ # name and bounded by the manifest, so it cannot grow with traffic.
53
+ def icon_cache = @icon_cache ||= {}
54
+
55
+ # Inline mode embeds the sprite in every response, and it used to be
56
+ # re-read off disk on every call: 13us and a fresh 10KB string per
57
+ # request, more than a whole page of icon() calls costs. Held instead,
58
+ # behind a stat, which is cheap next to the read and means a rebuild from
59
+ # anywhere — the dev watcher, or `rake fontico:build` in another terminal
60
+ # — is picked up without waiting for a reset!.
61
+ def sprite_markup
62
+ stamp = File.stat(sprite_file).then { [_1.mtime, _1.size] }
63
+ return @sprite_markup if @sprite_stamp == stamp
64
+
65
+ @sprite_stamp = stamp
66
+ @sprite_markup = File.read(sprite_file).freeze
67
+ end
68
+
69
+ # Rails engines that ship config/icons.yml (or icons.yml at the gem
70
+ # root), then the app's file. The app is last on purpose.
71
+ def discover(app)
72
+ files = []
73
+ app.railties.each do |rt|
74
+ next unless rt.is_a?(Rails::Engine)
75
+ next if rt.is_a?(Rails::Application)
76
+
77
+ root = rt.root
78
+ found = %w[config/icons.yml icons.yml].map { root.join(_1) }.find { File.file?(_1.to_s) }
79
+ files << found.to_s if found
80
+ end
81
+ # manifest_path goes in even when absent: the dev watcher has to notice
82
+ # an icons.yml created after boot. Manifest.load skips what isn't there.
83
+ (files.select { File.file?(_1) } << manifest_path).uniq
84
+ end
85
+
40
86
  # Inline mode embeds <symbol> definitions in the layout instead of
41
87
  # referencing an external file — required when assets are served from a
42
88
  # CDN, where cross-origin <use href> silently renders nothing.
@@ -85,13 +131,18 @@ module Fontico
85
131
  # a reference in Ruby source never goes stale.
86
132
  def codepoint(name)
87
133
  cp = lockfile.codepoint_for(name.to_s)
88
- raise Error, "no icon named #{name.inspect}; run rake fontico:build" if cp.nil?
134
+ raise Error, "no icon named #{name.inspect} in icons.lock" if cp.nil?
89
135
 
90
136
  cp
91
137
  end
92
138
 
93
139
  def glyph(name) = [codepoint(name)].pack("U")
94
- def reset! = (@manifest = @build_error = @missing_icons = nil)
140
+ # Drops everything derived from a build. The dev watcher calls this after
141
+ # every rebuild, which is what keeps the caches above honest.
142
+ def reset!
143
+ @manifest = @build_error = @missing_icons = nil
144
+ @icon_cache = @sprite_markup = @sprite_stamp = nil
145
+ end
95
146
 
96
147
  def configure = yield(self)
97
148
  end
@@ -4,43 +4,52 @@
4
4
  # is noise, and the message reads fine without it.
5
5
  def fontico_red(text) = $stdout.tty? ? "\e[31m#{text}\e[0m" : text
6
6
 
7
- namespace :fontico do
8
- desc "Build icon artifacts from icons.yml"
9
- task :build do
10
- require "fontico"
11
- report = Fontico.build
7
+ def fontico_print_report(report)
8
+ puts "fontico: #{report.written.size} artifact(s)"
9
+ report.written.each { puts " #{_1}" }
10
+ puts " fetched #{report.fetched.size}, cached #{report.cached.size}"
12
11
 
13
- puts "fontico: #{report.written.size} artifact(s)"
14
- report.written.each { puts " #{_1}" }
15
- puts " fetched #{report.fetched.size}, cached #{report.cached.size}"
12
+ # Loud, but not fatal: one bad name should not stop the other 199 icons
13
+ # from building. The artifacts simply come out without it.
14
+ if report.missing&.any?
15
+ puts
16
+ puts fontico_red("fontico: #{report.missing.size} icon(s) could not be resolved and were left out:")
17
+ report.missing.each { |name, reason| puts fontico_red(" #{name}: #{reason}") }
18
+ puts fontico_red(" fix the entry in icons.yml, then run rake fontico:build again")
19
+ puts
20
+ end
16
21
 
17
- # Loud, but not fatal: one bad name should not stop the other 199 icons
18
- # from building. The artifacts simply come out without it.
19
- if report.missing&.any?
20
- puts
21
- puts fontico_red("fontico: #{report.missing.size} icon(s) could not be resolved and were left out:")
22
- report.missing.each { |name, reason| puts fontico_red(" #{name}: #{reason}") }
23
- puts fontico_red(" fix the entry in icons.yml, then run rake fontico:build again")
24
- puts
25
- end
22
+ report.skipped.each do |target, names|
23
+ puts " #{target}: skipped #{names.size} multicolor icon(s): #{names.join(", ")}" if names.any?
24
+ end
25
+ puts " pending target(s): #{report.pending.join(", ")}" if report.pending.any?
26
26
 
27
- report.skipped.each do |target, names|
28
- puts " #{target}: skipped #{names.size} multicolor icon(s): #{names.join(", ")}" if names.any?
29
- end
30
- puts " pending target(s): #{report.pending.join(", ")}" if report.pending.any?
27
+ if report.warnings.any?
28
+ puts "\nfontico: #{report.warnings.size} icon(s) need fixing at the source:"
29
+ report.warnings.each { |name, list| puts " #{name}: #{list.join("; ")}" }
30
+ puts " see docs/icon-authoring.html"
31
+ end
32
+ end
31
33
 
32
- if report.warnings.any?
33
- puts "\nfontico: #{report.warnings.size} icon(s) need fixing at the source:"
34
- report.warnings.each { |name, list| puts " #{name}: #{list.join("; ")}" }
35
- puts " see docs/icon-authoring.html"
36
- end
34
+ def fontico_rails_load_path!
35
+ return unless defined?(Rails) && Rails.respond_to?(:application) && Rails.application
36
+
37
+ Fontico.load_path = Fontico.discover(Rails.application)
38
+ end
39
+
40
+ namespace :fontico do
41
+ desc "Build icon artifacts from icons.yml"
42
+ task :build do
43
+ require "fontico"
44
+ fontico_rails_load_path!
45
+ fontico_print_report(Fontico.build)
37
46
  end
38
47
 
39
- desc "Re-fetch every icon, ignoring icons.lock"
48
+ desc "Re-fetch every icon from its provider. Codepoints stay pinned."
40
49
  task :update do
41
50
  require "fontico"
42
- File.delete(File.join(Fontico.root, "icons.lock")) if File.exist?(File.join(Fontico.root, "icons.lock"))
43
- Rake::Task["fontico:build"].invoke
51
+ fontico_rails_load_path!
52
+ fontico_print_report(Fontico.build(force: true))
44
53
  end
45
54
  end
46
55
 
@@ -58,10 +67,13 @@ if Rake::Task.task_defined?("assets:clobber")
58
67
  end
59
68
 
60
69
  namespace :fontico do
70
+ # Only what lands in output_dir. A target given an explicit path in the
71
+ # manifest is pointing somewhere the app owns — a firmware include/ that is
72
+ # very likely committed — and deleting that is not precompile's business.
61
73
  desc "Remove generated icon artifacts (icons.lock is kept: it is source)"
62
74
  task :clobber do
63
75
  require "fontico"
64
- %w[icons.svg icons.css icons.ttf].each do |name|
76
+ %w[icons.svg icons.css icons.ttf icons.h icons_font.h].each do |name|
65
77
  path = File.join(Fontico.root, Fontico.output_dir, name)
66
78
  File.delete(path) if File.exist?(path)
67
79
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fontico
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nofxx
@@ -86,6 +86,8 @@ files:
86
86
  - lib/fontico.rb
87
87
  - lib/fontico/builder.rb
88
88
  - lib/fontico/emitters/font.rb
89
+ - lib/fontico/emitters/gfx_font.rb
90
+ - lib/fontico/emitters/header.rb
89
91
  - lib/fontico/emitters/sprite.rb
90
92
  - lib/fontico/emitters/stylesheet.rb
91
93
  - lib/fontico/helper.rb
@@ -95,6 +97,7 @@ files:
95
97
  - lib/fontico/node/build_font.mjs
96
98
  - lib/fontico/node/extract_glyphs.mjs
97
99
  - lib/fontico/node/package.json
100
+ - lib/fontico/node/rasterize.mjs
98
101
  - lib/fontico/node_runner.rb
99
102
  - lib/fontico/outliner.rb
100
103
  - lib/fontico/prawn.rb
@@ -126,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
129
  - !ruby/object:Gem::Version
127
130
  version: '0'
128
131
  requirements: []
129
- rubygems_version: 3.6.7
132
+ rubygems_version: 3.6.9
130
133
  specification_version: 4
131
134
  summary: Name icons by intent. Source them from anywhere. Ship one artifact.
132
135
  test_files: []