UNPKG

@jeli/compiler-cli

Version:

jeli compiler for frontend development

1,046 lines (948 loc) 34.9 kB
/** * html core parser */ const parser = require('html-dom-parser'); const { parseAst, parseAstJSON } = require('./ast.generator'); const interpolation = require('./interpolation'); const helper = require('@jeli/cli/lib/utils'); const { matchViewQueryFromAstNode } = require('./query_selector'); const restrictedCombination = ['j-template', 'j-place', 'case', 'default']; const isFragmentElement = tagName => ["j-fragment", "j-template", "j-place", 'template'].includes(tagName); const formInputs = ['select', 'input', 'textarea']; const standardAttributes = 'id|class|style|title|dir|lang|aria'; const isComponent = tagName => helper.isContain('-', tagName) && !isFragmentElement(tagName); const oneWayBinding = /\{(.*?)\}/; const twoWayBinding = /\@\{(.*?)\}/; const charMatchers = [':', '*', '#', '@', '{', '(']; const TEMPLATE_KEY = key => `<%${key}%>`; const selectorTypes = { '.': 'class', '#': 'id', '[': 'attr' }; const resolvedFilters = {}; const resolvedElements = {}; const pendingElements = {}; /** * * @param {*} htmlContent * @param {*} ctor * @param {*} resolvers * @param {*} componentClassName * @param {*} fileChanges * @returns */ module.exports = function (htmlContent, ctor, resolvers, componentClassName, fileChanges) { // removed resolved elements from cache if exists if (resolvedElements[ctor.selector] && fileChanges) { delete resolvedElements[ctor.selector]; } const templatesMapHolder = {}; const templateOptionsMapper = []; let pendingDependencies = false; const providers = { ViewParser: "@jeli/core" }; const errorLogs = []; /** * parse ast node received * @param {*} item * return astNode|null */ function astParser(item) { if (helper.is(item.type, 'text') && item.data.trim()) { return astTextParser(item.data); } else if (helper.is(item.type, 'tag')) { return astElementParser.apply(null, arguments); } return null; } /** * parse ast receive and convert into jeli ast object * @param {*} ast * @param {*} idx * @param {*} parent * @returns astNode|null */ function astElementParser(ast, idx, parent) { var newAstNode = { type: 1, name: isFragmentElement(ast.name) ? "#" : ast.name, index: idx, // attach isComponent property isc: isComponent(ast.name) }; // compile attributes if (ast.attribs) { for (const prop in ast.attribs) { attributeParser(prop, ast.attribs[prop], newAstNode); } // remove unmapped props removeUnmappedProps(newAstNode); } // throw Error when Element calls itsel if (newAstNode.isc) { _validateCustomElementAndThrowError(newAstNode); } attachViewChildToAst(newAstNode); compileChildren(ast, newAstNode); /** * extract component * get directives */ if (newAstNode.structuralDirective) { /** * remove the directives prop */ if (helper.is(ast.name, 'j-place')) { jPlaceCompiler(newAstNode, parent); } return buildStructuralDirectiveTemplates(newAstNode, parent); } return checkAndBuildTemplateElement(newAstNode, ast.name, parent); } /** * * @param {*} astNode * @param {*} elementName * @param {*} parentAst * @returns */ function checkAndBuildTemplateElement(astNode, elementName, parentAst) { switch (elementName) { case ('j-template'): return templateCompiler(astNode, parentAst); case ('j-place'): return jPlaceCompiler(astNode, parentAst); case ('j-fragment'): return jFragmentParser(astNode, elementName); } return astNode; } /** * * @param {*} newItem */ function removeUnmappedProps(newItem) { if (newItem.props) { Object.keys(newItem.props).forEach(prop => { if (helper.is(newItem.props[prop], '@!')) { delete newItem.props[prop]; } }); } } /** * attach conntentChildren to template * @param {*} parentAstNode * @param {*} childAst */ function attachContentChildren(parentAstNode, childAst) { if(!resolvedElements[parentAstNode.name]) return; const query = resolvedElements[parentAstNode.name].query; const isStructural = helper.is(childAst.type, 8); const matched = matchViewQueryFromAstNode(query.child, isStructural ? childAst.templates[childAst.text] : childAst); if (matched) { if (isStructural) { errorLogs.push(`ContentChild(ren) query does not currently support structural directive "${childAst.text}" -> ${matched.name}=${matched.value}`); return; } // args to be pushed to contentChildren method const qlAstNode = [matched.type || 'TemplateRef', matched.ql]; const name = matched.name || matched.value; // const id = !contentChildrenMapper[parentAstNode.name] ? if (!parentAstNode.cq) parentAstNode.cq = {}; // ContentChildren if (matched.ql) { if (!parentAstNode.cq[name]) { qlAstNode.push([]); parentAstNode.cq[name] = qlAstNode; } parentAstNode.cq[name][2].push(childAst); } else { qlAstNode.push(childAst); parentAstNode.cq[name] = qlAstNode; } } else { if (!query.place) { if (!pendingElements[parentAstNode.name]) pendingElements[parentAstNode.name] = {}; if (!pendingElements[parentAstNode.name][ctor.selector]) pendingElements[parentAstNode.name][ctor.selector] = [parentAstNode, []] // push the childlist pendingElements[parentAstNode.name][ctor.selector][1].push(childAst); pendingDependencies = true; return; } attachContentChildPlace(parentAstNode, childAst, query); } } /** * * @param {*} parentAstNode * @param {*} childAst * @param {*} query * @returns */ function attachContentChildPlace(parentAstNode, childAst, query) { if(!query || !query.place) return; const pushToPlace = id => { if (!parentAstNode.templates) { parentAstNode.templates = { place: {} }; } if (!parentAstNode.templates.place[id]) { parentAstNode.templates.place[id] = [] } // push all children if childAst is a fragment if (helper.is(childAst.name, '#') && childAst.children) { parentAstNode.templates.place[id].push.apply(parentAstNode.templates.place[id], childAst.children); } else { parentAstNode.templates.place[id].push(childAst); } }; const refs = Object.keys(query.place); if (refs.length === 1 && refs[0] === '@') { pushToPlace('@'); } else if (childAst.refId && query.place[childAst.refId]) { pushToPlace(childAst.refId); } else if (query.place[childAst.name]) { pushToPlace(childAst.name) } else if (childAst.attr) { // [id, class, attr] for (const ref in query.place) { const prop = (childAst.attr[query.place[ref]] || '').split(/\s/g); if ((childAst.attr.hasOwnProperty(ref) && query.place[ref] === 'attr') || (prop.includes(ref))) { pushToPlace(ref); return; } } } } /** * attach viewChild attribute to the element * @param {*} astNode */ function attachViewChildToAst(astNode) { if (ctor.viewChild && ctor.viewChild.length) { const option = matchViewQueryFromAstNode(ctor.viewChild, astNode); if (option) { astNode.vc = [option, ctor.selector]; } } } function checkTemplateCircularRef(isTemplate, refId, child) { if (isTemplate && refId) { if (child.name && child.attribs && child.attribs.hasOwnProperty('template') && child.attribs.template === refId) { child.isCircularRef = true; } if (child.children && child.children.length) { child.children.forEach(cchild => checkTemplateCircularRef(true, refId, cchild)); } } } /** * compile template children * @param {*} astNode * @param {*} compiledAstNode */ function compileChildren(astNode, compiledAstNode) { if (astNode.children && astNode.children.length) { compiledAstNode.children = []; astNode.children .forEach((child, idx) => { if (!(child.name || (child.data && child.data.trim()))) return; var childAstNode = astParser(child, idx, compiledAstNode); if (childAstNode) { if (compiledAstNode.isc) { attachContentChildren(compiledAstNode, childAstNode); } else { compiledAstNode.children.push(childAstNode); } } }); } } /** * * @param {*} astNode * @param {*} parent */ function buildStructuralDirectiveTemplates(astNode) { const definition = astNode.structuralDirective; delete astNode.structuralDirective; switch (definition.dirName) { case ('if'): return jIfCompiler(definition, astNode); default: return createDefaultTemplate(definition, astNode); } } /** * conditional template generator * @param {*} definition * @param {*} astNode */ function jIfCompiler(definition, astNode) { const newAstNode = createDefaultTemplate(definition, astNode); if (newAstNode.props) { for (var templateId in newAstNode.props) { if (typeof newAstNode.props[templateId] !== 'object' && !newAstNode.templates[templateId]) { newAstNode.templates[templateId] = TEMPLATE_KEY(newAstNode.props[templateId]); } } } return newAstNode; } /** * create AbstractTemplate Object * @param {*} definition */ function createDefaultTemplate(definition, ast) { var astNode = { type: 8, name: "##", text: definition.dirName, templates: {} }; if (definition.props) { astNode.props = definition.props; } /** * bind to templateFragments */ astNode.templates[definition.dirName] = jFragmentParser(ast); _attachProviders(definition.registeredDir, astNode); return astNode; } /** * * @param {*} astNode * @param {*} parentAst * @returns */ function templateCompiler(astNode, parentAst) { // set element to singleNode if single child const len = (astNode && astNode.children) ? astNode.children.length : 0; const refId = astNode.refId; const hasUseAttr = astNode.attr && astNode.attr.use; if (hasUseAttr && len === 1) { errorLogs.push(`<j-template> element does not support child elements with 'use' attribute. <j-template #${refId} use="${astNode.attr.use}"/ >`); return null; } if (len === 1) { astNode = astNode.children[0]; if (typeof astNode == 'object') { astNode.refId = refId; } } else if (hasUseAttr) { astNode = TEMPLATE_KEY(hasUseAttr); } else if (!len) { return null; } // if (outletElement.context) { // generateOutletContext(outletElement); // } if (parentAst && parentAst.name && parentAst.isc) { attachContentChildren(parentAst, hasUseAttr ? { refId: hasUseAttr, name: '#', children: [astNode] } : astNode); } else { templatesMapHolder[refId] = astNode; } return null; } /** * * @param {*} astNode * @param {*} parent * @returns */ function jPlaceCompiler(astNode, parent) { if (parent.isc) { errorLogs.push(`<${parent.name}><j-place></j-place></${parent.name}> usage not allowed, please look at documentation for more info`); return null; } // check for child content if (astNode.children) { errorLogs.push(`<j-place/> element does not support child elements`); return null; } if (astNode.refId && astNode.attr && astNode.attr.selector) { errorLogs.push(`<j-place/> element does not support [selector] and [#REFID], please use [selector="#REFID"] instead`); return null; } const attachPlaceToCtor = (key, value) => { if (!ctor.place) { ctor.place = {}; if (pendingElements[ctor.selector]) { resolvedElements[ctor.selector].query.place = ctor.place; } } ctor.place[key] = value; // attach query selector to astNode astNode.refId = key; }; astNode.type = 11; if (astNode.attr) { if (astNode.attr.selector) { var firstChar = astNode.attr.selector.charAt(0); attachPlaceToCtor(astNode.attr.selector.replace(/[\[\].#]/g, ''), selectorTypes[firstChar] || 'name'); } else if (astNode.attr.template) { attachPlaceToCtor(astNode.attr.template, '#'); } // remove the element delete astNode.attr; } else { attachPlaceToCtor('@', '@') } return astNode; } /** * * @param {*} astNode * @returns */ function jFragmentParser(astNode) { let templateId = (astNode.attr && astNode.attr['template']) || (astNode.attr$ && astNode.attr$['template']); if (astNode.children && astNode.children.length && templateId) { templateId = helper.typeOf(templateId, 'object') ? templateId.prop.join('.') : templateId; errorLogs.push(`<j-fragment template="${templateId}"/> does not support child elements and template linking.`); return; } if (templateId) { // it's possible to have a conditional template // we check for templateBinding and static reference if (astNode.attr) { let propsMapper = `|${templateOptionsMapper.length}`; const obj = {}; if (astNode.props && astNode.providers) { obj.props = astNode.props; obj.providers = astNode.providers; } // generate context obj.ctx$ = generateContext(astNode.attr); templateOptionsMapper.push(obj); return TEMPLATE_KEY(templateId + propsMapper); } else if (astNode.attr$) { // templateBinding found return { type: 13, $templateId: templateId, context: astNode.context, _GT: TEMPLATE_KEY('GT') } } } return astNode; } /** * * @param {*} astAttr */ function generateContext(astAttr) { if(astAttr.context) { if (astAttr.context === '*') { return astAttr.data || null; } else if (/\{(.*)\}/.test(astAttr.context)) { return parseAstJSON(`(${astAttr.context})`).expr; } } } /** * * @param {*} filter * @param {*} filterModel */ function pipesProvider(filter, filterModel) { let pipeName = filter; const separatorIndex = filter.indexOf(':'); if (separatorIndex > -1) { pipeName = filter.substr(0, separatorIndex); filterModel.args.push(filter.substr(separatorIndex + 1, filter.length).split(';').map(expressionAst)); } /** * Push the resolved filter to the model * @param {*} deps */ const _pushFilters = (deps) => { filterModel.fns.push(`%${deps.fn}%`); providers[`${deps.fn}`] = deps.module; }; if (!resolvedFilters.hasOwnProperty(pipeName)) { const deps = resolvers.getPipe(pipeName); if (deps) { resolvedFilters[pipeName] = deps; _pushFilters(deps); } else { errorLogs.push(`Unable to resolve pipe <${helper.colors.yellow(pipeName)}>. Please include pipe and recompile application.`) } } else { _pushFilters(resolvedFilters[pipeName]); } } /** * * @param {*} data * @param {*} isAttr */ function astTextParser(data, isAttr) { const ast = interpolation.parser(data, pipesProvider); if (ast.length > 1) { /** * pasrse ast */ ast[1].forEach(item => item[1].prop = expressionAst(item[1].prop)) } return isAttr ? ast : { type: 3, ast }; } /** * * @param {*} expression */ function expressionAst(expression) { if (!helper.typeOf(expression, 'string')) { return expression; } let ast; try { if (expression.startsWith('{')) ast = parseAstJSON(expression); else if (/^\[(.*)\]$/.test(expression)) { let parsed; try { parsed = new Function(`return ${expression}`)(); } catch (e) { console.log(e); } ast = { type: "raw", value: parsed } } else ast = parseAst(expression)[0]; } catch (e) { errorLogs.push(helper.colors.white(`${e.message} -> ${expression}`)) } return ast; } /** * * @param {*} dir * @param {*} restrictA * @param {*} restrictB */ function checkDetachAbleElement(dir, restrictA, restrictB) { if (dir.indexOf(restrictA) > -1 && dir.indexOf(restrictB) > -1) { errorLogs.push(restrictA + ' directive cannot be used with ' + restrictB + ' directive'); } } /** * * @param {*} node * @param {*} value * @param {*} astNode */ function attributeParser(node, value, astNode) { var firstCharMatcher = node.charAt(0); if (helper.isContain(firstCharMatcher, charMatchers)) { parseFirstCharMatcher(firstCharMatcher, node, value); } else if (helper.isContain('attr-', node)) { setAttributeBinder(node, value, true); } /** * remove DataMatchers */ else if (helper.isContain('data-', node)) { var propName = helper.camelCase(node.replace('data-', '')); setObjectType(astNode, 'data', propName, value || propName); } else if (interpolation.hasTemplateBinding(value)) { setObjectType(astNode, 'attr$', node, astTextParser(value, true)); } else { setObjectType(astNode, 'attr', node, helper.simpleArgumentParser(value)); } /** * * @param {*} attrName * @param {*} attrValue * @param {*} objType */ function setAttributeBinder(attrName, attrValue, once = false) { if (interpolation.hasTemplateBinding(attrValue)) { return errorLogs.push(`templating not allowed in binding segment ${attrName}=${attrValue}`); } const props = helper.splitAndTrim(attrName.replace('attr-', ''), '.'); const propName = props.shift(); const filter = interpolation.removeFilters(attrValue, pipesProvider); if (props.length) { filter.type = props.shift(); filter.suffix = props.pop(); } filter.once = once; if (helper.typeOf(filter.prop, 'string')) { filter.prop = expressionAst(filter.prop); } setObjectType(astNode, 'attr$', propName, filter); } /** * * @param {*} name * @param {*} value * @param {*} fChar * @param {*} hasBinding */ function addDirectives(name, value, fChar, hasBinding = false) { var isDetachedElem = helper.is(fChar, '*'); name = name.substr(1, name.length); const dirName = name; // mock props for querySelector const props = { name: astNode.name, attr: Object.assign({}, astNode.attr) }; props.attr[dirName] = true; const registeredDir = resolvers.getDirectives(dirName, props, componentClassName); if (!registeredDir.length) { errorLogs.push(`Element <${astNode.name}> does not support this attribute [${dirName}]. if [${dirName}] is a customAttribute please create and register it.`); return; } if (isDetachedElem) { parseStructuralDirective(dirName, value, astNode, registeredDir); } else { setObjectType(astNode, 'props', dirName, value, hasBinding, hasBinding); _attachProviders(registeredDir, astNode, true); } } /** * * @param {*} fChar * @param {*} node * @param {*} value */ function parseFirstCharMatcher(fChar, node, value) { switch (fChar) { /** * Directive Node */ case (':'): case ('*'): if (value && value.match(interpolation.getDelimeter())) { errorLogs.push( `[${node}] templating not allowed in directive binding.\n To add binding to a directive use the curly braces {${node}}` ); } addDirectives(node, value, fChar); break; /** * template Node */ case ('#'): astNode.refId = node.substring(1, node.length); break; /** * Event Node */ case ('@'): if (twoWayBinding.test(node)) { _pasrseTwoWayBinding(node); } else { _parseEventBinding(node); } break; case ('{'): _parseOneWayBinding(node); break; } } /** * @{:model}="twoWayBinding" * @param {*} binding */ function _pasrseTwoWayBinding(binding) { let prop = binding.match(twoWayBinding)[1]; if (helper.isContain(':', prop.charAt(0))) { addDirectives(prop, value, ':', true); prop = prop.substr(1, prop.length); } setArrayType(astNode, 'events', { name: `${prop}Change`, value: parseAst(`${value}=$event`), custom: true }); } /** * Event Binding * e.g @click="listener()" * @onCustomEvent="someCustomListener" * @click-delegate:elementName * @param {*} dir */ function _parseEventBinding(dir) { try { dir = dir.split(/[@:]/g).splice(1); const delegateOrNorm = dir.shift().split('-'); const item = { name: delegateOrNorm.shift(), value: parseAst(value) }; if (dir.length && delegateOrNorm.length) { item.target = dir.pop().split(','); } // set the item setArrayType(astNode, 'events', item); } catch (e) { errorLogs.push(helper.colors.white(`${e.message} -> ${value}`)); } } /** * parseOneWayBinding */ function _parseOneWayBinding(dir) { /** * set the component */ var extName = oneWayBinding.exec(dir); if (!helper.isContain('attr-', extName[1])) { if (helper.isContain(extName[1].charAt(0), [':', '*'])) return addDirectives(extName[1], value, extName[1].charAt(0), true); else if (astNode.isc) return setObjectType(astNode, 'props', extName[1], value || extName[1], true, true); } /** * attach attribute binder only if value is defined */ if (value) { setAttributeBinder(extName[1], value); } } } /** * * @param {*} dirName * @param {*} value * @param {*} astNode * @param {*} registeredDir */ function parseStructuralDirective(dirName, value, astNode, registeredDir) { /** * validate the structuralElement * make sure no combination of two structural elements */ var invalidIdx = restrictedCombination.indexOf(dirName); if (invalidIdx > -1) { errorLogs.push(`${dirName} cannot be used with ${restrictedCombination[invalidIdx]}`); return; } if (astNode.structuralDirective) { errorLogs.push(`${astNode.structuralDirective.dirName} directive cannot be used with ${dirName} directive`); return; } let props = null; helper.splitAndTrim(value, ';').forEach(_parseProps); astNode.structuralDirective = { registeredDir, dirName, props }; function _parseProps(key, idx) { if (idx && helper.isContain('=', key)) { const propSplt = helper.splitAndTrim(key, "="); astNode.context = astNode.context || {}; astNode.context[propSplt[0]] = expressionAst(propSplt[1]); } else if (idx && helper.isContain(' as ', key)) { const propSplt = helper.splitAndTrim(key, ' as '); props = (props || {}); props[helper.camelCase(`${dirName}-${propSplt[0]}`)] = expressionAst(propSplt[1]); } else { const ast = interpolation.removeFilters(key, pipesProvider); props = (props || {}); if (helper.typeOf(ast.prop, 'string')) { astStringParser(ast, props); } else { props[dirName] = ast; } } } /** * concat syntax to form a variable * e.g for in value = forIn * @param {*} ast * @param {*} props */ function astStringParser(ast, props) { const regExp = /\s+\w+\s/; if (regExp.test(ast.prop)) { const matched = ast.prop.match(regExp); const checkerSplt = helper.splitAndTrim(ast.prop, matched[0]); ast.prop = expressionAst(checkerSplt[1]); props[helper.camelCase(`${dirName}-${matched[0].trim()}`)] = ast; astNode.context = astNode.context || {}; astNode.context[checkerSplt[0]] = '$context'; } else { ast.prop = expressionAst(ast.prop); props[dirName] = ast; } } } /** * * @param {*} astNode * @param {*} prop * @param {*} name * @param {*} value * @param {*} binding * @param {*} parse */ function setObjectType(astNode, prop, name, value, binding, parse) { if (astNode.props && helper.is(astNode.props[name], '@!')) { prop = 'props'; } /** * check if an observer was already registered * and the observer is not registered to attribute prop */ if (astNode.attr$ && astNode.attr$.hasOwnProperty(name) && !helper.is(prop, 'attr')) { value = astNode.attr$[name]; delete astNode.attr$[name]; } if (!astNode[prop]) { astNode[prop] = {}; } if (parse) { value = interpolation.removeFilters(value, pipesProvider); /** * Adding the props Observer instead of adding a checker */ value.prop = expressionAst(value.prop); value = binding ? value : value.prop; } astNode[prop][name] = value; } /** * * @param {*} astNode * @param {*} name * @param {*} item */ function setArrayType(astNode, name, item) { if (!astNode[name]) { astNode[name] = []; } astNode[name].push(item); } /** * * @param {*} definition * @param {*} element * @param {*} attachProps */ function _attachProviders(definition, element, attachProps) { element.providers = element.providers || []; definition.forEach(def => { providers[`${def.fn}`] = def.obj.module; element.providers.push(`%${def.fn}%`); if (attachProps) { for (const prop in def.obj.props) { if (!element.props.hasOwnProperty(def.obj.props[prop].value || prop)) { setObjectType(element, 'props', prop, '@!'); } }; } }); } /** * * @param {*} elementAstNode */ function _validateCustomElementAndThrowError(elementAstNode) { if (helper.isContain(elementAstNode.name, ctor.selector || '')) { errorLogs.push(`Element <${elementAstNode.name}> cannot call itself, this will result to circular referencing`); } // check for cached elements const cached = resolvedElements[elementAstNode.name]; const definition = (cached ? cached.definition : resolvers.getElement(elementAstNode.name, componentClassName)); if (!definition.length) { errorLogs.push(`Cannot find Element <${elementAstNode.name}>, if this is a custom Element please register it`); return; } // validate the props const props = Object.keys(elementAstNode.attr || {}).concat(Object.keys(elementAstNode.props || {})); if (props.length) { props.forEach(prop => { if (!helper.isContain(prop.split('-')[0], standardAttributes) && (!definition[0].obj.props || !isPropertyValueMap(prop, definition[0].obj.props) && !_isDirective(prop))) { errorLogs.push(`Element <${helper.colors.yellow(elementAstNode.name)}> does not support this property {${helper.colors.yellow(prop)}}`); } }); } // attachProvider _attachProviders(definition, elementAstNode); // attach child query to elementAstNode if (!cached) { resolvedElements[elementAstNode.name] = { definition, query: { child: (definition[0].obj.contentChildren || []).concat(definition[0].obj.contentChild || []), place: definition[0].obj.place } }; } /** * * @param {*} prop */ function _isDirective(prop) { return (elementAstNode.directives || []).some(dir => helper.is(dir.name, prop)); } /** * * @param {*} prop * @param {*} obj */ function isPropertyValueMap(prop, obj) { if (obj.hasOwnProperty(prop)) { return true; } return Object.keys(obj).some(key => obj[key].value && obj[key].value === prop); } } EventHandlerTypes = { change: ['checkbox', 'radio', 'select-one', 'select-multiple', 'select'], input: ['text', 'password', 'textarea', 'email', 'url', 'week', 'time', 'search', 'tel', 'range', 'number', 'month', 'datetime-local', 'date', 'color'] }; function getEventType(el) { var type = "input"; if (inarray(el.type, EventHandlerTypes.input)) { type = 'input'; } else if (inarray(el.type, EventHandlerTypes.change)) { type = 'change'; } return type; } function canSetValue(element) { isequal('input', EventHandler.getEventType(this.nativeElement)); } var parsedContent = parser(htmlContent, { normalizeWhitespace: true, lowerCaseAttributeNames: false, decodeEntities: true }).map(astParser).filter(element => element); // check for unresolved Element childContent if (pendingElements[ctor.selector]) { const query = resolvedElements[ctor.selector].query; for (const n in pendingElements[ctor.selector]) { while (pendingElements[ctor.selector][n][1].length) { const ast = pendingElements[ctor.selector][n][1].shift(); attachContentChildPlace(pendingElements[ctor.selector][n][0], ast, query); } } // remove from pending elements delete pendingElements[ctor.selector]; } return { errorLogs, parsedContent, templateOptionsMapper, templatesMapHolder, providers, pendingDependencies }; }