Skip to content

Commit 363e338

Browse files
committed
chore: improve properties parsing schema
1 parent b4e17b0 commit 363e338

File tree

10 files changed

+377
-142
lines changed

10 files changed

+377
-142
lines changed

automation/utils/bin/rui-generate-package-xml.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
#!/usr/bin/env ts-node
22

3-
import { writeClientPackageXml, ClientPackageXML } from "../src/package-xml-v2";
3+
import { readPropertiesXml, writeClientPackage } from "../src/xml-reader";
44
import { getWidgetInfo } from "../src/package-info";
5-
import { readPropertiesFile } from "../src/package-xml-v2/properties-xml";
65
import path from "node:path";
76
import { existsSync } from "node:fs";
87

@@ -29,11 +28,11 @@ async function generatePackageXml(): Promise<void> {
2928
}
3029

3130
// Read properties file and extract widget ID
32-
const propertiesXml = await readPropertiesFile(propertiesFilePath);
31+
const propertiesXml = await readPropertiesXml(propertiesFilePath);
3332
const widgetId = propertiesXml.widget["@_id"];
3433

3534
// Generate ClientPackageXML structure
36-
const clientPackageXml: ClientPackageXML = {
35+
const clientPackageXml = {
3736
name: packageInfo.mxpackage.name,
3837
version: packageInfo.version,
3938
widgetFiles: [packageInfo.mxpackage.name + ".xml"],
@@ -42,10 +41,10 @@ async function generatePackageXml(): Promise<void> {
4241

4342
// Write the generated package.xml
4443
const packageXmlPath = path.join(srcDir, "package.xml");
45-
await writeClientPackageXml(packageXmlPath, clientPackageXml);
44+
await writeClientPackage(packageXmlPath, clientPackageXml);
4645
}
4746

48-
async function main() {
47+
async function main(): Promise<void> {
4948
try {
5049
await generatePackageXml();
5150
} catch (error) {

automation/utils/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"cross-zip": "^4.0.1",
4545
"enquirer": "^2.4.1",
4646
"execa": "^5.1.1",
47-
"fast-xml-parser": "^4.1.3",
47+
"fast-xml-parser": "^5.2.5",
4848
"glob": "^11.0.3",
4949
"node-fetch": "^2.7.0",
5050
"ora": "^5.4.1",

automation/utils/src/package-xml-v2/index.ts

Lines changed: 0 additions & 94 deletions
This file was deleted.

automation/utils/src/package-xml-v2/properties-xml.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { ClientPackageInfo, readClientPackage, writeClientPackage } from "./package-xml";
2+
export { readPropertiesXml } from "./properties-xml";
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { ClientModulePackageFile } from "./schema";
2+
import { Version, VersionString } from "../version";
3+
import { readFile, writeFile } from "fs/promises";
4+
import { xmlJsonToXmlText, xmlTextToXmlJson } from "./parser";
5+
6+
export interface ClientPackageInfo {
7+
name: string;
8+
version: Version;
9+
widgetFiles: string[];
10+
files: string[];
11+
}
12+
13+
function parseClientPackageXml(xmlJson: ClientModulePackageFile): ClientPackageInfo {
14+
const clientModule = xmlJson?.package?.clientModule ?? {};
15+
16+
const name = clientModule["@_name"] ?? "";
17+
const versionString = clientModule["@_version"] ?? "1.0.0";
18+
19+
return {
20+
name,
21+
version: Version.fromString(versionString as VersionString),
22+
widgetFiles: clientModule.widgetFiles !== "" ? clientModule.widgetFiles?.widgetFile.map(i => i["@_path"]) : [],
23+
files: clientModule.files !== "" ? clientModule.files?.file.map(i => i["@_path"]) : []
24+
};
25+
}
26+
27+
function buildClientPackageXml(clientPackage: ClientPackageInfo): ClientModulePackageFile {
28+
const toXmlArray = (paths: string[]): any => {
29+
return paths.map(path => ({ "@_path": path }));
30+
};
31+
return {
32+
"?xml": {
33+
"@_version": "1.0",
34+
"@_encoding": "utf-8"
35+
},
36+
package: {
37+
clientModule: {
38+
widgetFiles:
39+
clientPackage.widgetFiles.length !== 0 ? { widgetFile: toXmlArray(clientPackage.widgetFiles) } : "",
40+
files: clientPackage.files.length !== 0 ? { file: toXmlArray(clientPackage.files) } : "",
41+
"@_name": clientPackage.name,
42+
"@_version": clientPackage.version.format(),
43+
"@_xmlns": "http://www.mendix.com/clientModule/1.0/"
44+
},
45+
"@_xmlns": "http://www.mendix.com/package/1.0/"
46+
}
47+
};
48+
}
49+
50+
export async function readClientPackage(path: string): Promise<ClientPackageInfo> {
51+
return parseClientPackageXml(ClientModulePackageFile.passthrough().parse(xmlTextToXmlJson(await readFile(path))));
52+
}
53+
54+
export async function writeClientPackage(path: string, data: ClientPackageInfo): Promise<void> {
55+
await writeFile(path, xmlJsonToXmlText(buildClientPackageXml(data)));
56+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { XMLBuilder, XMLParser } from "fast-xml-parser";
2+
3+
const arrayNodes = [
4+
"file",
5+
"widgetFile",
6+
"propertyGroup",
7+
"enumerationValue",
8+
"property",
9+
"systemProperty",
10+
"attributeType",
11+
"translation",
12+
"selectionType",
13+
"actionVariable",
14+
"associationType"
15+
];
16+
17+
const singleNodes = [
18+
"name",
19+
"caption",
20+
"description",
21+
"widget",
22+
"helpUrl",
23+
"enumerationValues",
24+
"returnType",
25+
"attributeTypes",
26+
"studioCategory",
27+
"studioProCategory",
28+
"selectionTypes",
29+
"category",
30+
"translations",
31+
"actionVariables",
32+
"associationTypes",
33+
"icon",
34+
"module",
35+
"projectFile",
36+
"properties"
37+
];
38+
39+
export function xmlTextToXmlJson(xmlText: string | Buffer): unknown {
40+
const parser = new XMLParser({
41+
ignoreAttributes: false,
42+
allowBooleanAttributes: true,
43+
attributeValueProcessor: (attr, val) => {
44+
if (attr === "defaultValue") {
45+
return val;
46+
}
47+
if (val === "true") return true;
48+
if (val === "false") return false;
49+
return val;
50+
},
51+
isArray: (name, _jpath, _isLeafNode, isAttribute) => {
52+
if (isAttribute) return false;
53+
if (arrayNodes.includes(name)) return true;
54+
if (singleNodes.includes(name)) return false;
55+
56+
return false;
57+
}
58+
});
59+
60+
return parser.parse(xmlText);
61+
}
62+
63+
export function xmlJsonToXmlText(xmlObject: any): string {
64+
const builder = new XMLBuilder({
65+
ignoreAttributes: false,
66+
format: true,
67+
indentBy: " ",
68+
suppressEmptyNode: true
69+
});
70+
return builder
71+
.build(xmlObject)
72+
.replaceAll(/(<[^>]*?)\/>/g, "$1 />") // Add space before /> in self-closing tags
73+
.replaceAll(/(<\?[^>]*?)\?>/g, "$1 ?>"); // Add space before ?> in XML declarations
74+
}

0 commit comments

Comments
 (0)