sprockets 3.0.3 → 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 (95) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +101 -0
  3. data/{LICENSE → MIT-LICENSE} +2 -2
  4. data/README.md +531 -276
  5. data/bin/sprockets +12 -7
  6. data/lib/rake/sprocketstask.rb +9 -4
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +41 -28
  9. data/lib/sprockets/autoload/babel.rb +8 -0
  10. data/lib/sprockets/autoload/closure.rb +1 -0
  11. data/lib/sprockets/autoload/coffee_script.rb +1 -0
  12. data/lib/sprockets/autoload/eco.rb +1 -0
  13. data/lib/sprockets/autoload/ejs.rb +1 -0
  14. data/lib/sprockets/autoload/jsminc.rb +8 -0
  15. data/lib/sprockets/autoload/sass.rb +1 -0
  16. data/lib/sprockets/autoload/sassc.rb +8 -0
  17. data/lib/sprockets/autoload/uglifier.rb +1 -0
  18. data/lib/sprockets/autoload/yui.rb +1 -0
  19. data/lib/sprockets/autoload/zopfli.rb +7 -0
  20. data/lib/sprockets/autoload.rb +5 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +61 -13
  23. data/lib/sprockets/bower.rb +6 -3
  24. data/lib/sprockets/bundle.rb +41 -5
  25. data/lib/sprockets/cache/file_store.rb +32 -7
  26. data/lib/sprockets/cache/memory_store.rb +28 -10
  27. data/lib/sprockets/cache/null_store.rb +8 -0
  28. data/lib/sprockets/cache.rb +43 -6
  29. data/lib/sprockets/cached_environment.rb +15 -20
  30. data/lib/sprockets/closure_compressor.rb +6 -11
  31. data/lib/sprockets/coffee_script_processor.rb +20 -6
  32. data/lib/sprockets/compressing.rb +62 -2
  33. data/lib/sprockets/configuration.rb +5 -9
  34. data/lib/sprockets/context.rb +99 -25
  35. data/lib/sprockets/dependencies.rb +10 -9
  36. data/lib/sprockets/digest_utils.rb +103 -62
  37. data/lib/sprockets/directive_processor.rb +64 -36
  38. data/lib/sprockets/eco_processor.rb +4 -3
  39. data/lib/sprockets/ejs_processor.rb +4 -3
  40. data/lib/sprockets/encoding_utils.rb +1 -0
  41. data/lib/sprockets/environment.rb +9 -4
  42. data/lib/sprockets/erb_processor.rb +34 -21
  43. data/lib/sprockets/errors.rb +1 -0
  44. data/lib/sprockets/exporters/base.rb +71 -0
  45. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  46. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  47. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  48. data/lib/sprockets/exporting.rb +73 -0
  49. data/lib/sprockets/file_reader.rb +1 -0
  50. data/lib/sprockets/http_utils.rb +25 -7
  51. data/lib/sprockets/jsminc_compressor.rb +32 -0
  52. data/lib/sprockets/jst_processor.rb +11 -10
  53. data/lib/sprockets/loader.rb +244 -62
  54. data/lib/sprockets/manifest.rb +100 -46
  55. data/lib/sprockets/manifest_utils.rb +9 -6
  56. data/lib/sprockets/mime.rb +8 -42
  57. data/lib/sprockets/npm.rb +52 -0
  58. data/lib/sprockets/path_dependency_utils.rb +3 -11
  59. data/lib/sprockets/path_digest_utils.rb +2 -1
  60. data/lib/sprockets/path_utils.rb +107 -22
  61. data/lib/sprockets/paths.rb +1 -0
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +32 -52
  64. data/lib/sprockets/processor_utils.rb +38 -39
  65. data/lib/sprockets/resolve.rb +177 -97
  66. data/lib/sprockets/sass_cache_store.rb +1 -0
  67. data/lib/sprockets/sass_compressor.rb +21 -17
  68. data/lib/sprockets/sass_functions.rb +1 -0
  69. data/lib/sprockets/sass_importer.rb +1 -0
  70. data/lib/sprockets/sass_processor.rb +46 -18
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +77 -44
  74. data/lib/sprockets/source_map_processor.rb +66 -0
  75. data/lib/sprockets/source_map_utils.rb +483 -0
  76. data/lib/sprockets/transformers.rb +63 -35
  77. data/lib/sprockets/uglifier_compressor.rb +23 -20
  78. data/lib/sprockets/unloaded_asset.rb +139 -0
  79. data/lib/sprockets/uri_tar.rb +99 -0
  80. data/lib/sprockets/uri_utils.rb +14 -14
  81. data/lib/sprockets/utils/gzip.rb +99 -0
  82. data/lib/sprockets/utils.rb +63 -71
  83. data/lib/sprockets/version.rb +2 -1
  84. data/lib/sprockets/yui_compressor.rb +5 -14
  85. data/lib/sprockets.rb +105 -33
  86. metadata +157 -27
  87. data/lib/sprockets/coffee_script_template.rb +0 -6
  88. data/lib/sprockets/eco_template.rb +0 -6
  89. data/lib/sprockets/ejs_template.rb +0 -6
  90. data/lib/sprockets/engines.rb +0 -81
  91. data/lib/sprockets/erb_template.rb +0 -6
  92. data/lib/sprockets/legacy.rb +0 -314
  93. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  94. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  95. data/lib/sprockets/sass_template.rb +0 -7
@@ -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,33 @@ 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))
286
+ end
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)
277
304
  end
278
305
 
279
306
  # Allows dependency to be excluded from the asset bundle.
@@ -297,7 +324,8 @@ module Sprockets
297
324
  # /*= link "logo.png" */
298
325
  #
299
326
  def process_link_directive(path)
300
- @links << load(resolve(path)).uri
327
+ uri = to_load(resolve(path))
328
+ @to_link << uri
301
329
  end
302
330
 
303
331
  # `link_directory` links all the files inside a single
@@ -307,7 +335,7 @@ module Sprockets
307
335
  # //= link_directory "./fonts"
308
336
  #
309
337
  # Use caution when linking against JS or CSS assets. Include an explicit
310
- # extension or content type in these cases
338
+ # extension or content type in these cases.
311
339
  #
312
340
  # //= link_directory "./scripts" .js
313
341
  #
@@ -323,7 +351,7 @@ module Sprockets
323
351
  # //= link_tree "./images"
324
352
  #
325
353
  # Use caution when linking against JS or CSS assets. Include an explicit
326
- # extension or content type in these cases
354
+ # extension or content type in these cases.
327
355
  #
328
356
  # //= link_tree "./styles" .css
329
357
  #
@@ -354,17 +382,17 @@ module Sprockets
354
382
 
355
383
  def link_paths(paths, deps, accept)
356
384
  resolve_paths(paths, deps, accept: accept) do |uri|
357
- @links << load(uri).uri
385
+ @to_link << to_load(uri)
358
386
  end
359
387
  end
360
388
 
361
- def resolve_paths(paths, deps, options = {})
389
+ def resolve_paths(paths, deps, **kargs)
362
390
  @dependencies.merge(deps)
363
391
  paths.each do |subpath, stat|
364
392
  next if subpath == @filename || stat.directory?
365
- uri, deps = @environment.resolve(subpath, options.merge(compat: false))
393
+ uri, deps = @environment.resolve(subpath, **kargs)
366
394
  @dependencies.merge(deps)
367
- yield uri if uri
395
+ yield uri if uri && block_given?
368
396
  end
369
397
  end
370
398
 
@@ -384,19 +412,19 @@ module Sprockets
384
412
  end
385
413
  end
386
414
 
387
- def load(uri)
388
- asset = @environment.load(uri)
389
- @dependencies.merge(asset.metadata[:dependencies])
390
- asset
415
+ def to_load(uri)
416
+ @to_load << uri
417
+ uri
391
418
  end
392
419
 
393
- def resolve(path, options = {})
420
+ def resolve(path, **kargs)
394
421
  # Prevent absolute paths in directives
395
422
  if @environment.absolute_path?(path)
396
423
  raise FileOutsidePaths, "can't require absolute file: #{path}"
397
424
  end
398
425
 
399
- uri, deps = @environment.resolve!(path, options.merge(base_path: @dirname))
426
+ kargs[:base_path] = @dirname
427
+ uri, deps = @environment.resolve!(path, **kargs)
400
428
  @dependencies.merge(deps)
401
429
  uri
402
430
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/autoload'
2
3
 
3
4
  module Sprockets
4
5
  # Processor engine class for the Eco compiler. Depends on the `eco` gem.
5
6
  #
6
- # For more infomation see:
7
+ # For more information see:
7
8
  #
8
9
  # https://github.com/sstephenson/ruby-eco
9
10
  # https://github.com/sstephenson/eco
@@ -12,7 +13,7 @@ module Sprockets
12
13
  VERSION = '1'
13
14
 
14
15
  def self.cache_key
15
- @cache_key ||= [name, Autoload::Eco::Source::VERSION, VERSION].freeze
16
+ @cache_key ||= "#{name}:#{Autoload::Eco::Source::VERSION}:#{VERSION}".freeze
16
17
  end
17
18
 
18
19
  # Compile template data with Eco compiler.
@@ -24,7 +25,7 @@ module Sprockets
24
25
  #
25
26
  def self.call(input)
26
27
  data = input[:data]
27
- input[:cache].fetch(cache_key + [data]) do
28
+ input[:cache].fetch([cache_key, data]) do
28
29
  Autoload::Eco.compile(data)
29
30
  end
30
31
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/autoload'
2
3
 
3
4
  module Sprockets
4
5
  # Processor engine class for the EJS compiler. Depends on the `ejs` gem.
5
6
  #
6
- # For more infomation see:
7
+ # For more information see:
7
8
  #
8
9
  # https://github.com/sstephenson/ruby-ejs
9
10
  #
@@ -11,7 +12,7 @@ module Sprockets
11
12
  VERSION = '1'
12
13
 
13
14
  def self.cache_key
14
- @cache_key ||= [name, VERSION].freeze
15
+ @cache_key ||= "#{name}:#{VERSION}".freeze
15
16
  end
16
17
 
17
18
  # Compile template data with EJS compiler.
@@ -23,7 +24,7 @@ module Sprockets
23
24
  #
24
25
  def self.call(input)
25
26
  data = input[:data]
26
- input[:cache].fetch(cache_key + [data]) do
27
+ input[:cache].fetch([cache_key, data]) do
27
28
  Autoload::EJS.compile(data)
28
29
  end
29
30
  end
@@ -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,43 @@
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
+ if keyword_constructor? # Ruby 2.6+
22
+ engine = ::ERB.new(input[:data], trim_mode: '<>')
23
+ else
21
24
  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
25
  end
26
+ engine.filename = input[:filename]
27
+
28
+ context = input[:environment].context_class.new(input)
29
+ klass = (class << context; self; end)
30
+ klass.const_set(:ENV, context.env_proxy)
31
+ klass.class_eval(&@block) if @block
32
+
33
+ data = engine.result(context.instance_eval('binding'))
34
+ context.metadata.merge(data: data)
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])
29
42
  end
30
43
  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,71 @@
1
+ module Sprockets
2
+ module Exporters
3
+ # Convenience 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 initialization
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
@@ -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::ZlibExporter
32
+ #
33
+ # Can be called without a mime type
34
+ #
35
+ # environment.unregister_exporter Sprockets::Exporters::ZlibExporter
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