sass 3.3.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 +76 -62
- data/Rakefile +104 -24
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/extra/sass-spec-ref.sh +32 -0
- data/extra/update_watch.rb +1 -1
- data/lib/sass/cache_stores/filesystem.rb +9 -5
- data/lib/sass/cache_stores/memory.rb +4 -5
- data/lib/sass/callbacks.rb +2 -2
- data/lib/sass/css.rb +12 -13
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +106 -70
- data/lib/sass/environment.rb +39 -19
- data/lib/sass/error.rb +17 -20
- data/lib/sass/exec/base.rb +199 -0
- data/lib/sass/exec/sass_convert.rb +283 -0
- data/lib/sass/exec/sass_scss.rb +440 -0
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/features.rb +9 -2
- data/lib/sass/importers/base.rb +8 -3
- data/lib/sass/importers/filesystem.rb +30 -38
- 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/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +224 -90
- 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 +4 -4
- data/lib/sass/plugin.rb +6 -5
- data/lib/sass/script/css_lexer.rb +1 -1
- 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 +739 -318
- data/lib/sass/script/lexer.rb +134 -54
- data/lib/sass/script/parser.rb +252 -56
- data/lib/sass/script/tree/funcall.rb +13 -6
- data/lib/sass/script/tree/interpolation.rb +127 -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/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +59 -38
- data/lib/sass/script/tree/variable.rb +1 -1
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/value/base.rb +17 -14
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +78 -42
- data/lib/sass/script/value/helpers.rb +119 -2
- data/lib/sass/script/value/list.rb +0 -15
- data/lib/sass/script/value/map.rb +1 -1
- data/lib/sass/script/value/null.rb +0 -5
- data/lib/sass/script/value/number.rb +112 -31
- data/lib/sass/script/value/string.rb +102 -13
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/script.rb +3 -3
- data/lib/sass/scss/css_parser.rb +24 -4
- data/lib/sass/scss/parser.rb +290 -383
- data/lib/sass/scss/rx.rb +17 -9
- data/lib/sass/scss/static_parser.rb +306 -4
- data/lib/sass/scss.rb +0 -2
- data/lib/sass/selector/abstract_sequence.rb +35 -18
- data/lib/sass/selector/comma_sequence.rb +114 -19
- data/lib/sass/selector/pseudo.rb +266 -0
- data/lib/sass/selector/sequence.rb +146 -40
- data/lib/sass/selector/simple.rb +22 -33
- data/lib/sass/selector/simple_sequence.rb +122 -39
- data/lib/sass/selector.rb +57 -197
- data/lib/sass/shared.rb +2 -2
- data/lib/sass/source/map.rb +31 -14
- data/lib/sass/source/position.rb +4 -4
- data/lib/sass/stack.rb +2 -8
- data/lib/sass/supports.rb +10 -13
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/comment_node.rb +1 -1
- data/lib/sass/tree/css_import_node.rb +9 -1
- data/lib/sass/tree/directive_node.rb +8 -2
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +1 -1
- data/lib/sass/tree/function_node.rb +9 -0
- data/lib/sass/tree/import_node.rb +6 -5
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/node.rb +5 -3
- data/lib/sass/tree/prop_node.rb +6 -7
- data/lib/sass/tree/rule_node.rb +26 -11
- data/lib/sass/tree/visitors/check_nesting.rb +56 -32
- data/lib/sass/tree/visitors/convert.rb +59 -44
- data/lib/sass/tree/visitors/cssize.rb +34 -30
- data/lib/sass/tree/visitors/deep_copy.rb +6 -1
- data/lib/sass/tree/visitors/extend.rb +15 -13
- data/lib/sass/tree/visitors/perform.rb +87 -50
- data/lib/sass/tree/visitors/set_options.rb +15 -1
- data/lib/sass/tree/visitors/to_css.rb +72 -43
- 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 +2 -3
- data/lib/sass/util.rb +334 -154
- data/lib/sass/version.rb +7 -7
- data/lib/sass.rb +10 -8
- data/test/sass/cache_test.rb +62 -20
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +24 -11
- data/test/sass/conversion_test.rb +241 -50
- data/test/sass/css2sass_test.rb +73 -5
- data/test/sass/css_variable_test.rb +132 -0
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +343 -260
- data/test/sass/exec_test.rb +12 -2
- data/test/sass/extend_test.rb +333 -44
- data/test/sass/functions_test.rb +353 -260
- data/test/sass/importer_test.rb +40 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- 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 +24 -21
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +5 -5
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +71 -39
- data/test/sass/script_test.rb +714 -123
- data/test/sass/scss/css_test.rb +213 -30
- data/test/sass/scss/rx_test.rb +8 -4
- data/test/sass/scss/scss_test.rb +766 -22
- data/test/sass/source_map_test.rb +263 -95
- data/test/sass/superselector_test.rb +210 -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/scss_import.scss +2 -1
- 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/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
- data/test/sass/util/normalized_map_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +46 -45
- data/test/sass/value_helpers_test.rb +5 -7
- data/test/sass-spec.yml +3 -0
- data/test/test_helper.rb +7 -6
- data/vendor/listen/CHANGELOG.md +1 -228
- data/vendor/listen/Gemfile +5 -15
- data/vendor/listen/README.md +111 -77
- data/vendor/listen/Rakefile +0 -42
- data/vendor/listen/lib/listen/adapter.rb +195 -82
- data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
- data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
- data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
- data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
- data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
- data/vendor/listen/lib/listen/directory_record.rb +96 -61
- data/vendor/listen/lib/listen/listener.rb +135 -37
- data/vendor/listen/lib/listen/turnstile.rb +9 -5
- data/vendor/listen/lib/listen/version.rb +1 -1
- data/vendor/listen/lib/listen.rb +33 -19
- data/vendor/listen/listen.gemspec +6 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
- data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
- data/vendor/listen/spec/listen/listener_spec.rb +128 -39
- data/vendor/listen/spec/listen_spec.rb +15 -21
- data/vendor/listen/spec/spec_helper.rb +4 -0
- data/vendor/listen/spec/support/adapter_helper.rb +52 -15
- data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
- data/vendor/listen/spec/support/listeners_helper.rb +30 -7
- metadata +310 -300
- data/CONTRIBUTING +0 -3
- data/ext/mkrf_conf.rb +0 -27
- data/lib/sass/script/value/deprecated_false.rb +0 -55
- data/lib/sass/scss/script_lexer.rb +0 -15
- data/lib/sass/scss/script_parser.rb +0 -25
- data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
- data/vendor/listen/lib/listen/multi_listener.rb +0 -143
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
- data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
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
|
@@ -140,10 +140,10 @@ module Sass
|
|
140
140
|
def sass_backtrace_str(default_filename = "an unknown file")
|
141
141
|
lines = message.split("\n")
|
142
142
|
msg = lines[0] + lines[1..-1].
|
143
|
-
map {|l| "\n" + (" " * "
|
144
|
-
"
|
143
|
+
map {|l| "\n" + (" " * "Error: ".size) + l}.join
|
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,21 +153,19 @@ module Sass
|
|
153
153
|
# Returns an error report for an exception in CSS format.
|
154
154
|
#
|
155
155
|
# @param e [Exception]
|
156
|
-
# @param
|
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
|
160
160
|
# is set to false.
|
161
|
-
def exception_to_css(e,
|
162
|
-
|
163
|
-
|
164
|
-
header = header_string(e, options)
|
161
|
+
def exception_to_css(e, line_offset = 1)
|
162
|
+
header = header_string(e, line_offset)
|
165
163
|
|
166
164
|
<<END
|
167
165
|
/*
|
168
|
-
#{header.gsub(
|
166
|
+
#{header.gsub('*/', '*\\/')}
|
169
167
|
|
170
|
-
Backtrace:\n#{e.backtrace.join("\n").gsub(
|
168
|
+
Backtrace:\n#{e.backtrace.join("\n").gsub('*/', '*\\/')}
|
171
169
|
*/
|
172
170
|
body:before {
|
173
171
|
white-space: pre;
|
@@ -178,15 +176,14 @@ END
|
|
178
176
|
|
179
177
|
private
|
180
178
|
|
181
|
-
def header_string(e,
|
179
|
+
def header_string(e, line_offset)
|
182
180
|
unless e.is_a?(Sass::SyntaxError) && e.sass_line && e.sass_template
|
183
181
|
return "#{e.class}: #{e.message}"
|
184
182
|
end
|
185
183
|
|
186
|
-
line_offset = options[:line] || 1
|
187
184
|
line_num = e.sass_line + 1 - line_offset
|
188
185
|
min = [line_num - 6, 0].max
|
189
|
-
section = e.sass_template.rstrip.split("\n")[min
|
186
|
+
section = e.sass_template.rstrip.split("\n")[min...line_num + 5]
|
190
187
|
return e.sass_backtrace_str if section.nil? || section.empty?
|
191
188
|
|
192
189
|
e.sass_backtrace_str + "\n\n" + Sass::Util.enum_with_index(section).
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Sass::Exec
|
4
|
+
# The abstract base class for Sass executables.
|
5
|
+
class Base
|
6
|
+
# @param args [Array<String>] The command-line arguments
|
7
|
+
def initialize(args)
|
8
|
+
@args = args
|
9
|
+
@options = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Parses the command-line arguments and runs the executable.
|
13
|
+
# Calls `Kernel#exit` at the end, so it never returns.
|
14
|
+
#
|
15
|
+
# @see #parse
|
16
|
+
def parse!
|
17
|
+
# rubocop:disable RescueException
|
18
|
+
begin
|
19
|
+
parse
|
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
|
+
|
28
|
+
raise e if @options[:trace] || e.is_a?(SystemExit)
|
29
|
+
|
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
|
36
|
+
$stderr.puts " Use --trace for backtrace."
|
37
|
+
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
exit 0
|
41
|
+
# rubocop:enable RescueException
|
42
|
+
end
|
43
|
+
|
44
|
+
# Parses the command-line arguments and runs the executable.
|
45
|
+
# This does not handle exceptions or exit the program.
|
46
|
+
#
|
47
|
+
# @see #parse!
|
48
|
+
def parse
|
49
|
+
@opts = OptionParser.new(&method(:set_opts))
|
50
|
+
@opts.parse!(@args)
|
51
|
+
|
52
|
+
process_result
|
53
|
+
|
54
|
+
@options
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [String] A description of the executable
|
58
|
+
def to_s
|
59
|
+
@opts.to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
# Finds the line of the source template
|
65
|
+
# on which an exception was raised.
|
66
|
+
#
|
67
|
+
# @param exception [Exception] The exception
|
68
|
+
# @return [String] The line number
|
69
|
+
def get_line(exception)
|
70
|
+
# SyntaxErrors have weird line reporting
|
71
|
+
# when there's trailing whitespace
|
72
|
+
if exception.is_a?(::SyntaxError)
|
73
|
+
return (exception.message.scan(/:(\d+)/).first || ["??"]).first
|
74
|
+
end
|
75
|
+
(exception.backtrace[0].scan(/:(\d+)/).first || ["??"]).first
|
76
|
+
end
|
77
|
+
|
78
|
+
# Tells optparse how to parse the arguments
|
79
|
+
# available for all executables.
|
80
|
+
#
|
81
|
+
# This is meant to be overridden by subclasses
|
82
|
+
# so they can add their own options.
|
83
|
+
#
|
84
|
+
# @param opts [OptionParser]
|
85
|
+
def set_opts(opts)
|
86
|
+
Sass::Util.abstract(this)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Set an option for specifying `Encoding.default_external`.
|
90
|
+
#
|
91
|
+
# @param opts [OptionParser]
|
92
|
+
def encoding_option(opts)
|
93
|
+
encoding_desc = if Sass::Util.ruby1_8?
|
94
|
+
'Does not work in Ruby 1.8.'
|
95
|
+
else
|
96
|
+
'Specify the default encoding for input files.'
|
97
|
+
end
|
98
|
+
opts.on('-E', '--default-encoding ENCODING', encoding_desc) do |encoding|
|
99
|
+
if Sass::Util.ruby1_8?
|
100
|
+
$stderr.puts "Specifying the encoding is not supported in ruby 1.8."
|
101
|
+
exit 1
|
102
|
+
else
|
103
|
+
Encoding.default_external = encoding
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Processes the options set by the command-line arguments. In particular,
|
109
|
+
# sets `@options[:input]` and `@options[:output]` to appropriate IO streams.
|
110
|
+
#
|
111
|
+
# This is meant to be overridden by subclasses
|
112
|
+
# so they can run their respective programs.
|
113
|
+
def process_result
|
114
|
+
input, output = @options[:input], @options[:output]
|
115
|
+
args = @args.dup
|
116
|
+
input ||=
|
117
|
+
begin
|
118
|
+
filename = args.shift
|
119
|
+
@options[:filename] = filename
|
120
|
+
open_file(filename) || $stdin
|
121
|
+
end
|
122
|
+
@options[:output_filename] = args.shift
|
123
|
+
output ||= @options[:output_filename] || $stdout
|
124
|
+
@options[:input], @options[:output] = input, output
|
125
|
+
end
|
126
|
+
|
127
|
+
COLORS = {:red => 31, :green => 32, :yellow => 33}
|
128
|
+
|
129
|
+
# Prints a status message about performing the given action,
|
130
|
+
# colored using the given color (via terminal escapes) if possible.
|
131
|
+
#
|
132
|
+
# @param name [#to_s] A short name for the action being performed.
|
133
|
+
# Shouldn't be longer than 11 characters.
|
134
|
+
# @param color [Symbol] The name of the color to use for this action.
|
135
|
+
# Can be `:red`, `:green`, or `:yellow`.
|
136
|
+
def puts_action(name, color, arg)
|
137
|
+
return if @options[:for_engine][:quiet]
|
138
|
+
printf color(color, "%11s %s\n"), name, arg
|
139
|
+
STDOUT.flush
|
140
|
+
end
|
141
|
+
|
142
|
+
# Same as `Kernel.puts`, but doesn't print anything if the `--quiet` option is set.
|
143
|
+
#
|
144
|
+
# @param args [Array] Passed on to `Kernel.puts`
|
145
|
+
def puts(*args)
|
146
|
+
return if @options[:for_engine][:quiet]
|
147
|
+
Kernel.puts(*args)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Wraps the given string in terminal escapes
|
151
|
+
# causing it to have the given color.
|
152
|
+
# If terminal escapes aren't supported on this platform,
|
153
|
+
# just returns the string instead.
|
154
|
+
#
|
155
|
+
# @param color [Symbol] The name of the color to use.
|
156
|
+
# Can be `:red`, `:green`, or `:yellow`.
|
157
|
+
# @param str [String] The string to wrap in the given color.
|
158
|
+
# @return [String] The wrapped string.
|
159
|
+
def color(color, str)
|
160
|
+
raise "[BUG] Unrecognized color #{color}" unless COLORS[color]
|
161
|
+
|
162
|
+
# Almost any real Unix terminal will support color,
|
163
|
+
# so we just filter for Windows terms (which don't set TERM)
|
164
|
+
# and not-real terminals, which aren't ttys.
|
165
|
+
return str if ENV["TERM"].nil? || ENV["TERM"].empty? || !STDOUT.tty?
|
166
|
+
"\e[#{COLORS[color]}m#{str}\e[0m"
|
167
|
+
end
|
168
|
+
|
169
|
+
def write_output(text, destination)
|
170
|
+
if destination.is_a?(String)
|
171
|
+
open_file(destination, 'w') {|file| file.write(text)}
|
172
|
+
else
|
173
|
+
destination.write(text)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def open_file(filename, flag = 'r')
|
180
|
+
return if filename.nil?
|
181
|
+
flag = 'wb' if @options[:unix_newlines] && flag == 'w'
|
182
|
+
file = File.open(filename, flag)
|
183
|
+
return file unless block_given?
|
184
|
+
yield file
|
185
|
+
file.close
|
186
|
+
end
|
187
|
+
|
188
|
+
def handle_load_error(err)
|
189
|
+
dep = err.message[/^no such file to load -- (.*)/, 1]
|
190
|
+
raise err if @options[:trace] || dep.nil? || dep.empty?
|
191
|
+
$stderr.puts <<MESSAGE
|
192
|
+
Required dependency #{dep} not found!
|
193
|
+
Run "gem install #{dep}" to get it.
|
194
|
+
Use --trace for backtrace.
|
195
|
+
MESSAGE
|
196
|
+
exit 1
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Sass::Exec
|
5
|
+
# The `sass-convert` executable.
|
6
|
+
class SassConvert < Base
|
7
|
+
# @param args [Array<String>] The command-line arguments
|
8
|
+
def initialize(args)
|
9
|
+
super
|
10
|
+
require 'sass'
|
11
|
+
@options[:for_tree] = {}
|
12
|
+
@options[:for_engine] = {:cache => false, :read_cache => true}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Tells optparse how to parse the arguments.
|
16
|
+
#
|
17
|
+
# @param opts [OptionParser]
|
18
|
+
def set_opts(opts)
|
19
|
+
opts.banner = <<END
|
20
|
+
Usage: sass-convert [options] [INPUT] [OUTPUT]
|
21
|
+
|
22
|
+
Description:
|
23
|
+
Converts between CSS, indented syntax, and SCSS files. For example,
|
24
|
+
this can convert from the indented syntax to SCSS, or from CSS to
|
25
|
+
SCSS (adding appropriate nesting).
|
26
|
+
END
|
27
|
+
|
28
|
+
common_options(opts)
|
29
|
+
style(opts)
|
30
|
+
input_and_output(opts)
|
31
|
+
miscellaneous(opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Processes the options set by the command-line arguments,
|
35
|
+
# and runs the CSS compiler appropriately.
|
36
|
+
def process_result
|
37
|
+
require 'sass'
|
38
|
+
|
39
|
+
if @options[:recursive]
|
40
|
+
process_directory
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
super
|
45
|
+
input = @options[:input]
|
46
|
+
if File.directory?(input)
|
47
|
+
raise "Error: '#{input.path}' is a directory (did you mean to use --recursive?)"
|
48
|
+
end
|
49
|
+
output = @options[:output]
|
50
|
+
output = input if @options[:in_place]
|
51
|
+
process_file(input, output)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def common_options(opts)
|
57
|
+
opts.separator ''
|
58
|
+
opts.separator 'Common Options:'
|
59
|
+
|
60
|
+
opts.on('-F', '--from FORMAT',
|
61
|
+
'The format to convert from. Can be css, scss, sass.',
|
62
|
+
'By default, this is inferred from the input filename.',
|
63
|
+
'If there is none, defaults to css.') do |name|
|
64
|
+
@options[:from] = name.downcase.to_sym
|
65
|
+
raise "sass-convert no longer supports LessCSS." if @options[:from] == :less
|
66
|
+
unless [:css, :scss, :sass].include?(@options[:from])
|
67
|
+
raise "Unknown format for sass-convert --from: #{name}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.on('-T', '--to FORMAT',
|
72
|
+
'The format to convert to. Can be scss or sass.',
|
73
|
+
'By default, this is inferred from the output filename.',
|
74
|
+
'If there is none, defaults to sass.') do |name|
|
75
|
+
@options[:to] = name.downcase.to_sym
|
76
|
+
unless [:scss, :sass].include?(@options[:to])
|
77
|
+
raise "Unknown format for sass-convert --to: #{name}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
opts.on('-i', '--in-place',
|
82
|
+
'Convert a file to its own syntax.',
|
83
|
+
'This can be used to update some deprecated syntax.') do
|
84
|
+
@options[:in_place] = true
|
85
|
+
end
|
86
|
+
|
87
|
+
opts.on('-R', '--recursive',
|
88
|
+
'Convert all the files in a directory. Requires --from and --to.') do
|
89
|
+
@options[:recursive] = true
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.on("-?", "-h", "--help", "Show this help message.") do
|
93
|
+
puts opts
|
94
|
+
exit
|
95
|
+
end
|
96
|
+
|
97
|
+
opts.on("-v", "--version", "Print the Sass version.") do
|
98
|
+
puts("Sass #{Sass.version[:string]}")
|
99
|
+
exit
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def style(opts)
|
104
|
+
opts.separator ''
|
105
|
+
opts.separator 'Style:'
|
106
|
+
|
107
|
+
opts.on('--dasherize', 'Convert underscores to dashes.') do
|
108
|
+
@options[:for_tree][:dasherize] = true
|
109
|
+
end
|
110
|
+
|
111
|
+
opts.on(
|
112
|
+
'--indent NUM',
|
113
|
+
'How many spaces to use for each level of indentation. Defaults to 2.',
|
114
|
+
'"t" means use hard tabs.'
|
115
|
+
) do |indent|
|
116
|
+
if indent == 't'
|
117
|
+
@options[:for_tree][:indent] = "\t"
|
118
|
+
else
|
119
|
+
@options[:for_tree][:indent] = " " * indent.to_i
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
opts.on('--old', 'Output the old-style ":prop val" property syntax.',
|
124
|
+
'Only meaningful when generating Sass.') do
|
125
|
+
@options[:for_tree][:old] = true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def input_and_output(opts)
|
130
|
+
opts.separator ''
|
131
|
+
opts.separator 'Input and Output:'
|
132
|
+
|
133
|
+
opts.on('-s', '--stdin', :NONE,
|
134
|
+
'Read input from standard input instead of an input file.',
|
135
|
+
'This is the default if no input file is specified. Requires --from.') do
|
136
|
+
@options[:input] = $stdin
|
137
|
+
end
|
138
|
+
|
139
|
+
encoding_option(opts)
|
140
|
+
|
141
|
+
opts.on('--unix-newlines', 'Use Unix-style newlines in written files.',
|
142
|
+
('Always true on Unix.' unless Sass::Util.windows?)) do
|
143
|
+
@options[:unix_newlines] = true if Sass::Util.windows?
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def miscellaneous(opts)
|
148
|
+
opts.separator ''
|
149
|
+
opts.separator 'Miscellaneous:'
|
150
|
+
|
151
|
+
opts.on('--cache-location PATH',
|
152
|
+
'The path to save parsed Sass files. Defaults to .sass-cache.') do |loc|
|
153
|
+
@options[:for_engine][:cache_location] = loc
|
154
|
+
end
|
155
|
+
|
156
|
+
opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
|
157
|
+
@options[:for_engine][:read_cache] = false
|
158
|
+
end
|
159
|
+
|
160
|
+
opts.on('-q', '--quiet', 'Silence warnings and status messages during conversion.') do |bool|
|
161
|
+
@options[:for_engine][:quiet] = bool
|
162
|
+
end
|
163
|
+
|
164
|
+
opts.on('--trace', :NONE, 'Show a full Ruby stack trace on error') do
|
165
|
+
@options[:trace] = true
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def process_directory
|
170
|
+
@options[:input] = @args.shift
|
171
|
+
unless @options[:input]
|
172
|
+
raise "Error: directory required when using --recursive."
|
173
|
+
end
|
174
|
+
|
175
|
+
output = @options[:output] = @args.shift
|
176
|
+
raise "Error: --from required when using --recursive." unless @options[:from]
|
177
|
+
raise "Error: --to required when using --recursive." unless @options[:to]
|
178
|
+
unless File.directory?(@options[:input])
|
179
|
+
raise "Error: '#{@options[:input]}' is not a directory"
|
180
|
+
end
|
181
|
+
if @options[:output] && File.exist?(@options[:output]) &&
|
182
|
+
!File.directory?(@options[:output])
|
183
|
+
raise "Error: '#{@options[:output]}' is not a directory"
|
184
|
+
end
|
185
|
+
@options[:output] ||= @options[:input]
|
186
|
+
|
187
|
+
if @options[:to] == @options[:from] && !@options[:in_place]
|
188
|
+
fmt = @options[:from]
|
189
|
+
raise "Error: converting from #{fmt} to #{fmt} without --in-place"
|
190
|
+
end
|
191
|
+
|
192
|
+
ext = @options[:from]
|
193
|
+
Sass::Util.glob("#{@options[:input]}/**/*.#{ext}") do |f|
|
194
|
+
output =
|
195
|
+
if @options[:in_place]
|
196
|
+
f
|
197
|
+
elsif @options[:output]
|
198
|
+
output_name = f.gsub(/\.(c|sa|sc|le)ss$/, ".#{@options[:to]}")
|
199
|
+
output_name[0...@options[:input].size] = @options[:output]
|
200
|
+
output_name
|
201
|
+
else
|
202
|
+
f.gsub(/\.(c|sa|sc|le)ss$/, ".#{@options[:to]}")
|
203
|
+
end
|
204
|
+
|
205
|
+
unless File.directory?(File.dirname(output))
|
206
|
+
puts_action :directory, :green, File.dirname(output)
|
207
|
+
FileUtils.mkdir_p(File.dirname(output))
|
208
|
+
end
|
209
|
+
puts_action :convert, :green, f
|
210
|
+
if File.exist?(output)
|
211
|
+
puts_action :overwrite, :yellow, output
|
212
|
+
else
|
213
|
+
puts_action :create, :green, output
|
214
|
+
end
|
215
|
+
|
216
|
+
process_file(f, output)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def process_file(input, output)
|
221
|
+
input_path, output_path = path_for(input), path_for(output)
|
222
|
+
if input_path
|
223
|
+
@options[:from] ||=
|
224
|
+
case input_path
|
225
|
+
when /\.scss$/; :scss
|
226
|
+
when /\.sass$/; :sass
|
227
|
+
when /\.less$/; raise "sass-convert no longer supports LessCSS."
|
228
|
+
when /\.css$/; :css
|
229
|
+
end
|
230
|
+
elsif @options[:in_place]
|
231
|
+
raise "Error: the --in-place option requires a filename."
|
232
|
+
end
|
233
|
+
|
234
|
+
if output_path
|
235
|
+
@options[:to] ||=
|
236
|
+
case output_path
|
237
|
+
when /\.scss$/; :scss
|
238
|
+
when /\.sass$/; :sass
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
@options[:from] ||= :css
|
243
|
+
@options[:to] ||= :sass
|
244
|
+
@options[:for_engine][:syntax] = @options[:from]
|
245
|
+
|
246
|
+
out =
|
247
|
+
Sass::Util.silence_sass_warnings do
|
248
|
+
if @options[:from] == :css
|
249
|
+
require 'sass/css'
|
250
|
+
Sass::CSS.new(read(input), @options[:for_tree]).render(@options[:to])
|
251
|
+
else
|
252
|
+
if input_path
|
253
|
+
Sass::Engine.for_file(input_path, @options[:for_engine])
|
254
|
+
else
|
255
|
+
Sass::Engine.new(read(input), @options[:for_engine])
|
256
|
+
end.to_tree.send("to_#{@options[:to]}", @options[:for_tree])
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
output = input_path if @options[:in_place]
|
261
|
+
write_output(out, output)
|
262
|
+
rescue Sass::SyntaxError => e
|
263
|
+
raise e if @options[:trace]
|
264
|
+
file = " of #{e.sass_filename}" if e.sass_filename
|
265
|
+
raise "Error on line #{e.sass_line}#{file}: #{e.message}\n Use --trace for backtrace"
|
266
|
+
rescue LoadError => err
|
267
|
+
handle_load_error(err)
|
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
|
282
|
+
end
|
283
|
+
end
|