sprockets 3.7.2 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -261
  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 -36
  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 +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 +88 -68
  54. data/lib/sprockets/manifest.rb +67 -64
  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 +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 +52 -39
  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 +12 -9
  81. data/lib/sprockets/utils/gzip.rb +46 -14
  82. data/lib/sprockets/utils.rb +64 -89
  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 +139 -34
  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]
@@ -124,9 +172,9 @@ module Sprockets
124
172
  raise ConversionError, "could not convert #{file_type.inspect} to #{type.inspect}"
125
173
  end
126
174
 
127
- processors = processors_for(type, file_type, engine_extnames, pipeline)
175
+ processors = processors_for(type, file_type, pipeline)
128
176
 
129
- processors_dep_uri = build_processors_uri(type, file_type, engine_extnames, pipeline)
177
+ processors_dep_uri = build_processors_uri(type, file_type, pipeline)
130
178
  dependencies = config[:dependencies] + [processors_dep_uri]
131
179
 
132
180
  # Read into memory and process if theres a processor pipeline
@@ -139,7 +187,9 @@ module Sprockets
139
187
  load_path: load_path,
140
188
  name: name,
141
189
  content_type: type,
142
- metadata: { dependencies: dependencies }
190
+ metadata: {
191
+ dependencies: dependencies
192
+ }
143
193
  })
144
194
  validate_processor_result!(result)
145
195
  source = result.delete(:data)
@@ -147,12 +197,14 @@ module Sprockets
147
197
  metadata[:charset] = source.encoding.name.downcase unless metadata.key?(:charset)
148
198
  metadata[:digest] = digest(source)
149
199
  metadata[:length] = source.bytesize
200
+ metadata[:environment_version] = version
150
201
  else
151
202
  dependencies << build_file_digest_uri(unloaded.filename)
152
203
  metadata = {
153
204
  digest: file_digest(unloaded.filename),
154
205
  length: self.stat(unloaded.filename).size,
155
- dependencies: dependencies
206
+ dependencies: dependencies,
207
+ environment_version: version,
156
208
  }
157
209
  end
158
210
 
@@ -168,20 +220,9 @@ module Sprockets
168
220
  dependencies_digest: DigestUtils.digest(resolve_dependencies(metadata[:dependencies]))
169
221
  }
170
222
 
171
- asset[:id] = pack_hexdigest(digest(asset))
223
+ asset[:id] = hexdigest(asset)
172
224
  asset[:uri] = build_asset_uri(unloaded.filename, unloaded.params.merge(id: asset[:id]))
173
225
 
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
226
  store_asset(asset, unloaded)
186
227
  asset
187
228
  end
@@ -203,38 +244,17 @@ module Sprockets
203
244
  if cached_asset[:metadata]
204
245
  # Deep dup to avoid modifying `asset`
205
246
  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
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://") }
232
254
 
233
- # compress all _dependencies in metadata like `sass_dependencies`
234
255
  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) }
256
+ next unless key.match?(/_dependencies\z/) # rubocop:disable Performance/EndWith
257
+ compress_key_from_hash(cached_asset[:metadata], key)
238
258
  end
239
259
  end
240
260
 
@@ -255,11 +275,11 @@ module Sprockets
255
275
  # "processors:type=text/css&file_type=text/css&pipeline=self",
256
276
  # "file-digest:///Full/path/app/assets/stylesheets"]
257
277
  #
258
- # Returns back array of things that the given uri dpends on
278
+ # Returns back array of things that the given uri depends on
259
279
  # For example the environment version, if you're using a different version of sprockets
260
280
  # then the dependencies should be different, this is used only for generating cache key
261
281
  # for example the "environment-version" may be resolved to "environment-1.0-3.2.0" for
262
- # version "3.2.0" of sprockets.
282
+ # version "3.2.0" of sprockets.
263
283
  #
264
284
  # Any paths that are returned are converted to relative paths
265
285
  #
@@ -271,14 +291,14 @@ module Sprockets
271
291
  # Internal: Retrieves an asset based on its digest
272
292
  #
273
293
  # unloaded - An UnloadedAsset
274
- # limit - A Fixnum which sets the maximum number of versions of "histories"
294
+ # limit - An Integer which sets the maximum number of versions of "histories"
275
295
  # stored in the cache
276
296
  #
277
297
  # This method attempts to retrieve the last `limit` number of histories of an asset
278
298
  # 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"
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"
282
302
  # are used.
283
303
  #
284
304
  # For example a history array may look something like this
@@ -289,7 +309,7 @@ module Sprockets
289
309
  # "file-digest:///Full/path/app/assets/stylesheets"]]
290
310
  #
291
311
  # 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
312
+ # Multiple versions are stored since Sprockets keeps the last `limit` number of assets
293
313
  # generated present in the system.
294
314
  #
295
315
  # 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,34 +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
- ([nil] + pipelines.keys.map(&:to_s)).each do |pipeline|
115
- pipeline_extname = ".#{pipeline}" if pipeline
116
- ([[nil, nil]] + config[:mime_exts].to_a).each do |format_extname, format_type|
117
- 4.times do |n|
118
- config[:engines].keys.permutation(n).each do |engine_extnames|
119
- key = "#{pipeline_extname}#{format_extname}#{engine_extnames.join}"
120
- type = format_type || config[:engine_mime_types][engine_extnames.first]
121
- graph[key] = {type: type, engines: engine_extnames, pipeline: pipeline}
122
- end
123
- end
124
- end
125
- end
126
-
127
- graph
128
- end
129
95
  end
130
96
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+
4
+ module Sprockets
5
+ module Npm
6
+ # Internal: Override resolve_alternates to install package.json behavior.
7
+ #
8
+ # load_path - String environment path
9
+ # logical_path - String path relative to base
10
+ #
11
+ # Returns candidate filenames.
12
+ def resolve_alternates(load_path, logical_path)
13
+ candidates, deps = super
14
+
15
+ dirname = File.join(load_path, logical_path)
16
+
17
+ if directory?(dirname)
18
+ filename = File.join(dirname, 'package.json')
19
+
20
+ if self.file?(filename)
21
+ deps << build_file_digest_uri(filename)
22
+ read_package_directives(dirname, filename) do |path|
23
+ if file?(path)
24
+ candidates << path
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ return candidates, deps
31
+ end
32
+
33
+ # Internal: Read package.json's main and style directives.
34
+ #
35
+ # dirname - String path to component directory.
36
+ # filename - String path to package.json.
37
+ #
38
+ # Returns nothing.
39
+ def read_package_directives(dirname, filename)
40
+ package = JSON.parse(File.read(filename), create_additions: false)
41
+
42
+ case package['main']
43
+ when String
44
+ yield File.expand_path(package['main'], dirname)
45
+ when nil
46
+ yield File.expand_path('index.js', dirname)
47
+ end
48
+
49
+ yield File.expand_path(package['style'], dirname) if package['style']
50
+ end
51
+ end
52
+ end