hot-updater 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,12 +67,12 @@ const criticalProperties = [
67
67
  ];
68
68
  //#endregion
69
69
  //#region ../../node_modules/.pnpm/fast-xml-parser@5.7.2/node_modules/fast-xml-parser/src/validator.js
70
- const defaultOptions$2 = {
70
+ const defaultOptions$1 = {
71
71
  allowBooleanAttributes: false,
72
72
  unpairedTags: []
73
73
  };
74
74
  function validate(xmlData, options) {
75
- options = Object.assign({}, defaultOptions$2, options);
75
+ options = Object.assign({}, defaultOptions$1, options);
76
76
  const tags = [];
77
77
  let tagFound = false;
78
78
  let reachedRoot = false;
@@ -1839,7 +1839,7 @@ const defaultOnDangerousProperty = (name) => {
1839
1839
  if (DANGEROUS_PROPERTY_NAMES.includes(name)) return "__" + name;
1840
1840
  return name;
1841
1841
  };
1842
- const defaultOptions$1 = {
1842
+ const defaultOptions = {
1843
1843
  preserveOrder: false,
1844
1844
  attributeNamePrefix: "@_",
1845
1845
  attributesGroupName: false,
@@ -1926,7 +1926,7 @@ function normalizeProcessEntities(value, htmlEntities) {
1926
1926
  return normalizeProcessEntities(true);
1927
1927
  }
1928
1928
  const buildOptions = function(options) {
1929
- const built = Object.assign({}, defaultOptions$1, options);
1929
+ const built = Object.assign({}, defaultOptions, options);
1930
1930
  const propertyNameOptions = [
1931
1931
  {
1932
1932
  value: built.attributeNamePrefix,
@@ -2509,7 +2509,7 @@ function handleInfinity(str, num, options) {
2509
2509
  }
2510
2510
  //#endregion
2511
2511
  //#region ../../node_modules/.pnpm/fast-xml-parser@5.7.2/node_modules/fast-xml-parser/src/ignoreAttributes.js
2512
- function getIgnoreAttributesFn$1(ignoreAttributes) {
2512
+ function getIgnoreAttributesFn(ignoreAttributes) {
2513
2513
  if (typeof ignoreAttributes === "function") return ignoreAttributes;
2514
2514
  if (Array.isArray(ignoreAttributes)) return (attrName) => {
2515
2515
  for (const pattern of ignoreAttributes) {
@@ -3321,11 +3321,11 @@ var OrderedObjParser = class {
3321
3321
  this.resolveNameSpace = resolveNameSpace;
3322
3322
  this.buildAttributesMap = buildAttributesMap;
3323
3323
  this.isItStopNode = isItStopNode;
3324
- this.replaceEntitiesValue = replaceEntitiesValue$1;
3324
+ this.replaceEntitiesValue = replaceEntitiesValue;
3325
3325
  this.readStopNodeData = readStopNodeData;
3326
3326
  this.saveTextToParentTag = saveTextToParentTag;
3327
3327
  this.addChild = addChild;
3328
- this.ignoreAttributesFn = getIgnoreAttributesFn$1(this.options.ignoreAttributes);
3328
+ this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes);
3329
3329
  this.entityExpansionCount = 0;
3330
3330
  this.currentExpandedLength = 0;
3331
3331
  let namedEntities = { ...XML };
@@ -3624,7 +3624,7 @@ function addChild(currentNode, childNode, matcher, startIndex) {
3624
3624
  * @param {string} tagName - Tag name
3625
3625
  * @param {string|Matcher} jPath - jPath string or Matcher instance based on options.jPath
3626
3626
  */
3627
- function replaceEntitiesValue$1(val, tagName, jPath) {
3627
+ function replaceEntitiesValue(val, tagName, jPath) {
3628
3628
  const entityConfig = this.options.processEntities;
3629
3629
  if (!entityConfig || !entityConfig.enabled) return val;
3630
3630
  if (entityConfig.allowedTags) {
@@ -3832,7 +3832,7 @@ function compress(arr, options, matcher, readonlyMatcher) {
3832
3832
  const compressedObj = {};
3833
3833
  for (let i = 0; i < arr.length; i++) {
3834
3834
  const tagObj = arr[i];
3835
- const property = propName$1(tagObj);
3835
+ const property = propName(tagObj);
3836
3836
  if (property !== void 0 && property !== options.textNodeName) {
3837
3837
  const rawAttrs = stripAttributePrefix(tagObj[":@"] || {}, options.attributeNamePrefix);
3838
3838
  matcher.push(property, rawAttrs);
@@ -3867,7 +3867,7 @@ function compress(arr, options, matcher, readonlyMatcher) {
3867
3867
  } else if (text !== void 0) compressedObj[options.textNodeName] = text;
3868
3868
  return compressedObj;
3869
3869
  }
3870
- function propName$1(obj) {
3870
+ function propName(obj) {
3871
3871
  const keys = Object.keys(obj);
3872
3872
  for (let i = 0; i < keys.length; i++) {
3873
3873
  const key = keys[i];
@@ -3945,667 +3945,290 @@ var XMLParser = class {
3945
3945
  }
3946
3946
  };
3947
3947
  //#endregion
3948
- //#region ../../node_modules/.pnpm/fast-xml-builder@1.2.0/node_modules/fast-xml-builder/src/util.js
3949
- function safeComment(val) {
3950
- return String(val).replace(/--/g, "- -").replace(/--/g, "- -").replace(/-$/, "- ");
3951
- }
3952
- function safeCdata(val) {
3953
- return String(val).replace(/\]\]>/g, "]]]]><![CDATA[>");
3954
- }
3955
- function escapeAttribute(val) {
3956
- return String(val).replace(/"/g, "&quot;").replace(/'/g, "&apos;");
3957
- }
3958
- //#endregion
3959
- //#region ../../node_modules/.pnpm/xml-naming@0.1.0/node_modules/xml-naming/src/index.js
3960
- /**
3961
- * xml-naming
3962
- * Validates XML Name productions as defined in the XML 1.0 and 1.1 specifications.
3963
- * Covers: Name, NCName, QName, NMToken, NMTokens
3964
- *
3965
- * XML 1.0 spec: https://www.w3.org/TR/xml/#NT-Name
3966
- * XML 1.1 spec: https://www.w3.org/TR/xml11/#NT-NameStartChar
3967
- * XML NS spec: https://www.w3.org/TR/xml-names/#NT-NCName
3968
- */
3969
- const nameStartChar10 = ":A-Za-z_À-ÖØ-öø-˿Ͱ-ͽͿ-҆҈-῿‌-‍⁰-↏Ⰰ-⿯、-퟿豈-﷏ﷰ-�";
3970
- const nameChar10 = nameStartChar10 + "\\-\\.\\d" + "·" + "̀-ͯ" + "‿-⁀";
3971
- const nameStartChar11 = ":A-Za-z_À-˿Ͱ-ͽͿ-҆҈-῿‌-‍⁰-↏Ⰰ-⿯、-퟿豈-﷏ﷰ-�𐀀-󯿿";
3972
- const nameChar11 = nameStartChar11 + "\\-\\.\\d" + "·" + "̀-ͯ" + "҇" + "‿-⁀";
3973
- const buildRegexes = (startChar, char, flags = "") => {
3974
- const ncNamePat = `[${startChar.replace(":", "")}][${char.replace(":", "")}]*`;
3975
- return {
3976
- name: new RegExp(`^[${startChar}][${char}]*$`, flags),
3977
- ncName: new RegExp(`^${ncNamePat}$`, flags),
3978
- qName: new RegExp(`^${ncNamePat}(?::${ncNamePat})?$`, flags),
3979
- nmToken: new RegExp(`^[${char}]+$`, flags),
3980
- nmTokens: new RegExp(`^[${char}]+(?:\\s+[${char}]+)*$`, flags)
3981
- };
3948
+ //#region src/utils/configParser/androidManifestEdit.ts
3949
+ const NAME = /[^\s/>=]+/y;
3950
+ const WHITESPACE = /\s*/y;
3951
+ const matchAt = (pattern, text, index) => {
3952
+ pattern.lastIndex = index;
3953
+ return pattern.exec(text);
3954
+ };
3955
+ const skipTo = (xml, terminator, from) => {
3956
+ const index = xml.indexOf(terminator, from);
3957
+ if (index === -1) throw new Error(`unterminated markup, expected "${terminator}"`);
3958
+ return index + terminator.length;
3982
3959
  };
3983
- const regexes10 = buildRegexes(nameStartChar10, nameChar10);
3984
- const regexes11 = buildRegexes(nameStartChar11, nameChar11, "u");
3985
- const getRegexes = (xmlVersion = "1.0") => xmlVersion === "1.1" ? regexes11 : regexes10;
3986
- /**
3987
- * Returns true if the string is a valid QName (Qualified Name).
3988
- * Allows exactly one colon as a prefix separator: prefix:localName.
3989
- * Used for: element and attribute names in namespace-aware XML/SVG.
3990
- */
3991
- const qName = (str, { xmlVersion = "1.0" } = {}) => getRegexes(xmlVersion).qName.test(str);
3992
- //#endregion
3993
- //#region ../../node_modules/.pnpm/fast-xml-builder@1.2.0/node_modules/fast-xml-builder/src/orderedJs2Xml.js
3994
- const EOL = "\n";
3995
- /**
3996
- * Detect XML version from the first element of the ordered array input.
3997
- * The first element must be a ?xml processing instruction with a version attribute.
3998
- * Returns '1.0' if not found.
3999
- *
4000
- * @param {array} jArray
4001
- * @param {object} options
4002
- */
4003
- function detectXmlVersionFromArray(jArray, options) {
4004
- if (!Array.isArray(jArray) || jArray.length === 0) return "1.0";
4005
- const first = jArray[0];
4006
- if (propName(first) === "?xml") {
4007
- const attrs = first[":@"];
4008
- if (attrs) {
4009
- const versionKey = options.attributeNamePrefix + "version";
4010
- if (attrs[versionKey]) return attrs[versionKey];
4011
- }
4012
- }
4013
- return "1.0";
4014
- }
4015
- /**
4016
- * Resolve a tag or attribute name through sanitizeName if configured.
4017
- * Validation via xml-naming's qName is performed first; the sanitizeName
4018
- * callback is invoked only when the name is invalid. If sanitizeName is
4019
- * false (default), no validation occurs and the name is used as-is.
4020
- *
4021
- * @param {string} name - raw name from the JS object
4022
- * @param {boolean} isAttribute - true when resolving an attribute name
4023
- * @param {object} options
4024
- * @param {Matcher} matcher - current matcher state (readonly from callback perspective)
4025
- * @param {string} xmlVersion - '1.0' or '1.1', forwarded to xml-naming
4026
- */
4027
- function resolveTagName$1(name, isAttribute, options, matcher, xmlVersion) {
4028
- if (!options.sanitizeName) return name;
4029
- if (qName(name, { xmlVersion })) return name;
4030
- return options.sanitizeName(name, {
4031
- isAttribute,
4032
- matcher: matcher.readOnly()
4033
- });
4034
- }
4035
3960
  /**
4036
- * @param {array} jArray
4037
- * @param {any} options
4038
- * @returns
3961
+ * Element tags of an XML document in document order. Comments, CDATA,
3962
+ * processing instructions and the doctype are skipped, so markup inside them
3963
+ * is never mistaken for an element.
4039
3964
  */
4040
- function toXml(jArray, options) {
4041
- let indentation = "";
4042
- if (options.format) indentation = EOL;
4043
- const stopNodeExpressions = [];
4044
- if (options.stopNodes && Array.isArray(options.stopNodes)) for (let i = 0; i < options.stopNodes.length; i++) {
4045
- const node = options.stopNodes[i];
4046
- if (typeof node === "string") stopNodeExpressions.push(new Expression(node));
4047
- else if (node instanceof Expression) stopNodeExpressions.push(node);
4048
- }
4049
- const xmlVersion = detectXmlVersionFromArray(jArray, options);
4050
- const matcher = new Matcher();
4051
- return arrToStr(jArray, options, indentation, matcher, stopNodeExpressions, xmlVersion);
4052
- }
4053
- function arrToStr(arr, options, indentation, matcher, stopNodeExpressions, xmlVersion) {
4054
- let xmlStr = "";
4055
- let isPreviousElementTag = false;
4056
- if (options.maxNestedTags && matcher.getDepth() > options.maxNestedTags) throw new Error("Maximum nested tags exceeded");
4057
- if (!Array.isArray(arr)) {
4058
- if (arr !== void 0 && arr !== null) {
4059
- let text = arr.toString();
4060
- text = replaceEntitiesValue(text, options);
4061
- return text;
4062
- }
4063
- return "";
4064
- }
4065
- for (let i = 0; i < arr.length; i++) {
4066
- const tagObj = arr[i];
4067
- const rawTagName = propName(tagObj);
4068
- if (rawTagName === void 0) continue;
4069
- const tagName = rawTagName === options.textNodeName || rawTagName === options.cdataPropName || rawTagName === options.commentPropName || rawTagName[0] === "?" ? rawTagName : resolveTagName$1(rawTagName, false, options, matcher, xmlVersion);
4070
- const attrValues = extractAttributeValues(tagObj[":@"], options);
4071
- matcher.push(tagName, attrValues);
4072
- const isStopNode = checkStopNode(matcher, stopNodeExpressions);
4073
- if (tagName === options.textNodeName) {
4074
- let tagText = tagObj[rawTagName];
4075
- if (!isStopNode) {
4076
- tagText = options.tagValueProcessor(tagName, tagText);
4077
- tagText = replaceEntitiesValue(tagText, options);
4078
- }
4079
- if (isPreviousElementTag) xmlStr += indentation;
4080
- xmlStr += tagText;
4081
- isPreviousElementTag = false;
4082
- matcher.pop();
4083
- continue;
4084
- } else if (tagName === options.cdataPropName) {
4085
- if (isPreviousElementTag) xmlStr += indentation;
4086
- const val = tagObj[rawTagName][0][options.textNodeName];
4087
- const safeVal = safeCdata(val);
4088
- xmlStr += `<![CDATA[${safeVal}]]>`;
4089
- isPreviousElementTag = false;
4090
- matcher.pop();
3965
+ const scanTags = (xml) => {
3966
+ const tags = [];
3967
+ let index = 0;
3968
+ while (index < xml.length) {
3969
+ const start = xml.indexOf("<", index);
3970
+ if (start === -1) break;
3971
+ if (xml.startsWith("<!--", start)) {
3972
+ index = skipTo(xml, "-->", start + 4);
4091
3973
  continue;
4092
- } else if (tagName === options.commentPropName) {
4093
- const val = tagObj[rawTagName][0][options.textNodeName];
4094
- const safeVal = safeComment(val);
4095
- xmlStr += indentation + `<!--${safeVal}-->`;
4096
- isPreviousElementTag = true;
4097
- matcher.pop();
3974
+ }
3975
+ if (xml.startsWith("<![CDATA[", start)) {
3976
+ index = skipTo(xml, "]]>", start + 9);
4098
3977
  continue;
4099
- } else if (tagName[0] === "?") {
4100
- const attStr = attr_to_str(tagObj[":@"], options, isStopNode, matcher, xmlVersion);
4101
- xmlStr += (tagName === "?xml" ? "" : indentation) + `<${tagName}${attStr}?>`;
4102
- isPreviousElementTag = true;
4103
- matcher.pop();
3978
+ }
3979
+ if (xml.startsWith("<?", start)) {
3980
+ index = skipTo(xml, "?>", start + 2);
4104
3981
  continue;
4105
3982
  }
4106
- let newIdentation = indentation;
4107
- if (newIdentation !== "") newIdentation += options.indentBy;
4108
- const tagStart = indentation + `<${tagName}${attr_to_str(tagObj[":@"], options, isStopNode, matcher, xmlVersion)}`;
4109
- let tagValue;
4110
- if (isStopNode) tagValue = getRawContent(tagObj[rawTagName], options);
4111
- else tagValue = arrToStr(tagObj[rawTagName], options, newIdentation, matcher, stopNodeExpressions, xmlVersion);
4112
- if (options.unpairedTags.indexOf(tagName) !== -1) {
4113
- if (options.suppressUnpairedNode) xmlStr += tagStart + ">";
4114
- else xmlStr += tagStart + "/>";
4115
- } else if ((!tagValue || tagValue.length === 0) && options.suppressEmptyNode) xmlStr += tagStart + "/>";
4116
- else if (tagValue && tagValue.endsWith(">")) xmlStr += tagStart + `>${tagValue}${indentation}</${tagName}>`;
4117
- else {
4118
- xmlStr += tagStart + ">";
4119
- if (tagValue && indentation !== "" && (tagValue.includes("/>") || tagValue.includes("</"))) xmlStr += indentation + options.indentBy + tagValue + indentation;
4120
- else xmlStr += tagValue;
4121
- xmlStr += `</${tagName}>`;
3983
+ if (xml.startsWith("<!", start)) {
3984
+ index = skipTo(xml, ">", start + 2);
3985
+ continue;
4122
3986
  }
4123
- isPreviousElementTag = true;
4124
- matcher.pop();
4125
- }
4126
- return xmlStr;
4127
- }
4128
- /**
4129
- * Extract attribute values from the ":@" object and return as plain object
4130
- * for passing to matcher.push()
4131
- */
4132
- function extractAttributeValues(attrMap, options) {
4133
- if (!attrMap || options.ignoreAttributes) return null;
4134
- const attrValues = {};
4135
- let hasAttrs = false;
4136
- for (let attr in attrMap) {
4137
- if (!Object.prototype.hasOwnProperty.call(attrMap, attr)) continue;
4138
- const cleanAttrName = attr.startsWith(options.attributeNamePrefix) ? attr.substr(options.attributeNamePrefix.length) : attr;
4139
- attrValues[cleanAttrName] = escapeAttribute(attrMap[attr]);
4140
- hasAttrs = true;
4141
- }
4142
- return hasAttrs ? attrValues : null;
4143
- }
4144
- /**
4145
- * Extract raw content from a stopNode without any processing
4146
- * This preserves the content exactly as-is, including special characters
4147
- */
4148
- function getRawContent(arr, options) {
4149
- if (!Array.isArray(arr)) {
4150
- if (arr !== void 0 && arr !== null) return arr.toString();
4151
- return "";
4152
- }
4153
- let content = "";
4154
- for (let i = 0; i < arr.length; i++) {
4155
- const item = arr[i];
4156
- const tagName = propName(item);
4157
- if (tagName === options.textNodeName) content += item[tagName];
4158
- else if (tagName === options.cdataPropName) content += item[tagName][0][options.textNodeName];
4159
- else if (tagName === options.commentPropName) content += item[tagName][0][options.textNodeName];
4160
- else if (tagName && tagName[0] === "?") continue;
4161
- else if (tagName) {
4162
- const attStr = attr_to_str_raw(item[":@"], options);
4163
- const nestedContent = getRawContent(item[tagName], options);
4164
- if (!nestedContent || nestedContent.length === 0) content += `<${tagName}${attStr}/>`;
4165
- else content += `<${tagName}${attStr}>${nestedContent}</${tagName}>`;
4166
- }
4167
- }
4168
- return content;
4169
- }
4170
- /**
4171
- * Build attribute string for stopNodes - NO entity replacement
4172
- */
4173
- function attr_to_str_raw(attrMap, options) {
4174
- let attrStr = "";
4175
- if (attrMap && !options.ignoreAttributes) for (let attr in attrMap) {
4176
- if (!Object.prototype.hasOwnProperty.call(attrMap, attr)) continue;
4177
- let attrVal = attrMap[attr];
4178
- if (attrVal === true && options.suppressBooleanAttributes) attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}`;
4179
- else attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}="${escapeAttribute(attrVal)}"`;
4180
- }
4181
- return attrStr;
4182
- }
4183
- function propName(obj) {
4184
- const keys = Object.keys(obj);
4185
- for (let i = 0; i < keys.length; i++) {
4186
- const key = keys[i];
4187
- if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
4188
- if (key !== ":@") return key;
4189
- }
4190
- }
4191
- /**
4192
- * Build attribute string, resolving attribute names through sanitizeName when configured.
4193
- * Accepts matcher so the callback has path context.
4194
- */
4195
- function attr_to_str(attrMap, options, isStopNode, matcher, xmlVersion) {
4196
- let attrStr = "";
4197
- if (attrMap && !options.ignoreAttributes) for (let attr in attrMap) {
4198
- if (!Object.prototype.hasOwnProperty.call(attrMap, attr)) continue;
4199
- const cleanAttrName = attr.substr(options.attributeNamePrefix.length);
4200
- const resolvedAttrName = isStopNode ? cleanAttrName : resolveTagName$1(cleanAttrName, true, options, matcher, xmlVersion);
4201
- let attrVal;
4202
- if (isStopNode) attrVal = attrMap[attr];
4203
- else {
4204
- attrVal = options.attributeValueProcessor(attr, attrMap[attr]);
4205
- attrVal = replaceEntitiesValue(attrVal, options);
3987
+ let cursor = start + 1;
3988
+ const closing = xml[cursor] === "/";
3989
+ if (closing) cursor += 1;
3990
+ const name = matchAt(NAME, xml, cursor)?.[0];
3991
+ if (!name) throw new Error(`invalid tag at offset ${start}`);
3992
+ cursor += name.length;
3993
+ const attributes = [];
3994
+ let selfClosing = false;
3995
+ for (;;) {
3996
+ cursor += matchAt(WHITESPACE, xml, cursor)?.[0].length ?? 0;
3997
+ if (cursor >= xml.length) throw new Error(`unterminated tag <${name}> at offset ${start}`);
3998
+ if (xml.startsWith("/>", cursor)) {
3999
+ selfClosing = true;
4000
+ cursor += 2;
4001
+ break;
4002
+ }
4003
+ if (xml[cursor] === ">") {
4004
+ cursor += 1;
4005
+ break;
4006
+ }
4007
+ const attributeStart = cursor;
4008
+ const attributeName = matchAt(NAME, xml, cursor)?.[0];
4009
+ if (!attributeName) throw new Error(`invalid attribute in <${name}> at offset ${cursor}`);
4010
+ cursor += attributeName.length;
4011
+ cursor += matchAt(WHITESPACE, xml, cursor)?.[0].length ?? 0;
4012
+ if (xml[cursor] !== "=") throw new Error(`attribute ${attributeName} in <${name}> has no value`);
4013
+ cursor += 1;
4014
+ cursor += matchAt(WHITESPACE, xml, cursor)?.[0].length ?? 0;
4015
+ const quote = xml[cursor];
4016
+ if (quote !== "\"" && quote !== "'") throw new Error(`attribute ${attributeName} in <${name}> is not quoted`);
4017
+ const valueEnd = xml.indexOf(quote, cursor + 1);
4018
+ if (valueEnd === -1) throw new Error(`unterminated value of ${attributeName} in <${name}>`);
4019
+ attributes.push({
4020
+ name: attributeName,
4021
+ rawValue: xml.slice(cursor + 1, valueEnd),
4022
+ start: attributeStart,
4023
+ end: valueEnd + 1
4024
+ });
4025
+ cursor = valueEnd + 1;
4206
4026
  }
4207
- if (attrVal === true && options.suppressBooleanAttributes) attrStr += ` ${resolvedAttrName}`;
4208
- else attrStr += ` ${resolvedAttrName}="${escapeAttribute(attrVal)}"`;
4027
+ tags.push({
4028
+ name,
4029
+ start,
4030
+ end: cursor,
4031
+ closing,
4032
+ selfClosing,
4033
+ attributes
4034
+ });
4035
+ index = cursor;
4209
4036
  }
4210
- return attrStr;
4211
- }
4212
- function checkStopNode(matcher, stopNodeExpressions) {
4213
- if (!stopNodeExpressions || stopNodeExpressions.length === 0) return false;
4214
- for (let i = 0; i < stopNodeExpressions.length; i++) if (matcher.matches(stopNodeExpressions[i])) return true;
4215
- return false;
4216
- }
4217
- function replaceEntitiesValue(textValue, options) {
4218
- if (textValue && textValue.length > 0 && options.processEntities) for (let i = 0; i < options.entities.length; i++) {
4219
- const entity = options.entities[i];
4220
- textValue = textValue.replace(entity.regex, entity.val);
4037
+ return tags;
4038
+ };
4039
+ const decodeEntities = (value) => value.replace(/&(#x[0-9a-fA-F]+|#[0-9]+|amp|lt|gt|quot|apos);/g, (_, entity) => {
4040
+ switch (entity) {
4041
+ case "amp": return "&";
4042
+ case "lt": return "<";
4043
+ case "gt": return ">";
4044
+ case "quot": return "\"";
4045
+ case "apos": return "'";
4046
+ default: return String.fromCodePoint(entity.startsWith("#x") ? Number.parseInt(entity.slice(2), 16) : Number.parseInt(entity.slice(1), 10));
4221
4047
  }
4222
- return textValue;
4223
- }
4224
- //#endregion
4225
- //#region ../../node_modules/.pnpm/fast-xml-builder@1.2.0/node_modules/fast-xml-builder/src/ignoreAttributes.js
4226
- function getIgnoreAttributesFn(ignoreAttributes) {
4227
- if (typeof ignoreAttributes === "function") return ignoreAttributes;
4228
- if (Array.isArray(ignoreAttributes)) return (attrName) => {
4229
- for (const pattern of ignoreAttributes) {
4230
- if (typeof pattern === "string" && attrName === pattern) return true;
4231
- if (pattern instanceof RegExp && pattern.test(attrName)) return true;
4048
+ });
4049
+ const escapeAttribute = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
4050
+ const attributeValue = (tag, name) => {
4051
+ const attribute = tag.attributes.find((item) => item.name === name);
4052
+ return attribute === void 0 ? void 0 : decodeEntities(attribute.rawValue);
4053
+ };
4054
+ /** The first `<application>` directly under `<manifest>`, with its children. */
4055
+ const findApplication = (xml) => {
4056
+ const tags = scanTags(xml);
4057
+ const stack = [];
4058
+ for (let index = 0; index < tags.length; index += 1) {
4059
+ const tag = tags[index];
4060
+ if (tag.closing) {
4061
+ stack.pop();
4062
+ continue;
4232
4063
  }
4233
- };
4234
- return () => false;
4235
- }
4236
- //#endregion
4237
- //#region ../../node_modules/.pnpm/fast-xml-builder@1.2.0/node_modules/fast-xml-builder/src/fxb.js
4238
- const defaultOptions = {
4239
- attributeNamePrefix: "@_",
4240
- attributesGroupName: false,
4241
- textNodeName: "#text",
4242
- ignoreAttributes: true,
4243
- cdataPropName: false,
4244
- format: false,
4245
- indentBy: " ",
4246
- suppressEmptyNode: false,
4247
- suppressUnpairedNode: true,
4248
- suppressBooleanAttributes: true,
4249
- tagValueProcessor: function(key, a) {
4250
- return a;
4251
- },
4252
- attributeValueProcessor: function(attrName, a) {
4253
- return a;
4254
- },
4255
- preserveOrder: false,
4256
- commentPropName: false,
4257
- unpairedTags: [],
4258
- entities: [
4259
- {
4260
- regex: /* @__PURE__ */ new RegExp("&", "g"),
4261
- val: "&amp;"
4262
- },
4263
- {
4264
- regex: /* @__PURE__ */ new RegExp(">", "g"),
4265
- val: "&gt;"
4266
- },
4267
- {
4268
- regex: /* @__PURE__ */ new RegExp("<", "g"),
4269
- val: "&lt;"
4270
- },
4271
- {
4272
- regex: /* @__PURE__ */ new RegExp("'", "g"),
4273
- val: "&apos;"
4274
- },
4275
- {
4276
- regex: /* @__PURE__ */ new RegExp("\"", "g"),
4277
- val: "&quot;"
4064
+ if (!(tag.name === "application" && stack.length === 1 && stack[0] === "manifest")) {
4065
+ if (!tag.selfClosing) stack.push(tag.name);
4066
+ continue;
4278
4067
  }
4279
- ],
4280
- processEntities: true,
4281
- stopNodes: [],
4282
- oneListGroup: false,
4283
- maxNestedTags: 100,
4284
- jPath: true,
4285
- sanitizeName: false
4286
- };
4287
- function Builder(options) {
4288
- this.options = Object.assign({}, defaultOptions, options);
4289
- if (this.options.stopNodes && Array.isArray(this.options.stopNodes)) this.options.stopNodes = this.options.stopNodes.map((node) => {
4290
- if (typeof node === "string" && node.startsWith("*.")) return ".." + node.substring(2);
4291
- return node;
4292
- });
4293
- this.stopNodeExpressions = [];
4294
- if (this.options.stopNodes && Array.isArray(this.options.stopNodes)) for (let i = 0; i < this.options.stopNodes.length; i++) {
4295
- const node = this.options.stopNodes[i];
4296
- if (typeof node === "string") this.stopNodeExpressions.push(new Expression(node));
4297
- else if (node instanceof Expression) this.stopNodeExpressions.push(node);
4298
- }
4299
- if (this.options.ignoreAttributes === true || this.options.attributesGroupName) this.isAttribute = function() {
4300
- return false;
4301
- };
4302
- else {
4303
- this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes);
4304
- this.attrPrefixLen = this.options.attributeNamePrefix.length;
4305
- this.isAttribute = isAttribute;
4306
- }
4307
- this.processTextOrObjNode = processTextOrObjNode;
4308
- if (this.options.format) {
4309
- this.indentate = indentate;
4310
- this.tagEndChar = ">\n";
4311
- this.newLine = "\n";
4312
- } else {
4313
- this.indentate = function() {
4314
- return "";
4068
+ if (tag.selfClosing) return {
4069
+ openTag: tag,
4070
+ closeTag: null,
4071
+ children: []
4315
4072
  };
4316
- this.tagEndChar = ">";
4317
- this.newLine = "";
4318
- }
4319
- }
4320
- /**
4321
- * Detect XML version from the ?xml declaration at the root of a plain-object input.
4322
- * Checks both attributesGroupName and flat attribute forms.
4323
- * Returns '1.0' if no declaration is found.
4324
- */
4325
- function detectXmlVersionFromObj(jObj, options) {
4326
- const decl = jObj["?xml"];
4327
- if (decl && typeof decl === "object") {
4328
- if (options.attributesGroupName && decl[options.attributesGroupName]) {
4329
- const v = decl[options.attributesGroupName][options.attributeNamePrefix + "version"];
4330
- if (v) return v;
4073
+ const children = [];
4074
+ let depth = 0;
4075
+ let open = null;
4076
+ for (let inner = index + 1; inner < tags.length; inner += 1) {
4077
+ const child = tags[inner];
4078
+ if (child.closing) {
4079
+ if (depth === 0) return {
4080
+ openTag: tag,
4081
+ closeTag: child,
4082
+ children
4083
+ };
4084
+ depth -= 1;
4085
+ if (depth === 0 && open) {
4086
+ children.push({
4087
+ openTag: open,
4088
+ start: open.start,
4089
+ end: child.end
4090
+ });
4091
+ open = null;
4092
+ }
4093
+ continue;
4094
+ }
4095
+ if (child.selfClosing) {
4096
+ if (depth === 0) children.push({
4097
+ openTag: child,
4098
+ start: child.start,
4099
+ end: child.end
4100
+ });
4101
+ continue;
4102
+ }
4103
+ if (depth === 0) open = child;
4104
+ depth += 1;
4331
4105
  }
4332
- const v = decl[options.attributeNamePrefix + "version"];
4333
- if (v) return v;
4334
- }
4335
- return "1.0";
4336
- }
4337
- /**
4338
- * Resolve a tag or attribute name through sanitizeName if configured.
4339
- * Validation via xml-naming's qName is performed first; the sanitizeName
4340
- * callback is invoked only when the name is invalid. If sanitizeName is
4341
- * false (default), no validation occurs and the name is used as-is.
4342
- *
4343
- * @param {string} name - raw name from the JS object
4344
- * @param {boolean} isAttribute - true when resolving an attribute name
4345
- * @param {object} options
4346
- * @param {Matcher} matcher - current matcher state (readonly from callback perspective)
4347
- * @param {string} xmlVersion - '1.0' or '1.1', forwarded to xml-naming
4348
- */
4349
- function resolveTagName(name, isAttribute, options, matcher, xmlVersion) {
4350
- if (!options.sanitizeName) return name;
4351
- if (qName(name, { xmlVersion })) return name;
4352
- return options.sanitizeName(name, {
4353
- isAttribute,
4354
- matcher: matcher.readOnly()
4355
- });
4356
- }
4357
- Builder.prototype.build = function(jObj) {
4358
- if (this.options.preserveOrder) return toXml(jObj, this.options);
4359
- else {
4360
- if (Array.isArray(jObj) && this.options.arrayNodeName && this.options.arrayNodeName.length > 1) jObj = { [this.options.arrayNodeName]: jObj };
4361
- const matcher = new Matcher();
4362
- const xmlVersion = detectXmlVersionFromObj(jObj, this.options);
4363
- return this.j2x(jObj, 0, matcher, xmlVersion).val;
4364
- }
4365
- };
4366
- Builder.prototype.j2x = function(jObj, level, matcher, xmlVersion) {
4367
- let attrStr = "";
4368
- let val = "";
4369
- if (this.options.maxNestedTags && matcher.getDepth() >= this.options.maxNestedTags) throw new Error("Maximum nested tags exceeded");
4370
- const jPath = this.options.jPath ? matcher.toString() : matcher;
4371
- const isCurrentStopNode = this.checkStopNode(matcher);
4372
- for (let key in jObj) {
4373
- if (!Object.prototype.hasOwnProperty.call(jObj, key)) continue;
4374
- const resolvedKey = key === this.options.textNodeName || key === this.options.cdataPropName || key === this.options.commentPropName || this.options.attributesGroupName && key === this.options.attributesGroupName || this.isAttribute(key) || key[0] === "?" ? key : resolveTagName(key, false, this.options, matcher, xmlVersion);
4375
- if (typeof jObj[key] === "undefined") {
4376
- if (this.isAttribute(key)) val += "";
4377
- } else if (jObj[key] === null) {
4378
- if (this.isAttribute(key)) val += "";
4379
- else if (resolvedKey === this.options.cdataPropName || resolvedKey === this.options.commentPropName) val += "";
4380
- else if (resolvedKey[0] === "?") val += this.indentate(level) + "<" + resolvedKey + "?" + this.tagEndChar;
4381
- else val += this.indentate(level) + "<" + resolvedKey + "/" + this.tagEndChar;
4382
- } else if (jObj[key] instanceof Date) val += this.buildTextValNode(jObj[key], resolvedKey, "", level, matcher);
4383
- else if (typeof jObj[key] !== "object") {
4384
- const attr = this.isAttribute(key);
4385
- if (attr && !this.ignoreAttributesFn(attr, jPath)) {
4386
- const resolvedAttr = resolveTagName(attr, true, this.options, matcher, xmlVersion);
4387
- attrStr += this.buildAttrPairStr(resolvedAttr, "" + jObj[key], isCurrentStopNode);
4388
- } else if (!attr) {
4389
- if (key === this.options.textNodeName) {
4390
- let newval = this.options.tagValueProcessor(key, "" + jObj[key]);
4391
- val += this.replaceEntitiesValue(newval);
4392
- } else {
4393
- matcher.push(resolvedKey);
4394
- const isStopNode = this.checkStopNode(matcher);
4395
- matcher.pop();
4396
- if (isStopNode) {
4397
- const textValue = "" + jObj[key];
4398
- if (textValue === "") val += this.indentate(level) + "<" + resolvedKey + this.closeTag(resolvedKey) + this.tagEndChar;
4399
- else val += this.indentate(level) + "<" + resolvedKey + ">" + textValue + "</" + resolvedKey + this.tagEndChar;
4400
- } else val += this.buildTextValNode(jObj[key], resolvedKey, "", level, matcher);
4401
- }
4402
- }
4403
- } else if (Array.isArray(jObj[key])) {
4404
- const arrLen = jObj[key].length;
4405
- let listTagVal = "";
4406
- let listTagAttr = "";
4407
- for (let j = 0; j < arrLen; j++) {
4408
- const item = jObj[key][j];
4409
- if (typeof item === "undefined") {} else if (item === null) {
4410
- if (resolvedKey[0] === "?") val += this.indentate(level) + "<" + resolvedKey + "?" + this.tagEndChar;
4411
- else val += this.indentate(level) + "<" + resolvedKey + "/" + this.tagEndChar;
4412
- } else if (typeof item === "object") {
4413
- if (this.options.oneListGroup) {
4414
- matcher.push(resolvedKey);
4415
- const result = this.j2x(item, level + 1, matcher, xmlVersion);
4416
- matcher.pop();
4417
- listTagVal += result.val;
4418
- if (this.options.attributesGroupName && item.hasOwnProperty(this.options.attributesGroupName)) listTagAttr += result.attrStr;
4419
- } else listTagVal += this.processTextOrObjNode(item, resolvedKey, level, matcher, xmlVersion);
4420
- } else if (this.options.oneListGroup) {
4421
- let textValue = this.options.tagValueProcessor(resolvedKey, item);
4422
- textValue = this.replaceEntitiesValue(textValue);
4423
- listTagVal += textValue;
4424
- } else {
4425
- matcher.push(resolvedKey);
4426
- const isStopNode = this.checkStopNode(matcher);
4427
- matcher.pop();
4428
- if (isStopNode) {
4429
- const textValue = "" + item;
4430
- if (textValue === "") listTagVal += this.indentate(level) + "<" + resolvedKey + this.closeTag(resolvedKey) + this.tagEndChar;
4431
- else listTagVal += this.indentate(level) + "<" + resolvedKey + ">" + textValue + "</" + resolvedKey + this.tagEndChar;
4432
- } else listTagVal += this.buildTextValNode(item, resolvedKey, "", level, matcher);
4433
- }
4434
- }
4435
- if (this.options.oneListGroup) listTagVal = this.buildObjectNode(listTagVal, resolvedKey, listTagAttr, level);
4436
- val += listTagVal;
4437
- } else if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
4438
- const Ks = Object.keys(jObj[key]);
4439
- const L = Ks.length;
4440
- for (let j = 0; j < L; j++) {
4441
- const resolvedAttr = resolveTagName(Ks[j], true, this.options, matcher, xmlVersion);
4442
- attrStr += this.buildAttrPairStr(resolvedAttr, "" + jObj[key][Ks[j]], isCurrentStopNode);
4443
- }
4444
- } else val += this.processTextOrObjNode(jObj[key], resolvedKey, level, matcher, xmlVersion);
4106
+ throw new Error("<application> is not closed");
4445
4107
  }
4446
- return {
4447
- attrStr,
4448
- val
4449
- };
4108
+ return null;
4450
4109
  };
4451
- Builder.prototype.buildAttrPairStr = function(attrName, val, isStopNode) {
4452
- if (!isStopNode) {
4453
- val = this.options.attributeValueProcessor(attrName, "" + val);
4454
- val = this.replaceEntitiesValue(val);
4455
- }
4456
- if (this.options.suppressBooleanAttributes && val === "true") return " " + attrName;
4457
- else return " " + attrName + "=\"" + escapeAttribute(val) + "\"";
4110
+ const metaDataElements = (application, key) => application.children.filter((child) => child.openTag.name === "meta-data" && attributeValue(child.openTag, "android:name") === key);
4111
+ /** Leading whitespace of the line that contains `offset`. */
4112
+ const lineIndent = (xml, offset) => {
4113
+ const lineStart = xml.lastIndexOf("\n", offset - 1) + 1;
4114
+ return /^[ \t]*/.exec(xml.slice(lineStart, offset))?.[0] ?? "";
4458
4115
  };
4459
- function processTextOrObjNode(object, key, level, matcher, xmlVersion) {
4460
- const attrValues = this.extractAttributes(object);
4461
- matcher.push(key, attrValues);
4462
- if (this.checkStopNode(matcher)) {
4463
- const rawContent = this.buildRawContent(object);
4464
- const attrStr = this.buildAttributesForStopNode(object);
4465
- matcher.pop();
4466
- return this.buildObjectNode(rawContent, key, attrStr, level);
4467
- }
4468
- const result = this.j2x(object, level + 1, matcher, xmlVersion);
4469
- matcher.pop();
4470
- if (key[0] === "?") return this.buildTextValNode("", key, result.attrStr, level, matcher);
4471
- else if (object[this.options.textNodeName] !== void 0 && Object.keys(object).length === 1) return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level, matcher);
4472
- else return this.buildObjectNode(result.val, key, result.attrStr, level);
4473
- }
4474
- Builder.prototype.extractAttributes = function(obj) {
4475
- if (!obj || typeof obj !== "object") return null;
4476
- const attrValues = {};
4477
- let hasAttrs = false;
4478
- if (this.options.attributesGroupName && obj[this.options.attributesGroupName]) {
4479
- const attrGroup = obj[this.options.attributesGroupName];
4480
- for (let attrKey in attrGroup) {
4481
- if (!Object.prototype.hasOwnProperty.call(attrGroup, attrKey)) continue;
4482
- const cleanKey = attrKey.startsWith(this.options.attributeNamePrefix) ? attrKey.substring(this.options.attributeNamePrefix.length) : attrKey;
4483
- attrValues[cleanKey] = escapeAttribute(attrGroup[attrKey]);
4484
- hasAttrs = true;
4485
- }
4486
- } else for (let key in obj) {
4487
- if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
4488
- const attr = this.isAttribute(key);
4489
- if (attr) {
4490
- attrValues[attr] = escapeAttribute(obj[key]);
4491
- hasAttrs = true;
4492
- }
4493
- }
4494
- return hasAttrs ? attrValues : null;
4116
+ /** Whether only whitespace sits between the start of the line and `offset`. */
4117
+ const startsLine = (xml, offset) => {
4118
+ const lineStart = xml.lastIndexOf("\n", offset - 1) + 1;
4119
+ return /^[ \t]*$/.test(xml.slice(lineStart, offset));
4495
4120
  };
4496
- Builder.prototype.buildRawContent = function(obj) {
4497
- if (typeof obj === "string") return obj;
4498
- if (typeof obj !== "object" || obj === null) return String(obj);
4499
- if (obj[this.options.textNodeName] !== void 0) return obj[this.options.textNodeName];
4500
- let content = "";
4501
- for (let key in obj) {
4502
- if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
4503
- if (this.isAttribute(key)) continue;
4504
- if (this.options.attributesGroupName && key === this.options.attributesGroupName) continue;
4505
- const value = obj[key];
4506
- if (key === this.options.textNodeName) content += value;
4507
- else if (Array.isArray(value)) {
4508
- for (let item of value) if (typeof item === "string" || typeof item === "number") content += `<${key}>${item}</${key}>`;
4509
- else if (typeof item === "object" && item !== null) {
4510
- const nestedContent = this.buildRawContent(item);
4511
- const nestedAttrs = this.buildAttributesForStopNode(item);
4512
- if (nestedContent === "") content += `<${key}${nestedAttrs}/>`;
4513
- else content += `<${key}${nestedAttrs}>${nestedContent}</${key}>`;
4514
- }
4515
- } else if (typeof value === "object" && value !== null) {
4516
- const nestedContent = this.buildRawContent(value);
4517
- const nestedAttrs = this.buildAttributesForStopNode(value);
4518
- if (nestedContent === "") content += `<${key}${nestedAttrs}/>`;
4519
- else content += `<${key}${nestedAttrs}>${nestedContent}</${key}>`;
4520
- } else content += `<${key}>${value}</${key}>`;
4521
- }
4522
- return content;
4121
+ const lineEnding = (xml) => xml.includes("\r\n") ? "\r\n" : "\n";
4122
+ /** `" />"` or `"/>"`, whichever the file already uses more. */
4123
+ const selfClosingStyle = (xml) => {
4124
+ return (xml.match(/\s\/>/g)?.length ?? 0) >= (xml.match(/[^\s]\/>/g)?.length ?? 0) ? " />" : "/>";
4523
4125
  };
4524
- Builder.prototype.buildAttributesForStopNode = function(obj) {
4525
- if (!obj || typeof obj !== "object") return "";
4526
- let attrStr = "";
4527
- if (this.options.attributesGroupName && obj[this.options.attributesGroupName]) {
4528
- const attrGroup = obj[this.options.attributesGroupName];
4529
- for (let attrKey in attrGroup) {
4530
- if (!Object.prototype.hasOwnProperty.call(attrGroup, attrKey)) continue;
4531
- const cleanKey = attrKey.startsWith(this.options.attributeNamePrefix) ? attrKey.substring(this.options.attributeNamePrefix.length) : attrKey;
4532
- const val = attrGroup[attrKey];
4533
- if (val === true && this.options.suppressBooleanAttributes) attrStr += " " + cleanKey;
4534
- else attrStr += " " + cleanKey + "=\"" + val + "\"";
4535
- }
4536
- } else for (let key in obj) {
4537
- if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
4538
- const attr = this.isAttribute(key);
4539
- if (attr) {
4540
- const val = obj[key];
4541
- if (val === true && this.options.suppressBooleanAttributes) attrStr += " " + attr;
4542
- else attrStr += " " + attr + "=\"" + val + "\"";
4543
- }
4544
- }
4545
- return attrStr;
4126
+ const indentUnit = (xml, application) => {
4127
+ const applicationIndent = lineIndent(xml, application.openTag.start);
4128
+ if (applicationIndent.includes(" ")) return " ";
4129
+ const manifest = /<manifest\b/.exec(xml);
4130
+ const manifestIndent = manifest ? lineIndent(xml, manifest.index) : "";
4131
+ const unit = applicationIndent.slice(manifestIndent.length);
4132
+ return unit.length > 0 ? unit : " ";
4546
4133
  };
4547
- Builder.prototype.buildObjectNode = function(val, key, attrStr, level) {
4548
- if (val === "") {
4549
- if (key[0] === "?") return this.indentate(level) + "<" + key + attrStr + "?" + this.tagEndChar;
4550
- else return this.indentate(level) + "<" + key + attrStr + this.closeTag(key) + this.tagEndChar;
4551
- } else if (key[0] === "?") return this.indentate(level) + "<" + key + attrStr + "?" + this.tagEndChar;
4552
- else {
4553
- let tagEndExp = "</" + key + this.tagEndChar;
4554
- let piClosingChar = "";
4555
- if (key[0] === "?") {
4556
- piClosingChar = "?";
4557
- tagEndExp = "";
4134
+ const applyEdits = (xml, edits) => [...edits].sort((a, b) => b.start - a.start).reduce((result, edit) => result.slice(0, edit.start) + edit.text + result.slice(edit.end), xml);
4135
+ /**
4136
+ * Sets `<meta-data android:name={key} android:value={value}>` under
4137
+ * `<application>`, changing nothing else in the file. An existing element
4138
+ * gets its `android:value` replaced in place; otherwise a new element is
4139
+ * added as the last child, indented like its siblings.
4140
+ *
4141
+ * @returns the edited manifest, or null when it has no `<application>`.
4142
+ */
4143
+ const setManifestMetaData = (xml, key, value) => {
4144
+ const application = findApplication(xml);
4145
+ if (!application) return null;
4146
+ const valueAttribute = `android:value="${escapeAttribute(value)}"`;
4147
+ const existing = metaDataElements(application, key);
4148
+ if (existing.length > 0) return applyEdits(xml, existing.flatMap(({ openTag }) => {
4149
+ const attribute = (name) => openTag.attributes.find((item) => item.name === name);
4150
+ const current = attribute("android:value");
4151
+ const resource = attribute("android:resource");
4152
+ if (!current && resource) return [{
4153
+ start: resource.start,
4154
+ end: resource.end,
4155
+ text: valueAttribute
4156
+ }];
4157
+ const edits = current ? [{
4158
+ start: current.start,
4159
+ end: current.end,
4160
+ text: valueAttribute
4161
+ }] : [{
4162
+ start: attribute("android:name").end,
4163
+ end: attribute("android:name").end,
4164
+ text: ` ${valueAttribute}`
4165
+ }];
4166
+ if (resource) {
4167
+ let start = resource.start;
4168
+ while (start > openTag.start && /\s/.test(xml[start - 1])) start -= 1;
4169
+ edits.push({
4170
+ start,
4171
+ end: resource.end,
4172
+ text: ""
4173
+ });
4558
4174
  }
4559
- if ((attrStr || attrStr === "") && val.indexOf("<") === -1) return this.indentate(level) + "<" + key + attrStr + piClosingChar + ">" + val + tagEndExp;
4560
- else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) return this.indentate(level) + `<!--${val}-->` + this.newLine;
4561
- else return this.indentate(level) + "<" + key + attrStr + piClosingChar + this.tagEndChar + val + this.indentate(level) + tagEndExp;
4562
- }
4175
+ return edits;
4176
+ }));
4177
+ const eol = lineEnding(xml);
4178
+ const element = `<meta-data android:name="${escapeAttribute(key)}" ${valueAttribute}${selfClosingStyle(xml)}`;
4179
+ const applicationIndent = lineIndent(xml, application.openTag.start);
4180
+ const lastChild = application.children.at(-1);
4181
+ const childIndent = lastChild && startsLine(xml, lastChild.start) ? lineIndent(xml, lastChild.start) : applicationIndent + indentUnit(xml, application);
4182
+ if (application.closeTag === null) {
4183
+ const { openTag } = application;
4184
+ const head = xml.slice(openTag.start, openTag.end).replace(/\s*\/>$/, ">");
4185
+ return applyEdits(xml, [{
4186
+ start: openTag.start,
4187
+ end: openTag.end,
4188
+ text: `${head}${eol}${childIndent}${element}${eol}${applicationIndent}</application>`
4189
+ }]);
4190
+ }
4191
+ const closeStart = application.closeTag.start;
4192
+ if (startsLine(xml, closeStart)) {
4193
+ const lineStart = xml.lastIndexOf("\n", closeStart - 1) + 1;
4194
+ return applyEdits(xml, [{
4195
+ start: lineStart,
4196
+ end: lineStart,
4197
+ text: `${childIndent}${element}${eol}`
4198
+ }]);
4199
+ }
4200
+ return applyEdits(xml, [{
4201
+ start: closeStart,
4202
+ end: closeStart,
4203
+ text: element
4204
+ }]);
4563
4205
  };
4564
- Builder.prototype.closeTag = function(key) {
4565
- let closeTag = "";
4566
- if (this.options.unpairedTags.indexOf(key) !== -1) {
4567
- if (!this.options.suppressUnpairedNode) closeTag = "/";
4568
- } else if (this.options.suppressEmptyNode) closeTag = "/";
4569
- else closeTag = `></${key}`;
4570
- return closeTag;
4571
- };
4572
- Builder.prototype.checkStopNode = function(matcher) {
4573
- if (!this.stopNodeExpressions || this.stopNodeExpressions.length === 0) return false;
4574
- for (let i = 0; i < this.stopNodeExpressions.length; i++) if (matcher.matches(this.stopNodeExpressions[i])) return true;
4575
- return false;
4576
- };
4577
- Builder.prototype.buildTextValNode = function(val, key, attrStr, level, matcher) {
4578
- if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
4579
- const safeVal = safeCdata(val);
4580
- return this.indentate(level) + `<![CDATA[${safeVal}]]>` + this.newLine;
4581
- } else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
4582
- const safeVal = safeComment(val);
4583
- return this.indentate(level) + `<!--${safeVal}-->` + this.newLine;
4584
- } else if (key[0] === "?") return this.indentate(level) + "<" + key + attrStr + "?" + this.tagEndChar;
4585
- else {
4586
- let textValue = this.options.tagValueProcessor(key, val);
4587
- textValue = this.replaceEntitiesValue(textValue);
4588
- if (textValue === "") return this.indentate(level) + "<" + key + attrStr + this.closeTag(key) + this.tagEndChar;
4589
- else return this.indentate(level) + "<" + key + attrStr + ">" + textValue + "</" + key + this.tagEndChar;
4590
- }
4591
- };
4592
- Builder.prototype.replaceEntitiesValue = function(textValue) {
4593
- if (textValue && textValue.length > 0 && this.options.processEntities) for (let i = 0; i < this.options.entities.length; i++) {
4594
- const entity = this.options.entities[i];
4595
- textValue = textValue.replace(entity.regex, entity.val);
4596
- }
4597
- return textValue;
4206
+ /**
4207
+ * Removes every `<meta-data android:name={key}>` under `<application>`. An
4208
+ * element alone on its line takes the line with it.
4209
+ *
4210
+ * @returns the edited manifest, or null when there was nothing to remove.
4211
+ */
4212
+ const removeManifestMetaData = (xml, key) => {
4213
+ const application = findApplication(xml);
4214
+ if (!application) return null;
4215
+ const existing = metaDataElements(application, key);
4216
+ if (existing.length === 0) return null;
4217
+ return applyEdits(xml, existing.map(({ start, end }) => {
4218
+ const lineStart = xml.lastIndexOf("\n", start - 1) + 1;
4219
+ const newline = xml.indexOf("\n", end);
4220
+ const lineEnd = newline === -1 ? xml.length : newline + 1;
4221
+ return /^[ \t]*$/.test(xml.slice(lineStart, start)) && /^[ \t]*\r?\n?$/.test(xml.slice(end, lineEnd)) ? {
4222
+ start: lineStart,
4223
+ end: lineEnd,
4224
+ text: ""
4225
+ } : {
4226
+ start,
4227
+ end,
4228
+ text: ""
4229
+ };
4230
+ }));
4598
4231
  };
4599
- function indentate(level) {
4600
- return this.options.indentBy.repeat(level);
4601
- }
4602
- function isAttribute(name) {
4603
- if (name.startsWith(this.options.attributeNamePrefix) && name !== this.options.textNodeName) return name.substr(this.attrPrefixLen);
4604
- else return false;
4605
- }
4606
- //#endregion
4607
- //#region ../../node_modules/.pnpm/fast-xml-parser@5.7.2/node_modules/fast-xml-parser/src/xmlbuilder/json2xml.js
4608
- var json2xml_default = Builder;
4609
4232
  //#endregion
4610
4233
  //#region src/utils/configParser/androidParser.ts
4611
4234
  /** The AndroidManifest meta-data names the native SDK reads. */
@@ -4617,7 +4240,6 @@ const MANIFEST_KEYS = /* @__PURE__ */ new Set([
4617
4240
  var AndroidConfigParser = class {
4618
4241
  androidManifestPaths;
4619
4242
  parser;
4620
- builder;
4621
4243
  constructor(androidManifestPaths) {
4622
4244
  this.androidManifestPaths = (androidManifestPaths || []).map((p) => path.isAbsolute(p) ? p : path.join(getCwd(), p));
4623
4245
  const options = {
@@ -4629,13 +4251,6 @@ var AndroidConfigParser = class {
4629
4251
  suppressEmptyNode: true
4630
4252
  };
4631
4253
  this.parser = new XMLParser(options);
4632
- this.builder = new json2xml_default({
4633
- ...options,
4634
- format: true,
4635
- indentBy: " ",
4636
- suppressBooleanAttributes: false,
4637
- processEntities: true
4638
- });
4639
4254
  }
4640
4255
  async exists() {
4641
4256
  return this.getExistingManifestPaths().length > 0;
@@ -4688,14 +4303,8 @@ var AndroidConfigParser = class {
4688
4303
  const updatedPaths = [];
4689
4304
  for (const androidManifestPath of existingManifestPaths) try {
4690
4305
  const content = await fs.promises.readFile(androidManifestPath, "utf-8");
4691
- const result = this.parser.parse(content);
4692
- const application = this.getApplication(result);
4693
- if (!application?.["meta-data"]) continue;
4694
- const metaData = Array.isArray(application["meta-data"]) ? application["meta-data"] : [application["meta-data"]];
4695
- const filtered = metaData.filter((item) => item["@_android:name"] !== manifestKey);
4696
- if (filtered.length === metaData.length) continue;
4697
- application["meta-data"] = filtered.length === 0 ? void 0 : filtered.length === 1 ? filtered[0] : filtered;
4698
- const newContent = this.builder.build(result);
4306
+ const newContent = removeManifestMetaData(content, manifestKey);
4307
+ if (newContent === null) continue;
4699
4308
  await fs.promises.writeFile(androidManifestPath, newContent, "utf-8");
4700
4309
  updatedPaths.push(path.relative(getCwd(), androidManifestPath));
4701
4310
  } catch (error) {
@@ -4717,21 +4326,9 @@ var AndroidConfigParser = class {
4717
4326
  const updatedPaths = [];
4718
4327
  for (const androidManifestPath of existingPaths) try {
4719
4328
  const content = await fs.promises.readFile(androidManifestPath, "utf-8");
4720
- const result = this.parser.parse(content);
4721
- const application = this.getApplication(result);
4722
- if (!application) continue;
4723
- if (!application["meta-data"]) application["meta-data"] = [];
4724
- const metaData = Array.isArray(application["meta-data"]) ? application["meta-data"] : [application["meta-data"]];
4725
- const existingIndex = metaData.findIndex((item) => item["@_android:name"] === key);
4726
- const metaDataElement = {
4727
- "@_android:name": key,
4728
- "@_android:value": value
4729
- };
4730
- if (existingIndex !== -1) metaData[existingIndex] = metaDataElement;
4731
- else metaData.push(metaDataElement);
4732
- application["meta-data"] = metaData.length === 1 ? metaData[0] : metaData;
4733
- const newContent = this.builder.build(result);
4734
- await fs.promises.writeFile(androidManifestPath, newContent, "utf-8");
4329
+ const newContent = setManifestMetaData(content, key, value);
4330
+ if (newContent === null) continue;
4331
+ if (newContent !== content) await fs.promises.writeFile(androidManifestPath, newContent, "utf-8");
4735
4332
  updatedPaths.push(path.relative(getCwd(), androidManifestPath));
4736
4333
  } catch (error) {
4737
4334
  throw new Error(`Failed to parse or update AndroidManifest.xml: ${error}`);
@@ -15021,7 +14618,7 @@ var require_build = /* @__PURE__ */ __commonJSMin(((exports) => {
15021
14618
  }
15022
14619
  }));
15023
14620
  //#endregion
15024
- //#region src/utils/configParser/bplistParser.ts
14621
+ //#region src/utils/configParser/plistFormat.ts
15025
14622
  var import_plist = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
15026
14623
  /**
15027
14624
  * Parser functions.
@@ -15038,6 +14635,61 @@ var import_plist = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((expo
15038
14635
  exports[k] = builderFunctions[k];
15039
14636
  });
15040
14637
  })))(), 1);
14638
+ /** Elements whose text can span several lines in a pretty-printed plist. */
14639
+ const TEXT_ELEMENT = /<(key|string|data|date|integer|real)>/;
14640
+ const closingTag = (name) => `</${name}>`;
14641
+ /**
14642
+ * Lays out `plist.build` output the way Xcode writes an Info.plist, so that
14643
+ * writing one key does not shift every other line of the file.
14644
+ *
14645
+ * `plist.build` indents everything under `<plist>` one level, while Xcode
14646
+ * puts the root `<dict>` at column 0. When the previous file had its root at
14647
+ * column 0 (or there is no previous file), every element line under `<plist>`
14648
+ * loses one leading tab. A file whose root is already indented under
14649
+ * `<plist>` (the layout `plist.build` itself writes) is left indented. Text of a multi-line value is left as it is: a text line
14650
+ * never starts with a raw `<`, and lines between an opening and a closing text
14651
+ * tag are skipped. The file keeps the trailing newline it had.
14652
+ */
14653
+ function formatPlistLikeXcode(built, previous) {
14654
+ const lines = built.split("\n");
14655
+ const formatted = [];
14656
+ let insidePlist = false;
14657
+ let openText = null;
14658
+ const dedent = previous === void 0 || rootIsAtColumnZero(previous);
14659
+ for (const line of lines) {
14660
+ if (openText !== null) {
14661
+ formatted.push(line);
14662
+ if (line.includes(closingTag(openText))) openText = null;
14663
+ continue;
14664
+ }
14665
+ if (line.startsWith("</plist>")) insidePlist = false;
14666
+ formatted.push(dedent && insidePlist && /^\t+</.test(line) ? line.slice(1) : line);
14667
+ if (/^<plist\b/.test(line)) insidePlist = true;
14668
+ const opened = TEXT_ELEMENT.exec(line);
14669
+ if (opened) {
14670
+ if (!line.slice(opened.index + opened[0].length).includes(closingTag(opened[1]))) openText = opened[1];
14671
+ }
14672
+ }
14673
+ let result = formatted.join("\n");
14674
+ if (previous?.endsWith("\n") && !result.endsWith("\n")) result += "\n";
14675
+ return result;
14676
+ }
14677
+ /**
14678
+ * Whether the root element under `<plist>` in `xml` starts at column 0.
14679
+ * Blank lines and XML comments before the root are skipped.
14680
+ */
14681
+ function rootIsAtColumnZero(xml) {
14682
+ const open = /<plist\b[^>]*>/.exec(xml);
14683
+ if (!open) return true;
14684
+ const rest = xml.slice(open.index + open[0].length).replace(/<!--[\s\S]*?-->/g, (comment) => comment.replace(/[^\n]/g, " "));
14685
+ for (const line of rest.split("\n").slice(1)) {
14686
+ const indent = /^[^\S\n]*(?=<)/.exec(line);
14687
+ if (indent) return indent[0] === "";
14688
+ }
14689
+ return true;
14690
+ }
14691
+ //#endregion
14692
+ //#region src/utils/configParser/bplistParser.ts
15041
14693
  const MAX_OBJECT_SIZE = 1e8;
15042
14694
  const MAX_OBJECT_COUNT = 32768;
15043
14695
  const EPOCH = 9783072e5;
@@ -15329,13 +14981,14 @@ var IosConfigParser = class {
15329
14981
  for (const plistFile of plistPaths) {
15330
14982
  const relativePath = path.relative(getCwd(), plistFile);
15331
14983
  try {
15332
- const plistObject = parsePlist(await fs.promises.readFile(plistFile));
14984
+ const plistBuffer = await fs.promises.readFile(plistFile);
14985
+ const plistObject = parsePlist(plistBuffer);
15333
14986
  if (!(key in plistObject)) continue;
15334
14987
  delete plistObject[key];
15335
- const newPlistXml = import_plist.default.build(plistObject, {
14988
+ const newPlistXml = formatPlistLikeXcode(import_plist.default.build(plistObject, {
15336
14989
  indent: " ",
15337
14990
  pretty: true
15338
- });
14991
+ }), plistBuffer.toString("utf-8"));
15339
14992
  await fs.promises.writeFile(plistFile, newPlistXml);
15340
14993
  updatedPaths.push(relativePath);
15341
14994
  } catch (error) {
@@ -15354,13 +15007,14 @@ var IosConfigParser = class {
15354
15007
  for (const plistFile of plistPaths) {
15355
15008
  const relativePath = path.relative(getCwd(), plistFile);
15356
15009
  try {
15357
- const plistObject = parsePlist(await fs.promises.readFile(plistFile));
15010
+ const plistBuffer = await fs.promises.readFile(plistFile);
15011
+ const plistObject = parsePlist(plistBuffer);
15358
15012
  if (plistObject[key] === value) continue;
15359
15013
  plistObject[key] = value;
15360
- const newPlistXml = import_plist.default.build(plistObject, {
15014
+ const newPlistXml = formatPlistLikeXcode(import_plist.default.build(plistObject, {
15361
15015
  indent: " ",
15362
15016
  pretty: true
15363
- });
15017
+ }), plistBuffer.toString("utf-8"));
15364
15018
  await fs.promises.writeFile(plistFile, newPlistXml);
15365
15019
  updatedPaths.push(relativePath);
15366
15020
  } catch (error) {