oreorenasass 3.4.4 → 3.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +50 -70
  4. data/Rakefile +5 -26
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/sass +1 -1
  8. data/bin/scss +1 -1
  9. data/lib/sass.rb +12 -19
  10. data/lib/sass/cache_stores/base.rb +2 -2
  11. data/lib/sass/cache_stores/chain.rb +1 -2
  12. data/lib/sass/cache_stores/filesystem.rb +5 -1
  13. data/lib/sass/cache_stores/memory.rb +1 -1
  14. data/lib/sass/cache_stores/null.rb +2 -2
  15. data/lib/sass/callbacks.rb +0 -1
  16. data/lib/sass/css.rb +13 -11
  17. data/lib/sass/engine.rb +173 -424
  18. data/lib/sass/environment.rb +58 -148
  19. data/lib/sass/error.rb +14 -11
  20. data/lib/sass/exec.rb +703 -5
  21. data/lib/sass/importers/base.rb +6 -49
  22. data/lib/sass/importers/filesystem.rb +19 -44
  23. data/lib/sass/logger.rb +4 -1
  24. data/lib/sass/logger/base.rb +4 -2
  25. data/lib/sass/logger/log_level.rb +7 -3
  26. data/lib/sass/media.rb +23 -20
  27. data/lib/sass/plugin.rb +7 -7
  28. data/lib/sass/plugin/compiler.rb +145 -304
  29. data/lib/sass/plugin/configuration.rb +23 -18
  30. data/lib/sass/plugin/merb.rb +1 -1
  31. data/lib/sass/plugin/staleness_checker.rb +3 -3
  32. data/lib/sass/repl.rb +3 -3
  33. data/lib/sass/script.rb +8 -35
  34. data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
  35. data/lib/sass/script/bool.rb +18 -0
  36. data/lib/sass/script/color.rb +606 -0
  37. data/lib/sass/script/css_lexer.rb +4 -8
  38. data/lib/sass/script/css_parser.rb +2 -5
  39. data/lib/sass/script/funcall.rb +245 -0
  40. data/lib/sass/script/functions.rb +408 -1491
  41. data/lib/sass/script/interpolation.rb +79 -0
  42. data/lib/sass/script/lexer.rb +68 -172
  43. data/lib/sass/script/list.rb +85 -0
  44. data/lib/sass/script/literal.rb +221 -0
  45. data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
  46. data/lib/sass/script/{value/null.rb → null.rb} +7 -14
  47. data/lib/sass/script/{value/number.rb → number.rb} +75 -152
  48. data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
  49. data/lib/sass/script/parser.rb +110 -245
  50. data/lib/sass/script/string.rb +51 -0
  51. data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
  52. data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
  53. data/lib/sass/script/variable.rb +58 -0
  54. data/lib/sass/scss/css_parser.rb +3 -9
  55. data/lib/sass/scss/parser.rb +421 -450
  56. data/lib/sass/scss/rx.rb +11 -19
  57. data/lib/sass/scss/static_parser.rb +7 -321
  58. data/lib/sass/selector.rb +194 -68
  59. data/lib/sass/selector/abstract_sequence.rb +14 -29
  60. data/lib/sass/selector/comma_sequence.rb +25 -108
  61. data/lib/sass/selector/sequence.rb +66 -159
  62. data/lib/sass/selector/simple.rb +25 -23
  63. data/lib/sass/selector/simple_sequence.rb +63 -173
  64. data/lib/sass/shared.rb +1 -1
  65. data/lib/sass/supports.rb +15 -13
  66. data/lib/sass/tree/charset_node.rb +1 -1
  67. data/lib/sass/tree/comment_node.rb +3 -3
  68. data/lib/sass/tree/css_import_node.rb +11 -11
  69. data/lib/sass/tree/debug_node.rb +2 -2
  70. data/lib/sass/tree/directive_node.rb +4 -21
  71. data/lib/sass/tree/each_node.rb +8 -8
  72. data/lib/sass/tree/extend_node.rb +7 -14
  73. data/lib/sass/tree/for_node.rb +4 -4
  74. data/lib/sass/tree/function_node.rb +4 -9
  75. data/lib/sass/tree/if_node.rb +1 -1
  76. data/lib/sass/tree/import_node.rb +5 -4
  77. data/lib/sass/tree/media_node.rb +14 -4
  78. data/lib/sass/tree/mixin_def_node.rb +4 -4
  79. data/lib/sass/tree/mixin_node.rb +8 -21
  80. data/lib/sass/tree/node.rb +12 -54
  81. data/lib/sass/tree/prop_node.rb +20 -39
  82. data/lib/sass/tree/return_node.rb +2 -3
  83. data/lib/sass/tree/root_node.rb +3 -19
  84. data/lib/sass/tree/rule_node.rb +22 -35
  85. data/lib/sass/tree/supports_node.rb +13 -0
  86. data/lib/sass/tree/trace_node.rb +1 -2
  87. data/lib/sass/tree/variable_node.rb +3 -9
  88. data/lib/sass/tree/visitors/base.rb +8 -5
  89. data/lib/sass/tree/visitors/check_nesting.rb +19 -49
  90. data/lib/sass/tree/visitors/convert.rb +56 -74
  91. data/lib/sass/tree/visitors/cssize.rb +74 -202
  92. data/lib/sass/tree/visitors/deep_copy.rb +5 -10
  93. data/lib/sass/tree/visitors/extend.rb +7 -7
  94. data/lib/sass/tree/visitors/perform.rb +185 -278
  95. data/lib/sass/tree/visitors/set_options.rb +6 -20
  96. data/lib/sass/tree/visitors/to_css.rb +81 -234
  97. data/lib/sass/tree/warn_node.rb +2 -2
  98. data/lib/sass/tree/while_node.rb +2 -2
  99. data/lib/sass/util.rb +152 -522
  100. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  101. data/lib/sass/util/subset_map.rb +3 -4
  102. data/lib/sass/util/test.rb +1 -0
  103. data/lib/sass/version.rb +22 -20
  104. data/test/Gemfile +3 -0
  105. data/test/Gemfile.lock +10 -0
  106. data/test/sass/cache_test.rb +20 -62
  107. data/test/sass/callbacks_test.rb +1 -1
  108. data/test/sass/conversion_test.rb +2 -296
  109. data/test/sass/css2sass_test.rb +4 -23
  110. data/test/sass/engine_test.rb +354 -411
  111. data/test/sass/exec_test.rb +2 -2
  112. data/test/sass/extend_test.rb +145 -324
  113. data/test/sass/functions_test.rb +86 -873
  114. data/test/sass/importer_test.rb +21 -241
  115. data/test/sass/logger_test.rb +1 -1
  116. data/test/sass/more_results/more_import.css +1 -1
  117. data/test/sass/plugin_test.rb +26 -16
  118. data/test/sass/results/compact.css +1 -1
  119. data/test/sass/results/complex.css +4 -4
  120. data/test/sass/results/expanded.css +1 -1
  121. data/test/sass/results/import.css +1 -1
  122. data/test/sass/results/import_charset_ibm866.css +2 -2
  123. data/test/sass/results/mixins.css +17 -17
  124. data/test/sass/results/nested.css +1 -1
  125. data/test/sass/results/parent_ref.css +2 -2
  126. data/test/sass/results/script.css +3 -3
  127. data/test/sass/results/scss_import.css +1 -1
  128. data/test/sass/script_conversion_test.rb +7 -36
  129. data/test/sass/script_test.rb +53 -485
  130. data/test/sass/scss/css_test.rb +28 -143
  131. data/test/sass/scss/rx_test.rb +4 -4
  132. data/test/sass/scss/scss_test.rb +325 -2119
  133. data/test/sass/templates/scss_import.scss +1 -2
  134. data/test/sass/test_helper.rb +1 -1
  135. data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
  136. data/test/sass/util/subset_map_test.rb +2 -2
  137. data/test/sass/util_test.rb +1 -86
  138. data/test/test_helper.rb +8 -37
  139. metadata +19 -66
  140. data/lib/sass/exec/base.rb +0 -187
  141. data/lib/sass/exec/sass_convert.rb +0 -264
  142. data/lib/sass/exec/sass_scss.rb +0 -424
  143. data/lib/sass/features.rb +0 -47
  144. data/lib/sass/script/tree.rb +0 -16
  145. data/lib/sass/script/tree/funcall.rb +0 -306
  146. data/lib/sass/script/tree/interpolation.rb +0 -118
  147. data/lib/sass/script/tree/list_literal.rb +0 -77
  148. data/lib/sass/script/tree/literal.rb +0 -45
  149. data/lib/sass/script/tree/map_literal.rb +0 -64
  150. data/lib/sass/script/tree/selector.rb +0 -26
  151. data/lib/sass/script/tree/variable.rb +0 -57
  152. data/lib/sass/script/value.rb +0 -11
  153. data/lib/sass/script/value/base.rb +0 -240
  154. data/lib/sass/script/value/bool.rb +0 -35
  155. data/lib/sass/script/value/color.rb +0 -680
  156. data/lib/sass/script/value/helpers.rb +0 -262
  157. data/lib/sass/script/value/list.rb +0 -113
  158. data/lib/sass/script/value/map.rb +0 -70
  159. data/lib/sass/script/value/string.rb +0 -97
  160. data/lib/sass/selector/pseudo.rb +0 -256
  161. data/lib/sass/source/map.rb +0 -210
  162. data/lib/sass/source/position.rb +0 -39
  163. data/lib/sass/source/range.rb +0 -41
  164. data/lib/sass/stack.rb +0 -120
  165. data/lib/sass/tree/at_root_node.rb +0 -83
  166. data/lib/sass/tree/error_node.rb +0 -18
  167. data/lib/sass/tree/keyframe_rule_node.rb +0 -15
  168. data/lib/sass/util/cross_platform_random.rb +0 -19
  169. data/lib/sass/util/normalized_map.rb +0 -130
  170. data/lib/sass/util/ordered_hash.rb +0 -192
  171. data/test/sass/compiler_test.rb +0 -232
  172. data/test/sass/encoding_test.rb +0 -219
  173. data/test/sass/source_map_test.rb +0 -977
  174. data/test/sass/superselector_test.rb +0 -191
  175. data/test/sass/util/normalized_map_test.rb +0 -51
  176. data/test/sass/value_helpers_test.rb +0 -179
@@ -15,9 +15,13 @@ module Sass
15
15
  # They should also implement the \{#find\_relative} method.
16
16
  #
17
17
  # Importers should be serializable via `Marshal.dump`.
18
+ # In addition to the standard `_dump` and `_load` methods,
19
+ # importers can define `_before_dump`, `_after_dump`, `_around_dump`,
20
+ # and `_after_load` methods as per {Sass::Util#dump} and {Sass::Util#load}.
18
21
  #
19
22
  # @abstract
20
23
  class Base
24
+
21
25
  # Find a Sass file relative to another file.
22
26
  # Importers without a notion of "relative paths"
23
27
  # should just return nil here.
@@ -118,34 +122,6 @@ module Sass
118
122
  Sass::Util.abstract(self)
119
123
  end
120
124
 
121
- # Get the publicly-visible URL for an imported file. This URL is used by
122
- # source maps to link to the source stylesheet. This may return `nil` to
123
- # indicate that no public URL is available; however, this will cause
124
- # sourcemap generation to fail if any CSS is generated from files imported
125
- # from this importer.
126
- #
127
- # If an absolute "file:" URI can be produced for an imported file, that
128
- # should be preferred to returning `nil`. However, a URL relative to
129
- # `sourcemap_directory` should be preferred over an absolute "file:" URI.
130
- #
131
- # @param uri [String] A URI known to be valid for this importer.
132
- # @param sourcemap_directory [String, NilClass] The absolute path to a
133
- # directory on disk where the sourcemap will be saved. If uri refers to
134
- # a file on disk that's accessible relative to sourcemap_directory, this
135
- # may return a relative URL. This may be `nil` if the sourcemap's
136
- # eventual location is unknown.
137
- # @return [String?] The publicly-visible URL for this file, or `nil`
138
- # indicating that no publicly-visible URL exists. This should be
139
- # appropriately URL-escaped.
140
- def public_url(uri, sourcemap_directory)
141
- return if @public_url_warning_issued
142
- @public_url_warning_issued = true
143
- Sass::Util.sass_warn <<WARNING
144
- WARNING: #{self.class.name} should define the #public_url method.
145
- WARNING
146
- nil
147
- end
148
-
149
125
  # A string representation of the importer.
150
126
  # Should be overridden by subclasses.
151
127
  #
@@ -156,27 +132,8 @@ WARNING
156
132
  def to_s
157
133
  Sass::Util.abstract(self)
158
134
  end
159
-
160
- # If the importer is based on files on the local filesystem
161
- # this method should return folders which should be watched
162
- # for changes.
163
- #
164
- # @return [Array<String>] List of absolute paths of directories to watch
165
- def directories_to_watch
166
- []
167
- end
168
-
169
- # If this importer is based on files on the local filesystem This method
170
- # should return true if the file, when changed, should trigger a
171
- # recompile.
172
- #
173
- # It is acceptable for non-sass files to be watched and trigger a recompile.
174
- #
175
- # @param filename [String] The absolute filename for a file that has changed.
176
- # @return [Boolean] When the file changed should cause a recompile.
177
- def watched_file?(filename)
178
- false
179
- end
180
135
  end
181
136
  end
182
137
  end
138
+
139
+
@@ -1,3 +1,4 @@
1
+ require 'pathname'
1
2
  require 'set'
2
3
 
3
4
  module Sass
@@ -5,6 +6,7 @@ module Sass
5
6
  # The default importer, used for any strings found in the load path.
6
7
  # Simply loads Sass files from the filesystem using the default logic.
7
8
  class Filesystem < Base
9
+
8
10
  attr_accessor :root
9
11
 
10
12
  # Creates a new filesystem importer that imports files relative to a given path.
@@ -37,7 +39,7 @@ module Sass
37
39
  # @see Base#key
38
40
  def key(name, options)
39
41
  [self.class.name + ":" + File.dirname(File.expand_path(name)),
40
- File.basename(name)]
42
+ File.basename(name)]
41
43
  end
42
44
 
43
45
  # @see Base#to_s
@@ -53,31 +55,6 @@ module Sass
53
55
  root.eql?(other.root)
54
56
  end
55
57
 
56
- # @see Base#directories_to_watch
57
- def directories_to_watch
58
- [root]
59
- end
60
-
61
- # @see Base#watched_file?
62
- def watched_file?(filename)
63
- filename =~ /\.s[ac]ss$/ &&
64
- filename.start_with?(root + File::SEPARATOR)
65
- end
66
-
67
- def public_url(name, sourcemap_directory)
68
- file_pathname = Sass::Util.cleanpath(Sass::Util.absolute_path(name, @root))
69
- if sourcemap_directory.nil?
70
- Sass::Util.file_uri_from_path(file_pathname)
71
- else
72
- sourcemap_pathname = Sass::Util.cleanpath(sourcemap_directory)
73
- begin
74
- Sass::Util.file_uri_from_path(file_pathname.relative_path_from(sourcemap_pathname))
75
- rescue ArgumentError # when a relative path cannot be constructed
76
- Sass::Util.file_uri_from_path(file_pathname)
77
- end
78
- end
79
- end
80
-
81
58
  protected
82
59
 
83
60
  # If a full uri is passed, this removes the root from it
@@ -121,7 +98,7 @@ module Sass
121
98
  end
122
99
 
123
100
  # JRuby chokes when trying to import files from JARs when the path starts with './'.
124
- ret.map {|f, s| [f.sub(/^\.\//, ''), s]}
101
+ ret.map {|f, s| [f.sub(%r{^\./}, ''), s]}
125
102
  end
126
103
 
127
104
  def escape_glob_characters(name)
@@ -130,7 +107,7 @@ module Sass
130
107
  end
131
108
  end
132
109
 
133
- REDUNDANT_DIRECTORY = /#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}/
110
+ REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
134
111
  # Given a base directory and an `@import`ed name,
135
112
  # finds an existant file that matches the name.
136
113
  #
@@ -138,16 +115,14 @@ module Sass
138
115
  # @param name [String] The filename to search for.
139
116
  # @return [(String, Symbol)] A filename-syntax pair.
140
117
  def find_real_file(dir, name, options)
141
- # On windows 'dir' or 'name' can be in native File::ALT_SEPARATOR form.
118
+ # on windows 'dir' can be in native File::ALT_SEPARATOR form
142
119
  dir = dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
143
- name = name.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
144
120
 
145
121
  found = possible_files(remove_root(name)).map do |f, s|
146
- path = (dir == "." || Sass::Util.pathname(f).absolute?) ? f :
147
- "#{escape_glob_characters(dir)}/#{f}"
122
+ path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{escape_glob_characters(dir)}/#{f}"
148
123
  Dir[path].map do |full_path|
149
124
  full_path.gsub!(REDUNDANT_DIRECTORY, File::SEPARATOR)
150
- [Sass::Util.cleanpath(full_path).to_s, s]
125
+ [Pathname.new(full_path).cleanpath.to_s, s]
151
126
  end
152
127
  end
153
128
  found = Sass::Util.flatten(found, 1)
@@ -155,20 +130,20 @@ module Sass
155
130
 
156
131
  if found.size > 1 && !@same_name_warnings.include?(found.first.first)
157
132
  found.each {|(f, _)| @same_name_warnings << f}
158
- relative_to = Sass::Util.pathname(dir)
159
- if options[:_from_import_node]
133
+ relative_to = Pathname.new(dir)
134
+ if options[:_line]
160
135
  # If _line exists, we're here due to an actual import in an
161
136
  # import_node and we want to print a warning for a user writing an
162
137
  # ambiguous import.
163
- candidates = found.map do |(f, _)|
164
- " " + Sass::Util.pathname(f).relative_path_from(relative_to).to_s
165
- end.join("\n")
166
- raise Sass::SyntaxError.new(<<MESSAGE)
167
- It's not clear which file to import for '@import "#{name}"'.
168
- Candidates:
138
+ candidates = found.map {|(f, _)| " " + Pathname.new(f).relative_path_from(relative_to).to_s}.join("\n")
139
+ Sass::Util.sass_warn <<WARNING
140
+ WARNING: On line #{options[:_line]}#{" of #{options[:filename]}" if options[:filename]}:
141
+ It's not clear which file to import for '@import "#{name}"'.
142
+ Candidates:
169
143
  #{candidates}
170
- Please delete or rename all but one of these files.
171
- MESSAGE
144
+ For now I'll choose #{File.basename found.first.first}.
145
+ This will be an error in future versions of Sass.
146
+ WARNING
172
147
  else
173
148
  # Otherwise, we're here via StalenessChecker, and we want to print a
174
149
  # warning for a user running `sass --watch` with two ambiguous files.
@@ -188,7 +163,7 @@ WARNING
188
163
  def split(name)
189
164
  extension = nil
190
165
  dirname, basename = File.dirname(name), File.basename(name)
191
- if basename =~ /^(.*)\.(#{extensions.keys.map {|e| Regexp.escape(e)}.join('|')})$/
166
+ if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
192
167
  basename = $1
193
168
  extension = $2
194
169
  end
data/lib/sass/logger.rb CHANGED
@@ -1,9 +1,12 @@
1
- module Sass::Logger; end
1
+ module Sass::Logger
2
+
3
+ end
2
4
 
3
5
  require "sass/logger/log_level"
4
6
  require "sass/logger/base"
5
7
 
6
8
  module Sass
9
+
7
10
  class << self
8
11
  attr_accessor :logger
9
12
  end
@@ -1,6 +1,7 @@
1
1
  require 'sass/logger/log_level'
2
2
 
3
3
  class Sass::Logger::Base
4
+
4
5
  include Sass::Logger::LogLevel
5
6
 
6
7
  attr_accessor :log_level
@@ -21,10 +22,11 @@ class Sass::Logger::Base
21
22
  end
22
23
 
23
24
  def log(level, message)
24
- _log(level, message) if logging_level?(level)
25
+ self._log(level, message) if logging_level?(level)
25
26
  end
26
27
 
27
28
  def _log(level, message)
28
- Kernel.warn(message)
29
+ Kernel::warn(message)
29
30
  end
31
+
30
32
  end
@@ -1,21 +1,24 @@
1
1
  module Sass
2
2
  module Logger
3
3
  module LogLevel
4
+
4
5
  def self.included(base)
5
6
  base.extend(ClassMethods)
6
7
  end
7
-
8
+
8
9
  module ClassMethods
9
10
  def inherited(subclass)
10
11
  subclass.log_levels = subclass.superclass.log_levels.dup
11
12
  end
12
13
 
13
- attr_writer :log_levels
14
-
15
14
  def log_levels
16
15
  @log_levels ||= {}
17
16
  end
18
17
 
18
+ def log_levels=(levels)
19
+ @log_levels = levels
20
+ end
21
+
19
22
  def log_level?(level, min_level)
20
23
  log_levels[level] >= log_levels[min_level]
21
24
  end
@@ -40,6 +43,7 @@ module Sass
40
43
  RUBY
41
44
  end
42
45
  end
46
+
43
47
  end
44
48
  end
45
49
  end
data/lib/sass/media.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  module Sass::Media
3
3
  # A comma-separated list of queries.
4
4
  #
5
- # media_query [ ',' S* media_query ]*
5
+ # media_query [ ',' S* media_query ]*
6
6
  class QueryList
7
7
  # The queries contained in this list.
8
8
  #
@@ -43,11 +43,11 @@ module Sass::Media
43
43
  end
44
44
 
45
45
  # Returns a representation of the query as an array of strings and
46
- # potentially {Sass::Script::Tree::Node}s (if there's interpolation in it).
47
- # When the interpolation is resolved and the strings are joined together,
48
- # this will be the string representation of this query.
46
+ # potentially {Sass::Script::Node}s (if there's interpolation in it). When
47
+ # the interpolation is resolved and the strings are joined together, this
48
+ # will be the string representation of this query.
49
49
  #
50
- # @return [Array<String, Sass::Script::Tree::Node>]
50
+ # @return [Array<String, Sass::Script::Node>]
51
51
  def to_a
52
52
  Sass::Util.intersperse(queries.map {|q| q.to_a}, ', ').flatten
53
53
  end
@@ -62,7 +62,7 @@ module Sass::Media
62
62
 
63
63
  # A single media query.
64
64
  #
65
- # [ [ONLY | NOT]? S* media_type S* | expression ] [ AND S* expression ]*
65
+ # [ [ONLY | NOT]? S* media_type S* | expression ] [ AND S* expression ]*
66
66
  class Query
67
67
  # The modifier for the query.
68
68
  #
@@ -70,7 +70,7 @@ module Sass::Media
70
70
  # parsed as CSS, it contains a single string (accessible via
71
71
  # \{#resolved_modifier}).
72
72
  #
73
- # @return [Array<String, Sass::Script::Tree::Node>]
73
+ # @return [Array<String, Sass::Script::Node>]
74
74
  attr_accessor :modifier
75
75
 
76
76
  # The type of the query (e.g. `"screen"` or `"print"`).
@@ -79,7 +79,7 @@ module Sass::Media
79
79
  # parsed as CSS, it contains a single string (accessible via
80
80
  # \{#resolved_type}).
81
81
  #
82
- # @return [Array<String, Sass::Script::Tree::Node>]
82
+ # @return [Array<String, Sass::Script::Node>]
83
83
  attr_accessor :type
84
84
 
85
85
  # The trailing expressions in the query.
@@ -87,12 +87,12 @@ module Sass::Media
87
87
  # When parsed as Sass code, each expression contains strings and SassScript
88
88
  # nodes. When parsed as CSS, each one contains a single string.
89
89
  #
90
- # @return [Array<Array<String, Sass::Script::Tree::Node>>]
90
+ # @return [Array<Array<String, Sass::Script::Node>>]
91
91
  attr_accessor :expressions
92
92
 
93
- # @param modifier [Array<String, Sass::Script::Tree::Node>] See \{#modifier}
94
- # @param type [Array<String, Sass::Script::Tree::Node>] See \{#type}
95
- # @param expressions [Array<Array<String, Sass::Script::Tree::Node>>] See \{#expressions}
93
+ # @param modifier [Array<String, Sass::Script::Node>] See \{#modifier}
94
+ # @param type [Array<String, Sass::Script::Node>] See \{#type}
95
+ # @param expressions [Array<Array<String, Sass::Script::Node>>] See \{#expressions}
96
96
  def initialize(modifier, type, expressions)
97
97
  @modifier = modifier
98
98
  @type = type
@@ -125,7 +125,7 @@ module Sass::Media
125
125
  m2, t2 = other.resolved_modifier.downcase, other.resolved_type.downcase
126
126
  t1 = t2 if t1.empty?
127
127
  t2 = t1 if t2.empty?
128
- if (m1 == 'not') ^ (m2 == 'not')
128
+ if ((m1 == 'not') ^ (m2 == 'not'))
129
129
  return if t1 == t2
130
130
  type = m1 == 'not' ? t2 : t1
131
131
  mod = m1 == 'not' ? m2 : m1
@@ -140,7 +140,7 @@ module Sass::Media
140
140
  type = t1
141
141
  mod = m1.empty? ? m2 : m1
142
142
  end
143
- Query.new([mod], [type], other.expressions + expressions)
143
+ return Query.new([mod], [type], other.expressions + expressions)
144
144
  end
145
145
 
146
146
  # Returns the CSS for the media query.
@@ -156,7 +156,7 @@ module Sass::Media
156
156
  # It's possible for there to be script nodes in Expressions even when
157
157
  # we're converting to CSS in the case where we parsed the document as
158
158
  # CSS originally (as in css_test.rb).
159
- e.map {|c| c.is_a?(Sass::Script::Tree::Node) ? c.to_sass : c.to_s}.join
159
+ e.map {|c| c.is_a?(Sass::Script::Node) ? c.to_sass : c.to_s}.join
160
160
  end.join(' and ')
161
161
  css
162
162
  end
@@ -193,18 +193,21 @@ module Sass::Media
193
193
  # @return [Query]
194
194
  def deep_copy
195
195
  Query.new(
196
- modifier.map {|c| c.is_a?(Sass::Script::Tree::Node) ? c.deep_copy : c},
197
- type.map {|c| c.is_a?(Sass::Script::Tree::Node) ? c.deep_copy : c},
198
- expressions.map {|e| e.map {|c| c.is_a?(Sass::Script::Tree::Node) ? c.deep_copy : c}})
196
+ modifier.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
197
+ type.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
198
+ expressions.map {|e| e.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}})
199
199
  end
200
200
  end
201
201
 
202
202
  # Converts an interpolation array to source.
203
203
  #
204
- # @param interp [Array<String, Sass::Script::Tree::Node>] The interpolation array to convert.
204
+ # @param [Array<String, Sass::Script::Node>] The interpolation array to convert.
205
205
  # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
206
206
  # @return [String]
207
207
  def self._interp_to_src(interp, options)
208
- interp.map {|r| r.is_a?(String) ? r : r.to_sass(options)}.join
208
+ interp.map do |r|
209
+ next r if r.is_a?(String)
210
+ "\#{#{r.to_sass(options)}}"
211
+ end.join
209
212
  end
210
213
  end
data/lib/sass/plugin.rb CHANGED
@@ -11,8 +11,7 @@ module Sass
11
11
  # This module is used as the primary interface with Sass
12
12
  # when it's used as a plugin for various frameworks.
13
13
  # All Rack-enabled frameworks are supported out of the box.
14
- # The plugin is
15
- # {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
14
+ # The plugin is {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
16
15
  # Other frameworks must enable it explicitly; see {Sass::Plugin::Rack}.
17
16
  #
18
17
  # This module has a large set of callbacks available
@@ -33,6 +32,7 @@ module Sass
33
32
  # #=> Compiling app/sass/ie.scss to public/stylesheets/ie.css
34
33
  # @see Sass::Plugin::Compiler
35
34
  module Plugin
35
+ include Sass::Util
36
36
  extend self
37
37
 
38
38
  @checked_for_updates = false
@@ -65,8 +65,7 @@ module Sass
65
65
 
66
66
  # Updates out-of-date stylesheets.
67
67
  #
68
- # Checks each Sass/SCSS file in
69
- # {file:SASS_REFERENCE.md#template_location-option `:template_location`}
68
+ # Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
70
69
  # to see if it's been modified more recently than the corresponding CSS file
71
70
  # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
72
71
  # If it has, it updates the CSS file.
@@ -120,12 +119,13 @@ module Sass
120
119
  def options
121
120
  compiler.options
122
121
  end
122
+
123
123
  end
124
124
  end
125
125
 
126
- if defined?(ActionController)
127
- # On Rails 3+ the rails plugin is loaded at the right time in railtie.rb
128
- require 'sass/plugin/rails' unless Sass::Util.ap_geq_3?
126
+ # On Rails 3+ the rails plugin is loaded at the right time in railtie.rb
127
+ if defined?(ActionController) && !Sass::Util.ap_geq_3?
128
+ require 'sass/plugin/rails'
129
129
  elsif defined?(Merb::Plugins)
130
130
  require 'sass/plugin/merb'
131
131
  else
@@ -7,6 +7,7 @@ require 'sass/plugin/configuration'
7
7
  require 'sass/plugin/staleness_checker'
8
8
 
9
9
  module Sass::Plugin
10
+
10
11
  # The Compiler class handles compilation of multiple files and/or directories,
11
12
  # including checking which CSS files are out-of-date and need to be updated
12
13
  # and calling Sass to perform the compilation on those files.
@@ -25,41 +26,31 @@ module Sass::Plugin
25
26
  # * `:never_update`
26
27
  # * `:always_check`
27
28
  class Compiler
29
+ include Sass::Util
28
30
  include Configuration
29
31
  extend Sass::Callbacks
30
32
 
31
33
  # Creates a new compiler.
32
34
  #
33
- # @param opts [{Symbol => Object}]
35
+ # @param options [{Symbol => Object}]
34
36
  # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
35
- def initialize(opts = {})
36
- options.merge!(opts)
37
+ def initialize(options = {})
38
+ self.options.merge!(options)
37
39
  end
38
40
 
39
- # Register a callback to be run before stylesheets are mass-updated.
41
+ # Register a callback to be run after stylesheets are mass-updated.
40
42
  # This is run whenever \{#update\_stylesheets} is called,
41
43
  # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
42
44
  # is enabled.
43
45
  #
44
- # @yield [files]
45
- # @yieldparam files [<(String, String, String)>]
46
- # Individual files to be updated. Files in directories specified are included in this list.
46
+ # @yield [individual_files]
47
+ # @yieldparam individual_files [<(String, String)>]
48
+ # Individual files to be updated, in addition to the directories
49
+ # specified in the options.
47
50
  # The first element of each pair is the source file,
48
- # the second is the target CSS file,
49
- # the third is the target sourcemap file.
51
+ # the second is the target CSS file.
50
52
  define_callback :updating_stylesheets
51
53
 
52
- # Register a callback to be run after stylesheets are mass-updated.
53
- # This is run whenever \{#update\_stylesheets} is called,
54
- # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
55
- # is enabled.
56
- #
57
- # @yield [updated_files]
58
- # @yieldparam updated_files [<(String, String)>]
59
- # Individual files that were updated.
60
- # The first element of each pair is the source file, the second is the target CSS file.
61
- define_callback :updated_stylesheets
62
-
63
54
  # Register a callback to be run after a single stylesheet is updated.
64
55
  # The callback is only run if the stylesheet is really updated;
65
56
  # if the CSS file is fresh, this won't be run.
@@ -69,29 +60,35 @@ module Sass::Plugin
69
60
  # when an exception CSS file is being written.
70
61
  # To run an action for those files, use \{#on\_compilation\_error}.
71
62
  #
72
- # @yield [template, css, sourcemap]
63
+ # @yield [template, css]
73
64
  # @yieldparam template [String]
74
65
  # The location of the Sass/SCSS file being updated.
75
66
  # @yieldparam css [String]
76
67
  # The location of the CSS file being generated.
77
- # @yieldparam sourcemap [String]
78
- # The location of the sourcemap being generated, if any.
79
68
  define_callback :updated_stylesheet
80
69
 
81
- # Register a callback to be run when compilation starts.
70
+ # Register a callback to be run before a single stylesheet is updated.
71
+ # The callback is only run if the stylesheet is guaranteed to be updated;
72
+ # if the CSS file is fresh, this won't be run.
82
73
  #
83
- # In combination with on_updated_stylesheet, this could be used
84
- # to collect compilation statistics like timing or to take a
85
- # diff of the changes to the output file.
74
+ # Even if the \{file:SASS_REFERENCE.md#full_exception-option `:full_exception` option}
75
+ # is enabled, this callback won't be run
76
+ # when an exception CSS file is being written.
77
+ # To run an action for those files, use \{#on\_compilation\_error}.
86
78
  #
87
- # @yield [template, css, sourcemap]
79
+ # @yield [template, css]
88
80
  # @yieldparam template [String]
89
81
  # The location of the Sass/SCSS file being updated.
90
82
  # @yieldparam css [String]
91
83
  # The location of the CSS file being generated.
92
- # @yieldparam sourcemap [String]
93
- # The location of the sourcemap being generated, if any.
94
- define_callback :compilation_starting
84
+ define_callback :updating_stylesheet
85
+
86
+ def on_updating_stylesheet_with_deprecation_warning(&block)
87
+ Sass::Util.sass_warn("Sass::Compiler#on_updating_stylesheet callback is deprecated and will be removed in a future release. Use Sass::Compiler#on_updated_stylesheet instead, which is run after stylesheet compilation.")
88
+ on_updating_stylesheet_without_deprecation_warning(&block)
89
+ end
90
+ alias_method :on_updating_stylesheet_without_deprecation_warning, :on_updating_stylesheet
91
+ alias_method :on_updating_stylesheet, :on_updating_stylesheet_with_deprecation_warning
95
92
 
96
93
  # Register a callback to be run when Sass decides not to update a stylesheet.
97
94
  # In particular, the callback is run when Sass finds that
@@ -165,97 +162,49 @@ module Sass::Plugin
165
162
  define_callback :template_deleted
166
163
 
167
164
  # Register a callback to be run when Sass deletes a CSS file.
168
- # This happens when the corresponding Sass/SCSS file has been deleted
169
- # and when the compiler cleans the output files.
165
+ # This happens when the corresponding Sass/SCSS file has been deleted.
170
166
  #
171
167
  # @yield [filename]
172
168
  # @yieldparam filename [String]
173
169
  # The location of the CSS file that was deleted.
174
170
  define_callback :deleting_css
175
171
 
176
- # Register a callback to be run when Sass deletes a sourcemap file.
177
- # This happens when the corresponding Sass/SCSS file has been deleted
178
- # and when the compiler cleans the output files.
179
- #
180
- # @yield [filename]
181
- # @yieldparam filename [String]
182
- # The location of the sourcemap file that was deleted.
183
- define_callback :deleting_sourcemap
184
-
185
172
  # Updates out-of-date stylesheets.
186
173
  #
187
- # Checks each Sass/SCSS file in
188
- # {file:SASS_REFERENCE.md#template_location-option `:template_location`}
174
+ # Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
189
175
  # to see if it's been modified more recently than the corresponding CSS file
190
176
  # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
191
177
  # If it has, it updates the CSS file.
192
178
  #
193
- # @param individual_files [Array<(String, String[, String])>]
179
+ # @param individual_files [Array<(String, String)>]
194
180
  # A list of files to check for updates
195
181
  # **in addition to those specified by the
196
182
  # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
197
183
  # The first string in each pair is the location of the Sass/SCSS file,
198
184
  # the second is the location of the CSS file that it should be compiled to.
199
- # The third string, if provided, is the location of the Sourcemap file.
200
185
  def update_stylesheets(individual_files = [])
186
+ individual_files = individual_files.dup
201
187
  Sass::Plugin.checked_for_updates = true
202
188
  staleness_checker = StalenessChecker.new(engine_options)
203
189
 
204
- files = file_list(individual_files)
205
- run_updating_stylesheets(files)
206
-
207
- updated_stylesheets = []
208
- files.each do |file, css, sourcemap|
209
- # TODO: Does staleness_checker need to check the sourcemap file as well?
210
- if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
211
- # XXX For consistency, this should return the sourcemap too, but it would
212
- # XXX be an API change.
213
- updated_stylesheets << [file, css]
214
- update_stylesheet(file, css, sourcemap)
215
- else
216
- run_not_updating_stylesheet(file, css, sourcemap)
217
- end
218
- end
219
- run_updated_stylesheets(updated_stylesheets)
220
- end
221
-
222
- # Construct a list of files that might need to be compiled
223
- # from the provided individual_files and the template_locations.
224
- #
225
- # Note: this method does not cache the results as they can change
226
- # across invocations when sass files are added or removed.
227
- #
228
- # @param individual_files [Array<(String, String[, String])>]
229
- # A list of files to check for updates
230
- # **in addition to those specified by the
231
- # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
232
- # The first string in each pair is the location of the Sass/SCSS file,
233
- # the second is the location of the CSS file that it should be compiled to.
234
- # The third string, if provided, is the location of the Sourcemap file.
235
- # @return [Array<(String, String, String)>]
236
- # A list of [sass_file, css_file, sourcemap_file] tuples similar
237
- # to what was passed in, but expanded to include the current state
238
- # of the directories being updated.
239
- def file_list(individual_files = [])
240
- files = individual_files.map do |tuple|
241
- if tuple.size < 3
242
- [tuple[0], tuple[1], Sass::Util.sourcemap_name(tuple[1])]
243
- else
244
- tuple
245
- end
246
- end
247
-
248
190
  template_location_array.each do |template_location, css_location|
249
191
  Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
250
192
  # Get the relative path to the file
251
- name = Sass::Util.pathname(file).relative_path_from(
252
- Sass::Util.pathname(template_location.to_s)).to_s
193
+ name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
253
194
  css = css_filename(name, css_location)
254
- sourcemap = Sass::Util.sourcemap_name(css) unless engine_options[:sourcemap] == :none
255
- files << [file, css, sourcemap]
195
+ individual_files << [file, css]
196
+ end
197
+ end
198
+
199
+ run_updating_stylesheets individual_files
200
+
201
+ individual_files.each do |file, css|
202
+ if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
203
+ update_stylesheet(file, css)
204
+ else
205
+ run_not_updating_stylesheet(file, css)
256
206
  end
257
207
  end
258
- files
259
208
  end
260
209
 
261
210
  # Watches the template directory (or directories)
@@ -276,62 +225,73 @@ module Sass::Plugin
276
225
  # The version of Listen distributed with Sass is loaded by default,
277
226
  # but if another version has already been loaded that will be used instead.
278
227
  #
279
- # @param individual_files [Array<(String, String[, String])>]
280
- # A list of files to check for updates
228
+ # @param individual_files [Array<(String, String)>]
229
+ # A list of files to watch for updates
281
230
  # **in addition to those specified by the
282
231
  # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
283
232
  # The first string in each pair is the location of the Sass/SCSS file,
284
233
  # the second is the location of the CSS file that it should be compiled to.
285
- # The third string, if provided, is the location of the Sourcemap file.
286
- # @param options [Hash] The options that control how watching works.
287
- # @option options [Boolean] :skip_initial_update
288
- # Don't do an initial update when starting the watcher when true
289
- def watch(individual_files = [], options = {})
290
- options, individual_files = individual_files, [] if individual_files.is_a?(Hash)
291
- update_stylesheets(individual_files) unless options[:skip_initial_update]
292
-
293
- directories = watched_paths
294
- individual_files.each do |(source, _, _)|
295
- directories << File.dirname(File.expand_path(source))
296
- end
297
- directories = remove_redundant_directories(directories)
234
+ def watch(individual_files = [])
235
+ update_stylesheets(individual_files)
236
+
237
+ load_listen!
298
238
 
299
- # A Listen version prior to 2.0 will write a test file to a directory to
300
- # see if a watcher supports watching that directory. That breaks horribly
301
- # on read-only directories, so we filter those out.
302
- unless Sass::Util.listen_geq_2?
303
- directories = directories.select {|d| File.directory?(d) && File.writable?(d)}
239
+ template_paths = template_locations # cache the locations
240
+ individual_files_hash = individual_files.inject({}) do |h, files|
241
+ parent = File.dirname(files.first)
242
+ (h[parent] ||= []) << files unless template_paths.include?(parent)
243
+ h
304
244
  end
245
+ directories = template_paths + individual_files_hash.keys +
246
+ [{:relative_paths => true}]
305
247
 
306
248
  # TODO: Keep better track of what depends on what
307
249
  # so we don't have to run a global update every time anything changes.
308
- # XXX The :additional_watch_paths option exists for Compass to use until
309
- # a deprecated feature is removed. It may be removed without warning.
310
- listener_args = directories +
311
- Array(options[:additional_watch_paths]) +
312
- [{:relative_paths => false}]
313
-
314
- # The native windows listener is much slower than the polling option, according to
315
- # https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
316
- poll = @options[:poll] || Sass::Util.windows?
317
- if poll && Sass::Util.listen_geq_2?
318
- # In Listen 2.0.0 and on, :force_polling is an option. In earlier
319
- # versions, it's a method on the listener (called below).
320
- listener_args.last[:force_polling] = true
321
- end
250
+ listener = Listen::MultiListener.new(*directories) do |modified, added, removed|
251
+ modified.each do |f|
252
+ parent = File.dirname(f)
253
+ if files = individual_files_hash[parent]
254
+ next unless files.first == f
255
+ else
256
+ next unless f =~ /\.s[ac]ss$/
257
+ end
258
+ run_template_modified(f)
259
+ end
322
260
 
323
- listener = create_listener(*listener_args) do |modified, added, removed|
324
- on_file_changed(individual_files, modified, added, removed)
325
- yield(modified, added, removed) if block_given?
326
- end
261
+ added.each do |f|
262
+ parent = File.dirname(f)
263
+ if files = individual_files_hash[parent]
264
+ next unless files.first == f
265
+ else
266
+ next unless f =~ /\.s[ac]ss$/
267
+ end
268
+ run_template_created(f)
269
+ end
327
270
 
328
- if poll && !Sass::Util.listen_geq_2?
329
- # In Listen 2.0.0 and on, :force_polling is an option (set above). In
330
- # earlier versions, it's a method on the listener.
331
- listener.force_polling(true)
271
+ removed.each do |f|
272
+ parent = File.dirname(f)
273
+ if files = individual_files_hash[parent]
274
+ next unless files.first == f
275
+ try_delete_css files[1]
276
+ else
277
+ next unless f =~ /\.s[ac]ss$/
278
+ try_delete_css f.gsub(/\.s[ac]ss$/, '.css')
279
+ end
280
+ run_template_deleted(f)
281
+ end
282
+
283
+ update_stylesheets(individual_files)
332
284
  end
333
285
 
334
- listen_to(listener)
286
+ # The native windows listener is much slower than the polling
287
+ # option, according to https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e#commitcomment-1295118
288
+ listener.force_polling(true) if @options[:poll] || Sass::Util.windows?
289
+
290
+ begin
291
+ listener.start
292
+ rescue Exception => e
293
+ raise e unless e.is_a?(Interrupt)
294
+ end
335
295
  end
336
296
 
337
297
  # Non-destructively modifies \{#options} so that default values are properly set,
@@ -342,8 +302,6 @@ module Sass::Plugin
342
302
  def engine_options(additional_options = {})
343
303
  opts = options.merge(additional_options)
344
304
  opts[:load_paths] = load_paths(opts)
345
- options[:sourcemap] = :auto if options[:sourcemap] == true
346
- options[:sourcemap] = :none if options[:sourcemap] == false
347
305
  opts
348
306
  end
349
307
 
@@ -352,186 +310,81 @@ module Sass::Plugin
352
310
  StalenessChecker.stylesheet_needs_update?(css_file, template_file)
353
311
  end
354
312
 
355
- # Remove all output files that would be created by calling update_stylesheets, if they exist.
356
- #
357
- # This method runs the deleting_css and deleting_sourcemap callbacks for
358
- # the files that are deleted.
359
- #
360
- # @param individual_files [Array<(String, String[, String])>]
361
- # A list of files to check for updates
362
- # **in addition to those specified by the
363
- # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
364
- # The first string in each pair is the location of the Sass/SCSS file,
365
- # the second is the location of the CSS file that it should be compiled to.
366
- # The third string, if provided, is the location of the Sourcemap file.
367
- def clean(individual_files = [])
368
- file_list(individual_files).each do |(_, css_file, sourcemap_file)|
369
- if File.exist?(css_file)
370
- run_deleting_css css_file
371
- File.delete(css_file)
372
- end
373
- if sourcemap_file && File.exist?(sourcemap_file)
374
- run_deleting_sourcemap sourcemap_file
375
- File.delete(sourcemap_file)
376
- end
377
- end
378
- nil
379
- end
380
-
381
313
  private
382
314
 
383
- def create_listener(*args, &block)
384
- Sass::Util.load_listen!
385
- if Sass::Util.listen_geq_2?
386
- # Work around guard/listen#243.
387
- options = args.pop if args.last.is_a?(Hash)
388
- args.map do |dir|
389
- Listen.to(dir, options, &block)
315
+ def load_listen!
316
+ if defined?(gem)
317
+ begin
318
+ gem 'listen', '~> 0.7'
319
+ require 'listen'
320
+ rescue Gem::LoadError
321
+ dir = Sass::Util.scope("vendor/listen/lib")
322
+ $LOAD_PATH.unshift dir
323
+ begin
324
+ require 'listen'
325
+ rescue LoadError => e
326
+ e.message << "\n" <<
327
+ if File.exists?(scope(".git"))
328
+ 'Run "git submodule update --init" to get the recommended version.'
329
+ else
330
+ 'Run "gem install listen" to get it.'
331
+ end
332
+ raise e
333
+ end
390
334
  end
391
335
  else
392
- Listen::Listener.new(*args, &block)
393
- end
394
- end
395
-
396
- def listen_to(listener)
397
- if Sass::Util.listen_geq_2?
398
- listener.map {|l| l.start}.each {|thread| thread.join}
399
- else
400
- listener.start!
401
- end
402
- rescue Interrupt
403
- # Squelch Interrupt for clean exit from Listen::Listener
404
- end
405
-
406
- def remove_redundant_directories(directories)
407
- dedupped = []
408
- directories.each do |new_directory|
409
- # no need to add a directory that is already watched.
410
- next if dedupped.any? do |existing_directory|
411
- child_of_directory?(existing_directory, new_directory)
412
- end
413
- # get rid of any sub directories of this new directory
414
- dedupped.reject! do |existing_directory|
415
- child_of_directory?(new_directory, existing_directory)
416
- end
417
- dedupped << new_directory
418
- end
419
- dedupped
420
- end
421
-
422
- def on_file_changed(individual_files, modified, added, removed)
423
- recompile_required = false
424
-
425
- modified.uniq.each do |f|
426
- next unless watched_file?(f)
427
- recompile_required = true
428
- run_template_modified(relative_to_pwd(f))
429
- end
430
-
431
- added.uniq.each do |f|
432
- next unless watched_file?(f)
433
- recompile_required = true
434
- run_template_created(relative_to_pwd(f))
435
- end
436
-
437
- removed.uniq.each do |f|
438
- run_template_deleted(relative_to_pwd(f))
439
- if (files = individual_files.find {|(source, _, _)| File.expand_path(source) == f})
440
- recompile_required = true
441
- # This was a file we were watching explicitly and compiling to a particular location.
442
- # Delete the corresponding file.
443
- try_delete_css files[1]
444
- else
445
- next unless watched_file?(f)
446
- recompile_required = true
447
- # Look for the sass directory that contained the sass file
448
- # And try to remove the css file that corresponds to it
449
- template_location_array.each do |(sass_dir, css_dir)|
450
- sass_dir = File.expand_path(sass_dir)
451
- if child_of_directory?(sass_dir, f)
452
- remainder = f[(sass_dir.size + 1)..-1]
453
- try_delete_css(css_filename(remainder, css_dir))
454
- break
455
- end
336
+ begin
337
+ require 'listen'
338
+ rescue LoadError => e
339
+ dir = Sass::Util.scope("vendor/listen/lib")
340
+ if $LOAD_PATH.include?(dir)
341
+ raise e unless File.exists?(scope(".git"))
342
+ e.message << "\n" <<
343
+ 'Run "git submodule update --init" to get the recommended version.'
344
+ else
345
+ $LOAD_PATH.unshift dir
346
+ retry
456
347
  end
457
348
  end
458
349
  end
459
-
460
- if recompile_required
461
- # In case a file we're watching is removed and then recreated we
462
- # prune out the non-existant files here.
463
- watched_files_remaining = individual_files.select {|(source, _, _)| File.exist?(source)}
464
- update_stylesheets(watched_files_remaining)
465
- end
466
350
  end
467
351
 
468
- def update_stylesheet(filename, css, sourcemap)
352
+ def update_stylesheet(filename, css)
469
353
  dir = File.dirname(css)
470
- unless File.exist?(dir)
354
+ unless File.exists?(dir)
471
355
  run_creating_directory dir
472
356
  FileUtils.mkdir_p dir
473
357
  end
474
358
 
475
359
  begin
476
360
  File.read(filename) unless File.readable?(filename) # triggers an error for handling
477
- engine_opts = engine_options(:css_filename => css,
478
- :filename => filename,
479
- :sourcemap_filename => sourcemap)
480
- mapping = nil
481
- run_compilation_starting(filename, css, sourcemap)
482
- engine = Sass::Engine.for_file(filename, engine_opts)
483
- if sourcemap
484
- rendered, mapping = engine.render_with_sourcemap(File.basename(sourcemap))
485
- else
486
- rendered = engine.render
487
- end
488
- rescue StandardError => e
361
+ engine_opts = engine_options(:css_filename => css, :filename => filename)
362
+ result = Sass::Engine.for_file(filename, engine_opts).render
363
+ rescue Exception => e
489
364
  compilation_error_occured = true
490
- run_compilation_error e, filename, css, sourcemap
491
- raise e unless options[:full_exception]
492
- rendered = Sass::SyntaxError.exception_to_css(e, options[:line] || 1)
365
+ run_compilation_error e, filename, css
366
+ result = Sass::SyntaxError.exception_to_css(e, options)
367
+ else
368
+ run_updating_stylesheet filename, css
493
369
  end
494
370
 
495
- write_file(css, rendered)
496
- if mapping
497
- write_file(sourcemap, mapping.to_json(
498
- :css_path => css, :sourcemap_path => sourcemap, :type => options[:sourcemap]))
499
- end
500
- run_updated_stylesheet(filename, css, sourcemap) unless compilation_error_occured
371
+ write_file(css, result)
372
+ run_updated_stylesheet(filename, css) unless compilation_error_occured
501
373
  end
502
374
 
503
- def write_file(fileName, content)
375
+ def write_file(css, content)
504
376
  flag = 'w'
505
377
  flag = 'wb' if Sass::Util.windows? && options[:unix_newlines]
506
- File.open(fileName, flag) do |file|
378
+ File.open(css, flag) do |file|
507
379
  file.set_encoding(content.encoding) unless Sass::Util.ruby1_8?
508
380
  file.print(content)
509
381
  end
510
382
  end
511
383
 
512
384
  def try_delete_css(css)
513
- if File.exist?(css)
514
- run_deleting_css css
515
- File.delete css
516
- end
517
- map = Sass::Util.sourcemap_name(css)
518
- if File.exist?(map)
519
- run_deleting_sourcemap map
520
- File.delete map
521
- end
522
- end
523
-
524
- def watched_file?(file)
525
- normalized_load_paths.find {|lp| lp.watched_file?(file)}
526
- end
527
-
528
- def watched_paths
529
- @watched_paths ||= normalized_load_paths.map {|lp| lp.directories_to_watch}.compact.flatten
530
- end
531
-
532
- def normalized_load_paths
533
- @normalized_load_paths ||=
534
- Sass::Engine.normalize_options(:load_paths => load_paths)[:load_paths]
385
+ return unless File.exists?(css)
386
+ run_deleting_css css
387
+ File.delete css
535
388
  end
536
389
 
537
390
  def load_paths(opts = options)
@@ -547,19 +400,7 @@ module Sass::Plugin
547
400
  end
548
401
 
549
402
  def css_filename(name, path)
550
- "#{path}#{File::SEPARATOR unless path.end_with?(File::SEPARATOR)}#{name}".
551
- gsub(/\.s[ac]ss$/, '.css')
552
- end
553
-
554
- def relative_to_pwd(f)
555
- Sass::Util.pathname(f).relative_path_from(Sass::Util.pathname(Dir.pwd)).to_s
556
- rescue ArgumentError # when a relative path cannot be computed
557
- f
558
- end
559
-
560
- def child_of_directory?(parent, child)
561
- parent_dir = parent.end_with?(File::SEPARATOR) ? parent : (parent + File::SEPARATOR)
562
- child.start_with?(parent_dir) || parent == child
403
+ "#{path}/#{name}".gsub(/\.s[ac]ss$/, '.css')
563
404
  end
564
405
  end
565
406
  end