sprockets 2.12.5 → 3.7.2
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.
- 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
|