scss-lint 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/default.yml +19 -1
- data/data/properties.txt +4 -0
- data/lib/scss_lint.rb +1 -0
- data/lib/scss_lint/cli.rb +4 -42
- data/lib/scss_lint/config.rb +1 -45
- data/lib/scss_lint/control_comment_processor.rb +47 -15
- data/lib/scss_lint/file_finder.rb +57 -0
- data/lib/scss_lint/linter/bang_format.rb +1 -1
- data/lib/scss_lint/linter/border_zero.rb +25 -9
- data/lib/scss_lint/linter/color_keyword.rb +3 -13
- data/lib/scss_lint/linter/color_variable.rb +36 -0
- data/lib/scss_lint/linter/declaration_order.rb +2 -2
- data/lib/scss_lint/linter/important_rule.rb +12 -0
- data/lib/scss_lint/linter/indentation.rb +7 -1
- data/lib/scss_lint/linter/property_count.rb +44 -0
- data/lib/scss_lint/linter/property_sort_order.rb +73 -19
- data/lib/scss_lint/linter/string_quotes.rb +9 -0
- data/lib/scss_lint/linter/variable_for_property.rb +20 -0
- data/lib/scss_lint/linter/vendor_prefixes.rb +3 -3
- data/lib/scss_lint/runner.rb +5 -7
- data/lib/scss_lint/utils.rb +34 -0
- data/lib/scss_lint/version.rb +1 -1
- data/spec/scss_lint/cli_spec.rb +1 -1
- data/spec/scss_lint/config_spec.rb +4 -203
- data/spec/scss_lint/engine_spec.rb +4 -4
- data/spec/scss_lint/file_finder_spec.rb +110 -0
- data/spec/scss_lint/linter/bang_format_spec.rb +28 -18
- data/spec/scss_lint/linter/border_zero_spec.rb +50 -16
- data/spec/scss_lint/linter/color_keyword_spec.rb +16 -16
- data/spec/scss_lint/linter/color_variable_spec.rb +102 -0
- data/spec/scss_lint/linter/comment_spec.rb +9 -9
- data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +10 -10
- data/spec/scss_lint/linter/debug_statement_spec.rb +4 -4
- data/spec/scss_lint/linter/declaration_order_spec.rb +80 -80
- data/spec/scss_lint/linter/duplicate_property_spec.rb +30 -30
- data/spec/scss_lint/linter/else_placement_spec.rb +16 -16
- data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +38 -38
- data/spec/scss_lint/linter/empty_rule_spec.rb +4 -4
- data/spec/scss_lint/linter/final_newline_spec.rb +6 -6
- data/spec/scss_lint/linter/hex_length_spec.rb +16 -16
- data/spec/scss_lint/linter/hex_notation_spec.rb +16 -16
- data/spec/scss_lint/linter/hex_validation_spec.rb +6 -6
- data/spec/scss_lint/linter/id_selector_spec.rb +10 -10
- data/spec/scss_lint/linter/import_path_spec.rb +45 -45
- data/spec/scss_lint/linter/important_rule_spec.rb +43 -0
- data/spec/scss_lint/linter/indentation_spec.rb +103 -43
- data/spec/scss_lint/linter/leading_zero_spec.rb +45 -45
- data/spec/scss_lint/linter/mergeable_selector_spec.rb +32 -32
- data/spec/scss_lint/linter/name_format_spec.rb +75 -41
- data/spec/scss_lint/linter/nesting_depth_spec.rb +14 -14
- data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +12 -12
- data/spec/scss_lint/linter/property_count_spec.rb +104 -0
- data/spec/scss_lint/linter/property_sort_order_spec.rb +138 -48
- data/spec/scss_lint/linter/property_spelling_spec.rb +14 -14
- data/spec/scss_lint/linter/qualifying_element_spec.rb +26 -26
- data/spec/scss_lint/linter/selector_depth_spec.rb +26 -26
- data/spec/scss_lint/linter/selector_format_spec.rb +114 -114
- data/spec/scss_lint/linter/shorthand_spec.rb +32 -32
- data/spec/scss_lint/linter/single_line_per_property_spec.rb +10 -10
- data/spec/scss_lint/linter/single_line_per_selector_spec.rb +24 -24
- data/spec/scss_lint/linter/space_after_comma_spec.rb +60 -60
- data/spec/scss_lint/linter/space_after_property_colon_spec.rb +44 -44
- data/spec/scss_lint/linter/space_after_property_name_spec.rb +6 -6
- data/spec/scss_lint/linter/space_before_brace_spec.rb +119 -119
- data/spec/scss_lint/linter/space_between_parens_spec.rb +48 -48
- data/spec/scss_lint/linter/string_quotes_spec.rb +74 -62
- data/spec/scss_lint/linter/trailing_semicolon_spec.rb +53 -54
- data/spec/scss_lint/linter/trailing_zero_spec.rb +34 -34
- data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +10 -10
- data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +18 -18
- data/spec/scss_lint/linter/url_format_spec.rb +4 -4
- data/spec/scss_lint/linter/url_quotes_spec.rb +14 -14
- data/spec/scss_lint/linter/variable_for_property_spec.rb +115 -0
- data/spec/scss_lint/linter/vendor_prefixes_spec.rb +66 -56
- data/spec/scss_lint/linter/zero_unit_spec.rb +22 -22
- data/spec/scss_lint/linter_spec.rb +72 -28
- data/spec/scss_lint/runner_spec.rb +0 -1
- data/spec/scss_lint/selector_visitor_spec.rb +23 -23
- data/spec/spec_helper.rb +2 -2
- metadata +27 -12
@@ -4,11 +4,9 @@ module SCSSLint
|
|
4
4
|
class Linter::ColorKeyword < Linter
|
5
5
|
include LinterRegistry
|
6
6
|
|
7
|
-
COLOR_REGEX = /(#?[a-f0-9]{3,6}|[a-z]+)/i
|
8
|
-
|
9
7
|
def visit_script_color(node)
|
10
|
-
|
11
|
-
add_color_lint(node,
|
8
|
+
word = source_from_range(node.source_range)[/([a-z]+)/i, 1]
|
9
|
+
add_color_lint(node, word) if color_keyword?(word)
|
12
10
|
end
|
13
11
|
|
14
12
|
def visit_script_string(node)
|
@@ -22,7 +20,7 @@ module SCSSLint
|
|
22
20
|
private
|
23
21
|
|
24
22
|
def add_color_lint(node, original)
|
25
|
-
hex_form = Sass::Script::Value::Color.new(
|
23
|
+
hex_form = Sass::Script::Value::Color.new(color_keyword_to_code(original)).tap do |color|
|
26
24
|
color.options = {} # `inspect` requires options to be set
|
27
25
|
end.inspect
|
28
26
|
|
@@ -30,13 +28,5 @@ module SCSSLint
|
|
30
28
|
"Color `#{original}` should be written in hexadecimal form " \
|
31
29
|
"as `#{hex_form}`")
|
32
30
|
end
|
33
|
-
|
34
|
-
def color_keyword?(string)
|
35
|
-
color_rgb(string) && string != 'transparent'
|
36
|
-
end
|
37
|
-
|
38
|
-
def color_rgb(string)
|
39
|
-
Sass::Script::Value::Color::COLOR_NAMES[string]
|
40
|
-
end
|
41
31
|
end
|
42
32
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Ensures color literals are used only in variable declarations.
|
3
|
+
class Linter::ColorVariable < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_script_color(node)
|
7
|
+
return if in_variable_declaration?(node)
|
8
|
+
|
9
|
+
# Source range sometimes includes closing parenthesis, so extract it
|
10
|
+
color = source_from_range(node.source_range)[/(#?[a-z0-9]+)/i, 1]
|
11
|
+
|
12
|
+
record_lint(node, color)
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_script_string(node)
|
16
|
+
remove_quoted_strings(node.value)
|
17
|
+
.scan(/(^|\s)(#[a-f0-9]+|[a-z]+)(?=\s|$)/i)
|
18
|
+
.select { |_, word| color?(word) }
|
19
|
+
.each { |_, color| record_lint(node, color) }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def record_lint(node, color)
|
25
|
+
add_lint node, "Color literals like `#{color}` should only be used in " \
|
26
|
+
'variable declarations; they should be referred to via ' \
|
27
|
+
'variable everywhere else.'
|
28
|
+
end
|
29
|
+
|
30
|
+
def in_variable_declaration?(node)
|
31
|
+
parent = node.node_parent
|
32
|
+
parent.is_a?(Sass::Script::Tree::Literal) &&
|
33
|
+
parent.node_parent.is_a?(Sass::Tree::VariableNode)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -15,8 +15,8 @@ module SCSSLint
|
|
15
15
|
|
16
16
|
MESSAGE =
|
17
17
|
'Rule sets should be ordered as follows: '\
|
18
|
-
'
|
19
|
-
'properties,
|
18
|
+
'`@extends`, `@includes` without `@content`, ' \
|
19
|
+
'properties, `@includes` with `@content`, ' \
|
20
20
|
'nested rule sets'
|
21
21
|
|
22
22
|
MIXIN_WITH_CONTENT = 'mixin_with_content'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Reports the use of !important in properties.
|
3
|
+
class Linter::ImportantRule < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_prop(node)
|
7
|
+
return unless source_from_range(node.source_range).include?('!important')
|
8
|
+
|
9
|
+
add_lint(node, '!important should not be used')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -49,7 +49,7 @@ module SCSSLint
|
|
49
49
|
return true
|
50
50
|
end
|
51
51
|
|
52
|
-
unless actual_indent.length == @indent
|
52
|
+
unless allow_arbitrary_indent?(node) || actual_indent.length == @indent
|
53
53
|
add_lint(node.line,
|
54
54
|
"Line should be indented #{@indent} #{character_name}s, " \
|
55
55
|
"but was indented #{actual_indent.length} #{character_name}s")
|
@@ -130,5 +130,11 @@ module SCSSLint
|
|
130
130
|
|
131
131
|
same_position?(node.source_range.end_pos, first_child_source.start_pos)
|
132
132
|
end
|
133
|
+
|
134
|
+
def allow_arbitrary_indent?(node)
|
135
|
+
@indent == 0 &&
|
136
|
+
config['allow_non_nested_indentation'] &&
|
137
|
+
node.is_a?(Sass::Tree::RuleNode)
|
138
|
+
end
|
133
139
|
end
|
134
140
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks that the number of properties in a rule set is under a defined limit.
|
3
|
+
class Linter::PropertyCount < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_root(_node)
|
7
|
+
@property_count = {} # Lookup table of counts for rule sets
|
8
|
+
@max = config['max_properties']
|
9
|
+
yield # Continue linting children
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_rule(node)
|
13
|
+
count = property_count(node)
|
14
|
+
|
15
|
+
if count > @max
|
16
|
+
add_lint node,
|
17
|
+
"Rule set contains (#{count}/#{@max}) properties" \
|
18
|
+
"#{' (including properties in nested rule sets)' if config['include_nested']}"
|
19
|
+
|
20
|
+
# Don't lint nested rule sets as we already have them in the count
|
21
|
+
return if config['include_nested']
|
22
|
+
end
|
23
|
+
|
24
|
+
yield # Lint nested rule sets
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def property_count(rule_node)
|
30
|
+
@property_count[rule_node] ||=
|
31
|
+
begin
|
32
|
+
count = rule_node.children.count { |node| node.is_a?(Sass::Tree::PropNode) }
|
33
|
+
|
34
|
+
if config['include_nested']
|
35
|
+
count += rule_node.children.inject(0) do |sum, node|
|
36
|
+
node.is_a?(Sass::Tree::RuleNode) ? sum + property_count(node) : sum
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
count
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,48 +1,102 @@
|
|
1
1
|
module SCSSLint
|
2
2
|
# Checks the declaration order of properties.
|
3
|
-
class Linter::PropertySortOrder < Linter
|
3
|
+
class Linter::PropertySortOrder < Linter # rubocop:disable ClassLength
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
6
|
def visit_root(_node)
|
7
7
|
@preferred_order = extract_preferred_order_from_config
|
8
|
-
|
8
|
+
|
9
|
+
if @preferred_order && config['separate_groups']
|
10
|
+
@group = assign_groups(@preferred_order)
|
11
|
+
end
|
12
|
+
|
13
|
+
yield # Continue linting children
|
9
14
|
end
|
10
15
|
|
11
|
-
def
|
16
|
+
def check_order(node)
|
12
17
|
sortable_props = node.children.select do |child|
|
13
18
|
child.is_a?(Sass::Tree::PropNode) && !ignore_property?(child)
|
14
19
|
end
|
15
20
|
|
16
21
|
sortable_prop_info = sortable_props
|
17
|
-
.map
|
18
|
-
|
22
|
+
.map do |child|
|
23
|
+
name = child.name.join
|
19
24
|
/^(?<vendor>-\w+(-osx)?-)?(?<property>.+)/ =~ name
|
20
|
-
{ name: name, vendor: vendor, property: property }
|
25
|
+
{ name: name, vendor: vendor, property: property, node: child }
|
26
|
+
end
|
27
|
+
|
28
|
+
if sortable_props.any?
|
29
|
+
check_sort_order(sortable_prop_info)
|
30
|
+
check_group_separation(sortable_prop_info) if @group
|
31
|
+
end
|
32
|
+
|
33
|
+
yield # Continue linting children
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method :visit_media, :check_order
|
37
|
+
alias_method :visit_mixin, :check_order
|
38
|
+
alias_method :visit_rule, :check_order
|
39
|
+
|
40
|
+
def visit_if(node, &block)
|
41
|
+
check_order(node, &block)
|
42
|
+
visit(node.else) if node.else
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# When enforcing whether a blank line should separate "groups" of
|
48
|
+
# properties, we need to assign those properties to group numbers so we can
|
49
|
+
# quickly tell traversing from one property to the other that a blank line
|
50
|
+
# is required (since the group number would change).
|
51
|
+
def assign_groups(order)
|
52
|
+
group_number = 0
|
53
|
+
last_was_empty = false
|
54
|
+
|
55
|
+
order.each_with_object({}) do |property, group|
|
56
|
+
# A gap indicates the start of the next group
|
57
|
+
if property.nil? || property.strip.empty?
|
58
|
+
group_number += 1 unless last_was_empty # Treat multiple gaps as single gap
|
59
|
+
last_was_empty = true
|
60
|
+
next
|
21
61
|
end
|
22
62
|
|
63
|
+
last_was_empty = false
|
64
|
+
|
65
|
+
group[property] = group_number
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def check_sort_order(sortable_prop_info)
|
23
70
|
sorted_props = sortable_prop_info
|
24
71
|
.sort { |a, b| compare_properties(a, b) }
|
25
72
|
|
26
73
|
sorted_props.each_with_index do |prop, index|
|
27
74
|
next unless prop != sortable_prop_info[index]
|
28
75
|
|
29
|
-
add_lint(
|
76
|
+
add_lint(sortable_prop_info[index][:node], lint_message(sorted_props))
|
30
77
|
break
|
31
78
|
end
|
32
|
-
|
33
|
-
yield # Continue linting children
|
34
79
|
end
|
35
80
|
|
36
|
-
|
37
|
-
|
38
|
-
alias_method :visit_rule, :check_sort_order
|
81
|
+
def check_group_separation(sortable_prop_info) # rubocop:disable AbcSize
|
82
|
+
group_number = @group[sortable_prop_info.first[:property]]
|
39
83
|
|
40
|
-
|
41
|
-
|
42
|
-
visit(node.else) if node.else
|
43
|
-
end
|
84
|
+
sortable_prop_info[0..-2].zip(sortable_prop_info[1..-1]).each do |first, second|
|
85
|
+
next unless @group[second[:property]] != group_number
|
44
86
|
|
45
|
-
|
87
|
+
# We're now in the next group
|
88
|
+
group_number = @group[second[:property]]
|
89
|
+
|
90
|
+
# The group number has changed, so ensure this property is separated
|
91
|
+
# from the previous property by at least a line (could be a comment,
|
92
|
+
# we don't care, but at least one line that isn't another property).
|
93
|
+
next if first[:node].line < second[:node].line - 1
|
94
|
+
|
95
|
+
add_lint second[:node], "Property #{second[:name]} should be " \
|
96
|
+
'separated from the previous group of ' \
|
97
|
+
"properties ending with #{first[:name]}"
|
98
|
+
end
|
99
|
+
end
|
46
100
|
|
47
101
|
# Compares two properties which can contain a vendor prefix. It allows for a
|
48
102
|
# sort order like:
|
@@ -100,11 +154,11 @@ module SCSSLint
|
|
100
154
|
"#{config['order']}.txt"))
|
101
155
|
file.read.split("\n").reject { |line| line =~ /^(#|\s*$)/ }
|
102
156
|
rescue Errno::ENOENT
|
103
|
-
raise SCSSLint::LinterError,
|
157
|
+
raise SCSSLint::Exceptions::LinterError,
|
104
158
|
"Preset property sort order '#{config['order']}' does not exist"
|
105
159
|
end
|
106
160
|
else
|
107
|
-
raise SCSSLint::LinterError,
|
161
|
+
raise SCSSLint::Exceptions::LinterError,
|
108
162
|
'Invalid property sort order specified -- must be the name of a '\
|
109
163
|
'preset or an array of strings'
|
110
164
|
end
|
@@ -3,6 +3,15 @@ module SCSSLint
|
|
3
3
|
class Linter::StringQuotes < Linter
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
|
+
def visit_comment(_node)
|
7
|
+
# Sass allows you to write Sass Script in non-silent comments (/* ... */).
|
8
|
+
# Unfortunately, it doesn't report correct source ranges for these script
|
9
|
+
# nodes.
|
10
|
+
# It's unlikely that a developer wanted to lint the script they wrote in a
|
11
|
+
# comment, so just ignore this case entirely and stop traversing the
|
12
|
+
# children of comment nodes.
|
13
|
+
end
|
14
|
+
|
6
15
|
def visit_script_stringinterpolation(node)
|
7
16
|
# We can't statically determine what the resultant string looks like when
|
8
17
|
# string interpolation is used, e.g. "one #{$var} three" could be a very
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Reports the use of literals for properties where variables are prefered.
|
3
|
+
class Linter::VariableForProperty < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_root(_node)
|
7
|
+
@properties = Set.new(config['properties'])
|
8
|
+
yield if @properties.any?
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit_prop(node)
|
12
|
+
property_name = node.name.join
|
13
|
+
return unless @properties.include? property_name
|
14
|
+
return if node.children.first.is_a?(Sass::Script::Tree::Variable)
|
15
|
+
|
16
|
+
add_lint(node, "Property #{property_name} should use " \
|
17
|
+
'a variable rather than a literal value')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -16,8 +16,8 @@ module SCSSLint
|
|
16
16
|
check_identifier(node, name.gsub(/^@/, ''))
|
17
17
|
|
18
18
|
# Check for values
|
19
|
-
return unless node.respond_to?(:value) && node.value.respond_to?(:
|
20
|
-
check_identifier(node, node.value.
|
19
|
+
return unless node.respond_to?(:value) && node.value.respond_to?(:source_range)
|
20
|
+
check_identifier(node, source_from_range(node.value.source_range))
|
21
21
|
end
|
22
22
|
|
23
23
|
alias_method :visit_prop, :check_node
|
@@ -31,7 +31,7 @@ module SCSSLint
|
|
31
31
|
|
32
32
|
# Strip vendor prefix to check against identifiers.
|
33
33
|
# (Also strip closing parentheticals from values like linear-gradient.)
|
34
|
-
stripped_identifier = identifier.gsub(/(^[_-][a-zA-Z0-9_]+-|\(.*\))/, '')
|
34
|
+
stripped_identifier = identifier.gsub(/(^[_-][a-zA-Z0-9_]+-|\(.*\)|;)/, '').strip
|
35
35
|
return if @exclusions.include?(stripped_identifier)
|
36
36
|
return unless @identifiers.include?(stripped_identifier)
|
37
37
|
|
data/lib/scss_lint/runner.rb
CHANGED
@@ -31,12 +31,10 @@ module SCSSLint
|
|
31
31
|
# @param file [String]
|
32
32
|
def find_lints(file)
|
33
33
|
engine = Engine.new(file)
|
34
|
-
config = @config.preferred ? @config : Config.for_file(file)
|
35
|
-
config ||= @config
|
36
34
|
|
37
35
|
@linters.each do |linter|
|
38
36
|
begin
|
39
|
-
run_linter(linter, engine,
|
37
|
+
run_linter(linter, engine, file)
|
40
38
|
rescue => error
|
41
39
|
raise SCSSLint::Exceptions::LinterError,
|
42
40
|
"#{linter.class} raised unexpected error linting file #{file}: " \
|
@@ -52,10 +50,10 @@ module SCSSLint
|
|
52
50
|
end
|
53
51
|
|
54
52
|
# For stubbing in tests.
|
55
|
-
def run_linter(linter, engine,
|
56
|
-
return unless config.linter_enabled?(linter)
|
57
|
-
return if config.excluded_file_for_linter?(file, linter)
|
58
|
-
linter.run(engine, config.linter_options(linter))
|
53
|
+
def run_linter(linter, engine, file)
|
54
|
+
return unless @config.linter_enabled?(linter)
|
55
|
+
return if @config.excluded_file_for_linter?(file, linter)
|
56
|
+
linter.run(engine, @config.linter_options(linter))
|
59
57
|
end
|
60
58
|
end
|
61
59
|
end
|
data/lib/scss_lint/utils.rb
CHANGED
@@ -1,6 +1,40 @@
|
|
1
1
|
module SCSSLint
|
2
2
|
# Collection of helpers used across a variety of linters.
|
3
3
|
module Utils
|
4
|
+
COLOR_REGEX = /^#[a-f0-9]{3,6}$/i
|
5
|
+
|
6
|
+
# Returns whether the given string is a color literal (keyword or hex code).
|
7
|
+
#
|
8
|
+
# @param string [String]
|
9
|
+
# @return [true,false]
|
10
|
+
def color?(string)
|
11
|
+
color_keyword?(string) || color_hex?(string)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns whether the given string is a color hexadecimal code.
|
15
|
+
#
|
16
|
+
# @param string [String]
|
17
|
+
# @return [true,false]
|
18
|
+
def color_hex?(string)
|
19
|
+
string =~ COLOR_REGEX
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns whether the given string is a valid color keyword.
|
23
|
+
#
|
24
|
+
# @param string [String]
|
25
|
+
# @return [true,false]
|
26
|
+
def color_keyword?(string)
|
27
|
+
color_keyword_to_code(string) && string != 'transparent'
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the hexadecimal code for the given color keyword.
|
31
|
+
#
|
32
|
+
# @param string [String]
|
33
|
+
# @return [String] 7-character hexadecimal code (includes `#` prefix)
|
34
|
+
def color_keyword_to_code(string)
|
35
|
+
Sass::Script::Value::Color::COLOR_NAMES[string]
|
36
|
+
end
|
37
|
+
|
4
38
|
# Given a selector array which is a list of strings with Sass::Script::Nodes
|
5
39
|
# interspersed within them, return an array of strings representing those
|
6
40
|
# selectors with the Sass::Script::Nodes removed (i.e., ignoring
|
data/lib/scss_lint/version.rb
CHANGED
data/spec/scss_lint/cli_spec.rb
CHANGED
@@ -62,7 +62,7 @@ describe SCSSLint::Config do
|
|
62
62
|
let(:config_file) { '' }
|
63
63
|
|
64
64
|
it 'returns the default configuration' do
|
65
|
-
subject.should == described_class.default
|
65
|
+
subject.options.should == described_class.default.options
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
@@ -70,7 +70,7 @@ describe SCSSLint::Config do
|
|
70
70
|
let(:config_file) { '# This is a comment' }
|
71
71
|
|
72
72
|
it 'returns the default configuration' do
|
73
|
-
subject.should == described_class.default
|
73
|
+
subject.options.should == described_class.default.options
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
@@ -88,7 +88,7 @@ describe SCSSLint::Config do
|
|
88
88
|
let(:config_file) { default_file }
|
89
89
|
|
90
90
|
it 'returns a configuration equivalent to the default' do
|
91
|
-
subject.should == described_class.default
|
91
|
+
subject.options.should == described_class.default.options
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
@@ -100,159 +100,7 @@ describe SCSSLint::Config do
|
|
100
100
|
FILE
|
101
101
|
|
102
102
|
it 'returns a configuration equivalent to the default' do
|
103
|
-
subject.should == described_class.default
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
context 'with a file that includes another configuration file' do
|
108
|
-
let(:included_file_path) { '../included_file.yml' }
|
109
|
-
|
110
|
-
let(:config_file) { <<-FILE }
|
111
|
-
inherit_from: #{included_file_path}
|
112
|
-
|
113
|
-
linters:
|
114
|
-
FakeConfigLinter:
|
115
|
-
enabled: true
|
116
|
-
some_other_option: some_other_value
|
117
|
-
FILE
|
118
|
-
|
119
|
-
before do
|
120
|
-
described_class.stub(:load_file_contents)
|
121
|
-
.with("#{config_dir}/" + included_file_path)
|
122
|
-
.and_return(included_file)
|
123
|
-
end
|
124
|
-
|
125
|
-
context 'and the included file has a different setting from the default' do
|
126
|
-
let(:included_file) { <<-FILE }
|
127
|
-
linters:
|
128
|
-
OtherFakeConfigLinter:
|
129
|
-
enabled: true
|
130
|
-
some_option: some_value
|
131
|
-
FILE
|
132
|
-
|
133
|
-
it 'reflects the different setting of the included file' do
|
134
|
-
subject.options['linters']['OtherFakeConfigLinter']
|
135
|
-
.should == { 'enabled' => true, 'some_option' => 'some_value' }
|
136
|
-
end
|
137
|
-
|
138
|
-
it 'reflects the different setting of the file that included the file' do
|
139
|
-
subject.options['linters']['FakeConfigLinter']
|
140
|
-
.should == { 'enabled' => true, 'some_other_option' => 'some_other_value' }
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
context 'and the included file has the same setting as the default' do
|
145
|
-
let(:included_file) { <<-FILE }
|
146
|
-
linters:
|
147
|
-
OtherFakeConfigLinter:
|
148
|
-
enabled: false
|
149
|
-
FILE
|
150
|
-
|
151
|
-
it 'does not alter the default configuration' do
|
152
|
-
subject.options['linters']['OtherFakeConfigLinter']
|
153
|
-
.should == { 'enabled' => false }
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'reflects the different setting of the file that included the file' do
|
157
|
-
subject.options['linters']['FakeConfigLinter']
|
158
|
-
.should == { 'enabled' => true, 'some_other_option' => 'some_other_value' }
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
context 'and the included file includes another file' do
|
163
|
-
let(:other_included_file_path) { '/some/abs/other_included_file.yml' }
|
164
|
-
|
165
|
-
let(:other_included_file) { <<-FILE }
|
166
|
-
linters:
|
167
|
-
OtherFakeConfigLinter:
|
168
|
-
yet_another_option: yet_another_value
|
169
|
-
FILE
|
170
|
-
|
171
|
-
let(:included_file) { <<-FILE }
|
172
|
-
inherit_from: #{other_included_file_path}
|
173
|
-
|
174
|
-
linters:
|
175
|
-
OtherFakeConfigLinter:
|
176
|
-
enabled: true
|
177
|
-
some_option: some_value
|
178
|
-
FILE
|
179
|
-
|
180
|
-
before do
|
181
|
-
described_class.stub(:load_file_contents)
|
182
|
-
.with(other_included_file_path)
|
183
|
-
.and_return(other_included_file)
|
184
|
-
end
|
185
|
-
|
186
|
-
it "reflects the different setting of the included file's included file" do
|
187
|
-
subject.options['linters']['OtherFakeConfigLinter']
|
188
|
-
.should == {
|
189
|
-
'enabled' => true,
|
190
|
-
'some_option' => 'some_value',
|
191
|
-
'yet_another_option' => 'yet_another_value',
|
192
|
-
}
|
193
|
-
end
|
194
|
-
|
195
|
-
it 'reflects the different setting of the file that included the file' do
|
196
|
-
subject.options['linters']['FakeConfigLinter']
|
197
|
-
.should == { 'enabled' => true, 'some_other_option' => 'some_other_value' }
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
context 'with a file that includes multiple configuration files' do
|
203
|
-
let(:included_file_path) { '../included_file.yml' }
|
204
|
-
let(:other_included_file_path) { '/some/dir/other_included_file.yml' }
|
205
|
-
|
206
|
-
let(:config_file) { <<-FILE }
|
207
|
-
inherit_from:
|
208
|
-
- #{included_file_path}
|
209
|
-
- #{other_included_file_path}
|
210
|
-
|
211
|
-
linters:
|
212
|
-
FakeConfigLinter:
|
213
|
-
enabled: true
|
214
|
-
some_other_option: some_other_value
|
215
|
-
FILE
|
216
|
-
|
217
|
-
before do
|
218
|
-
described_class.stub(:load_file_contents)
|
219
|
-
.with("#{config_dir}/" + included_file_path)
|
220
|
-
.and_return(included_file)
|
221
|
-
|
222
|
-
described_class.stub(:load_file_contents)
|
223
|
-
.with(other_included_file_path)
|
224
|
-
.and_return(other_included_file)
|
225
|
-
end
|
226
|
-
|
227
|
-
context 'and the included files have settings different from each other' do
|
228
|
-
let(:included_file) { <<-FILE }
|
229
|
-
linters:
|
230
|
-
OtherFakeConfigLinter:
|
231
|
-
enabled: true
|
232
|
-
some_option: earlier_value
|
233
|
-
some_other_option: value
|
234
|
-
FILE
|
235
|
-
|
236
|
-
let(:other_included_file) { <<-FILE }
|
237
|
-
linters:
|
238
|
-
OtherFakeConfigLinter:
|
239
|
-
enabled: true
|
240
|
-
some_option: later_value
|
241
|
-
FILE
|
242
|
-
|
243
|
-
it 'uses the value of the file that was included last' do
|
244
|
-
subject.options['linters']['OtherFakeConfigLinter']['some_option']
|
245
|
-
.should == 'later_value'
|
246
|
-
end
|
247
|
-
|
248
|
-
it 'loads settings from both included files' do
|
249
|
-
subject.options['linters']['OtherFakeConfigLinter']
|
250
|
-
.should == {
|
251
|
-
'enabled' => true,
|
252
|
-
'some_option' => 'later_value',
|
253
|
-
'some_other_option' => 'value',
|
254
|
-
}
|
255
|
-
end
|
103
|
+
subject.options.should == described_class.default.options
|
256
104
|
end
|
257
105
|
end
|
258
106
|
|
@@ -284,53 +132,6 @@ describe SCSSLint::Config do
|
|
284
132
|
end
|
285
133
|
end
|
286
134
|
|
287
|
-
describe '.for_file' do
|
288
|
-
include_context 'isolated environment'
|
289
|
-
|
290
|
-
let(:linted_file) { File.join('foo', 'bar', 'baz', 'file-being-linted.scss') }
|
291
|
-
subject { described_class.for_file(linted_file) }
|
292
|
-
|
293
|
-
before do
|
294
|
-
described_class.instance_variable_set(:@dir_to_config, nil) # Clear cache
|
295
|
-
FileUtils.mkpath(File.dirname(linted_file))
|
296
|
-
FileUtils.touch(linted_file)
|
297
|
-
end
|
298
|
-
|
299
|
-
context 'when there are no configuration files in the directory hierarchy' do
|
300
|
-
it { should be_nil }
|
301
|
-
end
|
302
|
-
|
303
|
-
context 'when there is a configuration file in the same directory' do
|
304
|
-
let(:config_file) { File.join('foo', 'bar', 'baz', '.scss-lint.yml') }
|
305
|
-
before { FileUtils.touch(config_file) }
|
306
|
-
|
307
|
-
it 'loads that configuration file' do
|
308
|
-
described_class.should_receive(:load).with(File.expand_path(config_file))
|
309
|
-
subject
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
context 'when there is a configuration file in the parent directory' do
|
314
|
-
let(:config_file) { File.join('foo', 'bar', '.scss-lint.yml') }
|
315
|
-
before { FileUtils.touch(config_file) }
|
316
|
-
|
317
|
-
it 'loads that configuration file' do
|
318
|
-
described_class.should_receive(:load).with(File.expand_path(config_file))
|
319
|
-
subject
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
context 'when there is a configuration file in some ancestor directory' do
|
324
|
-
let(:config_file) { File.join('foo', '.scss-lint.yml') }
|
325
|
-
before { FileUtils.touch(config_file) }
|
326
|
-
|
327
|
-
it 'loads that configuration file' do
|
328
|
-
described_class.should_receive(:load).with(File.expand_path(config_file))
|
329
|
-
subject
|
330
|
-
end
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
135
|
describe '#linter_options' do
|
335
136
|
let(:config) { described_class.new(options) }
|
336
137
|
|