pip-services4-expressions-node
Version:
Tokenizers, parsers and expression calculators in Node.js / ES2017
220 lines (191 loc) • 7.01 kB
text/typescript
/** @module mustache */
import { MustacheParser } from "./parsers/MustacheParser";
import { MustacheToken } from "./parsers/MustacheToken";
import { MustacheTokenType } from "./parsers/MustacheTokenType";
import { Token } from "../tokenizers/Token";
import { MustacheException } from "./MustacheException";
/**
* Implements an mustache template class.
*/
export class MustacheTemplate {
private _defaultVariables: any = {};
private _parser: MustacheParser = new MustacheParser();
private _autoVariables = true;
/**
* Constructs this class and assigns mustache template.
* @param template The mustache template.
*/
public constructor(template?: string) {
if (template != null) {
this.template = template;
}
}
/**
* The mustache template.
*/
public get template(): string {
return this._parser.template;
}
/**
* The mustache template.
*/
public set template(value: string) {
this._parser.template = value;
if (this._autoVariables) {
this.createVariables(this._defaultVariables);
}
}
public get originalTokens(): Token[] {
return this._parser.originalTokens;
}
public set originalTokens(value: Token[]) {
this._parser.originalTokens = value;
if (this._autoVariables) {
this.createVariables(this._defaultVariables);
}
}
/**
* Gets the flag to turn on auto creation of variables for specified mustache.
*/
public get autoVariables(): boolean {
return this._autoVariables;
}
/**
* Sets the flag to turn on auto creation of variables for specified mustache.
*/
public set autoVariables(value: boolean) {
this._autoVariables = value;
}
/**
* The list with default variables.
*/
public get defaultVariables(): any {
return this._defaultVariables;
}
/**
* The list of original mustache tokens.
*/
public get initialTokens(): MustacheToken[] {
return this._parser.initialTokens;
}
/**
* The list of processed mustache tokens.
*/
public get resultTokens(): MustacheToken[] {
return this._parser.resultTokens;
}
/**
* Gets a variable value from the collection of variables
* @param variables a collection of variables.
* @param name a variable name to get.
* @returns a variable value or <code>undefined</code>
*/
public getVariable(variables: any, name: string): any {
if (variables == null || name == null) return undefined;
name = name.toLowerCase();
let result = undefined;
for (const propName in variables) {
if (propName.toLowerCase() == name) {
result = result || variables[propName];
}
}
return result;
}
/**
* Populates the specified variables list with variables from parsed mustache.
* @param variables The list of variables to be populated.
*/
public createVariables(variables: any): void {
if (variables == null) return;
for (const variableName of this._parser.variableNames) {
const found = this.getVariable(variables, variableName) != undefined;
if (!found) {
variables[variableName] = null;
}
}
}
/**
* Cleans up this calculator from all data.
*/
public clear(): void {
this._parser.clear();
this._defaultVariables = {};
}
/**
* Evaluates this mustache template using default variables.
* @returns the evaluated template
*/
public evaluate(): string {
return this.evaluateWithVariables(null);
}
/**
* Evaluates this mustache using specified variables.
* @param variables The collection of variables
* @returns the evaluated template
*/
public evaluateWithVariables(variables: any): string {
variables = variables || this._defaultVariables;
return this.evaluateTokens(this._parser.resultTokens, variables);
}
private isDefinedVariable(variables: any, name: string): boolean {
const value = this.getVariable(variables, name);
return value != null && value != "";
}
private escapeString(value: string): string {
if (value == null) return null;
return value
.replace(/[\\]/g, '\\\\')
.replace(/["]/g, '\\"')
.replace(/[/]/g, '/')
.replace(/[\b]/g, '\\b')
.replace(/[\f]/g, '\\f')
.replace(/[\n]/g, '\\n')
.replace(/[\r]/g, '\\r')
.replace(/[\t]/g, '\\t');
}
private evaluateTokens(tokens: MustacheToken[], variables: any): string {
if (tokens == null) return null;
let result = "";
for (const token of tokens) {
switch (token.type) {
case MustacheTokenType.Comment:
// Skip;
break;
case MustacheTokenType.Value: {
result += token.value || "";
break;
}
case MustacheTokenType.Variable: {
const value1 = this.getVariable(variables, token.value);
result += value1 || "";
break;
}
case MustacheTokenType.EscapedVariable: {
let value2 = this.getVariable(variables, token.value);
value2 = this.escapeString(value2);
result += value2 || "";
break;
}
case MustacheTokenType.Section: {
const defined1 = this.isDefinedVariable(variables, token.value);
if (defined1 && token.tokens != null) {
result += this.evaluateTokens(token.tokens, variables);
}
break;
}
case MustacheTokenType.InvertedSection: {
const defined2 = this.isDefinedVariable(variables, token.value);
if (!defined2 && token.tokens != null) {
result += this.evaluateTokens(token.tokens, variables);
}
break;
}
case MustacheTokenType.Partial:
throw new MustacheException(null, "PARTIALS_NOT_SUPPORTED", "Partials are not supported", token.line, token.column);
default:
throw new MustacheException(null, "INTERNAL", "Internal error", token.line, token.column);
}
}
return result;
}
}