sprockets 3.7.2 → 4.0.0

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 +42 -270
  3. data/README.md +443 -320
  4. data/bin/sprockets +11 -7
  5. data/lib/rake/sprocketstask.rb +3 -2
  6. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  7. data/lib/sprockets/asset.rb +16 -21
  8. data/lib/sprockets/autoload/babel.rb +8 -0
  9. data/lib/sprockets/autoload/closure.rb +1 -0
  10. data/lib/sprockets/autoload/coffee_script.rb +1 -0
  11. data/lib/sprockets/autoload/eco.rb +1 -0
  12. data/lib/sprockets/autoload/ejs.rb +1 -0
  13. data/lib/sprockets/autoload/jsminc.rb +8 -0
  14. data/lib/sprockets/autoload/sass.rb +1 -0
  15. data/lib/sprockets/autoload/sassc.rb +8 -0
  16. data/lib/sprockets/autoload/uglifier.rb +1 -0
  17. data/lib/sprockets/autoload/yui.rb +1 -0
  18. data/lib/sprockets/autoload/zopfli.rb +7 -0
  19. data/lib/sprockets/autoload.rb +5 -0
  20. data/lib/sprockets/babel_processor.rb +66 -0
  21. data/lib/sprockets/base.rb +47 -10
  22. data/lib/sprockets/bower.rb +5 -2
  23. data/lib/sprockets/bundle.rb +40 -4
  24. data/lib/sprockets/cache/file_store.rb +25 -3
  25. data/lib/sprockets/cache/memory_store.rb +9 -0
  26. data/lib/sprockets/cache/null_store.rb +8 -0
  27. data/lib/sprockets/cache.rb +36 -1
  28. data/lib/sprockets/cached_environment.rb +14 -19
  29. data/lib/sprockets/closure_compressor.rb +1 -0
  30. data/lib/sprockets/coffee_script_processor.rb +18 -4
  31. data/lib/sprockets/compressing.rb +43 -3
  32. data/lib/sprockets/configuration.rb +3 -7
  33. data/lib/sprockets/context.rb +97 -24
  34. data/lib/sprockets/dependencies.rb +1 -0
  35. data/lib/sprockets/digest_utils.rb +25 -5
  36. data/lib/sprockets/directive_processor.rb +45 -35
  37. data/lib/sprockets/eco_processor.rb +1 -0
  38. data/lib/sprockets/ejs_processor.rb +1 -0
  39. data/lib/sprockets/encoding_utils.rb +1 -0
  40. data/lib/sprockets/environment.rb +9 -4
  41. data/lib/sprockets/erb_processor.rb +28 -21
  42. data/lib/sprockets/errors.rb +1 -0
  43. data/lib/sprockets/exporters/base.rb +72 -0
  44. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  45. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  46. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  47. data/lib/sprockets/exporting.rb +73 -0
  48. data/lib/sprockets/file_reader.rb +1 -0
  49. data/lib/sprockets/http_utils.rb +25 -7
  50. data/lib/sprockets/jsminc_compressor.rb +32 -0
  51. data/lib/sprockets/jst_processor.rb +11 -10
  52. data/lib/sprockets/loader.rb +85 -67
  53. data/lib/sprockets/manifest.rb +64 -62
  54. data/lib/sprockets/manifest_utils.rb +9 -6
  55. data/lib/sprockets/mime.rb +8 -42
  56. data/lib/sprockets/npm.rb +52 -0
  57. data/lib/sprockets/path_dependency_utils.rb +3 -11
  58. data/lib/sprockets/path_digest_utils.rb +2 -1
  59. data/lib/sprockets/path_utils.rb +87 -7
  60. data/lib/sprockets/paths.rb +1 -0
  61. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  62. data/lib/sprockets/processing.rb +31 -61
  63. data/lib/sprockets/processor_utils.rb +24 -35
  64. data/lib/sprockets/resolve.rb +177 -93
  65. data/lib/sprockets/sass_cache_store.rb +2 -6
  66. data/lib/sprockets/sass_compressor.rb +13 -1
  67. data/lib/sprockets/sass_functions.rb +1 -0
  68. data/lib/sprockets/sass_importer.rb +1 -0
  69. data/lib/sprockets/sass_processor.rb +30 -9
  70. data/lib/sprockets/sassc_compressor.rb +56 -0
  71. data/lib/sprockets/sassc_processor.rb +297 -0
  72. data/lib/sprockets/server.rb +26 -23
  73. data/lib/sprockets/source_map_processor.rb +66 -0
  74. data/lib/sprockets/source_map_utils.rb +483 -0
  75. data/lib/sprockets/transformers.rb +63 -35
  76. data/lib/sprockets/uglifier_compressor.rb +21 -11
  77. data/lib/sprockets/unloaded_asset.rb +13 -11
  78. data/lib/sprockets/uri_tar.rb +1 -0
  79. data/lib/sprockets/uri_utils.rb +11 -8
  80. data/lib/sprockets/utils/gzip.rb +46 -14
  81. data/lib/sprockets/utils.rb +41 -74
  82. data/lib/sprockets/version.rb +2 -1
  83. data/lib/sprockets/yui_compressor.rb +1 -0
  84. data/lib/sprockets.rb +99 -39
  85. metadata +127 -23
  86. data/LICENSE +0 -21
  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,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'
@@ -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,13 +187,15 @@ 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)
146
196
  metadata = result
147
197
  metadata[:charset] = source.encoding.name.downcase unless metadata.key?(:charset)
148
- metadata[:digest] = digest(source)
198
+ metadata[:digest] = digest(self.version + source)
149
199
  metadata[:length] = source.bytesize
150
200
  else
151
201
  dependencies << build_file_digest_uri(unloaded.filename)
@@ -168,20 +218,9 @@ module Sprockets
168
218
  dependencies_digest: DigestUtils.digest(resolve_dependencies(metadata[:dependencies]))
169
219
  }
170
220
 
171
- asset[:id] = pack_hexdigest(digest(asset))
221
+ asset[:id] = hexdigest(asset)
172
222
  asset[:uri] = build_asset_uri(unloaded.filename, unloaded.params.merge(id: asset[:id]))
173
223
 
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
224
  store_asset(asset, unloaded)
186
225
  asset
187
226
  end
@@ -203,38 +242,17 @@ module Sprockets
203
242
  if cached_asset[:metadata]
204
243
  # Deep dup to avoid modifying `asset`
205
244
  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
245
+ compress_key_from_hash(cached_asset[:metadata], :included)
246
+ compress_key_from_hash(cached_asset[:metadata], :links)
247
+ compress_key_from_hash(cached_asset[:metadata], :stubbed)
248
+ compress_key_from_hash(cached_asset[:metadata], :required)
249
+ compress_key_from_hash(cached_asset[:metadata], :to_load)
250
+ compress_key_from_hash(cached_asset[:metadata], :to_link)
251
+ compress_key_from_hash(cached_asset[:metadata], :dependencies) { |uri| uri.start_with?("file-digest://") }
232
252
 
233
- # compress all _dependencies in metadata like `sass_dependencies`
234
253
  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) }
254
+ next unless key.match?(/_dependencies\z/) # rubocop:disable Performance/EndWith
255
+ compress_key_from_hash(cached_asset[:metadata], key)
238
256
  end
239
257
  end
240
258
 
@@ -255,11 +273,11 @@ module Sprockets
255
273
  # "processors:type=text/css&file_type=text/css&pipeline=self",
256
274
  # "file-digest:///Full/path/app/assets/stylesheets"]
257
275
  #
258
- # Returns back array of things that the given uri dpends on
276
+ # Returns back array of things that the given uri depends on
259
277
  # For example the environment version, if you're using a different version of sprockets
260
278
  # then the dependencies should be different, this is used only for generating cache key
261
279
  # for example the "environment-version" may be resolved to "environment-1.0-3.2.0" for
262
- # version "3.2.0" of sprockets.
280
+ # version "3.2.0" of sprockets.
263
281
  #
264
282
  # Any paths that are returned are converted to relative paths
265
283
  #
@@ -276,9 +294,9 @@ module Sprockets
276
294
  #
277
295
  # This method attempts to retrieve the last `limit` number of histories of an asset
278
296
  # 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"
297
+ # to compile. In this case a dependency can refer to either an asset e.g. index.js
298
+ # may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect
299
+ # compilation, such as the VERSION of Sprockets (i.e. the environment) and what "processors"
282
300
  # are used.
283
301
  #
284
302
  # For example a history array may look something like this
@@ -289,7 +307,7 @@ module Sprockets
289
307
  # "file-digest:///Full/path/app/assets/stylesheets"]]
290
308
  #
291
309
  # 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
310
+ # Multiple versions are stored since Sprockets keeps the last `limit` number of assets
293
311
  # generated present in the system.
294
312
  #
295
313
  # 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
@@ -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
@@ -125,26 +119,15 @@ module Sprockets
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
136
- end
137
- end
138
-
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
123
+ promises = args.flatten.map do |path|
124
+ Concurrent::Promise.execute(executor: executor) do
125
+ environment.find_all_linked_assets(path) do |asset|
126
+ yield asset
145
127
  end
146
128
  end
147
129
  end
130
+ promises.each(&:wait!)
148
131
 
149
132
  nil
150
133
  end
@@ -167,7 +150,7 @@ module Sprockets
167
150
  end
168
151
  end
169
152
 
170
- # Compile and write asset to directory. The asset is written to a
153
+ # Compile asset to directory. The asset is written to a
171
154
  # fingerprinted filename like
172
155
  # `application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js`. An entry is
173
156
  # also inserted into the manifest file.
@@ -179,14 +162,19 @@ module Sprockets
179
162
  raise Error, "manifest requires environment for compilation"
180
163
  end
181
164
 
182
- filenames = []
183
- concurrent_compressors = []
184
- concurrent_writers = []
165
+ filenames = []
166
+ concurrent_exporters = []
185
167
 
168
+ assets_to_export = Concurrent::Array.new
186
169
  find(*args) do |asset|
170
+ assets_to_export << asset
171
+ end
172
+
173
+ assets_to_export.each do |asset|
174
+ mtime = Time.now.iso8601
187
175
  files[asset.digest_path] = {
188
176
  'logical_path' => asset.logical_path,
189
- 'mtime' => asset.mtime.iso8601,
177
+ 'mtime' => mtime,
190
178
  'size' => asset.bytesize,
191
179
  'digest' => asset.hexdigest,
192
180
 
@@ -197,38 +185,23 @@ module Sprockets
197
185
  }
198
186
  assets[asset.logical_path] = asset.digest_path
199
187
 
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
188
  filenames << asset.filename
214
189
 
215
- next if environment.skip_gzip?
216
- gzip = Utils::Gzip.new(asset)
217
- next if gzip.cannot_compress?(environment.mime_types)
190
+ promise = nil
191
+ exporters_for_asset(asset) do |exporter|
192
+ next if exporter.skip?(logger)
218
193
 
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)
194
+ if promise.nil?
195
+ promise = Concurrent::Promise.new(executor: executor) { exporter.call }
196
+ concurrent_exporters << promise.execute
197
+ else
198
+ concurrent_exporters << promise.then { exporter.call }
226
199
  end
227
200
  end
228
-
229
201
  end
230
- concurrent_writers.each(&:wait!)
231
- concurrent_compressors.each(&:wait!)
202
+
203
+ # make sure all exporters have finished before returning the main thread
204
+ concurrent_exporters.each(&:wait!)
232
205
  save
233
206
 
234
207
  filenames
@@ -295,18 +268,13 @@ module Sprockets
295
268
  def clobber
296
269
  FileUtils.rm_r(directory) if File.exist?(directory)
297
270
  logger.info "Removed #{directory}"
271
+ # if we have an environment clear the cache too
272
+ environment.cache.clear if environment
298
273
  nil
299
274
  end
300
275
 
301
276
  # Persist manfiest back to FS
302
277
  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
278
  data = json_encode(@data)
311
279
  FileUtils.mkdir_p File.dirname(@filename)
312
280
  PathUtils.atomic_write(@filename) do |f|
@@ -315,6 +283,36 @@ module Sprockets
315
283
  end
316
284
 
317
285
  private
286
+
287
+ # Given an asset, finds all exporters that
288
+ # match its mime-type.
289
+ #
290
+ # Will yield each expoter to the passed in block.
291
+ #
292
+ # array = []
293
+ # puts asset.content_type # => "application/javascript"
294
+ # exporters_for_asset(asset) do |exporter|
295
+ # array << exporter
296
+ # end
297
+ # # puts array => [Exporters::FileExporter, Exporters::ZlibExporter]
298
+ def exporters_for_asset(asset)
299
+ exporters = [Exporters::FileExporter]
300
+
301
+ environment.exporters.each do |mime_type, exporter_list|
302
+ next unless asset.content_type
303
+ next unless environment.match_mime_type? asset.content_type, mime_type
304
+ exporter_list.each do |exporter|
305
+ exporters << exporter
306
+ end
307
+ end
308
+
309
+ exporters.uniq!
310
+
311
+ exporters.each do |exporter|
312
+ yield exporter.new(asset: asset, environment: environment, directory: dir)
313
+ end
314
+ end
315
+
318
316
  def json_decode(obj)
319
317
  JSON.parse(obj, create_additions: false)
320
318
  end
@@ -332,5 +330,9 @@ module Sprockets
332
330
  logger
333
331
  end
334
332
  end
333
+
334
+ def executor
335
+ @executor ||= environment.export_concurrent ? :fast : :immediate
336
+ end
335
337
  end
336
338
  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 candiate 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