sprockets 3.0.0 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +76 -0
  3. data/README.md +426 -404
  4. data/bin/sprockets +12 -7
  5. data/lib/rake/sprocketstask.rb +3 -2
  6. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  7. data/lib/sprockets/asset.rb +33 -24
  8. data/lib/sprockets/autoload/babel.rb +8 -0
  9. data/lib/sprockets/autoload/closure.rb +1 -0
  10. data/lib/sprockets/autoload/coffee_script.rb +1 -0
  11. data/lib/sprockets/autoload/eco.rb +1 -0
  12. data/lib/sprockets/autoload/ejs.rb +1 -0
  13. data/lib/sprockets/autoload/jsminc.rb +8 -0
  14. data/lib/sprockets/autoload/sass.rb +1 -0
  15. data/lib/sprockets/autoload/sassc.rb +8 -0
  16. data/lib/sprockets/autoload/uglifier.rb +1 -0
  17. data/lib/sprockets/autoload/yui.rb +1 -0
  18. data/lib/sprockets/autoload/zopfli.rb +7 -0
  19. data/lib/sprockets/autoload.rb +5 -0
  20. data/lib/sprockets/babel_processor.rb +66 -0
  21. data/lib/sprockets/base.rb +61 -13
  22. data/lib/sprockets/bower.rb +5 -2
  23. data/lib/sprockets/bundle.rb +44 -4
  24. data/lib/sprockets/cache/file_store.rb +32 -7
  25. data/lib/sprockets/cache/memory_store.rb +9 -0
  26. data/lib/sprockets/cache/null_store.rb +8 -0
  27. data/lib/sprockets/cache.rb +42 -5
  28. data/lib/sprockets/cached_environment.rb +14 -19
  29. data/lib/sprockets/closure_compressor.rb +6 -11
  30. data/lib/sprockets/coffee_script_processor.rb +19 -5
  31. data/lib/sprockets/compressing.rb +62 -2
  32. data/lib/sprockets/configuration.rb +3 -7
  33. data/lib/sprockets/context.rb +98 -23
  34. data/lib/sprockets/dependencies.rb +9 -8
  35. data/lib/sprockets/digest_utils.rb +104 -60
  36. data/lib/sprockets/directive_processor.rb +45 -35
  37. data/lib/sprockets/eco_processor.rb +3 -2
  38. data/lib/sprockets/ejs_processor.rb +3 -2
  39. data/lib/sprockets/encoding_utils.rb +8 -4
  40. data/lib/sprockets/environment.rb +9 -4
  41. data/lib/sprockets/erb_processor.rb +28 -21
  42. data/lib/sprockets/errors.rb +1 -1
  43. data/lib/sprockets/exporters/base.rb +71 -0
  44. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  45. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  46. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  47. data/lib/sprockets/exporting.rb +73 -0
  48. data/lib/sprockets/file_reader.rb +1 -0
  49. data/lib/sprockets/http_utils.rb +26 -6
  50. data/lib/sprockets/jsminc_compressor.rb +32 -0
  51. data/lib/sprockets/jst_processor.rb +11 -10
  52. data/lib/sprockets/loader.rb +239 -70
  53. data/lib/sprockets/manifest.rb +97 -44
  54. data/lib/sprockets/manifest_utils.rb +9 -6
  55. data/lib/sprockets/mime.rb +8 -42
  56. data/lib/sprockets/npm.rb +52 -0
  57. data/lib/sprockets/path_dependency_utils.rb +3 -11
  58. data/lib/sprockets/path_digest_utils.rb +2 -1
  59. data/lib/sprockets/path_utils.rb +106 -21
  60. data/lib/sprockets/paths.rb +1 -0
  61. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  62. data/lib/sprockets/processing.rb +31 -51
  63. data/lib/sprockets/processor_utils.rb +81 -15
  64. data/lib/sprockets/resolve.rb +182 -95
  65. data/lib/sprockets/sass_cache_store.rb +1 -0
  66. data/lib/sprockets/sass_compressor.rb +21 -17
  67. data/lib/sprockets/sass_functions.rb +1 -0
  68. data/lib/sprockets/sass_importer.rb +1 -0
  69. data/lib/sprockets/sass_processor.rb +45 -17
  70. data/lib/sprockets/sassc_compressor.rb +56 -0
  71. data/lib/sprockets/sassc_processor.rb +297 -0
  72. data/lib/sprockets/server.rb +57 -34
  73. data/lib/sprockets/source_map_processor.rb +66 -0
  74. data/lib/sprockets/source_map_utils.rb +483 -0
  75. data/lib/sprockets/transformers.rb +63 -35
  76. data/lib/sprockets/uglifier_compressor.rb +23 -20
  77. data/lib/sprockets/unloaded_asset.rb +139 -0
  78. data/lib/sprockets/uri_tar.rb +99 -0
  79. data/lib/sprockets/uri_utils.rb +15 -14
  80. data/lib/sprockets/utils/gzip.rb +99 -0
  81. data/lib/sprockets/utils.rb +43 -59
  82. data/lib/sprockets/version.rb +2 -1
  83. data/lib/sprockets/yui_compressor.rb +5 -14
  84. data/lib/sprockets.rb +103 -33
  85. metadata +151 -22
  86. data/LICENSE +0 -21
  87. data/lib/sprockets/coffee_script_template.rb +0 -6
  88. data/lib/sprockets/eco_template.rb +0 -6
  89. data/lib/sprockets/ejs_template.rb +0 -6
  90. data/lib/sprockets/engines.rb +0 -81
  91. data/lib/sprockets/erb_template.rb +0 -6
  92. data/lib/sprockets/legacy.rb +0 -314
  93. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  94. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  95. data/lib/sprockets/sass_template.rb +0 -7
@@ -1,10 +1,9 @@
1
- require 'pathname'
1
+ # frozen_string_literal: true
2
2
  require 'rack/utils'
3
3
  require 'set'
4
4
  require 'sprockets/errors'
5
5
 
6
6
  module Sprockets
7
- # Deprecated: `Context` provides helper methods to all processors.
8
7
  # They are typically accessed by ERB templates. You can mix in custom helpers
9
8
  # by injecting them into `Environment#context_class`. Do not mix them into
10
9
  # `Context` directly.
@@ -19,10 +18,25 @@ module Sprockets
19
18
  # The `Context` also collects dependencies declared by
20
19
  # assets. See `DirectiveProcessor` for an example of this.
21
20
  class Context
22
- attr_reader :environment, :filename, :pathname
21
+ # Internal: Proxy for ENV that keeps track of the environment variables used
22
+ class ENVProxy < SimpleDelegator
23
+ def initialize(context)
24
+ @context = context
25
+ super(ENV)
26
+ end
27
+
28
+ def [](key)
29
+ @context.depend_on_env(key)
30
+ super
31
+ end
32
+
33
+ def fetch(key, *)
34
+ @context.depend_on_env(key)
35
+ super
36
+ end
37
+ end
23
38
 
24
- # Deprecated
25
- attr_accessor :__LINE__
39
+ attr_reader :environment, :filename
26
40
 
27
41
  def initialize(input)
28
42
  @environment = input[:environment]
@@ -31,7 +45,6 @@ module Sprockets
31
45
  @logical_path = input[:name]
32
46
  @filename = input[:filename]
33
47
  @dirname = File.dirname(@filename)
34
- @pathname = Pathname.new(@filename)
35
48
  @content_type = input[:content_type]
36
49
 
37
50
  @required = Set.new(@metadata[:required])
@@ -47,6 +60,10 @@ module Sprockets
47
60
  dependencies: @dependencies }
48
61
  end
49
62
 
63
+ def env_proxy
64
+ ENVProxy.new(self)
65
+ end
66
+
50
67
  # Returns the environment path that contains the file.
51
68
  #
52
69
  # If `app/javascripts` and `app/stylesheets` are in your path, and
@@ -79,13 +96,13 @@ module Sprockets
79
96
  # resolve("./bar.js")
80
97
  # # => "file:///path/to/app/javascripts/bar.js?type=application/javascript"
81
98
  #
82
- # path - String logical or absolute path
83
- # options
84
- # accept - String content accept type
99
+ # path - String logical or absolute path
100
+ # accept - String content accept type
85
101
  #
86
102
  # Returns an Asset URI String.
87
- def resolve(path, options = {})
88
- uri, deps = environment.resolve!(path, options.merge(base_path: @dirname))
103
+ def resolve(path, **kargs)
104
+ kargs[:base_path] = @dirname
105
+ uri, deps = environment.resolve!(path, **kargs)
89
106
  @dependencies.merge(deps)
90
107
  uri
91
108
  end
@@ -105,13 +122,13 @@ module Sprockets
105
122
  # including it.
106
123
  #
107
124
  # This is used for caching purposes. Any changes made to
108
- # the dependency file with invalidate the cache of the
125
+ # the dependency file will invalidate the cache of the
109
126
  # source file.
110
127
  def depend_on(path)
111
- if environment.absolute_path?(path) && environment.directory?(path)
128
+ if environment.absolute_path?(path) && environment.stat(path)
112
129
  @dependencies << environment.build_file_digest_uri(path)
113
130
  else
114
- resolve(path, compat: false)
131
+ resolve(path)
115
132
  end
116
133
  nil
117
134
  end
@@ -121,10 +138,19 @@ module Sprockets
121
138
  #
122
139
  # This is used for caching purposes. Any changes that would
123
140
  # invalidate the dependency asset will invalidate the source
124
- # file. Unlike `depend_on`, this will include recursively include
141
+ # file. Unlike `depend_on`, this will recursively include
125
142
  # the target asset's dependencies.
126
143
  def depend_on_asset(path)
127
- load(resolve(path, compat: false))
144
+ load(resolve(path))
145
+ end
146
+
147
+ # `depend_on_env` allows you to state a dependency on an environment
148
+ # variable.
149
+ #
150
+ # This is used for caching purposes. Any changes in the value of the
151
+ # environment variable will invalidate the cache of the source file.
152
+ def depend_on_env(key)
153
+ @dependencies << "env:#{key}"
128
154
  end
129
155
 
130
156
  # `require_asset` declares `path` as a dependency of the file. The
@@ -137,7 +163,7 @@ module Sprockets
137
163
  # <%= require_asset "#{framework}.js" %>
138
164
  #
139
165
  def require_asset(path)
140
- @required << resolve(path, accept: @content_type, pipeline: :self, compat: false)
166
+ @required << resolve(path, accept: @content_type, pipeline: :self)
141
167
  nil
142
168
  end
143
169
 
@@ -145,7 +171,7 @@ module Sprockets
145
171
  # `path` must be an asset which may or may not already be included
146
172
  # in the bundle.
147
173
  def stub_asset(path)
148
- @stubbed << resolve(path, accept: @content_type, pipeline: :self, compat: false)
174
+ @stubbed << resolve(path, accept: @content_type, pipeline: :self)
149
175
  nil
150
176
  end
151
177
 
@@ -160,9 +186,10 @@ module Sprockets
160
186
  asset
161
187
  end
162
188
 
163
- # Returns a Base64-encoded `data:` URI with the contents of the
164
- # asset at the specified path, and marks that path as a dependency
165
- # 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.
166
193
  #
167
194
  # Use `asset_data_uri` from ERB with CSS or JavaScript assets:
168
195
  #
@@ -172,8 +199,11 @@ module Sprockets
172
199
  #
173
200
  def asset_data_uri(path)
174
201
  asset = depend_on_asset(path)
175
- data = EncodingUtils.base64(asset.source)
176
- "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
177
207
  end
178
208
 
179
209
  # Expands logical path to full url to asset.
@@ -225,5 +255,50 @@ Extend your environment context with a custom method.
225
255
  def stylesheet_path(path)
226
256
  asset_path(path, type: :stylesheet)
227
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
228
303
  end
229
304
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/digest_utils'
2
3
  require 'sprockets/path_digest_utils'
3
4
  require 'sprockets/uri_utils'
@@ -51,18 +52,18 @@ module Sprockets
51
52
  end
52
53
  alias_method :depend_on, :add_dependency
53
54
 
54
- # Internal: Resolve set of dependency URIs.
55
- #
56
- # Returns Array of resolved Objects.
57
- def resolve_dependencies(uris)
58
- uris.map { |uri| resolve_dependency(uri) }
59
- end
60
-
61
55
  # Internal: Resolve dependency URIs.
62
56
  #
63
57
  # Returns resolved Object.
64
58
  def resolve_dependency(str)
65
- scheme = str[/([^:]+)/, 1]
59
+ # Optimize for the most common scheme to
60
+ # save 22k allocations on an average Spree app.
61
+ scheme = if str.start_with?('file-digest:'.freeze)
62
+ 'file-digest'.freeze
63
+ else
64
+ str[/([^:]+)/, 1]
65
+ end
66
+
66
67
  if resolver = config[:dependency_resolvers][scheme]
67
68
  resolver.call(self, str)
68
69
  else
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'digest/md5'
2
3
  require 'digest/sha1'
3
4
  require 'digest/sha2'
@@ -34,6 +35,59 @@ module Sprockets
34
35
  DIGEST_SIZES[bytes.bytesize]
35
36
  end
36
37
 
38
+ ADD_VALUE_TO_DIGEST = {
39
+ String => ->(val, digest) { digest << val },
40
+ FalseClass => ->(val, digest) { digest << 'FalseClass'.freeze },
41
+ TrueClass => ->(val, digest) { digest << 'TrueClass'.freeze },
42
+ NilClass => ->(val, digest) { digest << 'NilClass'.freeze },
43
+
44
+ Symbol => ->(val, digest) {
45
+ digest << 'Symbol'.freeze
46
+ digest << val.to_s
47
+ },
48
+ Integer => ->(val, digest) {
49
+ digest << 'Integer'.freeze
50
+ digest << val.to_s
51
+ },
52
+ Array => ->(val, digest) {
53
+ digest << 'Array'.freeze
54
+ val.each do |element|
55
+ ADD_VALUE_TO_DIGEST[element.class].call(element, digest)
56
+ end
57
+ },
58
+ Hash => ->(val, digest) {
59
+ digest << 'Hash'.freeze
60
+ val.sort.each do |array|
61
+ ADD_VALUE_TO_DIGEST[Array].call(array, digest)
62
+ end
63
+ },
64
+ Set => ->(val, digest) {
65
+ digest << 'Set'.freeze
66
+ ADD_VALUE_TO_DIGEST[Array].call(val, digest)
67
+ },
68
+ Encoding => ->(val, digest) {
69
+ digest << 'Encoding'.freeze
70
+ digest << val.name
71
+ },
72
+ }
73
+ if 0.class != Integer # Ruby < 2.4
74
+ ADD_VALUE_TO_DIGEST[Fixnum] = ->(val, digest) {
75
+ digest << 'Integer'.freeze
76
+ digest << val.to_s
77
+ }
78
+ ADD_VALUE_TO_DIGEST[Bignum] = ->(val, digest) {
79
+ digest << 'Integer'.freeze
80
+ digest << val.to_s
81
+ }
82
+ end
83
+
84
+ ADD_VALUE_TO_DIGEST.compare_by_identity.rehash
85
+
86
+ ADD_VALUE_TO_DIGEST.default_proc = ->(_, val) {
87
+ raise TypeError, "couldn't digest #{ val }"
88
+ }
89
+ private_constant :ADD_VALUE_TO_DIGEST
90
+
37
91
  # Internal: Generate a hexdigest for a nested JSON serializable object.
38
92
  #
39
93
  # This is used for generating cache keys, so its pretty important its
@@ -43,48 +97,18 @@ module Sprockets
43
97
  #
44
98
  # Returns a String digest of the object.
45
99
  def digest(obj)
46
- digest = digest_class.new
47
- queue = [obj]
48
-
49
- while queue.length > 0
50
- obj = queue.shift
51
- klass = obj.class
52
-
53
- if klass == String
54
- digest << obj
55
- elsif klass == Symbol
56
- digest << 'Symbol'
57
- digest << obj.to_s
58
- elsif klass == Fixnum
59
- digest << 'Fixnum'
60
- digest << obj.to_s
61
- elsif klass == Bignum
62
- digest << 'Bignum'
63
- digest << obj.to_s
64
- elsif klass == TrueClass
65
- digest << 'TrueClass'
66
- elsif klass == FalseClass
67
- digest << 'FalseClass'
68
- elsif klass == NilClass
69
- digest << 'NilClass'
70
- elsif klass == Array
71
- digest << 'Array'
72
- queue.concat(obj)
73
- elsif klass == Hash
74
- digest << 'Hash'
75
- queue.concat(obj.sort)
76
- elsif klass == Set
77
- digest << 'Set'
78
- queue.concat(obj.to_a)
79
- elsif klass == Encoding
80
- digest << 'Encoding'
81
- digest << obj.name
82
- else
83
- raise TypeError, "couldn't digest #{klass}"
84
- end
85
- end
100
+ build_digest(obj).digest
101
+ end
86
102
 
87
- digest.digest
103
+ # Internal: Generate a hexdigest for a nested JSON serializable object.
104
+ #
105
+ # The same as `pack_hexdigest(digest(obj))`.
106
+ #
107
+ # obj - A JSON serializable object.
108
+ #
109
+ # Returns a String digest of the object.
110
+ def hexdigest(obj)
111
+ build_digest(obj).hexdigest!
88
112
  end
89
113
 
90
114
  # Internal: Pack a binary digest to a hex encoded string.
@@ -93,7 +117,16 @@ module Sprockets
93
117
  #
94
118
  # Returns hex String.
95
119
  def pack_hexdigest(bin)
96
- bin.unpack('H*').first
120
+ bin.unpack('H*'.freeze).first
121
+ end
122
+
123
+ # Internal: Unpack a hex encoded digest string into binary bytes.
124
+ #
125
+ # hex - String hex
126
+ #
127
+ # Returns binary String.
128
+ def unpack_hexdigest(hex)
129
+ [hex].pack('H*')
97
130
  end
98
131
 
99
132
  # Internal: Pack a binary digest to a base64 encoded string.
@@ -117,25 +150,20 @@ module Sprockets
117
150
  str
118
151
  end
119
152
 
120
- # Internal: Maps digest class to the named information hash algorithm name.
121
- #
122
- # http://www.iana.org/assignments/named-information/named-information.xhtml
123
- NI_HASH_ALGORITHMS = {
124
- Digest::SHA256 => 'sha-256'.freeze,
125
- Digest::SHA384 => 'sha-384'.freeze,
126
- Digest::SHA512 => 'sha-512'.freeze
153
+ # Internal: Maps digest class to the CSP hash algorithm name.
154
+ HASH_ALGORITHMS = {
155
+ Digest::SHA256 => 'sha256'.freeze,
156
+ Digest::SHA384 => 'sha384'.freeze,
157
+ Digest::SHA512 => 'sha512'.freeze
127
158
  }
128
159
 
129
- # Internal: Generate a "named information" URI for use in the `integrity`
130
- # attribute of an asset tag as per the subresource integrity specification.
160
+ # Public: Generate hash for use in the `integrity` attribute of an asset tag
161
+ # as per the subresource integrity specification.
131
162
  #
132
- # digest - The String byte digest of the asset content.
133
- # content_type - The content-type the asset will be served with. This *must*
134
- # be accurate if provided. Otherwise, subresource integrity
135
- # will block the loading of the asset.
163
+ # digest - The String byte digest of the asset content.
136
164
  #
137
165
  # Returns a String or nil if hash algorithm is incompatible.
138
- def integrity_uri(digest, content_type = nil)
166
+ def integrity_uri(digest)
139
167
  case digest
140
168
  when Digest::Base
141
169
  digest_class = digest.class
@@ -146,11 +174,27 @@ module Sprockets
146
174
  raise TypeError, "unknown digest: #{digest.inspect}"
147
175
  end
148
176
 
149
- if hash_name = NI_HASH_ALGORITHMS[digest_class]
150
- uri = "ni:///#{hash_name};#{pack_urlsafe_base64digest(digest)}"
151
- uri << "?ct=#{content_type}" if content_type
152
- uri
177
+ if hash_name = HASH_ALGORITHMS[digest_class]
178
+ "#{hash_name}-#{pack_base64digest(digest)}"
153
179
  end
154
180
  end
181
+
182
+ # Public: Generate hash for use in the `integrity` attribute of an asset tag
183
+ # as per the subresource integrity specification.
184
+ #
185
+ # digest - The String hexbyte digest of the asset content.
186
+ #
187
+ # Returns a String or nil if hash algorithm is incompatible.
188
+ def hexdigest_integrity_uri(hexdigest)
189
+ integrity_uri(unpack_hexdigest(hexdigest))
190
+ end
191
+
192
+ private
193
+ def build_digest(obj)
194
+ digest = digest_class.new
195
+
196
+ ADD_VALUE_TO_DIGEST[obj.class].call(obj, digest)
197
+ digest
198
+ end
155
199
  end
156
200
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'set'
2
3
  require 'shellwords'
3
4
 
@@ -34,8 +35,6 @@ module Sprockets
34
35
  # env.register_processor('text/css', MyProcessor)
35
36
  #
36
37
  class DirectiveProcessor
37
- VERSION = '1'
38
-
39
38
  # Directives are denoted by a `=` followed by the name, then
40
39
  # argument list.
41
40
  #
@@ -50,18 +49,16 @@ module Sprockets
50
49
  /x
51
50
 
52
51
  def self.instance
53
- @instance ||= new(
54
- # Deprecated: Default to C and Ruby comment styles
55
- comments: ["//", ["/*", "*/"]] + ["#", ["###", "###"]]
56
- )
52
+ # Default to C comment styles
53
+ @instance ||= new(comments: ["//", ["/*", "*/"]])
57
54
  end
58
55
 
59
56
  def self.call(input)
60
57
  instance.call(input)
61
58
  end
62
59
 
63
- def initialize(options = {})
64
- @header_pattern = compile_header_pattern(Array(options[:comments]))
60
+ def initialize(comments: [])
61
+ @header_pattern = compile_header_pattern(Array(comments))
65
62
  end
66
63
 
67
64
  def call(input)
@@ -73,20 +70,28 @@ module Sprockets
73
70
  @uri = input[:uri]
74
71
  @filename = input[:filename]
75
72
  @dirname = File.dirname(@filename)
76
- @content_type = input[:content_type]
73
+ # If loading a source map file like `application.js.map` resolve
74
+ # dependencies using `.js` instead of `.js.map`
75
+ @content_type = SourceMapProcessor.original_content_type(input[:content_type], error_when_not_found: false)
77
76
  @required = Set.new(input[:metadata][:required])
78
77
  @stubbed = Set.new(input[:metadata][:stubbed])
79
78
  @links = Set.new(input[:metadata][:links])
80
79
  @dependencies = Set.new(input[:metadata][:dependencies])
80
+ @to_link = Set.new
81
+ @to_load = Set.new
81
82
 
82
83
  data, directives = process_source(input[:data])
83
84
  process_directives(directives)
84
85
 
85
- { data: data,
86
- required: @required,
87
- stubbed: @stubbed,
88
- links: @links,
89
- dependencies: @dependencies }
86
+ {
87
+ data: data,
88
+ required: @required,
89
+ stubbed: @stubbed,
90
+ links: @links,
91
+ to_load: @to_load,
92
+ to_link: @to_link,
93
+ dependencies: @dependencies
94
+ }
90
95
  end
91
96
 
92
97
  protected
@@ -116,9 +121,9 @@ module Sprockets
116
121
 
117
122
  header, directives = extract_directives(header)
118
123
 
119
- data = ""
124
+ data = +""
120
125
  data.force_encoding(body.encoding)
121
- data << header << "\n" unless header.empty?
126
+ data << header unless header.empty?
122
127
  data << body
123
128
  # Ensure body ends in a new line
124
129
  data << "\n" if data.length > 0 && data[-1] != "\n"
@@ -134,7 +139,7 @@ module Sprockets
134
139
  # [[1, "require", "foo"], [2, "require", "bar"]]
135
140
  #
136
141
  def extract_directives(header)
137
- processed_header = ""
142
+ processed_header = +""
138
143
  directives = []
139
144
 
140
145
  header.lines.each_with_index do |line, index|
@@ -149,7 +154,11 @@ module Sprockets
149
154
  processed_header << line
150
155
  end
151
156
 
152
- return processed_header.chomp, directives
157
+ processed_header.chomp!
158
+ # Ensure header ends in a new line like before it was processed
159
+ processed_header << "\n" if processed_header.length > 0 && header[-1] == "\n"
160
+
161
+ return processed_header, directives
153
162
  end
154
163
 
155
164
  # Gathers comment directives in the source and processes them.
@@ -162,7 +171,7 @@ module Sprockets
162
171
  # `process_require_glob_directive`.
163
172
  #
164
173
  # class DirectiveProcessor < Sprockets::DirectiveProcessor
165
- # def process_require_glob_directive
174
+ # def process_require_glob_directive(glob)
166
175
  # Dir["#{dirname}/#{glob}"].sort.each do |filename|
167
176
  # require(filename)
168
177
  # end
@@ -187,7 +196,7 @@ module Sprockets
187
196
 
188
197
  # The `require` directive functions similar to Ruby's own `require`.
189
198
  # It provides a way to declare a dependency on a file in your path
190
- # and ensures its only loaded once before the source file.
199
+ # and ensures it's only loaded once before the source file.
191
200
  #
192
201
  # `require` works with files in the environment path:
193
202
  #
@@ -265,15 +274,15 @@ module Sprockets
265
274
  # it.
266
275
  #
267
276
  # This is used for caching purposes. Any changes that would
268
- # invalid the asset dependency will invalidate the cache our the
269
- # source file.
277
+ # invalidate the asset dependency will invalidate the cache of
278
+ # the source file.
270
279
  #
271
280
  # Unlike `depend_on`, the path must be a requirable asset.
272
281
  #
273
282
  # //= depend_on_asset "bar.js"
274
283
  #
275
284
  def process_depend_on_asset_directive(path)
276
- load(resolve(path))
285
+ to_load(resolve(path))
277
286
  end
278
287
 
279
288
  # Allows dependency to be excluded from the asset bundle.
@@ -297,7 +306,8 @@ module Sprockets
297
306
  # /*= link "logo.png" */
298
307
  #
299
308
  def process_link_directive(path)
300
- @links << load(resolve(path)).uri
309
+ uri = to_load(resolve(path))
310
+ @to_link << uri
301
311
  end
302
312
 
303
313
  # `link_directory` links all the files inside a single
@@ -307,7 +317,7 @@ module Sprockets
307
317
  # //= link_directory "./fonts"
308
318
  #
309
319
  # Use caution when linking against JS or CSS assets. Include an explicit
310
- # extension or content type in these cases
320
+ # extension or content type in these cases.
311
321
  #
312
322
  # //= link_directory "./scripts" .js
313
323
  #
@@ -323,7 +333,7 @@ module Sprockets
323
333
  # //= link_tree "./images"
324
334
  #
325
335
  # Use caution when linking against JS or CSS assets. Include an explicit
326
- # extension or content type in these cases
336
+ # extension or content type in these cases.
327
337
  #
328
338
  # //= link_tree "./styles" .css
329
339
  #
@@ -354,15 +364,15 @@ module Sprockets
354
364
 
355
365
  def link_paths(paths, deps, accept)
356
366
  resolve_paths(paths, deps, accept: accept) do |uri|
357
- @links << load(uri).uri
367
+ @to_link << to_load(uri)
358
368
  end
359
369
  end
360
370
 
361
- def resolve_paths(paths, deps, options = {})
371
+ def resolve_paths(paths, deps, **kargs)
362
372
  @dependencies.merge(deps)
363
373
  paths.each do |subpath, stat|
364
374
  next if subpath == @filename || stat.directory?
365
- uri, deps = @environment.resolve(subpath, options.merge(compat: false))
375
+ uri, deps = @environment.resolve(subpath, **kargs)
366
376
  @dependencies.merge(deps)
367
377
  yield uri if uri
368
378
  end
@@ -384,19 +394,19 @@ module Sprockets
384
394
  end
385
395
  end
386
396
 
387
- def load(uri)
388
- asset = @environment.load(uri)
389
- @dependencies.merge(asset.metadata[:dependencies])
390
- asset
397
+ def to_load(uri)
398
+ @to_load << uri
399
+ uri
391
400
  end
392
401
 
393
- def resolve(path, options = {})
402
+ def resolve(path, **kargs)
394
403
  # Prevent absolute paths in directives
395
404
  if @environment.absolute_path?(path)
396
405
  raise FileOutsidePaths, "can't require absolute file: #{path}"
397
406
  end
398
407
 
399
- uri, deps = @environment.resolve!(path, options.merge(base_path: @dirname))
408
+ kargs[:base_path] = @dirname
409
+ uri, deps = @environment.resolve!(path, **kargs)
400
410
  @dependencies.merge(deps)
401
411
  uri
402
412
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/autoload'
2
3
 
3
4
  module Sprockets
@@ -12,7 +13,7 @@ module Sprockets
12
13
  VERSION = '1'
13
14
 
14
15
  def self.cache_key
15
- @cache_key ||= [name, Autoload::Eco::Source::VERSION, VERSION].freeze
16
+ @cache_key ||= "#{name}:#{Autoload::Eco::Source::VERSION}:#{VERSION}".freeze
16
17
  end
17
18
 
18
19
  # Compile template data with Eco compiler.
@@ -24,7 +25,7 @@ module Sprockets
24
25
  #
25
26
  def self.call(input)
26
27
  data = input[:data]
27
- input[:cache].fetch(cache_key + [data]) do
28
+ input[:cache].fetch([cache_key, data]) do
28
29
  Autoload::Eco.compile(data)
29
30
  end
30
31
  end