Created
October 15, 2013 12:58
-
-
Save modernserf/6991211 to your computer and use it in GitHub Desktop.
Basic SASS linter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fs = require 'fs' | |
file_path = __INSERT_FILE_PATH_HERE__ | |
fs.readFile file_path, {encoding: 'utf-8'}, (err, data)-> | |
# global document object | |
doc = { | |
tab_size: 2 | |
lines: data.split("\n") | |
} | |
options = [ | |
'deep_selectors' | |
'multi_selectors' | |
'magic_numbers' | |
] | |
parsed = doc.lines.map parse_line | |
parsed.forEach (p, index)-> | |
errors = (warn(p, doc) for key, warn of warnings when (key in options)) | |
.filter (x)-> x? | |
warn_at_line(index, doc) if errors.length | |
warn_at_line = (index, doc)-> | |
console.warn "#{index}:", doc.lines[index], "\n" | |
null | |
warnings = { | |
deep_selectors: (line, doc)-> | |
# checks for selectors more than 3 levels deep which may be too specific | |
return unless line.kind is "selector" | |
depth = line.indent / doc.tab_size + Math.max.apply null, line.args.map (g)-> g.length | |
if depth > 3 | |
console.warn "Warning: selector #{depth} levels deep" | |
return {deep_selectors: depth} | |
multi_selectors: (line, doc)-> | |
# checks for multiple selectors on a line | |
return unless line.kind is "selector" | |
selector_count = line.args.length | |
if selector_count > 1 | |
console.warn "Warning: #{selector_count} selectors on single line" | |
return {multi_selectors: selector_count} | |
magic_numbers: (line, doc)-> | |
# checks for suspicious "magic numbers", i.e. pixel/color/percent values | |
# that are not attached to a variable | |
return unless line.kind is "attribute" | |
# ignore var definition lines | |
return if line.args[0][0] is '$' | |
# px, percent, or color values | |
blacklist = /(px|%|#|rgb)/ | |
# exceptions | |
whitelist = /^(1px|100%|50%|25%|20%)/ | |
errors = line.args | |
.map((arg)-> arg if blacklist.test(arg)) | |
.filter((arg)-> arg? && !whitelist.test(arg)) | |
if errors.length | |
console.warn "Warning: Magic Numbers ", errors.join(', ') | |
return {magic_numbers: errors} | |
} | |
# per line | |
parse_line = (line)-> | |
return null unless line? | |
# indent: # of preceding spaces | |
# kind: mixin, attribute, selector | |
# args: array of values or selectors | |
trim = line.trimLeft() | |
kind = if (line.indexOf('@') > 0) | |
"mixin" | |
else if (line.indexOf(': ') > 0) | |
"attribute" | |
else | |
"selector" | |
{ | |
indent: line.length - trim.length | |
kind: kind | |
args: do ()-> | |
if kind is "attribute" | |
# split on spaces, colons | |
trim.split(/\s|:\s/) | |
else if kind is "selector" | |
# split on commas | |
trim.split(/\s*,\s*/) | |
.filter( (group)-> group? ) # remove empty items | |
.map (group)-> | |
group.split(/\s+/) # split on spaces | |
else | |
null | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment