Comparing version 0.2.3 to 0.2.4
97
jslt.js
@@ -19,16 +19,19 @@ | ||
static transform(data, template, props = {}) { | ||
lastError = null; | ||
transformProps = props; | ||
state = { catchCount : 0, props }; | ||
var res = compileTemplate(data, template); | ||
if (lastError) { | ||
var ex = lastError.stack.reverse().join(".") + " - " + lastError.message; | ||
lastError = null; | ||
throw ex; | ||
} | ||
transformProps = null; | ||
return res; | ||
var lastState = state; | ||
state = null; | ||
if (!lastState.errors || lastState.errors.length == 0) | ||
return res; | ||
if (lastState.lastError) | ||
throw lastState.lastError.stack.reverse().join(".") + " - " + lastState.lastError.message; | ||
collectErrors(res); | ||
throw { result : res, errors : lastState.errors }; | ||
} | ||
}; | ||
var lastError = null, transformProps; | ||
var state = null; | ||
@@ -48,3 +51,3 @@ function compileTemplate(data, template) { | ||
if (typeof value == "function") { | ||
if (transformProps.disableFunctions) return error("[function]", "disableFunctions is enabled"); | ||
if (state.props.disableFunctions) return error("[function]", "disableFunctions is enabled"); | ||
try { return value(data); | ||
@@ -62,3 +65,8 @@ } catch(ex) { return error("[function]", ex.message); } | ||
obj[key] = visit(value); | ||
if (lastError) return error(key); | ||
if (state.lastError) { | ||
if (state.props.continueOnError && state.catchCount == 0) { | ||
obj[key] = state.lastError; | ||
state.lastError = null; | ||
} else return error(key); | ||
} | ||
} | ||
@@ -122,2 +130,6 @@ return obj; | ||
var payload = data; | ||
if (state.props.continueOnError) | ||
state.catchCount += ops.filter(o => o[0].replace(/\d+$/, "") == "$catch").length; | ||
for (var i = 0; i < ops.length; ++i) { | ||
@@ -130,13 +142,15 @@ const [opName, opValue] = ops[i]; | ||
if (lastError) { | ||
if (state.lastError) { | ||
for (++i; i < ops.length; ++i) { | ||
if (ops[i][0].replace(/\d+$/, "") == "$catch") { | ||
var exception = { message : lastError.message, stack : lastError.stack.reverse().join(".") }; | ||
lastError = null; | ||
if (state.props.continueOnError) --state.catchCount; | ||
var exception = { message : state.lastError.message, stack : state.lastError.stack.reverse().join(".") }; | ||
state.lastError = null; | ||
state.errors.pop(); | ||
payload = compileTemplate(exception, ops[i][1]); | ||
if (lastError) continue; | ||
if (state.lastError) continue; | ||
else break; | ||
} | ||
} | ||
if (lastError) return error(opName); | ||
if (state.lastError) return error(opName); | ||
} | ||
@@ -147,5 +161,13 @@ } | ||
function TransformError(message, prop) { | ||
this.message = message; | ||
this.stack = [ prop ]; | ||
} | ||
function error(prop, message) { | ||
if (lastError) lastError.stack.push(prop); | ||
else lastError = { stack : [ prop ], message }; | ||
if (state.lastError) state.lastError.stack.push(prop); | ||
else { | ||
state.lastError = new TransformError(message, prop); | ||
(state.errors || (state.errors = [])).push(state.lastError); | ||
} | ||
} | ||
@@ -162,3 +184,3 @@ | ||
if (type == actualType) return value; | ||
if (!transformProps.disableTypeCoercion) { | ||
if (!state.props.disableTypeCoercion) { | ||
if (type == "string" && actualType == "number") return String(value); | ||
@@ -176,2 +198,25 @@ if (type == "number" && actualType == "string" && !isNaN(Number(value))) return Number(value); | ||
function collectErrors(obj) { | ||
function visit(value, prefix) { | ||
if (prefix.length > 20) return; | ||
if (value instanceof Array) { | ||
value.forEach((val, i) => { | ||
if (val instanceof TransformError) { | ||
val.path = prefix.concat(i); | ||
value[i] = null; | ||
} else visit(val, prefix.concat(i)); | ||
}); | ||
} else if (value) { | ||
Object.entries(value).forEach(([key, val]) => { | ||
if (val instanceof TransformError) { | ||
val.path = prefix.concat(key); | ||
value[key] = null; | ||
} else visit(val, prefix.concat(key)); | ||
}); | ||
} | ||
} | ||
visit(obj, []); | ||
} | ||
const UpdateOperators = { | ||
@@ -183,2 +228,3 @@ $fetch(input, args, global) { | ||
$catch(input, args, global) { | ||
if (state.props.continueOnError) --state.catchCount; | ||
return input; | ||
@@ -262,3 +308,3 @@ }, | ||
$assert(input, args, global) { | ||
if (transformProps.disableAssertions) return input; | ||
if (state.props.disableAssertions) return input; | ||
@@ -281,3 +327,8 @@ var keywords = Object.keys(args); | ||
retVal.push(compileTemplate(newGlobal, args)); | ||
if (lastError) return error(`[${i}]`); | ||
if (state.lastError) { | ||
if (state.props.continueOnError && state.catchCount == 0) { | ||
retVal[retVal.length - 1] = { error : state.lastError.message }; | ||
state.lastError = null; | ||
} else return error(`[${i}]`); | ||
} | ||
} | ||
@@ -293,3 +344,3 @@ return retVal; | ||
if (processQuery(args, input[i], newGlobal)) retVal.push(input[i]); | ||
if (lastError) return error(`[${i}]`); | ||
if (state.lastError) return error(`[${i}]`); | ||
} | ||
@@ -296,0 +347,0 @@ return retVal; |
{ | ||
"name": "jslt", | ||
"version": "0.2.3", | ||
"version": "0.2.4", | ||
"description": "JSON transformer", | ||
@@ -5,0 +5,0 @@ "main": "jslt.js", |
22
test.js
@@ -34,3 +34,3 @@ | ||
try { | ||
var res = jslt.transform(data, template); | ||
var res = jslt.transform(data, template, transformProps); | ||
} catch(ex) { | ||
@@ -41,6 +41,6 @@ res = ex; | ||
success ? ++passed : ++failed; | ||
console.log(`${name.padEnd(40)} ${success ? "Passed" : "FAILED " + JSON.stringify(res)}`); | ||
console.log(`${name.padEnd(45)} ${success ? "Passed" : "FAILED " + JSON.stringify(res)}`); | ||
} | ||
var passed = 0, failed = 0; | ||
var passed = 0, failed = 0, transformProps; | ||
var jslt = require("./jslt.js"); | ||
@@ -197,2 +197,18 @@ | ||
transformProps = { disableAssertions : true }; | ||
test("disableAssertions", 4, { $assert : { maximum : 3 } }, 4); | ||
transformProps = { disableFunctions : true }; | ||
test("disableFunctions - function", { prop : "aa" }, { p : data => data.prop }, "p.[function] - disableFunctions is enabled"); | ||
transformProps = { disableTypeCoercion : true }; | ||
test("disableTypeCoercion - number/string", { prop : 3 }, { p : "{{prop:string}}" }, "p.{{prop}} - Expected string, but received number"); | ||
test("disableTypeCoercion - string/number", { prop : "3" }, { p : "{{prop:number}}" }, "p.{{prop}} - Expected number, but received string"); | ||
transformProps = { continueOnError : true }; | ||
test("continueOnError", { p1 : "{{prop1:number}}", p2 : "{{prop1:number}}" }, { p1 : { error : "Missing required value"}, p2 : { error : "Missing required value"} }); | ||
test("continueOnError - array", { a : [ 1,"z",3 ]}, { p : { $fetch : "{{a}}", $map : { b : "{{this:number}}" } } }, { p :[ { b : 1 }, { b : { error : "Expected number, but received string" } }, { b : 3 } ]}); | ||
test("continueOnError - array & catch1", { a : [ 1,"z",3 ]}, { p : { $fetch : "{{a}}", $map : { $fetch : "{{this:number}}", $catch : "err" } } }, { p : [1, "err", 3] }); | ||
test("continueOnError - array & catch2", { a : [ 1,"z",3 ]}, { p : { $fetch : "{{a}}", $map : { b : "{{this:number}}" }, $catch : "err" } }, { p : "err" }); | ||
console.log(`\nPassed: ${passed}, Failed: ${failed}`); |
39847
577