propshaft 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/propshaft/assembly.rb +1 -1
- data/lib/propshaft/asset.rb +10 -6
- data/lib/propshaft/compiler/css_asset_urls.rb +16 -3
- data/lib/propshaft/compiler/source_mapping_urls.rb +7 -5
- data/lib/propshaft/compiler.rb +7 -2
- data/lib/propshaft/compilers.rb +11 -1
- data/lib/propshaft/helper.rb +14 -9
- data/lib/propshaft/load_path.rb +35 -12
- data/lib/propshaft/processor.rb +2 -2
- data/lib/propshaft/railties/assets.rake +3 -2
- data/lib/propshaft/server.rb +1 -0
- data/lib/propshaft/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cec588585c21b06e765dbf70ac6f0d3a4b9ad2826c8a11120553a8db0931559
|
4
|
+
data.tar.gz: dde9f2d2a7095731ea93585e237421b73adc4acc71218ef574e566468796d1eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c372a207074f656261b8be407d224780678fd68e6caf645323c898ca06cb04f4cc0e1b2ab2ac2d59179162bb3a6c1e137297635aef8c49d1a0e4b3d279e609c6
|
7
|
+
data.tar.gz: 80c3963e4963ae52b19793b8de066a173b1227a5b6ea1d1ff4420f88b1939a09f2a476fd55e52a4a18fe688c53bc0862756700723557a108d052441ade893259
|
data/README.md
CHANGED
@@ -49,7 +49,7 @@ But for greenfield apps using the default import-map approach, Propshaft can als
|
|
49
49
|
|
50
50
|
## Will Propshaft replace Sprockets as the Rails default?
|
51
51
|
|
52
|
-
Most likely, but Sprockets
|
52
|
+
Most likely, but Sprockets needs to be supported as well for a long time to come. Plenty of apps and gems were built on Sprocket features, and they won't be migrating soon. Still working out the compatibility story. This is very much beta software at the moment.
|
53
53
|
|
54
54
|
|
55
55
|
## License
|
data/lib/propshaft/assembly.rb
CHANGED
@@ -15,7 +15,7 @@ class Propshaft::Assembly
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def load_path
|
18
|
-
@load_path ||= Propshaft::LoadPath.new(config.paths, version: config.version)
|
18
|
+
@load_path ||= Propshaft::LoadPath.new(config.paths, compilers: compilers, version: config.version)
|
19
19
|
end
|
20
20
|
|
21
21
|
def resolver
|
data/lib/propshaft/asset.rb
CHANGED
@@ -2,10 +2,10 @@ require "digest/sha1"
|
|
2
2
|
require "action_dispatch/http/mime_type"
|
3
3
|
|
4
4
|
class Propshaft::Asset
|
5
|
-
attr_reader :path, :logical_path, :
|
5
|
+
attr_reader :path, :logical_path, :load_path
|
6
6
|
|
7
|
-
def initialize(path, logical_path:,
|
8
|
-
@path, @logical_path, @
|
7
|
+
def initialize(path, logical_path:, load_path:)
|
8
|
+
@path, @logical_path, @load_path = path, Pathname.new(logical_path), load_path
|
9
9
|
end
|
10
10
|
|
11
11
|
def content
|
@@ -21,14 +21,14 @@ class Propshaft::Asset
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def digest
|
24
|
-
@digest ||= Digest::SHA1.hexdigest("#{
|
24
|
+
@digest ||= Digest::SHA1.hexdigest("#{content_with_compile_references}#{load_path.version}").first(8)
|
25
25
|
end
|
26
26
|
|
27
27
|
def digested_path
|
28
28
|
if already_digested?
|
29
29
|
logical_path
|
30
30
|
else
|
31
|
-
logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
|
31
|
+
logical_path.sub(/\.(\w+(\.map)?)$/) { |ext| "-#{digest}#{ext}" }
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -41,7 +41,11 @@ class Propshaft::Asset
|
|
41
41
|
end
|
42
42
|
|
43
43
|
private
|
44
|
+
def content_with_compile_references
|
45
|
+
content + load_path.find_referenced_by(self).collect(&:content).join
|
46
|
+
end
|
47
|
+
|
44
48
|
def already_digested?
|
45
|
-
logical_path.to_s =~ /-([0-9a-zA-
|
49
|
+
logical_path.to_s =~ /-([0-9a-zA-Z_-]{7,128})\.digested/
|
46
50
|
end
|
47
51
|
end
|
@@ -5,8 +5,21 @@ require "propshaft/compiler"
|
|
5
5
|
class Propshaft::Compiler::CssAssetUrls < Propshaft::Compiler
|
6
6
|
ASSET_URL_PATTERN = /url\(\s*["']?(?!(?:\#|%23|data|http|\/\/))([^"'\s?#)]+)([#?][^"')]+)?\s*["']?\)/
|
7
7
|
|
8
|
-
def compile(
|
9
|
-
input.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(logical_path.dirname, $1), logical_path, $2, $1 }
|
8
|
+
def compile(asset, input)
|
9
|
+
input.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(asset.logical_path.dirname, $1), asset.logical_path, $2, $1 }
|
10
|
+
end
|
11
|
+
|
12
|
+
def referenced_by(asset, references: Set.new)
|
13
|
+
asset.content.scan(ASSET_URL_PATTERN).each do |referenced_asset_url, _|
|
14
|
+
referenced_asset = load_path.find(resolve_path(asset.logical_path.dirname, referenced_asset_url))
|
15
|
+
|
16
|
+
if referenced_asset && references.exclude?(referenced_asset)
|
17
|
+
references << referenced_asset
|
18
|
+
references.merge referenced_by(referenced_asset, references: references)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
references
|
10
23
|
end
|
11
24
|
|
12
25
|
private
|
@@ -21,7 +34,7 @@ class Propshaft::Compiler::CssAssetUrls < Propshaft::Compiler
|
|
21
34
|
end
|
22
35
|
|
23
36
|
def asset_url(resolved_path, logical_path, fingerprint, pattern)
|
24
|
-
if asset =
|
37
|
+
if asset = load_path.find(resolved_path)
|
25
38
|
%[url("#{url_prefix}/#{asset.digested_path}#{fingerprint}")]
|
26
39
|
else
|
27
40
|
Propshaft.logger.warn "Unable to resolve '#{pattern}' for missing asset '#{resolved_path}' in #{logical_path}"
|
@@ -5,12 +5,14 @@ require "propshaft/compiler"
|
|
5
5
|
class Propshaft::Compiler::SourceMappingUrls < Propshaft::Compiler
|
6
6
|
SOURCE_MAPPING_PATTERN = %r{(//|/\*)# sourceMappingURL=(.+\.map)(\s*?\*\/)?\s*?\Z}
|
7
7
|
|
8
|
-
def compile(
|
9
|
-
input.gsub(SOURCE_MAPPING_PATTERN) { source_mapping_url(asset_path($2, logical_path), $1, $3) }
|
8
|
+
def compile(asset, input)
|
9
|
+
input.gsub(SOURCE_MAPPING_PATTERN) { source_mapping_url(asset.logical_path, asset_path($2, asset.logical_path), $1, $3) }
|
10
10
|
end
|
11
11
|
|
12
12
|
private
|
13
13
|
def asset_path(source_mapping_url, logical_path)
|
14
|
+
source_mapping_url.gsub!(/^(.+\/)?#{url_prefix}\//, "")
|
15
|
+
|
14
16
|
if logical_path.dirname.to_s == "."
|
15
17
|
source_mapping_url
|
16
18
|
else
|
@@ -18,11 +20,11 @@ class Propshaft::Compiler::SourceMappingUrls < Propshaft::Compiler
|
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
def source_mapping_url(resolved_path, comment_start, comment_end)
|
22
|
-
if asset =
|
23
|
+
def source_mapping_url(logical_path, resolved_path, comment_start, comment_end)
|
24
|
+
if asset = load_path.find(resolved_path)
|
23
25
|
"#{comment_start}# sourceMappingURL=#{url_prefix}/#{asset.digested_path}#{comment_end}"
|
24
26
|
else
|
25
|
-
Propshaft.logger.warn "Removed sourceMappingURL comment for missing asset '#{resolved_path}' from #{
|
27
|
+
Propshaft.logger.warn "Removed sourceMappingURL comment for missing asset '#{resolved_path}' from #{logical_path}"
|
26
28
|
"#{comment_start}#{comment_end}"
|
27
29
|
end
|
28
30
|
end
|
data/lib/propshaft/compiler.rb
CHANGED
@@ -3,18 +3,23 @@
|
|
3
3
|
# Base compiler from which other compilers can inherit
|
4
4
|
class Propshaft::Compiler
|
5
5
|
attr_reader :assembly
|
6
|
+
delegate :config, :load_path, to: :assembly
|
6
7
|
|
7
8
|
def initialize(assembly)
|
8
9
|
@assembly = assembly
|
9
10
|
end
|
10
11
|
|
11
12
|
# Override this in a specific compiler
|
12
|
-
def compile(
|
13
|
+
def compile(asset, input)
|
13
14
|
raise NotImplementedError
|
14
15
|
end
|
15
16
|
|
17
|
+
def referenced_by(asset)
|
18
|
+
Set.new
|
19
|
+
end
|
20
|
+
|
16
21
|
private
|
17
22
|
def url_prefix
|
18
|
-
@url_prefix ||= File.join(
|
23
|
+
@url_prefix ||= File.join(config.relative_url_root.to_s, config.prefix.to_s).chomp("/")
|
19
24
|
end
|
20
25
|
end
|
data/lib/propshaft/compilers.rb
CHANGED
@@ -23,11 +23,21 @@ class Propshaft::Compilers
|
|
23
23
|
if relevant_registrations = registrations[asset.content_type.to_s]
|
24
24
|
asset.content.dup.tap do |input|
|
25
25
|
relevant_registrations.each do |compiler|
|
26
|
-
input.replace compiler.new(assembly).compile(asset
|
26
|
+
input.replace compiler.new(assembly).compile(asset, input)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
else
|
30
30
|
asset.content
|
31
31
|
end
|
32
32
|
end
|
33
|
+
|
34
|
+
def referenced_by(asset)
|
35
|
+
Set.new.tap do |references|
|
36
|
+
if relevant_registrations = registrations[asset.content_type.to_s]
|
37
|
+
relevant_registrations.each do |compiler|
|
38
|
+
references.merge compiler.new(assembly).referenced_by(asset)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
33
43
|
end
|
data/lib/propshaft/helper.rb
CHANGED
@@ -4,10 +4,14 @@ module Propshaft
|
|
4
4
|
Rails.application.assets.resolver.resolve(path) || raise(MissingAssetError.new(path))
|
5
5
|
end
|
6
6
|
|
7
|
-
# Add an option to call `stylesheet_link_tag` with `:all` to include every css file found on the load path
|
8
|
-
|
9
|
-
|
10
|
-
|
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.
|
9
|
+
def stylesheet_link_tag(*sources, **options)
|
10
|
+
case sources.first
|
11
|
+
when :all
|
12
|
+
super(*all_stylesheets_paths , **options)
|
13
|
+
when :app
|
14
|
+
super(*app_stylesheets_paths , **options)
|
11
15
|
else
|
12
16
|
super
|
13
17
|
end
|
@@ -15,11 +19,12 @@ module Propshaft
|
|
15
19
|
|
16
20
|
# Returns a sorted and unique array of logical paths for all stylesheets in the load path.
|
17
21
|
def all_stylesheets_paths
|
18
|
-
Rails.application.assets.load_path
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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")
|
23
28
|
end
|
24
29
|
end
|
25
30
|
end
|
data/lib/propshaft/load_path.rb
CHANGED
@@ -1,23 +1,32 @@
|
|
1
1
|
require "propshaft/asset"
|
2
2
|
|
3
3
|
class Propshaft::LoadPath
|
4
|
-
attr_reader :paths, :version
|
4
|
+
attr_reader :paths, :compilers, :version
|
5
5
|
|
6
|
-
def initialize(paths = [], version: nil)
|
7
|
-
@paths
|
8
|
-
@version = version
|
6
|
+
def initialize(paths = [], compilers:, version: nil)
|
7
|
+
@paths, @compilers, @version = dedup(paths), compilers, version
|
9
8
|
end
|
10
9
|
|
11
10
|
def find(asset_name)
|
12
11
|
assets_by_path[asset_name]
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
def find_referenced_by(asset)
|
15
|
+
compilers.referenced_by(asset).delete(self)
|
16
|
+
end
|
17
|
+
|
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) })
|
21
30
|
end
|
22
31
|
|
23
32
|
def manifest
|
@@ -35,9 +44,13 @@ class Propshaft::LoadPath
|
|
35
44
|
@cache_sweeper ||= begin
|
36
45
|
exts_to_watch = Mime::EXTENSION_LOOKUP.map(&:first)
|
37
46
|
files_to_watch = Array(paths).collect { |dir| [ dir.to_s, exts_to_watch ] }.to_h
|
47
|
+
mutex = Mutex.new
|
38
48
|
|
39
49
|
Rails.application.config.file_watcher.new([], files_to_watch) do
|
40
|
-
|
50
|
+
mutex.synchronize do
|
51
|
+
clear_cache
|
52
|
+
seed_cache
|
53
|
+
end
|
41
54
|
end
|
42
55
|
end
|
43
56
|
end
|
@@ -48,7 +61,7 @@ class Propshaft::LoadPath
|
|
48
61
|
paths.each do |path|
|
49
62
|
without_dotfiles(all_files_from_tree(path)).each do |file|
|
50
63
|
logical_path = file.relative_path_from(path)
|
51
|
-
mapped[logical_path.to_s] ||= Propshaft::Asset.new(file, logical_path: logical_path,
|
64
|
+
mapped[logical_path.to_s] ||= Propshaft::Asset.new(file, logical_path: logical_path, load_path: self)
|
52
65
|
end if path.exist?
|
53
66
|
end
|
54
67
|
end
|
@@ -58,12 +71,22 @@ class Propshaft::LoadPath
|
|
58
71
|
path.children.flat_map { |child| child.directory? ? all_files_from_tree(child) : child }
|
59
72
|
end
|
60
73
|
|
74
|
+
def extract_logical_paths_from(assets)
|
75
|
+
assets.collect { |asset| asset.logical_path.to_s }.sort
|
76
|
+
end
|
77
|
+
|
61
78
|
def without_dotfiles(files)
|
62
79
|
files.reject { |file| file.basename.to_s.starts_with?(".") }
|
63
80
|
end
|
64
81
|
|
65
82
|
def clear_cache
|
66
83
|
@cached_assets_by_path = nil
|
84
|
+
@cached_asset_paths_by_type = nil
|
85
|
+
@cached_asset_paths_by_glob = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def seed_cache
|
89
|
+
assets_by_path
|
67
90
|
end
|
68
91
|
|
69
92
|
def dedup(paths)
|
data/lib/propshaft/processor.rb
CHANGED
@@ -20,8 +20,8 @@ class Propshaft::Processor
|
|
20
20
|
FileUtils.rm_r(output_path) if File.exist?(output_path)
|
21
21
|
end
|
22
22
|
|
23
|
-
def clean
|
24
|
-
Propshaft::OutputPath.new(output_path, load_path.manifest).clean(
|
23
|
+
def clean(count)
|
24
|
+
Propshaft::OutputPath.new(output_path, load_path.manifest).clean(count, 1.hour)
|
25
25
|
end
|
26
26
|
|
27
27
|
private
|
@@ -14,8 +14,9 @@ namespace :assets do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
desc "Removes old files in config.assets.output_path"
|
17
|
-
task clean: :environment do
|
18
|
-
|
17
|
+
task :clean, [:count] => [:environment] do |_, args|
|
18
|
+
count = args.fetch(:count, 2)
|
19
|
+
Rails.application.assets.processor.clean(count.to_i)
|
19
20
|
end
|
20
21
|
|
21
22
|
desc "Print all the assets available in config.assets.paths"
|
data/lib/propshaft/server.rb
CHANGED
data/lib/propshaft/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: propshaft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -114,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
114
|
- !ruby/object:Gem::Version
|
115
115
|
version: '0'
|
116
116
|
requirements: []
|
117
|
-
rubygems_version: 3.
|
117
|
+
rubygems_version: 3.5.10
|
118
118
|
signing_key:
|
119
119
|
specification_version: 4
|
120
120
|
summary: Deliver assets for Rails.
|