sprockets 2.12.5 → 3.7.2

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