Socket
Socket
Sign inDemoInstall

hogan.js

Package Overview
Dependencies
3
Maintainers
2
Versions
7
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.0 to 3.0.1

.npmignore

247

lib/compiler.js

@@ -23,8 +23,10 @@ /*

rCr = /\r/g,
rSlash = /\\/g,
tagTypes = {
'#': 1, '^': 2, '/': 3, '!': 4, '>': 5,
'<': 6, '=': 7, '_v': 8, '{': 9, '&': 10
};
rSlash = /\\/g;
Hogan.tags = {
'#': 1, '^': 2, '<': 3, '$': 4,
'/': 5, '!': 6, '>': 7, '=': 8, '_v': 9,
'{': 10, '&': 11, '_t': 12
};
Hogan.scan = function scan(text, delimiters) {

@@ -48,3 +50,3 @@ var len = text.length,

if (buf.length > 0) {
tokens.push(new String(buf));
tokens.push({tag: '_t', text: new String(buf)});
buf = '';

@@ -58,4 +60,4 @@ }

isAllWhitespace =
(tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) ||
(!tokens[j].tag && tokens[j].match(rIsWhitespace) === null);
(Hogan.tags[tokens[j].tag] < Hogan.tags['_v']) ||
(tokens[j].tag == '_t' && tokens[j].text.match(rIsWhitespace) === null);
if (!isAllWhitespace) {

@@ -74,6 +76,6 @@ return false;

for (var j = lineStart, next; j < tokens.length; j++) {
if (!tokens[j].tag) {
if (tokens[j].text) {
if ((next = tokens[j+1]) && next.tag == '>') {
// set indent to token value
next.indent = tokens[j].toString()
next.indent = tokens[j].text.toString()
}

@@ -99,3 +101,3 @@ tokens.splice(j, 1);

otag = delimiters[0];
ctag = delimiters[1];
ctag = delimiters[delimiters.length - 1];

@@ -126,3 +128,3 @@ return closeIndex + close.length - 1;

i += otag.length - 1;
tag = tagTypes[text.charAt(i + 1)];
tag = Hogan.tags[text.charAt(i + 1)];
tagType = tag ? text.charAt(i + 1) : '_v';

@@ -142,3 +144,3 @@ if (tagType == '=') {

tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag,
i: (tagType == '/') ? seenTag - ctag.length : i + otag.length});
i: (tagType == '/') ? seenTag - otag.length : i + ctag.length});
buf = '';

@@ -193,13 +195,23 @@ i += ctag.length - 1;

// the tags allowed inside super templates
var allowedInSuper = {'_t': true, '\n': true, '$': true, '/': true};
function buildTree(tokens, kind, stack, customTags) {
var instructions = [],
opener = null,
tail = null,
token = null;
tail = stack[stack.length - 1];
while (tokens.length > 0) {
token = tokens.shift();
if (token.tag == '#' || token.tag == '^' || isOpener(token, customTags)) {
if (tail && tail.tag == '<' && !(token.tag in allowedInSuper)) {
throw new Error('Illegal content in < super tag.');
}
if (Hogan.tags[token.tag] <= Hogan.tags['$'] || isOpener(token, customTags)) {
stack.push(token);
token.nodes = buildTree(tokens, token.tag, stack, customTags);
instructions.push(token);
} else if (token.tag == '/') {

@@ -215,5 +227,7 @@ if (stack.length === 0) {

return instructions;
} else {
instructions.push(token);
} else if (token.tag == '\n') {
token.last = (tokens.length == 0) || (tokens[0].tag == '\n');
}
instructions.push(token);
}

@@ -245,11 +259,58 @@

Hogan.generate = function (tree, text, options) {
var code = 'var _=this;_.b(i=i||"");' + walk(tree) + 'return _.fl();';
function stringifySubstitutions(obj) {
var items = [];
for (var key in obj) {
items.push('"' + esc(key) + '": function(c,p,t,i) {' + obj[key] + '}');
}
return "{ " + items.join(",") + " }";
}
function stringifyPartials(codeObj) {
var partials = [];
for (var key in codeObj.partials) {
partials.push('"' + esc(key) + '":{name:"' + esc(codeObj.partials[key].name) + '", ' + stringifyPartials(codeObj.partials[key]) + "}");
}
return "partials: {" + partials.join(",") + "}, subs: " + stringifySubstitutions(codeObj.subs);
}
Hogan.stringify = function(codeObj, text, options) {
return "{code: function (c,p,i) { " + Hogan.wrapMain(codeObj.code) + " }," + stringifyPartials(codeObj) + "}";
}
var serialNo = 0;
Hogan.generate = function(tree, text, options) {
serialNo = 0;
var context = { code: '', subs: {}, partials: {} };
Hogan.walk(tree, context);
if (options.asString) {
return 'function(c,p,i){' + code + ';}';
return this.stringify(context, text, options);
}
return new Hogan.Template(new Function('c', 'p', 'i', code), text, Hogan, options);
return this.makeTemplate(context, text, options);
}
Hogan.wrapMain = function(code) {
return 'var t=this;t.b(i=i||"");' + code + 'return t.fl();';
}
Hogan.template = Hogan.Template;
Hogan.makeTemplate = function(codeObj, text, options) {
var template = this.makePartials(codeObj);
template.code = new Function('c', 'p', 'i', this.wrapMain(codeObj.code));
return new this.template(template, text, this, options);
}
Hogan.makePartials = function(codeObj) {
var key, template = {subs: {}, partials: codeObj.partials, name: codeObj.name};
for (key in template.partials) {
template.partials[key] = this.makePartials(template.partials[key]);
}
for (key in codeObj.subs) {
template.subs[key] = new Function('c', 'p', 't', 'i', codeObj.subs[key]);
}
return template;
}
function esc(s) {

@@ -266,56 +327,75 @@ return s.replace(rSlash, '\\\\')

function walk(tree) {
var code = '';
for (var i = 0, l = tree.length; i < l; i++) {
var tag = tree[i].tag;
if (tag == '#') {
code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n),
tree[i].i, tree[i].end, tree[i].otag + " " + tree[i].ctag);
} else if (tag == '^') {
code += invertedSection(tree[i].nodes, tree[i].n,
chooseMethod(tree[i].n));
} else if (tag == '<' || tag == '>') {
code += partial(tree[i]);
} else if (tag == '{' || tag == '&') {
code += tripleStache(tree[i].n, chooseMethod(tree[i].n));
} else if (tag == '\n') {
code += text('"\\n"' + (tree.length-1 == i ? '' : ' + i'));
} else if (tag == '_v') {
code += variable(tree[i].n, chooseMethod(tree[i].n));
} else if (tag === undefined) {
code += text('"' + esc(tree[i]) + '"');
}
}
return code;
function createPartial(node, context) {
var prefix = "<" + (context.prefix || "");
var sym = prefix + node.n + serialNo++;
context.partials[sym] = {name: node.n, partials: {}};
context.code += 't.b(t.rp("' + esc(sym) + '",c,p,"' + (node.indent || '') + '"));';
return sym;
}
function section(nodes, id, method, start, end, tags) {
return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' +
'c,p,0,' + start + ',' + end + ',"' + tags + '")){' +
'_.rs(c,p,' +
'function(c,p,_){' +
walk(nodes) +
'});c.pop();}';
}
Hogan.codegen = {
'#': function(node, context) {
context.code += 'if(t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),' +
'c,p,0,' + node.i + ',' + node.end + ',"' + node.otag + " " + node.ctag + '")){' +
't.rs(c,p,' + 'function(c,p,t){';
Hogan.walk(node.nodes, context);
context.code += '});c.pop();}';
},
function invertedSection(nodes, id, method) {
return 'if(!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0,"")){' +
walk(nodes) +
'};';
}
'^': function(node, context) {
context.code += 'if(!t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),c,p,1,0,0,"")){';
Hogan.walk(node.nodes, context);
context.code += '};';
},
function partial(tok) {
return '_.b(_.rp("' + esc(tok.n) + '",c,p,"' + (tok.indent || '') + '"));';
'>': createPartial,
'<': function(node, context) {
var ctx = {partials: {}, code: '', subs: {}, inPartial: true};
Hogan.walk(node.nodes, ctx);
var template = context.partials[createPartial(node, context)];
template.subs = ctx.subs;
template.partials = ctx.partials;
},
'$': function(node, context) {
var ctx = {subs: {}, code: '', partials: context.partials, prefix: node.n};
Hogan.walk(node.nodes, ctx);
context.subs[node.n] = ctx.code;
if (!context.inPartial) {
context.code += 't.sub("' + esc(node.n) + '",c,p,i);';
}
},
'\n': function(node, context) {
context.code += write('"\\n"' + (node.last ? '' : ' + i'));
},
'_v': function(node, context) {
context.code += 't.b(t.v(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));';
},
'_t': function(node, context) {
context.code += write('"' + esc(node.text) + '"');
},
'{': tripleStache,
'&': tripleStache
}
function tripleStache(id, method) {
return '_.b(_.t(_.' + method + '("' + esc(id) + '",c,p,0)));';
function tripleStache(node, context) {
context.code += 't.b(t.t(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));';
}
function variable(id, method) {
return '_.b(_.v(_.' + method + '("' + esc(id) + '",c,p,0)));';
function write(s) {
return 't.b(' + s + ');';
}
function text(id) {
return '_.b(' + id + ');';
Hogan.walk = function(nodelist, context) {
var func;
for (var i = 0, l = nodelist.length; i < l; i++) {
func = Hogan.codegen[nodelist[i].tag];
func && func(nodelist[i], context);
}
return context;
}

@@ -326,31 +406,26 @@

return buildTree(tokens, '', [], options.sectionTags || []);
},
}
Hogan.cache = {};
Hogan.cacheKey = function(text, options) {
return [text, !!options.asString, !!options.disableLambda, options.delimiters, !!options.modelGet].join('||');
}
Hogan.compile = function(text, options) {
// options
//
// asString: false (default)
//
// sectionTags: [{o: '_foo', c: 'foo'}]
// An array of object with o and c fields that indicate names for custom
// section tags. The example above allows parsing of {{_foo}}{{/foo}}.
//
// delimiters: A string that overrides the default delimiters.
// Example: "<% %>"
//
options = options || {};
var key = Hogan.cacheKey(text, options);
var template = this.cache[key];
var key = text + '||' + !!options.asString;
var t = this.cache[key];
if (t) {
return t;
if (template) {
var partials = template.partials;
for (var name in partials) {
delete partials[name].instance;
}
return template;
}
t = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options);
return this.cache[key] = t;
};
template = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options);
return this.cache[key] = template;
}
})(typeof exports !== 'undefined' ? exports : Hogan);

@@ -20,2 +20,3 @@ /*

Hogan.Template = require('./template').Template;
module.exports = Hogan;
Hogan.template = Hogan.Template;
module.exports = Hogan;

@@ -18,9 +18,12 @@ /*

(function (Hogan, useArrayBuffer) {
Hogan.Template = function (renderFunc, text, compiler, options) {
this.r = renderFunc || this.r;
(function (Hogan) {
Hogan.Template = function (codeObj, text, compiler, options) {
codeObj = codeObj || {};
this.r = codeObj.code || this.r;
this.c = compiler;
this.options = options;
this.options = options || {};
this.text = text || '';
this.buf = (useArrayBuffer) ? [] : '';
this.partials = codeObj.partials || {};
this.subs = codeObj.subs || {};
this.buf = '';
}

@@ -47,6 +50,45 @@

// tries to find a partial in the curent scope and render it
rp: function(name, context, partials, indent) {
var partial = partials[name];
// ensurePartial
ep: function(symbol, partials) {
var partial = this.partials[symbol];
// check to see that if we've instantiated this partial before
var template = partials[partial.name];
if (partial.instance && partial.base == template) {
return partial.instance;
}
if (typeof template == 'string') {
if (!this.c) {
throw new Error("No compiler available.");
}
template = this.c.compile(template, this.options);
}
if (!template) {
return null;
}
// We use this to check whether the partials dictionary has changed
this.partials[symbol].base = template;
if (partial.subs) {
// Make sure we consider parent template now
if (!partials.stackText) partials.stackText = {};
for (key in partial.subs) {
if (!partials.stackText[key]) {
partials.stackText[key] = (this.activeSub !== undefined && partials.stackText[this.activeSub]) ? partials.stackText[this.activeSub] : this.text;
}
}
template = createSpecializedPartial(template, partial.subs, partial.partials,
this.stackSubs, this.stackPartials, partials.stackText);
}
this.partials[symbol].instance = template;
return template;
},
// tries to find a partial in the current scope and render it
rp: function(symbol, context, partials, indent) {
var partial = this.ep(symbol, partials);
if (!partial) {

@@ -56,6 +98,2 @@ return '';

if (this.c && typeof partial == 'string') {
partial = this.c.compile(partial, this.options);
}
return partial.ri(context, partials, indent);

@@ -89,6 +127,6 @@ },

if (typeof val == 'function') {
val = this.ls(val, ctx, partials, inverted, start, end, tags);
val = this.ms(val, ctx, partials, inverted, start, end, tags);
}
pass = (val === '') || !!val;
pass = !!val;

@@ -104,16 +142,19 @@ if (!inverted && pass && ctx) {

d: function(key, ctx, partials, returnFound) {
var names = key.split('.'),
var found,
names = key.split('.'),
val = this.f(names[0], ctx, partials, returnFound),
doModelGet = this.options.modelGet,
cx = null;
if (key === '.' && isArray(ctx[ctx.length - 2])) {
return ctx[ctx.length - 1];
}
for (var i = 1; i < names.length; i++) {
if (val && typeof val == 'object' && names[i] in val) {
cx = val;
val = val[names[i]];
} else {
val = '';
val = ctx[ctx.length - 1];
} else {
for (var i = 1; i < names.length; i++) {
found = findInScope(names[i], val, doModelGet);
if (found != null) {
cx = val;
val = found;
} else {
val = '';
}
}

@@ -128,3 +169,3 @@ }

ctx.push(cx);
val = this.lv(val, ctx, partials);
val = this.mv(val, ctx, partials);
ctx.pop();

@@ -140,8 +181,9 @@ }

v = null,
found = false;
found = false,
doModelGet = this.options.modelGet;
for (var i = ctx.length - 1; i >= 0; i--) {
v = ctx[i];
if (v && typeof v == 'object' && key in v) {
val = v[key];
val = findInScope(key, v, doModelGet);
if (val != null) {
found = true;

@@ -157,3 +199,3 @@ break;

if (!returnFound && typeof val == 'function') {
val = this.lv(val, ctx, partials);
val = this.mv(val, ctx, partials);
}

@@ -165,53 +207,62 @@

// higher order templates
ho: function(val, cx, partials, text, tags) {
var compiler = this.c;
var options = this.options;
options.delimiters = tags;
var text = val.call(cx, text);
text = (text == null) ? String(text) : text.toString();
this.b(compiler.compile(text, options).render(cx, partials));
ls: function(func, cx, partials, text, tags) {
var oldTags = this.options.delimiters;
this.options.delimiters = tags;
this.b(this.ct(coerceToString(func.call(cx, text)), cx, partials));
this.options.delimiters = oldTags;
return false;
},
// compile text
ct: function(text, cx, partials) {
if (this.options.disableLambda) {
throw new Error('Lambda features disabled.');
}
return this.c.compile(text, this.options).render(cx, partials);
},
// template result buffering
b: (useArrayBuffer) ? function(s) { this.buf.push(s); } :
function(s) { this.buf += s; },
fl: (useArrayBuffer) ? function() { var r = this.buf.join(''); this.buf = []; return r; } :
function() { var r = this.buf; this.buf = ''; return r; },
b: function(s) { this.buf += s; },
// lambda replace section
ls: function(val, ctx, partials, inverted, start, end, tags) {
var cx = ctx[ctx.length - 1],
t = null;
fl: function() { var r = this.buf; this.buf = ''; return r; },
if (!inverted && this.c && val.length > 0) {
return this.ho(val, cx, partials, this.text.substring(start, end), tags);
}
// method replace section
ms: function(func, ctx, partials, inverted, start, end, tags) {
var textSource,
cx = ctx[ctx.length - 1],
result = func.call(cx);
t = val.call(cx);
if (typeof t == 'function') {
if (typeof result == 'function') {
if (inverted) {
return true;
} else if (this.c) {
return this.ho(t, cx, partials, this.text.substring(start, end), tags);
} else {
textSource = (this.activeSub && this.subsText && this.subsText[this.activeSub]) ? this.subsText[this.activeSub] : this.text;
return this.ls(result, cx, partials, textSource.substring(start, end), tags);
}
}
return t;
return result;
},
// lambda replace variable
lv: function(val, ctx, partials) {
// method replace variable
mv: function(func, ctx, partials) {
var cx = ctx[ctx.length - 1];
var result = val.call(cx);
var result = func.call(cx);
if (typeof result == 'function') {
result = coerceToString(result.call(cx));
if (this.c && ~result.indexOf("{\u007B")) {
return this.c.compile(result, this.options).render(cx, partials);
}
return this.ct(coerceToString(result.call(cx)), cx, partials);
}
return coerceToString(result);
return result;
},
sub: function(name, context, partials, indent) {
var f = this.subs[name];
if (f) {
this.activeSub = name;
f(context, partials, this, indent);
this.activeSub = false;
}
}

@@ -221,10 +272,60 @@

//Find a key in an object
function findInScope(key, scope, doModelGet) {
var val, checkVal;
if (scope && typeof scope == 'object') {
if (scope[key] != null) {
val = scope[key];
// try lookup with get for backbone or similar model data
} else if (doModelGet && scope.get && typeof scope.get == 'function') {
val = scope.get(key);
}
}
return val;
}
function createSpecializedPartial(instance, subs, partials, stackSubs, stackPartials, stackText) {
function PartialTemplate() {};
PartialTemplate.prototype = instance;
function Substitutions() {};
Substitutions.prototype = instance.subs;
var key;
var partial = new PartialTemplate();
partial.subs = new Substitutions();
partial.subsText = {}; //hehe. substext.
partial.buf = '';
stackSubs = stackSubs || {};
partial.stackSubs = stackSubs;
partial.subsText = stackText;
for (key in subs) {
if (!stackSubs[key]) stackSubs[key] = subs[key];
}
for (key in stackSubs) {
partial.subs[key] = stackSubs[key];
}
stackPartials = stackPartials || {};
partial.stackPartials = stackPartials;
for (key in partials) {
if (!stackPartials[key]) stackPartials[key] = partials[key];
}
for (key in stackPartials) {
partial.partials[key] = stackPartials[key];
}
return partial;
}
var rAmp = /&/g,
rLt = /</g,
rGt = />/g,
rApos =/\'/g,
rApos = /\'/g,
rQuot = /\"/g,
hChars =/[&<>\"\']/;
hChars = /[&<>\"\']/;
function coerceToString(val) {

@@ -238,6 +339,6 @@ return String((val === null || val === undefined) ? '' : val);

str
.replace(rAmp,'&amp;')
.replace(rLt,'&lt;')
.replace(rGt,'&gt;')
.replace(rApos,'&#39;')
.replace(rAmp, '&amp;')
.replace(rLt, '&lt;')
.replace(rGt, '&gt;')
.replace(rApos, '&#39;')
.replace(rQuot, '&quot;') :

@@ -252,2 +353,1 @@ str;

})(typeof exports !== 'undefined' ? exports : Hogan);
{
"name": "hogan.js"
, "description": "A mustache compiler."
, "version": "2.0.0"
, "version": "3.0.1"
, "keywords": ["mustache", "template"]

@@ -18,8 +18,13 @@ , "main": "./lib/hogan.js"

]
, "dependencies" : {
"nopt" : "1.0.10"
, "mkdirp": "0.3.0"
}
, "devDependencies": {
"uglify-js": "*"
, "jsdom": "0.2.10"
"uglify-js": "2.x"
, "jsdom": "0.3.4"
, "step": "0.0.5"
, "rimraf": "2.0.1"
}
, "bin" : { "hulk" : "./bin/hulk" }
}

@@ -1,5 +0,5 @@

## Hogan.js - A mustache compiler.
## Hogan.js - A mustache compiler. [![Build Status](https://secure.travis-ci.org/twitter/hogan.js.png)](http://travis-ci.org/twitter/hogan.js)
[Hogan.js](http://twitter.github.com/hogan.js/) is a compiler for the
[Mustache](http://mustache.github.com/) templating language. For information
[Hogan.js](http://twitter.github.io/hogan.js/) is a compiler for the
[Mustache](http://mustache.github.io/) templating language. For information
on Mustache, see the [manpage](http://mustache.github.com/mustache.5.html) and

@@ -50,4 +50,8 @@ the [spec](https://github.com/mustache/spec).

avoid shipping the compiler. However, the optional lambda features from the
Mustache spec do require the compiler to be present.
Mustache spec require the compiler and the original template source to be present.
Hogan also supports [template inheritance](https://github.com/mustache/spec/pull/75),
and maintains compatibility with other implementations like [mustache.java](https://github.com/spullara/mustache.java),
[mustache.php](https://github.com/bobthecow/mustache.php), and [GRMustache](https://github.com/groue/GRMustache)
## Why Hogan.js?

@@ -60,2 +64,45 @@

## Install
# Node.js
```
npm install hogan.js
```
# component
```
component install twitter/hogan.js
```
## Compilation options
The second argument to Hogan.compile is an options hash.
```js
var text = "my <%example%> template."
Hogan.compile(text, {delimiters: '<% %>'});
```
There are currently four valid options.
asString: return the compiled template as a string. This feature is used
by hulk to produce strings containing pre-compiled templates.
sectionTags: allow custom tags that require opening and closing tags, and
treat them as though they were section tags.
```js
var text = "my {{_foo}}example{{/foo}} template."
Hogan.compile(text, { sectionTags: [{o: '_foo', c: 'foo'}]});
```
The value is an array of object with o and c fields that indicate names
for custom section tags. The example above allows parsing of {{_foo}}{{/foo}}.
delimiters: A string that overrides the default delimiters. Example: "<% %>".
disableLambda: disables the higher-order sections / lambda-replace features of Mustache.
## Issues

@@ -81,2 +128,17 @@

## Testing
To run the tests you first need to update all git submodules.
$ git submodule init
$ git submodule update
Unit tests are written using [QUnit](http://qunitjs.com/). To run them, open `test/index.html`
in a browser.
Use [node](http://nodejs.org/) to run all tests from the
[mustache spec](https://github.com/mustache/spec).
$ node test/spec.js
## Authors

@@ -96,2 +158,2 @@

Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc