UNPKG

lambda-live-debugger

Version:

Debug Lambda functions locally like it is running in the cloud

173 lines (146 loc) 5.52 kB
'use strict'; import XmlNode from './xmlNode.js'; import { Matcher } from 'path-expression-matcher'; const METADATA_SYMBOL = XmlNode.getMetaDataSymbol(); /** * Helper function to strip attribute prefix from attribute map * @param {object} attrs - Attributes with prefix (e.g., {"@_class": "code"}) * @param {string} prefix - Attribute prefix to remove (e.g., "@_") * @returns {object} Attributes without prefix (e.g., {"class": "code"}) */ function stripAttributePrefix(attrs, prefix) { if (!attrs || typeof attrs !== 'object') return {}; if (!prefix) return attrs; const rawAttrs = {}; for (const key in attrs) { if (key.startsWith(prefix)) { const rawName = key.substring(prefix.length); rawAttrs[rawName] = attrs[key]; } else { // Attribute without prefix (shouldn't normally happen, but be safe) rawAttrs[key] = attrs[key]; } } return rawAttrs; } /** * * @param {array} node * @param {any} options * @param {Matcher} matcher - Path matcher instance * @returns */ export default function prettify(node, options, matcher, readonlyMatcher) { return compress(node, options, matcher, readonlyMatcher); } /** * @param {array} arr * @param {object} options * @param {Matcher} matcher - Path matcher instance * @returns object */ function compress(arr, options, matcher, readonlyMatcher) { let text; const compressedObj = {}; //This is intended to be a plain object for (let i = 0; i < arr.length; i++) { const tagObj = arr[i]; const property = propName(tagObj); // Push current property to matcher WITH RAW ATTRIBUTES (no prefix) if (property !== undefined && property !== options.textNodeName) { const rawAttrs = stripAttributePrefix( tagObj[":@"] || {}, options.attributeNamePrefix ); matcher.push(property, rawAttrs); } if (property === options.textNodeName) { if (text === undefined) text = tagObj[property]; else text += "" + tagObj[property]; } else if (property === undefined) { continue; } else if (tagObj[property]) { let val = compress(tagObj[property], options, matcher, readonlyMatcher); const isLeaf = isLeafTag(val, options); if (tagObj[":@"]) { assignAttributes(val, tagObj[":@"], readonlyMatcher, options); } else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) { val = val[options.textNodeName]; } else if (Object.keys(val).length === 0) { if (options.alwaysCreateTextNode) val[options.textNodeName] = ""; else val = ""; } if (tagObj[METADATA_SYMBOL] !== undefined && typeof val === "object" && val !== null) { val[METADATA_SYMBOL] = tagObj[METADATA_SYMBOL]; // copy over metadata } if (compressedObj[property] !== undefined && Object.prototype.hasOwnProperty.call(compressedObj, property)) { if (!Array.isArray(compressedObj[property])) { compressedObj[property] = [compressedObj[property]]; } compressedObj[property].push(val); } else { //TODO: if a node is not an array, then check if it should be an array //also determine if it is a leaf node // Pass jPath string or readonlyMatcher based on options.jPath setting const jPathOrMatcher = options.jPath ? readonlyMatcher.toString() : readonlyMatcher; if (options.isArray(property, jPathOrMatcher, isLeaf)) { compressedObj[property] = [val]; } else { compressedObj[property] = val; } } // Pop property from matcher after processing if (property !== undefined && property !== options.textNodeName) { matcher.pop(); } } } // if(text && text.length > 0) compressedObj[options.textNodeName] = text; if (typeof text === "string") { if (text.length > 0) compressedObj[options.textNodeName] = text; } else if (text !== undefined) compressedObj[options.textNodeName] = text; return compressedObj; } function propName(obj) { const keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (key !== ":@") return key; } } function assignAttributes(obj, attrMap, readonlyMatcher, options) { if (attrMap) { const keys = Object.keys(attrMap); const len = keys.length; //don't make it inline for (let i = 0; i < len; i++) { const atrrName = keys[i]; // This is the PREFIXED name (e.g., "@_class") // Strip prefix for matcher path (for isArray callback) const rawAttrName = atrrName.startsWith(options.attributeNamePrefix) ? atrrName.substring(options.attributeNamePrefix.length) : atrrName; // For attributes, we need to create a temporary path // Pass jPath string or matcher based on options.jPath setting const jPathOrMatcher = options.jPath ? readonlyMatcher.toString() + "." + rawAttrName : readonlyMatcher; if (options.isArray(atrrName, jPathOrMatcher, true, true)) { obj[atrrName] = [attrMap[atrrName]]; } else { obj[atrrName] = attrMap[atrrName]; } } } } function isLeafTag(obj, options) { const { textNodeName } = options; const propCount = Object.keys(obj).length; if (propCount === 0) { return true; } if ( propCount === 1 && (obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0) ) { return true; } return false; }