Skip to content

Commit

Permalink
Add DuplicateRoot linter
Browse files Browse the repository at this point in the history
Closes sds#109

Change-Id: I8f7a16aadfff132385cd18fcbe03050f0f97d63f
Reviewed-on: http://gerrit.causes.com/37417
Tested-by: jenkins <[email protected]>
Reviewed-by: Shane da Silva <[email protected]>
  • Loading branch information
nschonni authored and Shane da Silva committed Apr 19, 2014
1 parent 15c3407 commit 40282d9
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
reported for braces that aren't part of declarations
* Teach `Compass::PropertyWithMixin` to prefer `inline-block` mixin over
`display: inline-block`
* Add `DuplicateRoot` linter which checks for identical rules used at as root
selectors in a document

## 0.22.0

Expand Down
3 changes: 3 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ linters:
DuplicateProperty:
enabled: true

DuplicateRoot:
enabled: true

EmptyLineBetweenBlocks:
enabled: true
ignore_single_line_blocks: true
Expand Down
35 changes: 35 additions & 0 deletions lib/scss_lint/linter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Below is a list of linters supported by `scss-lint`, ordered alphabetically.
* [DebugStatement](#debugstatement)
* [DeclarationOrder](#declarationorder)
* [DuplicateProperty](#duplicateproperty)
* [DuplicateRoot](#duplicateroot)
* [EmptyLineBetweenBlocks](#emptylinebetweenblocks)
* [EmptyRule](#emptyrule)
* [FinalNewline](#finalnewline)
Expand Down Expand Up @@ -185,6 +186,40 @@ more helpful since you don't want to clutter your CSS with fallbacks.
Otherwise, you may want to consider disabling this check in your
`.scss-lint.yml` configuration.

## DuplicateRoot

Reports when you define the same root selector twice in a single sheet.

**Bad**
```scss
h1 {
margin: 10px;
}
.baz {
color: red;
}
// Second copy of h1 rule
h1 {
text-transform: uppercase;
border: 0;
}
```

Combining duplicate selectors can result in an easier to read sheet, but
occasionally the root rules may be purposely duplicated to set precedence
after a rule with the same CSS specificity.

```scss
h1 {
margin: 10px;
text-transform: uppercase;
border: 0;
}
.baz {
color: red;
}
```

## EmptyLineBetweenBlocks

Separate rule, function, and mixin declarations with empty lines.
Expand Down
23 changes: 23 additions & 0 deletions lib/scss_lint/linter/duplicate_root.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module SCSSLint
# Checks identical root selectors.
class Linter::DuplicateRoot < Linter
include LinterRegistry

def visit_root(node)
# Root rules are evaluated per document, so use new hashes for eash file
@roots = Hash.new
yield # Continue linting children
end

def visit_rule(node)
# Check to see if we've seen this rule before
if @roots.has_key?(node.rule)
add_lint(node.line,
"Root #{node.rule} already seen at #{@roots[node.rule]}")
else
@roots[node.rule] = node.line
end
return # Only check one level deep
end
end
end
146 changes: 146 additions & 0 deletions spec/scss_lint/linter/duplicate_root_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
require 'spec_helper'

describe SCSSLint::Linter::DuplicateRoot do
context 'when single root' do
let(:css) { <<-CSS }
p {
}
CSS

it { should_not report_lint }
end

context 'when different roots' do
let(:css) { <<-CSS }
p {
background: #000;
margin: 5px;
}
a {
background: #000;
margin: 5px;
}
CSS

it { should_not report_lint }
end

context 'when different roots with matching inner rules' do
let(:css) { <<-CSS }
p {
background: #000;
margin: 5px;
.foo {
color: red;
}
}
a {
background: #000;
margin: 5px;
.foo {
color: red;
}
}
CSS

it { should_not report_lint }
end

context 'when multi-selector roots and parital rule match' do
let(:css) { <<-CSS }
p, a {
background: #000;
margin: 5px;
.foo {
color: red;
}
}
a {
background: #000;
margin: 5px;
.foo {
color: red;
}
}
CSS

it { should_not report_lint }
end

context 'when nested and unnested selectors match' do
let(:css) { <<-CSS }
a.current {
background: #000;
margin: 5px;
.foo {
color: red;
}
}
a {
&.current {
background: #000;
margin: 5px;
.foo {
color: red;
}
}
}
CSS

it { should_not report_lint }
end

context 'when same class roots' do
let(:css) { <<-CSS }
.warn {
font-weight: bold;
}
.warn {
color: #f00;
@extend .warn;
a {
color: #ccc;
}
}
CSS

it { should report_lint }
end

context 'when same compond selector roots' do
let(:css) { <<-CSS }
.warn .alert {
font-weight: bold;
}
.warn .alert {
color: #f00;
@extend .warn;
a {
color: #ccc;
}
}
CSS

it { should report_lint }
end

context 'when same class roots separated by another class' do
let(:css) { <<-CSS }
.warn {
font-weight: bold;
}
.foo {
color: red;
}
.warn {
color: #f00;
@extend .warn;
a {
color: #ccc;
}
}
CSS

it { should report_lint }
end
end

0 comments on commit 40282d9

Please sign in to comment.