diff --git a/package-lock.json b/package-lock.json index 5353d8976ff5c..bb232dab2a24e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,6 @@ "@types/node": "latest", "@types/source-map-support": "latest", "@types/which": "^2.0.1", - "@types/xml2js": "^0.4.11", "@typescript-eslint/eslint-plugin": "^5.33.1", "@typescript-eslint/parser": "^5.33.1", "@typescript-eslint/utils": "^5.33.1", @@ -41,6 +40,7 @@ "eslint-plugin-jsdoc": "^39.3.6", "eslint-plugin-local": "^1.0.0", "eslint-plugin-no-null": "^1.0.2", + "fast-xml-parser": "^4.0.11", "fs-extra": "^9.1.0", "glob": "latest", "hereby": "^1.6.4", @@ -53,8 +53,7 @@ "node-fetch": "^3.2.10", "source-map-support": "latest", "typescript": "^4.8.4", - "which": "^2.0.2", - "xml2js": "^0.4.23" + "which": "^2.0.2" }, "engines": { "node": ">=4.2.0" @@ -479,15 +478,6 @@ "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", "dev": true }, - "node_modules/@types/xml2js": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.11.tgz", - "integrity": "sha512-JdigeAKmCyoJUiQljjr7tQG3if9NkqGUgwEUqBvV0N7LM4HyQk7UXCnusRa1lnvXAEYJ8mw8GtZWioagNztOwA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.42.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.0.tgz", @@ -2161,6 +2151,22 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", + "integrity": "sha512-4aUg3aNRR/WjQAcpceODG1C3x3lFANXRo8+1biqfieHmg9pyMt7qB4lQV/Ta6sJCTbA5vfD8fnA8S54JATiFUA==", + "dev": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -3942,12 +3948,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, "node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -4124,6 +4124,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4457,28 +4463,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -4876,15 +4860,6 @@ "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", "dev": true }, - "@types/xml2js": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.11.tgz", - "integrity": "sha512-JdigeAKmCyoJUiQljjr7tQG3if9NkqGUgwEUqBvV0N7LM4HyQk7UXCnusRa1lnvXAEYJ8mw8GtZWioagNztOwA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@typescript-eslint/eslint-plugin": { "version": "5.42.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.0.tgz", @@ -6019,6 +5994,15 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-xml-parser": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", + "integrity": "sha512-4aUg3aNRR/WjQAcpceODG1C3x3lFANXRo8+1biqfieHmg9pyMt7qB4lQV/Ta6sJCTbA5vfD8fnA8S54JATiFUA==", + "dev": true, + "requires": { + "strnum": "^1.0.5" + } + }, "fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -7264,12 +7248,6 @@ "is-regex": "^1.1.4" } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, "semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -7407,6 +7385,12 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7665,22 +7649,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 24e15520192e8..0811e154e947c 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "@types/node": "latest", "@types/source-map-support": "latest", "@types/which": "^2.0.1", - "@types/xml2js": "^0.4.11", "@typescript-eslint/eslint-plugin": "^5.33.1", "@typescript-eslint/parser": "^5.33.1", "@typescript-eslint/utils": "^5.33.1", @@ -67,6 +66,7 @@ "eslint-plugin-jsdoc": "^39.3.6", "eslint-plugin-local": "^1.0.0", "eslint-plugin-no-null": "^1.0.2", + "fast-xml-parser": "^4.0.11", "fs-extra": "^9.1.0", "glob": "latest", "hereby": "^1.6.4", @@ -79,8 +79,7 @@ "node-fetch": "^3.2.10", "source-map-support": "latest", "typescript": "^4.8.4", - "which": "^2.0.2", - "xml2js": "^0.4.23" + "which": "^2.0.2" }, "scripts": { "test": "hereby runtests-parallel --light=false", diff --git a/scripts/generateLocalizedDiagnosticMessages.mjs b/scripts/generateLocalizedDiagnosticMessages.mjs index e32ba78d037a4..4970c9453f66e 100644 --- a/scripts/generateLocalizedDiagnosticMessages.mjs +++ b/scripts/generateLocalizedDiagnosticMessages.mjs @@ -1,8 +1,28 @@ import fs from "fs"; import path from "path"; -import xml2js from "xml2js"; +import { XMLParser } from "fast-xml-parser"; + +/** @typedef {{ + LCX: { + $_TgtCul: string; + Item: { + Item: { + Item: { + $_ItemId: string; + Str: { + Val: string; + Tgt: { + Val: string; + }; + }; + }[]; + }; + }; + } +}} ParsedLCL */ +void 0; -function main() { +async function main() { const args = process.argv.slice(2); if (args.length !== 3) { console.log("Usage:"); @@ -15,38 +35,33 @@ function main() { const diagnosticsMapFilePath = args[2]; // generate the lcg file for enu - generateLCGFile(); + await generateLCGFile(); // generate other langs - fs.readdir(inputPath, (err, files) => { - handleError(err); - files.forEach(visitDirectory); - }); + const files = await fs.promises.readdir(inputPath); + await Promise.all(files.map(visitDirectory)); return; /** * @param {string} name */ - function visitDirectory(name) { + async function visitDirectory(name) { const inputFilePath = path.join(inputPath, name, "diagnosticMessages", "diagnosticMessages.generated.json.lcl"); - - fs.readFile(inputFilePath, (err, data) => { - handleError(err); - xml2js.parseString(data.toString(), (err, result) => { - handleError(err); - if (!result || !result.LCX || !result.LCX.$ || !result.LCX.$.TgtCul) { - console.error("Unexpected XML file structure. Expected to find result.LCX.$.TgtCul."); - process.exit(1); - } - const outputDirectoryName = getPreferredLocaleName(result.LCX.$.TgtCul).toLowerCase(); - if (!outputDirectoryName) { - console.error(`Invalid output locale name for '${result.LCX.$.TgtCul}'.`); - process.exit(1); - } - writeFile(path.join(outputPath, outputDirectoryName, "diagnosticMessages.generated.json"), xmlObjectToString(result)); - }); - }); + const contents = await fs.promises.readFile(inputFilePath); + /** @type {ParsedLCL} */ + // eslint-disable-next-line local/object-literal-surrounding-space + const result = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "$_"}).parse(contents); + if (!result || !result.LCX || !result.LCX.$_TgtCul) { + console.error("Unexpected XML file structure. Expected to find result.LCX.$_TgtCul."); + process.exit(1); + } + const outputDirectoryName = getPreferredLocaleName(result.LCX.$_TgtCul).toLowerCase(); + if (!outputDirectoryName) { + console.error(`Invalid output locale name for '${result.LCX.$_TgtCul}'.`); + process.exit(1); + } + await writeFile(path.join(outputPath, outputDirectoryName, "diagnosticMessages.generated.json"), xmlObjectToString(result)); } /** @@ -81,24 +96,14 @@ function main() { } /** - * @param {null | object} err - */ - function handleError(err) { - if (err) { - console.error(err); - process.exit(1); - } - } - - /** - * @param {any} o + * @param {ParsedLCL} o */ function xmlObjectToString(o) { /** @type {any} */ const out = {}; - for (const item of o.LCX.Item[0].Item[0].Item) { - let ItemId = item.$.ItemId; - let val = item.Str[0].Tgt ? item.Str[0].Tgt[0].Val[0] : item.Str[0].Val[0]; + for (const item of o.LCX.Item.Item.Item) { + let ItemId = item.$_ItemId; + let val = item.Str.Tgt ? item.Str.Tgt.Val : item.Str.Val; if (typeof ItemId !== "string" || typeof val !== "string") { console.error("Unexpected XML file structure"); @@ -115,55 +120,26 @@ function main() { return JSON.stringify(out, undefined, 2); } - - /** - * @param {string} directoryPath - * @param {() => void} action - */ - function ensureDirectoryExists(directoryPath, action) { - fs.exists(directoryPath, exists => { - if (!exists) { - const basePath = path.dirname(directoryPath); - if (basePath !== directoryPath) { - return ensureDirectoryExists(basePath, () => fs.mkdir(directoryPath, action)); - } - } - action(); - }); - } - /** * @param {string} fileName * @param {string} contents */ - function writeFile(fileName, contents) { - ensureDirectoryExists(path.dirname(fileName), () => { - fs.writeFile(fileName, contents, handleError); - }); + async function writeFile(fileName, contents) { + await fs.promises.mkdir(path.dirname(fileName), { recursive: true }); + await fs.promises.writeFile(fileName, contents); } - /** - * @param {Record} o - */ - function objectToList(o) { - const list = []; - for (const key in o) { - list.push({ key, value: o[key] }); - } - return list; - } - - function generateLCGFile() { - return fs.readFile(diagnosticsMapFilePath, (err, data) => { - handleError(err); - writeFile( - path.join(outputPath, "enu", "diagnosticMessages.generated.json.lcg"), - getLCGFileXML( - objectToList(JSON.parse(data.toString())) - .sort((a, b) => a.key > b.key ? 1 : -1) // lcg sorted by property keys - .reduce((s, { key, value }) => s + getItemXML(key, value), "") - )); - }); + async function generateLCGFile() { + const contents = await fs.promises.readFile(diagnosticsMapFilePath, "utf-8"); + await writeFile( + path.join(outputPath, "enu", "diagnosticMessages.generated.json.lcg"), + getLCGFileXML( + Object.entries(JSON.parse(contents)) + .sort((a, b) => a[0] > b[0] ? 1 : -1) // lcg sorted by property keys + .reduce((s, [key, value]) => s + getItemXML(key, value), "") + ), + ); + return; /** * @param {string} key @@ -204,4 +180,4 @@ function main() { } } -main(); +await main();