forked from sds/scss-lint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
selector_format.rb
102 lines (84 loc) · 3.09 KB
/
selector_format.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
module SCSSLint
# Checks that selector names use a specified convention
class Linter::SelectorFormat < Linter
include LinterRegistry
def visit_root(_node)
@ignored_names = Array(config['ignored_names']).to_set
@ignored_types = Array(config['ignored_types']).to_set
yield
end
def visit_attribute(attribute)
check(attribute, 'attribute') unless @ignored_types.include?('attribute')
end
def visit_class(klass)
check(klass, 'class') unless @ignored_types.include?('class')
end
def visit_element(element)
check(element, 'element') unless @ignored_types.include?('element')
end
def visit_id(id)
check(id, 'id') unless @ignored_types.include?('id')
end
def visit_placeholder(placeholder)
check(placeholder, 'placeholder') unless @ignored_types.include?('placeholder')
end
private
def check(node, type)
name = node.name
return if @ignored_names.include?(name)
return unless violation = violated_convention(name, type)
add_lint(node, "Selector `#{name}` #{violation[:explanation]}")
end
CONVENTIONS = {
'hyphenated_lowercase' => {
explanation: 'should be written in lowercase with hyphens',
validator: ->(name) { name !~ /[^\-a-z0-9]/ },
},
'snake_case' => {
explanation: 'should be written in lowercase with underscores',
validator: ->(name) { name !~ /[^_a-z0-9]/ },
},
'camel_case' => {
explanation: 'should be written in camelCase format',
validator: ->(name) { name =~ /^[a-z][a-zA-Z0-9]*$/ },
},
'hyphenated_BEM' => {
explanation: 'should be written in hyphenated BEM (Block Element Modifier) format',
validator: ->(name) { name !~ /[A-Z]|-{3}|_{3}|[^_]_[^_]/ },
},
'strict_BEM' => {
explanation: 'should be written in BEM (Block Element Modifier) format',
validator: lambda do |name|
name =~ /
^[a-z]([-]?[a-z0-9]+)*
(__[a-z0-9]([-]?[a-z0-9]+)*)?
((_[a-z0-9]([-]?[a-z0-9]+)*){2})?$
/x
end,
},
}.freeze
# Checks the given name and returns the violated convention if it failed.
def violated_convention(name_string, type)
convention_name = convention_name(type)
existing_convention = CONVENTIONS[convention_name]
convention = (existing_convention || {
validator: ->(name) { name =~ /#{convention_name}/ }
}).merge(
explanation: convention_explanation(type), # Allow explanation to be customized
)
convention unless convention[:validator].call(name_string)
end
def convention_name(type)
config["#{type}_convention"] ||
config['convention'] ||
'hyphenated_lowercase'
end
def convention_explanation(type)
existing_convention = CONVENTIONS[convention_name(type)]
config["#{type}_convention_explanation"] ||
config['convention_explanation'] ||
(existing_convention && existing_convention[:explanation]) ||
"should match regex /#{convention_name(type)}/"
end
end
end