sass 3.4.0 → 3.4.25
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.
- checksums.yaml +4 -4
- data/.yardopts +3 -1
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +148 -0
- data/MIT-LICENSE +1 -1
- data/README.md +26 -20
- data/Rakefile +103 -20
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/extra/sass-spec-ref.sh +32 -0
- data/extra/update_watch.rb +1 -1
- data/lib/sass/cache_stores/filesystem.rb +7 -7
- data/lib/sass/cache_stores/memory.rb +4 -5
- data/lib/sass/callbacks.rb +2 -2
- data/lib/sass/css.rb +11 -10
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +83 -38
- data/lib/sass/environment.rb +26 -2
- data/lib/sass/error.rb +12 -12
- data/lib/sass/exec/base.rb +15 -3
- data/lib/sass/exec/sass_convert.rb +34 -15
- data/lib/sass/exec/sass_scss.rb +23 -7
- data/lib/sass/features.rb +2 -2
- data/lib/sass/importers/base.rb +1 -1
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +24 -16
- data/lib/sass/importers.rb +1 -0
- data/lib/sass/logger/base.rb +8 -2
- data/lib/sass/logger/delayed.rb +50 -0
- data/lib/sass/logger.rb +8 -3
- data/lib/sass/plugin/compiler.rb +42 -25
- data/lib/sass/plugin/configuration.rb +38 -22
- data/lib/sass/plugin/merb.rb +2 -2
- data/lib/sass/plugin/rack.rb +3 -3
- data/lib/sass/plugin/rails.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +3 -3
- data/lib/sass/plugin.rb +3 -2
- data/lib/sass/script/css_parser.rb +2 -3
- data/lib/sass/script/css_variable_warning.rb +52 -0
- data/lib/sass/script/functions.rb +140 -73
- data/lib/sass/script/lexer.rb +37 -22
- data/lib/sass/script/parser.rb +235 -40
- data/lib/sass/script/tree/funcall.rb +12 -5
- data/lib/sass/script/tree/interpolation.rb +109 -4
- data/lib/sass/script/tree/list_literal.rb +31 -4
- data/lib/sass/script/tree/literal.rb +4 -0
- data/lib/sass/script/tree/node.rb +21 -3
- data/lib/sass/script/tree/operation.rb +54 -1
- data/lib/sass/script/tree/string_interpolation.rb +58 -37
- data/lib/sass/script/tree/variable.rb +1 -1
- data/lib/sass/script/value/base.rb +10 -9
- data/lib/sass/script/value/color.rb +42 -24
- data/lib/sass/script/value/helpers.rb +16 -6
- data/lib/sass/script/value/map.rb +1 -1
- data/lib/sass/script/value/number.rb +52 -19
- data/lib/sass/script/value/string.rb +46 -5
- data/lib/sass/script.rb +3 -3
- data/lib/sass/scss/css_parser.rb +16 -2
- data/lib/sass/scss/parser.rb +120 -75
- data/lib/sass/scss/rx.rb +9 -10
- data/lib/sass/scss/static_parser.rb +19 -14
- data/lib/sass/scss.rb +0 -2
- data/lib/sass/selector/abstract_sequence.rb +8 -6
- data/lib/sass/selector/comma_sequence.rb +25 -9
- data/lib/sass/selector/pseudo.rb +45 -35
- data/lib/sass/selector/sequence.rb +54 -18
- data/lib/sass/selector/simple.rb +11 -11
- data/lib/sass/selector/simple_sequence.rb +34 -15
- data/lib/sass/selector.rb +7 -10
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +7 -4
- data/lib/sass/source/position.rb +4 -4
- data/lib/sass/stack.rb +2 -2
- data/lib/sass/supports.rb +8 -10
- data/lib/sass/tree/comment_node.rb +1 -1
- data/lib/sass/tree/css_import_node.rb +9 -1
- data/lib/sass/tree/function_node.rb +8 -3
- data/lib/sass/tree/import_node.rb +6 -5
- data/lib/sass/tree/node.rb +5 -3
- data/lib/sass/tree/prop_node.rb +5 -6
- data/lib/sass/tree/rule_node.rb +14 -4
- data/lib/sass/tree/visitors/check_nesting.rb +18 -22
- data/lib/sass/tree/visitors/convert.rb +43 -26
- data/lib/sass/tree/visitors/cssize.rb +5 -1
- data/lib/sass/tree/visitors/deep_copy.rb +1 -1
- data/lib/sass/tree/visitors/extend.rb +15 -13
- data/lib/sass/tree/visitors/perform.rb +42 -17
- data/lib/sass/tree/visitors/set_options.rb +1 -1
- data/lib/sass/tree/visitors/to_css.rb +58 -30
- data/lib/sass/util/multibyte_string_scanner.rb +0 -2
- data/lib/sass/util/normalized_map.rb +0 -1
- data/lib/sass/util/subset_map.rb +1 -2
- data/lib/sass/util.rb +125 -68
- data/lib/sass/version.rb +2 -2
- data/lib/sass.rb +10 -3
- data/test/sass/compiler_test.rb +6 -2
- data/test/sass/conversion_test.rb +187 -53
- data/test/sass/css2sass_test.rb +50 -1
- data/test/sass/css_variable_test.rb +132 -0
- data/test/sass/engine_test.rb +207 -61
- data/test/sass/exec_test.rb +10 -0
- data/test/sass/extend_test.rb +101 -29
- data/test/sass/functions_test.rb +60 -9
- data/test/sass/importer_test.rb +9 -0
- data/test/sass/more_templates/more1.sass +10 -10
- data/test/sass/more_templates/more_import.sass +2 -2
- data/test/sass/plugin_test.rb +10 -8
- data/test/sass/results/script.css +3 -3
- data/test/sass/script_conversion_test.rb +58 -29
- data/test/sass/script_test.rb +430 -53
- data/test/sass/scss/css_test.rb +73 -7
- data/test/sass/scss/rx_test.rb +4 -0
- data/test/sass/scss/scss_test.rb +309 -4
- data/test/sass/source_map_test.rb +152 -74
- data/test/sass/superselector_test.rb +19 -0
- data/test/sass/templates/_partial.sass +1 -1
- data/test/sass/templates/basic.sass +10 -10
- data/test/sass/templates/bork1.sass +1 -1
- data/test/sass/templates/bork5.sass +1 -1
- data/test/sass/templates/compact.sass +10 -10
- data/test/sass/templates/complex.sass +187 -187
- data/test/sass/templates/compressed.sass +10 -10
- data/test/sass/templates/expanded.sass +10 -10
- data/test/sass/templates/import.sass +2 -2
- data/test/sass/templates/importee.sass +3 -3
- data/test/sass/templates/mixins.sass +22 -22
- data/test/sass/templates/multiline.sass +4 -4
- data/test/sass/templates/nested.sass +13 -13
- data/test/sass/templates/parent_ref.sass +12 -12
- data/test/sass/templates/script.sass +70 -70
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
- data/test/sass/templates/subdir/subdir.sass +3 -3
- data/test/sass/templates/units.sass +10 -10
- data/test/sass/util/multibyte_string_scanner_test.rb +10 -2
- data/test/sass/util_test.rb +15 -44
- data/test/sass-spec.yml +3 -0
- data/test/test_helper.rb +5 -4
- metadata +302 -295
- data/CONTRIBUTING +0 -3
- data/lib/sass/scss/script_lexer.rb +0 -15
- data/lib/sass/scss/script_parser.rb +0 -25
data/lib/sass/engine.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'digest/sha1'
|
3
3
|
require 'sass/cache_stores'
|
4
|
+
require 'sass/deprecation'
|
4
5
|
require 'sass/source/position'
|
5
6
|
require 'sass/source/range'
|
6
7
|
require 'sass/source/map'
|
@@ -84,23 +85,25 @@ module Sass
|
|
84
85
|
# This class handles the parsing and compilation of the Sass template.
|
85
86
|
# Example usage:
|
86
87
|
#
|
87
|
-
# template = File.
|
88
|
+
# template = File.read('stylesheets/sassy.sass')
|
88
89
|
# sass_engine = Sass::Engine.new(template)
|
89
90
|
# output = sass_engine.render
|
90
91
|
# puts output
|
91
92
|
class Engine
|
93
|
+
@@old_property_deprecation = Deprecation.new
|
94
|
+
|
92
95
|
# A line of Sass code.
|
93
96
|
#
|
94
97
|
# `text`: `String`
|
95
98
|
# : The text in the line, without any whitespace at the beginning or end.
|
96
99
|
#
|
97
|
-
# `tabs`: `
|
100
|
+
# `tabs`: `Integer`
|
98
101
|
# : The level of indentation of the line.
|
99
102
|
#
|
100
|
-
# `index`: `
|
103
|
+
# `index`: `Integer`
|
101
104
|
# : The line number in the original document.
|
102
105
|
#
|
103
|
-
# `offset`: `
|
106
|
+
# `offset`: `Integer`
|
104
107
|
# : The number of bytes in on the line that the text begins.
|
105
108
|
# This ends up being the number of bytes of leading whitespace.
|
106
109
|
#
|
@@ -157,7 +160,7 @@ module Sass
|
|
157
160
|
# @api public
|
158
161
|
DEFAULT_OPTIONS = {
|
159
162
|
:style => :nested,
|
160
|
-
:load_paths => [
|
163
|
+
:load_paths => [],
|
161
164
|
:cache => true,
|
162
165
|
:cache_location => './.sass-cache',
|
163
166
|
:syntax => :sass,
|
@@ -168,11 +171,11 @@ module Sass
|
|
168
171
|
# default values and resolving aliases.
|
169
172
|
#
|
170
173
|
# @param options [{Symbol => Object}] The options hash;
|
171
|
-
# see {file:SASS_REFERENCE.md#
|
174
|
+
# see {file:SASS_REFERENCE.md#Options the Sass options documentation}
|
172
175
|
# @return [{Symbol => Object}] The normalized options hash.
|
173
176
|
# @private
|
174
177
|
def self.normalize_options(options)
|
175
|
-
options = DEFAULT_OPTIONS.merge(options.reject {|
|
178
|
+
options = DEFAULT_OPTIONS.merge(options.reject {|_k, v| v.nil?})
|
176
179
|
|
177
180
|
# If the `:filename` option is passed in without an importer,
|
178
181
|
# assume it's using the default filesystem importer.
|
@@ -192,6 +195,16 @@ module Sass
|
|
192
195
|
options[:filesystem_importer].new(p.to_s)
|
193
196
|
end
|
194
197
|
|
198
|
+
# Remove any deprecated importers if the location is imported explicitly
|
199
|
+
options[:load_paths].reject! do |importer|
|
200
|
+
importer.is_a?(Sass::Importers::DeprecatedPath) &&
|
201
|
+
options[:load_paths].find do |other_importer|
|
202
|
+
other_importer.is_a?(Sass::Importers::Filesystem) &&
|
203
|
+
other_importer != importer &&
|
204
|
+
other_importer.root == importer.root
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
195
208
|
# Backwards compatibility
|
196
209
|
options[:property_syntax] ||= options[:attribute_syntax]
|
197
210
|
case options[:property_syntax]
|
@@ -212,14 +225,14 @@ module Sass
|
|
212
225
|
#
|
213
226
|
# @param filename [String] The path to the Sass or SCSS file
|
214
227
|
# @param options [{Symbol => Object}] The options hash;
|
215
|
-
# See {file:SASS_REFERENCE.md#
|
228
|
+
# See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
216
229
|
# @return [Sass::Engine] The Engine for the given Sass or SCSS file.
|
217
230
|
# @raise [Sass::SyntaxError] if there's an error in the document.
|
218
231
|
def self.for_file(filename, options)
|
219
232
|
had_syntax = options[:syntax]
|
220
233
|
|
221
234
|
if had_syntax
|
222
|
-
# Use what was explicitly
|
235
|
+
# Use what was explicitly specified
|
223
236
|
elsif filename =~ /\.scss$/
|
224
237
|
options.merge!(:syntax => :scss)
|
225
238
|
elsif filename =~ /\.sass$/
|
@@ -230,7 +243,7 @@ module Sass
|
|
230
243
|
end
|
231
244
|
|
232
245
|
# The options for the Sass engine.
|
233
|
-
# See {file:SASS_REFERENCE.md#
|
246
|
+
# See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
234
247
|
#
|
235
248
|
# @return [{Symbol => Object}]
|
236
249
|
attr_reader :options
|
@@ -247,14 +260,17 @@ module Sass
|
|
247
260
|
# that can be converted to Unicode.
|
248
261
|
# If the template contains an `@charset` declaration,
|
249
262
|
# that overrides the Ruby encoding
|
250
|
-
# (see {file:SASS_REFERENCE.md#
|
263
|
+
# (see {file:SASS_REFERENCE.md#Encodings the encoding documentation})
|
251
264
|
# @param options [{Symbol => Object}] An options hash.
|
252
|
-
# See {file:SASS_REFERENCE.md#
|
265
|
+
# See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
253
266
|
# @see {Sass::Engine.for_file}
|
254
267
|
# @see {Sass::Plugin}
|
255
268
|
def initialize(template, options = {})
|
256
269
|
@options = self.class.normalize_options(options)
|
257
270
|
@template = template
|
271
|
+
@checked_encoding = false
|
272
|
+
@filename = nil
|
273
|
+
@line = nil
|
258
274
|
end
|
259
275
|
|
260
276
|
# Render the template to CSS.
|
@@ -466,7 +482,7 @@ ERR
|
|
466
482
|
|
467
483
|
line_tabs = line_tab_str.scan(tab_str).size
|
468
484
|
if tab_str * line_tabs != line_tab_str
|
469
|
-
message = <<END.strip.
|
485
|
+
message = <<END.strip.tr("\n", ' ')
|
470
486
|
Inconsistent indentation: #{Sass::Shared.human_indentation line_tab_str, true} used for indentation,
|
471
487
|
but the rest of the document was indented using #{Sass::Shared.human_indentation tab_str}.
|
472
488
|
END
|
@@ -479,15 +495,13 @@ END
|
|
479
495
|
end
|
480
496
|
|
481
497
|
# @comment
|
482
|
-
# rubocop:disable ParameterLists
|
483
498
|
def try_comment(line, last, tab_str, comment_tab_str, index)
|
484
|
-
# rubocop:enable ParameterLists
|
485
499
|
return unless last && last.comment?
|
486
500
|
# Nested comment stuff must be at least one whitespace char deeper
|
487
501
|
# than the normal indentation
|
488
502
|
return unless line =~ /^#{tab_str}\s/
|
489
503
|
unless line =~ /^(?:#{comment_tab_str})(.*)$/
|
490
|
-
raise SyntaxError.new(<<MSG.strip.
|
504
|
+
raise SyntaxError.new(<<MSG.strip.tr("\n", " "), :line => index)
|
491
505
|
Inconsistent indentation:
|
492
506
|
previous line was indented by #{Sass::Shared.human_indentation comment_tab_str},
|
493
507
|
but this line was indented by #{Sass::Shared.human_indentation line[/^\s*/]}.
|
@@ -564,8 +578,8 @@ MSG
|
|
564
578
|
if continued_comment &&
|
565
579
|
child.line == continued_comment.line +
|
566
580
|
continued_comment.lines + 1
|
567
|
-
continued_comment.value.last.sub!(
|
568
|
-
child.value.first.gsub!(
|
581
|
+
continued_comment.value.last.sub!(%r{ \*/\Z}, '')
|
582
|
+
child.value.first.gsub!(%r{\A/\*}, ' *')
|
569
583
|
continued_comment.value += ["\n"] + child.value
|
570
584
|
next
|
571
585
|
end
|
@@ -614,6 +628,11 @@ WARNING
|
|
614
628
|
raise SyntaxError.new("Invalid property: \"#{line.text}\".",
|
615
629
|
:line => @line) if name.nil? || value.nil?
|
616
630
|
|
631
|
+
@@old_property_deprecation.warn(@options[:filename], @line, <<WARNING)
|
632
|
+
Old-style properties like "#{line.text}" are deprecated and will be an error in future versions of Sass.
|
633
|
+
Use "#{name}: #{value}" instead.
|
634
|
+
WARNING
|
635
|
+
|
617
636
|
value_start_offset = name_end_offset = name_start_offset + name.length
|
618
637
|
unless value.empty?
|
619
638
|
# +1 and -1 both compensate for the leading ':', which is part of line.text
|
@@ -707,9 +726,12 @@ WARNING
|
|
707
726
|
expr = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
|
708
727
|
end_offset = start_offset
|
709
728
|
else
|
710
|
-
expr = parse_script(value,
|
729
|
+
expr = parse_script(value,
|
730
|
+
:offset => to_parser_offset(start_offset),
|
731
|
+
:css_variable => name.start_with?("--"))
|
711
732
|
end_offset = expr.source_range.end_pos.offset - 1
|
712
733
|
end
|
734
|
+
|
713
735
|
node = Tree::PropNode.new(parse_interp(name), expr, prop)
|
714
736
|
node.value_source_range = Sass::Source::Range.new(
|
715
737
|
Sass::Source::Position.new(line.index, to_parser_offset(start_offset)),
|
@@ -766,7 +788,19 @@ WARNING
|
|
766
788
|
else
|
767
789
|
:normal
|
768
790
|
end
|
769
|
-
Tree::CommentNode.new(value, type)
|
791
|
+
comment = Tree::CommentNode.new(value, type)
|
792
|
+
comment.line = line.index
|
793
|
+
text = line.text.rstrip
|
794
|
+
if text.include?("\n")
|
795
|
+
end_offset = text.length - text.rindex("\n")
|
796
|
+
else
|
797
|
+
end_offset = to_parser_offset(line.offset + text.length)
|
798
|
+
end
|
799
|
+
comment.source_range = Sass::Source::Range.new(
|
800
|
+
Sass::Source::Position.new(@line, to_parser_offset(line.offset)),
|
801
|
+
Sass::Source::Position.new(@line + text.count("\n"), end_offset),
|
802
|
+
@options[:filename])
|
803
|
+
comment
|
770
804
|
else
|
771
805
|
Tree::RuleNode.new(parse_interp(line.text), full_line_range(line))
|
772
806
|
end
|
@@ -776,14 +810,12 @@ WARNING
|
|
776
810
|
:each, :while, :if, :else, :extend, :import, :media, :charset, :content,
|
777
811
|
:at_root, :error]
|
778
812
|
|
779
|
-
# @comment
|
780
|
-
# rubocop:disable MethodLength
|
781
813
|
def parse_directive(parent, line, root)
|
782
814
|
directive, whitespace, value = line.text[1..-1].split(/(\s+)/, 2)
|
783
815
|
raise SyntaxError.new("Invalid directive: '@'.") unless directive
|
784
816
|
offset = directive.size + whitespace.size + 1 if whitespace
|
785
817
|
|
786
|
-
directive_name = directive.
|
818
|
+
directive_name = directive.tr('-', '_').to_sym
|
787
819
|
if DIRECTIVES.include?(directive_name)
|
788
820
|
return send("parse_#{directive_name}_directive", parent, line, root, value, offset)
|
789
821
|
end
|
@@ -838,8 +870,6 @@ WARNING
|
|
838
870
|
)
|
839
871
|
Tree::ExtendNode.new(interp_parsed, optional, selector_range)
|
840
872
|
end
|
841
|
-
# @comment
|
842
|
-
# rubocop:enable MethodLength
|
843
873
|
|
844
874
|
def parse_warn_directive(parent, line, root, value, offset)
|
845
875
|
raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
|
@@ -1003,12 +1033,21 @@ WARNING
|
|
1003
1033
|
end_pos = str.source_range.end_pos
|
1004
1034
|
node = Tree::CssImportNode.new(str)
|
1005
1035
|
else
|
1006
|
-
|
1036
|
+
supports_parser = Sass::SCSS::Parser.new(scanner,
|
1007
1037
|
@options[:filename], @options[:importer],
|
1008
1038
|
@line, str.source_range.end_pos.offset)
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1039
|
+
supports_condition = supports_parser.parse_supports_clause
|
1040
|
+
|
1041
|
+
if scanner.eos?
|
1042
|
+
node = Tree::CssImportNode.new(str, [], supports_condition)
|
1043
|
+
else
|
1044
|
+
media_parser = Sass::SCSS::Parser.new(scanner,
|
1045
|
+
@options[:filename], @options[:importer],
|
1046
|
+
@line, str.source_range.end_pos.offset)
|
1047
|
+
media = media_parser.parse_media_query_list
|
1048
|
+
end_pos = Sass::Source::Position.new(@line, media_parser.offset + 1)
|
1049
|
+
node = Tree::CssImportNode.new(str, media.to_a, supports_condition)
|
1050
|
+
end
|
1012
1051
|
end
|
1013
1052
|
|
1014
1053
|
node.source_range = Sass::Source::Range.new(
|
@@ -1115,9 +1154,9 @@ WARNING
|
|
1115
1154
|
end
|
1116
1155
|
|
1117
1156
|
def parse_script(script, options = {})
|
1118
|
-
line = options
|
1119
|
-
offset = options
|
1120
|
-
Script.parse(script, line, offset, @options)
|
1157
|
+
line = options.delete(:line) || @line
|
1158
|
+
offset = options.delete(:offset) || @offset + 1
|
1159
|
+
Script.parse(script, line, offset, @options.merge(options))
|
1121
1160
|
end
|
1122
1161
|
|
1123
1162
|
def format_comment_text(text, silent)
|
@@ -1129,9 +1168,10 @@ WARNING
|
|
1129
1168
|
end
|
1130
1169
|
|
1131
1170
|
return "/* */" if content.empty?
|
1132
|
-
content.last.gsub!(
|
1171
|
+
content.last.gsub!(%r{ ?\*/ *$}, '')
|
1172
|
+
first = content.shift unless removed_first
|
1133
1173
|
content.map! {|l| l.gsub!(/^\*( ?)/, '\1') || (l.empty? ? "" : " ") + l}
|
1134
|
-
content.first
|
1174
|
+
content.unshift first unless removed_first
|
1135
1175
|
if silent
|
1136
1176
|
"/*" + content.join("\n *") + " */"
|
1137
1177
|
else
|
@@ -1169,10 +1209,15 @@ WARNING
|
|
1169
1209
|
res << "\\" * (escapes - 1) << '#{'
|
1170
1210
|
else
|
1171
1211
|
res << "\\" * [0, escapes - 1].max
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1212
|
+
if scan[1].include?("\n")
|
1213
|
+
line += scan[1].count("\n")
|
1214
|
+
offset = scan.matched_size - scan[1].rindex("\n")
|
1215
|
+
else
|
1216
|
+
offset += scan.matched_size
|
1217
|
+
end
|
1218
|
+
node = Script::Parser.new(scan, line, offset, options).parse_interpolated
|
1219
|
+
offset = node.source_range.end_pos.offset
|
1220
|
+
res << node
|
1176
1221
|
end
|
1177
1222
|
end
|
1178
1223
|
res << rest
|
data/lib/sass/environment.rb
CHANGED
@@ -81,12 +81,19 @@ module Sass
|
|
81
81
|
inherited_hash_reader :function
|
82
82
|
|
83
83
|
# @param options [{Symbol => Object}] The options hash. See
|
84
|
-
# {file:SASS_REFERENCE.md#
|
84
|
+
# {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
85
85
|
# @param parent [Environment] See \{#parent}
|
86
86
|
def initialize(parent = nil, options = nil)
|
87
87
|
@parent = parent
|
88
88
|
@options = options || (parent && parent.options) || {}
|
89
|
-
@stack = Sass::Stack.new
|
89
|
+
@stack = @parent.nil? ? Sass::Stack.new : nil
|
90
|
+
@caller = nil
|
91
|
+
@content = nil
|
92
|
+
@filename = nil
|
93
|
+
@functions = nil
|
94
|
+
@mixins = nil
|
95
|
+
@selector = nil
|
96
|
+
@vars = nil
|
90
97
|
end
|
91
98
|
|
92
99
|
# Returns whether this is the global environment.
|
@@ -188,4 +195,21 @@ module Sass
|
|
188
195
|
@content ||= env.is_a?(ReadOnlyEnvironment) ? env : ReadOnlyEnvironment.new(env, env.options)
|
189
196
|
end
|
190
197
|
end
|
198
|
+
|
199
|
+
# An environment that can write to in-scope global variables, but doesn't
|
200
|
+
# create new variables in the global scope. Useful for top-level control
|
201
|
+
# directives.
|
202
|
+
class SemiGlobalEnvironment < Environment
|
203
|
+
def try_set_var(name, value)
|
204
|
+
@vars ||= {}
|
205
|
+
if @vars.include?(name)
|
206
|
+
@vars[name] = value
|
207
|
+
true
|
208
|
+
elsif @parent
|
209
|
+
@parent.try_set_var(name, value)
|
210
|
+
else
|
211
|
+
false
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
191
215
|
end
|
data/lib/sass/error.rb
CHANGED
@@ -69,14 +69,14 @@ module Sass
|
|
69
69
|
# The name of the mixin in which the error occurred.
|
70
70
|
# This could be `nil` if the error occurred outside a mixin.
|
71
71
|
#
|
72
|
-
# @return [
|
72
|
+
# @return [String]
|
73
73
|
def sass_mixin
|
74
74
|
sass_backtrace.first[:mixin]
|
75
75
|
end
|
76
76
|
|
77
77
|
# The line of the Sass template on which the error occurred.
|
78
78
|
#
|
79
|
-
# @return [
|
79
|
+
# @return [Integer]
|
80
80
|
def sass_line
|
81
81
|
sass_backtrace.first[:line]
|
82
82
|
end
|
@@ -86,7 +86,7 @@ module Sass
|
|
86
86
|
# @param attrs [{Symbol => Object}] The information in the backtrace entry.
|
87
87
|
# See \{#sass\_backtrace}
|
88
88
|
def add_backtrace(attrs)
|
89
|
-
sass_backtrace << attrs.reject {|
|
89
|
+
sass_backtrace << attrs.reject {|_k, v| v.nil?}
|
90
90
|
end
|
91
91
|
|
92
92
|
# Modify the top Sass backtrace entries
|
@@ -104,12 +104,12 @@ module Sass
|
|
104
104
|
# @param attrs [{Symbol => Object}] The information to add to the backtrace entry.
|
105
105
|
# See \{#sass\_backtrace}
|
106
106
|
def modify_backtrace(attrs)
|
107
|
-
attrs = attrs.reject {|
|
107
|
+
attrs = attrs.reject {|_k, v| v.nil?}
|
108
108
|
# Move backwards through the backtrace
|
109
|
-
(0...sass_backtrace.size).to_a.
|
109
|
+
(0...sass_backtrace.size).to_a.reverse_each do |i|
|
110
110
|
entry = sass_backtrace[i]
|
111
111
|
sass_backtrace[i] = attrs.merge(entry)
|
112
|
-
attrs.reject! {|k,
|
112
|
+
attrs.reject! {|k, _v| entry.include?(k)}
|
113
113
|
break if attrs.empty?
|
114
114
|
end
|
115
115
|
end
|
@@ -127,7 +127,7 @@ module Sass
|
|
127
127
|
return nil if super.nil?
|
128
128
|
return super if sass_backtrace.all? {|h| h.empty?}
|
129
129
|
sass_backtrace.map do |h|
|
130
|
-
"#{h[:filename] ||
|
130
|
+
"#{h[:filename] || '(sass)'}:#{h[:line]}" +
|
131
131
|
(h[:mixin] ? ":in `#{h[:mixin]}'" : "")
|
132
132
|
end + super
|
133
133
|
end
|
@@ -143,7 +143,7 @@ module Sass
|
|
143
143
|
map {|l| "\n" + (" " * "Error: ".size) + l}.join
|
144
144
|
"Error: #{msg}" +
|
145
145
|
Sass::Util.enum_with_index(sass_backtrace).map do |entry, i|
|
146
|
-
"\n #{i == 0 ?
|
146
|
+
"\n #{i == 0 ? 'on' : 'from'} line #{entry[:line]}" +
|
147
147
|
" of #{entry[:filename] || default_filename}" +
|
148
148
|
(entry[:mixin] ? ", in `#{entry[:mixin]}'" : "")
|
149
149
|
end.join
|
@@ -153,7 +153,7 @@ module Sass
|
|
153
153
|
# Returns an error report for an exception in CSS format.
|
154
154
|
#
|
155
155
|
# @param e [Exception]
|
156
|
-
# @param line_offset [
|
156
|
+
# @param line_offset [Integer] The number of the first line of the Sass template.
|
157
157
|
# @return [String] The error report
|
158
158
|
# @raise [Exception] `e`, if the
|
159
159
|
# {file:SASS_REFERENCE.md#full_exception-option `:full_exception`} option
|
@@ -163,9 +163,9 @@ module Sass
|
|
163
163
|
|
164
164
|
<<END
|
165
165
|
/*
|
166
|
-
#{header.gsub(
|
166
|
+
#{header.gsub('*/', '*\\/')}
|
167
167
|
|
168
|
-
Backtrace:\n#{e.backtrace.join("\n").gsub(
|
168
|
+
Backtrace:\n#{e.backtrace.join("\n").gsub('*/', '*\\/')}
|
169
169
|
*/
|
170
170
|
body:before {
|
171
171
|
white-space: pre;
|
@@ -183,7 +183,7 @@ END
|
|
183
183
|
|
184
184
|
line_num = e.sass_line + 1 - line_offset
|
185
185
|
min = [line_num - 6, 0].max
|
186
|
-
section = e.sass_template.rstrip.split("\n")[min
|
186
|
+
section = e.sass_template.rstrip.split("\n")[min...line_num + 5]
|
187
187
|
return e.sass_backtrace_str if section.nil? || section.empty?
|
188
188
|
|
189
189
|
e.sass_backtrace_str + "\n\n" + Sass::Util.enum_with_index(section).
|
data/lib/sass/exec/base.rb
CHANGED
@@ -18,11 +18,23 @@ module Sass::Exec
|
|
18
18
|
begin
|
19
19
|
parse
|
20
20
|
rescue Exception => e
|
21
|
+
# Exit code 65 indicates invalid data per
|
22
|
+
# http://www.freebsd.org/cgi/man.cgi?query=sysexits. Setting it via
|
23
|
+
# at_exit is a bit of a hack, but it allows us to rethrow when --trace
|
24
|
+
# is active and get both the built-in exception formatting and the
|
25
|
+
# correct exit code.
|
26
|
+
at_exit {exit Sass::Util.windows? ? 13 : 65} if e.is_a?(Sass::SyntaxError)
|
27
|
+
|
21
28
|
raise e if @options[:trace] || e.is_a?(SystemExit)
|
22
29
|
|
23
|
-
|
24
|
-
|
30
|
+
if e.is_a?(Sass::SyntaxError)
|
31
|
+
$stderr.puts e.sass_backtrace_str("standard input")
|
32
|
+
else
|
33
|
+
$stderr.print "#{e.class}: " unless e.class == RuntimeError
|
34
|
+
$stderr.puts e.message.to_s
|
35
|
+
end
|
25
36
|
$stderr.puts " Use --trace for backtrace."
|
37
|
+
|
26
38
|
exit 1
|
27
39
|
end
|
28
40
|
exit 0
|
@@ -137,7 +149,7 @@ module Sass::Exec
|
|
137
149
|
|
138
150
|
# Wraps the given string in terminal escapes
|
139
151
|
# causing it to have the given color.
|
140
|
-
# If terminal
|
152
|
+
# If terminal escapes aren't supported on this platform,
|
141
153
|
# just returns the string instead.
|
142
154
|
#
|
143
155
|
# @param color [Symbol] The name of the color to use.
|
@@ -108,10 +108,11 @@ END
|
|
108
108
|
@options[:for_tree][:dasherize] = true
|
109
109
|
end
|
110
110
|
|
111
|
-
opts.on(
|
111
|
+
opts.on(
|
112
|
+
'--indent NUM',
|
112
113
|
'How many spaces to use for each level of indentation. Defaults to 2.',
|
113
|
-
'"t" means use hard tabs.'
|
114
|
-
|
114
|
+
'"t" means use hard tabs.'
|
115
|
+
) do |indent|
|
115
116
|
if indent == 't'
|
116
117
|
@options[:for_tree][:indent] = "\t"
|
117
118
|
else
|
@@ -156,13 +157,18 @@ END
|
|
156
157
|
@options[:for_engine][:read_cache] = false
|
157
158
|
end
|
158
159
|
|
160
|
+
opts.on('-q', '--quiet', 'Silence warnings and status messages during conversion.') do |bool|
|
161
|
+
@options[:for_engine][:quiet] = bool
|
162
|
+
end
|
163
|
+
|
159
164
|
opts.on('--trace', :NONE, 'Show a full Ruby stack trace on error') do
|
160
165
|
@options[:trace] = true
|
161
166
|
end
|
162
167
|
end
|
163
168
|
|
164
169
|
def process_directory
|
165
|
-
|
170
|
+
@options[:input] = @args.shift
|
171
|
+
unless @options[:input]
|
166
172
|
raise "Error: directory required when using --recursive."
|
167
173
|
end
|
168
174
|
|
@@ -207,15 +213,15 @@ END
|
|
207
213
|
puts_action :create, :green, output
|
208
214
|
end
|
209
215
|
|
210
|
-
|
211
|
-
process_file(input, output)
|
216
|
+
process_file(f, output)
|
212
217
|
end
|
213
218
|
end
|
214
219
|
|
215
220
|
def process_file(input, output)
|
216
|
-
|
221
|
+
input_path, output_path = path_for(input), path_for(output)
|
222
|
+
if input_path
|
217
223
|
@options[:from] ||=
|
218
|
-
case
|
224
|
+
case input_path
|
219
225
|
when /\.scss$/; :scss
|
220
226
|
when /\.sass$/; :sass
|
221
227
|
when /\.less$/; raise "sass-convert no longer supports LessCSS."
|
@@ -225,9 +231,9 @@ END
|
|
225
231
|
raise "Error: the --in-place option requires a filename."
|
226
232
|
end
|
227
233
|
|
228
|
-
if
|
234
|
+
if output_path
|
229
235
|
@options[:to] ||=
|
230
|
-
case
|
236
|
+
case output_path
|
231
237
|
when /\.scss$/; :scss
|
232
238
|
when /\.sass$/; :sass
|
233
239
|
end
|
@@ -241,17 +247,17 @@ END
|
|
241
247
|
Sass::Util.silence_sass_warnings do
|
242
248
|
if @options[:from] == :css
|
243
249
|
require 'sass/css'
|
244
|
-
Sass::CSS.new(input
|
250
|
+
Sass::CSS.new(read(input), @options[:for_tree]).render(@options[:to])
|
245
251
|
else
|
246
|
-
if
|
247
|
-
Sass::Engine.for_file(
|
252
|
+
if input_path
|
253
|
+
Sass::Engine.for_file(input_path, @options[:for_engine])
|
248
254
|
else
|
249
|
-
Sass::Engine.new(input
|
255
|
+
Sass::Engine.new(read(input), @options[:for_engine])
|
250
256
|
end.to_tree.send("to_#{@options[:to]}", @options[:for_tree])
|
251
257
|
end
|
252
258
|
end
|
253
259
|
|
254
|
-
output =
|
260
|
+
output = input_path if @options[:in_place]
|
255
261
|
write_output(out, output)
|
256
262
|
rescue Sass::SyntaxError => e
|
257
263
|
raise e if @options[:trace]
|
@@ -260,5 +266,18 @@ END
|
|
260
266
|
rescue LoadError => err
|
261
267
|
handle_load_error(err)
|
262
268
|
end
|
269
|
+
|
270
|
+
def path_for(file)
|
271
|
+
return file.path if file.is_a?(File)
|
272
|
+
return file if file.is_a?(String)
|
273
|
+
end
|
274
|
+
|
275
|
+
def read(file)
|
276
|
+
if file.respond_to?(:read)
|
277
|
+
file.read
|
278
|
+
else
|
279
|
+
open(file, 'rb') {|f| f.read}
|
280
|
+
end
|
281
|
+
end
|
263
282
|
end
|
264
283
|
end
|
data/lib/sass/exec/sass_scss.rb
CHANGED
@@ -42,6 +42,7 @@ END
|
|
42
42
|
if @args.size == 1
|
43
43
|
@args = split_colon_path(@args.first)
|
44
44
|
else
|
45
|
+
@fake_update = true
|
45
46
|
@options[:update] = true
|
46
47
|
end
|
47
48
|
end
|
@@ -155,7 +156,7 @@ END
|
|
155
156
|
' file: always absolute file URIs',
|
156
157
|
' inline: include the source text in the sourcemap',
|
157
158
|
' none: no sourcemaps') do |type|
|
158
|
-
if type && !%w
|
159
|
+
if type && !%w(auto file inline none).include?(type)
|
159
160
|
$stderr.puts "Unknown sourcemap type #{type}.\n\n"
|
160
161
|
$stderr.puts opts
|
161
162
|
exit
|
@@ -291,6 +292,16 @@ MSG
|
|
291
292
|
|
292
293
|
dirs, files = @args.map {|name| split_colon_path(name)}.
|
293
294
|
partition {|i, _| File.directory? i}
|
295
|
+
|
296
|
+
if @fake_update && !dirs.empty?
|
297
|
+
# Issue 1602.
|
298
|
+
Sass::Util.sass_warn <<WARNING.strip
|
299
|
+
DEPRECATION WARNING: Compiling directories without --update or --watch is
|
300
|
+
deprecated and won't work in future versions of Sass. Instead use:
|
301
|
+
#{@default_syntax} --update #{@args}
|
302
|
+
WARNING
|
303
|
+
end
|
304
|
+
|
294
305
|
files.map! do |from, to|
|
295
306
|
to ||= from.gsub(/\.[^.]*?$/, '.css')
|
296
307
|
sourcemap = Sass::Util.sourcemap_name(to) if @options[:sourcemap]
|
@@ -355,6 +366,11 @@ MSG
|
|
355
366
|
input = @options[:input]
|
356
367
|
output = @options[:output]
|
357
368
|
|
369
|
+
if input == $stdin
|
370
|
+
# See issue 1745
|
371
|
+
(@options[:for_engine][:load_paths] ||= []) << ::Sass::Importers::DeprecatedPath.new(".")
|
372
|
+
end
|
373
|
+
|
358
374
|
@options[:for_engine][:syntax] ||= :scss if input.is_a?(File) && input.path =~ /\.scss$/
|
359
375
|
@options[:for_engine][:syntax] ||= @default_syntax
|
360
376
|
engine =
|
@@ -370,11 +386,12 @@ MSG
|
|
370
386
|
input.close if input.is_a?(File)
|
371
387
|
|
372
388
|
if @options[:sourcemap] != :none && @options[:sourcemap_filename]
|
373
|
-
relative_sourcemap_path = Sass::Util.
|
374
|
-
|
389
|
+
relative_sourcemap_path = Sass::Util.relative_path_from(
|
390
|
+
@options[:sourcemap_filename], Sass::Util.pathname(@options[:output_filename]).dirname)
|
375
391
|
rendered, mapping = engine.render_with_sourcemap(relative_sourcemap_path.to_s)
|
376
392
|
write_output(rendered, output)
|
377
|
-
write_output(
|
393
|
+
write_output(
|
394
|
+
mapping.to_json(
|
378
395
|
:type => @options[:sourcemap],
|
379
396
|
:css_path => @options[:output_filename],
|
380
397
|
:sourcemap_path => @options[:sourcemap_filename]) + "\n",
|
@@ -384,8 +401,7 @@ MSG
|
|
384
401
|
end
|
385
402
|
rescue Sass::SyntaxError => e
|
386
403
|
write_output(Sass::SyntaxError.exception_to_css(e), output) if output.is_a?(String)
|
387
|
-
raise e
|
388
|
-
raise e.sass_backtrace_str("standard input")
|
404
|
+
raise e
|
389
405
|
ensure
|
390
406
|
output.close if output.is_a? File
|
391
407
|
end
|
@@ -397,7 +413,7 @@ MSG
|
|
397
413
|
def split_colon_path(path)
|
398
414
|
one, two = path.split(':', 2)
|
399
415
|
if one && two && Sass::Util.windows? &&
|
400
|
-
one =~ /\A[A-Za-z]\Z/ && two =~
|
416
|
+
one =~ /\A[A-Za-z]\Z/ && two =~ %r{\A[/\\]}
|
401
417
|
# If we're on Windows and we were passed a drive letter path,
|
402
418
|
# don't split on that colon.
|
403
419
|
one2, two = two.split(':', 2)
|
data/lib/sass/features.rb
CHANGED
@@ -7,12 +7,12 @@ module Sass
|
|
7
7
|
#
|
8
8
|
# When this is updated, the documentation of `feature-exists()` should be
|
9
9
|
# updated as well.
|
10
|
-
KNOWN_FEATURES = Set[*%w
|
10
|
+
KNOWN_FEATURES = Set[*%w(
|
11
11
|
global-variable-shadowing
|
12
12
|
extend-selector-pseudoclass
|
13
13
|
units-level-3
|
14
14
|
at-error
|
15
|
-
|
15
|
+
)]
|
16
16
|
|
17
17
|
# Check if a feature exists by name. This is used to implement
|
18
18
|
# the Sass function `feature-exists($feature)`
|
data/lib/sass/importers/base.rb
CHANGED
@@ -90,7 +90,7 @@ module Sass
|
|
90
90
|
#
|
91
91
|
# @param uri [String] The URI of the file to check.
|
92
92
|
# Comes from a `:filename` option set on an engine returned by this importer.
|
93
|
-
# @param options [{Symbol =>
|
93
|
+
# @param options [{Symbol => Object}] Options for the Sass file
|
94
94
|
# containing the `@import` currently being checked.
|
95
95
|
# @return [Time, nil]
|
96
96
|
def mtime(uri, options)
|