sprockets 4.0.1

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 (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +72 -0
  3. data/README.md +665 -0
  4. data/bin/sprockets +93 -0
  5. data/lib/rake/sprocketstask.rb +153 -0
  6. data/lib/sprockets.rb +229 -0
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +202 -0
  9. data/lib/sprockets/autoload.rb +16 -0
  10. data/lib/sprockets/autoload/babel.rb +8 -0
  11. data/lib/sprockets/autoload/closure.rb +8 -0
  12. data/lib/sprockets/autoload/coffee_script.rb +8 -0
  13. data/lib/sprockets/autoload/eco.rb +8 -0
  14. data/lib/sprockets/autoload/ejs.rb +8 -0
  15. data/lib/sprockets/autoload/jsminc.rb +8 -0
  16. data/lib/sprockets/autoload/sass.rb +8 -0
  17. data/lib/sprockets/autoload/sassc.rb +8 -0
  18. data/lib/sprockets/autoload/uglifier.rb +8 -0
  19. data/lib/sprockets/autoload/yui.rb +8 -0
  20. data/lib/sprockets/autoload/zopfli.rb +7 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +147 -0
  23. data/lib/sprockets/bower.rb +61 -0
  24. data/lib/sprockets/bundle.rb +105 -0
  25. data/lib/sprockets/cache.rb +271 -0
  26. data/lib/sprockets/cache/file_store.rb +208 -0
  27. data/lib/sprockets/cache/memory_store.rb +75 -0
  28. data/lib/sprockets/cache/null_store.rb +54 -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 +304 -0
  35. data/lib/sprockets/dependencies.rb +74 -0
  36. data/lib/sprockets/digest_utils.rb +200 -0
  37. data/lib/sprockets/directive_processor.rb +414 -0
  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 +262 -0
  41. data/lib/sprockets/environment.rb +46 -0
  42. data/lib/sprockets/erb_processor.rb +37 -0
  43. data/lib/sprockets/errors.rb +12 -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 +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 +50 -0
  53. data/lib/sprockets/loader.rb +345 -0
  54. data/lib/sprockets/manifest.rb +338 -0
  55. data/lib/sprockets/manifest_utils.rb +48 -0
  56. data/lib/sprockets/mime.rb +96 -0
  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 +82 -0
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +228 -0
  64. data/lib/sprockets/processor_utils.rb +169 -0
  65. data/lib/sprockets/resolve.rb +295 -0
  66. data/lib/sprockets/sass_cache_store.rb +30 -0
  67. data/lib/sprockets/sass_compressor.rb +63 -0
  68. data/lib/sprockets/sass_functions.rb +3 -0
  69. data/lib/sprockets/sass_importer.rb +3 -0
  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 +295 -0
  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 +191 -0
  81. data/lib/sprockets/utils.rb +202 -0
  82. data/lib/sprockets/utils/gzip.rb +99 -0
  83. data/lib/sprockets/version.rb +4 -0
  84. data/lib/sprockets/yui_compressor.rb +56 -0
  85. metadata +444 -0
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+
4
+ module Sprockets
5
+ # Processor engine class for the Eco compiler. Depends on the `eco` gem.
6
+ #
7
+ # For more infomation see:
8
+ #
9
+ # https://github.com/sstephenson/ruby-eco
10
+ # https://github.com/sstephenson/eco
11
+ #
12
+ module EcoProcessor
13
+ VERSION = '1'
14
+
15
+ def self.cache_key
16
+ @cache_key ||= "#{name}:#{Autoload::Eco::Source::VERSION}:#{VERSION}".freeze
17
+ end
18
+
19
+ # Compile template data with Eco compiler.
20
+ #
21
+ # Returns a JS function definition String. The result should be
22
+ # assigned to a JS variable.
23
+ #
24
+ # # => "function(...) {...}"
25
+ #
26
+ def self.call(input)
27
+ data = input[:data]
28
+ input[:cache].fetch([cache_key, data]) do
29
+ Autoload::Eco.compile(data)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+
4
+ module Sprockets
5
+ # Processor engine class for the EJS compiler. Depends on the `ejs` gem.
6
+ #
7
+ # For more infomation see:
8
+ #
9
+ # https://github.com/sstephenson/ruby-ejs
10
+ #
11
+ module EjsProcessor
12
+ VERSION = '1'
13
+
14
+ def self.cache_key
15
+ @cache_key ||= "#{name}:#{VERSION}".freeze
16
+ end
17
+
18
+ # Compile template data with EJS compiler.
19
+ #
20
+ # Returns a JS function definition String. The result should be
21
+ # assigned to a JS variable.
22
+ #
23
+ # # => "function(obj){...}"
24
+ #
25
+ def self.call(input)
26
+ data = input[:data]
27
+ input[:cache].fetch([cache_key, data]) do
28
+ Autoload::EJS.compile(data)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/base'
3
+ require 'sprockets/cache/memory_store'
4
+ require 'sprockets/cached_environment'
5
+
6
+ module Sprockets
7
+ class Environment < Base
8
+ # `Environment` should be initialized with your application's root
9
+ # directory. This should be the same as your Rails or Rack root.
10
+ #
11
+ # env = Environment.new(Rails.root)
12
+ #
13
+ def initialize(root = ".")
14
+ initialize_configuration(Sprockets)
15
+ self.root = root
16
+ self.cache = Cache::MemoryStore.new
17
+ yield self if block_given?
18
+ end
19
+
20
+ # Returns a cached version of the environment.
21
+ #
22
+ # All of its file system calls are cached which makes `cached` much
23
+ # faster. This behavior is ideal in production since the file
24
+ # system only changes between deploys.
25
+ def cached
26
+ CachedEnvironment.new(self)
27
+ end
28
+ alias_method :index, :cached
29
+
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
37
+
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
45
+ end
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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # Define some basic Sprockets error classes
3
+ module Sprockets
4
+ class Error < StandardError; end
5
+ class ArgumentError < Error; end
6
+ class ContentTypeMismatch < Error; 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
12
+ end
@@ -0,0 +1,71 @@
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