scss-lint 0.29.0 → 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +31 -1
- data/data/prefixed-identifiers/base.txt +107 -0
- data/data/prefixed-identifiers/bourbon.txt +71 -0
- data/lib/scss_lint/cli.rb +34 -7
- data/lib/scss_lint/config.rb +12 -1
- data/lib/scss_lint/engine.rb +3 -1
- data/lib/scss_lint/linter/bang_format.rb +40 -0
- data/lib/scss_lint/linter/declaration_order.rb +35 -14
- data/lib/scss_lint/linter/import_path.rb +62 -0
- data/lib/scss_lint/linter/name_format.rb +1 -1
- data/lib/scss_lint/linter/nesting_depth.rb +24 -0
- data/lib/scss_lint/linter/property_sort_order.rb +4 -11
- data/lib/scss_lint/linter/property_spelling.rb +25 -8
- data/lib/scss_lint/linter/qualifying_element.rb +42 -0
- data/lib/scss_lint/linter/selector_format.rb +23 -11
- data/lib/scss_lint/linter/space_after_property_colon.rb +4 -4
- data/lib/scss_lint/linter/space_after_property_name.rb +16 -1
- data/lib/scss_lint/linter/space_before_brace.rb +36 -9
- data/lib/scss_lint/linter/trailing_semicolon.rb +6 -2
- data/lib/scss_lint/linter/vendor_prefixes.rb +64 -0
- data/lib/scss_lint/rake_task.rb +1 -0
- data/lib/scss_lint/runner.rb +2 -1
- data/lib/scss_lint/sass/script.rb +10 -0
- data/lib/scss_lint/version.rb +1 -1
- data/spec/scss_lint/cli_spec.rb +45 -2
- data/spec/scss_lint/linter/bang_format_spec.rb +79 -0
- data/spec/scss_lint/linter/declaration_order_spec.rb +466 -0
- data/spec/scss_lint/linter/import_path_spec.rb +300 -0
- data/spec/scss_lint/linter/nesting_depth_spec.rb +114 -0
- data/spec/scss_lint/linter/property_spelling_spec.rb +27 -0
- data/spec/scss_lint/linter/qualifying_element_spec.rb +125 -0
- data/spec/scss_lint/linter/selector_format_spec.rb +329 -0
- data/spec/scss_lint/linter/space_after_property_colon_spec.rb +14 -0
- data/spec/scss_lint/linter/space_after_property_name_spec.rb +14 -0
- data/spec/scss_lint/linter/space_before_brace_spec.rb +401 -17
- data/spec/scss_lint/linter/trailing_semicolon_spec.rb +47 -0
- data/spec/scss_lint/linter/vendor_prefixes_spec.rb +350 -0
- metadata +19 -2
@@ -0,0 +1,62 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks formatting of the basenames of @imported partials
|
3
|
+
class Linter::ImportPath < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_import(node)
|
7
|
+
# Ignore CSS imports
|
8
|
+
return if File.extname(node.imported_filename) == '.css'
|
9
|
+
basename = File.basename(node.imported_filename)
|
10
|
+
return if underscore_ok?(basename) && extension_ok?(basename)
|
11
|
+
add_lint(node, compose_message(node.imported_filename))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# Checks if the presence or absence of a leading underscore
|
17
|
+
# on a string is ok, given config option.
|
18
|
+
#
|
19
|
+
# @param str [String] the string to check
|
20
|
+
# @return [Boolean]
|
21
|
+
def underscore_ok?(str)
|
22
|
+
underscore_exists = str.start_with?('_')
|
23
|
+
config['leading_underscore'] ? underscore_exists : !underscore_exists
|
24
|
+
end
|
25
|
+
|
26
|
+
# Checks if the presence or absence of an `scss` filename
|
27
|
+
# extension on a string is ok, given config option.
|
28
|
+
#
|
29
|
+
# @param str [String] the string to check
|
30
|
+
# @return [Boolean]
|
31
|
+
def extension_ok?(str)
|
32
|
+
extension_exists = str.end_with?('.scss')
|
33
|
+
config['filename_extension'] ? extension_exists : !extension_exists
|
34
|
+
end
|
35
|
+
|
36
|
+
# Composes a helpful lint message based on the original filename
|
37
|
+
# and the config options.
|
38
|
+
#
|
39
|
+
# @param orig_filename [String] the original filename
|
40
|
+
# @return [String] the helpful lint message
|
41
|
+
def compose_message(orig_filename)
|
42
|
+
orig_basename = File.basename(orig_filename)
|
43
|
+
fixed_basename = orig_basename
|
44
|
+
|
45
|
+
if config['leading_underscore']
|
46
|
+
fixed_basename = '_' + fixed_basename unless fixed_basename.match(/^_/)
|
47
|
+
else
|
48
|
+
fixed_basename = fixed_basename.sub(/^_/, '')
|
49
|
+
end
|
50
|
+
|
51
|
+
if config['filename_extension']
|
52
|
+
fixed_basename += '.scss' unless fixed_basename.match(/\.scss$/)
|
53
|
+
else
|
54
|
+
fixed_basename = fixed_basename.sub(/\.scss$/, '')
|
55
|
+
end
|
56
|
+
|
57
|
+
fixed_filename = orig_filename.sub(/(.*)#{Regexp.quote(orig_basename)}/,
|
58
|
+
"\\1#{fixed_basename}")
|
59
|
+
"Imported partial `#{orig_filename}` should be written as `#{fixed_filename}`"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -60,7 +60,7 @@ module SCSSLint
|
|
60
60
|
|
61
61
|
CONVENTIONS = {
|
62
62
|
'hyphenated_lowercase' => {
|
63
|
-
explanation: 'in lowercase with hyphens instead of underscores',
|
63
|
+
explanation: 'in all lowercase letters with hyphens instead of underscores',
|
64
64
|
validator: ->(name) { name !~ /[_A-Z]/ },
|
65
65
|
},
|
66
66
|
'BEM' => {
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for rule sets nested deeper than a specified maximum depth.
|
3
|
+
class Linter::NestingDepth < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_root(_node)
|
7
|
+
@max_depth = config['max_depth']
|
8
|
+
@depth = 1
|
9
|
+
yield # Continue linting children
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_rule(node)
|
13
|
+
if @depth > @max_depth
|
14
|
+
add_lint(node, "Nesting should be no greater than #{@max_depth}, but was #{@depth}")
|
15
|
+
else
|
16
|
+
# Only continue if we didn't exceed the max depth already (this makes
|
17
|
+
# the lint less noisy)
|
18
|
+
@depth += 1
|
19
|
+
yield # Continue linting children
|
20
|
+
@depth -= 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -26,7 +26,7 @@ module SCSSLint
|
|
26
26
|
sorted_props.each_with_index do |prop, index|
|
27
27
|
next unless prop != sortable_prop_info[index]
|
28
28
|
|
29
|
-
add_lint(sortable_props[index], lint_message)
|
29
|
+
add_lint(sortable_props[index], lint_message(sorted_props))
|
30
30
|
break
|
31
31
|
end
|
32
32
|
|
@@ -127,16 +127,9 @@ module SCSSLint
|
|
127
127
|
config['order'].is_a?(String)
|
128
128
|
end
|
129
129
|
|
130
|
-
def lint_message
|
131
|
-
|
132
|
-
|
133
|
-
elsif @preferred_order
|
134
|
-
'Properties should be sorted according to the custom order ' \
|
135
|
-
'specified by the configuration'
|
136
|
-
else
|
137
|
-
'Properties should be sorted in order, with vendor-prefixed ' \
|
138
|
-
'extensions before the standardized CSS property'
|
139
|
-
end
|
130
|
+
def lint_message(sortable_prop_info)
|
131
|
+
props = sortable_prop_info.map { |prop| prop[:name] }.join(', ')
|
132
|
+
"Properties should be ordered #{props}"
|
140
133
|
end
|
141
134
|
end
|
142
135
|
end
|
@@ -3,6 +3,11 @@ module SCSSLint
|
|
3
3
|
class Linter::PropertySpelling < Linter
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
|
+
KNOWN_PROPERTIES = File.open(File.join(SCSS_LINT_DATA, 'properties.txt'))
|
7
|
+
.read
|
8
|
+
.split
|
9
|
+
.to_set
|
10
|
+
|
6
11
|
def visit_root(_node)
|
7
12
|
@extra_properties = config['extra_properties'].to_set
|
8
13
|
yield # Continue linting children
|
@@ -12,7 +17,26 @@ module SCSSLint
|
|
12
17
|
# Ignore properties with interpolation
|
13
18
|
return if node.name.count > 1 || !node.name.first.is_a?(String)
|
14
19
|
|
15
|
-
|
20
|
+
nested_properties = node.children.select { |child| child.is_a?(Sass::Tree::PropNode) }
|
21
|
+
if nested_properties.any?
|
22
|
+
# Treat nested properties specially, as they are a concatenation of the
|
23
|
+
# parent with child property
|
24
|
+
nested_properties.each do |nested_prop|
|
25
|
+
check_property(nested_prop, node.name.join)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
check_property(node)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def check_property(node, prefix = nil) # rubocop:disable CyclomaticComplexity
|
35
|
+
# Ignore properties with interpolation
|
36
|
+
return if node.name.count > 1 || !node.name.first.is_a?(String)
|
37
|
+
|
38
|
+
name = prefix ? "#{prefix}-" : ''
|
39
|
+
name += node.name.join
|
16
40
|
|
17
41
|
# Ignore vendor-prefixed properties
|
18
42
|
return if name.start_with?('-')
|
@@ -21,12 +45,5 @@ module SCSSLint
|
|
21
45
|
|
22
46
|
add_lint(node, "Unknown property #{name}")
|
23
47
|
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
KNOWN_PROPERTIES = File.open(File.join(SCSS_LINT_DATA, 'properties.txt'))
|
28
|
-
.read
|
29
|
-
.split
|
30
|
-
.to_set
|
31
48
|
end
|
32
49
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for element selectors qualifying id, classe, or attribute selectors.
|
3
|
+
class Linter::QualifyingElement < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_simple_sequence(seq)
|
7
|
+
return unless seq_contains_sel_class?(seq, Sass::Selector::Element)
|
8
|
+
check_id(seq) unless config['allow_element_with_id']
|
9
|
+
check_class(seq) unless config['allow_element_with_class']
|
10
|
+
check_attribute(seq) unless config['allow_element_with_attribute']
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Checks if a simple sequence contains a
|
16
|
+
# simple selector of a certain class.
|
17
|
+
#
|
18
|
+
# @param seq [Sass::Selector::SimpleSequence]
|
19
|
+
# @param selector_class [Sass::Selector::Simple]
|
20
|
+
# @returns [Boolean]
|
21
|
+
def seq_contains_sel_class?(seq, selector_class)
|
22
|
+
seq.members.any? do |simple|
|
23
|
+
simple.is_a?(selector_class)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_id(seq)
|
28
|
+
return unless seq_contains_sel_class?(seq, Sass::Selector::Id)
|
29
|
+
add_lint(seq.line, 'Avoid qualifying id selectors with an element.')
|
30
|
+
end
|
31
|
+
|
32
|
+
def check_class(seq)
|
33
|
+
return unless seq_contains_sel_class?(seq, Sass::Selector::Class)
|
34
|
+
add_lint(seq.line, 'Avoid qualifying class selectors with an element.')
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_attribute(seq)
|
38
|
+
return unless seq_contains_sel_class?(seq, Sass::Selector::Attribute)
|
39
|
+
add_lint(seq.line, 'Avoid qualifying attribute selectors with an element.')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -10,36 +10,36 @@ module SCSSLint
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def visit_attribute(attribute)
|
13
|
-
check(attribute) unless @ignored_types.include?('attribute')
|
13
|
+
check(attribute, 'attribute') unless @ignored_types.include?('attribute')
|
14
14
|
end
|
15
15
|
|
16
16
|
def visit_class(klass)
|
17
|
-
check(klass) unless @ignored_types.include?('class')
|
17
|
+
check(klass, 'class') unless @ignored_types.include?('class')
|
18
18
|
end
|
19
19
|
|
20
20
|
def visit_element(element)
|
21
|
-
check(element) unless @ignored_types.include?('element')
|
21
|
+
check(element, 'element') unless @ignored_types.include?('element')
|
22
22
|
end
|
23
23
|
|
24
24
|
def visit_id(id)
|
25
|
-
check(id) unless @ignored_types.include?('id')
|
25
|
+
check(id, 'id') unless @ignored_types.include?('id')
|
26
26
|
end
|
27
27
|
|
28
28
|
def visit_placeholder(placeholder)
|
29
|
-
check(placeholder) unless @ignored_types.include?('placeholder')
|
29
|
+
check(placeholder, 'placeholder') unless @ignored_types.include?('placeholder')
|
30
30
|
end
|
31
31
|
|
32
32
|
def visit_pseudo(pseudo)
|
33
|
-
check(pseudo) unless @ignored_types.include?('pseudo-selector')
|
33
|
+
check(pseudo, 'pseudo') unless @ignored_types.include?('pseudo-selector')
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
-
def check(node)
|
38
|
+
def check(node, type)
|
39
39
|
name = node.name
|
40
40
|
|
41
41
|
return if @ignored_names.include?(name)
|
42
|
-
return unless violation = violated_convention(name)
|
42
|
+
return unless violation = violated_convention(name, type)
|
43
43
|
|
44
44
|
add_lint(node, "Selector `#{name}` should be " \
|
45
45
|
"written #{violation[:explanation]}")
|
@@ -58,15 +58,27 @@ module SCSSLint
|
|
58
58
|
explanation: 'has no spaces with capitalized words except first',
|
59
59
|
validator: ->(name) { name =~ /^[a-z][a-zA-Z0-9]*$/ },
|
60
60
|
},
|
61
|
+
'hyphenated_BEM' => {
|
62
|
+
explanation: 'in hyphenated BEM (Block Element Modifier) format',
|
63
|
+
validator: ->(name) { name !~ /[A-Z]|-{3}|_{3}|[^_]_[^_]/ },
|
64
|
+
},
|
61
65
|
'BEM' => {
|
62
66
|
explanation: 'in BEM (Block Element Modifier) format',
|
63
|
-
validator:
|
67
|
+
validator: lambda do |name|
|
68
|
+
name =~ /
|
69
|
+
^[a-z]([-]?[a-z0-9]+)*
|
70
|
+
(__[a-z0-9]([-]?[a-z0-9]+)*)?
|
71
|
+
((_[a-z0-9]([-]?[a-z0-9]+)*){2})?$
|
72
|
+
/x
|
73
|
+
end,
|
64
74
|
},
|
65
75
|
}
|
66
76
|
|
67
77
|
# Checks the given name and returns the violated convention if it failed.
|
68
|
-
def violated_convention(name_string)
|
69
|
-
convention_name = config[
|
78
|
+
def violated_convention(name_string, type)
|
79
|
+
convention_name = config["#{type}_convention"] ||
|
80
|
+
config['convention'] ||
|
81
|
+
'hyphenated_lowercase'
|
70
82
|
|
71
83
|
convention = CONVENTIONS[convention_name] || {
|
72
84
|
explanation: "must match regex /#{convention_name}/",
|
@@ -65,13 +65,13 @@ module SCSSLint
|
|
65
65
|
spaces = 0
|
66
66
|
offset = 1
|
67
67
|
|
68
|
-
#
|
69
|
-
|
70
|
-
if character_at(node.name_source_range.end_pos, offset) == ':'
|
68
|
+
# Find the colon after the property name
|
69
|
+
while character_at(node.name_source_range.start_pos, offset - 1) != ':'
|
71
70
|
offset += 1
|
72
71
|
end
|
73
72
|
|
74
|
-
|
73
|
+
# Count spaces after the colon
|
74
|
+
while character_at(node.name_source_range.start_pos, offset) == ' '
|
75
75
|
spaces += 1
|
76
76
|
offset += 1
|
77
77
|
end
|
@@ -5,8 +5,23 @@ module SCSSLint
|
|
5
5
|
include LinterRegistry
|
6
6
|
|
7
7
|
def visit_prop(node)
|
8
|
-
|
8
|
+
offset = property_name_colon_offset(node)
|
9
|
+
return unless character_at(node.name_source_range.start_pos, offset - 1) == ' '
|
9
10
|
add_lint node, 'Property name should be immediately followed by a colon'
|
10
11
|
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Deals with a weird Sass bug where the name_source_range of a PropNode does
|
16
|
+
# not start at the beginning of the property name.
|
17
|
+
def property_name_colon_offset(node)
|
18
|
+
offset = 0
|
19
|
+
|
20
|
+
while character_at(node.name_source_range.start_pos, offset) != ':'
|
21
|
+
offset += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
offset
|
25
|
+
end
|
11
26
|
end
|
12
27
|
end
|
@@ -27,19 +27,46 @@ module SCSSLint
|
|
27
27
|
|
28
28
|
def check_for_space(node, string)
|
29
29
|
line = node.source_range.end_pos.line
|
30
|
-
char_before_is_whitespace = ["\n", ' '].include?(string[-2])
|
31
30
|
|
32
31
|
if config['allow_single_line_padding'] && node_on_single_line?(node)
|
33
|
-
unless
|
34
|
-
|
35
|
-
|
36
|
-
end
|
32
|
+
return unless string[-2] != ' '
|
33
|
+
add_lint(line, 'Opening curly brace in a single line rule set '\
|
34
|
+
'`{` should be preceded by at least one space')
|
37
35
|
else
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
return unless chars_before_incorrect(string)
|
37
|
+
style_message = (config['style'] == 'new_line') ? 'a new line' : 'one space'
|
38
|
+
add_lint(line, 'Opening curly brace `{` should be ' \
|
39
|
+
"preceded by #{style_message}")
|
42
40
|
end
|
43
41
|
end
|
42
|
+
|
43
|
+
# Check if the characters before the end of the string
|
44
|
+
# are not what they should be
|
45
|
+
def chars_before_incorrect(string)
|
46
|
+
if config['style'] != 'new_line'
|
47
|
+
return !single_space_before(string)
|
48
|
+
end
|
49
|
+
!newline_before_nonwhitespace(string)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Check if there is one space and only one
|
53
|
+
# space before the end of the string
|
54
|
+
def single_space_before(string)
|
55
|
+
return false if string[-2] != ' '
|
56
|
+
return false if string[-3] == ' '
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
# Check if, starting from the end of a string
|
61
|
+
# and moving backwards, towards the beginning,
|
62
|
+
# we find a new line before any non-whitespace characters
|
63
|
+
def newline_before_nonwhitespace(string)
|
64
|
+
offset = -2
|
65
|
+
while /\S/.match(string[offset]).nil?
|
66
|
+
return true if string[offset] == "\n"
|
67
|
+
offset -= 1
|
68
|
+
end
|
69
|
+
false
|
70
|
+
end
|
44
71
|
end
|
45
72
|
end
|
@@ -27,6 +27,10 @@ module SCSSLint
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
def visit_import(node)
|
31
|
+
check_semicolon(node)
|
32
|
+
end
|
33
|
+
|
30
34
|
private
|
31
35
|
|
32
36
|
def check_semicolon(node)
|
@@ -46,7 +50,7 @@ module SCSSLint
|
|
46
50
|
|
47
51
|
# Checks that the node is ended by a semicolon (with no whitespace)
|
48
52
|
def ends_with_semicolon?(node)
|
49
|
-
source_from_range(node.source_range) =~
|
53
|
+
source_from_range(node.source_range) =~ /;(\s*})?$/
|
50
54
|
end
|
51
55
|
|
52
56
|
def ends_with_multiple_semicolons?(node)
|
@@ -55,7 +59,7 @@ module SCSSLint
|
|
55
59
|
end
|
56
60
|
|
57
61
|
def has_space_before_semicolon?(node)
|
58
|
-
source_from_range(node.source_range) =~ /\s
|
62
|
+
source_from_range(node.source_range) =~ /\s;(\s*})?$/
|
59
63
|
end
|
60
64
|
end
|
61
65
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for vendor prefixes.
|
3
|
+
class Linter::VendorPrefixes < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_root(_node)
|
7
|
+
@identifiers = Set.new(extract_identifiers_from_config)
|
8
|
+
@identifiers.merge(Set.new(config['include']))
|
9
|
+
@exclusions = Set.new(config['exclude'])
|
10
|
+
yield
|
11
|
+
end
|
12
|
+
|
13
|
+
def check_node(node)
|
14
|
+
name = node.name.is_a?(Array) ? node.name.join : node.name
|
15
|
+
# Ignore '@' from @keyframes node name
|
16
|
+
check_identifier(node, name.gsub(/^@/, ''))
|
17
|
+
|
18
|
+
# Check for values
|
19
|
+
return unless node.respond_to?(:value) && node.value.respond_to?(:to_sass)
|
20
|
+
check_identifier(node, node.value.to_sass)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :visit_prop, :check_node
|
24
|
+
alias_method :visit_pseudo, :check_node
|
25
|
+
alias_method :visit_directive, :check_node
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def check_identifier(node, identifier)
|
30
|
+
return unless identifier =~ /^[_-]/
|
31
|
+
|
32
|
+
# Strip vendor prefix to check against identifiers.
|
33
|
+
# (Also strip closing parentheticals from values like linear-gradient.)
|
34
|
+
stripped_identifier = identifier.gsub(/(^[_-][a-zA-Z0-9_]+-|\(.*\))/, '')
|
35
|
+
return if @exclusions.include?(stripped_identifier)
|
36
|
+
return unless @identifiers.include?(stripped_identifier)
|
37
|
+
|
38
|
+
add_lint(node, 'Avoid vendor prefixes.')
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_identifiers_from_config
|
42
|
+
case config['identifier_list']
|
43
|
+
when nil
|
44
|
+
nil
|
45
|
+
when Array
|
46
|
+
config['identifier_list']
|
47
|
+
when String
|
48
|
+
begin
|
49
|
+
file = File.open(File.join(SCSS_LINT_DATA,
|
50
|
+
'prefixed-identifiers',
|
51
|
+
"#{config['identifier_list']}.txt"))
|
52
|
+
file.read.split("\n").reject { |line| line =~ /^(#|\s*$)/ }
|
53
|
+
rescue Errno::ENOENT
|
54
|
+
raise SCSSLint::Exceptions::LinterError,
|
55
|
+
"Identifier list '#{config['identifier_list']}' does not exist"
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise SCSSLint::Exceptions::LinterError,
|
59
|
+
'Invalid identifier list specified -- must be the name of a '\
|
60
|
+
'preset or an array of strings'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|