UNPKG

@cloudpss/template

Version:

String and object template engine for Node.js and the browser.

166 lines 6.07 kB
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