sprockets 2.3.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sprockets might be problematic. Click here for more details.

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