xass 0.1.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 (242) 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 +201 -0
  6. data/Rakefile +349 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/push +13 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/update_watch.rb +13 -0
  14. data/init.rb +18 -0
  15. data/lib/sass/cache_stores/base.rb +88 -0
  16. data/lib/sass/cache_stores/chain.rb +33 -0
  17. data/lib/sass/cache_stores/filesystem.rb +64 -0
  18. data/lib/sass/cache_stores/memory.rb +47 -0
  19. data/lib/sass/cache_stores/null.rb +25 -0
  20. data/lib/sass/cache_stores.rb +15 -0
  21. data/lib/sass/callbacks.rb +66 -0
  22. data/lib/sass/css.rb +409 -0
  23. data/lib/sass/engine.rb +930 -0
  24. data/lib/sass/environment.rb +101 -0
  25. data/lib/sass/error.rb +201 -0
  26. data/lib/sass/exec.rb +707 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +186 -0
  29. data/lib/sass/importers.rb +22 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/logger.rb +15 -0
  33. data/lib/sass/media.rb +213 -0
  34. data/lib/sass/plugin/compiler.rb +406 -0
  35. data/lib/sass/plugin/configuration.rb +123 -0
  36. data/lib/sass/plugin/generic.rb +15 -0
  37. data/lib/sass/plugin/merb.rb +48 -0
  38. data/lib/sass/plugin/rack.rb +60 -0
  39. data/lib/sass/plugin/rails.rb +47 -0
  40. data/lib/sass/plugin/staleness_checker.rb +199 -0
  41. data/lib/sass/plugin.rb +133 -0
  42. data/lib/sass/railtie.rb +10 -0
  43. data/lib/sass/repl.rb +57 -0
  44. data/lib/sass/root.rb +7 -0
  45. data/lib/sass/script/arg_list.rb +52 -0
  46. data/lib/sass/script/bool.rb +18 -0
  47. data/lib/sass/script/color.rb +606 -0
  48. data/lib/sass/script/css_lexer.rb +29 -0
  49. data/lib/sass/script/css_parser.rb +31 -0
  50. data/lib/sass/script/funcall.rb +245 -0
  51. data/lib/sass/script/functions.rb +1543 -0
  52. data/lib/sass/script/interpolation.rb +79 -0
  53. data/lib/sass/script/lexer.rb +345 -0
  54. data/lib/sass/script/list.rb +85 -0
  55. data/lib/sass/script/literal.rb +221 -0
  56. data/lib/sass/script/node.rb +99 -0
  57. data/lib/sass/script/null.rb +37 -0
  58. data/lib/sass/script/number.rb +453 -0
  59. data/lib/sass/script/operation.rb +110 -0
  60. data/lib/sass/script/parser.rb +502 -0
  61. data/lib/sass/script/string.rb +51 -0
  62. data/lib/sass/script/string_interpolation.rb +103 -0
  63. data/lib/sass/script/unary_operation.rb +69 -0
  64. data/lib/sass/script/variable.rb +58 -0
  65. data/lib/sass/script.rb +39 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1180 -0
  68. data/lib/sass/scss/rx.rb +133 -0
  69. data/lib/sass/scss/script_lexer.rb +15 -0
  70. data/lib/sass/scss/script_parser.rb +25 -0
  71. data/lib/sass/scss/static_parser.rb +54 -0
  72. data/lib/sass/scss.rb +16 -0
  73. data/lib/sass/selector/abstract_sequence.rb +94 -0
  74. data/lib/sass/selector/comma_sequence.rb +92 -0
  75. data/lib/sass/selector/sequence.rb +507 -0
  76. data/lib/sass/selector/simple.rb +119 -0
  77. data/lib/sass/selector/simple_sequence.rb +215 -0
  78. data/lib/sass/selector.rb +452 -0
  79. data/lib/sass/shared.rb +76 -0
  80. data/lib/sass/supports.rb +229 -0
  81. data/lib/sass/tree/charset_node.rb +22 -0
  82. data/lib/sass/tree/comment_node.rb +82 -0
  83. data/lib/sass/tree/content_node.rb +9 -0
  84. data/lib/sass/tree/css_import_node.rb +60 -0
  85. data/lib/sass/tree/debug_node.rb +18 -0
  86. data/lib/sass/tree/directive_node.rb +42 -0
  87. data/lib/sass/tree/each_node.rb +24 -0
  88. data/lib/sass/tree/extend_node.rb +36 -0
  89. data/lib/sass/tree/for_node.rb +36 -0
  90. data/lib/sass/tree/function_node.rb +34 -0
  91. data/lib/sass/tree/if_node.rb +52 -0
  92. data/lib/sass/tree/import_node.rb +75 -0
  93. data/lib/sass/tree/media_node.rb +58 -0
  94. data/lib/sass/tree/mixin_def_node.rb +38 -0
  95. data/lib/sass/tree/mixin_node.rb +39 -0
  96. data/lib/sass/tree/node.rb +196 -0
  97. data/lib/sass/tree/prop_node.rb +152 -0
  98. data/lib/sass/tree/return_node.rb +18 -0
  99. data/lib/sass/tree/root_node.rb +78 -0
  100. data/lib/sass/tree/rule_node.rb +132 -0
  101. data/lib/sass/tree/supports_node.rb +51 -0
  102. data/lib/sass/tree/trace_node.rb +32 -0
  103. data/lib/sass/tree/variable_node.rb +30 -0
  104. data/lib/sass/tree/visitors/base.rb +75 -0
  105. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  106. data/lib/sass/tree/visitors/convert.rb +316 -0
  107. data/lib/sass/tree/visitors/cssize.rb +241 -0
  108. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  109. data/lib/sass/tree/visitors/extend.rb +68 -0
  110. data/lib/sass/tree/visitors/perform.rb +446 -0
  111. data/lib/sass/tree/visitors/set_options.rb +125 -0
  112. data/lib/sass/tree/visitors/to_css.rb +228 -0
  113. data/lib/sass/tree/warn_node.rb +18 -0
  114. data/lib/sass/tree/while_node.rb +18 -0
  115. data/lib/sass/util/multibyte_string_scanner.rb +155 -0
  116. data/lib/sass/util/subset_map.rb +109 -0
  117. data/lib/sass/util/test.rb +10 -0
  118. data/lib/sass/util.rb +948 -0
  119. data/lib/sass/version.rb +126 -0
  120. data/lib/sass.rb +95 -0
  121. data/rails/init.rb +1 -0
  122. data/test/Gemfile +3 -0
  123. data/test/Gemfile.lock +10 -0
  124. data/test/sass/cache_test.rb +89 -0
  125. data/test/sass/callbacks_test.rb +61 -0
  126. data/test/sass/conversion_test.rb +1760 -0
  127. data/test/sass/css2sass_test.rb +458 -0
  128. data/test/sass/data/hsl-rgb.txt +319 -0
  129. data/test/sass/engine_test.rb +3244 -0
  130. data/test/sass/exec_test.rb +86 -0
  131. data/test/sass/extend_test.rb +1482 -0
  132. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  133. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  134. data/test/sass/functions_test.rb +1139 -0
  135. data/test/sass/importer_test.rb +192 -0
  136. data/test/sass/logger_test.rb +58 -0
  137. data/test/sass/mock_importer.rb +49 -0
  138. data/test/sass/more_results/more1.css +9 -0
  139. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  140. data/test/sass/more_results/more_import.css +29 -0
  141. data/test/sass/more_templates/_more_partial.sass +2 -0
  142. data/test/sass/more_templates/more1.sass +23 -0
  143. data/test/sass/more_templates/more_import.sass +11 -0
  144. data/test/sass/plugin_test.rb +564 -0
  145. data/test/sass/results/alt.css +4 -0
  146. data/test/sass/results/basic.css +9 -0
  147. data/test/sass/results/cached_import_option.css +3 -0
  148. data/test/sass/results/compact.css +5 -0
  149. data/test/sass/results/complex.css +86 -0
  150. data/test/sass/results/compressed.css +1 -0
  151. data/test/sass/results/expanded.css +19 -0
  152. data/test/sass/results/filename_fn.css +3 -0
  153. data/test/sass/results/if.css +3 -0
  154. data/test/sass/results/import.css +31 -0
  155. data/test/sass/results/import_charset.css +5 -0
  156. data/test/sass/results/import_charset_1_8.css +5 -0
  157. data/test/sass/results/import_charset_ibm866.css +5 -0
  158. data/test/sass/results/import_content.css +1 -0
  159. data/test/sass/results/line_numbers.css +49 -0
  160. data/test/sass/results/mixins.css +95 -0
  161. data/test/sass/results/multiline.css +24 -0
  162. data/test/sass/results/nested.css +22 -0
  163. data/test/sass/results/options.css +1 -0
  164. data/test/sass/results/parent_ref.css +13 -0
  165. data/test/sass/results/script.css +16 -0
  166. data/test/sass/results/scss_import.css +31 -0
  167. data/test/sass/results/scss_importee.css +2 -0
  168. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  169. data/test/sass/results/subdir/subdir.css +3 -0
  170. data/test/sass/results/units.css +11 -0
  171. data/test/sass/results/warn.css +0 -0
  172. data/test/sass/results/warn_imported.css +0 -0
  173. data/test/sass/script_conversion_test.rb +299 -0
  174. data/test/sass/script_test.rb +622 -0
  175. data/test/sass/scss/css_test.rb +1100 -0
  176. data/test/sass/scss/rx_test.rb +156 -0
  177. data/test/sass/scss/scss_test.rb +2106 -0
  178. data/test/sass/scss/test_helper.rb +37 -0
  179. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  180. data/test/sass/templates/_double_import_loop2.sass +1 -0
  181. data/test/sass/templates/_filename_fn_import.scss +11 -0
  182. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  183. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  184. data/test/sass/templates/_imported_content.sass +3 -0
  185. data/test/sass/templates/_partial.sass +2 -0
  186. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  187. data/test/sass/templates/alt.sass +16 -0
  188. data/test/sass/templates/basic.sass +23 -0
  189. data/test/sass/templates/bork1.sass +2 -0
  190. data/test/sass/templates/bork2.sass +2 -0
  191. data/test/sass/templates/bork3.sass +2 -0
  192. data/test/sass/templates/bork4.sass +2 -0
  193. data/test/sass/templates/bork5.sass +3 -0
  194. data/test/sass/templates/cached_import_option.scss +3 -0
  195. data/test/sass/templates/compact.sass +17 -0
  196. data/test/sass/templates/complex.sass +305 -0
  197. data/test/sass/templates/compressed.sass +15 -0
  198. data/test/sass/templates/double_import_loop1.sass +1 -0
  199. data/test/sass/templates/expanded.sass +17 -0
  200. data/test/sass/templates/filename_fn.scss +18 -0
  201. data/test/sass/templates/if.sass +11 -0
  202. data/test/sass/templates/import.sass +12 -0
  203. data/test/sass/templates/import_charset.sass +9 -0
  204. data/test/sass/templates/import_charset_1_8.sass +6 -0
  205. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  206. data/test/sass/templates/import_content.sass +4 -0
  207. data/test/sass/templates/importee.less +2 -0
  208. data/test/sass/templates/importee.sass +19 -0
  209. data/test/sass/templates/line_numbers.sass +13 -0
  210. data/test/sass/templates/mixin_bork.sass +5 -0
  211. data/test/sass/templates/mixins.sass +76 -0
  212. data/test/sass/templates/multiline.sass +20 -0
  213. data/test/sass/templates/nested.sass +25 -0
  214. data/test/sass/templates/nested_bork1.sass +2 -0
  215. data/test/sass/templates/nested_bork2.sass +2 -0
  216. data/test/sass/templates/nested_bork3.sass +2 -0
  217. data/test/sass/templates/nested_bork4.sass +2 -0
  218. data/test/sass/templates/nested_import.sass +2 -0
  219. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  220. data/test/sass/templates/options.sass +2 -0
  221. data/test/sass/templates/parent_ref.sass +25 -0
  222. data/test/sass/templates/same_name_different_ext.sass +2 -0
  223. data/test/sass/templates/same_name_different_ext.scss +1 -0
  224. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  225. data/test/sass/templates/script.sass +101 -0
  226. data/test/sass/templates/scss_import.scss +11 -0
  227. data/test/sass/templates/scss_importee.scss +1 -0
  228. data/test/sass/templates/single_import_loop.sass +1 -0
  229. data/test/sass/templates/subdir/import_up1.scss +1 -0
  230. data/test/sass/templates/subdir/import_up2.scss +1 -0
  231. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  232. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  233. data/test/sass/templates/subdir/subdir.sass +6 -0
  234. data/test/sass/templates/units.sass +11 -0
  235. data/test/sass/templates/warn.sass +3 -0
  236. data/test/sass/templates/warn_imported.sass +4 -0
  237. data/test/sass/test_helper.rb +8 -0
  238. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  239. data/test/sass/util/subset_map_test.rb +91 -0
  240. data/test/sass/util_test.rb +382 -0
  241. data/test/test_helper.rb +80 -0
  242. metadata +354 -0
@@ -0,0 +1,139 @@
1
+ module Sass
2
+ module Importers
3
+ # The abstract base class for Sass importers.
4
+ # All importers should inherit from this.
5
+ #
6
+ # At the most basic level, an importer is given a string
7
+ # and must return a {Sass::Engine} containing some Sass code.
8
+ # This string can be interpreted however the importer wants;
9
+ # however, subclasses are encouraged to use the URI format
10
+ # for pathnames.
11
+ #
12
+ # Importers that have some notion of "relative imports"
13
+ # should take a single load path in their constructor,
14
+ # and interpret paths as relative to that.
15
+ # They should also implement the \{#find\_relative} method.
16
+ #
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}.
21
+ #
22
+ # @abstract
23
+ class Base
24
+
25
+ # Find a Sass file relative to another file.
26
+ # Importers without a notion of "relative paths"
27
+ # should just return nil here.
28
+ #
29
+ # If the importer does have a notion of "relative paths",
30
+ # it should ignore its load path during this method.
31
+ #
32
+ # See \{#find} for important information on how this method should behave.
33
+ #
34
+ # The `:filename` option passed to the returned {Sass::Engine}
35
+ # should be of a format that could be passed to \{#find}.
36
+ #
37
+ # @param uri [String] The URI to import. This is not necessarily relative,
38
+ # but this method should only return true if it is.
39
+ # @param base [String] The base filename. If `uri` is relative,
40
+ # it should be interpreted as relative to `base`.
41
+ # `base` is guaranteed to be in a format importable by this importer.
42
+ # @param options [{Symbol => Object}] Options for the Sass file
43
+ # containing the `@import` that's currently being resolved.
44
+ # @return [Sass::Engine, nil] An Engine containing the imported file,
45
+ # or nil if it couldn't be found or was in the wrong format.
46
+ def find_relative(uri, base, options)
47
+ Sass::Util.abstract(self)
48
+ end
49
+
50
+ # Find a Sass file, if it exists.
51
+ #
52
+ # This is the primary entry point of the Importer.
53
+ # It corresponds directly to an `@import` statement in Sass.
54
+ # It should do three basic things:
55
+ #
56
+ # * Determine if the URI is in this importer's format.
57
+ # If not, return nil.
58
+ # * Determine if the file indicated by the URI actually exists and is readable.
59
+ # If not, return nil.
60
+ # * Read the file and place the contents in a {Sass::Engine}.
61
+ # Return that engine.
62
+ #
63
+ # If this importer's format allows for file extensions,
64
+ # it should treat them the same way as the default {Filesystem} importer.
65
+ # If the URI explicitly has a `.sass` or `.scss` filename,
66
+ # the importer should look for that exact file
67
+ # and import it as the syntax indicated.
68
+ # If it doesn't exist, the importer should return nil.
69
+ #
70
+ # If the URI doesn't have either of these extensions,
71
+ # the importer should look for files with the extensions.
72
+ # If no such files exist, it should return nil.
73
+ #
74
+ # The {Sass::Engine} to be returned should be passed `options`,
75
+ # with a few modifications. `:syntax` should be set appropriately,
76
+ # `:filename` should be set to `uri`,
77
+ # and `:importer` should be set to this importer.
78
+ #
79
+ # @param uri [String] The URI to import.
80
+ # @param options [{Symbol => Object}] Options for the Sass file
81
+ # containing the `@import` that's currently being resolved.
82
+ # This is safe for subclasses to modify destructively.
83
+ # Callers should only pass in a value they don't mind being destructively modified.
84
+ # @return [Sass::Engine, nil] An Engine containing the imported file,
85
+ # or nil if it couldn't be found or was in the wrong format.
86
+ def find(uri, options)
87
+ Sass::Util.abstract(self)
88
+ end
89
+
90
+ # Returns the time the given Sass file was last modified.
91
+ #
92
+ # If the given file has been deleted or the time can't be accessed
93
+ # for some other reason, this should return nil.
94
+ #
95
+ # @param uri [String] The URI of the file to check.
96
+ # Comes from a `:filename` option set on an engine returned by this importer.
97
+ # @param options [{Symbol => Objet}] Options for the Sass file
98
+ # containing the `@import` currently being checked.
99
+ # @return [Time, nil]
100
+ def mtime(uri, options)
101
+ Sass::Util.abstract(self)
102
+ end
103
+
104
+ # Get the cache key pair for the given Sass URI.
105
+ # The URI need not be checked for validity.
106
+ #
107
+ # The only strict requirement is that the returned pair of strings
108
+ # uniquely identify the file at the given URI.
109
+ # However, the first component generally corresponds roughly to the directory,
110
+ # and the second to the basename, of the URI.
111
+ #
112
+ # Note that keys must be unique *across importers*.
113
+ # Thus it's probably a good idea to include the importer name
114
+ # at the beginning of the first component.
115
+ #
116
+ # @param uri [String] A URI known to be valid for this importer.
117
+ # @param options [{Symbol => Object}] Options for the Sass file
118
+ # containing the `@import` currently being checked.
119
+ # @return [(String, String)] The key pair which uniquely identifies
120
+ # the file at the given URI.
121
+ def key(uri, options)
122
+ Sass::Util.abstract(self)
123
+ end
124
+
125
+ # A string representation of the importer.
126
+ # Should be overridden by subclasses.
127
+ #
128
+ # This is used to help debugging,
129
+ # and should usually just show the load path encapsulated by this importer.
130
+ #
131
+ # @return [String]
132
+ def to_s
133
+ Sass::Util.abstract(self)
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+
@@ -0,0 +1,186 @@
1
+ require 'pathname'
2
+ require 'set'
3
+
4
+ module Sass
5
+ module Importers
6
+ # The default importer, used for any strings found in the load path.
7
+ # Simply loads Sass files from the filesystem using the default logic.
8
+ class Filesystem < Base
9
+
10
+ attr_accessor :root
11
+
12
+ # Creates a new filesystem importer that imports files relative to a given path.
13
+ #
14
+ # @param root [String] The root path.
15
+ # This importer will import files relative to this path.
16
+ def initialize(root)
17
+ @root = File.expand_path(root)
18
+ @same_name_warnings = Set.new
19
+ end
20
+
21
+ # @see Base#find_relative
22
+ def find_relative(name, base, options)
23
+ _find(File.dirname(base), name, options)
24
+ end
25
+
26
+ # @see Base#find
27
+ def find(name, options)
28
+ _find(@root, name, options)
29
+ end
30
+
31
+ # @see Base#mtime
32
+ def mtime(name, options)
33
+ file, _ = Sass::Util.destructure(find_real_file(@root, name, options))
34
+ File.mtime(file) if file
35
+ rescue Errno::ENOENT
36
+ nil
37
+ end
38
+
39
+ # @see Base#key
40
+ def key(name, options)
41
+ [self.class.name + ":" + File.dirname(File.expand_path(name)),
42
+ File.basename(name)]
43
+ end
44
+
45
+ # @see Base#to_s
46
+ def to_s
47
+ @root
48
+ end
49
+
50
+ def hash
51
+ @root.hash
52
+ end
53
+
54
+ def eql?(other)
55
+ root.eql?(other.root)
56
+ end
57
+
58
+ protected
59
+
60
+ # If a full uri is passed, this removes the root from it
61
+ # otherwise returns the name unchanged
62
+ def remove_root(name)
63
+ if name.index(@root + "/") == 0
64
+ name[(@root.length + 1)..-1]
65
+ else
66
+ name
67
+ end
68
+ end
69
+
70
+ # A hash from file extensions to the syntaxes for those extensions.
71
+ # The syntaxes must be `:sass` or `:scss`.
72
+ #
73
+ # This can be overridden by subclasses that want normal filesystem importing
74
+ # with unusual extensions.
75
+ #
76
+ # @return [{String => Symbol}]
77
+ def extensions
78
+ {'sass' => :sass, 'scss' => :scss}
79
+ end
80
+
81
+ # Given an `@import`ed path, returns an array of possible
82
+ # on-disk filenames and their corresponding syntaxes for that path.
83
+ #
84
+ # @param name [String] The filename.
85
+ # @return [Array(String, Symbol)] An array of pairs.
86
+ # The first element of each pair is a filename to look for;
87
+ # the second element is the syntax that file would be in (`:sass` or `:scss`).
88
+ def possible_files(name)
89
+ name = escape_glob_characters(name)
90
+ dirname, basename, extname = split(name)
91
+ sorted_exts = extensions.sort
92
+ syntax = extensions[extname]
93
+
94
+ if syntax
95
+ ret = [["#{dirname}/{_,}#{basename}.#{extensions.invert[syntax]}", syntax]]
96
+ else
97
+ ret = sorted_exts.map {|ext, syn| ["#{dirname}/{_,}#{basename}.#{ext}", syn]}
98
+ end
99
+
100
+ # JRuby chokes when trying to import files from JARs when the path starts with './'.
101
+ ret.map {|f, s| [f.sub(%r{^\./}, ''), s]}
102
+ end
103
+
104
+ def escape_glob_characters(name)
105
+ name.gsub(/[\*\[\]\{\}\?]/) do |char|
106
+ "\\#{char}"
107
+ end
108
+ end
109
+
110
+ REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
111
+ # Given a base directory and an `@import`ed name,
112
+ # finds an existant file that matches the name.
113
+ #
114
+ # @param dir [String] The directory relative to which to search.
115
+ # @param name [String] The filename to search for.
116
+ # @return [(String, Symbol)] A filename-syntax pair.
117
+ def find_real_file(dir, name, options)
118
+ # on windows 'dir' can be in native File::ALT_SEPARATOR form
119
+ dir = dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
120
+
121
+ found = possible_files(remove_root(name)).map do |f, s|
122
+ path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{escape_glob_characters(dir)}/#{f}"
123
+ Dir[path].map do |full_path|
124
+ full_path.gsub!(REDUNDANT_DIRECTORY, File::SEPARATOR)
125
+ [Pathname.new(full_path).cleanpath.to_s, s]
126
+ end
127
+ end
128
+ found = Sass::Util.flatten(found, 1)
129
+ return if found.empty?
130
+
131
+ if found.size > 1 && !@same_name_warnings.include?(found.first.first)
132
+ found.each {|(f, _)| @same_name_warnings << f}
133
+ relative_to = Pathname.new(dir)
134
+ if options[:_line]
135
+ # If _line exists, we're here due to an actual import in an
136
+ # import_node and we want to print a warning for a user writing an
137
+ # ambiguous import.
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:
143
+ #{candidates}
144
+ For now I'll choose #{File.basename found.first.first}.
145
+ This will be an error in future versions of Sass.
146
+ WARNING
147
+ else
148
+ # Otherwise, we're here via StalenessChecker, and we want to print a
149
+ # warning for a user running `sass --watch` with two ambiguous files.
150
+ candidates = found.map {|(f, _)| " " + File.basename(f)}.join("\n")
151
+ Sass::Util.sass_warn <<WARNING
152
+ WARNING: In #{File.dirname(name)}:
153
+ There are multiple files that match the name "#{File.basename(name)}":
154
+ #{candidates}
155
+ WARNING
156
+ end
157
+ end
158
+ found.first
159
+ end
160
+
161
+ # Splits a filename into three parts, a directory part, a basename, and an extension
162
+ # Only the known extensions returned from the extensions method will be recognized as such.
163
+ def split(name)
164
+ extension = nil
165
+ dirname, basename = File.dirname(name), File.basename(name)
166
+ if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
167
+ basename = $1
168
+ extension = $2
169
+ end
170
+ [dirname, basename, extension]
171
+ end
172
+
173
+ private
174
+
175
+ def _find(dir, name, options)
176
+ full_filename, syntax = Sass::Util.destructure(find_real_file(dir, name, options))
177
+ return unless full_filename && File.readable?(full_filename)
178
+
179
+ options[:syntax] = syntax
180
+ options[:filename] = full_filename
181
+ options[:importer] = self
182
+ Sass::Engine.new(File.read(full_filename), options)
183
+ end
184
+ end
185
+ end
186
+ end
@@ -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,32 @@
1
+ require 'sass/logger/log_level'
2
+
3
+ class Sass::Logger::Base
4
+
5
+ include Sass::Logger::LogLevel
6
+
7
+ attr_accessor :log_level
8
+ attr_accessor :disabled
9
+
10
+ log_level :trace
11
+ log_level :debug
12
+ log_level :info
13
+ log_level :warn
14
+ log_level :error
15
+
16
+ def initialize(log_level = :debug)
17
+ self.log_level = log_level
18
+ end
19
+
20
+ def logging_level?(level)
21
+ !disabled && self.class.log_level?(level, log_level)
22
+ end
23
+
24
+ def log(level, message)
25
+ self._log(level, message) if logging_level?(level)
26
+ end
27
+
28
+ def _log(level, message)
29
+ Kernel::warn(message)
30
+ end
31
+
32
+ end
@@ -0,0 +1,49 @@
1
+ module Sass
2
+ module Logger
3
+ module LogLevel
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def inherited(subclass)
11
+ subclass.log_levels = subclass.superclass.log_levels.dup
12
+ end
13
+
14
+ def log_levels
15
+ @log_levels ||= {}
16
+ end
17
+
18
+ def log_levels=(levels)
19
+ @log_levels = levels
20
+ end
21
+
22
+ def log_level?(level, min_level)
23
+ log_levels[level] >= log_levels[min_level]
24
+ end
25
+
26
+ def log_level(name, options = {})
27
+ if options[:prepend]
28
+ level = log_levels.values.min
29
+ level = level.nil? ? 0 : level - 1
30
+ else
31
+ level = log_levels.values.max
32
+ level = level.nil? ? 0 : level + 1
33
+ end
34
+ log_levels.update(name => level)
35
+ define_logger(name)
36
+ end
37
+
38
+ def define_logger(name, options = {})
39
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
40
+ def #{name}(message)
41
+ #{options.fetch(:to, :log)}(#{name.inspect}, message)
42
+ end
43
+ RUBY
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ module Sass::Logger
2
+
3
+ end
4
+
5
+ require "sass/logger/log_level"
6
+ require "sass/logger/base"
7
+
8
+ module Sass
9
+
10
+ class << self
11
+ attr_accessor :logger
12
+ end
13
+
14
+ self.logger = Sass::Logger::Base.new
15
+ end
data/lib/sass/media.rb ADDED
@@ -0,0 +1,213 @@
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::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
+ #
50
+ # @return [Array<String, Sass::Script::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::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::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::Node>>]
91
+ attr_accessor :expressions
92
+
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
+ 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
+ return 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::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::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
+ end
200
+ end
201
+
202
+ # Converts an interpolation array to source.
203
+ #
204
+ # @param [Array<String, Sass::Script::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 do |r|
209
+ next r if r.is_a?(String)
210
+ "\#{#{r.to_sass(options)}}"
211
+ end.join
212
+ end
213
+ end