propshaft 0.6.4 → 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 +11 -1
- data/lib/propshaft/assembly.rb +3 -3
- data/lib/propshaft/asset.rb +10 -6
- data/lib/propshaft/compiler/css_asset_urls.rb +44 -0
- data/lib/propshaft/compiler/source_mapping_urls.rb +31 -0
- data/lib/propshaft/compiler.rb +25 -0
- data/lib/propshaft/compilers.rb +11 -1
- data/lib/propshaft/helper.rb +23 -0
- data/lib/propshaft/load_path.rb +36 -13
- data/lib/propshaft/processor.rb +2 -2
- data/lib/propshaft/quiet_assets.rb +14 -0
- data/lib/propshaft/railtie.rb +13 -5
- data/lib/propshaft/railties/assets.rake +7 -2
- data/lib/propshaft/resolver/static.rb +1 -1
- data/lib/propshaft/server.rb +15 -8
- data/lib/propshaft/version.rb +1 -1
- metadata +7 -5
- data/lib/propshaft/compilers/css_asset_urls.rb +0 -35
- data/lib/propshaft/compilers/source_mapping_urls.rb +0 -33
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
@@ -27,6 +27,16 @@ These assets can be referenced through their logical path using the normal helpe
|
|
27
27
|
|
28
28
|
If you need to put multiple files that refer to each other through Propshaft, like a JavaScript file and its source map, you have to digest these files in advance to retain stable file names. Propshaft looks for the specific pattern of `-[digest].digested.js` as the postfix to any asset file as an indication that the file has already been digested.
|
29
29
|
|
30
|
+
## Improving performance in development
|
31
|
+
|
32
|
+
Before every request Propshaft checks if any asset was updated to decide if a cache sweep is needed. This verification is done using the application's configured file watcher which, by default, is `ActiveSupport::FileUpdateChecker`.
|
33
|
+
|
34
|
+
If you have a lot of assets in your project, you can improve performance by adding the `listen` gem to the development group in your Gemfile, and this line to the `development.rb` environment file:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
38
|
+
```
|
39
|
+
|
30
40
|
|
31
41
|
## Migrating from Sprockets
|
32
42
|
|
@@ -39,7 +49,7 @@ But for greenfield apps using the default import-map approach, Propshaft can als
|
|
39
49
|
|
40
50
|
## Will Propshaft replace Sprockets as the Rails default?
|
41
51
|
|
42
|
-
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.
|
43
53
|
|
44
54
|
|
45
55
|
## License
|
data/lib/propshaft/assembly.rb
CHANGED
@@ -4,8 +4,8 @@ require "propshaft/resolver/static"
|
|
4
4
|
require "propshaft/server"
|
5
5
|
require "propshaft/processor"
|
6
6
|
require "propshaft/compilers"
|
7
|
-
require "propshaft/
|
8
|
-
require "propshaft/
|
7
|
+
require "propshaft/compiler/css_asset_urls"
|
8
|
+
require "propshaft/compiler/source_mapping_urls"
|
9
9
|
|
10
10
|
class Propshaft::Assembly
|
11
11
|
attr_reader :config
|
@@ -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
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "propshaft/compiler"
|
4
|
+
|
5
|
+
class Propshaft::Compiler::CssAssetUrls < Propshaft::Compiler
|
6
|
+
ASSET_URL_PATTERN = /url\(\s*["']?(?!(?:\#|%23|data|http|\/\/))([^"'\s?#)]+)([#?][^"')]+)?\s*["']?\)/
|
7
|
+
|
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
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def resolve_path(directory, filename)
|
27
|
+
if filename.start_with?("../")
|
28
|
+
Pathname.new(directory + filename).relative_path_from("").to_s
|
29
|
+
elsif filename.start_with?("/")
|
30
|
+
filename.delete_prefix("/").to_s
|
31
|
+
else
|
32
|
+
(directory + filename.delete_prefix("./")).to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def asset_url(resolved_path, logical_path, fingerprint, pattern)
|
37
|
+
if asset = load_path.find(resolved_path)
|
38
|
+
%[url("#{url_prefix}/#{asset.digested_path}#{fingerprint}")]
|
39
|
+
else
|
40
|
+
Propshaft.logger.warn "Unable to resolve '#{pattern}' for missing asset '#{resolved_path}' in #{logical_path}"
|
41
|
+
%[url("#{pattern}")]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "propshaft/compiler"
|
4
|
+
|
5
|
+
class Propshaft::Compiler::SourceMappingUrls < Propshaft::Compiler
|
6
|
+
SOURCE_MAPPING_PATTERN = %r{(//|/\*)# sourceMappingURL=(.+\.map)(\s*?\*\/)?\s*?\Z}
|
7
|
+
|
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
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def asset_path(source_mapping_url, logical_path)
|
14
|
+
source_mapping_url.gsub!(/^(.+\/)?#{url_prefix}\//, "")
|
15
|
+
|
16
|
+
if logical_path.dirname.to_s == "."
|
17
|
+
source_mapping_url
|
18
|
+
else
|
19
|
+
logical_path.dirname.join(source_mapping_url).to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def source_mapping_url(logical_path, resolved_path, comment_start, comment_end)
|
24
|
+
if asset = load_path.find(resolved_path)
|
25
|
+
"#{comment_start}# sourceMappingURL=#{url_prefix}/#{asset.digested_path}#{comment_end}"
|
26
|
+
else
|
27
|
+
Propshaft.logger.warn "Removed sourceMappingURL comment for missing asset '#{resolved_path}' from #{logical_path}"
|
28
|
+
"#{comment_start}#{comment_end}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Base compiler from which other compilers can inherit
|
4
|
+
class Propshaft::Compiler
|
5
|
+
attr_reader :assembly
|
6
|
+
delegate :config, :load_path, to: :assembly
|
7
|
+
|
8
|
+
def initialize(assembly)
|
9
|
+
@assembly = assembly
|
10
|
+
end
|
11
|
+
|
12
|
+
# Override this in a specific compiler
|
13
|
+
def compile(asset, input)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
def referenced_by(asset)
|
18
|
+
Set.new
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def url_prefix
|
23
|
+
@url_prefix ||= File.join(config.relative_url_root.to_s, config.prefix.to_s).chomp("/")
|
24
|
+
end
|
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
@@ -3,5 +3,28 @@ module Propshaft
|
|
3
3
|
def compute_asset_path(path, options = {})
|
4
4
|
Rails.application.assets.resolver.resolve(path) || raise(MissingAssetError.new(path))
|
5
5
|
end
|
6
|
+
|
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)
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a sorted and unique array of logical paths for all stylesheets in the load path.
|
21
|
+
def all_stylesheets_paths
|
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")
|
28
|
+
end
|
6
29
|
end
|
7
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
|
@@ -28,16 +37,20 @@ class Propshaft::LoadPath
|
|
28
37
|
end
|
29
38
|
end
|
30
39
|
|
31
|
-
# Returns a
|
40
|
+
# Returns a file watcher object configured to clear the cache of the load_path
|
32
41
|
# when the directories passed during its initialization have changes. This is used in development
|
33
42
|
# and test to ensure the map caches are reset when javascript files are changed.
|
34
43
|
def cache_sweeper
|
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
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Propshaft::QuietAssets
|
2
|
+
def initialize(app)
|
3
|
+
@app = app
|
4
|
+
@assets_regex = %r(\A/{0,2}#{::Rails.application.config.assets.prefix})
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
if env['PATH_INFO'] =~ @assets_regex
|
9
|
+
::Rails.logger.silence { @app.call(env) }
|
10
|
+
else
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/propshaft/railtie.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "rails"
|
2
2
|
require "active_support/ordered_options"
|
3
|
+
require "propshaft/quiet_assets"
|
3
4
|
|
4
5
|
module Propshaft
|
5
6
|
class Railtie < ::Rails::Railtie
|
@@ -8,13 +9,15 @@ module Propshaft
|
|
8
9
|
config.assets.excluded_paths = []
|
9
10
|
config.assets.version = "1"
|
10
11
|
config.assets.prefix = "/assets"
|
12
|
+
config.assets.quiet = false
|
11
13
|
config.assets.compilers = [
|
12
|
-
[ "text/css", Propshaft::
|
13
|
-
[ "text/css", Propshaft::
|
14
|
-
[ "text/javascript", Propshaft::
|
14
|
+
[ "text/css", Propshaft::Compiler::CssAssetUrls ],
|
15
|
+
[ "text/css", Propshaft::Compiler::SourceMappingUrls ],
|
16
|
+
[ "text/javascript", Propshaft::Compiler::SourceMappingUrls ]
|
15
17
|
]
|
16
18
|
config.assets.sweep_cache = Rails.env.development?
|
17
19
|
config.assets.server = Rails.env.development? || Rails.env.test?
|
20
|
+
config.assets.relative_url_root = nil
|
18
21
|
|
19
22
|
# Register propshaft initializer to copy the assets path in all the Rails Engines.
|
20
23
|
# This makes possible for us to keep all `assets` config in this Railtie, but still
|
@@ -28,6 +31,7 @@ module Propshaft
|
|
28
31
|
end
|
29
32
|
|
30
33
|
config.after_initialize do |app|
|
34
|
+
config.assets.relative_url_root ||= app.config.relative_url_root
|
31
35
|
config.assets.output_path ||=
|
32
36
|
Pathname.new(File.join(app.config.paths["public"].first, app.config.assets.prefix))
|
33
37
|
|
@@ -54,6 +58,12 @@ module Propshaft
|
|
54
58
|
Propshaft.logger = config.assets.logger || Rails.logger
|
55
59
|
end
|
56
60
|
|
61
|
+
initializer :quiet_assets do |app|
|
62
|
+
if app.config.assets.quiet
|
63
|
+
app.middleware.insert_before ::Rails::Rack::Logger, Propshaft::QuietAssets
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
57
67
|
rake_tasks do
|
58
68
|
load "propshaft/railties/assets.rake"
|
59
69
|
end
|
@@ -61,9 +71,7 @@ module Propshaft
|
|
61
71
|
# Compatibility shiming (need to provide log warnings when used)
|
62
72
|
config.assets.precompile = []
|
63
73
|
config.assets.debug = nil
|
64
|
-
config.assets.quiet = nil
|
65
74
|
config.assets.compile = nil
|
66
|
-
config.assets.version = nil
|
67
75
|
config.assets.css_compressor = nil
|
68
76
|
config.assets.js_compressor = nil
|
69
77
|
end
|
@@ -2,6 +2,10 @@ namespace :assets do
|
|
2
2
|
desc "Compile all the assets from config.assets.paths"
|
3
3
|
task precompile: :environment do
|
4
4
|
Rails.application.assets.processor.process
|
5
|
+
if Rails.env.development?
|
6
|
+
puts "Warning: You are precompiling assets in development. Rails will not " \
|
7
|
+
"serve any changed assets until you delete public#{Rails.application.config.assets.prefix}/.manifest.json"
|
8
|
+
end
|
5
9
|
end
|
6
10
|
|
7
11
|
desc "Remove config.assets.output_path"
|
@@ -10,8 +14,9 @@ namespace :assets do
|
|
10
14
|
end
|
11
15
|
|
12
16
|
desc "Removes old files in config.assets.output_path"
|
13
|
-
task clean: :environment do
|
14
|
-
|
17
|
+
task :clean, [:count] => [:environment] do |_, args|
|
18
|
+
count = args.fetch(:count, 2)
|
19
|
+
Rails.application.assets.processor.clean(count.to_i)
|
15
20
|
end
|
16
21
|
|
17
22
|
desc "Print all the assets available in config.assets.paths"
|
data/lib/propshaft/server.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "rack/utils"
|
2
|
+
require "rack/version"
|
2
3
|
|
3
4
|
class Propshaft::Server
|
4
5
|
def initialize(assembly)
|
@@ -11,19 +12,19 @@ class Propshaft::Server
|
|
11
12
|
if (asset = @assembly.load_path.find(path)) && asset.fresh?(digest)
|
12
13
|
compiled_content = @assembly.compilers.compile(asset)
|
13
14
|
|
14
|
-
[
|
15
|
-
200,
|
15
|
+
[
|
16
|
+
200,
|
16
17
|
{
|
17
|
-
|
18
|
-
|
19
|
-
"Accept-Encoding"
|
20
|
-
|
21
|
-
|
18
|
+
Rack::CONTENT_LENGTH => compiled_content.length.to_s,
|
19
|
+
Rack::CONTENT_TYPE => asset.content_type.to_s,
|
20
|
+
VARY => "Accept-Encoding",
|
21
|
+
Rack::ETAG => asset.digest,
|
22
|
+
Rack::CACHE_CONTROL => "public, max-age=31536000, immutable"
|
22
23
|
},
|
23
24
|
[ compiled_content ]
|
24
25
|
]
|
25
26
|
else
|
26
|
-
[ 404, {
|
27
|
+
[ 404, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "9" }, [ "Not found" ] ]
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
@@ -39,4 +40,10 @@ class Propshaft::Server
|
|
39
40
|
|
40
41
|
[ path, digest ]
|
41
42
|
end
|
43
|
+
|
44
|
+
if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
|
45
|
+
VARY = "Vary"
|
46
|
+
else
|
47
|
+
VARY = "vary"
|
48
|
+
end
|
42
49
|
end
|
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
|
@@ -78,14 +78,16 @@ files:
|
|
78
78
|
- lib/propshaft.rb
|
79
79
|
- lib/propshaft/assembly.rb
|
80
80
|
- lib/propshaft/asset.rb
|
81
|
+
- lib/propshaft/compiler.rb
|
82
|
+
- lib/propshaft/compiler/css_asset_urls.rb
|
83
|
+
- lib/propshaft/compiler/source_mapping_urls.rb
|
81
84
|
- lib/propshaft/compilers.rb
|
82
|
-
- lib/propshaft/compilers/css_asset_urls.rb
|
83
|
-
- lib/propshaft/compilers/source_mapping_urls.rb
|
84
85
|
- lib/propshaft/errors.rb
|
85
86
|
- lib/propshaft/helper.rb
|
86
87
|
- lib/propshaft/load_path.rb
|
87
88
|
- lib/propshaft/output_path.rb
|
88
89
|
- lib/propshaft/processor.rb
|
90
|
+
- lib/propshaft/quiet_assets.rb
|
89
91
|
- lib/propshaft/railtie.rb
|
90
92
|
- lib/propshaft/railties/assets.rake
|
91
93
|
- lib/propshaft/resolver/dynamic.rb
|
@@ -112,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
114
|
- !ruby/object:Gem::Version
|
113
115
|
version: '0'
|
114
116
|
requirements: []
|
115
|
-
rubygems_version: 3.
|
117
|
+
rubygems_version: 3.5.10
|
116
118
|
signing_key:
|
117
119
|
specification_version: 4
|
118
120
|
summary: Deliver assets for Rails.
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Propshaft::Compilers::CssAssetUrls
|
4
|
-
attr_reader :assembly
|
5
|
-
|
6
|
-
ASSET_URL_PATTERN = /url\(\s*["']?(?!(?:\#|data|http))([^"'\s?#)]+)([#?][^"']+)?\s*["']?\)/
|
7
|
-
|
8
|
-
def initialize(assembly)
|
9
|
-
@assembly = assembly
|
10
|
-
end
|
11
|
-
|
12
|
-
def compile(logical_path, input)
|
13
|
-
input.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(logical_path.dirname, $1), logical_path, $1 }
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
def resolve_path(directory, filename)
|
18
|
-
if filename.start_with?("../")
|
19
|
-
Pathname.new(directory + filename).relative_path_from("").to_s
|
20
|
-
elsif filename.start_with?("/")
|
21
|
-
filename.delete_prefix("/").to_s
|
22
|
-
else
|
23
|
-
(directory + filename.delete_prefix("./")).to_s
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def asset_url(resolved_path, logical_path, pattern)
|
28
|
-
if asset = assembly.load_path.find(resolved_path)
|
29
|
-
%[url("#{assembly.config.prefix}/#{asset.digested_path}")]
|
30
|
-
else
|
31
|
-
Propshaft.logger.warn "Unable to resolve '#{pattern}' for missing asset '#{resolved_path}' in #{logical_path}"
|
32
|
-
%[url("#{pattern}")]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Propshaft::Compilers::SourceMappingUrls
|
4
|
-
attr_reader :assembly
|
5
|
-
|
6
|
-
SOURCE_MAPPING_PATTERN = %r{^(//|/\*)# sourceMappingURL=(.+\.map)}
|
7
|
-
|
8
|
-
def initialize(assembly)
|
9
|
-
@assembly = assembly
|
10
|
-
end
|
11
|
-
|
12
|
-
def compile(logical_path, input)
|
13
|
-
input.gsub(SOURCE_MAPPING_PATTERN) { source_mapping_url(asset_path($2, logical_path), $1) }
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
def asset_path(source_mapping_url, logical_path)
|
18
|
-
if logical_path.dirname.to_s == "."
|
19
|
-
source_mapping_url
|
20
|
-
else
|
21
|
-
logical_path.dirname.join(source_mapping_url).to_s
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def source_mapping_url(resolved_path, comment)
|
26
|
-
if asset = assembly.load_path.find(resolved_path)
|
27
|
-
"#{comment}# sourceMappingURL=#{assembly.config.prefix}/#{asset.digested_path}"
|
28
|
-
else
|
29
|
-
Propshaft.logger.warn "Removed sourceMappingURL comment for missing asset '#{resolved_path}' from #{resolved_path}"
|
30
|
-
nil
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|