@bonniernews/local-esi
Advanced tools
Comparing version 1.1.1 to 1.2.0
16
index.js
@@ -46,5 +46,6 @@ "use strict"; | ||
pipeline.once("set_response_code", onResponseCode); | ||
pipeline.once("add_header", onAddHeader); | ||
pipeline.once("set_redirect", close); | ||
pipeline | ||
.on("set_response_code", onResponseCode) | ||
.on("add_header", onAddHeader) | ||
.once("set_redirect", close); | ||
@@ -66,6 +67,7 @@ return pipeline; | ||
function close() { | ||
pipeline.removeListener("set_response_code", onResponseCode); | ||
pipeline.removeListener("add_header", onAddHeader); | ||
pipeline.removeListener("set_redirect", close); | ||
pipeline.destroy(); | ||
pipeline | ||
.removeListener("set_response_code", onResponseCode) | ||
.removeListener("add_header", onAddHeader) | ||
.removeListener("set_redirect", close) | ||
.destroy(); | ||
} | ||
@@ -72,0 +74,0 @@ } |
@@ -212,3 +212,4 @@ "use strict"; | ||
if (!context.inForeach) return next(new Error("esi:break outside esi:foreach")); | ||
return shouldWrite() ? next(null, {name: "esi:break"}) : next(); | ||
context.breakHit = context.breakHit || shouldWrite(); | ||
return context.breakHit ? next(null, {name: "esi:break"}) : next(); | ||
} | ||
@@ -357,5 +358,4 @@ }; | ||
function shouldWrite() { | ||
if (context.inExcept && !context.lastAttemptWasError) { | ||
return false; | ||
} | ||
if (context.inExcept && !context.lastAttemptWasError) return false; | ||
if (context.breakHit) return false; | ||
@@ -362,0 +362,0 @@ if (context.chooses.length) { |
@@ -66,2 +66,6 @@ /* eslint-disable camelcase */ | ||
}, | ||
str([arg]) { | ||
const value = getFunc(arg.type)(arg); | ||
return (typeof value === "undefined") ? "None" : String(value); | ||
}, | ||
substr([arg1, arg2, arg3]) { | ||
@@ -68,0 +72,0 @@ const string = getFunc(arg1.type)(arg1); |
@@ -45,7 +45,6 @@ /* eslint-disable prefer-template */ | ||
function Lexer(str, sourceMap) { | ||
function Lexer(str, columnOffset, line) { | ||
const scanner = Scanner(str); | ||
let colIndex, char, c1, c2, c3; | ||
let collectedWs = ""; | ||
let column, char, c1, c2, c3, source = ""; | ||
getChar(); | ||
@@ -58,3 +57,3 @@ | ||
function get() { | ||
const token = Token(char); | ||
const token = Token(char, columnOffset, line); | ||
@@ -91,5 +90,5 @@ if (c1 === undefined) { | ||
} else if (token.type === FUNCTION) { | ||
if (c1 !== "(") abort({colIndex}, "Unexpected", c1 ? "char " + c1 : "end of line"); | ||
if (c1 !== "(") abort({column}, "Unexpected", c1 ? "char " + c1 : "end of line"); | ||
} else if (token.type === IDENTIFIER) { | ||
if (c1 !== ")") abort({colIndex}, "Unexpected", c1 ? "char " + c1 : "end of line"); | ||
if (c1 !== ")") abort({column}, "Unexpected", c1 ? "char " + c1 : "end of line"); | ||
} | ||
@@ -99,13 +98,2 @@ | ||
if (sourceMap) { | ||
switch (token.type) { | ||
case IDENTIFIER: | ||
return next("$(", token.cargo, ")"); | ||
case MEMBER: | ||
return next("$(", token.cargo, "{"); | ||
case FUNCTION: | ||
return next("$", token.cargo, "("); | ||
} | ||
} | ||
return next(); | ||
@@ -191,3 +179,3 @@ } | ||
if (c3 === undefined || colIndex === 0) token.abort("Unexpected token", c2); | ||
if (c3 === undefined || column === 0) token.abort("Unexpected token", c2); | ||
@@ -215,3 +203,3 @@ getChar(); | ||
if (c1 === "|" && (c2 === undefined || colIndex === 0)) token.abort("Unexpected token |"); | ||
if (c1 === "|" && (c2 === undefined || column === 0)) token.abort("Unexpected token |"); | ||
@@ -248,15 +236,10 @@ getChar(); | ||
return abort({colIndex}, "Unexpected token", c1); | ||
return abort({column}, "Unexpected token", c1); | ||
function next(...sourceArgs) { | ||
if (sourceMap) { | ||
token.raw = collectedWs + (sourceArgs.length ? sourceArgs.join("") : token.cargo); | ||
collectedWs = ""; | ||
while (c1 === " ") { | ||
collectedWs += c1; | ||
getChar(); | ||
} | ||
function next() { | ||
while (c1 === " ") { | ||
getChar(); | ||
} | ||
token.end(source, column); | ||
source = ""; | ||
return token; | ||
@@ -267,4 +250,6 @@ } | ||
function getChar() { | ||
if (c1) source += c1; | ||
char = scanner.get(); | ||
colIndex = char.colIndex; | ||
column = char.column; | ||
c1 = char.c; | ||
@@ -279,3 +264,3 @@ c2 = char.c2; | ||
const l = str.length; | ||
let colIndex = -1; | ||
let column = -1; | ||
return { | ||
@@ -286,18 +271,32 @@ get, | ||
function get() { | ||
const idx = ++colIndex; | ||
const idx = ++column; | ||
const c = str[idx]; | ||
const c2 = (idx + 1) < l ? c + str[idx + 1] : undefined; | ||
const c3 = (idx + 2) < l ? c2 + str[idx + 2] : undefined; | ||
return {colIndex: idx, c, c2, c3}; | ||
return {column: idx, c, c2, c3}; | ||
} | ||
} | ||
function Token(startChar) { | ||
const {colIndex, c: cargo} = startChar; | ||
function Token(startChar, columnOffset = 0, line = 1) { | ||
const {column, c: cargo} = startChar; | ||
return { | ||
column, | ||
cargo, | ||
loc: { | ||
start: { | ||
line, | ||
column, | ||
} | ||
}, | ||
end(source, endColumn) { | ||
this.loc.source = source; | ||
this.loc.start.column += columnOffset; | ||
this.loc.end = { | ||
line, | ||
column: endColumn + columnOffset, | ||
}; | ||
}, | ||
abort(...args) { | ||
abort(this, ...args); | ||
}, | ||
colIndex, | ||
cargo, | ||
get(opts) { | ||
@@ -308,2 +307,3 @@ switch (this.type) { | ||
type: this.type, | ||
loc: this.loc, | ||
}; | ||
@@ -315,2 +315,3 @@ } | ||
value: this.cargo === "true", | ||
loc: this.loc, | ||
}; | ||
@@ -322,2 +323,3 @@ } | ||
name: this.cargo, | ||
loc: this.loc, | ||
}; | ||
@@ -329,2 +331,3 @@ } | ||
value: this.cargo, | ||
loc: this.loc, | ||
}; | ||
@@ -336,2 +339,3 @@ } | ||
value: Number(this.cargo), | ||
loc: this.loc, | ||
}; | ||
@@ -346,2 +350,3 @@ } | ||
}, | ||
loc: this.loc | ||
}; | ||
@@ -357,2 +362,3 @@ } | ||
arguments: [], | ||
loc: this.loc, | ||
}; | ||
@@ -364,2 +370,3 @@ } | ||
elements: [], | ||
loc: this.loc, | ||
}; | ||
@@ -370,3 +377,3 @@ } | ||
type: this.type, | ||
properties: [], | ||
loc: this.loc, | ||
}; | ||
@@ -379,2 +386,3 @@ } | ||
prefix: true, | ||
loc: this.loc, | ||
}; | ||
@@ -389,2 +397,3 @@ } | ||
operator: this.cargo, | ||
loc: this.loc, | ||
}; | ||
@@ -410,2 +419,3 @@ } | ||
operator: this.cargo, | ||
loc: this.loc, | ||
...opts, | ||
@@ -418,2 +428,3 @@ }; | ||
cargo: this.cargo, | ||
loc: this.loc, | ||
}; | ||
@@ -425,5 +436,5 @@ } | ||
function abort({colIndex}, ...args) { | ||
args.push(`at 0:${colIndex}`); | ||
function abort({column}, ...args) { | ||
args.push(`at 0:${column}`); | ||
throw new SyntaxError(args.join(" ")); | ||
} |
@@ -28,8 +28,8 @@ /* eslint-disable prefer-template */ | ||
function parse(input, sourceMap) { | ||
function parse(input, columnOffset) { | ||
if (!input) return; | ||
input = input.trim(); | ||
const ast = AST(sourceMap); | ||
const parser = Parser(input, ast.openNode, ast.closeNode, sourceMap); | ||
const ast = AST(columnOffset); | ||
const parser = Parser(input, ast.openNode, ast.closeNode, columnOffset); | ||
while ((parser.consume())) { | ||
@@ -43,44 +43,44 @@ // No-op | ||
function split(input) { | ||
if (!input) return; | ||
const lines = input.split("\n"); | ||
const hits = []; | ||
let str = input; | ||
return lines.reduce((result, str, idx) => { | ||
let columnOffset = 0; | ||
let match; | ||
let match; | ||
let offset = 0; | ||
while ((match = str.match(/(?!\\)\$(.*)/))) { | ||
if (("(" + IDENTIFIER_CHARS).indexOf(match[1][0]) === -1) { | ||
throw new SyntaxError("Illegal character $ at 0:" + match.index); | ||
} | ||
while ((match = str.match(/(?!\\)\$(.*)/))) { | ||
const line = idx + 1; | ||
if (("(" + IDENTIFIER_CHARS).indexOf(match[1][0]) === -1) { | ||
throw new SyntaxError(`Illegal character $ at ${line}:${match.index + columnOffset}`); | ||
} | ||
if (match.index > 0) { | ||
hits.push({ | ||
type: "TEXT", | ||
text: str.substring(0, match.index) | ||
}); | ||
} | ||
if (match.index > 0) { | ||
result.push({type: "TEXT", text: str.substring(0, match.index)}); | ||
} | ||
columnOffset += match.index; | ||
const hit = { | ||
index: match.index + offset, | ||
raw: "", | ||
}; | ||
hits.push(hit); | ||
const ast = AST(); | ||
const parser = Parser(match[0], ast.openNode, ast.closeNode, columnOffset, line); | ||
const hit = { | ||
expression: parser.consume(), | ||
}; | ||
result.push(hit); | ||
const ast = AST(true); | ||
const parser = Parser(match[0], ast.openNode, ast.closeNode, true); | ||
const expr = hit.expression = parser.consume(true); | ||
if (expr.raw) hit.raw += expr.raw; | ||
const hitSourceLength = hit.expression.loc.source.trim().length; | ||
columnOffset += hitSourceLength; | ||
offset = hit.index + hit.raw.length; | ||
str = str.substring(offset); | ||
} | ||
str = str.substring(match.index + hitSourceLength); | ||
} | ||
if (str) { | ||
hits.push({type: "TEXT", text: str}); | ||
} | ||
if (lines.length > 1 && idx < lines.length - 1) { | ||
str += "\n"; | ||
} | ||
if (str) { | ||
result.push({type: "TEXT", text: str}); | ||
} | ||
return hits; | ||
return result; | ||
}, []); | ||
} | ||
function AST(sourceMap) { | ||
function AST() { | ||
const tree = { | ||
@@ -96,2 +96,5 @@ type: EXPRESSION, | ||
get tree() { | ||
while (stack.length > 1) { | ||
doClose(stack.pop(), getLast()); | ||
} | ||
return tree.body; | ||
@@ -138,7 +141,22 @@ } | ||
function closeNode(node) { | ||
const popped = stack.pop(); | ||
const current = getLast(); | ||
if (current === popped) return current; | ||
function doClose(node, current) { | ||
current = current || getLast(); | ||
switch (node.type) { | ||
case LOGICAL: | ||
case BINARY: { | ||
node.loc.source = node.left.loc.source + node.loc.source + node.right.loc.source; | ||
node.loc.start = {...node.left.loc.start}; | ||
node.loc.end = {...node.right.loc.end}; | ||
break; | ||
} | ||
case UNARY: { | ||
node.loc.source = node.loc.source + node.argument.loc.source; | ||
node.loc.end = {...node.argument.loc.end}; | ||
break; | ||
} | ||
} | ||
if (current === node) return; | ||
switch (current && current.type) { | ||
@@ -150,6 +168,7 @@ case BINARY: { | ||
} | ||
case LOGICAL: | ||
current.right = node; | ||
break; | ||
case BLOCK: { | ||
current.body = node; | ||
if (sourceMap) current.raw += node.raw; | ||
// pop(current); | ||
break; | ||
@@ -161,43 +180,14 @@ } | ||
} | ||
case LOGICAL: { | ||
current.right = node; | ||
break; | ||
} | ||
case UNARY: { | ||
current.argument = node; | ||
if (sourceMap) current.raw += node.raw; | ||
closeNode(current); | ||
break; | ||
} | ||
case FUNCTION: { | ||
if (node.type === BINARY) break; | ||
if (sourceMap) current.raw += node.raw; | ||
addToCommaSeparatedList(node, current.arguments); | ||
break; | ||
} | ||
case ARRAY: { | ||
if (sourceMap) current.raw += node.raw; | ||
addToCommaSeparatedList(node, current.elements); | ||
addArrayElement(current, node); | ||
break; | ||
} | ||
case OBJECT: { | ||
let prop = current.properties[current.properties.length - 1]; | ||
if (!prop || popped.type === ",") { | ||
prop = { | ||
type: "Property" | ||
}; | ||
current.properties.push(prop); | ||
} | ||
if (popped.type === ",") { | ||
break; | ||
} else if (!prop.key) { | ||
prop.key = { | ||
type: IDENTIFIER, | ||
name: popped.value | ||
}; | ||
} else if (!prop.value) { | ||
prop.value = popped; | ||
} | ||
case FUNCTION: { | ||
if (node.type === BINARY) break; | ||
addFunctionArgument(current, node); | ||
break; | ||
@@ -208,2 +198,9 @@ } | ||
function closeNode(node) { | ||
stack.pop(); | ||
const current = getLast(); | ||
doClose(node, current); | ||
} | ||
function getLast() { | ||
@@ -213,20 +210,45 @@ return stack[stack.length - 1] || tree.body; | ||
function addToCommaSeparatedList(node, list) { | ||
const lastIdx = list.length - 1; | ||
function addArrayElement(current, node) { | ||
const lastIdx = current.elements.length - 1; | ||
if (lastIdx > -1) { | ||
const lastElm = current.elements[lastIdx]; | ||
if (node.type === ",") { | ||
if (list[lastIdx].type === ",") throw new SyntaxError("Unexpected comma"); | ||
list.push(node); | ||
if (lastElm.type === ",") throw new SyntaxError(`Unexpected ${node.type} in ${current.type}`); | ||
current.elements.push(node); | ||
} else { | ||
if (list[lastIdx].type !== ",") throw new SyntaxError(`Unexpected ${node.type}`); | ||
list[lastIdx] = node; | ||
if (lastElm.type !== ",") throw new SyntaxError(`Unexpected ${node.type} in ${current.type}`); | ||
current.elements[lastIdx] = node; | ||
node.loc.source = lastElm.loc.source + node.loc.source; | ||
node.loc.start = {...lastElm.loc.start}; | ||
} | ||
} else { | ||
list.push(node); | ||
current.elements.push(node); | ||
} | ||
} | ||
function addFunctionArgument(current, node) { | ||
const lastIdx = current.arguments.length - 1; | ||
if (lastIdx > -1) { | ||
const lastArg = current.arguments[lastIdx]; | ||
if (node.type === ",") { | ||
if (lastArg.type === ",") throw new SyntaxError(`Unexpected ${node.type} in ${current.type}`); | ||
current.arguments.push(node); | ||
} else { | ||
// if (lastArg.type !== ",") throw new SyntaxError(`Unexpected ${node.type} in ${current.type} ----`); | ||
if (lastArg.type !== ",") return node.abort("Unexpected", node.type, "in", current.type); | ||
current.arguments[lastIdx] = node; | ||
node.loc.source = lastArg.loc.source + node.loc.source; | ||
node.loc.start = {...lastArg.loc.start}; | ||
} | ||
} else { | ||
current.arguments.push(node); | ||
} | ||
} | ||
} | ||
function Parser(sourceText, onStart, onEnd, sourceMap) { | ||
const lexer = Lexer(sourceText, sourceMap); | ||
function Parser(sourceText, onStart, onEnd, columnOffset, line) { | ||
const lexer = Lexer(sourceText, columnOffset, line); | ||
let token; | ||
return { | ||
@@ -237,9 +259,6 @@ consume, | ||
function consume() { | ||
const token = lexer.get(); | ||
token = lexer.get(); | ||
if (token.type === ENDMARK) return; | ||
const node = Node(token.get()); | ||
if (sourceMap) { | ||
node.raw = token.raw; | ||
} | ||
@@ -255,8 +274,27 @@ switch (node.type) { | ||
onStart(node); | ||
let elm = consume(); | ||
node.elements = []; | ||
let elm; | ||
elm = consume(); | ||
while (elm && elm.type !== "]") { | ||
if (elm.type === ",") return token.abort("Unexpected", elm.type, "in", node.type); | ||
elm = consume(); | ||
if (!elm) break; | ||
if (elm.type === ",") { | ||
elm = consume(); | ||
} else if (elm.type !== "]") return token.abort("Unexpected", elm.type, "in", node.type); | ||
} | ||
if (sourceMap) node.raw += elm.raw; | ||
if (!elm || elm.type !== "]") return token.abort("Unclosed", node.type); | ||
node.elements.forEach((a) => { | ||
node.loc.source += a.loc.source; | ||
}); | ||
node.loc.source += elm.loc.source; | ||
node.loc.end = {...elm.loc.end}; | ||
onEnd(node); | ||
@@ -267,2 +305,4 @@ break; | ||
onStart(node); | ||
const source = node.loc.source; | ||
let arg = consume(); | ||
@@ -273,5 +313,7 @@ while (arg && arg.type !== ")") { | ||
if (!arg || arg.type !== ")") return token.abort("Unclosed", BLOCK); | ||
if (!arg || arg.type !== ")") return token.abort("Unclosed", node.type); | ||
if (sourceMap) node.raw += arg.raw; | ||
node.loc.source = source + node.body.loc.source + arg.loc.source; | ||
node.loc.end = arg.loc.end; | ||
onEnd(node); | ||
@@ -285,7 +327,18 @@ break; | ||
onStart(node); | ||
let arg = consume(); | ||
if (arg && arg.type === ",") return token.abort("Unexpected", arg.type, "in", node.type); | ||
while (arg && arg.type !== ")") { | ||
arg = consume(); | ||
} | ||
if (sourceMap) node.raw += arg.raw; | ||
if (!arg || arg.type !== ")") return token.abort("Unclosed", node.type); | ||
node.arguments.forEach((a) => { | ||
node.loc.source += a.loc.source; | ||
}); | ||
node.loc.source += arg.loc.source; | ||
node.loc.end = {...arg.loc.end}; | ||
onEnd(node); | ||
@@ -307,12 +360,15 @@ break; | ||
onStart(node); | ||
do { | ||
node.property = prop; | ||
prop = consume(); | ||
if (sourceMap) node.raw += prop.raw; | ||
if (!prop) continue; | ||
node.loc.source += prop.loc.source; | ||
} while (prop && prop.type !== "}"); | ||
const end = consume(); | ||
if (end.type !== ")") token.abort("Unclosed", MEMBER); | ||
if (!end || end.type !== ")") return token.abort("Unclosed", node.type); | ||
if (sourceMap) node.raw += end.raw; | ||
node.loc.source += end.loc.source; | ||
node.loc.end = end.loc.end; | ||
onEnd(node); | ||
@@ -324,8 +380,22 @@ | ||
onStart(node); | ||
let arg = consume(); | ||
while (arg && arg.type !== "}") { | ||
arg = consume(); | ||
node.properties = []; | ||
let prop = consume(); | ||
addObjectProperty(node, null, prop); | ||
while (prop && prop.type !== "}") { | ||
prop = consume(); | ||
if (prop && prop.type === ",") { | ||
addObjectProperty(node, prop, consume()); | ||
} | ||
} | ||
if (sourceMap) node.raw += arg.raw; | ||
if (!prop || prop.type !== "}") return token.abort("Unclosed", node.type); | ||
node.properties.forEach((a) => { | ||
node.loc.source += a.loc.source; | ||
}); | ||
node.loc.source += prop.loc.source; | ||
node.loc.end = prop.loc.end; | ||
onEnd(node); | ||
@@ -342,10 +412,48 @@ break; | ||
function Node(t) { | ||
const {type, ...rest} = t; | ||
return { | ||
type, | ||
...rest, | ||
function Node(firstToken) { | ||
const tokenNode = {...firstToken}; | ||
Object.defineProperty(tokenNode, "abort", { | ||
enumerable: false, | ||
value: abort, | ||
}); | ||
return tokenNode; | ||
function abort(...args) { | ||
args.push("at", `${firstToken.loc.start.line}:${firstToken.loc.start.column}`); | ||
throw new SyntaxError(args.join(" ")); | ||
} | ||
} | ||
function addObjectProperty(objectNode, commaNode, keyNode) { | ||
if (keyNode && keyNode.type === "}") return; | ||
if (!keyNode) return token.abort("Unclosed", OBJECT); | ||
if (keyNode.type !== LITERAL) return token.abort("Unexpected key", keyNode.type); | ||
const colon = consume(); | ||
if (!colon) return token.abort("Missing key value separator"); | ||
if (colon.type !== ":") return token.abort("Unexpected", colon.type, "in object"); | ||
const value = consume(); | ||
const property = { | ||
type: "Property", | ||
key: { | ||
type: IDENTIFIER, | ||
name: keyNode.value, | ||
}, | ||
value, | ||
loc: { | ||
source: [commaNode, keyNode, colon, value] | ||
.filter(Boolean) | ||
.map((n) => n.loc.source) | ||
.join(""), | ||
start: {...((commaNode || keyNode).loc.start)}, | ||
end: {...value.loc.end}, | ||
} | ||
}; | ||
objectNode.properties.push(property); | ||
} | ||
} | ||
} |
{ | ||
"name": "@bonniernews/local-esi", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"description": "Local Edge Side Includes parser", | ||
@@ -19,5 +19,5 @@ "main": "index.js", | ||
"chronokinesis": "^2.0.1", | ||
"eslint": "^6.5.1", | ||
"mocha": "^6.2.1", | ||
"nock": "^11.4.0" | ||
"eslint": "^6.8.0", | ||
"mocha": "^7.0.1", | ||
"nock": "^11.7.2" | ||
}, | ||
@@ -24,0 +24,0 @@ "dependencies": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
50114
1579