Skip to content

Commit

Permalink
Digests of files that can have dependencies on other files in the loa…
Browse files Browse the repository at this point in the history
…d path need to reflect those dependencies
dhh committed May 17, 2024

Verified

This commit was signed with the committer’s verified signature.
renovate-bot Mend Renovate
1 parent 6d6969d commit 0b02b0d
Showing 16 changed files with 106 additions and 27 deletions.
2 changes: 1 addition & 1 deletion lib/propshaft/assembly.rb
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ def initialize(config)
end

def load_path
@load_path ||= Propshaft::LoadPath.new(config.paths, version: config.version)
@load_path ||= Propshaft::LoadPath.new(config.paths, compilers: compilers, version: config.version)
end

def resolver
12 changes: 8 additions & 4 deletions lib/propshaft/asset.rb
Original file line number Diff line number Diff line change
@@ -2,10 +2,10 @@
require "action_dispatch/http/mime_type"

class Propshaft::Asset
attr_reader :path, :logical_path, :version
attr_reader :path, :logical_path, :load_path

def initialize(path, logical_path:, version: nil)
@path, @logical_path, @version = path, Pathname.new(logical_path), version
def initialize(path, logical_path:, load_path:)
@path, @logical_path, @load_path = path, Pathname.new(logical_path), load_path
end

def content
@@ -21,7 +21,7 @@ def length
end

def digest
@digest ||= Digest::SHA1.hexdigest("#{content}#{version}").first(8)
@digest ||= Digest::SHA1.hexdigest("#{content_with_compile_dependencies}#{load_path.version}").first(8)
end

def digested_path
@@ -44,4 +44,8 @@ def ==(other_asset)
def already_digested?
logical_path.to_s =~ /-([0-9a-zA-Z_-]{7,128})\.digested/
end

def content_with_compile_dependencies
content + load_path.find_compiler_dependencies_to(self).collect(&:content).join
end
end
6 changes: 5 additions & 1 deletion lib/propshaft/compiler.rb
Original file line number Diff line number Diff line change
@@ -9,10 +9,14 @@ def initialize(assembly)
end

# Override this in a specific compiler
def compile(logical_path, input)
def compile(asset)
raise NotImplementedError
end

def find_dependencies(asset)
Set.new
end

private
def url_prefix
@url_prefix ||= File.join(assembly.config.relative_url_root.to_s, assembly.config.prefix.to_s).chomp("/")
17 changes: 15 additions & 2 deletions lib/propshaft/compiler/css_asset_urls.rb
Original file line number Diff line number Diff line change
@@ -5,8 +5,21 @@
class Propshaft::Compiler::CssAssetUrls < Propshaft::Compiler
ASSET_URL_PATTERN = /url\(\s*["']?(?!(?:\#|%23|data|http|\/\/))([^"'\s?#)]+)([#?][^"')]+)?\s*["']?\)/

def compile(logical_path, input)
input.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(logical_path.dirname, $1), logical_path, $2, $1 }
def compile(asset)
asset.content.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(asset.logical_path.dirname, $1), asset.logical_path, $2, $1 }
end

def find_dependencies(asset)
Set.new.tap do |dependencies|
asset.content.scan(ASSET_URL_PATTERN).each do |dependent_asset_url, _|
dependent_asset = assembly.load_path.find(resolve_path(asset.logical_path.dirname, dependent_asset_url))

if dependencies.exclude?(dependent_asset)
dependencies << dependent_asset
dependencies.merge(find_dependencies(dependent_asset))
end
end
end
end

private
4 changes: 2 additions & 2 deletions lib/propshaft/compiler/source_mapping_urls.rb
Original file line number Diff line number Diff line change
@@ -5,8 +5,8 @@
class Propshaft::Compiler::SourceMappingUrls < Propshaft::Compiler
SOURCE_MAPPING_PATTERN = %r{(//|/\*)# sourceMappingURL=(.+\.map)(\s*?\*\/)?\s*?\Z}

def compile(logical_path, input)
input.gsub(SOURCE_MAPPING_PATTERN) { source_mapping_url(logical_path, asset_path($2, logical_path), $1, $3) }
def compile(asset)
asset.content.gsub(SOURCE_MAPPING_PATTERN) { source_mapping_url(asset.logical_path, asset_path($2, asset.logical_path), $1, $3) }
end

private
12 changes: 11 additions & 1 deletion lib/propshaft/compilers.rb
Original file line number Diff line number Diff line change
@@ -23,11 +23,21 @@ def compile(asset)
if relevant_registrations = registrations[asset.content_type.to_s]
asset.content.dup.tap do |input|
relevant_registrations.each do |compiler|
input.replace compiler.new(assembly).compile(asset.logical_path, input)
input.replace compiler.new(assembly).compile(asset)
end
end
else
asset.content
end
end

def find_dependencies(asset)
Set.new.tap do |dependencies|
if relevant_registrations = registrations[asset.content_type.to_s]
relevant_registrations.each do |compiler|
dependencies.merge compiler.new(assembly).find_dependencies(asset)
end
end
end
end
end
13 changes: 8 additions & 5 deletions lib/propshaft/load_path.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
require "propshaft/asset"

class Propshaft::LoadPath
attr_reader :paths, :version
attr_reader :paths, :compilers, :version

def initialize(paths = [], version: nil)
@paths = dedup(paths)
@version = version
def initialize(paths = [], compilers: nil, version: nil)
@paths, @compilers, @version = dedup(paths), compilers, version
end

def find(asset_name)
assets_by_path[asset_name]
end

def find_compiler_dependencies_to(asset)
compilers&.find_dependencies(asset) || Set.new
end

def assets(content_types: nil)
if content_types
assets_by_path.values.select { |asset| asset.content_type.in?(content_types) }
@@ -48,7 +51,7 @@ def assets_by_path
paths.each do |path|
without_dotfiles(all_files_from_tree(path)).each do |file|
logical_path = file.relative_path_from(path)
mapped[logical_path.to_s] ||= Propshaft::Asset.new(file, logical_path: logical_path, version: version)
mapped[logical_path.to_s] ||= Propshaft::Asset.new(file, logical_path: logical_path, load_path: self)
end if path.exist?
end
end
1 change: 1 addition & 0 deletions test/fixtures/assets/first_path/dependent/a.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import url('b.css')
1 change: 1 addition & 0 deletions test/fixtures/assets/first_path/dependent/b.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import url('c.css')
3 changes: 3 additions & 0 deletions test/fixtures/assets/first_path/dependent/c.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
p {
color: red;
}
43 changes: 42 additions & 1 deletion test/propshaft/asset_test.rb
Original file line number Diff line number Diff line change
@@ -54,10 +54,51 @@ class Propshaft::AssetTest < ActiveSupport::TestCase
assert_equal asset.digest.object_id, asset.digest.object_id
end

test "digest depends on first level of compiler dependency" do
open_asset_with_reset("dependent/b.css") do |asset_file|
digest_before_dependency_change = find_asset("dependent/a.css").digest

asset_file.write "changes!"
asset_file.flush

digest_after_dependency_change = find_asset("dependent/a.css").digest

assert_not_equal digest_before_dependency_change, digest_after_dependency_change
end
end

test "digest depends on second level of compiler dependency" do
open_asset_with_reset("dependent/c.css") do |asset_file|
digest_before_dependency_change = find_asset("dependent/a.css").digest

asset_file.write "changes!"
asset_file.flush

digest_after_dependency_change = find_asset("dependent/a.css").digest

assert_not_equal digest_before_dependency_change, digest_after_dependency_change
end
end

private
def find_asset(logical_path)
root_path = Pathname.new("#{__dir__}/../fixtures/assets/first_path")
path = root_path.join(logical_path)
Propshaft::Asset.new(path, logical_path: logical_path)

assembly = Propshaft::Assembly.new(ActiveSupport::OrderedOptions.new.tap { |config|
config.paths = [ root_path ]
config.compilers = [[ "text/css", Propshaft::Compiler::CssAssetUrls ]]
})

Propshaft::Asset.new(path, logical_path: logical_path, load_path: assembly.load_path)
end

def open_asset_with_reset(logical_path)
dependency_path = Pathname.new("#{__dir__}/../fixtures/assets/first_path/#{logical_path}")
existing_dependency_content = File.read(dependency_path)

File.open(dependency_path, "a") { |f| yield f }
ensure
File.write(dependency_path, existing_dependency_content)
end
end
7 changes: 4 additions & 3 deletions test/propshaft/compiler/css_asset_urls_test.rb
Original file line number Diff line number Diff line change
@@ -130,10 +130,11 @@ def compile_asset_with_content(content)
root_path = Pathname.new("#{__dir__}/../../fixtures/assets/vendor")
logical_path = "foobar/source/test.css"

asset = Propshaft::Asset.new(root_path.join(logical_path), logical_path: logical_path)
assembly = Propshaft::Assembly.new(@options)
assembly.compilers.register "text/css", Propshaft::Compiler::CssAssetUrls

asset = Propshaft::Asset.new(root_path.join(logical_path), logical_path: logical_path, load_path: assembly.load_path)
asset.stub :content, content do
assembly = Propshaft::Assembly.new(@options)
assembly.compilers.register "text/css", Propshaft::Compiler::CssAssetUrls
assembly.compilers.compile(asset)
end
end
2 changes: 1 addition & 1 deletion test/propshaft/compilers_test.rb
Original file line number Diff line number Diff line change
@@ -20,6 +20,6 @@ class Propshaft::CompilersTest < ActiveSupport::TestCase
private
def find_asset(logical_path)
root_path = Pathname.new("#{__dir__}/../fixtures/assets/first_path")
Propshaft::Asset.new(root_path.join(logical_path), logical_path: logical_path)
Propshaft::Asset.new(root_path.join(logical_path), logical_path: logical_path, load_path: Propshaft::LoadPath.new([ root_path ]))
end
end
6 changes: 2 additions & 4 deletions test/propshaft/load_path_test.rb
Original file line number Diff line number Diff line change
@@ -72,9 +72,7 @@ class Propshaft::LoadPathTest < ActiveSupport::TestCase

private
def find_asset(logical_path)
Propshaft::Asset.new(
Pathname.new("#{__dir__}/../fixtures/assets/first_path/#{logical_path}"),
logical_path: Pathname.new(logical_path)
)
root_path = Pathname.new("#{__dir__}/../fixtures/assets/first_path")
Propshaft::Asset.new(root_path.join(logical_path), logical_path: logical_path, load_path: Propshaft::LoadPath.new([ root_path ]))
end
end
2 changes: 1 addition & 1 deletion test/propshaft/output_path_test.rb
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ class Propshaft::OutputPathTest < ActiveSupport::TestCase

private
def output_asset(filename, content, created_at: Time.now)
asset = Propshaft::Asset.new(nil, logical_path: filename)
asset = Propshaft::Asset.new(nil, logical_path: filename, load_path: Propshaft::LoadPath.new)
asset.stub :content, content do
output_path = @output_path.path.join(asset.digested_path)
`touch -mt #{created_at.strftime('%y%m%d%H%M')} #{output_path}`
2 changes: 1 addition & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -14,6 +14,6 @@ class ActiveSupport::TestCase
def find_asset(logical_path, fixture_path:)
root_path = Pathname.new("#{__dir__}/fixtures/assets/#{fixture_path}")
path = root_path.join(logical_path)
Propshaft::Asset.new(path, logical_path: logical_path)
Propshaft::Asset.new(path, logical_path: logical_path, load_path: Propshaft::LoadPath.new([ root_path ]))
end
end

0 comments on commit 0b02b0d

Please sign in to comment.