sprockets 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
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