sprockets 2.12.5 → 3.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +296 -0
- data/LICENSE +2 -2
- data/README.md +235 -262
- data/bin/sprockets +1 -0
- data/lib/rake/sprocketstask.rb +5 -4
- data/lib/sprockets/asset.rb +143 -212
- data/lib/sprockets/autoload/closure.rb +7 -0
- data/lib/sprockets/autoload/coffee_script.rb +7 -0
- data/lib/sprockets/autoload/eco.rb +7 -0
- data/lib/sprockets/autoload/ejs.rb +7 -0
- data/lib/sprockets/autoload/sass.rb +7 -0
- data/lib/sprockets/autoload/uglifier.rb +7 -0
- data/lib/sprockets/autoload/yui.rb +7 -0
- data/lib/sprockets/autoload.rb +11 -0
- data/lib/sprockets/base.rb +56 -393
- data/lib/sprockets/bower.rb +58 -0
- data/lib/sprockets/bundle.rb +69 -0
- data/lib/sprockets/cache/file_store.rb +168 -14
- data/lib/sprockets/cache/memory_store.rb +66 -0
- data/lib/sprockets/cache/null_store.rb +46 -0
- data/lib/sprockets/cache.rb +236 -0
- data/lib/sprockets/cached_environment.rb +69 -0
- data/lib/sprockets/closure_compressor.rb +35 -10
- data/lib/sprockets/coffee_script_processor.rb +25 -0
- data/lib/sprockets/coffee_script_template.rb +17 -0
- data/lib/sprockets/compressing.rb +44 -23
- data/lib/sprockets/configuration.rb +83 -0
- data/lib/sprockets/context.rb +86 -144
- data/lib/sprockets/dependencies.rb +73 -0
- data/lib/sprockets/deprecation.rb +90 -0
- data/lib/sprockets/digest_utils.rb +180 -0
- data/lib/sprockets/directive_processor.rb +207 -211
- data/lib/sprockets/eco_processor.rb +32 -0
- data/lib/sprockets/eco_template.rb +9 -30
- data/lib/sprockets/ejs_processor.rb +31 -0
- data/lib/sprockets/ejs_template.rb +9 -29
- data/lib/sprockets/encoding_utils.rb +261 -0
- data/lib/sprockets/engines.rb +53 -35
- data/lib/sprockets/environment.rb +17 -64
- data/lib/sprockets/erb_processor.rb +30 -0
- data/lib/sprockets/erb_template.rb +11 -0
- data/lib/sprockets/errors.rb +4 -13
- data/lib/sprockets/file_reader.rb +15 -0
- data/lib/sprockets/http_utils.rb +117 -0
- data/lib/sprockets/jst_processor.rb +35 -15
- data/lib/sprockets/legacy.rb +330 -0
- data/lib/sprockets/legacy_proc_processor.rb +35 -0
- data/lib/sprockets/legacy_tilt_processor.rb +29 -0
- data/lib/sprockets/loader.rb +325 -0
- data/lib/sprockets/manifest.rb +202 -127
- data/lib/sprockets/manifest_utils.rb +45 -0
- data/lib/sprockets/mime.rb +112 -31
- data/lib/sprockets/path_dependency_utils.rb +85 -0
- data/lib/sprockets/path_digest_utils.rb +47 -0
- data/lib/sprockets/path_utils.rb +287 -0
- data/lib/sprockets/paths.rb +42 -19
- data/lib/sprockets/processing.rb +178 -126
- data/lib/sprockets/processor_utils.rb +180 -0
- data/lib/sprockets/resolve.rb +211 -0
- data/lib/sprockets/sass_cache_store.rb +22 -17
- data/lib/sprockets/sass_compressor.rb +39 -15
- data/lib/sprockets/sass_functions.rb +2 -70
- data/lib/sprockets/sass_importer.rb +2 -30
- data/lib/sprockets/sass_processor.rb +292 -0
- data/lib/sprockets/sass_template.rb +12 -59
- data/lib/sprockets/server.rb +129 -84
- data/lib/sprockets/transformers.rb +145 -0
- data/lib/sprockets/uglifier_compressor.rb +39 -12
- data/lib/sprockets/unloaded_asset.rb +137 -0
- data/lib/sprockets/uri_tar.rb +98 -0
- data/lib/sprockets/uri_utils.rb +188 -0
- data/lib/sprockets/utils/gzip.rb +67 -0
- data/lib/sprockets/utils.rb +210 -44
- data/lib/sprockets/version.rb +1 -1
- data/lib/sprockets/yui_compressor.rb +39 -11
- data/lib/sprockets.rb +142 -81
- metadata +96 -90
- data/lib/sprockets/asset_attributes.rb +0 -137
- data/lib/sprockets/bundled_asset.rb +0 -78
- data/lib/sprockets/caching.rb +0 -96
- data/lib/sprockets/charset_normalizer.rb +0 -41
- data/lib/sprockets/index.rb +0 -100
- data/lib/sprockets/processed_asset.rb +0 -152
- data/lib/sprockets/processor.rb +0 -32
- data/lib/sprockets/safety_colons.rb +0 -28
- data/lib/sprockets/scss_template.rb +0 -13
- data/lib/sprockets/static_asset.rb +0 -60
data/lib/sprockets/base.rb
CHANGED
@@ -1,89 +1,26 @@
|
|
1
|
-
require 'sprockets/
|
2
|
-
require 'sprockets/
|
3
|
-
require 'sprockets/
|
1
|
+
require 'sprockets/asset'
|
2
|
+
require 'sprockets/bower'
|
3
|
+
require 'sprockets/cache'
|
4
|
+
require 'sprockets/configuration'
|
5
|
+
require 'sprockets/digest_utils'
|
4
6
|
require 'sprockets/errors'
|
5
|
-
require 'sprockets/
|
7
|
+
require 'sprockets/loader'
|
8
|
+
require 'sprockets/path_digest_utils'
|
9
|
+
require 'sprockets/path_dependency_utils'
|
10
|
+
require 'sprockets/path_utils'
|
11
|
+
require 'sprockets/resolve'
|
6
12
|
require 'sprockets/server'
|
7
|
-
require 'sprockets/
|
8
|
-
require '
|
9
|
-
require 'pathname'
|
13
|
+
require 'sprockets/loader'
|
14
|
+
require 'sprockets/uri_tar'
|
10
15
|
|
11
16
|
module Sprockets
|
12
|
-
# `Base` class for `Environment` and `
|
17
|
+
# `Base` class for `Environment` and `Cached`.
|
13
18
|
class Base
|
14
|
-
include
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
attr_reader :digest_class
|
20
|
-
|
21
|
-
# Assign a `Digest` implementation class. This may be any Ruby
|
22
|
-
# `Digest::` implementation such as `Digest::MD5` or
|
23
|
-
# `Digest::SHA1`.
|
24
|
-
#
|
25
|
-
# environment.digest_class = Digest::SHA1
|
26
|
-
#
|
27
|
-
def digest_class=(klass)
|
28
|
-
expire_index!
|
29
|
-
@digest_class = klass
|
30
|
-
end
|
31
|
-
|
32
|
-
# The `Environment#version` is a custom value used for manually
|
33
|
-
# expiring all asset caches.
|
34
|
-
#
|
35
|
-
# Sprockets is able to track most file and directory changes and
|
36
|
-
# will take care of expiring the cache for you. However, its
|
37
|
-
# impossible to know when any custom helpers change that you mix
|
38
|
-
# into the `Context`.
|
39
|
-
#
|
40
|
-
# It would be wise to increment this value anytime you make a
|
41
|
-
# configuration change to the `Environment` object.
|
42
|
-
attr_reader :version
|
43
|
-
|
44
|
-
# Assign an environment version.
|
45
|
-
#
|
46
|
-
# environment.version = '2.0'
|
47
|
-
#
|
48
|
-
def version=(version)
|
49
|
-
expire_index!
|
50
|
-
@version = version
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns a `Digest` instance for the `Environment`.
|
54
|
-
#
|
55
|
-
# This value serves two purposes. If two `Environment`s have the
|
56
|
-
# same digest value they can be treated as equal. This is more
|
57
|
-
# useful for comparing environment states between processes rather
|
58
|
-
# than in the same. Two equal `Environment`s can share the same
|
59
|
-
# cached assets.
|
60
|
-
#
|
61
|
-
# The value also provides a seed digest for all `Asset`
|
62
|
-
# digests. Any change in the environment digest will affect all of
|
63
|
-
# its assets.
|
64
|
-
def digest
|
65
|
-
# Compute the initial digest using the implementation class. The
|
66
|
-
# Sprockets release version and custom environment version are
|
67
|
-
# mixed in. So any new releases will affect all your assets.
|
68
|
-
@digest ||= digest_class.new.update(VERSION).update(version.to_s)
|
69
|
-
|
70
|
-
# Returned a dupped copy so the caller can safely mutate it with `.update`
|
71
|
-
@digest.dup
|
72
|
-
end
|
73
|
-
|
74
|
-
# Get and set `Logger` instance.
|
75
|
-
attr_accessor :logger
|
76
|
-
|
77
|
-
# Get `Context` class.
|
78
|
-
#
|
79
|
-
# This class maybe mutated and mixed in with custom helpers.
|
80
|
-
#
|
81
|
-
# environment.context_class.instance_eval do
|
82
|
-
# include MyHelpers
|
83
|
-
# def asset_url; end
|
84
|
-
# end
|
85
|
-
#
|
86
|
-
attr_reader :context_class
|
19
|
+
include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils
|
20
|
+
include Configuration
|
21
|
+
include Server
|
22
|
+
include Resolve, Loader
|
23
|
+
include Bower
|
87
24
|
|
88
25
|
# Get persistent cache store
|
89
26
|
attr_reader :cache
|
@@ -94,197 +31,57 @@ module Sprockets
|
|
94
31
|
# setters. Either `get(key)`/`set(key, value)`,
|
95
32
|
# `[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
|
96
33
|
def cache=(cache)
|
97
|
-
|
98
|
-
@cache = cache
|
99
|
-
end
|
100
|
-
|
101
|
-
def prepend_path(path)
|
102
|
-
# Overrides the global behavior to expire the index
|
103
|
-
expire_index!
|
104
|
-
super
|
105
|
-
end
|
106
|
-
|
107
|
-
def append_path(path)
|
108
|
-
# Overrides the global behavior to expire the index
|
109
|
-
expire_index!
|
110
|
-
super
|
111
|
-
end
|
112
|
-
|
113
|
-
def clear_paths
|
114
|
-
# Overrides the global behavior to expire the index
|
115
|
-
expire_index!
|
116
|
-
super
|
34
|
+
@cache = Cache.new(cache, logger)
|
117
35
|
end
|
118
36
|
|
119
|
-
#
|
120
|
-
|
121
|
-
#
|
122
|
-
# resolve("application.js")
|
123
|
-
# # => "/path/to/app/javascripts/application.js.coffee"
|
124
|
-
#
|
125
|
-
# A `FileNotFound` exception is raised if the file does not exist.
|
126
|
-
def resolve(logical_path, options = {})
|
127
|
-
# If a block is given, preform an iterable search
|
128
|
-
if block_given?
|
129
|
-
args = attributes_for(logical_path).search_paths + [options]
|
130
|
-
@trail.find(*args) do |path|
|
131
|
-
pathname = Pathname.new(path)
|
132
|
-
if %w( .bower.json bower.json component.json ).include?(pathname.basename.to_s)
|
133
|
-
bower = json_decode(pathname.read)
|
134
|
-
case bower['main']
|
135
|
-
when String
|
136
|
-
yield pathname.dirname.join(bower['main'])
|
137
|
-
when Array
|
138
|
-
extname = File.extname(logical_path)
|
139
|
-
bower['main'].each do |fn|
|
140
|
-
if extname == "" || extname == File.extname(fn)
|
141
|
-
yield pathname.dirname.join(fn)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
else
|
146
|
-
yield pathname
|
147
|
-
end
|
148
|
-
end
|
149
|
-
else
|
150
|
-
resolve(logical_path, options) do |pathname|
|
151
|
-
return pathname
|
152
|
-
end
|
153
|
-
raise FileNotFound, "couldn't find file '#{logical_path}'"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
# Register a new mime type.
|
158
|
-
def register_mime_type(mime_type, ext)
|
159
|
-
# Overrides the global behavior to expire the index
|
160
|
-
expire_index!
|
161
|
-
@trail.append_extension(ext)
|
162
|
-
super
|
163
|
-
end
|
164
|
-
|
165
|
-
# Registers a new Engine `klass` for `ext`.
|
166
|
-
def register_engine(ext, klass)
|
167
|
-
# Overrides the global behavior to expire the index
|
168
|
-
expire_index!
|
169
|
-
add_engine_to_trail(ext, klass)
|
170
|
-
super
|
171
|
-
end
|
172
|
-
|
173
|
-
def register_preprocessor(mime_type, klass, &block)
|
174
|
-
# Overrides the global behavior to expire the index
|
175
|
-
expire_index!
|
176
|
-
super
|
177
|
-
end
|
178
|
-
|
179
|
-
def unregister_preprocessor(mime_type, klass)
|
180
|
-
# Overrides the global behavior to expire the index
|
181
|
-
expire_index!
|
182
|
-
super
|
183
|
-
end
|
184
|
-
|
185
|
-
def register_postprocessor(mime_type, klass, &block)
|
186
|
-
# Overrides the global behavior to expire the index
|
187
|
-
expire_index!
|
188
|
-
super
|
189
|
-
end
|
190
|
-
|
191
|
-
def unregister_postprocessor(mime_type, klass)
|
192
|
-
# Overrides the global behavior to expire the index
|
193
|
-
expire_index!
|
194
|
-
super
|
195
|
-
end
|
196
|
-
|
197
|
-
def register_bundle_processor(mime_type, klass, &block)
|
198
|
-
# Overrides the global behavior to expire the index
|
199
|
-
expire_index!
|
200
|
-
super
|
201
|
-
end
|
202
|
-
|
203
|
-
def unregister_bundle_processor(mime_type, klass)
|
204
|
-
# Overrides the global behavior to expire the index
|
205
|
-
expire_index!
|
206
|
-
super
|
207
|
-
end
|
208
|
-
|
209
|
-
# Return an `Index`. Must be implemented by the subclass.
|
210
|
-
def index
|
37
|
+
# Return an `Cached`. Must be implemented by the subclass.
|
38
|
+
def cached
|
211
39
|
raise NotImplementedError
|
212
40
|
end
|
41
|
+
alias_method :index, :cached
|
213
42
|
|
214
|
-
|
215
|
-
# Define `default_external_encoding` accessor on 1.9.
|
216
|
-
# Defaults to UTF-8.
|
217
|
-
attr_accessor :default_external_encoding
|
218
|
-
end
|
219
|
-
|
220
|
-
# Works like `Dir.entries`.
|
221
|
-
#
|
222
|
-
# Subclasses may cache this method.
|
223
|
-
def entries(pathname)
|
224
|
-
@trail.entries(pathname)
|
225
|
-
end
|
226
|
-
|
227
|
-
# Works like `File.stat`.
|
43
|
+
# Internal: Compute digest for path.
|
228
44
|
#
|
229
|
-
#
|
230
|
-
def stat(path)
|
231
|
-
@trail.stat(path)
|
232
|
-
end
|
233
|
-
|
234
|
-
# Read and compute digest of filename.
|
45
|
+
# path - String filename or directory path.
|
235
46
|
#
|
236
|
-
#
|
47
|
+
# Returns a String digest or nil.
|
237
48
|
def file_digest(path)
|
238
49
|
if stat = self.stat(path)
|
239
|
-
#
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
#
|
244
|
-
|
245
|
-
|
246
|
-
|
50
|
+
# Caveat: Digests are cached by the path's current mtime. Its possible
|
51
|
+
# for a files contents to have changed and its mtime to have been
|
52
|
+
# negligently reset thus appearing as if the file hasn't changed on
|
53
|
+
# disk. Also, the mtime is only read to the nearest second. It's
|
54
|
+
# also possible the file was updated more than once in a given second.
|
55
|
+
key = UnloadedAsset.new(path, self).file_digest_key(stat.mtime.to_i)
|
56
|
+
cache.fetch(key) do
|
57
|
+
self.stat_digest(path, stat)
|
247
58
|
end
|
248
59
|
end
|
249
60
|
end
|
250
61
|
|
251
|
-
#
|
252
|
-
def
|
253
|
-
|
62
|
+
# Find asset by logical path or expanded path.
|
63
|
+
def find_asset(path, options = {})
|
64
|
+
uri, _ = resolve(path, options.merge(compat: false))
|
65
|
+
if uri
|
66
|
+
load(uri)
|
67
|
+
end
|
254
68
|
end
|
255
69
|
|
256
|
-
|
257
|
-
|
258
|
-
attributes_for(path).content_type
|
259
|
-
end
|
70
|
+
def find_all_linked_assets(path, options = {})
|
71
|
+
return to_enum(__method__, path, options) unless block_given?
|
260
72
|
|
261
|
-
|
262
|
-
|
263
|
-
logical_path = path
|
264
|
-
pathname = Pathname.new(path).cleanpath
|
73
|
+
asset = find_asset(path, options)
|
74
|
+
return unless asset
|
265
75
|
|
266
|
-
|
267
|
-
|
268
|
-
logical_path = attributes_for(pathname).logical_path
|
269
|
-
else
|
270
|
-
begin
|
271
|
-
pathname = resolve(logical_path)
|
76
|
+
yield asset
|
77
|
+
stack = asset.links.to_a
|
272
78
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
# Ensures some consistency between finding "foo/bar" vs
|
277
|
-
# "foo/bar.js".
|
278
|
-
if File.extname(logical_path) == ""
|
279
|
-
expanded_logical_path = attributes_for(pathname).logical_path
|
280
|
-
logical_path += File.extname(expanded_logical_path)
|
281
|
-
end
|
282
|
-
rescue FileNotFound
|
283
|
-
return nil
|
284
|
-
end
|
79
|
+
while uri = stack.shift
|
80
|
+
yield asset = load(uri)
|
81
|
+
stack = asset.links.to_a + stack
|
285
82
|
end
|
286
83
|
|
287
|
-
|
84
|
+
nil
|
288
85
|
end
|
289
86
|
|
290
87
|
# Preferred `find_asset` shorthand.
|
@@ -295,153 +92,19 @@ module Sprockets
|
|
295
92
|
find_asset(*args)
|
296
93
|
end
|
297
94
|
|
298
|
-
def each_entry(root, &block)
|
299
|
-
return to_enum(__method__, root) unless block_given?
|
300
|
-
root = Pathname.new(root) unless root.is_a?(Pathname)
|
301
|
-
|
302
|
-
paths = []
|
303
|
-
entries(root).sort.each do |filename|
|
304
|
-
path = root.join(filename)
|
305
|
-
paths << path
|
306
|
-
|
307
|
-
if stat(path).directory?
|
308
|
-
each_entry(path) do |subpath|
|
309
|
-
paths << subpath
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
paths.sort_by(&:to_s).each(&block)
|
315
|
-
|
316
|
-
nil
|
317
|
-
end
|
318
|
-
|
319
|
-
def each_file
|
320
|
-
return to_enum(__method__) unless block_given?
|
321
|
-
paths.each do |root|
|
322
|
-
each_entry(root) do |path|
|
323
|
-
if !stat(path).directory?
|
324
|
-
yield path
|
325
|
-
end
|
326
|
-
end
|
327
|
-
end
|
328
|
-
nil
|
329
|
-
end
|
330
|
-
|
331
|
-
def each_logical_path(*args, &block)
|
332
|
-
return to_enum(__method__, *args) unless block_given?
|
333
|
-
filters = args.flatten
|
334
|
-
files = {}
|
335
|
-
each_file do |filename|
|
336
|
-
if logical_path = logical_path_for_filename(filename, filters)
|
337
|
-
unless files[logical_path]
|
338
|
-
if block.arity == 2
|
339
|
-
yield logical_path, filename.to_s
|
340
|
-
else
|
341
|
-
yield logical_path
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
files[logical_path] = true
|
346
|
-
end
|
347
|
-
end
|
348
|
-
nil
|
349
|
-
end
|
350
|
-
|
351
95
|
# Pretty inspect
|
352
96
|
def inspect
|
353
97
|
"#<#{self.class}:0x#{object_id.to_s(16)} " +
|
354
98
|
"root=#{root.to_s.inspect}, " +
|
355
|
-
"paths=#{paths.inspect}
|
356
|
-
"digest=#{digest.to_s.inspect}" +
|
357
|
-
">"
|
99
|
+
"paths=#{paths.inspect}>"
|
358
100
|
end
|
359
101
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
raise NotImplementedError
|
364
|
-
end
|
365
|
-
|
366
|
-
def build_asset(logical_path, pathname, options)
|
367
|
-
pathname = Pathname.new(pathname)
|
368
|
-
|
369
|
-
# If there are any processors to run on the pathname, use
|
370
|
-
# `BundledAsset`. Otherwise use `StaticAsset` and treat is as binary.
|
371
|
-
if attributes_for(pathname).processors.any?
|
372
|
-
if options[:bundle] == false
|
373
|
-
circular_call_protection(pathname.to_s) do
|
374
|
-
ProcessedAsset.new(index, logical_path, pathname)
|
375
|
-
end
|
376
|
-
else
|
377
|
-
BundledAsset.new(index, logical_path, pathname)
|
378
|
-
end
|
379
|
-
else
|
380
|
-
StaticAsset.new(index, logical_path, pathname)
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
def cache_key_for(path, options)
|
385
|
-
"#{path}:#{options[:bundle] ? '1' : '0'}"
|
386
|
-
end
|
387
|
-
|
388
|
-
def circular_call_protection(path)
|
389
|
-
reset = Thread.current[:sprockets_circular_calls].nil?
|
390
|
-
calls = Thread.current[:sprockets_circular_calls] ||= Set.new
|
391
|
-
if calls.include?(path)
|
392
|
-
raise CircularDependencyError, "#{path} has already been required"
|
393
|
-
end
|
394
|
-
calls << path
|
395
|
-
yield
|
396
|
-
ensure
|
397
|
-
Thread.current[:sprockets_circular_calls] = nil if reset
|
398
|
-
end
|
399
|
-
|
400
|
-
def logical_path_for_filename(filename, filters)
|
401
|
-
logical_path = attributes_for(filename).logical_path.to_s
|
402
|
-
|
403
|
-
if matches_filter(filters, logical_path, filename)
|
404
|
-
return logical_path
|
405
|
-
end
|
406
|
-
|
407
|
-
# If filename is an index file, retest with alias
|
408
|
-
if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
|
409
|
-
path = logical_path.sub(/\/index\./, '.')
|
410
|
-
if matches_filter(filters, path, filename)
|
411
|
-
return path
|
412
|
-
end
|
413
|
-
end
|
414
|
-
|
415
|
-
nil
|
416
|
-
end
|
417
|
-
|
418
|
-
def matches_filter(filters, logical_path, filename)
|
419
|
-
return true if filters.empty?
|
420
|
-
|
421
|
-
filters.any? do |filter|
|
422
|
-
if filter.is_a?(Regexp)
|
423
|
-
filter.match(logical_path)
|
424
|
-
elsif filter.respond_to?(:call)
|
425
|
-
if filter.arity == 1
|
426
|
-
filter.call(logical_path)
|
427
|
-
else
|
428
|
-
filter.call(logical_path, filename.to_s)
|
429
|
-
end
|
430
|
-
else
|
431
|
-
File.fnmatch(filter.to_s, logical_path)
|
432
|
-
end
|
433
|
-
end
|
434
|
-
end
|
102
|
+
def compress_from_root(uri)
|
103
|
+
URITar.new(uri, self).compress
|
104
|
+
end
|
435
105
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
MultiJson.load(obj)
|
440
|
-
end
|
441
|
-
else
|
442
|
-
def json_decode(obj)
|
443
|
-
MultiJson.decode(obj)
|
444
|
-
end
|
445
|
-
end
|
106
|
+
def expand_from_root(uri)
|
107
|
+
URITar.new(uri, self).expand
|
108
|
+
end
|
446
109
|
end
|
447
110
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Sprockets
|
4
|
+
module Bower
|
5
|
+
# Internal: All supported bower.json files.
|
6
|
+
#
|
7
|
+
# https://github.com/bower/json/blob/0.4.0/lib/json.js#L7
|
8
|
+
POSSIBLE_BOWER_JSONS = ['bower.json', 'component.json', '.bower.json']
|
9
|
+
|
10
|
+
# Internal: Override resolve_alternates to install bower.json behavior.
|
11
|
+
#
|
12
|
+
# load_path - String environment path
|
13
|
+
# logical_path - String path relative to base
|
14
|
+
#
|
15
|
+
# Returns candiate filenames.
|
16
|
+
def resolve_alternates(load_path, logical_path)
|
17
|
+
candidates, deps = super
|
18
|
+
|
19
|
+
# bower.json can only be nested one level deep
|
20
|
+
if !logical_path.index('/')
|
21
|
+
dirname = File.join(load_path, logical_path)
|
22
|
+
|
23
|
+
if directory?(dirname)
|
24
|
+
filenames = POSSIBLE_BOWER_JSONS.map { |basename| File.join(dirname, basename) }
|
25
|
+
filename = filenames.detect { |fn| self.file?(fn) }
|
26
|
+
|
27
|
+
if filename
|
28
|
+
deps << build_file_digest_uri(filename)
|
29
|
+
read_bower_main(dirname, filename) do |path|
|
30
|
+
candidates << path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
return candidates, deps
|
37
|
+
end
|
38
|
+
|
39
|
+
# Internal: Read bower.json's main directive.
|
40
|
+
#
|
41
|
+
# dirname - String path to component directory.
|
42
|
+
# filename - String path to bower.json.
|
43
|
+
#
|
44
|
+
# Returns nothing.
|
45
|
+
def read_bower_main(dirname, filename)
|
46
|
+
bower = JSON.parse(File.read(filename), create_additions: false)
|
47
|
+
|
48
|
+
case bower['main']
|
49
|
+
when String
|
50
|
+
yield File.expand_path(bower['main'], dirname)
|
51
|
+
when Array
|
52
|
+
bower['main'].each do |name|
|
53
|
+
yield File.expand_path(name, dirname)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'sprockets/utils'
|
3
|
+
|
4
|
+
module Sprockets
|
5
|
+
# Internal: Bundle processor takes a single file asset and prepends all the
|
6
|
+
# `:required` URIs to the contents.
|
7
|
+
#
|
8
|
+
# Uses pipeline metadata:
|
9
|
+
#
|
10
|
+
# :required - Ordered Set of asset URIs to prepend
|
11
|
+
# :stubbed - Set of asset URIs to substract from the required set.
|
12
|
+
#
|
13
|
+
# Also see DirectiveProcessor.
|
14
|
+
class Bundle
|
15
|
+
def self.call(input)
|
16
|
+
env = input[:environment]
|
17
|
+
type = input[:content_type]
|
18
|
+
dependencies = Set.new(input[:metadata][:dependencies])
|
19
|
+
|
20
|
+
processed_uri, deps = env.resolve(input[:filename], accept: type, pipeline: :self, compat: false)
|
21
|
+
dependencies.merge(deps)
|
22
|
+
|
23
|
+
find_required = proc { |uri| env.load(uri).metadata[:required] }
|
24
|
+
required = Utils.dfs(processed_uri, &find_required)
|
25
|
+
stubbed = Utils.dfs(env.load(processed_uri).metadata[:stubbed], &find_required)
|
26
|
+
required.subtract(stubbed)
|
27
|
+
assets = required.map { |uri| env.load(uri) }
|
28
|
+
|
29
|
+
(required + stubbed).each do |uri|
|
30
|
+
dependencies.merge(env.load(uri).metadata[:dependencies])
|
31
|
+
end
|
32
|
+
|
33
|
+
reducers = Hash[env.match_mime_type_keys(env.config[:bundle_reducers], type).flat_map(&:to_a)]
|
34
|
+
process_bundle_reducers(assets, reducers).merge(dependencies: dependencies, included: assets.map(&:uri))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Internal: Run bundle reducers on set of Assets producing a reduced
|
38
|
+
# metadata Hash.
|
39
|
+
#
|
40
|
+
# assets - Array of Assets
|
41
|
+
# reducers - Array of [initial, reducer_proc] pairs
|
42
|
+
#
|
43
|
+
# Returns reduced asset metadata Hash.
|
44
|
+
def self.process_bundle_reducers(assets, reducers)
|
45
|
+
initial = {}
|
46
|
+
reducers.each do |k, (v, _)|
|
47
|
+
if v.respond_to?(:call)
|
48
|
+
initial[k] = v.call
|
49
|
+
elsif !v.nil?
|
50
|
+
initial[k] = v
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
assets.reduce(initial) do |h, asset|
|
55
|
+
reducers.each do |k, (_, block)|
|
56
|
+
value = k == :data ? asset.source : asset.metadata[k]
|
57
|
+
if h.key?(k)
|
58
|
+
if !value.nil?
|
59
|
+
h[k] = block.call(h[k], value)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
h[k] = value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
h
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|