928ba56b95
Rather than keeping state in the AST, which requires some gymnastics, we create a separate visitor flow which does the top down iteration necessary to calculate all of the state needed for proper whitespace control evaluation.
115 lines
3.1 KiB
JavaScript
115 lines
3.1 KiB
JavaScript
import Exception from "../exception";
|
|
|
|
export function SourceLocation(source, locInfo) {
|
|
this.source = source;
|
|
this.start = {
|
|
line: locInfo.first_line,
|
|
column: locInfo.first_column
|
|
};
|
|
this.end = {
|
|
line: locInfo.last_line,
|
|
column: locInfo.last_column
|
|
};
|
|
}
|
|
|
|
export function stripFlags(open, close) {
|
|
return {
|
|
open: open.charAt(2) === '~',
|
|
close: close.charAt(close.length-3) === '~'
|
|
};
|
|
}
|
|
|
|
export function stripComment(comment) {
|
|
return comment.replace(/^\{\{~?\!-?-?/, '')
|
|
.replace(/-?-?~?\}\}$/, '');
|
|
}
|
|
|
|
export function preparePath(data, parts, locInfo) {
|
|
/*jshint -W040 */
|
|
locInfo = this.locInfo(locInfo);
|
|
|
|
var original = data ? '@' : '',
|
|
dig = [],
|
|
depth = 0,
|
|
depthString = '';
|
|
|
|
for(var i=0,l=parts.length; i<l; i++) {
|
|
var part = parts[i].part;
|
|
original += (parts[i].separator || '') + part;
|
|
|
|
if (part === '..' || part === '.' || part === 'this') {
|
|
if (dig.length > 0) {
|
|
throw new Exception('Invalid path: ' + original, {loc: locInfo});
|
|
} else if (part === '..') {
|
|
depth++;
|
|
depthString += '../';
|
|
}
|
|
} else {
|
|
dig.push(part);
|
|
}
|
|
}
|
|
|
|
return new this.PathExpression(data, depth, dig, original, locInfo);
|
|
}
|
|
|
|
export function prepareMustache(sexpr, open, strip, locInfo) {
|
|
/*jshint -W040 */
|
|
// Must use charAt to support IE pre-10
|
|
var escapeFlag = open.charAt(3) || open.charAt(2),
|
|
escaped = escapeFlag !== '{' && escapeFlag !== '&';
|
|
|
|
return new this.MustacheStatement(sexpr, escaped, strip, this.locInfo(locInfo));
|
|
}
|
|
|
|
export function prepareRawBlock(openRawBlock, content, close, locInfo) {
|
|
/*jshint -W040 */
|
|
if (openRawBlock.sexpr.path.original !== close) {
|
|
var errorNode = {loc: openRawBlock.sexpr.loc};
|
|
|
|
throw new Exception(openRawBlock.sexpr.path.original + " doesn't match " + close, errorNode);
|
|
}
|
|
|
|
locInfo = this.locInfo(locInfo);
|
|
var program = new this.Program([content], null, {}, locInfo);
|
|
|
|
return new this.BlockStatement(
|
|
openRawBlock.sexpr, program, undefined,
|
|
{}, {}, {},
|
|
locInfo);
|
|
}
|
|
|
|
export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
|
|
/*jshint -W040 */
|
|
// When we are chaining inverse calls, we will not have a close path
|
|
if (close && close.path && openBlock.sexpr.path.original !== close.path.original) {
|
|
var errorNode = {loc: openBlock.sexpr.loc};
|
|
|
|
throw new Exception(openBlock.sexpr.path.original + ' doesn\'t match ' + close.path.original, errorNode);
|
|
}
|
|
|
|
program.blockParams = openBlock.blockParams;
|
|
|
|
var inverse,
|
|
inverseStrip;
|
|
|
|
if (inverseAndProgram) {
|
|
if (inverseAndProgram.chain) {
|
|
inverseAndProgram.program.body[0].closeStrip = close.strip || close.openStrip;
|
|
}
|
|
|
|
inverseStrip = inverseAndProgram.strip;
|
|
inverse = inverseAndProgram.program;
|
|
}
|
|
|
|
if (inverted) {
|
|
inverted = inverse;
|
|
inverse = program;
|
|
program = inverted;
|
|
}
|
|
|
|
return new this.BlockStatement(
|
|
openBlock.sexpr, program, inverse,
|
|
openBlock.strip, inverseStrip, close && (close.strip || close.openStrip),
|
|
this.locInfo(locInfo));
|
|
}
|