diff --git a/.licenses/npm/fast-xml-parser.dep.yml b/.licenses/npm/fast-xml-parser.dep.yml index 07fd88dd..88bd3e26 100644 Binary files a/.licenses/npm/fast-xml-parser.dep.yml and b/.licenses/npm/fast-xml-parser.dep.yml differ diff --git a/.licenses/npm/strnum.dep.yml b/.licenses/npm/strnum.dep.yml index c472ee04..f8fbc949 100644 Binary files a/.licenses/npm/strnum.dep.yml and b/.licenses/npm/strnum.dep.yml differ diff --git a/dist/cleanup/767.index.js b/dist/cleanup/767.index.js index b73e9e9f..35dd21b9 100644 --- a/dist/cleanup/767.index.js +++ b/dist/cleanup/767.index.js @@ -18680,12 +18680,26 @@ class XmlNode { this.child.push({ [node.tagname]: node.child }); } // if requested, add the startIndex + this.addStartIndex(startIndex); + } + + addStartIndex(startIndex) { if (startIndex !== undefined) { // Note: for now we just overwrite the metadata. If we had more complex metadata, // we might need to do an object append here: metadata = { ...metadata, startIndex } this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex }; } } + + addEndIndex(endIndex) { + const lastChild = this.child[this.child.length - 1]; + // endIndex is write-once: when updateTag drops a node, the last child is a + // previously completed sibling whose endIndex must not be overwritten + if (lastChild !== undefined && lastChild[METADATA_SYMBOL] !== undefined + && lastChild[METADATA_SYMBOL].endIndex === undefined) { + lastChild[METADATA_SYMBOL].endIndex = endIndex; + } + } /** symbol used for metadata */ static getMetaDataSymbol() { return METADATA_SYMBOL; @@ -18718,8 +18732,23 @@ class DocTypeReader { i = i + 9; let angleBracketsCount = 1; let hasBody = false, comment = false; + let quoteChar = null; // tracks an open SYSTEM/PUBLIC literal before the '[' body let exp = ""; for (; i < xmlData.length; i++) { + // Inside a quoted external-identifier literal — XML allows '<' + // and '>' as plain data here, so they must not be interpreted + // as DOCTYPE structure until the matching quote closes. + if (quoteChar !== null) { + if (xmlData[i] === quoteChar) quoteChar = null; + exp += xmlData[i]; + continue; + } + if (!hasBody && !comment && (xmlData[i] === '"' || xmlData[i] === "'")) { + quoteChar = xmlData[i]; + exp += xmlData[i]; + continue; + } + if (xmlData[i] === '<' && !comment) { //Determine the tag type if (hasBody && hasSeq(xmlData, "!ENTITY", i)) { i += 7; @@ -18774,7 +18803,7 @@ class DocTypeReader { exp += xmlData[i]; } } - if (angleBracketsCount !== 0) { + if (quoteChar !== null || angleBracketsCount !== 0) { throw new Error(`Unclosed DOCTYPE`); } } else { @@ -19489,7 +19518,11 @@ function resolveEnotation(str, trimmedStr, options) { */ function trimZeros(numStr) { if (numStr && numStr.indexOf(".") !== -1) {//float - numStr = numStr.replace(/0+$/, ""); //remove ending zeros + //remove ending zeros without the O(n^2) backtracking that /0+$/ hits + //when the string doesn't end in 0 but has a long internal zero-run + let end = numStr.length; + while (end > 0 && numStr.charCodeAt(end - 1) === 48 /* '0' */) end--; + numStr = numStr.slice(0, end); if (numStr === ".") numStr = "0"; else if (numStr[0] === ".") numStr = "0" + numStr; else if (numStr[numStr.length - 1] === ".") numStr = numStr.substring(0, numStr.length - 1); @@ -22965,7 +22998,12 @@ const parseXml = function (xmlData) { this.matcher.pop(); this.isCurrentNodeStopNode = false; // Reset flag when closing tag - currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope + //a closing tag with no matching opening tag leaves the stack empty + currentNode = this.tagsNodeStack.pop() || xmlObj;//avoid recursion, set the parent tag scope + + if (options.captureMetaData && currentNode) { + currentNode.addEndIndex(closeIndex + 1); + } textData = ""; i = closeIndex; } else if (c1 === 63) { //'?' @@ -22991,6 +23029,11 @@ const parseXml = function (xmlData) { childNode[":@"] = attsMap } this.addChild(currentNode, childNode, this.readonlyMatcher, i); + + if (options.captureMetaData) { + // closeIndex points at '?' of the closing '?>' + currentNode.addEndIndex(tagData.closeIndex + 2); + } } @@ -23155,6 +23198,10 @@ const parseXml = function (xmlData) { this.isCurrentNodeStopNode = false; // Reset flag this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex); + + if (options.captureMetaData) { + currentNode.addEndIndex(i + 1); + } } else { //selfClosing tag if (isSelfClosing) { @@ -23165,6 +23212,10 @@ const parseXml = function (xmlData) { childNode[":@"] = prefixedAttrs; } this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex); + + if (options.captureMetaData) { + currentNode.addEndIndex(closeIndex + 1); + } this.matcher.pop(); // Pop self-closing tag this.isCurrentNodeStopNode = false; // Reset flag } @@ -23174,6 +23225,10 @@ const parseXml = function (xmlData) { childNode[":@"] = prefixedAttrs; } this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex); + + if (options.captureMetaData) { + currentNode.addEndIndex(result.closeIndex + 1); + } this.matcher.pop(); // Pop unpaired tag this.isCurrentNodeStopNode = false; // Reset flag i = result.closeIndex; diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 8dae155d..1fd64fd6 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -35575,6 +35575,9 @@ function _unique(values) { /******/ __nccwpck_require__.m = __webpack_modules__; /******/ /************************************************************************/ +/******/ /* webpack/runtime/asset-relocator-loader */ +/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = decodeURIComponent(new URL('.', import.meta.url).pathname).slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/"; +/******/ /******/ /* webpack/runtime/compat get default export */ /******/ (() => { /******/ // getDefaultExport function for compatibility with non-harmony modules @@ -35667,10 +35670,6 @@ function _unique(values) { /******/ }; /******/ })(); /******/ -/******/ /* webpack/runtime/compat */ -/******/ -/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/"; -/******/ /******/ /* webpack/runtime/import chunk loading */ /******/ (() => { /******/ // no baseURI diff --git a/dist/setup/824.index.js b/dist/setup/824.index.js index c474cecf..8e59e5d1 100644 --- a/dist/setup/824.index.js +++ b/dist/setup/824.index.js @@ -741,12 +741,26 @@ class XmlNode { this.child.push({ [node.tagname]: node.child }); } // if requested, add the startIndex + this.addStartIndex(startIndex); + } + + addStartIndex(startIndex) { if (startIndex !== undefined) { // Note: for now we just overwrite the metadata. If we had more complex metadata, // we might need to do an object append here: metadata = { ...metadata, startIndex } this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex }; } } + + addEndIndex(endIndex) { + const lastChild = this.child[this.child.length - 1]; + // endIndex is write-once: when updateTag drops a node, the last child is a + // previously completed sibling whose endIndex must not be overwritten + if (lastChild !== undefined && lastChild[METADATA_SYMBOL] !== undefined + && lastChild[METADATA_SYMBOL].endIndex === undefined) { + lastChild[METADATA_SYMBOL].endIndex = endIndex; + } + } /** symbol used for metadata */ static getMetaDataSymbol() { return METADATA_SYMBOL; @@ -781,8 +795,23 @@ class DocTypeReader { i = i + 9; let angleBracketsCount = 1; let hasBody = false, comment = false; + let quoteChar = null; // tracks an open SYSTEM/PUBLIC literal before the '[' body let exp = ""; for (; i < xmlData.length; i++) { + // Inside a quoted external-identifier literal — XML allows '<' + // and '>' as plain data here, so they must not be interpreted + // as DOCTYPE structure until the matching quote closes. + if (quoteChar !== null) { + if (xmlData[i] === quoteChar) quoteChar = null; + exp += xmlData[i]; + continue; + } + if (!hasBody && !comment && (xmlData[i] === '"' || xmlData[i] === "'")) { + quoteChar = xmlData[i]; + exp += xmlData[i]; + continue; + } + if (xmlData[i] === '<' && !comment) { //Determine the tag type if (hasBody && hasSeq(xmlData, "!ENTITY", i)) { i += 7; @@ -837,7 +866,7 @@ class DocTypeReader { exp += xmlData[i]; } } - if (angleBracketsCount !== 0) { + if (quoteChar !== null || angleBracketsCount !== 0) { throw new Error(`Unclosed DOCTYPE`); } } else { @@ -1552,7 +1581,11 @@ function resolveEnotation(str, trimmedStr, options) { */ function trimZeros(numStr) { if (numStr && numStr.indexOf(".") !== -1) {//float - numStr = numStr.replace(/0+$/, ""); //remove ending zeros + //remove ending zeros without the O(n^2) backtracking that /0+$/ hits + //when the string doesn't end in 0 but has a long internal zero-run + let end = numStr.length; + while (end > 0 && numStr.charCodeAt(end - 1) === 48 /* '0' */) end--; + numStr = numStr.slice(0, end); if (numStr === ".") numStr = "0"; else if (numStr[0] === ".") numStr = "0" + numStr; else if (numStr[numStr.length - 1] === ".") numStr = numStr.substring(0, numStr.length - 1); @@ -5032,7 +5065,12 @@ const parseXml = function (xmlData) { this.matcher.pop(); this.isCurrentNodeStopNode = false; // Reset flag when closing tag - currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope + //a closing tag with no matching opening tag leaves the stack empty + currentNode = this.tagsNodeStack.pop() || xmlObj;//avoid recursion, set the parent tag scope + + if (options.captureMetaData && currentNode) { + currentNode.addEndIndex(closeIndex + 1); + } textData = ""; i = closeIndex; } else if (c1 === 63) { //'?' @@ -5058,6 +5096,11 @@ const parseXml = function (xmlData) { childNode[":@"] = attsMap } this.addChild(currentNode, childNode, this.readonlyMatcher, i); + + if (options.captureMetaData) { + // closeIndex points at '?' of the closing '?>' + currentNode.addEndIndex(tagData.closeIndex + 2); + } } @@ -5222,6 +5265,10 @@ const parseXml = function (xmlData) { this.isCurrentNodeStopNode = false; // Reset flag this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex); + + if (options.captureMetaData) { + currentNode.addEndIndex(i + 1); + } } else { //selfClosing tag if (isSelfClosing) { @@ -5232,6 +5279,10 @@ const parseXml = function (xmlData) { childNode[":@"] = prefixedAttrs; } this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex); + + if (options.captureMetaData) { + currentNode.addEndIndex(closeIndex + 1); + } this.matcher.pop(); // Pop self-closing tag this.isCurrentNodeStopNode = false; // Reset flag } @@ -5241,6 +5292,10 @@ const parseXml = function (xmlData) { childNode[":@"] = prefixedAttrs; } this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex); + + if (options.captureMetaData) { + currentNode.addEndIndex(result.closeIndex + 1); + } this.matcher.pop(); // Pop unpaired tag this.isCurrentNodeStopNode = false; // Reset flag i = result.closeIndex; diff --git a/dist/setup/index.js b/dist/setup/index.js index a2957253..d4cbdcc5 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -36065,6 +36065,9 @@ function _unique(values) { /******/ __nccwpck_require__.m = __webpack_modules__; /******/ /************************************************************************/ +/******/ /* webpack/runtime/asset-relocator-loader */ +/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = decodeURIComponent(new URL('.', import.meta.url).pathname).slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/"; +/******/ /******/ /* webpack/runtime/compat get default export */ /******/ (() => { /******/ // getDefaultExport function for compatibility with non-harmony modules @@ -36157,10 +36160,6 @@ function _unique(values) { /******/ }; /******/ })(); /******/ -/******/ /* webpack/runtime/compat */ -/******/ -/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/"; -/******/ /******/ /* webpack/runtime/import chunk loading */ /******/ (() => { /******/ // no baseURI diff --git a/package-lock.json b/package-lock.json index 6cf18f9a..a4ac7141 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,21 +16,21 @@ "@actions/http-client": "^4.0.1", "@actions/io": "^3.0.2", "@actions/tool-cache": "^4.0.0", - "fast-xml-parser": "^5.10.1", + "fast-xml-parser": "^5.11.0", "semver": "^7.8.5" }, "devDependencies": { "@eslint/js": "^10.0.1", "@jest/globals": "^30.4.1", - "@types/node": "^26.1.1", + "@types/node": "^26.2.0", "@types/semver": "^7.8.0", "@typescript-eslint/eslint-plugin": "^8.67.0", "@typescript-eslint/parser": "^8.65.0", - "@vercel/ncc": "^0.44.0", + "@vercel/ncc": "^0.45.0", "eslint": "^10.7.0", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jest": "^29.15.4", - "eslint-plugin-n": "^18.2.2", + "eslint-plugin-jest": "^29.16.1", + "eslint-plugin-n": "^18.3.0", "globals": "^17.9.0", "husky": "^9.1.7", "jest": "^30.4.2", @@ -1726,9 +1726,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", - "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", + "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", "dev": true, "license": "MIT", "dependencies": { @@ -2364,9 +2364,9 @@ ] }, "node_modules/@vercel/ncc": { - "version": "0.44.1", - "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.44.1.tgz", - "integrity": "sha512-cUjIE5P2YY1n+Kt9rFIazMMpGoPn1Fic04rOmTkElMkiDP5oszGfERMpo2shVkFKDL7rVppdM2pqJKC59shQWQ==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.45.0.tgz", + "integrity": "sha512-8zPi1yO2mHpoKTD+e+Bf0ZT3e+sWHSOyGapm9s7b5R0gxJi3CiFTqmeQiMEyu6ejrz2s09M8JkEoUaTWjBJPQQ==", "dev": true, "license": "MIT", "bin": { @@ -3123,9 +3123,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "29.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.16.0.tgz", - "integrity": "sha512-0WFBxDHlT2ratGQfnFQEVIsgQJ5cfd+0IV8Kc6U3X2onB8ATLG23voD2Ch5G9fCkEpCPmCMuzW0tbS0kYb8biw==", + "version": "29.16.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.16.1.tgz", + "integrity": "sha512-tfxOIsjzaBud+f74aLbBMRcnrztt5eCIgnAdeoGdnzMAQ4IdAa/s/p8Ls55mk9MC79N3j2jbv4Qetz6Hclbcfw==", "dev": true, "license": "MIT", "dependencies": { @@ -3153,9 +3153,9 @@ } }, "node_modules/eslint-plugin-n": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-18.2.2.tgz", - "integrity": "sha512-gOO0lIqwEjZ750kv9/SptCWArUoAZXJoBr0vYWTO2dCBxctHUXlBIigiC8xuxxr/NKqgIT6Ehz1xRcilj8a5cA==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-18.3.0.tgz", + "integrity": "sha512-cPVguuDe6DrIPb/qUXHf8P89MaVTUmiYWwpt5gX5AILsvRIiZAxMFXcFR6QHYBksqKJpjfUBlL/RleCJUWcD7w==", "dev": true, "license": "MIT", "dependencies": { @@ -3463,9 +3463,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", - "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.0.tgz", + "integrity": "sha512-9IGxMqvqLOnqP+Egi1nqDHKv5k8aZ7r9n558enxcucmyVGEBNPAU+MOg/8jPIS7rO7sSq4gFm1/nHtiaubMruw==", "funding": [ { "type": "github", @@ -3478,7 +3478,7 @@ "fast-xml-builder": "^1.2.0", "is-unsafe": "^2.0.0", "path-expression-matcher": "^1.6.2", - "strnum": "^2.4.1", + "strnum": "^2.4.2", "xml-naming": "^0.3.0" }, "bin": { @@ -5530,9 +5530,9 @@ } }, "node_modules/strnum": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", - "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.2.tgz", + "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==", "funding": [ { "type": "github", diff --git a/package.json b/package.json index 26be2e0e..29069ace 100644 --- a/package.json +++ b/package.json @@ -49,21 +49,21 @@ "@actions/http-client": "^4.0.1", "@actions/io": "^3.0.2", "@actions/tool-cache": "^4.0.0", - "fast-xml-parser": "^5.10.1", + "fast-xml-parser": "^5.11.0", "semver": "^7.8.5" }, "devDependencies": { "@eslint/js": "^10.0.1", "@jest/globals": "^30.4.1", - "@types/node": "^26.1.1", + "@types/node": "^26.2.0", "@types/semver": "^7.8.0", "@typescript-eslint/eslint-plugin": "^8.67.0", "@typescript-eslint/parser": "^8.65.0", - "@vercel/ncc": "^0.44.0", + "@vercel/ncc": "^0.45.0", "eslint": "^10.7.0", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jest": "^29.15.4", - "eslint-plugin-n": "^18.2.2", + "eslint-plugin-jest": "^29.16.1", + "eslint-plugin-n": "^18.3.0", "globals": "^17.9.0", "husky": "^9.1.7", "jest": "^30.4.2",