sprockets 2.3.2 → 3.0.0

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 (84) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +2 -2
  3. data/README.md +332 -115
  4. data/bin/sprockets +8 -0
  5. data/lib/rake/sprocketstask.rb +25 -13
  6. data/lib/sprockets/asset.rb +143 -205
  7. data/lib/sprockets/autoload/closure.rb +7 -0
  8. data/lib/sprockets/autoload/coffee_script.rb +7 -0
  9. data/lib/sprockets/autoload/eco.rb +7 -0
  10. data/lib/sprockets/autoload/ejs.rb +7 -0
  11. data/lib/sprockets/autoload/sass.rb +7 -0
  12. data/lib/sprockets/autoload/uglifier.rb +7 -0
  13. data/lib/sprockets/autoload/yui.rb +7 -0
  14. data/lib/sprockets/autoload.rb +11 -0
  15. data/lib/sprockets/base.rb +49 -257
  16. data/lib/sprockets/bower.rb +58 -0
  17. data/lib/sprockets/bundle.rb +65 -0
  18. data/lib/sprockets/cache/file_store.rb +165 -14
  19. data/lib/sprockets/cache/memory_store.rb +66 -0
  20. data/lib/sprockets/cache/null_store.rb +46 -0
  21. data/lib/sprockets/cache.rb +234 -0
  22. data/lib/sprockets/cached_environment.rb +69 -0
  23. data/lib/sprockets/closure_compressor.rb +53 -0
  24. data/lib/sprockets/coffee_script_processor.rb +25 -0
  25. data/lib/sprockets/coffee_script_template.rb +6 -0
  26. data/lib/sprockets/compressing.rb +74 -0
  27. data/lib/sprockets/configuration.rb +83 -0
  28. data/lib/sprockets/context.rb +125 -131
  29. data/lib/sprockets/dependencies.rb +73 -0
  30. data/lib/sprockets/digest_utils.rb +156 -0
  31. data/lib/sprockets/directive_processor.rb +209 -211
  32. data/lib/sprockets/eco_processor.rb +32 -0
  33. data/lib/sprockets/eco_template.rb +3 -35
  34. data/lib/sprockets/ejs_processor.rb +31 -0
  35. data/lib/sprockets/ejs_template.rb +3 -34
  36. data/lib/sprockets/encoding_utils.rb +258 -0
  37. data/lib/sprockets/engines.rb +45 -38
  38. data/lib/sprockets/environment.rb +17 -67
  39. data/lib/sprockets/erb_processor.rb +30 -0
  40. data/lib/sprockets/erb_template.rb +6 -0
  41. data/lib/sprockets/errors.rb +6 -13
  42. data/lib/sprockets/file_reader.rb +15 -0
  43. data/lib/sprockets/http_utils.rb +115 -0
  44. data/lib/sprockets/jst_processor.rb +35 -19
  45. data/lib/sprockets/legacy.rb +314 -0
  46. data/lib/sprockets/legacy_proc_processor.rb +35 -0
  47. data/lib/sprockets/legacy_tilt_processor.rb +29 -0
  48. data/lib/sprockets/loader.rb +176 -0
  49. data/lib/sprockets/manifest.rb +179 -98
  50. data/lib/sprockets/manifest_utils.rb +45 -0
  51. data/lib/sprockets/mime.rb +114 -32
  52. data/lib/sprockets/path_dependency_utils.rb +85 -0
  53. data/lib/sprockets/path_digest_utils.rb +47 -0
  54. data/lib/sprockets/path_utils.rb +282 -0
  55. data/lib/sprockets/paths.rb +81 -0
  56. data/lib/sprockets/processing.rb +157 -189
  57. data/lib/sprockets/processor_utils.rb +103 -0
  58. data/lib/sprockets/resolve.rb +208 -0
  59. data/lib/sprockets/sass_cache_store.rb +19 -15
  60. data/lib/sprockets/sass_compressor.rb +59 -0
  61. data/lib/sprockets/sass_functions.rb +2 -0
  62. data/lib/sprockets/sass_importer.rb +2 -29
  63. data/lib/sprockets/sass_processor.rb +285 -0
  64. data/lib/sprockets/sass_template.rb +4 -44
  65. data/lib/sprockets/server.rb +109 -84
  66. data/lib/sprockets/transformers.rb +145 -0
  67. data/lib/sprockets/uglifier_compressor.rb +63 -0
  68. data/lib/sprockets/uri_utils.rb +190 -0
  69. data/lib/sprockets/utils.rb +193 -44
  70. data/lib/sprockets/version.rb +1 -1
  71. data/lib/sprockets/yui_compressor.rb +65 -0
  72. data/lib/sprockets.rb +144 -53
  73. metadata +248 -238
  74. data/lib/sprockets/asset_attributes.rb +0 -126
  75. data/lib/sprockets/bundled_asset.rb +0 -79
  76. data/lib/sprockets/caching.rb +0 -96
  77. data/lib/sprockets/charset_normalizer.rb +0 -41
  78. data/lib/sprockets/index.rb +0 -99
  79. data/lib/sprockets/processed_asset.rb +0 -152
  80. data/lib/sprockets/processor.rb +0 -32
  81. data/lib/sprockets/safety_colons.rb +0 -28
  82. data/lib/sprockets/scss_template.rb +0 -13
  83. data/lib/sprockets/static_asset.rb +0 -57
  84. data/lib/sprockets/trail.rb +0 -90
@@ -0,0 +1,115 @@
1
+ module Sprockets
2
+ # Internal: HTTP URI utilities. Many adapted from Rack::Utils. Mixed into
3
+ # Environment.
4
+ module HTTPUtils
5
+ extend self
6
+
7
+ # Public: Test mime type against mime range.
8
+ #
9
+ # match_mime_type?('text/html', 'text/*') => true
10
+ # match_mime_type?('text/plain', '*') => true
11
+ # match_mime_type?('text/html', 'application/json') => false
12
+ #
13
+ # Returns true if the given value is a mime match for the given mime match
14
+ # specification, false otherwise.
15
+ def match_mime_type?(value, matcher)
16
+ v1, v2 = value.split('/', 2)
17
+ m1, m2 = matcher.split('/', 2)
18
+ (m1 == '*' || v1 == m1) && (m2.nil? || m2 == '*' || m2 == v2)
19
+ end
20
+
21
+ # Public: Return values from Hash where the key matches the mime type.
22
+ #
23
+ # hash - Hash of String matcher keys to Object values
24
+ # mime_type - String mime type
25
+ #
26
+ # Returns Array of Object values.
27
+ def match_mime_type_keys(hash, mime_type)
28
+ type, subtype = mime_type.split('/', 2)
29
+ [
30
+ hash["*"],
31
+ hash["*/*"],
32
+ hash["#{type}/*"],
33
+ hash["#{type}/#{subtype}"]
34
+ ].compact
35
+ end
36
+
37
+ # Internal: Parse Accept header quality values.
38
+ #
39
+ # Adapted from Rack::Utils#q_values.
40
+ #
41
+ # Returns an Array of [String, Float].
42
+ def parse_q_values(values)
43
+ values.to_s.split(/\s*,\s*/).map do |part|
44
+ value, parameters = part.split(/\s*;\s*/, 2)
45
+ quality = 1.0
46
+ if md = /\Aq=([\d.]+)/.match(parameters)
47
+ quality = md[1].to_f
48
+ end
49
+ [value, quality]
50
+ end
51
+ end
52
+
53
+ # Internal: Find all qvalue matches from an Array of available options.
54
+ #
55
+ # Adapted from Rack::Utils#q_values.
56
+ #
57
+ # Returns Array of matched Strings from available Array or [].
58
+ def find_q_matches(q_values, available, &matcher)
59
+ matcher ||= lambda { |a, b| a == b }
60
+
61
+ matches = []
62
+
63
+ case q_values
64
+ when Array
65
+ when String
66
+ q_values = parse_q_values(q_values)
67
+ when NilClass
68
+ q_values = []
69
+ else
70
+ raise TypeError, "unknown q_values type: #{q_values.class}"
71
+ end
72
+
73
+ q_values.each do |accepted, quality|
74
+ if match = available.find { |option| matcher.call(option, accepted) }
75
+ matches << [match, quality]
76
+ end
77
+ end
78
+
79
+ matches.sort_by { |match, quality| -quality }.map { |match, quality| match }
80
+ end
81
+
82
+ # Internal: Find the best qvalue match from an Array of available options.
83
+ #
84
+ # Adapted from Rack::Utils#q_values.
85
+ #
86
+ # Returns the matched String from available Array or nil.
87
+ def find_best_q_match(q_values, available, &matcher)
88
+ find_q_matches(q_values, available, &matcher).first
89
+ end
90
+
91
+ # Internal: Find the all qvalue match from an Array of available mime type
92
+ # options.
93
+ #
94
+ # Adapted from Rack::Utils#q_values.
95
+ #
96
+ # Returns Array of matched mime type Strings from available Array or [].
97
+ def find_mime_type_matches(q_value_header, available)
98
+ find_q_matches(q_value_header, available) do |a, b|
99
+ match_mime_type?(a, b)
100
+ end
101
+ end
102
+
103
+ # Internal: Find the best qvalue match from an Array of available mime type
104
+ # options.
105
+ #
106
+ # Adapted from Rack::Utils#q_values.
107
+ #
108
+ # Returns the matched mime type String from available Array or nil.
109
+ def find_best_mime_type_match(q_value_header, available)
110
+ find_best_q_match(q_value_header, available) do |a, b|
111
+ match_mime_type?(a, b)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -1,33 +1,49 @@
1
- require 'tilt'
2
-
3
1
  module Sprockets
4
- class JstProcessor < Tilt::Template
5
- def self.default_mime_type
6
- 'application/javascript'
7
- end
8
-
2
+ # Public: .jst engine.
3
+ #
4
+ # Exports server side compiled templates to an object.
5
+ #
6
+ # Name your template "users/show.jst.ejs", "users/new.jst.eco", etc.
7
+ #
8
+ # To accept the default options
9
+ #
10
+ # environment.register_engine '.jst',
11
+ # JstProcessor,
12
+ # mime_type: 'application/javascript'
13
+ #
14
+ # Change the default namespace.
15
+ #
16
+ # environment.register_engine '.jst',
17
+ # JstProcessor.new(namespace: 'App.templates'),
18
+ # mime_type: 'application/javascript'
19
+ #
20
+ class JstProcessor
9
21
  def self.default_namespace
10
22
  'this.JST'
11
23
  end
12
24
 
13
- def prepare
14
- @namespace = self.class.default_namespace
25
+ # Public: Return singleton instance with default options.
26
+ #
27
+ # Returns JstProcessor object.
28
+ def self.instance
29
+ @instance ||= new
15
30
  end
16
31
 
17
- attr_reader :namespace
32
+ def self.call(input)
33
+ instance.call(input)
34
+ end
18
35
 
19
- def evaluate(scope, locals, &block)
36
+ def initialize(options = {})
37
+ @namespace = options[:namespace] || self.class.default_namespace
38
+ end
39
+
40
+ def call(input)
41
+ data = input[:data].gsub(/$(.)/m, "\\1 ").strip
42
+ key = input[:name]
20
43
  <<-JST
21
- (function() {
22
- #{namespace} || (#{namespace} = {});
23
- #{namespace}[#{scope.logical_path.inspect}] = #{indent(data)};
44
+ (function() { #{@namespace} || (#{@namespace} = {}); #{@namespace}[#{key.inspect}] = #{data};
24
45
  }).call(this);
25
46
  JST
26
47
  end
27
-
28
- private
29
- def indent(string)
30
- string.gsub(/$(.)/m, "\\1 ").strip
31
- end
32
48
  end
33
49
  end
@@ -0,0 +1,314 @@
1
+ require 'pathname'
2
+ require 'sprockets/asset'
3
+ require 'sprockets/base'
4
+ require 'sprockets/cached_environment'
5
+ require 'sprockets/context'
6
+ require 'sprockets/manifest'
7
+ require 'sprockets/resolve'
8
+
9
+ module Sprockets
10
+ autoload :CoffeeScriptTemplate, 'sprockets/coffee_script_template'
11
+ autoload :EcoTemplate, 'sprockets/eco_template'
12
+ autoload :EjsTemplate, 'sprockets/ejs_template'
13
+ autoload :ERBTemplate, 'sprockets/erb_template'
14
+ autoload :SassTemplate, 'sprockets/sass_template'
15
+ autoload :ScssTemplate, 'sprockets/sass_template'
16
+
17
+ # Deprecated
18
+ Index = CachedEnvironment
19
+
20
+ class Base
21
+ include Resolve
22
+
23
+ # Deprecated: Change default return type of resolve() to return 2.x
24
+ # compatible plain filename String. 4.x will always return an Asset URI
25
+ # and a set of file system dependencies that had to be read to compute the
26
+ # result.
27
+ #
28
+ # 2.x
29
+ #
30
+ # resolve("foo.js")
31
+ # # => "/path/to/app/javascripts/foo.js"
32
+ #
33
+ # 3.x
34
+ #
35
+ # resolve("foo.js")
36
+ # # => "/path/to/app/javascripts/foo.js"
37
+ #
38
+ # resolve("foo.js", compat: true)
39
+ # # => "/path/to/app/javascripts/foo.js"
40
+ #
41
+ # resolve("foo.js", compat: false)
42
+ # # => [
43
+ # # "file:///path/to/app/javascripts/foo.js?type=application/javascript"
44
+ # # #<Set: {"file-digest:/path/to/app/javascripts/foo.js"}>
45
+ # # ]
46
+ #
47
+ # 4.x
48
+ #
49
+ # resolve("foo.js")
50
+ # # => [
51
+ # # "file:///path/to/app/javascripts/foo.js?type=application/javascript"
52
+ # # #<Set: {"file-digest:/path/to/app/javascripts/foo.js"}>
53
+ # # ]
54
+ #
55
+ def resolve_with_compat(path, options = {})
56
+ options = options.dup
57
+ if options.delete(:compat) { true }
58
+ uri, _ = resolve_without_compat(path, options)
59
+ if uri
60
+ path, _ = parse_asset_uri(uri)
61
+ path
62
+ else
63
+ nil
64
+ end
65
+ else
66
+ resolve_without_compat(path, options)
67
+ end
68
+ end
69
+ alias_method :resolve_without_compat, :resolve
70
+ alias_method :resolve, :resolve_with_compat
71
+
72
+ # Deprecated: Iterate over all logical paths with a matcher.
73
+ #
74
+ # Remove from 4.x.
75
+ #
76
+ # args - List of matcher objects.
77
+ #
78
+ # Returns Enumerator if no block is given.
79
+ def each_logical_path(*args, &block)
80
+ return to_enum(__method__, *args) unless block_given?
81
+
82
+ filters = args.flatten.map { |arg| Manifest.compile_match_filter(arg) }
83
+ logical_paths.each do |a, b|
84
+ if filters.any? { |f| f.call(a, b) }
85
+ if block.arity == 2
86
+ yield a, b
87
+ else
88
+ yield a
89
+ end
90
+ end
91
+ end
92
+
93
+ nil
94
+ end
95
+
96
+ # Deprecated: Enumerate over all logical paths in the environment.
97
+ #
98
+ # Returns an Enumerator of [logical_path, filename].
99
+ def logical_paths
100
+ return to_enum(__method__) unless block_given?
101
+
102
+ seen = Set.new
103
+
104
+ paths.each do |load_path|
105
+ stat_tree(load_path).each do |filename, stat|
106
+ next unless stat.file?
107
+
108
+ path = split_subpath(load_path, filename)
109
+ path, mime_type, _, _ = parse_path_extnames(path)
110
+ path = normalize_logical_path(path)
111
+ path += mime_types[mime_type][:extensions].first if mime_type
112
+
113
+ if !seen.include?(path)
114
+ yield path, filename
115
+ seen << path
116
+ end
117
+ end
118
+ end
119
+
120
+ nil
121
+ end
122
+
123
+ def cache_get(key)
124
+ cache.get(key)
125
+ end
126
+
127
+ def cache_set(key, value)
128
+ cache.set(key, value)
129
+ end
130
+
131
+ private
132
+ # Deprecated: Seriously.
133
+ def matches_filter(filters, logical_path, filename)
134
+ return true if filters.empty?
135
+
136
+ filters.any? do |filter|
137
+ if filter.is_a?(Regexp)
138
+ filter.match(logical_path)
139
+ elsif filter.respond_to?(:call)
140
+ if filter.arity == 1
141
+ filter.call(logical_path)
142
+ else
143
+ filter.call(logical_path, filename.to_s)
144
+ end
145
+ else
146
+ File.fnmatch(filter.to_s, logical_path)
147
+ end
148
+ end
149
+ end
150
+
151
+ # URI.unescape is deprecated on 1.9. We need to use URI::Parser
152
+ # if its available.
153
+ if defined? URI::DEFAULT_PARSER
154
+ def unescape(str)
155
+ str = URI::DEFAULT_PARSER.unescape(str)
156
+ str.force_encoding(Encoding.default_internal) if Encoding.default_internal
157
+ str
158
+ end
159
+ else
160
+ def unescape(str)
161
+ URI.unescape(str)
162
+ end
163
+ end
164
+ end
165
+
166
+ class Asset
167
+ # Deprecated: Use #filename instead.
168
+ #
169
+ # Returns Pathname.
170
+ def pathname
171
+ @pathname ||= Pathname.new(filename)
172
+ end
173
+
174
+ # Deprecated: Expand asset into an `Array` of parts.
175
+ #
176
+ # Appending all of an assets body parts together should give you
177
+ # the asset's contents as a whole.
178
+ #
179
+ # This allows you to link to individual files for debugging
180
+ # purposes.
181
+ #
182
+ # Use Asset#included instead. Keeping a full copy of the bundle's processed
183
+ # assets in memory (and in cache) is expensive and redundant. The common use
184
+ # case is to relink to the assets anyway.
185
+ #
186
+ # Returns Array of Assets.
187
+ def to_a
188
+ if metadata[:included]
189
+ metadata[:included].map { |uri| @environment.load(uri) }
190
+ else
191
+ [self]
192
+ end
193
+ end
194
+
195
+ # Deprecated: Get all required Assets.
196
+ #
197
+ # See Asset#to_a
198
+ #
199
+ # Returns Array of Assets.
200
+ def dependencies
201
+ to_a.reject { |a| a.filename.eql?(self.filename) }
202
+ end
203
+
204
+ # Deprecated: Returns Time of the last time the source was modified.
205
+ #
206
+ # Time resolution is normalized to the nearest second.
207
+ #
208
+ # Returns Time.
209
+ def mtime
210
+ Time.at(@mtime)
211
+ end
212
+ end
213
+
214
+ class Context
215
+ # Deprecated: Change default return type of resolve() to return 2.x
216
+ # compatible plain filename String. 4.x will always return an Asset URI.
217
+ #
218
+ # 2.x
219
+ #
220
+ # resolve("foo.js")
221
+ # # => "/path/to/app/javascripts/foo.js"
222
+ #
223
+ # 3.x
224
+ #
225
+ # resolve("foo.js")
226
+ # # => "/path/to/app/javascripts/foo.js"
227
+ #
228
+ # resolve("foo.js", compat: true)
229
+ # # => "/path/to/app/javascripts/foo.js"
230
+ #
231
+ # resolve("foo.js", compat: false)
232
+ # # => "file:///path/to/app/javascripts/foo.js?type=application/javascript"
233
+ #
234
+ # 4.x
235
+ #
236
+ # resolve("foo.js")
237
+ # # => "file:///path/to/app/javascripts/foo.js?type=application/javascript"
238
+ #
239
+ def resolve_with_compat(path, options = {})
240
+ options = options.dup
241
+
242
+ # Support old :content_type option, prefer :accept going forward
243
+ if type = options.delete(:content_type)
244
+ type = self.content_type if type == :self
245
+ options[:accept] ||= type
246
+ end
247
+
248
+ if options.delete(:compat) { true }
249
+ uri = resolve_without_compat(path, options)
250
+ path, _ = environment.parse_asset_uri(uri)
251
+ path
252
+ else
253
+ resolve_without_compat(path, options)
254
+ end
255
+ end
256
+ alias_method :resolve_without_compat, :resolve
257
+ alias_method :resolve, :resolve_with_compat
258
+ end
259
+
260
+ class Manifest
261
+ # Deprecated: Compile logical path matching filter into a proc that can be
262
+ # passed to logical_paths.select(&proc).
263
+ #
264
+ # compile_match_filter(proc { |logical_path|
265
+ # File.extname(logical_path) == '.js'
266
+ # })
267
+ #
268
+ # compile_match_filter(/application.js/)
269
+ #
270
+ # compile_match_filter("foo/*.js")
271
+ #
272
+ # Returns a Proc or raise a TypeError.
273
+ def self.compile_match_filter(filter)
274
+ # If the filter is already a proc, great nothing to do.
275
+ if filter.respond_to?(:call)
276
+ filter
277
+ # If the filter is a regexp, wrap it in a proc that tests it against the
278
+ # logical path.
279
+ elsif filter.is_a?(Regexp)
280
+ proc { |logical_path| filter.match(logical_path) }
281
+ elsif filter.is_a?(String)
282
+ # If its an absolute path, detect the matching full filename
283
+ if PathUtils.absolute_path?(filter)
284
+ proc { |logical_path, filename| filename == filter.to_s }
285
+ else
286
+ # Otherwise do an fnmatch against the logical path.
287
+ proc { |logical_path| File.fnmatch(filter.to_s, logical_path) }
288
+ end
289
+ else
290
+ raise TypeError, "unknown filter type: #{filter.inspect}"
291
+ end
292
+ end
293
+
294
+ def self.simple_logical_path?(str)
295
+ str.is_a?(String) &&
296
+ !PathUtils.absolute_path?(str) &&
297
+ str !~ /\*|\*\*|\?|\[|\]|\{|\}/
298
+ end
299
+
300
+ # Deprecated: Filter logical paths in environment. Useful for selecting what
301
+ # files you want to compile.
302
+ #
303
+ # Returns an Enumerator.
304
+ def filter_logical_paths(*args)
305
+ filters = args.flatten.map { |arg| self.class.compile_match_filter(arg) }
306
+ environment.cached.logical_paths.select do |a, b|
307
+ filters.any? { |f| f.call(a, b) }
308
+ end
309
+ end
310
+
311
+ # Deprecated alias.
312
+ alias_method :find_logical_paths, :filter_logical_paths
313
+ end
314
+ end
@@ -0,0 +1,35 @@
1
+ require 'delegate'
2
+
3
+ module Sprockets
4
+ # Deprecated: Wraps legacy process Procs with new processor call signature.
5
+ #
6
+ # Will be removed in Sprockets 4.x.
7
+ #
8
+ # LegacyProcProcessor.new(:compress,
9
+ # proc { |context, data| data.gsub(...) })
10
+ #
11
+ class LegacyProcProcessor < Delegator
12
+ def initialize(name, proc)
13
+ @name = name
14
+ @proc = proc
15
+ end
16
+
17
+ def __getobj__
18
+ @proc
19
+ end
20
+
21
+ def name
22
+ "Sprockets::LegacyProcProcessor (#{@name})"
23
+ end
24
+
25
+ def to_s
26
+ name
27
+ end
28
+
29
+ def call(input)
30
+ context = input[:environment].context_class.new(input)
31
+ data = @proc.call(context, input[:data])
32
+ context.metadata.merge(data: data)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ require 'delegate'
2
+
3
+ module Sprockets
4
+ # Deprecated: Wraps legacy engine and process Tilt templates with new
5
+ # processor call signature.
6
+ #
7
+ # Will be removed in Sprockets 4.x.
8
+ #
9
+ # LegacyTiltProcessor.new(Tilt::CoffeeScriptProcessor)
10
+ #
11
+ class LegacyTiltProcessor < Delegator
12
+ def initialize(klass)
13
+ @klass = klass
14
+ end
15
+
16
+ def __getobj__
17
+ @klass
18
+ end
19
+
20
+ def call(input)
21
+ filename = input[:filename]
22
+ data = input[:data]
23
+ context = input[:environment].context_class.new(input)
24
+
25
+ data = @klass.new(filename) { data }.render(context)
26
+ context.metadata.merge(data: data)
27
+ end
28
+ end
29
+ end