sprockets 3.7.3 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -259
  3. data/{LICENSE → MIT-LICENSE} +2 -2
  4. data/README.md +527 -320
  5. data/bin/sprockets +11 -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 +39 -27
  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 +49 -12
  23. data/lib/sprockets/bower.rb +6 -3
  24. data/lib/sprockets/bundle.rb +41 -5
  25. data/lib/sprockets/cache/file_store.rb +25 -3
  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 +37 -2
  29. data/lib/sprockets/cached_environment.rb +15 -20
  30. data/lib/sprockets/closure_compressor.rb +1 -0
  31. data/lib/sprockets/coffee_script_processor.rb +19 -5
  32. data/lib/sprockets/compressing.rb +43 -3
  33. data/lib/sprockets/configuration.rb +5 -9
  34. data/lib/sprockets/context.rb +99 -25
  35. data/lib/sprockets/dependencies.rb +2 -1
  36. data/lib/sprockets/digest_utils.rb +35 -18
  37. data/lib/sprockets/directive_processor.rb +64 -38
  38. data/lib/sprockets/eco_processor.rb +2 -1
  39. data/lib/sprockets/ejs_processor.rb +2 -1
  40. data/lib/sprockets/encoding_utils.rb +2 -2
  41. data/lib/sprockets/environment.rb +9 -4
  42. data/lib/sprockets/erb_processor.rb +33 -32
  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 +91 -69
  54. data/lib/sprockets/manifest.rb +67 -64
  55. data/lib/sprockets/manifest_utils.rb +9 -6
  56. data/lib/sprockets/mime.rb +8 -62
  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 +88 -8
  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 -62
  64. data/lib/sprockets/processor_utils.rb +28 -38
  65. data/lib/sprockets/resolve.rb +177 -93
  66. data/lib/sprockets/sass_cache_store.rb +2 -6
  67. data/lib/sprockets/sass_compressor.rb +13 -1
  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 +31 -10
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +63 -40
  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 +21 -11
  78. data/lib/sprockets/unloaded_asset.rb +13 -11
  79. data/lib/sprockets/uri_tar.rb +1 -0
  80. data/lib/sprockets/uri_utils.rb +19 -16
  81. data/lib/sprockets/utils/gzip.rb +46 -14
  82. data/lib/sprockets/utils.rb +64 -90
  83. data/lib/sprockets/version.rb +2 -1
  84. data/lib/sprockets/yui_compressor.rb +1 -0
  85. data/lib/sprockets.rb +102 -39
  86. metadata +148 -45
  87. data/lib/sprockets/coffee_script_template.rb +0 -17
  88. data/lib/sprockets/deprecation.rb +0 -90
  89. data/lib/sprockets/eco_template.rb +0 -17
  90. data/lib/sprockets/ejs_template.rb +0 -17
  91. data/lib/sprockets/engines.rb +0 -92
  92. data/lib/sprockets/erb_template.rb +0 -11
  93. data/lib/sprockets/legacy.rb +0 -330
  94. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  95. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  96. data/lib/sprockets/sass_template.rb +0 -19
@@ -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'
@@ -18,7 +18,7 @@ module Sprockets
18
18
  # object.
19
19
  module Loader
20
20
  include DigestUtils, PathUtils, ProcessorUtils, URIUtils
21
- include Engines, Mime, Processing, Resolve, Transformers
21
+ include Mime, Processing, Resolve, Transformers
22
22
 
23
23
 
24
24
  # Public: Load Asset by Asset URI.
@@ -27,7 +27,6 @@ module Sprockets
27
27
  # and full path such as:
28
28
  # "file:///Path/app/assets/js/app.js?type=application/javascript"
29
29
  #
30
- #
31
30
  # Returns Asset.
32
31
  def load(uri)
33
32
  unloaded = UnloadedAsset.new(uri, self)
@@ -46,7 +45,7 @@ module Sprockets
46
45
  # The presence of `paths` indicates dependencies were stored.
47
46
  # We can check to see if the dependencies have not changed by "resolving" them and
48
47
  # generating a digest key from the resolved entries. If this digest key has not
49
- # changed the asset will be pulled from cache.
48
+ # changed, the asset will be pulled from cache.
50
49
  #
51
50
  # If this `paths` is present but the cache returns nothing then `fetch_asset_from_dependency_cache`
52
51
  # will confusingly be called again with `paths` set to nil where the asset will be
@@ -61,10 +60,47 @@ module Sprockets
61
60
  end
62
61
  end
63
62
  end
64
- Asset.new(self, asset)
63
+ Asset.new(asset)
65
64
  end
66
65
 
67
66
  private
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
68
104
 
69
105
  # Internal: Load asset hash from cache
70
106
  #
@@ -78,15 +114,17 @@ module Sprockets
78
114
  asset[:uri] = expand_from_root(asset[:uri])
79
115
  asset[:load_path] = expand_from_root(asset[:load_path])
80
116
  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]
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://") }
86
124
 
87
125
  asset[:metadata].each_key do |k|
88
- next unless k =~ /_dependencies\z/
89
- asset[:metadata][k].map! { |uri| expand_from_root(uri) }
126
+ next unless k.match?(/_dependencies\z/) # rubocop:disable Performance/EndWith
127
+ expand_key_from_hash(asset[:metadata], k)
90
128
  end
91
129
  end
92
130
  asset
@@ -103,13 +141,23 @@ module Sprockets
103
141
  raise FileNotFound, "could not find file: #{unloaded.filename}"
104
142
  end
105
143
 
106
- load_path, logical_path = paths_split(config[:paths], unloaded.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)
107
152
 
108
153
  unless load_path
109
- raise FileOutsidePaths, "#{unloaded.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(', ')}"
110
157
  end
111
158
 
112
- logical_path, file_type, engine_extnames, _ = parse_path_extnames(logical_path)
159
+ extname, file_type = match_path_extname(logical_path, mime_exts)
160
+ logical_path = logical_path.chomp(extname)
113
161
  name = logical_path
114
162
 
115
163
  if pipeline = unloaded.params[:pipeline]
@@ -117,16 +165,18 @@ module Sprockets
117
165
  end
118
166
 
119
167
  if type = unloaded.params[:type]
120
- logical_path += config[:mime_types][type][:extensions].first
168
+ extensions = config[:mime_types][type][:extensions]
169
+ extension = extensions.include?(extname) ? extname : extensions.first
170
+ logical_path += extension
121
171
  end
122
172
 
123
173
  if type != file_type && !config[:transformers][file_type][type]
124
174
  raise ConversionError, "could not convert #{file_type.inspect} to #{type.inspect}"
125
175
  end
126
176
 
127
- processors = processors_for(type, file_type, engine_extnames, pipeline)
177
+ processors = processors_for(type, file_type, pipeline)
128
178
 
129
- processors_dep_uri = build_processors_uri(type, file_type, engine_extnames, pipeline)
179
+ processors_dep_uri = build_processors_uri(type, file_type, pipeline)
130
180
  dependencies = config[:dependencies] + [processors_dep_uri]
131
181
 
132
182
  # Read into memory and process if theres a processor pipeline
@@ -139,7 +189,9 @@ module Sprockets
139
189
  load_path: load_path,
140
190
  name: name,
141
191
  content_type: type,
142
- metadata: { dependencies: dependencies }
192
+ metadata: {
193
+ dependencies: dependencies
194
+ }
143
195
  })
144
196
  validate_processor_result!(result)
145
197
  source = result.delete(:data)
@@ -147,12 +199,14 @@ module Sprockets
147
199
  metadata[:charset] = source.encoding.name.downcase unless metadata.key?(:charset)
148
200
  metadata[:digest] = digest(source)
149
201
  metadata[:length] = source.bytesize
202
+ metadata[:environment_version] = version
150
203
  else
151
204
  dependencies << build_file_digest_uri(unloaded.filename)
152
205
  metadata = {
153
206
  digest: file_digest(unloaded.filename),
154
207
  length: self.stat(unloaded.filename).size,
155
- dependencies: dependencies
208
+ dependencies: dependencies,
209
+ environment_version: version,
156
210
  }
157
211
  end
158
212
 
@@ -168,20 +222,9 @@ module Sprockets
168
222
  dependencies_digest: DigestUtils.digest(resolve_dependencies(metadata[:dependencies]))
169
223
  }
170
224
 
171
- asset[:id] = pack_hexdigest(digest(asset))
225
+ asset[:id] = hexdigest(asset)
172
226
  asset[:uri] = build_asset_uri(unloaded.filename, unloaded.params.merge(id: asset[:id]))
173
227
 
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
228
  store_asset(asset, unloaded)
186
229
  asset
187
230
  end
@@ -203,38 +246,17 @@ module Sprockets
203
246
  if cached_asset[:metadata]
204
247
  # Deep dup to avoid modifying `asset`
205
248
  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
249
+ compress_key_from_hash(cached_asset[:metadata], :included)
250
+ compress_key_from_hash(cached_asset[:metadata], :links)
251
+ compress_key_from_hash(cached_asset[:metadata], :stubbed)
252
+ compress_key_from_hash(cached_asset[:metadata], :required)
253
+ compress_key_from_hash(cached_asset[:metadata], :to_load)
254
+ compress_key_from_hash(cached_asset[:metadata], :to_link)
255
+ compress_key_from_hash(cached_asset[:metadata], :dependencies) { |uri| uri.start_with?("file-digest://") }
232
256
 
233
- # compress all _dependencies in metadata like `sass_dependencies`
234
257
  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) }
258
+ next unless key.match?(/_dependencies\z/) # rubocop:disable Performance/EndWith
259
+ compress_key_from_hash(cached_asset[:metadata], key)
238
260
  end
239
261
  end
240
262
 
@@ -255,11 +277,11 @@ module Sprockets
255
277
  # "processors:type=text/css&file_type=text/css&pipeline=self",
256
278
  # "file-digest:///Full/path/app/assets/stylesheets"]
257
279
  #
258
- # Returns back array of things that the given uri dpends on
280
+ # Returns back array of things that the given uri depends on
259
281
  # For example the environment version, if you're using a different version of sprockets
260
282
  # then the dependencies should be different, this is used only for generating cache key
261
283
  # for example the "environment-version" may be resolved to "environment-1.0-3.2.0" for
262
- # version "3.2.0" of sprockets.
284
+ # version "3.2.0" of sprockets.
263
285
  #
264
286
  # Any paths that are returned are converted to relative paths
265
287
  #
@@ -271,14 +293,14 @@ module Sprockets
271
293
  # Internal: Retrieves an asset based on its digest
272
294
  #
273
295
  # unloaded - An UnloadedAsset
274
- # limit - A Fixnum which sets the maximum number of versions of "histories"
296
+ # limit - An Integer which sets the maximum number of versions of "histories"
275
297
  # stored in the cache
276
298
  #
277
299
  # This method attempts to retrieve the last `limit` number of histories of an asset
278
300
  # 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"
301
+ # to compile. In this case a dependency can refer to either an asset e.g. index.js
302
+ # may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect
303
+ # compilation, such as the VERSION of Sprockets (i.e. the environment) and what "processors"
282
304
  # are used.
283
305
  #
284
306
  # For example a history array may look something like this
@@ -289,7 +311,7 @@ module Sprockets
289
311
  # "file-digest:///Full/path/app/assets/stylesheets"]]
290
312
  #
291
313
  # 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
314
+ # Multiple versions are stored since Sprockets keeps the last `limit` number of assets
293
315
  # generated present in the system.
294
316
  #
295
317
  # If a "history" of dependencies is present in the cache, each version of "history" will be
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require 'json'
2
3
  require 'time'
3
4
 
4
5
  require 'concurrent'
5
6
 
6
7
  require 'sprockets/manifest_utils'
7
- require 'sprockets/utils/gzip'
8
8
 
9
9
  module Sprockets
10
10
  # The Manifest logs the contents of assets compiled to a single directory. It
@@ -15,7 +15,7 @@ module Sprockets
15
15
  # The JSON is part of the public API and should be considered stable. This
16
16
  # should make it easy to read from other programming languages and processes
17
17
  # that don't have sprockets loaded. See `#assets` and `#files` for more
18
- # infomation about the structure.
18
+ # information about the structure.
19
19
  class Manifest
20
20
  include ManifestUtils
21
21
 
@@ -52,14 +52,8 @@ module Sprockets
52
52
  @directory ||= File.dirname(@filename) if @filename
53
53
 
54
54
  # If directory is given w/o filename, pick a random manifest location
55
- @rename_filename = nil
56
55
  if @directory && @filename.nil?
57
- @filename = find_directory_manifest(@directory)
58
-
59
- # If legacy manifest name autodetected, mark to rename on save
60
- if File.basename(@filename).start_with?("manifest")
61
- @rename_filename = File.join(@directory, generate_manifest_path)
62
- end
56
+ @filename = find_directory_manifest(@directory, logger)
63
57
  end
64
58
 
65
59
  unless @directory && @filename
@@ -118,32 +112,22 @@ module Sprockets
118
112
  # Public: Find all assets matching pattern set in environment.
119
113
  #
120
114
  # Returns Enumerator of Assets.
121
- def find(*args)
115
+ def find(*args, &block)
122
116
  unless environment
123
117
  raise Error, "manifest requires environment for compilation"
124
118
  end
125
119
 
126
120
  return to_enum(__method__, *args) unless block_given?
127
121
 
128
- paths, filters = args.flatten.partition { |arg| self.class.simple_logical_path?(arg) }
129
- filters = filters.map { |arg| self.class.compile_match_filter(arg) }
130
-
131
122
  environment = self.environment.cached
132
-
133
- paths.each do |path|
134
- environment.find_all_linked_assets(path) do |asset|
135
- yield asset
123
+ promises = args.flatten.map do |path|
124
+ Concurrent::Promise.execute(executor: executor) do
125
+ environment.find_all_linked_assets(path).to_a
136
126
  end
137
127
  end
138
128
 
139
- if filters.any?
140
- environment.logical_paths do |logical_path, filename|
141
- if filters.any? { |f| f.call(logical_path, filename) }
142
- environment.find_all_linked_assets(filename) do |asset|
143
- yield asset
144
- end
145
- end
146
- end
129
+ promises.each do |promise|
130
+ promise.value!.each(&block)
147
131
  end
148
132
 
149
133
  nil
@@ -167,7 +151,7 @@ module Sprockets
167
151
  end
168
152
  end
169
153
 
170
- # Compile and write asset to directory. The asset is written to a
154
+ # Compile asset to directory. The asset is written to a
171
155
  # fingerprinted filename like
172
156
  # `application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js`. An entry is
173
157
  # also inserted into the manifest file.
@@ -179,14 +163,19 @@ module Sprockets
179
163
  raise Error, "manifest requires environment for compilation"
180
164
  end
181
165
 
182
- filenames = []
183
- concurrent_compressors = []
184
- concurrent_writers = []
166
+ filenames = []
167
+ concurrent_exporters = []
185
168
 
169
+ assets_to_export = Concurrent::Array.new
186
170
  find(*args) do |asset|
171
+ assets_to_export << asset
172
+ end
173
+
174
+ assets_to_export.each do |asset|
175
+ mtime = Time.now.iso8601
187
176
  files[asset.digest_path] = {
188
177
  'logical_path' => asset.logical_path,
189
- 'mtime' => asset.mtime.iso8601,
178
+ 'mtime' => mtime,
190
179
  'size' => asset.bytesize,
191
180
  'digest' => asset.hexdigest,
192
181
 
@@ -197,38 +186,23 @@ module Sprockets
197
186
  }
198
187
  assets[asset.logical_path] = asset.digest_path
199
188
 
200
- if alias_logical_path = self.class.compute_alias_logical_path(asset.logical_path)
201
- assets[alias_logical_path] = asset.digest_path
202
- end
203
-
204
- target = File.join(dir, asset.digest_path)
205
-
206
- if File.exist?(target)
207
- logger.debug "Skipping #{target}, already exists"
208
- else
209
- logger.info "Writing #{target}"
210
- write_file = Concurrent::Future.execute { asset.write_to target }
211
- concurrent_writers << write_file
212
- end
213
189
  filenames << asset.filename
214
190
 
215
- next if environment.skip_gzip?
216
- gzip = Utils::Gzip.new(asset)
217
- next if gzip.cannot_compress?(environment.mime_types)
191
+ promise = nil
192
+ exporters_for_asset(asset) do |exporter|
193
+ next if exporter.skip?(logger)
218
194
 
219
- if File.exist?("#{target}.gz")
220
- logger.debug "Skipping #{target}.gz, already exists"
221
- else
222
- logger.info "Writing #{target}.gz"
223
- concurrent_compressors << Concurrent::Future.execute do
224
- write_file.wait! if write_file
225
- gzip.compress(target)
195
+ if promise.nil?
196
+ promise = Concurrent::Promise.new(executor: executor) { exporter.call }
197
+ concurrent_exporters << promise.execute
198
+ else
199
+ concurrent_exporters << promise.then { exporter.call }
226
200
  end
227
201
  end
228
-
229
202
  end
230
- concurrent_writers.each(&:wait!)
231
- concurrent_compressors.each(&:wait!)
203
+
204
+ # make sure all exporters have finished before returning the main thread
205
+ concurrent_exporters.each(&:wait!)
232
206
  save
233
207
 
234
208
  filenames
@@ -295,18 +269,13 @@ module Sprockets
295
269
  def clobber
296
270
  FileUtils.rm_r(directory) if File.exist?(directory)
297
271
  logger.info "Removed #{directory}"
272
+ # if we have an environment clear the cache too
273
+ environment.cache.clear if environment
298
274
  nil
299
275
  end
300
276
 
301
- # Persist manfiest back to FS
277
+ # Persist manifest back to FS
302
278
  def save
303
- if @rename_filename
304
- logger.info "Renaming #{@filename} to #{@rename_filename}"
305
- FileUtils.mv(@filename, @rename_filename)
306
- @filename = @rename_filename
307
- @rename_filename = nil
308
- end
309
-
310
279
  data = json_encode(@data)
311
280
  FileUtils.mkdir_p File.dirname(@filename)
312
281
  PathUtils.atomic_write(@filename) do |f|
@@ -315,6 +284,36 @@ module Sprockets
315
284
  end
316
285
 
317
286
  private
287
+
288
+ # Given an asset, finds all exporters that
289
+ # match its mime-type.
290
+ #
291
+ # Will yield each expoter to the passed in block.
292
+ #
293
+ # array = []
294
+ # puts asset.content_type # => "application/javascript"
295
+ # exporters_for_asset(asset) do |exporter|
296
+ # array << exporter
297
+ # end
298
+ # # puts array => [Exporters::FileExporter, Exporters::ZlibExporter]
299
+ def exporters_for_asset(asset)
300
+ exporters = [Exporters::FileExporter]
301
+
302
+ environment.exporters.each do |mime_type, exporter_list|
303
+ next unless asset.content_type
304
+ next unless environment.match_mime_type? asset.content_type, mime_type
305
+ exporter_list.each do |exporter|
306
+ exporters << exporter
307
+ end
308
+ end
309
+
310
+ exporters.uniq!
311
+
312
+ exporters.each do |exporter|
313
+ yield exporter.new(asset: asset, environment: environment, directory: dir)
314
+ end
315
+ end
316
+
318
317
  def json_decode(obj)
319
318
  JSON.parse(obj, create_additions: false)
320
319
  end
@@ -332,5 +331,9 @@ module Sprockets
332
331
  logger
333
332
  end
334
333
  end
334
+
335
+ def executor
336
+ @executor ||= environment.export_concurrent ? :fast : :immediate
337
+ end
335
338
  end
336
339
  end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  require 'securerandom'
3
+ require 'logger'
2
4
 
3
5
  module Sprockets
4
6
  # Public: Manifest utilities.
@@ -6,7 +8,6 @@ module Sprockets
6
8
  extend self
7
9
 
8
10
  MANIFEST_RE = /^\.sprockets-manifest-[0-9a-f]{32}.json$/
9
- LEGACY_MANIFEST_RE = /^manifest(-[0-9a-f]{32})?.json$/
10
11
 
11
12
  # Public: Generate a new random manifest path.
12
13
  #
@@ -33,12 +34,14 @@ module Sprockets
33
34
  # # => "/app/public/assets/.sprockets-manifest-abc123.json"
34
35
  #
35
36
  # Returns String filename.
36
- def find_directory_manifest(dirname)
37
+ def find_directory_manifest(dirname, logger = Logger.new($stderr))
37
38
  entries = File.directory?(dirname) ? Dir.entries(dirname) : []
38
- entry = entries.find { |e| e =~ MANIFEST_RE } ||
39
- # Deprecated: Will be removed in 4.x
40
- entries.find { |e| e =~ LEGACY_MANIFEST_RE } ||
41
- generate_manifest_path
39
+ manifest_entries = entries.select { |e| e =~ MANIFEST_RE }
40
+ if manifest_entries.length > 1
41
+ manifest_entries.sort!
42
+ logger.warn("Found multiple manifests: #{manifest_entries}. Choosing the first alphabetically: #{manifest_entries.first}")
43
+ end
44
+ entry = manifest_entries.first || generate_manifest_path
42
45
  File.join(dirname, entry)
43
46
  end
44
47
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/encoding_utils'
2
3
  require 'sprockets/http_utils'
3
4
  require 'sprockets/utils'
@@ -36,29 +37,18 @@ module Sprockets
36
37
 
37
38
  # Public: Register a new mime type.
38
39
  #
39
- # mime_type - String MIME Type
40
- # options - Hash
41
- # extensions: Array of String extnames
42
- # charset: Proc/Method that detects the charset of a file.
43
- # See EncodingUtils.
40
+ # mime_type - String MIME Type
41
+ # extensions - Array of String extnames
42
+ # charset - Proc/Method that detects the charset of a file.
43
+ # See EncodingUtils.
44
44
  #
45
45
  # Returns nothing.
46
- def register_mime_type(mime_type, options = {})
47
- # Legacy extension argument, will be removed from 4.x
48
- if options.is_a?(String)
49
- options = { extensions: [options] }
50
- end
51
-
52
- extnames = Array(options[:extensions]).map { |extname|
53
- Sprockets::Utils.normalize_extension(extname)
54
- }
46
+ def register_mime_type(mime_type, extensions: [], charset: nil)
47
+ extnames = Array(extensions)
55
48
 
56
- charset = options[:charset]
57
49
  charset ||= :default if mime_type.start_with?('text/')
58
50
  charset = EncodingUtils::CHARSET_DETECT[charset] if charset.is_a?(Symbol)
59
51
 
60
- self.computed_config = {}
61
-
62
52
  self.config = hash_reassoc(config, :mime_exts) do |mime_exts|
63
53
  extnames.each do |extname|
64
54
  mime_exts[extname] = mime_type
@@ -97,54 +87,10 @@ module Sprockets
97
87
  data = File.binread(filename)
98
88
 
99
89
  if detect = mime_type_charset_detecter(content_type)
100
- detect.call(data).encode(Encoding::UTF_8, :universal_newline => true)
90
+ detect.call(data).encode(Encoding::UTF_8, universal_newline: true)
101
91
  else
102
92
  data
103
93
  end
104
94
  end
105
-
106
- private
107
- def extname_map
108
- self.computed_config[:_extnames] ||= compute_extname_map
109
- end
110
-
111
- def compute_extname_map
112
- graph = {}
113
-
114
- engine_extname_permutation = []
115
-
116
- 4.times do |n|
117
- config[:engines].keys.permutation(n).each do |engine_extnames|
118
- engine_extname_permutation << engine_extnames
119
- end
120
- end
121
-
122
- mime_exts_grouped_by_mime_type = {}
123
- config[:mime_exts].each do |format_extname,format_type|
124
- mime_exts_grouped_by_mime_type[format_type] ||= []
125
- mime_exts_grouped_by_mime_type[format_type] << format_extname
126
- end
127
-
128
- ([nil] + pipelines.keys.map(&:to_s)).each do |pipeline|
129
- pipeline_extname = pipeline ? ".#{pipeline}" : ''.freeze
130
- engine_extname_permutation.each do |engine_extnames|
131
- mime_exts_grouped_by_mime_type.each do |format_type, format_extnames|
132
- type = format_type
133
- value = [type, engine_extnames, pipeline]
134
- format_extnames.each do |format_extname|
135
- key = "#{pipeline_extname}#{format_extname}#{engine_extnames.join}"
136
- graph[key] = value
137
- end
138
- if format_type == config[:engine_mime_types][engine_extnames.first]
139
- key = "#{pipeline_extname}#{engine_extnames.join}"
140
- graph[key] = value
141
- end
142
- end
143
- end
144
- graph[pipeline_extname] = [nil, [], pipeline]
145
- end
146
-
147
- graph
148
- end
149
95
  end
150
96
  end