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,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'set'
2
3
  require 'shellwords'
3
4
 
@@ -34,8 +35,6 @@ module Sprockets
34
35
  # env.register_processor('text/css', MyProcessor)
35
36
  #
36
37
  class DirectiveProcessor
37
- VERSION = '1'
38
-
39
38
  # Directives are denoted by a `=` followed by the name, then
40
39
  # argument list.
41
40
  #
@@ -50,18 +49,16 @@ module Sprockets
50
49
  /x
51
50
 
52
51
  def self.instance
53
- @instance ||= new(
54
- # Deprecated: Default to C and Ruby comment styles
55
- comments: ["//", ["/*", "*/"]] + ["#", ["###", "###"]]
56
- )
52
+ # Default to C comment styles
53
+ @instance ||= new(comments: ["//", ["/*", "*/"]])
57
54
  end
58
55
 
59
56
  def self.call(input)
60
57
  instance.call(input)
61
58
  end
62
59
 
63
- def initialize(options = {})
64
- @header_pattern = compile_header_pattern(Array(options[:comments]))
60
+ def initialize(comments: [])
61
+ @header_pattern = compile_header_pattern(Array(comments))
65
62
  end
66
63
 
67
64
  def call(input)
@@ -73,20 +70,28 @@ module Sprockets
73
70
  @uri = input[:uri]
74
71
  @filename = input[:filename]
75
72
  @dirname = File.dirname(@filename)
76
- @content_type = input[:content_type]
73
+ # If loading a source map file like `application.js.map` resolve
74
+ # dependencies using `.js` instead of `.js.map`
75
+ @content_type = SourceMapProcessor.original_content_type(input[:content_type], error_when_not_found: false)
77
76
  @required = Set.new(input[:metadata][:required])
78
77
  @stubbed = Set.new(input[:metadata][:stubbed])
79
78
  @links = Set.new(input[:metadata][:links])
80
79
  @dependencies = Set.new(input[:metadata][:dependencies])
80
+ @to_link = Set.new
81
+ @to_load = Set.new
81
82
 
82
83
  data, directives = process_source(input[:data])
83
84
  process_directives(directives)
84
85
 
85
- { data: data,
86
- required: @required,
87
- stubbed: @stubbed,
88
- links: @links,
89
- dependencies: @dependencies }
86
+ {
87
+ data: data,
88
+ required: @required,
89
+ stubbed: @stubbed,
90
+ links: @links,
91
+ to_load: @to_load,
92
+ to_link: @to_link,
93
+ dependencies: @dependencies
94
+ }
90
95
  end
91
96
 
92
97
  protected
@@ -116,9 +121,9 @@ module Sprockets
116
121
 
117
122
  header, directives = extract_directives(header)
118
123
 
119
- data = ""
124
+ data = +""
120
125
  data.force_encoding(body.encoding)
121
- data << header << "\n" unless header.empty?
126
+ data << header unless header.empty?
122
127
  data << body
123
128
  # Ensure body ends in a new line
124
129
  data << "\n" if data.length > 0 && data[-1] != "\n"
@@ -134,7 +139,7 @@ module Sprockets
134
139
  # [[1, "require", "foo"], [2, "require", "bar"]]
135
140
  #
136
141
  def extract_directives(header)
137
- processed_header = ""
142
+ processed_header = +""
138
143
  directives = []
139
144
 
140
145
  header.lines.each_with_index do |line, index|
@@ -149,7 +154,11 @@ module Sprockets
149
154
  processed_header << line
150
155
  end
151
156
 
152
- return processed_header.chomp, directives
157
+ processed_header.chomp!
158
+ # Ensure header ends in a new line like before it was processed
159
+ processed_header << "\n" if processed_header.length > 0 && header[-1] == "\n"
160
+
161
+ return processed_header, directives
153
162
  end
154
163
 
155
164
  # Gathers comment directives in the source and processes them.
@@ -162,7 +171,7 @@ module Sprockets
162
171
  # `process_require_glob_directive`.
163
172
  #
164
173
  # class DirectiveProcessor < Sprockets::DirectiveProcessor
165
- # def process_require_glob_directive
174
+ # def process_require_glob_directive(glob)
166
175
  # Dir["#{dirname}/#{glob}"].sort.each do |filename|
167
176
  # require(filename)
168
177
  # end
@@ -187,7 +196,7 @@ module Sprockets
187
196
 
188
197
  # The `require` directive functions similar to Ruby's own `require`.
189
198
  # It provides a way to declare a dependency on a file in your path
190
- # and ensures its only loaded once before the source file.
199
+ # and ensures it's only loaded once before the source file.
191
200
  #
192
201
  # `require` works with files in the environment path:
193
202
  #
@@ -265,15 +274,15 @@ module Sprockets
265
274
  # it.
266
275
  #
267
276
  # This is used for caching purposes. Any changes that would
268
- # invalid the asset dependency will invalidate the cache our the
269
- # source file.
277
+ # invalidate the asset dependency will invalidate the cache of
278
+ # the source file.
270
279
  #
271
280
  # Unlike `depend_on`, the path must be a requirable asset.
272
281
  #
273
282
  # //= depend_on_asset "bar.js"
274
283
  #
275
284
  def process_depend_on_asset_directive(path)
276
- load(resolve(path))
285
+ to_load(resolve(path))
277
286
  end
278
287
 
279
288
  # Allows dependency to be excluded from the asset bundle.
@@ -297,7 +306,8 @@ module Sprockets
297
306
  # /*= link "logo.png" */
298
307
  #
299
308
  def process_link_directive(path)
300
- @links << load(resolve(path)).uri
309
+ uri = to_load(resolve(path))
310
+ @to_link << uri
301
311
  end
302
312
 
303
313
  # `link_directory` links all the files inside a single
@@ -307,7 +317,7 @@ module Sprockets
307
317
  # //= link_directory "./fonts"
308
318
  #
309
319
  # Use caution when linking against JS or CSS assets. Include an explicit
310
- # extension or content type in these cases
320
+ # extension or content type in these cases.
311
321
  #
312
322
  # //= link_directory "./scripts" .js
313
323
  #
@@ -323,7 +333,7 @@ module Sprockets
323
333
  # //= link_tree "./images"
324
334
  #
325
335
  # Use caution when linking against JS or CSS assets. Include an explicit
326
- # extension or content type in these cases
336
+ # extension or content type in these cases.
327
337
  #
328
338
  # //= link_tree "./styles" .css
329
339
  #
@@ -354,15 +364,15 @@ module Sprockets
354
364
 
355
365
  def link_paths(paths, deps, accept)
356
366
  resolve_paths(paths, deps, accept: accept) do |uri|
357
- @links << load(uri).uri
367
+ @to_link << to_load(uri)
358
368
  end
359
369
  end
360
370
 
361
- def resolve_paths(paths, deps, options = {})
371
+ def resolve_paths(paths, deps, **kargs)
362
372
  @dependencies.merge(deps)
363
373
  paths.each do |subpath, stat|
364
374
  next if subpath == @filename || stat.directory?
365
- uri, deps = @environment.resolve(subpath, options.merge(compat: false))
375
+ uri, deps = @environment.resolve(subpath, **kargs)
366
376
  @dependencies.merge(deps)
367
377
  yield uri if uri
368
378
  end
@@ -384,19 +394,19 @@ module Sprockets
384
394
  end
385
395
  end
386
396
 
387
- def load(uri)
388
- asset = @environment.load(uri)
389
- @dependencies.merge(asset.metadata[:dependencies])
390
- asset
397
+ def to_load(uri)
398
+ @to_load << uri
399
+ uri
391
400
  end
392
401
 
393
- def resolve(path, options = {})
402
+ def resolve(path, **kargs)
394
403
  # Prevent absolute paths in directives
395
404
  if @environment.absolute_path?(path)
396
405
  raise FileOutsidePaths, "can't require absolute file: #{path}"
397
406
  end
398
407
 
399
- uri, deps = @environment.resolve!(path, options.merge(base_path: @dirname))
408
+ kargs[:base_path] = @dirname
409
+ uri, deps = @environment.resolve!(path, **kargs)
400
410
  @dependencies.merge(deps)
401
411
  uri
402
412
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/autoload'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/autoload'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'base64'
2
3
  require 'stringio'
3
4
  require 'zlib'
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/base'
2
3
  require 'sprockets/cache/memory_store'
3
4
  require 'sprockets/cached_environment'
4
5
 
5
6
  module Sprockets
6
7
  class Environment < Base
7
- # `Environment` should initialized with your application's root
8
+ # `Environment` should be initialized with your application's root
8
9
  # directory. This should be the same as your Rails or Rack root.
9
10
  #
10
11
  # env = Environment.new(Rails.root)
@@ -18,7 +19,7 @@ module Sprockets
18
19
 
19
20
  # Returns a cached version of the environment.
20
21
  #
21
- # All its file system calls are cached which makes `cached` much
22
+ # All of its file system calls are cached which makes `cached` much
22
23
  # faster. This behavior is ideal in production since the file
23
24
  # system only changes between deploys.
24
25
  def cached
@@ -26,8 +27,12 @@ module Sprockets
26
27
  end
27
28
  alias_method :index, :cached
28
29
 
29
- def find_asset(*args)
30
- cached.find_asset(*args)
30
+ def find_asset(*args, **options)
31
+ cached.find_asset(*args, **options)
32
+ end
33
+
34
+ def find_asset!(*args)
35
+ cached.find_asset!(*args)
31
36
  end
32
37
 
33
38
  def find_all_linked_assets(*args, &block)
@@ -1,30 +1,37 @@
1
+ # frozen_string_literal: true
1
2
  require 'erb'
2
3
 
3
- module Sprockets
4
- class ERBProcessor
5
- # Public: Return singleton instance with default options.
6
- #
7
- # Returns ERBProcessor object.
8
- def self.instance
9
- @instance ||= new
10
- end
4
+ class Sprockets::ERBProcessor
5
+ # Public: Return singleton instance with default options.
6
+ #
7
+ # Returns ERBProcessor object.
8
+ def self.instance
9
+ @instance ||= new
10
+ end
11
11
 
12
- def self.call(input)
13
- instance.call(input)
14
- end
12
+ def self.call(input)
13
+ instance.call(input)
14
+ end
15
15
 
16
- def initialize(&block)
17
- @block = block
18
- end
16
+ def initialize(&block)
17
+ @block = block
18
+ end
19
19
 
20
- def call(input)
20
+ def call(input)
21
+ match = ERB.version.match(/\Aerb\.rb \[(?<version>[^ ]+) /)
22
+ if match && match[:version] >= "2.2.0" # Ruby 2.6+
23
+ engine = ::ERB.new(input[:data], trim_mode: '<>')
24
+ else
21
25
  engine = ::ERB.new(input[:data], nil, '<>')
22
- context = input[:environment].context_class.new(input)
23
- klass = (class << context; self; end)
24
- klass.class_eval(&@block) if @block
25
- engine.def_method(klass, :_evaluate_template, input[:filename])
26
- data = context._evaluate_template
27
- context.metadata.merge(data: data)
28
26
  end
27
+ engine.filename = input[:filename]
28
+
29
+ context = input[:environment].context_class.new(input)
30
+ klass = (class << context; self; end)
31
+ klass.const_set(:ENV, context.env_proxy)
32
+ klass.class_eval(&@block) if @block
33
+
34
+ data = engine.result(context.instance_eval('binding'))
35
+ context.metadata.merge(data: data)
29
36
  end
30
37
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Define some basic Sprockets error classes
2
3
  module Sprockets
3
4
  class Error < StandardError; end
@@ -0,0 +1,72 @@
1
+ module Sprockets
2
+ module Exporters
3
+ # Convienence class for all exporters to inherit from
4
+ #
5
+ # An exporter is responsible for exporting a Sprockets::Asset
6
+ # to a file system. For example the Exporters::File class
7
+ # writes the asset to it's destination. The Exporters::Zlib class
8
+ # writes a gzip copy of the asset to disk.
9
+ class Base
10
+ attr_reader :asset, :environment, :directory, :target
11
+
12
+ # Public: Creates new instance
13
+ #
14
+ # Initialize will be called with
15
+ # keyword arguments:
16
+ #
17
+ # - asset: An instance of Sprockets::Asset.
18
+ # - environment: An instance of Sprockets::Environment.
19
+ # - directory: String representing the target directory to write to.
20
+ #
21
+ # These will all be stored as accessible values. In addition a
22
+ # +target+ will be available which is the target directory and
23
+ # the asset's digest path combined.
24
+ def initialize(asset: nil, environment: nil, directory: nil)
25
+ @asset = asset
26
+ @environment = environment
27
+ @directory = directory
28
+ @target = ::File.join(directory, asset.digest_path)
29
+ setup
30
+ end
31
+
32
+ # Public: Callback that is executed after intialization
33
+ #
34
+ # Any setup that needs to be done can be performed in the +setup+
35
+ # method. It will be called immediately after initialization.
36
+ def setup
37
+ end
38
+
39
+ # Public: Handles logic for skipping exporter and notifying logger
40
+ #
41
+ # The `skip?` will be called before anything will be written.
42
+ # If `skip?` returns truthy it will not continue. This method
43
+ # takes a `logger` that responds to +debug+ and +info+. The `skip?`
44
+ # method is the only place expected to write to a logger, any other
45
+ # messages may produce jumbled logs.
46
+ def skip?(logger)
47
+ false
48
+ end
49
+
50
+ # Public: Contains logic for writing "exporting" asset to disk
51
+ #
52
+ # If the exporter is not skipped it then Sprockets will execute it's
53
+ # `call` method. This method takes no arguments and should only use
54
+ # elements passed in via initialize or stored in `setup`.
55
+ def call
56
+ raise "Must subclass and implement call"
57
+ end
58
+
59
+ # Public: Yields a file that can be written to with the input
60
+ #
61
+ # `filename`. Defaults to the `target`. Method
62
+ # is safe to use in forked or threaded environments.
63
+ def write(filename = target)
64
+ FileUtils.mkdir_p File.dirname(filename)
65
+ PathUtils.atomic_write(filename) do |f|
66
+ yield f
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,24 @@
1
+ require 'sprockets/exporters/base'
2
+
3
+ module Sprockets
4
+ module Exporters
5
+ # Writes a an asset file to disk
6
+ class FileExporter < Exporters::Base
7
+ def skip?(logger)
8
+ if ::File.exist?(target)
9
+ logger.debug "Skipping #{ target }, already exists"
10
+ true
11
+ else
12
+ logger.info "Writing #{ target }"
13
+ false
14
+ end
15
+ end
16
+
17
+ def call
18
+ write(target) do |file|
19
+ file.write(asset.source)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ require 'sprockets/exporters/base'
2
+ require 'sprockets/utils/gzip'
3
+
4
+ module Sprockets
5
+ module Exporters
6
+ # Generates a `.gz` file using the zlib algorithm built into
7
+ # Ruby's standard library.
8
+ class ZlibExporter < Exporters::Base
9
+ def setup
10
+ @gzip_target = "#{ target }.gz"
11
+ @gzip = Sprockets::Utils::Gzip.new(asset, archiver: Utils::Gzip::ZlibArchiver)
12
+ end
13
+
14
+ def skip?(logger)
15
+ return true if environment.skip_gzip?
16
+ return true if @gzip.cannot_compress?
17
+ if ::File.exist?(@gzip_target)
18
+ logger.debug "Skipping #{ @gzip_target }, already exists"
19
+ true
20
+ else
21
+ logger.info "Writing #{ @gzip_target }"
22
+ false
23
+ end
24
+ end
25
+
26
+ def call
27
+ write(@gzip_target) do |file|
28
+ @gzip.compress(file, target)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ require 'sprockets/exporters/zlib_exporter'
2
+
3
+ module Sprockets
4
+ module Exporters
5
+ # Generates a `.gz` file using the zopfli algorithm from the
6
+ # Zopfli gem.
7
+ class ZopfliExporter < ZlibExporter
8
+ def setup
9
+ @gzip_target = "#{ target }.gz"
10
+ @gzip = Sprockets::Utils::Gzip.new(asset, archiver: Utils::Gzip::ZopfliArchiver)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,73 @@
1
+ module Sprockets
2
+ # `Exporting` is an internal mixin whose public methods are exposed on
3
+ # the `Environment` and `CachedEnvironment` classes.
4
+ module Exporting
5
+ # Exporters are ran on the assets:precompile task
6
+ def exporters
7
+ config[:exporters]
8
+ end
9
+
10
+ # Public: Registers a new Exporter `klass` for `mime_type`.
11
+ #
12
+ # If your exporter depends on one or more other exporters you can
13
+ # specify this via the `depend_on` keyword.
14
+ #
15
+ # register_exporter '*/*', Sprockets::Exporters::ZlibExporter
16
+ #
17
+ # This ensures that `Sprockets::Exporters::File` will always execute before
18
+ # `Sprockets::Exporters::Zlib`
19
+ def register_exporter(mime_types, klass = nil)
20
+ mime_types = Array(mime_types)
21
+
22
+ mime_types.each do |mime_type|
23
+ self.config = hash_reassoc(config, :exporters, mime_type) do |_exporters|
24
+ _exporters << klass
25
+ end
26
+ end
27
+ end
28
+
29
+ # Public: Remove Exporting processor `klass` for `mime_type`.
30
+ #
31
+ # environment.unregister_exporter '*/*', Sprockets::Exporters::Zlib
32
+ #
33
+ # Can be called without a mime type
34
+ #
35
+ # environment.unregister_exporter Sprockets::Exporters::Zlib
36
+ #
37
+ # Does not remove any exporters that depend on `klass`.
38
+ def unregister_exporter(mime_types, exporter = nil)
39
+ unless mime_types.is_a? Array
40
+ if mime_types.is_a? String
41
+ mime_types = [mime_types]
42
+ else # called with no mime type
43
+ exporter = mime_types
44
+ mime_types = nil
45
+ end
46
+ end
47
+
48
+ self.config = hash_reassoc(config, :exporters) do |_exporters|
49
+ _exporters.each do |mime_type, exporters_array|
50
+ next if mime_types && !mime_types.include?(mime_type)
51
+ if exporters_array.include? exporter
52
+ _exporters[mime_type] = exporters_array.dup.delete exporter
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # Public: Checks if concurrent exporting is allowed
59
+ def export_concurrent
60
+ config[:export_concurrent]
61
+ end
62
+
63
+ # Public: Enable or disable the concurrently exporting files
64
+ #
65
+ # Defaults to true.
66
+ #
67
+ # environment.export_concurrent = false
68
+ #
69
+ def export_concurrent=(export_concurrent)
70
+ self.config = config.merge(export_concurrent: export_concurrent).freeze
71
+ end
72
+ end
73
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'set'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Sprockets
2
3
  # Internal: HTTP URI utilities. Many adapted from Rack::Utils. Mixed into
3
4
  # Environment.
@@ -13,9 +14,9 @@ module Sprockets
13
14
  # Returns true if the given value is a mime match for the given mime match
14
15
  # specification, false otherwise.
15
16
  def match_mime_type?(value, matcher)
16
- v1, v2 = value.split('/', 2)
17
- m1, m2 = matcher.split('/', 2)
18
- (m1 == '*' || v1 == m1) && (m2.nil? || m2 == '*' || m2 == v2)
17
+ v1, v2 = value.split('/'.freeze, 2)
18
+ m1, m2 = matcher.split('/'.freeze, 2)
19
+ (m1 == '*'.freeze || v1 == m1) && (m2.nil? || m2 == '*'.freeze || m2 == v2)
19
20
  end
20
21
 
21
22
  # Public: Return values from Hash where the key matches the mime type.
@@ -36,7 +37,22 @@ module Sprockets
36
37
 
37
38
  # Internal: Parse Accept header quality values.
38
39
  #
39
- # Adapted from Rack::Utils#q_values.
40
+ # values - String e.g. "application/javascript"
41
+ #
42
+ # Adapted from Rack::Utils#q_values. Quality values are
43
+ # described in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
44
+ #
45
+ # parse_q_values("application/javascript")
46
+ # # => [["application/javascript", 1.0]]
47
+ #
48
+ # parse_q_values("*/*")
49
+ # # => [["*/*", 1.0]]
50
+ #
51
+ # parse_q_values("text/plain; q=0.5, image/*")
52
+ # # => [["text/plain", 0.5], ["image/*", 1.0]]
53
+ #
54
+ # parse_q_values("application/javascript, text/css")
55
+ # # => [["application/javascript", 1.0], ["text/css", 1.0]]
40
56
  #
41
57
  # Returns an Array of [String, Float].
42
58
  def parse_q_values(values)
@@ -70,14 +86,16 @@ module Sprockets
70
86
  raise TypeError, "unknown q_values type: #{q_values.class}"
71
87
  end
72
88
 
89
+ i = 0
73
90
  q_values.each do |accepted, quality|
74
91
  if match = available.find { |option| matcher.call(option, accepted) }
75
- matches << [match, quality]
92
+ i += 1
93
+ matches << [-quality, i, match]
76
94
  end
77
95
  end
78
96
 
79
- matches.sort_by! { |match, quality| -quality }
80
- matches.map! { |match, quality| match }
97
+ matches.sort!
98
+ matches.map! { |_, _, match| match }
81
99
  matches
82
100
  end
83
101
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+ require 'sprockets/digest_utils'
4
+
5
+ module Sprockets
6
+ class JSMincCompressor
7
+ VERSION = '1'
8
+
9
+ def self.instance
10
+ @instance ||= new
11
+ end
12
+
13
+ def self.call(input)
14
+ instance.call(input)
15
+ end
16
+
17
+ def self.cache_key
18
+ instance.cache_key
19
+ end
20
+
21
+ attr_reader :cache_key
22
+
23
+ def initialize(options = {})
24
+ @compressor_class = Autoload::JSMinC
25
+ @cache_key = "#{self.class.name}:#{Autoload::JSMinC::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
26
+ end
27
+
28
+ def call(input)
29
+ @compressor_class.minify(input[:data])
30
+ end
31
+ end
32
+ end