sprockets 3.7.3 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -259
  3. data/{LICENSE → MIT-LICENSE} +2 -2
  4. data/README.md +527 -320
  5. data/bin/sprockets +11 -7
  6. data/lib/rake/sprocketstask.rb +9 -4
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +39 -27
  9. data/lib/sprockets/autoload/babel.rb +8 -0
  10. data/lib/sprockets/autoload/closure.rb +1 -0
  11. data/lib/sprockets/autoload/coffee_script.rb +1 -0
  12. data/lib/sprockets/autoload/eco.rb +1 -0
  13. data/lib/sprockets/autoload/ejs.rb +1 -0
  14. data/lib/sprockets/autoload/jsminc.rb +8 -0
  15. data/lib/sprockets/autoload/sass.rb +1 -0
  16. data/lib/sprockets/autoload/sassc.rb +8 -0
  17. data/lib/sprockets/autoload/uglifier.rb +1 -0
  18. data/lib/sprockets/autoload/yui.rb +1 -0
  19. data/lib/sprockets/autoload/zopfli.rb +7 -0
  20. data/lib/sprockets/autoload.rb +5 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +49 -12
  23. data/lib/sprockets/bower.rb +6 -3
  24. data/lib/sprockets/bundle.rb +41 -5
  25. data/lib/sprockets/cache/file_store.rb +25 -3
  26. data/lib/sprockets/cache/memory_store.rb +28 -10
  27. data/lib/sprockets/cache/null_store.rb +8 -0
  28. data/lib/sprockets/cache.rb +37 -2
  29. data/lib/sprockets/cached_environment.rb +15 -20
  30. data/lib/sprockets/closure_compressor.rb +1 -0
  31. data/lib/sprockets/coffee_script_processor.rb +19 -5
  32. data/lib/sprockets/compressing.rb +43 -3
  33. data/lib/sprockets/configuration.rb +5 -9
  34. data/lib/sprockets/context.rb +99 -25
  35. data/lib/sprockets/dependencies.rb +2 -1
  36. data/lib/sprockets/digest_utils.rb +35 -18
  37. data/lib/sprockets/directive_processor.rb +64 -38
  38. data/lib/sprockets/eco_processor.rb +2 -1
  39. data/lib/sprockets/ejs_processor.rb +2 -1
  40. data/lib/sprockets/encoding_utils.rb +2 -2
  41. data/lib/sprockets/environment.rb +9 -4
  42. data/lib/sprockets/erb_processor.rb +33 -32
  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 +91 -69
  54. data/lib/sprockets/manifest.rb +67 -64
  55. data/lib/sprockets/manifest_utils.rb +9 -6
  56. data/lib/sprockets/mime.rb +8 -62
  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 +88 -8
  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 +32 -62
  64. data/lib/sprockets/processor_utils.rb +28 -38
  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 +31 -10
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +63 -40
  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 +19 -16
  81. data/lib/sprockets/utils/gzip.rb +46 -14
  82. data/lib/sprockets/utils.rb +64 -90
  83. data/lib/sprockets/version.rb +2 -1
  84. data/lib/sprockets/yui_compressor.rb +1 -0
  85. data/lib/sprockets.rb +102 -39
  86. metadata +148 -45
  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,42 +1,43 @@
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)
21
- if keyword_constructor? # Ruby 2.6+
22
- engine = ::ERB.new(input[:data], trim_mode: '<>')
23
- else
24
- engine = ::ERB.new(input[:data], nil, '<>')
25
- end
26
-
27
- context = input[:environment].context_class.new(input)
28
- klass = (class << context; self; end)
29
- klass.class_eval(&@block) if @block
30
- engine.def_method(klass, :_evaluate_template, input[:filename])
31
- data = context._evaluate_template
32
- context.metadata.merge(data: data)
20
+ def call(input)
21
+ if keyword_constructor? # Ruby 2.6+
22
+ engine = ::ERB.new(input[:data], trim_mode: '<>')
23
+ else
24
+ engine = ::ERB.new(input[:data], nil, '<>')
33
25
  end
26
+ engine.filename = input[:filename]
34
27
 
35
- private
28
+ context = input[:environment].context_class.new(input)
29
+ klass = (class << context; self; end)
30
+ klass.const_set(:ENV, context.env_proxy)
31
+ klass.class_eval(&@block) if @block
36
32
 
37
- def keyword_constructor?
38
- return @keyword_constructor if defined? @keyword_constructor
39
- @keyword_constructor = ::ERB.instance_method(:initialize).parameters.include?([:key, :trim_mode])
40
- end
33
+ data = engine.result(context.instance_eval('binding'))
34
+ context.metadata.merge(data: data)
35
+ end
36
+
37
+ private
38
+
39
+ def keyword_constructor?
40
+ return @keyword_constructor if defined? @keyword_constructor
41
+ @keyword_constructor = ::ERB.instance_method(:initialize).parameters.include?([:key, :trim_mode])
41
42
  end
42
43
  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
+ # Convenience 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 initialization
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
@@ -0,0 +1,24 @@
1
+ require 'sprockets/exporters/base'
2
+
3
+ module Sprockets
4
+ module Exporters
5
+ # Writes a an asset file to disk
6
+ class FileExporter < Exporters::Base
7
+ def skip?(logger)
8
+ if ::File.exist?(target)
9
+ logger.debug "Skipping #{ target }, already exists"
10
+ true
11
+ else
12
+ logger.info "Writing #{ target }"
13
+ false
14
+ end
15
+ end
16
+
17
+ def call
18
+ write(target) do |file|
19
+ file.write(asset.source)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ require 'sprockets/exporters/base'
2
+ require 'sprockets/utils/gzip'
3
+
4
+ module Sprockets
5
+ module Exporters
6
+ # Generates a `.gz` file using the zlib algorithm built into
7
+ # Ruby's standard library.
8
+ class ZlibExporter < Exporters::Base
9
+ def setup
10
+ @gzip_target = "#{ target }.gz"
11
+ @gzip = Sprockets::Utils::Gzip.new(asset, archiver: Utils::Gzip::ZlibArchiver)
12
+ end
13
+
14
+ def skip?(logger)
15
+ return true if environment.skip_gzip?
16
+ return true if @gzip.cannot_compress?
17
+ if ::File.exist?(@gzip_target)
18
+ logger.debug "Skipping #{ @gzip_target }, already exists"
19
+ true
20
+ else
21
+ logger.info "Writing #{ @gzip_target }"
22
+ false
23
+ end
24
+ end
25
+
26
+ def call
27
+ write(@gzip_target) do |file|
28
+ @gzip.compress(file, target)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ require 'sprockets/exporters/zlib_exporter'
2
+
3
+ module Sprockets
4
+ module Exporters
5
+ # Generates a `.gz` file using the zopfli algorithm from the
6
+ # Zopfli gem.
7
+ class ZopfliExporter < ZlibExporter
8
+ def setup
9
+ @gzip_target = "#{ target }.gz"
10
+ @gzip = Sprockets::Utils::Gzip.new(asset, archiver: Utils::Gzip::ZopfliArchiver)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,73 @@
1
+ module Sprockets
2
+ # `Exporting` is an internal mixin whose public methods are exposed on
3
+ # the `Environment` and `CachedEnvironment` classes.
4
+ module Exporting
5
+ # Exporters are ran on the assets:precompile task
6
+ def exporters
7
+ config[:exporters]
8
+ end
9
+
10
+ # Public: Registers a new Exporter `klass` for `mime_type`.
11
+ #
12
+ # If your exporter depends on one or more other exporters you can
13
+ # specify this via the `depend_on` keyword.
14
+ #
15
+ # register_exporter '*/*', Sprockets::Exporters::ZlibExporter
16
+ #
17
+ # This ensures that `Sprockets::Exporters::File` will always execute before
18
+ # `Sprockets::Exporters::Zlib`
19
+ def register_exporter(mime_types, klass = nil)
20
+ mime_types = Array(mime_types)
21
+
22
+ mime_types.each do |mime_type|
23
+ self.config = hash_reassoc(config, :exporters, mime_type) do |_exporters|
24
+ _exporters << klass
25
+ end
26
+ end
27
+ end
28
+
29
+ # Public: Remove Exporting processor `klass` for `mime_type`.
30
+ #
31
+ # environment.unregister_exporter '*/*', Sprockets::Exporters::ZlibExporter
32
+ #
33
+ # Can be called without a mime type
34
+ #
35
+ # environment.unregister_exporter Sprockets::Exporters::ZlibExporter
36
+ #
37
+ # Does not remove any exporters that depend on `klass`.
38
+ def unregister_exporter(mime_types, exporter = nil)
39
+ unless mime_types.is_a? Array
40
+ if mime_types.is_a? String
41
+ mime_types = [mime_types]
42
+ else # called with no mime type
43
+ exporter = mime_types
44
+ mime_types = nil
45
+ end
46
+ end
47
+
48
+ self.config = hash_reassoc(config, :exporters) do |_exporters|
49
+ _exporters.each do |mime_type, exporters_array|
50
+ next if mime_types && !mime_types.include?(mime_type)
51
+ if exporters_array.include? exporter
52
+ _exporters[mime_type] = exporters_array.dup.delete exporter
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # Public: Checks if concurrent exporting is allowed
59
+ def export_concurrent
60
+ config[:export_concurrent]
61
+ end
62
+
63
+ # Public: Enable or disable the concurrently exporting files
64
+ #
65
+ # Defaults to true.
66
+ #
67
+ # environment.export_concurrent = false
68
+ #
69
+ def export_concurrent=(export_concurrent)
70
+ self.config = config.merge(export_concurrent: export_concurrent).freeze
71
+ end
72
+ end
73
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'set'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Sprockets
2
3
  # Internal: HTTP URI utilities. Many adapted from Rack::Utils. Mixed into
3
4
  # Environment.
@@ -13,9 +14,9 @@ module Sprockets
13
14
  # Returns true if the given value is a mime match for the given mime match
14
15
  # specification, false otherwise.
15
16
  def match_mime_type?(value, matcher)
16
- v1, v2 = value.split('/', 2)
17
- m1, m2 = matcher.split('/', 2)
18
- (m1 == '*' || v1 == m1) && (m2.nil? || m2 == '*' || m2 == v2)
17
+ v1, v2 = value.split('/'.freeze, 2)
18
+ m1, m2 = matcher.split('/'.freeze, 2)
19
+ (m1 == '*'.freeze || v1 == m1) && (m2.nil? || m2 == '*'.freeze || m2 == v2)
19
20
  end
20
21
 
21
22
  # Public: Return values from Hash where the key matches the mime type.
@@ -36,7 +37,22 @@ module Sprockets
36
37
 
37
38
  # Internal: Parse Accept header quality values.
38
39
  #
39
- # Adapted from Rack::Utils#q_values.
40
+ # values - String e.g. "application/javascript"
41
+ #
42
+ # Adapted from Rack::Utils#q_values. Quality values are
43
+ # described in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
44
+ #
45
+ # parse_q_values("application/javascript")
46
+ # # => [["application/javascript", 1.0]]
47
+ #
48
+ # parse_q_values("*/*")
49
+ # # => [["*/*", 1.0]]
50
+ #
51
+ # parse_q_values("text/plain; q=0.5, image/*")
52
+ # # => [["text/plain", 0.5], ["image/*", 1.0]]
53
+ #
54
+ # parse_q_values("application/javascript, text/css")
55
+ # # => [["application/javascript", 1.0], ["text/css", 1.0]]
40
56
  #
41
57
  # Returns an Array of [String, Float].
42
58
  def parse_q_values(values)
@@ -70,14 +86,16 @@ module Sprockets
70
86
  raise TypeError, "unknown q_values type: #{q_values.class}"
71
87
  end
72
88
 
89
+ i = 0
73
90
  q_values.each do |accepted, quality|
74
91
  if match = available.find { |option| matcher.call(option, accepted) }
75
- matches << [match, quality]
92
+ i += 1
93
+ matches << [-quality, i, match]
76
94
  end
77
95
  end
78
96
 
79
- matches.sort_by! { |match, quality| -quality }
80
- matches.map! { |match, quality| match }
97
+ matches.sort!
98
+ matches.map! { |_, _, match| match }
81
99
  matches
82
100
  end
83
101
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+ require 'sprockets/digest_utils'
4
+
5
+ module Sprockets
6
+ class JSMincCompressor
7
+ VERSION = '1'
8
+
9
+ def self.instance
10
+ @instance ||= new
11
+ end
12
+
13
+ def self.call(input)
14
+ instance.call(input)
15
+ end
16
+
17
+ def self.cache_key
18
+ instance.cache_key
19
+ end
20
+
21
+ attr_reader :cache_key
22
+
23
+ def initialize(options = {})
24
+ @compressor_class = Autoload::JSMinC
25
+ @cache_key = "#{self.class.name}:#{Autoload::JSMinC::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
26
+ end
27
+
28
+ def call(input)
29
+ @compressor_class.minify(input[:data])
30
+ end
31
+ end
32
+ end
@@ -1,21 +1,22 @@
1
+ # frozen_string_literal: true
1
2
  module Sprockets
2
- # Public: .jst engine.
3
+ # Public: JST transformer.
3
4
  #
4
5
  # Exports server side compiled templates to an object.
5
6
  #
6
- # Name your template "users/show.jst.ejs", "users/new.jst.eco", etc.
7
+ # Name your template "users/show.ejs", "users/new.eco", etc.
7
8
  #
8
9
  # To accept the default options
9
10
  #
10
- # environment.register_engine '.jst',
11
- # JstProcessor,
12
- # mime_type: 'application/javascript'
11
+ # environment.register_transformer
12
+ # 'application/javascript+function',
13
+ # 'application/javascript', JstProcessor
13
14
  #
14
15
  # Change the default namespace.
15
16
  #
16
- # environment.register_engine '.jst',
17
- # JstProcessor.new(namespace: 'App.templates'),
18
- # mime_type: 'application/javascript'
17
+ # environment.register_transformer
18
+ # 'application/javascript+function',
19
+ # 'application/javascript', JstProcessor.new(namespace: 'App.templates')
19
20
  #
20
21
  class JstProcessor
21
22
  def self.default_namespace
@@ -33,8 +34,8 @@ module Sprockets
33
34
  instance.call(input)
34
35
  end
35
36
 
36
- def initialize(options = {})
37
- @namespace = options[:namespace] || self.class.default_namespace
37
+ def initialize(namespace: self.class.default_namespace)
38
+ @namespace = namespace
38
39
  end
39
40
 
40
41
  def call(input)