xml-disassembler 1.10.11 → 1.10.13

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.
package/dist/index.cjs CHANGED
@@ -55,34 +55,49 @@ function mergeXmlElements(elements) {
55
55
  const rootKey = Object.keys(first).find((k) => k !== "?xml");
56
56
  const mergedContent = {};
57
57
  for (const element of elements) {
58
- const current = element[rootKey];
59
- for (const [childKey, value] of Object.entries(current)) {
60
- if (Array.isArray(value)) {
61
- mergedContent[childKey] = [...value];
62
- }
63
- else if (typeof value === "object") {
64
- if (Array.isArray(mergedContent[childKey])) {
65
- mergedContent[childKey].push(value);
66
- }
67
- else if (mergedContent[childKey]) {
68
- mergedContent[childKey] = [mergedContent[childKey], value];
69
- }
70
- else {
71
- mergedContent[childKey] = value;
72
- }
73
- }
74
- else {
75
- if (!mergedContent.hasOwnProperty(childKey)) {
76
- mergedContent[childKey] = value;
77
- }
78
- }
58
+ mergeElementContent(mergedContent, element[rootKey]);
59
+ }
60
+ return buildFinalXmlElement(first["?xml"], rootKey, mergedContent);
61
+ }
62
+ function mergeElementContent(target, source) {
63
+ for (const [key, value] of Object.entries(source)) {
64
+ if (Array.isArray(value)) {
65
+ mergeArrayValue(target, key, value);
66
+ }
67
+ else if (isMergeableObject(value)) {
68
+ mergeObjectValue(target, key, value);
69
+ }
70
+ else {
71
+ mergePrimitiveValue(target, key, value);
79
72
  }
80
73
  }
81
- const declaration = first["?xml"];
82
- const finalMerged = declaration
83
- ? { "?xml": declaration, [rootKey]: mergedContent }
84
- : { [rootKey]: mergedContent };
85
- return finalMerged;
74
+ }
75
+ function mergeArrayValue(target, key, value) {
76
+ target[key] = [...value];
77
+ }
78
+ function mergeObjectValue(target, key, value) {
79
+ if (Array.isArray(target[key])) {
80
+ target[key].push(value);
81
+ }
82
+ else if (target[key]) {
83
+ target[key] = [target[key], value];
84
+ }
85
+ else {
86
+ target[key] = value;
87
+ }
88
+ }
89
+ function mergePrimitiveValue(target, key, value) {
90
+ if (!Object.prototype.hasOwnProperty.call(target, key)) {
91
+ target[key] = value;
92
+ }
93
+ }
94
+ function isMergeableObject(value) {
95
+ return typeof value === "object" && value !== null;
96
+ }
97
+ function buildFinalXmlElement(declaration, rootKey, content) {
98
+ return declaration
99
+ ? { "?xml": declaration, [rootKey]: content }
100
+ : { [rootKey]: content };
86
101
  }
87
102
 
88
103
  const INDENT = " ";
@@ -99,25 +114,33 @@ const XML_PARSER_OPTION = {
99
114
  };
100
115
  const JSON_PARSER_OPTION = Object.assign(Object.assign({}, XML_PARSER_OPTION), { format: true, indentBy: INDENT, suppressBooleanAttributes: false, suppressEmptyNode: false });
101
116
 
117
+ function isEmptyTextNode(key, value) {
118
+ return key === "#text" && typeof value === "string" && value.trim() === "";
119
+ }
120
+ function cleanArray(arr) {
121
+ return arr
122
+ .map(stripWhitespaceTextNodes)
123
+ .filter((entry) => !(typeof entry === "object" && Object.keys(entry).length === 0));
124
+ }
125
+ function cleanObject(obj) {
126
+ const result = {};
127
+ for (const key in obj) {
128
+ const value = obj[key];
129
+ if (isEmptyTextNode(key, value))
130
+ continue;
131
+ const cleaned = stripWhitespaceTextNodes(value);
132
+ if (cleaned !== undefined) {
133
+ result[key] = cleaned;
134
+ }
135
+ }
136
+ return result;
137
+ }
102
138
  function stripWhitespaceTextNodes(node) {
103
139
  if (Array.isArray(node)) {
104
- return node.map(stripWhitespaceTextNodes).filter((entry) => {
105
- return !(typeof entry === "object" && Object.keys(entry).length === 0);
106
- });
140
+ return cleanArray(node);
107
141
  }
108
142
  else if (typeof node === "object" && node !== null) {
109
- const result = {};
110
- for (const key in node) {
111
- const value = node[key];
112
- if (key === "#text" && typeof value === "string" && value.trim() === "") {
113
- continue;
114
- }
115
- const cleaned = stripWhitespaceTextNodes(value);
116
- if (cleaned !== undefined) {
117
- result[key] = cleaned;
118
- }
119
- }
120
- return result;
143
+ return cleanObject(node);
121
144
  }
122
145
  else {
123
146
  return node;
@@ -141,90 +164,91 @@ function parseXML(filePath) {
141
164
  });
142
165
  }
143
166
 
167
+ const parsers = {
168
+ ".yaml": yaml.parse,
169
+ ".yml": yaml.parse,
170
+ ".json": JSON.parse,
171
+ ".json5": json5.parse,
172
+ ".toml": smolToml.parse,
173
+ ".ini": ini.parse,
174
+ };
144
175
  function parseToXmlObject(filePath) {
145
176
  return __awaiter(this, void 0, void 0, function* () {
146
177
  if (filePath.endsWith(".xml")) {
147
178
  return yield parseXML(filePath);
148
179
  }
180
+ const ext = Object.keys(parsers).find((ext) => filePath.endsWith(ext));
149
181
  const fileContent = yield promises$1.readFile(filePath, "utf-8");
150
- let parsed;
151
- if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
152
- parsed = yaml.parse(fileContent);
153
- }
154
- else if (filePath.endsWith(".json5")) {
155
- parsed = json5.parse(fileContent);
156
- }
157
- else if (filePath.endsWith(".json")) {
158
- parsed = JSON.parse(fileContent);
159
- }
160
- else if (filePath.endsWith(".toml")) {
161
- parsed = smolToml.parse(fileContent);
162
- }
163
- else if (filePath.endsWith(".ini")) {
164
- parsed = ini.parse(fileContent);
165
- }
166
- return parsed;
182
+ return parsers[ext](fileContent);
167
183
  });
168
184
  }
169
185
 
170
186
  class ReassembleXMLFileHandler {
187
+ reassemble(xmlAttributes) {
188
+ return __awaiter(this, void 0, void 0, function* () {
189
+ const { filePath, fileExtension, postPurge = false } = xmlAttributes;
190
+ if (!(yield this._validateDirectory(filePath)))
191
+ return;
192
+ logger.debug(`Parsing directory to reassemble: ${filePath}`);
193
+ const parsedXmlObjects = yield this.processFilesInDirectory(filePath);
194
+ if (!parsedXmlObjects.length) {
195
+ this._logEmptyParseError(filePath);
196
+ return;
197
+ }
198
+ const mergedXml = mergeXmlElements(parsedXmlObjects);
199
+ const finalXml = buildXMLString(mergedXml);
200
+ const outputPath = this._getOutputPath(filePath, fileExtension);
201
+ yield promises.writeFile(outputPath, finalXml, "utf-8");
202
+ if (postPurge)
203
+ yield promises.rm(filePath, { recursive: true });
204
+ });
205
+ }
171
206
  processFilesInDirectory(dirPath) {
172
207
  return __awaiter(this, void 0, void 0, function* () {
173
208
  const parsedXmlObjects = [];
174
209
  const files = yield promises.readdir(dirPath);
175
- files.sort((fileA, fileB) => {
176
- const fullNameA = fileA.split(".")[0].toLowerCase();
177
- const fullNameB = fileB.split(".")[0].toLowerCase();
178
- return fullNameA.localeCompare(fullNameB);
179
- });
180
- for (const file of files) {
210
+ const sortedFiles = this._sortFilesByBaseName(files);
211
+ for (const file of sortedFiles) {
181
212
  const filePath = posix.join(dirPath, file);
182
213
  const fileStat = yield promises.stat(filePath);
183
- if (fileStat.isFile()) {
184
- if (/\.(xml|json|json5|ya?ml|toml|ini)$/.test(file)) {
185
- const parsedObject = yield parseToXmlObject(filePath);
186
- if (parsedObject === undefined)
187
- continue;
188
- parsedXmlObjects.push(parsedObject);
189
- }
214
+ if (fileStat.isFile() && this._isParsableFile(file)) {
215
+ const parsed = yield parseToXmlObject(filePath);
216
+ if (parsed)
217
+ parsedXmlObjects.push(parsed);
190
218
  }
191
219
  else if (fileStat.isDirectory()) {
192
- const subParsedObjects = yield this.processFilesInDirectory(filePath);
193
- parsedXmlObjects.push(...subParsedObjects);
220
+ const subParsed = yield this.processFilesInDirectory(filePath);
221
+ parsedXmlObjects.push(...subParsed);
194
222
  }
195
223
  }
196
224
  return parsedXmlObjects;
197
225
  });
198
226
  }
199
- reassemble(xmlAttributes) {
227
+ _sortFilesByBaseName(files) {
228
+ return files.sort((a, b) => a.split(".")[0].localeCompare(b.split(".")[0]));
229
+ }
230
+ _isParsableFile(fileName) {
231
+ return /\.(xml|json|json5|ya?ml|toml|ini)$/i.test(fileName);
232
+ }
233
+ _validateDirectory(path) {
200
234
  return __awaiter(this, void 0, void 0, function* () {
201
- const { filePath, fileExtension, postPurge = false } = xmlAttributes;
202
- const fileStat = yield promises.stat(filePath);
203
- if (!fileStat.isDirectory()) {
204
- logger.error(`The provided path to reassemble is not a directory: ${filePath}`);
205
- return;
206
- }
207
- logger.debug(`Parsing directory to reassemble: ${filePath}`);
208
- const parsedXmlObjects = yield this.processFilesInDirectory(filePath);
209
- if (!parsedXmlObjects.length) {
210
- logger.error(`No files under ${filePath} were parsed successfully. A reassembled XML file was not created.`);
211
- return;
212
- }
213
- const mergedXml = mergeXmlElements(parsedXmlObjects);
214
- const xmlContent = buildXMLString(mergedXml);
215
- const finalXml = xmlContent;
216
- const parentDirectory = posix.dirname(filePath);
217
- const subdirectoryBasename = posix.basename(filePath);
218
- const fileName = fileExtension
219
- ? `${subdirectoryBasename}.${fileExtension}`
220
- : `${subdirectoryBasename}.xml`;
221
- const outputPath = posix.join(parentDirectory, fileName);
222
- yield promises.writeFile(outputPath, finalXml, "utf-8");
223
- if (postPurge) {
224
- yield promises.rm(filePath, { recursive: true });
235
+ const stats = yield promises.stat(path);
236
+ if (!stats.isDirectory()) {
237
+ logger.error(`The provided path to reassemble is not a directory: ${path}`);
238
+ return false;
225
239
  }
240
+ return true;
226
241
  });
227
242
  }
243
+ _logEmptyParseError(path) {
244
+ logger.error(`No files under ${path} were parsed successfully. A reassembled XML file was not created.`);
245
+ }
246
+ _getOutputPath(dirPath, extension) {
247
+ const parentDir = posix.dirname(dirPath);
248
+ const baseName = posix.basename(dirPath);
249
+ const fileName = `${baseName}.${extension !== null && extension !== void 0 ? extension : "xml"}`;
250
+ return posix.join(parentDir, fileName);
251
+ }
228
252
  }
229
253
 
230
254
  function buildXMLString(element) {
@@ -281,32 +305,39 @@ function getTransformer(format) {
281
305
  }
282
306
 
283
307
  function parseUniqueIdElement(element, uniqueIdElements) {
284
- if (uniqueIdElements === undefined) {
308
+ var _a, _b;
309
+ if (!uniqueIdElements) {
285
310
  return createShortHash(element);
286
311
  }
287
- const uniqueIdElementsArray = uniqueIdElements.split(",");
288
- for (const fieldName of uniqueIdElementsArray) {
289
- if (element[fieldName] !== undefined) {
290
- if (typeof element[fieldName] === "string") {
291
- return element[fieldName];
292
- }
312
+ const id = (_b = (_a = findDirectFieldMatch(element, uniqueIdElements.split(","))) !== null && _a !== void 0 ? _a : findNestedFieldMatch(element, uniqueIdElements)) !== null && _b !== void 0 ? _b : createShortHash(element);
313
+ return id;
314
+ }
315
+ function findDirectFieldMatch(element, fieldNames) {
316
+ for (const name of fieldNames) {
317
+ const value = element[name];
318
+ if (typeof value === "string") {
319
+ return value;
293
320
  }
294
321
  }
322
+ }
323
+ function findNestedFieldMatch(element, uniqueIdElements) {
295
324
  for (const key in element) {
296
- if (typeof element[key] === "object" && element[key] !== null) {
297
- const childFieldName = parseUniqueIdElement(element[key], uniqueIdElements);
298
- if (childFieldName !== undefined) {
299
- return childFieldName;
300
- }
301
- }
325
+ const child = element[key];
326
+ if (!isObject(child))
327
+ continue;
328
+ const result = parseUniqueIdElement(child, uniqueIdElements);
329
+ if (result)
330
+ return result;
302
331
  }
303
- return createShortHash(element);
332
+ }
333
+ function isObject(value) {
334
+ return typeof value === "object" && value !== null && !Array.isArray(value);
304
335
  }
305
336
  function createShortHash(element) {
306
- const hash = node_crypto.createHash("sha256");
307
- hash.update(JSON.stringify(element));
308
- const fullHash = hash.digest("hex");
309
- return fullHash.slice(0, 8);
337
+ const hash = node_crypto.createHash("sha256")
338
+ .update(JSON.stringify(element))
339
+ .digest("hex");
340
+ return hash.slice(0, 8);
310
341
  }
311
342
 
312
343
  function buildDisassembledFile(_a) {
@@ -338,135 +369,135 @@ function buildDisassembledFile(_a) {
338
369
  });
339
370
  }
340
371
 
341
- function parseElement$1(params) {
342
- return __awaiter(this, void 0, void 0, function* () {
343
- const { element, disassembledPath, uniqueIdElements, rootElementName, rootAttributes, key, leafCount, hasNestedElements, format, xmlDeclaration, } = params;
344
- if (typeof element === "object" && element !== null) {
345
- yield buildDisassembledFile({
346
- content: element,
347
- disassembledPath,
348
- subdirectory: key,
349
- wrapKey: key,
350
- rootElementName,
351
- rootAttributes,
352
- xmlDeclaration,
353
- format,
354
- uniqueIdElements,
355
- });
356
- return [{}, leafCount, true];
357
- }
358
- const leafContent = {
359
- [key]: [element],
360
- };
361
- return [leafContent, leafCount + 1, hasNestedElements];
362
- });
363
- }
364
-
365
- function extractRootAttributes(rootElement) {
366
- const attributesOnly = {};
367
- for (const [attrKey, attrValue] of Object.entries(rootElement)) {
368
- if (attrKey.startsWith("@")) {
369
- attributesOnly[attrKey] = attrValue;
372
+ function extractRootAttributes(element) {
373
+ const attributes = {};
374
+ for (const [key, value] of Object.entries(element)) {
375
+ if (key.startsWith("@") && typeof value === "string") {
376
+ attributes[key] = value;
370
377
  }
371
378
  }
372
- return attributesOnly;
379
+ return attributes;
373
380
  }
374
381
 
375
- function buildDisassembledFiles$1(filePath, disassembledPath, uniqueIdElements, baseName, postPurge, format) {
382
+ function parseElementUnified(params) {
376
383
  return __awaiter(this, void 0, void 0, function* () {
377
- var _a;
378
- const parsedXml = yield parseXML(filePath);
379
- if (parsedXml === undefined)
380
- return;
381
- const rawDeclaration = parsedXml["?xml"];
382
- const xmlDeclaration = typeof rawDeclaration === "object" && rawDeclaration !== null
383
- ? rawDeclaration
384
- : undefined;
385
- const rootElementName = Object.keys(parsedXml).find((k) => k !== "?xml");
386
- const rootElement = parsedXml[rootElementName];
387
- const rootAttributes = extractRootAttributes(rootElement);
388
- let leafContent = {};
389
- let leafCount = 0;
390
- let hasNestedElements = false;
391
- for (const key of Object.keys(rootElement).filter((k) => !k.startsWith("@"))) {
392
- const elements = Array.isArray(rootElement[key])
393
- ? rootElement[key]
394
- : [rootElement[key]];
395
- for (const element of elements) {
396
- const [parsedLeafContent, updatedLeafCount, updatedHasNestedElements] = yield parseElement$1({
397
- element,
384
+ const { element, disassembledPath, uniqueIdElements, rootElementName, rootAttributes, key, leafCount, hasNestedElements, format, xmlDeclaration, strategy, } = params;
385
+ const isArray = Array.isArray(element);
386
+ const isNestedObject = typeof element === "object" &&
387
+ element !== null &&
388
+ Object.keys(element).some((k) => !k.startsWith("#"));
389
+ const isNested = isArray || isNestedObject;
390
+ if (isNested) {
391
+ if (strategy === "grouped-by-tag") {
392
+ return {
393
+ leafContent: {},
394
+ leafCount,
395
+ hasNestedElements: true,
396
+ nestedGroups: { [key]: [element] },
397
+ };
398
+ }
399
+ else {
400
+ yield buildDisassembledFile({
401
+ content: element,
398
402
  disassembledPath,
399
- uniqueIdElements,
403
+ subdirectory: key,
404
+ wrapKey: key,
400
405
  rootElementName,
401
406
  rootAttributes,
402
- key,
403
- leafCount,
404
- hasNestedElements,
405
- format,
406
407
  xmlDeclaration,
408
+ format,
409
+ uniqueIdElements,
407
410
  });
408
- const newContent = parsedLeafContent[key];
409
- if (newContent !== undefined) {
410
- leafContent[key] = [
411
- ...((_a = leafContent[key]) !== null && _a !== void 0 ? _a : []),
412
- ...newContent,
413
- ];
414
- }
415
- leafCount = updatedLeafCount;
416
- hasNestedElements = updatedHasNestedElements;
411
+ return {
412
+ leafContent: {},
413
+ leafCount,
414
+ hasNestedElements: true,
415
+ };
417
416
  }
418
417
  }
419
- if (!hasNestedElements) {
420
- logger.error(`The XML file ${filePath} only has leaf elements. This file will not be disassembled.`);
418
+ return {
419
+ leafContent: {
420
+ [key]: [element],
421
+ },
422
+ leafCount: leafCount + 1,
423
+ hasNestedElements,
424
+ };
425
+ });
426
+ }
427
+
428
+ function buildDisassembledFilesUnified(_a) {
429
+ return __awaiter(this, arguments, void 0, function* ({ filePath, disassembledPath, baseName, postPurge, format, uniqueIdElements, strategy, }) {
430
+ const parsedXml = yield parseXML(filePath);
431
+ if (!parsedXml)
421
432
  return;
422
- }
423
- if (leafCount > 0) {
424
- yield buildDisassembledFile({
425
- content: leafContent,
433
+ const { rootElementName, rootElement, xmlDeclaration } = getRootInfo(parsedXml);
434
+ const rootAttributes = extractRootAttributes(rootElement);
435
+ const keyOrder = Object.keys(rootElement).filter((k) => !k.startsWith("@"));
436
+ const { leafContent, nestedGroups, leafCount, hasNestedElements } = yield disassembleElementKeys({
437
+ rootElement,
438
+ keyOrder,
439
+ disassembledPath,
440
+ rootElementName,
441
+ rootAttributes,
442
+ xmlDeclaration,
443
+ uniqueIdElements,
444
+ strategy,
445
+ format,
446
+ });
447
+ if (shouldAbortForLeafOnly(leafCount, hasNestedElements, filePath))
448
+ return;
449
+ yield writeNestedGroups(nestedGroups, strategy, {
450
+ disassembledPath,
451
+ rootElementName,
452
+ rootAttributes,
453
+ xmlDeclaration,
454
+ format,
455
+ });
456
+ yield writeLeafContentIfAny({
457
+ leafCount,
458
+ leafContent,
459
+ strategy,
460
+ keyOrder,
461
+ options: {
426
462
  disassembledPath,
427
463
  outputFileName: `${baseName}.${format}`,
428
464
  rootElementName,
429
465
  rootAttributes,
430
466
  xmlDeclaration,
431
467
  format,
432
- });
433
- }
468
+ },
469
+ });
434
470
  if (postPurge) {
435
471
  yield promises.unlink(filePath);
436
472
  }
437
473
  });
438
474
  }
439
-
440
- function parseElement(params) {
441
- return __awaiter(this, void 0, void 0, function* () {
442
- const { element, key, hasNestedElements } = params;
443
- const nestedGroups = {};
444
- const isArray = Array.isArray(element);
445
- const isNestedObject = typeof element === "object" &&
446
- element !== null &&
447
- Object.keys(element).some((k) => !k.startsWith("#"));
448
- const isNested = isArray || isNestedObject;
449
- if (isNested) {
450
- nestedGroups[key] = [element];
451
- return {
452
- leafContent: {},
453
- leafCount: params.leafCount,
454
- hasNestedElements: true,
455
- nestedGroups,
456
- };
457
- }
458
- const leafContent = {
459
- [key]: [element],
460
- };
461
- return {
462
- leafContent,
463
- leafCount: params.leafCount + 1,
464
- hasNestedElements,
465
- nestedGroups,
466
- };
475
+ function shouldAbortForLeafOnly(leafCount, hasNestedElements, filePath) {
476
+ if (!hasNestedElements && leafCount > 0) {
477
+ logger.error(`The XML file ${filePath} only has leaf elements. This file will not be disassembled.`);
478
+ return true;
479
+ }
480
+ return false;
481
+ }
482
+ function writeLeafContentIfAny(_a) {
483
+ return __awaiter(this, arguments, void 0, function* ({ leafCount, leafContent, strategy, keyOrder, options, }) {
484
+ if (leafCount === 0)
485
+ return;
486
+ const finalLeafContent = strategy === "grouped-by-tag"
487
+ ? orderXmlElementKeys(leafContent, keyOrder)
488
+ : leafContent;
489
+ yield buildDisassembledFile(Object.assign({ content: finalLeafContent }, options));
467
490
  });
468
491
  }
469
-
492
+ function getRootInfo(parsedXml) {
493
+ const rawDeclaration = parsedXml["?xml"];
494
+ const xmlDeclaration = typeof rawDeclaration === "object" && rawDeclaration !== null
495
+ ? rawDeclaration
496
+ : undefined;
497
+ const rootElementName = Object.keys(parsedXml).find((k) => k !== "?xml");
498
+ const rootElement = parsedXml[rootElementName];
499
+ return { rootElementName, rootElement, xmlDeclaration };
500
+ }
470
501
  function orderXmlElementKeys(content, keyOrder) {
471
502
  const ordered = {};
472
503
  for (const key of keyOrder) {
@@ -476,84 +507,69 @@ function orderXmlElementKeys(content, keyOrder) {
476
507
  }
477
508
  return ordered;
478
509
  }
479
- function buildDisassembledFiles(filePath, disassembledPath, baseName, postPurge, format) {
480
- return __awaiter(this, void 0, void 0, function* () {
481
- var _a;
482
- const parsedXml = yield parseXML(filePath);
483
- if (parsedXml === undefined)
484
- return;
485
- const rawDeclaration = parsedXml["?xml"];
486
- const xmlDeclaration = typeof rawDeclaration === "object" && rawDeclaration !== null
487
- ? rawDeclaration
488
- : undefined;
489
- const rootElementName = Object.keys(parsedXml).find((k) => k !== "?xml");
490
- const rootElement = parsedXml[rootElementName];
491
- const rootAttributes = extractRootAttributes(rootElement);
510
+ function disassembleElementKeys(_a) {
511
+ return __awaiter(this, arguments, void 0, function* ({ rootElement, keyOrder, disassembledPath, rootElementName, rootAttributes, xmlDeclaration, uniqueIdElements, strategy, format, }) {
512
+ var _b, _c;
492
513
  let leafContent = {};
514
+ let nestedGroups = {};
493
515
  let leafCount = 0;
494
516
  let hasNestedElements = false;
495
- const nestedGroups = {};
496
- const keyOrder = Object.keys(rootElement).filter((k) => !k.startsWith("@"));
497
517
  for (const key of keyOrder) {
498
518
  const elements = Array.isArray(rootElement[key])
499
519
  ? rootElement[key]
500
520
  : [rootElement[key]];
501
521
  for (const element of elements) {
502
- const result = yield parseElement({
522
+ const result = yield parseElementUnified({
503
523
  element,
524
+ disassembledPath,
525
+ uniqueIdElements,
526
+ rootElementName,
527
+ rootAttributes,
504
528
  key,
505
529
  leafCount,
506
- hasNestedElements});
507
- if (Object.keys(result.leafContent).length > 0) {
508
- const newContent = result.leafContent[key];
509
- if (newContent !== undefined) {
510
- leafContent[key] = [
511
- ...((_a = leafContent[key]) !== null && _a !== void 0 ? _a : []),
512
- ...newContent,
530
+ hasNestedElements,
531
+ format,
532
+ xmlDeclaration,
533
+ strategy,
534
+ });
535
+ if (result.leafContent[key]) {
536
+ leafContent[key] = [
537
+ ...((_b = leafContent[key]) !== null && _b !== void 0 ? _b : []),
538
+ ...result.leafContent[key],
539
+ ];
540
+ }
541
+ if (strategy === "grouped-by-tag" && result.nestedGroups) {
542
+ for (const tag in result.nestedGroups) {
543
+ nestedGroups[tag] = [
544
+ ...((_c = nestedGroups[tag]) !== null && _c !== void 0 ? _c : []),
545
+ ...result.nestedGroups[tag],
513
546
  ];
514
547
  }
515
548
  }
516
549
  leafCount = result.leafCount;
517
550
  hasNestedElements = result.hasNestedElements;
518
- for (const tag in result.nestedGroups) {
519
- if (!nestedGroups[tag])
520
- nestedGroups[tag] = [];
521
- nestedGroups[tag].push(...result.nestedGroups[tag]);
522
- }
523
551
  }
524
552
  }
525
- if (!hasNestedElements && leafCount > 0) {
526
- logger.error(`The XML file ${filePath} only has leaf elements. This file will not be disassembled.`);
553
+ return { leafContent, nestedGroups, leafCount, hasNestedElements };
554
+ });
555
+ }
556
+ function writeNestedGroups(nestedGroups, strategy, options) {
557
+ return __awaiter(this, void 0, void 0, function* () {
558
+ if (strategy !== "grouped-by-tag")
527
559
  return;
528
- }
529
560
  for (const tag in nestedGroups) {
530
561
  yield buildDisassembledFile({
531
562
  content: nestedGroups[tag],
532
- disassembledPath,
533
- outputFileName: `${tag}.${format}`,
563
+ disassembledPath: options.disassembledPath,
564
+ outputFileName: `${tag}.${options.format}`,
534
565
  wrapKey: tag,
535
566
  isGroupedArray: true,
536
- rootElementName,
537
- rootAttributes,
538
- xmlDeclaration,
539
- format,
567
+ rootElementName: options.rootElementName,
568
+ rootAttributes: options.rootAttributes,
569
+ xmlDeclaration: options.xmlDeclaration,
570
+ format: options.format,
540
571
  });
541
572
  }
542
- if (leafCount > 0) {
543
- const orderedLeafContent = orderXmlElementKeys(leafContent, keyOrder);
544
- yield buildDisassembledFile({
545
- content: orderedLeafContent,
546
- disassembledPath,
547
- outputFileName: `${baseName}.${format}`,
548
- rootElementName,
549
- rootAttributes,
550
- xmlDeclaration,
551
- format,
552
- });
553
- }
554
- if (postPurge) {
555
- yield promises.unlink(filePath);
556
- }
557
573
  });
558
574
  }
559
575
 
@@ -563,75 +579,97 @@ class DisassembleXMLFileHandler {
563
579
  }
564
580
  disassemble(xmlAttributes) {
565
581
  return __awaiter(this, void 0, void 0, function* () {
566
- const { filePath, uniqueIdElements, strategy = "unique-id", prePurge = false, postPurge = false, ignorePath = ".xmldisassemblerignore", format = "xml", } = xmlAttributes;
567
- const resolvedIgnorePath = node_path.resolve(ignorePath);
568
- if (node_fs.existsSync(resolvedIgnorePath)) {
569
- const content = yield promises.readFile(resolvedIgnorePath);
570
- this.ign.add(content.toString());
582
+ let { filePath, uniqueIdElements, strategy = "unique-id", prePurge = false, postPurge = false, ignorePath = ".xmldisassemblerignore", format = "xml", } = xmlAttributes;
583
+ if (!["unique-id", "grouped-by-tag"].includes(strategy)) {
584
+ logger.warn(`Unsupported strategy "${strategy}", defaulting to "unique-id".`);
585
+ strategy = "unique-id";
571
586
  }
587
+ yield this._loadIgnoreRules(ignorePath);
572
588
  const fileStat = yield promises.stat(filePath);
573
589
  const relativePath = this.posixPath(node_path.relative(process.cwd(), filePath));
574
590
  if (fileStat.isFile()) {
575
- const resolvedPath = node_path.resolve(filePath);
576
- if (!resolvedPath.endsWith(".xml")) {
577
- logger.error(`The file path provided is not an XML file: ${resolvedPath}`);
578
- return;
579
- }
580
- if (this.ign.ignores(relativePath)) {
581
- logger.warn(`File ignored by ${ignorePath}: ${resolvedPath}`);
582
- return;
583
- }
584
- const dirPath = node_path.dirname(resolvedPath);
585
- yield this.processFile({
586
- dirPath,
587
- strategy,
588
- filePath: resolvedPath,
591
+ yield this._handleFile(filePath, relativePath, {
589
592
  uniqueIdElements,
593
+ strategy,
590
594
  prePurge,
591
595
  postPurge,
592
596
  format,
593
597
  });
594
598
  }
595
599
  else if (fileStat.isDirectory()) {
596
- const subFiles = yield promises.readdir(filePath);
597
- for (const subFile of subFiles) {
598
- const subFilePath = node_path.join(filePath, subFile);
599
- const relativeSubFilePath = this.posixPath(node_path.relative(process.cwd(), subFilePath));
600
- if (subFilePath.endsWith(".xml") &&
601
- !this.ign.ignores(relativeSubFilePath)) {
602
- yield this.processFile({
603
- dirPath: filePath,
604
- strategy,
605
- filePath: subFilePath,
606
- uniqueIdElements,
607
- prePurge,
608
- postPurge,
609
- format,
610
- });
611
- }
612
- else if (this.ign.ignores(relativeSubFilePath)) {
613
- logger.warn(`File ignored by ${ignorePath}: ${subFilePath}`);
614
- }
600
+ yield this._handleDirectory(filePath, {
601
+ uniqueIdElements,
602
+ strategy,
603
+ prePurge,
604
+ postPurge,
605
+ format,
606
+ ignorePath,
607
+ });
608
+ }
609
+ });
610
+ }
611
+ _loadIgnoreRules(ignorePath) {
612
+ return __awaiter(this, void 0, void 0, function* () {
613
+ const resolvedIgnorePath = node_path.resolve(ignorePath);
614
+ if (node_fs.existsSync(resolvedIgnorePath)) {
615
+ const content = yield promises.readFile(resolvedIgnorePath);
616
+ this.ign.add(content.toString());
617
+ }
618
+ });
619
+ }
620
+ _handleFile(filePath, relativePath, options) {
621
+ return __awaiter(this, void 0, void 0, function* () {
622
+ const resolvedPath = node_path.resolve(filePath);
623
+ if (!this._isXmlFile(resolvedPath)) {
624
+ logger.error(`The file path provided is not an XML file: ${resolvedPath}`);
625
+ return;
626
+ }
627
+ if (this.ign.ignores(relativePath)) {
628
+ logger.warn(`File ignored by ignore rules: ${resolvedPath}`);
629
+ return;
630
+ }
631
+ const dirPath = node_path.dirname(resolvedPath);
632
+ yield this.processFile(Object.assign(Object.assign({}, options), { dirPath, filePath: resolvedPath }));
633
+ });
634
+ }
635
+ _handleDirectory(dirPath, options) {
636
+ return __awaiter(this, void 0, void 0, function* () {
637
+ const subFiles = yield promises.readdir(dirPath);
638
+ for (const subFile of subFiles) {
639
+ const subFilePath = node_path.join(dirPath, subFile);
640
+ const relativeSubFilePath = this.posixPath(node_path.relative(process.cwd(), subFilePath));
641
+ if (this._isXmlFile(subFilePath) &&
642
+ !this.ign.ignores(relativeSubFilePath)) {
643
+ yield this.processFile(Object.assign(Object.assign({}, options), { dirPath, filePath: subFilePath }));
644
+ }
645
+ else if (this.ign.ignores(relativeSubFilePath)) {
646
+ logger.warn(`File ignored by ignore rules: ${subFilePath}`);
615
647
  }
616
648
  }
617
649
  });
618
650
  }
651
+ _isXmlFile(filePath) {
652
+ return filePath.endsWith(".xml");
653
+ }
619
654
  processFile(xmlAttributes) {
620
655
  return __awaiter(this, void 0, void 0, function* () {
621
656
  const { dirPath, strategy, filePath, uniqueIdElements, prePurge, postPurge, format, } = xmlAttributes;
622
657
  logger.debug(`Parsing file to disassemble: ${filePath}`);
623
658
  const fullName = node_path.basename(filePath, node_path.extname(filePath));
624
659
  const baseName = fullName.split(".")[0];
625
- let outputPath;
626
- outputPath = node_path.join(dirPath, baseName);
627
- if (prePurge && node_fs.existsSync(outputPath))
660
+ const outputPath = node_path.join(dirPath, baseName);
661
+ if (prePurge && node_fs.existsSync(outputPath)) {
628
662
  yield promises.rm(outputPath, { recursive: true });
629
- if (strategy === "grouped-by-tag") {
630
- yield buildDisassembledFiles(filePath, outputPath, fullName, postPurge, format);
631
- }
632
- else {
633
- yield buildDisassembledFiles$1(filePath, outputPath, uniqueIdElements, fullName, postPurge, format);
634
663
  }
664
+ yield buildDisassembledFilesUnified({
665
+ filePath,
666
+ disassembledPath: outputPath,
667
+ uniqueIdElements,
668
+ baseName: fullName,
669
+ postPurge,
670
+ format,
671
+ strategy,
672
+ });
635
673
  });
636
674
  }
637
675
  posixPath(path) {