sprockets 2.6.0 → 4.2.2

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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +118 -0
  3. data/{LICENSE → MIT-LICENSE} +2 -2
  4. data/README.md +541 -289
  5. data/bin/sprockets +20 -7
  6. data/lib/rake/sprocketstask.rb +34 -17
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +158 -210
  9. data/lib/sprockets/autoload/babel.rb +8 -0
  10. data/lib/sprockets/autoload/closure.rb +8 -0
  11. data/lib/sprockets/autoload/coffee_script.rb +8 -0
  12. data/lib/sprockets/autoload/eco.rb +8 -0
  13. data/lib/sprockets/autoload/ejs.rb +8 -0
  14. data/lib/sprockets/autoload/jsminc.rb +8 -0
  15. data/lib/sprockets/autoload/sass.rb +8 -0
  16. data/lib/sprockets/autoload/sassc.rb +8 -0
  17. data/lib/sprockets/autoload/uglifier.rb +8 -0
  18. data/lib/sprockets/autoload/yui.rb +8 -0
  19. data/lib/sprockets/autoload/zopfli.rb +7 -0
  20. data/lib/sprockets/autoload.rb +16 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +89 -378
  23. data/lib/sprockets/bower.rb +61 -0
  24. data/lib/sprockets/bundle.rb +105 -0
  25. data/lib/sprockets/cache/file_store.rb +190 -14
  26. data/lib/sprockets/cache/memory_store.rb +84 -0
  27. data/lib/sprockets/cache/null_store.rb +54 -0
  28. data/lib/sprockets/cache.rb +271 -0
  29. data/lib/sprockets/cached_environment.rb +64 -0
  30. data/lib/sprockets/closure_compressor.rb +48 -0
  31. data/lib/sprockets/coffee_script_processor.rb +39 -0
  32. data/lib/sprockets/compressing.rb +134 -0
  33. data/lib/sprockets/configuration.rb +79 -0
  34. data/lib/sprockets/context.rb +166 -150
  35. data/lib/sprockets/dependencies.rb +74 -0
  36. data/lib/sprockets/digest_utils.rb +197 -0
  37. data/lib/sprockets/directive_processor.rb +241 -215
  38. data/lib/sprockets/eco_processor.rb +33 -0
  39. data/lib/sprockets/ejs_processor.rb +32 -0
  40. data/lib/sprockets/encoding_utils.rb +261 -0
  41. data/lib/sprockets/environment.rb +23 -64
  42. data/lib/sprockets/erb_processor.rb +43 -0
  43. data/lib/sprockets/errors.rb +5 -13
  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 +16 -0
  50. data/lib/sprockets/http_utils.rb +135 -0
  51. data/lib/sprockets/jsminc_compressor.rb +32 -0
  52. data/lib/sprockets/jst_processor.rb +36 -19
  53. data/lib/sprockets/loader.rb +347 -0
  54. data/lib/sprockets/manifest.rb +228 -112
  55. data/lib/sprockets/manifest_utils.rb +48 -0
  56. data/lib/sprockets/mime.rb +78 -31
  57. data/lib/sprockets/npm.rb +52 -0
  58. data/lib/sprockets/path_dependency_utils.rb +77 -0
  59. data/lib/sprockets/path_digest_utils.rb +48 -0
  60. data/lib/sprockets/path_utils.rb +367 -0
  61. data/lib/sprockets/paths.rb +43 -19
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +146 -164
  64. data/lib/sprockets/processor_utils.rb +170 -0
  65. data/lib/sprockets/resolve.rb +295 -0
  66. data/lib/sprockets/sass_cache_store.rb +20 -15
  67. data/lib/sprockets/sass_compressor.rb +55 -10
  68. data/lib/sprockets/sass_functions.rb +3 -70
  69. data/lib/sprockets/sass_importer.rb +3 -29
  70. data/lib/sprockets/sass_processor.rb +313 -0
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +159 -91
  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 +173 -0
  77. data/lib/sprockets/uglifier_compressor.rb +66 -0
  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 +194 -0
  81. data/lib/sprockets/utils/gzip.rb +99 -0
  82. data/lib/sprockets/utils.rb +193 -52
  83. data/lib/sprockets/version.rb +2 -1
  84. data/lib/sprockets/yui_compressor.rb +56 -0
  85. data/lib/sprockets.rb +217 -75
  86. metadata +272 -117
  87. data/lib/sprockets/asset_attributes.rb +0 -131
  88. data/lib/sprockets/bundled_asset.rb +0 -80
  89. data/lib/sprockets/caching.rb +0 -96
  90. data/lib/sprockets/charset_normalizer.rb +0 -41
  91. data/lib/sprockets/eco_template.rb +0 -38
  92. data/lib/sprockets/ejs_template.rb +0 -37
  93. data/lib/sprockets/engines.rb +0 -74
  94. data/lib/sprockets/index.rb +0 -99
  95. data/lib/sprockets/processed_asset.rb +0 -152
  96. data/lib/sprockets/processor.rb +0 -32
  97. data/lib/sprockets/safety_colons.rb +0 -28
  98. data/lib/sprockets/sass_template.rb +0 -60
  99. data/lib/sprockets/scss_template.rb +0 -13
  100. data/lib/sprockets/static_asset.rb +0 -58
@@ -0,0 +1,261 @@
1
+ # frozen_string_literal: true
2
+ require 'stringio'
3
+ require 'zlib'
4
+
5
+ module Sprockets
6
+ # Internal: HTTP transport encoding and charset detecting related functions.
7
+ # Mixed into Environment.
8
+ module EncodingUtils
9
+ extend self
10
+
11
+ ## Binary encodings ##
12
+
13
+ # Public: Use deflate to compress data.
14
+ #
15
+ # str - String data
16
+ #
17
+ # Returns a compressed String
18
+ def deflate(str)
19
+ deflater = Zlib::Deflate.new(
20
+ Zlib::BEST_COMPRESSION,
21
+ -Zlib::MAX_WBITS,
22
+ Zlib::MAX_MEM_LEVEL,
23
+ Zlib::DEFAULT_STRATEGY
24
+ )
25
+ deflater << str
26
+ deflater.finish
27
+ end
28
+
29
+ # Internal: Unmarshal optionally deflated data.
30
+ #
31
+ # Checks leading marshal header to see if the bytes are uncompressed
32
+ # otherwise inflate the data an unmarshal.
33
+ #
34
+ # str - Marshaled String
35
+ # window_bits - Integer deflate window size. See ZLib::Inflate.new()
36
+ #
37
+ # Returns unmarshaled Object or raises an Exception.
38
+ def unmarshaled_deflated(str, window_bits = -Zlib::MAX_WBITS)
39
+ major, minor = str[0], str[1]
40
+ if major && major.ord == Marshal::MAJOR_VERSION &&
41
+ minor && minor.ord <= Marshal::MINOR_VERSION
42
+ marshaled = str
43
+ else
44
+ begin
45
+ marshaled = Zlib::Inflate.new(window_bits).inflate(str)
46
+ rescue Zlib::DataError
47
+ marshaled = str
48
+ end
49
+ end
50
+ Marshal.load(marshaled)
51
+ end
52
+
53
+ # Public: Use gzip to compress data.
54
+ #
55
+ # str - String data
56
+ #
57
+ # Returns a compressed String
58
+ def gzip(str)
59
+ io = StringIO.new
60
+ gz = Zlib::GzipWriter.new(io, Zlib::BEST_COMPRESSION)
61
+ gz.mtime = 1
62
+ gz << str
63
+ gz.finish
64
+ io.string
65
+ end
66
+
67
+ # Public: Use base64 to encode data.
68
+ #
69
+ # str - String data
70
+ #
71
+ # Returns a encoded String
72
+ def base64(str)
73
+ [str].pack("m0")
74
+ end
75
+
76
+
77
+ ## Charset encodings ##
78
+
79
+ # Internal: Shorthand aliases for detecter functions.
80
+ CHARSET_DETECT = {}
81
+
82
+ # Internal: Mapping unicode encodings to byte order markers.
83
+ BOM = {
84
+ Encoding::UTF_32LE => [0xFF, 0xFE, 0x00, 0x00],
85
+ Encoding::UTF_32BE => [0x00, 0x00, 0xFE, 0xFF],
86
+ Encoding::UTF_8 => [0xEF, 0xBB, 0xBF],
87
+ Encoding::UTF_16LE => [0xFF, 0xFE],
88
+ Encoding::UTF_16BE => [0xFE, 0xFF]
89
+ }
90
+
91
+ # Public: Basic string detecter.
92
+ #
93
+ # Attempts to parse any Unicode BOM otherwise falls back to the
94
+ # environment's external encoding.
95
+ #
96
+ # str - ASCII-8BIT encoded String
97
+ #
98
+ # Returns encoded String.
99
+ def detect(str)
100
+ str = detect_unicode_bom(str)
101
+
102
+ # Attempt Charlock detection
103
+ if str.encoding == Encoding::BINARY
104
+ charlock_detect(str)
105
+ end
106
+
107
+ # Fallback to environment's external encoding
108
+ if str.encoding == Encoding::BINARY
109
+ str.force_encoding(Encoding.default_external)
110
+ end
111
+
112
+ str
113
+ end
114
+ CHARSET_DETECT[:default] = method(:detect)
115
+
116
+ # Internal: Use Charlock Holmes to detect encoding.
117
+ #
118
+ # To enable this code path, require 'charlock_holmes'
119
+ #
120
+ # Returns encoded String.
121
+ def charlock_detect(str)
122
+ if defined? CharlockHolmes::EncodingDetector
123
+ if detected = CharlockHolmes::EncodingDetector.detect(str)
124
+ str.force_encoding(detected[:encoding]) if detected[:encoding]
125
+ end
126
+ end
127
+
128
+ str
129
+ end
130
+
131
+ # Public: Detect Unicode string.
132
+ #
133
+ # Attempts to parse Unicode BOM and falls back to UTF-8.
134
+ #
135
+ # str - ASCII-8BIT encoded String
136
+ #
137
+ # Returns encoded String.
138
+ def detect_unicode(str)
139
+ str = detect_unicode_bom(str)
140
+
141
+ # Fallback to UTF-8
142
+ if str.encoding == Encoding::BINARY
143
+ str.force_encoding(Encoding::UTF_8)
144
+ end
145
+
146
+ str
147
+ end
148
+ CHARSET_DETECT[:unicode] = method(:detect_unicode)
149
+
150
+ # Public: Detect and strip BOM from possible unicode string.
151
+ #
152
+ # str - ASCII-8BIT encoded String
153
+ #
154
+ # Returns UTF 8/16/32 encoded String without BOM or the original String if
155
+ # no BOM was present.
156
+ def detect_unicode_bom(str)
157
+ bom_bytes = str.byteslice(0, 4).bytes.to_a
158
+
159
+ BOM.each do |encoding, bytes|
160
+ if bom_bytes[0, bytes.size] == bytes
161
+ str = str.dup
162
+ str.force_encoding(Encoding::BINARY)
163
+ str.slice!(0, bytes.size)
164
+ str.force_encoding(encoding)
165
+ return str
166
+ end
167
+ end
168
+
169
+ return str
170
+ end
171
+
172
+ # Public: Detect and strip @charset from CSS style sheet.
173
+ #
174
+ # str - String.
175
+ #
176
+ # Returns a encoded String.
177
+ def detect_css(str)
178
+ str = detect_unicode_bom(str)
179
+
180
+ if name = scan_css_charset(str)
181
+ encoding = Encoding.find(name)
182
+ str = str.dup
183
+ str.force_encoding(encoding)
184
+ len = "@charset \"#{name}\";".encode(encoding).size
185
+ str.slice!(0, len)
186
+ str
187
+ end
188
+
189
+ # Fallback to UTF-8
190
+ if str.encoding == Encoding::BINARY
191
+ str.force_encoding(Encoding::UTF_8)
192
+ end
193
+
194
+ str
195
+ end
196
+ CHARSET_DETECT[:css] = method(:detect_css)
197
+
198
+ # Internal: @charset bytes
199
+ CHARSET_START = [0x40, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x20, 0x22]
200
+ CHARSET_SIZE = CHARSET_START.size
201
+
202
+ # Internal: Scan binary CSS string for @charset encoding name.
203
+ #
204
+ # str - ASCII-8BIT encoded String
205
+ #
206
+ # Returns encoding String name or nil.
207
+ def scan_css_charset(str)
208
+ buf = []
209
+ i = 0
210
+
211
+ str.each_byte.each do |byte|
212
+ # Halt on line breaks
213
+ break if byte == 0x0A || byte == 0x0D
214
+
215
+ # Only ascii bytes
216
+ next unless 0x0 < byte && byte <= 0xFF
217
+
218
+ if i < CHARSET_SIZE
219
+ elsif i == CHARSET_SIZE
220
+ if buf == CHARSET_START
221
+ buf = []
222
+ else
223
+ break
224
+ end
225
+ elsif byte == 0x22
226
+ return buf.pack('C*')
227
+ end
228
+
229
+ buf << byte
230
+ i += 1
231
+ end
232
+
233
+ nil
234
+ end
235
+
236
+ # Public: Detect charset from HTML document.
237
+ #
238
+ # Attempts to parse any Unicode BOM otherwise attempt Charlock detection
239
+ # and finally falls back to the environment's external encoding.
240
+ #
241
+ # str - String.
242
+ #
243
+ # Returns a encoded String.
244
+ def detect_html(str)
245
+ str = detect_unicode_bom(str)
246
+
247
+ # Attempt Charlock detection
248
+ if str.encoding == Encoding::BINARY
249
+ charlock_detect(str)
250
+ end
251
+
252
+ # Fallback to environment's external encoding
253
+ if str.encoding == Encoding::BINARY
254
+ str.force_encoding(Encoding.default_external)
255
+ end
256
+
257
+ str
258
+ end
259
+ CHARSET_DETECT[:html] = method(:detect_html)
260
+ end
261
+ end
@@ -1,87 +1,46 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/base'
2
- require 'sprockets/context'
3
- require 'sprockets/index'
4
-
5
- require 'hike'
6
- require 'logger'
7
- require 'pathname'
8
- require 'tilt'
3
+ require 'sprockets/cache/memory_store'
4
+ require 'sprockets/cached_environment'
9
5
 
10
6
  module Sprockets
11
7
  class Environment < Base
12
- # `Environment` should initialized with your application's root
8
+ # `Environment` should be initialized with your application's root
13
9
  # directory. This should be the same as your Rails or Rack root.
14
10
  #
15
11
  # env = Environment.new(Rails.root)
16
12
  #
17
13
  def initialize(root = ".")
18
- @trail = Hike::Trail.new(root)
19
-
20
- self.logger = Logger.new($stderr)
21
- self.logger.level = Logger::FATAL
22
-
23
- if respond_to?(:default_external_encoding)
24
- self.default_external_encoding = Encoding::UTF_8
25
- end
26
-
27
- # Create a safe `Context` subclass to mutate
28
- @context_class = Class.new(Context)
29
-
30
- # Set MD5 as the default digest
31
- require 'digest/md5'
32
- @digest_class = ::Digest::MD5
33
- @version = ''
34
-
35
- @mime_types = Sprockets.registered_mime_types
36
- @engines = Sprockets.engines
37
- @preprocessors = Sprockets.preprocessors
38
- @postprocessors = Sprockets.postprocessors
39
- @bundle_processors = Sprockets.bundle_processors
40
-
41
- Sprockets.paths.each do |path|
42
- append_path(path)
43
- end
44
-
45
- @engines.each do |ext, klass|
46
- add_engine_to_trail(ext, klass)
47
- end
48
-
49
- @mime_types.each do |ext, type|
50
- @trail.append_extension(ext)
51
- end
52
-
53
- expire_index!
54
-
14
+ initialize_configuration(Sprockets)
15
+ self.root = root
16
+ self.cache = Cache::MemoryStore.new
55
17
  yield self if block_given?
56
18
  end
57
19
 
58
20
  # Returns a cached version of the environment.
59
21
  #
60
- # All its file system calls are cached which makes `index` much
22
+ # All of its file system calls are cached which makes `cached` much
61
23
  # faster. This behavior is ideal in production since the file
62
24
  # system only changes between deploys.
63
- def index
64
- Index.new(self)
25
+ def cached
26
+ CachedEnvironment.new(self)
65
27
  end
28
+ alias_method :index, :cached
66
29
 
67
- # Cache `find_asset` calls
68
- def find_asset(path, options = {})
69
- options[:bundle] = true unless options.key?(:bundle)
30
+ def find_asset(*args, **options)
31
+ cached.find_asset(*args, **options)
32
+ end
70
33
 
71
- # Ensure inmemory cached assets are still fresh on every lookup
72
- if (asset = @assets[cache_key_for(path, options)]) && asset.fresh?(self)
73
- asset
74
- elsif asset = index.find_asset(path, options)
75
- # Cache is pushed upstream by Index#find_asset
76
- asset
77
- end
34
+ def find_asset!(*args)
35
+ cached.find_asset!(*args)
78
36
  end
79
37
 
80
- protected
81
- def expire_index!
82
- # Clear digest to be recomputed
83
- @digest = nil
84
- @assets = {}
85
- end
38
+ def find_all_linked_assets(*args, &block)
39
+ cached.find_all_linked_assets(*args, &block)
40
+ end
41
+
42
+ def load(*args)
43
+ cached.load(*args)
44
+ end
86
45
  end
87
46
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require 'erb'
3
+
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
+
12
+ def self.call(input)
13
+ instance.call(input)
14
+ end
15
+
16
+ def initialize(&block)
17
+ @block = block
18
+ end
19
+
20
+ def call(input)
21
+ if keyword_constructor? # Ruby 2.6+
22
+ engine = ::ERB.new(input[:data], trim_mode: '<>')
23
+ else
24
+ engine = ::ERB.new(input[:data], nil, '<>')
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])
42
+ end
43
+ end
@@ -1,20 +1,12 @@
1
+ # frozen_string_literal: true
1
2
  # Define some basic Sprockets error classes
2
3
  module Sprockets
3
4
  class Error < StandardError; end
4
5
  class ArgumentError < Error; end
5
- class CircularDependencyError < Error; end
6
6
  class ContentTypeMismatch < Error; end
7
- class EncodingError < Error; end
8
- class FileNotFound < Error; end
9
- class FileOutsidePaths < Error; end
10
7
  class NotImplementedError < Error; end
11
- class UnserializeError < Error; end
12
-
13
- module EngineError
14
- attr_accessor :sprockets_annotation
15
-
16
- def message
17
- [super, sprockets_annotation].compact.join("\n")
18
- end
19
- end
8
+ class NotFound < Error; end
9
+ class ConversionError < NotFound; end
10
+ class FileNotFound < NotFound; end
11
+ class FileOutsidePaths < NotFound; end
20
12
  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
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+
4
+ module Sprockets
5
+ # Internal: The first processor in the pipeline that reads the file into
6
+ # memory and passes it along as `input[:data]`.
7
+ class FileReader
8
+ def self.call(input)
9
+ env = input[:environment]
10
+ data = env.read_file(input[:filename], input[:content_type])
11
+ dependencies = Set.new(input[:metadata][:dependencies])
12
+ dependencies += [env.build_file_digest_uri(input[:filename])]
13
+ { data: data, dependencies: dependencies }
14
+ end
15
+ end
16
+ end