Add support to v1 to connect to GHES (#69)

* Bumping actions/github to 2.2.0 for GHES

* Husky commit correct node modules
This commit is contained in:
PJ Quirk authored and GitHub committed 2020-05-15 15:25:57 -04:00
1 parent c201d45ef4
commit 0649bd8119
7893 files changed
+2232806 -48998

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
import runtime from './handlebars.runtime';
// Compiler imports
import AST from './handlebars/compiler/ast';
import { parser as Parser, parse } from './handlebars/compiler/base';
import { Compiler, compile, precompile } from './handlebars/compiler/compiler';
import JavaScriptCompiler from './handlebars/compiler/javascript-compiler';
import Visitor from './handlebars/compiler/visitor';
import noConflict from './handlebars/no-conflict';
let _create = runtime.create;
function create() {
let hb = _create();
hb.compile = function(input, options) {
return compile(input, options, hb);
};
hb.precompile = function(input, options) {
return precompile(input, options, hb);
};
hb.AST = AST;
hb.Compiler = Compiler;
hb.JavaScriptCompiler = JavaScriptCompiler;
hb.Parser = Parser;
hb.parse = parse;
return hb;
}
let inst = create();
inst.create = create;
noConflict(inst);
inst.Visitor = Visitor;
inst['default'] = inst;
export default inst;
+37
View File
@@ -0,0 +1,37 @@
import * as base from './handlebars/base';
// Each of these augment the Handlebars object. No need to setup here.
// (This is done to easily share code between commonjs and browse envs)
import SafeString from './handlebars/safe-string';
import Exception from './handlebars/exception';
import * as Utils from './handlebars/utils';
import * as runtime from './handlebars/runtime';
import noConflict from './handlebars/no-conflict';
// For compatibility and usage outside of module systems, make the Handlebars object a namespace
function create() {
let hb = new base.HandlebarsEnvironment();
Utils.extend(hb, base);
hb.SafeString = SafeString;
hb.Exception = Exception;
hb.Utils = Utils;
hb.escapeExpression = Utils.escapeExpression;
hb.VM = runtime;
hb.template = function(spec) {
return runtime.template(spec, hb);
};
return hb;
}
let inst = create();
inst.create = create;
noConflict(inst);
inst['default'] = inst;
export default inst;
+78
View File
@@ -0,0 +1,78 @@
import {createFrame, extend, toString} from './utils';
import Exception from './exception';
import {registerDefaultHelpers} from './helpers';
import {registerDefaultDecorators} from './decorators';
import logger from './logger';
export const VERSION = '4.1.2';
export const COMPILER_REVISION = 7;
export const REVISION_CHANGES = {
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
2: '== 1.0.0-rc.3',
3: '== 1.0.0-rc.4',
4: '== 1.x.x',
5: '== 2.0.0-alpha.x',
6: '>= 2.0.0-beta.1',
7: '>= 4.0.0'
};
const objectType = '[object Object]';
export function HandlebarsEnvironment(helpers, partials, decorators) {
this.helpers = helpers || {};
this.partials = partials || {};
this.decorators = decorators || {};
registerDefaultHelpers(this);
registerDefaultDecorators(this);
}
HandlebarsEnvironment.prototype = {
constructor: HandlebarsEnvironment,
logger: logger,
log: logger.log,
registerHelper: function(name, fn) {
if (toString.call(name) === objectType) {
if (fn) { throw new Exception('Arg not supported with multiple helpers'); }
extend(this.helpers, name);
} else {
this.helpers[name] = fn;
}
},
unregisterHelper: function(name) {
delete this.helpers[name];
},
registerPartial: function(name, partial) {
if (toString.call(name) === objectType) {
extend(this.partials, name);
} else {
if (typeof partial === 'undefined') {
throw new Exception(`Attempting to register a partial called "${name}" as undefined`);
}
this.partials[name] = partial;
}
},
unregisterPartial: function(name) {
delete this.partials[name];
},
registerDecorator: function(name, fn) {
if (toString.call(name) === objectType) {
if (fn) { throw new Exception('Arg not supported with multiple decorators'); }
extend(this.decorators, name);
} else {
this.decorators[name] = fn;
}
},
unregisterDecorator: function(name) {
delete this.decorators[name];
}
};
export let log = logger.log;
export {createFrame, logger};
+28
View File
@@ -0,0 +1,28 @@
let AST = {
// Public API used to evaluate derived attributes regarding AST nodes
helpers: {
// a mustache is definitely a helper if:
// * it is an eligible helper, and
// * it has at least one parameter or hash segment
helperExpression: function(node) {
return (node.type === 'SubExpression')
|| ((node.type === 'MustacheStatement' || node.type === 'BlockStatement')
&& !!((node.params && node.params.length) || node.hash));
},
scopedId: function(path) {
return (/^\.|this\b/).test(path.original);
},
// an ID is simple if it only has one part, and that part is not
// `..` or `this`.
simpleId: function(path) {
return path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth;
}
}
};
// Must be exported as an object rather than the root of the module as the jison lexer
// must modify the object to operate properly.
export default AST;
+24
View File
@@ -0,0 +1,24 @@
import parser from './parser';
import WhitespaceControl from './whitespace-control';
import * as Helpers from './helpers';
import { extend } from '../utils';
export { parser };
let yy = {};
extend(yy, Helpers);
export function parse(input, options) {
// Just return if an already-compiled AST was passed in.
if (input.type === 'Program') { return input; }
parser.yy = yy;
// Altering the shared object here, but this is ok as parser is a sync operation
yy.locInfo = function(locInfo) {
return new yy.SourceLocation(options && options.srcName, locInfo);
};
let strip = new WhitespaceControl(options);
return strip.accept(parser.parse(input));
}
+168
View File
@@ -0,0 +1,168 @@
/* global define */
import {isArray} from '../utils';
let SourceNode;
try {
/* istanbul ignore next */
if (typeof define !== 'function' || !define.amd) {
// We don't support this in AMD environments. For these environments, we asusme that
// they are running on the browser and thus have no need for the source-map library.
let SourceMap = require('source-map');
SourceNode = SourceMap.SourceNode;
}
} catch (err) {
/* NOP */
}
/* istanbul ignore if: tested but not covered in istanbul due to dist build */
if (!SourceNode) {
SourceNode = function(line, column, srcFile, chunks) {
this.src = '';
if (chunks) {
this.add(chunks);
}
};
/* istanbul ignore next */
SourceNode.prototype = {
add: function(chunks) {
if (isArray(chunks)) {
chunks = chunks.join('');
}
this.src += chunks;
},
prepend: function(chunks) {
if (isArray(chunks)) {
chunks = chunks.join('');
}
this.src = chunks + this.src;
},
toStringWithSourceMap: function() {
return {code: this.toString()};
},
toString: function() {
return this.src;
}
};
}
function castChunk(chunk, codeGen, loc) {
if (isArray(chunk)) {
let ret = [];
for (let i = 0, len = chunk.length; i < len; i++) {
ret.push(codeGen.wrap(chunk[i], loc));
}
return ret;
} else if (typeof chunk === 'boolean' || typeof chunk === 'number') {
// Handle primitives that the SourceNode will throw up on
return chunk + '';
}
return chunk;
}
function CodeGen(srcFile) {
this.srcFile = srcFile;
this.source = [];
}
CodeGen.prototype = {
isEmpty() {
return !this.source.length;
},
prepend: function(source, loc) {
this.source.unshift(this.wrap(source, loc));
},
push: function(source, loc) {
this.source.push(this.wrap(source, loc));
},
merge: function() {
let source = this.empty();
this.each(function(line) {
source.add([' ', line, '\n']);
});
return source;
},
each: function(iter) {
for (let i = 0, len = this.source.length; i < len; i++) {
iter(this.source[i]);
}
},
empty: function() {
let loc = this.currentLocation || {start: {}};
return new SourceNode(loc.start.line, loc.start.column, this.srcFile);
},
wrap: function(chunk, loc = this.currentLocation || {start: {}}) {
if (chunk instanceof SourceNode) {
return chunk;
}
chunk = castChunk(chunk, this, loc);
return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk);
},
functionCall: function(fn, type, params) {
params = this.generateList(params);
return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']);
},
quotedString: function(str) {
return '"' + (str + '')
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
.replace(/\u2029/g, '\\u2029') + '"';
},
objectLiteral: function(obj) {
let pairs = [];
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
let value = castChunk(obj[key], this);
if (value !== 'undefined') {
pairs.push([this.quotedString(key), ':', value]);
}
}
}
let ret = this.generateList(pairs);
ret.prepend('{');
ret.add('}');
return ret;
},
generateList: function(entries) {
let ret = this.empty();
for (let i = 0, len = entries.length; i < len; i++) {
if (i) {
ret.add(',');
}
ret.add(castChunk(entries[i], this));
}
return ret;
},
generateArray: function(entries) {
let ret = this.generateList(entries);
ret.prepend('[');
ret.add(']');
return ret;
}
};
export default CodeGen;
+559
View File
@@ -0,0 +1,559 @@
/* eslint-disable new-cap */
import Exception from '../exception';
import {isArray, indexOf, extend} from '../utils';
import AST from './ast';
const slice = [].slice;
export function Compiler() {}
// the foundHelper register will disambiguate helper lookup from finding a
// function in a context. This is necessary for mustache compatibility, which
// requires that context functions in blocks are evaluated by blockHelperMissing,
// and then proceed as if the resulting value was provided to blockHelperMissing.
Compiler.prototype = {
compiler: Compiler,
equals: function(other) {
let len = this.opcodes.length;
if (other.opcodes.length !== len) {
return false;
}
for (let i = 0; i < len; i++) {
let opcode = this.opcodes[i],
otherOpcode = other.opcodes[i];
if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) {
return false;
}
}
// We know that length is the same between the two arrays because they are directly tied
// to the opcode behavior above.
len = this.children.length;
for (let i = 0; i < len; i++) {
if (!this.children[i].equals(other.children[i])) {
return false;
}
}
return true;
},
guid: 0,
compile: function(program, options) {
this.sourceNode = [];
this.opcodes = [];
this.children = [];
this.options = options;
this.stringParams = options.stringParams;
this.trackIds = options.trackIds;
options.blockParams = options.blockParams || [];
// These changes will propagate to the other compiler components
let knownHelpers = options.knownHelpers;
options.knownHelpers = {
'helperMissing': true,
'blockHelperMissing': true,
'each': true,
'if': true,
'unless': true,
'with': true,
'log': true,
'lookup': true
};
if (knownHelpers) {
// the next line should use "Object.keys", but the code has been like this a long time and changing it, might
// cause backwards-compatibility issues... It's an old library...
// eslint-disable-next-line guard-for-in
for (let name in knownHelpers) {
this.options.knownHelpers[name] = knownHelpers[name];
}
}
return this.accept(program);
},
compileProgram: function(program) {
let childCompiler = new this.compiler(), // eslint-disable-line new-cap
result = childCompiler.compile(program, this.options),
guid = this.guid++;
this.usePartial = this.usePartial || result.usePartial;
this.children[guid] = result;
this.useDepths = this.useDepths || result.useDepths;
return guid;
},
accept: function(node) {
/* istanbul ignore next: Sanity code */
if (!this[node.type]) {
throw new Exception('Unknown type: ' + node.type, node);
}
this.sourceNode.unshift(node);
let ret = this[node.type](node);
this.sourceNode.shift();
return ret;
},
Program: function(program) {
this.options.blockParams.unshift(program.blockParams);
let body = program.body,
bodyLength = body.length;
for (let i = 0; i < bodyLength; i++) {
this.accept(body[i]);
}
this.options.blockParams.shift();
this.isSimple = bodyLength === 1;
this.blockParams = program.blockParams ? program.blockParams.length : 0;
return this;
},
BlockStatement: function(block) {
transformLiteralToPath(block);
let program = block.program,
inverse = block.inverse;
program = program && this.compileProgram(program);
inverse = inverse && this.compileProgram(inverse);
let type = this.classifySexpr(block);
if (type === 'helper') {
this.helperSexpr(block, program, inverse);
} else if (type === 'simple') {
this.simpleSexpr(block);
// now that the simple mustache is resolved, we need to
// evaluate it by executing `blockHelperMissing`
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
this.opcode('emptyHash');
this.opcode('blockValue', block.path.original);
} else {
this.ambiguousSexpr(block, program, inverse);
// now that the simple mustache is resolved, we need to
// evaluate it by executing `blockHelperMissing`
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
this.opcode('emptyHash');
this.opcode('ambiguousBlockValue');
}
this.opcode('append');
},
DecoratorBlock(decorator) {
let program = decorator.program && this.compileProgram(decorator.program);
let params = this.setupFullMustacheParams(decorator, program, undefined),
path = decorator.path;
this.useDecorators = true;
this.opcode('registerDecorator', params.length, path.original);
},
PartialStatement: function(partial) {
this.usePartial = true;
let program = partial.program;
if (program) {
program = this.compileProgram(partial.program);
}
let params = partial.params;
if (params.length > 1) {
throw new Exception('Unsupported number of partial arguments: ' + params.length, partial);
} else if (!params.length) {
if (this.options.explicitPartialContext) {
this.opcode('pushLiteral', 'undefined');
} else {
params.push({type: 'PathExpression', parts: [], depth: 0});
}
}
let partialName = partial.name.original,
isDynamic = partial.name.type === 'SubExpression';
if (isDynamic) {
this.accept(partial.name);
}
this.setupFullMustacheParams(partial, program, undefined, true);
let indent = partial.indent || '';
if (this.options.preventIndent && indent) {
this.opcode('appendContent', indent);
indent = '';
}
this.opcode('invokePartial', isDynamic, partialName, indent);
this.opcode('append');
},
PartialBlockStatement: function(partialBlock) {
this.PartialStatement(partialBlock);
},
MustacheStatement: function(mustache) {
this.SubExpression(mustache);
if (mustache.escaped && !this.options.noEscape) {
this.opcode('appendEscaped');
} else {
this.opcode('append');
}
},
Decorator(decorator) {
this.DecoratorBlock(decorator);
},
ContentStatement: function(content) {
if (content.value) {
this.opcode('appendContent', content.value);
}
},
CommentStatement: function() {},
SubExpression: function(sexpr) {
transformLiteralToPath(sexpr);
let type = this.classifySexpr(sexpr);
if (type === 'simple') {
this.simpleSexpr(sexpr);
} else if (type === 'helper') {
this.helperSexpr(sexpr);
} else {
this.ambiguousSexpr(sexpr);
}
},
ambiguousSexpr: function(sexpr, program, inverse) {
let path = sexpr.path,
name = path.parts[0],
isBlock = program != null || inverse != null;
this.opcode('getContext', path.depth);
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
path.strict = true;
this.accept(path);
this.opcode('invokeAmbiguous', name, isBlock);
},
simpleSexpr: function(sexpr) {
let path = sexpr.path;
path.strict = true;
this.accept(path);
this.opcode('resolvePossibleLambda');
},
helperSexpr: function(sexpr, program, inverse) {
let params = this.setupFullMustacheParams(sexpr, program, inverse),
path = sexpr.path,
name = path.parts[0];
if (this.options.knownHelpers[name]) {
this.opcode('invokeKnownHelper', params.length, name);
} else if (this.options.knownHelpersOnly) {
throw new Exception('You specified knownHelpersOnly, but used the unknown helper ' + name, sexpr);
} else {
path.strict = true;
path.falsy = true;
this.accept(path);
this.opcode('invokeHelper', params.length, path.original, AST.helpers.simpleId(path));
}
},
PathExpression: function(path) {
this.addDepth(path.depth);
this.opcode('getContext', path.depth);
let name = path.parts[0],
scoped = AST.helpers.scopedId(path),
blockParamId = !path.depth && !scoped && this.blockParamIndex(name);
if (blockParamId) {
this.opcode('lookupBlockParam', blockParamId, path.parts);
} else if (!name) {
// Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
this.opcode('pushContext');
} else if (path.data) {
this.options.data = true;
this.opcode('lookupData', path.depth, path.parts, path.strict);
} else {
this.opcode('lookupOnContext', path.parts, path.falsy, path.strict, scoped);
}
},
StringLiteral: function(string) {
this.opcode('pushString', string.value);
},
NumberLiteral: function(number) {
this.opcode('pushLiteral', number.value);
},
BooleanLiteral: function(bool) {
this.opcode('pushLiteral', bool.value);
},
UndefinedLiteral: function() {
this.opcode('pushLiteral', 'undefined');
},
NullLiteral: function() {
this.opcode('pushLiteral', 'null');
},
Hash: function(hash) {
let pairs = hash.pairs,
i = 0,
l = pairs.length;
this.opcode('pushHash');
for (; i < l; i++) {
this.pushParam(pairs[i].value);
}
while (i--) {
this.opcode('assignToHash', pairs[i].key);
}
this.opcode('popHash');
},
// HELPERS
opcode: function(name) {
this.opcodes.push({ opcode: name, args: slice.call(arguments, 1), loc: this.sourceNode[0].loc });
},
addDepth: function(depth) {
if (!depth) {
return;
}
this.useDepths = true;
},
classifySexpr: function(sexpr) {
let isSimple = AST.helpers.simpleId(sexpr.path);
let isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]);
// a mustache is an eligible helper if:
// * its id is simple (a single part, not `this` or `..`)
let isHelper = !isBlockParam && AST.helpers.helperExpression(sexpr);
// if a mustache is an eligible helper but not a definite
// helper, it is ambiguous, and will be resolved in a later
// pass or at runtime.
let isEligible = !isBlockParam && (isHelper || isSimple);
// if ambiguous, we can possibly resolve the ambiguity now
// An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc.
if (isEligible && !isHelper) {
let name = sexpr.path.parts[0],
options = this.options;
if (options.knownHelpers[name]) {
isHelper = true;
} else if (options.knownHelpersOnly) {
isEligible = false;
}
}
if (isHelper) {
return 'helper';
} else if (isEligible) {
return 'ambiguous';
} else {
return 'simple';
}
},
pushParams: function(params) {
for (let i = 0, l = params.length; i < l; i++) {
this.pushParam(params[i]);
}
},
pushParam: function(val) {
let value = val.value != null ? val.value : val.original || '';
if (this.stringParams) {
if (value.replace) {
value = value
.replace(/^(\.?\.\/)*/g, '')
.replace(/\//g, '.');
}
if (val.depth) {
this.addDepth(val.depth);
}
this.opcode('getContext', val.depth || 0);
this.opcode('pushStringParam', value, val.type);
if (val.type === 'SubExpression') {
// SubExpressions get evaluated and passed in
// in string params mode.
this.accept(val);
}
} else {
if (this.trackIds) {
let blockParamIndex;
if (val.parts && !AST.helpers.scopedId(val) && !val.depth) {
blockParamIndex = this.blockParamIndex(val.parts[0]);
}
if (blockParamIndex) {
let blockParamChild = val.parts.slice(1).join('.');
this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild);
} else {
value = val.original || value;
if (value.replace) {
value = value
.replace(/^this(?:\.|$)/, '')
.replace(/^\.\//, '')
.replace(/^\.$/, '');
}
this.opcode('pushId', val.type, value);
}
}
this.accept(val);
}
},
setupFullMustacheParams: function(sexpr, program, inverse, omitEmpty) {
let params = sexpr.params;
this.pushParams(params);
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
if (sexpr.hash) {
this.accept(sexpr.hash);
} else {
this.opcode('emptyHash', omitEmpty);
}
return params;
},
blockParamIndex: function(name) {
for (let depth = 0, len = this.options.blockParams.length; depth < len; depth++) {
let blockParams = this.options.blockParams[depth],
param = blockParams && indexOf(blockParams, name);
if (blockParams && param >= 0) {
return [depth, param];
}
}
}
};
export function precompile(input, options, env) {
if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {
throw new Exception('You must pass a string or Handlebars AST to Handlebars.precompile. You passed ' + input);
}
options = options || {};
if (!('data' in options)) {
options.data = true;
}
if (options.compat) {
options.useDepths = true;
}
let ast = env.parse(input, options),
environment = new env.Compiler().compile(ast, options);
return new env.JavaScriptCompiler().compile(environment, options);
}
export function compile(input, options = {}, env) {
if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {
throw new Exception('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input);
}
options = extend({}, options);
if (!('data' in options)) {
options.data = true;
}
if (options.compat) {
options.useDepths = true;
}
let compiled;
function compileInput() {
let ast = env.parse(input, options),
environment = new env.Compiler().compile(ast, options),
templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true);
return env.template(templateSpec);
}
// Template is only compiled on first use and cached after that point.
function ret(context, execOptions) {
if (!compiled) {
compiled = compileInput();
}
return compiled.call(this, context, execOptions);
}
ret._setup = function(setupOptions) {
if (!compiled) {
compiled = compileInput();
}
return compiled._setup(setupOptions);
};
ret._child = function(i, data, blockParams, depths) {
if (!compiled) {
compiled = compileInput();
}
return compiled._child(i, data, blockParams, depths);
};
return ret;
}
function argEquals(a, b) {
if (a === b) {
return true;
}
if (isArray(a) && isArray(b) && a.length === b.length) {
for (let i = 0; i < a.length; i++) {
if (!argEquals(a[i], b[i])) {
return false;
}
}
return true;
}
}
function transformLiteralToPath(sexpr) {
if (!sexpr.path.parts) {
let literal = sexpr.path;
// Casting to string here to make false and 0 literal values play nicely with the rest
// of the system.
sexpr.path = {
type: 'PathExpression',
data: false,
depth: 0,
parts: [literal.original + ''],
original: literal.original + '',
loc: literal.loc
};
}
}
+210
View File
@@ -0,0 +1,210 @@
import Exception from '../exception';
function validateClose(open, close) {
close = close.path ? close.path.original : close;
if (open.path.original !== close) {
let errorNode = {loc: open.path.loc};
throw new Exception(open.path.original + " doesn't match " + close, errorNode);
}
}
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 id(token) {
if (/^\[.*\]$/.test(token)) {
return token.substring(1, token.length - 1);
} else {
return token;
}
}
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, loc) {
loc = this.locInfo(loc);
let original = data ? '@' : '',
dig = [],
depth = 0;
for (let i = 0, l = parts.length; i < l; i++) {
let part = parts[i].part,
// If we have [] syntax then we do not treat path references as operators,
// i.e. foo.[this] resolves to approximately context.foo['this']
isLiteral = parts[i].original !== part;
original += (parts[i].separator || '') + part;
if (!isLiteral && (part === '..' || part === '.' || part === 'this')) {
if (dig.length > 0) {
throw new Exception('Invalid path: ' + original, {loc});
} else if (part === '..') {
depth++;
}
} else {
dig.push(part);
}
}
return {
type: 'PathExpression',
data,
depth,
parts: dig,
original,
loc
};
}
export function prepareMustache(path, params, hash, open, strip, locInfo) {
// Must use charAt to support IE pre-10
let escapeFlag = open.charAt(3) || open.charAt(2),
escaped = escapeFlag !== '{' && escapeFlag !== '&';
let decorator = (/\*/.test(open));
return {
type: decorator ? 'Decorator' : 'MustacheStatement',
path,
params,
hash,
escaped,
strip,
loc: this.locInfo(locInfo)
};
}
export function prepareRawBlock(openRawBlock, contents, close, locInfo) {
validateClose(openRawBlock, close);
locInfo = this.locInfo(locInfo);
let program = {
type: 'Program',
body: contents,
strip: {},
loc: locInfo
};
return {
type: 'BlockStatement',
path: openRawBlock.path,
params: openRawBlock.params,
hash: openRawBlock.hash,
program,
openStrip: {},
inverseStrip: {},
closeStrip: {},
loc: locInfo
};
}
export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
if (close && close.path) {
validateClose(openBlock, close);
}
let decorator = (/\*/.test(openBlock.open));
program.blockParams = openBlock.blockParams;
let inverse,
inverseStrip;
if (inverseAndProgram) {
if (decorator) {
throw new Exception('Unexpected inverse block on decorator', inverseAndProgram);
}
if (inverseAndProgram.chain) {
inverseAndProgram.program.body[0].closeStrip = close.strip;
}
inverseStrip = inverseAndProgram.strip;
inverse = inverseAndProgram.program;
}
if (inverted) {
inverted = inverse;
inverse = program;
program = inverted;
}
return {
type: decorator ? 'DecoratorBlock' : 'BlockStatement',
path: openBlock.path,
params: openBlock.params,
hash: openBlock.hash,
program,
inverse,
openStrip: openBlock.strip,
inverseStrip,
closeStrip: close && close.strip,
loc: this.locInfo(locInfo)
};
}
export function prepareProgram(statements, loc) {
if (!loc && statements.length) {
const firstLoc = statements[0].loc,
lastLoc = statements[statements.length - 1].loc;
/* istanbul ignore else */
if (firstLoc && lastLoc) {
loc = {
source: firstLoc.source,
start: {
line: firstLoc.start.line,
column: firstLoc.start.column
},
end: {
line: lastLoc.end.line,
column: lastLoc.end.column
}
};
}
}
return {
type: 'Program',
body: statements,
strip: {},
loc: loc
};
}
export function preparePartialBlock(open, program, close, locInfo) {
validateClose(open, close);
return {
type: 'PartialBlockStatement',
name: open.path,
params: open.params,
hash: open.hash,
program,
openStrip: open.strip,
closeStrip: close && close.strip,
loc: this.locInfo(locInfo)
};
}
File diff suppressed because it is too large. Load diff
+622
View File
@@ -0,0 +1,622 @@
// File ignored in coverage tests via setting in .istanbul.yml
/* Jison generated parser */
var handlebars = (function(){
var parser = {trace: function trace () { },
yy: {},
symbols_: {"error":2,"root":3,"program":4,"EOF":5,"program_repetition0":6,"statement":7,"mustache":8,"block":9,"rawBlock":10,"partial":11,"partialBlock":12,"content":13,"COMMENT":14,"CONTENT":15,"openRawBlock":16,"rawBlock_repetition_plus0":17,"END_RAW_BLOCK":18,"OPEN_RAW_BLOCK":19,"helperName":20,"openRawBlock_repetition0":21,"openRawBlock_option0":22,"CLOSE_RAW_BLOCK":23,"openBlock":24,"block_option0":25,"closeBlock":26,"openInverse":27,"block_option1":28,"OPEN_BLOCK":29,"openBlock_repetition0":30,"openBlock_option0":31,"openBlock_option1":32,"CLOSE":33,"OPEN_INVERSE":34,"openInverse_repetition0":35,"openInverse_option0":36,"openInverse_option1":37,"openInverseChain":38,"OPEN_INVERSE_CHAIN":39,"openInverseChain_repetition0":40,"openInverseChain_option0":41,"openInverseChain_option1":42,"inverseAndProgram":43,"INVERSE":44,"inverseChain":45,"inverseChain_option0":46,"OPEN_ENDBLOCK":47,"OPEN":48,"mustache_repetition0":49,"mustache_option0":50,"OPEN_UNESCAPED":51,"mustache_repetition1":52,"mustache_option1":53,"CLOSE_UNESCAPED":54,"OPEN_PARTIAL":55,"partialName":56,"partial_repetition0":57,"partial_option0":58,"openPartialBlock":59,"OPEN_PARTIAL_BLOCK":60,"openPartialBlock_repetition0":61,"openPartialBlock_option0":62,"param":63,"sexpr":64,"OPEN_SEXPR":65,"sexpr_repetition0":66,"sexpr_option0":67,"CLOSE_SEXPR":68,"hash":69,"hash_repetition_plus0":70,"hashSegment":71,"ID":72,"EQUALS":73,"blockParams":74,"OPEN_BLOCK_PARAMS":75,"blockParams_repetition_plus0":76,"CLOSE_BLOCK_PARAMS":77,"path":78,"dataName":79,"STRING":80,"NUMBER":81,"BOOLEAN":82,"UNDEFINED":83,"NULL":84,"DATA":85,"pathSegments":86,"SEP":87,"$accept":0,"$end":1},
terminals_: {2:"error",5:"EOF",14:"COMMENT",15:"CONTENT",18:"END_RAW_BLOCK",19:"OPEN_RAW_BLOCK",23:"CLOSE_RAW_BLOCK",29:"OPEN_BLOCK",33:"CLOSE",34:"OPEN_INVERSE",39:"OPEN_INVERSE_CHAIN",44:"INVERSE",47:"OPEN_ENDBLOCK",48:"OPEN",51:"OPEN_UNESCAPED",54:"CLOSE_UNESCAPED",55:"OPEN_PARTIAL",60:"OPEN_PARTIAL_BLOCK",65:"OPEN_SEXPR",68:"CLOSE_SEXPR",72:"ID",73:"EQUALS",75:"OPEN_BLOCK_PARAMS",77:"CLOSE_BLOCK_PARAMS",80:"STRING",81:"NUMBER",82:"BOOLEAN",83:"UNDEFINED",84:"NULL",85:"DATA",87:"SEP"},
productions_: [0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[13,1],[10,3],[16,5],[9,4],[9,4],[24,6],[27,6],[38,6],[43,2],[45,3],[45,1],[26,3],[8,5],[8,5],[11,5],[12,3],[59,5],[63,1],[63,1],[64,5],[69,1],[71,3],[74,3],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[56,1],[56,1],[79,2],[78,1],[86,3],[86,1],[6,0],[6,2],[17,1],[17,2],[21,0],[21,2],[22,0],[22,1],[25,0],[25,1],[28,0],[28,1],[30,0],[30,2],[31,0],[31,1],[32,0],[32,1],[35,0],[35,2],[36,0],[36,1],[37,0],[37,1],[40,0],[40,2],[41,0],[41,1],[42,0],[42,1],[46,0],[46,1],[49,0],[49,2],[50,0],[50,1],[52,0],[52,2],[53,0],[53,1],[57,0],[57,2],[58,0],[58,1],[61,0],[61,2],[62,0],[62,1],[66,0],[66,2],[67,0],[67,1],[70,1],[70,2],[76,1],[76,2]],
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$
) {
var $0 = $$.length - 1;
switch (yystate) {
case 1: return $$[$0-1];
break;
case 2:this.$ = yy.prepareProgram($$[$0]);
break;
case 3:this.$ = $$[$0];
break;
case 4:this.$ = $$[$0];
break;
case 5:this.$ = $$[$0];
break;
case 6:this.$ = $$[$0];
break;
case 7:this.$ = $$[$0];
break;
case 8:this.$ = $$[$0];
break;
case 9:
this.$ = {
type: 'CommentStatement',
value: yy.stripComment($$[$0]),
strip: yy.stripFlags($$[$0], $$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 10:
this.$ = {
type: 'ContentStatement',
original: $$[$0],
value: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 11:this.$ = yy.prepareRawBlock($$[$0-2], $$[$0-1], $$[$0], this._$);
break;
case 12:this.$ = { path: $$[$0-3], params: $$[$0-2], hash: $$[$0-1] };
break;
case 13:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], false, this._$);
break;
case 14:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], true, this._$);
break;
case 15:this.$ = { open: $$[$0-5], path: $$[$0-4], params: $$[$0-3], hash: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-5], $$[$0]) };
break;
case 16:this.$ = { path: $$[$0-4], params: $$[$0-3], hash: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-5], $$[$0]) };
break;
case 17:this.$ = { path: $$[$0-4], params: $$[$0-3], hash: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-5], $$[$0]) };
break;
case 18:this.$ = { strip: yy.stripFlags($$[$0-1], $$[$0-1]), program: $$[$0] };
break;
case 19:
var inverse = yy.prepareBlock($$[$0-2], $$[$0-1], $$[$0], $$[$0], false, this._$),
program = yy.prepareProgram([inverse], $$[$0-1].loc);
program.chained = true;
this.$ = { strip: $$[$0-2].strip, program: program, chain: true };
break;
case 20:this.$ = $$[$0];
break;
case 21:this.$ = {path: $$[$0-1], strip: yy.stripFlags($$[$0-2], $$[$0])};
break;
case 22:this.$ = yy.prepareMustache($$[$0-3], $$[$0-2], $$[$0-1], $$[$0-4], yy.stripFlags($$[$0-4], $$[$0]), this._$);
break;
case 23:this.$ = yy.prepareMustache($$[$0-3], $$[$0-2], $$[$0-1], $$[$0-4], yy.stripFlags($$[$0-4], $$[$0]), this._$);
break;
case 24:
this.$ = {
type: 'PartialStatement',
name: $$[$0-3],
params: $$[$0-2],
hash: $$[$0-1],
indent: '',
strip: yy.stripFlags($$[$0-4], $$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 25:this.$ = yy.preparePartialBlock($$[$0-2], $$[$0-1], $$[$0], this._$);
break;
case 26:this.$ = { path: $$[$0-3], params: $$[$0-2], hash: $$[$0-1], strip: yy.stripFlags($$[$0-4], $$[$0]) };
break;
case 27:this.$ = $$[$0];
break;
case 28:this.$ = $$[$0];
break;
case 29:
this.$ = {
type: 'SubExpression',
path: $$[$0-3],
params: $$[$0-2],
hash: $$[$0-1],
loc: yy.locInfo(this._$)
};
break;
case 30:this.$ = {type: 'Hash', pairs: $$[$0], loc: yy.locInfo(this._$)};
break;
case 31:this.$ = {type: 'HashPair', key: yy.id($$[$0-2]), value: $$[$0], loc: yy.locInfo(this._$)};
break;
case 32:this.$ = yy.id($$[$0-1]);
break;
case 33:this.$ = $$[$0];
break;
case 34:this.$ = $$[$0];
break;
case 35:this.$ = {type: 'StringLiteral', value: $$[$0], original: $$[$0], loc: yy.locInfo(this._$)};
break;
case 36:this.$ = {type: 'NumberLiteral', value: Number($$[$0]), original: Number($$[$0]), loc: yy.locInfo(this._$)};
break;
case 37:this.$ = {type: 'BooleanLiteral', value: $$[$0] === 'true', original: $$[$0] === 'true', loc: yy.locInfo(this._$)};
break;
case 38:this.$ = {type: 'UndefinedLiteral', original: undefined, value: undefined, loc: yy.locInfo(this._$)};
break;
case 39:this.$ = {type: 'NullLiteral', original: null, value: null, loc: yy.locInfo(this._$)};
break;
case 40:this.$ = $$[$0];
break;
case 41:this.$ = $$[$0];
break;
case 42:this.$ = yy.preparePath(true, $$[$0], this._$);
break;
case 43:this.$ = yy.preparePath(false, $$[$0], this._$);
break;
case 44: $$[$0-2].push({part: yy.id($$[$0]), original: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
break;
case 45:this.$ = [{part: yy.id($$[$0]), original: $$[$0]}];
break;
case 46:this.$ = [];
break;
case 47:$$[$0-1].push($$[$0]);
break;
case 48:this.$ = [$$[$0]];
break;
case 49:$$[$0-1].push($$[$0]);
break;
case 50:this.$ = [];
break;
case 51:$$[$0-1].push($$[$0]);
break;
case 58:this.$ = [];
break;
case 59:$$[$0-1].push($$[$0]);
break;
case 64:this.$ = [];
break;
case 65:$$[$0-1].push($$[$0]);
break;
case 70:this.$ = [];
break;
case 71:$$[$0-1].push($$[$0]);
break;
case 78:this.$ = [];
break;
case 79:$$[$0-1].push($$[$0]);
break;
case 82:this.$ = [];
break;
case 83:$$[$0-1].push($$[$0]);
break;
case 86:this.$ = [];
break;
case 87:$$[$0-1].push($$[$0]);
break;
case 90:this.$ = [];
break;
case 91:$$[$0-1].push($$[$0]);
break;
case 94:this.$ = [];
break;
case 95:$$[$0-1].push($$[$0]);
break;
case 98:this.$ = [$$[$0]];
break;
case 99:$$[$0-1].push($$[$0]);
break;
case 100:this.$ = [$$[$0]];
break;
case 101:$$[$0-1].push($$[$0]);
break;
}
},
table: [{3:1,4:2,5:[2,46],6:3,14:[2,46],15:[2,46],19:[2,46],29:[2,46],34:[2,46],48:[2,46],51:[2,46],55:[2,46],60:[2,46]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:10,13:11,14:[1,12],15:[1,20],16:17,19:[1,23],24:15,27:16,29:[1,21],34:[1,22],39:[2,2],44:[2,2],47:[2,2],48:[1,13],51:[1,14],55:[1,18],59:19,60:[1,24]},{1:[2,1]},{5:[2,47],14:[2,47],15:[2,47],19:[2,47],29:[2,47],34:[2,47],39:[2,47],44:[2,47],47:[2,47],48:[2,47],51:[2,47],55:[2,47],60:[2,47]},{5:[2,3],14:[2,3],15:[2,3],19:[2,3],29:[2,3],34:[2,3],39:[2,3],44:[2,3],47:[2,3],48:[2,3],51:[2,3],55:[2,3],60:[2,3]},{5:[2,4],14:[2,4],15:[2,4],19:[2,4],29:[2,4],34:[2,4],39:[2,4],44:[2,4],47:[2,4],48:[2,4],51:[2,4],55:[2,4],60:[2,4]},{5:[2,5],14:[2,5],15:[2,5],19:[2,5],29:[2,5],34:[2,5],39:[2,5],44:[2,5],47:[2,5],48:[2,5],51:[2,5],55:[2,5],60:[2,5]},{5:[2,6],14:[2,6],15:[2,6],19:[2,6],29:[2,6],34:[2,6],39:[2,6],44:[2,6],47:[2,6],48:[2,6],51:[2,6],55:[2,6],60:[2,6]},{5:[2,7],14:[2,7],15:[2,7],19:[2,7],29:[2,7],34:[2,7],39:[2,7],44:[2,7],47:[2,7],48:[2,7],51:[2,7],55:[2,7],60:[2,7]},{5:[2,8],14:[2,8],15:[2,8],19:[2,8],29:[2,8],34:[2,8],39:[2,8],44:[2,8],47:[2,8],48:[2,8],51:[2,8],55:[2,8],60:[2,8]},{5:[2,9],14:[2,9],15:[2,9],19:[2,9],29:[2,9],34:[2,9],39:[2,9],44:[2,9],47:[2,9],48:[2,9],51:[2,9],55:[2,9],60:[2,9]},{20:25,72:[1,35],78:26,79:27,80:[1,28],81:[1,29],82:[1,30],83:[1,31],84:[1,32],85:[1,34],86:33},{20:36,72:[1,35],78:26,79:27,80:[1,28],81:[1,29],82:[1,30],83:[1,31],84:[1,32],85:[1,34],86:33},{4:37,6:3,14:[2,46],15:[2,46],19:[2,46],29:[2,46],34:[2,46],39:[2,46],44:[2,46],47:[2,46],48:[2,46],51:[2,46],55:[2,46],60:[2,46]},{4:38,6:3,14:[2,46],15:[2,46],19:[2,46],29:[2,46],34:[2,46],44:[2,46],47:[2,46],48:[2,46],51:[2,46],55:[2,46],60:[2,46]},{13:40,15:[1,20],17:39},{20:42,56:41,64:43,65:[1,44],72:[1,35],78:26,79:27,80:[1,28],81:[1,29],82:[1,30],83:[1,31],84:[1,32],85:[1,34],86:33},{4:45,6:3,14:[2,46],15:[2,46],19:[2,46],29:[2,46],34:[2,46],47:[2,46],48:[2,46],51:[2,46],55:[2,46],60:[2,46]},{5:[2,10],14:[2,10],15:[2,10],18:[2,10],19:[2,10],29:[2,10],34:[2,10],39:[2,10],44:[2,10],47:[2,10],48:[2,10],51:[2,10],55:[2,10],60:[2,10]},{20:46,72:[1,35],78:26,79:27,80:[1,28],81:[1,29],82:[1,30],83:[1,31],84:[1,32],85:[1,34],86:33},{20:47,72:[1,35],78:26,79:27,80:[1,28],81:[1,29],82:[1,30],83:[1,31],84:[1,32],85:[1,34],86:33},{20:48,72:[1,35],78:26,79:27,80:[1,28],81:[1,29],82:[1,30],83:[1,31],84:[1,32],85:[1,34],86:33},{20:42,56:49,64:43,65:[1,44],72:[1,35],78:26,79:27,80:[1,28],81:[1,29],82:[1,30],83:[1,31],84:[1,32],85:[1,34],86:33},{33:[2,78],49:50,65:[2,78],72:[2,78],80:[2,78],81:[2,78],82:[2,78],83:[2,78],84:[2,78],85:[2,78]},{23:[2,33],33:[2,33],54:[2,33],65:[2,33],68:[2,33],72:[2,33],75:[2,33],80:[2,33],81:[2,33],82:[2,33],83:[2,33],84:[2,33],85:[2,33]},{23:[2,34],33:[2,34],54:[2,34],65:[2,34],68:[2,34],72:[2,34],75:[2,34],80:[2,34],81:[2,34],82:[2,34],83:[2,34],84:[2,34],85:[2,34]},{23:[2,35],33:[2,35],54:[2,35],65:[2,35],68:[2,35],72:[2,35],75:[2,35],80:[2,35],81:[2,35],82:[2,35],83:[2,35],84:[2,35],85:[2,35]},{23:[2,36],33:[2,36],54:[2,36],65:[2,36],68:[2,36],72:[2,36],75:[2,36],80:[2,36],81:[2,36],82:[2,36],83:[2,36],84:[2,36],85:[2,36]},{23:[2,37],33:[2,37],54:[2,37],65:[2,37],68:[2,37],72:[2,37],75:[2,37],80:[2,37],81:[2,37],82:[2,37],83:[2,37],84:[2,37],85:[2,37]},{23:[2,38],33:[2,38],54:[2,38],65:[2,38],68:[2,38],72:[2,38],75:[2,38],80:[2,38],81:[2,38],82:[2,38],83:[2,38],84:[2,38],85:[2,38]},{23:[2,39],33:[2,39],54:[2,39],65:[2,39],68:[2,39],72:[2,39],75:[2,39],80:[2,39],81:[2,39],82:[2,39],83:[2,39],84:[2,39],85:[2,39]},{23:[2,43],33:[2,43],54:[2,43],65:[2,43],68:[2,43],72:[2,43],75:[2,43],80:[2,43],81:[2,43],82:[2,43],83:[2,43],84:[2,43],85:[2,43],87:[1,51]},{72:[1,35],86:52},{23:[2,45],33:[2,45],54:[2,45],65:[2,45],68:[2,45],72:[2,45],75:[2,45],80:[2,45],81:[2,45],82:[2,45],83:[2,45],84:[2,45],85:[2,45],87:[2,45]},{52:53,54:[2,82],65:[2,82],72:[2,82],80:[2,82],81:[2,82],82:[2,82],83:[2,82],84:[2,82],85:[2,82]},{25:54,38:56,39:[1,58],43:57,44:[1,59],45:55,47:[2,54]},{28:60,43:61,44:[1,59],47:[2,56]},{13:63,15:[1,20],18:[1,62]},{15:[2,48],18:[2,48]},{33:[2,86],57:64,65:[2,86],72:[2,86],80:[2,86],81:[2,86],82:[2,86],83:[2,86],84:[2,86],85:[2,86]},{33:[2,40],65:[2,40],72:[2,40],80:[2,40],81:[2,40],82:[2,40],83:[2,40],84:[2,40],85:[2,40]},{33:[2,41],65:[2,41],72:[2,41],80:[2,41],81:[2,41],82:[2,41],83:[2,41],84:[2,41],85:[2,41]},{20:65,72:[1,35],78:26,79:27,80:[1,28],81:[1,29],82:[1,30],83:[1,31],84:[1,32],85:[1,34],86:33},{26:66,47:[1,67]},{30:68,33:[2,58],65:[2,58],72:[2,58],75:[2,58],80:[2,58],81:[2,58],82:[2,58],83:[2,58],84:[2,58],85:[2,58]},{33:[2,64],35:69,65:[2,64],72:[2,64],75:[2,64],80:[2,64],81:[2,64],82:[2,64],83:[2,64],84:[2,64],85:[2,64]},{21:70,23:[2,50],65:[2,50],72:[2,50],80:[2,50],81:[2,50],82:[2,50],83:[2,50],84:[2,50],85:[2,50]},{33:[2,90],61:71,65:[2,90],72:[2,90],80:[2,90],81:[2,90],82:[2,90],83:[2,90],84:[2,90],85:[2,90]},{20:75,33:[2,80],50:72,63:73,64:76,65:[1,44],69:74,70:77,71:78,72:[1,79],78:26,79:27,80:[1,28]Line truncated
defaultActions: {4:[2,1],55:[2,55],57:[2,20],61:[2,57],74:[2,81],83:[2,85],87:[2,18],91:[2,89],102:[2,53],105:[2,93],111:[2,19],112:[2,77],117:[2,97],120:[2,63],123:[2,69],124:[2,12],136:[2,75],137:[2,32]},
parseError: function parseError (str, hash) {
throw new Error(str);
},
parse: function parse(input) {
var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
this.lexer.setInput(input);
this.lexer.yy = this.yy;
this.yy.lexer = this.lexer;
this.yy.parser = this;
if (typeof this.lexer.yylloc == "undefined")
this.lexer.yylloc = {};
var yyloc = this.lexer.yylloc;
lstack.push(yyloc);
var ranges = this.lexer.options && this.lexer.options.ranges;
if (typeof this.yy.parseError === "function")
this.parseError = this.yy.parseError;
function popStack(n) {
stack.length = stack.length - 2 * n;
vstack.length = vstack.length - n;
lstack.length = lstack.length - n;
}
function lex() {
var token;
token = self.lexer.lex() || 1;
if (typeof token !== "number") {
token = self.symbols_[token] || token;
}
return token;
}
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
while (true) {
state = stack[stack.length - 1];
if (this.defaultActions[state]) {
action = this.defaultActions[state];
} else {
if (symbol === null || typeof symbol == "undefined") {
symbol = lex();
}
action = table[state] && table[state][symbol];
}
if (typeof action === "undefined" || !action.length || !action[0]) {
var errStr = "";
if (!recovering) {
expected = [];
for (p in table[state])
if (this.terminals_[p] && p > 2) {
expected.push("'" + this.terminals_[p] + "'");
}
if (this.lexer.showPosition) {
errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
} else {
errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
}
this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
}
}
if (action[0] instanceof Array && action.length > 1) {
throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
}
switch (action[0]) {
case 1:
stack.push(symbol);
vstack.push(this.lexer.yytext);
lstack.push(this.lexer.yylloc);
stack.push(action[1]);
symbol = null;
if (!preErrorSymbol) {
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
yyloc = this.lexer.yylloc;
if (recovering > 0)
recovering--;
} else {
symbol = preErrorSymbol;
preErrorSymbol = null;
}
break;
case 2:
len = this.productions_[action[1]][1];
yyval.$ = vstack[vstack.length - len];
yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
if (ranges) {
yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
}
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
if (typeof r !== "undefined") {
return r;
}
if (len) {
stack = stack.slice(0, -1 * len * 2);
vstack = vstack.slice(0, -1 * len);
lstack = lstack.slice(0, -1 * len);
}
stack.push(this.productions_[action[1]][0]);
vstack.push(yyval.$);
lstack.push(yyval._$);
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
stack.push(newState);
break;
case 3:
return true;
}
}
return true;
}
};
/* Jison generated lexer */
var lexer = (function(){
var lexer = ({EOF:1,
parseError:function parseError(str, hash) {
if (this.yy.parser) {
this.yy.parser.parseError(str, hash);
} else {
throw new Error(str);
}
},
setInput:function (input) {
this._input = input;
this._more = this._less = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
if (this.options.ranges) this.yylloc.range = [0,0];
this.offset = 0;
return this;
},
input:function () {
var ch = this._input[0];
this.yytext += ch;
this.yyleng++;
this.offset++;
this.match += ch;
this.matched += ch;
var lines = ch.match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno++;
this.yylloc.last_line++;
} else {
this.yylloc.last_column++;
}
if (this.options.ranges) this.yylloc.range[1]++;
this._input = this._input.slice(1);
return ch;
},
unput:function (ch) {
var len = ch.length;
var lines = ch.split(/(?:\r\n?|\n)/g);
this._input = ch + this._input;
this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
//this.yyleng -= len;
this.offset -= len;
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
this.match = this.match.substr(0, this.match.length-1);
this.matched = this.matched.substr(0, this.matched.length-1);
if (lines.length-1) this.yylineno -= lines.length-1;
var r = this.yylloc.range;
this.yylloc = {first_line: this.yylloc.first_line,
last_line: this.yylineno+1,
first_column: this.yylloc.first_column,
last_column: lines ?
(lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
this.yylloc.first_column - len
};
if (this.options.ranges) {
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
}
return this;
},
more:function () {
this._more = true;
return this;
},
less:function (n) {
this.unput(this.match.slice(n));
},
pastInput:function () {
var past = this.matched.substr(0, this.matched.length - this.match.length);
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
},
upcomingInput:function () {
var next = this.match;
if (next.length < 20) {
next += this._input.substr(0, 20-next.length);
}
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
},
showPosition:function () {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c+"^";
},
next:function () {
if (this.done) {
return this.EOF;
}
if (!this._input) this.done = true;
var token,
match,
tempMatch,
index,
col,
lines;
if (!this._more) {
this.yytext = '';
this.match = '';
}
var rules = this._currentRules();
for (var i=0;i < rules.length; i++) {
tempMatch = this._input.match(this.rules[rules[i]]);
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
match = tempMatch;
index = i;
if (!this.options.flex) break;
}
}
if (match) {
lines = match[0].match(/(?:\r\n?|\n).*/g);
if (lines) this.yylineno += lines.length;
this.yylloc = {first_line: this.yylloc.last_line,
last_line: this.yylineno+1,
first_column: this.yylloc.last_column,
last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
if (this.options.ranges) {
this.yylloc.range = [this.offset, this.offset += this.yyleng];
}
this._more = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
if (this.done && this._input) this.done = false;
if (token) return token;
else return;
}
if (this._input === "") {
return this.EOF;
} else {
return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
{text: "", token: null, line: this.yylineno});
}
},
lex:function lex () {
var r = this.next();
if (typeof r !== 'undefined') {
return r;
} else {
return this.lex();
}
},
begin:function begin (condition) {
this.conditionStack.push(condition);
},
popState:function popState () {
return this.conditionStack.pop();
},
_currentRules:function _currentRules () {
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
},
topState:function () {
return this.conditionStack[this.conditionStack.length-2];
},
pushState:function begin (condition) {
this.begin(condition);
}});
lexer.options = {};
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START
) {
function strip(start, end) {
return yy_.yytext = yy_.yytext.substring(start, yy_.yyleng - end + start);
}
var YYSTATE=YY_START
switch($avoiding_name_collisions) {
case 0:
if(yy_.yytext.slice(-2) === "\\\\") {
strip(0,1);
this.begin("mu");
} else if(yy_.yytext.slice(-1) === "\\") {
strip(0,1);
this.begin("emu");
} else {
this.begin("mu");
}
if(yy_.yytext) return 15;
break;
case 1:return 15;
break;
case 2:
this.popState();
return 15;
break;
case 3:this.begin('raw'); return 15;
break;
case 4:
this.popState();
// Should be using `this.topState()` below, but it currently
// returns the second top instead of the first top. Opened an
// issue about it at https://github.com/zaach/jison/issues/291
if (this.conditionStack[this.conditionStack.length-1] === 'raw') {
return 15;
} else {
strip(5, 9);
return 'END_RAW_BLOCK';
}
break;
case 5: return 15;
break;
case 6:
this.popState();
return 14;
break;
case 7:return 65;
break;
case 8:return 68;
break;
case 9: return 19;
break;
case 10:
this.popState();
this.begin('raw');
return 23;
break;
case 11:return 55;
break;
case 12:return 60;
break;
case 13:return 29;
break;
case 14:return 47;
break;
case 15:this.popState(); return 44;
break;
case 16:this.popState(); return 44;
break;
case 17:return 34;
break;
case 18:return 39;
break;
case 19:return 51;
break;
case 20:return 48;
break;
case 21:
this.unput(yy_.yytext);
this.popState();
this.begin('com');
break;
case 22:
this.popState();
return 14;
break;
case 23:return 48;
break;
case 24:return 73;
break;
case 25:return 72;
break;
case 26:return 72;
break;
case 27:return 87;
break;
case 28:// ignore whitespace
break;
case 29:this.popState(); return 54;
break;
case 30:this.popState(); return 33;
break;
case 31:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 80;
break;
case 32:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 80;
break;
case 33:return 85;
break;
case 34:return 82;
break;
case 35:return 82;
break;
case 36:return 83;
break;
case 37:return 84;
break;
case 38:return 81;
break;
case 39:return 75;
break;
case 40:return 77;
break;
case 41:return 72;
break;
case 42:yy_.yytext = yy_.yytext.replace(/\\([\\\]])/g,'$1'); return 72;
break;
case 43:return 'INVALID';
break;
case 44:return 5;
break;
}
};
lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{(?=[^\/]))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#>)/,/^(?:\{\{(~)?#\*?)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?\*?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:undefined(?=([~}\s)])))/,/^(?:null(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[(\\\]|[^\]])*\])/,/^(?:.)/,/^(?:$)/];
lexer.conditions = {"mu":{"rules":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[6],"inclusive":false},"raw":{"rules":[3,4,5],"inclusive":false},"INITIAL":{"rules":[0,1,44],"inclusive":true}};
return lexer;})()
parser.lexer = lexer;
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
return new Parser;
})();export default handlebars;
+171
View File
@@ -0,0 +1,171 @@
/* eslint-disable new-cap */
import Visitor from './visitor';
export function print(ast) {
return new PrintVisitor().accept(ast);
}
export function PrintVisitor() {
this.padding = 0;
}
PrintVisitor.prototype = new Visitor();
PrintVisitor.prototype.pad = function(string) {
let out = '';
for (let i = 0, l = this.padding; i < l; i++) {
out += ' ';
}
out += string + '\n';
return out;
};
PrintVisitor.prototype.Program = function(program) {
let out = '',
body = program.body,
i, l;
if (program.blockParams) {
let blockParams = 'BLOCK PARAMS: [';
for (i = 0, l = program.blockParams.length; i < l; i++) {
blockParams += ' ' + program.blockParams[i];
}
blockParams += ' ]';
out += this.pad(blockParams);
}
for (i = 0, l = body.length; i < l; i++) {
out += this.accept(body[i]);
}
this.padding--;
return out;
};
PrintVisitor.prototype.MustacheStatement = function(mustache) {
return this.pad('{{ ' + this.SubExpression(mustache) + ' }}');
};
PrintVisitor.prototype.Decorator = function(mustache) {
return this.pad('{{ DIRECTIVE ' + this.SubExpression(mustache) + ' }}');
};
PrintVisitor.prototype.BlockStatement =
PrintVisitor.prototype.DecoratorBlock = function(block) {
let out = '';
out += this.pad((block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:');
this.padding++;
out += this.pad(this.SubExpression(block));
if (block.program) {
out += this.pad('PROGRAM:');
this.padding++;
out += this.accept(block.program);
this.padding--;
}
if (block.inverse) {
if (block.program) { this.padding++; }
out += this.pad('{{^}}');
this.padding++;
out += this.accept(block.inverse);
this.padding--;
if (block.program) { this.padding--; }
}
this.padding--;
return out;
};
PrintVisitor.prototype.PartialStatement = function(partial) {
let content = 'PARTIAL:' + partial.name.original;
if (partial.params[0]) {
content += ' ' + this.accept(partial.params[0]);
}
if (partial.hash) {
content += ' ' + this.accept(partial.hash);
}
return this.pad('{{> ' + content + ' }}');
};
PrintVisitor.prototype.PartialBlockStatement = function(partial) {
let content = 'PARTIAL BLOCK:' + partial.name.original;
if (partial.params[0]) {
content += ' ' + this.accept(partial.params[0]);
}
if (partial.hash) {
content += ' ' + this.accept(partial.hash);
}
content += ' ' + this.pad('PROGRAM:');
this.padding++;
content += this.accept(partial.program);
this.padding--;
return this.pad('{{> ' + content + ' }}');
};
PrintVisitor.prototype.ContentStatement = function(content) {
return this.pad("CONTENT[ '" + content.value + "' ]");
};
PrintVisitor.prototype.CommentStatement = function(comment) {
return this.pad("{{! '" + comment.value + "' }}");
};
PrintVisitor.prototype.SubExpression = function(sexpr) {
let params = sexpr.params,
paramStrings = [],
hash;
for (let i = 0, l = params.length; i < l; i++) {
paramStrings.push(this.accept(params[i]));
}
params = '[' + paramStrings.join(', ') + ']';
hash = sexpr.hash ? ' ' + this.accept(sexpr.hash) : '';
return this.accept(sexpr.path) + ' ' + params + hash;
};
PrintVisitor.prototype.PathExpression = function(id) {
let path = id.parts.join('/');
return (id.data ? '@' : '') + 'PATH:' + path;
};
PrintVisitor.prototype.StringLiteral = function(string) {
return '"' + string.value + '"';
};
PrintVisitor.prototype.NumberLiteral = function(number) {
return 'NUMBER{' + number.value + '}';
};
PrintVisitor.prototype.BooleanLiteral = function(bool) {
return 'BOOLEAN{' + bool.value + '}';
};
PrintVisitor.prototype.UndefinedLiteral = function() {
return 'UNDEFINED';
};
PrintVisitor.prototype.NullLiteral = function() {
return 'NULL';
};
PrintVisitor.prototype.Hash = function(hash) {
let pairs = hash.pairs,
joinedPairs = [];
for (let i = 0, l = pairs.length; i < l; i++) {
joinedPairs.push(this.accept(pairs[i]));
}
return 'HASH{' + joinedPairs.join(', ') + '}';
};
PrintVisitor.prototype.HashPair = function(pair) {
return pair.key + '=' + this.accept(pair.value);
};
/* eslint-enable new-cap */
+129
View File
@@ -0,0 +1,129 @@
import Exception from '../exception';
function Visitor() {
this.parents = [];
}
Visitor.prototype = {
constructor: Visitor,
mutating: false,
// Visits a given value. If mutating, will replace the value if necessary.
acceptKey: function(node, name) {
let value = this.accept(node[name]);
if (this.mutating) {
// Hacky sanity check: This may have a few false positives for type for the helper
// methods but will generally do the right thing without a lot of overhead.
if (value && !Visitor.prototype[value.type]) {
throw new Exception('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
}
node[name] = value;
}
},
// Performs an accept operation with added sanity check to ensure
// required keys are not removed.
acceptRequired: function(node, name) {
this.acceptKey(node, name);
if (!node[name]) {
throw new Exception(node.type + ' requires ' + name);
}
},
// Traverses a given array. If mutating, empty respnses will be removed
// for child elements.
acceptArray: function(array) {
for (let i = 0, l = array.length; i < l; i++) {
this.acceptKey(array, i);
if (!array[i]) {
array.splice(i, 1);
i--;
l--;
}
}
},
accept: function(object) {
if (!object) {
return;
}
/* istanbul ignore next: Sanity code */
if (!this[object.type]) {
throw new Exception('Unknown type: ' + object.type, object);
}
if (this.current) {
this.parents.unshift(this.current);
}
this.current = object;
let ret = this[object.type](object);
this.current = this.parents.shift();
if (!this.mutating || ret) {
return ret;
} else if (ret !== false) {
return object;
}
},
Program: function(program) {
this.acceptArray(program.body);
},
MustacheStatement: visitSubExpression,
Decorator: visitSubExpression,
BlockStatement: visitBlock,
DecoratorBlock: visitBlock,
PartialStatement: visitPartial,
PartialBlockStatement: function(partial) {
visitPartial.call(this, partial);
this.acceptKey(partial, 'program');
},
ContentStatement: function(/* content */) {},
CommentStatement: function(/* comment */) {},
SubExpression: visitSubExpression,
PathExpression: function(/* path */) {},
StringLiteral: function(/* string */) {},
NumberLiteral: function(/* number */) {},
BooleanLiteral: function(/* bool */) {},
UndefinedLiteral: function(/* literal */) {},
NullLiteral: function(/* literal */) {},
Hash: function(hash) {
this.acceptArray(hash.pairs);
},
HashPair: function(pair) {
this.acceptRequired(pair, 'value');
}
};
function visitSubExpression(mustache) {
this.acceptRequired(mustache, 'path');
this.acceptArray(mustache.params);
this.acceptKey(mustache, 'hash');
}
function visitBlock(block) {
visitSubExpression.call(this, block);
this.acceptKey(block, 'program');
this.acceptKey(block, 'inverse');
}
function visitPartial(partial) {
this.acceptRequired(partial, 'name');
this.acceptArray(partial.params);
this.acceptKey(partial, 'hash');
}
export default Visitor;
+216
View File
@@ -0,0 +1,216 @@
import Visitor from './visitor';
function WhitespaceControl(options = {}) {
this.options = options;
}
WhitespaceControl.prototype = new Visitor();
WhitespaceControl.prototype.Program = function(program) {
const doStandalone = !this.options.ignoreStandalone;
let isRoot = !this.isRootSeen;
this.isRootSeen = true;
let body = program.body;
for (let i = 0, l = body.length; i < l; i++) {
let current = body[i],
strip = this.accept(current);
if (!strip) {
continue;
}
let _isPrevWhitespace = isPrevWhitespace(body, i, isRoot),
_isNextWhitespace = isNextWhitespace(body, i, isRoot),
openStandalone = strip.openStandalone && _isPrevWhitespace,
closeStandalone = strip.closeStandalone && _isNextWhitespace,
inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
if (strip.close) {
omitRight(body, i, true);
}
if (strip.open) {
omitLeft(body, i, true);
}
if (doStandalone && inlineStandalone) {
omitRight(body, i);
if (omitLeft(body, i)) {
// If we are on a standalone node, save the indent info for partials
if (current.type === 'PartialStatement') {
// Pull out the whitespace from the final line
current.indent = (/([ \t]+$)/).exec(body[i - 1].original)[1];
}
}
}
if (doStandalone && openStandalone) {
omitRight((current.program || current.inverse).body);
// Strip out the previous content node if it's whitespace only
omitLeft(body, i);
}
if (doStandalone && closeStandalone) {
// Always strip the next node
omitRight(body, i);
omitLeft((current.inverse || current.program).body);
}
}
return program;
};
WhitespaceControl.prototype.BlockStatement =
WhitespaceControl.prototype.DecoratorBlock =
WhitespaceControl.prototype.PartialBlockStatement = function(block) {
this.accept(block.program);
this.accept(block.inverse);
// Find the inverse program that is involed with whitespace stripping.
let program = block.program || block.inverse,
inverse = block.program && block.inverse,
firstInverse = inverse,
lastInverse = inverse;
if (inverse && inverse.chained) {
firstInverse = inverse.body[0].program;
// Walk the inverse chain to find the last inverse that is actually in the chain.
while (lastInverse.chained) {
lastInverse = lastInverse.body[lastInverse.body.length - 1].program;
}
}
let strip = {
open: block.openStrip.open,
close: block.closeStrip.close,
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
// so our parent can determine if we actually are standalone
openStandalone: isNextWhitespace(program.body),
closeStandalone: isPrevWhitespace((firstInverse || program).body)
};
if (block.openStrip.close) {
omitRight(program.body, null, true);
}
if (inverse) {
let inverseStrip = block.inverseStrip;
if (inverseStrip.open) {
omitLeft(program.body, null, true);
}
if (inverseStrip.close) {
omitRight(firstInverse.body, null, true);
}
if (block.closeStrip.open) {
omitLeft(lastInverse.body, null, true);
}
// Find standalone else statments
if (!this.options.ignoreStandalone
&& isPrevWhitespace(program.body)
&& isNextWhitespace(firstInverse.body)) {
omitLeft(program.body);
omitRight(firstInverse.body);
}
} else if (block.closeStrip.open) {
omitLeft(program.body, null, true);
}
return strip;
};
WhitespaceControl.prototype.Decorator =
WhitespaceControl.prototype.MustacheStatement = function(mustache) {
return mustache.strip;
};
WhitespaceControl.prototype.PartialStatement =
WhitespaceControl.prototype.CommentStatement = function(node) {
/* istanbul ignore next */
let strip = node.strip || {};
return {
inlineStandalone: true,
open: strip.open,
close: strip.close
};
};
function isPrevWhitespace(body, i, isRoot) {
if (i === undefined) {
i = body.length;
}
// Nodes that end with newlines are considered whitespace (but are special
// cased for strip operations)
let prev = body[i - 1],
sibling = body[i - 2];
if (!prev) {
return isRoot;
}
if (prev.type === 'ContentStatement') {
return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original);
}
}
function isNextWhitespace(body, i, isRoot) {
if (i === undefined) {
i = -1;
}
let next = body[i + 1],
sibling = body[i + 2];
if (!next) {
return isRoot;
}
if (next.type === 'ContentStatement') {
return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original);
}
}
// Marks the node to the right of the position as omitted.
// I.e. {{foo}}' ' will mark the ' ' node as omitted.
//
// If i is undefined, then the first child will be marked as such.
//
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
// content is met.
function omitRight(body, i, multiple) {
let current = body[i == null ? 0 : i + 1];
if (!current || current.type !== 'ContentStatement' || (!multiple && current.rightStripped)) {
return;
}
let original = current.value;
current.value = current.value.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), '');
current.rightStripped = current.value !== original;
}
// Marks the node to the left of the position as omitted.
// I.e. ' '{{foo}} will mark the ' ' node as omitted.
//
// If i is undefined then the last child will be marked as such.
//
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
// content is met.
function omitLeft(body, i, multiple) {
let current = body[i == null ? body.length - 1 : i - 1];
if (!current || current.type !== 'ContentStatement' || (!multiple && current.leftStripped)) {
return;
}
// We omit the last node if it's whitespace only and not preceeded by a non-content node.
let original = current.value;
current.value = current.value.replace(multiple ? (/\s+$/) : (/[ \t]+$/), '');
current.leftStripped = current.value !== original;
return current.leftStripped;
}
export default WhitespaceControl;
+6
View File
@@ -0,0 +1,6 @@
import registerInline from './decorators/inline';
export function registerDefaultDecorators(instance) {
registerInline(instance);
}
+22
View File
@@ -0,0 +1,22 @@
import {extend} from '../utils';
export default function(instance) {
instance.registerDecorator('inline', function(fn, props, container, options) {
let ret = fn;
if (!props.partials) {
props.partials = {};
ret = function(context, options) {
// Create a new partials stack frame prior to exec.
let original = container.partials;
container.partials = extend({}, original, props.partials);
let ret = fn(context, options);
container.partials = original;
return ret;
};
}
props.partials[options.args[0]] = options.fn;
return ret;
});
}
+49
View File
@@ -0,0 +1,49 @@
const errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
function Exception(message, node) {
let loc = node && node.loc,
line,
column;
if (loc) {
line = loc.start.line;
column = loc.start.column;
message += ' - ' + line + ':' + column;
}
let tmp = Error.prototype.constructor.call(this, message);
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (let idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
/* istanbul ignore else */
if (Error.captureStackTrace) {
Error.captureStackTrace(this, Exception);
}
try {
if (loc) {
this.lineNumber = line;
// Work around issue under safari where we can't directly set the column value
/* istanbul ignore next */
if (Object.defineProperty) {
Object.defineProperty(this, 'column', {
value: column,
enumerable: true
});
} else {
this.column = column;
}
}
} catch (nop) {
/* Ignore if the browser is very particular */
}
}
Exception.prototype = new Error();
export default Exception;
+17
View File
@@ -0,0 +1,17 @@
import registerBlockHelperMissing from './helpers/block-helper-missing';
import registerEach from './helpers/each';
import registerHelperMissing from './helpers/helper-missing';
import registerIf from './helpers/if';
import registerLog from './helpers/log';
import registerLookup from './helpers/lookup';
import registerWith from './helpers/with';
export function registerDefaultHelpers(instance) {
registerBlockHelperMissing(instance);
registerEach(instance);
registerHelperMissing(instance);
registerIf(instance);
registerLog(instance);
registerLookup(instance);
registerWith(instance);
}
+32
View File
@@ -0,0 +1,32 @@
import {appendContextPath, createFrame, isArray} from '../utils';
export default function(instance) {
instance.registerHelper('blockHelperMissing', function(context, options) {
let inverse = options.inverse,
fn = options.fn;
if (context === true) {
return fn(this);
} else if (context === false || context == null) {
return inverse(this);
} else if (isArray(context)) {
if (context.length > 0) {
if (options.ids) {
options.ids = [options.name];
}
return instance.helpers.each(context, options);
} else {
return inverse(this);
}
} else {
if (options.data && options.ids) {
let data = createFrame(options.data);
data.contextPath = appendContextPath(options.data.contextPath, options.name);
options = {data: data};
}
return fn(context, options);
}
});
}
+79
View File
@@ -0,0 +1,79 @@
import {appendContextPath, blockParams, createFrame, isArray, isFunction} from '../utils';
import Exception from '../exception';
export default function(instance) {
instance.registerHelper('each', function(context, options) {
if (!options) {
throw new Exception('Must pass iterator to #each');
}
let fn = options.fn,
inverse = options.inverse,
i = 0,
ret = '',
data,
contextPath;
if (options.data && options.ids) {
contextPath = appendContextPath(options.data.contextPath, options.ids[0]) + '.';
}
if (isFunction(context)) { context = context.call(this); }
if (options.data) {
data = createFrame(options.data);
}
function execIteration(field, index, last) {
if (data) {
data.key = field;
data.index = index;
data.first = index === 0;
data.last = !!last;
if (contextPath) {
data.contextPath = contextPath + field;
}
}
ret = ret + fn(context[field], {
data: data,
blockParams: blockParams([context[field], field], [contextPath + field, null])
});
}
if (context && typeof context === 'object') {
if (isArray(context)) {
for (let j = context.length; i < j; i++) {
if (i in context) {
execIteration(i, i, i === context.length - 1);
}
}
} else {
let priorKey;
for (let key in context) {
if (context.hasOwnProperty(key)) {
// We're running the iterations one step out of sync so we can detect
// the last iteration without have to scan the object twice and create
// an itermediate keys array.
if (priorKey !== undefined) {
execIteration(priorKey, i - 1);
}
priorKey = key;
i++;
}
}
if (priorKey !== undefined) {
execIteration(priorKey, i - 1, true);
}
}
}
if (i === 0) {
ret = inverse(this);
}
return ret;
});
}
+13
View File
@@ -0,0 +1,13 @@
import Exception from '../exception';
export default function(instance) {
instance.registerHelper('helperMissing', function(/* [args, ]options */) {
if (arguments.length === 1) {
// A missing field in a {{foo}} construct.
return undefined;
} else {
// Someone is actually trying to call something, blow up.
throw new Exception('Missing helper: "' + arguments[arguments.length - 1].name + '"');
}
});
}
+20
View File
@@ -0,0 +1,20 @@
import {isEmpty, isFunction} from '../utils';
export default function(instance) {
instance.registerHelper('if', function(conditional, options) {
if (isFunction(conditional)) { conditional = conditional.call(this); }
// Default behavior is to render the positive path if the value is truthy and not empty.
// The `includeZero` option may be set to treat the condtional as purely not empty based on the
// behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
if ((!options.hash.includeZero && !conditional) || isEmpty(conditional)) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
instance.registerHelper('unless', function(conditional, options) {
return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash});
});
}
+19
View File
@@ -0,0 +1,19 @@
export default function(instance) {
instance.registerHelper('log', function(/* message, options */) {
let args = [undefined],
options = arguments[arguments.length - 1];
for (let i = 0; i < arguments.length - 1; i++) {
args.push(arguments[i]);
}
let level = 1;
if (options.hash.level != null) {
level = options.hash.level;
} else if (options.data && options.data.level != null) {
level = options.data.level;
}
args[0] = level;
instance.log(... args);
});
}
+11
View File
@@ -0,0 +1,11 @@
export default function(instance) {
instance.registerHelper('lookup', function(obj, field) {
if (!obj) {
return obj;
}
if (field === 'constructor' && !obj.propertyIsEnumerable(field)) {
return undefined;
}
return obj[field];
});
}
+24
View File
@@ -0,0 +1,24 @@
import {appendContextPath, blockParams, createFrame, isEmpty, isFunction} from '../utils';
export default function(instance) {
instance.registerHelper('with', function(context, options) {
if (isFunction(context)) { context = context.call(this); }
let fn = options.fn;
if (!isEmpty(context)) {
let data = options.data;
if (options.data && options.ids) {
data = createFrame(options.data);
data.contextPath = appendContextPath(options.data.contextPath, options.ids[0]);
}
return fn(context, {
data: data,
blockParams: blockParams([context], [data && data.contextPath])
});
} else {
return options.inverse(this);
}
});
}
+35
View File
@@ -0,0 +1,35 @@
import {indexOf} from './utils';
let logger = {
methodMap: ['debug', 'info', 'warn', 'error'],
level: 'info',
// Maps a given level value to the `methodMap` indexes above.
lookupLevel: function(level) {
if (typeof level === 'string') {
let levelMap = indexOf(logger.methodMap, level.toLowerCase());
if (levelMap >= 0) {
level = levelMap;
} else {
level = parseInt(level, 10);
}
}
return level;
},
// Can be overridden in the host environment
log: function(level, ...message) {
level = logger.lookupLevel(level);
if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) {
let method = logger.methodMap[level];
if (!console[method]) { // eslint-disable-line no-console
method = 'log';
}
console[method](...message); // eslint-disable-line no-console
}
}
};
export default logger;
+13
View File
@@ -0,0 +1,13 @@
/* global window */
export default function(Handlebars) {
/* istanbul ignore next */
let root = typeof global !== 'undefined' ? global : window,
$Handlebars = root.Handlebars;
/* istanbul ignore next */
Handlebars.noConflict = function() {
if (root.Handlebars === Handlebars) {
root.Handlebars = $Handlebars;
}
return Handlebars;
};
}
+281
View File
@@ -0,0 +1,281 @@
import * as Utils from './utils';
import Exception from './exception';
import { COMPILER_REVISION, REVISION_CHANGES, createFrame } from './base';
export function checkRevision(compilerInfo) {
const compilerRevision = compilerInfo && compilerInfo[0] || 1,
currentRevision = COMPILER_REVISION;
if (compilerRevision !== currentRevision) {
if (compilerRevision < currentRevision) {
const runtimeVersions = REVISION_CHANGES[currentRevision],
compilerVersions = REVISION_CHANGES[compilerRevision];
throw new Exception('Template was precompiled with an older version of Handlebars than the current runtime. ' +
'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
throw new Exception('Template was precompiled with a newer version of Handlebars than the current runtime. ' +
'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
}
}
}
export function template(templateSpec, env) {
/* istanbul ignore next */
if (!env) {
throw new Exception('No environment passed to template');
}
if (!templateSpec || !templateSpec.main) {
throw new Exception('Unknown template object: ' + typeof templateSpec);
}
templateSpec.main.decorator = templateSpec.main_d;
// Note: Using env.VM references rather than local var references throughout this section to allow
// for external users to override these as psuedo-supported APIs.
env.VM.checkRevision(templateSpec.compiler);
function invokePartialWrapper(partial, context, options) {
if (options.hash) {
context = Utils.extend({}, context, options.hash);
if (options.ids) {
options.ids[0] = true;
}
}
partial = env.VM.resolvePartial.call(this, partial, context, options);
let result = env.VM.invokePartial.call(this, partial, context, options);
if (result == null && env.compile) {
options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
result = options.partials[options.name](context, options);
}
if (result != null) {
if (options.indent) {
let lines = result.split('\n');
for (let i = 0, l = lines.length; i < l; i++) {
if (!lines[i] && i + 1 === l) {
break;
}
lines[i] = options.indent + lines[i];
}
result = lines.join('\n');
}
return result;
} else {
throw new Exception('The partial ' + options.name + ' could not be compiled when running in runtime-only mode');
}
}
// Just add water
let container = {
strict: function(obj, name) {
if (!(name in obj)) {
throw new Exception('"' + name + '" not defined in ' + obj);
}
return obj[name];
},
lookup: function(depths, name) {
const len = depths.length;
for (let i = 0; i < len; i++) {
if (depths[i] && depths[i][name] != null) {
return depths[i][name];
}
}
},
lambda: function(current, context) {
return typeof current === 'function' ? current.call(context) : current;
},
escapeExpression: Utils.escapeExpression,
invokePartial: invokePartialWrapper,
fn: function(i) {
let ret = templateSpec[i];
ret.decorator = templateSpec[i + '_d'];
return ret;
},
programs: [],
program: function(i, data, declaredBlockParams, blockParams, depths) {
let programWrapper = this.programs[i],
fn = this.fn(i);
if (data || depths || blockParams || declaredBlockParams) {
programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths);
} else if (!programWrapper) {
programWrapper = this.programs[i] = wrapProgram(this, i, fn);
}
return programWrapper;
},
data: function(value, depth) {
while (value && depth--) {
value = value._parent;
}
return value;
},
merge: function(param, common) {
let obj = param || common;
if (param && common && (param !== common)) {
obj = Utils.extend({}, common, param);
}
return obj;
},
// An empty object to use as replacement for null-contexts
nullContext: Object.seal({}),
noop: env.VM.noop,
compilerInfo: templateSpec.compiler
};
function ret(context, options = {}) {
let data = options.data;
ret._setup(options);
if (!options.partial && templateSpec.useData) {
data = initData(context, data);
}
let depths,
blockParams = templateSpec.useBlockParams ? [] : undefined;
if (templateSpec.useDepths) {
if (options.depths) {
depths = context != options.depths[0] ? [context].concat(options.depths) : options.depths;
} else {
depths = [context];
}
}
function main(context/*, options*/) {
return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths);
}
main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams);
return main(context, options);
}
ret.isTop = true;
ret._setup = function(options) {
if (!options.partial) {
container.helpers = container.merge(options.helpers, env.helpers);
if (templateSpec.usePartial) {
container.partials = container.merge(options.partials, env.partials);
}
if (templateSpec.usePartial || templateSpec.useDecorators) {
container.decorators = container.merge(options.decorators, env.decorators);
}
} else {
container.helpers = options.helpers;
container.partials = options.partials;
container.decorators = options.decorators;
}
};
ret._child = function(i, data, blockParams, depths) {
if (templateSpec.useBlockParams && !blockParams) {
throw new Exception('must pass block params');
}
if (templateSpec.useDepths && !depths) {
throw new Exception('must pass parent depths');
}
return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths);
};
return ret;
}
export function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) {
function prog(context, options = {}) {
let currentDepths = depths;
if (depths && context != depths[0] && !(context === container.nullContext && depths[0] === null)) {
currentDepths = [context].concat(depths);
}
return fn(container,
context,
container.helpers, container.partials,
options.data || data,
blockParams && [options.blockParams].concat(blockParams),
currentDepths);
}
prog = executeDecorators(fn, prog, container, depths, data, blockParams);
prog.program = i;
prog.depth = depths ? depths.length : 0;
prog.blockParams = declaredBlockParams || 0;
return prog;
}
export function resolvePartial(partial, context, options) {
if (!partial) {
if (options.name === '@partial-block') {
partial = options.data['partial-block'];
} else {
partial = options.partials[options.name];
}
} else if (!partial.call && !options.name) {
// This is a dynamic partial that returned a string
options.name = partial;
partial = options.partials[partial];
}
return partial;
}
export function invokePartial(partial, context, options) {
// Use the current closure context to save the partial-block if this partial
const currentPartialBlock = options.data && options.data['partial-block'];
options.partial = true;
if (options.ids) {
options.data.contextPath = options.ids[0] || options.data.contextPath;
}
let partialBlock;
if (options.fn && options.fn !== noop) {
options.data = createFrame(options.data);
// Wrapper function to get access to currentPartialBlock from the closure
let fn = options.fn;
partialBlock = options.data['partial-block'] = function partialBlockWrapper(context, options = {}) {
// Restore the partial-block from the closure for the execution of the block
// i.e. the part inside the block of the partial call.
options.data = createFrame(options.data);
options.data['partial-block'] = currentPartialBlock;
return fn(context, options);
};
if (fn.partials) {
options.partials = Utils.extend({}, options.partials, fn.partials);
}
}
if (partial === undefined && partialBlock) {
partial = partialBlock;
}
if (partial === undefined) {
throw new Exception('The partial ' + options.name + ' could not be found');
} else if (partial instanceof Function) {
return partial(context, options);
}
}
export function noop() { return ''; }
function initData(context, data) {
if (!data || !('root' in data)) {
data = data ? createFrame(data) : {};
data.root = context;
}
return data;
}
function executeDecorators(fn, prog, container, depths, data, blockParams) {
if (fn.decorator) {
let props = {};
prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths);
Utils.extend(prog, props);
}
return prog;
}
+10
View File
@@ -0,0 +1,10 @@
// Build out our basic SafeString type
function SafeString(string) {
this.string = string;
}
SafeString.prototype.toString = SafeString.prototype.toHTML = function() {
return '' + this.string;
};
export default SafeString;
+108
View File
@@ -0,0 +1,108 @@
const escape = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;',
'=': '&#x3D;'
};
const badChars = /[&<>"'`=]/g,
possible = /[&<>"'`=]/;
function escapeChar(chr) {
return escape[chr];
}
export function extend(obj/* , ...source */) {
for (let i = 1; i < arguments.length; i++) {
for (let key in arguments[i]) {
if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
obj[key] = arguments[i][key];
}
}
}
return obj;
}
export let toString = Object.prototype.toString;
// Sourced from lodash
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
/* eslint-disable func-style */
let isFunction = function(value) {
return typeof value === 'function';
};
// fallback for older versions of Chrome and Safari
/* istanbul ignore next */
if (isFunction(/x/)) {
isFunction = function(value) {
return typeof value === 'function' && toString.call(value) === '[object Function]';
};
}
export {isFunction};
/* eslint-enable func-style */
/* istanbul ignore next */
export const isArray = Array.isArray || function(value) {
return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
};
// Older IE versions do not directly support indexOf so we must implement our own, sadly.
export function indexOf(array, value) {
for (let i = 0, len = array.length; i < len; i++) {
if (array[i] === value) {
return i;
}
}
return -1;
}
export function escapeExpression(string) {
if (typeof string !== 'string') {
// don't escape SafeStrings, since they're already safe
if (string && string.toHTML) {
return string.toHTML();
} else if (string == null) {
return '';
} else if (!string) {
return string + '';
}
// Force a string conversion as this will be done by the append regardless and
// the regex test will do this transparently behind the scenes, causing issues if
// an object's to string has escaped characters in it.
string = '' + string;
}
if (!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
}
export function isEmpty(value) {
if (!value && value !== 0) {
return true;
} else if (isArray(value) && value.length === 0) {
return true;
} else {
return false;
}
}
export function createFrame(object) {
let frame = extend({}, object);
frame._parent = object;
return frame;
}
export function blockParams(params, ids) {
params.path = ids;
return params;
}
export function appendContextPath(contextPath, id) {
return (contextPath ? contextPath + '.' : '') + id;
}
+25
View File
@@ -0,0 +1,25 @@
// USAGE:
// var handlebars = require('handlebars');
/* eslint-disable no-var */
// var local = handlebars.create();
var handlebars = require('../dist/cjs/handlebars')['default'];
var printer = require('../dist/cjs/handlebars/compiler/printer');
handlebars.PrintVisitor = printer.PrintVisitor;
handlebars.print = printer.print;
module.exports = handlebars;
// Publish a Node.js require() handler for .handlebars and .hbs files
function extension(module, filename) {
var fs = require('fs');
var templateString = fs.readFileSync(filename, 'utf8');
module.exports = handlebars.compile(templateString);
}
/* istanbul ignore else */
if (typeof require !== 'undefined' && require.extensions) {
require.extensions['.handlebars'] = extension;
require.extensions['.hbs'] = extension;
}
+298
View File
@@ -0,0 +1,298 @@
/* eslint-disable no-console */
import Async from 'neo-async';
import fs from 'fs';
import * as Handlebars from './handlebars';
import {basename} from 'path';
import {SourceMapConsumer, SourceNode} from 'source-map';
module.exports.loadTemplates = function(opts, callback) {
loadStrings(opts, function(err, strings) {
if (err) {
callback(err);
} else {
loadFiles(opts, function(err, files) {
if (err) {
callback(err);
} else {
opts.templates = strings.concat(files);
callback(undefined, opts);
}
});
}
});
};
function loadStrings(opts, callback) {
let strings = arrayCast(opts.string),
names = arrayCast(opts.name);
if (names.length !== strings.length
&& strings.length > 1) {
return callback(new Handlebars.Exception('Number of names did not match the number of string inputs'));
}
Async.map(strings, function(string, callback) {
if (string !== '-') {
callback(undefined, string);
} else {
// Load from stdin
let buffer = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(chunk) {
buffer += chunk;
});
process.stdin.on('end', function() {
callback(undefined, buffer);
});
}
},
function(err, strings) {
strings = strings.map((string, index) => ({
name: names[index],
path: names[index],
source: string
}));
callback(err, strings);
});
}
function loadFiles(opts, callback) {
// Build file extension pattern
let extension = (opts.extension || 'handlebars').replace(/[\\^$*+?.():=!|{}\-[\]]/g, function(arg) { return '\\' + arg; });
extension = new RegExp('\\.' + extension + '$');
let ret = [],
queue = (opts.files || []).map((template) => ({template, root: opts.root}));
Async.whilst(() => queue.length, function(callback) {
let {template: path, root} = queue.shift();
fs.stat(path, function(err, stat) {
if (err) {
return callback(new Handlebars.Exception(`Unable to open template file "${path}"`));
}
if (stat.isDirectory()) {
opts.hasDirectory = true;
fs.readdir(path, function(err, children) {
/* istanbul ignore next : Race condition that being too lazy to test */
if (err) {
return callback(err);
}
children.forEach(function(file) {
let childPath = path + '/' + file;
if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) {
queue.push({template: childPath, root: root || path});
}
});
callback();
});
} else {
fs.readFile(path, 'utf8', function(err, data) {
/* istanbul ignore next : Race condition that being too lazy to test */
if (err) {
return callback(err);
}
if (opts.bom && data.indexOf('\uFEFF') === 0) {
data = data.substring(1);
}
// Clean the template name
let name = path;
if (!root) {
name = basename(name);
} else if (name.indexOf(root) === 0) {
name = name.substring(root.length + 1);
}
name = name.replace(extension, '');
ret.push({
path: path,
name: name,
source: data
});
callback();
});
}
});
},
function(err) {
if (err) {
callback(err);
} else {
callback(undefined, ret);
}
});
}
module.exports.cli = function(opts) {
if (opts.version) {
console.log(Handlebars.VERSION);
return;
}
if (!opts.templates.length && !opts.hasDirectory) {
throw new Handlebars.Exception('Must define at least one template or directory.');
}
if (opts.simple && opts.min) {
throw new Handlebars.Exception('Unable to minimize simple output');
}
const multiple = opts.templates.length !== 1 || opts.hasDirectory;
if (opts.simple && multiple) {
throw new Handlebars.Exception('Unable to output multiple templates in simple mode');
}
// Force simple mode if we have only one template and it's unnamed.
if (!opts.amd && !opts.commonjs && opts.templates.length === 1
&& !opts.templates[0].name) {
opts.simple = true;
}
// Convert the known list into a hash
let known = {};
if (opts.known && !Array.isArray(opts.known)) {
opts.known = [opts.known];
}
if (opts.known) {
for (let i = 0, len = opts.known.length; i < len; i++) {
known[opts.known[i]] = true;
}
}
const objectName = opts.partial ? 'Handlebars.partials' : 'templates';
let output = new SourceNode();
if (!opts.simple) {
if (opts.amd) {
output.add('define([\'' + opts.handlebarPath + 'handlebars.runtime\'], function(Handlebars) {\n Handlebars = Handlebars["default"];');
} else if (opts.commonjs) {
output.add('var Handlebars = require("' + opts.commonjs + '");');
} else {
output.add('(function() {\n');
}
output.add(' var template = Handlebars.template, templates = ');
if (opts.namespace) {
output.add(opts.namespace);
output.add(' = ');
output.add(opts.namespace);
output.add(' || ');
}
output.add('{};\n');
}
opts.templates.forEach(function(template) {
let options = {
knownHelpers: known,
knownHelpersOnly: opts.o
};
if (opts.map) {
options.srcName = template.path;
}
if (opts.data) {
options.data = true;
}
let precompiled = Handlebars.precompile(template.source, options);
// If we are generating a source map, we have to reconstruct the SourceNode object
if (opts.map) {
let consumer = new SourceMapConsumer(precompiled.map);
precompiled = SourceNode.fromStringWithSourceMap(precompiled.code, consumer);
}
if (opts.simple) {
output.add([precompiled, '\n']);
} else {
if (!template.name) {
throw new Handlebars.Exception('Name missing for template');
}
if (opts.amd && !multiple) {
output.add('return ');
}
output.add([objectName, '[\'', template.name, '\'] = template(', precompiled, ');\n']);
}
});
// Output the content
if (!opts.simple) {
if (opts.amd) {
if (multiple) {
output.add(['return ', objectName, ';\n']);
}
output.add('});');
} else if (!opts.commonjs) {
output.add('})();');
}
}
if (opts.map) {
output.add('\n//# sourceMappingURL=' + opts.map + '\n');
}
output = output.toStringWithSourceMap();
output.map = output.map + '';
if (opts.min) {
output = minify(output, opts.map);
}
if (opts.map) {
fs.writeFileSync(opts.map, output.map, 'utf8');
}
output = output.code;
if (opts.output) {
fs.writeFileSync(opts.output, output, 'utf8');
} else {
console.log(output);
}
};
function arrayCast(value) {
value = value != null ? value : [];
if (!Array.isArray(value)) {
value = [value];
}
return value;
}
/**
* Run uglify to minify the compiled template, if uglify exists in the dependencies.
*
* We are using `require` instead of `import` here, because es6-modules do not allow
* dynamic imports and uglify-js is an optional dependency. Since we are inside NodeJS here, this
* should not be a problem.
*
* @param {string} output the compiled template
* @param {string} sourceMapFile the file to write the source map to.
*/
function minify(output, sourceMapFile) {
try {
// Try to resolve uglify-js in order to see if it does exist
require.resolve('uglify-js');
} catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') {
// Something else seems to be wrong
throw e;
}
// it does not exist!
console.error('Code minimization is disabled due to missing uglify-js dependency');
return output;
}
return require('uglify-js').minify(output.code, {
sourceMap: {
content: output.map,
url: sourceMapFile
}
});
}