| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,37 +1,38 @@ | ||
| /* | ||
| * Rule: Disallow duplicate background-images (using url). | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "duplicate-background-images", | ||
| name: "Disallow duplicate background images", | ||
| desc: "Every background-image should be unique. Use a common class for e.g. sprites.", | ||
| url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this, | ||
| stack = {}; | ||
|
|
||
| parser.addListener("property", function(event) { | ||
| var name = event.property.text, | ||
| value = event.value, | ||
| i, len; | ||
|
|
||
| if (name.match(/background/i)) { | ||
| for (i=0, len=value.parts.length; i < len; i++) { | ||
| if (value.parts[i].type === "uri") { | ||
| if (typeof stack[value.parts[i].uri] === "undefined") { | ||
| stack[value.parts[i].uri] = event; | ||
| } else { | ||
| reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,34 +1,37 @@ | ||
| /* | ||
| * Rule: Style rules without any properties defined should be removed. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "empty-rules", | ||
| name: "Disallow empty rules", | ||
| desc: "Rules without any properties specified should be removed.", | ||
| url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this, | ||
| count = 0; | ||
|
|
||
| parser.addListener("startrule", function() { | ||
| count=0; | ||
| }); | ||
|
|
||
| parser.addListener("property", function() { | ||
| count++; | ||
| }); | ||
|
|
||
| parser.addListener("endrule", function(event) { | ||
| var selectors = event.selectors; | ||
|
|
||
| if (count === 0) { | ||
| reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,24 @@ | ||
| /* | ||
| * Rule: There should be no syntax errors. (Duh.) | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "errors", | ||
| name: "Parsing Errors", | ||
| desc: "This rule looks for recoverable syntax errors.", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this; | ||
|
|
||
| parser.addListener("error", function(event) { | ||
| reporter.error(event.message, event.line, event.col, rule); | ||
| }); | ||
|
|
||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,30 +1,34 @@ | ||
| /* | ||
| * Rule: Avoid too many @font-face declarations in the same stylesheet. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "font-faces", | ||
| name: "Don't use too many web fonts", | ||
| desc: "Too many different web fonts in the same stylesheet.", | ||
| url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this, | ||
| count = 0; | ||
|
|
||
|
|
||
| parser.addListener("startfontface", function(event) { | ||
| if (!reporter.isIgnored(event.line)) { | ||
| count++; | ||
| } | ||
| }); | ||
|
|
||
| parser.addListener("endstylesheet", function() { | ||
| if (count > 5) { | ||
| reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,50 +1,52 @@ | ||
| /* | ||
| * Rule: Don't use IDs for selectors. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "ids", | ||
| name: "Disallow IDs in selectors", | ||
| desc: "Selectors should not contain IDs.", | ||
| url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this; | ||
| parser.addListener("startrule", function(event) { | ||
| var selectors = event.selectors, | ||
| selector, | ||
| part, | ||
| modifier, | ||
| idCount, | ||
| i, j, k; | ||
|
|
||
| for (i=0; i < selectors.length; i++) { | ||
| selector = selectors[i]; | ||
| idCount = 0; | ||
|
|
||
| for (j=0; j < selector.parts.length; j++) { | ||
| part = selector.parts[j]; | ||
| if (part.type === parser.SELECTOR_PART_TYPE) { | ||
| for (k=0; k < part.modifiers.length; k++) { | ||
| modifier = part.modifiers[k]; | ||
| if (modifier.type === "id") { | ||
| idCount++; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (idCount === 1) { | ||
| reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule); | ||
| } else if (idCount > 1) { | ||
| reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule); | ||
| } | ||
| } | ||
|
|
||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * Rule: IE6-9 supports up to 31 stylesheet import. | ||
| * Reference: | ||
| * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "import-ie-limit", | ||
| name: "@import limit on IE6-IE9", | ||
| desc: "IE6-9 supports up to 31 @import per stylesheet", | ||
| browsers: "IE6, IE7, IE8, IE9", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this, | ||
| MAX_IMPORT_COUNT = 31, | ||
| count = 0; | ||
|
|
||
| function startPage() { | ||
| count = 0; | ||
| } | ||
|
|
||
| parser.addListener("startpage", startPage); | ||
|
|
||
| parser.addListener("import", function() { | ||
| count++; | ||
| }); | ||
|
|
||
| parser.addListener("endstylesheet", function() { | ||
| if (count > MAX_IMPORT_COUNT) { | ||
| reporter.rollupError( | ||
| "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.", | ||
| rule | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,25 @@ | ||
| /* | ||
| * Rule: Don't use @import, use <link> instead. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "import", | ||
| name: "Disallow @import", | ||
| desc: "Don't use @import, use <link> instead.", | ||
| url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this; | ||
|
|
||
| parser.addListener("import", function(event) { | ||
| reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule); | ||
| }); | ||
|
|
||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /* | ||
| * Rule: All properties should be in alphabetical order. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "order-alphabetical", | ||
| name: "Alphabetical order", | ||
| desc: "Assure properties are in alphabetical order", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this, | ||
| properties; | ||
|
|
||
| var startRule = function () { | ||
| properties = []; | ||
| }; | ||
|
|
||
| var endRule = function(event) { | ||
| var currentProperties = properties.join(","), | ||
| expectedProperties = properties.sort().join(","); | ||
|
|
||
| if (currentProperties !== expectedProperties) { | ||
| reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule); | ||
| } | ||
| }; | ||
|
|
||
| parser.addListener("startrule", startRule); | ||
| parser.addListener("startfontface", startRule); | ||
| parser.addListener("startpage", startRule); | ||
| parser.addListener("startpagemargin", startRule); | ||
| parser.addListener("startkeyframerule", startRule); | ||
| parser.addListener("startviewport", startRule); | ||
|
|
||
| parser.addListener("property", function(event) { | ||
| var name = event.property.text, | ||
| lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, ""); | ||
|
|
||
| properties.push(lowerCasePrefixLessName); | ||
| }); | ||
|
|
||
| parser.addListener("endrule", endRule); | ||
| parser.addListener("endfontface", endRule); | ||
| parser.addListener("endpage", endRule); | ||
| parser.addListener("endpagemargin", endRule); | ||
| parser.addListener("endkeyframerule", endRule); | ||
| parser.addListener("endviewport", endRule); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,63 +1,68 @@ | ||
| /* | ||
| * Rule: Don't use classes or IDs with elements (a.foo or a#foo). | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "overqualified-elements", | ||
| name: "Disallow overqualified elements", | ||
| desc: "Don't use classes or IDs with elements (a.foo or a#foo).", | ||
| url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this, | ||
| classes = {}; | ||
|
|
||
| parser.addListener("startrule", function(event) { | ||
| var selectors = event.selectors, | ||
| selector, | ||
| part, | ||
| modifier, | ||
| i, j, k; | ||
|
|
||
| for (i=0; i < selectors.length; i++) { | ||
| selector = selectors[i]; | ||
|
|
||
| for (j=0; j < selector.parts.length; j++) { | ||
| part = selector.parts[j]; | ||
| if (part.type === parser.SELECTOR_PART_TYPE) { | ||
| for (k=0; k < part.modifiers.length; k++) { | ||
| modifier = part.modifiers[k]; | ||
| if (part.elementName && modifier.type === "id") { | ||
| reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule); | ||
| } else if (modifier.type === "class") { | ||
|
|
||
| if (!classes[modifier]) { | ||
| classes[modifier] = []; | ||
| } | ||
| classes[modifier].push({ | ||
| modifier: modifier, | ||
| part: part | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| parser.addListener("endstylesheet", function() { | ||
|
|
||
| var prop; | ||
| for (prop in classes) { | ||
| if (classes.hasOwnProperty(prop)) { | ||
|
|
||
| // one use means that this is overqualified | ||
| if (classes[prop].length === 1 && classes[prop][0].part.elementName) { | ||
| reporter.report("Element (" + classes[prop][0].part + ") is overqualified, just use " + classes[prop][0].modifier + " without element name.", classes[prop][0].part.line, classes[prop][0].part.col, rule); | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| CSSLint.addRule({ | ||
| id: "performant-transitions", | ||
| name: "Allow only performant transisitons", | ||
| desc: "Only allow transitions that trigger compositing for performant, 60fps transformations.", | ||
| url: "", | ||
| browsers: "All", | ||
|
|
||
| init: function(parser, reporter){ | ||
| "use strict"; | ||
| var rule = this; | ||
|
|
||
| var transitionProperties = ["transition-property", "transition", "-webkit-transition", "-o-transition"]; | ||
| var allowedTransitions = [/-webkit-transform/g, /-ms-transform/g, /transform/g, /opacity/g]; | ||
|
|
||
| parser.addListener("property", function(event) { | ||
| var propertyName = event.property.toString().toLowerCase(), | ||
| propertyValue = event.value.toString(), | ||
| line = event.line, | ||
| col = event.col; | ||
|
|
||
| var values = propertyValue.split(","); | ||
| if (transitionProperties.indexOf(propertyName) !== -1) { | ||
| var reportValues = values.filter(function(value) { | ||
| var didMatch = []; | ||
| for (var i = 0; i < allowedTransitions.length; i++) { | ||
| if(value.match(allowedTransitions[i])) { | ||
| didMatch.push(i); | ||
| } | ||
| } | ||
| return didMatch.length === 0; | ||
| }); | ||
| if(reportValues.length > 0) { | ||
| reporter.report("Unexpected transition property '"+reportValues.join(",").trim()+"'", line, col, rule); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,28 @@ | ||
| /* | ||
| * Rule: Total number of rules should not exceed x. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "rules-count", | ||
| name: "Rules Count", | ||
| desc: "Track how many rules there are.", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var count = 0; | ||
|
|
||
| // count each rule | ||
| parser.addListener("startrule", function() { | ||
| count++; | ||
| }); | ||
|
|
||
| parser.addListener("endstylesheet", function() { | ||
| reporter.stat("rule-count", count); | ||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,29 @@ | ||
| /* | ||
| * Rule: Warn people past the IE 4095 limit | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "selector-max", | ||
| name: "Error when past the 4095 selector limit for IE", | ||
| desc: "Will error when selector count is > 4095.", | ||
| browsers: "IE", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this, count = 0; | ||
|
|
||
| parser.addListener("startrule", function(event) { | ||
| count += event.selectors.length; | ||
| }); | ||
|
|
||
| parser.addListener("endstylesheet", function() { | ||
| if (count > 4095) { | ||
| reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /* | ||
| * Rule: Avoid new-line characters in selectors. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "selector-newline", | ||
| name: "Disallow new-line characters in selectors", | ||
| desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this; | ||
|
|
||
| function startRule(event) { | ||
| var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine, | ||
| selectors = event.selectors; | ||
|
|
||
| for (i = 0, len = selectors.length; i < len; i++) { | ||
| selector = selectors[i]; | ||
| for (p = 0, pLen = selector.parts.length; p < pLen; p++) { | ||
| for (n = p + 1; n < pLen; n++) { | ||
| part = selector.parts[p]; | ||
| part2 = selector.parts[n]; | ||
| type = part.type; | ||
| currentLine = part.line; | ||
| nextLine = part2.line; | ||
|
|
||
| if (type === "descendant" && nextLine > currentLine) { | ||
| reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
| } | ||
|
|
||
| parser.addListener("startrule", startRule); | ||
|
|
||
| } | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,35 +1,36 @@ | ||
| /* | ||
| * Rule: Don't use universal selector because it's slow. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "universal-selector", | ||
| name: "Disallow universal selector", | ||
| desc: "The universal selector (*) is known to be slow.", | ||
| url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
| var rule = this; | ||
|
|
||
| parser.addListener("startrule", function(event) { | ||
| var selectors = event.selectors, | ||
| selector, | ||
| part, | ||
| i; | ||
|
|
||
| for (i=0; i < selectors.length; i++) { | ||
| selector = selectors[i]; | ||
|
|
||
| part = selector.parts[selector.parts.length-1]; | ||
| if (part.elementName === "*") { | ||
| reporter.report(rule.desc, part.line, part.col, rule); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,42 +1,57 @@ | ||
| /* | ||
| * Rule: Don't use unqualified attribute selectors because they're just like universal selectors. | ||
| */ | ||
|
|
||
| CSSLint.addRule({ | ||
|
|
||
| // rule information | ||
| id: "unqualified-attributes", | ||
| name: "Disallow unqualified attribute selectors", | ||
| desc: "Unqualified attribute selectors are known to be slow.", | ||
| url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors", | ||
| browsers: "All", | ||
|
|
||
| // initialization | ||
| init: function(parser, reporter) { | ||
| "use strict"; | ||
|
|
||
| var rule = this; | ||
|
|
||
| parser.addListener("startrule", function(event) { | ||
|
|
||
| var selectors = event.selectors, | ||
| selectorContainsClassOrId = false, | ||
| selector, | ||
| part, | ||
| modifier, | ||
| i, k; | ||
|
|
||
| for (i=0; i < selectors.length; i++) { | ||
| selector = selectors[i]; | ||
|
|
||
| part = selector.parts[selector.parts.length-1]; | ||
| if (part.type === parser.SELECTOR_PART_TYPE) { | ||
| for (k=0; k < part.modifiers.length; k++) { | ||
| modifier = part.modifiers[k]; | ||
|
|
||
| if (modifier.type === "class" || modifier.type === "id") { | ||
| selectorContainsClassOrId = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (!selectorContainsClassOrId) { | ||
| for (k=0; k < part.modifiers.length; k++) { | ||
| modifier = part.modifiers[k]; | ||
| if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) { | ||
| reporter.report(rule.desc, part.line, part.col, rule); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
| }); | ||
| } | ||
|
|
||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /* jshint node:true */ | ||
| "use strict"; | ||
|
|
||
| module.exports = function(grunt) { | ||
| grunt.registerMultiTask("changelog", "Write the changelog file", function() { | ||
| var done = this.async(); | ||
| var lastTag; | ||
| var files = this.filesSrc; | ||
|
|
||
|
|
||
| grunt.util.spawn({ | ||
| cmd: "git", | ||
| args: ["tag"] | ||
| }, function(error, result) { | ||
| // Find the latest git tag | ||
| var tags = result.stdout.split("\n"), | ||
| semver = tags[0].replace("v", "").split("."), | ||
| major = parseInt(semver[0], 10), | ||
| minor = parseInt(semver[1], 10), | ||
| patch = parseInt(semver[2], 10); | ||
|
|
||
| // A simple array sort can't be used because of the comparison of | ||
| // the strings "0.9.9" > "0.9.10" | ||
| for (var i = 1, len = tags.length; i < len; i++) { | ||
| semver = tags[i].replace("v", "").split("."); | ||
|
|
||
| var currentMajor = parseInt(semver[0], 10); | ||
| if (currentMajor < major) { | ||
| continue; | ||
| } else if (currentMajor > major) { | ||
| major = currentMajor; | ||
| } | ||
|
|
||
| var currentMinor = parseInt(semver[1], 10); | ||
| if (currentMinor < minor) { | ||
| continue; | ||
| } else if (currentMinor > minor) { | ||
| minor = currentMinor; | ||
| } | ||
|
|
||
| var currentPatch = parseInt(semver[2], 10); | ||
| if (currentPatch < patch) { | ||
| continue; | ||
| } else if (currentPatch > patch) { | ||
| patch = currentPatch; | ||
| } | ||
| } | ||
|
|
||
| lastTag = "v" + major + "." + minor + "." + patch; | ||
|
|
||
| grunt.verbose.write("Last tag: " + lastTag).writeln(); | ||
|
|
||
| grunt.util.spawn({ | ||
| cmd: "git", | ||
| args: ["log", "--pretty=format:'* %s (%an)'", lastTag + "..HEAD"] | ||
| }, function(error, result) { | ||
| var prettyPrint = result.stdout.split("'\n'") | ||
| .join("\n") | ||
| .replace(/"$/, "") | ||
| .replace(/^"/, "") | ||
| .replace(/^'/, "") | ||
| .replace(/'$/, ""); | ||
|
|
||
| grunt.verbose.writeln().write(prettyPrint).writeln(); | ||
|
|
||
| var template = "<%= grunt.template.today('mmmm d, yyyy') %> - v<%= pkg.version %>\n\n" + | ||
| prettyPrint + "\n\n" + | ||
| grunt.file.read(files[0]); | ||
|
|
||
| grunt.file.write(files[0], grunt.template.process(template)); | ||
|
|
||
| done(); | ||
| }); | ||
| }); | ||
|
|
||
| }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /* jshint node:true */ | ||
| "use strict"; | ||
|
|
||
| module.exports = function(grunt) { | ||
| // Run test suite through rhino | ||
| grunt.registerMultiTask("test_rhino", "Run the test suite through rhino", function() { | ||
| var done = this.async(); | ||
| var files = this.filesSrc; | ||
| var progress = files.length; | ||
|
|
||
| files.forEach(function(filepath) { | ||
| grunt.util.spawn({ | ||
| cmd: "java", | ||
| args: ["-jar", "lib/js.jar", "lib/yuitest-rhino-cli.js", "dist/csslint.js", filepath], | ||
| opts: { stdio: "inherit" } | ||
| }, function() { | ||
| progress--; | ||
| if (progress === 0) { | ||
| done(); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| /* jshint evil:true, node:true */ | ||
| "use strict"; | ||
|
|
||
| module.exports = function(grunt) { | ||
| grunt.registerMultiTask("yuitest", "Run the YUITests for the project", function() { | ||
|
|
||
| var YUITest = require("yuitest"); | ||
| var CSSLint = require("../dist/csslint-node").CSSLint; // jshint ignore:line | ||
| var files = this.filesSrc; | ||
| var TestRunner = YUITest.TestRunner; | ||
| var done = this.async(); | ||
| var failures = [], | ||
| stack = []; | ||
|
|
||
| // Eval each file so the tests are brought into this scope where CSSLint and YUITest are loaded already | ||
| files.forEach(function(filepath) { | ||
| eval(grunt.file.read(filepath)); | ||
| }); | ||
|
|
||
| // From YUITest Node CLI with minor colourization changes | ||
| function handleEvent(event) { | ||
|
|
||
| var message = "", | ||
| results = event.results, | ||
| i, len, gruntFailMessage; | ||
|
|
||
| switch (event.type) { | ||
| case TestRunner.BEGIN_EVENT: | ||
| grunt.verbose.subhead("YUITest for Node.js"); | ||
|
|
||
| if (TestRunner._groups) { | ||
| grunt.verbose.writeln("Filtering on groups '" + TestRunner._groups.slice(1, -1) + "'"); | ||
| } | ||
| break; | ||
|
|
||
| case TestRunner.COMPLETE_EVENT: | ||
| grunt.log.writeln("Total tests: " + results.total + ", " + | ||
| ("Failures: " + results.failed).red + ", " + | ||
| ("Skipped: " + results.ignored).yellow + | ||
| ", Time: " + results.duration / 1000 + " seconds\n"); | ||
|
|
||
| if (failures.length) { | ||
| grunt.log.writeln("Tests failed:"); | ||
|
|
||
| for (i=0, len=failures.length; i < len; i++) { | ||
| gruntFailMessage += failures[i].name + "\n" + failures[i].error; | ||
| } | ||
| grunt.fail.warn(gruntFailMessage); | ||
| } | ||
|
|
||
| // Tell grunt we're done the async operation | ||
| done(); | ||
| break; | ||
|
|
||
| case TestRunner.TEST_FAIL_EVENT: | ||
| message = "F".red; | ||
| failures.push({ | ||
| name: stack.concat([event.testName]).join(" > "), | ||
| error: event.error | ||
| }); | ||
|
|
||
| break; | ||
|
|
||
| case TestRunner.ERROR_EVENT: | ||
| grunt.fail.fatal(event.error, stack); | ||
| break; | ||
|
|
||
| case TestRunner.TEST_IGNORE_EVENT: | ||
| message = "S".yellow; | ||
| break; | ||
|
|
||
| case TestRunner.TEST_PASS_EVENT: | ||
| message = ".".green; | ||
| break; | ||
|
|
||
| case TestRunner.TEST_SUITE_BEGIN_EVENT: | ||
| stack.push(event.testSuite.name); | ||
| break; | ||
|
|
||
| case TestRunner.TEST_CASE_COMPLETE_EVENT: | ||
| case TestRunner.TEST_SUITE_COMPLETE_EVENT: | ||
| stack.pop(); | ||
| break; | ||
|
|
||
| case TestRunner.TEST_CASE_BEGIN_EVENT: | ||
| stack.push(event.testCase.name); | ||
| break; | ||
|
|
||
| // no default | ||
| } | ||
|
|
||
| grunt.log.write(message); | ||
| } | ||
| // Add event listeners | ||
| TestRunner.subscribe(TestRunner.BEGIN_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.TEST_FAIL_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.TEST_PASS_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.ERROR_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.TEST_IGNORE_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.TEST_CASE_BEGIN_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.TEST_CASE_COMPLETE_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.TEST_SUITE_BEGIN_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.TEST_SUITE_COMPLETE_EVENT, handleEvent); | ||
| TestRunner.subscribe(TestRunner.COMPLETE_EVENT, handleEvent); | ||
| TestRunner.run(); | ||
| }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "extends" : "../.jshintrc", | ||
| "globals": { | ||
| "YUITest": true | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| /* jshint node:true */ | ||
|
|
||
| "use strict"; | ||
|
|
||
| var stub = { | ||
| logbook: function(log) { | ||
| this.logs.push(log); | ||
| }, | ||
| readLogs: function() { | ||
| return this.logs.slice(); | ||
| }, | ||
|
|
||
| getFullPath: function(path) { | ||
| return path; | ||
| }, | ||
| getFiles: function(dir) { | ||
| var filesobj = this.fakedFs[dir], | ||
| fileix, | ||
| out = []; | ||
| for (fileix in filesobj) { | ||
| if (filesobj.hasOwnProperty(fileix) && /\.css$/.test(fileix)) { | ||
| out.push(dir + "/" + fileix); | ||
| } | ||
| } | ||
| return out; | ||
| }, | ||
| readFile: function(path) { | ||
| var spath = path.split("/"), | ||
| spathLen = spath.length, | ||
| i, | ||
| out = this.fakedFs; | ||
|
|
||
| for (i = 0; i < spathLen; i += 1) { | ||
| out = out[spath[i]]; | ||
| } | ||
|
|
||
| return out; | ||
| }, | ||
| isDirectory: function(checkit) { | ||
| var result = this.fakedFs[checkit]; | ||
| return typeof result === "object"; | ||
| }, | ||
| print: function(msg) { | ||
| this.logbook(msg); | ||
| }, | ||
| quit: function(signal) { | ||
| this.logbook(signal); | ||
| } | ||
| }; | ||
|
|
||
| module.exports = function(setup) { | ||
| var api, | ||
| setix; | ||
|
|
||
| api = Object.create(stub); | ||
|
|
||
| for (setix in setup) { | ||
| if (setup.hasOwnProperty(setix)) { | ||
| api[setix] = setup[setix]; | ||
| } | ||
| } | ||
|
|
||
| api.logs = []; | ||
| return api; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| /* jshint node:true */ | ||
|
|
||
| module.exports = { | ||
| "suites": { | ||
| "config csslintrc override": { | ||
| "args": [ | ||
| "--config=.rc1", | ||
| "dir" | ||
| ], | ||
| "expecting": [ | ||
| "csslint: No errors in dir/a.css.", | ||
| "csslint: No errors in dir/b.css.", | ||
| 0 | ||
| ] | ||
| }, | ||
| "straight linting": { | ||
| "args": [ | ||
| "dir" | ||
| ], | ||
| "expecting": [ | ||
| "csslint: There is 1 problem in dir/a.css.", | ||
| "csslint: There is 1 problem in dir/b.css.", | ||
| 0 | ||
| ] | ||
| }, | ||
| "mix of cli options": { | ||
| "args": [ | ||
| "--config=.rc1", | ||
| "--ignore=important", | ||
| "dir" | ||
| ], | ||
| "expecting": [ | ||
| "csslint: No errors in dir/a.css.", | ||
| "csslint: There is 1 problem in dir/b.css.", | ||
| 0 | ||
| ] | ||
| }, | ||
| "more mixes of cli options": { | ||
| "args": [ | ||
| "--config=.rc1", | ||
| "--errors=important", | ||
| "dir" | ||
| ], | ||
| "expecting": [ | ||
| "csslint: There is 1 problem in dir/a.css.", | ||
| "csslint: No errors in dir/b.css.", | ||
| 1 | ||
| ] | ||
| } | ||
| }, | ||
|
|
||
| "fakedFs": { | ||
| ".rc1": "--ignore=important,ids", | ||
| "dir": { | ||
| "a.css": ".a {color: red!important;}", | ||
| "b.css": "#a {color: red;}" | ||
| } | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| /* jshint loopfunc:true, node:true */ | ||
|
|
||
| "use strict"; | ||
| function include(path, sandbox) { | ||
| var vm = require("vm"), | ||
| fs = require("fs"), | ||
| file; | ||
|
|
||
| file = fs.readFileSync(path); | ||
| vm.runInNewContext(file, sandbox); | ||
| } | ||
|
|
||
|
|
||
| (function() { | ||
|
|
||
| var Assert = YUITest.Assert, | ||
| suite = new YUITest.TestSuite("General Tests for CLI"), | ||
| apiStub = require("../tests/cli/assets/apiStub.js"), | ||
| data = require("../tests/cli/assets/data.js"), | ||
| suites = data.suites, | ||
| suiteix, | ||
| sandbox = { | ||
| CSSLint: CSSLint | ||
| }; | ||
|
|
||
| include("./src/cli/common.js", sandbox); /* expose sandbox.cli */ | ||
|
|
||
| for (suiteix in suites) { | ||
| if (suites.hasOwnProperty(suiteix)) { | ||
| (function (suiteix) { | ||
|
|
||
| suite.add(new YUITest.TestCase({ | ||
|
|
||
| name: "Test " + suiteix, | ||
|
|
||
| "Outcome logs should match expected": function () { | ||
| var it = suites[suiteix], | ||
| expecting = it.expecting, | ||
| expectingLen = expecting.length, | ||
| outcome, | ||
| api, | ||
| exp, | ||
| out, | ||
| i = 0; | ||
|
|
||
| data.args = it.args.slice(); | ||
| api = apiStub(data); | ||
| sandbox.cli(api); | ||
| outcome = api.readLogs(); | ||
|
|
||
| for (i; i < expectingLen; i += 1) { | ||
| exp = expecting[i]; | ||
| out = outcome[i]; | ||
|
|
||
| if (typeof out === "string") { | ||
| out = /^.*/.exec(out.trim())[0]; | ||
| } | ||
| if (exp !== out) { | ||
| Assert.fail("Expecting: " + exp + " Got: " + out); | ||
| } | ||
| } | ||
| Assert.pass(); | ||
|
|
||
| } | ||
| })); | ||
| })(suiteix); | ||
| } | ||
| } | ||
|
|
||
| YUITest.TestRunner.add(suite); | ||
| })(); |