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