Comparing version 1.1.5 to 1.2.0
@@ -49,3 +49,3 @@ #!/usr/bin/env node | ||
console.log(' # jade over stdio'); | ||
console.log(' $ echo "h1 Jade!" | jade'); | ||
console.log(' $ echo \'h1 Jade!\' | jade'); | ||
console.log(''); | ||
@@ -132,2 +132,9 @@ console.log(' # foo, bar dirs rendering to /tmp'); | ||
}).resume(); | ||
process.on('SIGINT', function() { | ||
process.stdout.write('\n'); | ||
process.stdin.emit('end'); | ||
process.stdout.write('\n'); | ||
process.exit(); | ||
}) | ||
} | ||
@@ -134,0 +141,0 @@ |
@@ -5,3 +5,3 @@ { | ||
"description": "Jade template runtime", | ||
"version": "1.1.5", | ||
"version": "1.2.0", | ||
"keywords": [ | ||
@@ -8,0 +8,0 @@ "template" |
module.exports = process.env.JADE_COV | ||
? require('./lib-cov/jade') | ||
: require('./lib/jade'); | ||
module.exports = require('./lib/jade'); |
@@ -6,10 +6,15 @@ 'use strict'; | ||
var doctypes = require('./doctypes'); | ||
var selfClosing = require('./self-closing'); | ||
var runtime = require('./runtime'); | ||
var utils = require('./utils'); | ||
var parseJSExpression = require('character-parser').parseMax; | ||
var isConstant = require('constantinople'); | ||
var toConstant = require('constantinople').toConstant; | ||
var constantinople = require('constantinople'); | ||
function isConstant(src) { | ||
return constantinople(src, {jade: runtime, 'jade_interp': undefined}); | ||
} | ||
function toConstant(src) { | ||
return constantinople.toConstant(src, {jade: runtime, 'jade_interp': undefined}); | ||
} | ||
/** | ||
@@ -33,2 +38,4 @@ * Initialize `Compiler` with the given `node`. | ||
this.terse = false; | ||
this.mixins = {}; | ||
this.dynamicMixins = false; | ||
if (options.doctype) this.setDoctype(options.doctype); | ||
@@ -51,5 +58,17 @@ }; | ||
this.buf = []; | ||
if (this.pp) this.buf.push("jade.indent = [];"); | ||
if (this.pp) this.buf.push("var jade_indent = [];"); | ||
this.lastBufferedIdx = -1; | ||
this.visit(this.node); | ||
if (!this.dynamicMixins) { | ||
// if there are no dynamic mixins we can remove any un-used mixins | ||
var mixinNames = Object.keys(this.mixins); | ||
for (var i = 0; i < mixinNames.length; i++) { | ||
var mixin = this.mixins[mixinNames[i]]; | ||
for (var x = 0; x < mixin.length; x++) { | ||
for (var y = mixin[x].start; y < mixin[x].end; y++) { | ||
this.buf[y] = ''; | ||
} | ||
} | ||
} | ||
} | ||
return this.buf.join('\n'); | ||
@@ -68,3 +87,2 @@ }, | ||
setDoctype: function(name){ | ||
name = name || 'default'; | ||
this.doctype = doctypes[name.toLowerCase()] || '<!DOCTYPE ' + name + '>'; | ||
@@ -94,13 +112,5 @@ this.terse = this.doctype.toLowerCase() == '<!doctype html>'; | ||
} else { | ||
try { | ||
var rest = match[3]; | ||
var range = parseJSExpression(rest); | ||
var code = ('!' == match[2] ? '' : 'jade.escape') + "((jade.interp = " + range.src + ") == null ? '' : jade.interp)"; | ||
} catch (ex) { | ||
throw ex; | ||
//didn't match, just as if escaped | ||
this.buffer(match[2] + '{', false); | ||
this.buffer(match[3], true); | ||
return; | ||
} | ||
var rest = match[3]; | ||
var range = parseJSExpression(rest); | ||
var code = ('!' == match[2] ? '' : 'jade.escape') + "((jade_interp = " + range.src + ") == null ? '' : jade_interp)"; | ||
this.bufferExpression(code); | ||
@@ -138,5 +148,4 @@ this.buffer(rest.substr(range.end + 1), true); | ||
bufferExpression: function (src) { | ||
var fn = Function('', 'return (' + src + ');'); | ||
if (isConstant(src)) { | ||
return this.buffer(fn(), false) | ||
return this.buffer(toConstant(src) + '', false) | ||
} | ||
@@ -171,3 +180,3 @@ if (this.lastBufferedIdx == this.buf.length) { | ||
if (this.parentIndents) | ||
this.buf.push("buf.push.apply(buf, jade.indent);"); | ||
this.buf.push("buf.push.apply(buf, jade_indent);"); | ||
}, | ||
@@ -245,4 +254,6 @@ | ||
} | ||
this.visit(node.block); | ||
this.buf.push(' break;'); | ||
if (node.block) { | ||
this.visit(node.block); | ||
this.buf.push(' break;'); | ||
} | ||
}, | ||
@@ -297,5 +308,5 @@ | ||
visitMixinBlock: function(block){ | ||
if (this.pp) this.buf.push("jade.indent.push('" + Array(this.indents + 1).join(' ') + "');"); | ||
if (this.pp) this.buf.push("jade_indent.push('" + Array(this.indents + 1).join(' ') + "');"); | ||
this.buf.push('block && block();'); | ||
if (this.pp) this.buf.push("jade.indent.pop();"); | ||
if (this.pp) this.buf.push("jade_indent.pop();"); | ||
}, | ||
@@ -336,7 +347,15 @@ | ||
var pp = this.pp; | ||
var dynamic = mixin.name[0]==='#'; | ||
var key = mixin.name; | ||
if (dynamic) this.dynamicMixins = true; | ||
name += (dynamic ? mixin.name.substr(2,mixin.name.length-3):'"'+mixin.name+'"')+']'; | ||
name += (mixin.name[0]=='#' ? mixin.name.substr(2,mixin.name.length-3):'"'+mixin.name+'"')+']'; | ||
if (mixin.call) { | ||
if (pp) this.buf.push("jade.indent.push('" + Array(this.indents + 1).join(' ') + "');") | ||
if (this.mixins[key]) { | ||
// clear list of mixins with this key so they are not removed | ||
this.mixins[key] = []; | ||
} else { | ||
// todo: throw error for calling a mixin that's not defined | ||
} | ||
if (pp) this.buf.push("jade_indent.push('" + Array(this.indents + 1).join(' ') + "');") | ||
if (block || attrs.length || attrsBlocks.length) { | ||
@@ -384,4 +403,5 @@ | ||
} | ||
if (pp) this.buf.push("jade.indent.pop();") | ||
if (pp) this.buf.push("jade_indent.pop();") | ||
} else { | ||
var mixin_start = this.buf.length; | ||
this.buf.push(name + ' = function(' + args + '){'); | ||
@@ -393,2 +413,5 @@ this.buf.push('var block = (this && this.block), attributes = (this && this.attributes) || {};'); | ||
this.buf.push('};'); | ||
var mixin_end = this.buf.length; | ||
this.mixins[key] = this.mixins[key] || []; | ||
this.mixins[key].push({start: mixin_start, end: mixin_end}); | ||
} | ||
@@ -429,3 +452,3 @@ }, | ||
if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) { | ||
if (tag.selfClosing && !this.xml) { | ||
this.buffer('<'); | ||
@@ -437,7 +460,2 @@ bufferName(); | ||
: this.buffer('/>'); | ||
// if it is non-empty throw an error | ||
if (tag.block && !(tag.block.type === 'Block' && tag.block.nodes.length === 0) | ||
&& tag.block.nodes.some(function (tag) { return tag.type !== 'Text' || !/^\s*$/.test(tag.val)})) { | ||
throw new Error(name + ' is self closing and should not have content.'); | ||
} | ||
} else { | ||
@@ -477,3 +495,2 @@ // Optimize attributes buffering | ||
).join('\n'); | ||
filter.attrs = filter.attrs || {}; | ||
filter.attrs.filename = this.options.filename; | ||
@@ -540,3 +557,3 @@ this.buffer(filters(filter.name, text, filter.attrs), true); | ||
var val = code.val.trimLeft(); | ||
val = 'null == (jade.interp = '+val+') ? "" : jade.interp'; | ||
val = 'null == (jade_interp = '+val+') ? "" : jade_interp'; | ||
if (code.escape) val = 'jade.escape(' + val + ')'; | ||
@@ -659,3 +676,3 @@ this.bufferExpression(val); | ||
} else if (escaped) { | ||
val = '(typeof (jade.interp = ' + val + ') == "string" ? jade.escape(jade.interp) : jade.interp)"'; | ||
val = '(typeof (jade_interp = ' + val + ') == "string" ? jade.escape(jade_interp) : jade_interp)'; | ||
} | ||
@@ -672,3 +689,3 @@ buf.push(JSON.stringify(key) + ': ' + val); | ||
} | ||
} else { | ||
} else if (classes.length) { | ||
if (classes.every(isConstant)) { | ||
@@ -678,6 +695,6 @@ classes = JSON.stringify(runtime.joinClasses(classes.map(toConstant).map(runtime.joinClasses).map(function (cls, i) { | ||
}))); | ||
} else if (classes.length) { | ||
classes = '(jade.interp = ' + JSON.stringify(classEscaping) + ',' + | ||
} else { | ||
classes = '(jade_interp = ' + JSON.stringify(classEscaping) + ',' + | ||
' jade.joinClasses([' + classes.join(',') + '].map(jade.joinClasses).map(function (cls, i) {' + | ||
' return jade.interp[i] ? jade.escape(cls) : cls' + | ||
' return jade_interp[i] ? jade.escape(cls) : cls' + | ||
' }))' + | ||
@@ -684,0 +701,0 @@ ')'; |
@@ -12,4 +12,1 @@ 'use strict'; | ||
} | ||
filter.exists = function (name, str, options) { | ||
return typeof filter[name] === 'function'; | ||
}; |
@@ -16,4 +16,1 @@ 'use strict'; | ||
} | ||
filter.exists = function (name, str, options) { | ||
return typeof filter[name] === 'function' || transformers[name]; | ||
}; |
@@ -107,2 +107,3 @@ 'use strict'; | ||
globals.push('jade_mixins'); | ||
globals.push('jade_interp'); | ||
globals.push('jade_debug'); | ||
@@ -114,2 +115,3 @@ globals.push('buf'); | ||
+ 'var jade_mixins = {};\n' | ||
+ 'var jade_interp;\n' | ||
+ (options.self | ||
@@ -116,0 +118,0 @@ ? 'var self = locals || {};\n' + js |
@@ -5,2 +5,3 @@ 'use strict'; | ||
var characterParser = require('character-parser'); | ||
var selfClosing = require('./self-closing'); | ||
@@ -230,3 +231,3 @@ | ||
} | ||
tok.selfClosing = !! captures[2]; | ||
tok.selfClosing = !!captures[2] || selfClosing.indexOf(name) !== -1; | ||
return tok; | ||
@@ -286,3 +287,3 @@ } | ||
if (tok = this.scan(/^([^\.\n][^\n]+)/, 'text')) { | ||
console.warn('Warning: missing space before text for line ' + this.lineno + | ||
console.warn('Warning: missing space before text for line ' + this.lineno + | ||
' of jade file "' + this.filename + '"'); | ||
@@ -492,3 +493,5 @@ return tok; | ||
var type = captures[1] | ||
, js = captures[2]; | ||
var js = captures[2]; | ||
var isIf = false; | ||
var isElse = false; | ||
@@ -499,2 +502,3 @@ switch (type) { | ||
js = 'if (' + js + ')'; | ||
isIf = true; | ||
break; | ||
@@ -504,2 +508,3 @@ case 'unless': | ||
js = 'if (!(' + js + '))'; | ||
isIf = true; | ||
break; | ||
@@ -509,2 +514,4 @@ case 'else if': | ||
js = 'else if (' + js + ')'; | ||
isIf = true; | ||
isElse = true; | ||
break; | ||
@@ -516,6 +523,10 @@ case 'else': | ||
js = 'else'; | ||
isElse = true; | ||
break; | ||
} | ||
return this.tok('code', js); | ||
var tok = this.tok('code', js); | ||
tok.isElse = isElse; | ||
tok.isIf = isIf; | ||
tok.requiresBlock = true; | ||
return tok; | ||
} | ||
@@ -533,3 +544,5 @@ }, | ||
assertExpression(captures[1]) | ||
return this.tok('code', 'while (' + captures[1] + ')'); | ||
var tok = this.tok('code', 'while (' + captures[1] + ')'); | ||
tok.requiresBlock = true; | ||
return tok; | ||
} | ||
@@ -662,3 +675,3 @@ }, | ||
throw new Error('Unexpected character ' + str[i + 1] + ' expected ` `, `\\n`, `,`, `!` or `=`'); | ||
} else if (loc === 'key-char') { | ||
} else { | ||
key += str[i]; | ||
@@ -665,0 +678,0 @@ } |
'use strict'; | ||
var Node = require('./node'); | ||
var Block = require('./block'); | ||
@@ -37,3 +36,2 @@ /** | ||
Attrs.prototype.setAttribute = function(name, val, escaped){ | ||
this.attributeNames = this.attributeNames || []; | ||
if (name !== 'class' && this.attributeNames.indexOf(name) !== -1) { | ||
@@ -40,0 +38,0 @@ throw new Error('Duplicate attribute "' + name + '" is not allowed.'); |
'use strict'; | ||
var Node = require('./node') | ||
var Block = require('./block'); | ||
var Node = require('./node'); | ||
@@ -6,0 +5,0 @@ /** |
@@ -15,7 +15,6 @@ 'use strict'; | ||
var Mixin = module.exports = function Mixin(name, args, block, call){ | ||
Attrs.call(this); | ||
this.name = name; | ||
this.args = args; | ||
this.block = block; | ||
this.attrs = []; | ||
this.attributeBlocks = []; | ||
this.call = call; | ||
@@ -22,0 +21,0 @@ }; |
@@ -16,5 +16,4 @@ 'use strict'; | ||
var Tag = module.exports = function Tag(name, block) { | ||
Attrs.call(this); | ||
this.name = name; | ||
this.attrs = []; | ||
this.attributeBlocks = []; | ||
this.block = block || new Block; | ||
@@ -21,0 +20,0 @@ }; |
@@ -70,13 +70,2 @@ 'use strict'; | ||
/** | ||
* Skip `n` tokens. | ||
* | ||
* @param {Number} n | ||
* @api private | ||
*/ | ||
skip: function(n){ | ||
while (n--) this.advance(); | ||
}, | ||
/** | ||
* Single token lookahead. | ||
@@ -209,6 +198,2 @@ * | ||
return this.parseCase(); | ||
case 'when': | ||
return this.parseWhen(); | ||
case 'default': | ||
return this.parseDefault(); | ||
case 'extends': | ||
@@ -287,3 +272,27 @@ return this.parseExtends(); | ||
node.line = this.line(); | ||
node.block = this.block(); | ||
var block = new nodes.Block; | ||
block.line = this.line(); | ||
block.filename = this.filename; | ||
this.expect('indent'); | ||
while ('outdent' != this.peek().type) { | ||
switch (this.peek().type) { | ||
case 'newline': | ||
this.advance(); | ||
break; | ||
case 'when': | ||
block.push(this.parseWhen()); | ||
break; | ||
case 'default': | ||
block.push(this.parseDefault()); | ||
break; | ||
default: | ||
throw new Error('Unexpected token "' + this.peek().type | ||
+ '", expected "when", "default" or "newline"'); | ||
} | ||
} | ||
this.expect('outdent'); | ||
node.block = block; | ||
return node; | ||
@@ -297,4 +306,7 @@ }, | ||
parseWhen: function(){ | ||
var val = this.expect('when').val | ||
return new nodes.Case.When(val, this.parseBlockExpansion()); | ||
var val = this.expect('when').val; | ||
if (this.peek().type !== 'newline') | ||
return new nodes.Case.When(val, this.parseBlockExpansion()); | ||
else | ||
return new nodes.Case.When(val); | ||
}, | ||
@@ -315,14 +327,31 @@ | ||
parseCode: function(){ | ||
parseCode: function(afterIf){ | ||
var tok = this.expect('code'); | ||
var node = new nodes.Code(tok.val, tok.buffer, tok.escape); | ||
var block; | ||
var i = 1; | ||
node.line = this.line(); | ||
while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i; | ||
block = 'indent' == this.lookahead(i).type; | ||
// throw an error if an else does not have an if | ||
if (tok.isElse && !tok.hasIf) { | ||
throw new Error('Unexpected else without if'); | ||
} | ||
// handle block | ||
block = 'indent' == this.peek().type; | ||
if (block) { | ||
this.skip(i-1); | ||
node.block = this.block(); | ||
} | ||
// handle missing block | ||
if (tok.requiresBlock && !block) { | ||
node.block = new nodes.Block(); | ||
} | ||
// mark presense of if for future elses | ||
if (tok.isIf && this.peek().isElse) { | ||
this.peek().hasIf = true; | ||
} else if (tok.isIf && this.peek().type === 'newline' && this.lookahead(2).isElse) { | ||
this.lookahead(2).hasIf = true; | ||
} | ||
return node; | ||
@@ -743,6 +772,6 @@ }, | ||
} | ||
if (tag.selfClosing | ||
&& ['newline', 'outdent', 'eos'].indexOf(this.peek().type) === -1 | ||
&& (this.peek().type !== 'text' || /^\s*$/.text(this.peek().val))) { | ||
&& (this.peek().type !== 'text' || !/^\s*$/.test(this.peek().val))) { | ||
throw new Error(name + ' is self closing and should not have content.'); | ||
@@ -784,8 +813,4 @@ } | ||
var block = this.block(); | ||
if (tag.block) { | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
tag.block.push(block.nodes[i]); | ||
} | ||
} else { | ||
tag.block = block; | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
tag.block.push(block.nodes[i]); | ||
} | ||
@@ -792,0 +817,0 @@ } |
{ | ||
"name": "jade", | ||
"description": "Jade template engine", | ||
"version": "1.1.5", | ||
"version": "1.2.0", | ||
"author": "TJ Holowaychuk <tj@vision-media.ca>", | ||
"license": "MIT", | ||
"repository": "git://github.com/visionmedia/jade", | ||
@@ -19,3 +20,3 @@ "main": "./index.js", | ||
"with": "~2.0.0", | ||
"constantinople": "~1.0.2" | ||
"constantinople": "~2.0.0" | ||
}, | ||
@@ -25,2 +26,3 @@ "devDependencies": { | ||
"mocha": "*", | ||
"istanbul": "*", | ||
"markdown": "*", | ||
@@ -40,3 +42,4 @@ "stylus": "*", | ||
"scripts": { | ||
"test": "mocha -R spec", | ||
"test": "mocha -R spec && npm run coverage", | ||
"coverage": "istanbul cover node_modules/mocha/bin/_mocha", | ||
"prepublish": "npm prune && linify transform bin && npm run build", | ||
@@ -43,0 +46,0 @@ "build": "npm run compile", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
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
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent 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
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
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent 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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
939305
24137
23
16
10
+ Addedconstantinople@2.0.1(transitive)
- Removedconstantinople@1.0.2(transitive)
Updatedconstantinople@~2.0.0