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.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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
193
|
-
parsedXmlObjects.push(...
|
|
220
|
+
const subParsed = yield this.processFilesInDirectory(filePath);
|
|
221
|
+
parsedXmlObjects.push(...subParsed);
|
|
194
222
|
}
|
|
195
223
|
}
|
|
196
224
|
return parsedXmlObjects;
|
|
197
225
|
});
|
|
198
226
|
}
|
|
199
|
-
|
|
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
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
308
|
+
var _a, _b;
|
|
309
|
+
if (!uniqueIdElements) {
|
|
285
310
|
return createShortHash(element);
|
|
286
311
|
}
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
return
|
|
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
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (typeof
|
|
345
|
-
|
|
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
|
|
379
|
+
return attributes;
|
|
373
380
|
}
|
|
374
381
|
|
|
375
|
-
function
|
|
382
|
+
function parseElementUnified(params) {
|
|
376
383
|
return __awaiter(this, void 0, void 0, function* () {
|
|
377
|
-
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
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
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
];
|
|
414
|
-
}
|
|
415
|
-
leafCount = updatedLeafCount;
|
|
416
|
-
hasNestedElements = updatedHasNestedElements;
|
|
411
|
+
return {
|
|
412
|
+
leafContent: {},
|
|
413
|
+
leafCount,
|
|
414
|
+
hasNestedElements: true,
|
|
415
|
+
};
|
|
417
416
|
}
|
|
418
417
|
}
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
|
480
|
-
return __awaiter(this,
|
|
481
|
-
var
|
|
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
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
526
|
-
|
|
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
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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
|
-
|
|
626
|
-
|
|
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) {
|