sprockets 3.7.2 → 4.0.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 +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