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