feat: add Prism and Assets
This commit is contained in:
1 parent
6b7850d756
commit
c149318d16
2 files changed
+118
No files matched your search
@@ -0,0 +1,68 @@
|
||||
require 'fileutils'
|
||||
|
||||
module Prism
|
||||
module Assets
|
||||
# Sprockets/Propshaft-lite precompile step: walks every file under
|
||||
# `source`, content-addresses it (sha256, first 8 hex chars) into
|
||||
# `name-digest.ext` under `output`, and writes a manifest.json mapping
|
||||
# the original relative path ("css/application.css") to the
|
||||
# fingerprinted one ("css/application-3f2a9c1e.css"). Re-running with
|
||||
# unchanged file content reproduces the exact same fingerprinted name
|
||||
# (pure function of the bytes, no timestamps involved), and any
|
||||
# fingerprinted file left over from a PREVIOUS compile that no logical
|
||||
# path points to anymore is deleted - the same cleanup
|
||||
# `rake assets:clean` does in Rails, done automatically on every compile
|
||||
# instead of as a separate step.
|
||||
class Compiler
|
||||
DIGEST_LENGTH = 8
|
||||
|
||||
def initialize(source:, output:)
|
||||
@source = File.expand_path(source)
|
||||
@output = File.expand_path(output)
|
||||
end
|
||||
|
||||
def compile!
|
||||
FileUtils.mkdir_p(@output)
|
||||
previous = Manifest.load(manifest_path).to_h.values
|
||||
manifest = Manifest.new
|
||||
|
||||
each_source_file do |absolute, logical_path|
|
||||
fingerprinted = fingerprint(logical_path, absolute)
|
||||
destination = File.join(@output, fingerprinted)
|
||||
FileUtils.mkdir_p(File.dirname(destination))
|
||||
FileUtils.cp(absolute, destination)
|
||||
manifest[logical_path] = fingerprinted
|
||||
end
|
||||
|
||||
stale = previous - manifest.to_h.values
|
||||
stale.each { |path| FileUtils.rm_f(File.join(@output, path)) }
|
||||
|
||||
manifest.save(manifest_path)
|
||||
manifest
|
||||
end
|
||||
|
||||
def manifest_path
|
||||
File.join(@output, 'manifest.json')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def each_source_file
|
||||
return unless File.directory?(@source)
|
||||
|
||||
Dir.glob(File.join(@source, '**', '*')).sort.each do |absolute|
|
||||
next unless File.file?(absolute)
|
||||
|
||||
yield absolute, absolute.delete_prefix("#{@source}/")
|
||||
end
|
||||
end
|
||||
|
||||
def fingerprint(logical_path, absolute)
|
||||
digest = Digest::SHA256.file(absolute).hexdigest[0, DIGEST_LENGTH]
|
||||
ext = File.extname(logical_path)
|
||||
base = logical_path.delete_suffix(ext)
|
||||
"#{base}-#{digest}#{ext}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
module Prism
|
||||
module Assets
|
||||
# Maps logical asset paths ("application.css") to fingerprinted paths
|
||||
# ("application-3f2a9c1e.css") the way Sprockets/Propshaft's
|
||||
# manifest.json does. Purely a lookup table plus JSON (de)serialization -
|
||||
# Compiler is what actually populates one.
|
||||
class Manifest
|
||||
AssetNotFound = Class.new(StandardError)
|
||||
|
||||
def initialize(assets = {})
|
||||
@assets = assets.transform_keys(&:to_s)
|
||||
end
|
||||
|
||||
attr_reader :assets
|
||||
|
||||
def []=(logical_path, fingerprinted_path)
|
||||
@assets[logical_path.to_s] = fingerprinted_path
|
||||
end
|
||||
|
||||
# Raises when the manifest is authoritative (production-style,
|
||||
# precompiled ahead of time) so a typo in a view never silently links
|
||||
# to a 404 - the same tradeoff Rails makes with
|
||||
# config.assets.unknown_asset_fallback = false.
|
||||
def lookup(logical_path)
|
||||
@assets.fetch(logical_path.to_s) do
|
||||
raise AssetNotFound, "asset #{logical_path.inspect} is not in the manifest " \
|
||||
"(known: #{@assets.keys.sort.join(', ')})"
|
||||
end
|
||||
end
|
||||
|
||||
def key?(logical_path)
|
||||
@assets.key?(logical_path.to_s)
|
||||
end
|
||||
|
||||
def to_h
|
||||
@assets.dup
|
||||
end
|
||||
|
||||
def save(path)
|
||||
File.write(path, JSON.pretty_generate(@assets))
|
||||
end
|
||||
|
||||
def self.load(path)
|
||||
return new unless File.file?(path)
|
||||
|
||||
new(JSON.parse(File.read(path)))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user