beard
Advanced tools
Comparing version 0.5.5 to 0.5.6
114
beard.js
@@ -6,2 +6,13 @@ const fs = require('fs'); | ||
class BeardError { | ||
constructor(realError, template, lineNumber, tag) { | ||
this.name = 'Beard Syntax Error'; | ||
this.message = `"{{${tag}}}" in ${template} on line ${lineNumber}`; | ||
this.lineNumber = lineNumber; | ||
this.fileName = template; | ||
this.functionName = tag; | ||
Error.captureStackTrace(this, compile); | ||
} | ||
} | ||
class Beard { | ||
@@ -56,2 +67,13 @@ constructor(opts = {}) { | ||
function validateSyntax(templateCode, tag, lineNumber, template) { | ||
if (templateCode.match(/^.*\{[^\}]*$/)) templateCode += '}'; // append a } to templateCode that needs it | ||
if (templateCode.match(/^\}/)) templateCode = 'if (false) {' + templateCode; // prepend a { to templateCode that needs it | ||
try { | ||
new Function(templateCode); | ||
} catch(e) { | ||
throw new BeardError(e, template, lineNumber, tag); | ||
} | ||
} | ||
const cleanWhitespace = str => str.replace(/\s+/g, ' ').trim(); | ||
@@ -65,7 +87,7 @@ const getDir = path => path.replace(/\/[^\/]+$/, ''); | ||
'if', 'exists', 'elseIf', 'else', | ||
'for', 'each', 'end' | ||
'for', 'each', 'end', 'extends' | ||
]; | ||
const exps = { | ||
extends: (/\{{extends\s\'([^}}]+?)\'\}}/g), | ||
extends: (/^extends\s\'([^}}]+?)\'$/g), | ||
include: (/^include\s\'([^\(]*?)\'(\s*,\s+([\s\S]+))?$/m), | ||
@@ -102,2 +124,3 @@ asset: (/^asset\s+\'(.+)\'$/), | ||
const parse = { | ||
extends: (_, path) => ` _context.globals.view = _buffer; _buffer = _context.compiled('${path}', _currentPath)(_context);`, | ||
block: (_, blockname) => `_blockName = "${blockname}"; _blockCapture = "";`, | ||
@@ -122,14 +145,8 @@ blockEnd: () => 'eval(`var ${_blockName} = _blockCapture`); _context.globals[_blockName] = _blockCapture; _blockName = null;', | ||
}, | ||
for: (_, key, value, object) => { | ||
if (!value) { | ||
value = key; | ||
key = `_iterator_${uniqueIterator(value)}`; | ||
} | ||
for: (_, value, key, object) => { | ||
if (!key) key = `_iterator_${uniqueIterator(value)}`; | ||
return `for (var ${key} in ${object}) { var ${value} = ${object}[${key}];`; | ||
}, | ||
each: (_, iter, value, array) => { | ||
if (!value) { | ||
value = iter; | ||
iter = `_iterator_${uniqueIterator(value)}`; | ||
} | ||
each: (_, value, iter, array) => { | ||
if (!iter) iter = `_iterator_${uniqueIterator(value)}`; | ||
const length = `_iterator_${uniqueIterator(value)}`; | ||
@@ -140,25 +157,57 @@ return `for (var ${iter} = 0, ${length} = ${array}.length; ${iter} < ${length}; ${iter}++) { var ${value} = ${array}[${iter}];`; | ||
function parser(match, inner) { | ||
const prev = inner; | ||
inner = tags.reduce(reducer, inner); | ||
const middle = inner === prev && !/^:/.test(inner) | ||
? `_capture(${inner});` | ||
: inner.replace(/\t|\n|\r|^:/, ''); | ||
return `"); ${middle} _capture("`; | ||
function scanner(template, path) { | ||
let statements = []; | ||
const contentCompiler = (content) => statements.push(`_capture("${content}");`); | ||
const tagCompiler = (tag, lineNumber) => { | ||
const parsedStatement = parser(tag); | ||
validateSyntax(parsedStatement, tag, lineNumber, path); | ||
statements.push(parsedStatement); | ||
}; | ||
exps.statement.lastIndex = 0; | ||
let result = exps.statement.exec(template); | ||
let lastIndex = 0; | ||
let extendsResult; | ||
while (result) { | ||
const content = template.substring(lastIndex, result.index); | ||
if (content.length > 0) contentCompiler(content); | ||
const tag = result[1]; | ||
const extendsMatch = exps.extends.exec(tag); | ||
if (extendsMatch) { // hold extends until the end | ||
extendsResult = result; | ||
} else { | ||
const lineNumber = template.substring(0, result.index).split('\n').length; | ||
tagCompiler(tag, lineNumber); | ||
} | ||
lastIndex = exps.statement.lastIndex; | ||
result = exps.statement.exec(template); | ||
} | ||
if (lastIndex < template.length) { | ||
const content = template.substring(lastIndex, template.length); | ||
contentCompiler(content); | ||
} | ||
if (extendsResult) { | ||
const lineNumber = template.substring(0, extendsResult.index).split('\n').length; | ||
tagCompiler(extendsResult[1], lineNumber); | ||
} | ||
return statements; | ||
} | ||
function parser(statement) { | ||
const parsedStatement = tags.reduce(reducer, statement); | ||
return statement === parsedStatement | ||
? `_capture(${statement});` | ||
: parsedStatement.replace(/\t|\n|\r/, ''); | ||
} | ||
function compile(str, path) { | ||
let layout = ''; | ||
const templateCode = scanner(str, path).join(' ') | ||
.replace(new RegExp('\\\\', 'g'), '\\\\'); | ||
str = str | ||
.replace(exps.extends, (_, path) => { | ||
layout = ` | ||
_context.globals.view = _buffer; | ||
_buffer = _context.compiled('${path}', _currentPath)(_context); | ||
`; | ||
return ''; | ||
}) | ||
.replace(new RegExp('\\\\', 'g'), '\\\\').replace(/"/g, '\\"') | ||
.replace(exps.statement, parser); | ||
const fn = ` | ||
@@ -201,4 +250,3 @@ var _currentPath = '${path}'; | ||
_capture("${str}"); | ||
${layout} | ||
${templateCode} | ||
return _buffer; | ||
@@ -205,0 +253,0 @@ `.replace(/_capture\(""\);(\s+)?/g, ''); |
{ | ||
"name": "beard", | ||
"version": "0.5.5", | ||
"version": "0.5.6", | ||
"description": "More than a mustache.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
16150
284
2