Skip to content

Commit 463a502

Browse files
authored
Provide :all and :app helpers for stylesheet references via cached methods (#190)
* Cache asset paths by methods * Fix references * Add test for app globing * Ensure we only glob from the app root * Test that :app really only pulls from app/assets * Document :app for helper
1 parent 59406ab commit 463a502

File tree

6 files changed

+54
-21
lines changed

6 files changed

+54
-21
lines changed

lib/propshaft/helper.rb

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,27 @@ def compute_asset_path(path, options = {})
44
Rails.application.assets.resolver.resolve(path) || raise(MissingAssetError.new(path))
55
end
66

7-
# Add an option to call `stylesheet_link_tag` with `:all` to include every css file found on the load path.
7+
# Add an option to call `stylesheet_link_tag` with `:all` to include every css file found on the load path
8+
# or `:app` to include css files found in `Rails.root("app/assets/**/*.css")`, which will exclude lib/ and plugins.
89
def stylesheet_link_tag(*sources, **options)
9-
if sources.first == :all
10-
super(*all_stylesheets_paths, **options)
10+
case sources.first
11+
when :all
12+
super(*all_stylesheets_paths , **options)
13+
when :app
14+
super(*app_stylesheets_paths , **options)
1115
else
1216
super
1317
end
1418
end
1519

1620
# Returns a sorted and unique array of logical paths for all stylesheets in the load path.
1721
def all_stylesheets_paths
18-
Rails.application.assets.load_path
19-
.assets(content_types: [ Mime::EXTENSION_LOOKUP["css"] ])
20-
.collect { |css| css.logical_path.to_s }
21-
.sort
22-
.uniq
22+
Rails.application.assets.load_path.asset_paths_by_type("css")
23+
end
24+
25+
# Returns a sorted and unique array of logical paths for all stylesheets in app/assets/**/*.css.
26+
def app_stylesheets_paths
27+
Rails.application.assets.load_path.asset_paths_by_glob("#{Rails.root.join("app/assets")}/**/*.css")
2328
end
2429
end
2530
end

lib/propshaft/load_path.rb

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ def find_referenced_by(asset)
1515
compilers.referenced_by(asset).delete(self)
1616
end
1717

18-
def assets(content_types: nil)
19-
if content_types
20-
assets_by_path.values.select { |asset| asset.content_type.in?(content_types) }
21-
else
22-
assets_by_path.values
23-
end
18+
def assets
19+
assets_by_path.values
20+
end
21+
22+
def asset_paths_by_type(content_type)
23+
(@cached_asset_paths_by_type ||= Hash.new)[content_type] ||=
24+
extract_logical_paths_from(assets.select { |a| a.content_type == Mime::EXTENSION_LOOKUP[content_type] })
25+
end
26+
27+
def asset_paths_by_glob(glob)
28+
(@cached_asset_paths_by_glob ||= Hash.new)[glob] ||=
29+
extract_logical_paths_from(assets.select { |a| a.path.fnmatch?(glob) })
2430
end
2531

2632
def manifest
@@ -61,12 +67,18 @@ def all_files_from_tree(path)
6167
path.children.flat_map { |child| child.directory? ? all_files_from_tree(child) : child }
6268
end
6369

70+
def extract_logical_paths_from(assets)
71+
assets.collect { |asset| asset.logical_path.to_s }.sort
72+
end
73+
6474
def without_dotfiles(files)
6575
files.reject { |file| file.basename.to_s.starts_with?(".") }
6676
end
6777

6878
def clear_cache
6979
@cached_assets_by_path = nil
80+
@cached_asset_paths_by_type = nil
81+
@cached_asset_paths_by_glob = nil
7082
end
7183

7284
def dedup(paths)

test/dummy/app/views/layouts/application.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<%= csp_meta_tag %>
77

88
<%= stylesheet_link_tag :all, data: { custom_attribute: true } %>
9+
<%= stylesheet_link_tag :app, data: { glob_attribute: true } %>
910
</head>
1011

1112
<body>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Library css that should not be included by :app but should be included by :all

test/propshaft/load_path_test.rb

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,6 @@ class Propshaft::LoadPathTest < ActiveSupport::TestCase
2929
assert_not_includes @load_path.assets, find_asset(".stuff")
3030
end
3131

32-
test "assets by given content types" do
33-
assert_not_includes @load_path.assets(content_types: [ Mime[:js] ]), find_asset("one.txt")
34-
assert_includes @load_path.assets(content_types: [ Mime[:js] ]), find_asset("again.js")
35-
assert_includes @load_path.assets(content_types: [ Mime[:js], Mime[:css] ]), find_asset("again.js")
36-
assert_includes @load_path.assets(content_types: [ Mime[:js], Mime[:css] ]), find_asset("another.css")
37-
end
38-
3932
test "manifest" do
4033
@load_path.manifest.tap do |manifest|
4134
assert_equal "one-f2e1ec14.txt", manifest["one.txt"]
@@ -70,6 +63,18 @@ class Propshaft::LoadPathTest < ActiveSupport::TestCase
7063
assert_equal Pathname.new("app/assets"), paths.last
7164
end
7265

66+
test "asset paths by type" do
67+
assert_equal \
68+
["another.css", "dependent/a.css", "dependent/b.css", "dependent/c.css", "file-already-abcdefVWXYZ0123456789_-.digested.css", "file-already-abcdefVWXYZ0123456789_-.digested.debug.css", "file-not.digested.css"],
69+
@load_path.asset_paths_by_type("css")
70+
end
71+
72+
test "asset paths by glob" do
73+
assert_equal \
74+
["dependent/a.css", "dependent/b.css", "dependent/c.css"],
75+
@load_path.asset_paths_by_glob("**/dependent/*.css")
76+
end
77+
7378
private
7479
def find_asset(logical_path)
7580
root_path = Pathname.new("#{__dir__}/../fixtures/assets/first_path")

test/propshaft_integration_test.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@ class PropshaftIntegrationTest < ActionDispatch::IntegrationTest
88

99
assert_select 'link[href="/assets/hello_world-4137140a.css"][data-custom-attribute="true"]'
1010
assert_select 'link[href="/assets/goodbye-b1dc9940.css"][data-custom-attribute="true"]'
11+
assert_select 'link[href="/assets/library-86a3b7a9.css"][data-custom-attribute="true"]'
1112

1213
assert_select 'script[src="/assets/hello_world-888761f8.js"]'
1314
end
1415

16+
test "should find app styles via glob" do
17+
get sample_load_real_assets_url
18+
19+
assert_select 'link[href="/assets/hello_world-4137140a.css"][data-glob-attribute="true"]'
20+
assert_select 'link[href="/assets/goodbye-b1dc9940.css"][data-glob-attribute="true"]'
21+
assert_select('link[href="/assets/library-86a3b7a9.css"][data-glob-attribute="true"]', count: 0)
22+
end
23+
1524
test "should raise an exception when resolving nonexistent assets" do
1625
exception = assert_raises ActionView::Template::Error do
1726
get sample_load_nonexistent_assets_url

0 commit comments

Comments
 (0)