scss-lint 0.11.1 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/scss_lint.rb +5 -3
- data/lib/scss_lint/cli.rb +9 -2
- data/lib/scss_lint/constants.rb +5 -0
- data/lib/scss_lint/linter.rb +11 -0
- data/lib/scss_lint/linter/border_zero.rb +8 -7
- data/lib/scss_lint/linter/capitalization_in_selector.rb +1 -0
- data/lib/scss_lint/linter/color_keyword.rb +2 -0
- data/lib/scss_lint/linter/comment.rb +1 -0
- data/lib/scss_lint/linter/debug_statement.rb +1 -0
- data/lib/scss_lint/linter/declaration_order.rb +1 -0
- data/lib/scss_lint/linter/declared_name.rb +2 -0
- data/lib/scss_lint/linter/id_with_extraneous_selector.rb +1 -0
- data/lib/scss_lint/linter/indentation.rb +1 -0
- data/lib/scss_lint/linter/leading_zero.rb +1 -0
- data/lib/scss_lint/linter/placeholder_in_extend.rb +1 -0
- data/lib/scss_lint/linter/shorthand.rb +2 -0
- data/lib/scss_lint/linter/single_line_per_selector.rb +1 -0
- data/lib/scss_lint/linter/sorted_properties.rb +1 -0
- data/lib/scss_lint/linter/space_after_comma.rb +87 -0
- data/lib/scss_lint/linter/space_after_property_colon.rb +39 -0
- data/lib/scss_lint/linter/space_after_property_name.rb +13 -0
- data/lib/scss_lint/linter/space_before_brace.rb +1 -0
- data/lib/scss_lint/linter/trailing_semicolon_after_property_value.rb +29 -0
- data/lib/scss_lint/linter/zero_unit.rb +1 -0
- data/lib/scss_lint/runner.rb +18 -7
- data/lib/{sass → scss_lint/sass}/script.rb +0 -0
- data/lib/{sass → scss_lint/sass}/tree.rb +0 -0
- data/lib/scss_lint/utils.rb +5 -0
- data/lib/scss_lint/version.rb +1 -1
- metadata +10 -6
- data/lib/scss_lint/linter/property_format.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5efc8809d431b7de019112e2771839116d88df95
|
4
|
+
data.tar.gz: d54b6ba942585c4fbdc969c389af86e0da47088c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 406a4cd62baa39e5dc561c5cd84cedc7ef7b4d64a0b8813a693c22fb3192640f0382b3a839f317fdbe0f75738c6ed53b3f494bd5e9c32ed50f79833841e76f43
|
7
|
+
data.tar.gz: e0c8b74d3cf6bb80f592cfacc9ed25fe642b397e36ca9e965ee31a4889a8291ea2b88334aba45aae0c4eaf5cf52c610150bf6bc0b2ecd5b6af92aa4547ea21b5
|
data/lib/scss_lint.rb
CHANGED
@@ -10,12 +10,14 @@ module SCSSLint
|
|
10
10
|
autoload :Runner, 'scss_lint/runner'
|
11
11
|
autoload :SelectorVisitor, 'scss_lint/selector_visitor'
|
12
12
|
autoload :Utils, 'scss_lint/utils'
|
13
|
-
|
13
|
+
|
14
|
+
require 'scss_lint/constants'
|
15
|
+
require 'scss_lint/version'
|
14
16
|
|
15
17
|
# Preload Sass so we can monkey patch it
|
16
18
|
require 'sass'
|
17
|
-
require File.expand_path('sass/script', File.dirname(__FILE__))
|
18
|
-
require File.expand_path('sass/tree', File.dirname(__FILE__))
|
19
|
+
require File.expand_path('scss_lint/sass/script', File.dirname(__FILE__))
|
20
|
+
require File.expand_path('scss_lint/sass/tree', File.dirname(__FILE__))
|
19
21
|
|
20
22
|
# Load all linters
|
21
23
|
Dir[File.expand_path('scss_lint/linter/*.rb', File.dirname(__FILE__))].each do |file|
|
data/lib/scss_lint/cli.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
3
|
module SCSSLint
|
4
|
+
# Responsible for parsing command-line options and executing the appropriate
|
5
|
+
# application logic based on the options specified.
|
4
6
|
class CLI
|
5
7
|
attr_accessor :options
|
6
8
|
|
@@ -62,10 +64,15 @@ module SCSSLint
|
|
62
64
|
runner = Runner.new(options)
|
63
65
|
runner.run(find_files)
|
64
66
|
report_lints(runner.lints)
|
65
|
-
halt
|
67
|
+
halt(1) if runner.lints?
|
66
68
|
rescue NoFilesError, NoSuchLinter, Errno::ENOENT => ex
|
67
69
|
puts ex.message
|
68
|
-
halt
|
70
|
+
halt(-1)
|
71
|
+
rescue => ex
|
72
|
+
puts ex.message
|
73
|
+
puts ex.backtrace
|
74
|
+
puts 'Report this bug at '.yellow + BUG_REPORT_URL.cyan
|
75
|
+
halt(-1)
|
69
76
|
end
|
70
77
|
|
71
78
|
private
|
data/lib/scss_lint/linter.rb
CHANGED
@@ -28,6 +28,17 @@ module SCSSLint
|
|
28
28
|
message || description)
|
29
29
|
end
|
30
30
|
|
31
|
+
# Returns the character at the given [Sass::Source::Position]
|
32
|
+
def character_at(source_position, offset = 0)
|
33
|
+
actual_line = source_position.line - 1
|
34
|
+
actual_offset = source_position.offset + offset - 1
|
35
|
+
|
36
|
+
# Return a newline if offset points at the very end of the line
|
37
|
+
return "\n" if actual_offset == engine.lines[actual_line].length
|
38
|
+
|
39
|
+
engine.lines[actual_line][actual_offset]
|
40
|
+
end
|
41
|
+
|
31
42
|
# Monkey-patched implementation that adds support for traversing
|
32
43
|
# Sass::Script::Nodes (original implementation only supports
|
33
44
|
# Sass::Tree::Nodes).
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module SCSSLint
|
2
|
+
# Reports when `border: 0` can be used instead of `border: none`.
|
2
3
|
class Linter::BorderZero < Linter
|
3
4
|
include LinterRegistry
|
4
5
|
|
@@ -10,13 +11,13 @@ module SCSSLint
|
|
10
11
|
def description
|
11
12
|
'`border: 0;` is preferred over `border: none;`'
|
12
13
|
end
|
13
|
-
end
|
14
14
|
|
15
|
-
private
|
15
|
+
private
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
BORDER_PROPERTIES = %w[border
|
18
|
+
border-top
|
19
|
+
border-right
|
20
|
+
border-bottom
|
21
|
+
border-left]
|
22
|
+
end
|
22
23
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for spaces after commas in argument lists.
|
3
|
+
class Linter::SpaceAfterComma < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_mixindef(node)
|
7
|
+
check_definition(node, :mixin)
|
8
|
+
yield
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit_mixin(node)
|
12
|
+
check_invocation(node, :mixin)
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_function(node)
|
17
|
+
check_definition(node, :function)
|
18
|
+
yield
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit_script_funcall(node)
|
22
|
+
check_invocation(node, :function)
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_script_listliteral(node)
|
27
|
+
check_commas_after_args(node.elements, 'lists') if node.separator == :comma
|
28
|
+
yield
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Check parameters of a function/mixin definition
|
34
|
+
def check_definition(node, type)
|
35
|
+
# Use the default value's source range if one is defined, since that will
|
36
|
+
# be the item with the comma after it
|
37
|
+
args = node.args.map { |name, default_value| default_value || name }
|
38
|
+
args << node.splat if node.splat
|
39
|
+
|
40
|
+
check_commas_after_args(args, "#{type} parameters")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Check arguments passed to a function/mixin invocation
|
44
|
+
def check_invocation(node, type)
|
45
|
+
args = sort_args_by_position(node.args,
|
46
|
+
node.splat,
|
47
|
+
node.keywords.values,
|
48
|
+
node.kwarg_splat)
|
49
|
+
|
50
|
+
check_commas_after_args(args, "#{type} arguments")
|
51
|
+
end
|
52
|
+
|
53
|
+
# Since keyword arguments are not guaranteed to be in order, use the source
|
54
|
+
# range to order arguments so we check them in the order they were declared.
|
55
|
+
def sort_args_by_position(*args)
|
56
|
+
args.flatten.compact.sort_by do |arg|
|
57
|
+
pos = arg.source_range.end_pos
|
58
|
+
[pos.line, pos.offset]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
EXPECTED_SPACES_AFTER_COMMA = 1
|
63
|
+
|
64
|
+
# Check the comma after each argument in a list for a space following it,
|
65
|
+
# reporting a lint using the given [arg_type].
|
66
|
+
def check_commas_after_args(args, arg_type)
|
67
|
+
# For each arg except the last, check the character following the comma
|
68
|
+
args[0..-2].each do |arg|
|
69
|
+
# Find the first comma following the arg
|
70
|
+
offset = 1
|
71
|
+
offset += 1 while character_at(arg.source_range.end_pos, offset - 1) != ','
|
72
|
+
|
73
|
+
# Check for space or newline after arg (we allow arguments to be split
|
74
|
+
# up over multiple lines).
|
75
|
+
spaces = 0
|
76
|
+
while character_at(arg.source_range.end_pos, offset) =~ / |\n/
|
77
|
+
spaces += 1
|
78
|
+
offset += 1
|
79
|
+
end
|
80
|
+
|
81
|
+
if spaces != EXPECTED_SPACES_AFTER_COMMA
|
82
|
+
add_lint arg, 'Commas in %s should be followed by a single space' % arg_type
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for spaces following the colon that separates a property's name from
|
3
|
+
# its value.
|
4
|
+
class Linter::SpaceAfterPropertyColon < Linter
|
5
|
+
include LinterRegistry
|
6
|
+
|
7
|
+
EXPECTED_SPACES_AFTER_COLON = 1
|
8
|
+
|
9
|
+
def visit_prop(node)
|
10
|
+
spaces = spaces_after_colon(node)
|
11
|
+
|
12
|
+
if spaces != EXPECTED_SPACES_AFTER_COLON
|
13
|
+
add_lint node, 'Colon after property should be followed by ' <<
|
14
|
+
"#{pluralize(EXPECTED_SPACES_AFTER_COLON, 'space')} instead of " <<
|
15
|
+
pluralize(spaces, 'space')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def spaces_after_colon(node)
|
22
|
+
spaces = 0
|
23
|
+
offset = 1
|
24
|
+
|
25
|
+
# Handle quirk where Sass parser doesn't include colon in source range
|
26
|
+
# when property name is followed by spaces
|
27
|
+
if character_at(node.name_source_range.end_pos, offset) == ':'
|
28
|
+
offset += 1
|
29
|
+
end
|
30
|
+
|
31
|
+
while character_at(node.name_source_range.end_pos, offset) == ' '
|
32
|
+
spaces += 1
|
33
|
+
offset += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
spaces
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for spaces following the name of a property and before the colon
|
3
|
+
# separating the property's name from its value.
|
4
|
+
class Linter::SpaceAfterPropertyName < Linter
|
5
|
+
include LinterRegistry
|
6
|
+
|
7
|
+
def visit_prop(node)
|
8
|
+
if character_at(node.name_source_range.end_pos) != ':'
|
9
|
+
add_lint node, 'Property name should be immediately followed by a colon'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks properties for a trailing semicolon (unless that property is a
|
3
|
+
# namespace which has nested properties).
|
4
|
+
class Linter::TrailingSemicolonAfterPropertyValue < Linter
|
5
|
+
include LinterRegistry
|
6
|
+
|
7
|
+
def visit_prop(node)
|
8
|
+
has_nested_props = has_nested_properties?(node)
|
9
|
+
|
10
|
+
if !has_nested_props && !ends_with_semicolon?(node)
|
11
|
+
add_lint node.line, 'Property declaration should end with a semicolon'
|
12
|
+
end
|
13
|
+
|
14
|
+
yield if has_nested_props
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def has_nested_properties?(node)
|
20
|
+
node.children.any? { |n| n.is_a?(Sass::Tree::PropNode) }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Checks that the property is ended by a semicolon (with no whitespace)
|
24
|
+
def ends_with_semicolon?(node)
|
25
|
+
character_at(node.source_range.end_pos) == ';' &&
|
26
|
+
character_at(node.source_range.end_pos, -1) !~ /\s/
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/scss_lint/runner.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module SCSSLint
|
2
|
+
class LinterError < StandardError; end
|
2
3
|
class NoFilesError < StandardError; end
|
3
4
|
class NoLintersError < StandardError; end
|
4
5
|
|
6
|
+
# Finds and aggregates all lints found by running the registered linters
|
7
|
+
# against a set of SCSS files.
|
5
8
|
class Runner
|
6
9
|
attr_reader :linters, :lints
|
7
10
|
|
@@ -20,8 +23,8 @@ module SCSSLint
|
|
20
23
|
end
|
21
24
|
|
22
25
|
def run(files = [])
|
23
|
-
raise NoFilesError
|
24
|
-
raise NoLintersError
|
26
|
+
raise NoFilesError, 'No SCSS files specified' if files.empty?
|
27
|
+
raise NoLintersError, 'No linters specified' if linters.empty?
|
25
28
|
|
26
29
|
files.each do |file|
|
27
30
|
find_lints(file)
|
@@ -32,18 +35,26 @@ module SCSSLint
|
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
38
|
+
def lints?
|
39
|
+
lints.any?
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
35
44
|
def find_lints(file)
|
36
45
|
engine = Engine.new(file)
|
37
46
|
|
38
47
|
linters.each do |linter|
|
39
|
-
|
48
|
+
begin
|
49
|
+
linter.run(engine)
|
50
|
+
rescue => error
|
51
|
+
raise LinterError,
|
52
|
+
"#{linter.class} raised unexpected error: '#{error.message}'",
|
53
|
+
error.backtrace
|
54
|
+
end
|
40
55
|
end
|
41
56
|
rescue Sass::SyntaxError => ex
|
42
57
|
@lints << Lint.new(ex.sass_filename, ex.sass_line, ex.to_s, :error)
|
43
58
|
end
|
44
|
-
|
45
|
-
def lints?
|
46
|
-
lints.any?
|
47
|
-
end
|
48
59
|
end
|
49
60
|
end
|
File without changes
|
File without changes
|
data/lib/scss_lint/utils.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module SCSSLint
|
2
|
+
# Collection of helpers used across a variety of linters.
|
2
3
|
module Utils
|
3
4
|
# Given a selector array which is a list of strings with Sass::Script::Nodes
|
4
5
|
# interspersed within them, return an array of strings representing those
|
@@ -55,6 +56,10 @@ module SCSSLint
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
59
|
+
def pluralize(value, word)
|
60
|
+
value == 1 ? "#{value} #{word}" : "#{value} #{word}s"
|
61
|
+
end
|
62
|
+
|
58
63
|
private
|
59
64
|
|
60
65
|
INVALID_NAME_CHARS = '[_A-Z]'
|
data/lib/scss_lint/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scss-lint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Causes Engineering
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: colorize
|
@@ -77,6 +77,7 @@ extensions: []
|
|
77
77
|
extra_rdoc_files: []
|
78
78
|
files:
|
79
79
|
- lib/scss_lint/version.rb
|
80
|
+
- lib/scss_lint/constants.rb
|
80
81
|
- lib/scss_lint/utils.rb
|
81
82
|
- lib/scss_lint/reporter/xml_reporter.rb
|
82
83
|
- lib/scss_lint/reporter/default_reporter.rb
|
@@ -85,19 +86,24 @@ files:
|
|
85
86
|
- lib/scss_lint/lint.rb
|
86
87
|
- lib/scss_lint/linter.rb
|
87
88
|
- lib/scss_lint/selector_visitor.rb
|
88
|
-
- lib/scss_lint/
|
89
|
+
- lib/scss_lint/sass/tree.rb
|
90
|
+
- lib/scss_lint/sass/script.rb
|
91
|
+
- lib/scss_lint/linter/space_after_comma.rb
|
89
92
|
- lib/scss_lint/linter/space_before_brace.rb
|
90
93
|
- lib/scss_lint/linter/capitalization_in_selector.rb
|
91
94
|
- lib/scss_lint/linter/indentation.rb
|
92
95
|
- lib/scss_lint/linter/declared_name.rb
|
93
96
|
- lib/scss_lint/linter/sorted_properties.rb
|
97
|
+
- lib/scss_lint/linter/trailing_semicolon_after_property_value.rb
|
94
98
|
- lib/scss_lint/linter/shorthand.rb
|
95
99
|
- lib/scss_lint/linter/id_with_extraneous_selector.rb
|
96
100
|
- lib/scss_lint/linter/declaration_order.rb
|
97
101
|
- lib/scss_lint/linter/zero_unit.rb
|
98
102
|
- lib/scss_lint/linter/placeholder_in_extend.rb
|
103
|
+
- lib/scss_lint/linter/space_after_property_name.rb
|
99
104
|
- lib/scss_lint/linter/usage_name.rb
|
100
105
|
- lib/scss_lint/linter/border_zero.rb
|
106
|
+
- lib/scss_lint/linter/space_after_property_colon.rb
|
101
107
|
- lib/scss_lint/linter/debug_statement.rb
|
102
108
|
- lib/scss_lint/linter/duplicate_property.rb
|
103
109
|
- lib/scss_lint/linter/hex_format.rb
|
@@ -109,11 +115,9 @@ files:
|
|
109
115
|
- lib/scss_lint/cli.rb
|
110
116
|
- lib/scss_lint/engine.rb
|
111
117
|
- lib/scss_lint/linter_registry.rb
|
112
|
-
- lib/sass/tree.rb
|
113
|
-
- lib/sass/script.rb
|
114
118
|
- lib/scss_lint.rb
|
115
119
|
- bin/scss-lint
|
116
|
-
homepage:
|
120
|
+
homepage: https://github.com/causes/scss-lint
|
117
121
|
licenses:
|
118
122
|
- MIT
|
119
123
|
metadata: {}
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module SCSSLint
|
2
|
-
class Linter::PropertyFormat < Linter
|
3
|
-
include LinterRegistry
|
4
|
-
|
5
|
-
def visit_prop(node)
|
6
|
-
line = engine.lines[node.line - 1] if node.line
|
7
|
-
|
8
|
-
add_lint(node) unless line =~ PROPERTY_RE
|
9
|
-
end
|
10
|
-
|
11
|
-
def description
|
12
|
-
'Property declarations should always be on one line of the form ' <<
|
13
|
-
'`name: value;` or `name: [value] {` ' <<
|
14
|
-
'(are you missing a trailing semi-colon?)'
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
VALUE_RE = %r{(\S+\s)*\S+} # eg. "10px", "10px normal"
|
20
|
-
PROPERTY_RE = %r{
|
21
|
-
^\s*[^:]+(?<!\s):\s # property name, colon not preceded by a space, one space
|
22
|
-
( # followed by
|
23
|
-
#{VALUE_RE}; # property and terminating semi-colon eg. a b c;
|
24
|
-
| # or
|
25
|
-
((#{VALUE_RE}\s)?\{) # nested property, optional value, trailing curly
|
26
|
-
)
|
27
|
-
}x
|
28
|
-
end
|
29
|
-
end
|