Skip to content

Commit

Permalink
Adds support for relative url root (#138)
Browse files Browse the repository at this point in the history
* Adds support for relative url root

* Creates base compiler class to hold url prefix
  • Loading branch information
markstanley-nps authored Jun 2, 2023
1 parent 69c285f commit 26082d0
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 84 deletions.
6 changes: 3 additions & 3 deletions benchmarks/css_asset_urls
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ require "open-uri"
require_relative "./trackrod"
require_relative "../lib/propshaft"
require_relative "../lib/propshaft/compilers"
require_relative "../lib/propshaft/compilers/css_asset_urls"
require_relative "../lib/propshaft/compiler/css_asset_urls"

trackrod = Trackrod.new(Dir.mktmpdir)
trackrod.build

assets = ActiveSupport::OrderedOptions.new
assets.paths = [ trackrod.root ]
assets.prefix = "/assets"
assets.compilers = [ [ "text/css", Propshaft::Compilers::CssAssetUrls ] ]
assets.compilers = [ [ "text/css", Propshaft::Compiler::CssAssetUrls ] ]

assembly = Propshaft::Assembly.new(assets)
asset = assembly.load_path.find(trackrod.assets.css)
compiler = Propshaft::Compilers::CssAssetUrls.new(assembly)
compiler = Propshaft::Compiler::CssAssetUrls.new(assembly)

Benchmark.ips do |x|
x.config(time: 5, warmup: 2)
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/dynamic_resolver
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ trackrod.build
assets = ActiveSupport::OrderedOptions.new
assets.paths = [ trackrod.root ]
assets.prefix = "/assets"
assets.compilers = [ [ "text/css", Propshaft::Compilers::CssAssetUrls ] ]
assets.compilers = [ [ "text/css", Propshaft::Compiler::CssAssetUrls ] ]
assets.output_path ||= Pathname.new(Dir.mktmpdir)

assembly = Propshaft::Assembly.new(assets)
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/static_resolver
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ trackrod.build
assets = ActiveSupport::OrderedOptions.new
assets.paths = [ trackrod.root ]
assets.prefix = "/assets"
assets.compilers = [ [ "text/css", Propshaft::Compilers::CssAssetUrls ] ]
assets.compilers = [ [ "text/css", Propshaft::Compiler::CssAssetUrls ] ]
assets.output_path ||= Pathname.new(Dir.mktmpdir)

assembly = Propshaft::Assembly.new(assets)
Expand Down
4 changes: 2 additions & 2 deletions lib/propshaft/assembly.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
require "propshaft/server"
require "propshaft/processor"
require "propshaft/compilers"
require "propshaft/compilers/css_asset_urls"
require "propshaft/compilers/source_mapping_urls"
require "propshaft/compiler/css_asset_urls"
require "propshaft/compiler/source_mapping_urls"

class Propshaft::Assembly
attr_reader :config
Expand Down
20 changes: 20 additions & 0 deletions lib/propshaft/compiler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

# Base compiler from which other compilers can inherit
class Propshaft::Compiler
attr_reader :assembly

def initialize(assembly)
@assembly = assembly
end

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

private
def url_prefix
@url_prefix ||= File.join(assembly.config.relative_url_root.to_s, assembly.config.prefix.to_s).chomp("/")
end
end
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
# frozen_string_literal: true

class Propshaft::Compilers::CssAssetUrls
attr_reader :assembly
require "propshaft/compiler"

class Propshaft::Compiler::CssAssetUrls < Propshaft::Compiler
ASSET_URL_PATTERN = /url\(\s*["']?(?!(?:\#|%23|data|http|\/\/))([^"'\s?#)]+)([#?][^"')]+)?\s*["']?\)/

def initialize(assembly)
@assembly = assembly
end

def compile(logical_path, input)
input.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(logical_path.dirname, $1), logical_path, $2, $1 }
end
Expand All @@ -26,7 +22,7 @@ def resolve_path(directory, filename)

def asset_url(resolved_path, logical_path, fingerprint, pattern)
if asset = assembly.load_path.find(resolved_path)
%[url("#{assembly.config.prefix}/#{asset.digested_path}#{fingerprint}")]
%[url("#{url_prefix}/#{asset.digested_path}#{fingerprint}")]
else
Propshaft.logger.warn "Unable to resolve '#{pattern}' for missing asset '#{resolved_path}' in #{logical_path}"
%[url("#{pattern}")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
# frozen_string_literal: true

class Propshaft::Compilers::SourceMappingUrls
attr_reader :assembly
require "propshaft/compiler"

class Propshaft::Compiler::SourceMappingUrls < Propshaft::Compiler
SOURCE_MAPPING_PATTERN = %r{^(//|/\*)# sourceMappingURL=(.+\.map)}

def initialize(assembly)
@assembly = assembly
end

def compile(logical_path, input)
input.gsub(SOURCE_MAPPING_PATTERN) { source_mapping_url(asset_path($2, logical_path), $1) }
end
Expand All @@ -24,7 +20,7 @@ def asset_path(source_mapping_url, logical_path)

def source_mapping_url(resolved_path, comment)
if asset = assembly.load_path.find(resolved_path)
"#{comment}# sourceMappingURL=#{assembly.config.prefix}/#{asset.digested_path}"
"#{comment}# sourceMappingURL=#{url_prefix}/#{asset.digested_path}"
else
Propshaft.logger.warn "Removed sourceMappingURL comment for missing asset '#{resolved_path}' from #{resolved_path}"
comment
Expand Down
8 changes: 5 additions & 3 deletions lib/propshaft/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ class Railtie < ::Rails::Railtie
config.assets.prefix = "/assets"
config.assets.quiet = false
config.assets.compilers = [
[ "text/css", Propshaft::Compilers::CssAssetUrls ],
[ "text/css", Propshaft::Compilers::SourceMappingUrls ],
[ "text/javascript", Propshaft::Compilers::SourceMappingUrls ]
[ "text/css", Propshaft::Compiler::CssAssetUrls ],
[ "text/css", Propshaft::Compiler::SourceMappingUrls ],
[ "text/javascript", Propshaft::Compiler::SourceMappingUrls ]
]
config.assets.sweep_cache = Rails.env.development?
config.assets.server = Rails.env.development? || Rails.env.test?
config.assets.relative_url_root = nil

# Register propshaft initializer to copy the assets path in all the Rails Engines.
# This makes possible for us to keep all `assets` config in this Railtie, but still
Expand All @@ -30,6 +31,7 @@ class Railtie < ::Rails::Railtie
end

config.after_initialize do |app|
config.assets.relative_url_root ||= app.config.relative_url_root
config.assets.output_path ||=
Pathname.new(File.join(app.config.paths["public"].first, app.config.assets.prefix))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
require "propshaft/assembly"
require "propshaft/compilers"

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

@assembly.compilers.register "text/css", Propshaft::Compilers::CssAssetUrls
}
end

test "basic" do
Expand Down Expand Up @@ -120,14 +118,23 @@ class Propshaft::Compilers::CssAssetUrlsTest < ActiveSupport::TestCase
assert_match(/{ background: url\("file-not-found.jpg"\); }/, compiled)
end

test "relative url root" do
@options.relative_url_root = "/url-root"

compiled = compile_asset_with_content(%({ background: url(file.jpg); }))
assert_match(/{ background: url\("\/url-root\/assets\/foobar\/source\/file-[a-z0-9]{40}.jpg"\); }/, compiled)
end

private
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)
asset.stub :content, content do
@assembly.compilers.compile(asset)
assembly = Propshaft::Assembly.new(@options)
assembly.compilers.register "text/css", Propshaft::Compiler::CssAssetUrls
assembly.compilers.compile(asset)
end
end
end
66 changes: 66 additions & 0 deletions test/propshaft/compiler/source_mapping_urls_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require "test_helper"
require "minitest/mock"
require "propshaft/asset"
require "propshaft/assembly"
require "propshaft/compilers"

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

test "matching source map" do
assert_match %r{//# sourceMappingURL=/assets/source.js-[a-z0-9]{40}\.map},
compile_asset(find_asset("source.js", fixture_path: "mapped"))
assert_match %r{/\*# sourceMappingURL=/assets/source.css-[a-z0-9]{40}\.map},
compile_asset(find_asset("source.css", fixture_path: "mapped"))
end

test "matching nested source map" do
assert_match %r{//# sourceMappingURL=/assets/nested/another-source.js-[a-z0-9]{40}\.map},
compile_asset(find_asset("nested/another-source.js", fixture_path: "mapped"))
end

test "missing source map" do
assert_no_match %r{sourceMappingURL},
compile_asset(find_asset("sourceless.js", fixture_path: "mapped"))
assert_no_match %r{sourceMappingURL},
compile_asset(find_asset("sourceless.css", fixture_path: "mapped"))
end

test "sourceMappingURL removal due to missing map does not damage /* ... */ comments" do
assert_match %r{\A#{Regexp.escape ".failure { color: red; }\n/* */\n"}\Z},
compile_asset(find_asset("sourceless.css", fixture_path: "mapped"))
end

test "sourceMappingURL outside of a comment should be left alone" do
assert_match %r{sourceMappingURL=sourceMappingURL-outside-comment.css.map},
compile_asset(find_asset("sourceMappingURL-outside-comment.css", fixture_path: "mapped"))
end

test "sourceMappingURL not at the beginning of the line should be left alone" do
assert_match %r{sourceMappingURL=sourceMappingURL-not-at-start.css.map},
compile_asset(find_asset("sourceMappingURL-not-at-start.css", fixture_path: "mapped"))
end

test "relative url root" do
@options.relative_url_root = "/url-root"

assert_match %r{//# sourceMappingURL=/url-root/assets/source.js-[a-z0-9]{40}\.map},
compile_asset(find_asset("source.js", fixture_path: "mapped"))
end

private
def compile_asset(asset)

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

assembly.compilers.compile(asset)
end
end
52 changes: 0 additions & 52 deletions test/propshaft/compilers/source_mapping_urls_test.rb

This file was deleted.

2 changes: 1 addition & 1 deletion test/propshaft/compilers_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Propshaft::CompilersTest < ActiveSupport::TestCase
end

test "replace asset-path function in css with digested url" do
@assembly.compilers.register "text/css", Propshaft::Compilers::CssAssetUrls
@assembly.compilers.register "text/css", Propshaft::Compiler::CssAssetUrls
assert_match(/"\/assets\/archive-[a-z0-9]{40}.svg/, @assembly.compilers.compile(find_asset("another.css")))
end

Expand Down
2 changes: 1 addition & 1 deletion test/propshaft/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Propshaft::ServerTest < ActiveSupport::TestCase
config.output_path = Pathname.new("#{__dir__}../fixtures/output")
})

@assembly.compilers.register "text/css", Propshaft::Compilers::CssAssetUrls
@assembly.compilers.register "text/css", Propshaft::Compiler::CssAssetUrls
@server = Propshaft::Server.new(@assembly)
end

Expand Down

0 comments on commit 26082d0

Please sign in to comment.