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