From 5547e8a27e3b8598d78d3fcb08ac77b85f6a346b Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Mon, 8 Jul 2013 14:55:30 -0700 Subject: [PATCH 1/4] Refactor parser's private getLocation method for clarity, and reuse appropriately. --- lib/less/parser.js | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/less/parser.js b/lib/less/parser.js index 9c7f878e..f5a1826a 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -235,13 +235,23 @@ less.Parser = function Parser(env) { } } - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } + function getLocation(index, inputStream) { + var n = index + 1, + line = null, + column = -1; - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; + while (--n >= 0 && inputStream.charAt(n) !== '\n') { + column++; + } + + if (typeof index === 'number') { + line = (inputStream.slice(0, index).match(/\n/g) || "").length; + } + + return { + line: line, + column: column + }; } function getDebugInfo(index, inputStream, env) { @@ -261,6 +271,7 @@ less.Parser = function Parser(env) { loc = getLocation(e.index, input), line = loc.line, col = loc.column, + callLine = e.call && getLocation(e.call, input).line, lines = input.split('\n'); this.type = e.type || 'Syntax'; @@ -268,8 +279,8 @@ less.Parser = function Parser(env) { this.filename = e.filename || env.currentFileInfo.filename; this.index = e.index; this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; + this.callLine = callLine + 1; + this.callExtract = lines[callLine]; this.stack = e.stack; this.column = col; this.extract = [ @@ -466,10 +477,9 @@ less.Parser = function Parser(env) { // and the part which didn't), so we can color them differently. if (i < input.length - 1) { i = furthest; + var loc = getLocation(i, input); lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } + line = loc.line + 1; error = { type: "Parse", @@ -477,7 +487,7 @@ less.Parser = function Parser(env) { index: i, filename: env.currentFileInfo.filename, line: line, - column: column, + column: loc.column, extract: [ lines[line - 2], lines[line - 1], From 8eeaf87a791b095434be41d86fd18dda04be76ec Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Mon, 8 Jul 2013 19:05:01 -0700 Subject: [PATCH 2/4] JSHint lib/* and test runners. --- lib/less/browser.js | 41 ++++---- lib/less/env.js | 5 +- lib/less/extend-visitor.js | 8 +- lib/less/functions.js | 17 ++-- lib/less/index.js | 10 +- lib/less/lessc_helper.js | 2 +- lib/less/parser.js | 153 ++++++++++++++++++---------- lib/less/rhino.js | 8 +- lib/less/tree.js | 11 +- lib/less/tree/anonymous.js | 2 +- lib/less/tree/assignment.js | 2 +- lib/less/tree/call.js | 1 + lib/less/tree/color.js | 2 +- lib/less/tree/condition.js | 2 +- lib/less/tree/dimension.js | 9 +- lib/less/tree/element.js | 2 +- lib/less/tree/javascript.js | 1 + lib/less/tree/keyword.js | 2 +- lib/less/tree/media.js | 2 +- lib/less/tree/mixin.js | 13 +-- lib/less/tree/ruleset.js | 38 +++---- lib/less/tree/selector.js | 3 +- lib/less/tree/unicode-descriptor.js | 2 +- lib/less/tree/variable.js | 8 +- test/browser-test-prepare.js | 4 +- test/less-test.js | 10 +- 26 files changed, 217 insertions(+), 141 deletions(-) diff --git a/lib/less/browser.js b/lib/less/browser.js index 0fc98811..f7f6a964 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -1,6 +1,7 @@ // // browser.js - client-side engine // +/*global less */ var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); @@ -43,7 +44,7 @@ less.watch = function () { less.env = 'development'; initRunningMode(); } - return this.watchMode = true + return this.watchMode = true; }; less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; @@ -105,12 +106,12 @@ less.modifyVars = function(record) { newVars += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); } - less.refresh(false, newVars) + less.refresh(false, newVars); }; less.refresh = function (reload, newVars) { var startTime, endTime; - startTime = endTime = new(Date); + startTime = endTime = new Date(); loadStyleSheets(function (e, root, _, sheet, env) { if (e) { @@ -122,9 +123,9 @@ less.refresh = function (reload, newVars) { log("parsed " + sheet.href + " successfully."); createCSS(root.toCSS(less), sheet, env.lastModified); } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); + log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); + (env.remaining === 0) && log("css generated in " + (new Date() - startTime) + 'ms'); + endTime = new Date(); }, reload, newVars); loadStyles(newVars); @@ -145,6 +146,7 @@ function loadStyles(newVars) { lessText += "\n" + newVars; } + /*jshint loopfunc:true */ // use closure to store current value of i var callback = (function(style) { return function (e, cssAST) { @@ -158,7 +160,7 @@ function loadStyles(newVars) { } else { style.innerHTML = css; } - } + }; })(style); new(less.Parser)(env).parse(lessText, callback); } @@ -337,20 +339,20 @@ function loadFile(originalHref, currentFileInfo, callback, env, newVars) { if (newVars) { lessText += "\n" + newVars; } - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }) + callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); } catch (e) { callback(e, null, href); } return; } - xhr(href, env.mime, function (data, lastModified) { + doXHR(href, env.mime, function (data, lastModified) { // per file cache fileCache[href] = data; // Use remote copy (re-parse) try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }) + callback(null, data, href, newFileInfo, { lastModified: lastModified }); } catch (e) { callback(e, null, href); } @@ -406,7 +408,7 @@ function createCSS(styles, sheet, lastModified) { // If there is no oldCss, just append; otherwise, only append if we need // to replace oldCss with an updated stylesheet - if (oldCss == null || keepOldCss === false) { + if (oldCss === null || keepOldCss === false) { var nextEl = sheet && sheet.nextSibling || null; (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl); } @@ -427,7 +429,7 @@ function createCSS(styles, sheet, lastModified) { } } -function xhr(url, type, callback, errback) { +function doXHR(url, type, callback, errback) { var xhr = getXMLHttpRequest(); var async = isFileProtocol ? less.fileAsync : less.async; @@ -467,10 +469,11 @@ function xhr(url, type, callback, errback) { function getXMLHttpRequest() { if (window.XMLHttpRequest) { - return new(XMLHttpRequest); + return new XMLHttpRequest(); } else { try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); + /*global ActiveXObject */ + return new ActiveXObject("MSXML2.XMLHTTP.3.0"); } catch (e) { log("browser doesn't support AJAX."); return null; @@ -483,13 +486,13 @@ function removeNode(node) { } function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } + if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str); } } function error(e, rootHref) { var id = 'less-error-message:' + extractId(rootHref || ""); var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; + var elem = document.createElement('div'), timer, content, errors = []; var filename = e.filename || rootHref; var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; @@ -500,8 +503,8 @@ function error(e, rootHref) { '' + '

    in ' + filenameNoPath + " "; var errorline = function (e, i, classname) { - if (e.extract[i] != undefined) { - error.push(template.replace(/\{line\}/, (parseInt(e.line) || 0) + (i - 1)) + if (e.extract[i] !== undefined) { + errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) .replace(/\{class\}/, classname) .replace(/\{content\}/, e.extract[i])); } @@ -512,7 +515,7 @@ function error(e, rootHref) { errorline(e, 1, 'line'); errorline(e, 2, ''); content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - ''; + ''; } else if (e.stack) { content += '
    ' + e.stack.split('\n').slice(1).join('
    '); } diff --git a/lib/less/env.js b/lib/less/env.js index 203830e0..18d8d5c4 100644 --- a/lib/less/env.js +++ b/lib/less/env.js @@ -97,5 +97,6 @@ destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; } } - } -})(require('./tree')); \ No newline at end of file + }; + +})(require('./tree')); diff --git a/lib/less/extend-visitor.js b/lib/less/extend-visitor.js index cd9d9458..6d04ff1f 100644 --- a/lib/less/extend-visitor.js +++ b/lib/less/extend-visitor.js @@ -1,4 +1,6 @@ (function (tree) { + /*jshint loopfunc:true */ + tree.extendFinderVisitor = function() { this._visitor = new tree.visitor(this); this.contexts = []; @@ -246,7 +248,7 @@ haystackElement = hackstackSelector.elements[hackstackElementIndex]; // if we allow elements before our match we can add a potential match every time. otherwise only at the first element. - if (extend.allowBefore || (haystackSelectorIndex == 0 && hackstackElementIndex == 0)) { + if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) { potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); } @@ -257,7 +259,7 @@ // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out // what the resulting combinator will be targetCombinator = haystackElement.combinator.value; - if (targetCombinator == '' && hackstackElementIndex === 0) { + if (targetCombinator === '' && hackstackElementIndex === 0) { targetCombinator = ' '; } @@ -388,4 +390,4 @@ } }; -})(require('./tree')); \ No newline at end of file +})(require('./tree')); diff --git a/lib/less/functions.js b/lib/less/functions.js index 93b13fdf..cee8379a 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -26,10 +26,10 @@ tree.functions = { function hue(h) { h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h); - if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; - else if (h * 2 < 1) return m2; - else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6; - else return m1; + if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; } + else if (h * 2 < 1) { return m2; } + else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; } + else { return m1; } } }, @@ -223,6 +223,7 @@ tree.functions = { str = quoted.value; for (var i = 0; i < args.length; i++) { + /*jshint loopfunc:true */ str = str.replace(/%[sda]/i, function(token) { var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; @@ -259,6 +260,7 @@ tree.functions = { }, _math: function (fn, unit, n) { if (n instanceof tree.Dimension) { + /*jshint eqnull:true */ return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); } else if (typeof(n) === 'number') { return fn(n); @@ -463,10 +465,10 @@ tree.functions = { // use base 64 unless it's an ASCII or UTF-8 format var charset = mime.charsets.lookup(mimetype); useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; - if (useBase64) mimetype += ';base64'; + if (useBase64) { mimetype += ';base64'; } } else { - useBase64 = /;base64$/.test(mimetype) + useBase64 = /;base64$/.test(mimetype); } var buf = fs.readFileSync(filePath); @@ -532,7 +534,7 @@ tree.functions = { gradientType = "radial"; gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"'; rectangleDimension = 'x="-50" y="-50" width="101" height="101"'; - break + break; default: throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'radial'" }; } @@ -607,6 +609,7 @@ var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"} {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], createMathFunction = function(name, unit) { return function(n) { + /*jshint eqnull:true */ if (unit != null) { n = n.unify(); } diff --git a/lib/less/index.js b/lib/less/index.js index 94631555..297a7152 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -24,7 +24,7 @@ var less = { catch (err) { callback(err); } }); } else { - ee = new(require('events').EventEmitter); + ee = new (require('events').EventEmitter)(); process.nextTick(function () { parser.parse(input, function (e, root) { @@ -42,10 +42,10 @@ var less = { var message = ""; var extract = ctx.extract; var error = []; - var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str }; + var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red') } + if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } if (!ctx.hasOwnProperty('index') || !extract) { return ctx.stack || ctx.message; @@ -132,7 +132,7 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { newFileInfo.filename = pathname; callback(null, data, pathname, newFileInfo); - }; + } var isUrl = isUrlRe.test( file ); if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) { @@ -205,7 +205,7 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { }); } } -} +}; require('./env'); require('./functions'); diff --git a/lib/less/lessc_helper.js b/lib/less/lessc_helper.js index 08d29992..ed5df671 100644 --- a/lib/less/lessc_helper.js +++ b/lib/less/lessc_helper.js @@ -67,4 +67,4 @@ var lessc_helper = { }; // Exports helper functions -for (var h in lessc_helper) { exports[h] = lessc_helper[h] } +for (var h in lessc_helper) { exports[h] = lessc_helper[h]; } diff --git a/lib/less/parser.js b/lib/less/parser.js index 9c7f878e..e2c60339 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1,21 +1,22 @@ var less, tree, charset; +/*global environment */ if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { // Rhino // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } + if (typeof(window) === 'undefined') { less = {}; } + else { less = window.less = {}; } tree = less.tree = {}; less.mode = 'rhino'; } else if (typeof(window) === 'undefined') { // Node.js - less = exports, + less = exports; tree = require('./tree'); less.mode = 'node'; } else { // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, + if (typeof(window.less) === 'undefined') { window.less = {}; } + less = window.less; tree = window.less.tree = {}; less.mode = 'browser'; } @@ -117,7 +118,7 @@ less.Parser = function Parser(env) { fileParsedFunc(e, root, fullPath); }); } - }, env) + }, env); } } }; @@ -189,13 +190,13 @@ less.Parser = function Parser(env) { mem = i += length; while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } + if (! isWhitespace(input.charAt(i))) { break; } i++; } chunks[j] = chunks[j].slice(length + (i - mem)); current = i; - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } + if (chunks[j].length === 0 && j < chunks.length - 1) { j++; } return oldi !== i || oldj !== j; } @@ -238,7 +239,7 @@ less.Parser = function Parser(env) { function getLocation(index, input) { for (var n = index, column = -1; n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } + n--) { column++; } return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, column: column }; @@ -353,16 +354,42 @@ less.Parser = function Parser(env) { } switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); + case '{': + if (!inParam) { + level++; + chunk.push(c); + break; + } + /* falls through */ + case '}': + if (!inParam) { + level--; + chunk.push(c); + chunks[++j] = chunk = []; + break; + } + /* falls through */ + case '(': + if (!inParam) { + inParam = true; + chunk.push(c); + break; + } + /* falls through */ + case ')': + if (inParam) { + inParam = false; + chunk.push(c); + break; + } + /* falls through */ + default: + chunk.push(c); } i++; } - if (level != 0) { + if (level !== 0) { error = new(LessError)({ index: i-1, type: 'Parse', @@ -371,7 +398,7 @@ less.Parser = function Parser(env) { }, env); } - return chunks.map(function (c) { return c.join('') }); + return chunks.map(function (c) { return c.join(''); }); })([[]]); if (error) { @@ -396,6 +423,8 @@ less.Parser = function Parser(env) { return function (options, variables) { options = options || {}; var importError, + evaldRoot, + css, evalEnv = new tree.evalEnv(options); // @@ -427,7 +456,7 @@ less.Parser = function Parser(env) { } try { - var evaldRoot = evaluate.call(this, evalEnv); + evaldRoot = evaluate.call(this, evalEnv); new(tree.joinSelectorVisitor)() .run(evaldRoot); @@ -438,7 +467,7 @@ less.Parser = function Parser(env) { new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) .run(evaldRoot); - var css = evaldRoot.toCSS({ + css = evaldRoot.toCSS({ compress: Boolean(options.compress), dumpLineNumbers: env.dumpLineNumbers, strictUnits: Boolean(options.strictUnits)}); @@ -469,7 +498,7 @@ less.Parser = function Parser(env) { lines = input.split('\n'); line = (input.slice(0, i).match(/\n/g) || "").length + 1; - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } + for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++; } error = { type: "Parse", @@ -571,7 +600,7 @@ less.Parser = function Parser(env) { comment: function () { var comment; - if (input.charAt(i) !== '/') return; + if (input.charAt(i) !== '/') { return; } if (input.charAt(i + 1) === '/') { return new(tree.Comment)($(/^\/\/.*/), true, i, env.currentFileInfo); @@ -602,8 +631,8 @@ less.Parser = function Parser(env) { quoted: function () { var str, j = i, e, index = i; - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; + if (input.charAt(j) === '~') { j++, e = true; } // Escaped strings + if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } e && $('~'); @@ -643,13 +672,13 @@ less.Parser = function Parser(env) { call: function () { var name, nameLC, args, alpha_ret, index = i; - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; + if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) { return; } name = name[1]; nameLC = name.toLowerCase(); - if (nameLC === 'url') { return null } - else { i += name.length } + if (nameLC === 'url') { return null; } + else { i += name.length; } if (nameLC === 'alpha') { alpha_ret = $(this.alpha); @@ -673,7 +702,9 @@ less.Parser = function Parser(env) { while (arg = $(this.entities.assignment) || $(this.expression)) { args.push(arg); - if (! $(',')) { break } + if (! $(',')) { + break; + } } return args; }, @@ -707,12 +738,16 @@ less.Parser = function Parser(env) { url: function () { var value; - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; + if (input.charAt(i) !== 'u' || !$(/^url\(/)) { + return; + } + value = $(this.entities.quoted) || $(this.entities.variable) || $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; expect(')'); + /*jshint eqnull:true */ return new(tree.URL)((value.value != null || value instanceof tree.Variable) ? value : new(tree.Anonymous)(value), env.currentFileInfo); }, @@ -765,7 +800,9 @@ less.Parser = function Parser(env) { dimension: function () { var value, c = input.charCodeAt(i); //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; + if ((c > 57 || c < 43) || c === 47 || c == 44) { + return; + } if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { return new(tree.Dimension)(value[1], value[2]); @@ -815,7 +852,7 @@ less.Parser = function Parser(env) { variable: function () { var name; - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } + if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1]; } }, // @@ -841,7 +878,7 @@ less.Parser = function Parser(env) { extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - } while($(",")) + } while($(",")); expect(/^\)/); @@ -877,7 +914,7 @@ less.Parser = function Parser(env) { call: function () { var elements = [], e, c, args, delim, arg, index = i, s = input.charAt(i), important = false; - if (s !== '.' && s !== '#') { return } + if (s !== '.' && s !== '#') { return; } save(); // stop us absorbing part of an invalid selector @@ -937,7 +974,7 @@ less.Parser = function Parser(env) { if (isCall) { // Variable if (arg.value.length == 1) { - var val = arg.value[0]; + val = arg.value[0]; } } else { val = arg; @@ -986,7 +1023,7 @@ less.Parser = function Parser(env) { isSemiColonSeperated = true; if (expressions.length > 1) { - value = new (tree.Value)(expressions); + value = new(tree.Value)(expressions); } argsSemiColon.push({ name:name, value:value }); @@ -1021,7 +1058,9 @@ less.Parser = function Parser(env) { definition: function () { var name, params = [], match, ruleset, param, value, cond, variadic = false; if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; + peek(/^[^{]*\}/)) { + return; + } save(); @@ -1083,7 +1122,7 @@ less.Parser = function Parser(env) { alpha: function () { var value; - if (! $(/^\(opacity=/i)) return; + if (! $(/^\(opacity=/i)) { return; } if (value = $(/^\d+/) || $(this.entities.variable)) { expect(')'); return new(tree.Alpha)(value); @@ -1119,7 +1158,7 @@ less.Parser = function Parser(env) { } } - if (e) { return new(tree.Element)(c, e, i) } + if (e) { return new(tree.Element)(c, e, i); } }, // @@ -1136,7 +1175,7 @@ less.Parser = function Parser(env) { if (c === '>' || c === '+' || c === '~' || c === '|') { i++; - while (input.charAt(i).match(/\s/)) { i++ } + while (input.charAt(i).match(/\s/)) { i++; } return new(tree.Combinator)(c); } else if (input.charAt(i - 1).match(/\s/)) { return new(tree.Combinator)(" "); @@ -1174,10 +1213,12 @@ less.Parser = function Parser(env) { error("Extend can only be used at the end of selector"); } c = input.charAt(i); - elements.push(e) + elements.push(e); e = null; } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } + if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { + break; + } } if (elements.length > 0) { return new(tree.Selector)(elements, extendList, condition, i, env.currentFileInfo); } @@ -1186,7 +1227,7 @@ less.Parser = function Parser(env) { attribute: function () { var attr = '', key, val, op; - if (! $('[')) return; + if (! $('[')) { return; } if (!(key = $(this.entities.variableCurly))) { key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); @@ -1220,8 +1261,9 @@ less.Parser = function Parser(env) { save(); - if (env.dumpLineNumbers) + if (env.dumpLineNumbers) { debugInfo = getDebugInfo(i, input, env); + } while (s = $(this.lessSelector)) { selectors.push(s); @@ -1235,8 +1277,9 @@ less.Parser = function Parser(env) { if (selectors.length > 0 && (rules = $(this.block))) { var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) + if (env.dumpLineNumbers) { ruleset.debugInfo = debugInfo; + } return ruleset; } else { // Backtrack @@ -1248,7 +1291,7 @@ less.Parser = function Parser(env) { var name, value, c = input.charAt(i), important, merge = false; save(); - if (c === '.' || c === '#' || c === '&') { return } + if (c === '.' || c === '#' || c === '&') { return; } if (name = $(this.variable) || $(this.ruleProperty)) { // prefer to try to parse first if its a variable or we are compressing @@ -1333,7 +1376,7 @@ less.Parser = function Parser(env) { break; } options[optionName] = value; - if (! $(',')) { break } + if (! $(',')) { break; } } } while (o); expect(')'); @@ -1364,7 +1407,7 @@ less.Parser = function Parser(env) { } else { return null; } - } else { return null } + } else { return null; } } } while (e); @@ -1379,10 +1422,10 @@ less.Parser = function Parser(env) { do { if (e = $(this.mediaFeature)) { features.push(e); - if (! $(',')) { break } + if (! $(',')) { break; } } else if (e = $(this.entities.variable)) { features.push(e); - if (! $(',')) { break } + if (! $(',')) { break; } } } while (e); @@ -1392,16 +1435,18 @@ less.Parser = function Parser(env) { media: function () { var features, rules, media, debugInfo; - if (env.dumpLineNumbers) + if (env.dumpLineNumbers) { debugInfo = getDebugInfo(i, input, env); + } if ($(/^@media/)) { features = $(this.mediaFeatures); if (rules = $(this.block)) { media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if(env.dumpLineNumbers) + if (env.dumpLineNumbers) { media.debugInfo = debugInfo; + } return media; } } @@ -1416,7 +1461,7 @@ less.Parser = function Parser(env) { var name, value, rules, identifier, e, nodes, nonVendorSpecificName, hasBlock, hasIdentifier, hasExpression; - if (input.charAt(i) !== '@') return; + if (input.charAt(i) !== '@') { return; } if (value = $(this['import']) || $(this.media)) { return value; @@ -1426,7 +1471,7 @@ less.Parser = function Parser(env) { name = $(/^@[a-z-]+/); - if (!name) return; + if (!name) { return; } nonVendorSpecificName = name; if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { @@ -1503,7 +1548,7 @@ less.Parser = function Parser(env) { while (e = $(this.expression)) { expressions.push(e); - if (! $(',')) { break } + if (! $(',')) { break; } } if (expressions.length > 0) { @@ -1571,7 +1616,7 @@ less.Parser = function Parser(env) { condition: function () { var a, b, c, op, index = i, negate = false; - if ($(/^not/)) { negate = true } + if ($(/^not/)) { negate = true; } expect('('); if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { if (op = $(/^(?:>=|=<|[<=>])/)) { @@ -1595,7 +1640,7 @@ less.Parser = function Parser(env) { operand: function () { var negate, p = input.charAt(i + 1); - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } + if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-'); } var o = $(this.sub) || $(this.entities.dimension) || $(this.entities.color) || $(this.entities.variable) || $(this.entities.call); diff --git a/lib/less/rhino.js b/lib/less/rhino.js index fe917a2b..08eba88a 100644 --- a/lib/less/rhino.js +++ b/lib/less/rhino.js @@ -1,3 +1,5 @@ +/*jshint rhino:true */ +/*global name:true, less */ var name; function loadStyleSheet(sheet, callback, reload, remaining) { @@ -61,7 +63,7 @@ function writeFile(filename, content) { print('No files present in the fileset; Check your pattern match in build.xml'); quit(1); } - path = name.split("/");path.pop();path=path.join("/") + path = name.split("/");path.pop();path=path.join("/"); var input = readFile(name); @@ -109,7 +111,7 @@ function error(e, filename) { var errorline = function (e, i, classname) { if (e.extract[i]) { content += - String(parseInt(e.line) + (i - 1)) + + String(parseInt(e.line, 10) + (i - 1)) + ":" + e.extract[i] + "\n"; } }; @@ -123,4 +125,4 @@ function error(e, filename) { errorline(e, 2); } print(content); -} \ No newline at end of file +} diff --git a/lib/less/tree.js b/lib/less/tree.js index 9aee4613..924aa891 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -24,19 +24,24 @@ tree.debugInfo.asComment = function(ctx) { tree.debugInfo.asMediaQuery = function(ctx) { return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function(a){if(a=='\\') a = '\/'; return '\\' + a}) + + ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) { + if (a == '\\') { + a = '\/'; + } + return '\\' + a; + }) + '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; }; tree.find = function (obj, fun) { for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } + if (r = fun.call(obj, obj[i])) { return r; } } return null; }; tree.jsify = function (obj) { if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; + return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; } else { return obj.toCSS(false); } diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index a009cf27..b78e0b94 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -8,7 +8,7 @@ tree.Anonymous.prototype = { toCSS: function () { return this.value; }, - eval: function () { return this }, + eval: function () { return this; }, compare: function (x) { if (!x.toCSS) { return -1; diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js index b0e2c555..1ba43685 100644 --- a/lib/less/tree/assignment.js +++ b/lib/less/tree/assignment.js @@ -20,4 +20,4 @@ tree.Assignment.prototype = { } }; -})(require('../tree')); \ No newline at end of file +})(require('../tree')); diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index 20a1fc0e..2bb88441 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -36,6 +36,7 @@ tree.Call.prototype = { try { func = new tree.functionCall(env, this.currentFileInfo); result = func[nameLC].apply(func, args); + /*jshint eqnull:true */ if (result != null) { return result; } diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index 95d5a101..a0465dbd 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -24,7 +24,7 @@ tree.Color = function (rgb, a) { }; tree.Color.prototype = { type: "Color", - eval: function () { return this }, + eval: function () { return this; }, luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, // diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index 8608b69d..47b1d008 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -19,7 +19,7 @@ tree.Condition.prototype = { var i = this.index, result; - var result = (function (op) { + result = (function (op) { switch (op) { case 'and': return a && b; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index 9850e6ff..bd58efe4 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -52,6 +52,7 @@ tree.Dimension.prototype = { // we default to the first Dimension's unit, // so `1px + 2` will yield `3px`. operate: function (env, op, other) { + /*jshint noempty:false */ var value = tree.operate(env, op, this.value, other.value), unit = this.unit.clone(); @@ -59,7 +60,7 @@ tree.Dimension.prototype = { if (unit.numerator.length === 0 && unit.denominator.length === 0) { unit.numerator = other.unit.numerator.slice(0); unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length == 0 && unit.denominator.length == 0) { + } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { // do nothing } else { other = other.convertTo(this.unit.usedUnits()); @@ -126,6 +127,7 @@ tree.Dimension.prototype = { targetUnit = conversions[groupName]; group = tree.UnitConversions[groupName]; + /*jshint loopfunc:true */ unit.map(function (atomicUnit, denominator) { if (group.hasOwnProperty(atomicUnit)) { if (denominator) { @@ -216,11 +218,11 @@ tree.Unit.prototype = { }, isEmpty: function () { - return this.numerator.length == 0 && this.denominator.length == 0; + return this.numerator.length === 0 && this.denominator.length === 0; }, isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length == 0; + return this.numerator.length <= 1 && this.denominator.length === 0; }, map: function(callback) { @@ -242,6 +244,7 @@ tree.Unit.prototype = { if (tree.UnitConversions.hasOwnProperty(groupName)) { group = tree.UnitConversions[groupName]; + /*jshint loopfunc:true */ this.map(function (atomicUnit) { if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { result[groupName] = atomicUnit; diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index 55d47903..8adc28ac 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -26,7 +26,7 @@ tree.Element.prototype = { }, toCSS: function (env) { var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { + if (value === '' && this.combinator.value.charAt(0) === '&') { return ''; } else { return this.combinator.toCSS(env || {}) + value; diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index eaa9c0fe..428a56d6 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -24,6 +24,7 @@ tree.JavaScript.prototype = { } for (var k in env.frames[0].variables()) { + /*jshint loopfunc:true */ context[k.slice(1)] = { value: env.frames[0].variables()[k].value, toJS: function () { diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index 3184fce2..25ab7851 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -1,6 +1,6 @@ (function (tree) { -tree.Keyword = function (value) { this.value = value }; +tree.Keyword = function (value) { this.value = value; }; tree.Keyword.prototype = { type: "Keyword", eval: function () { return this; }, diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 57d68a2d..55105929 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -69,7 +69,7 @@ tree.Media.prototype = { env.mediaPath.pop(); return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) + media.evalNested(env); }, variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js index b7e7f3e8..2908d981 100644 --- a/lib/less/tree/mixin.js +++ b/lib/less/tree/mixin.js @@ -96,8 +96,8 @@ tree.mixin.Definition = function (name, params, rules, condition, variadic) { this.rules = rules; this._lookups = {}; this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } + if (!p.name || (p.name && !p.value)) { return count + 1; } + else { return count; } }, 0); this.parent = tree.Ruleset.prototype; this.frames = []; @@ -116,6 +116,7 @@ tree.mixin.Definition.prototype = { rulesets: function () { return this.parent.rulesets.apply(this); }, evalParams: function (env, mixinEnv, args, evaldArguments) { + /*jshint boss:true */ var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), @@ -151,7 +152,7 @@ tree.mixin.Definition.prototype = { } argIndex = 0; for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; + if (evaldArguments[i]) { continue; } arg = args && args[argIndex]; @@ -219,9 +220,9 @@ tree.mixin.Definition.prototype = { var argsLength = (args && args.length) || 0, len, frame; if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } + if (argsLength < this.required) { return false; } + if (argsLength > this.params.length) { return false; } + if ((this.required > 0) && (argsLength > this.params.length)) { return false; } } len = Math.min(argsLength, this.arity); diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 9f7f0574..817b57c7 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -13,9 +13,11 @@ tree.Ruleset.prototype = { this.rules = visitor.visit(this.rules); }, eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); + var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env); }); var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); var rules; + var rule; + var i; ruleset.originalRuleset = this; ruleset.root = this.root; @@ -42,7 +44,7 @@ tree.Ruleset.prototype = { // Store the frames around mixin definitions, // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { + for (i = 0; i < ruleset.rules.length; i++) { if (ruleset.rules[i] instanceof tree.mixin.Definition) { ruleset.rules[i].frames = env.frames.slice(0); } @@ -51,8 +53,9 @@ tree.Ruleset.prototype = { var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { + for (i = 0; i < ruleset.rules.length; i++) { if (ruleset.rules[i] instanceof tree.mixin.Call) { + /*jshint loopfunc:true */ rules = ruleset.rules[i].eval(env).filter(function(r) { if ((r instanceof tree.Rule) && r.variable) { // do not pollute the scope if the variable is @@ -69,7 +72,7 @@ tree.Ruleset.prototype = { } // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { + for (i = 0; i < ruleset.rules.length; i++) { rule = ruleset.rules[i]; if (! (rule instanceof tree.mixin.Definition)) { @@ -82,7 +85,7 @@ tree.Ruleset.prototype = { env.selectors.shift(); if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { + for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { env.mediaBlocks[i].bubbleSelectors(selectors); } } @@ -132,7 +135,7 @@ tree.Ruleset.prototype = { this._lookups = {}; }, variables: function () { - if (this._variables) { return this._variables } + if (this._variables) { return this._variables; } else { return this._variables = this.rules.reduce(function (hash, r) { if (r instanceof tree.Rule && r.variable === true) { @@ -155,7 +158,7 @@ tree.Ruleset.prototype = { var rules = [], rule, match, key = selector.toCSS(); - if (key in this._lookups) { return this._lookups[key] } + if (key in this._lookups) { return this._lookups[key]; } this.rulesets().forEach(function (rule) { if (rule !== self) { @@ -186,12 +189,13 @@ tree.Ruleset.prototype = { rulesets = [], // node.Ruleset instances selector, // The fully rendered selector debugInfo, // Line number debugging - rule; + rule, + i; this.mergeRules(); // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { + for (i = 0; i < this.rules.length; i++) { rule = this.rules[i]; if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive) { @@ -238,7 +242,7 @@ tree.Ruleset.prototype = { if (selector) { // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { + for (i = rules.length - 1; i >= 0; i--) { if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) { _rules.unshift(rules[i]); } @@ -289,7 +293,7 @@ tree.Ruleset.prototype = { if (!hasParentSelector) { if (context.length > 0) { - for(i = 0; i < context.length; i++) { + for (i = 0; i < context.length; i++) { paths.push(context[i].concat(selector)); } } @@ -333,11 +337,11 @@ tree.Ruleset.prototype = { } // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { + for (j = 0; j < newSelectors.length; j++) { sel = newSelectors[j]; // if we don't have any parent paths, the & might be in a mixin so that it can be used // whether there are parents or not - if (context.length == 0) { + if (context.length === 0) { // the combinator used on el should now be applied to the next element instead so that // it is not lost if (sel.length > 0) { @@ -348,7 +352,7 @@ tree.Ruleset.prototype = { } else { // and the parent selectors - for(k = 0; k < context.length; k++) { + for (k = 0; k < context.length; k++) { parentSel = context[k]; // We need to put the current selectors // then join the last selector's elements on to the parents selectors @@ -410,7 +414,7 @@ tree.Ruleset.prototype = { this.mergeElementsOnToSelectors(currentElements, newSelectors); } - for(i = 0; i < newSelectors.length; i++) { + for (i = 0; i < newSelectors.length; i++) { if (newSelectors[i].length > 0) { paths.push(newSelectors[i]); } @@ -420,12 +424,12 @@ tree.Ruleset.prototype = { mergeElementsOnToSelectors: function(elements, selectors) { var i, sel, extendList; - if (selectors.length == 0) { + if (selectors.length === 0) { selectors.push([ new(tree.Selector)(elements) ]); return; } - for(i = 0; i < selectors.length; i++) { + for (i = 0; i < selectors.length; i++) { sel = selectors[i]; // if the previous thing in sel is a parent this needs to join on to it diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index 0fc8582d..943b694a 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -18,6 +18,7 @@ tree.Selector.prototype = { this.condition = visitor.visit(this.condition); }, createDerived: function(elements, extendList, evaldCondition) { + /*jshint eqnull:true */ evaldCondition = evaldCondition != null ? evaldCondition : this.evaldCondition; var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced); newSelector.evaldCondition = evaldCondition; @@ -54,7 +55,7 @@ tree.Selector.prototype = { }), evaldCondition); }, toCSS: function (env) { - if (this._css) { return this._css } + if (this._css) { return this._css; } if (this.elements[0].combinator.value === "") { this._css = ' '; diff --git a/lib/less/tree/unicode-descriptor.js b/lib/less/tree/unicode-descriptor.js index 3f725127..cf4ff853 100644 --- a/lib/less/tree/unicode-descriptor.js +++ b/lib/less/tree/unicode-descriptor.js @@ -8,7 +8,7 @@ tree.UnicodeDescriptor.prototype = { toCSS: function (env) { return this.value; }, - eval: function () { return this } + eval: function () { return this; } }; })(require('../tree')); diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index ff57494c..8f146932 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -1,12 +1,16 @@ (function (tree) { -tree.Variable = function (name, index, currentFileInfo) { this.name = name, this.index = index, this.currentFileInfo = currentFileInfo }; +tree.Variable = function (name, index, currentFileInfo) { + this.name = name; + this.index = index; + this.currentFileInfo = currentFileInfo; +}; tree.Variable.prototype = { type: "Variable", eval: function (env) { var variable, v, name = this.name; - if (name.indexOf('@@') == 0) { + if (name.indexOf('@@') === 0) { name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; } diff --git a/test/browser-test-prepare.js b/test/browser-test-prepare.js index 1a1556f6..644ac6ff 100644 --- a/test/browser-test-prepare.js +++ b/test/browser-test-prepare.js @@ -7,7 +7,7 @@ var readDirFilesSync = function(dir, regex, callback) { if (! regex.test(file)) { return; } callback(file); }); -} +}; var createTestRunnerPage = function(dir, exclude, testSuiteName, dir2) { var output = '\n'; @@ -33,7 +33,7 @@ var removeFiles = function(dir, regex) { console.log("Failed to delete " + file); }); }); -} +}; removeFiles("test/browser", /test-runner-[a-zA-Z-]*\.htm$/); createTestRunnerPage("", /javascript|urls/, "main"); diff --git a/test/less-test.js b/test/less-test.js index 50b868ca..5b984004 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -20,7 +20,7 @@ less.tree.functions.increment = function (a) { return new(less.tree.Dimension)(a.value + 1); }; less.tree.functions._color = function (str) { - if (str.value === "evil red") { return new(less.tree.Color)("600") } + if (str.value === "evil red") { return new(less.tree.Color)("600"); } }; sys.puts("\n" + stylize("LESS", 'underline') + "\n"); @@ -101,7 +101,7 @@ function runTestSet(options, foldername, verifyFunction, nameModifier, doReplace doReplacements = globalReplacements; fs.readdirSync(path.join('test/less/', foldername)).forEach(function (file) { - if (! /\.less/.test(file)) { return } + if (! /\.less/.test(file)) { return; } var name = foldername + path.basename(file, '.less'); @@ -117,7 +117,7 @@ function runTestSet(options, foldername, verifyFunction, nameModifier, doReplace var css_name = name; if(nameModifier) css_name=nameModifier(name); fs.readFile(path.join('test/css', css_name) + '.css', 'utf8', function (e, css) { - sys.print("- " + css_name + ": ") + sys.print("- " + css_name + ": "); css = css && doReplacements(css, 'test/less/' + foldername); if (less === css) { ok('OK'); } @@ -189,7 +189,7 @@ function toCSS(options, path, callback) { var tree, css; options = options || {}; fs.readFile(path, 'utf8', function (e, str) { - if (e) { return callback(e) } + if (e) { return callback(e); } options.paths = [require('path').dirname(path)]; options.filename = require('path').resolve(process.cwd(), path); @@ -214,7 +214,7 @@ function testNoOptions() { totalTests++; try { sys.print("- Integration - creating parser without options: "); - new(less.Parser); + new(less.Parser)(); } catch(e) { fail(stylize("FAIL\n", "red")); return; From f000522855b5739650e28e14d3f8761f6515c5e0 Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Mon, 8 Jul 2013 19:09:52 -0700 Subject: [PATCH 3/4] Run JSHint in pretest phase, with basic config and ignores. --- .jshintignore | 5 +++++ .jshintrc | 7 +++++++ package.json | 4 +++- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .jshintignore create mode 100644 .jshintrc diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 00000000..ccc70d75 --- /dev/null +++ b/.jshintignore @@ -0,0 +1,5 @@ +benchmark/ +build/ +dist/ +node_modules/ +test/browser/ diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..2265f328 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,7 @@ +{ + "evil": true, + "boss": true, + "expr": true, + "laxbreak": true, + "node": true +} diff --git a/package.json b/package.json index 1a12f314..ed77b6df 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "node": ">=0.4.2" }, "scripts": { + "pretest": "./node_modules/.bin/jshint --config ./.jshintrc .", "test": "make test" }, "optionalDependencies": { @@ -37,7 +38,8 @@ "ycssmin": ">=1.0.1" }, "devDependencies": { - "diff": "~1.0" + "diff": "~1.0", + "jshint": "~2.1.4" }, "keywords": [ "compile less", From 66a9890c3951a1347167a4a07743414603a6b28e Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Mon, 8 Jul 2013 19:17:04 -0700 Subject: [PATCH 4/4] Log stacks when errors are caught in tests to aid debugging. --- test/less-test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/less-test.js b/test/less-test.js index 50b868ca..e7b2bb47 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -9,6 +9,8 @@ var globals = Object.keys(global); var oneTestOnly = process.argv[2]; +var isVerbose = process.env.npm_config_loglevel === 'verbose'; + var totalTests = 0, failedTests = 0, passedTests = 0; @@ -123,6 +125,10 @@ function runTestSet(options, foldername, verifyFunction, nameModifier, doReplace if (less === css) { ok('OK'); } else if (err) { fail("ERROR: " + (err && err.message)); + if (isVerbose) { + console.error(); + console.error(err.stack); + } } else { difference("FAIL", css, less); }