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