sprockets 2.11.3 → 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 +5 -5
  2. data/CHANGELOG.md +296 -0
  3. data/LICENSE +2 -2
  4. data/README.md +235 -242
  5. data/bin/sprockets +1 -0
  6. data/lib/rake/sprocketstask.rb +5 -4
  7. data/lib/sprockets/asset.rb +143 -210
  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 -29
  65. data/lib/sprockets/sass_processor.rb +292 -0
  66. data/lib/sprockets/sass_template.rb +12 -53
  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 +100 -80
  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 -58
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,267 +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
- options[:compress] ||= File.extname(filename) == '.gz'
143
-
161
+ # Returns nothing.
162
+ def write_to(filename)
144
163
  FileUtils.mkdir_p File.dirname(filename)
145
164
 
146
- File.open("#{filename}+", 'wb') do |f|
147
- if options[:compress]
148
- # Run contents through `Zlib`
149
- gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
150
- gz.mtime = mtime.to_i
151
- gz.write to_s
152
- gz.close
153
- else
154
- # Write out as is
155
- f.write to_s
156
- end
165
+ PathUtils.atomic_write(filename) do |f|
166
+ f.write source
157
167
  end
158
168
 
159
- # Atomic write
160
- FileUtils.mv("#{filename}+", filename)
161
-
162
169
  # Set mtime correctly
163
170
  File.utime(mtime, mtime, filename)
164
171
 
165
172
  nil
166
- ensure
167
- # Ensure tmp file gets cleaned up
168
- FileUtils.rm("#{filename}+") if File.exist?("#{filename}+")
169
173
  end
170
174
 
171
- # Pretty inspect
175
+ # Public: Pretty inspect
176
+ #
177
+ # Returns String.
172
178
  def inspect
173
- "#<#{self.class}:0x#{object_id.to_s(16)} " +
174
- "pathname=#{pathname.to_s.inspect}, " +
175
- "mtime=#{mtime.inspect}, " +
176
- "digest=#{digest.inspect}" +
177
- ">"
179
+ "#<#{self.class}:#{object_id.to_s(16)} #{uri.inspect}>"
178
180
  end
179
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.
180
186
  def hash
181
- digest.hash
187
+ id.hash
182
188
  end
183
189
 
184
- # 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.
185
195
  def eql?(other)
186
- other.class == self.class &&
187
- other.logical_path == self.logical_path &&
188
- other.mtime.to_i == self.mtime.to_i &&
189
- other.digest == self.digest
196
+ self.class == other.class && self.id == other.id
190
197
  end
191
198
  alias_method :==, :eql?
192
-
193
- protected
194
- # Internal: String paths that are marked as dependencies after processing.
195
- #
196
- # Default to an empty `Array`.
197
- def dependency_paths
198
- @dependency_paths ||= []
199
- end
200
-
201
- # Internal: `ProccessedAsset`s that are required after processing.
202
- #
203
- # Default to an empty `Array`.
204
- def required_assets
205
- @required_assets ||= []
206
- end
207
-
208
- # Get pathname with its root stripped.
209
- def relative_pathname
210
- @relative_pathname ||= Pathname.new(relativize_root_path(pathname))
211
- end
212
-
213
- # Replace `$root` placeholder with actual environment root.
214
- def expand_root_path(path)
215
- path.to_s.sub(/^\$root/, @root)
216
- end
217
-
218
- # Replace actual environment root with `$root` placeholder.
219
- def relativize_root_path(path)
220
- path.to_s.sub(/^#{Regexp.escape(@root)}/, '$root')
221
- end
222
-
223
- # Check if dependency is fresh.
224
- #
225
- # `dep` is a `Hash` with `path`, `mtime` and `hexdigest` keys.
226
- #
227
- # A `Hash` is used rather than other `Asset` object because we
228
- # want to test non-asset files and directories.
229
- def dependency_fresh?(environment, dep)
230
- path, mtime, hexdigest = dep.pathname.to_s, dep.mtime, dep.digest
231
-
232
- stat = environment.stat(path)
233
-
234
- # If path no longer exists, its definitely stale.
235
- if stat.nil?
236
- return false
237
- end
238
-
239
- # Compare dependency mtime to the actual mtime. If the
240
- # dependency mtime is newer than the actual mtime, the file
241
- # hasn't changed since we created this `Asset` instance.
242
- #
243
- # However, if the mtime is newer it doesn't mean the asset is
244
- # stale. Many deployment environments may recopy or recheckout
245
- # assets on each deploy. In this case the mtime would be the
246
- # time of deploy rather than modified time.
247
- #
248
- # Note: to_i is used in eql? and write_to we assume fidelity of 1 second
249
- # if people save files more frequently than 1 second sprockets may
250
- # not pick it up, by design
251
- if mtime.to_i >= stat.mtime.to_i
252
- return true
253
- end
254
-
255
- digest = environment.file_digest(path)
256
-
257
- # If the mtime is newer, do a full digest comparsion. Return
258
- # fresh if the digests match.
259
- if hexdigest == digest.hexdigest
260
- return true
261
- end
262
-
263
- # Otherwise, its stale.
264
- false
265
- end
266
199
  end
267
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