sass 3.7.4 → 4.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (257) hide show
  1. checksums.yaml +13 -5
  2. data/.yardopts +1 -1
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/CONTRIBUTING.md +1 -146
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +25 -39
  7. data/Rakefile +274 -0
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/lib/sass.rb +3 -3
  11. data/lib/sass/cache_stores/filesystem.rb +2 -2
  12. data/lib/sass/cache_stores/memory.rb +5 -4
  13. data/lib/sass/callbacks.rb +2 -2
  14. data/lib/sass/css.rb +12 -12
  15. data/lib/sass/engine.rb +44 -62
  16. data/lib/sass/environment.rb +7 -35
  17. data/lib/sass/error.rb +14 -14
  18. data/lib/sass/exec/base.rb +14 -3
  19. data/lib/sass/exec/sass_convert.rb +6 -20
  20. data/lib/sass/exec/sass_scss.rb +29 -5
  21. data/lib/sass/features.rb +2 -3
  22. data/lib/sass/importers/filesystem.rb +6 -11
  23. data/lib/sass/logger.rb +3 -8
  24. data/lib/sass/logger/base.rb +2 -19
  25. data/lib/sass/plugin.rb +2 -3
  26. data/lib/sass/plugin/compiler.rb +67 -48
  27. data/lib/sass/plugin/configuration.rb +3 -3
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/rack.rb +3 -3
  30. data/lib/sass/plugin/staleness_checker.rb +3 -3
  31. data/lib/sass/railtie.rb +1 -1
  32. data/lib/sass/script.rb +3 -3
  33. data/lib/sass/script/css_parser.rb +15 -5
  34. data/lib/sass/script/functions.rb +121 -337
  35. data/lib/sass/script/lexer.rb +36 -102
  36. data/lib/sass/script/parser.rb +153 -529
  37. data/lib/sass/script/tree/funcall.rb +34 -42
  38. data/lib/sass/script/tree/interpolation.rb +26 -171
  39. data/lib/sass/script/tree/list_literal.rb +8 -23
  40. data/lib/sass/script/tree/map_literal.rb +2 -2
  41. data/lib/sass/script/tree/node.rb +3 -3
  42. data/lib/sass/script/tree/operation.rb +16 -43
  43. data/lib/sass/script/tree/string_interpolation.rb +43 -64
  44. data/lib/sass/script/tree/variable.rb +1 -1
  45. data/lib/sass/script/value.rb +0 -2
  46. data/lib/sass/script/value/arg_list.rb +1 -1
  47. data/lib/sass/script/value/base.rb +9 -27
  48. data/lib/sass/script/value/color.rb +18 -26
  49. data/lib/sass/script/value/helpers.rb +18 -44
  50. data/lib/sass/script/value/list.rb +14 -35
  51. data/lib/sass/script/value/map.rb +2 -2
  52. data/lib/sass/script/value/number.rb +16 -26
  53. data/lib/sass/script/value/string.rb +1 -30
  54. data/lib/sass/scss.rb +2 -0
  55. data/lib/sass/scss/css_parser.rb +3 -7
  56. data/lib/sass/scss/parser.rb +78 -196
  57. data/lib/sass/scss/rx.rb +14 -7
  58. data/lib/sass/scss/script_lexer.rb +15 -0
  59. data/lib/sass/scss/script_parser.rb +25 -0
  60. data/lib/sass/scss/static_parser.rb +55 -38
  61. data/lib/sass/selector.rb +10 -7
  62. data/lib/sass/selector/abstract_sequence.rb +12 -15
  63. data/lib/sass/selector/comma_sequence.rb +6 -24
  64. data/lib/sass/selector/pseudo.rb +6 -19
  65. data/lib/sass/selector/sequence.rb +16 -14
  66. data/lib/sass/selector/simple.rb +7 -9
  67. data/lib/sass/selector/simple_sequence.rb +12 -16
  68. data/lib/sass/shared.rb +1 -1
  69. data/lib/sass/source/map.rb +9 -7
  70. data/lib/sass/source/position.rb +4 -4
  71. data/lib/sass/stack.rb +3 -23
  72. data/lib/sass/tree/charset_node.rb +1 -1
  73. data/lib/sass/tree/comment_node.rb +1 -1
  74. data/lib/sass/tree/function_node.rb +3 -2
  75. data/lib/sass/tree/node.rb +3 -5
  76. data/lib/sass/tree/prop_node.rb +58 -49
  77. data/lib/sass/tree/rule_node.rb +8 -15
  78. data/lib/sass/tree/visitors/check_nesting.rb +23 -19
  79. data/lib/sass/tree/visitors/convert.rb +13 -15
  80. data/lib/sass/tree/visitors/cssize.rb +15 -4
  81. data/lib/sass/tree/visitors/deep_copy.rb +2 -2
  82. data/lib/sass/tree/visitors/extend.rb +14 -10
  83. data/lib/sass/tree/visitors/perform.rb +18 -29
  84. data/lib/sass/tree/visitors/set_options.rb +2 -2
  85. data/lib/sass/tree/visitors/to_css.rb +47 -77
  86. data/lib/sass/util.rb +311 -98
  87. data/lib/sass/util/cross_platform_random.rb +19 -0
  88. data/lib/sass/util/multibyte_string_scanner.rb +133 -127
  89. data/lib/sass/util/normalized_map.rb +8 -1
  90. data/lib/sass/util/ordered_hash.rb +192 -0
  91. data/lib/sass/version.rb +6 -2
  92. data/test/sass/cache_test.rb +131 -0
  93. data/test/sass/callbacks_test.rb +61 -0
  94. data/test/sass/compiler_test.rb +236 -0
  95. data/test/sass/conversion_test.rb +2171 -0
  96. data/test/sass/css2sass_test.rb +526 -0
  97. data/test/sass/data/hsl-rgb.txt +319 -0
  98. data/test/sass/encoding_test.rb +219 -0
  99. data/test/sass/engine_test.rb +3400 -0
  100. data/test/sass/exec_test.rb +86 -0
  101. data/test/sass/extend_test.rb +1719 -0
  102. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  103. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  104. data/test/sass/functions_test.rb +1984 -0
  105. data/test/sass/importer_test.rb +421 -0
  106. data/test/sass/logger_test.rb +58 -0
  107. data/test/sass/mock_importer.rb +49 -0
  108. data/test/sass/more_results/more1.css +9 -0
  109. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  110. data/test/sass/more_results/more_import.css +29 -0
  111. data/test/sass/more_templates/_more_partial.sass +2 -0
  112. data/test/sass/more_templates/more1.sass +23 -0
  113. data/test/sass/more_templates/more_import.sass +11 -0
  114. data/test/sass/plugin_test.rb +556 -0
  115. data/test/sass/results/alt.css +4 -0
  116. data/test/sass/results/basic.css +9 -0
  117. data/test/sass/results/cached_import_option.css +3 -0
  118. data/test/sass/results/compact.css +5 -0
  119. data/test/sass/results/complex.css +86 -0
  120. data/test/sass/results/compressed.css +1 -0
  121. data/test/sass/results/expanded.css +19 -0
  122. data/test/sass/results/filename_fn.css +3 -0
  123. data/test/sass/results/if.css +3 -0
  124. data/test/sass/results/import.css +31 -0
  125. data/test/sass/results/import_charset.css +5 -0
  126. data/test/sass/results/import_charset_1_8.css +5 -0
  127. data/test/sass/results/import_charset_ibm866.css +5 -0
  128. data/test/sass/results/import_content.css +1 -0
  129. data/test/sass/results/line_numbers.css +49 -0
  130. data/test/sass/results/mixins.css +95 -0
  131. data/test/sass/results/multiline.css +24 -0
  132. data/test/sass/results/nested.css +22 -0
  133. data/test/sass/results/options.css +1 -0
  134. data/test/sass/results/parent_ref.css +13 -0
  135. data/test/sass/results/script.css +16 -0
  136. data/test/sass/results/scss_import.css +31 -0
  137. data/test/sass/results/scss_importee.css +2 -0
  138. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/test/sass/results/subdir/subdir.css +3 -0
  140. data/test/sass/results/units.css +11 -0
  141. data/test/sass/results/warn.css +0 -0
  142. data/test/sass/results/warn_imported.css +0 -0
  143. data/test/sass/script_conversion_test.rb +306 -0
  144. data/test/sass/script_test.rb +1206 -0
  145. data/test/sass/scss/css_test.rb +1281 -0
  146. data/test/sass/scss/rx_test.rb +160 -0
  147. data/test/sass/scss/scss_test.rb +4147 -0
  148. data/test/sass/scss/test_helper.rb +37 -0
  149. data/test/sass/source_map_test.rb +1055 -0
  150. data/test/sass/superselector_test.rb +210 -0
  151. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  152. data/test/sass/templates/_double_import_loop2.sass +1 -0
  153. data/test/sass/templates/_filename_fn_import.scss +11 -0
  154. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  155. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  156. data/test/sass/templates/_imported_content.sass +3 -0
  157. data/test/sass/templates/_partial.sass +2 -0
  158. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  159. data/test/sass/templates/alt.sass +16 -0
  160. data/test/sass/templates/basic.sass +23 -0
  161. data/test/sass/templates/bork1.sass +2 -0
  162. data/test/sass/templates/bork2.sass +2 -0
  163. data/test/sass/templates/bork3.sass +2 -0
  164. data/test/sass/templates/bork4.sass +2 -0
  165. data/test/sass/templates/bork5.sass +3 -0
  166. data/test/sass/templates/cached_import_option.scss +3 -0
  167. data/test/sass/templates/compact.sass +17 -0
  168. data/test/sass/templates/complex.sass +305 -0
  169. data/test/sass/templates/compressed.sass +15 -0
  170. data/test/sass/templates/double_import_loop1.sass +1 -0
  171. data/test/sass/templates/expanded.sass +17 -0
  172. data/test/sass/templates/filename_fn.scss +18 -0
  173. data/test/sass/templates/if.sass +11 -0
  174. data/test/sass/templates/import.sass +12 -0
  175. data/test/sass/templates/import_charset.sass +9 -0
  176. data/test/sass/templates/import_charset_1_8.sass +6 -0
  177. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  178. data/test/sass/templates/import_content.sass +4 -0
  179. data/test/sass/templates/importee.less +2 -0
  180. data/test/sass/templates/importee.sass +19 -0
  181. data/test/sass/templates/line_numbers.sass +13 -0
  182. data/test/sass/templates/mixin_bork.sass +5 -0
  183. data/test/sass/templates/mixins.sass +76 -0
  184. data/test/sass/templates/multiline.sass +20 -0
  185. data/test/sass/templates/nested.sass +25 -0
  186. data/test/sass/templates/nested_bork1.sass +2 -0
  187. data/test/sass/templates/nested_bork2.sass +2 -0
  188. data/test/sass/templates/nested_bork3.sass +2 -0
  189. data/test/sass/templates/nested_bork4.sass +2 -0
  190. data/test/sass/templates/nested_import.sass +2 -0
  191. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  192. data/test/sass/templates/options.sass +2 -0
  193. data/test/sass/templates/parent_ref.sass +25 -0
  194. data/test/sass/templates/same_name_different_ext.sass +2 -0
  195. data/test/sass/templates/same_name_different_ext.scss +1 -0
  196. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  197. data/test/sass/templates/script.sass +101 -0
  198. data/test/sass/templates/scss_import.scss +12 -0
  199. data/test/sass/templates/scss_importee.scss +1 -0
  200. data/test/sass/templates/single_import_loop.sass +1 -0
  201. data/test/sass/templates/subdir/import_up1.scss +1 -0
  202. data/test/sass/templates/subdir/import_up2.scss +1 -0
  203. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  204. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  205. data/test/sass/templates/subdir/subdir.sass +6 -0
  206. data/test/sass/templates/units.sass +11 -0
  207. data/test/sass/templates/warn.sass +3 -0
  208. data/test/sass/templates/warn_imported.sass +4 -0
  209. data/test/sass/test_helper.rb +8 -0
  210. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  211. data/test/sass/util/normalized_map_test.rb +51 -0
  212. data/test/sass/util/subset_map_test.rb +91 -0
  213. data/test/sass/util_test.rb +438 -0
  214. data/test/sass/value_helpers_test.rb +179 -0
  215. data/test/test_helper.rb +110 -0
  216. data/vendor/listen/CHANGELOG.md +1 -0
  217. data/vendor/listen/CONTRIBUTING.md +38 -0
  218. data/vendor/listen/Gemfile +20 -0
  219. data/vendor/listen/Guardfile +8 -0
  220. data/vendor/listen/LICENSE +20 -0
  221. data/vendor/listen/README.md +349 -0
  222. data/vendor/listen/Rakefile +5 -0
  223. data/vendor/listen/Vagrantfile +96 -0
  224. data/vendor/listen/lib/listen.rb +54 -0
  225. data/vendor/listen/lib/listen/adapter.rb +327 -0
  226. data/vendor/listen/lib/listen/adapters/bsd.rb +75 -0
  227. data/vendor/listen/lib/listen/adapters/darwin.rb +48 -0
  228. data/vendor/listen/lib/listen/adapters/linux.rb +81 -0
  229. data/vendor/listen/lib/listen/adapters/polling.rb +58 -0
  230. data/vendor/listen/lib/listen/adapters/windows.rb +91 -0
  231. data/vendor/listen/lib/listen/directory_record.rb +406 -0
  232. data/vendor/listen/lib/listen/listener.rb +323 -0
  233. data/vendor/listen/lib/listen/turnstile.rb +32 -0
  234. data/vendor/listen/lib/listen/version.rb +3 -0
  235. data/vendor/listen/listen.gemspec +28 -0
  236. data/vendor/listen/spec/listen/adapter_spec.rb +149 -0
  237. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  238. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  239. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  240. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  241. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  242. data/vendor/listen/spec/listen/directory_record_spec.rb +1250 -0
  243. data/vendor/listen/spec/listen/listener_spec.rb +258 -0
  244. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  245. data/vendor/listen/spec/listen_spec.rb +67 -0
  246. data/vendor/listen/spec/spec_helper.rb +25 -0
  247. data/vendor/listen/spec/support/adapter_helper.rb +666 -0
  248. data/vendor/listen/spec/support/directory_record_helper.rb +57 -0
  249. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  250. data/vendor/listen/spec/support/listeners_helper.rb +179 -0
  251. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  252. metadata +217 -76
  253. data/extra/sass-spec-ref.sh +0 -40
  254. data/lib/sass/deprecation.rb +0 -55
  255. data/lib/sass/logger/delayed.rb +0 -50
  256. data/lib/sass/script/value/callable.rb +0 -25
  257. data/lib/sass/script/value/function.rb +0 -19
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+ require 'sass/engine'
3
+
4
+ module ScssTestHelper
5
+ def assert_parses(scss)
6
+ assert_equal scss.rstrip, render(scss).rstrip
7
+ end
8
+
9
+ def assert_not_parses(expected, scss)
10
+ raise "Template must include <err> where an error is expected" unless scss.include?("<err>")
11
+
12
+ after, was = scss.split("<err>")
13
+ line = after.count("\n") + 1
14
+
15
+ after.gsub!(/\s*\n\s*$/, '')
16
+ after.gsub!(/.*\n/, '')
17
+ after = "..." + after[-15..-1] if after.size > 18
18
+
19
+ was.gsub!(/^\s*\n\s*/, '')
20
+ was.gsub!(/\n.*/, '')
21
+ was = was[0...15] + "..." if was.size > 18
22
+
23
+ to_render = scss.sub("<err>", "")
24
+ render(to_render)
25
+ assert(false, "Expected syntax error for:\n#{to_render}\n")
26
+ rescue Sass::SyntaxError => err
27
+ assert_equal("Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
28
+ err.message)
29
+ assert_equal line, err.sass_line
30
+ end
31
+
32
+ def render(scss, options = {})
33
+ options[:syntax] ||= :scss
34
+ munge_filename options
35
+ Sass::Engine.new(scss, options).render
36
+ end
37
+ end
@@ -0,0 +1,1055 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/../test_helper'
4
+ require File.dirname(__FILE__) + '/test_helper'
5
+
6
+ class SourcemapTest < MiniTest::Test
7
+ def test_to_json_requires_args
8
+ _, sourcemap = render_with_sourcemap('')
9
+ assert_raises(ArgumentError) {sourcemap.to_json({})}
10
+ assert_raises(ArgumentError) {sourcemap.to_json({:css_path => 'foo'})}
11
+ assert_raises(ArgumentError) {sourcemap.to_json({:sourcemap_path => 'foo'})}
12
+ end
13
+
14
+ def test_simple_mapping_scss
15
+ assert_parses_with_sourcemap <<SCSS, <<CSS, <<JSON
16
+ a {
17
+ foo: bar;
18
+ /* SOME COMMENT */
19
+ font-size: 12px;
20
+ }
21
+ SCSS
22
+ a {
23
+ foo: bar;
24
+ /* SOME COMMENT */
25
+ font-size: 12px; }
26
+
27
+ /*# sourceMappingURL=test.css.map */
28
+ CSS
29
+ {
30
+ "version": 3,
31
+ "mappings": "AAAA,CAAE;EACA,GAAG,EAAE,GAAG;EACV,kBAAkB;EAChB,SAAS,EAAE,IAAI",
32
+ "sources": ["test_simple_mapping_scss_inline.scss"],
33
+ "names": [],
34
+ "file": "test.css"
35
+ }
36
+ JSON
37
+ end
38
+
39
+ def test_simple_mapping_sass
40
+ assert_parses_with_sourcemap <<SASS, <<CSS, <<JSON, :syntax => :sass
41
+ a
42
+ foo: bar
43
+ /* SOME COMMENT */
44
+ :font-size 12px
45
+ SASS
46
+ a {
47
+ foo: bar;
48
+ /* SOME COMMENT */
49
+ font-size: 12px; }
50
+
51
+ /*# sourceMappingURL=test.css.map */
52
+ CSS
53
+ {
54
+ "version": 3,
55
+ "mappings": "AAAA,CAAC;EACC,GAAG,EAAE,GAAG;;EAEP,SAAS,EAAC,IAAI",
56
+ "sources": ["test_simple_mapping_sass_inline.sass"],
57
+ "names": [],
58
+ "file": "test.css"
59
+ }
60
+ JSON
61
+ end
62
+
63
+ def test_simple_mapping_with_file_uris
64
+ uri = Sass::Util.file_uri_from_path(Sass::Util.absolute_path(filename_for_test(:scss)))
65
+ assert_parses_with_sourcemap <<SCSS, <<CSS, <<JSON, :sourcemap => :file
66
+ a {
67
+ foo: bar;
68
+ /* SOME COMMENT */
69
+ font-size: 12px;
70
+ }
71
+ SCSS
72
+ a {
73
+ foo: bar;
74
+ /* SOME COMMENT */
75
+ font-size: 12px; }
76
+
77
+ /*# sourceMappingURL=test.css.map */
78
+ CSS
79
+ {
80
+ "version": 3,
81
+ "mappings": "AAAA,CAAE;EACA,GAAG,EAAE,GAAG;EACV,kBAAkB;EAChB,SAAS,EAAE,IAAI",
82
+ "sources": ["#{uri}"],
83
+ "names": [],
84
+ "file": "test.css"
85
+ }
86
+ JSON
87
+ end
88
+
89
+ def test_mapping_with_directory_scss
90
+ options = {:filename => "scss/style.scss", :output => "css/style.css"}
91
+ assert_parses_with_sourcemap <<SCSS, <<CSS, <<JSON, options
92
+ a {
93
+ foo: bar;
94
+ /* SOME COMMENT */
95
+ font-size: 12px;
96
+ }
97
+ SCSS
98
+ a {
99
+ foo: bar;
100
+ /* SOME COMMENT */
101
+ font-size: 12px; }
102
+
103
+ /*# sourceMappingURL=style.css.map */
104
+ CSS
105
+ {
106
+ "version": 3,
107
+ "mappings": "AAAA,CAAE;EACA,GAAG,EAAE,GAAG;EACV,kBAAkB;EAChB,SAAS,EAAE,IAAI",
108
+ "sources": ["../scss/style.scss"],
109
+ "names": [],
110
+ "file": "style.css"
111
+ }
112
+ JSON
113
+ end
114
+
115
+ def test_mapping_with_directory_sass
116
+ options = {:filename => "sass/style.sass", :output => "css/style.css", :syntax => :sass}
117
+ assert_parses_with_sourcemap <<SASS, <<CSS, <<JSON, options
118
+ a
119
+ foo: bar
120
+ /* SOME COMMENT */
121
+ :font-size 12px
122
+ SASS
123
+ a {
124
+ foo: bar;
125
+ /* SOME COMMENT */
126
+ font-size: 12px; }
127
+
128
+ /*# sourceMappingURL=style.css.map */
129
+ CSS
130
+ {
131
+ "version": 3,
132
+ "mappings": "AAAA,CAAC;EACC,GAAG,EAAE,GAAG;;EAEP,SAAS,EAAC,IAAI",
133
+ "sources": ["../sass/style.sass"],
134
+ "names": [],
135
+ "file": "style.css"
136
+ }
137
+ JSON
138
+ end
139
+
140
+ unless Sass::Util.ruby1_8?
141
+ def test_simple_charset_mapping_scss
142
+ assert_parses_with_sourcemap <<SCSS, <<CSS, <<JSON
143
+ a {
144
+ fóó: bár;
145
+ }
146
+ SCSS
147
+ @charset "UTF-8";
148
+ a {
149
+ fóó: bár; }
150
+
151
+ /*# sourceMappingURL=test.css.map */
152
+ CSS
153
+ {
154
+ "version": 3,
155
+ "mappings": ";AAAA,CAAE;EACA,GAAG,EAAE,GAAG",
156
+ "sources": ["test_simple_charset_mapping_scss_inline.scss"],
157
+ "names": [],
158
+ "file": "test.css"
159
+ }
160
+ JSON
161
+ end
162
+
163
+ def test_simple_charset_mapping_sass
164
+ assert_parses_with_sourcemap <<SASS, <<CSS, <<JSON, :syntax => :sass
165
+ a
166
+ fóó: bár
167
+ SASS
168
+ @charset "UTF-8";
169
+ a {
170
+ fóó: bár; }
171
+
172
+ /*# sourceMappingURL=test.css.map */
173
+ CSS
174
+ {
175
+ "version": 3,
176
+ "mappings": ";AAAA,CAAC;EACC,GAAG,EAAE,GAAG",
177
+ "sources": ["test_simple_charset_mapping_sass_inline.sass"],
178
+ "names": [],
179
+ "file": "test.css"
180
+ }
181
+ JSON
182
+ end
183
+
184
+ def test_different_charset_than_encoding_scss
185
+ assert_parses_with_sourcemap(<<SCSS.force_encoding("IBM866"), <<CSS, <<JSON)
186
+ @charset "IBM866";
187
+ f\x86\x86 {
188
+ \x86: b;
189
+ }
190
+ SCSS
191
+ @charset "UTF-8";
192
+ fЖЖ {
193
+ Ж: b; }
194
+
195
+ /*# sourceMappingURL=test.css.map */
196
+ CSS
197
+ {
198
+ "version": 3,
199
+ "mappings": ";AACA,GAAI;EACF,CAAC,EAAE,CAAC",
200
+ "sources": ["test_different_charset_than_encoding_scss_inline.scss"],
201
+ "names": [],
202
+ "file": "test.css"
203
+ }
204
+ JSON
205
+ end
206
+
207
+ def test_different_charset_than_encoding_sass
208
+ assert_parses_with_sourcemap(<<SASS.force_encoding("IBM866"), <<CSS, <<JSON, :syntax => :sass)
209
+ @charset "IBM866"
210
+ f\x86\x86
211
+ \x86: b
212
+ SASS
213
+ @charset "UTF-8";
214
+ fЖЖ {
215
+ Ж: b; }
216
+
217
+ /*# sourceMappingURL=test.css.map */
218
+ CSS
219
+ {
220
+ "version": 3,
221
+ "mappings": ";AACA,GAAG;EACD,CAAC,EAAE,CAAC",
222
+ "sources": ["test_different_charset_than_encoding_sass_inline.sass"],
223
+ "names": [],
224
+ "file": "test.css"
225
+ }
226
+ JSON
227
+ end
228
+ end
229
+
230
+ def test_import_sourcemap_scss
231
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
232
+ @import {{1}}url(foo){{/1}},{{2}}url(moo) {{/2}}, {{3}}url(bar) {{/3}};
233
+ @import {{4}}url(baz) screen print{{/4}};
234
+ SCSS
235
+ {{1}}@import url(foo){{/1}};
236
+ {{2}}@import url(moo){{/2}};
237
+ {{3}}@import url(bar){{/3}};
238
+ {{4}}@import url(baz) screen print{{/4}};
239
+
240
+ /*# sourceMappingURL=test.css.map */
241
+ CSS
242
+ end
243
+
244
+ def test_import_sourcemap_sass
245
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
246
+ @import {{1}}foo.css{{/1}},{{2}}moo.css{{/2}}, {{3}}bar.css{{/3}}
247
+ @import {{4}}url(baz.css){{/4}}
248
+ @import {{5}}url(qux.css) screen print{{/5}}
249
+ SASS
250
+ {{1}}@import url(foo.css){{/1}};
251
+ {{2}}@import url(moo.css){{/2}};
252
+ {{3}}@import url(bar.css){{/3}};
253
+ {{4}}@import url(baz.css){{/4}};
254
+ {{5}}@import url(qux.css) screen print{{/5}};
255
+
256
+ /*# sourceMappingURL=test.css.map */
257
+ CSS
258
+ end
259
+
260
+ def test_media_sourcemap_scss
261
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
262
+ {{1}}@media screen, tv {{/1}}{
263
+ {{2}}body {{/2}}{
264
+ {{3}}max-width{{/3}}: {{4}}1070px{{/4}};
265
+ }
266
+ }
267
+ SCSS
268
+ {{1}}@media screen, tv{{/1}} {
269
+ {{2}}body{{/2}} {
270
+ {{3}}max-width{{/3}}: {{4}}1070px{{/4}}; } }
271
+
272
+ /*# sourceMappingURL=test.css.map */
273
+ CSS
274
+ end
275
+
276
+ def test_media_sourcemap_sass
277
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
278
+ {{1}}@media screen, tv{{/1}}
279
+ {{2}}body{{/2}}
280
+ {{3}}max-width{{/3}}: {{4}}1070px{{/4}}
281
+ SASS
282
+ {{1}}@media screen, tv{{/1}} {
283
+ {{2}}body{{/2}} {
284
+ {{3}}max-width{{/3}}: {{4}}1070px{{/4}}; } }
285
+
286
+ /*# sourceMappingURL=test.css.map */
287
+ CSS
288
+ end
289
+
290
+ def test_interpolation_and_vars_sourcemap_scss
291
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
292
+ $te: "te";
293
+ $teal: {{5}}teal{{/5}};
294
+ {{1}}p {{/1}}{
295
+ {{2}}con#{$te}nt{{/2}}: {{3}}"I a#{$te} #{5 + 10} pies!"{{/3}};
296
+ {{4}}color{{/4}}: $teal;
297
+ }
298
+
299
+ $name: foo;
300
+ $attr: border;
301
+ {{6}}p.#{$name} {{/6}}{
302
+ {{7}}#{$attr}-color{{/7}}: {{8}}blue{{/8}};
303
+ $font-size: 12px;
304
+ $line-height: 30px;
305
+ {{9}}font{{/9}}: {{10}}#{$font-size}/#{$line-height}{{/10}};
306
+ }
307
+ SCSS
308
+ {{1}}p{{/1}} {
309
+ {{2}}content{{/2}}: {{3}}"I ate 15 pies!"{{/3}};
310
+ {{4}}color{{/4}}: {{5}}teal{{/5}}; }
311
+
312
+ {{6}}p.foo{{/6}} {
313
+ {{7}}border-color{{/7}}: {{8}}blue{{/8}};
314
+ {{9}}font{{/9}}: {{10}}12px/30px{{/10}}; }
315
+
316
+ /*# sourceMappingURL=test.css.map */
317
+ CSS
318
+ end
319
+
320
+ def test_interpolation_and_vars_sourcemap_sass
321
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
322
+ $te: "te"
323
+ $teal: {{5}}teal{{/5}}
324
+ {{1}}p{{/1}}
325
+ {{2}}con#{$te}nt{{/2}}: {{3}}"I a#{$te} #{5 + 10} pies!"{{/3}}
326
+ {{4}}color{{/4}}: $teal
327
+
328
+ $name: foo
329
+ $attr: border
330
+ {{6}}p.#{$name}{{/6}}
331
+ {{7}}#{$attr}-color{{/7}}: {{8}}blue{{/8}}
332
+ $font-size: 12px
333
+ $line-height: 30px
334
+ {{9}}font{{/9}}: {{10}}#{$font-size}/#{$line-height}{{/10}}
335
+ SASS
336
+ {{1}}p{{/1}} {
337
+ {{2}}content{{/2}}: {{3}}"I ate 15 pies!"{{/3}};
338
+ {{4}}color{{/4}}: {{5}}teal{{/5}}; }
339
+
340
+ {{6}}p.foo{{/6}} {
341
+ {{7}}border-color{{/7}}: {{8}}blue{{/8}};
342
+ {{9}}font{{/9}}: {{10}}12px/30px{{/10}}; }
343
+
344
+ /*# sourceMappingURL=test.css.map */
345
+ CSS
346
+ end
347
+
348
+ def test_selectors_properties_sourcemap_scss
349
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
350
+ $width: 2px;
351
+ $translucent-red: rgba(255, 0, 0, 0.5);
352
+ {{1}}a {{/1}}{
353
+ {{9}}.special {{/9}}{
354
+ {{10}}color{{/10}}: {{11}}red{{/11}};
355
+ {{12}}&:hover {{/12}}{
356
+ {{13}}foo{{/13}}: {{14}}bar{{/14}};
357
+ {{15}}cursor{{/15}}: {{16}}e + -resize{{/16}};
358
+ {{17}}color{{/17}}: {{18}}opacify($translucent-red, 0.3){{/18}};
359
+ }
360
+ {{19}}&:after {{/19}}{
361
+ {{20}}content{{/20}}: {{21}}"I ate #{5 + 10} pies #{$width} thick!"{{/21}};
362
+ }
363
+ }
364
+ {{22}}&:active {{/22}}{
365
+ {{23}}color{{/23}}: {{24}}#010203 + #040506{{/24}};
366
+ {{25}}border{{/25}}: {{26}}$width solid black{{/26}};
367
+ }
368
+ {{2}}/* SOME COMMENT */{{/2}}
369
+ {{3}}font{{/3}}: {{4}}2px/3px {{/4}}{
370
+ {{5}}family{{/5}}: {{6}}fantasy{{/6}};
371
+ {{7}}size{{/7}}: {{8}}1em + (2em * 3){{/8}};
372
+ }
373
+ }
374
+ SCSS
375
+ {{1}}a{{/1}} {
376
+ {{2}}/* SOME COMMENT */{{/2}}
377
+ {{3}}font{{/3}}: {{4}}2px/3px{{/4}};
378
+ {{5}}font-family{{/5}}: {{6}}fantasy{{/6}};
379
+ {{7}}font-size{{/7}}: {{8}}7em{{/8}}; }
380
+ {{9}}a .special{{/9}} {
381
+ {{10}}color{{/10}}: {{11}}red{{/11}}; }
382
+ {{12}}a .special:hover{{/12}} {
383
+ {{13}}foo{{/13}}: {{14}}bar{{/14}};
384
+ {{15}}cursor{{/15}}: {{16}}e-resize{{/16}};
385
+ {{17}}color{{/17}}: {{18}}rgba(255, 0, 0, 0.8){{/18}}; }
386
+ {{19}}a .special:after{{/19}} {
387
+ {{20}}content{{/20}}: {{21}}"I ate 15 pies 2px thick!"{{/21}}; }
388
+ {{22}}a:active{{/22}} {
389
+ {{23}}color{{/23}}: {{24}}#050709{{/24}};
390
+ {{25}}border{{/25}}: {{26}}2px solid black{{/26}}; }
391
+
392
+ /*# sourceMappingURL=test.css.map */
393
+ CSS
394
+ end
395
+
396
+ def test_selectors_properties_sourcemap_sass
397
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
398
+ $width: 2px
399
+ $translucent-red: rgba(255, 0, 0, 0.5)
400
+ {{1}}a{{/1}}
401
+ {{9}}.special{{/9}}
402
+ {{10}}color{{/10}}: {{11}}red{{/11}}
403
+ {{12}}&:hover{{/12}}
404
+ {{13}}foo{{/13}}: {{14}}bar{{/14}}
405
+ {{15}}cursor{{/15}}: {{16}}e + -resize{{/16}}
406
+ {{17}}color{{/17}}: {{18}}opacify($translucent-red, 0.3){{/18}}
407
+ {{19}}&:after{{/19}}
408
+ {{20}}content{{/20}}: {{21}}"I ate #{5 + 10} pies #{$width} thick!"{{/21}}
409
+ {{22}}&:active{{/22}}
410
+ {{23}}color{{/23}}: {{24}}#010203 + #040506{{/24}}
411
+ {{25}}border{{/25}}: {{26}}$width solid black{{/26}}
412
+
413
+ {{2}}/* SOME COMMENT */{{/2}}
414
+ {{3}}font{{/3}}: {{4}}2px/3px{{/4}}
415
+ {{5}}family{{/5}}: {{6}}fantasy{{/6}}
416
+ {{7}}size{{/7}}: {{8}}1em + (2em * 3){{/8}}
417
+ SASS
418
+ {{1}}a{{/1}} {
419
+ {{2}}/* SOME COMMENT */{{/2}}
420
+ {{3}}font{{/3}}: {{4}}2px/3px{{/4}};
421
+ {{5}}font-family{{/5}}: {{6}}fantasy{{/6}};
422
+ {{7}}font-size{{/7}}: {{8}}7em{{/8}}; }
423
+ {{9}}a .special{{/9}} {
424
+ {{10}}color{{/10}}: {{11}}red{{/11}}; }
425
+ {{12}}a .special:hover{{/12}} {
426
+ {{13}}foo{{/13}}: {{14}}bar{{/14}};
427
+ {{15}}cursor{{/15}}: {{16}}e-resize{{/16}};
428
+ {{17}}color{{/17}}: {{18}}rgba(255, 0, 0, 0.8){{/18}}; }
429
+ {{19}}a .special:after{{/19}} {
430
+ {{20}}content{{/20}}: {{21}}"I ate 15 pies 2px thick!"{{/21}}; }
431
+ {{22}}a:active{{/22}} {
432
+ {{23}}color{{/23}}: {{24}}#050709{{/24}};
433
+ {{25}}border{{/25}}: {{26}}2px solid black{{/26}}; }
434
+
435
+ /*# sourceMappingURL=test.css.map */
436
+ CSS
437
+ end
438
+
439
+ def test_extend_sourcemap_scss
440
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
441
+ {{1}}.error {{/1}}{
442
+ {{2}}border{{/2}}: {{3}}1px #ff00aa{{/3}};
443
+ {{4}}background-color{{/4}}: {{5}}#fdd{{/5}};
444
+ }
445
+ {{6}}.seriousError {{/6}}{
446
+ @extend .error;
447
+ {{7}}border-width{{/7}}: {{8}}3px{{/8}};
448
+ }
449
+ SCSS
450
+ {{1}}.error, .seriousError{{/1}} {
451
+ {{2}}border{{/2}}: {{3}}1px #ff00aa{{/3}};
452
+ {{4}}background-color{{/4}}: {{5}}#fdd{{/5}}; }
453
+
454
+ {{6}}.seriousError{{/6}} {
455
+ {{7}}border-width{{/7}}: {{8}}3px{{/8}}; }
456
+
457
+ /*# sourceMappingURL=test.css.map */
458
+ CSS
459
+ end
460
+
461
+ def test_extend_sourcemap_sass
462
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
463
+ {{1}}.error{{/1}}
464
+ {{2}}border{{/2}}: {{3}}1px #f00{{/3}}
465
+ {{4}}background-color{{/4}}: {{5}}#fdd{{/5}}
466
+
467
+ {{6}}.seriousError{{/6}}
468
+ @extend .error
469
+ {{7}}border-width{{/7}}: {{8}}3px{{/8}}
470
+ SASS
471
+ {{1}}.error, .seriousError{{/1}} {
472
+ {{2}}border{{/2}}: {{3}}1px #f00{{/3}};
473
+ {{4}}background-color{{/4}}: {{5}}#fdd{{/5}}; }
474
+
475
+ {{6}}.seriousError{{/6}} {
476
+ {{7}}border-width{{/7}}: {{8}}3px{{/8}}; }
477
+
478
+ /*# sourceMappingURL=test.css.map */
479
+ CSS
480
+ end
481
+
482
+ def test_for_sourcemap_scss
483
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
484
+ @for $i from 1 through 3 {
485
+ {{1}}{{4}}{{7}}.item-#{$i} {{/1}}{{/4}}{{/7}}{ {{2}}{{5}}{{8}}width{{/2}}{{/5}}{{/8}}: {{3}}{{6}}{{9}}2em * $i{{/3}}{{/6}}{{/9}}; }
486
+ }
487
+ SCSS
488
+ {{1}}.item-1{{/1}} {
489
+ {{2}}width{{/2}}: {{3}}2em{{/3}}; }
490
+
491
+ {{4}}.item-2{{/4}} {
492
+ {{5}}width{{/5}}: {{6}}4em{{/6}}; }
493
+
494
+ {{7}}.item-3{{/7}} {
495
+ {{8}}width{{/8}}: {{9}}6em{{/9}}; }
496
+
497
+ /*# sourceMappingURL=test.css.map */
498
+ CSS
499
+ end
500
+
501
+ def test_for_sourcemap_sass
502
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
503
+ @for $i from 1 through 3
504
+ {{1}}{{4}}{{7}}.item-#{$i}{{/1}}{{/4}}{{/7}}
505
+ {{2}}{{5}}{{8}}width{{/2}}{{/5}}{{/8}}: {{3}}{{6}}{{9}}2em * $i{{/3}}{{/6}}{{/9}}
506
+ SASS
507
+ {{1}}.item-1{{/1}} {
508
+ {{2}}width{{/2}}: {{3}}2em{{/3}}; }
509
+
510
+ {{4}}.item-2{{/4}} {
511
+ {{5}}width{{/5}}: {{6}}4em{{/6}}; }
512
+
513
+ {{7}}.item-3{{/7}} {
514
+ {{8}}width{{/8}}: {{9}}6em{{/9}}; }
515
+
516
+ /*# sourceMappingURL=test.css.map */
517
+ CSS
518
+ end
519
+
520
+ def test_while_sourcemap_scss
521
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
522
+ $i: 6;
523
+ @while $i > 0 {
524
+ {{1}}{{4}}{{7}}.item-#{$i} {{/1}}{{/4}}{{/7}}{ {{2}}{{5}}{{8}}width{{/2}}{{/5}}{{/8}}: {{3}}{{6}}{{9}}2em * $i{{/3}}{{/6}}{{/9}}; }
525
+ $i: $i - 2 !global;
526
+ }
527
+ SCSS
528
+ {{1}}.item-6{{/1}} {
529
+ {{2}}width{{/2}}: {{3}}12em{{/3}}; }
530
+
531
+ {{4}}.item-4{{/4}} {
532
+ {{5}}width{{/5}}: {{6}}8em{{/6}}; }
533
+
534
+ {{7}}.item-2{{/7}} {
535
+ {{8}}width{{/8}}: {{9}}4em{{/9}}; }
536
+
537
+ /*# sourceMappingURL=test.css.map */
538
+ CSS
539
+ end
540
+
541
+ def test_while_sourcemap_sass
542
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
543
+ $i: 6
544
+ @while $i > 0
545
+ {{1}}{{4}}{{7}}.item-#{$i}{{/1}}{{/4}}{{/7}}
546
+ {{2}}{{5}}{{8}}width{{/2}}{{/5}}{{/8}}: {{3}}{{6}}{{9}}2em * $i{{/3}}{{/6}}{{/9}}
547
+ $i: $i - 2 !global
548
+ SASS
549
+ {{1}}.item-6{{/1}} {
550
+ {{2}}width{{/2}}: {{3}}12em{{/3}}; }
551
+
552
+ {{4}}.item-4{{/4}} {
553
+ {{5}}width{{/5}}: {{6}}8em{{/6}}; }
554
+
555
+ {{7}}.item-2{{/7}} {
556
+ {{8}}width{{/8}}: {{9}}4em{{/9}}; }
557
+
558
+ /*# sourceMappingURL=test.css.map */
559
+ CSS
560
+ end
561
+
562
+ def test_each_sourcemap_scss
563
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
564
+ @each $animal in puma, sea-slug, egret, salamander {
565
+ {{1}}{{4}}{{7}}{{10}}.#{$animal}-icon {{/1}}{{/4}}{{/7}}{{/10}}{
566
+ {{2}}{{5}}{{8}}{{11}}background-image{{/2}}{{/5}}{{/8}}{{/11}}: {{3}}{{6}}{{9}}{{12}}url('/images/#{$animal}.png'){{/3}}{{/6}}{{/9}}{{/12}};
567
+ }
568
+ }
569
+ SCSS
570
+ {{1}}.puma-icon{{/1}} {
571
+ {{2}}background-image{{/2}}: {{3}}url("/images/puma.png"){{/3}}; }
572
+
573
+ {{4}}.sea-slug-icon{{/4}} {
574
+ {{5}}background-image{{/5}}: {{6}}url("/images/sea-slug.png"){{/6}}; }
575
+
576
+ {{7}}.egret-icon{{/7}} {
577
+ {{8}}background-image{{/8}}: {{9}}url("/images/egret.png"){{/9}}; }
578
+
579
+ {{10}}.salamander-icon{{/10}} {
580
+ {{11}}background-image{{/11}}: {{12}}url("/images/salamander.png"){{/12}}; }
581
+
582
+ /*# sourceMappingURL=test.css.map */
583
+ CSS
584
+ end
585
+
586
+ def test_each_sourcemap_sass
587
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
588
+ @each $animal in puma, sea-slug, egret, salamander
589
+ {{1}}{{4}}{{7}}{{10}}.#{$animal}-icon{{/1}}{{/4}}{{/7}}{{/10}}
590
+ {{2}}{{5}}{{8}}{{11}}background-image{{/2}}{{/5}}{{/8}}{{/11}}: {{3}}{{6}}{{9}}{{12}}url('/images/#{$animal}.png'){{/3}}{{/6}}{{/9}}{{/12}}
591
+ SASS
592
+ {{1}}.puma-icon{{/1}} {
593
+ {{2}}background-image{{/2}}: {{3}}url("/images/puma.png"){{/3}}; }
594
+
595
+ {{4}}.sea-slug-icon{{/4}} {
596
+ {{5}}background-image{{/5}}: {{6}}url("/images/sea-slug.png"){{/6}}; }
597
+
598
+ {{7}}.egret-icon{{/7}} {
599
+ {{8}}background-image{{/8}}: {{9}}url("/images/egret.png"){{/9}}; }
600
+
601
+ {{10}}.salamander-icon{{/10}} {
602
+ {{11}}background-image{{/11}}: {{12}}url("/images/salamander.png"){{/12}}; }
603
+
604
+ /*# sourceMappingURL=test.css.map */
605
+ CSS
606
+ end
607
+
608
+ def test_mixin_sourcemap_scss
609
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
610
+ @mixin large-text {
611
+ font: {
612
+ {{2}}size{{/2}}: {{3}}20px{{/3}};
613
+ {{4}}weight{{/4}}: {{5}}bold{{/5}};
614
+ }
615
+ {{6}}color{{/6}}: {{7}}#ff0000{{/7}};
616
+ }
617
+
618
+ {{1}}.page-title {{/1}}{
619
+ @include large-text;
620
+ {{8}}padding{{/8}}: {{9}}4px{{/9}};
621
+ }
622
+
623
+ @mixin dashed-border($color, $width: {{14}}1in{{/14}}) {
624
+ border: {
625
+ {{11}}{{18}}color{{/11}}{{/18}}: $color;
626
+ {{13}}{{20}}width{{/13}}{{/20}}: $width;
627
+ {{15}}{{22}}style{{/15}}{{/22}}: {{16}}{{23}}dashed{{/16}}{{/23}};
628
+ }
629
+ }
630
+
631
+ {{10}}p {{/10}}{ @include dashed-border({{12}}blue{{/12}}); }
632
+ {{17}}h1 {{/17}}{ @include dashed-border({{19}}blue{{/19}}, {{21}}2in{{/21}}); }
633
+
634
+ @mixin box-shadow($shadows...) {
635
+ {{25}}-moz-box-shadow{{/25}}: {{26}}$shadows{{/26}};
636
+ {{27}}-webkit-box-shadow{{/27}}: {{28}}$shadows{{/28}};
637
+ {{29}}box-shadow{{/29}}: {{30}}$shadows{{/30}};
638
+ }
639
+
640
+ {{24}}.shadows {{/24}}{
641
+ @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
642
+ }
643
+ SCSS
644
+ {{1}}.page-title{{/1}} {
645
+ {{2}}font-size{{/2}}: {{3}}20px{{/3}};
646
+ {{4}}font-weight{{/4}}: {{5}}bold{{/5}};
647
+ {{6}}color{{/6}}: {{7}}#ff0000{{/7}};
648
+ {{8}}padding{{/8}}: {{9}}4px{{/9}}; }
649
+
650
+ {{10}}p{{/10}} {
651
+ {{11}}border-color{{/11}}: {{12}}blue{{/12}};
652
+ {{13}}border-width{{/13}}: {{14}}1in{{/14}};
653
+ {{15}}border-style{{/15}}: {{16}}dashed{{/16}}; }
654
+
655
+ {{17}}h1{{/17}} {
656
+ {{18}}border-color{{/18}}: {{19}}blue{{/19}};
657
+ {{20}}border-width{{/20}}: {{21}}2in{{/21}};
658
+ {{22}}border-style{{/22}}: {{23}}dashed{{/23}}; }
659
+
660
+ {{24}}.shadows{{/24}} {
661
+ {{25}}-moz-box-shadow{{/25}}: {{26}}0px 4px 5px #666, 2px 6px 10px #999{{/26}};
662
+ {{27}}-webkit-box-shadow{{/27}}: {{28}}0px 4px 5px #666, 2px 6px 10px #999{{/28}};
663
+ {{29}}box-shadow{{/29}}: {{30}}0px 4px 5px #666, 2px 6px 10px #999{{/30}}; }
664
+
665
+ /*# sourceMappingURL=test.css.map */
666
+ CSS
667
+ end
668
+
669
+ def test_mixin_sourcemap_sass
670
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
671
+ =large-text
672
+ :font
673
+ {{2}}size{{/2}}: {{3}}20px{{/3}}
674
+ {{4}}weight{{/4}}: {{5}}bold{{/5}}
675
+ {{6}}color{{/6}}: {{7}}#ff0000{{/7}}
676
+
677
+ {{1}}.page-title{{/1}}
678
+ +large-text
679
+ {{8}}padding{{/8}}: {{9}}4px{{/9}}
680
+
681
+ =dashed-border($color, $width: {{14}}1in{{/14}})
682
+ border:
683
+ {{11}}{{18}}color{{/11}}{{/18}}: $color
684
+ {{13}}{{20}}width{{/13}}{{/20}}: $width
685
+ {{15}}{{22}}style{{/15}}{{/22}}: {{16}}{{23}}dashed{{/16}}{{/23}}
686
+
687
+ {{10}}p{{/10}}
688
+ +dashed-border({{12}}blue{{/12}})
689
+
690
+ {{17}}h1{{/17}}
691
+ +dashed-border({{19}}blue{{/19}}, {{21}}2in{{/21}})
692
+
693
+ =box-shadow($shadows...)
694
+ {{25}}-moz-box-shadow{{/25}}: {{26}}$shadows{{/26}}
695
+ {{27}}-webkit-box-shadow{{/27}}: {{28}}$shadows{{/28}}
696
+ {{29}}box-shadow{{/29}}: {{30}}$shadows{{/30}}
697
+
698
+ {{24}}.shadows{{/24}}
699
+ +box-shadow(0px 4px 5px #666, 2px 6px 10px #999)
700
+ SASS
701
+ {{1}}.page-title{{/1}} {
702
+ {{2}}font-size{{/2}}: {{3}}20px{{/3}};
703
+ {{4}}font-weight{{/4}}: {{5}}bold{{/5}};
704
+ {{6}}color{{/6}}: {{7}}#ff0000{{/7}};
705
+ {{8}}padding{{/8}}: {{9}}4px{{/9}}; }
706
+
707
+ {{10}}p{{/10}} {
708
+ {{11}}border-color{{/11}}: {{12}}blue{{/12}};
709
+ {{13}}border-width{{/13}}: {{14}}1in{{/14}};
710
+ {{15}}border-style{{/15}}: {{16}}dashed{{/16}}; }
711
+
712
+ {{17}}h1{{/17}} {
713
+ {{18}}border-color{{/18}}: {{19}}blue{{/19}};
714
+ {{20}}border-width{{/20}}: {{21}}2in{{/21}};
715
+ {{22}}border-style{{/22}}: {{23}}dashed{{/23}}; }
716
+
717
+ {{24}}.shadows{{/24}} {
718
+ {{25}}-moz-box-shadow{{/25}}: {{26}}0px 4px 5px #666, 2px 6px 10px #999{{/26}};
719
+ {{27}}-webkit-box-shadow{{/27}}: {{28}}0px 4px 5px #666, 2px 6px 10px #999{{/28}};
720
+ {{29}}box-shadow{{/29}}: {{30}}0px 4px 5px #666, 2px 6px 10px #999{{/30}}; }
721
+
722
+ /*# sourceMappingURL=test.css.map */
723
+ CSS
724
+ end
725
+
726
+ def test_function_sourcemap_scss
727
+ assert_parses_with_mapping <<'SCSS', <<'CSS'
728
+ $grid-width: 20px;
729
+ $gutter-width: 5px;
730
+
731
+ @function grid-width($n) {
732
+ @return $n * $grid-width + ($n - 1) * $gutter-width;
733
+ }
734
+ {{1}}sidebar {{/1}}{ {{2}}width{{/2}}: {{3}}grid-width(5){{/3}}; }
735
+ SCSS
736
+ {{1}}sidebar{{/1}} {
737
+ {{2}}width{{/2}}: {{3}}120px{{/3}}; }
738
+
739
+ /*# sourceMappingURL=test.css.map */
740
+ CSS
741
+ end
742
+
743
+ def test_function_sourcemap_sass
744
+ assert_parses_with_mapping <<'SASS', <<'CSS', :syntax => :sass
745
+ $grid-width: 20px
746
+ $gutter-width: 5px
747
+
748
+ @function grid-width($n)
749
+ @return $n * $grid-width + ($n - 1) * $gutter-width
750
+
751
+ {{1}}sidebar{{/1}}
752
+ {{2}}width{{/2}}: {{3}}grid-width(5){{/3}}
753
+ SASS
754
+ {{1}}sidebar{{/1}} {
755
+ {{2}}width{{/2}}: {{3}}120px{{/3}}; }
756
+
757
+ /*# sourceMappingURL=test.css.map */
758
+ CSS
759
+ end
760
+
761
+ # Regression tests
762
+
763
+ def test_properties_sass
764
+ assert_parses_with_mapping <<SASS, <<CSS, :syntax => :sass
765
+ {{1}}.foo{{/1}}
766
+ :{{2}}name{{/2}} {{3}}value{{/3}}
767
+ {{4}}name{{/4}}: {{5}}value{{/5}}
768
+ :{{6}}name{{/6}} {{7}}value{{/7}}
769
+ {{8}}name{{/8}}: {{9}}value{{/9}}
770
+ SASS
771
+ {{1}}.foo{{/1}} {
772
+ {{2}}name{{/2}}: {{3}}value{{/3}};
773
+ {{4}}name{{/4}}: {{5}}value{{/5}};
774
+ {{6}}name{{/6}}: {{7}}value{{/7}};
775
+ {{8}}name{{/8}}: {{9}}value{{/9}}; }
776
+
777
+ /*# sourceMappingURL=test.css.map */
778
+ CSS
779
+ end
780
+
781
+ def test_multiline_script_scss
782
+ assert_parses_with_mapping <<SCSS, <<CSS, :syntax => :scss
783
+ $var: {{3}}foo +
784
+ bar{{/3}}; {{1}}x {{/1}}{ {{2}}y{{/2}}: $var }
785
+ SCSS
786
+ {{1}}x{{/1}} {
787
+ {{2}}y{{/2}}: {{3}}foobar{{/3}}; }
788
+
789
+ /*# sourceMappingURL=test.css.map */
790
+ CSS
791
+ end
792
+
793
+ def test_multiline_interpolation_source_range
794
+ engine = Sass::Engine.new(<<-SCSS, :cache => false, :syntax => :scss)
795
+ p {
796
+ filter: progid:DXImageTransform(
797
+ '\#{123}');
798
+ }
799
+ SCSS
800
+
801
+ interpolated = engine.to_tree.children.
802
+ first.children.
803
+ first.value.contents[1]
804
+ assert_equal "123", interpolated.to_sass
805
+ range = interpolated.source_range
806
+ assert_equal 3, range.start_pos.line
807
+ assert_equal 14, range.start_pos.offset
808
+ assert_equal 3, range.end_pos.line
809
+ assert_equal 17, range.end_pos.offset
810
+ end
811
+
812
+ def test_list_source_range
813
+ engine = Sass::Engine.new(<<-SCSS, :cache => false, :syntax => :scss)
814
+ @each $a, $b in (1, 2), (2, 4), (3, 6) { }
815
+ SCSS
816
+ list = engine.to_tree.children.first.list
817
+ assert_equal 1, list.source_range.start_pos.line
818
+ assert_equal 1, list.source_range.end_pos.line
819
+ assert_equal 16, list.source_range.start_pos.offset
820
+ assert_equal 38, list.source_range.end_pos.offset
821
+ end
822
+
823
+ def test_sources_array_is_uri_escaped
824
+ map = Sass::Source::Map.new
825
+ importer = Sass::Importers::Filesystem.new('.')
826
+ map.add(
827
+ Sass::Source::Range.new(
828
+ Sass::Source::Position.new(0, 0),
829
+ Sass::Source::Position.new(0, 10),
830
+ 'source file.scss',
831
+ importer),
832
+ Sass::Source::Range.new(
833
+ Sass::Source::Position.new(0, 0),
834
+ Sass::Source::Position.new(0, 10),
835
+ nil, nil))
836
+
837
+ json = map.to_json(:css_path => 'output file.css', :sourcemap_path => 'output file.css.map')
838
+ assert_equal json, <<JSON.rstrip
839
+ {
840
+ "version": 3,
841
+ "mappings": "DADD,UAAU",
842
+ "sources": ["source%20file.scss"],
843
+ "names": [],
844
+ "file": "output%20file.css"
845
+ }
846
+ JSON
847
+ end
848
+
849
+ def test_scss_comment_source_range
850
+ assert_parses_with_mapping <<SCSS, <<CSS, :syntax => :scss
851
+ $var: val; {{1}}/* text */{{/1}}
852
+
853
+ {{2}}/* multiline
854
+ comment */{{/2}}
855
+ SCSS
856
+ {{1}}/* text */{{/1}}
857
+ {{2}}/* multiline
858
+ comment */{{/2}}
859
+
860
+ /*# sourceMappingURL=test.css.map */
861
+ CSS
862
+ end
863
+
864
+ def test_sass_comment_source_range
865
+ assert_parses_with_mapping <<SASS, <<CSS, :syntax => :sass
866
+ {{1}}body{{/1}}
867
+ {{2}}/* text */{{/2}}
868
+
869
+ {{3}}/* multiline
870
+ comment */{{/3}}
871
+ SASS
872
+ {{1}}body{{/1}} {
873
+ {{2}}/* text */{{/2}} }
874
+
875
+ {{3}}/* multiline
876
+ * comment */{{/3}}
877
+
878
+ /*# sourceMappingURL=test.css.map */
879
+ CSS
880
+ end
881
+
882
+ def test_scss_comment_interpolation_source_range
883
+ assert_parses_with_mapping <<SCSS, <<CSS, :syntax => :scss
884
+ $var: 2; {{1}}/* two \#{$var} and four \#{2 * $var} */{{/1}}
885
+
886
+ {{2}}/* multiline
887
+ comment \#{ 2 + 2 } and \#{ 2 +
888
+ 2 } */{{/2}}
889
+ SCSS
890
+ {{1}}/* two 2 and four 4 */{{/1}}
891
+ {{2}}/* multiline
892
+ comment 4 and 4 */{{/2}}
893
+
894
+ /*# sourceMappingURL=test.css.map */
895
+ CSS
896
+ end
897
+
898
+ def test_sass_comment_interpolation_source_range
899
+ assert_parses_with_mapping <<SASS, <<CSS, :syntax => :sass
900
+ $var: 2
901
+ {{1}}/* two \#{$var} and four \#{2 * $var} */{{/1}}
902
+
903
+ {{2}}/* multiline
904
+ comment \#{ 2 + 2 } and \#{ 2 +
905
+ 2 } */{{/2}}
906
+ SASS
907
+ {{1}}/* two 2 and four 4 */{{/1}}
908
+ {{2}}/* multiline
909
+ * comment 4 and 4 */{{/2}}
910
+
911
+ /*# sourceMappingURL=test.css.map */
912
+ CSS
913
+ end
914
+
915
+ private
916
+
917
+ ANNOTATION_REGEX = /\{\{(\/?)([^}]+)\}\}/
918
+
919
+ def build_ranges(text, file_name = nil)
920
+ ranges = Hash.new {|h, k| h[k] = []}
921
+ start_positions = {}
922
+ text.split("\n").each_with_index do |line_text, line|
923
+ line += 1 # lines shoud be 1-based
924
+ while (match = line_text.match(ANNOTATION_REGEX))
925
+ closing = !match[1].empty?
926
+ name = match[2]
927
+ match_offsets = match.offset(0)
928
+ offset = match_offsets[0] + 1 # Offsets are 1-based in source maps.
929
+ assert(!closing || start_positions[name], "Closing annotation #{name} found before opening one.")
930
+ position = Sass::Source::Position.new(line, offset)
931
+ if closing
932
+ ranges[name] << Sass::Source::Range.new(
933
+ start_positions[name], position, file_name,
934
+ Sass::Importers::Filesystem.new('.'))
935
+ start_positions.delete name
936
+ else
937
+ assert(!start_positions[name], "Overlapping range annotation #{name} encountered on line #{line}")
938
+ start_positions[name] = position
939
+ end
940
+ line_text.slice!(match_offsets[0], match_offsets[1] - match_offsets[0])
941
+ end
942
+ end
943
+ ranges
944
+ end
945
+
946
+ def build_mapping_from_annotations(source, css, source_file_name)
947
+ source_ranges = build_ranges(source, source_file_name)
948
+ target_ranges = build_ranges(css)
949
+ map = Sass::Source::Map.new
950
+ source_ranges.map do |(name, sources)|
951
+ assert(sources.length == 1, "#{sources.length} source ranges encountered for annotation #{name}")
952
+ assert(target_ranges[name], "No target ranges for annotation #{name}")
953
+ target_ranges[name].map {|target_range| [sources.first, target_range]}
954
+ end.
955
+ flatten(1).
956
+ sort_by {|(_, target)| [target.start_pos.line, target.start_pos.offset]}.
957
+ each {|(s2, target)| map.add(s2, target)}
958
+ map
959
+ end
960
+
961
+ def assert_parses_with_mapping(source, css, options={})
962
+ options[:syntax] ||= :scss
963
+ input_filename = filename_for_test(options[:syntax])
964
+ mapping = build_mapping_from_annotations(source, css, input_filename)
965
+ source.gsub!(ANNOTATION_REGEX, "")
966
+ css.gsub!(ANNOTATION_REGEX, "")
967
+ rendered, sourcemap = render_with_sourcemap(source, options)
968
+ assert_equal css.rstrip, rendered.rstrip
969
+ assert_sourcemaps_equal source, css, mapping, sourcemap
970
+ end
971
+
972
+ def assert_positions_equal(expected, actual, lines, message = nil)
973
+ prefix = message ? message + ": " : ""
974
+ expected_location = lines[expected.line - 1] + "\n" + ("-" * (expected.offset - 1)) + "^"
975
+ actual_location = lines[actual.line - 1] + "\n" + ("-" * (actual.offset - 1)) + "^"
976
+ assert_equal(expected.line, actual.line, prefix +
977
+ "Expected #{expected.inspect}\n" +
978
+ expected_location + "\n\n" +
979
+ "But was #{actual.inspect}\n" +
980
+ actual_location)
981
+ assert_equal(expected.offset, actual.offset, prefix +
982
+ "Expected #{expected.inspect}\n" +
983
+ expected_location + "\n\n" +
984
+ "But was #{actual.inspect}\n" +
985
+ actual_location)
986
+ end
987
+
988
+ def assert_ranges_equal(expected, actual, lines, prefix)
989
+ assert_positions_equal(expected.start_pos, actual.start_pos, lines, prefix + " start position")
990
+ assert_positions_equal(expected.end_pos, actual.end_pos, lines, prefix + " end position")
991
+ assert_equal(expected.file, actual.file)
992
+ end
993
+
994
+ def assert_sourcemaps_equal(source, css, expected, actual)
995
+ assert_equal(expected.data.length, actual.data.length, <<MESSAGE)
996
+ Wrong number of mappings. Expected:
997
+ #{dump_sourcemap_as_expectation(source, css, expected).gsub(/^/, '| ')}
998
+
999
+ Actual:
1000
+ #{dump_sourcemap_as_expectation(source, css, actual).gsub(/^/, '| ')}
1001
+ MESSAGE
1002
+ source_lines = source.split("\n")
1003
+ css_lines = css.split("\n")
1004
+ expected.data.zip(actual.data) do |expected_mapping, actual_mapping|
1005
+ assert_ranges_equal(expected_mapping.input, actual_mapping.input, source_lines, "Input")
1006
+ assert_ranges_equal(expected_mapping.output, actual_mapping.output, css_lines, "Output")
1007
+ end
1008
+ end
1009
+
1010
+ def assert_parses_with_sourcemap(source, css, sourcemap_json, options={})
1011
+ rendered, sourcemap = render_with_sourcemap(source, options)
1012
+ css_path = options[:output] || "test.css"
1013
+ sourcemap_path = Sass::Util.sourcemap_name(css_path)
1014
+ rendered_json = sourcemap.to_json(:css_path => css_path, :sourcemap_path => sourcemap_path, :type => options[:sourcemap])
1015
+
1016
+ assert_equal css.rstrip, rendered.rstrip
1017
+ assert_equal sourcemap_json.rstrip, rendered_json
1018
+ end
1019
+
1020
+ def render_with_sourcemap(source, options={})
1021
+ options[:syntax] ||= :scss
1022
+ munge_filename options
1023
+ engine = Sass::Engine.new(source, options)
1024
+ engine.options[:cache] = false
1025
+ sourcemap_path = Sass::Util.sourcemap_name(options[:output] || "test.css")
1026
+ engine.render_with_sourcemap File.basename(sourcemap_path)
1027
+ end
1028
+
1029
+ def dump_sourcemap_as_expectation(source, css, sourcemap)
1030
+ mappings_to_annotations(source, sourcemap.data.map {|d| d.input}) + "\n\n" +
1031
+ "=" * 20 + " maps to:\n\n" +
1032
+ mappings_to_annotations(css, sourcemap.data.map {|d| d.output})
1033
+ end
1034
+
1035
+ def mappings_to_annotations(source, ranges)
1036
+ additional_offsets = Hash.new(0)
1037
+ lines = source.split("\n")
1038
+
1039
+ add_annotation = lambda do |pos, str|
1040
+ line_num = pos.line - 1
1041
+ line = lines[line_num]
1042
+ offset = pos.offset + additional_offsets[line_num] - 1
1043
+ line << " " * (offset - line.length) if offset > line.length
1044
+ line.insert(offset, str)
1045
+ additional_offsets[line_num] += str.length
1046
+ end
1047
+
1048
+ ranges.each_with_index do |range, i|
1049
+ add_annotation[range.start_pos, "{{#{i + 1}}}"]
1050
+ add_annotation[range.end_pos, "{{/#{i + 1}}}"]
1051
+ end
1052
+
1053
+ return lines.join("\n")
1054
+ end
1055
+ end