propshaft 0.6.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bee1e0c2fcf39062df053618528b93dd5977bd8968fea03a67d31b7196f04f9
4
- data.tar.gz: ad9c5f3b7bc878a359931d2c1edc2a2ecb98004b8cfba38dd226acd0a19e43ff
3
+ metadata.gz: a149fdc0fb45487422a71e21df47105ee0071e23f752f596f54cd98400bc99e2
4
+ data.tar.gz: 9dcd99aa258a91aa99ac46f1da9379712d836f06c42cd8ce9e6df8b020cc94c6
5
5
  SHA512:
6
- metadata.gz: 41a54360ed2d9424f1c190af2606c5d2bc88a2d01d61a1396ed60fc64ba1531e815cc259ac41904fe3a49053d057ee2f838be0c8763b37a1ebbe79067f52301f
7
- data.tar.gz: be6381394ff2c2dfa9bf0ca23d2023b196492d83012970091eca5ce03822ee127fa40103382dd85d6093ae1edc5b91545a59be21beb0a9736fa3037a322d5678
6
+ metadata.gz: 79779f26c1d121049451c5a8d7545e9fbfbcdd2b01a966f89fa0a23aa2b140ea2932828bd4c1b9edbab8be106366f8f92f9daa91bfa1c7af2207619a6fcce9fe
7
+ data.tar.gz: 7f609b53f85ef6bdeb53972c11d9a54301cd974c7d5573d5d93fbeb043daed863e08407bb95b45d7c8a55a122770dcb5f9f8b2d55f6830c2adbb5465281613ad
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
 
@@ -2,10 +2,15 @@ require "digest/sha1"
2
2
  require "action_dispatch/http/mime_type"
3
3
 
4
4
  class Propshaft::Asset
5
+ PREDIGESTED_REGEX = /-([0-9a-zA-Z]{7,128}\.digested)/
6
+
5
7
  attr_reader :path, :logical_path, :version
6
8
 
7
9
  def initialize(path, logical_path:, version: nil)
8
- @path, @logical_path, @version = path, Pathname.new(logical_path), version
10
+ @path = path
11
+ @digest = logical_path.to_s[PREDIGESTED_REGEX, 1]
12
+ @logical_path = Pathname.new(@digest ? logical_path.sub("-#{@digest}", "") : logical_path)
13
+ @version = version
9
14
  end
10
15
 
11
16
  def content
@@ -25,23 +30,14 @@ class Propshaft::Asset
25
30
  end
26
31
 
27
32
  def digested_path
28
- if already_digested?
29
- logical_path
30
- else
31
- logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
32
- end
33
+ logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
33
34
  end
34
35
 
35
36
  def fresh?(digest)
36
- self.digest == digest || already_digested?
37
+ self.digest == digest
37
38
  end
38
39
 
39
40
  def ==(other_asset)
40
41
  logical_path.hash == other_asset.logical_path.hash
41
42
  end
42
-
43
- private
44
- def already_digested?
45
- logical_path.to_s =~ /-([0-9a-zA-Z]{7,128})\.digested/
46
- end
47
43
  end
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Propshaft::Compilers::CssAssetUrls
4
- attr_reader :assembly
4
+ attr_reader :assembly, :url_prefix
5
5
 
6
- ASSET_URL_PATTERN = /url\(\s*["']?(?!(?:\#|data|http))([^"'\s?#)]+)([#?][^"']+)?\s*["']?\)/
6
+ ASSET_URL_PATTERN = /url\(\s*["']?(?!(?:\#|%23|data|http|\/\/))([^"'\s?#)]+)([#?][^"')]+)?\s*["']?\)/
7
7
 
8
8
  def initialize(assembly)
9
- @assembly = assembly
9
+ @assembly = assembly
10
+ @url_prefix = File.join(assembly.config.host.to_s, assembly.config.prefix.to_s).chomp("/")
10
11
  end
11
12
 
12
13
  def compile(logical_path, input)
13
- input.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(logical_path.dirname, $1), logical_path, $1 }
14
+ input.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(logical_path.dirname, $1), logical_path, $2, $1 }
14
15
  end
15
16
 
16
17
  private
@@ -24,9 +25,9 @@ class Propshaft::Compilers::CssAssetUrls
24
25
  end
25
26
  end
26
27
 
27
- def asset_url(resolved_path, logical_path, pattern)
28
+ def asset_url(resolved_path, logical_path, fingerprint, pattern)
28
29
  if asset = assembly.load_path.find(resolved_path)
29
- %[url("#{assembly.config.prefix}/#{asset.digested_path}")]
30
+ %[url("#{url_prefix}/#{asset.digested_path}#{fingerprint}")]
30
31
  else
31
32
  Propshaft.logger.warn "Unable to resolve '#{pattern}' for missing asset '#{resolved_path}' in #{logical_path}"
32
33
  %[url("#{pattern}")]
@@ -28,7 +28,7 @@ class Propshaft::LoadPath
28
28
  end
29
29
  end
30
30
 
31
- # Returns a ActiveSupport::FileUpdateChecker object configured to clear the cache of the load_path
31
+ # Returns a file watcher object configured to clear the cache of the load_path
32
32
  # when the directories passed during its initialization have changes. This is used in development
33
33
  # and test to ensure the map caches are reset when javascript files are changed.
34
34
  def cache_sweeper
@@ -48,7 +48,8 @@ class Propshaft::LoadPath
48
48
  paths.each do |path|
49
49
  without_dotfiles(all_files_from_tree(path)).each do |file|
50
50
  logical_path = file.relative_path_from(path)
51
- mapped[logical_path.to_s] ||= Propshaft::Asset.new(file, logical_path: logical_path, version: version)
51
+ asset = Propshaft::Asset.new(file, logical_path: logical_path, version: version)
52
+ mapped[asset.logical_path.to_s] ||= asset
52
53
  end if path.exist?
53
54
  end
54
55
  end
@@ -42,7 +42,7 @@ class Propshaft::OutputPath
42
42
  modified_at = [ 0, Time.now - mtime ].max
43
43
  modified_at < expires_at || limit < count
44
44
  end
45
-
45
+
46
46
  def remove(path)
47
47
  FileUtils.rm(@path.join(path))
48
48
  Propshaft.logger.info "Removed #{path}"
@@ -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
@@ -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,6 +9,7 @@ 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
14
  [ "text/css", Propshaft::Compilers::CssAssetUrls ],
13
15
  [ "text/css", Propshaft::Compilers::SourceMappingUrls ],
@@ -15,6 +17,7 @@ module Propshaft
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.host = 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.host = app.config.asset_host
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/assets/.manifest.json"
8
+ end
5
9
  end
6
10
 
7
11
  desc "Remove config.assets.output_path"
@@ -20,7 +20,7 @@ module Propshaft::Resolver
20
20
 
21
21
  private
22
22
  def parsed_manifest
23
- @parsed_manifest ||= JSON.parse(manifest_path.read)
23
+ @parsed_manifest ||= JSON.parse(manifest_path.read, symbolize_names: false)
24
24
  end
25
25
  end
26
26
  end
@@ -11,19 +11,19 @@ class Propshaft::Server
11
11
  if (asset = @assembly.load_path.find(path)) && asset.fresh?(digest)
12
12
  compiled_content = @assembly.compilers.compile(asset)
13
13
 
14
- [
15
- 200,
14
+ [
15
+ 200,
16
16
  {
17
- "Content-Length" => compiled_content.length.to_s,
18
- "Content-Type" => asset.content_type.to_s,
19
- "Accept-Encoding" => "Vary",
20
- "ETag" => asset.digest,
21
- "Cache-Control" => "public, max-age=31536000, immutable"
17
+ "content-length" => compiled_content.length.to_s,
18
+ "content-type" => asset.content_type.to_s,
19
+ "accept-encoding" => "vary",
20
+ "etag" => asset.digest,
21
+ "cache-control" => "public, max-age=31536000, immutable"
22
22
  },
23
23
  [ compiled_content ]
24
24
  ]
25
25
  else
26
- [ 404, { "Content-Type" => "text/plain", "Content-Length" => "9" }, [ "Not found" ] ]
26
+ [ 404, { "content-type" => "text/plain", "content-length" => "9" }, [ "Not found" ] ]
27
27
  end
28
28
  end
29
29
 
@@ -34,7 +34,7 @@ class Propshaft::Server
34
34
  private
35
35
  def extract_path_and_digest(env)
36
36
  full_path = Rack::Utils.unescape(env["PATH_INFO"].to_s.sub(/^\//, ""))
37
- digest = full_path[/-([0-9a-zA-Z]{7,128})\.(?!digested)[^.]+\z/, 1]
37
+ digest = full_path[/-([0-9a-zA-Z]{7,128}(?:\.digested)?)\.[^.]+\z/, 1]
38
38
  path = digest ? full_path.sub("-#{digest}", "") : full_path
39
39
 
40
40
  [ path, digest ]
@@ -1,3 +1,3 @@
1
1
  module Propshaft
2
- VERSION = "0.6.4"
2
+ VERSION = "0.7.0"
3
3
  end
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.6.4
4
+ version: 0.7.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: 2022-02-22 00:00:00.000000000 Z
11
+ date: 2023-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -86,6 +86,7 @@ files:
86
86
  - lib/propshaft/load_path.rb
87
87
  - lib/propshaft/output_path.rb
88
88
  - lib/propshaft/processor.rb
89
+ - lib/propshaft/quiet_assets.rb
89
90
  - lib/propshaft/railtie.rb
90
91
  - lib/propshaft/railties/assets.rake
91
92
  - lib/propshaft/resolver/dynamic.rb
@@ -112,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
113
  - !ruby/object:Gem::Version
113
114
  version: '0'
114
115
  requirements: []
115
- rubygems_version: 3.2.32
116
+ rubygems_version: 3.4.6
116
117
  signing_key:
117
118
  specification_version: 4
118
119
  summary: Deliver assets for Rails.