sprockets 2.2.3 → 4.0.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 (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