sprockets 2.2.3 → 4.0.0

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