sprockets 4.0.0.beta6 → 4.0.0.beta7

Sign up to get free protection for your applications and to get access to all the features.
@@ -72,10 +72,14 @@ module Sprockets
72
72
  register_mime_type 'audio/aiff', extensions: ['.aiff']
73
73
  register_mime_type 'audio/mpeg', extensions: ['.mp3', '.mp2', '.m2a', '.m3a']
74
74
  register_mime_type 'application/ogg', extensions: ['.ogx']
75
+ register_mime_type 'audio/ogg', extensions: ['.ogg', '.oga']
75
76
  register_mime_type 'audio/midi', extensions: ['.midi', '.mid']
76
77
  register_mime_type 'video/avi', extensions: ['.avi']
77
78
  register_mime_type 'audio/wave', extensions: ['.wav', '.wave']
78
79
  register_mime_type 'video/mp4', extensions: ['.mp4', '.m4v']
80
+ register_mime_type 'audio/aac', extensions: ['.aac']
81
+ register_mime_type 'audio/mp4', extensions: ['.m4a']
82
+ register_mime_type 'audio/flac', extensions: ['.flac']
79
83
 
80
84
  # Common font types
81
85
  register_mime_type 'application/vnd.ms-fontobject', extensions: ['.eot']
@@ -102,9 +106,9 @@ module Sprockets
102
106
  env.default_processors_for(type, file_type)
103
107
  end
104
108
 
105
- require 'sprockets/source_map_comment_processor'
109
+ require 'sprockets/add_source_map_comment_to_asset_processor'
106
110
  register_pipeline :debug do
107
- [SourceMapCommentProcessor]
111
+ [AddSourceMapCommentToAssetProcessor]
108
112
  end
109
113
 
110
114
  require 'sprockets/directive_processor'
@@ -184,6 +188,7 @@ module Sprockets
184
188
  text/sass
185
189
  text/scss
186
190
  text/yaml
191
+ text/eco
187
192
  ), 'application/\2+ruby', '.erb', ERBProcessor)
188
193
 
189
194
  register_mime_type 'application/html+ruby', extensions: ['.html.erb', '.erb', '.rhtml'], charset: :html
@@ -3,8 +3,31 @@ require 'sprockets/uri_utils'
3
3
  require 'sprockets/path_utils'
4
4
 
5
5
  module Sprockets
6
- class SourceMapCommentProcessor
6
+ # This is a processor designed to add a source map "comment"
7
+ # to the bottom of a css or JS file that is serving a source
8
+ # map. An example of a comment might look like this
9
+ #
10
+ # //# application.js-80af0efcc960fc2ac93eda2f7b12e3db40ab360bf6ea269ceed3bea3678326f9.map
11
+ #
12
+ # As an asset is built it gets source map information added
13
+ # to the `asset.to_hash[:metadata][:map]` key. This contains all the
14
+ # information that is needed to build a source map file.
15
+ #
16
+ # To add this comment we must have an asset we can link to.
17
+ # To do this we ensure that the original aset is loaded, then
18
+ # we use a use a special mime type. For example `application/js-sourcemap+json`
19
+ # for a JS source map.
20
+ #
21
+ # This will trigger a new asset to be loaded and generated by the
22
+ # `SourceMapProcessor` processor.
23
+ #
24
+ # Finally once we have that file, we can generate a link to it
25
+ # with it's full fingerprint. This is done and then
26
+ # added to the original asset as a comment at the bottom.
27
+ #
28
+ class AddSourceMapCommentToAssetProcessor
7
29
  def self.call(input)
30
+
8
31
  case input[:content_type]
9
32
  when "application/javascript"
10
33
  comment = "\n//# sourceMappingURL=%s"
@@ -16,6 +16,18 @@ require 'sprockets/source_map_utils'
16
16
  require 'sprockets/uri_tar'
17
17
 
18
18
  module Sprockets
19
+
20
+ class DoubleLinkError < Sprockets::Error
21
+ def initialize(parent_filename:, logical_path:, last_filename:, filename:)
22
+ message = String.new
23
+ message << "Multiple files with the same output path cannot be linked (#{logical_path.inspect})\n"
24
+ message << "In #{parent_filename.inspect} these files were linked:\n"
25
+ message << " - #{last_filename}\n"
26
+ message << " - #{filename}\n"
27
+ super(message)
28
+ end
29
+ end
30
+
19
31
  # `Base` class for `Environment` and `CachedEnvironment`.
20
32
  class Base
21
33
  include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils, SourceMapUtils
@@ -73,14 +85,26 @@ module Sprockets
73
85
  def find_all_linked_assets(*args)
74
86
  return to_enum(__method__, *args) unless block_given?
75
87
 
76
- asset = find_asset(*args)
88
+ parent_asset = asset = find_asset(*args)
77
89
  return unless asset
78
90
 
79
91
  yield asset
80
92
  stack = asset.links.to_a
93
+ linked_paths = {}
81
94
 
82
95
  while uri = stack.shift
83
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
+ )
106
+ end
107
+ linked_paths[asset.logical_path] = asset.filename
84
108
  stack = asset.links.to_a + stack
85
109
  end
86
110
 
@@ -186,9 +186,10 @@ module Sprockets
186
186
  asset
187
187
  end
188
188
 
189
- # Returns a Base64-encoded `data:` URI with the contents of the
190
- # asset at the specified path, and marks that path as a dependency
191
- # of the current file.
189
+ # Returns a `data:` URI with the contents of the asset at the specified
190
+ # path, and marks that path as a dependency of the current file.
191
+ #
192
+ # Uses URI encoding for SVG files, base64 encoding for all the other files.
192
193
  #
193
194
  # Use `asset_data_uri` from ERB with CSS or JavaScript assets:
194
195
  #
@@ -198,8 +199,11 @@ module Sprockets
198
199
  #
199
200
  def asset_data_uri(path)
200
201
  asset = depend_on_asset(path)
201
- data = EncodingUtils.base64(asset.source)
202
- "data:#{asset.content_type};base64,#{Rack::Utils.escape(data)}"
202
+ if asset.content_type == 'image/svg+xml'
203
+ svg_asset_data_uri(asset)
204
+ else
205
+ base64_asset_data_uri(asset)
206
+ end
203
207
  end
204
208
 
205
209
  # Expands logical path to full url to asset.
@@ -251,5 +255,50 @@ Extend your environment context with a custom method.
251
255
  def stylesheet_path(path)
252
256
  asset_path(path, type: :stylesheet)
253
257
  end
258
+
259
+ protected
260
+
261
+ # Returns a URI-encoded data URI (always "-quoted).
262
+ def svg_asset_data_uri(asset)
263
+ svg = asset.source.dup
264
+ optimize_svg_for_uri_escaping!(svg)
265
+ data = Rack::Utils.escape(svg)
266
+ optimize_quoted_uri_escapes!(data)
267
+ "\"data:#{asset.content_type};charset=utf-8,#{data}\""
268
+ end
269
+
270
+ # Returns a Base64-encoded data URI.
271
+ def base64_asset_data_uri(asset)
272
+ data = Rack::Utils.escape(EncodingUtils.base64(asset.source))
273
+ "data:#{asset.content_type};base64,#{data}"
274
+ end
275
+
276
+ # Optimizes an SVG for being URI-escaped.
277
+ #
278
+ # This method only performs these basic but crucial optimizations:
279
+ # * Replaces " with ', because ' does not need escaping.
280
+ # * Removes comments, meta, doctype, and newlines.
281
+ # * Collapses whitespace.
282
+ def optimize_svg_for_uri_escaping!(svg)
283
+ # Remove comments, xml meta, and doctype
284
+ svg.gsub!(/<!--.*?-->|<\?.*?\?>|<!.*?>/m, '')
285
+ # Replace consecutive whitespace and newlines with a space
286
+ svg.gsub!(/\s+/, ' ')
287
+ # Collapse inter-tag whitespace
288
+ svg.gsub!('> <', '><')
289
+ # Replace " with '
290
+ svg.gsub!(/([\w:])="(.*?)"/, "\\1='\\2'")
291
+ svg.strip!
292
+ end
293
+
294
+ # Un-escapes characters in the given URI-escaped string that do not need
295
+ # escaping in "-quoted data URIs.
296
+ def optimize_quoted_uri_escapes!(escaped)
297
+ escaped.gsub!('%3D', '=')
298
+ escaped.gsub!('%3A', ':')
299
+ escaped.gsub!('%2F', '/')
300
+ escaped.gsub!('%27', "'")
301
+ escaped.tr!('+', ' ')
302
+ end
254
303
  end
255
304
  end
@@ -81,6 +81,8 @@ module Sprockets
81
81
  asset[:metadata][:links].map! { |uri| expand_from_root(uri) } if asset[:metadata][:links]
82
82
  asset[:metadata][:stubbed].map! { |uri| expand_from_root(uri) } if asset[:metadata][:stubbed]
83
83
  asset[:metadata][:required].map! { |uri| expand_from_root(uri) } if asset[:metadata][:required]
84
+ asset[:metadata][:to_load].map! { |uri| expand_from_root(uri) } if asset[:metadata][:to_load]
85
+ asset[:metadata][:to_link].map! { |uri| expand_from_root(uri) } if asset[:metadata][:to_link]
84
86
  asset[:metadata][:dependencies].map! { |uri| uri.start_with?("file-digest://") ? expand_from_root(uri) : uri } if asset[:metadata][:dependencies]
85
87
 
86
88
  asset[:metadata].each_key do |k|
@@ -140,7 +142,6 @@ module Sprockets
140
142
 
141
143
  # Read into memory and process if theres a processor pipeline
142
144
  if processors.any?
143
-
144
145
  result = call_processors(processors, {
145
146
  environment: self,
146
147
  cache: self.cache,
@@ -224,6 +225,16 @@ module Sprockets
224
225
  cached_asset[:metadata][:required].map! { |uri| compress_from_root(uri) }
225
226
  end
226
227
 
228
+ if cached_asset[:metadata][:to_load] && !cached_asset[:metadata][:to_load].empty?
229
+ cached_asset[:metadata][:to_load] = cached_asset[:metadata][:to_load].dup
230
+ cached_asset[:metadata][:to_load].map! { |uri| compress_from_root(uri) }
231
+ end
232
+
233
+ if cached_asset[:metadata][:to_link] && !cached_asset[:metadata][:to_link].empty?
234
+ cached_asset[:metadata][:to_link] = cached_asset[:metadata][:to_link].dup
235
+ cached_asset[:metadata][:to_link].map! { |uri| compress_from_root(uri) }
236
+ end
237
+
227
238
  if cached_asset[:metadata][:dependencies] && !cached_asset[:metadata][:dependencies].empty?
228
239
  cached_asset[:metadata][:dependencies] = cached_asset[:metadata][:dependencies].dup
229
240
  cached_asset[:metadata][:dependencies].map! do |uri|
@@ -165,7 +165,12 @@ module Sprockets
165
165
  filenames = []
166
166
  concurrent_exporters = []
167
167
 
168
+ assets_to_export = Concurrent::Array.new
168
169
  find(*args) do |asset|
170
+ assets_to_export << asset
171
+ end
172
+
173
+ assets_to_export.each do |asset|
169
174
  mtime = Time.now.iso8601
170
175
  files[asset.digest_path] = {
171
176
  'logical_path' => asset.logical_path,
@@ -54,11 +54,11 @@ module Sprockets
54
54
  if fingerprint
55
55
  if_match = fingerprint
56
56
  elsif env['HTTP_IF_MATCH']
57
- if_match = env['HTTP_IF_MATCH'][/^"(\w+)"$/, 1]
57
+ if_match = env['HTTP_IF_MATCH'][/"(\w+)"$/, 1]
58
58
  end
59
59
 
60
60
  if env['HTTP_IF_NONE_MATCH']
61
- if_none_match = env['HTTP_IF_NONE_MATCH'][/^"(\w+)"$/, 1]
61
+ if_none_match = env['HTTP_IF_NONE_MATCH'][/"(\w+)"$/, 1]
62
62
  end
63
63
 
64
64
  # Look up the asset.
@@ -2,24 +2,32 @@
2
2
  require 'set'
3
3
 
4
4
  module Sprockets
5
- class SourceMapProcessor
6
- def self.original_content_type(source_map_content_type, error_when_not_found: true)
7
- case source_map_content_type
8
- when "application/js-sourcemap+json"
9
- "application/javascript"
10
- when "application/css-sourcemap+json"
11
- "text/css"
12
- else
13
- fail(source_map_content_type) if error_when_not_found
14
- source_map_content_type
15
- end
16
- end
17
5
 
6
+ # The purpose of this class is to generate a source map file
7
+ # that can be read and understood by browsers.
8
+ #
9
+ # When a file is passed in it will have a `application/js-sourcemap+json`
10
+ # or `application/css-sourcemap+json` mime type. The filename will be
11
+ # match the original asset. The original asset is loaded. As it
12
+ # gets processed by Sprockets it will aquire all information
13
+ # needed to build a source map file in the `asset.to_hash[:metadata][:map]`
14
+ # key.
15
+ #
16
+ # The output is an asset with a properly formatted source map file:
17
+ #
18
+ # {
19
+ # "version": 3,
20
+ # "sources": ["foo.js"],
21
+ # "names": [ ],
22
+ # "mappings": "AAAA,GAAIA"
23
+ # }
24
+ #
25
+ class SourceMapProcessor
18
26
  def self.call(input)
19
27
  links = Set.new(input[:metadata][:links])
20
28
  env = input[:environment]
21
29
 
22
- uri, _ = env.resolve!(input[:filename], accept: original_content_type(input[:content_type]))
30
+ uri, _ = env.resolve!(input[:filename], accept: self.original_content_type(input[:content_type]))
23
31
  asset = env.load(uri)
24
32
  map = asset.metadata[:map]
25
33
 
@@ -42,5 +50,17 @@ module Sprockets
42
50
 
43
51
  { data: json, links: links, dependencies: dependencies }
44
52
  end
53
+
54
+ def self.original_content_type(source_map_content_type, error_when_not_found: true)
55
+ case source_map_content_type
56
+ when "application/js-sourcemap+json"
57
+ "application/javascript"
58
+ when "application/css-sourcemap+json"
59
+ "text/css"
60
+ else
61
+ fail(source_map_content_type) if error_when_not_found
62
+ source_map_content_type
63
+ end
64
+ end
45
65
  end
46
66
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Sprockets
3
- VERSION = "4.0.0.beta6"
3
+ VERSION = "4.0.0.beta7"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sprockets
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.beta6
4
+ version: 4.0.0.beta7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Stephenson
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-11-15 00:00:00.000000000 Z
12
+ date: 2018-03-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -45,6 +45,20 @@ dependencies:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '1.0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: m
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
48
62
  - !ruby/object:Gem::Dependency
49
63
  name: babel-transpiler
50
64
  requirement: !ruby/object:Gem::Requirement
@@ -304,6 +318,7 @@ files:
304
318
  - bin/sprockets
305
319
  - lib/rake/sprocketstask.rb
306
320
  - lib/sprockets.rb
321
+ - lib/sprockets/add_source_map_comment_to_asset_processor.rb
307
322
  - lib/sprockets/asset.rb
308
323
  - lib/sprockets/autoload.rb
309
324
  - lib/sprockets/autoload/babel.rb
@@ -370,7 +385,6 @@ files:
370
385
  - lib/sprockets/sassc_compressor.rb
371
386
  - lib/sprockets/sassc_processor.rb
372
387
  - lib/sprockets/server.rb
373
- - lib/sprockets/source_map_comment_processor.rb
374
388
  - lib/sprockets/source_map_processor.rb
375
389
  - lib/sprockets/source_map_utils.rb
376
390
  - lib/sprockets/transformers.rb
@@ -402,7 +416,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
402
416
  version: 1.3.1
403
417
  requirements: []
404
418
  rubyforge_project: sprockets
405
- rubygems_version: 2.6.14
419
+ rubygems_version: 2.7.6
406
420
  signing_key:
407
421
  specification_version: 4
408
422
  summary: Rack-based asset packaging system