@cloudpss/template
Version:
String and object template engine for Node.js and the browser.
166 lines • 6.07 kB
JavaScript
const PARSER_STATUS_TEXT = 0;
const PARSER_STATUS_EXPRESSION_SIMPLE = 1;
const PARSER_STATUS_EXPRESSION_COMPLEX = 2;
/** 检查字符满足 [a-zA-Z0-9_] */
function isIdentifierChar(char) {
const charCode = char.charCodeAt(0);
return ((charCode >= 97 && charCode <= 122) ||
(charCode >= 65 && charCode <= 90) ||
(charCode >= 48 && charCode <= 57) ||
charCode === 95);
}
/** 检查字符满足 [a-zA-Z_] */
function isIdentifierFirstChar(char) {
const charCode = char.charCodeAt(0);
return (charCode >= 97 && charCode <= 122) || (charCode >= 65 && charCode <= 90) || charCode === 95;
}
/** 字符串插值模板标识符 */
const INTERPOLATION_CHAR = '$';
/** 字符串插值模板表达式开始标识符 */
const INTERPOLATION_EXPRESSION_START = ['{', '[', '<', '('];
/** 字符串插值模板表达式结束标识符 */
const INTERPOLATION_EXPRESSION_END = ['}', ']', '>', ')'];
/**
* 解析字符串插值模板
* @example parseInterpolationImpl("hello $name! I am $age years old. I'll be ${age+1} next year. Give me $$100.")
* // {
* // type: 'interpolation',
* // templates: ['hello ', '! I am ', ' years old. I\'ll be ', ' next year. Give me $$100.'],
* // values: ['name', 'age', 'age+1']
* // }
*/
function parseInterpolationImpl(template, start, length) {
const templates = [];
const values = [];
let currentTemplate = '';
let currentValue = '';
let expressionComplexDepth = 0;
let status = PARSER_STATUS_TEXT;
let complexExpressionStartType = -1;
const end = start + length - 1;
for (let i = start; i <= end; i++) {
if (status === PARSER_STATUS_TEXT) {
const nextInterpolationChar = template.indexOf(INTERPOLATION_CHAR, i);
if (nextInterpolationChar < 0 || nextInterpolationChar >= end) {
// No more interpolation
currentTemplate += template.slice(i, end + 1);
break;
}
currentTemplate += template.slice(i, nextInterpolationChar);
i = nextInterpolationChar;
const nextChar = template.charAt(nextInterpolationChar + 1);
complexExpressionStartType = INTERPOLATION_EXPRESSION_START.indexOf(nextChar);
if (complexExpressionStartType >= 0) {
// Start of complex expression
templates.push(currentTemplate);
currentTemplate = '';
status = PARSER_STATUS_EXPRESSION_COMPLEX;
expressionComplexDepth = 1;
i++;
continue;
}
if (isIdentifierFirstChar(nextChar)) {
// Start of simple expression
templates.push(currentTemplate);
currentTemplate = '';
currentValue = nextChar;
status = PARSER_STATUS_EXPRESSION_SIMPLE;
i++;
continue;
}
// Not an expression
currentTemplate += INTERPOLATION_CHAR;
continue;
}
const char = template.charAt(i);
if (status === PARSER_STATUS_EXPRESSION_SIMPLE) {
if (isIdentifierChar(char)) {
currentValue += char;
continue;
}
// End of expression
values.push(currentValue);
currentValue = '';
status = PARSER_STATUS_TEXT;
i--;
continue;
}
if (status === PARSER_STATUS_EXPRESSION_COMPLEX) {
if (char === INTERPOLATION_EXPRESSION_START[complexExpressionStartType]) {
expressionComplexDepth++;
}
else if (char === INTERPOLATION_EXPRESSION_END[complexExpressionStartType]) {
expressionComplexDepth--;
if (expressionComplexDepth === 0) {
// End of expression
values.push(currentValue.trim());
currentValue = '';
status = PARSER_STATUS_TEXT;
continue;
}
}
currentValue += char;
continue;
}
}
if (status === PARSER_STATUS_TEXT) {
templates.push(currentTemplate);
}
else if (status === PARSER_STATUS_EXPRESSION_SIMPLE) {
values.push(currentValue);
templates.push('');
}
else {
throw new Error('Unexpected end of input');
}
return {
type: 'interpolation',
templates,
values,
};
}
/**
* 解析字符串插值模板
* @example parseInterpolation("hello $name! I am $age years old. I'll be ${age+1} next year. And $(2*age) after $<age> years.")
* // {
* // type: 'interpolation',
* // templates: ['hello ', '! I am ', ' years old. I\'ll be ', ' next year. And ', ' after ', ' years.'],
* // values: ['name', 'age', 'age+1', '2*age', 'age']
* // }
*/
export function parseInterpolation(template, start, length) {
start ??= 0;
length ??= template.length - start;
if (start < 0 || start > template.length)
throw new Error('start out of range');
if (length < 0 || start + length > template.length)
throw new Error('length out of range');
return parseInterpolationImpl(template, start, length);
}
/**
* 解析字符串模板
* - 长度大于 1
* - 如果模板以 `=` 开头,则表示是一个公式
* - 如果模板以 `$` 开头,则表示是一个插值模板
* - 否则表示是一个普通字符串
*/
export function parseTemplate(template) {
if (!template)
return '';
if (template.length <= 1)
return template;
if (template.startsWith('=')) {
return {
type: 'formula',
value: template.slice(1),
};
}
if (template.startsWith('$')) {
const result = parseInterpolationImpl(template, 1, template.length - 1);
if (result.values.length === 0)
return result.templates[0];
return result;
}
return template;
}
//# sourceMappingURL=parser.js.map