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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +296 -0
  3. data/LICENSE +2 -2
  4. data/README.md +235 -262
  5. data/bin/sprockets +1 -0
  6. data/lib/rake/sprocketstask.rb +5 -4
  7. data/lib/sprockets/asset.rb +143 -212
  8. data/lib/sprockets/autoload/closure.rb +7 -0
  9. data/lib/sprockets/autoload/coffee_script.rb +7 -0
  10. data/lib/sprockets/autoload/eco.rb +7 -0
  11. data/lib/sprockets/autoload/ejs.rb +7 -0
  12. data/lib/sprockets/autoload/sass.rb +7 -0
  13. data/lib/sprockets/autoload/uglifier.rb +7 -0
  14. data/lib/sprockets/autoload/yui.rb +7 -0
  15. data/lib/sprockets/autoload.rb +11 -0
  16. data/lib/sprockets/base.rb +56 -393
  17. data/lib/sprockets/bower.rb +58 -0
  18. data/lib/sprockets/bundle.rb +69 -0
  19. data/lib/sprockets/cache/file_store.rb +168 -14
  20. data/lib/sprockets/cache/memory_store.rb +66 -0
  21. data/lib/sprockets/cache/null_store.rb +46 -0
  22. data/lib/sprockets/cache.rb +236 -0
  23. data/lib/sprockets/cached_environment.rb +69 -0
  24. data/lib/sprockets/closure_compressor.rb +35 -10
  25. data/lib/sprockets/coffee_script_processor.rb +25 -0
  26. data/lib/sprockets/coffee_script_template.rb +17 -0
  27. data/lib/sprockets/compressing.rb +44 -23
  28. data/lib/sprockets/configuration.rb +83 -0
  29. data/lib/sprockets/context.rb +86 -144
  30. data/lib/sprockets/dependencies.rb +73 -0
  31. data/lib/sprockets/deprecation.rb +90 -0
  32. data/lib/sprockets/digest_utils.rb +180 -0
  33. data/lib/sprockets/directive_processor.rb +207 -211
  34. data/lib/sprockets/eco_processor.rb +32 -0
  35. data/lib/sprockets/eco_template.rb +9 -30
  36. data/lib/sprockets/ejs_processor.rb +31 -0
  37. data/lib/sprockets/ejs_template.rb +9 -29
  38. data/lib/sprockets/encoding_utils.rb +261 -0
  39. data/lib/sprockets/engines.rb +53 -35
  40. data/lib/sprockets/environment.rb +17 -64
  41. data/lib/sprockets/erb_processor.rb +30 -0
  42. data/lib/sprockets/erb_template.rb +11 -0
  43. data/lib/sprockets/errors.rb +4 -13
  44. data/lib/sprockets/file_reader.rb +15 -0
  45. data/lib/sprockets/http_utils.rb +117 -0
  46. data/lib/sprockets/jst_processor.rb +35 -15
  47. data/lib/sprockets/legacy.rb +330 -0
  48. data/lib/sprockets/legacy_proc_processor.rb +35 -0
  49. data/lib/sprockets/legacy_tilt_processor.rb +29 -0
  50. data/lib/sprockets/loader.rb +325 -0
  51. data/lib/sprockets/manifest.rb +202 -127
  52. data/lib/sprockets/manifest_utils.rb +45 -0
  53. data/lib/sprockets/mime.rb +112 -31
  54. data/lib/sprockets/path_dependency_utils.rb +85 -0
  55. data/lib/sprockets/path_digest_utils.rb +47 -0
  56. data/lib/sprockets/path_utils.rb +287 -0
  57. data/lib/sprockets/paths.rb +42 -19
  58. data/lib/sprockets/processing.rb +178 -126
  59. data/lib/sprockets/processor_utils.rb +180 -0
  60. data/lib/sprockets/resolve.rb +211 -0
  61. data/lib/sprockets/sass_cache_store.rb +22 -17
  62. data/lib/sprockets/sass_compressor.rb +39 -15
  63. data/lib/sprockets/sass_functions.rb +2 -70
  64. data/lib/sprockets/sass_importer.rb +2 -30
  65. data/lib/sprockets/sass_processor.rb +292 -0
  66. data/lib/sprockets/sass_template.rb +12 -59
  67. data/lib/sprockets/server.rb +129 -84
  68. data/lib/sprockets/transformers.rb +145 -0
  69. data/lib/sprockets/uglifier_compressor.rb +39 -12
  70. data/lib/sprockets/unloaded_asset.rb +137 -0
  71. data/lib/sprockets/uri_tar.rb +98 -0
  72. data/lib/sprockets/uri_utils.rb +188 -0
  73. data/lib/sprockets/utils/gzip.rb +67 -0
  74. data/lib/sprockets/utils.rb +210 -44
  75. data/lib/sprockets/version.rb +1 -1
  76. data/lib/sprockets/yui_compressor.rb +39 -11
  77. data/lib/sprockets.rb +142 -81
  78. metadata +96 -90
  79. data/lib/sprockets/asset_attributes.rb +0 -137
  80. data/lib/sprockets/bundled_asset.rb +0 -78
  81. data/lib/sprockets/caching.rb +0 -96
  82. data/lib/sprockets/charset_normalizer.rb +0 -41
  83. data/lib/sprockets/index.rb +0 -100
  84. data/lib/sprockets/processed_asset.rb +0 -152
  85. data/lib/sprockets/processor.rb +0 -32
  86. data/lib/sprockets/safety_colons.rb +0 -28
  87. data/lib/sprockets/scss_template.rb +0 -13
  88. data/lib/sprockets/static_asset.rb +0 -60
data/bin/sprockets CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ $VERBOSE = nil
2
3
 
3
4
  require 'sprockets'
4
5
  require 'optparse'
@@ -37,10 +37,11 @@ module Rake
37
37
  end
38
38
  attr_writer :environment
39
39
 
40
- # Returns cached indexed environment
41
- def index
42
- @index ||= environment.index if environment
40
+ # Returns cached cached environment
41
+ def cached
42
+ @cached ||= environment.cached if environment
43
43
  end
44
+ alias_method :index, :cached
44
45
 
45
46
  # `Manifest` instance used for already compiled assets.
46
47
  #
@@ -97,7 +98,7 @@ module Rake
97
98
  def initialize(name = :assets)
98
99
  @name = name
99
100
  @environment = lambda { Sprockets::Environment.new(Dir.pwd) }
100
- @manifest = lambda { Sprockets::Manifest.new(index, output) }
101
+ @manifest = lambda { Sprockets::Manifest.new(cached, output) }
101
102
  @logger = Logger.new($stderr)
102
103
  @logger.level = Logger::INFO
103
104
  @keep = 2
@@ -1,269 +1,200 @@
1
- require 'time'
2
- require 'set'
1
+ require 'fileutils'
2
+ require 'sprockets/digest_utils'
3
3
 
4
4
  module Sprockets
5
- # `Asset` is the base class for `BundledAsset` and `StaticAsset`.
6
5
  class Asset
7
- # Internal initializer to load `Asset` from serialized `Hash`.
8
- def self.from_hash(environment, hash)
9
- return unless hash.is_a?(Hash)
6
+ attr_reader :logical_path
10
7
 
11
- klass = case hash['class']
12
- when 'BundledAsset'
13
- BundledAsset
14
- when 'ProcessedAsset'
15
- ProcessedAsset
16
- when 'StaticAsset'
17
- StaticAsset
18
- else
19
- nil
20
- end
21
-
22
- if klass
23
- asset = klass.allocate
24
- asset.init_with(environment, hash)
25
- asset
26
- end
27
- rescue UnserializeError
28
- nil
8
+ # Private: Intialize Asset wrapper from attributes Hash.
9
+ #
10
+ # Asset wrappers should not be initialized directly, only
11
+ # Environment#find_asset should vend them.
12
+ #
13
+ # attributes - Hash of ivars
14
+ #
15
+ # Returns Asset.
16
+ def initialize(environment, attributes = {})
17
+ @environment = environment
18
+ @attributes = attributes
19
+ @content_type = attributes[:content_type]
20
+ @filename = attributes[:filename]
21
+ @id = attributes[:id]
22
+ @load_path = attributes[:load_path]
23
+ @logical_path = attributes[:logical_path]
24
+ @metadata = attributes[:metadata]
25
+ @mtime = attributes[:mtime]
26
+ @name = attributes[:name]
27
+ @source = attributes[:source]
28
+ @uri = attributes[:uri]
29
+ end
30
+
31
+ # Internal: Return all internal instance variables as a hash.
32
+ #
33
+ # Returns a Hash.
34
+ def to_hash
35
+ @attributes
29
36
  end
30
37
 
31
- attr_reader :logical_path, :pathname
32
- attr_reader :content_type, :mtime, :length, :digest
33
- alias_method :bytesize, :length
38
+ # Public: Metadata accumulated from pipeline process.
39
+ #
40
+ # The API status of the keys is dependent on the pipeline processors
41
+ # itself. So some values maybe considered public and others internal.
42
+ # See the pipeline proccessor documentation itself.
43
+ #
44
+ # Returns Hash.
45
+ attr_reader :metadata
34
46
 
35
- def initialize(environment, logical_path, pathname)
36
- raise ArgumentError, "Asset logical path has no extension: #{logical_path}" if File.extname(logical_path) == ""
47
+ # Public: Returns String path of asset.
48
+ attr_reader :filename
37
49
 
38
- @root = environment.root
39
- @logical_path = logical_path.to_s
40
- @pathname = Pathname.new(pathname)
41
- @content_type = environment.content_type_of(pathname)
42
- # drop precision to 1 second, same pattern followed elsewhere
43
- @mtime = Time.at(environment.stat(pathname).mtime.to_i)
44
- @length = environment.stat(pathname).size
45
- @digest = environment.file_digest(pathname).hexdigest
46
- end
50
+ # Internal: Unique asset object ID.
51
+ #
52
+ # Returns a String.
53
+ attr_reader :id
47
54
 
48
- # Initialize `Asset` from serialized `Hash`.
49
- def init_with(environment, coder)
50
- @root = environment.root
55
+ # Public: Internal URI to lookup asset by.
56
+ #
57
+ # NOT a publically accessible URL.
58
+ #
59
+ # Returns URI.
60
+ attr_reader :uri
51
61
 
52
- @logical_path = coder['logical_path']
53
- @content_type = coder['content_type']
54
- @digest = coder['digest']
62
+ # Public: Return logical path with digest spliced in.
63
+ #
64
+ # "foo/bar-37b51d194a7513e45b56f6524f2d51f2.js"
65
+ #
66
+ # Returns String.
67
+ def digest_path
68
+ logical_path.sub(/\.(\w+)$/) { |ext| "-#{etag}#{ext}" }
69
+ end
55
70
 
56
- if pathname = coder['pathname']
57
- # Expand `$root` placeholder and wrapper string in a `Pathname`
58
- @pathname = Pathname.new(expand_root_path(pathname))
59
- end
71
+ # Public: Returns String MIME type of asset. Returns nil if type is unknown.
72
+ attr_reader :content_type
60
73
 
61
- if mtime = coder['mtime']
62
- @mtime = Time.at(mtime)
63
- end
74
+ # Public: Get all externally linked asset filenames from asset.
75
+ #
76
+ # All linked assets should be compiled anytime this asset is.
77
+ #
78
+ # Returns Set of String asset URIs.
79
+ def links
80
+ metadata[:links] || Set.new
81
+ end
64
82
 
65
- if length = coder['length']
66
- # Convert length to an `Integer`
67
- @length = Integer(length)
68
- end
83
+ # Public: Get all internally required assets that were concated into this
84
+ # asset.
85
+ #
86
+ # Returns Array of String asset URIs.
87
+ def included
88
+ metadata[:included]
69
89
  end
70
90
 
71
- # Copy serialized attributes to the coder object
72
- def encode_with(coder)
73
- coder['class'] = self.class.name.sub(/Sprockets::/, '')
74
- coder['logical_path'] = logical_path
75
- coder['pathname'] = relativize_root_path(pathname).to_s
76
- coder['content_type'] = content_type
77
- coder['mtime'] = mtime.to_i
78
- coder['length'] = length
79
- coder['digest'] = digest
91
+ # Public: Return `String` of concatenated source.
92
+ #
93
+ # Returns String.
94
+ def source
95
+ if @source
96
+ @source
97
+ else
98
+ # File is read everytime to avoid memory bloat of large binary files
99
+ File.binread(filename)
100
+ end
80
101
  end
81
102
 
82
- # Return logical path with digest spliced in.
103
+ # Public: Alias for #source.
83
104
  #
84
- # "foo/bar-37b51d194a7513e45b56f6524f2d51f2.js"
105
+ # Returns String.
106
+ def to_s
107
+ source
108
+ end
109
+
110
+ # Public: Get charset of source.
85
111
  #
86
- def digest_path
87
- logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
112
+ # Returns a String charset name or nil if binary.
113
+ def charset
114
+ metadata[:charset]
88
115
  end
89
116
 
90
- # Return an `Array` of `Asset` files that are declared dependencies.
91
- def dependencies
92
- []
117
+ # Public: Returns Integer length of source.
118
+ def length
119
+ metadata[:length]
93
120
  end
121
+ alias_method :bytesize, :length
94
122
 
95
- # Expand asset into an `Array` of parts.
96
- #
97
- # Appending all of an assets body parts together should give you
98
- # the asset's contents as a whole.
99
- #
100
- # This allows you to link to individual files for debugging
101
- # purposes.
102
- def to_a
103
- [self]
123
+ # Public: Returns String hexdigest of source.
124
+ def hexdigest
125
+ DigestUtils.pack_hexdigest(metadata[:digest])
104
126
  end
105
127
 
106
- # `body` is aliased to source by default if it can't have any dependencies.
107
- def body
108
- source
128
+ # Deprecated: Returns String hexdigest of source.
129
+ #
130
+ # In 4.x this will be changed to return a raw Digest byte String.
131
+ alias_method :digest, :hexdigest
132
+
133
+ # Pubic: ETag String of Asset.
134
+ alias_method :etag, :hexdigest
135
+
136
+ # Public: Returns String base64 digest of source.
137
+ def base64digest
138
+ DigestUtils.pack_base64digest(metadata[:digest])
109
139
  end
110
140
 
111
- # Return `String` of concatenated source.
112
- def to_s
113
- source
141
+ # Public: A "named information" URL for subresource integrity.
142
+ def integrity
143
+ DigestUtils.integrity_uri(metadata[:digest])
114
144
  end
115
145
 
116
- # Add enumerator to allow `Asset` instances to be used as Rack
146
+ # Public: Add enumerator to allow `Asset` instances to be used as Rack
117
147
  # compatible body objects.
148
+ #
149
+ # block
150
+ # part - String body chunk
151
+ #
152
+ # Returns nothing.
118
153
  def each
119
154
  yield to_s
120
155
  end
121
156
 
122
- # Checks if Asset is fresh by comparing the actual mtime and
123
- # digest to the inmemory model.
157
+ # Deprecated: Save asset to disk.
124
158
  #
125
- # Used to test if cached models need to be rebuilt.
126
- def fresh?(environment)
127
- # Check current mtime and digest
128
- dependency_fresh?(environment, self)
129
- end
130
-
131
- # Checks if Asset is stale by comparing the actual mtime and
132
- # digest to the inmemory model.
159
+ # filename - String target
133
160
  #
134
- # Subclass must override `fresh?` or `stale?`.
135
- def stale?(environment)
136
- !fresh?(environment)
137
- end
138
-
139
- # Save asset to disk.
140
- def write_to(filename, options = {})
141
- # Gzip contents if filename has '.gz'
142
- unless options.key?(:compress)
143
- options[:compress] = File.extname(filename) == '.gz' && File.extname(logical_path) != '.gz'
144
- end
145
-
161
+ # Returns nothing.
162
+ def write_to(filename)
146
163
  FileUtils.mkdir_p File.dirname(filename)
147
164
 
148
- File.open("#{filename}+", 'wb') do |f|
149
- if options[:compress]
150
- # Run contents through `Zlib`
151
- gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
152
- gz.mtime = mtime.to_i
153
- gz.write to_s
154
- gz.close
155
- else
156
- # Write out as is
157
- f.write to_s
158
- end
165
+ PathUtils.atomic_write(filename) do |f|
166
+ f.write source
159
167
  end
160
168
 
161
- # Atomic write
162
- FileUtils.mv("#{filename}+", filename)
163
-
164
169
  # Set mtime correctly
165
170
  File.utime(mtime, mtime, filename)
166
171
 
167
172
  nil
168
- ensure
169
- # Ensure tmp file gets cleaned up
170
- FileUtils.rm("#{filename}+") if File.exist?("#{filename}+")
171
173
  end
172
174
 
173
- # Pretty inspect
175
+ # Public: Pretty inspect
176
+ #
177
+ # Returns String.
174
178
  def inspect
175
- "#<#{self.class}:0x#{object_id.to_s(16)} " +
176
- "pathname=#{pathname.to_s.inspect}, " +
177
- "mtime=#{mtime.inspect}, " +
178
- "digest=#{digest.inspect}" +
179
- ">"
179
+ "#<#{self.class}:#{object_id.to_s(16)} #{uri.inspect}>"
180
180
  end
181
181
 
182
+ # Public: Implements Object#hash so Assets can be used as a Hash key or
183
+ # in a Set.
184
+ #
185
+ # Returns Integer hash of the id.
182
186
  def hash
183
- digest.hash
187
+ id.hash
184
188
  end
185
189
 
186
- # Assets are equal if they share the same path, mtime and digest.
190
+ # Public: Compare assets.
191
+ #
192
+ # Assets are equal if they share the same path and digest.
193
+ #
194
+ # Returns true or false.
187
195
  def eql?(other)
188
- other.class == self.class &&
189
- other.logical_path == self.logical_path &&
190
- other.mtime.to_i == self.mtime.to_i &&
191
- other.digest == self.digest
196
+ self.class == other.class && self.id == other.id
192
197
  end
193
198
  alias_method :==, :eql?
194
-
195
- protected
196
- # Internal: String paths that are marked as dependencies after processing.
197
- #
198
- # Default to an empty `Array`.
199
- def dependency_paths
200
- @dependency_paths ||= []
201
- end
202
-
203
- # Internal: `ProccessedAsset`s that are required after processing.
204
- #
205
- # Default to an empty `Array`.
206
- def required_assets
207
- @required_assets ||= []
208
- end
209
-
210
- # Get pathname with its root stripped.
211
- def relative_pathname
212
- @relative_pathname ||= Pathname.new(relativize_root_path(pathname))
213
- end
214
-
215
- # Replace `$root` placeholder with actual environment root.
216
- def expand_root_path(path)
217
- path.to_s.sub(/^\$root/, @root)
218
- end
219
-
220
- # Replace actual environment root with `$root` placeholder.
221
- def relativize_root_path(path)
222
- path.to_s.sub(/^#{Regexp.escape(@root)}/, '$root')
223
- end
224
-
225
- # Check if dependency is fresh.
226
- #
227
- # `dep` is a `Hash` with `path`, `mtime` and `hexdigest` keys.
228
- #
229
- # A `Hash` is used rather than other `Asset` object because we
230
- # want to test non-asset files and directories.
231
- def dependency_fresh?(environment, dep)
232
- path, mtime, hexdigest = dep.pathname.to_s, dep.mtime, dep.digest
233
-
234
- stat = environment.stat(path)
235
-
236
- # If path no longer exists, its definitely stale.
237
- if stat.nil?
238
- return false
239
- end
240
-
241
- # Compare dependency mtime to the actual mtime. If the
242
- # dependency mtime is newer than the actual mtime, the file
243
- # hasn't changed since we created this `Asset` instance.
244
- #
245
- # However, if the mtime is newer it doesn't mean the asset is
246
- # stale. Many deployment environments may recopy or recheckout
247
- # assets on each deploy. In this case the mtime would be the
248
- # time of deploy rather than modified time.
249
- #
250
- # Note: to_i is used in eql? and write_to we assume fidelity of 1 second
251
- # if people save files more frequently than 1 second sprockets may
252
- # not pick it up, by design
253
- if mtime.to_i >= stat.mtime.to_i
254
- return true
255
- end
256
-
257
- digest = environment.file_digest(path)
258
-
259
- # If the mtime is newer, do a full digest comparsion. Return
260
- # fresh if the digests match.
261
- if hexdigest == digest.hexdigest
262
- return true
263
- end
264
-
265
- # Otherwise, its stale.
266
- false
267
- end
268
199
  end
269
200
  end
@@ -0,0 +1,7 @@
1
+ require 'closure-compiler'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ Closure = ::Closure
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'coffee_script'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ CoffeeScript = ::CoffeeScript
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'eco'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ Eco = ::Eco
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'ejs'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ EJS = ::EJS
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'sass'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ Sass = ::Sass
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'uglifier'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ Uglifier = ::Uglifier
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'yui/compressor'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ YUI = ::YUI
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module Sprockets
2
+ module Autoload
3
+ autoload :Closure, 'sprockets/autoload/closure'
4
+ autoload :CoffeeScript, 'sprockets/autoload/coffee_script'
5
+ autoload :Eco, 'sprockets/autoload/eco'
6
+ autoload :EJS, 'sprockets/autoload/ejs'
7
+ autoload :Sass, 'sprockets/autoload/sass'
8
+ autoload :Uglifier, 'sprockets/autoload/uglifier'
9
+ autoload :YUI, 'sprockets/autoload/yui'
10
+ end
11
+ end