jstoxml.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. (function (global, factory) {
  2. if (typeof define === "function" && define.amd) {
  3. define(["exports"], factory);
  4. } else if (typeof exports !== "undefined") {
  5. factory(exports);
  6. } else {
  7. var mod = {
  8. exports: {}
  9. };
  10. factory(mod.exports);
  11. global.jstoxml = mod.exports;
  12. }
  13. })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports) {
  14. "use strict";
  15. Object.defineProperty(_exports, "__esModule", {
  16. value: true
  17. });
  18. _exports.toXML = _exports.default = void 0;
  19. function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
  20. function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
  21. function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
  22. function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
  23. function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
  24. function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
  25. function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
  26. function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
  27. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  28. function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
  29. var ARRAY = "array";
  30. var BOOLEAN = "boolean";
  31. var DATE = "date";
  32. var NULL = "null";
  33. var NUMBER = "number";
  34. var OBJECT = "object";
  35. var SPECIAL_OBJECT = "special-object";
  36. var STRING = "string";
  37. var PRIVATE_VARS = ["_selfCloseTag", "_attrs"];
  38. var PRIVATE_VARS_REGEXP = new RegExp(PRIVATE_VARS.join("|"), "g");
  39. /**
  40. * Determines the indent string based on current tree depth.
  41. */
  42. var getIndentStr = function getIndentStr() {
  43. var indent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
  44. var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  45. return indent.repeat(depth);
  46. };
  47. /**
  48. * Sugar function supplementing JS's quirky typeof operator, plus some extra help to detect
  49. * "special objects" expected by jstoxml.
  50. * Example:
  51. * getType(new Date());
  52. * -> 'date'
  53. */
  54. var getType = function getType(val) {
  55. return Array.isArray(val) && ARRAY || _typeof(val) === OBJECT && val !== null && val._name && SPECIAL_OBJECT || val instanceof Date && DATE || val === null && NULL || _typeof(val);
  56. };
  57. /**
  58. * Replaces matching values in a string with a new value.
  59. * Example:
  60. * filterStr('foo&bar', { '&': '&amp;' });
  61. * -> 'foo&amp;bar'
  62. */
  63. var filterStr = function filterStr() {
  64. var inputStr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
  65. var filter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  66. // Passthrough/no-op for nonstrings (e.g. number, boolean).
  67. if (typeof inputStr !== "string") {
  68. return inputStr;
  69. }
  70. var regexp = new RegExp("(".concat(Object.keys(filter).join("|"), ")(?!(\\w|#)*;)"), "g");
  71. return String(inputStr).replace(regexp, function (str, entity) {
  72. return filter[entity] || "";
  73. });
  74. };
  75. /**
  76. * Maps an object or array of arribute keyval pairs to a string.
  77. * Examples:
  78. * { foo: 'bar', baz: 'g' } -> 'foo="bar" baz="g"'
  79. * [ { ⚡: true }, { foo: 'bar' } ] -> '⚡ foo="bar"'
  80. */
  81. var getAttributeKeyVals = function getAttributeKeyVals() {
  82. var attributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  83. var filter = arguments.length > 1 ? arguments[1] : undefined;
  84. var keyVals = [];
  85. if (Array.isArray(attributes)) {
  86. // Array containing complex objects and potentially duplicate attributes.
  87. keyVals = attributes.map(function (attr) {
  88. var key = Object.keys(attr)[0];
  89. var val = attr[key];
  90. var filteredVal = filter ? filterStr(val, filter) : val;
  91. var valStr = filteredVal === true ? "" : "=\"".concat(filteredVal, "\"");
  92. return "".concat(key).concat(valStr);
  93. });
  94. } else {
  95. var keys = Object.keys(attributes);
  96. keyVals = keys.map(function (key) {
  97. // Simple object - keyval pairs.
  98. // For boolean true, simply output the key.
  99. var filteredVal = filter ? filterStr(attributes[key], filter) : attributes[key];
  100. var valStr = attributes[key] === true ? "" : "=\"".concat(filteredVal, "\"");
  101. return "".concat(key).concat(valStr);
  102. });
  103. }
  104. return keyVals;
  105. };
  106. /**
  107. * Converts an attributes object/array to a string of keyval pairs.
  108. * Example:
  109. * formatAttributes({ a: 1, b: 2 })
  110. * -> 'a="1" b="2"'
  111. */
  112. var formatAttributes = function formatAttributes() {
  113. var attributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  114. var filter = arguments.length > 1 ? arguments[1] : undefined;
  115. var keyVals = getAttributeKeyVals(attributes, filter);
  116. if (keyVals.length === 0) return "";
  117. var keysValsJoined = keyVals.join(" ");
  118. return " ".concat(keysValsJoined);
  119. };
  120. /**
  121. * Converts an object to a jstoxml array.
  122. * Example:
  123. * objToArray({ foo: 'bar', baz: 2 });
  124. * ->
  125. * [
  126. * {
  127. * _name: 'foo',
  128. * _content: 'bar'
  129. * },
  130. * {
  131. * _name: 'baz',
  132. * _content: 2
  133. * }
  134. * ]
  135. */
  136. var objToArray = function objToArray() {
  137. var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  138. return Object.keys(obj).map(function (key) {
  139. return {
  140. _name: key,
  141. _content: obj[key]
  142. };
  143. });
  144. };
  145. /**
  146. * Determines if a value is a primitive JavaScript value (not including Symbol).
  147. * Example:
  148. * isPrimitive(4);
  149. * -> true
  150. */
  151. var PRIMITIVE_TYPES = [STRING, NUMBER, BOOLEAN];
  152. var isPrimitive = function isPrimitive(val) {
  153. return PRIMITIVE_TYPES.includes(getType(val));
  154. };
  155. /**
  156. * Determines if a value is a simple primitive type that can fit onto one line. Needed for
  157. * determining any needed indenting and line breaks.
  158. * Example:
  159. * isSimpleType(new Date());
  160. * -> true
  161. */
  162. var SIMPLE_TYPES = [].concat(PRIMITIVE_TYPES, [DATE, SPECIAL_OBJECT]);
  163. var isSimpleType = function isSimpleType(val) {
  164. return SIMPLE_TYPES.includes(getType(val));
  165. };
  166. /**
  167. * Determines if an XML string is a simple primitive, or contains nested data.
  168. * Example:
  169. * isSimpleXML('<foo />');
  170. * -> false
  171. */
  172. var isSimpleXML = function isSimpleXML(xmlStr) {
  173. return !xmlStr.match("<");
  174. };
  175. /**
  176. * Assembles an XML header as defined by the config.
  177. */
  178. var DEFAULT_XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>';
  179. var getHeaderString = function getHeaderString(_ref) {
  180. var header = _ref.header,
  181. indent = _ref.indent,
  182. isOutputStart = _ref.isOutputStart;
  183. var shouldOutputHeader = header && isOutputStart;
  184. if (!shouldOutputHeader) return "";
  185. var shouldUseDefaultHeader = _typeof(header) === BOOLEAN; // return `${shouldUseDefaultHeader ? DEFAULT_XML_HEADER : header}${indent ? "\n" : ""
  186. // }`;
  187. return shouldUseDefaultHeader ? DEFAULT_XML_HEADER : header;
  188. };
  189. /**
  190. * Recursively traverses an object tree and converts the output to an XML string.
  191. * Example:
  192. * toXML({ foo: 'bar' });
  193. * -> <foo>bar</foo>
  194. */
  195. var defaultEntityFilter = {
  196. "<": "&lt;",
  197. ">": "&gt;",
  198. "&": "&amp;"
  199. };
  200. var toXML = function toXML() {
  201. var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  202. var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  203. var _config$depth = config.depth,
  204. depth = _config$depth === void 0 ? 0 : _config$depth,
  205. indent = config.indent,
  206. _isFirstItem = config._isFirstItem,
  207. _config$_isOutputStar = config._isOutputStart,
  208. _isOutputStart = _config$_isOutputStar === void 0 ? true : _config$_isOutputStar,
  209. header = config.header,
  210. _config$attributesFil = config.attributesFilter,
  211. rawAttributesFilter = _config$attributesFil === void 0 ? {} : _config$attributesFil,
  212. _config$filter = config.filter,
  213. rawFilter = _config$filter === void 0 ? {} : _config$filter;
  214. var shouldTurnOffAttributesFilter = typeof rawAttributesFilter === 'boolean' && !rawAttributesFilter;
  215. var attributesFilter = shouldTurnOffAttributesFilter ? {} : _objectSpread(_objectSpread(_objectSpread({}, defaultEntityFilter), {
  216. '"': "&quot;"
  217. }), rawAttributesFilter);
  218. var shouldTurnOffFilter = typeof rawFilter === 'boolean' && !rawFilter;
  219. var filter = shouldTurnOffFilter ? {} : _objectSpread(_objectSpread({}, defaultEntityFilter), rawFilter); // Determine indent string based on depth.
  220. var indentStr = getIndentStr(indent, depth); // For branching based on value type.
  221. var valType = getType(obj);
  222. var headerStr = getHeaderString({
  223. header: header,
  224. indent: indent,
  225. depth: depth,
  226. isOutputStart: _isOutputStart
  227. });
  228. var isOutputStart = _isOutputStart && !headerStr && _isFirstItem && depth === 0;
  229. var outputStr = "";
  230. switch (valType) {
  231. case "special-object":
  232. {
  233. // Processes a specially-formatted object used by jstoxml.
  234. var _name = obj._name,
  235. _content = obj._content; // Output text content without a tag wrapper.
  236. if (_content === null) {
  237. outputStr = _name;
  238. break;
  239. } // Handles arrays of primitive values. (#33)
  240. var isArrayOfPrimitives = Array.isArray(_content) && _content.every(isPrimitive);
  241. if (isArrayOfPrimitives) {
  242. var primitives = _content.map(function (a) {
  243. return toXML({
  244. _name: _name,
  245. _content: a
  246. }, _objectSpread(_objectSpread({}, config), {}, {
  247. depth: depth,
  248. _isOutputStart: false
  249. }));
  250. });
  251. return primitives.join('');
  252. } // Don't output private vars (such as _attrs).
  253. if (_name.match(PRIVATE_VARS_REGEXP)) break; // Process the nested new value and create new config.
  254. var newVal = toXML(_content, _objectSpread(_objectSpread({}, config), {}, {
  255. depth: depth + 1,
  256. _isOutputStart: isOutputStart
  257. }));
  258. var newValType = getType(newVal);
  259. var isNewValSimple = isSimpleXML(newVal); // Pre-tag output (indent and line breaks).
  260. var preIndentStr = indent && !isOutputStart ? "\n" : "";
  261. var preTag = "".concat(preIndentStr).concat(indentStr); // Special handling for comments, preserving preceding line breaks/indents.
  262. if (_name === '_comment') {
  263. outputStr += "".concat(preTag, "<!-- ").concat(_content, " -->");
  264. break;
  265. } // Tag output.
  266. var valIsEmpty = newValType === "undefined" || newVal === "";
  267. var shouldSelfClose = _typeof(obj._selfCloseTag) === BOOLEAN ? valIsEmpty && obj._selfCloseTag : valIsEmpty;
  268. var selfCloseStr = shouldSelfClose ? "/" : "";
  269. var attributesString = formatAttributes(obj._attrs, attributesFilter);
  270. var tag = "<".concat(_name).concat(attributesString).concat(selfCloseStr, ">"); // Post-tag output (closing tag, indent, line breaks).
  271. var preTagCloseStr = indent && !isNewValSimple ? "\n".concat(indentStr) : "";
  272. var postTag = !shouldSelfClose ? "".concat(newVal).concat(preTagCloseStr, "</").concat(_name, ">") : "";
  273. outputStr += "".concat(preTag).concat(tag).concat(postTag);
  274. break;
  275. }
  276. case "object":
  277. {
  278. // Iterates over keyval pairs in an object, converting each item to a special-object.
  279. var keys = Object.keys(obj);
  280. var outputArr = keys.map(function (key, index) {
  281. var newConfig = _objectSpread(_objectSpread({}, config), {}, {
  282. _isFirstItem: index === 0,
  283. _isLastItem: index + 1 === keys.length,
  284. _isOutputStart: isOutputStart
  285. });
  286. var outputObj = {
  287. _name: key
  288. };
  289. if (getType(obj[key]) === "object") {
  290. // Sub-object contains an object.
  291. // Move private vars up as needed. Needed to support certain types of objects
  292. // E.g. { foo: { _attrs: { a: 1 } } } -> <foo a="1"/>
  293. PRIVATE_VARS.forEach(function (privateVar) {
  294. var val = obj[key][privateVar];
  295. if (typeof val !== "undefined") {
  296. outputObj[privateVar] = val;
  297. delete obj[key][privateVar];
  298. }
  299. });
  300. var hasContent = typeof obj[key]._content !== "undefined";
  301. if (hasContent) {
  302. // _content has sibling keys, so pass as an array (edge case).
  303. // E.g. { foo: 'bar', _content: { baz: 2 } } -> <foo>bar</foo><baz>2</baz>
  304. if (Object.keys(obj[key]).length > 1) {
  305. var newContentObj = Object.assign({}, obj[key]);
  306. delete newContentObj._content;
  307. outputObj._content = [].concat(_toConsumableArray(objToArray(newContentObj)), [obj[key]._content]);
  308. }
  309. }
  310. } // Fallthrough: just pass the key as the content for the new special-object.
  311. if (typeof outputObj._content === "undefined") outputObj._content = obj[key];
  312. var xml = toXML(outputObj, newConfig, key);
  313. return xml;
  314. }, config);
  315. outputStr = outputArr.join('');
  316. break;
  317. }
  318. case "function":
  319. {
  320. // Executes a user-defined function and returns output.
  321. var fnResult = obj(config);
  322. outputStr = toXML(fnResult, config);
  323. break;
  324. }
  325. case "array":
  326. {
  327. // Iterates and converts each value in an array.
  328. var _outputArr = obj.map(function (singleVal, index) {
  329. var newConfig = _objectSpread(_objectSpread({}, config), {}, {
  330. _isFirstItem: index === 0,
  331. _isLastItem: index + 1 === obj.length,
  332. _isOutputStart: isOutputStart
  333. });
  334. return toXML(singleVal, newConfig);
  335. });
  336. outputStr = _outputArr.join('');
  337. break;
  338. }
  339. // number, string, boolean, date, null, etc
  340. default:
  341. {
  342. outputStr = filterStr(obj, filter);
  343. break;
  344. }
  345. }
  346. return "".concat(headerStr).concat(outputStr);
  347. };
  348. _exports.toXML = toXML;
  349. var _default = {
  350. toXML: toXML
  351. };
  352. _exports.default = _default;
  353. });