Class: Fontico::Builder
- Inherits:
-
Object
- Object
- Fontico::Builder
- Defined in:
- lib/fontico/builder.rb
Overview
Resolve -> preprocess -> lock -> emit. Everything the rake task does.
Defined Under Namespace
Classes: Report
Constant Summary collapse
- PENDING =
Declared in manifests, emitter not landed yet. Skipped with a notice so the manifest can state intent without breaking the build.
%w[woff2].freeze
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(manifest, root: Dir.pwd, output: "app/assets/builds", offline: false, force: false) ⇒ Builder
constructor
A new instance of Builder.
Constructor Details
#initialize(manifest, root: Dir.pwd, output: "app/assets/builds", offline: false, force: false) ⇒ Builder
Returns a new instance of Builder.
15 16 17 18 19 20 21 22 23 |
# File 'lib/fontico/builder.rb', line 15 def initialize(manifest, root: Dir.pwd, output: "app/assets/builds", offline: false, force: false) @manifest = manifest @root = root @output = File.join(root, output) @offline = offline @force = force @lock = Lockfile.new(File.join(root, "icons.lock")) end |
Instance Method Details
#call ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/fontico/builder.rb', line 25 def call warnings = Hash.new { |h, k| h[k] = [] } cached, fetched = [], [] missing = {} # The lock caches Iconify bodies so deploys run offline. Local files # are already on disk: treating them as fresh meant editing logo.svg # rebuilt nothing. Force is `rake fontico:update` — re-fetch remotes, # keep the append-only codepoints. stale = @manifest.icons.select { _1.local? || @force || !@lock.fresh?(_1.name, _1.source) } cached = @manifest.icons.map(&:name) - stale.map(&:name) unless stale.empty? remote_stale = stale.reject(&:local?) # Local files do not need the network. Offline is a hard fail only # when a remote body has to come off the wire — which under force is # every remote, lock or no lock. Saying "missing" there would be a # lie: the bodies are present, force is what made them stale. if @offline && remote_stale.any? reason = @force ? "re-fetch #{remote_stale.size} icon(s)" : "resolve #{remote_stale.size} icon(s) missing from icons.lock" raise Error, "cannot #{reason} and --offline was given" end resolver = Resolver.new(@manifest, root: @root) sources = resolver.call(only: stale.map(&:name)) missing = resolver.missing stale.each do |icon| src = sources[icon.name] # Named in #missing already, and reported by the caller. It keeps # its codepoint and whatever the lock still holds, so fixing the # manifest entry is all it takes to bring it back. next if src.nil? pre = Preprocessor.new(icon, size: @manifest.size) .call(src.markup, width: src.width, height: src.height) # Locals are re-read every build, so "stale" does not mean changed. # Compare the stored digest to keep an untouched logo.svg counted as # cached rather than reported as a fetch that never happened. A # remote is judged by the wire, not the digest: under force it was # genuinely re-fetched even when it came back byte-identical. before = @lock.entry(icon.name)&.fetch("digest", nil) @lock.store(icon.name, source: icon.source, body: pre.body, multicolor: pre.multicolor, warnings: pre.warnings) unchanged = icon.local? && @lock.entry(icon.name)["digest"] == before (unchanged ? cached : fetched) << icon.name end end # Missing icons stay in the list: their codepoints must not be reissued # while the manifest still claims them. @lock.retire_missing!(@manifest.icons.map(&:name)) @lock.save! buildable = @manifest.icons.reject { missing.key?(_1.name) } buildable.each do |icon| found = @lock.warnings(icon.name) warnings[icon.name] = found if found.any? end written, skipped = emit(buildable) Report.new(written: written, warnings: warnings, skipped: skipped, cached: cached, fetched: fetched, missing: missing, pending: @manifest.targets & PENDING) end |