xml-disassembler 1.11.4 → 2.0.0
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 +18 -0
- package/CONTRIBUTING.md +41 -17
- package/README.md +109 -102
- package/dist/index.cjs +17 -711
- 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.mjs +18 -708
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -22
- package/xml-disassembler-crate/README.md +90 -0
- package/xml-disassembler-crate/index.node +0 -0
- package/xml-disassembler-crate/package-lock.json +13 -0
- package/xml-disassembler-crate/package.json +22 -0
- package/disassemble.log +0 -452
- package/dist/index.min.umd.js +0 -2
- package/dist/index.min.umd.js.map +0 -1
- package/dist/index.umd.js +0 -768
- package/dist/index.umd.js.map +0 -1
- package/dist/typings/builders/buildDisassembledFile.d.ts +0 -2
- package/dist/typings/builders/buildDisassembledFiles.d.ts +0 -2
- package/dist/typings/builders/buildXMLString.d.ts +0 -2
- package/dist/typings/builders/extractRootAttributes.d.ts +0 -2
- package/dist/typings/builders/mergeXmlElements.d.ts +0 -2
- package/dist/typings/constants/constants.d.ts +0 -27
- package/dist/typings/handlers/disassembleXMLFileHandler.d.ts +0 -28
- package/dist/typings/handlers/reassembleXMLFileHandler.d.ts +0 -13
- package/dist/typings/index.d.ts +0 -8
- package/dist/typings/parsers/parseElement.d.ts +0 -4
- package/dist/typings/parsers/parseToXmlObject.d.ts +0 -1
- package/dist/typings/parsers/parseUniqueIdElements.d.ts +0 -2
- package/dist/typings/parsers/parseXML.d.ts +0 -2
- package/dist/typings/parsers/stripWhitespace.d.ts +0 -1
- package/dist/typings/transformers/getTransformer.d.ts +0 -2
- package/dist/typings/transformers/transformers.d.ts +0 -6
- package/dist/typings/types/types.d.ts +0 -60
- package/dist/typings/utils/asyncQueue.d.ts +0 -10
- package/dist/typings/utils/objectPool.d.ts +0 -15
- package/samples/array-of-leafs/Dreamhouse.app-meta.xml +0 -60
- package/samples/attributes/notes.xml +0 -14
- package/samples/cdata/VidLand_US.marketingappextension-meta.xml +0 -51
- package/samples/comments/Numbers-fr.globalValueSetTranslation-meta.xml +0 -17
- package/samples/deeply-nested-unique-id-element/Get_Info.flow-meta.xml +0 -343
- package/samples/general/HR_Admin.permissionset-meta.xml +0 -42
- package/samples/ignore/HR_Admin.permissionset-meta.xml +0 -44
- package/samples/no-namespace/HR_Admin.permissionset-meta.xml +0 -44
- package/samples/no-nested-elements/HR_Admin.xml +0 -6
- package/samples/no-root-element/Assessment_Bot/Assessment_Bot.bot-meta.xml +0 -6
- package/samples/no-root-element/Assessment_Bot/botMlDomain/419e0199.botMlDomain-meta.xml +0 -7
- package/samples/no-root-element/Assessment_Bot.bot-meta.xml +0 -10
package/dist/index.cjs
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var log4js = require('log4js');
|
|
4
|
-
var promises = require('node:fs/promises');
|
|
5
|
-
var posix = require('node:path/posix');
|
|
6
|
-
var node_fs = require('node:fs');
|
|
7
|
-
var node_path = require('node:path');
|
|
8
|
-
var ignore = require('ignore');
|
|
9
|
-
var fastXmlParser = require('fast-xml-parser');
|
|
10
|
-
var json5 = require('json5');
|
|
11
|
-
var smolToml = require('smol-toml');
|
|
12
|
-
var ini = require('ini');
|
|
13
|
-
var yaml = require('yaml');
|
|
14
|
-
var node_crypto = require('node:crypto');
|
|
15
|
-
var promises$1 = require('fs/promises');
|
|
16
|
-
|
|
17
3
|
/******************************************************************************
|
|
18
4
|
Copyright (c) Microsoft Corporation.
|
|
19
5
|
|
|
@@ -46,731 +32,51 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
46
32
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
47
33
|
};
|
|
48
34
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const rootKey = Object.keys(first).find((k) => k !== "?xml");
|
|
56
|
-
const mergedContent = {};
|
|
57
|
-
for (const element of elements) {
|
|
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);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function mergeArrayValue(target, key, value) {
|
|
76
|
-
if (!target[key]) {
|
|
77
|
-
target[key] = value;
|
|
78
|
-
}
|
|
79
|
-
else if (Array.isArray(target[key])) {
|
|
80
|
-
target[key].push(...value);
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
target[key] = [target[key], ...value];
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function mergeObjectValue(target, key, value) {
|
|
87
|
-
if (Array.isArray(target[key])) {
|
|
88
|
-
target[key].push(value);
|
|
89
|
-
}
|
|
90
|
-
else if (target[key]) {
|
|
91
|
-
target[key] = [target[key], value];
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
target[key] = value;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function mergePrimitiveValue(target, key, value) {
|
|
98
|
-
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
|
99
|
-
target[key] = value;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function isMergeableObject(value) {
|
|
103
|
-
return typeof value === "object" && value !== null;
|
|
104
|
-
}
|
|
105
|
-
function buildFinalXmlElement(declaration, rootKey, content) {
|
|
106
|
-
return declaration
|
|
107
|
-
? { "?xml": declaration, [rootKey]: content }
|
|
108
|
-
: { [rootKey]: content };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const INDENT = " ";
|
|
112
|
-
const XML_PARSER_OPTION = {
|
|
113
|
-
commentPropName: "!---",
|
|
114
|
-
ignoreAttributes: false,
|
|
115
|
-
ignoreNameSpace: false,
|
|
116
|
-
parseTagValue: false,
|
|
117
|
-
parseNodeValue: false,
|
|
118
|
-
parseAttributeValue: false,
|
|
119
|
-
trimValues: false,
|
|
120
|
-
processEntities: false,
|
|
121
|
-
cdataPropName: "![CDATA[",
|
|
122
|
-
};
|
|
123
|
-
const JSON_PARSER_OPTION = Object.assign(Object.assign({}, XML_PARSER_OPTION), { format: true, indentBy: INDENT, suppressBooleanAttributes: false, suppressEmptyNode: false });
|
|
124
|
-
|
|
125
|
-
function isEmptyTextNode(key, value) {
|
|
126
|
-
return key === "#text" && typeof value === "string" && value.trim() === "";
|
|
127
|
-
}
|
|
128
|
-
function cleanArray(arr) {
|
|
129
|
-
return arr
|
|
130
|
-
.map(stripWhitespaceTextNodes)
|
|
131
|
-
.filter((entry) => !(typeof entry === "object" && Object.keys(entry).length === 0));
|
|
132
|
-
}
|
|
133
|
-
function cleanObject(obj) {
|
|
134
|
-
const result = {};
|
|
135
|
-
for (const key in obj) {
|
|
136
|
-
const value = obj[key];
|
|
137
|
-
if (isEmptyTextNode(key, value))
|
|
138
|
-
continue;
|
|
139
|
-
const cleaned = stripWhitespaceTextNodes(value);
|
|
140
|
-
if (cleaned !== undefined) {
|
|
141
|
-
result[key] = cleaned;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return result;
|
|
145
|
-
}
|
|
146
|
-
function stripWhitespaceTextNodes(node) {
|
|
147
|
-
if (Array.isArray(node)) {
|
|
148
|
-
return cleanArray(node);
|
|
149
|
-
}
|
|
150
|
-
else if (typeof node === "object" && node !== null) {
|
|
151
|
-
return cleanObject(node);
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
return node;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
let cachedParser = null;
|
|
159
|
-
function getParser() {
|
|
160
|
-
if (!cachedParser) {
|
|
161
|
-
cachedParser = new fastXmlParser.XMLParser(XML_PARSER_OPTION);
|
|
35
|
+
const native = require("xml-disassembler-crate");
|
|
36
|
+
class DisassembleXMLFileHandler {
|
|
37
|
+
disassemble(xmlAttributes) {
|
|
38
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
+
native.disassemble(xmlAttributes);
|
|
40
|
+
});
|
|
162
41
|
}
|
|
163
|
-
return cachedParser;
|
|
164
42
|
}
|
|
165
|
-
function parseXML(filePath) {
|
|
166
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
167
|
-
const xmlParser = getParser();
|
|
168
|
-
let xmlParsed;
|
|
169
|
-
try {
|
|
170
|
-
const xmlContent = yield promises.readFile(filePath, "utf-8");
|
|
171
|
-
xmlParsed = xmlParser.parse(xmlContent, true);
|
|
172
|
-
const cleaned = stripWhitespaceTextNodes(xmlParsed);
|
|
173
|
-
return cleaned;
|
|
174
|
-
}
|
|
175
|
-
catch (err) {
|
|
176
|
-
logger.error(`${filePath} was unabled to be parsed and will not be processed. Confirm formatting and try again.`);
|
|
177
|
-
return undefined;
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const parsers = {
|
|
183
|
-
".yaml": yaml.parse,
|
|
184
|
-
".yml": yaml.parse,
|
|
185
|
-
".json": JSON.parse,
|
|
186
|
-
".json5": json5.parse,
|
|
187
|
-
".toml": smolToml.parse,
|
|
188
|
-
".ini": ini.parse,
|
|
189
|
-
};
|
|
190
|
-
function parseToXmlObject(filePath) {
|
|
191
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
192
|
-
if (filePath.endsWith(".xml")) {
|
|
193
|
-
return yield parseXML(filePath);
|
|
194
|
-
}
|
|
195
|
-
const ext = Object.keys(parsers).find((ext) => filePath.endsWith(ext));
|
|
196
|
-
const fileContent = yield promises$1.readFile(filePath, "utf-8");
|
|
197
|
-
return parsers[ext](fileContent);
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
43
|
class ReassembleXMLFileHandler {
|
|
202
44
|
reassemble(xmlAttributes) {
|
|
203
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
204
|
-
|
|
205
|
-
if (!(yield this._validateDirectory(filePath)))
|
|
206
|
-
return;
|
|
207
|
-
logger.debug(`Parsing directory to reassemble: ${filePath}`);
|
|
208
|
-
const parsedXmlObjects = yield this.processFilesInDirectory(filePath);
|
|
209
|
-
if (!parsedXmlObjects.length) {
|
|
210
|
-
this._logEmptyParseError(filePath);
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const mergedXml = mergeXmlElements(parsedXmlObjects);
|
|
214
|
-
const finalXml = buildXMLString(mergedXml);
|
|
215
|
-
const outputPath = this._getOutputPath(filePath, fileExtension);
|
|
216
|
-
yield promises.writeFile(outputPath, finalXml, "utf-8");
|
|
217
|
-
if (postPurge)
|
|
218
|
-
yield promises.rm(filePath, { recursive: true });
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
processFilesInDirectory(dirPath) {
|
|
222
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
223
|
-
const parsedXmlObjects = [];
|
|
224
|
-
const files = yield promises.readdir(dirPath);
|
|
225
|
-
const sortedFiles = this._sortFilesByBaseName(files);
|
|
226
|
-
const statPromises = sortedFiles.map((file) => promises.stat(posix.join(dirPath, file)).then((stats) => ({ file, stats })));
|
|
227
|
-
const fileStats = yield Promise.all(statPromises);
|
|
228
|
-
for (const { file, stats } of fileStats) {
|
|
229
|
-
const filePath = posix.join(dirPath, file);
|
|
230
|
-
if (stats.isFile() && this._isParsableFile(file)) {
|
|
231
|
-
const parsed = yield parseToXmlObject(filePath);
|
|
232
|
-
if (parsed)
|
|
233
|
-
parsedXmlObjects.push(parsed);
|
|
234
|
-
}
|
|
235
|
-
else if (stats.isDirectory()) {
|
|
236
|
-
const subParsed = yield this.processFilesInDirectory(filePath);
|
|
237
|
-
parsedXmlObjects.push(...subParsed);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return parsedXmlObjects;
|
|
46
|
+
native.reassemble(xmlAttributes);
|
|
241
47
|
});
|
|
242
48
|
}
|
|
243
|
-
_sortFilesByBaseName(files) {
|
|
244
|
-
return files.sort((a, b) => a.split(".")[0].localeCompare(b.split(".")[0]));
|
|
245
|
-
}
|
|
246
|
-
_isParsableFile(fileName) {
|
|
247
|
-
return /\.(xml|json|json5|ya?ml|toml|ini)$/i.test(fileName);
|
|
248
|
-
}
|
|
249
|
-
_validateDirectory(path) {
|
|
250
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
251
|
-
const stats = yield promises.stat(path);
|
|
252
|
-
if (!stats.isDirectory()) {
|
|
253
|
-
logger.error(`The provided path to reassemble is not a directory: ${path}`);
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
return true;
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
_logEmptyParseError(path) {
|
|
260
|
-
logger.error(`No files under ${path} were parsed successfully. A reassembled XML file was not created.`);
|
|
261
|
-
}
|
|
262
|
-
_getOutputPath(dirPath, extension) {
|
|
263
|
-
const parentDir = posix.dirname(dirPath);
|
|
264
|
-
const baseName = posix.basename(dirPath);
|
|
265
|
-
const fileName = `${baseName}.${extension !== null && extension !== void 0 ? extension : "xml"}`;
|
|
266
|
-
return posix.join(parentDir, fileName);
|
|
267
|
-
}
|
|
268
49
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
return cachedBuilder;
|
|
50
|
+
function parseXML(filePath) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
const result = native.parseXml(filePath);
|
|
53
|
+
return result ? JSON.parse(result) : undefined;
|
|
54
|
+
});
|
|
276
55
|
}
|
|
277
56
|
function buildXMLString(element) {
|
|
278
|
-
|
|
279
|
-
return xmlBuilder.build(element).trimEnd();
|
|
57
|
+
return native.buildXmlString(JSON.stringify(element));
|
|
280
58
|
}
|
|
281
|
-
|
|
282
59
|
function transformToYaml(parsedXml) {
|
|
283
60
|
return __awaiter(this, void 0, void 0, function* () {
|
|
284
|
-
|
|
285
|
-
return yamlString;
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
function transformToJson5(parsedXml) {
|
|
289
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
290
|
-
const jsonString = json5.stringify(parsedXml, null, 2);
|
|
291
|
-
return jsonString;
|
|
61
|
+
return native.transformToYaml(JSON.stringify(parsedXml));
|
|
292
62
|
});
|
|
293
63
|
}
|
|
294
64
|
function transformToJson(parsedXml) {
|
|
295
65
|
return __awaiter(this, void 0, void 0, function* () {
|
|
296
|
-
|
|
297
|
-
return jsonString;
|
|
66
|
+
return native.transformToJson(JSON.stringify(parsedXml));
|
|
298
67
|
});
|
|
299
68
|
}
|
|
300
|
-
function
|
|
301
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
302
|
-
const tomlString = smolToml.stringify(parsedXml);
|
|
303
|
-
return tomlString;
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
function transformToIni(parsedXml) {
|
|
307
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
308
|
-
const iniString = ini.stringify(parsedXml);
|
|
309
|
-
return iniString;
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const transformers = {
|
|
314
|
-
yaml: transformToYaml,
|
|
315
|
-
json5: transformToJson5,
|
|
316
|
-
json: transformToJson,
|
|
317
|
-
toml: transformToToml,
|
|
318
|
-
ini: transformToIni,
|
|
319
|
-
};
|
|
320
|
-
function getTransformer(format) {
|
|
321
|
-
return transformers[format];
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const stringifyCache = new WeakMap();
|
|
325
|
-
function parseUniqueIdElement(element, uniqueIdElements) {
|
|
326
|
-
var _a, _b;
|
|
327
|
-
if (!uniqueIdElements) {
|
|
328
|
-
return createShortHash(element);
|
|
329
|
-
}
|
|
330
|
-
const id = (_b = (_a = findDirectFieldMatch(element, uniqueIdElements.split(","))) !== null && _a !== void 0 ? _a : findNestedFieldMatch(element, uniqueIdElements)) !== null && _b !== void 0 ? _b : createShortHash(element);
|
|
331
|
-
return id;
|
|
332
|
-
}
|
|
333
|
-
function findDirectFieldMatch(element, fieldNames) {
|
|
334
|
-
for (const name of fieldNames) {
|
|
335
|
-
const value = element[name];
|
|
336
|
-
if (typeof value === "string") {
|
|
337
|
-
return value;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function findNestedFieldMatch(element, uniqueIdElements) {
|
|
342
|
-
for (const key in element) {
|
|
343
|
-
const child = element[key];
|
|
344
|
-
if (!isObject(child))
|
|
345
|
-
continue;
|
|
346
|
-
const result = parseUniqueIdElement(child, uniqueIdElements);
|
|
347
|
-
if (result)
|
|
348
|
-
return result;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
function isObject(value) {
|
|
352
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
353
|
-
}
|
|
354
|
-
function createShortHash(element) {
|
|
355
|
-
let stringified = stringifyCache.get(element);
|
|
356
|
-
if (!stringified) {
|
|
357
|
-
stringified = JSON.stringify(element);
|
|
358
|
-
stringifyCache.set(element, stringified);
|
|
359
|
-
}
|
|
360
|
-
const hash = node_crypto.createHash("sha256").update(stringified).digest("hex");
|
|
361
|
-
return hash.slice(0, 8);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
function buildDisassembledFile(_a) {
|
|
365
|
-
return __awaiter(this, arguments, void 0, function* ({ content, disassembledPath, outputFileName, subdirectory, wrapKey, isGroupedArray = false, rootElementName, rootAttributes, xmlDeclaration, format, uniqueIdElements, }) {
|
|
366
|
-
const targetDirectory = subdirectory
|
|
367
|
-
? posix.join(disassembledPath, subdirectory)
|
|
368
|
-
: disassembledPath;
|
|
369
|
-
let fileName = outputFileName;
|
|
370
|
-
if (!fileName && wrapKey && !isGroupedArray && typeof content === "object") {
|
|
371
|
-
const fieldName = parseUniqueIdElement(content, uniqueIdElements);
|
|
372
|
-
fileName = `${fieldName}.${wrapKey}-meta.${format}`;
|
|
373
|
-
}
|
|
374
|
-
const outputPath = posix.join(targetDirectory, fileName);
|
|
375
|
-
yield promises.mkdir(targetDirectory, { recursive: true });
|
|
376
|
-
let wrappedXml = {
|
|
377
|
-
[rootElementName]: Object.assign(Object.assign({}, rootAttributes), (wrapKey
|
|
378
|
-
? { [wrapKey]: isGroupedArray ? content : content }
|
|
379
|
-
: content)),
|
|
380
|
-
};
|
|
381
|
-
if (typeof xmlDeclaration === "object" && xmlDeclaration !== null) {
|
|
382
|
-
wrappedXml = Object.assign({ "?xml": xmlDeclaration }, wrappedXml);
|
|
383
|
-
}
|
|
384
|
-
const transformer = getTransformer(format);
|
|
385
|
-
const outputString = transformer
|
|
386
|
-
? yield transformer(wrappedXml)
|
|
387
|
-
: buildXMLString(wrappedXml);
|
|
388
|
-
yield promises.writeFile(outputPath, outputString);
|
|
389
|
-
logger.debug(`Created disassembled file: ${outputPath}`);
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function extractRootAttributes(element) {
|
|
394
|
-
const attributes = {};
|
|
395
|
-
for (const [key, value] of Object.entries(element)) {
|
|
396
|
-
if (key.startsWith("@") && typeof value === "string") {
|
|
397
|
-
attributes[key] = value;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return attributes;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function parseElementUnified(params) {
|
|
404
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
405
|
-
const { element, disassembledPath, uniqueIdElements, rootElementName, rootAttributes, key, leafCount, hasNestedElements, format, xmlDeclaration, strategy, } = params;
|
|
406
|
-
const isArray = Array.isArray(element);
|
|
407
|
-
const isNestedObject = typeof element === "object" &&
|
|
408
|
-
element !== null &&
|
|
409
|
-
Object.keys(element).some((k) => !k.startsWith("#"));
|
|
410
|
-
const isNested = isArray || isNestedObject;
|
|
411
|
-
if (isNested) {
|
|
412
|
-
if (strategy === "grouped-by-tag") {
|
|
413
|
-
return {
|
|
414
|
-
leafContent: {},
|
|
415
|
-
leafCount,
|
|
416
|
-
hasNestedElements: true,
|
|
417
|
-
nestedGroups: { [key]: [element] },
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
else {
|
|
421
|
-
yield buildDisassembledFile({
|
|
422
|
-
content: element,
|
|
423
|
-
disassembledPath,
|
|
424
|
-
subdirectory: key,
|
|
425
|
-
wrapKey: key,
|
|
426
|
-
rootElementName,
|
|
427
|
-
rootAttributes,
|
|
428
|
-
xmlDeclaration,
|
|
429
|
-
format,
|
|
430
|
-
uniqueIdElements,
|
|
431
|
-
});
|
|
432
|
-
return {
|
|
433
|
-
leafContent: {},
|
|
434
|
-
leafCount,
|
|
435
|
-
hasNestedElements: true,
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
return {
|
|
440
|
-
leafContent: {
|
|
441
|
-
[key]: [element],
|
|
442
|
-
},
|
|
443
|
-
leafCount: leafCount + 1,
|
|
444
|
-
hasNestedElements,
|
|
445
|
-
};
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
function buildDisassembledFilesUnified(_a) {
|
|
450
|
-
return __awaiter(this, arguments, void 0, function* ({ filePath, disassembledPath, baseName, postPurge, format, uniqueIdElements, strategy, }) {
|
|
451
|
-
const parsedXml = yield parseXML(filePath);
|
|
452
|
-
if (!parsedXml)
|
|
453
|
-
return;
|
|
454
|
-
const { rootElementName, rootElement, xmlDeclaration } = getRootInfo(parsedXml);
|
|
455
|
-
const rootAttributes = extractRootAttributes(rootElement);
|
|
456
|
-
const keyOrder = Object.keys(rootElement).filter((k) => !k.startsWith("@"));
|
|
457
|
-
const { leafContent, nestedGroups, leafCount, hasNestedElements } = yield disassembleElementKeys({
|
|
458
|
-
rootElement,
|
|
459
|
-
keyOrder,
|
|
460
|
-
disassembledPath,
|
|
461
|
-
rootElementName,
|
|
462
|
-
rootAttributes,
|
|
463
|
-
xmlDeclaration,
|
|
464
|
-
uniqueIdElements,
|
|
465
|
-
strategy,
|
|
466
|
-
format,
|
|
467
|
-
});
|
|
468
|
-
if (shouldAbortForLeafOnly(leafCount, hasNestedElements, filePath))
|
|
469
|
-
return;
|
|
470
|
-
yield writeNestedGroups(nestedGroups, strategy, {
|
|
471
|
-
disassembledPath,
|
|
472
|
-
rootElementName,
|
|
473
|
-
rootAttributes,
|
|
474
|
-
xmlDeclaration,
|
|
475
|
-
format,
|
|
476
|
-
});
|
|
477
|
-
yield writeLeafContentIfAny({
|
|
478
|
-
leafCount,
|
|
479
|
-
leafContent,
|
|
480
|
-
strategy,
|
|
481
|
-
keyOrder,
|
|
482
|
-
options: {
|
|
483
|
-
disassembledPath,
|
|
484
|
-
outputFileName: `${baseName}.${format}`,
|
|
485
|
-
rootElementName,
|
|
486
|
-
rootAttributes,
|
|
487
|
-
xmlDeclaration,
|
|
488
|
-
format,
|
|
489
|
-
},
|
|
490
|
-
});
|
|
491
|
-
if (postPurge) {
|
|
492
|
-
yield promises.unlink(filePath);
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
function shouldAbortForLeafOnly(leafCount, hasNestedElements, filePath) {
|
|
497
|
-
if (!hasNestedElements && leafCount > 0) {
|
|
498
|
-
logger.error(`The XML file ${filePath} only has leaf elements. This file will not be disassembled.`);
|
|
499
|
-
return true;
|
|
500
|
-
}
|
|
501
|
-
return false;
|
|
502
|
-
}
|
|
503
|
-
function writeLeafContentIfAny(_a) {
|
|
504
|
-
return __awaiter(this, arguments, void 0, function* ({ leafCount, leafContent, strategy, keyOrder, options, }) {
|
|
505
|
-
if (leafCount === 0)
|
|
506
|
-
return;
|
|
507
|
-
const finalLeafContent = strategy === "grouped-by-tag"
|
|
508
|
-
? orderXmlElementKeys(leafContent, keyOrder)
|
|
509
|
-
: leafContent;
|
|
510
|
-
yield buildDisassembledFile(Object.assign({ content: finalLeafContent }, options));
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
function getRootInfo(parsedXml) {
|
|
514
|
-
const rawDeclaration = parsedXml["?xml"];
|
|
515
|
-
const xmlDeclaration = typeof rawDeclaration === "object" && rawDeclaration !== null
|
|
516
|
-
? rawDeclaration
|
|
517
|
-
: undefined;
|
|
518
|
-
const rootElementName = Object.keys(parsedXml).find((k) => k !== "?xml");
|
|
519
|
-
const rootElement = parsedXml[rootElementName];
|
|
520
|
-
return { rootElementName, rootElement, xmlDeclaration };
|
|
521
|
-
}
|
|
522
|
-
function orderXmlElementKeys(content, keyOrder) {
|
|
523
|
-
const ordered = {};
|
|
524
|
-
for (const key of keyOrder) {
|
|
525
|
-
if (content[key] !== undefined) {
|
|
526
|
-
ordered[key] = content[key];
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
return ordered;
|
|
530
|
-
}
|
|
531
|
-
function disassembleElementKeys(_a) {
|
|
532
|
-
return __awaiter(this, arguments, void 0, function* ({ rootElement, keyOrder, disassembledPath, rootElementName, rootAttributes, xmlDeclaration, uniqueIdElements, strategy, format, }) {
|
|
533
|
-
var _b, _c;
|
|
534
|
-
let leafContent = {};
|
|
535
|
-
let nestedGroups = {};
|
|
536
|
-
let leafCount = 0;
|
|
537
|
-
let hasNestedElements = false;
|
|
538
|
-
const BATCH_SIZE = 20;
|
|
539
|
-
for (const key of keyOrder) {
|
|
540
|
-
const elements = Array.isArray(rootElement[key])
|
|
541
|
-
? rootElement[key]
|
|
542
|
-
: [rootElement[key]];
|
|
543
|
-
for (let i = 0; i < elements.length; i += BATCH_SIZE) {
|
|
544
|
-
const batch = elements.slice(i, i + BATCH_SIZE);
|
|
545
|
-
const batchResults = yield Promise.all(batch.map((element, index) => parseElementUnified({
|
|
546
|
-
element,
|
|
547
|
-
disassembledPath,
|
|
548
|
-
uniqueIdElements,
|
|
549
|
-
rootElementName,
|
|
550
|
-
rootAttributes,
|
|
551
|
-
key,
|
|
552
|
-
leafCount,
|
|
553
|
-
hasNestedElements,
|
|
554
|
-
format,
|
|
555
|
-
xmlDeclaration,
|
|
556
|
-
strategy,
|
|
557
|
-
})));
|
|
558
|
-
for (let j = 0; j < batchResults.length; j++) {
|
|
559
|
-
const result = batchResults[j];
|
|
560
|
-
if (result.leafContent[key]) {
|
|
561
|
-
leafContent[key] = [
|
|
562
|
-
...((_b = leafContent[key]) !== null && _b !== void 0 ? _b : []),
|
|
563
|
-
...result.leafContent[key],
|
|
564
|
-
];
|
|
565
|
-
}
|
|
566
|
-
if (strategy === "grouped-by-tag" && result.nestedGroups) {
|
|
567
|
-
for (const tag in result.nestedGroups) {
|
|
568
|
-
nestedGroups[tag] = [
|
|
569
|
-
...((_c = nestedGroups[tag]) !== null && _c !== void 0 ? _c : []),
|
|
570
|
-
...result.nestedGroups[tag],
|
|
571
|
-
];
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
leafCount = result.leafCount;
|
|
575
|
-
hasNestedElements = result.hasNestedElements;
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
return { leafContent, nestedGroups, leafCount, hasNestedElements };
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
function writeNestedGroups(nestedGroups, strategy, options) {
|
|
69
|
+
function transformToJson5(parsedXml) {
|
|
583
70
|
return __awaiter(this, void 0, void 0, function* () {
|
|
584
|
-
|
|
585
|
-
return;
|
|
586
|
-
for (const tag in nestedGroups) {
|
|
587
|
-
yield buildDisassembledFile({
|
|
588
|
-
content: nestedGroups[tag],
|
|
589
|
-
disassembledPath: options.disassembledPath,
|
|
590
|
-
outputFileName: `${tag}.${options.format}`,
|
|
591
|
-
wrapKey: tag,
|
|
592
|
-
isGroupedArray: true,
|
|
593
|
-
rootElementName: options.rootElementName,
|
|
594
|
-
rootAttributes: options.rootAttributes,
|
|
595
|
-
xmlDeclaration: options.xmlDeclaration,
|
|
596
|
-
format: options.format,
|
|
597
|
-
});
|
|
598
|
-
}
|
|
71
|
+
return native.transformToJson5(JSON.stringify(parsedXml));
|
|
599
72
|
});
|
|
600
73
|
}
|
|
601
74
|
|
|
602
|
-
class AsyncTaskQueue {
|
|
603
|
-
constructor(concurrency = 10) {
|
|
604
|
-
this.queue = [];
|
|
605
|
-
this.running = 0;
|
|
606
|
-
this.concurrency = concurrency;
|
|
607
|
-
}
|
|
608
|
-
add(task) {
|
|
609
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
610
|
-
return new Promise((resolve, reject) => {
|
|
611
|
-
const wrappedTask = () => __awaiter(this, void 0, void 0, function* () {
|
|
612
|
-
try {
|
|
613
|
-
const result = yield task();
|
|
614
|
-
resolve(result);
|
|
615
|
-
}
|
|
616
|
-
catch (error) {
|
|
617
|
-
reject(error);
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
this.queue.push(wrappedTask);
|
|
621
|
-
this.process();
|
|
622
|
-
});
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
process() {
|
|
626
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
627
|
-
if (this.running >= this.concurrency || this.queue.length === 0) {
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
this.running++;
|
|
631
|
-
const task = this.queue.shift();
|
|
632
|
-
try {
|
|
633
|
-
yield task();
|
|
634
|
-
}
|
|
635
|
-
finally {
|
|
636
|
-
this.running--;
|
|
637
|
-
this.process();
|
|
638
|
-
}
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
waitForCompletion() {
|
|
642
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
643
|
-
while (this.running > 0 || this.queue.length > 0) {
|
|
644
|
-
yield new Promise((resolve) => setTimeout(resolve, 10));
|
|
645
|
-
}
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
class DisassembleXMLFileHandler {
|
|
651
|
-
constructor() {
|
|
652
|
-
this.ign = ignore();
|
|
653
|
-
this.taskQueue = new AsyncTaskQueue(10);
|
|
654
|
-
}
|
|
655
|
-
disassemble(xmlAttributes) {
|
|
656
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
657
|
-
let { filePath, uniqueIdElements, strategy = "unique-id", prePurge = false, postPurge = false, ignorePath = ".xmldisassemblerignore", format = "xml", } = xmlAttributes;
|
|
658
|
-
if (!["unique-id", "grouped-by-tag"].includes(strategy)) {
|
|
659
|
-
logger.warn(`Unsupported strategy "${strategy}", defaulting to "unique-id".`);
|
|
660
|
-
strategy = "unique-id";
|
|
661
|
-
}
|
|
662
|
-
yield this._loadIgnoreRules(ignorePath);
|
|
663
|
-
const fileStat = yield promises.stat(filePath);
|
|
664
|
-
const relativePath = this.posixPath(node_path.relative(process.cwd(), filePath));
|
|
665
|
-
if (fileStat.isFile()) {
|
|
666
|
-
yield this._handleFile(filePath, relativePath, {
|
|
667
|
-
uniqueIdElements,
|
|
668
|
-
strategy,
|
|
669
|
-
prePurge,
|
|
670
|
-
postPurge,
|
|
671
|
-
format,
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
else if (fileStat.isDirectory()) {
|
|
675
|
-
yield this._handleDirectory(filePath, {
|
|
676
|
-
uniqueIdElements,
|
|
677
|
-
strategy,
|
|
678
|
-
prePurge,
|
|
679
|
-
postPurge,
|
|
680
|
-
format,
|
|
681
|
-
ignorePath,
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
_loadIgnoreRules(ignorePath) {
|
|
687
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
688
|
-
const resolvedIgnorePath = node_path.resolve(ignorePath);
|
|
689
|
-
if (node_fs.existsSync(resolvedIgnorePath)) {
|
|
690
|
-
const content = yield promises.readFile(resolvedIgnorePath);
|
|
691
|
-
this.ign.add(content.toString());
|
|
692
|
-
}
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
_handleFile(filePath, relativePath, options) {
|
|
696
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
697
|
-
const resolvedPath = node_path.resolve(filePath);
|
|
698
|
-
if (!this._isXmlFile(resolvedPath)) {
|
|
699
|
-
logger.error(`The file path provided is not an XML file: ${resolvedPath}`);
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
if (this.ign.ignores(relativePath)) {
|
|
703
|
-
logger.warn(`File ignored by ignore rules: ${resolvedPath}`);
|
|
704
|
-
return;
|
|
705
|
-
}
|
|
706
|
-
const dirPath = node_path.dirname(resolvedPath);
|
|
707
|
-
yield this.processFile(Object.assign(Object.assign({}, options), { dirPath, filePath: resolvedPath }));
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
_handleDirectory(dirPath, options) {
|
|
711
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
712
|
-
const subFiles = yield promises.readdir(dirPath);
|
|
713
|
-
const processingPromises = subFiles.map((subFile) => __awaiter(this, void 0, void 0, function* () {
|
|
714
|
-
const subFilePath = node_path.join(dirPath, subFile);
|
|
715
|
-
const relativeSubFilePath = this.posixPath(node_path.relative(process.cwd(), subFilePath));
|
|
716
|
-
if (this._isXmlFile(subFilePath) &&
|
|
717
|
-
!this.ign.ignores(relativeSubFilePath)) {
|
|
718
|
-
return this.taskQueue.add(() => this.processFile(Object.assign(Object.assign({}, options), { dirPath, filePath: subFilePath })));
|
|
719
|
-
}
|
|
720
|
-
else if (this.ign.ignores(relativeSubFilePath)) {
|
|
721
|
-
logger.warn(`File ignored by ignore rules: ${subFilePath}`);
|
|
722
|
-
}
|
|
723
|
-
}));
|
|
724
|
-
yield Promise.all(processingPromises);
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
_isXmlFile(filePath) {
|
|
728
|
-
return filePath.endsWith(".xml");
|
|
729
|
-
}
|
|
730
|
-
processFile(xmlAttributes) {
|
|
731
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
732
|
-
const { dirPath, strategy, filePath, uniqueIdElements, prePurge, postPurge, format, } = xmlAttributes;
|
|
733
|
-
logger.debug(`Parsing file to disassemble: ${filePath}`);
|
|
734
|
-
const fullName = node_path.basename(filePath, node_path.extname(filePath));
|
|
735
|
-
const baseName = fullName.split(".")[0];
|
|
736
|
-
const outputPath = node_path.join(dirPath, baseName);
|
|
737
|
-
if (prePurge && node_fs.existsSync(outputPath)) {
|
|
738
|
-
yield promises.rm(outputPath, { recursive: true });
|
|
739
|
-
}
|
|
740
|
-
yield buildDisassembledFilesUnified({
|
|
741
|
-
filePath,
|
|
742
|
-
disassembledPath: outputPath,
|
|
743
|
-
uniqueIdElements,
|
|
744
|
-
baseName: fullName,
|
|
745
|
-
postPurge,
|
|
746
|
-
format,
|
|
747
|
-
strategy,
|
|
748
|
-
});
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
posixPath(path) {
|
|
752
|
-
return path.replace(/\\+/g, "/");
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
function setLogLevel(level) {
|
|
757
|
-
log4js.getLogger().level = level;
|
|
758
|
-
}
|
|
759
|
-
const logger = log4js.getLogger();
|
|
760
|
-
log4js.configure({
|
|
761
|
-
appenders: { disassemble: { type: "file", filename: "disassemble.log" } },
|
|
762
|
-
categories: { default: { appenders: ["disassemble"], level: "error" } },
|
|
763
|
-
});
|
|
764
|
-
|
|
765
75
|
exports.DisassembleXMLFileHandler = DisassembleXMLFileHandler;
|
|
766
76
|
exports.ReassembleXMLFileHandler = ReassembleXMLFileHandler;
|
|
767
77
|
exports.buildXMLString = buildXMLString;
|
|
768
|
-
exports.logger = logger;
|
|
769
78
|
exports.parseXML = parseXML;
|
|
770
|
-
exports.setLogLevel = setLogLevel;
|
|
771
|
-
exports.transformToIni = transformToIni;
|
|
772
79
|
exports.transformToJson = transformToJson;
|
|
773
80
|
exports.transformToJson5 = transformToJson5;
|
|
774
|
-
exports.transformToToml = transformToToml;
|
|
775
81
|
exports.transformToYaml = transformToYaml;
|
|
776
82
|
//# sourceMappingURL=index.cjs.map
|