sprockets 3.0.3 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +101 -0
  3. data/{LICENSE → MIT-LICENSE} +2 -2
  4. data/README.md +531 -276
  5. data/bin/sprockets +12 -7
  6. data/lib/rake/sprocketstask.rb +9 -4
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +41 -28
  9. data/lib/sprockets/autoload/babel.rb +8 -0
  10. data/lib/sprockets/autoload/closure.rb +1 -0
  11. data/lib/sprockets/autoload/coffee_script.rb +1 -0
  12. data/lib/sprockets/autoload/eco.rb +1 -0
  13. data/lib/sprockets/autoload/ejs.rb +1 -0
  14. data/lib/sprockets/autoload/jsminc.rb +8 -0
  15. data/lib/sprockets/autoload/sass.rb +1 -0
  16. data/lib/sprockets/autoload/sassc.rb +8 -0
  17. data/lib/sprockets/autoload/uglifier.rb +1 -0
  18. data/lib/sprockets/autoload/yui.rb +1 -0
  19. data/lib/sprockets/autoload/zopfli.rb +7 -0
  20. data/lib/sprockets/autoload.rb +5 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +61 -13
  23. data/lib/sprockets/bower.rb +6 -3
  24. data/lib/sprockets/bundle.rb +41 -5
  25. data/lib/sprockets/cache/file_store.rb +32 -7
  26. data/lib/sprockets/cache/memory_store.rb +28 -10
  27. data/lib/sprockets/cache/null_store.rb +8 -0
  28. data/lib/sprockets/cache.rb +43 -6
  29. data/lib/sprockets/cached_environment.rb +15 -20
  30. data/lib/sprockets/closure_compressor.rb +6 -11
  31. data/lib/sprockets/coffee_script_processor.rb +20 -6
  32. data/lib/sprockets/compressing.rb +62 -2
  33. data/lib/sprockets/configuration.rb +5 -9
  34. data/lib/sprockets/context.rb +99 -25
  35. data/lib/sprockets/dependencies.rb +10 -9
  36. data/lib/sprockets/digest_utils.rb +103 -62
  37. data/lib/sprockets/directive_processor.rb +64 -36
  38. data/lib/sprockets/eco_processor.rb +4 -3
  39. data/lib/sprockets/ejs_processor.rb +4 -3
  40. data/lib/sprockets/encoding_utils.rb +1 -0
  41. data/lib/sprockets/environment.rb +9 -4
  42. data/lib/sprockets/erb_processor.rb +34 -21
  43. data/lib/sprockets/errors.rb +1 -0
  44. data/lib/sprockets/exporters/base.rb +71 -0
  45. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  46. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  47. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  48. data/lib/sprockets/exporting.rb +73 -0
  49. data/lib/sprockets/file_reader.rb +1 -0
  50. data/lib/sprockets/http_utils.rb +25 -7
  51. data/lib/sprockets/jsminc_compressor.rb +32 -0
  52. data/lib/sprockets/jst_processor.rb +11 -10
  53. data/lib/sprockets/loader.rb +244 -62
  54. data/lib/sprockets/manifest.rb +100 -46
  55. data/lib/sprockets/manifest_utils.rb +9 -6
  56. data/lib/sprockets/mime.rb +8 -42
  57. data/lib/sprockets/npm.rb +52 -0
  58. data/lib/sprockets/path_dependency_utils.rb +3 -11
  59. data/lib/sprockets/path_digest_utils.rb +2 -1
  60. data/lib/sprockets/path_utils.rb +107 -22
  61. data/lib/sprockets/paths.rb +1 -0
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +32 -52
  64. data/lib/sprockets/processor_utils.rb +38 -39
  65. data/lib/sprockets/resolve.rb +177 -97
  66. data/lib/sprockets/sass_cache_store.rb +1 -0
  67. data/lib/sprockets/sass_compressor.rb +21 -17
  68. data/lib/sprockets/sass_functions.rb +1 -0
  69. data/lib/sprockets/sass_importer.rb +1 -0
  70. data/lib/sprockets/sass_processor.rb +46 -18
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +77 -44
  74. data/lib/sprockets/source_map_processor.rb +66 -0
  75. data/lib/sprockets/source_map_utils.rb +483 -0
  76. data/lib/sprockets/transformers.rb +63 -35
  77. data/lib/sprockets/uglifier_compressor.rb +23 -20
  78. data/lib/sprockets/unloaded_asset.rb +139 -0
  79. data/lib/sprockets/uri_tar.rb +99 -0
  80. data/lib/sprockets/uri_utils.rb +14 -14
  81. data/lib/sprockets/utils/gzip.rb +99 -0
  82. data/lib/sprockets/utils.rb +63 -71
  83. data/lib/sprockets/version.rb +2 -1
  84. data/lib/sprockets/yui_compressor.rb +5 -14
  85. data/lib/sprockets.rb +105 -33
  86. metadata +157 -27
  87. data/lib/sprockets/coffee_script_template.rb +0 -6
  88. data/lib/sprockets/eco_template.rb +0 -6
  89. data/lib/sprockets/ejs_template.rb +0 -6
  90. data/lib/sprockets/engines.rb +0 -81
  91. data/lib/sprockets/erb_template.rb +0 -6
  92. data/lib/sprockets/legacy.rb +0 -314
  93. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  94. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  95. data/lib/sprockets/sass_template.rb +0 -7
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Sprockets
2
3
  # Internal: HTTP URI utilities. Many adapted from Rack::Utils. Mixed into
3
4
  # Environment.
@@ -13,9 +14,9 @@ module Sprockets
13
14
  # Returns true if the given value is a mime match for the given mime match
14
15
  # specification, false otherwise.
15
16
  def match_mime_type?(value, matcher)
16
- v1, v2 = value.split('/', 2)
17
- m1, m2 = matcher.split('/', 2)
18
- (m1 == '*' || v1 == m1) && (m2.nil? || m2 == '*' || m2 == v2)
17
+ v1, v2 = value.split('/'.freeze, 2)
18
+ m1, m2 = matcher.split('/'.freeze, 2)
19
+ (m1 == '*'.freeze || v1 == m1) && (m2.nil? || m2 == '*'.freeze || m2 == v2)
19
20
  end
20
21
 
21
22
  # Public: Return values from Hash where the key matches the mime type.
@@ -36,7 +37,22 @@ module Sprockets
36
37
 
37
38
  # Internal: Parse Accept header quality values.
38
39
  #
39
- # Adapted from Rack::Utils#q_values.
40
+ # values - String e.g. "application/javascript"
41
+ #
42
+ # Adapted from Rack::Utils#q_values. Quality values are
43
+ # described in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
44
+ #
45
+ # parse_q_values("application/javascript")
46
+ # # => [["application/javascript", 1.0]]
47
+ #
48
+ # parse_q_values("*/*")
49
+ # # => [["*/*", 1.0]]
50
+ #
51
+ # parse_q_values("text/plain; q=0.5, image/*")
52
+ # # => [["text/plain", 0.5], ["image/*", 1.0]]
53
+ #
54
+ # parse_q_values("application/javascript, text/css")
55
+ # # => [["application/javascript", 1.0], ["text/css", 1.0]]
40
56
  #
41
57
  # Returns an Array of [String, Float].
42
58
  def parse_q_values(values)
@@ -70,14 +86,16 @@ module Sprockets
70
86
  raise TypeError, "unknown q_values type: #{q_values.class}"
71
87
  end
72
88
 
89
+ i = 0
73
90
  q_values.each do |accepted, quality|
74
91
  if match = available.find { |option| matcher.call(option, accepted) }
75
- matches << [match, quality]
92
+ i += 1
93
+ matches << [-quality, i, match]
76
94
  end
77
95
  end
78
96
 
79
- matches.sort_by! { |match, quality| -quality }
80
- matches.map! { |match, quality| match }
97
+ matches.sort!
98
+ matches.map! { |_, _, match| match }
81
99
  matches
82
100
  end
83
101
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+ require 'sprockets/digest_utils'
4
+
5
+ module Sprockets
6
+ class JSMincCompressor
7
+ VERSION = '1'
8
+
9
+ def self.instance
10
+ @instance ||= new
11
+ end
12
+
13
+ def self.call(input)
14
+ instance.call(input)
15
+ end
16
+
17
+ def self.cache_key
18
+ instance.cache_key
19
+ end
20
+
21
+ attr_reader :cache_key
22
+
23
+ def initialize(options = {})
24
+ @compressor_class = Autoload::JSMinC
25
+ @cache_key = "#{self.class.name}:#{Autoload::JSMinC::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
26
+ end
27
+
28
+ def call(input)
29
+ @compressor_class.minify(input[:data])
30
+ end
31
+ end
32
+ end
@@ -1,21 +1,22 @@
1
+ # frozen_string_literal: true
1
2
  module Sprockets
2
- # Public: .jst engine.
3
+ # Public: JST transformer.
3
4
  #
4
5
  # Exports server side compiled templates to an object.
5
6
  #
6
- # Name your template "users/show.jst.ejs", "users/new.jst.eco", etc.
7
+ # Name your template "users/show.ejs", "users/new.eco", etc.
7
8
  #
8
9
  # To accept the default options
9
10
  #
10
- # environment.register_engine '.jst',
11
- # JstProcessor,
12
- # mime_type: 'application/javascript'
11
+ # environment.register_transformer
12
+ # 'application/javascript+function',
13
+ # 'application/javascript', JstProcessor
13
14
  #
14
15
  # Change the default namespace.
15
16
  #
16
- # environment.register_engine '.jst',
17
- # JstProcessor.new(namespace: 'App.templates'),
18
- # mime_type: 'application/javascript'
17
+ # environment.register_transformer
18
+ # 'application/javascript+function',
19
+ # 'application/javascript', JstProcessor.new(namespace: 'App.templates')
19
20
  #
20
21
  class JstProcessor
21
22
  def self.default_namespace
@@ -33,8 +34,8 @@ module Sprockets
33
34
  instance.call(input)
34
35
  end
35
36
 
36
- def initialize(options = {})
37
- @namespace = options[:namespace] || self.class.default_namespace
37
+ def initialize(namespace: self.class.default_namespace)
38
+ @namespace = namespace
38
39
  end
39
40
 
40
41
  def call(input)
@@ -1,6 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/asset'
2
3
  require 'sprockets/digest_utils'
3
- require 'sprockets/engines'
4
4
  require 'sprockets/errors'
5
5
  require 'sprockets/file_reader'
6
6
  require 'sprockets/mime'
@@ -10,66 +10,161 @@ require 'sprockets/processor_utils'
10
10
  require 'sprockets/resolve'
11
11
  require 'sprockets/transformers'
12
12
  require 'sprockets/uri_utils'
13
+ require 'sprockets/unloaded_asset'
13
14
 
14
15
  module Sprockets
16
+
15
17
  # The loader phase takes a asset URI location and returns a constructed Asset
16
18
  # object.
17
19
  module Loader
18
20
  include DigestUtils, PathUtils, ProcessorUtils, URIUtils
19
- include Engines, Mime, Processing, Resolve, Transformers
21
+ include Mime, Processing, Resolve, Transformers
22
+
20
23
 
21
- # Public: Load Asset by AssetURI.
24
+ # Public: Load Asset by Asset URI.
22
25
  #
23
- # uri - AssetURI
26
+ # uri - A String containing complete URI to a file including schema
27
+ # and full path such as:
28
+ # "file:///Path/app/assets/js/app.js?type=application/javascript"
24
29
  #
25
30
  # Returns Asset.
26
31
  def load(uri)
27
- filename, params = parse_asset_uri(uri)
28
- if params.key?(:id)
29
- unless asset = cache.get("asset-uri:#{VERSION}:#{uri}", true)
30
- id = params.delete(:id)
31
- uri_without_id = build_asset_uri(filename, params)
32
- asset = load_asset_by_uri(uri_without_id, filename, params)
32
+ unloaded = UnloadedAsset.new(uri, self)
33
+ if unloaded.params.key?(:id)
34
+ unless asset = asset_from_cache(unloaded.asset_key)
35
+ id = unloaded.params.delete(:id)
36
+ uri_without_id = build_asset_uri(unloaded.filename, unloaded.params)
37
+ asset = load_from_unloaded(UnloadedAsset.new(uri_without_id, self))
33
38
  if asset[:id] != id
34
39
  @logger.warn "Sprockets load error: Tried to find #{uri}, but latest was id #{asset[:id]}"
35
40
  end
36
41
  end
37
42
  else
38
- asset = fetch_asset_from_dependency_cache(uri, filename) do |paths|
43
+ asset = fetch_asset_from_dependency_cache(unloaded) do |paths|
44
+ # When asset is previously generated, its "dependencies" are stored in the cache.
45
+ # The presence of `paths` indicates dependencies were stored.
46
+ # We can check to see if the dependencies have not changed by "resolving" them and
47
+ # generating a digest key from the resolved entries. If this digest key has not
48
+ # changed, the asset will be pulled from cache.
49
+ #
50
+ # If this `paths` is present but the cache returns nothing then `fetch_asset_from_dependency_cache`
51
+ # will confusingly be called again with `paths` set to nil where the asset will be
52
+ # loaded from disk.
39
53
  if paths
40
- digest = digest(resolve_dependencies(paths))
41
- if id_uri = cache.get("asset-uri-digest:#{VERSION}:#{uri}:#{digest}", true)
42
- cache.get("asset-uri:#{VERSION}:#{id_uri}", true)
54
+ digest = DigestUtils.digest(resolve_dependencies(paths))
55
+ if uri_from_cache = cache.get(unloaded.digest_key(digest), true)
56
+ asset_from_cache(UnloadedAsset.new(uri_from_cache, self).asset_key)
43
57
  end
44
58
  else
45
- load_asset_by_uri(uri, filename, params)
59
+ load_from_unloaded(unloaded)
46
60
  end
47
61
  end
48
62
  end
49
- Asset.new(self, asset)
63
+ Asset.new(asset)
50
64
  end
51
65
 
52
66
  private
53
- def load_asset_by_uri(uri, filename, params)
54
- unless file?(filename)
55
- raise FileNotFound, "could not find file: #{filename}"
67
+ def compress_key_from_hash(hash, key)
68
+ return unless hash.key?(key)
69
+ value = hash[key].dup
70
+ return if !value
71
+
72
+ if block_given?
73
+ value.map! do |x|
74
+ if yield x
75
+ compress_from_root(x)
76
+ else
77
+ x
78
+ end
79
+ end
80
+ else
81
+ value.map! { |x| compress_from_root(x) }
82
+ end
83
+ hash[key] = value
84
+ end
85
+
86
+
87
+ def expand_key_from_hash(hash, key)
88
+ return unless hash.key?(key)
89
+ value = hash[key].dup
90
+ return if !value
91
+ if block_given?
92
+ value.map! do |x|
93
+ if yield x
94
+ expand_from_root(x)
95
+ else
96
+ x
97
+ end
98
+ end
99
+ else
100
+ value.map! { |x| expand_from_root(x) }
101
+ end
102
+ hash[key] = value
103
+ end
104
+
105
+ # Internal: Load asset hash from cache
106
+ #
107
+ # key - A String containing lookup information for an asset
108
+ #
109
+ # This method converts all "compressed" paths to absolute paths.
110
+ # Returns a hash of values representing an asset
111
+ def asset_from_cache(key)
112
+ asset = cache.get(key, true)
113
+ if asset
114
+ asset[:uri] = expand_from_root(asset[:uri])
115
+ asset[:load_path] = expand_from_root(asset[:load_path])
116
+ asset[:filename] = expand_from_root(asset[:filename])
117
+ expand_key_from_hash(asset[:metadata], :included)
118
+ expand_key_from_hash(asset[:metadata], :links)
119
+ expand_key_from_hash(asset[:metadata], :stubbed)
120
+ expand_key_from_hash(asset[:metadata], :required)
121
+ expand_key_from_hash(asset[:metadata], :to_load)
122
+ expand_key_from_hash(asset[:metadata], :to_link)
123
+ expand_key_from_hash(asset[:metadata], :dependencies) { |uri| uri.start_with?("file-digest://") }
124
+
125
+ asset[:metadata].each_key do |k|
126
+ next unless k.match?(/_dependencies\z/) # rubocop:disable Performance/EndWith
127
+ expand_key_from_hash(asset[:metadata], k)
128
+ end
129
+ end
130
+ asset
131
+ end
132
+
133
+ # Internal: Loads an asset and saves it to cache
134
+ #
135
+ # unloaded - An UnloadedAsset
136
+ #
137
+ # This method is only called when the given unloaded asset could not be
138
+ # successfully pulled from cache.
139
+ def load_from_unloaded(unloaded)
140
+ unless file?(unloaded.filename)
141
+ raise FileNotFound, "could not find file: #{unloaded.filename}"
56
142
  end
57
143
 
58
- load_path, logical_path = paths_split(config[:paths], filename)
144
+ path_to_split =
145
+ if index_alias = unloaded.params[:index_alias]
146
+ expand_from_root index_alias
147
+ else
148
+ unloaded.filename
149
+ end
150
+
151
+ load_path, logical_path = paths_split(config[:paths], path_to_split)
59
152
 
60
153
  unless load_path
61
- raise FileOutsidePaths, "#{filename} is no longer under a load path: #{self.paths.join(', ')}"
154
+ target = path_to_split
155
+ target += " (index alias of #{unloaded.filename})" if unloaded.params[:index_alias]
156
+ raise FileOutsidePaths, "#{target} is no longer under a load path: #{self.paths.join(', ')}"
62
157
  end
63
158
 
64
- logical_path, file_type, engine_extnames, _ = parse_path_extnames(logical_path)
65
- logical_path = normalize_logical_path(logical_path)
159
+ extname, file_type = match_path_extname(logical_path, mime_exts)
160
+ logical_path = logical_path.chomp(extname)
66
161
  name = logical_path
67
162
 
68
- if pipeline = params[:pipeline]
163
+ if pipeline = unloaded.params[:pipeline]
69
164
  logical_path += ".#{pipeline}"
70
165
  end
71
166
 
72
- if type = params[:type]
167
+ if type = unloaded.params[:type]
73
168
  logical_path += config[:mime_types][type][:extensions].first
74
169
  end
75
170
 
@@ -77,9 +172,9 @@ module Sprockets
77
172
  raise ConversionError, "could not convert #{file_type.inspect} to #{type.inspect}"
78
173
  end
79
174
 
80
- processors = processors_for(type, file_type, engine_extnames, pipeline)
175
+ processors = processors_for(type, file_type, pipeline)
81
176
 
82
- processors_dep_uri = build_processors_uri(type, file_type, engine_extnames, pipeline)
177
+ processors_dep_uri = build_processors_uri(type, file_type, pipeline)
83
178
  dependencies = config[:dependencies] + [processors_dep_uri]
84
179
 
85
180
  # Read into memory and process if theres a processor pipeline
@@ -87,75 +182,162 @@ module Sprockets
87
182
  result = call_processors(processors, {
88
183
  environment: self,
89
184
  cache: self.cache,
90
- uri: uri,
91
- filename: filename,
185
+ uri: unloaded.uri,
186
+ filename: unloaded.filename,
92
187
  load_path: load_path,
93
188
  name: name,
94
189
  content_type: type,
95
- metadata: { dependencies: dependencies }
190
+ metadata: {
191
+ dependencies: dependencies
192
+ }
96
193
  })
97
194
  validate_processor_result!(result)
98
195
  source = result.delete(:data)
99
- metadata = result.merge!(
100
- charset: source.encoding.name.downcase,
101
- digest: digest(source),
102
- length: source.bytesize
103
- )
196
+ metadata = result
197
+ metadata[:charset] = source.encoding.name.downcase unless metadata.key?(:charset)
198
+ metadata[:digest] = digest(source)
199
+ metadata[:length] = source.bytesize
200
+ metadata[:environment_version] = version
104
201
  else
105
- dependencies << build_file_digest_uri(filename)
202
+ dependencies << build_file_digest_uri(unloaded.filename)
106
203
  metadata = {
107
- digest: file_digest(filename),
108
- length: self.stat(filename).size,
109
- dependencies: dependencies
204
+ digest: file_digest(unloaded.filename),
205
+ length: self.stat(unloaded.filename).size,
206
+ dependencies: dependencies,
207
+ environment_version: version,
110
208
  }
111
209
  end
112
210
 
113
211
  asset = {
114
- uri: uri,
212
+ uri: unloaded.uri,
115
213
  load_path: load_path,
116
- filename: filename,
214
+ filename: unloaded.filename,
117
215
  name: name,
118
216
  logical_path: logical_path,
119
217
  content_type: type,
120
218
  source: source,
121
219
  metadata: metadata,
122
- integrity: integrity_uri(metadata[:digest], type),
123
- dependencies_digest: digest(resolve_dependencies(metadata[:dependencies]))
220
+ dependencies_digest: DigestUtils.digest(resolve_dependencies(metadata[:dependencies]))
124
221
  }
125
222
 
126
- asset[:id] = pack_hexdigest(digest(asset))
127
- asset[:uri] = build_asset_uri(filename, params.merge(id: asset[:id]))
223
+ asset[:id] = hexdigest(asset)
224
+ asset[:uri] = build_asset_uri(unloaded.filename, unloaded.params.merge(id: asset[:id]))
128
225
 
129
- # Deprecated: Avoid tracking Asset mtime
130
- asset[:mtime] = metadata[:dependencies].map { |u|
131
- if u.start_with?("file-digest:")
132
- s = self.stat(parse_file_digest_uri(u))
133
- s ? s.mtime.to_i : nil
134
- else
135
- nil
226
+ store_asset(asset, unloaded)
227
+ asset
228
+ end
229
+
230
+ # Internal: Save a given asset to the cache
231
+ #
232
+ # asset - A hash containing values of loaded asset
233
+ # unloaded - The UnloadedAsset used to lookup the `asset`
234
+ #
235
+ # This method converts all absolute paths to "compressed" paths
236
+ # which are relative if they're in the root.
237
+ def store_asset(asset, unloaded)
238
+ # Save the asset in the cache under the new URI
239
+ cached_asset = asset.dup
240
+ cached_asset[:uri] = compress_from_root(asset[:uri])
241
+ cached_asset[:filename] = compress_from_root(asset[:filename])
242
+ cached_asset[:load_path] = compress_from_root(asset[:load_path])
243
+
244
+ if cached_asset[:metadata]
245
+ # Deep dup to avoid modifying `asset`
246
+ cached_asset[:metadata] = cached_asset[:metadata].dup
247
+ compress_key_from_hash(cached_asset[:metadata], :included)
248
+ compress_key_from_hash(cached_asset[:metadata], :links)
249
+ compress_key_from_hash(cached_asset[:metadata], :stubbed)
250
+ compress_key_from_hash(cached_asset[:metadata], :required)
251
+ compress_key_from_hash(cached_asset[:metadata], :to_load)
252
+ compress_key_from_hash(cached_asset[:metadata], :to_link)
253
+ compress_key_from_hash(cached_asset[:metadata], :dependencies) { |uri| uri.start_with?("file-digest://") }
254
+
255
+ cached_asset[:metadata].each do |key, value|
256
+ next unless key.match?(/_dependencies\z/) # rubocop:disable Performance/EndWith
257
+ compress_key_from_hash(cached_asset[:metadata], key)
136
258
  end
137
- }.compact.max
138
- asset[:mtime] ||= self.stat(filename).mtime.to_i
259
+ end
139
260
 
140
- cache.set("asset-uri:#{VERSION}:#{asset[:uri]}", asset, true)
141
- cache.set("asset-uri-digest:#{VERSION}:#{uri}:#{asset[:dependencies_digest]}", asset[:uri], true)
261
+ # Unloaded asset and stored_asset now have a different URI
262
+ stored_asset = UnloadedAsset.new(asset[:uri], self)
263
+ cache.set(stored_asset.asset_key, cached_asset, true)
142
264
 
143
- asset
265
+ # Save the new relative path for the digest key of the unloaded asset
266
+ cache.set(unloaded.digest_key(asset[:dependencies_digest]), stored_asset.compressed_path, true)
144
267
  end
145
268
 
146
- def fetch_asset_from_dependency_cache(uri, filename, limit = 3)
147
- key = "asset-uri-cache-dependencies:#{VERSION}:#{uri}:#{file_digest(filename)}"
148
- history = cache.get(key) || []
149
269
 
270
+ # Internal: Resolve set of dependency URIs.
271
+ #
272
+ # uris - An Array of "dependencies" for example:
273
+ # ["environment-version", "environment-paths", "processors:type=text/css&file_type=text/css",
274
+ # "file-digest:///Full/path/app/assets/stylesheets/application.css",
275
+ # "processors:type=text/css&file_type=text/css&pipeline=self",
276
+ # "file-digest:///Full/path/app/assets/stylesheets"]
277
+ #
278
+ # Returns back array of things that the given uri depends on
279
+ # For example the environment version, if you're using a different version of sprockets
280
+ # then the dependencies should be different, this is used only for generating cache key
281
+ # for example the "environment-version" may be resolved to "environment-1.0-3.2.0" for
282
+ # version "3.2.0" of sprockets.
283
+ #
284
+ # Any paths that are returned are converted to relative paths
285
+ #
286
+ # Returns array of resolved dependencies
287
+ def resolve_dependencies(uris)
288
+ uris.map { |uri| resolve_dependency(uri) }
289
+ end
290
+
291
+ # Internal: Retrieves an asset based on its digest
292
+ #
293
+ # unloaded - An UnloadedAsset
294
+ # limit - An Integer which sets the maximum number of versions of "histories"
295
+ # stored in the cache
296
+ #
297
+ # This method attempts to retrieve the last `limit` number of histories of an asset
298
+ # from the cache a "history" which is an array of unresolved "dependencies" that the asset needs
299
+ # to compile. In this case a dependency can refer to either an asset e.g. index.js
300
+ # may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect
301
+ # compilation, such as the VERSION of Sprockets (i.e. the environment) and what "processors"
302
+ # are used.
303
+ #
304
+ # For example a history array may look something like this
305
+ #
306
+ # [["environment-version", "environment-paths", "processors:type=text/css&file_type=text/css",
307
+ # "file-digest:///Full/path/app/assets/stylesheets/application.css",
308
+ # "processors:type=text/css&file_digesttype=text/css&pipeline=self",
309
+ # "file-digest:///Full/path/app/assets/stylesheets"]]
310
+ #
311
+ # Where the first entry is a Set of dependencies for last generated version of that asset.
312
+ # Multiple versions are stored since Sprockets keeps the last `limit` number of assets
313
+ # generated present in the system.
314
+ #
315
+ # If a "history" of dependencies is present in the cache, each version of "history" will be
316
+ # yielded to the passed block which is responsible for loading the asset. If found, the existing
317
+ # history will be saved with the dependency that found a valid asset moved to the front.
318
+ #
319
+ # If no history is present, or if none of the histories could be resolved to a valid asset then,
320
+ # the block is yielded to and expected to return a valid asset.
321
+ # When this happens the dependencies for the returned asset are added to the "history", and older
322
+ # entries are removed if the "history" is above `limit`.
323
+ def fetch_asset_from_dependency_cache(unloaded, limit = 3)
324
+ key = unloaded.dependency_history_key
325
+
326
+ history = cache.get(key) || []
150
327
  history.each_with_index do |deps, index|
151
- if asset = yield(deps)
328
+ expanded_deps = deps.map do |path|
329
+ path.start_with?("file-digest://") ? expand_from_root(path) : path
330
+ end
331
+ if asset = yield(expanded_deps)
152
332
  cache.set(key, history.rotate!(index)) if index > 0
153
333
  return asset
154
334
  end
155
335
  end
156
336
 
157
337
  asset = yield
158
- deps = asset[:metadata][:dependencies]
338
+ deps = asset[:metadata][:dependencies].dup.map! do |uri|
339
+ uri.start_with?("file-digest://") ? compress_from_root(uri) : uri
340
+ end
159
341
  cache.set(key, history.unshift(deps).take(limit))
160
342
  asset
161
343
  end