sprockets 2.6.0 → 4.2.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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +118 -0
  3. data/{LICENSE → MIT-LICENSE} +2 -2
  4. data/README.md +541 -289
  5. data/bin/sprockets +20 -7
  6. data/lib/rake/sprocketstask.rb +34 -17
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +158 -210
  9. data/lib/sprockets/autoload/babel.rb +8 -0
  10. data/lib/sprockets/autoload/closure.rb +8 -0
  11. data/lib/sprockets/autoload/coffee_script.rb +8 -0
  12. data/lib/sprockets/autoload/eco.rb +8 -0
  13. data/lib/sprockets/autoload/ejs.rb +8 -0
  14. data/lib/sprockets/autoload/jsminc.rb +8 -0
  15. data/lib/sprockets/autoload/sass.rb +8 -0
  16. data/lib/sprockets/autoload/sassc.rb +8 -0
  17. data/lib/sprockets/autoload/uglifier.rb +8 -0
  18. data/lib/sprockets/autoload/yui.rb +8 -0
  19. data/lib/sprockets/autoload/zopfli.rb +7 -0
  20. data/lib/sprockets/autoload.rb +16 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +89 -378
  23. data/lib/sprockets/bower.rb +61 -0
  24. data/lib/sprockets/bundle.rb +105 -0
  25. data/lib/sprockets/cache/file_store.rb +190 -14
  26. data/lib/sprockets/cache/memory_store.rb +84 -0
  27. data/lib/sprockets/cache/null_store.rb +54 -0
  28. data/lib/sprockets/cache.rb +271 -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 +166 -150
  35. data/lib/sprockets/dependencies.rb +74 -0
  36. data/lib/sprockets/digest_utils.rb +197 -0
  37. data/lib/sprockets/directive_processor.rb +241 -215
  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 +261 -0
  41. data/lib/sprockets/environment.rb +23 -64
  42. data/lib/sprockets/erb_processor.rb +43 -0
  43. data/lib/sprockets/errors.rb +5 -13
  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 +36 -19
  53. data/lib/sprockets/loader.rb +347 -0
  54. data/lib/sprockets/manifest.rb +228 -112
  55. data/lib/sprockets/manifest_utils.rb +48 -0
  56. data/lib/sprockets/mime.rb +78 -31
  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 +43 -19
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +146 -164
  64. data/lib/sprockets/processor_utils.rb +170 -0
  65. data/lib/sprockets/resolve.rb +295 -0
  66. data/lib/sprockets/sass_cache_store.rb +20 -15
  67. data/lib/sprockets/sass_compressor.rb +55 -10
  68. data/lib/sprockets/sass_functions.rb +3 -70
  69. data/lib/sprockets/sass_importer.rb +3 -29
  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 +159 -91
  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 +194 -0
  81. data/lib/sprockets/utils/gzip.rb +99 -0
  82. data/lib/sprockets/utils.rb +193 -52
  83. data/lib/sprockets/version.rb +2 -1
  84. data/lib/sprockets/yui_compressor.rb +56 -0
  85. data/lib/sprockets.rb +217 -75
  86. metadata +272 -117
  87. data/lib/sprockets/asset_attributes.rb +0 -131
  88. data/lib/sprockets/bundled_asset.rb +0 -80
  89. data/lib/sprockets/caching.rb +0 -96
  90. data/lib/sprockets/charset_normalizer.rb +0 -41
  91. data/lib/sprockets/eco_template.rb +0 -38
  92. data/lib/sprockets/ejs_template.rb +0 -37
  93. data/lib/sprockets/engines.rb +0 -74
  94. data/lib/sprockets/index.rb +0 -99
  95. data/lib/sprockets/processed_asset.rb +0 -152
  96. data/lib/sprockets/processor.rb +0 -32
  97. data/lib/sprockets/safety_colons.rb +0 -28
  98. data/lib/sprockets/sass_template.rb +0 -60
  99. data/lib/sprockets/scss_template.rb +0 -13
  100. data/lib/sprockets/static_asset.rb +0 -58
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'ejs'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ EJS = ::EJS
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'jsminc'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ JSMinC = ::JSMinC
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'sass'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ Sass = ::Sass
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'sassc'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ SassC = ::SassC
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'uglifier'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ Uglifier = ::Uglifier
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'yui/compressor'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ YUI = ::YUI
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'zopfli'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ Zopfli = ::Zopfli
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ module Sprockets
3
+ module Autoload
4
+ autoload :Babel, 'sprockets/autoload/babel'
5
+ autoload :Closure, 'sprockets/autoload/closure'
6
+ autoload :CoffeeScript, 'sprockets/autoload/coffee_script'
7
+ autoload :Eco, 'sprockets/autoload/eco'
8
+ autoload :EJS, 'sprockets/autoload/ejs'
9
+ autoload :JSMinC, 'sprockets/autoload/jsminc'
10
+ autoload :Sass, 'sprockets/autoload/sass'
11
+ autoload :SassC, 'sprockets/autoload/sassc'
12
+ autoload :Uglifier, 'sprockets/autoload/uglifier'
13
+ autoload :YUI, 'sprockets/autoload/yui'
14
+ autoload :Zopfli, 'sprockets/autoload/zopfli'
15
+ end
16
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+ require 'sprockets/path_utils'
4
+ require 'sprockets/source_map_utils'
5
+ require 'json'
6
+
7
+ module Sprockets
8
+ class BabelProcessor
9
+ VERSION = '1'
10
+
11
+ def self.instance
12
+ @instance ||= new
13
+ end
14
+
15
+ def self.call(input)
16
+ instance.call(input)
17
+ end
18
+
19
+ def self.cache_key
20
+ instance.cache_key
21
+ end
22
+
23
+ attr_reader :cache_key
24
+
25
+ def initialize(options = {})
26
+ @options = options.merge({
27
+ 'blacklist' => (options['blacklist'] || []) + ['useStrict'],
28
+ 'sourceMap' => true
29
+ }).freeze
30
+
31
+ @cache_key = [
32
+ self.class.name,
33
+ Autoload::Babel::Transpiler::VERSION,
34
+ Autoload::Babel::Source::VERSION,
35
+ VERSION,
36
+ @options
37
+ ].freeze
38
+ end
39
+
40
+ def call(input)
41
+ data = input[:data]
42
+
43
+ result = input[:cache].fetch(@cache_key + [input[:filename]] + [data]) do
44
+ opts = {
45
+ 'moduleRoot' => nil,
46
+ 'filename' => input[:filename],
47
+ 'filenameRelative' => PathUtils.split_subpath(input[:load_path], input[:filename]),
48
+ 'sourceFileName' => File.basename(input[:filename]),
49
+ 'sourceMapTarget' => input[:filename]
50
+ }.merge(@options)
51
+
52
+ if opts['moduleIds'] && opts['moduleRoot']
53
+ opts['moduleId'] ||= File.join(opts['moduleRoot'], input[:name])
54
+ elsif opts['moduleIds']
55
+ opts['moduleId'] ||= input[:name]
56
+ end
57
+ Autoload::Babel::Transpiler.transform(data, opts)
58
+ end
59
+
60
+ map = SourceMapUtils.format_source_map(result["map"], input)
61
+ map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
62
+
63
+ { data: result['code'], map: map }
64
+ end
65
+ end
66
+ end
@@ -1,89 +1,41 @@
1
- require 'sprockets/asset_attributes'
2
- require 'sprockets/bundled_asset'
3
- require 'sprockets/caching'
1
+ # frozen_string_literal: true
2
+ require 'sprockets/asset'
3
+ require 'sprockets/bower'
4
+ require 'sprockets/cache'
5
+ require 'sprockets/configuration'
6
+ require 'sprockets/digest_utils'
4
7
  require 'sprockets/errors'
5
- require 'sprockets/processed_asset'
8
+ require 'sprockets/loader'
9
+ require 'sprockets/npm'
10
+ require 'sprockets/path_dependency_utils'
11
+ require 'sprockets/path_digest_utils'
12
+ require 'sprockets/path_utils'
13
+ require 'sprockets/resolve'
6
14
  require 'sprockets/server'
7
- require 'sprockets/static_asset'
8
- require 'multi_json'
9
- require 'pathname'
15
+ require 'sprockets/source_map_utils'
16
+ require 'sprockets/uri_tar'
10
17
 
11
18
  module Sprockets
12
- # `Base` class for `Environment` and `Index`.
13
- class Base
14
- include Caching, Paths, Mime, Processing, Engines, Server
15
-
16
- # Returns a `Digest` implementation class.
17
- #
18
- # Defaults to `Digest::MD5`.
19
- attr_reader :digest_class
20
-
21
- # Assign a `Digest` implementation class. This maybe 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
19
 
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
20
+ class DoubleLinkError < Sprockets::Error
21
+ def initialize(parent_filename:, logical_path:, last_filename:, filename:)
22
+ super <<~MSG
23
+ Multiple files with the same output path cannot be linked (#{logical_path.inspect})
24
+ In #{parent_filename.inspect} these files were linked:
25
+ - #{last_filename}
26
+ - #{filename}
27
+ MSG
72
28
  end
29
+ end
73
30
 
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
31
+ # `Base` class for `Environment` and `CachedEnvironment`.
32
+ class Base
33
+ include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils, SourceMapUtils
34
+ include Configuration
35
+ include Server
36
+ include Resolve, Loader
37
+ include Bower
38
+ include Npm
87
39
 
88
40
  # Get persistent cache store
89
41
  attr_reader :cache
@@ -94,343 +46,102 @@ module Sprockets
94
46
  # setters. Either `get(key)`/`set(key, value)`,
95
47
  # `[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
96
48
  def cache=(cache)
97
- expire_index!
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
117
- end
118
-
119
- # Finds the expanded real path for a given logical path by
120
- # searching the environment's paths.
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 pathname.basename.to_s == 'component.json'
133
- component = json_decode(pathname.read)
134
- case component['main']
135
- when String
136
- yield pathname.dirname.join(component['main'])
137
- when Array
138
- extname = File.extname(logical_path)
139
- component['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
49
+ @cache = Cache.new(cache, logger)
177
50
  end
178
51
 
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
52
+ # Return an `CachedEnvironment`. Must be implemented by the subclass.
53
+ def cached
211
54
  raise NotImplementedError
212
55
  end
56
+ alias_method :index, :cached
213
57
 
214
- if defined? Encoding.default_external
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`.
58
+ # Internal: Compute digest for path.
228
59
  #
229
- # Subclasses may cache this method.
230
- def stat(path)
231
- @trail.stat(path)
232
- end
233
-
234
- # Read and compute digest of filename.
60
+ # path - String filename or directory path.
235
61
  #
236
- # Subclasses may cache this method.
62
+ # Returns a String digest or nil.
237
63
  def file_digest(path)
238
64
  if stat = self.stat(path)
239
- # If its a file, digest the contents
240
- if stat.file?
241
- digest.file(path.to_s)
242
-
243
- # If its a directive, digest the list of filenames
244
- elsif stat.directory?
245
- contents = self.entries(path).join(',')
246
- digest.update(contents)
65
+ # Caveat: Digests are cached by the path's current mtime. Its possible
66
+ # for a files contents to have changed and its mtime to have been
67
+ # negligently reset thus appearing as if the file hasn't changed on
68
+ # disk. Also, the mtime is only read to the nearest second. It's
69
+ # also possible the file was updated more than once in a given second.
70
+ key = UnloadedAsset.new(path, self).file_digest_key(stat.mtime.to_i)
71
+ cache.fetch(key) do
72
+ self.stat_digest(path, stat)
247
73
  end
248
74
  end
249
75
  end
250
76
 
251
- # Internal. Return a `AssetAttributes` for `path`.
252
- def attributes_for(path)
253
- AssetAttributes.new(self, path)
77
+ # Find asset by logical path or expanded path.
78
+ def find_asset(*args, **options)
79
+ uri, _ = resolve(*args, **options)
80
+ if uri
81
+ load(uri)
82
+ end
254
83
  end
255
84
 
256
- # Internal. Return content type of `path`.
257
- def content_type_of(path)
258
- attributes_for(path).content_type
259
- end
85
+ def find_all_linked_assets(*args)
86
+ return to_enum(__method__, *args) unless block_given?
260
87
 
261
- # Find asset by logical path or expanded path.
262
- def find_asset(path, options = {})
263
- logical_path = path
264
- pathname = Pathname.new(path)
88
+ parent_asset = asset = find_asset(*args)
89
+ return unless asset
265
90
 
266
- if pathname.absolute?
267
- return unless stat(pathname)
268
- logical_path = attributes_for(pathname).logical_path
269
- else
270
- begin
271
- pathname = resolve(logical_path)
91
+ yield asset
92
+ stack = asset.links.to_a
93
+ linked_paths = {}
272
94
 
273
- # If logical path is missing a mime type extension, append
274
- # the absolute path extname so it has one.
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
95
+ while uri = stack.shift
96
+ yield asset = load(uri)
97
+
98
+ last_filename = linked_paths[asset.logical_path]
99
+ if last_filename && last_filename != asset.filename
100
+ raise DoubleLinkError.new(
101
+ parent_filename: parent_asset.filename,
102
+ last_filename: last_filename,
103
+ logical_path: asset.logical_path,
104
+ filename: asset.filename
105
+ )
284
106
  end
107
+ linked_paths[asset.logical_path] = asset.filename
108
+ stack = asset.links.to_a + stack
285
109
  end
286
110
 
287
- build_asset(logical_path, pathname, options)
111
+ nil
288
112
  end
289
113
 
290
114
  # Preferred `find_asset` shorthand.
291
115
  #
292
116
  # environment['application.js']
293
117
  #
294
- def [](*args)
295
- find_asset(*args)
296
- end
297
-
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
118
+ def [](*args, **options)
119
+ find_asset(*args, **options)
329
120
  end
330
121
 
331
- def each_logical_path(*args)
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
- yield logical_path unless files[logical_path]
338
- files[logical_path] = true
339
- end
122
+ # Find asset by logical path or expanded path.
123
+ #
124
+ # If the asset is not found an error will be raised.
125
+ def find_asset!(*args)
126
+ uri, _ = resolve!(*args)
127
+ if uri
128
+ load(uri)
340
129
  end
341
- nil
342
130
  end
343
131
 
344
132
  # Pretty inspect
345
133
  def inspect
346
134
  "#<#{self.class}:0x#{object_id.to_s(16)} " +
347
135
  "root=#{root.to_s.inspect}, " +
348
- "paths=#{paths.inspect}, " +
349
- "digest=#{digest.to_s.inspect}" +
350
- ">"
136
+ "paths=#{paths.inspect}>"
351
137
  end
352
138
 
353
- protected
354
- # Clear index after mutating state. Must be implemented by the subclass.
355
- def expire_index!
356
- raise NotImplementedError
357
- end
358
-
359
- def build_asset(logical_path, pathname, options)
360
- pathname = Pathname.new(pathname)
361
-
362
- # If there are any processors to run on the pathname, use
363
- # `BundledAsset`. Otherwise use `StaticAsset` and treat is as binary.
364
- if attributes_for(pathname).processors.any?
365
- if options[:bundle] == false
366
- circular_call_protection(pathname.to_s) do
367
- ProcessedAsset.new(index, logical_path, pathname)
368
- end
369
- else
370
- BundledAsset.new(index, logical_path, pathname)
371
- end
372
- else
373
- StaticAsset.new(index, logical_path, pathname)
374
- end
375
- end
376
-
377
- def cache_key_for(path, options)
378
- "#{path}:#{options[:bundle] ? '1' : '0'}"
379
- end
380
-
381
- def circular_call_protection(path)
382
- reset = Thread.current[:sprockets_circular_calls].nil?
383
- calls = Thread.current[:sprockets_circular_calls] ||= Set.new
384
- if calls.include?(path)
385
- raise CircularDependencyError, "#{path} has already been required"
386
- end
387
- calls << path
388
- yield
389
- ensure
390
- Thread.current[:sprockets_circular_calls] = nil if reset
391
- end
392
-
393
- def logical_path_for_filename(filename, filters)
394
- logical_path = attributes_for(filename).logical_path.to_s
395
-
396
- if matches_filter(filters, logical_path)
397
- return logical_path
398
- end
399
-
400
- # If filename is an index file, retest with alias
401
- if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
402
- path = logical_path.sub(/\/index\./, '.')
403
- if matches_filter(filters, path)
404
- return path
405
- end
406
- end
407
-
408
- nil
409
- end
410
-
411
- def matches_filter(filters, filename)
412
- return true if filters.empty?
413
-
414
- filters.any? do |filter|
415
- if filter.is_a?(Regexp)
416
- filter.match(filename)
417
- elsif filter.respond_to?(:call)
418
- filter.call(filename)
419
- else
420
- File.fnmatch(filter.to_s, filename)
421
- end
422
- end
423
- end
139
+ def compress_from_root(uri)
140
+ URITar.new(uri, self).compress
141
+ end
424
142
 
425
- # Feature detect newer MultiJson API
426
- if MultiJson.respond_to?(:dump)
427
- def json_decode(obj)
428
- MultiJson.load(obj)
429
- end
430
- else
431
- def json_decode(obj)
432
- MultiJson.decode(obj)
433
- end
434
- end
143
+ def expand_from_root(uri)
144
+ URITar.new(uri, self).expand
145
+ end
435
146
  end
436
147
  end