scss-lint 0.29.0 → 0.30.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 +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
|