sprockets 2.2.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +482 -255
  4. data/bin/sprockets +20 -7
  5. data/lib/rake/sprocketstask.rb +28 -15
  6. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  7. data/lib/sprockets/asset.rb +142 -207
  8. data/lib/sprockets/autoload/babel.rb +8 -0
  9. data/lib/sprockets/autoload/closure.rb +8 -0
  10. data/lib/sprockets/autoload/coffee_script.rb +8 -0
  11. data/lib/sprockets/autoload/eco.rb +8 -0
  12. data/lib/sprockets/autoload/ejs.rb +8 -0
  13. data/lib/sprockets/autoload/jsminc.rb +8 -0
  14. data/lib/sprockets/autoload/sass.rb +8 -0
  15. data/lib/sprockets/autoload/sassc.rb +8 -0
  16. data/lib/sprockets/autoload/uglifier.rb +8 -0
  17. data/lib/sprockets/autoload/yui.rb +8 -0
  18. data/lib/sprockets/autoload/zopfli.rb +7 -0
  19. data/lib/sprockets/autoload.rb +16 -0
  20. data/lib/sprockets/babel_processor.rb +66 -0
  21. data/lib/sprockets/base.rb +89 -249
  22. data/lib/sprockets/bower.rb +61 -0
  23. data/lib/sprockets/bundle.rb +105 -0
  24. data/lib/sprockets/cache/file_store.rb +190 -14
  25. data/lib/sprockets/cache/memory_store.rb +75 -0
  26. data/lib/sprockets/cache/null_store.rb +54 -0
  27. data/lib/sprockets/cache.rb +271 -0
  28. data/lib/sprockets/cached_environment.rb +64 -0
  29. data/lib/sprockets/closure_compressor.rb +48 -0
  30. data/lib/sprockets/coffee_script_processor.rb +39 -0
  31. data/lib/sprockets/compressing.rb +134 -0
  32. data/lib/sprockets/configuration.rb +79 -0
  33. data/lib/sprockets/context.rb +204 -135
  34. data/lib/sprockets/dependencies.rb +74 -0
  35. data/lib/sprockets/digest_utils.rb +200 -0
  36. data/lib/sprockets/directive_processor.rb +224 -216
  37. data/lib/sprockets/eco_processor.rb +33 -0
  38. data/lib/sprockets/ejs_processor.rb +32 -0
  39. data/lib/sprockets/encoding_utils.rb +262 -0
  40. data/lib/sprockets/environment.rb +23 -68
  41. data/lib/sprockets/erb_processor.rb +37 -0
  42. data/lib/sprockets/errors.rb +6 -13
  43. data/lib/sprockets/exporters/base.rb +72 -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 +16 -0
  49. data/lib/sprockets/http_utils.rb +135 -0
  50. data/lib/sprockets/jsminc_compressor.rb +32 -0
  51. data/lib/sprockets/jst_processor.rb +36 -19
  52. data/lib/sprockets/loader.rb +343 -0
  53. data/lib/sprockets/manifest.rb +231 -96
  54. data/lib/sprockets/manifest_utils.rb +48 -0
  55. data/lib/sprockets/mime.rb +80 -32
  56. data/lib/sprockets/npm.rb +52 -0
  57. data/lib/sprockets/path_dependency_utils.rb +77 -0
  58. data/lib/sprockets/path_digest_utils.rb +48 -0
  59. data/lib/sprockets/path_utils.rb +367 -0
  60. data/lib/sprockets/paths.rb +82 -0
  61. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  62. data/lib/sprockets/processing.rb +140 -192
  63. data/lib/sprockets/processor_utils.rb +169 -0
  64. data/lib/sprockets/resolve.rb +295 -0
  65. data/lib/sprockets/sass_cache_store.rb +30 -0
  66. data/lib/sprockets/sass_compressor.rb +63 -0
  67. data/lib/sprockets/sass_functions.rb +3 -0
  68. data/lib/sprockets/sass_importer.rb +3 -0
  69. data/lib/sprockets/sass_processor.rb +313 -0
  70. data/lib/sprockets/sassc_compressor.rb +56 -0
  71. data/lib/sprockets/sassc_processor.rb +297 -0
  72. data/lib/sprockets/server.rb +138 -90
  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 +173 -0
  76. data/lib/sprockets/uglifier_compressor.rb +66 -0
  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 +191 -0
  80. data/lib/sprockets/utils/gzip.rb +99 -0
  81. data/lib/sprockets/utils.rb +186 -53
  82. data/lib/sprockets/version.rb +2 -1
  83. data/lib/sprockets/yui_compressor.rb +56 -0
  84. data/lib/sprockets.rb +217 -52
  85. metadata +250 -59
  86. data/LICENSE +0 -21
  87. data/lib/sprockets/asset_attributes.rb +0 -126
  88. data/lib/sprockets/bundled_asset.rb +0 -79
  89. data/lib/sprockets/caching.rb +0 -96
  90. data/lib/sprockets/charset_normalizer.rb +0 -41
  91. data/lib/sprockets/eco_template.rb +0 -38
  92. data/lib/sprockets/ejs_template.rb +0 -37
  93. data/lib/sprockets/engines.rb +0 -74
  94. data/lib/sprockets/index.rb +0 -99
  95. data/lib/sprockets/processed_asset.rb +0 -152
  96. data/lib/sprockets/processor.rb +0 -32
  97. data/lib/sprockets/safety_colons.rb +0 -28
  98. data/lib/sprockets/static_asset.rb +0 -57
  99. data/lib/sprockets/trail.rb +0 -90
@@ -1,15 +1,12 @@
1
- require 'base64'
1
+ # frozen_string_literal: true
2
2
  require 'rack/utils'
3
- require 'sprockets/errors'
4
- require 'sprockets/utils'
5
- require 'pathname'
6
3
  require 'set'
4
+ require 'sprockets/errors'
7
5
 
8
6
  module Sprockets
9
- # `Context` provides helper methods to all `Tilt` processors. They
10
- # are typically accessed by ERB templates. You can mix in custom
11
- # helpers by injecting them into `Environment#context_class`. Do not
12
- # mix them into `Context` directly.
7
+ # They are typically accessed by ERB templates. You can mix in custom helpers
8
+ # by injecting them into `Environment#context_class`. Do not mix them into
9
+ # `Context` directly.
13
10
  #
14
11
  # environment.context_class.class_eval do
15
12
  # include MyHelper
@@ -21,98 +18,118 @@ module Sprockets
21
18
  # The `Context` also collects dependencies declared by
22
19
  # assets. See `DirectiveProcessor` for an example of this.
23
20
  class Context
24
- attr_reader :environment, :pathname
25
- attr_reader :_required_paths, :_stubbed_assets
26
- attr_reader :_dependency_paths, :_dependency_assets
27
- attr_writer :__LINE__
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
38
+
39
+ attr_reader :environment, :filename
28
40
 
29
- def initialize(environment, logical_path, pathname)
30
- @environment = environment
31
- @logical_path = logical_path
32
- @pathname = pathname
33
- @__LINE__ = nil
41
+ def initialize(input)
42
+ @environment = input[:environment]
43
+ @metadata = input[:metadata]
44
+ @load_path = input[:load_path]
45
+ @logical_path = input[:name]
46
+ @filename = input[:filename]
47
+ @dirname = File.dirname(@filename)
48
+ @content_type = input[:content_type]
34
49
 
35
- @_required_paths = []
36
- @_stubbed_assets = Set.new
37
- @_dependency_paths = Set.new
38
- @_dependency_assets = Set.new([pathname.to_s])
50
+ @required = Set.new(@metadata[:required])
51
+ @stubbed = Set.new(@metadata[:stubbed])
52
+ @links = Set.new(@metadata[:links])
53
+ @dependencies = Set.new(input[:metadata][:dependencies])
54
+ end
55
+
56
+ def metadata
57
+ { required: @required,
58
+ stubbed: @stubbed,
59
+ links: @links,
60
+ dependencies: @dependencies }
61
+ end
62
+
63
+ def env_proxy
64
+ ENVProxy.new(self)
39
65
  end
40
66
 
41
67
  # Returns the environment path that contains the file.
42
68
  #
43
69
  # If `app/javascripts` and `app/stylesheets` are in your path, and
44
- # current file is `app/javascripts/foo/bar.js`, `root_path` would
70
+ # current file is `app/javascripts/foo/bar.js`, `load_path` would
45
71
  # return `app/javascripts`.
46
- def root_path
47
- environment.paths.detect { |path| pathname.to_s[path] }
48
- end
72
+ attr_reader :load_path
73
+ alias_method :root_path, :load_path
49
74
 
50
75
  # Returns logical path without any file extensions.
51
76
  #
52
77
  # 'app/javascripts/application.js'
53
78
  # # => 'application'
54
79
  #
55
- def logical_path
56
- @logical_path[/^([^.]+)/, 0]
57
- end
80
+ attr_reader :logical_path
58
81
 
59
82
  # Returns content type of file
60
83
  #
61
84
  # 'application/javascript'
62
85
  # 'text/css'
63
86
  #
64
- def content_type
65
- environment.content_type_of(pathname)
66
- end
87
+ attr_reader :content_type
67
88
 
68
- # Given a logical path, `resolve` will find and return the fully
69
- # expanded path. Relative paths will also be resolved. An optional
70
- # `:content_type` restriction can be supplied to restrict the
71
- # search.
89
+ # Public: Given a logical path, `resolve` will find and return an Asset URI.
90
+ # Relative paths will also be resolved. An accept type maybe given to
91
+ # restrict the search.
72
92
  #
73
93
  # resolve("foo.js")
74
- # # => "/path/to/app/javascripts/foo.js"
94
+ # # => "file:///path/to/app/javascripts/foo.js?type=application/javascript"
75
95
  #
76
96
  # resolve("./bar.js")
77
- # # => "/path/to/app/javascripts/bar.js"
97
+ # # => "file:///path/to/app/javascripts/bar.js?type=application/javascript"
78
98
  #
79
- def resolve(path, options = {}, &block)
80
- pathname = Pathname.new(path)
81
- attributes = environment.attributes_for(pathname)
82
-
83
- if pathname.absolute?
84
- pathname
85
-
86
- elsif content_type = options[:content_type]
87
- content_type = self.content_type if content_type == :self
88
-
89
- if attributes.format_extension
90
- if content_type != attributes.content_type
91
- raise ContentTypeMismatch, "#{path} is " +
92
- "'#{attributes.content_type}', not '#{content_type}'"
93
- end
94
- end
95
-
96
- resolve(path) do |candidate|
97
- if self.content_type == environment.content_type_of(candidate)
98
- return candidate
99
- end
100
- end
99
+ # path - String logical or absolute path
100
+ # accept - String content accept type
101
+ #
102
+ # Returns an Asset URI String.
103
+ def resolve(path, **kargs)
104
+ kargs[:base_path] = @dirname
105
+ uri, deps = environment.resolve!(path, **kargs)
106
+ @dependencies.merge(deps)
107
+ uri
108
+ end
101
109
 
102
- raise FileNotFound, "couldn't find file '#{path}'"
103
- else
104
- environment.resolve(path, :base_path => self.pathname.dirname, &block)
105
- end
110
+ # Public: Load Asset by AssetURI and track it as a dependency.
111
+ #
112
+ # uri - AssetURI
113
+ #
114
+ # Returns Asset.
115
+ def load(uri)
116
+ asset = environment.load(uri)
117
+ @dependencies.merge(asset.metadata[:dependencies])
118
+ asset
106
119
  end
107
120
 
108
121
  # `depend_on` allows you to state a dependency on a file without
109
122
  # including it.
110
123
  #
111
124
  # This is used for caching purposes. Any changes made to
112
- # the dependency file with invalidate the cache of the
125
+ # the dependency file will invalidate the cache of the
113
126
  # source file.
114
127
  def depend_on(path)
115
- @_dependency_paths << resolve(path).to_s
128
+ if environment.absolute_path?(path) && environment.stat(path)
129
+ @dependencies << environment.build_file_digest_uri(path)
130
+ else
131
+ resolve(path)
132
+ end
116
133
  nil
117
134
  end
118
135
 
@@ -121,12 +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
- filename = resolve(path).to_s
128
- @_dependency_assets << filename
129
- nil
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}"
130
154
  end
131
155
 
132
156
  # `require_asset` declares `path` as a dependency of the file. The
@@ -139,9 +163,7 @@ module Sprockets
139
163
  # <%= require_asset "#{framework}.js" %>
140
164
  #
141
165
  def require_asset(path)
142
- pathname = resolve(path, :content_type => :self)
143
- depend_on_asset(pathname)
144
- @_required_paths << pathname.to_s
166
+ @required << resolve(path, accept: @content_type, pipeline: :self)
145
167
  nil
146
168
  end
147
169
 
@@ -149,60 +171,25 @@ module Sprockets
149
171
  # `path` must be an asset which may or may not already be included
150
172
  # in the bundle.
151
173
  def stub_asset(path)
152
- @_stubbed_assets << resolve(path, :content_type => :self).to_s
174
+ @stubbed << resolve(path, accept: @content_type, pipeline: :self)
153
175
  nil
154
176
  end
155
177
 
156
- # Tests if target path is able to be safely required into the
157
- # current concatenation.
158
- def asset_requirable?(path)
159
- pathname = resolve(path)
160
- content_type = environment.content_type_of(pathname)
161
- stat = environment.stat(path)
162
- return false unless stat && stat.file?
163
- self.content_type.nil? || self.content_type == content_type
164
- end
165
-
166
- # Reads `path` and runs processors on the file.
167
- #
168
- # This allows you to capture the result of an asset and include it
169
- # directly in another.
170
- #
171
- # <%= evaluate "bar.js" %>
178
+ # `link_asset` declares an external dependency on an asset without directly
179
+ # including it. The target asset is returned from this function making it
180
+ # easy to construct a link to it.
172
181
  #
173
- def evaluate(path, options = {})
174
- pathname = resolve(path)
175
- attributes = environment.attributes_for(pathname)
176
- processors = options[:processors] || attributes.processors
177
-
178
- if options[:data]
179
- result = options[:data]
180
- else
181
- if environment.respond_to?(:default_external_encoding)
182
- mime_type = environment.mime_types(pathname.extname)
183
- encoding = environment.encoding_for_mime_type(mime_type)
184
- result = Sprockets::Utils.read_unicode(pathname, encoding)
185
- else
186
- result = Sprockets::Utils.read_unicode(pathname)
187
- end
188
- end
189
-
190
- processors.each do |processor|
191
- begin
192
- template = processor.new(pathname.to_s) { result }
193
- result = template.render(self, {})
194
- rescue Exception => e
195
- annotate_exception! e
196
- raise
197
- end
198
- end
199
-
200
- result
182
+ # Returns an Asset or nil.
183
+ def link_asset(path)
184
+ asset = depend_on_asset(path)
185
+ @links << asset.uri
186
+ asset
201
187
  end
202
188
 
203
- # Returns a Base64-encoded `data:` URI with the contents of the
204
- # asset at the specified path, and marks that path as a dependency
205
- # 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.
206
193
  #
207
194
  # Use `asset_data_uri` from ERB with CSS or JavaScript assets:
208
195
  #
@@ -211,25 +198,107 @@ module Sprockets
211
198
  # $('<img>').attr('src', '<%= asset_data_uri 'avatar.jpg' %>')
212
199
  #
213
200
  def asset_data_uri(path)
214
- depend_on_asset(path)
215
- asset = environment.find_asset(path)
216
- base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "")
217
- "data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
201
+ asset = depend_on_asset(path)
202
+ if asset.content_type == 'image/svg+xml'
203
+ svg_asset_data_uri(asset)
204
+ else
205
+ base64_asset_data_uri(asset)
206
+ end
218
207
  end
219
208
 
220
- private
221
- # Annotates exception backtrace with the original template that
222
- # the exception was raised in.
223
- def annotate_exception!(exception)
224
- location = pathname.to_s
225
- location << ":#{@__LINE__}" if @__LINE__
209
+ # Expands logical path to full url to asset.
210
+ #
211
+ # NOTE: This helper is currently not implemented and should be
212
+ # customized by the application. Though, in the future, some
213
+ # basics implemention may be provided with different methods that
214
+ # are required to be overridden.
215
+ def asset_path(path, options = {})
216
+ message = <<-EOS
217
+ Custom asset_path helper is not implemented
226
218
 
227
- exception.extend(Sprockets::EngineError)
228
- exception.sprockets_annotation = " (in #{location})"
229
- end
219
+ Extend your environment context with a custom method.
230
220
 
231
- def logger
232
- environment.logger
221
+ environment.context_class.class_eval do
222
+ def asset_path(path, options = {})
233
223
  end
224
+ end
225
+ EOS
226
+ raise NotImplementedError, message
227
+ end
228
+
229
+ # Expand logical image asset path.
230
+ def image_path(path)
231
+ asset_path(path, type: :image)
232
+ end
233
+
234
+ # Expand logical video asset path.
235
+ def video_path(path)
236
+ asset_path(path, type: :video)
237
+ end
238
+
239
+ # Expand logical audio asset path.
240
+ def audio_path(path)
241
+ asset_path(path, type: :audio)
242
+ end
243
+
244
+ # Expand logical font asset path.
245
+ def font_path(path)
246
+ asset_path(path, type: :font)
247
+ end
248
+
249
+ # Expand logical javascript asset path.
250
+ def javascript_path(path)
251
+ asset_path(path, type: :javascript)
252
+ end
253
+
254
+ # Expand logical stylesheet asset path.
255
+ def stylesheet_path(path)
256
+ asset_path(path, type: :stylesheet)
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
234
303
  end
235
304
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/digest_utils'
3
+ require 'sprockets/path_digest_utils'
4
+ require 'sprockets/uri_utils'
5
+
6
+ module Sprockets
7
+ # `Dependencies` is an internal mixin whose public methods are exposed on the
8
+ # `Environment` and `CachedEnvironment` classes.
9
+ module Dependencies
10
+ include DigestUtils, PathDigestUtils, URIUtils
11
+
12
+ # Public: Mapping dependency schemes to resolver functions.
13
+ #
14
+ # key - String scheme
15
+ # value - Proc.call(Environment, String)
16
+ #
17
+ # Returns Hash.
18
+ def dependency_resolvers
19
+ config[:dependency_resolvers]
20
+ end
21
+
22
+ # Public: Default set of dependency URIs for assets.
23
+ #
24
+ # Returns Set of String URIs.
25
+ def dependencies
26
+ config[:dependencies]
27
+ end
28
+
29
+ # Public: Register new dependency URI resolver.
30
+ #
31
+ # scheme - String scheme
32
+ # block -
33
+ # environment - Environment
34
+ # uri - String dependency URI
35
+ #
36
+ # Returns nothing.
37
+ def register_dependency_resolver(scheme, &block)
38
+ self.config = hash_reassoc(config, :dependency_resolvers) do |hash|
39
+ hash.merge(scheme => block)
40
+ end
41
+ end
42
+
43
+ # Public: Add environmental dependency inheirted by all assets.
44
+ #
45
+ # uri - String dependency URI
46
+ #
47
+ # Returns nothing.
48
+ def add_dependency(uri)
49
+ self.config = hash_reassoc(config, :dependencies) do |set|
50
+ set + Set.new([uri])
51
+ end
52
+ end
53
+ alias_method :depend_on, :add_dependency
54
+
55
+ # Internal: Resolve dependency URIs.
56
+ #
57
+ # Returns resolved Object.
58
+ def resolve_dependency(str)
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
+
67
+ if resolver = config[:dependency_resolvers][scheme]
68
+ resolver.call(self, str)
69
+ else
70
+ nil
71
+ end
72
+ end
73
+ end
74
+ end