scss-lint-bliss 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/scss-lint +6 -0
- data/config/default.yml +220 -0
- data/data/prefixed-identifiers/base.txt +107 -0
- data/data/prefixed-identifiers/bourbon.txt +71 -0
- data/data/properties.txt +477 -0
- data/data/property-sort-orders/concentric.txt +134 -0
- data/data/property-sort-orders/recess.txt +149 -0
- data/data/property-sort-orders/smacss.txt +137 -0
- data/lib/scss_lint.rb +31 -0
- data/lib/scss_lint/cli.rb +215 -0
- data/lib/scss_lint/config.rb +251 -0
- data/lib/scss_lint/constants.rb +8 -0
- data/lib/scss_lint/control_comment_processor.rb +126 -0
- data/lib/scss_lint/engine.rb +56 -0
- data/lib/scss_lint/exceptions.rb +21 -0
- data/lib/scss_lint/file_finder.rb +68 -0
- data/lib/scss_lint/lint.rb +24 -0
- data/lib/scss_lint/linter.rb +161 -0
- data/lib/scss_lint/linter/bang_format.rb +52 -0
- data/lib/scss_lint/linter/bliss.rb +8 -0
- data/lib/scss_lint/linter/bliss/module.rb +101 -0
- data/lib/scss_lint/linter/border_zero.rb +39 -0
- data/lib/scss_lint/linter/color_keyword.rb +32 -0
- data/lib/scss_lint/linter/color_variable.rb +49 -0
- data/lib/scss_lint/linter/comment.rb +21 -0
- data/lib/scss_lint/linter/compass.rb +7 -0
- data/lib/scss_lint/linter/compass/property_with_mixin.rb +47 -0
- data/lib/scss_lint/linter/debug_statement.rb +10 -0
- data/lib/scss_lint/linter/declaration_order.rb +71 -0
- data/lib/scss_lint/linter/duplicate_property.rb +58 -0
- data/lib/scss_lint/linter/else_placement.rb +48 -0
- data/lib/scss_lint/linter/empty_line_between_blocks.rb +85 -0
- data/lib/scss_lint/linter/empty_rule.rb +11 -0
- data/lib/scss_lint/linter/final_newline.rb +20 -0
- data/lib/scss_lint/linter/hex_length.rb +56 -0
- data/lib/scss_lint/linter/hex_notation.rb +38 -0
- data/lib/scss_lint/linter/hex_validation.rb +23 -0
- data/lib/scss_lint/linter/id_selector.rb +10 -0
- data/lib/scss_lint/linter/import_path.rb +62 -0
- data/lib/scss_lint/linter/important_rule.rb +12 -0
- data/lib/scss_lint/linter/indentation.rb +197 -0
- data/lib/scss_lint/linter/leading_zero.rb +49 -0
- data/lib/scss_lint/linter/mergeable_selector.rb +60 -0
- data/lib/scss_lint/linter/name_format.rb +117 -0
- data/lib/scss_lint/linter/nesting_depth.rb +24 -0
- data/lib/scss_lint/linter/placeholder_in_extend.rb +22 -0
- data/lib/scss_lint/linter/property_count.rb +44 -0
- data/lib/scss_lint/linter/property_sort_order.rb +189 -0
- data/lib/scss_lint/linter/property_spelling.rb +49 -0
- data/lib/scss_lint/linter/property_units.rb +59 -0
- data/lib/scss_lint/linter/qualifying_element.rb +42 -0
- data/lib/scss_lint/linter/selector_depth.rb +64 -0
- data/lib/scss_lint/linter/selector_format.rb +102 -0
- data/lib/scss_lint/linter/shorthand.rb +139 -0
- data/lib/scss_lint/linter/single_line_per_property.rb +59 -0
- data/lib/scss_lint/linter/single_line_per_selector.rb +35 -0
- data/lib/scss_lint/linter/space_after_comma.rb +110 -0
- data/lib/scss_lint/linter/space_after_property_colon.rb +84 -0
- data/lib/scss_lint/linter/space_after_property_name.rb +27 -0
- data/lib/scss_lint/linter/space_before_brace.rb +72 -0
- data/lib/scss_lint/linter/space_between_parens.rb +35 -0
- data/lib/scss_lint/linter/string_quotes.rb +94 -0
- data/lib/scss_lint/linter/trailing_semicolon.rb +67 -0
- data/lib/scss_lint/linter/trailing_zero.rb +41 -0
- data/lib/scss_lint/linter/unnecessary_mantissa.rb +42 -0
- data/lib/scss_lint/linter/unnecessary_parent_reference.rb +49 -0
- data/lib/scss_lint/linter/url_format.rb +56 -0
- data/lib/scss_lint/linter/url_quotes.rb +27 -0
- data/lib/scss_lint/linter/variable_for_property.rb +30 -0
- data/lib/scss_lint/linter/vendor_prefix.rb +64 -0
- data/lib/scss_lint/linter/zero_unit.rb +39 -0
- data/lib/scss_lint/linter_registry.rb +26 -0
- data/lib/scss_lint/location.rb +38 -0
- data/lib/scss_lint/options.rb +109 -0
- data/lib/scss_lint/rake_task.rb +106 -0
- data/lib/scss_lint/reporter.rb +18 -0
- data/lib/scss_lint/reporter/config_reporter.rb +26 -0
- data/lib/scss_lint/reporter/default_reporter.rb +27 -0
- data/lib/scss_lint/reporter/files_reporter.rb +8 -0
- data/lib/scss_lint/reporter/json_reporter.rb +30 -0
- data/lib/scss_lint/reporter/xml_reporter.rb +33 -0
- data/lib/scss_lint/runner.rb +51 -0
- data/lib/scss_lint/sass/script.rb +78 -0
- data/lib/scss_lint/sass/tree.rb +168 -0
- data/lib/scss_lint/selector_visitor.rb +34 -0
- data/lib/scss_lint/utils.rb +112 -0
- data/lib/scss_lint/version.rb +4 -0
- data/spec/scss_lint/cli_spec.rb +177 -0
- data/spec/scss_lint/config_spec.rb +253 -0
- data/spec/scss_lint/engine_spec.rb +24 -0
- data/spec/scss_lint/file_finder_spec.rb +134 -0
- data/spec/scss_lint/linter/bang_format_spec.rb +121 -0
- data/spec/scss_lint/linter/bliss/module_spec.rb +254 -0
- data/spec/scss_lint/linter/border_zero_spec.rb +118 -0
- data/spec/scss_lint/linter/color_keyword_spec.rb +83 -0
- data/spec/scss_lint/linter/color_variable_spec.rb +145 -0
- data/spec/scss_lint/linter/comment_spec.rb +79 -0
- data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +55 -0
- data/spec/scss_lint/linter/debug_statement_spec.rb +21 -0
- data/spec/scss_lint/linter/declaration_order_spec.rb +575 -0
- data/spec/scss_lint/linter/duplicate_property_spec.rb +189 -0
- data/spec/scss_lint/linter/else_placement_spec.rb +106 -0
- data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +276 -0
- data/spec/scss_lint/linter/empty_rule_spec.rb +27 -0
- data/spec/scss_lint/linter/final_newline_spec.rb +49 -0
- data/spec/scss_lint/linter/hex_length_spec.rb +104 -0
- data/spec/scss_lint/linter/hex_notation_spec.rb +104 -0
- data/spec/scss_lint/linter/hex_validation_spec.rb +40 -0
- data/spec/scss_lint/linter/id_selector_spec.rb +62 -0
- data/spec/scss_lint/linter/import_path_spec.rb +300 -0
- data/spec/scss_lint/linter/important_rule_spec.rb +43 -0
- data/spec/scss_lint/linter/indentation_spec.rb +347 -0
- data/spec/scss_lint/linter/leading_zero_spec.rb +233 -0
- data/spec/scss_lint/linter/mergeable_selector_spec.rb +283 -0
- data/spec/scss_lint/linter/name_format_spec.rb +282 -0
- data/spec/scss_lint/linter/nesting_depth_spec.rb +114 -0
- data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +63 -0
- data/spec/scss_lint/linter/property_count_spec.rb +104 -0
- data/spec/scss_lint/linter/property_sort_order_spec.rb +426 -0
- data/spec/scss_lint/linter/property_spelling_spec.rb +84 -0
- data/spec/scss_lint/linter/property_units_spec.rb +229 -0
- data/spec/scss_lint/linter/qualifying_element_spec.rb +125 -0
- data/spec/scss_lint/linter/selector_depth_spec.rb +159 -0
- data/spec/scss_lint/linter/selector_format_spec.rb +632 -0
- data/spec/scss_lint/linter/shorthand_spec.rb +198 -0
- data/spec/scss_lint/linter/single_line_per_property_spec.rb +73 -0
- data/spec/scss_lint/linter/single_line_per_selector_spec.rb +130 -0
- data/spec/scss_lint/linter/space_after_comma_spec.rb +332 -0
- data/spec/scss_lint/linter/space_after_property_colon_spec.rb +264 -0
- data/spec/scss_lint/linter/space_after_property_name_spec.rb +37 -0
- data/spec/scss_lint/linter/space_before_brace_spec.rb +829 -0
- data/spec/scss_lint/linter/space_between_parens_spec.rb +263 -0
- data/spec/scss_lint/linter/string_quotes_spec.rb +335 -0
- data/spec/scss_lint/linter/trailing_semicolon_spec.rb +304 -0
- data/spec/scss_lint/linter/trailing_zero_spec.rb +176 -0
- data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +67 -0
- data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +98 -0
- data/spec/scss_lint/linter/url_format_spec.rb +55 -0
- data/spec/scss_lint/linter/url_quotes_spec.rb +73 -0
- data/spec/scss_lint/linter/variable_for_property_spec.rb +145 -0
- data/spec/scss_lint/linter/vendor_prefix_spec.rb +371 -0
- data/spec/scss_lint/linter/zero_unit_spec.rb +113 -0
- data/spec/scss_lint/linter_registry_spec.rb +50 -0
- data/spec/scss_lint/linter_spec.rb +292 -0
- data/spec/scss_lint/location_spec.rb +42 -0
- data/spec/scss_lint/options_spec.rb +34 -0
- data/spec/scss_lint/rake_task_spec.rb +43 -0
- data/spec/scss_lint/reporter/config_reporter_spec.rb +42 -0
- data/spec/scss_lint/reporter/default_reporter_spec.rb +73 -0
- data/spec/scss_lint/reporter/files_reporter_spec.rb +38 -0
- data/spec/scss_lint/reporter/json_reporter_spec.rb +96 -0
- data/spec/scss_lint/reporter/xml_reporter_spec.rb +103 -0
- data/spec/scss_lint/reporter_spec.rb +11 -0
- data/spec/scss_lint/runner_spec.rb +123 -0
- data/spec/scss_lint/selector_visitor_spec.rb +264 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/isolated_environment.rb +25 -0
- data/spec/support/matchers/report_lint.rb +48 -0
- metadata +342 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks spacing of ! declarations, like !important and !default
|
3
|
+
class Linter::BangFormat < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
STOPPING_CHARACTERS = ['!', "'", '"', nil]
|
7
|
+
|
8
|
+
def visit_prop(node)
|
9
|
+
return unless source_from_range(node.source_range).include?('!')
|
10
|
+
return unless check_spacing(node)
|
11
|
+
|
12
|
+
before_qualifier = config['space_before_bang'] ? '' : 'not '
|
13
|
+
after_qualifier = config['space_after_bang'] ? '' : 'not '
|
14
|
+
|
15
|
+
add_lint(node, "! should #{before_qualifier}be preceeded by a space, " \
|
16
|
+
"and should #{after_qualifier}be followed by a space")
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Start from the back and move towards the front so that any !important or
|
22
|
+
# !default !'s will be found *before* quotation marks. Then we can
|
23
|
+
# stop at quotation marks to protect against linting !'s within strings
|
24
|
+
# (e.g. `content`)
|
25
|
+
def find_bang_offset(range)
|
26
|
+
offset = 0
|
27
|
+
offset -= 1 until STOPPING_CHARACTERS.include?(character_at(range.end_pos, offset))
|
28
|
+
offset
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_before_wrong?(range, offset)
|
32
|
+
before_expected = config['space_before_bang'] ? / / : /[^ ]/
|
33
|
+
before_actual = character_at(range.end_pos, offset - 1)
|
34
|
+
(before_actual =~ before_expected).nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def is_after_wrong?(range, offset)
|
38
|
+
after_expected = config['space_after_bang'] ? / / : /[^ ]/
|
39
|
+
after_actual = character_at(range.end_pos, offset + 1)
|
40
|
+
(after_actual =~ after_expected).nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_spacing(node)
|
44
|
+
range = node.value_source_range
|
45
|
+
offset = find_bang_offset(range)
|
46
|
+
|
47
|
+
return if character_at(range.end_pos, offset) != '!'
|
48
|
+
|
49
|
+
is_before_wrong?(range, offset) || is_after_wrong?(range, offset)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks that selectors in CSS Modules (https://github.com/gilbox/css-bliss#module) use a specified convention
|
3
|
+
class Linter::Bliss::Module < Linter::Bliss
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_root(_node)
|
7
|
+
@ignored_utility_class_prefixes = Array(config['ignored_utility_class_prefixes']).to_set
|
8
|
+
yield
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit_rule(node)
|
12
|
+
return if config['allow_utility_direct_styling']
|
13
|
+
return unless is_module
|
14
|
+
|
15
|
+
node.parsed_rules.members.each { |rule|
|
16
|
+
match = /(^|\s)\.([a-z]\S*)$/.match(rule.to_s)
|
17
|
+
|
18
|
+
if match
|
19
|
+
add_lint(node, "Class `#{match.captures[1]}` is used at the end of a descendant selector. Avoid styling utility or state classes directly from within a Module. https://github.com/gilbox/css-bliss/blob/master/solving-complexity.md#7-breaking-isolation")
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_attribute(attribute)
|
27
|
+
return if config['allow_attribute_selector_in_module']
|
28
|
+
return unless is_module
|
29
|
+
|
30
|
+
add_lint(attribute, 'Avoid using attribute selectors. https://github.com/gilbox/css-bliss/blob/master/solving-complexity.md#7-breaking-isolation')
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_class(klass)
|
34
|
+
return unless is_module
|
35
|
+
|
36
|
+
unless config['allow_utility_classes_in_module']
|
37
|
+
if /^[a-z]/.match(klass.name)
|
38
|
+
unless @ignored_utility_class_prefixes.any? { |prefix| klass.name.start_with?(prefix) }
|
39
|
+
add_lint(klass, "Class `#{klass.name}` is not allowed in the #{@module_name} module (use a module-namespaced class)")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if /^[A-Z]/.match(klass.name) && ! klass.name.start_with?(@module_name)
|
45
|
+
add_lint(klass, "Class selector `#{klass.name}` is not allowed in the #{@module_name} module https://github.com/gilbox/css-bliss#encapsulation")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def visit_element(element)
|
50
|
+
return if config['allow_element_selector_in_module']
|
51
|
+
return unless is_module
|
52
|
+
|
53
|
+
add_lint(element, 'Avoid using element selectors. https://github.com/gilbox/css-bliss/blob/master/solving-complexity.md#7-breaking-isolation')
|
54
|
+
end
|
55
|
+
|
56
|
+
def visit_id(id)
|
57
|
+
return if config['allow_id_selector_in_module']
|
58
|
+
return unless is_module
|
59
|
+
|
60
|
+
add_lint(id, 'Don\'t use id selectors in CSS Modules.')
|
61
|
+
end
|
62
|
+
|
63
|
+
def visit_prop(node)
|
64
|
+
return unless is_module
|
65
|
+
|
66
|
+
if node.node_parent.rule.include?(".#{@module_name}")
|
67
|
+
name = node.name[0]
|
68
|
+
unless config['allow_module_margin']
|
69
|
+
if name.start_with?('margin')
|
70
|
+
add_lint(node, 'A module should not define it\'s own margin, instead the parent element should define padding. https://github.com/gilbox/css-bliss#module')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
unless config['allow_module_width']
|
75
|
+
if name == 'width'
|
76
|
+
value = node.value
|
77
|
+
if value.is_a?(Sass::Script::Tree::Literal) && /\d/.match("#{value.value}") && ! ['100%', '100vw'].include?("#{value.value}")
|
78
|
+
add_lint(node, 'A module should not define it\'s own bespoke width, it should take up the full width of it\'s parent element. https://github.com/gilbox/css-bliss#module')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def is_module
|
87
|
+
|
88
|
+
# really just used for testing
|
89
|
+
if config['module_name']
|
90
|
+
@module_name = config['module_name']
|
91
|
+
return true
|
92
|
+
end
|
93
|
+
|
94
|
+
match_module_file = (config['module_file_pattern'] || /[\/\\]_?([A-Z][a-zA-Z0-9]+)\.scss/).match(@engine.filename)
|
95
|
+
return false unless match_module_file # not a module
|
96
|
+
|
97
|
+
@module_name = match_module_file.captures[0]
|
98
|
+
true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Enforce a particular value for empty borders.
|
3
|
+
class Linter::BorderZero < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
CONVENTION_TO_PREFERENCE = {
|
7
|
+
'zero' => %w[0 none],
|
8
|
+
'none' => %w[none 0],
|
9
|
+
}
|
10
|
+
|
11
|
+
BORDER_PROPERTIES = %w[
|
12
|
+
border
|
13
|
+
border-top
|
14
|
+
border-right
|
15
|
+
border-bottom
|
16
|
+
border-left
|
17
|
+
]
|
18
|
+
|
19
|
+
def visit_root(_node)
|
20
|
+
@preference = CONVENTION_TO_PREFERENCE[config['convention']]
|
21
|
+
yield # Continue linting children
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_prop(node)
|
25
|
+
return unless BORDER_PROPERTIES.include?(node.name.first.to_s)
|
26
|
+
check_border(node, node.value.to_sass.strip)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def check_border(node, border)
|
32
|
+
return unless %w[0 none].include?(border)
|
33
|
+
return if @preference[0] == border
|
34
|
+
|
35
|
+
add_lint(node, "`border: #{@preference[0]} is preferred over " \
|
36
|
+
"`border: #{@preference[1]}`")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for uses of a color keyword instead of the preferred hexadecimal
|
3
|
+
# form.
|
4
|
+
class Linter::ColorKeyword < Linter
|
5
|
+
include LinterRegistry
|
6
|
+
|
7
|
+
def visit_script_color(node)
|
8
|
+
word = source_from_range(node.source_range)[/([a-z]+)/i, 1]
|
9
|
+
add_color_lint(node, word) if color_keyword?(word)
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_script_string(node)
|
13
|
+
return unless node.type == :identifier
|
14
|
+
|
15
|
+
remove_quoted_strings(node.value).scan(/(^|\s)([a-z]+)(?=\s|$)/i) do |_, word|
|
16
|
+
add_color_lint(node, word) if color_keyword?(word)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def add_color_lint(node, original)
|
23
|
+
hex_form = Sass::Script::Value::Color.new(color_keyword_to_code(original)).tap do |color|
|
24
|
+
color.options = {} # `inspect` requires options to be set
|
25
|
+
end.inspect
|
26
|
+
|
27
|
+
add_lint(node,
|
28
|
+
"Color `#{original}` should be written in hexadecimal form " \
|
29
|
+
"as `#{hex_form}`")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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
|
+
in_map_declaration?(node) ||
|
9
|
+
in_rgba_function_call?(node)
|
10
|
+
|
11
|
+
# Source range sometimes includes closing parenthesis, so extract it
|
12
|
+
color = source_from_range(node.source_range)[/(#?[a-z0-9]+)/i, 1]
|
13
|
+
|
14
|
+
record_lint(node, color) if color?(color)
|
15
|
+
end
|
16
|
+
|
17
|
+
def visit_script_string(node)
|
18
|
+
remove_quoted_strings(node.value)
|
19
|
+
.scan(/(^|\s)(#[a-f0-9]+|[a-z]+)(?=\s|$)/i)
|
20
|
+
.select { |_, word| color?(word) }
|
21
|
+
.each { |_, color| record_lint(node, color) }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def record_lint(node, color)
|
27
|
+
add_lint node, "Color literals like `#{color}` should only be used in " \
|
28
|
+
'variable declarations; they should be referred to via ' \
|
29
|
+
'variable everywhere else.'
|
30
|
+
end
|
31
|
+
|
32
|
+
def in_variable_declaration?(node)
|
33
|
+
parent = node.node_parent
|
34
|
+
parent.is_a?(Sass::Script::Tree::Literal) &&
|
35
|
+
parent.node_parent.is_a?(Sass::Tree::VariableNode)
|
36
|
+
end
|
37
|
+
|
38
|
+
def in_rgba_function_call?(node)
|
39
|
+
grandparent = node_ancestor(node, 2)
|
40
|
+
|
41
|
+
grandparent.is_a?(Sass::Script::Tree::Funcall) &&
|
42
|
+
grandparent.name == 'rgba'
|
43
|
+
end
|
44
|
+
|
45
|
+
def in_map_declaration?(node)
|
46
|
+
node_ancestor(node, 2).is_a?(Sass::Script::Tree::MapLiteral)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for uses of renderable comments (/* ... */)
|
3
|
+
class Linter::Comment < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_comment(node)
|
7
|
+
add_lint(node, 'Use `//` comments everywhere') unless node.invisible? || allowed?(node)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# @param node [CommentNode]
|
13
|
+
# @return [Boolean]
|
14
|
+
def allowed?(node)
|
15
|
+
return false unless config['allowed']
|
16
|
+
re = Regexp.new(config['allowed'])
|
17
|
+
|
18
|
+
node.value.join.match(re)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for uses of properties where a Compass mixin would be preferred.
|
3
|
+
class Linter::Compass::PropertyWithMixin < Linter::Compass
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_prop(node)
|
7
|
+
check_for_properties_with_mixins(node)
|
8
|
+
check_for_inline_block(node)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Set of properties where the Compass mixin version is preferred
|
14
|
+
PROPERTIES_WITH_MIXINS = %w[
|
15
|
+
background-clip
|
16
|
+
background-origin
|
17
|
+
border-radius
|
18
|
+
box-shadow
|
19
|
+
box-sizing
|
20
|
+
opacity
|
21
|
+
text-shadow
|
22
|
+
transform
|
23
|
+
].to_set
|
24
|
+
|
25
|
+
def check_for_properties_with_mixins(node)
|
26
|
+
prop_name = node.name.join
|
27
|
+
return unless PROPERTIES_WITH_MIXINS.include?(prop_name) &&
|
28
|
+
!ignore_compass_mixin?(prop_name)
|
29
|
+
|
30
|
+
add_lint node, "Use the Compass `#{prop_name}` mixin instead of the property"
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_for_inline_block(node)
|
34
|
+
prop_name = node.name.join
|
35
|
+
return unless prop_name == 'display' &&
|
36
|
+
node.value.to_sass == 'inline-block' &&
|
37
|
+
!ignore_compass_mixin?('inline-block')
|
38
|
+
|
39
|
+
add_lint node,
|
40
|
+
'Use the Compass `inline-block` mixin instead of `display: inline-block`'
|
41
|
+
end
|
42
|
+
|
43
|
+
def ignore_compass_mixin?(prop_name)
|
44
|
+
config.fetch('ignore', []).include?(prop_name)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks the order of nested items within a rule set.
|
3
|
+
class Linter::DeclarationOrder < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def check_order(node)
|
7
|
+
check_node(node)
|
8
|
+
yield # Continue linting children
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method :visit_rule, :check_order
|
12
|
+
alias_method :visit_mixin, :check_order
|
13
|
+
alias_method :visit_media, :check_order
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
MESSAGE =
|
18
|
+
'Rule sets should be ordered as follows: '\
|
19
|
+
'`@extends`, `@includes` without `@content`, ' \
|
20
|
+
'properties, `@includes` with `@content`, ' \
|
21
|
+
'nested rule sets'
|
22
|
+
|
23
|
+
MIXIN_WITH_CONTENT = 'mixin_with_content'
|
24
|
+
|
25
|
+
DECLARATION_ORDER = [
|
26
|
+
Sass::Tree::ExtendNode,
|
27
|
+
Sass::Tree::MixinNode,
|
28
|
+
Sass::Tree::PropNode,
|
29
|
+
MIXIN_WITH_CONTENT,
|
30
|
+
Sass::Tree::RuleNode,
|
31
|
+
]
|
32
|
+
|
33
|
+
def important_node?(node)
|
34
|
+
DECLARATION_ORDER.include?(node.class)
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_node(node)
|
38
|
+
children = node.children.each_with_index
|
39
|
+
.select { |n, _| important_node?(n) }
|
40
|
+
.map { |n, i| [n, node_declaration_type(n), i] }
|
41
|
+
|
42
|
+
sorted_children = children.sort do |(_, a_type, i), (_, b_type, j)|
|
43
|
+
[DECLARATION_ORDER.index(a_type), i] <=> [DECLARATION_ORDER.index(b_type), j]
|
44
|
+
end
|
45
|
+
|
46
|
+
check_children_order(sorted_children, children)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Find the child that is out of place
|
50
|
+
def check_children_order(sorted_children, children)
|
51
|
+
sorted_children.each_with_index do |sorted_item, index|
|
52
|
+
next if sorted_item == children[index]
|
53
|
+
|
54
|
+
add_lint(sorted_item.first.line,
|
55
|
+
"Expected item on line #{sorted_item.first.line} to appear " \
|
56
|
+
"before line #{children[index].first.line}. #{MESSAGE}")
|
57
|
+
break
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def node_declaration_type(node)
|
62
|
+
# If the node has no children, return the class.
|
63
|
+
return node.class unless node.has_children
|
64
|
+
|
65
|
+
# If the node is a mixin with children, indicate that;
|
66
|
+
# otherwise, just return the class.
|
67
|
+
return node.class unless node.is_a?(Sass::Tree::MixinNode)
|
68
|
+
MIXIN_WITH_CONTENT
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|