UNPKG

@structium/xml

Version:

Serialize and deserialize XML

1,508 lines (1,325 loc) 81.5 kB
/******/ // The require scope /******/ var __nccwpck_require__ = {}; /******/ /************************************************************************/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter functions for harmony exports /******/ __nccwpck_require__.d = (exports, definition) => { /******/ for(var key in definition) { /******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /******/ /* webpack/runtime/compat */ /******/ /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/"; /******/ /************************************************************************/ var __webpack_exports__ = {}; // EXPORTS __nccwpck_require__.d(__webpack_exports__, { i: () => (/* binding */ deserialize), l: () => (/* binding */ serialize) }); ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/fast-xml-parser@5.2.5/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js const defaultOptions = { preserveOrder: false, attributeNamePrefix: '@_', attributesGroupName: false, textNodeName: '#text', ignoreAttributes: true, removeNSPrefix: false, // remove NS from tag name or attribute name if true allowBooleanAttributes: false, //a tag can have attributes without any value //ignoreRootElement : false, parseTagValue: true, parseAttributeValue: false, trimValues: true, //Trim string values of tag and attributes cdataPropName: false, numberParseOptions: { hex: true, leadingZeros: true, eNotation: true }, tagValueProcessor: function(tagName, val) { return val; }, attributeValueProcessor: function(attrName, val) { return val; }, stopNodes: [], //nested tags will not be parsed even for errors alwaysCreateTextNode: false, isArray: () => false, commentPropName: false, unpairedTags: [], processEntities: true, htmlEntities: false, ignoreDeclaration: false, ignorePiTags: false, transformTagName: false, transformAttributeName: false, updateTag: function(tagName, jPath, attrs){ return tagName }, // skipEmptyListItem: false captureMetaData: false, }; const buildOptions = function(options) { return Object.assign({}, defaultOptions, options); }; ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/fast-xml-parser@5.2.5/node_modules/fast-xml-parser/src/util.js const nameStartChar = ':A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'; const nameChar = nameStartChar + '\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040'; const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*'; const regexName = new RegExp('^' + nameRegexp + '$'); function getAllMatches(string, regex) { const matches = []; let match = regex.exec(string); while (match) { const allmatches = []; allmatches.startIndex = regex.lastIndex - match[0].length; const len = match.length; for (let index = 0; index < len; index++) { allmatches.push(match[index]); } matches.push(allmatches); match = regex.exec(string); } return matches; } const isName = function(string) { const match = regexName.exec(string); return !(match === null || typeof match === 'undefined'); } function isExist(v) { return typeof v !== 'undefined'; } function isEmptyObject(obj) { return Object.keys(obj).length === 0; } /** * Copy all the properties of a into b. * @param {*} target * @param {*} a */ function merge(target, a, arrayMode) { if (a) { const keys = Object.keys(a); // will return an array of own properties const len = keys.length; //don't make it inline for (let i = 0; i < len; i++) { if (arrayMode === 'strict') { target[keys[i]] = [ a[keys[i]] ]; } else { target[keys[i]] = a[keys[i]]; } } } } /* exports.merge =function (b,a){ return Object.assign(b,a); } */ function getValue(v) { if (exports.isExist(v)) { return v; } else { return ''; } } // const fakeCall = function(a) {return a;}; // const fakeCallNoReturn = function() {}; ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/fast-xml-parser@5.2.5/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js let METADATA_SYMBOL; if (typeof Symbol !== "function") { METADATA_SYMBOL = "@@xmlMetadata"; } else { METADATA_SYMBOL = Symbol("XML Node Metadata"); } class XmlNode{ constructor(tagname) { this.tagname = tagname; this.child = []; //nested tags, text, cdata, comments in order this[":@"] = {}; //attributes map } add(key,val){ // this.child.push( {name : key, val: val, isCdata: isCdata }); if(key === "__proto__") key = "#__proto__"; this.child.push( {[key]: val }); } addChild(node, startIndex) { if(node.tagname === "__proto__") node.tagname = "#__proto__"; if(node[":@"] && Object.keys(node[":@"]).length > 0){ this.child.push( { [node.tagname]: node.child, [":@"]: node[":@"] }); }else{ this.child.push( { [node.tagname]: node.child }); } // if requested, add the startIndex if (startIndex !== undefined) { // Note: for now we just overwrite the metadata. If we had more complex metadata, // we might need to do an object append here: metadata = { ...metadata, startIndex } this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex }; } } /** symbol used for metadata */ static getMetaDataSymbol() { return METADATA_SYMBOL; } } ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/fast-xml-parser@5.2.5/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js //TODO: handle comments function readDocType(xmlData, i){ const entities = {}; if( xmlData[i + 3] === 'O' && xmlData[i + 4] === 'C' && xmlData[i + 5] === 'T' && xmlData[i + 6] === 'Y' && xmlData[i + 7] === 'P' && xmlData[i + 8] === 'E') { i = i+9; let angleBracketsCount = 1; let hasBody = false, comment = false; let exp = ""; for(;i<xmlData.length;i++){ if (xmlData[i] === '<' && !comment) { //Determine the tag type if( hasBody && hasSeq(xmlData, "!ENTITY",i)){ i += 7; let entityName, val; [entityName, val,i] = readEntityExp(xmlData,i+1); if(val.indexOf("&") === -1) //Parameter entities are not supported entities[ entityName ] = { regx : RegExp( `&${entityName};`,"g"), val: val }; } else if( hasBody && hasSeq(xmlData, "!ELEMENT",i)) { i += 8;//Not supported const {index} = readElementExp(xmlData,i+1); i = index; }else if( hasBody && hasSeq(xmlData, "!ATTLIST",i)){ i += 8;//Not supported // const {index} = readAttlistExp(xmlData,i+1); // i = index; }else if( hasBody && hasSeq(xmlData, "!NOTATION",i)) { i += 9;//Not supported const {index} = readNotationExp(xmlData,i+1); i = index; }else if( hasSeq(xmlData, "!--",i) ) comment = true; else throw new Error(`Invalid DOCTYPE`); angleBracketsCount++; exp = ""; } else if (xmlData[i] === '>') { //Read tag content if(comment){ if( xmlData[i - 1] === "-" && xmlData[i - 2] === "-"){ comment = false; angleBracketsCount--; } }else{ angleBracketsCount--; } if (angleBracketsCount === 0) { break; } }else if( xmlData[i] === '['){ hasBody = true; }else{ exp += xmlData[i]; } } if(angleBracketsCount !== 0){ throw new Error(`Unclosed DOCTYPE`); } }else{ throw new Error(`Invalid Tag instead of DOCTYPE`); } return {entities, i}; } const skipWhitespace = (data, index) => { while (index < data.length && /\s/.test(data[index])) { index++; } return index; }; function readEntityExp(xmlData, i) { //External entities are not supported // <!ENTITY ext SYSTEM "http://normal-website.com" > //Parameter entities are not supported // <!ENTITY entityname "&anotherElement;"> //Internal entities are supported // <!ENTITY entityname "replacement text"> // Skip leading whitespace after <!ENTITY i = skipWhitespace(xmlData, i); // Read entity name let entityName = ""; while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") { entityName += xmlData[i]; i++; } validateEntityName(entityName); // Skip whitespace after entity name i = skipWhitespace(xmlData, i); // Check for unsupported constructs (external entities or parameter entities) if (xmlData.substring(i, i + 6).toUpperCase() === "SYSTEM") { throw new Error("External entities are not supported"); }else if (xmlData[i] === "%") { throw new Error("Parameter entities are not supported"); } // Read entity value (internal entity) let entityValue = ""; [i, entityValue] = readIdentifierVal(xmlData, i, "entity"); i--; return [entityName, entityValue, i ]; } function readNotationExp(xmlData, i) { // Skip leading whitespace after <!NOTATION i = skipWhitespace(xmlData, i); // Read notation name let notationName = ""; while (i < xmlData.length && !/\s/.test(xmlData[i])) { notationName += xmlData[i]; i++; } validateEntityName(notationName); // Skip whitespace after notation name i = skipWhitespace(xmlData, i); // Check identifier type (SYSTEM or PUBLIC) const identifierType = xmlData.substring(i, i + 6).toUpperCase(); if (identifierType !== "SYSTEM" && identifierType !== "PUBLIC") { throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`); } i += identifierType.length; // Skip whitespace after identifier type i = skipWhitespace(xmlData, i); // Read public identifier (if PUBLIC) let publicIdentifier = null; let systemIdentifier = null; if (identifierType === "PUBLIC") { [i, publicIdentifier ] = readIdentifierVal(xmlData, i, "publicIdentifier"); // Skip whitespace after public identifier i = skipWhitespace(xmlData, i); // Optionally read system identifier if (xmlData[i] === '"' || xmlData[i] === "'") { [i, systemIdentifier ] = readIdentifierVal(xmlData, i,"systemIdentifier"); } } else if (identifierType === "SYSTEM") { // Read system identifier (mandatory for SYSTEM) [i, systemIdentifier ] = readIdentifierVal(xmlData, i, "systemIdentifier"); if (!systemIdentifier) { throw new Error("Missing mandatory system identifier for SYSTEM notation"); } } return {notationName, publicIdentifier, systemIdentifier, index: --i}; } function readIdentifierVal(xmlData, i, type) { let identifierVal = ""; const startChar = xmlData[i]; if (startChar !== '"' && startChar !== "'") { throw new Error(`Expected quoted string, found "${startChar}"`); } i++; while (i < xmlData.length && xmlData[i] !== startChar) { identifierVal += xmlData[i]; i++; } if (xmlData[i] !== startChar) { throw new Error(`Unterminated ${type} value`); } i++; return [i, identifierVal]; } function readElementExp(xmlData, i) { // <!ELEMENT br EMPTY> // <!ELEMENT div ANY> // <!ELEMENT title (#PCDATA)> // <!ELEMENT book (title, author+)> // <!ELEMENT name (content-model)> // Skip leading whitespace after <!ELEMENT i = skipWhitespace(xmlData, i); // Read element name let elementName = ""; while (i < xmlData.length && !/\s/.test(xmlData[i])) { elementName += xmlData[i]; i++; } // Validate element name if (!validateEntityName(elementName)) { throw new Error(`Invalid element name: "${elementName}"`); } // Skip whitespace after element name i = skipWhitespace(xmlData, i); let contentModel = ""; // Expect '(' to start content model if(xmlData[i] === "E" && hasSeq(xmlData, "MPTY",i)) i+=4; else if(xmlData[i] === "A" && hasSeq(xmlData, "NY",i)) i+=2; else if (xmlData[i] === "(") { i++; // Move past '(' // Read content model while (i < xmlData.length && xmlData[i] !== ")") { contentModel += xmlData[i]; i++; } if (xmlData[i] !== ")") { throw new Error("Unterminated content model"); } }else{ throw new Error(`Invalid Element Expression, found "${xmlData[i]}"`); } return { elementName, contentModel: contentModel.trim(), index: i }; } function readAttlistExp(xmlData, i) { // Skip leading whitespace after <!ATTLIST i = skipWhitespace(xmlData, i); // Read element name let elementName = ""; while (i < xmlData.length && !/\s/.test(xmlData[i])) { elementName += xmlData[i]; i++; } // Validate element name validateEntityName(elementName) // Skip whitespace after element name i = skipWhitespace(xmlData, i); // Read attribute name let attributeName = ""; while (i < xmlData.length && !/\s/.test(xmlData[i])) { attributeName += xmlData[i]; i++; } // Validate attribute name if (!validateEntityName(attributeName)) { throw new Error(`Invalid attribute name: "${attributeName}"`); } // Skip whitespace after attribute name i = skipWhitespace(xmlData, i); // Read attribute type let attributeType = ""; if (xmlData.substring(i, i + 8).toUpperCase() === "NOTATION") { attributeType = "NOTATION"; i += 8; // Move past "NOTATION" // Skip whitespace after "NOTATION" i = skipWhitespace(xmlData, i); // Expect '(' to start the list of notations if (xmlData[i] !== "(") { throw new Error(`Expected '(', found "${xmlData[i]}"`); } i++; // Move past '(' // Read the list of allowed notations let allowedNotations = []; while (i < xmlData.length && xmlData[i] !== ")") { let notation = ""; while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") { notation += xmlData[i]; i++; } // Validate notation name notation = notation.trim(); if (!validateEntityName(notation)) { throw new Error(`Invalid notation name: "${notation}"`); } allowedNotations.push(notation); // Skip '|' separator or exit loop if (xmlData[i] === "|") { i++; // Move past '|' i = skipWhitespace(xmlData, i); // Skip optional whitespace after '|' } } if (xmlData[i] !== ")") { throw new Error("Unterminated list of notations"); } i++; // Move past ')' // Store the allowed notations as part of the attribute type attributeType += " (" + allowedNotations.join("|") + ")"; } else { // Handle simple types (e.g., CDATA, ID, IDREF, etc.) while (i < xmlData.length && !/\s/.test(xmlData[i])) { attributeType += xmlData[i]; i++; } // Validate simple attribute type const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"]; if (!validTypes.includes(attributeType.toUpperCase())) { throw new Error(`Invalid attribute type: "${attributeType}"`); } } // Skip whitespace after attribute type i = skipWhitespace(xmlData, i); // Read default value let defaultValue = ""; if (xmlData.substring(i, i + 8).toUpperCase() === "#REQUIRED") { defaultValue = "#REQUIRED"; i += 8; } else if (xmlData.substring(i, i + 7).toUpperCase() === "#IMPLIED") { defaultValue = "#IMPLIED"; i += 7; } else { [i, defaultValue] = readIdentifierVal(xmlData, i, "ATTLIST"); } return { elementName, attributeName, attributeType, defaultValue, index: i } } function hasSeq(data, seq,i){ for(let j=0;j<seq.length;j++){ if(seq[j]!==data[i+j+1]) return false; } return true; } function validateEntityName(name){ if (isName(name)) return name; else throw new Error(`Invalid entity name ${name}`); } ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/strnum@2.1.1/node_modules/strnum/strnum.js const hexRegex = /^[-+]?0x[a-fA-F0-9]+$/; const numRegex = /^([\-\+])?(0*)([0-9]*(\.[0-9]*)?)$/; // const octRegex = /^0x[a-z0-9]+/; // const binRegex = /0x[a-z0-9]+/; const consider = { hex : true, // oct: false, leadingZeros: true, decimalPoint: "\.", eNotation: true, //skipLike: /regex/ }; function toNumber(str, options = {}){ options = Object.assign({}, consider, options ); if(!str || typeof str !== "string" ) return str; let trimmedStr = str.trim(); if(options.skipLike !== undefined && options.skipLike.test(trimmedStr)) return str; else if(str==="0") return 0; else if (options.hex && hexRegex.test(trimmedStr)) { return parse_int(trimmedStr, 16); // }else if (options.oct && octRegex.test(str)) { // return Number.parseInt(val, 8); }else if (trimmedStr.search(/.+[eE].+/)!== -1) { //eNotation return resolveEnotation(str,trimmedStr,options); // }else if (options.parseBin && binRegex.test(str)) { // return Number.parseInt(val, 2); }else{ //separate negative sign, leading zeros, and rest number const match = numRegex.exec(trimmedStr); // +00.123 => [ , '+', '00', '.123', .. if(match){ const sign = match[1] || ""; const leadingZeros = match[2]; let numTrimmedByZeros = trimZeros(match[3]); //complete num without leading zeros const decimalAdjacentToLeadingZeros = sign ? // 0., -00., 000. str[leadingZeros.length+1] === "." : str[leadingZeros.length] === "."; //trim ending zeros for floating number if(!options.leadingZeros //leading zeros are not allowed && (leadingZeros.length > 1 || (leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros))){ // 00, 00.3, +03.24, 03, 03.24 return str; } else{//no leading zeros or leading zeros are allowed const num = Number(trimmedStr); const parsedStr = String(num); if( num === 0) return num; if(parsedStr.search(/[eE]/) !== -1){ //given number is long and parsed to eNotation if(options.eNotation) return num; else return str; }else if(trimmedStr.indexOf(".") !== -1){ //floating number if(parsedStr === "0") return num; //0.0 else if(parsedStr === numTrimmedByZeros) return num; //0.456. 0.79000 else if( parsedStr === `${sign}${numTrimmedByZeros}`) return num; else return str; } let n = leadingZeros? numTrimmedByZeros : trimmedStr; if(leadingZeros){ // -009 => -9 return (n === parsedStr) || (sign+n === parsedStr) ? num : str }else { // +9 return (n === parsedStr) || (n === sign+parsedStr) ? num : str } } }else{ //non-numeric string return str; } } } const eNotationRegx = /^([-+])?(0*)(\d*(\.\d*)?[eE][-\+]?\d+)$/; function resolveEnotation(str,trimmedStr,options){ if(!options.eNotation) return str; const notation = trimmedStr.match(eNotationRegx); if(notation){ let sign = notation[1] || ""; const eChar = notation[3].indexOf("e") === -1 ? "E" : "e"; const leadingZeros = notation[2]; const eAdjacentToLeadingZeros = sign ? // 0E. str[leadingZeros.length+1] === eChar : str[leadingZeros.length] === eChar; if(leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str; else if(leadingZeros.length === 1 && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)){ return Number(trimmedStr); }else if(options.leadingZeros && !eAdjacentToLeadingZeros){ //accept with leading zeros //remove leading 0s trimmedStr = (notation[1] || "") + notation[3]; return Number(trimmedStr); }else return str; }else{ return str; } } /** * * @param {string} numStr without leading zeros * @returns */ function trimZeros(numStr){ if(numStr && numStr.indexOf(".") !== -1){//float numStr = numStr.replace(/0+$/, ""); //remove ending zeros if(numStr === ".") numStr = "0"; else if(numStr[0] === ".") numStr = "0"+numStr; else if(numStr[numStr.length-1] === ".") numStr = numStr.substring(0,numStr.length-1); return numStr; } return numStr; } function parse_int(numStr, base){ //polyfill if(parseInt) return parseInt(numStr, base); else if(Number.parseInt) return Number.parseInt(numStr, base); else if(window && window.parseInt) return window.parseInt(numStr, base); else throw new Error("parseInt, Number.parseInt, window.parseInt are not supported") } ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/fast-xml-parser@5.2.5/node_modules/fast-xml-parser/src/ignoreAttributes.js function getIgnoreAttributesFn(ignoreAttributes) { if (typeof ignoreAttributes === 'function') { return ignoreAttributes } if (Array.isArray(ignoreAttributes)) { return (attrName) => { for (const pattern of ignoreAttributes) { if (typeof pattern === 'string' && attrName === pattern) { return true } if (pattern instanceof RegExp && pattern.test(attrName)) { return true } } } } return () => false } ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/fast-xml-parser@5.2.5/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js ///@ts-check // const regx = // '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)' // .replace(/NAME/g, util.nameRegexp); //const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g"); //const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g"); class OrderedObjParser{ constructor(options){ this.options = options; this.currentNode = null; this.tagsNodeStack = []; this.docTypeEntities = {}; this.lastEntities = { "apos" : { regex: /&(apos|#39|#x27);/g, val : "'"}, "gt" : { regex: /&(gt|#62|#x3E);/g, val : ">"}, "lt" : { regex: /&(lt|#60|#x3C);/g, val : "<"}, "quot" : { regex: /&(quot|#34|#x22);/g, val : "\""}, }; this.ampEntity = { regex: /&(amp|#38|#x26);/g, val : "&"}; this.htmlEntities = { "space": { regex: /&(nbsp|#160);/g, val: " " }, // "lt" : { regex: /&(lt|#60);/g, val: "<" }, // "gt" : { regex: /&(gt|#62);/g, val: ">" }, // "amp" : { regex: /&(amp|#38);/g, val: "&" }, // "quot" : { regex: /&(quot|#34);/g, val: "\"" }, // "apos" : { regex: /&(apos|#39);/g, val: "'" }, "cent" : { regex: /&(cent|#162);/g, val: "¢" }, "pound" : { regex: /&(pound|#163);/g, val: "£" }, "yen" : { regex: /&(yen|#165);/g, val: "¥" }, "euro" : { regex: /&(euro|#8364);/g, val: "€" }, "copyright" : { regex: /&(copy|#169);/g, val: "©" }, "reg" : { regex: /&(reg|#174);/g, val: "®" }, "inr" : { regex: /&(inr|#8377);/g, val: "₹" }, "num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) }, "num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) }, }; this.addExternalEntities = addExternalEntities; this.parseXml = parseXml; this.parseTextData = parseTextData; this.resolveNameSpace = resolveNameSpace; this.buildAttributesMap = buildAttributesMap; this.isItStopNode = isItStopNode; this.replaceEntitiesValue = replaceEntitiesValue; this.readStopNodeData = readStopNodeData; this.saveTextToParentTag = saveTextToParentTag; this.addChild = addChild; this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes) } } function addExternalEntities(externalEntities){ const entKeys = Object.keys(externalEntities); for (let i = 0; i < entKeys.length; i++) { const ent = entKeys[i]; this.lastEntities[ent] = { regex: new RegExp("&"+ent+";","g"), val : externalEntities[ent] } } } /** * @param {string} val * @param {string} tagName * @param {string} jPath * @param {boolean} dontTrim * @param {boolean} hasAttributes * @param {boolean} isLeafNode * @param {boolean} escapeEntities */ function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) { if (val !== undefined) { if (this.options.trimValues && !dontTrim) { val = val.trim(); } if(val.length > 0){ if(!escapeEntities) val = this.replaceEntitiesValue(val); const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode); if(newval === null || newval === undefined){ //don't parse return val; }else if(typeof newval !== typeof val || newval !== val){ //overwrite return newval; }else if(this.options.trimValues){ return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions); }else{ const trimmedVal = val.trim(); if(trimmedVal === val){ return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions); }else{ return val; } } } } } function resolveNameSpace(tagname) { if (this.options.removeNSPrefix) { const tags = tagname.split(':'); const prefix = tagname.charAt(0) === '/' ? '/' : ''; if (tags[0] === 'xmlns') { return ''; } if (tags.length === 2) { tagname = prefix + tags[1]; } } return tagname; } //TODO: change regex to capture NS //const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm"); const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm'); function buildAttributesMap(attrStr, jPath, tagName) { if (this.options.ignoreAttributes !== true && typeof attrStr === 'string') { // attrStr = attrStr.replace(/\r?\n/g, ' '); //attrStr = attrStr || attrStr.trim(); const matches = getAllMatches(attrStr, attrsRegx); const len = matches.length; //don't make it inline const attrs = {}; for (let i = 0; i < len; i++) { const attrName = this.resolveNameSpace(matches[i][1]); if (this.ignoreAttributesFn(attrName, jPath)) { continue } let oldVal = matches[i][4]; let aName = this.options.attributeNamePrefix + attrName; if (attrName.length) { if (this.options.transformAttributeName) { aName = this.options.transformAttributeName(aName); } if(aName === "__proto__") aName = "#__proto__"; if (oldVal !== undefined) { if (this.options.trimValues) { oldVal = oldVal.trim(); } oldVal = this.replaceEntitiesValue(oldVal); const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath); if(newVal === null || newVal === undefined){ //don't parse attrs[aName] = oldVal; }else if(typeof newVal !== typeof oldVal || newVal !== oldVal){ //overwrite attrs[aName] = newVal; }else{ //parse attrs[aName] = parseValue( oldVal, this.options.parseAttributeValue, this.options.numberParseOptions ); } } else if (this.options.allowBooleanAttributes) { attrs[aName] = true; } } } if (!Object.keys(attrs).length) { return; } if (this.options.attributesGroupName) { const attrCollection = {}; attrCollection[this.options.attributesGroupName] = attrs; return attrCollection; } return attrs } } const parseXml = function(xmlData) { xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line const xmlObj = new XmlNode('!xml'); let currentNode = xmlObj; let textData = ""; let jPath = ""; for(let i=0; i< xmlData.length; i++){//for each char in XML data const ch = xmlData[i]; if(ch === '<'){ // const nextIndex = i+1; // const _2ndChar = xmlData[nextIndex]; if( xmlData[i+1] === '/') {//Closing Tag const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.") let tagName = xmlData.substring(i+2,closeIndex).trim(); if(this.options.removeNSPrefix){ const colonIndex = tagName.indexOf(":"); if(colonIndex !== -1){ tagName = tagName.substr(colonIndex+1); } } if(this.options.transformTagName) { tagName = this.options.transformTagName(tagName); } if(currentNode){ textData = this.saveTextToParentTag(textData, currentNode, jPath); } //check if last tag of nested tag was unpaired tag const lastTagName = jPath.substring(jPath.lastIndexOf(".")+1); if(tagName && this.options.unpairedTags.indexOf(tagName) !== -1 ){ throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`); } let propIndex = 0 if(lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1 ){ propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.')-1) this.tagsNodeStack.pop(); }else{ propIndex = jPath.lastIndexOf("."); } jPath = jPath.substring(0, propIndex); currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope textData = ""; i = closeIndex; } else if( xmlData[i+1] === '?') { let tagData = readTagExp(xmlData,i, false, "?>"); if(!tagData) throw new Error("Pi Tag is not closed."); textData = this.saveTextToParentTag(textData, currentNode, jPath); if( (this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags){ }else{ const childNode = new XmlNode(tagData.tagName); childNode.add(this.options.textNodeName, ""); if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){ childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName); } this.addChild(currentNode, childNode, jPath, i); } i = tagData.closeIndex + 1; } else if(xmlData.substr(i + 1, 3) === '!--') { const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.") if(this.options.commentPropName){ const comment = xmlData.substring(i + 4, endIndex - 2); textData = this.saveTextToParentTag(textData, currentNode, jPath); currentNode.add(this.options.commentPropName, [ { [this.options.textNodeName] : comment } ]); } i = endIndex; } else if( xmlData.substr(i + 1, 2) === '!D') { const result = readDocType(xmlData, i); this.docTypeEntities = result.entities; i = result.i; }else if(xmlData.substr(i + 1, 2) === '![') { const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2; const tagExp = xmlData.substring(i + 9,closeIndex); textData = this.saveTextToParentTag(textData, currentNode, jPath); let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true); if(val == undefined) val = ""; //cdata should be set even if it is 0 length string if(this.options.cdataPropName){ currentNode.add(this.options.cdataPropName, [ { [this.options.textNodeName] : tagExp } ]); }else{ currentNode.add(this.options.textNodeName, val); } i = closeIndex + 2; }else {//Opening tag let result = readTagExp(xmlData,i, this.options.removeNSPrefix); let tagName= result.tagName; const rawTagName = result.rawTagName; let tagExp = result.tagExp; let attrExpPresent = result.attrExpPresent; let closeIndex = result.closeIndex; if (this.options.transformTagName) { tagName = this.options.transformTagName(tagName); } //save text as child node if (currentNode && textData) { if(currentNode.tagname !== '!xml'){ //when nested tag is found textData = this.saveTextToParentTag(textData, currentNode, jPath, false); } } //check if last tag was unpaired tag const lastTag = currentNode; if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1 ){ currentNode = this.tagsNodeStack.pop(); jPath = jPath.substring(0, jPath.lastIndexOf(".")); } if(tagName !== xmlObj.tagname){ jPath += jPath ? "." + tagName : tagName; } const startIndex = i; if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) { let tagContent = ""; //self-closing tag if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){ if(tagName[tagName.length - 1] === "/"){ //remove trailing '/' tagName = tagName.substr(0, tagName.length - 1); jPath = jPath.substr(0, jPath.length - 1); tagExp = tagName; }else{ tagExp = tagExp.substr(0, tagExp.length - 1); } i = result.closeIndex; } //unpaired tag else if(this.options.unpairedTags.indexOf(tagName) !== -1){ i = result.closeIndex; } //normal tag else{ //read until closing tag is found const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1); if(!result) throw new Error(`Unexpected end of ${rawTagName}`); i = result.i; tagContent = result.tagContent; } const childNode = new XmlNode(tagName); if(tagName !== tagExp && attrExpPresent){ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); } if(tagContent) { tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true); } jPath = jPath.substr(0, jPath.lastIndexOf(".")); childNode.add(this.options.textNodeName, tagContent); this.addChild(currentNode, childNode, jPath, startIndex); }else{ //selfClosing tag if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){ if(tagName[tagName.length - 1] === "/"){ //remove trailing '/' tagName = tagName.substr(0, tagName.length - 1); jPath = jPath.substr(0, jPath.length - 1); tagExp = tagName; }else{ tagExp = tagExp.substr(0, tagExp.length - 1); } if(this.options.transformTagName) { tagName = this.options.transformTagName(tagName); } const childNode = new XmlNode(tagName); if(tagName !== tagExp && attrExpPresent){ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); } this.addChild(currentNode, childNode, jPath, startIndex); jPath = jPath.substr(0, jPath.lastIndexOf(".")); } //opening tag else{ const childNode = new XmlNode( tagName); this.tagsNodeStack.push(currentNode); if(tagName !== tagExp && attrExpPresent){ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); } this.addChild(currentNode, childNode, jPath, startIndex); currentNode = childNode; } textData = ""; i = closeIndex; } } }else{ textData += xmlData[i]; } } return xmlObj.child; } function addChild(currentNode, childNode, jPath, startIndex){ // unset startIndex if not requested if (!this.options.captureMetaData) startIndex = undefined; const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"]) if(result === false){ } else if(typeof result === "string"){ childNode.tagname = result currentNode.addChild(childNode, startIndex); }else{ currentNode.addChild(childNode, startIndex); } } const replaceEntitiesValue = function(val){ if(this.options.processEntities){ for(let entityName in this.docTypeEntities){ const entity = this.docTypeEntities[entityName]; val = val.replace( entity.regx, entity.val); } for(let entityName in this.lastEntities){ const entity = this.lastEntities[entityName]; val = val.replace( entity.regex, entity.val); } if(this.options.htmlEntities){ for(let entityName in this.htmlEntities){ const entity = this.htmlEntities[entityName]; val = val.replace( entity.regex, entity.val); } } val = val.replace( this.ampEntity.regex, this.ampEntity.val); } return val; } function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) { if (textData) { //store previously collected data as textNode if(isLeafNode === undefined) isLeafNode = currentNode.child.length === 0 textData = this.parseTextData(textData, currentNode.tagname, jPath, false, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false, isLeafNode); if (textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData); textData = ""; } return textData; } //TODO: use jPath to simplify the logic /** * * @param {string[]} stopNodes * @param {string} jPath * @param {string} currentTagName */ function isItStopNode(stopNodes, jPath, currentTagName){ const allNodesExp = "*." + currentTagName; for (const stopNodePath in stopNodes) { const stopNodeExp = stopNodes[stopNodePath]; if( allNodesExp === stopNodeExp || jPath === stopNodeExp ) return true; } return false; } /** * Returns the tag Expression and where it is ending handling single-double quotes situation * @param {string} xmlData * @param {number} i starting index * @returns */ function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){ let attrBoundary; let tagExp = ""; for (let index = i; index < xmlData.length; index++) { let ch = xmlData[index]; if (attrBoundary) { if (ch === attrBoundary) attrBoundary = "";//reset } else if (ch === '"' || ch === "'") { attrBoundary = ch; } else if (ch === closingChar[0]) { if(closingChar[1]){ if(xmlData[index + 1] === closingChar[1]){ return { data: tagExp, index: index } } }else{ return { data: tagExp, index: index } } } else if (ch === '\t') { ch = " " } tagExp += ch; } } function findClosingIndex(xmlData, str, i, errMsg){ const closingIndex = xmlData.indexOf(str, i); if(closingIndex === -1){ throw new Error(errMsg) }else{ return closingIndex + str.length - 1; } } function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){ const result = tagExpWithClosingIndex(xmlData, i+1, closingChar); if(!result) return; let tagExp = result.data; const closeIndex = result.index; const separatorIndex = tagExp.search(/\s/); let tagName = tagExp; let attrExpPresent = true; if(separatorIndex !== -1){//separate tag name and attributes expression tagName = tagExp.substring(0, separatorIndex); tagExp = tagExp.substring(separatorIndex + 1).trimStart(); } const rawTagName = tagName; if(removeNSPrefix){ const colonIndex = tagName.indexOf(":"); if(colonIndex !== -1){ tagName = tagName.substr(colonIndex+1); attrExpPresent = tagName !== result.data.substr(colonIndex + 1); } } return { tagName: tagName, tagExp: tagExp, closeIndex: closeIndex, attrExpPresent: attrExpPresent, rawTagName: rawTagName, } } /** * find paired tag for a stop node * @param {string} xmlData * @param {string} tagName * @param {number} i */ function readStopNodeData(xmlData, tagName, i){ const startIndex = i; // Starting at 1 since we already have an open tag let openTagCount = 1; for (; i < xmlData.length; i++) { if( xmlData[i] === "<"){ if (xmlData[i+1] === "/") {//close tag const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`); let closeTagName = xmlData.substring(i+2,closeIndex).trim(); if(closeTagName === tagName){ openTagCount--; if (openTagCount === 0) { return { tagContent: xmlData.substring(startIndex, i), i : closeIndex } } } i=closeIndex; } else if(xmlData[i+1] === '?') { const closeIndex = findClosingIndex(xmlData, "?>", i+1, "StopNode is not closed.") i=closeIndex; } else if(xmlData.substr(i + 1, 3) === '!--') { const closeIndex = findClosingIndex(xmlData, "-->", i+3, "StopNode is not closed.") i=closeIndex; } else if(xmlData.substr(i + 1, 2) === '![') { const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2; i=closeIndex; } else { const tagData = readTagExp(xmlData, i, '>') if (tagData) { const openTagName = tagData && tagData.tagName; if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") { openTagCount++; } i=tagData.closeIndex; } } } }//end for loop } function parseValue(val, shouldParse, options) { if (shouldParse && typeof val === 'string') { //console.log(options) const newval = val.trim(); if(newval === 'true' ) return true; else if(newval === 'false' ) return false; else return toNumber(val, options); } else { if (isExist(val)) { return val; } else { return ''; } } } ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/fast-xml-parser@5.2.5/node_modules/fast-xml-parser/src/xmlparser/node2json.js const node2json_METADATA_SYMBOL = XmlNode.getMetaDataSymbol(); /** * * @param {array} node * @param {any} options * @returns */ function prettify(node, options){ return compress( node, options); } /** * * @param {array} arr * @param {object} options * @param {string} jPath * @returns object */ function compress(arr, options, jPath){ let text; const compressedObj = {}; for (let i = 0; i < arr.length; i++) { const tagObj = arr[i]; const property = propName(tagObj); let newJpath = ""; if(jPath === undefined) newJpath = property; else newJpath = jPath + "." + property; 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, newJpath); const isLeaf = isLeafTag(val, options); if (tagObj[node2json_METADATA_SYMBOL] !== undefined) { val[node2json_METADATA_SYMBOL] = tagObj[node2json_METADATA_SYMBOL]; // copy over metadata } if(tagObj[":@"]){ assignAttributes( val, tagObj[":@"], newJpath, 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(compressedObj[property] !== undefined && compressedObj.hasOwnProperty(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 if (options.isArray(property, newJpath, isLeaf )) { compressedObj[property] = [val]; }else{ compressedObj[property] = val; } } } } // 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, jpath, 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]; if (options.isArray(atrrName, jpath + "." + atrrName, 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; } ;// CONCATENATED MODULE: ../../../node_modules/.pnpm/fast-xml-parser@5.2.5/node_modules/fast-xml-parser/src/validator.js const validator_defaultOptions = { allowBooleanAttributes: false, //A tag can have attributes without any value unpairedTags: [] }; //const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g"); function validate(xmlData, options) { options = Object.assign({}, validator_defaultOptions, options); //xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line //xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag //xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE const tags = []; let tagFound = false; //indicates that the root tag has been closed (aka. depth 0 has been reached) let reachedRoot = false; if (xmlData[0] === '\ufeff') { // check for byte order mark (BOM) xmlData = xmlData.substr(1); } for (let i = 0; i < xmlData.length; i++) { if (xmlData[i] === '<' && xmlData[i+1] === '?') { i+=2; i = readPI(xmlData,i); if (i.err) return i; }else if (xmlData[i] === '<') { //starting of tag //read until you reach to '>' avoiding any '>' in attribute value let tagStartPos = i; i++; if (xmlData[i] === '!') { i = readCommentAndCDATA(xmlData, i); continue; } else { let closingTag = false; if (xmlData[i] === '/') { //closing tag closingTag = true; i++; } //read tagname let tagName = ''; for (; i < xmlData.length && xmlData[i] !== '>' && xmlData[i] !== ' ' && xmlData[i] !== '\t' && xmlData[i] !== '\n' && xmlData[i] !== '\r'; i++ ) { tagName += xmlData[i]; } tagName = tagName.trim(); //console.log(tagName); if (tagName[tagName.length - 1] === '/') {