From d3c6f2208fcb414a812a0102df252a80d8e2d22a Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 17:42:38 +0100 Subject: [PATCH] Move toCSS out to a new class (still need to remove creation of that class from parser) --- lib/less/less-error.js | 44 +++++++++++-------- lib/less/parse-tree.js | 55 +++++++++++++++++++++++ lib/less/parser/imports.js | 2 +- lib/less/parser/parser-input.js | 21 --------- lib/less/parser/parser.js | 78 +++++---------------------------- lib/less/tree/javascript.js | 3 +- lib/less/tree/js-eval-node.js | 17 ++++--- lib/less/tree/variable.js | 4 +- lib/less/utils.js | 20 +++++++++ 9 files changed, 127 insertions(+), 117 deletions(-) create mode 100644 lib/less/parse-tree.js create mode 100644 lib/less/utils.js diff --git a/lib/less/less-error.js b/lib/less/less-error.js index 750bad80..e21bd571 100644 --- a/lib/less/less-error.js +++ b/lib/less/less-error.js @@ -1,26 +1,32 @@ +var utils = require("./utils.js"); -var LessError = module.exports = function LessError(parser, e, env) { - var input = parser.getInput(e, env), - loc = parser.getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && parser.getLocation(e.call, input).line, - lines = input.split('\n'); +var LessError = module.exports = function LessError(e, importManager, currentFilename) { - this.type = e.type || 'Syntax'; + var filename = e.filename || currentFilename; + + if (importManager && filename) { + var input = importManager.contents[filename], + loc = utils.getLocation(e.index, input), + line = loc.line, + col = loc.column, + callLine = e.call && utils.getLocation(e.call, input).line, + lines = input.split('\n'); + + this.type = e.type || 'Syntax'; + this.filename = filename; + this.index = e.index; + this.line = typeof(line) === 'number' ? line + 1 : null; + this.callLine = callLine + 1; + this.callExtract = lines[callLine]; + this.column = col; + this.extract = [ + lines[line - 1], + lines[line], + lines[line + 1] + ]; + } this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; }; LessError.prototype = new Error(); diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js new file mode 100644 index 00000000..50f5f3b3 --- /dev/null +++ b/lib/less/parse-tree.js @@ -0,0 +1,55 @@ +var LessError = require('./less-error.js'), + transformTree = require("./transform-tree.js"); + +module.exports = function(environment) +{ +var SourceMapOutput = require("./source-map-output")(environment); + +var ParseTree = function(root, imports) { + this.root = root; + this.imports = imports; +}; + +ParseTree.prototype.toCSS = function(options) { + var evaldRoot; + try { + evaldRoot = transformTree(this.root, options); + } catch (e) { + throw new LessError(e, this.imports); + } + var css; + try { + if (options.sourceMap) { + evaldRoot = new SourceMapOutput( + { + contentsIgnoredCharsMap: this.imports.contentsIgnoredChars, + writeSourceMap: options.writeSourceMap, + rootNode: evaldRoot, + contentsMap: this.imports.contents, + sourceMapFilename: options.sourceMapFilename, + sourceMapURL: options.sourceMapURL, + outputFilename: options.sourceMapOutputFilename, + sourceMapBasepath: options.sourceMapBasepath, + sourceMapRootpath: options.sourceMapRootpath, + outputSourceFiles: options.outputSourceFiles, + sourceMapGenerator: options.sourceMapGenerator + }); + } + + css = evaldRoot.toCSS({ + compress: Boolean(options.compress), + dumpLineNumbers: options.dumpLineNumbers, + strictUnits: Boolean(options.strictUnits), + numPrecision: 8}); + } catch (e) { + throw new LessError(e, this.imports); + } + + if (options.compress) { + return css.replace(/(^(\s)+)|((\s)+$)/g, ""); + } else { + return css; + } +}; +return ParseTree; +}; diff --git a/lib/less/parser/imports.js b/lib/less/parser/imports.js index 2b7c4efa..7b2fd564 100644 --- a/lib/less/parser/imports.js +++ b/lib/less/parser/imports.js @@ -70,7 +70,7 @@ module.exports = function(environment, env, Parser) { } else { new(Parser)(newEnv).parse(contents, function (e, root) { fileParsedFunc(e, root, resolvedFilename); - }); + }, null, true); } }); } diff --git a/lib/less/parser/parser-input.js b/lib/less/parser/parser-input.js index 7b03e249..aca272f1 100644 --- a/lib/less/parser/parser-input.js +++ b/lib/less/parser/parser-input.js @@ -211,27 +211,6 @@ module.exports = function() { return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA; }; - parserInput.getLocation = function(index, inputStream) { - inputStream = inputStream == null ? input : inputStream; - - var n = index + 1, - line = null, - column = -1; - - 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 - }; - }; - parserInput.start = function(str, chunkInput, parser, env) { input = str; parserInput.i = j = currentPos = furthest = 0; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 6d19dd9c..aadef58a 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -3,10 +3,11 @@ var LessError = require('../less-error.js'), visitor = require("../visitor/index.js"), contexts = require("../contexts.js"), getImportManager = require("./imports.js"), - getParserInput = require("./parser-input.js"); + getParserInput = require("./parser-input.js"), + utils = require("../utils.js"); module.exports = function(environment) { -var SourceMapOutput = require("../source-map-output")(environment); + var ParseTree = require("../parse-tree.js")(environment); // // less.js - parser // @@ -79,20 +80,12 @@ var Parser = function Parser(env) { throw e; } - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return parserInput.getInput(); - } - } - function getDebugInfo(index) { var filename = env.currentFileInfo.filename; filename = environment.getAbsolutePath(env, filename); return { - lineNumber: parserInput.getLocation(index).line + 1, + lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1, fileName: filename }; } @@ -109,7 +102,7 @@ var Parser = function Parser(env) { // @param callback call `callback` when done. // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply // - parse: function (str, callback, additionalData) { + parse: function (str, callback, additionalData, returnSubParseTree) { var root, error = null, globalVars, modifyVars, preText = ""; globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : ''; @@ -136,54 +129,9 @@ var Parser = function Parser(env) { root.root = true; root.firstRoot = true; } catch (e) { - return callback(new LessError(parser, e, env)); + return callback(new LessError(e, parser.imports, env.currentFileInfo.filename)); } - root.toCSS = (function (evaluate) { - return function (options) { - var transformTree = require("../transform-tree.js"), - evaldRoot; - try { - evaldRoot = transformTree(this, options); - } catch (e) { - throw new LessError(parser, e, env); - } - var css; - try { - if (options.sourceMap) { - evaldRoot = new SourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new LessError(parser, e, env); - } - - if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - // If `i` is smaller than the `input.length - 1`, // it means the parser wasn't able to parse the whole // string, so we've got a parsing error. @@ -208,12 +156,12 @@ var Parser = function Parser(env) { } } - error = new LessError(parser, { + error = new LessError({ type: "Parse", message: message, index: endInfo.furthest, filename: env.currentFileInfo.filename - }, env); + }, parser.imports); } var finish = function (e) { @@ -221,13 +169,14 @@ var Parser = function Parser(env) { if (e) { if (!(e instanceof LessError)) { - e = new LessError(parser, e, env); + e = new LessError(e, parser.imports, env.currentFileInfo.filename); } return callback(e); } else { - return callback(null, root); + return callback(null, + returnSubParseTree ? root : new ParseTree(root, parser.imports)); } }; @@ -547,7 +496,7 @@ var Parser = function Parser(env) { js = parserInput.$re(/^(~)?`([^`]*)`/); if (js) { - return new(tree.JavaScript)(js[2], index, Boolean(js[1])); + return new(tree.JavaScript)(js[2], Boolean(js[1]), index, env.currentFileInfo); } } }, @@ -1636,9 +1585,6 @@ var Parser = function Parser(env) { } }; - parser.getInput = getInput; - parser.getLocation = parserInput.getLocation; - return parser; }; Parser.serializeVars = function(vars) { diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index a8f5533c..4b5a2c07 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -3,10 +3,11 @@ var JsEvalNode = require("./js-eval-node.js"), Quoted = require("./quoted.js"), Anonymous = require("./anonymous.js"); -var JavaScript = function (string, index, escaped) { +var JavaScript = function (string, escaped, index, currentFileInfo) { this.escaped = escaped; this.expression = string; this.index = index; + this.currentFileInfo = currentFileInfo; }; JavaScript.prototype = new JsEvalNode(); JavaScript.prototype.type = "JavaScript"; diff --git a/lib/less/tree/js-eval-node.js b/lib/less/tree/js-eval-node.js index 35cabab1..297a7a19 100644 --- a/lib/less/tree/js-eval-node.js +++ b/lib/less/tree/js-eval-node.js @@ -1,28 +1,30 @@ var Node = require("./node.js"), Variable = require("./variable.js"); -var jsEvalNode = function() { +var JsEvalNode = function() { }; -jsEvalNode.prototype = new Node(); +JsEvalNode.prototype = new Node(); -jsEvalNode.prototype.evaluateJavaScript = function (expression, env) { +JsEvalNode.prototype.evaluateJavaScript = function (expression, env) { var result, that = this, context = {}; if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - throw { message: "You are using JavaScript, which has been disabled." , + throw { message: "You are using JavaScript, which has been disabled.", + filename: this.currentFileInfo.filename, index: this.index }; } expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return that.jsify(new(Variable)('@' + name, that.index).eval(env)); + return that.jsify(new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env)); }); try { expression = new(Function)('return (' + expression + ')'); } catch (e) { throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , + filename: this.currentFileInfo.filename, index: this.index }; } @@ -43,11 +45,12 @@ jsEvalNode.prototype.evaluateJavaScript = function (expression, env) { result = expression.call(context); } catch (e) { throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , + filename: this.currentFileInfo.filename, index: this.index }; } return result; }; -jsEvalNode.prototype.jsify = function (obj) { +JsEvalNode.prototype.jsify = function (obj) { if (Array.isArray(obj.value) && (obj.value.length > 1)) { return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; } else { @@ -55,4 +58,4 @@ jsEvalNode.prototype.jsify = function (obj) { } }; -module.exports = jsEvalNode; +module.exports = JsEvalNode; diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index 85641a75..cff740ef 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -11,13 +11,13 @@ Variable.prototype.eval = function (env) { var variable, name = this.name; if (name.indexOf('@@') === 0) { - name = '@' + new(Variable)(name.slice(1)).eval(env).value; + name = '@' + new(Variable)(name.slice(1), this.index, this.currentFileInfo).eval(env).value; } if (this.evaluating) { throw { type: 'Name', message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, + filename: this.currentFileInfo.filename, index: this.index }; } diff --git a/lib/less/utils.js b/lib/less/utils.js new file mode 100644 index 00000000..6efdb21b --- /dev/null +++ b/lib/less/utils.js @@ -0,0 +1,20 @@ +module.exports = { + getLocation: function(index, inputStream) { + var n = index + 1, + line = null, + column = -1; + + 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 + }; + } +};