Skip to content

Commit

Permalink
Add Propshaft::Compiler::JsAssetUrls
Browse files Browse the repository at this point in the history
  • Loading branch information
keiththomps committed Sep 27, 2024
1 parent 48df362 commit a28e06c
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
46 changes: 46 additions & 0 deletions lib/propshaft/compiler/js_asset_urls.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

require "propshaft/compiler"

class Propshaft::Compiler::JsAssetUrls < Propshaft::Compiler
ASSET_URL_PATTERN = %r{rails_asset_url\(\s*["']?(?!(?:\#|%23|data|http|//))([^"'\s?#)]+)([#?][^"')]+)?\s*["']?\)}

def compile(asset, input)
input.gsub(ASSET_URL_PATTERN) { asset_url(resolve_path(asset.logical_path.dirname, ::Regexp.last_match(1)), asset.logical_path, ::Regexp.last_match(2), ::Regexp.last_match(1)) }
end

def referenced_by(asset, references: Set.new)
asset.content.scan(ASSET_URL_PATTERN).each do |referenced_asset_url, _|
referenced_asset = load_path.find(resolve_path(asset.logical_path.dirname, referenced_asset_url))

if referenced_asset && references.exclude?(referenced_asset)
references << referenced_asset
references.merge referenced_by(referenced_asset, references: references)
end
end

references
end

private

def resolve_path(directory, filename)
if filename.start_with?("../")
Pathname.new(directory + filename).relative_path_from("").to_s
elsif filename.start_with?("/")
filename.delete_prefix("/").to_s
else
(directory + filename.delete_prefix("./")).to_s
end
end

def asset_url(resolved_path, logical_path, fingerprint, pattern)
asset = load_path.find(resolved_path)
if asset
%["#{url_prefix}/#{asset.digested_path}#{fingerprint}"]
else
Propshaft.logger.warn("Unable to resolve '#{pattern}' for missing asset '#{resolved_path}' in #{logical_path}")
%["#{pattern}"]
end
end
end
65 changes: 65 additions & 0 deletions test/propshaft/compiler/js_asset_urls_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

require "test_helper"
require "minitest/mock"
require "propshaft/asset"
require "propshaft/assembly"
require "propshaft/compilers"

module Propshaft
class Compiler
class JsAssetUrlsTest < ActiveSupport::TestCase
setup do
@options = ActiveSupport::OrderedOptions.new.tap do |config|
config.paths = [Pathname.new("#{__dir__}/../../fixtures/assets/vendor")]
config.output_path = Pathname.new("#{__dir__}/../../fixtures/output")
config.prefix = "/assets"
end
end

test "the asset exists" do
js_content = <<~JS
export default class extends Controller {
init() {
this.img = rails_asset_url("/foobar/source/file.svg");
}
}
JS

compiled = compile_asset_with_content(js_content)

assert_match(%r{this\.img = "/assets/foobar/source/file-[a-z0-9]{8}.svg"\;}, compiled)
end

test "the asset does not exist" do
js_content = <<~JS
export default class extends Controller {
init() {
this.img = rails_asset_url("missing.svg");
}
}
JS

compiled = compile_asset_with_content(js_content)

assert_match(/this\.img = "missing.svg"\;/, compiled)
end

private

def compile_asset_with_content(content)
# This has one more set of .. than it would in the propshaft repo
root_path = Pathname.new("#{__dir__}/../../fixtures/assets/vendor")
logical_path = "foobar/source/test.js"

assembly = Propshaft::Assembly.new(@options)
assembly.compilers.register("text/javascript", Propshaft::Compiler::JsAssetUrls)

asset = Propshaft::Asset.new(root_path.join(logical_path), logical_path: logical_path, load_path: assembly.load_path)
asset.stub(:content, content) do
assembly.compilers.compile(asset)
end
end
end
end
end

0 comments on commit a28e06c

Please sign in to comment.