From b26799f9fc2a2137e3b4225f1a9f8852cd02ed5b Mon Sep 17 00:00:00 2001 From: hacksalot Date: Fri, 12 Feb 2016 17:11:11 -0500 Subject: [PATCH] Improve JSON error handling. Add support for detection of invalid line breaks in JSON string values. Fixes #137. Could be improved to fetch the column number and drop the messy grabbing of the line number from the exception message via regex, but currently the "jsonlint" library (not to be confused with "json-lint") only emits an error string. Since this is also the library that drives http://jsonlint.com, we'll accept the messy regex in return for more robust error checking when our default json-lint path fails. All of the above only necessary because standard JSON.parse error handling is broken in all environments. : ) --- dist/cli/error.js | 12 ++++++++--- dist/cli/msg.yml | 5 ++++- dist/utils/syntax-error-ex.js | 36 ++++++++++++++++++++++---------- package.json | 1 + src/cli/error.coffee | 13 ++++++++---- src/cli/msg.yml | 5 ++++- src/utils/syntax-error-ex.coffee | 24 ++++++++++++++------- 7 files changed, 69 insertions(+), 27 deletions(-) diff --git a/dist/cli/error.js b/dist/cli/error.js index cfa539a..462c9d2 100644 --- a/dist/cli/error.js +++ b/dist/cli/error.js @@ -213,9 +213,15 @@ Error-handling routines for HackMyResume. if (SyntaxErrorEx.is(ex.inner)) { console.error(printf(M2C(this.msgs.readError.msg, 'red'), ex.file)); se = new SyntaxErrorEx(ex, ex.raw); - msg = printf(M2C(this.msgs.parseError.msg, 'red'), se.line, se.col); - } else if (ex.inner && ex.inner.line !== void 0 && ex.inner.col !== void 0) { - msg = printf(M2C(this.msgs.parseError.msg, 'red'), ex.inner.line, ex.inner.col); + if ((se.line != null) && (se.col != null)) { + msg = printf(M2C(this.msgs.parseError.msg[0], 'red'), se.line, se.col); + } else if (se.line != null) { + msg = printf(M2C(this.msgs.parseError.msg[1], 'red'), se.line); + } else { + msg = M2C(this.msgs.parseError.msg[2], 'red'); + } + } else if (ex.inner && (ex.inner.line != null) && (ex.inner.col != null)) { + msg = printf(M2C(this.msgs.parseError.msg[0], 'red'), ex.inner.line, ex.inner.col); } else { msg = ex; } diff --git a/dist/cli/msg.yml b/dist/cli/msg.yml index ae19ff1..1a17818 100644 --- a/dist/cli/msg.yml +++ b/dist/cli/msg.yml @@ -81,7 +81,10 @@ errors: readError: msg: Reading **???** resume: **%s** parseError: - msg: Invalid or corrupt JSON on line %s column %s. + msg: + - Invalid or corrupt JSON on line %s column %s. + - Invalid or corrupt JSON on line %s. + - Invalid or corrupt JSON. invalidHelperUse: msg: "**Warning**: Incorrect use of the **%s** theme helper." fileSaveError: diff --git a/dist/utils/syntax-error-ex.js b/dist/utils/syntax-error-ex.js index 5ea2cea..b2d795a 100644 --- a/dist/utils/syntax-error-ex.js +++ b/dist/utils/syntax-error-ex.js @@ -18,17 +18,31 @@ See: http://stackoverflow.com/q/13323356 (function() { var SyntaxErrorEx; - SyntaxErrorEx = function(ex, rawData) { - var JSONLint, colNum, lineNum, lint; - lineNum = null; - colNum = null; - JSONLint = require('json-lint'); - lint = JSONLint(rawData, { - comments: false - }); - this.line = lint.error ? lint.line : '???'; - return this.col = lint.error ? lint.character : '???'; - }; + SyntaxErrorEx = (function() { + function SyntaxErrorEx(ex, rawData) { + var JSONLint, colNum, lineNum, lint, ref; + lineNum = null; + colNum = null; + JSONLint = require('json-lint'); + lint = JSONLint(rawData, { + comments: false + }); + if (lint.error) { + ref = [lint.line, lint.character], this.line = ref[0], this.col = ref[1]; + } + if (!lint.error) { + JSONLint = require('jsonlint'); + try { + JSONLint.parse(rawData); + } catch (_error) { + this.line = (/on line (\d+)/.exec(_error))[1]; + } + } + } + + return SyntaxErrorEx; + + })(); SyntaxErrorEx.is = function(ex) { return ex instanceof SyntaxError; diff --git a/package.json b/package.json index e6d2f7f..d39e97f 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "html": "0.0.10", "is-my-json-valid": "^2.12.4", "json-lint": "^0.1.0", + "jsonlint": "^1.6.2", "lodash": "^3.10.1", "marked": "^0.3.5", "mkdirp": "^0.5.1", diff --git a/src/cli/error.coffee b/src/cli/error.coffee index d9e7d5f..98b56be 100644 --- a/src/cli/error.coffee +++ b/src/cli/error.coffee @@ -202,12 +202,17 @@ assembleError = ( ex ) -> etype = 'custom' when HMSTATUS.parseError - if SyntaxErrorEx.is( ex.inner ) + if SyntaxErrorEx.is ex.inner console.error printf( M2C(this.msgs.readError.msg, 'red'), ex.file ) se = new SyntaxErrorEx ex, ex.raw - msg = printf M2C( this.msgs.parseError.msg, 'red' ), se.line, se.col - else if ex.inner && ex.inner.line != undefined && ex.inner.col != undefined - msg = printf( M2C( this.msgs.parseError.msg, 'red' ), ex.inner.line, ex.inner.col) + if se.line? and se.col? + msg = printf M2C( this.msgs.parseError.msg[0], 'red' ), se.line, se.col + else if se.line? + msg = printf M2C( this.msgs.parseError.msg[1], 'red' ), se.line + else + msg = M2C @msgs.parseError.msg[2], 'red' + else if ex.inner && ex.inner.line? && ex.inner.col? + msg = printf( M2C( this.msgs.parseError.msg[0], 'red' ), ex.inner.line, ex.inner.col) else msg = ex etype = 'error' diff --git a/src/cli/msg.yml b/src/cli/msg.yml index ae19ff1..1a17818 100644 --- a/src/cli/msg.yml +++ b/src/cli/msg.yml @@ -81,7 +81,10 @@ errors: readError: msg: Reading **???** resume: **%s** parseError: - msg: Invalid or corrupt JSON on line %s column %s. + msg: + - Invalid or corrupt JSON on line %s column %s. + - Invalid or corrupt JSON on line %s. + - Invalid or corrupt JSON. invalidHelperUse: msg: "**Warning**: Incorrect use of the **%s** theme helper." fileSaveError: diff --git a/src/utils/syntax-error-ex.coffee b/src/utils/syntax-error-ex.coffee index 09e1def..88d6e8c 100644 --- a/src/utils/syntax-error-ex.coffee +++ b/src/utils/syntax-error-ex.coffee @@ -4,6 +4,8 @@ Definition of the SyntaxErrorEx class. @license MIT. See LICENSE.md for details. ### + + ###* Represents a SyntaxError exception with line and column info. Collect syntax error information from the provided exception object. The @@ -13,13 +15,21 @@ See: http://stackoverflow.com/q/13323356 @class SyntaxErrorEx ### -SyntaxErrorEx = ( ex, rawData ) -> - lineNum = null - colNum = null - JSONLint = require 'json-lint' - lint = JSONLint rawData, { comments: false } - this.line = if lint.error then lint.line else '???' - this.col = if lint.error then lint.character else '???' +class SyntaxErrorEx + constructor: ( ex, rawData ) -> + lineNum = null + colNum = null + JSONLint = require 'json-lint' + lint = JSONLint rawData, { comments: false } + [@line, @col] = [lint.line, lint.character] if lint.error + if !lint.error + JSONLint = require 'jsonlint' + try + JSONLint.parse rawData + catch + @line = (/on line (\d+)/.exec _error)[1] + + SyntaxErrorEx.is = ( ex ) -> ex instanceof SyntaxError module.exports = SyntaxErrorEx;