sprockets 2.12.5 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sprockets might be problematic. Click here for more details.

Files changed (62) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +2 -2
  3. data/README.md +61 -34
  4. data/lib/rake/sprocketstask.rb +5 -4
  5. data/lib/sprockets.rb +123 -85
  6. data/lib/sprockets/asset.rb +161 -200
  7. data/lib/sprockets/asset_uri.rb +64 -0
  8. data/lib/sprockets/base.rb +138 -373
  9. data/lib/sprockets/bower.rb +56 -0
  10. data/lib/sprockets/bundle.rb +32 -0
  11. data/lib/sprockets/cache.rb +220 -0
  12. data/lib/sprockets/cache/file_store.rb +145 -13
  13. data/lib/sprockets/cache/memory_store.rb +66 -0
  14. data/lib/sprockets/cache/null_store.rb +46 -0
  15. data/lib/sprockets/cached_environment.rb +103 -0
  16. data/lib/sprockets/closure_compressor.rb +30 -12
  17. data/lib/sprockets/coffee_script_template.rb +23 -0
  18. data/lib/sprockets/compressing.rb +20 -25
  19. data/lib/sprockets/configuration.rb +95 -0
  20. data/lib/sprockets/context.rb +68 -131
  21. data/lib/sprockets/directive_processor.rb +138 -179
  22. data/lib/sprockets/eco_template.rb +10 -19
  23. data/lib/sprockets/ejs_template.rb +10 -19
  24. data/lib/sprockets/encoding_utils.rb +246 -0
  25. data/lib/sprockets/engines.rb +40 -29
  26. data/lib/sprockets/environment.rb +10 -66
  27. data/lib/sprockets/erb_template.rb +23 -0
  28. data/lib/sprockets/errors.rb +5 -13
  29. data/lib/sprockets/http_utils.rb +97 -0
  30. data/lib/sprockets/jst_processor.rb +28 -15
  31. data/lib/sprockets/lazy_processor.rb +15 -0
  32. data/lib/sprockets/legacy.rb +23 -0
  33. data/lib/sprockets/legacy_proc_processor.rb +35 -0
  34. data/lib/sprockets/legacy_tilt_processor.rb +29 -0
  35. data/lib/sprockets/manifest.rb +128 -99
  36. data/lib/sprockets/mime.rb +114 -33
  37. data/lib/sprockets/path_utils.rb +179 -0
  38. data/lib/sprockets/paths.rb +13 -26
  39. data/lib/sprockets/processing.rb +198 -107
  40. data/lib/sprockets/resolve.rb +289 -0
  41. data/lib/sprockets/sass_compressor.rb +36 -17
  42. data/lib/sprockets/sass_template.rb +269 -46
  43. data/lib/sprockets/server.rb +113 -83
  44. data/lib/sprockets/transformers.rb +69 -0
  45. data/lib/sprockets/uglifier_compressor.rb +36 -15
  46. data/lib/sprockets/utils.rb +161 -44
  47. data/lib/sprockets/version.rb +1 -1
  48. data/lib/sprockets/yui_compressor.rb +37 -12
  49. metadata +64 -106
  50. data/lib/sprockets/asset_attributes.rb +0 -137
  51. data/lib/sprockets/bundled_asset.rb +0 -78
  52. data/lib/sprockets/caching.rb +0 -96
  53. data/lib/sprockets/charset_normalizer.rb +0 -41
  54. data/lib/sprockets/index.rb +0 -100
  55. data/lib/sprockets/processed_asset.rb +0 -152
  56. data/lib/sprockets/processor.rb +0 -32
  57. data/lib/sprockets/safety_colons.rb +0 -28
  58. data/lib/sprockets/sass_cache_store.rb +0 -29
  59. data/lib/sprockets/sass_functions.rb +0 -70
  60. data/lib/sprockets/sass_importer.rb +0 -30
  61. data/lib/sprockets/scss_template.rb +0 -13
  62. data/lib/sprockets/static_asset.rb +0 -60
@@ -1,28 +1,15 @@
1
- require 'tilt'
1
+ require 'eco'
2
2
 
3
3
  module Sprockets
4
- # Tilt engine class for the Eco compiler. Depends on the `eco` gem.
4
+ # Template engine class for the Eco compiler. Depends on the `eco` gem.
5
5
  #
6
6
  # For more infomation see:
7
7
  #
8
8
  # https://github.com/sstephenson/ruby-eco
9
9
  # https://github.com/sstephenson/eco
10
10
  #
11
- class EcoTemplate < Tilt::Template
12
- # Check to see if Eco is loaded
13
- def self.engine_initialized?
14
- defined? ::Eco
15
- end
16
-
17
- # Autoload eco library. If the library isn't loaded, Tilt will produce
18
- # a thread safetly warning. If you intend to use `.eco` files, you
19
- # should explicitly require it.
20
- def initialize_engine
21
- require_template_library 'eco'
22
- end
23
-
24
- def prepare
25
- end
11
+ module EcoTemplate
12
+ VERSION = '1'
26
13
 
27
14
  # Compile template data with Eco compiler.
28
15
  #
@@ -31,8 +18,12 @@ module Sprockets
31
18
  #
32
19
  # # => "function(...) {...}"
33
20
  #
34
- def evaluate(scope, locals, &block)
35
- Eco.compile(data)
21
+ def self.call(input)
22
+ data = input[:data]
23
+ key = ['EcoTemplate', ::Eco::Source::VERSION, VERSION, data]
24
+ input[:cache].fetch(key) do
25
+ ::Eco.compile(data)
26
+ end
36
27
  end
37
28
  end
38
29
  end
@@ -1,27 +1,14 @@
1
- require 'tilt'
1
+ require 'ejs'
2
2
 
3
3
  module Sprockets
4
- # Tilt engine class for the EJS compiler. Depends on the `ejs` gem.
4
+ # Template engine class for the EJS compiler. Depends on the `ejs` gem.
5
5
  #
6
6
  # For more infomation see:
7
7
  #
8
8
  # https://github.com/sstephenson/ruby-ejs
9
9
  #
10
- class EjsTemplate < Tilt::Template
11
- # Check to see if EJS is loaded
12
- def self.engine_initialized?
13
- defined? ::EJS
14
- end
15
-
16
- # Autoload ejs library. If the library isn't loaded, Tilt will produce
17
- # a thread safetly warning. If you intend to use `.ejs` files, you
18
- # should explicitly require it.
19
- def initialize_engine
20
- require_template_library 'ejs'
21
- end
22
-
23
- def prepare
24
- end
10
+ module EjsTemplate
11
+ VERSION = '1'
25
12
 
26
13
  # Compile template data with EJS compiler.
27
14
  #
@@ -30,8 +17,12 @@ module Sprockets
30
17
  #
31
18
  # # => "function(obj){...}"
32
19
  #
33
- def evaluate(scope, locals, &block)
34
- EJS.compile(data)
20
+ def self.call(input)
21
+ data = input[:data]
22
+ key = ['EjsTemplate', VERSION, data]
23
+ input[:cache].fetch(key) do
24
+ ::EJS.compile(data)
25
+ end
35
26
  end
36
27
  end
37
28
  end
@@ -0,0 +1,246 @@
1
+ require 'base64'
2
+ require 'stringio'
3
+ require 'zlib'
4
+
5
+ module Sprockets
6
+ module EncodingUtils
7
+ extend self
8
+
9
+ ## Binary encodings ##
10
+
11
+ # Public: Use deflate to compress data.
12
+ #
13
+ # str - String data
14
+ #
15
+ # Returns a compressed String
16
+ def deflate(str)
17
+ deflater = Zlib::Deflate.new(
18
+ Zlib::BEST_COMPRESSION,
19
+ -Zlib::MAX_WBITS,
20
+ Zlib::MAX_MEM_LEVEL,
21
+ Zlib::DEFAULT_STRATEGY
22
+ )
23
+ deflater << str
24
+ deflater.finish
25
+ end
26
+
27
+ # Public: Alias for CodingUtils.deflate
28
+ DEFLATE = method(:deflate)
29
+
30
+ # Public: Use gzip to compress data.
31
+ #
32
+ # str - String data
33
+ #
34
+ # Returns a compressed String
35
+ def gzip(str)
36
+ io = StringIO.new
37
+ gz = Zlib::GzipWriter.new(io, Zlib::BEST_COMPRESSION)
38
+ gz.mtime = 1
39
+ gz << str
40
+ gz.finish
41
+ io.string
42
+ end
43
+
44
+ # Public: Alias for CodingUtils.gzip
45
+ GZIP = method(:gzip)
46
+
47
+ # Public: Use base64 to encode data.
48
+ #
49
+ # str - String data
50
+ #
51
+ # Returns a encoded String
52
+ def base64(str)
53
+ Base64.strict_encode64(str)
54
+ end
55
+
56
+ # Public: Alias for CodingUtils.base64
57
+ BASE64 = method(:base64)
58
+
59
+
60
+ ## Charset encodings ##
61
+
62
+ # Internal: Mapping unicode encodings to byte order markers.
63
+ BOM = {
64
+ Encoding::UTF_32LE => [0xFF, 0xFE, 0x00, 0x00],
65
+ Encoding::UTF_32BE => [0x00, 0x00, 0xFE, 0xFF],
66
+ Encoding::UTF_8 => [0xEF, 0xBB, 0xBF],
67
+ Encoding::UTF_16LE => [0xFF, 0xFE],
68
+ Encoding::UTF_16BE => [0xFE, 0xFF]
69
+ }
70
+
71
+ # Public: Basic string detecter.
72
+ #
73
+ # Attempts to parse any Unicode BOM otherwise falls back to the
74
+ # environment's external encoding.
75
+ #
76
+ # str - ASCII-8BIT encoded String
77
+ #
78
+ # Returns encoded String.
79
+ def detect(str)
80
+ str = detect_unicode_bom(str)
81
+
82
+ # Attempt Charlock detection
83
+ if str.encoding == Encoding::BINARY
84
+ charlock_detect(str)
85
+ end
86
+
87
+ # Fallback to UTF-8
88
+ if str.encoding == Encoding::BINARY
89
+ str.force_encoding(Encoding.default_external)
90
+ end
91
+
92
+ str
93
+ end
94
+
95
+ # Public: Alias for EncodingUtils.detect_unicode
96
+ DETECT = method(:detect)
97
+
98
+ # Internal: Use Charlock Holmes to detect encoding.
99
+ #
100
+ # To enable this code path, require 'charlock_holmes'
101
+ #
102
+ # Returns encoded String.
103
+ def charlock_detect(str)
104
+ if defined? CharlockHolmes::EncodingDetector
105
+ if detected = CharlockHolmes::EncodingDetector.detect(str)
106
+ str.force_encoding(detected[:encoding]) if detected[:encoding]
107
+ end
108
+ end
109
+
110
+ str
111
+ end
112
+
113
+ # Public: Detect Unicode string.
114
+ #
115
+ # Attempts to parse Unicode BOM and falls back to UTF-8.
116
+ #
117
+ # str - ASCII-8BIT encoded String
118
+ #
119
+ # Returns encoded String.
120
+ def detect_unicode(str)
121
+ str = detect_unicode_bom(str)
122
+
123
+ # Fallback to UTF-8
124
+ if str.encoding == Encoding::BINARY
125
+ str.force_encoding(Encoding::UTF_8)
126
+ end
127
+
128
+ str
129
+ end
130
+
131
+ # Public: Alias for EncodingUtils.detect_unicode
132
+ DETECT_UNICODE = method(:detect_unicode)
133
+
134
+ # Public: Detect and strip BOM from possible unicode string.
135
+ #
136
+ # str - ASCII-8BIT encoded String
137
+ #
138
+ # Returns UTF 8/16/32 encoded String without BOM or the original String if
139
+ # no BOM was present.
140
+ def detect_unicode_bom(str)
141
+ bom_bytes = str.byteslice(0, 4).bytes.to_a
142
+
143
+ BOM.each do |encoding, bytes|
144
+ if bom_bytes[0, bytes.size] == bytes
145
+ str = str.dup
146
+ str.force_encoding(Encoding::BINARY)
147
+ str.slice!(0, bytes.size)
148
+ str.force_encoding(encoding)
149
+ return str
150
+ end
151
+ end
152
+
153
+ return str
154
+ end
155
+
156
+ # Public: Detect and strip @charset from CSS style sheet.
157
+ #
158
+ # str - String.
159
+ #
160
+ # Returns a encoded String.
161
+ def detect_css(str)
162
+ str = detect_unicode_bom(str)
163
+
164
+ if name = scan_css_charset(str)
165
+ encoding = Encoding.find(name)
166
+ str = str.dup
167
+ str.force_encoding(encoding)
168
+ len = "@charset \"#{name}\";".encode(encoding).size
169
+ str.slice!(0, len)
170
+ str
171
+ end
172
+
173
+ # Fallback to UTF-8
174
+ if str.encoding == Encoding::BINARY
175
+ str.force_encoding(Encoding::UTF_8)
176
+ end
177
+
178
+ str
179
+ end
180
+
181
+ # Public: Alias for EncodingUtils.detect_css
182
+ DETECT_CSS = method(:detect_css)
183
+
184
+ # Internal: @charset bytes
185
+ CHARSET_START = [0x40, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x20, 0x22]
186
+ CHARSET_SIZE = CHARSET_START.size
187
+
188
+ # Internal: Scan binary CSS string for @charset encoding name.
189
+ #
190
+ # str - ASCII-8BIT encoded String
191
+ #
192
+ # Returns encoding String name or nil.
193
+ def scan_css_charset(str)
194
+ buf = []
195
+ i = 0
196
+
197
+ str.each_byte.each do |byte|
198
+ # Halt on line breaks
199
+ break if byte == 0x0A || byte == 0x0D
200
+
201
+ # Only ascii bytes
202
+ next unless 0x0 < byte && byte <= 0xFF
203
+
204
+ if i < CHARSET_SIZE
205
+ elsif i == CHARSET_SIZE
206
+ if buf == CHARSET_START
207
+ buf = []
208
+ else
209
+ break
210
+ end
211
+ elsif byte == 0x22
212
+ return buf.pack('C*')
213
+ end
214
+
215
+ buf << byte
216
+ i += 1
217
+ end
218
+
219
+ nil
220
+ end
221
+
222
+ # Public: Detect charset from HTML document. Defaults to ISO-8859-1.
223
+ #
224
+ # str - String.
225
+ #
226
+ # Returns a encoded String.
227
+ def detect_html(str)
228
+ str = detect_unicode_bom(str)
229
+
230
+ # Attempt Charlock detection
231
+ if str.encoding == Encoding::BINARY
232
+ charlock_detect(str)
233
+ end
234
+
235
+ # Fallback to ISO-8859-1
236
+ if str.encoding == Encoding::BINARY
237
+ str.force_encoding(Encoding::ISO_8859_1)
238
+ end
239
+
240
+ str
241
+ end
242
+
243
+ # Public: Alias for EncodingUtils.detect_html
244
+ DETECT_HTML = method(:detect_html)
245
+ end
246
+ end
@@ -1,13 +1,11 @@
1
- require 'sprockets/eco_template'
2
- require 'sprockets/ejs_template'
3
- require 'sprockets/jst_processor'
1
+ require 'sprockets/lazy_processor'
2
+ require 'sprockets/legacy_tilt_processor'
4
3
  require 'sprockets/utils'
5
- require 'tilt'
6
4
 
7
5
  module Sprockets
8
6
  # `Engines` provides a global and `Environment` instance registry.
9
7
  #
10
- # An engine is a type of processor that is bound to an filename
8
+ # An engine is a type of processor that is bound to a filename
11
9
  # extension. `application.js.coffee` indicates that the
12
10
  # `CoffeeScriptTemplate` engine will be ran on the file.
13
11
  #
@@ -15,8 +13,8 @@ module Sprockets
15
13
  # left. `application.js.coffee.erb` will first run `ERBTemplate`
16
14
  # then `CoffeeScriptTemplate`.
17
15
  #
18
- # All `Engine`s must follow the `Tilt::Template` interface. It is
19
- # recommended to subclass `Tilt::Template`.
16
+ # All `Engine`s must follow the `Template` interface. It is
17
+ # recommended to subclass `Template`.
20
18
  #
21
19
  # Its recommended that you register engine changes on your local
22
20
  # `Environment` instance.
@@ -35,24 +33,24 @@ module Sprockets
35
33
  # environment.engines
36
34
  # # => {".coffee" => CoffeeScriptTemplate, ".sass" => SassTemplate, ...}
37
35
  #
38
- # environment.engines('.coffee')
39
- # # => CoffeeScriptTemplate
36
+ attr_reader :engines
37
+
38
+ # Internal: Returns a `Hash` of engine extensions to mime types.
40
39
  #
41
- def engines(ext = nil)
42
- if ext
43
- ext = Sprockets::Utils.normalize_extension(ext)
44
- @engines[ext]
45
- else
46
- @engines.dup
47
- end
48
- end
40
+ # # => { '.coffee' => 'application/javascript' }
41
+ attr_reader :engine_mime_types
49
42
 
50
- # Returns an `Array` of engine extension `String`s.
43
+ # Internal: Find and load engines by extension.
51
44
  #
52
- # environment.engine_extensions
53
- # # => ['.coffee', '.sass', ...]
54
- def engine_extensions
55
- @engines.keys
45
+ # extnames - Array of String extnames
46
+ #
47
+ # Returns Array of Procs.
48
+ def unwrap_engines(extnames)
49
+ extnames.map { |ext|
50
+ engines[ext]
51
+ }.map { |engine|
52
+ unwrap_processor(engine)
53
+ }
56
54
  end
57
55
 
58
56
  # Registers a new Engine `klass` for `ext`. If the `ext` already
@@ -60,15 +58,28 @@ module Sprockets
60
58
  #
61
59
  # environment.register_engine '.coffee', CoffeeScriptTemplate
62
60
  #
63
- def register_engine(ext, klass)
61
+ def register_engine(ext, klass, options = {})
64
62
  ext = Sprockets::Utils.normalize_extension(ext)
65
- @engines[ext] = klass
66
- end
67
63
 
68
- private
69
- def deep_copy_hash(hash)
70
- initial = Hash.new { |h, k| h[k] = [] }
71
- hash.inject(initial) { |h, (k, a)| h[k] = a.dup; h }
64
+ if klass.class == Sprockets::LazyProcessor || klass.respond_to?(:call)
65
+ mutate_config(:engines) do |engines|
66
+ engines.merge(ext => klass)
67
+ end
68
+ if options[:mime_type]
69
+ mutate_config(:engine_mime_types) do |mime_types|
70
+ mime_types.merge(ext.to_s => options[:mime_type])
71
+ end
72
+ end
73
+ else
74
+ mutate_config(:engines) do |engines|
75
+ engines.merge(ext => LegacyTiltProcessor.new(klass))
76
+ end
77
+ if klass.respond_to?(:default_mime_type) && klass.default_mime_type
78
+ mutate_config(:engine_mime_types) do |mime_types|
79
+ mime_types.merge(ext.to_s => klass.default_mime_type)
80
+ end
81
+ end
72
82
  end
83
+ end
73
84
  end
74
85
  end
@@ -1,11 +1,5 @@
1
1
  require 'sprockets/base'
2
- require 'sprockets/context'
3
- require 'sprockets/index'
4
-
5
- require 'hike'
6
- require 'logger'
7
- require 'pathname'
8
- require 'tilt'
2
+ require 'sprockets/cached_environment'
9
3
 
10
4
  module Sprockets
11
5
  class Environment < Base
@@ -15,74 +9,24 @@ module Sprockets
15
9
  # env = Environment.new(Rails.root)
16
10
  #
17
11
  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
- @compressors = Sprockets.compressors
41
-
42
- Sprockets.paths.each do |path|
43
- append_path(path)
44
- end
45
-
46
- @engines.each do |ext, klass|
47
- add_engine_to_trail(ext, klass)
48
- end
49
-
50
- @mime_types.each do |ext, type|
51
- @trail.append_extension(ext)
52
- end
53
-
54
- expire_index!
55
-
12
+ initialize_configuration(Sprockets)
13
+ @root = File.expand_path(root)
14
+ self.cache = Cache::MemoryStore.new
56
15
  yield self if block_given?
57
16
  end
58
17
 
59
18
  # Returns a cached version of the environment.
60
19
  #
61
- # All its file system calls are cached which makes `index` much
20
+ # All its file system calls are cached which makes `cached` much
62
21
  # faster. This behavior is ideal in production since the file
63
22
  # system only changes between deploys.
64
- def index
65
- Index.new(self)
23
+ def cached
24
+ CachedEnvironment.new(self)
66
25
  end
26
+ alias_method :index, :cached
67
27
 
68
- # Cache `find_asset` calls
69
- def find_asset(path, options = {})
70
- options[:bundle] = true unless options.key?(:bundle)
71
-
72
- # Ensure inmemory cached assets are still fresh on every lookup
73
- if (asset = @assets[cache_key_for(path, options)]) && asset.fresh?(self)
74
- asset
75
- elsif asset = index.find_asset(path, options)
76
- # Cache is pushed upstream by Index#find_asset
77
- asset
78
- end
28
+ def find_asset(*args)
29
+ cached.find_asset(*args)
79
30
  end
80
-
81
- protected
82
- def expire_index!
83
- # Clear digest to be recomputed
84
- @digest = nil
85
- @assets = {}
86
- end
87
31
  end
88
32
  end