oreorenasass 3.4.0

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 (268) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +221 -0
  6. data/Rakefile +370 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/sass +13 -0
  10. data/bin/sass-convert +12 -0
  11. data/bin/scss +13 -0
  12. data/extra/update_watch.rb +13 -0
  13. data/init.rb +18 -0
  14. data/lib/sass/cache_stores/base.rb +88 -0
  15. data/lib/sass/cache_stores/chain.rb +34 -0
  16. data/lib/sass/cache_stores/filesystem.rb +60 -0
  17. data/lib/sass/cache_stores/memory.rb +47 -0
  18. data/lib/sass/cache_stores/null.rb +25 -0
  19. data/lib/sass/cache_stores.rb +15 -0
  20. data/lib/sass/callbacks.rb +67 -0
  21. data/lib/sass/css.rb +407 -0
  22. data/lib/sass/engine.rb +1181 -0
  23. data/lib/sass/environment.rb +191 -0
  24. data/lib/sass/error.rb +198 -0
  25. data/lib/sass/exec/base.rb +187 -0
  26. data/lib/sass/exec/sass_convert.rb +264 -0
  27. data/lib/sass/exec/sass_scss.rb +424 -0
  28. data/lib/sass/exec.rb +9 -0
  29. data/lib/sass/features.rb +47 -0
  30. data/lib/sass/importers/base.rb +182 -0
  31. data/lib/sass/importers/filesystem.rb +211 -0
  32. data/lib/sass/importers.rb +22 -0
  33. data/lib/sass/logger/base.rb +30 -0
  34. data/lib/sass/logger/log_level.rb +45 -0
  35. data/lib/sass/logger.rb +12 -0
  36. data/lib/sass/media.rb +210 -0
  37. data/lib/sass/plugin/compiler.rb +565 -0
  38. data/lib/sass/plugin/configuration.rb +118 -0
  39. data/lib/sass/plugin/generic.rb +15 -0
  40. data/lib/sass/plugin/merb.rb +48 -0
  41. data/lib/sass/plugin/rack.rb +60 -0
  42. data/lib/sass/plugin/rails.rb +47 -0
  43. data/lib/sass/plugin/staleness_checker.rb +199 -0
  44. data/lib/sass/plugin.rb +133 -0
  45. data/lib/sass/railtie.rb +10 -0
  46. data/lib/sass/repl.rb +57 -0
  47. data/lib/sass/root.rb +7 -0
  48. data/lib/sass/script/css_lexer.rb +33 -0
  49. data/lib/sass/script/css_parser.rb +34 -0
  50. data/lib/sass/script/functions.rb +2626 -0
  51. data/lib/sass/script/lexer.rb +449 -0
  52. data/lib/sass/script/parser.rb +637 -0
  53. data/lib/sass/script/tree/funcall.rb +306 -0
  54. data/lib/sass/script/tree/interpolation.rb +118 -0
  55. data/lib/sass/script/tree/list_literal.rb +77 -0
  56. data/lib/sass/script/tree/literal.rb +45 -0
  57. data/lib/sass/script/tree/map_literal.rb +64 -0
  58. data/lib/sass/script/tree/node.rb +109 -0
  59. data/lib/sass/script/tree/operation.rb +103 -0
  60. data/lib/sass/script/tree/selector.rb +26 -0
  61. data/lib/sass/script/tree/string_interpolation.rb +104 -0
  62. data/lib/sass/script/tree/unary_operation.rb +69 -0
  63. data/lib/sass/script/tree/variable.rb +57 -0
  64. data/lib/sass/script/tree.rb +16 -0
  65. data/lib/sass/script/value/arg_list.rb +36 -0
  66. data/lib/sass/script/value/base.rb +240 -0
  67. data/lib/sass/script/value/bool.rb +35 -0
  68. data/lib/sass/script/value/color.rb +680 -0
  69. data/lib/sass/script/value/helpers.rb +262 -0
  70. data/lib/sass/script/value/list.rb +113 -0
  71. data/lib/sass/script/value/map.rb +70 -0
  72. data/lib/sass/script/value/null.rb +44 -0
  73. data/lib/sass/script/value/number.rb +530 -0
  74. data/lib/sass/script/value/string.rb +97 -0
  75. data/lib/sass/script/value.rb +11 -0
  76. data/lib/sass/script.rb +66 -0
  77. data/lib/sass/scss/css_parser.rb +42 -0
  78. data/lib/sass/scss/parser.rb +1209 -0
  79. data/lib/sass/scss/rx.rb +141 -0
  80. data/lib/sass/scss/script_lexer.rb +15 -0
  81. data/lib/sass/scss/script_parser.rb +25 -0
  82. data/lib/sass/scss/static_parser.rb +368 -0
  83. data/lib/sass/scss.rb +16 -0
  84. data/lib/sass/selector/abstract_sequence.rb +109 -0
  85. data/lib/sass/selector/comma_sequence.rb +175 -0
  86. data/lib/sass/selector/pseudo.rb +256 -0
  87. data/lib/sass/selector/sequence.rb +600 -0
  88. data/lib/sass/selector/simple.rb +117 -0
  89. data/lib/sass/selector/simple_sequence.rb +325 -0
  90. data/lib/sass/selector.rb +326 -0
  91. data/lib/sass/shared.rb +76 -0
  92. data/lib/sass/source/map.rb +210 -0
  93. data/lib/sass/source/position.rb +39 -0
  94. data/lib/sass/source/range.rb +41 -0
  95. data/lib/sass/stack.rb +120 -0
  96. data/lib/sass/supports.rb +227 -0
  97. data/lib/sass/tree/at_root_node.rb +83 -0
  98. data/lib/sass/tree/charset_node.rb +22 -0
  99. data/lib/sass/tree/comment_node.rb +82 -0
  100. data/lib/sass/tree/content_node.rb +9 -0
  101. data/lib/sass/tree/css_import_node.rb +60 -0
  102. data/lib/sass/tree/debug_node.rb +18 -0
  103. data/lib/sass/tree/directive_node.rb +59 -0
  104. data/lib/sass/tree/each_node.rb +24 -0
  105. data/lib/sass/tree/error_node.rb +18 -0
  106. data/lib/sass/tree/extend_node.rb +43 -0
  107. data/lib/sass/tree/for_node.rb +36 -0
  108. data/lib/sass/tree/function_node.rb +39 -0
  109. data/lib/sass/tree/if_node.rb +52 -0
  110. data/lib/sass/tree/import_node.rb +74 -0
  111. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  112. data/lib/sass/tree/media_node.rb +48 -0
  113. data/lib/sass/tree/mixin_def_node.rb +38 -0
  114. data/lib/sass/tree/mixin_node.rb +52 -0
  115. data/lib/sass/tree/node.rb +238 -0
  116. data/lib/sass/tree/prop_node.rb +171 -0
  117. data/lib/sass/tree/return_node.rb +19 -0
  118. data/lib/sass/tree/root_node.rb +44 -0
  119. data/lib/sass/tree/rule_node.rb +145 -0
  120. data/lib/sass/tree/supports_node.rb +38 -0
  121. data/lib/sass/tree/trace_node.rb +33 -0
  122. data/lib/sass/tree/variable_node.rb +36 -0
  123. data/lib/sass/tree/visitors/base.rb +72 -0
  124. data/lib/sass/tree/visitors/check_nesting.rb +177 -0
  125. data/lib/sass/tree/visitors/convert.rb +334 -0
  126. data/lib/sass/tree/visitors/cssize.rb +369 -0
  127. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  128. data/lib/sass/tree/visitors/extend.rb +68 -0
  129. data/lib/sass/tree/visitors/perform.rb +539 -0
  130. data/lib/sass/tree/visitors/set_options.rb +139 -0
  131. data/lib/sass/tree/visitors/to_css.rb +381 -0
  132. data/lib/sass/tree/warn_node.rb +18 -0
  133. data/lib/sass/tree/while_node.rb +18 -0
  134. data/lib/sass/util/cross_platform_random.rb +19 -0
  135. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  136. data/lib/sass/util/normalized_map.rb +130 -0
  137. data/lib/sass/util/ordered_hash.rb +192 -0
  138. data/lib/sass/util/subset_map.rb +110 -0
  139. data/lib/sass/util/test.rb +9 -0
  140. data/lib/sass/util.rb +1318 -0
  141. data/lib/sass/version.rb +124 -0
  142. data/lib/sass.rb +102 -0
  143. data/rails/init.rb +1 -0
  144. data/test/sass/cache_test.rb +131 -0
  145. data/test/sass/callbacks_test.rb +61 -0
  146. data/test/sass/compiler_test.rb +232 -0
  147. data/test/sass/conversion_test.rb +2054 -0
  148. data/test/sass/css2sass_test.rb +477 -0
  149. data/test/sass/data/hsl-rgb.txt +319 -0
  150. data/test/sass/encoding_test.rb +219 -0
  151. data/test/sass/engine_test.rb +3301 -0
  152. data/test/sass/exec_test.rb +86 -0
  153. data/test/sass/extend_test.rb +1661 -0
  154. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  155. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  156. data/test/sass/functions_test.rb +1926 -0
  157. data/test/sass/importer_test.rb +412 -0
  158. data/test/sass/logger_test.rb +58 -0
  159. data/test/sass/mock_importer.rb +49 -0
  160. data/test/sass/more_results/more1.css +9 -0
  161. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  162. data/test/sass/more_results/more_import.css +29 -0
  163. data/test/sass/more_templates/_more_partial.sass +2 -0
  164. data/test/sass/more_templates/more1.sass +23 -0
  165. data/test/sass/more_templates/more_import.sass +11 -0
  166. data/test/sass/plugin_test.rb +554 -0
  167. data/test/sass/results/alt.css +4 -0
  168. data/test/sass/results/basic.css +9 -0
  169. data/test/sass/results/cached_import_option.css +3 -0
  170. data/test/sass/results/compact.css +5 -0
  171. data/test/sass/results/complex.css +86 -0
  172. data/test/sass/results/compressed.css +1 -0
  173. data/test/sass/results/expanded.css +19 -0
  174. data/test/sass/results/filename_fn.css +3 -0
  175. data/test/sass/results/if.css +3 -0
  176. data/test/sass/results/import.css +31 -0
  177. data/test/sass/results/import_charset.css +5 -0
  178. data/test/sass/results/import_charset_1_8.css +5 -0
  179. data/test/sass/results/import_charset_ibm866.css +5 -0
  180. data/test/sass/results/import_content.css +1 -0
  181. data/test/sass/results/line_numbers.css +49 -0
  182. data/test/sass/results/mixins.css +95 -0
  183. data/test/sass/results/multiline.css +24 -0
  184. data/test/sass/results/nested.css +22 -0
  185. data/test/sass/results/options.css +1 -0
  186. data/test/sass/results/parent_ref.css +13 -0
  187. data/test/sass/results/script.css +16 -0
  188. data/test/sass/results/scss_import.css +31 -0
  189. data/test/sass/results/scss_importee.css +2 -0
  190. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  191. data/test/sass/results/subdir/subdir.css +3 -0
  192. data/test/sass/results/units.css +11 -0
  193. data/test/sass/results/warn.css +0 -0
  194. data/test/sass/results/warn_imported.css +0 -0
  195. data/test/sass/script_conversion_test.rb +328 -0
  196. data/test/sass/script_test.rb +1054 -0
  197. data/test/sass/scss/css_test.rb +1215 -0
  198. data/test/sass/scss/rx_test.rb +156 -0
  199. data/test/sass/scss/scss_test.rb +3900 -0
  200. data/test/sass/scss/test_helper.rb +37 -0
  201. data/test/sass/source_map_test.rb +977 -0
  202. data/test/sass/superselector_test.rb +191 -0
  203. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  204. data/test/sass/templates/_double_import_loop2.sass +1 -0
  205. data/test/sass/templates/_filename_fn_import.scss +11 -0
  206. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  207. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  208. data/test/sass/templates/_imported_content.sass +3 -0
  209. data/test/sass/templates/_partial.sass +2 -0
  210. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  211. data/test/sass/templates/alt.sass +16 -0
  212. data/test/sass/templates/basic.sass +23 -0
  213. data/test/sass/templates/bork1.sass +2 -0
  214. data/test/sass/templates/bork2.sass +2 -0
  215. data/test/sass/templates/bork3.sass +2 -0
  216. data/test/sass/templates/bork4.sass +2 -0
  217. data/test/sass/templates/bork5.sass +3 -0
  218. data/test/sass/templates/cached_import_option.scss +3 -0
  219. data/test/sass/templates/compact.sass +17 -0
  220. data/test/sass/templates/complex.sass +305 -0
  221. data/test/sass/templates/compressed.sass +15 -0
  222. data/test/sass/templates/double_import_loop1.sass +1 -0
  223. data/test/sass/templates/expanded.sass +17 -0
  224. data/test/sass/templates/filename_fn.scss +18 -0
  225. data/test/sass/templates/if.sass +11 -0
  226. data/test/sass/templates/import.sass +12 -0
  227. data/test/sass/templates/import_charset.sass +9 -0
  228. data/test/sass/templates/import_charset_1_8.sass +6 -0
  229. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  230. data/test/sass/templates/import_content.sass +4 -0
  231. data/test/sass/templates/importee.less +2 -0
  232. data/test/sass/templates/importee.sass +19 -0
  233. data/test/sass/templates/line_numbers.sass +13 -0
  234. data/test/sass/templates/mixin_bork.sass +5 -0
  235. data/test/sass/templates/mixins.sass +76 -0
  236. data/test/sass/templates/multiline.sass +20 -0
  237. data/test/sass/templates/nested.sass +25 -0
  238. data/test/sass/templates/nested_bork1.sass +2 -0
  239. data/test/sass/templates/nested_bork2.sass +2 -0
  240. data/test/sass/templates/nested_bork3.sass +2 -0
  241. data/test/sass/templates/nested_bork4.sass +2 -0
  242. data/test/sass/templates/nested_import.sass +2 -0
  243. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  244. data/test/sass/templates/options.sass +2 -0
  245. data/test/sass/templates/parent_ref.sass +25 -0
  246. data/test/sass/templates/same_name_different_ext.sass +2 -0
  247. data/test/sass/templates/same_name_different_ext.scss +1 -0
  248. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  249. data/test/sass/templates/script.sass +101 -0
  250. data/test/sass/templates/scss_import.scss +12 -0
  251. data/test/sass/templates/scss_importee.scss +1 -0
  252. data/test/sass/templates/single_import_loop.sass +1 -0
  253. data/test/sass/templates/subdir/import_up1.scss +1 -0
  254. data/test/sass/templates/subdir/import_up2.scss +1 -0
  255. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  256. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  257. data/test/sass/templates/subdir/subdir.sass +6 -0
  258. data/test/sass/templates/units.sass +11 -0
  259. data/test/sass/templates/warn.sass +3 -0
  260. data/test/sass/templates/warn_imported.sass +4 -0
  261. data/test/sass/test_helper.rb +8 -0
  262. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  263. data/test/sass/util/normalized_map_test.rb +51 -0
  264. data/test/sass/util/subset_map_test.rb +91 -0
  265. data/test/sass/util_test.rb +467 -0
  266. data/test/sass/value_helpers_test.rb +179 -0
  267. data/test/test_helper.rb +109 -0
  268. metadata +386 -0
@@ -0,0 +1,22 @@
1
+ module Sass
2
+ # Sass importers are in charge of taking paths passed to `@import`
3
+ # and finding the appropriate Sass code for those paths.
4
+ # By default, this code is always loaded from the filesystem,
5
+ # but importers could be added to load from a database or over HTTP.
6
+ #
7
+ # Each importer is in charge of a single load path
8
+ # (or whatever the corresponding notion is for the backend).
9
+ # Importers can be placed in the {file:SASS_REFERENCE.md#load_paths-option `:load_paths` array}
10
+ # alongside normal filesystem paths.
11
+ #
12
+ # When resolving an `@import`, Sass will go through the load paths
13
+ # looking for an importer that successfully imports the path.
14
+ # Once one is found, the imported file is used.
15
+ #
16
+ # User-created importers must inherit from {Importers::Base}.
17
+ module Importers
18
+ end
19
+ end
20
+
21
+ require 'sass/importers/base'
22
+ require 'sass/importers/filesystem'
@@ -0,0 +1,30 @@
1
+ require 'sass/logger/log_level'
2
+
3
+ class Sass::Logger::Base
4
+ include Sass::Logger::LogLevel
5
+
6
+ attr_accessor :log_level
7
+ attr_accessor :disabled
8
+
9
+ log_level :trace
10
+ log_level :debug
11
+ log_level :info
12
+ log_level :warn
13
+ log_level :error
14
+
15
+ def initialize(log_level = :debug)
16
+ self.log_level = log_level
17
+ end
18
+
19
+ def logging_level?(level)
20
+ !disabled && self.class.log_level?(level, log_level)
21
+ end
22
+
23
+ def log(level, message)
24
+ _log(level, message) if logging_level?(level)
25
+ end
26
+
27
+ def _log(level, message)
28
+ Kernel.warn(message)
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ module Sass
2
+ module Logger
3
+ module LogLevel
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def inherited(subclass)
10
+ subclass.log_levels = subclass.superclass.log_levels.dup
11
+ end
12
+
13
+ attr_writer :log_levels
14
+
15
+ def log_levels
16
+ @log_levels ||= {}
17
+ end
18
+
19
+ def log_level?(level, min_level)
20
+ log_levels[level] >= log_levels[min_level]
21
+ end
22
+
23
+ def log_level(name, options = {})
24
+ if options[:prepend]
25
+ level = log_levels.values.min
26
+ level = level.nil? ? 0 : level - 1
27
+ else
28
+ level = log_levels.values.max
29
+ level = level.nil? ? 0 : level + 1
30
+ end
31
+ log_levels.update(name => level)
32
+ define_logger(name)
33
+ end
34
+
35
+ def define_logger(name, options = {})
36
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
37
+ def #{name}(message)
38
+ #{options.fetch(:to, :log)}(#{name.inspect}, message)
39
+ end
40
+ RUBY
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,12 @@
1
+ module Sass::Logger; end
2
+
3
+ require "sass/logger/log_level"
4
+ require "sass/logger/base"
5
+
6
+ module Sass
7
+ class << self
8
+ attr_accessor :logger
9
+ end
10
+
11
+ self.logger = Sass::Logger::Base.new
12
+ end
data/lib/sass/media.rb ADDED
@@ -0,0 +1,210 @@
1
+ # A namespace for the `@media` query parse tree.
2
+ module Sass::Media
3
+ # A comma-separated list of queries.
4
+ #
5
+ # media_query [ ',' S* media_query ]*
6
+ class QueryList
7
+ # The queries contained in this list.
8
+ #
9
+ # @return [Array<Query>]
10
+ attr_accessor :queries
11
+
12
+ # @param queries [Array<Query>] See \{#queries}
13
+ def initialize(queries)
14
+ @queries = queries
15
+ end
16
+
17
+ # Merges this query list with another. The returned query list
18
+ # queries for the intersection between the two inputs.
19
+ #
20
+ # Both query lists should be resolved.
21
+ #
22
+ # @param other [QueryList]
23
+ # @return [QueryList?] The merged list, or nil if there is no intersection.
24
+ def merge(other)
25
+ new_queries = queries.map {|q1| other.queries.map {|q2| q1.merge(q2)}}.flatten.compact
26
+ return if new_queries.empty?
27
+ QueryList.new(new_queries)
28
+ end
29
+
30
+ # Returns the CSS for the media query list.
31
+ #
32
+ # @return [String]
33
+ def to_css
34
+ queries.map {|q| q.to_css}.join(', ')
35
+ end
36
+
37
+ # Returns the Sass/SCSS code for the media query list.
38
+ #
39
+ # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
40
+ # @return [String]
41
+ def to_src(options)
42
+ queries.map {|q| q.to_src(options)}.join(', ')
43
+ end
44
+
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.
49
+ #
50
+ # @return [Array<String, Sass::Script::Tree::Node>]
51
+ def to_a
52
+ Sass::Util.intersperse(queries.map {|q| q.to_a}, ', ').flatten
53
+ end
54
+
55
+ # Returns a deep copy of this query list and all its children.
56
+ #
57
+ # @return [QueryList]
58
+ def deep_copy
59
+ QueryList.new(queries.map {|q| q.deep_copy})
60
+ end
61
+ end
62
+
63
+ # A single media query.
64
+ #
65
+ # [ [ONLY | NOT]? S* media_type S* | expression ] [ AND S* expression ]*
66
+ class Query
67
+ # The modifier for the query.
68
+ #
69
+ # When parsed as Sass code, this contains strings and SassScript nodes. When
70
+ # parsed as CSS, it contains a single string (accessible via
71
+ # \{#resolved_modifier}).
72
+ #
73
+ # @return [Array<String, Sass::Script::Tree::Node>]
74
+ attr_accessor :modifier
75
+
76
+ # The type of the query (e.g. `"screen"` or `"print"`).
77
+ #
78
+ # When parsed as Sass code, this contains strings and SassScript nodes. When
79
+ # parsed as CSS, it contains a single string (accessible via
80
+ # \{#resolved_type}).
81
+ #
82
+ # @return [Array<String, Sass::Script::Tree::Node>]
83
+ attr_accessor :type
84
+
85
+ # The trailing expressions in the query.
86
+ #
87
+ # When parsed as Sass code, each expression contains strings and SassScript
88
+ # nodes. When parsed as CSS, each one contains a single string.
89
+ #
90
+ # @return [Array<Array<String, Sass::Script::Tree::Node>>]
91
+ attr_accessor :expressions
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}
96
+ def initialize(modifier, type, expressions)
97
+ @modifier = modifier
98
+ @type = type
99
+ @expressions = expressions
100
+ end
101
+
102
+ # See \{#modifier}.
103
+ # @return [String]
104
+ def resolved_modifier
105
+ # modifier should contain only a single string
106
+ modifier.first || ''
107
+ end
108
+
109
+ # See \{#type}.
110
+ # @return [String]
111
+ def resolved_type
112
+ # type should contain only a single string
113
+ type.first || ''
114
+ end
115
+
116
+ # Merges this query with another. The returned query queries for
117
+ # the intersection between the two inputs.
118
+ #
119
+ # Both queries should be resolved.
120
+ #
121
+ # @param other [Query]
122
+ # @return [Query?] The merged query, or nil if there is no intersection.
123
+ def merge(other)
124
+ m1, t1 = resolved_modifier.downcase, resolved_type.downcase
125
+ m2, t2 = other.resolved_modifier.downcase, other.resolved_type.downcase
126
+ t1 = t2 if t1.empty?
127
+ t2 = t1 if t2.empty?
128
+ if (m1 == 'not') ^ (m2 == 'not')
129
+ return if t1 == t2
130
+ type = m1 == 'not' ? t2 : t1
131
+ mod = m1 == 'not' ? m2 : m1
132
+ elsif m1 == 'not' && m2 == 'not'
133
+ # CSS has no way of representing "neither screen nor print"
134
+ return unless t1 == t2
135
+ type = t1
136
+ mod = 'not'
137
+ elsif t1 != t2
138
+ return
139
+ else # t1 == t2, neither m1 nor m2 are "not"
140
+ type = t1
141
+ mod = m1.empty? ? m2 : m1
142
+ end
143
+ Query.new([mod], [type], other.expressions + expressions)
144
+ end
145
+
146
+ # Returns the CSS for the media query.
147
+ #
148
+ # @return [String]
149
+ def to_css
150
+ css = ''
151
+ css << resolved_modifier
152
+ css << ' ' unless resolved_modifier.empty?
153
+ css << resolved_type
154
+ css << ' and ' unless resolved_type.empty? || expressions.empty?
155
+ css << expressions.map do |e|
156
+ # It's possible for there to be script nodes in Expressions even when
157
+ # we're converting to CSS in the case where we parsed the document as
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
160
+ end.join(' and ')
161
+ css
162
+ end
163
+
164
+ # Returns the Sass/SCSS code for the media query.
165
+ #
166
+ # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
167
+ # @return [String]
168
+ def to_src(options)
169
+ src = ''
170
+ src << Sass::Media._interp_to_src(modifier, options)
171
+ src << ' ' unless modifier.empty?
172
+ src << Sass::Media._interp_to_src(type, options)
173
+ src << ' and ' unless type.empty? || expressions.empty?
174
+ src << expressions.map do |e|
175
+ Sass::Media._interp_to_src(e, options)
176
+ end.join(' and ')
177
+ src
178
+ end
179
+
180
+ # @see \{MediaQuery#to\_a}
181
+ def to_a
182
+ res = []
183
+ res += modifier
184
+ res << ' ' unless modifier.empty?
185
+ res += type
186
+ res << ' and ' unless type.empty? || expressions.empty?
187
+ res += Sass::Util.intersperse(expressions, ' and ').flatten
188
+ res
189
+ end
190
+
191
+ # Returns a deep copy of this query and all its children.
192
+ #
193
+ # @return [Query]
194
+ def deep_copy
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}})
199
+ end
200
+ end
201
+
202
+ # Converts an interpolation array to source.
203
+ #
204
+ # @param interp [Array<String, Sass::Script::Tree::Node>] The interpolation array to convert.
205
+ # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
206
+ # @return [String]
207
+ def self._interp_to_src(interp, options)
208
+ interp.map {|r| r.is_a?(String) ? r : r.to_sass(options)}.join
209
+ end
210
+ end
@@ -0,0 +1,565 @@
1
+ require 'fileutils'
2
+
3
+ require 'sass'
4
+ # XXX CE: is this still necessary now that we have the compiler class?
5
+ require 'sass/callbacks'
6
+ require 'sass/plugin/configuration'
7
+ require 'sass/plugin/staleness_checker'
8
+
9
+ module Sass::Plugin
10
+ # The Compiler class handles compilation of multiple files and/or directories,
11
+ # including checking which CSS files are out-of-date and need to be updated
12
+ # and calling Sass to perform the compilation on those files.
13
+ #
14
+ # {Sass::Plugin} uses this class to update stylesheets for a single application.
15
+ # Unlike {Sass::Plugin}, though, the Compiler class has no global state,
16
+ # and so multiple instances may be created and used independently.
17
+ #
18
+ # If you need to compile a Sass string into CSS,
19
+ # please see the {Sass::Engine} class.
20
+ #
21
+ # Unlike {Sass::Plugin}, this class doesn't keep track of
22
+ # whether or how many times a stylesheet should be updated.
23
+ # Therefore, the following `Sass::Plugin` options are ignored by the Compiler:
24
+ #
25
+ # * `:never_update`
26
+ # * `:always_check`
27
+ class Compiler
28
+ include Configuration
29
+ extend Sass::Callbacks
30
+
31
+ # Creates a new compiler.
32
+ #
33
+ # @param opts [{Symbol => Object}]
34
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
35
+ def initialize(opts = {})
36
+ options.merge!(opts)
37
+ end
38
+
39
+ # Register a callback to be run before stylesheets are mass-updated.
40
+ # This is run whenever \{#update\_stylesheets} is called,
41
+ # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
42
+ # is enabled.
43
+ #
44
+ # @yield [files]
45
+ # @yieldparam files [<(String, String, String)>]
46
+ # Individual files to be updated. Files in directories specified are included in this list.
47
+ # 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.
50
+ define_callback :updating_stylesheets
51
+
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
+ # Register a callback to be run after a single stylesheet is updated.
64
+ # The callback is only run if the stylesheet is really updated;
65
+ # if the CSS file is fresh, this won't be run.
66
+ #
67
+ # Even if the \{file:SASS_REFERENCE.md#full_exception-option `:full_exception` option}
68
+ # is enabled, this callback won't be run
69
+ # when an exception CSS file is being written.
70
+ # To run an action for those files, use \{#on\_compilation\_error}.
71
+ #
72
+ # @yield [template, css, sourcemap]
73
+ # @yieldparam template [String]
74
+ # The location of the Sass/SCSS file being updated.
75
+ # @yieldparam css [String]
76
+ # The location of the CSS file being generated.
77
+ # @yieldparam sourcemap [String]
78
+ # The location of the sourcemap being generated, if any.
79
+ define_callback :updated_stylesheet
80
+
81
+ # Register a callback to be run when compilation starts.
82
+ #
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.
86
+ #
87
+ # @yield [template, css, sourcemap]
88
+ # @yieldparam template [String]
89
+ # The location of the Sass/SCSS file being updated.
90
+ # @yieldparam css [String]
91
+ # 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
95
+
96
+ # Register a callback to be run when Sass decides not to update a stylesheet.
97
+ # In particular, the callback is run when Sass finds that
98
+ # the template file and none of its dependencies
99
+ # have been modified since the last compilation.
100
+ #
101
+ # Note that this is **not** run when the
102
+ # \{file:SASS_REFERENCE.md#never-update_option `:never_update` option} is set,
103
+ # nor when Sass decides not to compile a partial.
104
+ #
105
+ # @yield [template, css]
106
+ # @yieldparam template [String]
107
+ # The location of the Sass/SCSS file not being updated.
108
+ # @yieldparam css [String]
109
+ # The location of the CSS file not being generated.
110
+ define_callback :not_updating_stylesheet
111
+
112
+ # Register a callback to be run when there's an error
113
+ # compiling a Sass file.
114
+ # This could include not only errors in the Sass document,
115
+ # but also errors accessing the file at all.
116
+ #
117
+ # @yield [error, template, css]
118
+ # @yieldparam error [Exception] The exception that was raised.
119
+ # @yieldparam template [String]
120
+ # The location of the Sass/SCSS file being updated.
121
+ # @yieldparam css [String]
122
+ # The location of the CSS file being generated.
123
+ define_callback :compilation_error
124
+
125
+ # Register a callback to be run when Sass creates a directory
126
+ # into which to put CSS files.
127
+ #
128
+ # Note that even if multiple levels of directories need to be created,
129
+ # the callback may only be run once.
130
+ # For example, if "foo/" exists and "foo/bar/baz/" needs to be created,
131
+ # this may only be run for "foo/bar/baz/".
132
+ # This is not a guarantee, however;
133
+ # it may also be run for "foo/bar/".
134
+ #
135
+ # @yield [dirname]
136
+ # @yieldparam dirname [String]
137
+ # The location of the directory that was created.
138
+ define_callback :creating_directory
139
+
140
+ # Register a callback to be run when Sass detects
141
+ # that a template has been modified.
142
+ # This is only run when using \{#watch}.
143
+ #
144
+ # @yield [template]
145
+ # @yieldparam template [String]
146
+ # The location of the template that was modified.
147
+ define_callback :template_modified
148
+
149
+ # Register a callback to be run when Sass detects
150
+ # that a new template has been created.
151
+ # This is only run when using \{#watch}.
152
+ #
153
+ # @yield [template]
154
+ # @yieldparam template [String]
155
+ # The location of the template that was created.
156
+ define_callback :template_created
157
+
158
+ # Register a callback to be run when Sass detects
159
+ # that a template has been deleted.
160
+ # This is only run when using \{#watch}.
161
+ #
162
+ # @yield [template]
163
+ # @yieldparam template [String]
164
+ # The location of the template that was deleted.
165
+ define_callback :template_deleted
166
+
167
+ # 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.
170
+ #
171
+ # @yield [filename]
172
+ # @yieldparam filename [String]
173
+ # The location of the CSS file that was deleted.
174
+ define_callback :deleting_css
175
+
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
+ # Updates out-of-date stylesheets.
186
+ #
187
+ # Checks each Sass/SCSS file in
188
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location`}
189
+ # to see if it's been modified more recently than the corresponding CSS file
190
+ # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
191
+ # If it has, it updates the CSS file.
192
+ #
193
+ # @param individual_files [Array<(String, String[, String])>]
194
+ # A list of files to check for updates
195
+ # **in addition to those specified by the
196
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
197
+ # The first string in each pair is the location of the Sass/SCSS file,
198
+ # 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
+ def update_stylesheets(individual_files = [])
201
+ Sass::Plugin.checked_for_updates = true
202
+ staleness_checker = StalenessChecker.new(engine_options)
203
+
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
+ template_location_array.each do |template_location, css_location|
249
+ Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
250
+ # 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
253
+ css = css_filename(name, css_location)
254
+ sourcemap = Sass::Util.sourcemap_name(css) unless engine_options[:sourcemap] == :none
255
+ files << [file, css, sourcemap]
256
+ end
257
+ end
258
+ files
259
+ end
260
+
261
+ # Watches the template directory (or directories)
262
+ # and updates the CSS files whenever the related Sass/SCSS files change.
263
+ # `watch` never returns.
264
+ #
265
+ # Whenever a change is detected to a Sass/SCSS file in
266
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location`},
267
+ # the corresponding CSS file in {file:SASS_REFERENCE.md#css_location-option `:css_location`}
268
+ # will be recompiled.
269
+ # The CSS files of any Sass/SCSS files that import the changed file will also be recompiled.
270
+ #
271
+ # Before the watching starts in earnest, `watch` calls \{#update\_stylesheets}.
272
+ #
273
+ # Note that `watch` uses the [Listen](http://github.com/guard/listen) library
274
+ # to monitor the filesystem for changes.
275
+ # Listen isn't loaded until `watch` is run.
276
+ # The version of Listen distributed with Sass is loaded by default,
277
+ # but if another version has already been loaded that will be used instead.
278
+ #
279
+ # @param individual_files [Array<(String, String[, String])>]
280
+ # A list of files to check for updates
281
+ # **in addition to those specified by the
282
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
283
+ # The first string in each pair is the location of the Sass/SCSS file,
284
+ # 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)
298
+
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)}
304
+ end
305
+
306
+ # TODO: Keep better track of what depends on what
307
+ # 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
322
+
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
327
+
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)
332
+ end
333
+
334
+ listen_to(listener)
335
+ end
336
+
337
+ # Non-destructively modifies \{#options} so that default values are properly set,
338
+ # and returns the result.
339
+ #
340
+ # @param additional_options [{Symbol => Object}] An options hash with which to merge \{#options}
341
+ # @return [{Symbol => Object}] The modified options hash
342
+ def engine_options(additional_options = {})
343
+ opts = options.merge(additional_options)
344
+ opts[:load_paths] = load_paths(opts)
345
+ options[:sourcemap] = :auto if options[:sourcemap] == true
346
+ options[:sourcemap] = :none if options[:sourcemap] == false
347
+ opts
348
+ end
349
+
350
+ # Compass expects this to exist
351
+ def stylesheet_needs_update?(css_file, template_file)
352
+ StalenessChecker.stylesheet_needs_update?(css_file, template_file)
353
+ end
354
+
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
+ private
382
+
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)
390
+ end
391
+ 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
456
+ end
457
+ end
458
+ 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
+ end
467
+
468
+ def update_stylesheet(filename, css, sourcemap)
469
+ dir = File.dirname(css)
470
+ unless File.exist?(dir)
471
+ run_creating_directory dir
472
+ FileUtils.mkdir_p dir
473
+ end
474
+
475
+ begin
476
+ 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
489
+ 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)
493
+ end
494
+
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
501
+ end
502
+
503
+ def write_file(fileName, content)
504
+ flag = 'w'
505
+ flag = 'wb' if Sass::Util.windows? && options[:unix_newlines]
506
+ File.open(fileName, flag) do |file|
507
+ file.set_encoding(content.encoding) unless Sass::Util.ruby1_8?
508
+ file.print(content)
509
+ end
510
+ end
511
+
512
+ 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]
535
+ end
536
+
537
+ def load_paths(opts = options)
538
+ (opts[:load_paths] || []) + template_locations
539
+ end
540
+
541
+ def template_locations
542
+ template_location_array.to_a.map {|l| l.first}
543
+ end
544
+
545
+ def css_locations
546
+ template_location_array.to_a.map {|l| l.last}
547
+ end
548
+
549
+ 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
563
+ end
564
+ end
565
+ end