UNPKG

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