diff --git a/lib/compiler.js b/lib/compiler.js index 9b57ed25a..a669c0641 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -122,7 +122,7 @@ Compiler.prototype = { } } - str = JSON.stringify(str); + str = utils.stringify(str); str = str.substr(1, str.length - 2); if (this.lastBufferedIdx == this.buf.length) { @@ -194,7 +194,7 @@ Compiler.prototype = { if (debug) { this.buf.push('jade_debug.unshift({ lineno: ' + node.line + ', filename: ' + (node.filename - ? JSON.stringify(node.filename) + ? utils.stringify(node.filename) : 'jade_debug[0].filename') + ' });'); } @@ -631,7 +631,7 @@ Compiler.prototype = { var val = this.attrs(attrs); attributeBlocks.unshift(val); } - this.bufferExpression('jade.attrs(jade.merge([' + attributeBlocks.join(',') + ']), ' + JSON.stringify(this.terse) + ')'); + this.bufferExpression('jade.attrs(jade.merge([' + attributeBlocks.join(',') + ']), ' + utils.stringify(this.terse) + ')'); } else if (attrs.length) { this.attrs(attrs, true); } @@ -661,11 +661,11 @@ Compiler.prototype = { if (escaped && !(key.indexOf('data') === 0 && typeof val !== 'string')) { val = runtime.escape(val); } - buf.push(JSON.stringify(key) + ': ' + JSON.stringify(val)); + buf.push(utils.stringify(key) + ': ' + utils.stringify(val)); } } else { if (buffer) { - this.bufferExpression('jade.attr("' + key + '", ' + attr.val + ', ' + JSON.stringify(escaped) + ', ' + JSON.stringify(this.terse) + ')'); + this.bufferExpression('jade.attr("' + key + '", ' + attr.val + ', ' + utils.stringify(escaped) + ', ' + utils.stringify(this.terse) + ')'); } else { var val = attr.val; if (escaped && !(key.indexOf('data') === 0)) { @@ -673,7 +673,7 @@ Compiler.prototype = { } else if (escaped) { val = '(typeof (jade_interp = ' + val + ') == "string" ? jade.escape(jade_interp) : jade_interp)'; } - buf.push(JSON.stringify(key) + ': ' + val); + buf.push(utils.stringify(key) + ': ' + val); } } }.bind(this)); @@ -681,15 +681,15 @@ Compiler.prototype = { if (classes.every(isConstant)) { this.buffer(runtime.cls(classes.map(toConstant), classEscaping)); } else { - this.bufferExpression('jade.cls([' + classes.join(',') + '], ' + JSON.stringify(classEscaping) + ')'); + this.bufferExpression('jade.cls([' + classes.join(',') + '], ' + utils.stringify(classEscaping) + ')'); } } else if (classes.length) { if (classes.every(isConstant)) { - classes = JSON.stringify(runtime.joinClasses(classes.map(toConstant).map(runtime.joinClasses).map(function (cls, i) { + classes = utils.stringify(runtime.joinClasses(classes.map(toConstant).map(runtime.joinClasses).map(function (cls, i) { return classEscaping[i] ? runtime.escape(cls) : cls; }))); } else { - classes = '(jade_interp = ' + JSON.stringify(classEscaping) + ',' + + classes = '(jade_interp = ' + utils.stringify(classEscaping) + ',' + ' jade.joinClasses([' + classes.join(',') + '].map(jade.joinClasses).map(function (cls, i) {' + ' return jade_interp[i] ? jade.escape(cls) : cls' + ' }))' + diff --git a/lib/jade.js b/lib/jade.js index 64b1f5535..72bdfb98c 100644 --- a/lib/jade.js +++ b/lib/jade.js @@ -15,7 +15,8 @@ var Parser = require('./parser') , Compiler = require('./compiler') , runtime = require('./runtime') , addWith = require('with') - , fs = require('fs'); + , fs = require('fs') + , utils = require('./utils'); /** * Expose self closing tags. @@ -152,7 +153,7 @@ function parse(str, options){ exports.compile = function(str, options){ var options = options || {} , filename = options.filename - ? JSON.stringify(options.filename) + ? utils.stringify(options.filename) : 'undefined' , fn; @@ -165,7 +166,7 @@ exports.compile = function(str, options){ , 'try {' , parsed.body , '} catch (err) {' - , ' jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno' + (options.compileDebug === true ? ',' + JSON.stringify(str) : '') + ');' + , ' jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno' + (options.compileDebug === true ? ',' + utils.stringify(str) : '') + ');' , '}' ].join('\n'); } else { @@ -204,7 +205,7 @@ exports.compile = function(str, options){ exports.compileClient = function(str, options){ var options = options || {}; var name = options.name || 'template'; - var filename = options.filename ? JSON.stringify(options.filename) : 'undefined'; + var filename = options.filename ? utils.stringify(options.filename) : 'undefined'; var fn; str = String(str); @@ -216,7 +217,7 @@ exports.compileClient = function(str, options){ , 'try {' , parse(str, options).body , '} catch (err) {' - , ' jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno, ' + JSON.stringify(str) + ');' + , ' jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno, ' + utils.stringify(str) + ');' , '}' ].join('\n'); } else { diff --git a/lib/lexer.js b/lib/lexer.js index 1e8b9d7eb..f4b3551ed 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -402,7 +402,7 @@ Lexer.prototype = { var filter = captures[1]; var attrs = captures[2] === '(' ? this.attrs() : null; if (!(captures[2] === ' ' || this.input[0] === ' ')) { - throw new Error('expected space after include:filter but got ' + JSON.stringify(this.input[0])); + throw new Error('expected space after include:filter but got ' + utils.stringify(this.input[0])); } captures = /^ *([^\n]+)/.exec(this.input); if (!captures || captures[1].trim() === '') { diff --git a/lib/utils.js b/lib/utils.js index 813a55aba..ad5f01654 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -14,6 +14,12 @@ exports.merge = function(a, b) { return a; }; +exports.stringify = function(str) { + return JSON.stringify(str) + .replace(/\u2028/g, '\\u2028') + .replace(/\u2029/g, '\\u2029'); +}; + exports.walkAST = function walkAST(ast, before, after) { before && before(ast); switch (ast.type) { diff --git a/test/jade.test.js b/test/jade.test.js index db88758e5..a7016ad55 100644 --- a/test/jade.test.js +++ b/test/jade.test.js @@ -1058,5 +1058,9 @@ describe('jade', function(){ path.resolve(__dirname + '/dependencies/dependency3.jade') ],info.dependencies); }); + it('should not fail on js newlines', function(){ + assert.equal("

foo\u2028bar

", jade.render("p foo\u2028bar")); + assert.equal("

foo\u2029bar

", jade.render("p foo\u2029bar")); + }); }); });