sprockets 3.7.4 → 4.0.0.beta1

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 (66) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +2 -303
  3. data/README.md +21 -35
  4. data/bin/sprockets +11 -8
  5. data/lib/rake/sprocketstask.rb +2 -2
  6. data/lib/sprockets/asset.rb +8 -21
  7. data/lib/sprockets/autoload/babel.rb +7 -0
  8. data/lib/sprockets/autoload/jsminc.rb +7 -0
  9. data/lib/sprockets/autoload/sassc.rb +7 -0
  10. data/lib/sprockets/autoload.rb +3 -0
  11. data/lib/sprockets/babel_processor.rb +58 -0
  12. data/lib/sprockets/base.rb +8 -8
  13. data/lib/sprockets/bower.rb +4 -2
  14. data/lib/sprockets/bundle.rb +1 -1
  15. data/lib/sprockets/cache.rb +2 -4
  16. data/lib/sprockets/closure_compressor.rb +1 -2
  17. data/lib/sprockets/coffee_script_processor.rb +9 -3
  18. data/lib/sprockets/compressing.rb +2 -2
  19. data/lib/sprockets/configuration.rb +1 -7
  20. data/lib/sprockets/context.rb +10 -18
  21. data/lib/sprockets/digest_utils.rb +40 -52
  22. data/lib/sprockets/directive_processor.rb +10 -15
  23. data/lib/sprockets/erb_processor.rb +1 -13
  24. data/lib/sprockets/http_utils.rb +19 -4
  25. data/lib/sprockets/jsminc_compressor.rb +31 -0
  26. data/lib/sprockets/jst_processor.rb +10 -10
  27. data/lib/sprockets/loader.rb +34 -28
  28. data/lib/sprockets/manifest.rb +3 -35
  29. data/lib/sprockets/manifest_utils.rb +0 -2
  30. data/lib/sprockets/mime.rb +7 -62
  31. data/lib/sprockets/path_dependency_utils.rb +2 -11
  32. data/lib/sprockets/path_digest_utils.rb +1 -1
  33. data/lib/sprockets/path_utils.rb +43 -18
  34. data/lib/sprockets/preprocessors/default_source_map.rb +24 -0
  35. data/lib/sprockets/processing.rb +30 -61
  36. data/lib/sprockets/processor_utils.rb +27 -28
  37. data/lib/sprockets/resolve.rb +172 -92
  38. data/lib/sprockets/sass_cache_store.rb +1 -6
  39. data/lib/sprockets/sass_compressor.rb +14 -1
  40. data/lib/sprockets/sass_processor.rb +18 -8
  41. data/lib/sprockets/sassc_compressor.rb +30 -0
  42. data/lib/sprockets/sassc_processor.rb +68 -0
  43. data/lib/sprockets/server.rb +11 -22
  44. data/lib/sprockets/source_map_comment_processor.rb +29 -0
  45. data/lib/sprockets/source_map_processor.rb +40 -0
  46. data/lib/sprockets/source_map_utils.rb +345 -0
  47. data/lib/sprockets/transformers.rb +62 -35
  48. data/lib/sprockets/uglifier_compressor.rb +12 -5
  49. data/lib/sprockets/unloaded_asset.rb +12 -11
  50. data/lib/sprockets/uri_tar.rb +4 -2
  51. data/lib/sprockets/uri_utils.rb +9 -14
  52. data/lib/sprockets/utils.rb +30 -79
  53. data/lib/sprockets/version.rb +1 -1
  54. data/lib/sprockets.rb +80 -35
  55. metadata +70 -41
  56. data/LICENSE +0 -21
  57. data/lib/sprockets/coffee_script_template.rb +0 -17
  58. data/lib/sprockets/deprecation.rb +0 -90
  59. data/lib/sprockets/eco_template.rb +0 -17
  60. data/lib/sprockets/ejs_template.rb +0 -17
  61. data/lib/sprockets/engines.rb +0 -92
  62. data/lib/sprockets/erb_template.rb +0 -11
  63. data/lib/sprockets/legacy.rb +0 -322
  64. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  65. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  66. data/lib/sprockets/sass_template.rb +0 -19
@@ -18,6 +18,8 @@ module Sprockets
18
18
  config[:transformers]
19
19
  end
20
20
 
21
+ Transformer = Struct.new :from, :to, :proc
22
+
21
23
  # Public: Register a transformer from and to a mime type.
22
24
  #
23
25
  # from - String mime type
@@ -33,10 +35,30 @@ module Sprockets
33
35
  #
34
36
  # Returns nothing.
35
37
  def register_transformer(from, to, proc)
36
- self.config = hash_reassoc(config, :registered_transformers, from) do |transformers|
37
- transformers.merge(to => proc)
38
+ self.config = hash_reassoc(config, :registered_transformers) do |transformers|
39
+ transformers << Transformer.new(from, to, proc)
40
+ end
41
+ compute_transformers!(self.config[:registered_transformers])
42
+ end
43
+
44
+ # Internal: Register transformer for existing type adding a suffix.
45
+ #
46
+ # types - Array of existing mime type Strings
47
+ # type_format - String suffix formatting string
48
+ # extname - String extension to append
49
+ # processor - Callable block that accepts an input Hash.
50
+ #
51
+ # Returns nothing.
52
+ def register_transformer_suffix(types, type_format, extname, processor)
53
+ Array(types).each do |type|
54
+ extensions, charset = mime_types[type].values_at(:extensions, :charset)
55
+ parts = type.split('/')
56
+ suffix_type = type_format.sub('\1', parts[0]).sub('\2', parts[1])
57
+ extensions = extensions.map { |ext| "#{ext}#{extname}" }
58
+
59
+ register_mime_type(suffix_type, extensions: extensions, charset: charset)
60
+ register_transformer(suffix_type, type, processor)
38
61
  end
39
- compute_transformers!
40
62
  end
41
63
 
42
64
  # Internal: Resolve target mime type that the source type should be
@@ -89,53 +111,58 @@ module Sprockets
89
111
  # types - Array of mime type steps
90
112
  #
91
113
  # Returns Processor.
92
- def compose_transformers(transformers, types)
114
+ def compose_transformers(transformers, types, preprocessors, postprocessors)
93
115
  if types.length < 2
94
116
  raise ArgumentError, "too few transform types: #{types.inspect}"
95
117
  end
96
118
 
97
- i = 0
98
- processors = []
99
-
100
- loop do
101
- src = types[i]
102
- dst = types[i+1]
103
- break unless src && dst
104
-
119
+ processors = types.each_cons(2).map { |src, dst|
105
120
  unless processor = transformers[src][dst]
106
121
  raise ArgumentError, "missing transformer for type: #{src} to #{dst}"
107
122
  end
108
- processors.concat config[:postprocessors][src]
109
- processors << processor
110
- processors.concat config[:preprocessors][dst]
123
+ processor
124
+ }
111
125
 
112
- i += 1
113
- end
114
-
115
- if processors.size > 1
116
- compose_processors(*processors.reverse)
117
- elsif processors.size == 1
118
- processors.first
119
- end
126
+ compose_transformer_list processors, preprocessors, postprocessors
120
127
  end
121
128
 
122
129
  private
123
- def compute_transformers!
124
- registered_transformers = self.config[:registered_transformers]
125
- transformers = Hash.new { {} }
130
+ def compose_transformer_list(transformers, preprocessors, postprocessors)
131
+ processors = []
132
+
133
+ transformers.each do |processor|
134
+ processors.concat postprocessors[processor.from]
135
+ processors << processor.proc
136
+ processors.concat preprocessors[processor.to]
137
+ end
138
+
139
+ if processors.size > 1
140
+ compose_processors(*processors.reverse)
141
+ elsif processors.size == 1
142
+ processors.first
143
+ end
144
+ end
145
+
146
+ def compute_transformers!(registered_transformers)
147
+ preprocessors = self.config[:preprocessors]
148
+ postprocessors = self.config[:postprocessors]
149
+ transformers = Hash.new { {} }
126
150
  inverted_transformers = Hash.new { Set.new }
151
+ incoming_edges = registered_transformers.group_by(&:from)
152
+
153
+ registered_transformers.each do |t|
154
+ traversals = dfs_paths([t]) { |k| incoming_edges.fetch(k.to, []) }
127
155
 
128
- registered_transformers.keys.flat_map do |key|
129
- dfs_paths([key]) { |k| registered_transformers[k].keys }
130
- end.each do |types|
131
- src, dst = types.first, types.last
132
- processor = compose_transformers(registered_transformers, types)
156
+ traversals.each do |nodes|
157
+ src, dst = nodes.first.from, nodes.last.to
158
+ processor = compose_transformer_list nodes, preprocessors, postprocessors
133
159
 
134
- transformers[src] = {} unless transformers.key?(src)
135
- transformers[src][dst] = processor
160
+ transformers[src] = {} unless transformers.key?(src)
161
+ transformers[src][dst] = processor
136
162
 
137
- inverted_transformers[dst] = Set.new unless inverted_transformers.key?(dst)
138
- inverted_transformers[dst] << src
163
+ inverted_transformers[dst] = Set.new unless inverted_transformers.key?(dst)
164
+ inverted_transformers[dst] << src
165
+ end
139
166
  end
140
167
 
141
168
  self.config = hash_reassoc(config, :transformers) { transformers }
@@ -1,5 +1,6 @@
1
1
  require 'sprockets/autoload'
2
2
  require 'sprockets/digest_utils'
3
+ require 'sprockets/source_map_utils'
3
4
 
4
5
  module Sprockets
5
6
  # Public: Uglifier/Uglify compressor.
@@ -15,7 +16,7 @@ module Sprockets
15
16
  # Sprockets::UglifierCompressor.new(comments: :copyright)
16
17
  #
17
18
  class UglifierCompressor
18
- VERSION = '1'
19
+ VERSION = '2'
19
20
 
20
21
  # Public: Return singleton instance with default options.
21
22
  #
@@ -41,16 +42,22 @@ module Sprockets
41
42
  options[:copyright] ||= false
42
43
  else
43
44
  # Uglifier >= 2.x
44
- options[:comments] ||= :none
45
+ options[:copyright] ||= :none
45
46
  end
46
47
 
47
- @options = options
48
+ @uglifier = Autoload::Uglifier.new(options)
48
49
  @cache_key = "#{self.class.name}:#{Autoload::Uglifier::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
49
50
  end
50
51
 
51
52
  def call(input)
52
- @uglifier ||= Autoload::Uglifier.new(@options)
53
- @uglifier.compile(input[:data])
53
+ js, map = @uglifier.compile_with_map(input[:data])
54
+
55
+ map = SourceMapUtils.combine_source_maps(
56
+ input[:metadata][:map],
57
+ SourceMapUtils.decode_json_source_map(map)["mappings"]
58
+ )
59
+
60
+ { data: js, map: map }
54
61
  end
55
62
  end
56
63
  end
@@ -12,8 +12,8 @@ module Sprockets
12
12
  # and full path such as
13
13
  # "file:///Path/app/assets/js/app.js?type=application/javascript"
14
14
  # env - The current "environment" that assets are being loaded into.
15
- # We need it so we know where the +root+ (directory where sprockets
16
- # is being invoked). We also need for the `file_digest` method,
15
+ # We need it so we know where the +root+ (directory where Sprockets
16
+ # is being invoked). We also need it for the `file_digest` method,
17
17
  # since, for some strange reason, memoization is provided by
18
18
  # overriding methods such as `stat` in the `PathUtils` module.
19
19
  #
@@ -30,7 +30,7 @@ module Sprockets
30
30
  # Internal: Full file path without schema
31
31
  #
32
32
  # This returns a string containing the full path to the asset without the schema.
33
- # Information is loaded lazilly since we want `UnloadedAsset.new(dep, self).relative_path`
33
+ # Information is loaded lazily since we want `UnloadedAsset.new(dep, self).relative_path`
34
34
  # to be fast. Calling this method the first time allocates an array and a hash.
35
35
  #
36
36
  # Example
@@ -48,8 +48,8 @@ module Sprockets
48
48
 
49
49
  # Internal: Hash of param values
50
50
  #
51
- # This information is generated and used internally by sprockets.
52
- # Known keys include `:type` which store the asset's mime-type, `:id` which is a fully resolved
51
+ # This information is generated and used internally by Sprockets.
52
+ # Known keys include `:type` which stores the asset's mime-type, `:id` which is a fully resolved
53
53
  # digest for the asset (includes dependency digest as opposed to a digest of only file contents)
54
54
  # and `:pipeline`. Hash may be empty.
55
55
  #
@@ -79,12 +79,12 @@ module Sprockets
79
79
 
80
80
  # Public: Dependency History key
81
81
  #
82
- # Used to retrieve an array of "histories" each of which contain a set of stored dependencies
82
+ # Used to retrieve an array of "histories" each of which contains a set of stored dependencies
83
83
  # for a given asset path and filename digest.
84
84
  #
85
- # A dependency can refer to either an asset i.e. index.js
85
+ # A dependency can refer to either an asset e.g. index.js
86
86
  # may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect
87
- # compilation, such as the VERSION of sprockets (i.e. the environment) and what "processors"
87
+ # compilation, such as the VERSION of Sprockets (i.e. the environment) and what "processors"
88
88
  # are used.
89
89
  #
90
90
  # For example a history array with one Set of dependencies may look like:
@@ -97,7 +97,7 @@ module Sprockets
97
97
  # This method of asset lookup is used to ensure that none of the dependencies have been modified
98
98
  # since last lookup. If one of them has, the key will be different and a new entry must be stored.
99
99
  #
100
- # URI depndencies are later converted to "compressed" paths
100
+ # URI dependencies are later converted to "compressed" paths
101
101
  #
102
102
  # Returns a String.
103
103
  def dependency_history_key
@@ -108,8 +108,9 @@ module Sprockets
108
108
  #
109
109
  # Used to retrieve a string containing the "compressed" path to an asset based on
110
110
  # a digest. The digest is generated from dependencies stored via information stored in
111
- # the `dependency_history_key` after each of the "dependencies" is "resolved" for example
112
- # "environment-version" may be resolved to "environment-1.0-3.2.0" for version "3.2.0" of sprockets
111
+ # the `dependency_history_key` after each of the "dependencies" is "resolved".
112
+ # For example "environment-version" may be resolved to "environment-1.0-3.2.0"
113
+ # for version "3.2.0" of Sprockets
113
114
  #
114
115
  # Returns a String.
115
116
  def digest_key(digest)
@@ -14,8 +14,10 @@ module Sprockets
14
14
  @env = env
15
15
  uri = uri.to_s
16
16
  if uri.include?("://".freeze)
17
- @scheme, _, @path = uri.partition("://".freeze)
18
- @scheme << "://".freeze
17
+ uri_array = uri.split("://".freeze)
18
+ @scheme = uri_array.shift
19
+ @scheme << "://".freeze
20
+ @path = uri_array.join("".freeze)
19
21
  else
20
22
  @scheme = "".freeze
21
23
  @path = uri
@@ -20,8 +20,6 @@ module Sprockets
20
20
  module URIUtils
21
21
  extend self
22
22
 
23
- URI_PARSER = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
24
-
25
23
  # Internal: Parse URI into component parts.
26
24
  #
27
25
  # uri - String uri
@@ -46,15 +44,12 @@ module Sprockets
46
44
  def split_file_uri(uri)
47
45
  scheme, _, host, _, _, path, _, query, _ = URI.split(uri)
48
46
 
49
- path = URI_PARSER.unescape(path)
47
+ path = URI::Generic::DEFAULT_PARSER.unescape(path)
50
48
  path.force_encoding(Encoding::UTF_8)
51
49
 
52
50
  # Hack for parsing Windows "file:///C:/Users/IEUser" paths
53
51
  path.gsub!(/^\/([a-zA-Z]:)/, '\1'.freeze)
54
52
 
55
- host = nil if host && host.empty?
56
- query = nil if query && query.empty?
57
-
58
53
  [scheme, host, path, query]
59
54
  end
60
55
 
@@ -64,8 +59,8 @@ module Sprockets
64
59
  def join_file_uri(scheme, host, path, query)
65
60
  str = "#{scheme}://"
66
61
  str << host if host
67
- path = "/#{path}" unless path.start_with?("/")
68
- str << URI_PARSER.escape(path)
62
+ path = "/#{path}" unless path.start_with?("/".freeze)
63
+ str << URI::Generic::DEFAULT_PARSER.escape(path)
69
64
  str << "?#{query}" if query
70
65
  str
71
66
  end
@@ -77,7 +72,7 @@ module Sprockets
77
72
  # Returns true or false.
78
73
  def valid_asset_uri?(str)
79
74
  # Quick prefix check before attempting a full parse
80
- str.start_with?("file://") && parse_asset_uri(str) ? true : false
75
+ str.start_with?("file://".freeze) && parse_asset_uri(str) ? true : false
81
76
  rescue URI::InvalidURIError
82
77
  false
83
78
  end
@@ -164,7 +159,7 @@ module Sprockets
164
159
  when Integer
165
160
  query << "#{key}=#{value}"
166
161
  when String, Symbol
167
- query << "#{key}=#{URI_PARSER.escape(value.to_s)}"
162
+ query << "#{key}=#{URI::Generic::DEFAULT_PARSER.escape(value.to_s)}"
168
163
  when TrueClass
169
164
  query << "#{key}"
170
165
  when FalseClass, NilClass
@@ -173,7 +168,7 @@ module Sprockets
173
168
  end
174
169
  end
175
170
 
176
- "#{query.join('&')}" if query.any?
171
+ "#{query.join('&'.freeze)}" if query.any?
177
172
  end
178
173
 
179
174
  # Internal: Parse query string into hash of params
@@ -182,9 +177,9 @@ module Sprockets
182
177
  #
183
178
  # Return Hash of params.
184
179
  def parse_uri_query_params(query)
185
- query.to_s.split('&').reduce({}) do |h, p|
186
- k, v = p.split('=', 2)
187
- v = URI_PARSER.unescape(v) if v
180
+ query.to_s.split('&'.freeze).reduce({}) do |h, p|
181
+ k, v = p.split('='.freeze, 2)
182
+ v = URI::Generic::DEFAULT_PARSER.unescape(v) if v
188
183
  h[k.to_sym] = v || true
189
184
  h
190
185
  end
@@ -14,15 +14,11 @@ module Sprockets
14
14
  #
15
15
  # Returns false if .dup would raise a TypeError, otherwise true.
16
16
  def duplicable?(obj)
17
- if RUBY_VERSION >= "2.4.0"
18
- true
17
+ case obj
18
+ when NilClass, FalseClass, TrueClass, Symbol, Numeric
19
+ false
19
20
  else
20
- case obj
21
- when NilClass, FalseClass, TrueClass, Symbol, Numeric
22
- false
23
- else
24
- true
25
- end
21
+ true
26
22
  end
27
23
  end
28
24
 
@@ -49,7 +45,8 @@ module Sprockets
49
45
  # Similar to Hash#store for nested frozen hashes.
50
46
  #
51
47
  # hash - Hash
52
- # key - Object keys. Use multiple keys for nested hashes.
48
+ # key_a - Object key. Use multiple keys for nested hashes.
49
+ # key_b - Object key. Use multiple keys for nested hashes.
53
50
  # block - Receives current value at key.
54
51
  #
55
52
  # Examples
@@ -60,13 +57,13 @@ module Sprockets
60
57
  # end
61
58
  #
62
59
  # Returns duplicated frozen Hash.
63
- def hash_reassoc(hash, *keys, &block)
64
- if keys.size == 1
65
- hash_reassoc1(hash, keys[0], &block)
66
- else
67
- hash_reassoc1(hash, keys[0]) do |value|
68
- hash_reassoc(value, *keys[1..-1], &block)
60
+ def hash_reassoc(hash, key_a, key_b = nil, &block)
61
+ if key_b
62
+ hash_reassoc1(hash, key_a) do |value|
63
+ hash_reassoc(value, key_b, &block)
69
64
  end
65
+ else
66
+ hash_reassoc1(hash, key_a, &block)
70
67
  end
71
68
  end
72
69
 
@@ -78,19 +75,16 @@ module Sprockets
78
75
  def string_end_with_semicolon?(str)
79
76
  i = str.size - 1
80
77
  while i >= 0
81
- c = str[i].ord
78
+ c = str[i]
82
79
  i -= 1
83
-
84
- # Need to compare against the ordinals because the string can be UTF_8 or UTF_32LE encoded
85
- # 0x0A == "\n"
86
- # 0x20 == " "
87
- # 0x09 == "\t"
88
- # 0x3B == ";"
89
- unless c == 0x0A || c == 0x20 || c == 0x09
90
- return c === 0x3B
80
+ if c == "\n" || c == " " || c == "\t"
81
+ next
82
+ elsif c != ";"
83
+ return false
84
+ else
85
+ return true
91
86
  end
92
87
  end
93
-
94
88
  true
95
89
  end
96
90
 
@@ -102,50 +96,11 @@ module Sprockets
102
96
  #
103
97
  # Returns buf String.
104
98
  def concat_javascript_sources(buf, source)
105
- buf = +buf
106
- if source.bytesize > 0
107
- buf << source
108
-
109
- # If the source contains non-ASCII characters, indexing on it becomes O(N).
110
- # This will lead to O(N^2) performance in string_end_with_semicolon?, so we should use 32 bit encoding to make sure indexing stays O(1)
111
- source = source.encode(Encoding::UTF_32LE) unless source.ascii_only?
112
-
113
- if !string_end_with_semicolon?(source)
114
- buf << ";\n"
115
- elsif source[source.size - 1].ord != 0x0A
116
- buf << "\n"
117
- end
99
+ if buf.bytesize > 0
100
+ buf << ";" unless string_end_with_semicolon?(buf)
101
+ buf << "\n" unless buf.end_with?("\n")
118
102
  end
119
-
120
- buf
121
- end
122
-
123
- # Internal: Prepends a leading "." to an extension if its missing.
124
- #
125
- # normalize_extension("js")
126
- # # => ".js"
127
- #
128
- # normalize_extension(".css")
129
- # # => ".css"
130
- #
131
- def normalize_extension(extension)
132
- extension = extension.to_s
133
- if extension[/^\./]
134
- extension
135
- else
136
- ".#{extension}"
137
- end
138
- end
139
-
140
- # Internal: Feature detect if UnboundMethods can #bind to any Object or
141
- # just Objects that share the same super class.
142
- # Basically if RUBY_VERSION >= 2.
143
- UNBOUND_METHODS_BIND_TO_ANY_OBJECT = begin
144
- foo = Module.new { def bar; end }
145
- foo.instance_method(:bar).bind(Object.new)
146
- true
147
- rescue TypeError
148
- false
103
+ buf << source
149
104
  end
150
105
 
151
106
  # Internal: Inject into target module for the duration of the block.
@@ -160,10 +115,6 @@ module Sprockets
160
115
  old_methods[sym] = base.instance_method(sym) if base.method_defined?(sym)
161
116
  end
162
117
 
163
- unless UNBOUND_METHODS_BIND_TO_ANY_OBJECT
164
- base.send(:include, mod) unless base < mod
165
- end
166
-
167
118
  mod.instance_methods.each do |sym|
168
119
  method = mod.instance_method(sym)
169
120
  base.send(:define_method, sym, method)
@@ -217,16 +168,16 @@ module Sprockets
217
168
  # Returns an Array of node Arrays.
218
169
  def dfs_paths(path)
219
170
  paths = []
220
- stack, seen = [path], Set.new
171
+ stack = [path]
172
+ seen = Set.new
221
173
 
222
174
  while path = stack.pop
223
- if !seen.include?(path.last)
224
- seen.add(path.last)
225
- paths << path if path.size > 1
175
+ seen.add(path.last)
176
+ paths << path
226
177
 
227
- Array(yield path.last).reverse_each do |node|
228
- stack.push(path + [node])
229
- end
178
+ children = yield path.last
179
+ children.reverse_each do |node|
180
+ stack.push(path + [node]) unless seen.include?(node)
230
181
  end
231
182
  end
232
183
 
@@ -1,3 +1,3 @@
1
1
  module Sprockets
2
- VERSION = "3.7.4"
2
+ VERSION = "4.0.0.beta1"
3
3
  end