xml-disassembler 1.10.10 → 1.10.12

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.mjs CHANGED
@@ -4,12 +4,12 @@ import { join, dirname, basename } from 'node:path/posix';
4
4
  import { existsSync } from 'node:fs';
5
5
  import { resolve, relative, dirname as dirname$1, join as join$1, basename as basename$1, extname } from 'node:path';
6
6
  import ignore from 'ignore';
7
- import { createHash } from 'node:crypto';
8
7
  import { XMLParser, XMLBuilder } from 'fast-xml-parser';
9
8
  import { parse as parse$1, stringify as stringify$1 } from 'json5';
10
9
  import { parse as parse$2, stringify as stringify$2 } from 'smol-toml';
11
10
  import { parse as parse$3, stringify as stringify$3 } from 'ini';
12
11
  import { parse, stringify } from 'yaml';
12
+ import { createHash } from 'node:crypto';
13
13
  import { readFile as readFile$1 } from 'fs/promises';
14
14
 
15
15
  /******************************************************************************
@@ -225,35 +225,6 @@ class ReassembleXMLFileHandler {
225
225
  }
226
226
  }
227
227
 
228
- function parseUniqueIdElement(element, uniqueIdElements) {
229
- if (uniqueIdElements === undefined) {
230
- return createShortHash(element);
231
- }
232
- const uniqueIdElementsArray = uniqueIdElements.split(",");
233
- for (const fieldName of uniqueIdElementsArray) {
234
- if (element[fieldName] !== undefined) {
235
- if (typeof element[fieldName] === "string") {
236
- return element[fieldName];
237
- }
238
- }
239
- }
240
- for (const key in element) {
241
- if (typeof element[key] === "object" && element[key] !== null) {
242
- const childFieldName = parseUniqueIdElement(element[key], uniqueIdElements);
243
- if (childFieldName !== undefined) {
244
- return childFieldName;
245
- }
246
- }
247
- }
248
- return createShortHash(element);
249
- }
250
- function createShortHash(element) {
251
- const hash = createHash("sha256");
252
- hash.update(JSON.stringify(element));
253
- const fullHash = hash.digest("hex");
254
- return fullHash.slice(0, 8);
255
- }
256
-
257
228
  function buildXMLString(element) {
258
229
  const xmlBuilder = new XMLBuilder(JSON_PARSER_OPTION);
259
230
  return xmlBuilder.build(element).trimEnd();
@@ -307,189 +278,182 @@ function getTransformer(format) {
307
278
  }
308
279
  }
309
280
 
310
- function buildNestedFile(element, disassembledPath, uniqueIdElements, rootElementName, rootAttributes, parentKey, format, xmlDeclaration) {
311
- return __awaiter(this, void 0, void 0, function* () {
312
- const fieldName = parseUniqueIdElement(element, uniqueIdElements);
313
- const outputDirectory = join(disassembledPath, parentKey);
314
- const outputFileName = `${fieldName}.${parentKey}-meta.${format}`;
315
- const outputPath = join(outputDirectory, outputFileName);
316
- yield mkdir(outputDirectory, { recursive: true });
317
- let finalXml = {
318
- [rootElementName]: Object.assign(Object.assign({}, rootAttributes), { [parentKey]: element }),
319
- };
320
- if (typeof xmlDeclaration === "object" && xmlDeclaration !== null) {
321
- finalXml = Object.assign({ "?xml": xmlDeclaration }, finalXml);
322
- }
323
- let nestedString;
324
- const transformer = getTransformer(format);
325
- if (transformer) {
326
- nestedString = yield transformer(finalXml);
327
- }
328
- else {
329
- nestedString = buildXMLString(finalXml);
281
+ function parseUniqueIdElement(element, uniqueIdElements) {
282
+ var _a, _b;
283
+ if (!uniqueIdElements) {
284
+ return createShortHash(element);
285
+ }
286
+ const id = (_b = (_a = findDirectFieldMatch(element, uniqueIdElements.split(","))) !== null && _a !== void 0 ? _a : findNestedFieldMatch(element, uniqueIdElements)) !== null && _b !== void 0 ? _b : createShortHash(element);
287
+ return id;
288
+ }
289
+ function findDirectFieldMatch(element, fieldNames) {
290
+ for (const name of fieldNames) {
291
+ const value = element[name];
292
+ if (typeof value === "string") {
293
+ return value;
330
294
  }
331
- yield writeFile(outputPath, nestedString);
332
- logger.debug(`Created disassembled file: ${outputPath}`);
333
- });
295
+ }
334
296
  }
335
-
336
- function parseElement$1(params) {
337
- return __awaiter(this, void 0, void 0, function* () {
338
- const { element, disassembledPath, uniqueIdElements, rootElementName, rootAttributes, key, leafCount, hasNestedElements, format, xmlDeclaration, } = params;
339
- if (typeof element === "object" && element !== null) {
340
- yield buildNestedFile(element, disassembledPath, uniqueIdElements, rootElementName, rootAttributes, key, format, xmlDeclaration);
341
- return [{}, leafCount, true];
297
+ function findNestedFieldMatch(element, uniqueIdElements) {
298
+ for (const key in element) {
299
+ const child = element[key];
300
+ if (typeof child === "object" && child !== null) {
301
+ const result = parseUniqueIdElement(child, uniqueIdElements);
302
+ if (result)
303
+ return result;
342
304
  }
343
- const leafContent = {
344
- [key]: [element],
345
- };
346
- return [leafContent, leafCount + 1, hasNestedElements];
347
- });
305
+ }
306
+ }
307
+ function createShortHash(element) {
308
+ const hash = createHash("sha256")
309
+ .update(JSON.stringify(element))
310
+ .digest("hex");
311
+ return hash.slice(0, 8);
348
312
  }
349
313
 
350
- function buildLeafFile(leafContent, disassembledPath, baseName, rootElementName, rootAttributes, format, xmlDeclaration) {
351
- return __awaiter(this, void 0, void 0, function* () {
352
- const leafOutputPath = join(disassembledPath, `${baseName}.${format}`);
353
- yield mkdir(disassembledPath, { recursive: true });
314
+ function buildDisassembledFile(_a) {
315
+ return __awaiter(this, arguments, void 0, function* ({ content, disassembledPath, outputFileName, subdirectory, wrapKey, isGroupedArray = false, rootElementName, rootAttributes, xmlDeclaration, format, uniqueIdElements, }) {
316
+ const targetDirectory = subdirectory
317
+ ? join(disassembledPath, subdirectory)
318
+ : disassembledPath;
319
+ let fileName = outputFileName;
320
+ if (!fileName && wrapKey && !isGroupedArray && typeof content === "object") {
321
+ const fieldName = parseUniqueIdElement(content, uniqueIdElements);
322
+ fileName = `${fieldName}.${wrapKey}-meta.${format}`;
323
+ }
324
+ const outputPath = join(targetDirectory, fileName);
325
+ yield mkdir(targetDirectory, { recursive: true });
354
326
  let wrappedXml = {
355
- [rootElementName]: Object.assign(Object.assign({}, rootAttributes), leafContent),
327
+ [rootElementName]: Object.assign(Object.assign({}, rootAttributes), (wrapKey
328
+ ? { [wrapKey]: isGroupedArray ? content : content }
329
+ : content)),
356
330
  };
357
331
  if (typeof xmlDeclaration === "object" && xmlDeclaration !== null) {
358
332
  wrappedXml = Object.assign({ "?xml": xmlDeclaration }, wrappedXml);
359
333
  }
360
- let leafString;
361
334
  const transformer = getTransformer(format);
362
- if (transformer) {
363
- leafString = yield transformer(wrappedXml);
364
- }
365
- else {
366
- leafString = buildXMLString(wrappedXml);
367
- }
368
- yield writeFile(leafOutputPath, leafString);
369
- logger.debug(`Created disassembled file: ${leafOutputPath}`);
335
+ const outputString = transformer
336
+ ? yield transformer(wrappedXml)
337
+ : buildXMLString(wrappedXml);
338
+ yield writeFile(outputPath, outputString);
339
+ logger.debug(`Created disassembled file: ${outputPath}`);
370
340
  });
371
341
  }
372
342
 
373
- function extractRootAttributes(rootElement) {
374
- const attributesOnly = {};
375
- for (const [attrKey, attrValue] of Object.entries(rootElement)) {
376
- if (attrKey.startsWith("@")) {
377
- attributesOnly[attrKey] = attrValue;
343
+ function extractRootAttributes(element) {
344
+ const attributes = {};
345
+ for (const [key, value] of Object.entries(element)) {
346
+ if (key.startsWith("@") && typeof value === "string") {
347
+ attributes[key] = value;
378
348
  }
379
349
  }
380
- return attributesOnly;
350
+ return attributes;
381
351
  }
382
352
 
383
- function buildDisassembledFiles$1(filePath, disassembledPath, uniqueIdElements, baseName, postPurge, format) {
353
+ function parseElementUnified(params) {
384
354
  return __awaiter(this, void 0, void 0, function* () {
385
- var _a;
386
- const parsedXml = yield parseXML(filePath);
387
- if (parsedXml === undefined)
388
- return;
389
- const rawDeclaration = parsedXml["?xml"];
390
- const xmlDeclaration = typeof rawDeclaration === "object" && rawDeclaration !== null
391
- ? rawDeclaration
392
- : undefined;
393
- const rootElementName = Object.keys(parsedXml).find((k) => k !== "?xml");
394
- const rootElement = parsedXml[rootElementName];
395
- const rootAttributes = extractRootAttributes(rootElement);
396
- let leafContent = {};
397
- let leafCount = 0;
398
- let hasNestedElements = false;
399
- for (const key of Object.keys(rootElement).filter((k) => !k.startsWith("@"))) {
400
- const elements = Array.isArray(rootElement[key])
401
- ? rootElement[key]
402
- : [rootElement[key]];
403
- for (const element of elements) {
404
- const [parsedLeafContent, updatedLeafCount, updatedHasNestedElements] = yield parseElement$1({
405
- element,
406
- disassembledPath,
407
- uniqueIdElements,
408
- rootElementName,
409
- rootAttributes,
410
- key,
411
- leafCount,
412
- hasNestedElements,
413
- format,
414
- xmlDeclaration,
415
- });
416
- const newContent = parsedLeafContent[key];
417
- if (newContent !== undefined) {
418
- leafContent[key] = [
419
- ...((_a = leafContent[key]) !== null && _a !== void 0 ? _a : []),
420
- ...newContent,
421
- ];
422
- }
423
- leafCount = updatedLeafCount;
424
- hasNestedElements = updatedHasNestedElements;
425
- }
426
- }
427
- if (!hasNestedElements) {
428
- logger.error(`The XML file ${filePath} only has leaf elements. This file will not be disassembled.`);
429
- return;
430
- }
431
- if (leafCount > 0) {
432
- yield buildLeafFile(leafContent, disassembledPath, baseName, rootElementName, rootAttributes, format, xmlDeclaration);
433
- }
434
- if (postPurge) {
435
- yield unlink(filePath);
436
- }
437
- });
438
- }
439
-
440
- function parseElement(params) {
441
- return __awaiter(this, void 0, void 0, function* () {
442
- const { element, key, hasNestedElements } = params;
443
- const nestedGroups = {};
355
+ const { element, disassembledPath, uniqueIdElements, rootElementName, rootAttributes, key, leafCount, hasNestedElements, format, xmlDeclaration, strategy, } = params;
444
356
  const isArray = Array.isArray(element);
445
357
  const isNestedObject = typeof element === "object" &&
446
358
  element !== null &&
447
359
  Object.keys(element).some((k) => !k.startsWith("#"));
448
360
  const isNested = isArray || isNestedObject;
449
361
  if (isNested) {
450
- nestedGroups[key] = [element];
451
- return {
452
- leafContent: {},
453
- leafCount: params.leafCount,
454
- hasNestedElements: true,
455
- nestedGroups,
456
- };
362
+ if (strategy === "grouped-by-tag") {
363
+ return {
364
+ leafContent: {},
365
+ leafCount,
366
+ hasNestedElements: true,
367
+ nestedGroups: { [key]: [element] },
368
+ };
369
+ }
370
+ else {
371
+ yield buildDisassembledFile({
372
+ content: element,
373
+ disassembledPath,
374
+ subdirectory: key,
375
+ wrapKey: key,
376
+ rootElementName,
377
+ rootAttributes,
378
+ xmlDeclaration,
379
+ format,
380
+ uniqueIdElements,
381
+ });
382
+ return {
383
+ leafContent: {},
384
+ leafCount,
385
+ hasNestedElements: true,
386
+ };
387
+ }
457
388
  }
458
- const leafContent = {
459
- [key]: [element],
460
- };
461
389
  return {
462
- leafContent,
463
- leafCount: params.leafCount + 1,
390
+ leafContent: {
391
+ [key]: [element],
392
+ },
393
+ leafCount: leafCount + 1,
464
394
  hasNestedElements,
465
- nestedGroups,
466
395
  };
467
396
  });
468
397
  }
469
398
 
470
- function buildGroupedNestedFile(tag, elements, disassembledPath, rootElementName, rootAttributes, format, xmlDeclaration) {
471
- return __awaiter(this, void 0, void 0, function* () {
472
- const outputPath = join(disassembledPath, `${tag}.${format}`);
473
- yield mkdir(disassembledPath, { recursive: true });
474
- let rootElement = {
475
- [rootElementName]: Object.assign(Object.assign({}, rootAttributes), { [tag]: elements }),
476
- };
477
- if (typeof xmlDeclaration === "object" && xmlDeclaration !== null) {
478
- rootElement = Object.assign({ "?xml": xmlDeclaration }, rootElement);
399
+ function buildDisassembledFilesUnified(_a) {
400
+ return __awaiter(this, arguments, void 0, function* ({ filePath, disassembledPath, baseName, postPurge, format, uniqueIdElements, strategy, }) {
401
+ const parsedXml = yield parseXML(filePath);
402
+ if (!parsedXml)
403
+ return;
404
+ const { rootElementName, rootElement, xmlDeclaration } = getRootInfo(parsedXml);
405
+ const rootAttributes = extractRootAttributes(rootElement);
406
+ const keyOrder = Object.keys(rootElement).filter((k) => !k.startsWith("@"));
407
+ const { leafContent, nestedGroups, leafCount, hasNestedElements } = yield disassembleElementKeys({
408
+ rootElement,
409
+ keyOrder,
410
+ disassembledPath,
411
+ rootElementName,
412
+ rootAttributes,
413
+ xmlDeclaration,
414
+ uniqueIdElements,
415
+ strategy,
416
+ format,
417
+ });
418
+ if (!hasNestedElements && leafCount > 0) {
419
+ logger.error(`The XML file ${filePath} only has leaf elements. This file will not be disassembled.`);
420
+ return;
479
421
  }
480
- let nestedString;
481
- const transformer = getTransformer(format);
482
- if (transformer) {
483
- nestedString = yield transformer(rootElement);
422
+ yield writeNestedGroups(nestedGroups, strategy, {
423
+ disassembledPath,
424
+ rootElementName,
425
+ rootAttributes,
426
+ xmlDeclaration,
427
+ format,
428
+ });
429
+ if (leafCount > 0) {
430
+ const finalLeafContent = strategy === "grouped-by-tag"
431
+ ? orderXmlElementKeys(leafContent, keyOrder)
432
+ : leafContent;
433
+ yield buildDisassembledFile({
434
+ content: finalLeafContent,
435
+ disassembledPath,
436
+ outputFileName: `${baseName}.${format}`,
437
+ rootElementName,
438
+ rootAttributes,
439
+ xmlDeclaration,
440
+ format,
441
+ });
484
442
  }
485
- else {
486
- nestedString = buildXMLString(rootElement);
443
+ if (postPurge) {
444
+ yield unlink(filePath);
487
445
  }
488
- yield writeFile(outputPath, nestedString);
489
- logger.debug(`Created disassembled file: ${outputPath}`);
490
446
  });
491
447
  }
492
-
448
+ function getRootInfo(parsedXml) {
449
+ const rawDeclaration = parsedXml["?xml"];
450
+ const xmlDeclaration = typeof rawDeclaration === "object" && rawDeclaration !== null
451
+ ? rawDeclaration
452
+ : undefined;
453
+ const rootElementName = Object.keys(parsedXml).find((k) => k !== "?xml");
454
+ const rootElement = parsedXml[rootElementName];
455
+ return { rootElementName, rootElement, xmlDeclaration };
456
+ }
493
457
  function orderXmlElementKeys(content, keyOrder) {
494
458
  const ordered = {};
495
459
  for (const key of keyOrder) {
@@ -499,65 +463,68 @@ function orderXmlElementKeys(content, keyOrder) {
499
463
  }
500
464
  return ordered;
501
465
  }
502
- function buildDisassembledFiles(filePath, disassembledPath, baseName, postPurge, format) {
503
- return __awaiter(this, void 0, void 0, function* () {
504
- var _a;
505
- const parsedXml = yield parseXML(filePath);
506
- if (parsedXml === undefined)
507
- return;
508
- const rawDeclaration = parsedXml["?xml"];
509
- const xmlDeclaration = typeof rawDeclaration === "object" && rawDeclaration !== null
510
- ? rawDeclaration
511
- : undefined;
512
- const rootElementName = Object.keys(parsedXml).find((k) => k !== "?xml");
513
- const rootElement = parsedXml[rootElementName];
514
- const rootAttributes = extractRootAttributes(rootElement);
466
+ function disassembleElementKeys(_a) {
467
+ return __awaiter(this, arguments, void 0, function* ({ rootElement, keyOrder, disassembledPath, rootElementName, rootAttributes, xmlDeclaration, uniqueIdElements, strategy, format, }) {
468
+ var _b, _c;
515
469
  let leafContent = {};
470
+ let nestedGroups = {};
516
471
  let leafCount = 0;
517
472
  let hasNestedElements = false;
518
- const nestedGroups = {};
519
- const keyOrder = Object.keys(rootElement).filter((k) => !k.startsWith("@"));
520
473
  for (const key of keyOrder) {
521
474
  const elements = Array.isArray(rootElement[key])
522
475
  ? rootElement[key]
523
476
  : [rootElement[key]];
524
477
  for (const element of elements) {
525
- const result = yield parseElement({
478
+ const result = yield parseElementUnified({
526
479
  element,
480
+ disassembledPath,
481
+ uniqueIdElements,
482
+ rootElementName,
483
+ rootAttributes,
527
484
  key,
528
485
  leafCount,
529
- hasNestedElements});
530
- if (Object.keys(result.leafContent).length > 0) {
531
- const newContent = result.leafContent[key];
532
- if (newContent !== undefined) {
533
- leafContent[key] = [
534
- ...((_a = leafContent[key]) !== null && _a !== void 0 ? _a : []),
535
- ...newContent,
486
+ hasNestedElements,
487
+ format,
488
+ xmlDeclaration,
489
+ strategy,
490
+ });
491
+ if (result.leafContent[key]) {
492
+ leafContent[key] = [
493
+ ...((_b = leafContent[key]) !== null && _b !== void 0 ? _b : []),
494
+ ...result.leafContent[key],
495
+ ];
496
+ }
497
+ if (strategy === "grouped-by-tag" && result.nestedGroups) {
498
+ for (const tag in result.nestedGroups) {
499
+ nestedGroups[tag] = [
500
+ ...((_c = nestedGroups[tag]) !== null && _c !== void 0 ? _c : []),
501
+ ...result.nestedGroups[tag],
536
502
  ];
537
503
  }
538
504
  }
539
505
  leafCount = result.leafCount;
540
506
  hasNestedElements = result.hasNestedElements;
541
- for (const tag in result.nestedGroups) {
542
- if (!nestedGroups[tag])
543
- nestedGroups[tag] = [];
544
- nestedGroups[tag].push(...result.nestedGroups[tag]);
545
- }
546
507
  }
547
508
  }
548
- if (!hasNestedElements && leafCount > 0) {
549
- logger.error(`The XML file ${filePath} only has leaf elements. This file will not be disassembled.`);
509
+ return { leafContent, nestedGroups, leafCount, hasNestedElements };
510
+ });
511
+ }
512
+ function writeNestedGroups(nestedGroups, strategy, options) {
513
+ return __awaiter(this, void 0, void 0, function* () {
514
+ if (strategy !== "grouped-by-tag")
550
515
  return;
551
- }
552
516
  for (const tag in nestedGroups) {
553
- yield buildGroupedNestedFile(tag, nestedGroups[tag], disassembledPath, rootElementName, rootAttributes, format, xmlDeclaration);
554
- }
555
- if (leafCount > 0) {
556
- const orderedLeafContent = orderXmlElementKeys(leafContent, keyOrder);
557
- yield buildLeafFile(orderedLeafContent, disassembledPath, baseName, rootElementName, rootAttributes, format, xmlDeclaration);
558
- }
559
- if (postPurge) {
560
- yield unlink(filePath);
517
+ yield buildDisassembledFile({
518
+ content: nestedGroups[tag],
519
+ disassembledPath: options.disassembledPath,
520
+ outputFileName: `${tag}.${options.format}`,
521
+ wrapKey: tag,
522
+ isGroupedArray: true,
523
+ rootElementName: options.rootElementName,
524
+ rootAttributes: options.rootAttributes,
525
+ xmlDeclaration: options.xmlDeclaration,
526
+ format: options.format,
527
+ });
561
528
  }
562
529
  });
563
530
  }
@@ -568,7 +535,11 @@ class DisassembleXMLFileHandler {
568
535
  }
569
536
  disassemble(xmlAttributes) {
570
537
  return __awaiter(this, void 0, void 0, function* () {
571
- const { filePath, uniqueIdElements, strategy = "unique-id", prePurge = false, postPurge = false, ignorePath = ".xmldisassemblerignore", format = "xml", } = xmlAttributes;
538
+ let { filePath, uniqueIdElements, strategy = "unique-id", prePurge = false, postPurge = false, ignorePath = ".xmldisassemblerignore", format = "xml", } = xmlAttributes;
539
+ if (!["unique-id", "grouped-by-tag"].includes(strategy)) {
540
+ logger.warn(`Unsupported strategy "${strategy}", defaulting to "unique-id".`);
541
+ strategy = "unique-id";
542
+ }
572
543
  const resolvedIgnorePath = resolve(ignorePath);
573
544
  if (existsSync(resolvedIgnorePath)) {
574
545
  const content = yield readFile(resolvedIgnorePath);
@@ -631,12 +602,15 @@ class DisassembleXMLFileHandler {
631
602
  outputPath = join$1(dirPath, baseName);
632
603
  if (prePurge && existsSync(outputPath))
633
604
  yield rm(outputPath, { recursive: true });
634
- if (strategy === "grouped-by-tag") {
635
- yield buildDisassembledFiles(filePath, outputPath, fullName, postPurge, format);
636
- }
637
- else {
638
- yield buildDisassembledFiles$1(filePath, outputPath, uniqueIdElements, fullName, postPurge, format);
639
- }
605
+ yield buildDisassembledFilesUnified({
606
+ filePath,
607
+ disassembledPath: outputPath,
608
+ uniqueIdElements,
609
+ baseName: fullName,
610
+ postPurge,
611
+ format,
612
+ strategy,
613
+ });
640
614
  });
641
615
  }
642
616
  posixPath(path) {