From 68ea31dc89c60efc403089a96f84fc97a2b2d945 Mon Sep 17 00:00:00 2001 From: Matthew Dean Date: Sun, 1 Jan 2017 12:58:09 -0800 Subject: [PATCH] Line and column # reporting for Node errors --- .../environment/abstract-plugin-loader.js | 57 ++++++++++++------- lib/less/less-error.js | 29 ++++++++-- lib/less/tree/import.js | 11 +++- test/browser/common.js | 2 +- test/less-test.js | 2 +- test/less/errors/plugin-2.less | 1 + test/less/errors/plugin-2.txt | 5 ++ test/less/errors/plugin-3.less | 1 + test/less/errors/plugin-3.txt | 5 ++ test/less/errors/plugin/plugin-error-2.js | 5 ++ test/less/errors/plugin/plugin-error-3.js | 5 ++ 11 files changed, 92 insertions(+), 31 deletions(-) create mode 100644 test/less/errors/plugin-2.less create mode 100644 test/less/errors/plugin-2.txt create mode 100644 test/less/errors/plugin-3.less create mode 100644 test/less/errors/plugin-3.txt create mode 100644 test/less/errors/plugin/plugin-error-2.js create mode 100644 test/less/errors/plugin/plugin-error-3.js diff --git a/lib/less/environment/abstract-plugin-loader.js b/lib/less/environment/abstract-plugin-loader.js index abf50d33..d1a5e115 100644 --- a/lib/less/environment/abstract-plugin-loader.js +++ b/lib/less/environment/abstract-plugin-loader.js @@ -39,8 +39,14 @@ AbstractPluginLoader.prototype.evalPlugin = function(contents, context, imports, if (pluginObj) { this.trySetOptions(pluginObj, filename, shortname, pluginOptions); - if (pluginObj.use) { - pluginObj.use.call(this.context, pluginObj); + try { + if (pluginObj.use) { + pluginObj.use.call(this.context, pluginObj); + } + } + catch (e) { + e.message = 'Error during @plugin call'; + return new this.less.LessError(e, imports, filename); } return pluginObj; } @@ -52,37 +58,46 @@ AbstractPluginLoader.prototype.evalPlugin = function(contents, context, imports, }; localExports = localModule.exports; registry = functionRegistry.create(); + + var registerPlugin = function(obj) { + pluginObj = obj; + }; try { - var registerPlugin = function(obj) { - pluginObj = obj; - }; loader = new Function("module", "require", "registerPlugin", "functions", "tree", "less", "fileInfo", contents); loader(localModule, this.require, registerPlugin, registry, this.less.tree, this.less, fileInfo); + } catch (e) { + return new this.less.LessError({ message: 'Parse error' }, imports, filename); + } + + if (!pluginObj) { + pluginObj = localModule.exports; + } + pluginObj = this.validatePlugin(pluginObj, filename, shortname); - if (!pluginObj) { - pluginObj = localModule.exports; - } - pluginObj = this.validatePlugin(pluginObj, filename, shortname); + if (pluginObj) { + // Run on first load + pluginManager.addPlugin(pluginObj, fileInfo.filename, registry); + pluginObj.functions = registry.getLocalFunctions(); + pluginObj.imports = imports; + pluginObj.filename = filename; - if (pluginObj) { - // Run on first load - pluginManager.addPlugin(pluginObj, fileInfo.filename, registry); - pluginObj.functions = registry.getLocalFunctions(); + this.trySetOptions(pluginObj, filename, shortname, pluginOptions); - this.trySetOptions(pluginObj, filename, shortname, pluginOptions); - - // Run every @plugin call + // Run every @plugin call + try { if (pluginObj.use) { pluginObj.use.call(this.context, pluginObj); } } - else { - return new this.less.LessError({ message: "Not a valid plugin" }); + catch (e) { + e.message = 'Error during @plugin call'; + return new this.less.LessError(e, imports, filename); } - - } catch (e) { - return new this.less.LessError({ message: 'Parse error' }, imports, filename); + + } + else { + return new this.less.LessError({ message: "Not a valid plugin" }); } return pluginObj; diff --git a/lib/less/less-error.js b/lib/less/less-error.js index 6d0cbe7b..67172a7a 100644 --- a/lib/less/less-error.js +++ b/lib/less/less-error.js @@ -26,6 +26,9 @@ var LessError = module.exports = function LessError(e, importManager, currentFil var filename = e.filename || currentFilename; + this.message = e.message; + this.stack = e.stack; + if (importManager && filename) { var input = importManager.contents[filename], loc = utils.getLocation(e.index, input), @@ -38,17 +41,31 @@ var LessError = module.exports = function LessError(e, importManager, currentFil this.filename = filename; this.index = e.index; this.line = typeof line === 'number' ? line + 1 : null; + this.column = col; + + if (!this.line && this.stack) { + var found = this.stack.match(/(|Function):(\d+):(\d+)/); + + if (found) { + if (found[2]) { + this.line = parseInt(found[2]) - 2; + } + if (found[3]) { + this.column = parseInt(found[3]); + } + } + } + this.callLine = callLine + 1; this.callExtract = lines[callLine]; - this.column = col; + this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] + lines[this.line - 2], + lines[this.line - 1], + lines[this.line] ]; + } - this.message = e.message; - this.stack = e.stack; }; diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index 37ff5510..b459bb39 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -4,7 +4,8 @@ var Node = require("./node"), Quoted = require("./quoted"), Ruleset = require("./ruleset"), Anonymous = require("./anonymous"), - utils = require("../utils"); + utils = require("../utils"), + LessError = require("../less-error"); // // CSS @import node @@ -131,7 +132,13 @@ Import.prototype.doEval = function (context) { if (this.options.isPlugin) { if (this.root && this.root.eval) { - this.root.eval(context); + try { + this.root.eval(context); + } + catch (e) { + e.message = "Plugin error during evaluation"; + throw new LessError(e, this.root.imports, this.root.filename); + } } registry = context.frames[0] && context.frames[0].functionRegistry; if ( registry && this.root && this.root.functions ) { diff --git a/test/browser/common.js b/test/browser/common.js index 5b2f748e..98a26994 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -207,7 +207,7 @@ testErrorSheet = function (sheet) { .replace(/\{pathrel\}/g, "") .replace(/\{pathhref\}/g, "http://localhost:8081/test/less/errors/") .replace(/\{404status\}/g, " (404)") - .replace(/\{node\}.*\{\/node\}/g, "") + .replace(/\{node\}[\s\S]*\{\/node\}/g, "") .replace(/\n$/, "") .trim(); expect(actualErrorMsg).toEqual(errorTxt); diff --git a/test/less-test.js b/test/less-test.js index e7bf6893..11902779 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -137,7 +137,7 @@ module.exports = function() { function testErrors(name, err, compiledLess, doReplacements, sourcemap, baseFolder) { fs.readFile(path.join(baseFolder, name) + '.txt', 'utf8', function (e, expectedErr) { process.stdout.write('- ' + path.join(baseFolder, name) + ": "); - expectedErr = doReplacements(expectedErr, baseFolder, err.filename); + expectedErr = doReplacements(expectedErr, baseFolder, err && err.filename); if (!err) { if (compiledLess) { fail('No Error', 'red'); diff --git a/test/less/errors/plugin-2.less b/test/less/errors/plugin-2.less new file mode 100644 index 00000000..f4bbe5da --- /dev/null +++ b/test/less/errors/plugin-2.less @@ -0,0 +1 @@ +@plugin "plugin/plugin-error-2"; \ No newline at end of file diff --git a/test/less/errors/plugin-2.txt b/test/less/errors/plugin-2.txt new file mode 100644 index 00000000..949fb2d0 --- /dev/null +++ b/test/less/errors/plugin-2.txt @@ -0,0 +1,5 @@ +SyntaxError: Error during @plugin call in {path}plugin-error-2.js{node} on line 3, column 16: +2 use: function() { +3 throw new Error("An error was here.") +4 } +{/node} \ No newline at end of file diff --git a/test/less/errors/plugin-3.less b/test/less/errors/plugin-3.less new file mode 100644 index 00000000..e01636a0 --- /dev/null +++ b/test/less/errors/plugin-3.less @@ -0,0 +1 @@ +@plugin "plugin/plugin-error-3"; \ No newline at end of file diff --git a/test/less/errors/plugin-3.txt b/test/less/errors/plugin-3.txt new file mode 100644 index 00000000..a2819b31 --- /dev/null +++ b/test/less/errors/plugin-3.txt @@ -0,0 +1,5 @@ +SyntaxError: Plugin error during evaluation in {path}plugin-error-3.js{node} on line 3, column 16: +2 eval: function() { +3 throw new Error("An error was here.") +4 } +{/node} \ No newline at end of file diff --git a/test/less/errors/plugin/plugin-error-2.js b/test/less/errors/plugin/plugin-error-2.js new file mode 100644 index 00000000..dbe4609e --- /dev/null +++ b/test/less/errors/plugin/plugin-error-2.js @@ -0,0 +1,5 @@ +registerPlugin({ + use: function() { + throw new Error("An error was here.") + } +}); \ No newline at end of file diff --git a/test/less/errors/plugin/plugin-error-3.js b/test/less/errors/plugin/plugin-error-3.js new file mode 100644 index 00000000..755f099c --- /dev/null +++ b/test/less/errors/plugin/plugin-error-3.js @@ -0,0 +1,5 @@ +registerPlugin({ + eval: function() { + throw new Error("An error was here.") + } +}); \ No newline at end of file