diff --git a/dist/abs-template.d.ts b/dist/abs-template.d.ts index fdd01f9..2c9debc 100644 --- a/dist/abs-template.d.ts +++ b/dist/abs-template.d.ts @@ -13,17 +13,21 @@ export interface AbsTemplateBuildConfig { } export declare class AbsTemplate { private static readonly CONSOLE_PREFIX; - private static readonly PARAMETER_PATTERN; - private static readonly CONDITION_STATEMENT_PATTERN; - private static readonly CONDITION_PATTERN; - private static readonly CYCLE_STATEMENT_PATTERN; + private static readonly VALUE_STATEMENT_OPEN; + private static readonly VALUE_PATTERN_STRING; + private static readonly CONDITION_STATEMENT_OPEN; + private static readonly CONDITION_STATEMENT_PATTERN_STRING; + private static readonly CONDITION_PATTERN_STRING; + private static readonly CONDITION_STATEMENT_CLOSE; + private static readonly CYCLE_STATEMENT_OPEN; + private static readonly CYCLE_STATEMENT_PATTERN_STRING; + private static readonly CYCLE_STATEMENT_CLOSE; static build(config: AbsTemplateBuildConfig): void; private static getContentFromTemplateNode; private static print; - private static getParseMatches; - private static parseParameters; - private static parseConditions; - private static parseCycles; + private static parseValue; + private static parseCondition; + private static parseCycle; private static parse; static compile(template: string, data: AbsTemplateData): string; private static _utils; diff --git a/dist/abs-template.js b/dist/abs-template.js index 4f663b3..eacfd04 100644 --- a/dist/abs-template.js +++ b/dist/abs-template.js @@ -1,3 +1,4 @@ +var _a; export var AbsTemplatePrintMethod; (function (AbsTemplatePrintMethod) { AbsTemplatePrintMethod["BEFORE_BEGIN"] = "beforebegin"; @@ -31,58 +32,47 @@ export class AbsTemplate { } node.forEach(nodeItem => { nodeItem.nodeType !== Node.TEXT_NODE && target.insertAdjacentElement(method, nodeItem); + nodeItem.nodeType === Node.TEXT_NODE && target.insertAdjacentText(method, nodeItem.nodeValue); }); } - static parseParameters(template, data, patternOverride) { - const parameterPattern = new RegExp(patternOverride || this.PARAMETER_PATTERN, ''); - const matches = this.getParseMatches(template, patternOverride || this.PARAMETER_PATTERN); - matches?.forEach(match => { - const dataMatches = parameterPattern.exec(match); - const key = dataMatches[1]; - const keyValue = data[key]; - if (keyValue || keyValue === '') { - template = template.replace(match, keyValue); - } - }); - return template; + static parseValue(template, data) { + let compiledTemplate = ''; + const matches = template.match(new RegExp(this.VALUE_PATTERN_STRING)); + if (matches?.length) { + const fullMatch = matches[0]; + const valueIdentifier = matches[1]; + const valueFromData = this._utils.getValueByPath(data, valueIdentifier); + compiledTemplate = valueFromData !== undefined ? valueFromData : fullMatch; + } + return compiledTemplate; } - static parseConditions(template, data) { - const conditionStatementPattern = new RegExp(this.CONDITION_STATEMENT_PATTERN, ''); - const conditionPattern = new RegExp(this.CONDITION_PATTERN, ''); - const matches = this.getParseMatches(template, this.CONDITION_STATEMENT_PATTERN); - matches?.forEach(match => { - const matchGroups = conditionStatementPattern.exec(match); - const statementBlock = matchGroups[0]; - const condition = matchGroups[1]; + static parseCondition(template, data) { + const conditionStatementPattern = new RegExp(this.CONDITION_STATEMENT_PATTERN_STRING, ''); + const conditionPattern = new RegExp(this.CONDITION_PATTERN_STRING, ''); + let compiledTemplate = ''; + const statementMatches = template.match(conditionStatementPattern); + if (statementMatches?.length) { + const condition = statementMatches[1]; const parsedCondition = conditionPattern.exec(condition); - const isConditionSingleParameter = !Boolean(parsedCondition); - const positiveContent = matchGroups[2]; - const negativeContent = matchGroups[3]; - const printConditionResult = (conditionResult) => { - if (Boolean(conditionResult)) { - template = template.replace(statementBlock, positiveContent); - } - else { - template = template.replace(statementBlock, negativeContent || ''); - } - }; - if (isConditionSingleParameter) { - const parameter = data[condition]; - printConditionResult(Boolean(parameter)); + const isConditionImplicit = !Boolean(parsedCondition); + const positiveContent = statementMatches[2]; + const negativeContent = statementMatches[3]; + if (isConditionImplicit) { + const valueFromData = this._utils.getValueByPath(data, condition); + const implicitCheck = Boolean(valueFromData); + const parsedContent = this._utils.if.parseContentFromCondition(implicitCheck, data, positiveContent, negativeContent); + compiledTemplate = parsedContent; } - else { - const sanitizeParameter = (parameter) => { - return (!Number.isNaN(parseFloat(parameter)) ? parseFloat(parameter) : - parameter === 'true' ? true : - parameter === 'false' ? false : - parameter === 'undefined' ? undefined : - parameter === 'null' ? null : - parameter); - }; - const conditionMatchGroups = conditionPattern.exec(condition); - const firstParameter = sanitizeParameter(conditionMatchGroups[1]); - const operator = conditionMatchGroups[2]; - const secondParameter = sanitizeParameter(conditionMatchGroups[3]); + else if (parsedCondition?.length) { + const firstSanitizedParameter = this._utils.if.sanitizeParameter(parsedCondition[1]); + const firstParameter = (this._utils.if.isParameterLiteral(firstSanitizedParameter) ? + this._utils.if.fixStringLiteral(firstSanitizedParameter) : + this._utils.getValueByPath(data, firstSanitizedParameter)); + const secondSanitizedParameter = this._utils.if.sanitizeParameter(parsedCondition[3]); + const secondParameter = (this._utils.if.isParameterLiteral(secondSanitizedParameter) ? + this._utils.if.fixStringLiteral(secondSanitizedParameter) : + this._utils.getValueByPath(data, secondSanitizedParameter)); + const operator = parsedCondition[2]; let conditionResult = false; switch (operator) { case '==': @@ -116,49 +106,110 @@ export class AbsTemplate { conditionResult = Boolean(parseFloat(firstParameter) ^ parseFloat(secondParameter)); break; } - printConditionResult(conditionResult); + const parsedContent = this._utils.if.parseContentFromCondition(conditionResult, data, positiveContent, negativeContent); + compiledTemplate = parsedContent; } - }); - return template; + } + return compiledTemplate; } - static parseCycles(template, data) { - const cycleStatementPattern = new RegExp(this.CYCLE_STATEMENT_PATTERN, ''); - const matches = this.getParseMatches(template, this.CYCLE_STATEMENT_PATTERN); - matches?.forEach(match => { - const matchGroups = cycleStatementPattern.exec(match); - const itemKey = matchGroups[1]; - const listKey = matchGroups[2]; - const cycleContent = matchGroups[3]; - const templateData = data; - if (templateData) { - const list = templateData[listKey]; - let res = ''; + static parseCycle(template, data) { + let compiledTemplate = ''; + const matches = template.match(new RegExp(this.CYCLE_STATEMENT_PATTERN_STRING)); + if (matches) { + const keyOfListIdentifier = matches[1]; + const listIdentifier = matches[2]; + const cycleContent = matches[3]; + if (data) { + const list = this._utils.getValueByPath(data, listIdentifier); if (!Array.isArray(list)) - throw `${this.CONSOLE_PREFIX} Template contains a "forEach" with a parameter that cannot be iterated.`; + throw `${this.CONSOLE_PREFIX} Parameter "${listIdentifier}" is not iterable.`; list.forEach(listItem => { - const subParamRegex = new RegExp(`\\\{\\\{${itemKey}\\.(.+?)\\\}\\\}`, 'g'); - res += this.parseParameters(cycleContent, listItem, subParamRegex); + const iterationData = { + ...this._utils.deepCopy(data), + [keyOfListIdentifier]: listItem, + }; + compiledTemplate += this.parse(cycleContent, iterationData); }); - template = template.replace(match, res); } - }); - return template; + } + return compiledTemplate; } static parse(template, data) { - template = this.parseConditions(template, data); - template = this.parseCycles(template, data); - template = this.parseParameters(template, data); - return template; + let isTagOpen = false; + let tagOpenStack = 0; + let currentClosingTag = ''; + let openTagIndex = -1; + let closeTagIndex = -1; + let compiledTemplate = ''; + for (let i = 0; i < template.length; i++) { + const isConditionOpening = template.slice(i, i + this.CONDITION_STATEMENT_OPEN.length) === this.CONDITION_STATEMENT_OPEN; + const isCycleOpening = template.slice(i, i + this.CYCLE_STATEMENT_OPEN.length) === this.CYCLE_STATEMENT_OPEN; + const isValueOpening = template.slice(i, i + this.VALUE_STATEMENT_OPEN.length) === this.VALUE_STATEMENT_OPEN; + const isClosingCurrentTag = isTagOpen && currentClosingTag && template.slice(i, i + currentClosingTag.length) === currentClosingTag; + const isOpeningNested = isTagOpen && ((isCycleOpening && currentClosingTag === this.CYCLE_STATEMENT_CLOSE) || + (isConditionOpening && currentClosingTag === this.CONDITION_STATEMENT_CLOSE)); + if (isConditionOpening || isCycleOpening) { + if (isOpeningNested) { + tagOpenStack++; + } + else if (!isTagOpen) { + isTagOpen = true; + openTagIndex = i; + currentClosingTag = + isCycleOpening ? this.CYCLE_STATEMENT_CLOSE : + isConditionOpening ? this.CONDITION_STATEMENT_CLOSE : + ''; + } + } + else if (isClosingCurrentTag) { + if (tagOpenStack !== 0) { + tagOpenStack--; + } + else { + isTagOpen = false; + closeTagIndex = i + currentClosingTag.length; + i += (currentClosingTag.length - 1); + const currentBlockTemplate = template.slice(openTagIndex, closeTagIndex); + const isBlockCondition = currentBlockTemplate.startsWith(this.CONDITION_STATEMENT_OPEN); + const isBlockCycle = currentBlockTemplate.startsWith(this.CYCLE_STATEMENT_OPEN); + const currentParsedBlock = (isBlockCondition ? this.parseCondition(currentBlockTemplate, data) : + isBlockCycle ? this.parseCycle(currentBlockTemplate, data) : + ''); + compiledTemplate += currentParsedBlock; + openTagIndex = -1; + closeTagIndex = -1; + } + } + else if (isValueOpening && !isTagOpen) { + const valueMatches = template.slice(i).match(new RegExp(this.VALUE_PATTERN_STRING)); + if (valueMatches) { + const fullMatch = valueMatches[0]; + const compiledValue = this.parseValue(fullMatch, data); + compiledTemplate += compiledValue; + i += (fullMatch.length - 1); + } + } + else if (!isTagOpen) { + compiledTemplate += template[i]; + } + } + return compiledTemplate; } static compile(template, data) { return this.parse(template, data); } } +_a = AbsTemplate; AbsTemplate.CONSOLE_PREFIX = '[ABS][TEMPLATE]'; -AbsTemplate.PARAMETER_PATTERN = /\{\{(.+?)\}\}/; -AbsTemplate.CONDITION_STATEMENT_PATTERN = /\{\{if (.+?)\}\}(.+?)(?:\{\{else\}\}(.+?))?\{\{\/if\}\}/; -AbsTemplate.CONDITION_PATTERN = /(.+) (==|===|!=|!==|>|>=|<|<=|&&|\|\||%|\^) (.+)/; -AbsTemplate.CYCLE_STATEMENT_PATTERN = /\{\{forEach (.+?) in (.+?)\}\}(.+?)\{\{\/forEach\}\}/; +AbsTemplate.VALUE_STATEMENT_OPEN = '{{'; +AbsTemplate.VALUE_PATTERN_STRING = '{{(.+?)}}'; +AbsTemplate.CONDITION_STATEMENT_OPEN = '{{if'; +AbsTemplate.CONDITION_STATEMENT_PATTERN_STRING = '{{if (.+?)}}(.+?)(?:{{else}}(.+?))?{{/if}}'; +AbsTemplate.CONDITION_PATTERN_STRING = '(.+) (==|===|!=|!==|>|>=|<|<=|&&|\|\||%|\^) (.+)'; +AbsTemplate.CONDITION_STATEMENT_CLOSE = '{{/if}}'; +AbsTemplate.CYCLE_STATEMENT_OPEN = '{{forEach'; +AbsTemplate.CYCLE_STATEMENT_PATTERN_STRING = '{{forEach (.+?) in (.+?)}}(.*){{/forEach}}'; +AbsTemplate.CYCLE_STATEMENT_CLOSE = '{{/forEach}}'; AbsTemplate.getContentFromTemplateNode = (templateNode) => { const templateNodeContent = templateNode.innerHTML; const domParser = new DOMParser(); @@ -166,11 +217,6 @@ AbsTemplate.getContentFromTemplateNode = (templateNode) => { const parsedDocumentBodyNode = parsedDocument.querySelector('body'); return parsedDocumentBodyNode.innerHTML; }; -AbsTemplate.getParseMatches = (template, pattern) => { - const parameterGlobalPattern = new RegExp(pattern, 'g'); - const matches = template.match(parameterGlobalPattern); - return matches; -}; AbsTemplate._utils = { nodeToString: (node) => { return node.outerHTML.replaceAll('\n', ''); @@ -182,6 +228,77 @@ AbsTemplate._utils = { }, removeCharacterFromString: (string, characterIndex) => { return string.substring(0, characterIndex) + string.substring(characterIndex + 1, string.length); - } + }, + deepCopy: (inObject) => { + let outObject, value, key; + if (typeof inObject !== 'object' || inObject === null) { + return inObject; + } + outObject = Array.isArray(inObject) ? [] : {}; + for (key in inObject) { + value = inObject[key]; + outObject[key] = _a._utils.deepCopy(value); + } + return outObject; + }, + getValueByPath: (obj, path) => { + const keys = path.split('.'); + let value = obj; + for (const key of keys) { + if (value && typeof value === 'object' && key in value) { + value = value[key]; + } + else { + return undefined; + } + } + return value; + }, + if: { + parseContentFromCondition: (conditionResult, data, positiveContent, negativeContent) => { + let compiledContent = ''; + compiledContent = _a.parse(conditionResult ? positiveContent || '' : negativeContent || '', data); + return compiledContent; + }, + sanitizeParameter: (parameter) => { + return (!Number.isNaN(parseFloat(parameter)) ? parseFloat(parameter) : + parameter === 'true' ? true : + parameter === 'false' ? false : + parameter === 'undefined' ? undefined : + parameter === 'null' ? null : + parameter); + }, + isParameterLiteralString: (parameter) => { + return typeof parameter === 'string' && ((parameter.startsWith(`'`) && parameter.endsWith(`'`)) || + (parameter.startsWith(`"`) && parameter.endsWith(`"`))); + }, + fixStringLiteral: (parameter) => { + if (_a._utils.if.isParameterLiteralString(parameter)) { + return parameter.slice(1, parameter.length - 1); + } + else { + return parameter; + } + }, + isParameterLiteral: (parameter) => { + const isParamKeyword = Boolean(typeof parameter === 'number' || + _a._utils.if.isParameterLiteralString(parameter) || + parameter === true || + parameter === false || + parameter === null || + parameter === undefined); + return isParamKeyword; + }, + }, + _log: (template, position) => { + let res = ''; + const COL = '%c'; + res += COL; + for (let i = 0; i < template.length; i++) { + res += i === position ? COL : ''; + res += template[i]; + } + console.log(res, 'color: lime;', 'color: white;'); + }, }; //# sourceMappingURL=abs-template.js.map \ No newline at end of file diff --git a/dist/abs-template.js.map b/dist/abs-template.js.map index 55f4b79..c0ee5d6 100644 --- a/dist/abs-template.js.map +++ b/dist/abs-template.js.map @@ -1 +1 @@ -{"version":3,"file":"abs-template.js","sourceRoot":"","sources":["../src/abs-template.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,sBAKX;AALD,WAAY,sBAAsB;IAChC,sDAA4B,CAAA;IAC5B,kDAAwB,CAAA;IACxB,oDAA0B,CAAA;IAC1B,gDAAsB,CAAA;AACxB,CAAC,EALW,sBAAsB,KAAtB,sBAAsB,QAKjC;AAAA,CAAC;AASD,CAAC;AAEF,MAAM,OAAO,WAAW;IAOf,MAAM,CAAC,KAAK,CAAC,MAA8B;QAChD,IAAI;YACF,IAAG,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;gBAAE,MAAO,GAAG,IAAI,CAAC,cAAc,gDAAgD,CAAC;YAChH,IAAI,yBAAyB,GAAG,IAAI,CAAC,0BAA0B,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAErF,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC;YAC3F,IAAG,aAAa,EAAE;gBAChB,yBAAyB,GAAG,yBAAyB,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC3E,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAAC,yBAAyB,EAAE,MAAM,CAAC,YAA+B,CAAC,CAAC;aAC3G;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,yBAAyB,CAAC,CAAC;YACvE,IAAI,CAAC,KAAK,CAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAmB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;SAC9G;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SACtB;IACH,CAAC;IAUO,MAAM,CAAC,KAAK,CAAC,IAAmB,EAAE,MAAmB,EAAE,MAA8B;QAC3F,IAAG,MAAM,KAAK,sBAAsB,CAAC,WAAW,IAAI,MAAM,KAAK,sBAAsB,CAAC,SAAS,EAAE;YAC/F,IAAI,CAAC,OAAO,EAAE,CAAC;SAChB;QACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;YACtB,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;IACL,CAAC;IAQO,MAAM,CAAC,eAAe,CAAC,QAAgB,EAAE,IAAqB,EAAE,eAAwB;QAC9F,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,eAAe,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QACnF,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,eAAe,IAAI,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC1F,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE;YACvB,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAkB,CAAC;YAClE,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,QAAQ,GAAI,IAA+B,CAAC,GAAG,CAAC,CAAC;YACvD,IAAG,QAAQ,IAAI,QAAQ,KAAK,EAAE,EAAE;gBAC9B,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;aAC9C;QACH,CAAC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,MAAM,CAAC,eAAe,CAAC,QAAgB,EAAE,IAAqB;QAQpE,MAAM,yBAAyB,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC;QACnF,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACjF,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE;YACvB,MAAM,WAAW,GAAG,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAkB,CAAC;YAC3E,MAAM,cAAc,GAAW,WAAW,CAAC,CAAC,CAAC,CAAC;YAC9C,MAAM,SAAS,GAAW,WAAW,CAAC,CAAC,CAAC,CAAC;YACzC,MAAM,eAAe,GAA4B,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAkB,CAAC;YACnG,MAAM,0BAA0B,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YAC7D,MAAM,eAAe,GAAW,WAAW,CAAC,CAAC,CAAC,CAAC;YAC/C,MAAM,eAAe,GAAqB,WAAW,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,oBAAoB,GAAG,CAAC,eAAwB,EAAQ,EAAE;gBAC9D,IAAG,OAAO,CAAC,eAAe,CAAC,EAAE;oBAC3B,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;iBAC9D;qBAAM;oBACL,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,eAAe,IAAI,EAAE,CAAC,CAAC;iBACpE;YACH,CAAC,CAAC;YACF,IAAG,0BAA0B,EAAE;gBAC7B,MAAM,SAAS,GAAI,IAA8B,CAAC,SAAS,CAAC,CAAC;gBAC7D,oBAAoB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;aAC1C;iBAAM;gBACL,MAAM,iBAAiB,GAAG,CAAC,SAAiB,EAAwC,EAAE;oBACpF,OAAO,CACL,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;wBAC9D,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;4BAC7B,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gCAC/B,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;oCACvC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;wCAC7B,SAAS,CACV,CAAC;gBACJ,CAAC,CAAC;gBACF,MAAM,oBAAoB,GAAG,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAkB,CAAC;gBAC/E,MAAM,cAAc,GAAG,iBAAiB,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;gBAClE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;gBACzC,MAAM,eAAe,GAAG,iBAAiB,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;gBACnE,IAAI,eAAe,GAAY,KAAK,CAAC;gBACrC,QAAO,QAAQ,EAAE;oBACf,KAAK,IAAI;wBAAG,eAAe,GAAG,OAAO,CAAE,cAAsB,IAAM,eAAuB,CAAC,CAAC;wBAAC,MAAM;oBACnG,KAAK,KAAK;wBAAE,eAAe,GAAG,OAAO,CAAE,cAAsB,KAAM,eAAuB,CAAC,CAAC;wBAAC,MAAM;oBACnG,KAAK,IAAI;wBAAG,eAAe,GAAG,OAAO,CAAE,cAAsB,IAAM,eAAuB,CAAC,CAAC;wBAAC,MAAM;oBACnG,KAAK,KAAK;wBAAE,eAAe,GAAG,OAAO,CAAE,cAAsB,KAAM,eAAuB,CAAC,CAAC;wBAAC,MAAM;oBACnG,KAAK,GAAG;wBAAI,eAAe,GAAG,OAAO,CAAE,cAAsB,GAAM,eAAuB,CAAC,CAAC;wBAAC,MAAM;oBACnG,KAAK,IAAI;wBAAG,eAAe,GAAG,OAAO,CAAE,cAAsB,IAAM,eAAuB,CAAC,CAAC;wBAAC,MAAM;oBAGnG,KAAK,IAAI;wBAAG,eAAe,GAAG,OAAO,CAAE,cAAsB,IAAM,eAAuB,CAAC,CAAC;wBAAC,MAAM;oBACnG,KAAK,IAAI;wBAAG,eAAe,GAAG,OAAO,CAAE,cAAsB,IAAM,eAAuB,CAAC,CAAC;wBAAC,MAAM;oBACnG,KAAK,GAAG;wBAAI,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,cAAwB,CAAC,GAAG,UAAU,CAAC,eAAyB,CAAC,CAAC,CAAC;wBAAC,MAAM;oBAC3H,KAAK,GAAG;wBAAI,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,cAAwB,CAAC,GAAG,UAAU,CAAC,eAAyB,CAAC,CAAC,CAAC;wBAAC,MAAM;iBAC5H;gBACD,oBAAoB,CAAC,eAAe,CAAC,CAAC;aACvC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,MAAM,CAAC,WAAW,CAAC,QAAgB,EAAE,IAAqB;QAQhE,MAAM,qBAAqB,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;QAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAC7E,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE;YACvB,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAkB,CAAC;YACvE,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAEpC,MAAM,YAAY,GAAG,IAA2C,CAAC;YACjE,IAAG,YAAY,EAAE;gBACf,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;gBACnC,IAAI,GAAG,GAAG,EAAE,CAAC;gBACb,IAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;oBAAE,MAAM,GAAG,IAAI,CAAC,cAAc,0EAA0E,CAAC;gBAChI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;oBAGtB,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,WAAW,OAAO,kBAAkB,EAAC,GAAG,CAAC,CAAC;oBAC3E,GAAG,IAAI,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;gBACrE,CAAC,CAAC,CAAC;gBACH,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;aACzC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,MAAM,CAAC,KAAK,CAAC,QAAgB,EAAE,IAAqB;QAC1D,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAChD,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC5C,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAChD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEM,MAAM,CAAC,OAAO,CAAC,QAAgB,EAAE,IAAqB;QAC3D,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;;AAxKuB,0BAAc,GAAW,iBAAiB,CAAC;AAC3C,6BAAiB,GAAW,eAAe,CAAC;AAC5C,uCAA2B,GAAW,yDAAyD,CAAC;AAChG,6BAAiB,GAAW,kDAAkD,CAAC;AAC/E,mCAAuB,GAAW,sDAAsD,CAAC;AAoBlG,sCAA0B,GAAG,CAAC,YAA+C,EAAU,EAAE;IACtG,MAAM,mBAAmB,GAAG,YAAY,CAAC,SAAS,CAAC;IACnD,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;IAClC,MAAM,cAAc,GAAG,SAAS,CAAC,eAAe,CAAC,mBAA6B,EAAE,WAAW,CAAC,CAAC;IAC7F,MAAM,sBAAsB,GAAG,cAAc,CAAC,aAAa,CAAC,MAAM,CAAgB,CAAC;IACnF,OAAO,sBAAsB,CAAC,SAAS,CAAC;AAC1C,CAAC,CAAA;AAWc,2BAAe,GAAG,CAAC,QAAgB,EAAE,OAAe,EAA2B,EAAE;IAC9F,MAAM,sBAAsB,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACvD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAA;AA6Hc,kBAAM,GAAG;IACtB,YAAY,EAAE,CAAC,IAAiB,EAAU,EAAE;QAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,YAAY,EAAE,CAAC,IAAY,EAAe,EAAE;QAC1C,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACjD,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC;QAC5B,OAAO,UAAU,CAAC;IAEpB,CAAC;IACD,yBAAyB,EAAE,CAAC,MAAc,EAAE,cAAsB,EAAU,EAAE;QAC5E,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,cAAc,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACnG,CAAC;CACF,CAAA"} \ No newline at end of file +{"version":3,"file":"abs-template.js","sourceRoot":"","sources":["../src/abs-template.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/abs-template.nx.js b/dist/abs-template.nx.js index 1b15c05..d47e37b 100644 --- a/dist/abs-template.nx.js +++ b/dist/abs-template.nx.js @@ -1,3 +1,4 @@ +var _a; var AbsTemplatePrintMethod; (function (AbsTemplatePrintMethod) { AbsTemplatePrintMethod["BEFORE_BEGIN"] = "beforebegin"; @@ -31,58 +32,47 @@ class AbsTemplate { } node.forEach(nodeItem => { nodeItem.nodeType !== Node.TEXT_NODE && target.insertAdjacentElement(method, nodeItem); + nodeItem.nodeType === Node.TEXT_NODE && target.insertAdjacentText(method, nodeItem.nodeValue); }); } - static parseParameters(template, data, patternOverride) { - const parameterPattern = new RegExp(patternOverride || this.PARAMETER_PATTERN, ''); - const matches = this.getParseMatches(template, patternOverride || this.PARAMETER_PATTERN); - matches?.forEach(match => { - const dataMatches = parameterPattern.exec(match); - const key = dataMatches[1]; - const keyValue = data[key]; - if (keyValue || keyValue === '') { - template = template.replace(match, keyValue); - } - }); - return template; + static parseValue(template, data) { + let compiledTemplate = ''; + const matches = template.match(new RegExp(this.VALUE_PATTERN_STRING)); + if (matches?.length) { + const fullMatch = matches[0]; + const valueIdentifier = matches[1]; + const valueFromData = this._utils.getValueByPath(data, valueIdentifier); + compiledTemplate = valueFromData !== undefined ? valueFromData : fullMatch; + } + return compiledTemplate; } - static parseConditions(template, data) { - const conditionStatementPattern = new RegExp(this.CONDITION_STATEMENT_PATTERN, ''); - const conditionPattern = new RegExp(this.CONDITION_PATTERN, ''); - const matches = this.getParseMatches(template, this.CONDITION_STATEMENT_PATTERN); - matches?.forEach(match => { - const matchGroups = conditionStatementPattern.exec(match); - const statementBlock = matchGroups[0]; - const condition = matchGroups[1]; + static parseCondition(template, data) { + const conditionStatementPattern = new RegExp(this.CONDITION_STATEMENT_PATTERN_STRING, ''); + const conditionPattern = new RegExp(this.CONDITION_PATTERN_STRING, ''); + let compiledTemplate = ''; + const statementMatches = template.match(conditionStatementPattern); + if (statementMatches?.length) { + const condition = statementMatches[1]; const parsedCondition = conditionPattern.exec(condition); - const isConditionSingleParameter = !Boolean(parsedCondition); - const positiveContent = matchGroups[2]; - const negativeContent = matchGroups[3]; - const printConditionResult = (conditionResult) => { - if (Boolean(conditionResult)) { - template = template.replace(statementBlock, positiveContent); - } - else { - template = template.replace(statementBlock, negativeContent || ''); - } - }; - if (isConditionSingleParameter) { - const parameter = data[condition]; - printConditionResult(Boolean(parameter)); + const isConditionImplicit = !Boolean(parsedCondition); + const positiveContent = statementMatches[2]; + const negativeContent = statementMatches[3]; + if (isConditionImplicit) { + const valueFromData = this._utils.getValueByPath(data, condition); + const implicitCheck = Boolean(valueFromData); + const parsedContent = this._utils.if.parseContentFromCondition(implicitCheck, data, positiveContent, negativeContent); + compiledTemplate = parsedContent; } - else { - const sanitizeParameter = (parameter) => { - return (!Number.isNaN(parseFloat(parameter)) ? parseFloat(parameter) : - parameter === 'true' ? true : - parameter === 'false' ? false : - parameter === 'undefined' ? undefined : - parameter === 'null' ? null : - parameter); - }; - const conditionMatchGroups = conditionPattern.exec(condition); - const firstParameter = sanitizeParameter(conditionMatchGroups[1]); - const operator = conditionMatchGroups[2]; - const secondParameter = sanitizeParameter(conditionMatchGroups[3]); + else if (parsedCondition?.length) { + const firstSanitizedParameter = this._utils.if.sanitizeParameter(parsedCondition[1]); + const firstParameter = (this._utils.if.isParameterLiteral(firstSanitizedParameter) ? + this._utils.if.fixStringLiteral(firstSanitizedParameter) : + this._utils.getValueByPath(data, firstSanitizedParameter)); + const secondSanitizedParameter = this._utils.if.sanitizeParameter(parsedCondition[3]); + const secondParameter = (this._utils.if.isParameterLiteral(secondSanitizedParameter) ? + this._utils.if.fixStringLiteral(secondSanitizedParameter) : + this._utils.getValueByPath(data, secondSanitizedParameter)); + const operator = parsedCondition[2]; let conditionResult = false; switch (operator) { case '==': @@ -116,49 +106,110 @@ class AbsTemplate { conditionResult = Boolean(parseFloat(firstParameter) ^ parseFloat(secondParameter)); break; } - printConditionResult(conditionResult); + const parsedContent = this._utils.if.parseContentFromCondition(conditionResult, data, positiveContent, negativeContent); + compiledTemplate = parsedContent; } - }); - return template; + } + return compiledTemplate; } - static parseCycles(template, data) { - const cycleStatementPattern = new RegExp(this.CYCLE_STATEMENT_PATTERN, ''); - const matches = this.getParseMatches(template, this.CYCLE_STATEMENT_PATTERN); - matches?.forEach(match => { - const matchGroups = cycleStatementPattern.exec(match); - const itemKey = matchGroups[1]; - const listKey = matchGroups[2]; - const cycleContent = matchGroups[3]; - const templateData = data; - if (templateData) { - const list = templateData[listKey]; - let res = ''; + static parseCycle(template, data) { + let compiledTemplate = ''; + const matches = template.match(new RegExp(this.CYCLE_STATEMENT_PATTERN_STRING)); + if (matches) { + const keyOfListIdentifier = matches[1]; + const listIdentifier = matches[2]; + const cycleContent = matches[3]; + if (data) { + const list = this._utils.getValueByPath(data, listIdentifier); if (!Array.isArray(list)) - throw `${this.CONSOLE_PREFIX} Template contains a "forEach" with a parameter that cannot be iterated.`; + throw `${this.CONSOLE_PREFIX} Parameter "${listIdentifier}" is not iterable.`; list.forEach(listItem => { - const subParamRegex = new RegExp(`\\\{\\\{${itemKey}\\.(.+?)\\\}\\\}`, 'g'); - res += this.parseParameters(cycleContent, listItem, subParamRegex); + const iterationData = { + ...this._utils.deepCopy(data), + [keyOfListIdentifier]: listItem, + }; + compiledTemplate += this.parse(cycleContent, iterationData); }); - template = template.replace(match, res); } - }); - return template; + } + return compiledTemplate; } static parse(template, data) { - template = this.parseConditions(template, data); - template = this.parseCycles(template, data); - template = this.parseParameters(template, data); - return template; + let isTagOpen = false; + let tagOpenStack = 0; + let currentClosingTag = ''; + let openTagIndex = -1; + let closeTagIndex = -1; + let compiledTemplate = ''; + for (let i = 0; i < template.length; i++) { + const isConditionOpening = template.slice(i, i + this.CONDITION_STATEMENT_OPEN.length) === this.CONDITION_STATEMENT_OPEN; + const isCycleOpening = template.slice(i, i + this.CYCLE_STATEMENT_OPEN.length) === this.CYCLE_STATEMENT_OPEN; + const isValueOpening = template.slice(i, i + this.VALUE_STATEMENT_OPEN.length) === this.VALUE_STATEMENT_OPEN; + const isClosingCurrentTag = isTagOpen && currentClosingTag && template.slice(i, i + currentClosingTag.length) === currentClosingTag; + const isOpeningNested = isTagOpen && ((isCycleOpening && currentClosingTag === this.CYCLE_STATEMENT_CLOSE) || + (isConditionOpening && currentClosingTag === this.CONDITION_STATEMENT_CLOSE)); + if (isConditionOpening || isCycleOpening) { + if (isOpeningNested) { + tagOpenStack++; + } + else if (!isTagOpen) { + isTagOpen = true; + openTagIndex = i; + currentClosingTag = + isCycleOpening ? this.CYCLE_STATEMENT_CLOSE : + isConditionOpening ? this.CONDITION_STATEMENT_CLOSE : + ''; + } + } + else if (isClosingCurrentTag) { + if (tagOpenStack !== 0) { + tagOpenStack--; + } + else { + isTagOpen = false; + closeTagIndex = i + currentClosingTag.length; + i += (currentClosingTag.length - 1); + const currentBlockTemplate = template.slice(openTagIndex, closeTagIndex); + const isBlockCondition = currentBlockTemplate.startsWith(this.CONDITION_STATEMENT_OPEN); + const isBlockCycle = currentBlockTemplate.startsWith(this.CYCLE_STATEMENT_OPEN); + const currentParsedBlock = (isBlockCondition ? this.parseCondition(currentBlockTemplate, data) : + isBlockCycle ? this.parseCycle(currentBlockTemplate, data) : + ''); + compiledTemplate += currentParsedBlock; + openTagIndex = -1; + closeTagIndex = -1; + } + } + else if (isValueOpening && !isTagOpen) { + const valueMatches = template.slice(i).match(new RegExp(this.VALUE_PATTERN_STRING)); + if (valueMatches) { + const fullMatch = valueMatches[0]; + const compiledValue = this.parseValue(fullMatch, data); + compiledTemplate += compiledValue; + i += (fullMatch.length - 1); + } + } + else if (!isTagOpen) { + compiledTemplate += template[i]; + } + } + return compiledTemplate; } static compile(template, data) { return this.parse(template, data); } } +_a = AbsTemplate; AbsTemplate.CONSOLE_PREFIX = '[ABS][TEMPLATE]'; -AbsTemplate.PARAMETER_PATTERN = /\{\{(.+?)\}\}/; -AbsTemplate.CONDITION_STATEMENT_PATTERN = /\{\{if (.+?)\}\}(.+?)(?:\{\{else\}\}(.+?))?\{\{\/if\}\}/; -AbsTemplate.CONDITION_PATTERN = /(.+) (==|===|!=|!==|>|>=|<|<=|&&|\|\||%|\^) (.+)/; -AbsTemplate.CYCLE_STATEMENT_PATTERN = /\{\{forEach (.+?) in (.+?)\}\}(.+?)\{\{\/forEach\}\}/; +AbsTemplate.VALUE_STATEMENT_OPEN = '{{'; +AbsTemplate.VALUE_PATTERN_STRING = '{{(.+?)}}'; +AbsTemplate.CONDITION_STATEMENT_OPEN = '{{if'; +AbsTemplate.CONDITION_STATEMENT_PATTERN_STRING = '{{if (.+?)}}(.+?)(?:{{else}}(.+?))?{{/if}}'; +AbsTemplate.CONDITION_PATTERN_STRING = '(.+) (==|===|!=|!==|>|>=|<|<=|&&|\|\||%|\^) (.+)'; +AbsTemplate.CONDITION_STATEMENT_CLOSE = '{{/if}}'; +AbsTemplate.CYCLE_STATEMENT_OPEN = '{{forEach'; +AbsTemplate.CYCLE_STATEMENT_PATTERN_STRING = '{{forEach (.+?) in (.+?)}}(.*){{/forEach}}'; +AbsTemplate.CYCLE_STATEMENT_CLOSE = '{{/forEach}}'; AbsTemplate.getContentFromTemplateNode = (templateNode) => { const templateNodeContent = templateNode.innerHTML; const domParser = new DOMParser(); @@ -166,11 +217,6 @@ AbsTemplate.getContentFromTemplateNode = (templateNode) => { const parsedDocumentBodyNode = parsedDocument.querySelector('body'); return parsedDocumentBodyNode.innerHTML; }; -AbsTemplate.getParseMatches = (template, pattern) => { - const parameterGlobalPattern = new RegExp(pattern, 'g'); - const matches = template.match(parameterGlobalPattern); - return matches; -}; AbsTemplate._utils = { nodeToString: (node) => { return node.outerHTML.replaceAll('\n', ''); @@ -182,6 +228,77 @@ AbsTemplate._utils = { }, removeCharacterFromString: (string, characterIndex) => { return string.substring(0, characterIndex) + string.substring(characterIndex + 1, string.length); - } + }, + deepCopy: (inObject) => { + let outObject, value, key; + if (typeof inObject !== 'object' || inObject === null) { + return inObject; + } + outObject = Array.isArray(inObject) ? [] : {}; + for (key in inObject) { + value = inObject[key]; + outObject[key] = _a._utils.deepCopy(value); + } + return outObject; + }, + getValueByPath: (obj, path) => { + const keys = path.split('.'); + let value = obj; + for (const key of keys) { + if (value && typeof value === 'object' && key in value) { + value = value[key]; + } + else { + return undefined; + } + } + return value; + }, + if: { + parseContentFromCondition: (conditionResult, data, positiveContent, negativeContent) => { + let compiledContent = ''; + compiledContent = _a.parse(conditionResult ? positiveContent || '' : negativeContent || '', data); + return compiledContent; + }, + sanitizeParameter: (parameter) => { + return (!Number.isNaN(parseFloat(parameter)) ? parseFloat(parameter) : + parameter === 'true' ? true : + parameter === 'false' ? false : + parameter === 'undefined' ? undefined : + parameter === 'null' ? null : + parameter); + }, + isParameterLiteralString: (parameter) => { + return typeof parameter === 'string' && ((parameter.startsWith(`'`) && parameter.endsWith(`'`)) || + (parameter.startsWith(`"`) && parameter.endsWith(`"`))); + }, + fixStringLiteral: (parameter) => { + if (_a._utils.if.isParameterLiteralString(parameter)) { + return parameter.slice(1, parameter.length - 1); + } + else { + return parameter; + } + }, + isParameterLiteral: (parameter) => { + const isParamKeyword = Boolean(typeof parameter === 'number' || + _a._utils.if.isParameterLiteralString(parameter) || + parameter === true || + parameter === false || + parameter === null || + parameter === undefined); + return isParamKeyword; + }, + }, + _log: (template, position) => { + let res = ''; + const COL = '%c'; + res += COL; + for (let i = 0; i < template.length; i++) { + res += i === position ? COL : ''; + res += template[i]; + } + console.log(res, 'color: lime;', 'color: white;'); + }, }; //# sourceMappingURL=abs-template.js.map \ No newline at end of file diff --git a/dist/abs-template.ts b/dist/abs-template.ts index f488200..adad1dc 100644 --- a/dist/abs-template.ts +++ b/dist/abs-template.ts @@ -16,10 +16,15 @@ export interface AbsTemplateBuildConfig { export class AbsTemplate { private static readonly CONSOLE_PREFIX: string = '[ABS][TEMPLATE]'; - private static readonly PARAMETER_PATTERN: RegExp = /\{\{(.+?)\}\}/; - private static readonly CONDITION_STATEMENT_PATTERN: RegExp = /\{\{if (.+?)\}\}(.+?)(?:\{\{else\}\}(.+?))?\{\{\/if\}\}/; - private static readonly CONDITION_PATTERN: RegExp = /(.+) (==|===|!=|!==|>|>=|<|<=|&&|\|\||%|\^) (.+)/; - private static readonly CYCLE_STATEMENT_PATTERN: RegExp = /\{\{forEach (.+?) in (.+?)\}\}(.+?)\{\{\/forEach\}\}/; + private static readonly VALUE_STATEMENT_OPEN: string = '{{'; + private static readonly VALUE_PATTERN_STRING: string = '{{(.+?)}}'; + private static readonly CONDITION_STATEMENT_OPEN: string = '{{if'; + private static readonly CONDITION_STATEMENT_PATTERN_STRING: string = '{{if (.+?)}}(.+?)(?:{{else}}(.+?))?{{/if}}'; + private static readonly CONDITION_PATTERN_STRING: string = '(.+) (==|===|!=|!==|>|>=|<|<=|&&|\|\||%|\^) (.+)'; + private static readonly CONDITION_STATEMENT_CLOSE: string = '{{/if}}'; + private static readonly CYCLE_STATEMENT_OPEN: string = '{{forEach'; + private static readonly CYCLE_STATEMENT_PATTERN_STRING: string = '{{forEach (.+?) in (.+?)}}(.*){{/forEach}}'; + private static readonly CYCLE_STATEMENT_CLOSE: string = '{{/forEach}}'; public static build(config: AbsTemplateBuildConfig): void { try { @@ -47,79 +52,67 @@ export class AbsTemplate { return parsedDocumentBodyNode.innerHTML; } - private static print(node: HTMLElement[], target: HTMLElement, method: AbsTemplatePrintMethod): void { + private static print(node: ChildNode[], target: HTMLElement, method: AbsTemplatePrintMethod): void { if(method === AbsTemplatePrintMethod.AFTER_BEGIN || method === AbsTemplatePrintMethod.AFTER_END) { node.reverse(); } node.forEach(nodeItem => { - nodeItem.nodeType !== Node.TEXT_NODE && target.insertAdjacentElement(method, nodeItem); + nodeItem.nodeType !== Node.TEXT_NODE && target.insertAdjacentElement(method, (nodeItem as HTMLElement)); + nodeItem.nodeType === Node.TEXT_NODE && target.insertAdjacentText(method, nodeItem.nodeValue as string); }); } - private static getParseMatches = (template: string, pattern: RegExp): RegExpMatchArray | null => { - const parameterGlobalPattern = new RegExp(pattern, 'g'); - const matches = template.match(parameterGlobalPattern); - return matches; - } + private static parseValue(template: string, data: AbsTemplateData): string { + let compiledTemplate = ''; - private static parseParameters(template: string, data: AbsTemplateData, patternOverride?: RegExp): string { - const parameterPattern = new RegExp(patternOverride || this.PARAMETER_PATTERN, ''); - const matches = this.getParseMatches(template, patternOverride || this.PARAMETER_PATTERN); - matches?.forEach(match => { - const dataMatches = parameterPattern.exec(match) as Array; - const key = dataMatches[1]; - const keyValue = (data as Record)[key]; - if(keyValue || keyValue === '') { - template = template.replace(match, keyValue); - } - }); - return template; + const matches = template.match(new RegExp(this.VALUE_PATTERN_STRING)); + if(matches?.length) { + const fullMatch = matches[0]; + const valueIdentifier = matches[1]; + const valueFromData = this._utils.getValueByPath(data, valueIdentifier); + compiledTemplate = valueFromData !== undefined ? valueFromData : fullMatch; + } + + return compiledTemplate; } - private static parseConditions(template: string, data: AbsTemplateData): string { - //BUG if there are multiple statements of the same type inside each other - //the first level will probably match very first closing pattern found (the inner-most statement) - //and the result is overlapped - //FIXME run a `this.parse()` before anything else - //by passing the `conditionContent` of the current match as restricted `template` parameter - //as this will work recursively by finding the inner-most level - //and leave only the correct closing pattern as last one - const conditionStatementPattern = new RegExp(this.CONDITION_STATEMENT_PATTERN, ''); - const conditionPattern = new RegExp(this.CONDITION_PATTERN, ''); - const matches = this.getParseMatches(template, this.CONDITION_STATEMENT_PATTERN); - matches?.forEach(match => { - const matchGroups = conditionStatementPattern.exec(match) as Array; - const statementBlock: string = matchGroups[0]; - const condition: string = matchGroups[1]; - const parsedCondition: Array|undefined = conditionPattern.exec(condition) as Array; - const isConditionSingleParameter = !Boolean(parsedCondition); - const positiveContent: string = matchGroups[2]; - const negativeContent: string|undefined = matchGroups[3]; - const printConditionResult = (conditionResult: boolean): void => { - if(Boolean(conditionResult)) { - template = template.replace(statementBlock, positiveContent); - } else { - template = template.replace(statementBlock, negativeContent || ''); - } - }; - if(isConditionSingleParameter) { - const parameter = (data as Record)[condition]; - printConditionResult(Boolean(parameter)); - } else { - const sanitizeParameter = (parameter: string): boolean|null|undefined|string|number => { - return ( - !Number.isNaN(parseFloat(parameter)) ? parseFloat(parameter) : - parameter === 'true' ? true : - parameter === 'false' ? false : - parameter === 'undefined' ? undefined : - parameter === 'null' ? null : - parameter - ); - }; - const conditionMatchGroups = conditionPattern.exec(condition) as Array; - const firstParameter = sanitizeParameter(conditionMatchGroups[1]); - const operator = conditionMatchGroups[2]; - const secondParameter = sanitizeParameter(conditionMatchGroups[3]); + private static parseCondition(template: string, data: AbsTemplateData): string { + const conditionStatementPattern = new RegExp(this.CONDITION_STATEMENT_PATTERN_STRING, ''); + const conditionPattern = new RegExp(this.CONDITION_PATTERN_STRING, ''); + + let compiledTemplate = ''; + + const statementMatches = template.match(conditionStatementPattern); + if(statementMatches?.length) { + const condition = statementMatches[1]; + const parsedCondition = conditionPattern.exec(condition); + const isConditionImplicit = !Boolean(parsedCondition); + const positiveContent = statementMatches[2]; + const negativeContent = statementMatches[3] as string|undefined; + + if(isConditionImplicit) { + const valueFromData = this._utils.getValueByPath(data, condition); + const implicitCheck = Boolean(valueFromData); + + const parsedContent = this._utils.if.parseContentFromCondition(implicitCheck, data, positiveContent, negativeContent); + compiledTemplate = parsedContent; + } else if(parsedCondition?.length) { + const firstSanitizedParameter = this._utils.if.sanitizeParameter(parsedCondition[1]); + const firstParameter = ( + this._utils.if.isParameterLiteral(firstSanitizedParameter) ? + this._utils.if.fixStringLiteral(firstSanitizedParameter) : + this._utils.getValueByPath(data, firstSanitizedParameter as string) + ); + + const secondSanitizedParameter = this._utils.if.sanitizeParameter(parsedCondition[3]); + const secondParameter = ( + this._utils.if.isParameterLiteral(secondSanitizedParameter) ? + this._utils.if.fixStringLiteral(secondSanitizedParameter) : + this._utils.getValueByPath(data, secondSanitizedParameter as string) + ); + + const operator = parsedCondition[2]; + let conditionResult: boolean = false; switch(operator) { case '==': conditionResult = Boolean((firstParameter as any) == (secondParameter as any)); break; @@ -135,50 +128,105 @@ export class AbsTemplate { case '%': conditionResult = Boolean(parseFloat(firstParameter as string) % parseFloat(secondParameter as string)); break; case '^': conditionResult = Boolean(parseFloat(firstParameter as string) ^ parseFloat(secondParameter as string)); break; } - printConditionResult(conditionResult); + + const parsedContent = this._utils.if.parseContentFromCondition(conditionResult, data, positiveContent, negativeContent); + compiledTemplate = parsedContent; } - }); - return template; + } + + return compiledTemplate; } - private static parseCycles(template: string, data: AbsTemplateData): string { - //BUG if there are multiple statements of the same type inside each other - //the first level will probably match very first closing pattern found (the inner-most statement) - //and the result is overlapped - //FIXME run a `this.parse()` before anything else - //by passing the `cycleContent` of the current match as restricted `template` parameter - //as this will work recursively by finding the inner-most level - //and leave only the correct closing pattern as last one - const cycleStatementPattern = new RegExp(this.CYCLE_STATEMENT_PATTERN, ''); - const matches = this.getParseMatches(template, this.CYCLE_STATEMENT_PATTERN); - matches?.forEach(match => { - const matchGroups = cycleStatementPattern.exec(match) as Array; - const itemKey = matchGroups[1]; - const listKey = matchGroups[2]; - const cycleContent = matchGroups[3]; - - const templateData = data as Record; - if(templateData) { - const list = templateData[listKey]; - let res = ''; - if(!Array.isArray(list)) throw `${this.CONSOLE_PREFIX} Template contains a "forEach" with a parameter that cannot be iterated.`; + private static parseCycle(template: string, data: AbsTemplateData): string { + let compiledTemplate = ''; + + const matches = template.match(new RegExp(this.CYCLE_STATEMENT_PATTERN_STRING)); + if(matches) { + const keyOfListIdentifier = matches[1]; + const listIdentifier = matches[2]; + const cycleContent = matches[3]; + + if(data) { + const list = this._utils.getValueByPath(data, listIdentifier); + if(!Array.isArray(list)) throw `${this.CONSOLE_PREFIX} Parameter "${listIdentifier}" is not iterable.`; + list.forEach(listItem => { - //BUG same case for statements inside each other - //an `${itemKey}.(...listItem)` could be found not wrapped by standard curly brackets pattern - const subParamRegex = new RegExp(`\\\{\\\{${itemKey}\\.(.+?)\\\}\\\}`,'g'); - res += this.parseParameters(cycleContent, listItem, subParamRegex); + const iterationData = { + ...this._utils.deepCopy(data), + [keyOfListIdentifier]: listItem, + }; + compiledTemplate += this.parse(cycleContent, iterationData); }); - template = template.replace(match, res); } - }); - return template; + } + return compiledTemplate; } private static parse(template: string, data: AbsTemplateData): string { - template = this.parseConditions(template, data); - template = this.parseCycles(template, data); - template = this.parseParameters(template, data); - return template; + let isTagOpen = false; + let tagOpenStack = 0; + let currentClosingTag = ''; + let openTagIndex = -1; + let closeTagIndex = -1; + let compiledTemplate = ''; + + for(let i = 0; i < template.length; i++) { + const isConditionOpening = template.slice(i, i + this.CONDITION_STATEMENT_OPEN.length) === this.CONDITION_STATEMENT_OPEN; + const isCycleOpening = template.slice(i, i + this.CYCLE_STATEMENT_OPEN.length) === this.CYCLE_STATEMENT_OPEN; + const isValueOpening = template.slice(i, i + this.VALUE_STATEMENT_OPEN.length) === this.VALUE_STATEMENT_OPEN; + const isClosingCurrentTag = isTagOpen && currentClosingTag && template.slice(i, i + currentClosingTag.length) === currentClosingTag; + const isOpeningNested = isTagOpen && ( + (isCycleOpening && currentClosingTag === this.CYCLE_STATEMENT_CLOSE) || + (isConditionOpening && currentClosingTag === this.CONDITION_STATEMENT_CLOSE) + ); + + if(isConditionOpening || isCycleOpening) { + if(isOpeningNested) { + tagOpenStack++; + } else if(!isTagOpen) { + isTagOpen = true; + openTagIndex = i; + + currentClosingTag = + isCycleOpening ? this.CYCLE_STATEMENT_CLOSE : + isConditionOpening ? this.CONDITION_STATEMENT_CLOSE : + ''; + } + } else if(isClosingCurrentTag) { + if(tagOpenStack !== 0) { + tagOpenStack--; + } else { + isTagOpen = false; + closeTagIndex = i + currentClosingTag.length; + i += (currentClosingTag.length - 1); + const currentBlockTemplate = template.slice(openTagIndex, closeTagIndex); + const isBlockCondition = currentBlockTemplate.startsWith(this.CONDITION_STATEMENT_OPEN); + const isBlockCycle = currentBlockTemplate.startsWith(this.CYCLE_STATEMENT_OPEN); + + const currentParsedBlock = ( + isBlockCondition ? this.parseCondition(currentBlockTemplate, data) : + isBlockCycle ? this.parseCycle(currentBlockTemplate, data) : + '' + ); + compiledTemplate += currentParsedBlock; + openTagIndex = -1; + closeTagIndex = -1; + } + + } else if(isValueOpening && !isTagOpen) { + const valueMatches = template.slice(i).match(new RegExp(this.VALUE_PATTERN_STRING)); + if(valueMatches) { + const fullMatch = valueMatches[0]; + const compiledValue = this.parseValue(fullMatch, data); + compiledTemplate += compiledValue; + i += (fullMatch.length - 1); + } + } else if(!isTagOpen) { + compiledTemplate += template[i]; + } + } + + return compiledTemplate; } public static compile(template: string, data: AbsTemplateData): string { @@ -197,6 +245,84 @@ export class AbsTemplate { }, removeCharacterFromString: (string: string, characterIndex: number): string => { return string.substring(0, characterIndex) + string.substring(characterIndex + 1, string.length); - } + }, + deepCopy: (inObject: any): any => { + let outObject, value, key; + if (typeof inObject !== 'object' || inObject === null) { + return inObject; + } + outObject = Array.isArray(inObject) ? [] : {}; + for (key in inObject) { + value = inObject[key]; + (outObject as any)[key] = this._utils.deepCopy(value); + } + return outObject; + }, + getValueByPath: (obj: any, path: string): any => { + const keys = path.split('.'); + let value = obj; + for (const key of keys) { + if (value && typeof value === 'object' && key in value) { + value = value[key]; + } else { + return undefined; + } + } + return value; + }, + if: { + parseContentFromCondition: (conditionResult: boolean, data: any, positiveContent?: string, negativeContent?: string): string => { + let compiledContent = ''; + compiledContent = this.parse( + conditionResult ? positiveContent || '' : negativeContent || '', + data + ); + return compiledContent; + }, + sanitizeParameter: (parameter: string): boolean|null|undefined|string|number => { + return ( + !Number.isNaN(parseFloat(parameter)) ? parseFloat(parameter) : + parameter === 'true' ? true : + parameter === 'false' ? false : + parameter === 'undefined' ? undefined : + parameter === 'null' ? null : + parameter + ); + }, + isParameterLiteralString: (parameter: boolean|null|undefined|string|number): boolean => { + return typeof parameter === 'string' && ( + ( (parameter as string).startsWith(`'`) && (parameter as string).endsWith(`'`)) || + ( (parameter as string).startsWith(`"`) && (parameter as string).endsWith(`"`)) + ); + }, + fixStringLiteral: (parameter: boolean|null|undefined|string|number): boolean|null|undefined|string|number => { + if(this._utils.if.isParameterLiteralString(parameter)) { + return (parameter as string).slice(1, (parameter as string).length - 1); + } else { + return parameter; + } + }, + isParameterLiteral: (parameter: boolean|null|undefined|string|number): boolean => { + const isParamKeyword = Boolean( + typeof parameter === 'number' || + this._utils.if.isParameterLiteralString(parameter) || + parameter === true || + parameter === false || + parameter === null || + parameter === undefined + ); + return isParamKeyword; + }, + }, + _log: (template: string, position: number): void => { + let res = ''; + const COL = '%c'; + res += COL; + for(let i = 0; i < template.length; i++) { + res += i === position ? COL : ''; + res += template[i]; + } + console.log(res, 'color: lime;', 'color: white;'); + }, } } \ No newline at end of file diff --git a/package.json b/package.json index d62bca2..b19b51f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "abs-template", - "version": "1.0.0", + "version": "1.2.0", "description": "Parse and compile template partials in static HTMLs", "types": "dist/abs-template.d.ts", "browser": "dist/abs-template.js",