sprockets 4.0.0 → 4.2.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -2
  3. data/MIT-LICENSE +21 -0
  4. data/README.md +103 -19
  5. data/lib/rake/sprocketstask.rb +6 -2
  6. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +1 -1
  7. data/lib/sprockets/asset.rb +23 -6
  8. data/lib/sprockets/base.rb +2 -2
  9. data/lib/sprockets/bower.rb +1 -1
  10. data/lib/sprockets/bundle.rb +1 -1
  11. data/lib/sprockets/cache/memory_store.rb +20 -11
  12. data/lib/sprockets/cache.rb +1 -1
  13. data/lib/sprockets/cached_environment.rb +11 -11
  14. data/lib/sprockets/coffee_script_processor.rb +2 -2
  15. data/lib/sprockets/configuration.rb +2 -2
  16. data/lib/sprockets/context.rb +2 -1
  17. data/lib/sprockets/dependencies.rb +1 -1
  18. data/lib/sprockets/digest_utils.rb +10 -13
  19. data/lib/sprockets/directive_processor.rb +19 -1
  20. data/lib/sprockets/eco_processor.rb +1 -1
  21. data/lib/sprockets/ejs_processor.rb +1 -1
  22. data/lib/sprockets/erb_processor.rb +8 -2
  23. data/lib/sprockets/exporters/base.rb +2 -3
  24. data/lib/sprockets/exporting.rb +2 -2
  25. data/lib/sprockets/loader.rb +5 -3
  26. data/lib/sprockets/manifest.rb +8 -7
  27. data/lib/sprockets/npm.rb +1 -1
  28. data/lib/sprockets/path_utils.rb +1 -1
  29. data/lib/sprockets/processing.rb +1 -1
  30. data/lib/sprockets/processor_utils.rb +4 -3
  31. data/lib/sprockets/sass_processor.rb +1 -1
  32. data/lib/sprockets/sassc_processor.rb +1 -1
  33. data/lib/sprockets/server.rb +30 -20
  34. data/lib/sprockets/source_map_processor.rb +1 -1
  35. data/lib/sprockets/source_map_utils.rb +4 -4
  36. data/lib/sprockets/uri_utils.rb +1 -1
  37. data/lib/sprockets/utils.rb +24 -16
  38. data/lib/sprockets/version.rb +1 -1
  39. data/lib/sprockets.rb +4 -1
  40. metadata +15 -14
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require 'digest/md5'
3
2
  require 'digest/sha1'
4
3
  require 'digest/sha2'
5
4
  require 'set'
@@ -19,7 +18,6 @@ module Sprockets
19
18
 
20
19
  # Internal: Maps digest bytesize to the digest class.
21
20
  DIGEST_SIZES = {
22
- 16 => Digest::MD5,
23
21
  20 => Digest::SHA1,
24
22
  32 => Digest::SHA256,
25
23
  48 => Digest::SHA384,
@@ -68,18 +66,8 @@ module Sprockets
68
66
  Encoding => ->(val, digest) {
69
67
  digest << 'Encoding'.freeze
70
68
  digest << val.name
71
- },
72
- }
73
- if 0.class != Integer # Ruby < 2.4
74
- ADD_VALUE_TO_DIGEST[Fixnum] = ->(val, digest) {
75
- digest << 'Integer'.freeze
76
- digest << val.to_s
77
- }
78
- ADD_VALUE_TO_DIGEST[Bignum] = ->(val, digest) {
79
- digest << 'Integer'.freeze
80
- digest << val.to_s
81
69
  }
82
- end
70
+ }
83
71
 
84
72
  ADD_VALUE_TO_DIGEST.compare_by_identity.rehash
85
73
 
@@ -189,6 +177,15 @@ module Sprockets
189
177
  integrity_uri(unpack_hexdigest(hexdigest))
190
178
  end
191
179
 
180
+ # Internal: Checks an asset name for a valid digest
181
+ #
182
+ # name - The name of the asset
183
+ #
184
+ # Returns true if the name contains a digest like string and .digested before the extension
185
+ def already_digested?(name)
186
+ return name =~ /-([0-9a-zA-Z]{7,128})\.digested/
187
+ end
188
+
192
189
  private
193
190
  def build_digest(obj)
194
191
  digest = digest_class.new
@@ -285,6 +285,24 @@ module Sprockets
285
285
  to_load(resolve(path))
286
286
  end
287
287
 
288
+ # Allows you to state a dependency on a relative directory
289
+ # without including it.
290
+ #
291
+ # This is used for caching purposes. Any changes made to
292
+ # the dependency directory will invalidate the cache of the
293
+ # source file.
294
+ #
295
+ # This is useful if you are using ERB and File.read to pull
296
+ # in contents from multiple files in a directory.
297
+ #
298
+ # //= depend_on_directory ./data
299
+ #
300
+ def process_depend_on_directory_directive(path = ".", accept = nil)
301
+ path = expand_relative_dirname(:depend_on_directory, path)
302
+ accept = expand_accept_shorthand(accept)
303
+ resolve_paths(*@environment.stat_directory_with_dependencies(path), accept: accept)
304
+ end
305
+
288
306
  # Allows dependency to be excluded from the asset bundle.
289
307
  #
290
308
  # The `path` must be a valid asset and may or may not already
@@ -374,7 +392,7 @@ module Sprockets
374
392
  next if subpath == @filename || stat.directory?
375
393
  uri, deps = @environment.resolve(subpath, **kargs)
376
394
  @dependencies.merge(deps)
377
- yield uri if uri
395
+ yield uri if uri && block_given?
378
396
  end
379
397
  end
380
398
 
@@ -4,7 +4,7 @@ require 'sprockets/autoload'
4
4
  module Sprockets
5
5
  # Processor engine class for the Eco compiler. Depends on the `eco` gem.
6
6
  #
7
- # For more infomation see:
7
+ # For more information see:
8
8
  #
9
9
  # https://github.com/sstephenson/ruby-eco
10
10
  # https://github.com/sstephenson/eco
@@ -4,7 +4,7 @@ require 'sprockets/autoload'
4
4
  module Sprockets
5
5
  # Processor engine class for the EJS compiler. Depends on the `ejs` gem.
6
6
  #
7
- # For more infomation see:
7
+ # For more information see:
8
8
  #
9
9
  # https://github.com/sstephenson/ruby-ejs
10
10
  #
@@ -18,8 +18,7 @@ class Sprockets::ERBProcessor
18
18
  end
19
19
 
20
20
  def call(input)
21
- match = ERB.version.match(/\Aerb\.rb \[(?<version>[^ ]+) /)
22
- if match && match[:version] >= "2.2.0" # Ruby 2.6+
21
+ if keyword_constructor? # Ruby 2.6+
23
22
  engine = ::ERB.new(input[:data], trim_mode: '<>')
24
23
  else
25
24
  engine = ::ERB.new(input[:data], nil, '<>')
@@ -34,4 +33,11 @@ class Sprockets::ERBProcessor
34
33
  data = engine.result(context.instance_eval('binding'))
35
34
  context.metadata.merge(data: data)
36
35
  end
36
+
37
+ private
38
+
39
+ def keyword_constructor?
40
+ return @keyword_constructor if defined? @keyword_constructor
41
+ @keyword_constructor = ::ERB.instance_method(:initialize).parameters.include?([:key, :trim_mode])
42
+ end
37
43
  end
@@ -1,6 +1,6 @@
1
1
  module Sprockets
2
2
  module Exporters
3
- # Convienence class for all exporters to inherit from
3
+ # Convenience class for all exporters to inherit from
4
4
  #
5
5
  # An exporter is responsible for exporting a Sprockets::Asset
6
6
  # to a file system. For example the Exporters::File class
@@ -29,7 +29,7 @@ module Sprockets
29
29
  setup
30
30
  end
31
31
 
32
- # Public: Callback that is executed after intialization
32
+ # Public: Callback that is executed after initialization
33
33
  #
34
34
  # Any setup that needs to be done can be performed in the +setup+
35
35
  # method. It will be called immediately after initialization.
@@ -69,4 +69,3 @@ module Sprockets
69
69
  end
70
70
  end
71
71
  end
72
-
@@ -28,11 +28,11 @@ module Sprockets
28
28
 
29
29
  # Public: Remove Exporting processor `klass` for `mime_type`.
30
30
  #
31
- # environment.unregister_exporter '*/*', Sprockets::Exporters::Zlib
31
+ # environment.unregister_exporter '*/*', Sprockets::Exporters::ZlibExporter
32
32
  #
33
33
  # Can be called without a mime type
34
34
  #
35
- # environment.unregister_exporter Sprockets::Exporters::Zlib
35
+ # environment.unregister_exporter Sprockets::Exporters::ZlibExporter
36
36
  #
37
37
  # Does not remove any exporters that depend on `klass`.
38
38
  def unregister_exporter(mime_types, exporter = nil)
@@ -195,14 +195,16 @@ module Sprockets
195
195
  source = result.delete(:data)
196
196
  metadata = result
197
197
  metadata[:charset] = source.encoding.name.downcase unless metadata.key?(:charset)
198
- metadata[:digest] = digest(self.version + source)
198
+ metadata[:digest] = digest(source)
199
199
  metadata[:length] = source.bytesize
200
+ metadata[:environment_version] = version
200
201
  else
201
202
  dependencies << build_file_digest_uri(unloaded.filename)
202
203
  metadata = {
203
204
  digest: file_digest(unloaded.filename),
204
205
  length: self.stat(unloaded.filename).size,
205
- dependencies: dependencies
206
+ dependencies: dependencies,
207
+ environment_version: version,
206
208
  }
207
209
  end
208
210
 
@@ -289,7 +291,7 @@ module Sprockets
289
291
  # Internal: Retrieves an asset based on its digest
290
292
  #
291
293
  # unloaded - An UnloadedAsset
292
- # 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"
293
295
  # stored in the cache
294
296
  #
295
297
  # This method attempts to retrieve the last `limit` number of histories of an asset
@@ -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
 
@@ -112,7 +112,7 @@ module Sprockets
112
112
  # Public: Find all assets matching pattern set in environment.
113
113
  #
114
114
  # Returns Enumerator of Assets.
115
- def find(*args)
115
+ def find(*args, &block)
116
116
  unless environment
117
117
  raise Error, "manifest requires environment for compilation"
118
118
  end
@@ -122,12 +122,13 @@ module Sprockets
122
122
  environment = self.environment.cached
123
123
  promises = args.flatten.map do |path|
124
124
  Concurrent::Promise.execute(executor: executor) do
125
- environment.find_all_linked_assets(path) do |asset|
126
- yield asset
127
- end
125
+ environment.find_all_linked_assets(path).to_a
128
126
  end
129
127
  end
130
- promises.each(&:wait!)
128
+
129
+ promises.each do |promise|
130
+ promise.value!.each(&block)
131
+ end
131
132
 
132
133
  nil
133
134
  end
@@ -273,7 +274,7 @@ module Sprockets
273
274
  nil
274
275
  end
275
276
 
276
- # Persist manfiest back to FS
277
+ # Persist manifest back to FS
277
278
  def save
278
279
  data = json_encode(@data)
279
280
  FileUtils.mkdir_p File.dirname(@filename)
data/lib/sprockets/npm.rb CHANGED
@@ -8,7 +8,7 @@ module Sprockets
8
8
  # load_path - String environment path
9
9
  # logical_path - String path relative to base
10
10
  #
11
- # Returns candiate filenames.
11
+ # Returns candidate filenames.
12
12
  def resolve_alternates(load_path, logical_path)
13
13
  candidates, deps = super
14
14
 
@@ -162,7 +162,7 @@ module Sprockets
162
162
  def split_subpath(path, subpath)
163
163
  return "" if path == subpath
164
164
  path = File.join(path, ''.freeze)
165
- if subpath.start_with?(path)
165
+ if subpath&.start_with?(path)
166
166
  subpath[path.length..-1]
167
167
  else
168
168
  nil
@@ -130,7 +130,7 @@ module Sprockets
130
130
  #
131
131
  # mime_type - String MIME Type. Use '*/*' applies to all types.
132
132
  # key - Symbol metadata key
133
- # initial - Initial memo to pass to the reduce funciton (default: nil)
133
+ # initial - Initial memo to pass to the reduce function (default: nil)
134
134
  # block - Proc accepting the memo accumulator and current value
135
135
  #
136
136
  # Returns nothing.
@@ -6,7 +6,7 @@ module Sprockets
6
6
  #
7
7
  # A Processor is a general function that may modify or transform an asset as
8
8
  # part of the pipeline. CoffeeScript to JavaScript conversion, Minification
9
- # or Concatenation are all implemented as seperate Processor steps.
9
+ # or Concatenation are all implemented as separate Processor steps.
10
10
  #
11
11
  # Processors maybe any object that responds to call. So procs or a class that
12
12
  # defines a self.call method.
@@ -118,8 +118,9 @@ module Sprockets
118
118
  Symbol,
119
119
  TrueClass,
120
120
  FalseClass,
121
- NilClass
122
- ] + (0.class == Integer ? [Integer] : [Bignum, Fixnum])).freeze
121
+ NilClass,
122
+ Integer
123
+ ]).freeze
123
124
 
124
125
  # Internal: Set of all nested compound metadata types that can nest values.
125
126
  VALID_METADATA_COMPOUND_TYPES = Set.new([
@@ -7,7 +7,7 @@ require 'uri'
7
7
  module Sprockets
8
8
  # Processor engine class for the SASS/SCSS compiler. Depends on the `sass` gem.
9
9
  #
10
- # For more infomation see:
10
+ # For more information see:
11
11
  #
12
12
  # https://github.com/sass/sass
13
13
  # https://github.com/rails/sass-rails
@@ -7,7 +7,7 @@ require 'uri'
7
7
  module Sprockets
8
8
  # Processor engine class for the SASS/SCSS compiler. Depends on the `sassc` gem.
9
9
  #
10
- # For more infomation see:
10
+ # For more information see:
11
11
  #
12
12
  # https://github.com/sass/sassc-ruby
13
13
  # https://github.com/sass/sassc-rails
@@ -35,7 +35,8 @@ module Sprockets
35
35
  msg = "Served asset #{env['PATH_INFO']} -"
36
36
 
37
37
  # Extract the path from everything after the leading slash
38
- path = Rack::Utils.unescape(env['PATH_INFO'].to_s.sub(/^\//, ''))
38
+ full_path = Rack::Utils.unescape(env['PATH_INFO'].to_s.sub(/^\//, ''))
39
+ path = full_path
39
40
 
40
41
  unless path.valid_encoding?
41
42
  return bad_request_response(env)
@@ -64,6 +65,15 @@ module Sprockets
64
65
  # Look up the asset.
65
66
  asset = find_asset(path)
66
67
 
68
+ # Fallback to looking up the asset with the full path.
69
+ # This will make assets that are hashed with webpack or
70
+ # other js bundlers work consistently between production
71
+ # and development pipelines.
72
+ if asset.nil? && (asset = find_asset(full_path))
73
+ if_match = asset.etag if fingerprint
74
+ fingerprint = asset.etag
75
+ end
76
+
67
77
  if asset.nil?
68
78
  status = :not_found
69
79
  elsif fingerprint && asset.etag != fingerprint
@@ -138,39 +148,39 @@ module Sprockets
138
148
  # Returns a 400 Forbidden response tuple
139
149
  def bad_request_response(env)
140
150
  if head_request?(env)
141
- [ 400, { "Content-Type" => "text/plain", "Content-Length" => "0" }, [] ]
151
+ [ 400, { "content-type" => "text/plain", "content-length" => "0" }, [] ]
142
152
  else
143
- [ 400, { "Content-Type" => "text/plain", "Content-Length" => "11" }, [ "Bad Request" ] ]
153
+ [ 400, { "content-type" => "text/plain", "content-length" => "11" }, [ "Bad Request" ] ]
144
154
  end
145
155
  end
146
156
 
147
157
  # Returns a 403 Forbidden response tuple
148
158
  def forbidden_response(env)
149
159
  if head_request?(env)
150
- [ 403, { "Content-Type" => "text/plain", "Content-Length" => "0" }, [] ]
160
+ [ 403, { "content-type" => "text/plain", "content-length" => "0" }, [] ]
151
161
  else
152
- [ 403, { "Content-Type" => "text/plain", "Content-Length" => "9" }, [ "Forbidden" ] ]
162
+ [ 403, { "content-type" => "text/plain", "content-length" => "9" }, [ "Forbidden" ] ]
153
163
  end
154
164
  end
155
165
 
156
166
  # Returns a 404 Not Found response tuple
157
167
  def not_found_response(env)
158
168
  if head_request?(env)
159
- [ 404, { "Content-Type" => "text/plain", "Content-Length" => "0", "X-Cascade" => "pass" }, [] ]
169
+ [ 404, { "content-type" => "text/plain", "content-length" => "0", "x-cascade" => "pass" }, [] ]
160
170
  else
161
- [ 404, { "Content-Type" => "text/plain", "Content-Length" => "9", "X-Cascade" => "pass" }, [ "Not found" ] ]
171
+ [ 404, { "content-type" => "text/plain", "content-length" => "9", "x-cascade" => "pass" }, [ "Not found" ] ]
162
172
  end
163
173
  end
164
174
 
165
175
  def method_not_allowed_response
166
- [ 405, { "Content-Type" => "text/plain", "Content-Length" => "18" }, [ "Method Not Allowed" ] ]
176
+ [ 405, { "content-type" => "text/plain", "content-length" => "18" }, [ "Method Not Allowed" ] ]
167
177
  end
168
178
 
169
179
  def precondition_failed_response(env)
170
180
  if head_request?(env)
171
- [ 412, { "Content-Type" => "text/plain", "Content-Length" => "0", "X-Cascade" => "pass" }, [] ]
181
+ [ 412, { "content-type" => "text/plain", "content-length" => "0", "x-cascade" => "pass" }, [] ]
172
182
  else
173
- [ 412, { "Content-Type" => "text/plain", "Content-Length" => "19", "X-Cascade" => "pass" }, [ "Precondition Failed" ] ]
183
+ [ 412, { "content-type" => "text/plain", "content-length" => "19", "x-cascade" => "pass" }, [ "Precondition Failed" ] ]
174
184
  end
175
185
  end
176
186
 
@@ -179,7 +189,7 @@ module Sprockets
179
189
  def javascript_exception_response(exception)
180
190
  err = "#{exception.class.name}: #{exception.message}\n (in #{exception.backtrace[0]})"
181
191
  body = "throw Error(#{err.inspect})"
182
- [ 200, { "Content-Type" => "application/javascript", "Content-Length" => body.bytesize.to_s }, [ body ] ]
192
+ [ 200, { "content-type" => "application/javascript", "content-length" => body.bytesize.to_s }, [ body ] ]
183
193
  end
184
194
 
185
195
  # Returns a CSS response that hides all elements on the page and
@@ -232,7 +242,7 @@ module Sprockets
232
242
  }
233
243
  CSS
234
244
 
235
- [ 200, { "Content-Type" => "text/css; charset=utf-8", "Content-Length" => body.bytesize.to_s }, [ body ] ]
245
+ [ 200, { "content-type" => "text/css; charset=utf-8", "content-length" => body.bytesize.to_s }, [ body ] ]
236
246
  end
237
247
 
238
248
  # Escape special characters for use inside a CSS content("...") string
@@ -248,18 +258,18 @@ module Sprockets
248
258
  headers = {}
249
259
 
250
260
  # Set caching headers
251
- headers["Cache-Control"] = +"public"
252
- headers["ETag"] = %("#{etag}")
261
+ headers["cache-control"] = +"public"
262
+ headers["etag"] = %("#{etag}")
253
263
 
254
264
  # If the request url contains a fingerprint, set a long
255
265
  # expires on the response
256
266
  if path_fingerprint(env["PATH_INFO"])
257
- headers["Cache-Control"] << ", max-age=31536000, immutable"
267
+ headers["cache-control"] << ", max-age=31536000, immutable"
258
268
 
259
269
  # Otherwise set `must-revalidate` since the asset could be modified.
260
270
  else
261
- headers["Cache-Control"] << ", must-revalidate"
262
- headers["Vary"] = "Accept-Encoding"
271
+ headers["cache-control"] << ", must-revalidate"
272
+ headers["vary"] = "Accept-Encoding"
263
273
  end
264
274
 
265
275
  headers
@@ -269,7 +279,7 @@ module Sprockets
269
279
  headers = {}
270
280
 
271
281
  # Set content length header
272
- headers["Content-Length"] = length.to_s
282
+ headers["content-length"] = length.to_s
273
283
 
274
284
  # Set content type header
275
285
  if type = asset.content_type
@@ -277,7 +287,7 @@ module Sprockets
277
287
  if type.start_with?("text/") && asset.charset
278
288
  type += "; charset=#{asset.charset}"
279
289
  end
280
- headers["Content-Type"] = type
290
+ headers["content-type"] = type
281
291
  end
282
292
 
283
293
  headers.merge(cache_headers(env, asset.etag))
@@ -289,7 +299,7 @@ module Sprockets
289
299
  # # => "0aa2105d29558f3eb790d411d7d8fb66"
290
300
  #
291
301
  def path_fingerprint(path)
292
- path[/-([0-9a-f]{7,128})\.[^.]+\z/, 1]
302
+ path[/-([0-9a-zA-Z]{7,128})\.[^.]+\z/, 1]
293
303
  end
294
304
  end
295
305
  end
@@ -9,7 +9,7 @@ module Sprockets
9
9
  # When a file is passed in it will have a `application/js-sourcemap+json`
10
10
  # or `application/css-sourcemap+json` mime type. The filename will be
11
11
  # match the original asset. The original asset is loaded. As it
12
- # gets processed by Sprockets it will aquire all information
12
+ # gets processed by Sprockets it will acquire all information
13
13
  # needed to build a source map file in the `asset.to_hash[:metadata][:map]`
14
14
  # key.
15
15
  #
@@ -78,7 +78,7 @@ module Sprockets
78
78
  offset = 0
79
79
  if a["sections"].count != 0 && !a["sections"].last["map"]["mappings"].empty?
80
80
  last_line_count = a["sections"].last["map"].delete("x_sprockets_linecount")
81
- offset += last_line_count
81
+ offset += last_line_count || 1
82
82
 
83
83
  last_offset = a["sections"].last["offset"]["line"]
84
84
  offset += last_offset
@@ -140,7 +140,7 @@ module Sprockets
140
140
  }
141
141
  end
142
142
 
143
- # Public: Combine two seperate source map transformations into a single
143
+ # Public: Combine two separate source map transformations into a single
144
144
  # mapping.
145
145
  #
146
146
  # Source transformations may happen in discrete steps producing separate
@@ -436,7 +436,7 @@ module Sprockets
436
436
  digit = BASE64_VALUES[str[i]]
437
437
  raise ArgumentError unless digit
438
438
  continuation = (digit & VLQ_CONTINUATION_BIT) != 0
439
- digit &= VLQ_CONTINUATION_BIT - 1
439
+ digit &= VLQ_BASE_MASK
440
440
  value += digit << shift
441
441
  if continuation
442
442
  shift += VLQ_BASE_SHIFT
@@ -453,7 +453,7 @@ module Sprockets
453
453
  #
454
454
  # ary - Two dimensional Array of Integers.
455
455
  #
456
- # Returns a VLQ encoded String seperated by , and ;.
456
+ # Returns a VLQ encoded String separated by , and ;.
457
457
  def vlq_encode_mappings(ary)
458
458
  ary.map { |group|
459
459
  group.map { |segment|
@@ -53,7 +53,7 @@ module Sprockets
53
53
  path = path[1..-1]
54
54
  end
55
55
 
56
- [scheme, host, path, query]
56
+ [scheme, host || '', path, query]
57
57
  end
58
58
 
59
59
  # Internal: Join file: URI component parts into String.
@@ -25,7 +25,7 @@ module Sprockets
25
25
 
26
26
  # Internal: Duplicate and store key/value on new frozen hash.
27
27
  #
28
- # Seperated for recursive calls, always use hash_reassoc(hash, *keys).
28
+ # Separated for recursive calls, always use hash_reassoc(hash, *keys).
29
29
  #
30
30
  # hash - Hash
31
31
  # key - Object key
@@ -118,30 +118,38 @@ module Sprockets
118
118
  buf
119
119
  end
120
120
 
121
+ MODULE_INCLUDE_MUTEX = Mutex.new
122
+ private_constant :MODULE_INCLUDE_MUTEX
123
+
121
124
  # Internal: Inject into target module for the duration of the block.
122
125
  #
123
126
  # mod - Module
124
127
  #
125
128
  # Returns result of block.
126
129
  def module_include(base, mod)
127
- old_methods = {}
130
+ MODULE_INCLUDE_MUTEX.synchronize do
131
+ old_methods = {}
128
132
 
129
- mod.instance_methods.each do |sym|
130
- old_methods[sym] = base.instance_method(sym) if base.method_defined?(sym)
131
- end
133
+ mod.instance_methods.each do |sym|
134
+ old_methods[sym] = base.instance_method(sym) if base.method_defined?(sym)
135
+ end
132
136
 
133
- mod.instance_methods.each do |sym|
134
- method = mod.instance_method(sym)
135
- base.send(:define_method, sym, method)
136
- end
137
+ mod.instance_methods.each do |sym|
138
+ method = mod.instance_method(sym)
139
+ if base.method_defined?(sym)
140
+ base.send(:alias_method, sym, sym)
141
+ end
142
+ base.send(:define_method, sym, method)
143
+ end
137
144
 
138
- yield
139
- ensure
140
- mod.instance_methods.each do |sym|
141
- base.send(:undef_method, sym) if base.method_defined?(sym)
142
- end
143
- old_methods.each do |sym, method|
144
- base.send(:define_method, sym, method)
145
+ yield
146
+ ensure
147
+ mod.instance_methods.each do |sym|
148
+ base.send(:undef_method, sym) if base.method_defined?(sym)
149
+ end
150
+ old_methods.each do |sym, method|
151
+ base.send(:define_method, sym, method)
152
+ end
145
153
  end
146
154
  end
147
155
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Sprockets
3
- VERSION = "4.0.0"
3
+ VERSION = "4.2.0"
4
4
  end
data/lib/sprockets.rb CHANGED
@@ -51,6 +51,7 @@ module Sprockets
51
51
  register_mime_type 'application/json', extensions: ['.json'], charset: :unicode
52
52
  register_mime_type 'application/ruby', extensions: ['.rb']
53
53
  register_mime_type 'application/xml', extensions: ['.xml']
54
+ register_mime_type 'application/manifest+json', extensions: ['.webmanifest']
54
55
  register_mime_type 'text/css', extensions: ['.css'], charset: :css
55
56
  register_mime_type 'text/html', extensions: ['.html', '.htm'], charset: :html
56
57
  register_mime_type 'text/plain', extensions: ['.txt', '.text']
@@ -89,7 +90,7 @@ module Sprockets
89
90
  register_mime_type 'application/font-woff2', extensions: ['.woff2']
90
91
 
91
92
  require 'sprockets/source_map_processor'
92
- register_mime_type 'application/js-sourcemap+json', extensions: ['.js.map']
93
+ register_mime_type 'application/js-sourcemap+json', extensions: ['.js.map'], charset: :unicode
93
94
  register_mime_type 'application/css-sourcemap+json', extensions: ['.css.map']
94
95
  register_transformer 'application/javascript', 'application/js-sourcemap+json', SourceMapProcessor
95
96
  register_transformer 'text/css', 'application/css-sourcemap+json', SourceMapProcessor
@@ -180,6 +181,7 @@ module Sprockets
180
181
  application/ecmascript-6
181
182
  application/javascript
182
183
  application/json
184
+ application/manifest+json
183
185
  application/xml
184
186
  text/coffeescript
185
187
  text/css
@@ -189,6 +191,7 @@ module Sprockets
189
191
  text/scss
190
192
  text/yaml
191
193
  text/eco
194
+ text/ejs
192
195
  ), 'application/\2+ruby', '.erb', ERBProcessor)
193
196
 
194
197
  register_mime_type 'application/html+ruby', extensions: ['.html.erb', '.erb', '.rhtml'], charset: :html