oreorenasass 3.4.4 → 3.4.5

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 (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