sprockets 3.7.2 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -267
  3. data/README.md +477 -321
  4. data/bin/sprockets +11 -7
  5. data/lib/rake/sprocketstask.rb +3 -2
  6. data/lib/sprockets.rb +99 -39
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +31 -23
  9. data/lib/sprockets/autoload.rb +5 -0
  10. data/lib/sprockets/autoload/babel.rb +8 -0
  11. data/lib/sprockets/autoload/closure.rb +1 -0
  12. data/lib/sprockets/autoload/coffee_script.rb +1 -0
  13. data/lib/sprockets/autoload/eco.rb +1 -0
  14. data/lib/sprockets/autoload/ejs.rb +1 -0
  15. data/lib/sprockets/autoload/jsminc.rb +8 -0
  16. data/lib/sprockets/autoload/sass.rb +1 -0
  17. data/lib/sprockets/autoload/sassc.rb +8 -0
  18. data/lib/sprockets/autoload/uglifier.rb +1 -0
  19. data/lib/sprockets/autoload/yui.rb +1 -0
  20. data/lib/sprockets/autoload/zopfli.rb +7 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +49 -12
  23. data/lib/sprockets/bower.rb +5 -2
  24. data/lib/sprockets/bundle.rb +40 -4
  25. data/lib/sprockets/cache.rb +36 -1
  26. data/lib/sprockets/cache/file_store.rb +25 -3
  27. data/lib/sprockets/cache/memory_store.rb +9 -0
  28. data/lib/sprockets/cache/null_store.rb +8 -0
  29. data/lib/sprockets/cached_environment.rb +14 -19
  30. data/lib/sprockets/closure_compressor.rb +1 -0
  31. data/lib/sprockets/coffee_script_processor.rb +18 -4
  32. data/lib/sprockets/compressing.rb +43 -3
  33. data/lib/sprockets/configuration.rb +3 -7
  34. data/lib/sprockets/context.rb +97 -24
  35. data/lib/sprockets/dependencies.rb +1 -0
  36. data/lib/sprockets/digest_utils.rb +25 -5
  37. data/lib/sprockets/directive_processor.rb +45 -35
  38. data/lib/sprockets/eco_processor.rb +1 -0
  39. data/lib/sprockets/ejs_processor.rb +1 -0
  40. data/lib/sprockets/encoding_utils.rb +1 -0
  41. data/lib/sprockets/environment.rb +9 -4
  42. data/lib/sprockets/erb_processor.rb +28 -21
  43. data/lib/sprockets/errors.rb +1 -0
  44. data/lib/sprockets/exporters/base.rb +71 -0
  45. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  46. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  47. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  48. data/lib/sprockets/exporting.rb +73 -0
  49. data/lib/sprockets/file_reader.rb +1 -0
  50. data/lib/sprockets/http_utils.rb +25 -7
  51. data/lib/sprockets/jsminc_compressor.rb +32 -0
  52. data/lib/sprockets/jst_processor.rb +11 -10
  53. data/lib/sprockets/loader.rb +87 -67
  54. data/lib/sprockets/manifest.rb +64 -62
  55. data/lib/sprockets/manifest_utils.rb +9 -6
  56. data/lib/sprockets/mime.rb +8 -42
  57. data/lib/sprockets/npm.rb +52 -0
  58. data/lib/sprockets/path_dependency_utils.rb +3 -11
  59. data/lib/sprockets/path_digest_utils.rb +2 -1
  60. data/lib/sprockets/path_utils.rb +87 -7
  61. data/lib/sprockets/paths.rb +1 -0
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +31 -61
  64. data/lib/sprockets/processor_utils.rb +24 -35
  65. data/lib/sprockets/resolve.rb +177 -93
  66. data/lib/sprockets/sass_cache_store.rb +2 -6
  67. data/lib/sprockets/sass_compressor.rb +13 -1
  68. data/lib/sprockets/sass_functions.rb +1 -0
  69. data/lib/sprockets/sass_importer.rb +1 -0
  70. data/lib/sprockets/sass_processor.rb +30 -9
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +26 -23
  74. data/lib/sprockets/source_map_processor.rb +66 -0
  75. data/lib/sprockets/source_map_utils.rb +483 -0
  76. data/lib/sprockets/transformers.rb +63 -35
  77. data/lib/sprockets/uglifier_compressor.rb +21 -11
  78. data/lib/sprockets/unloaded_asset.rb +13 -11
  79. data/lib/sprockets/uri_tar.rb +1 -0
  80. data/lib/sprockets/uri_utils.rb +11 -8
  81. data/lib/sprockets/utils.rb +41 -74
  82. data/lib/sprockets/utils/gzip.rb +46 -14
  83. data/lib/sprockets/version.rb +2 -1
  84. data/lib/sprockets/yui_compressor.rb +1 -0
  85. metadata +127 -23
  86. data/LICENSE +0 -21
  87. data/lib/sprockets/coffee_script_template.rb +0 -17
  88. data/lib/sprockets/deprecation.rb +0 -90
  89. data/lib/sprockets/eco_template.rb +0 -17
  90. data/lib/sprockets/ejs_template.rb +0 -17
  91. data/lib/sprockets/engines.rb +0 -92
  92. data/lib/sprockets/erb_template.rb +0 -11
  93. data/lib/sprockets/legacy.rb +0 -330
  94. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  95. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  96. data/lib/sprockets/sass_template.rb +0 -19
@@ -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'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'digest/md5'
2
3
  require 'digest/sha1'
3
4
  require 'digest/sha2'
@@ -62,7 +63,7 @@ module Sprockets
62
63
  },
63
64
  Set => ->(val, digest) {
64
65
  digest << 'Set'.freeze
65
- ADD_VALUE_TO_DIGEST[Array].call(val.to_a, digest)
66
+ ADD_VALUE_TO_DIGEST[Array].call(val, digest)
66
67
  },
67
68
  Encoding => ->(val, digest) {
68
69
  digest << 'Encoding'.freeze
@@ -79,6 +80,9 @@ module Sprockets
79
80
  digest << val.to_s
80
81
  }
81
82
  end
83
+
84
+ ADD_VALUE_TO_DIGEST.compare_by_identity.rehash
85
+
82
86
  ADD_VALUE_TO_DIGEST.default_proc = ->(_, val) {
83
87
  raise TypeError, "couldn't digest #{ val }"
84
88
  }
@@ -93,10 +97,18 @@ module Sprockets
93
97
  #
94
98
  # Returns a String digest of the object.
95
99
  def digest(obj)
96
- digest = digest_class.new
100
+ build_digest(obj).digest
101
+ end
97
102
 
98
- ADD_VALUE_TO_DIGEST[obj.class].call(obj, digest)
99
- 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!
100
112
  end
101
113
 
102
114
  # Internal: Pack a binary digest to a hex encoded string.
@@ -105,7 +117,7 @@ module Sprockets
105
117
  #
106
118
  # Returns hex String.
107
119
  def pack_hexdigest(bin)
108
- bin.unpack('H*').first
120
+ bin.unpack('H*'.freeze).first
109
121
  end
110
122
 
111
123
  # Internal: Unpack a hex encoded digest string into binary bytes.
@@ -176,5 +188,13 @@ module Sprockets
176
188
  def hexdigest_integrity_uri(hexdigest)
177
189
  integrity_uri(unpack_hexdigest(hexdigest))
178
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
179
199
  end
180
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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/autoload'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'base64'
2
3
  require 'stringio'
3
4
  require 'zlib'
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/base'
2
3
  require 'sprockets/cache/memory_store'
3
4
  require 'sprockets/cached_environment'
4
5
 
5
6
  module Sprockets
6
7
  class Environment < Base
7
- # `Environment` should initialized with your application's root
8
+ # `Environment` should be initialized with your application's root
8
9
  # directory. This should be the same as your Rails or Rack root.
9
10
  #
10
11
  # env = Environment.new(Rails.root)
@@ -18,7 +19,7 @@ module Sprockets
18
19
 
19
20
  # Returns a cached version of the environment.
20
21
  #
21
- # All its file system calls are cached which makes `cached` much
22
+ # All of its file system calls are cached which makes `cached` much
22
23
  # faster. This behavior is ideal in production since the file
23
24
  # system only changes between deploys.
24
25
  def cached
@@ -26,8 +27,12 @@ module Sprockets
26
27
  end
27
28
  alias_method :index, :cached
28
29
 
29
- def find_asset(*args)
30
- cached.find_asset(*args)
30
+ def find_asset(*args, **options)
31
+ cached.find_asset(*args, **options)
32
+ end
33
+
34
+ def find_asset!(*args)
35
+ cached.find_asset!(*args)
31
36
  end
32
37
 
33
38
  def find_all_linked_assets(*args, &block)
@@ -1,30 +1,37 @@
1
+ # frozen_string_literal: true
1
2
  require 'erb'
2
3
 
3
- module Sprockets
4
- class ERBProcessor
5
- # Public: Return singleton instance with default options.
6
- #
7
- # Returns ERBProcessor object.
8
- def self.instance
9
- @instance ||= new
10
- end
4
+ class Sprockets::ERBProcessor
5
+ # Public: Return singleton instance with default options.
6
+ #
7
+ # Returns ERBProcessor object.
8
+ def self.instance
9
+ @instance ||= new
10
+ end
11
11
 
12
- def self.call(input)
13
- instance.call(input)
14
- end
12
+ def self.call(input)
13
+ instance.call(input)
14
+ end
15
15
 
16
- def initialize(&block)
17
- @block = block
18
- end
16
+ def initialize(&block)
17
+ @block = block
18
+ end
19
19
 
20
- def call(input)
20
+ def call(input)
21
+ match = ERB.version.match(/\Aerb\.rb \[(?<version>[^ ]+) /)
22
+ if match && match[:version] >= "2.2.0" # Ruby 2.6+
23
+ engine = ::ERB.new(input[:data], trim_mode: '<>')
24
+ else
21
25
  engine = ::ERB.new(input[:data], nil, '<>')
22
- context = input[:environment].context_class.new(input)
23
- klass = (class << context; self; end)
24
- klass.class_eval(&@block) if @block
25
- engine.def_method(klass, :_evaluate_template, input[:filename])
26
- data = context._evaluate_template
27
- context.metadata.merge(data: data)
28
26
  end
27
+ engine.filename = input[:filename]
28
+
29
+ context = input[:environment].context_class.new(input)
30
+ klass = (class << context; self; end)
31
+ klass.const_set(:ENV, context.env_proxy)
32
+ klass.class_eval(&@block) if @block
33
+
34
+ data = engine.result(context.instance_eval('binding'))
35
+ context.metadata.merge(data: data)
29
36
  end
30
37
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Define some basic Sprockets error classes
2
3
  module Sprockets
3
4
  class Error < StandardError; end
@@ -0,0 +1,71 @@
1
+ module Sprockets
2
+ module Exporters
3
+ # Convienence class for all exporters to inherit from
4
+ #
5
+ # An exporter is responsible for exporting a Sprockets::Asset
6
+ # to a file system. For example the Exporters::File class
7
+ # writes the asset to it's destination. The Exporters::Zlib class
8
+ # writes a gzip copy of the asset to disk.
9
+ class Base
10
+ attr_reader :asset, :environment, :directory, :target
11
+
12
+ # Public: Creates new instance
13
+ #
14
+ # Initialize will be called with
15
+ # keyword arguments:
16
+ #
17
+ # - asset: An instance of Sprockets::Asset.
18
+ # - environment: An instance of Sprockets::Environment.
19
+ # - directory: String representing the target directory to write to.
20
+ #
21
+ # These will all be stored as accessible values. In addition a
22
+ # +target+ will be available which is the target directory and
23
+ # the asset's digest path combined.
24
+ def initialize(asset: nil, environment: nil, directory: nil)
25
+ @asset = asset
26
+ @environment = environment
27
+ @directory = directory
28
+ @target = ::File.join(directory, asset.digest_path)
29
+ setup
30
+ end
31
+
32
+ # Public: Callback that is executed after intialization
33
+ #
34
+ # Any setup that needs to be done can be performed in the +setup+
35
+ # method. It will be called immediately after initialization.
36
+ def setup
37
+ end
38
+
39
+ # Public: Handles logic for skipping exporter and notifying logger
40
+ #
41
+ # The `skip?` will be called before anything will be written.
42
+ # If `skip?` returns truthy it will not continue. This method
43
+ # takes a `logger` that responds to +debug+ and +info+. The `skip?`
44
+ # method is the only place expected to write to a logger, any other
45
+ # messages may produce jumbled logs.
46
+ def skip?(logger)
47
+ false
48
+ end
49
+
50
+ # Public: Contains logic for writing "exporting" asset to disk
51
+ #
52
+ # If the exporter is not skipped it then Sprockets will execute it's
53
+ # `call` method. This method takes no arguments and should only use
54
+ # elements passed in via initialize or stored in `setup`.
55
+ def call
56
+ raise "Must subclass and implement call"
57
+ end
58
+
59
+ # Public: Yields a file that can be written to with the input
60
+ #
61
+ # `filename`. Defaults to the `target`. Method
62
+ # is safe to use in forked or threaded environments.
63
+ def write(filename = target)
64
+ FileUtils.mkdir_p File.dirname(filename)
65
+ PathUtils.atomic_write(filename) do |f|
66
+ yield f
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end