Socket
Socket
Sign inDemoInstall

beard

Package Overview
Dependencies
1
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.5.3 to 0.5.4

341

beard.js
const fs = require('fs');
const exts = '(.brd$|.brd.html$)';
const exts = '(.beard$)';
const traversy = require('traversy');
const normalize = require('path').normalize;
function hash(str) {
let hash = 5381;
let i = str.length;
while (i) hash = (hash * 33) ^ str.charCodeAt(--i);
return hash >>> 0;
}
class Beard {
constructor(opts = {}) {
if (!opts.hasOwnProperty('cache')) opts.cache = true;
if (!opts.hasOwnProperty('asset')) opts.asset = path => false;
opts.templates = opts.templates || {};
this.opts = opts;
this.fnCache = {};
this.pathMap = {};
const cleanWhitespace = str => str.replace(/\s+/g, ' ').trim();
module.exports = function(opts = {}) {
opts.cache = opts.cache != undefined ? opts.cache : true;
opts.templates = opts.templates || {};
let fnCache = {};
let pathMap = {};
let iterator = 0;
const Beard = function() {
if (opts.root) {
const regex = new RegExp(`(^${opts.root}|.brd$|.brd.html$)`, 'g');
traversy(opts.root, exts, (path) => {
if (this.opts.root) {
const regex = new RegExp(`(^${this.opts.root}|.beard$)`, 'g');
traversy(this.opts.root, exts, (path) => {
const key = path.replace(regex, '');
const body = fs.readFileSync(path, 'utf8');
opts.templates[key] = opts.cache ? cleanWhitespace(body) : body;
pathMap[key] = path;
this.opts.templates[key] = this.opts.cache ? cleanWhitespace(body) : body;
this.pathMap[key] = path;
});

@@ -35,177 +26,183 @@ }

Beard.prototype = {
render: (path, data = {}) => {
iterator = 0;
let context = {
globals: {},
locals: [data],
path: null
};
return compiled(path, '/')(context);
}
};
function resolvePath(path, parentPath) {
if (path.startsWith('/')) {
return path;
compiled(path, context) {
context.path = resolvePath(path, context.path);
if (this.opts.cache) {
const str = this.opts.templates[context.path];
const key = hash(context.path);
if (!this.fnCache[key]) this.fnCache[key] = compile(str, context.path);
return this.fnCache[key];
} else {
const currentDir = parentPath.replace(/\/[^\/]+$/, '');
return normalize(`${currentDir}/${path}`);
const str = fs.readFileSync(this.pathMap[context.path], 'utf8');
return compile(str, context.path);
}
}
const exps = {
extends: (/\{{extends\s\'([^}}]+?)\'\}}/g),
include: (/^include\s\'([^\(]*?)\'$/g),
includeFn: (/^include\((\s?\'([^\(]*?)\'\,\s?\{([^\)]*)\})\)$/g),
block: (/^block\s+(.[^}]*)/g),
blockEnd: (/^endblock$/g),
encode: (/^\:(.*)/),
comment: (/^\*.*\*$/g),
statement: (/{{\s*([\S\s(?!}})]+?)\s*}}/g),
if: (/^if\s+([^]*)$/),
elseIf: (/^else\s+if\s+([^]*)$/),
else: (/^else$/),
for: (/^for\s+([$A-Za-z_][0-9A-Za-z_]*)(?:\s*,\s*([$A-Za-z_][0-9A-Za-z_]*))?\s+in\s+(.*)$/),
each: (/^each\s+([$A-Za-z_][0-9A-Za-z_]*)(?:\s*,\s*([$A-Za-z_][0-9A-Za-z_]*))?\s+in\s(.*)$/),
end: (/^end$/)
};
asset(p, path) {
const absolutePath = resolvePath(p, path);
return this.opts.asset(absolutePath) || absolutePath;
}
const parse = {
include: (_, includePath) => `_capture(compiled("${includePath}", path)(_context));`,
includeFn: (_, __, includePath, data) => `_context.locals.push({${data}}); _capture(compiled("${includePath}", path)(_context)); _context.locals.pop();`,
block: (_, blockname) => `_blockName = "${blockname}"; _blockCapture = "";`,
blockEnd: () => 'eval(`var ${_blockName} = _blockCapture`); _context.globals[_blockName] = _blockCapture; _blockName = null;',
encode: (_, statement) => `_encode(${statement});`,
comment: () => '',
if: (_, statement) => `if (${statement}) {`,
elseIf: (_, statement) => `} else if (${statement}) {`,
else: () => '} else {',
end: () => '}',
for: (_, key, value, object) => {
if (!value) key = (value = key, 'iterator' + iterator++);
return `for (var ${key} in ${object}){ var ${value} = ${object}[${key}];`;
},
each: (_, iter, value, array) => {
if (!value) iter = (value = iter, 'iterator' + iterator++);
const length = 'length' + iterator++;
return `for (var ${iter} = 0, ${length} = ${array}.length; ${iter} < ${length}; ${iter}++) { var ${value} = ${array}[${(iter)}];`;
render(path, data = {}) {
const context = {
globals: {},
locals: [data],
compiled: this.compiled.bind(this),
asset: this.asset.bind(this),
path: ''
}
};
return this.compiled(path, context)(context);
}
}
function parser(match, inner) {
const prev = inner;
inner = inner
.replace(exps.include, parse.include)
.replace(exps.includeFn, parse.includeFn)
.replace(exps.block, parse.block)
.replace(exps.blockEnd, parse.blockEnd)
.replace(exps.encode, parse.encode)
.replace(exps.comment, parse.comment)
.replace(exps.end, parse.end)
.replace(exps.else, parse.else)
.replace(exps.elseIf, parse.elseIf)
.replace(exps.if, parse.if)
.replace(exps.each, parse.each)
.replace(exps.for, parse.for);
const cleanWhitespace = str => str.replace(/\s+/g, ' ').trim();
const getDir = path => path.replace(/\/[^\/]+$/, '');
const reducer = (inner, tag) => inner.replace(exps[tag], parse[tag]);
const uniqueIterator = value => Math.random().toString().substring(2);
const tags = [
'include', 'block', 'blockEnd',
'asset', 'put', 'encode', 'comment',
'if', 'exists', 'elseIf', 'else',
'for', 'each', 'end'
];
const middle = inner === prev && !/^:/.test(inner)
? `_capture(${inner});`
: inner.replace(/\t|\n|\r|^:/, '');
const exps = {
extends: (/\{{extends\s\'([^}}]+?)\'\}}/g),
include: (/^include\s\'([^\(]*?)\'(\s*,\s+([\s\S]+))?$/m),
asset: (/^asset\s+\'(.+)\'$/),
put: (/^put\s+(.+)$/),
exists: (/^exists\s+(.+)$/),
block: (/^block\s+(.[^}]*)/),
blockEnd: (/^endblock$/),
encode: (/^\:(.*)/),
comment: (/^\*.*\*$/),
statement: (/{{\s*([\S\s(?!}})]+?)\s*}}(?!\})/g),
if: (/^if\s+([^]*)$/),
elseIf: (/^else\s+if\s+([^]*)$/),
else: (/^else$/),
for: (/^for\s+([$A-Za-z_][0-9A-Za-z_]*)(?:\s*,\s*([$A-Za-z_][0-9A-Za-z_]*))?\s+in\s+(.*)$/),
each: (/^each\s+([$A-Za-z_][0-9A-Za-z_]*)(?:\s*,\s*([$A-Za-z_][0-9A-Za-z_]*))?\s+in\s(.*)$/),
end: (/^end$/)
};
return `"); ${middle} _capture("`;
}
function resolvePath(path, parentPath) {
return path.startsWith('/')
? path
: normalize(`${getDir(parentPath)}/${path}`);
}
function compiled(path, parentPath) {
const fullPath = resolvePath(path, parentPath);
if (opts.cache) {
const str = opts.templates[fullPath];
const key = hash(fullPath);
if (!fnCache[key]) fnCache[key] = compile(str, fullPath);
return fnCache[key];
} else {
const str = fs.readFileSync(pathMap[fullPath], 'utf8');
return compile(str, fullPath);
function hash(str) {
let hash = 5381;
let i = str.length;
while (i) hash = (hash * 33) ^ str.charCodeAt(--i);
return hash >>> 0;
}
const parse = {
block: (_, blockname) => `_blockName = "${blockname}"; _blockCapture = "";`,
blockEnd: () => 'eval(`var ${_blockName} = _blockCapture`); _context.globals[_blockName] = _blockCapture; _blockName = null;',
asset: (_, path) => `_capture(_context.asset("${path}", _context.path));`,
put: (_, varname) => `_capture(typeof ${varname} !== "undefined" ? ${varname} : "");`,
exists: (_, varname) => `if (typeof ${varname} !== "undefined") {`,
encode: (_, statement) => `_encode(${statement});`,
comment: () => '',
if: (_, statement) => `if (${statement}) {`,
elseIf: (_, statement) => `} else if (${statement}) {`,
else: () => '} else {',
end: () => '}',
include: (_, includePath, __, data) => {
data = data || '{}';
return `
_context.locals.push(Object.assign(_context.locals[_context.locals.length - 1], ${data}));
_capture(_context.compiled("${includePath}", _context)(_context));
_context.locals.pop();
`;
},
for: (_, key, value, object) => {
if (!value) {
value = 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)}`;
}
const length = `_iterator_${uniqueIterator(value)}`;
return `for (var ${iter} = 0, ${length} = ${array}.length; ${iter} < ${length}; ${iter}++) { var ${value} = ${array}[${iter}];`;
}
};
function compile(str, path) {
let layout = '';
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("`;
}
str = str
.replace(exps.extends, (_, path) => {
layout = `
_context.globals.view = _buffer;
_buffer = compiled('${path}', path)(_context);
`;
return '';
})
.replace(new RegExp('\\\\', 'g'), '\\\\').replace(/"/g, '\\"')
.replace(exps.statement, parser);
function compile(str, context) {
let layout = '';
const fn = `
function _compiledFn(_context){
var path = '${path}';
var _buffer = '';
var _blockName;
var _blockCapture;
str = str
.replace(exps.extends, (_, path) => {
layout = `
_context.globals.view = _buffer;
_buffer = _context.compiled('${path}', _context)(_context);
`;
return '';
})
.replace(new RegExp('\\\\', 'g'), '\\\\').replace(/"/g, '\\"')
.replace(exps.statement, parser);
function _capture(str) {
if (_blockName) {
_blockCapture += str;
} else {
_buffer += str;
}
}
const fn = `
var _buffer = '';
var _blockName;
var _blockCapture;
function _encode(str) {
_capture(str
.replace(/&(?!\\w+;)/g, '&#38;')
.replace(/\</g, '&#60;')
.replace(/\>/g, '&#62;')
.replace(/\"/g, '&#34;')
.replace(/\'/g, '&#39;')
.replace(/\\//g, '&#47;'));
function _capture(str) {
if (_blockName) {
_blockCapture += str;
} else {
_buffer += str;
}
}
function exists(varname) {
return eval('typeof ' + varname + ' !== "undefined";');
}
function _encode(str) {
_capture(str
.replace(/&(?!\\w+;)/g, '&#38;')
.replace(/\</g, '&#60;')
.replace(/\>/g, '&#62;')
.replace(/\"/g, '&#34;')
.replace(/\'/g, '&#39;')
.replace(/\\//g, '&#47;'));
}
function put(varname) {
return exists(varname)
? eval(varname)
: '';
for (var prop in _context.globals) {
if (_context.globals.hasOwnProperty(prop)) {
eval('var ' + prop + ' = _context.globals[prop]');
}
}
for (var prop in _context.globals) {
if (_context.globals.hasOwnProperty(prop)) {
eval('var ' + prop + ' = _context.globals[prop]');
}
var _locals = _context.locals[_context.locals.length - 1];
for (var prop in _locals) {
if (_locals.hasOwnProperty(prop)) {
eval('var ' + prop + ' = _locals[prop]');
}
}
var _locals = _context.locals[_context.locals.length - 1];
for (var prop in _locals) {
if (_locals.hasOwnProperty(prop)) {
eval('var ' + prop + ' = _locals[prop]');
}
}
_capture("${str}");
${layout}
return _buffer;
`.replace(/_capture\(""\);(\s+)?/g, '');
_capture("${str}");
${layout}
return _buffer;
}
`.replace(/_capture\(""\);(\s+)?/g, '');
try {
eval(cleanWhitespace(fn));
return _compiledFn.bind(_compiledFn);
} catch (e) {
throw new Error(`Compilation error: ${fn}`);
}
try {
return new Function('_context', cleanWhitespace(fn));
} catch (e) {
throw new Error(`Compilation error: ${fn}`);
}
}
return new Beard();
};
module.exports = opts => new Beard(opts);
{
"name": "beard",
"version": "0.5.3",
"version": "0.5.4",
"description": "More than a mustache.",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -41,2 +41,3 @@ # Beard

- **cache** (boolean) - Set to `false` to disable caching of template files. Defaults to `true`.
- **asset** (function) - Callback used for `asset` tag. Looks up asset paths. See asset example below.

@@ -73,3 +74,7 @@ ### Render Arguments

{{include 'template'}}
{{include('template', {arg: 'val', arg2: 'val2'})}}
{{include 'template', {arg: 'val', arg2: 'val2'}}}
{{include 'template', {
arg1: 'val1',
arg2: 'val2'
}}}
```

@@ -106,4 +111,41 @@

### asset
Assets are used to reference external files. You can control and modify the behavior of the tag via
the `assets` callback option.
```
engine = beard({
asset: path => '/assets/' + path
});
```
Used in a template:
```
<html>
<head><link rel="stylesheet" type="text/css" href="{{asset 'styles.css'}}"></head>
</html>
```
Returns:
```
<html>
<head><link rel="stylesheet" type="text/css" href="/assets/styles.css"></head>
</html>
```
### put
The put tag outputs a local variable or a block, or an empty string if the value doesn't exist.
```
{{put foo}}
```
This will output the value of `foo`, if defined, or a blank string if not. Conversely, accessing it
directly, such as `{{foo}}` would raise an error if it were undefined.
### block
Makes content available as variable name.
Make content available for rendering in any context (such as an extended layout or an included partial.)

@@ -130,2 +172,31 @@ ```

You can also conditionally check if a block is set.
```
{{block cart}}
everything you have put in your cart...
{{endblock}}
// another template
{{exists cart}}
{{cart}}
{{else}}
Your cart is empty.
{{end}}
```
Will returns:
```
everything you have put in your cart...
```
Using `put` is a simple way to output the block content if you are unsure if it has been set:
```
{{put header}}
```
This will output an empty string if the header has not been set.
### conditionals

@@ -166,2 +237,2 @@

Released under [MIT license](http://en.wikipedia.org/wiki/MIT_License).
Released under [MIT license](http://en.wikipedia.org/wiki/MIT_License).

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc