From 49bddfe1f98df992190f22c8e7313d83be8c6ef6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:34:58 +0000 Subject: [PATCH 01/10] build(deps-dev): Bump @feedic/eslint-config from 0.4.1 to 0.5.0 Bumps [@feedic/eslint-config](https://github.com/fb55/eslint-config) from 0.4.1 to 0.5.0. - [Commits](https://github.com/fb55/eslint-config/compare/v0.4.1...v0.5.0) --- updated-dependencies: - dependency-name: "@feedic/eslint-config" dependency-version: 0.5.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 396 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 199 insertions(+), 199 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a9f4c81..9cf08bbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "devDependencies": { "@biomejs/biome": "^2.5.11", "@eslint/compat": "^2.1.0", - "@feedic/eslint-config": "^0.4.1", + "@feedic/eslint-config": "^0.5.0", "@types/node": "^26.4.0", "eslint": "^10.9.1", "eslint-config-biome": "^2.1.3", @@ -34,16 +34,16 @@ } }, "node_modules/@altano/repository-tools": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@altano/repository-tools/-/repository-tools-2.0.3.tgz", - "integrity": "sha512-cSR/ZYDF6Wp9OeAJMyLYYN1GenAAhV17W+w38ELP+3c5Ltsy9jkkCymi33nz/qnXyef3n6Fbr1h2yt3dvUN5sQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@altano/repository-tools/-/repository-tools-2.1.0.tgz", + "integrity": "sha512-RGM8IimoK/RDlj+uG9sPgb9CSuHIAMVrtVVeslEMhc7yqDHpdmdKeMQcJfc0WveTtAmBdj/o9r1VW/A27SoPTg==", "dev": true, "license": "ISC" }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -226,17 +226,17 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.86.0.tgz", - "integrity": "sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==", + "version": "0.91.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.91.0.tgz", + "integrity": "sha512-vgqlMGNNhZxwDYbUNIHj3Hskb4R28iqdXx90ufHyt/NeuTQkeqjTDslAs9I0/GCAfbxP5BpH5WsL1R1fht5Lxg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "^1.0.8", - "@typescript-eslint/types": "^8.58.0", - "comment-parser": "1.4.6", + "@types/estree": "^1.0.9", + "@typescript-eslint/types": "^8.65.0", + "comment-parser": "1.4.7", "esquery": "^1.7.0", - "jsdoc-type-pratt-parser": "~7.2.0" + "jsdoc-type-pratt-parser": "~8.0.0" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -381,18 +381,18 @@ } }, "node_modules/@feedic/eslint-config": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@feedic/eslint-config/-/eslint-config-0.4.1.tgz", - "integrity": "sha512-F6zETaaeGbsbHy8mqfQgVoNVIkneqd2NAtQkl4PSajVRcwTQ4D+UqrKVlF6yUpn6G0wudjS5gp5WloW+6hig/A==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@feedic/eslint-config/-/eslint-config-0.5.0.tgz", + "integrity": "sha512-j3rj0vDAGJ/FpgYKiHgrknWx2vEzdIvTcE6/jCWsYmCI8YV6Mpkn9gzpmkBsomQ4XgiDXD6nQeaz0m83I9oGog==", "dev": true, "license": "MIT", "dependencies": { "@eslint/js": "^10.0.1", - "eslint-plugin-jsdoc": "^62.9.0", - "eslint-plugin-n": "^17.24.0", - "eslint-plugin-package-json": "^0.91.2", - "eslint-plugin-unicorn": "^64.0.0", - "globals": "^17.6.0" + "eslint-plugin-jsdoc": "^63.0.7", + "eslint-plugin-n": "^18.1.0", + "eslint-plugin-package-json": "^1.5.0", + "eslint-plugin-unicorn": "^68.0.0", + "globals": "^17.7.0" }, "engines": { "node": ">=20.19.0" @@ -880,9 +880,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -1341,9 +1341,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.27", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.27.tgz", - "integrity": "sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==", + "version": "2.11.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz", + "integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1367,9 +1367,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", "dev": true, "funding": [ { @@ -1387,11 +1387,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" }, "bin": { "browserslist": "cli.js" @@ -1401,9 +1401,9 @@ } }, "node_modules/builtin-modules": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.1.0.tgz", - "integrity": "sha512-c5JxaDrzwRjq3WyJkI1AGR5xy6Gr6udlt7sQPbl09+3ckB+Zo2qqQ2KhCTBr7Q8dHB43bENGYEk4xddrFH/b7A==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.3.0.tgz", + "integrity": "sha512-hMQUl2bUFG339QygPM97E+mc8OY1IAchORZxm4a/frcYwKzozMzRVDBwHW0NjOqGElLm2O37AVQE8ikxlZHrMQ==", "dev": true, "license": "MIT", "engines": { @@ -1414,9 +1414,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001791", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", - "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -1467,33 +1467,10 @@ "node": ">=8" } }, - "node_modules/clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/comment-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.6.tgz", - "integrity": "sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.7.tgz", + "integrity": "sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==", "dev": true, "license": "MIT", "engines": { @@ -1508,13 +1485,16 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.50.0.tgz", + "integrity": "sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.28.1" + "browserslist": "^4.28.7" + }, + "engines": { + "node": ">=6.4.0" }, "funding": { "type": "opencollective", @@ -1668,21 +1648,21 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.349", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.349.tgz", - "integrity": "sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==", + "version": "1.5.416", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.416.tgz", + "integrity": "sha512-K6bvB2BjnNrugtIih6ewlbBI9DXa976jIdiIlRLHhBoEI9a4JaQjjHyF+A1IQI543aQYR4LnmOrT/K5fZj0aPA==", "dev": true, "license": "ISC" }, "node_modules/enhanced-resolve": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", - "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -1814,9 +1794,9 @@ "license": "MIT" }, "node_modules/eslint-fix-utils": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/eslint-fix-utils/-/eslint-fix-utils-0.4.2.tgz", - "integrity": "sha512-n7ZTcwwkP5scedlhvWMcqxED+O1NzXcj5Rxn/0kJQMP88k02vRcBfQ1qsk/JHb6Aw8bajFoetFCCBiNIcNCsvA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eslint-fix-utils/-/eslint-fix-utils-0.4.3.tgz", + "integrity": "sha512-EKzNhsNavV5DdkHVltHBM9TqfsonCmiUhOm1Ra97zo03M9IAx3wtM1eC27eWGMj/D3LrALmHHNe1QlQH1P7Nxw==", "dev": true, "license": "MIT", "engines": { @@ -1832,6 +1812,28 @@ } } }, + "node_modules/eslint-json-compat-utils": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/eslint-json-compat-utils/-/eslint-json-compat-utils-0.2.3.tgz", + "integrity": "sha512-RbBmDFyu7FqnjE8F0ZxPNzx5UaptdeS9Uu50r7A+D7s/+FCX+ybiyViYEgFUaFIFqSWJgZRTpL5d8Kanxxl2lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esquery": "^1.6.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": "*", + "jsonc-eslint-parser": "^2.4.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@eslint/json": { + "optional": true + } + } + }, "node_modules/eslint-plugin-es-x": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", @@ -1855,38 +1857,38 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "62.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.9.0.tgz", - "integrity": "sha512-PY7/X4jrVgoIDncUmITlUqK546Ltmx/Pd4Hdsu4CvSjryQZJI2mEV4vrdMufyTetMiZ5taNSqvK//BTgVUlNkA==", + "version": "63.3.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.3.3.tgz", + "integrity": "sha512-xI4IeVRzRFA2DGHrPLIxF3U+oJHU3FE+P9Zb27fVs5dPHgfcpoAs0PyCbznVhK7pwR+9BPUztFeXSgpw/CL4Yg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@es-joy/jsdoccomment": "~0.86.0", + "@es-joy/jsdoccomment": "~0.91.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", - "comment-parser": "1.4.6", + "comment-parser": "1.4.7", "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", "espree": "^11.2.0", "esquery": "^1.7.0", "html-entities": "^2.6.0", - "object-deep-merge": "^2.0.0", + "object-deep-merge": "^2.0.1", "parse-imports-exports": "^0.2.4", - "semver": "^7.7.4", - "spdx-expression-parse": "^4.0.0", + "semver": "^7.8.5", + "spdx-expression-parse": "^5.0.0", "to-valid-identifier": "^1.0.0" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^22.13.0 || >=24" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-n": { - "version": "17.24.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.24.0.tgz", - "integrity": "sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==", + "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": { @@ -1897,17 +1899,26 @@ "globals": "^15.11.0", "globrex": "^0.1.2", "ignore": "^5.3.2", - "semver": "^7.6.3", - "ts-declaration-location": "^1.0.6" + "semver": "^7.6.3" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" }, "peerDependencies": { - "eslint": ">=8.23.0" + "eslint": ">=8.57.1", + "ts-declaration-location": "^1.0.6", + "typescript": ">=5.0.0" + }, + "peerDependenciesMeta": { + "ts-declaration-location": { + "optional": true + }, + "typescript": { + "optional": true + } } }, "node_modules/eslint-plugin-n/node_modules/globals": { @@ -1924,63 +1935,70 @@ } }, "node_modules/eslint-plugin-package-json": { - "version": "0.91.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-package-json/-/eslint-plugin-package-json-0.91.2.tgz", - "integrity": "sha512-tuPjHVYOjqEJtErmzuYiQY6o877l9Kb7+lfLhR/mAzHuy9CBqhRreIGPsQmVM/dMJ8yQVg92Bz1vBAgugELIXw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-package-json/-/eslint-plugin-package-json-1.8.0.tgz", + "integrity": "sha512-nfWWrak5t8yPgSVchNOHCjdFW/pl3+1135Bxsq0KZKo6PUdWWqUTqOVBrvzw1S+8BNOhhEx5c/YxeeCSYySFog==", "dev": true, "license": "MIT", "dependencies": { "@altano/repository-tools": "^2.0.1", + "@types/estree": "^1.0.0", "change-case": "^5.4.4", "detect-indent": "^7.0.2", "detect-newline": "^4.0.1", - "eslint-fix-utils": "~0.4.1", - "package-json-validator": "^1.4.1", + "eslint-fix-utils": "~0.4.3", + "eslint-json-compat-utils": "^0.2.3", + "jsonc-eslint-parser": "^3.1.0", + "package-json-validator": "^1.6.0", "semver": "^7.7.3", "sort-object-keys": "^2.0.0", - "sort-package-json": "^3.4.0", - "validate-npm-package-name": "^7.0.0" + "sort-package-json": "^4.0.0" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^22.22.2 || >=24.15.0" }, "peerDependencies": { - "eslint": ">=8.0.0", - "jsonc-eslint-parser": ">=2.0.0" + "@eslint/json": ">=1.0.0", + "eslint": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@eslint/json": { + "optional": true + } } }, "node_modules/eslint-plugin-unicorn": { - "version": "64.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-64.0.0.tgz", - "integrity": "sha512-rNZwalHh8i0UfPlhNwg5BTUO1CMdKNmjqe+TgzOTZnpKoi8VBgsW7u9qCHIdpxEzZ1uwrJrPF0uRb7l//K38gA==", + "version": "68.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-68.0.0.tgz", + "integrity": "sha512-mHYWvX948Q4H3bGc39bsNMxD/leOuNI+Iws9NVsoSz5VA7EGP86wnz7mZ/SPSvRhefT8L4hd8DHfDuGC+lIoCQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "@eslint-community/eslint-utils": "^4.9.1", + "browserslist": "^4.28.2", "change-case": "^5.4.4", "ci-info": "^4.4.0", - "clean-regexp": "^1.0.0", "core-js-compat": "^3.49.0", + "detect-indent": "^7.0.2", "find-up-simple": "^1.0.1", - "globals": "^17.4.0", + "globals": "^17.6.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.13.0", - "semver": "^7.7.4", + "regjsparser": "^0.13.1", + "semver": "^7.8.4", "strip-indent": "^4.1.1" }, "engines": { - "node": "^20.10.0 || >=21.0.0" + "node": ">=22" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.38.0" + "eslint": ">=10.4" } }, "node_modules/eslint-scope": { @@ -2200,9 +2218,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", "dev": true, "license": "MIT", "dependencies": { @@ -2236,9 +2254,9 @@ } }, "node_modules/globals": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", - "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz", + "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", "dev": true, "license": "MIT", "engines": { @@ -2408,9 +2426,9 @@ "license": "ISC" }, "node_modules/jsdoc-type-pratt-parser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.2.0.tgz", - "integrity": "sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-8.0.0.tgz", + "integrity": "sha512-uQu/fXVqVaMg6gM8/E5G5+eygVcZ1NV0Z51CvqhNa2bDWxvHMl484ETr6vph4oPyC+KUcbP/w2W2pewfCiR9aQ==", "dev": true, "license": "MIT", "engines": { @@ -2452,16 +2470,15 @@ "license": "MIT" }, "node_modules/jsonc-eslint-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-3.1.0.tgz", - "integrity": "sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-3.3.0.tgz", + "integrity": "sha512-hYTGkHGNRZnXOFZ1urhINADoqDrGfpy53cjw+dxk84QE0pUDujQzeUeamNs6Mz44/TKD49z2x6/GVSu4ZrtA+Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "acorn": "^8.5.0", "eslint-visitor-keys": "^5.0.0", - "semver": "^7.3.5" + "verkit": "^0.3.2" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -2804,9 +2821,9 @@ } }, "node_modules/lru-cache": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", - "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -2928,11 +2945,14 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", - "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/npm-package-arg": { "version": "13.0.2", @@ -2951,9 +2971,9 @@ } }, "node_modules/object-deep-merge": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.0.tgz", - "integrity": "sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.1.tgz", + "integrity": "sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==", "dev": true, "license": "MIT" }, @@ -3019,9 +3039,9 @@ } }, "node_modules/package-json-validator": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/package-json-validator/-/package-json-validator-1.5.1.tgz", - "integrity": "sha512-5WpI9I6pMy/ueq7rkDcJXfVjcHX6OBVTQk4Z7ObVZzsgPv/sX3kUfFFfvobErl/HpgleLcW4egvNypyBwGlkpw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/package-json-validator/-/package-json-validator-1.6.1.tgz", + "integrity": "sha512-eyl3DGFoxdUO0RA1Ym+H11S6P2XWIlA6Sy2PbvslbwFJ14kkz+FgX1Br3MaebrvLY3Q10Iih3PsL3741vsevrA==", "dev": true, "license": "MIT", "dependencies": { @@ -3177,20 +3197,10 @@ "node": ">=6" } }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, "node_modules/regjsparser": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", - "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", + "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3258,9 +3268,9 @@ } }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -3308,9 +3318,9 @@ "license": "MIT" }, "node_modules/sort-package-json": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.6.1.tgz", - "integrity": "sha512-Chgejw1+10p2D0U2tB7au1lHtz6TkFnxmvZktyBCRyV0GgmF6nl1IxXxAsPtJVsUyg/fo+BfCMAVVFUVRkAHrQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-4.0.0.tgz", + "integrity": "sha512-6aYOlYI9AWioZ+rzu+4zKLmoFqJP0/fHDxrd7X04yqEibikY+5YVF0EYlyGn4v6X2PJY7yAUWV7oeP+i5rOm/g==", "dev": true, "license": "MIT", "dependencies": { @@ -3326,7 +3336,7 @@ "sort-package-json": "cli.js" }, "engines": { - "node": ">=20" + "node": ">=22" } }, "node_modules/source-map-js": { @@ -3369,9 +3379,9 @@ "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", - "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-5.0.0.tgz", + "integrity": "sha512-vngmw3Rgn+o2arXNbnZaj5UtOEBuWBfvaI+Wc8GFfykIhA5/vdK9/Sp/XkLv63dykz2rxKDvKEHupF5P0FORcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3414,9 +3424,9 @@ } }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { @@ -3519,29 +3529,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/ts-declaration-location": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz", - "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==", - "dev": true, - "funding": [ - { - "type": "ko-fi", - "url": "https://ko-fi.com/rebeccastevens" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/ts-declaration-location" - } - ], - "license": "BSD-3-Clause", - "dependencies": { - "picomatch": "^4.0.2" - }, - "peerDependencies": { - "typescript": ">=4.0.0" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3642,9 +3629,9 @@ "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "dev": true, "funding": [ { @@ -3714,6 +3701,19 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/verkit": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.3.2.tgz", + "integrity": "sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, "node_modules/vite": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", diff --git a/package.json b/package.json index bc7f790f..2ab6cb4c 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "devDependencies": { "@biomejs/biome": "^2.5.11", "@eslint/compat": "^2.1.0", - "@feedic/eslint-config": "^0.4.1", + "@feedic/eslint-config": "^0.5.0", "@types/node": "^26.4.0", "eslint": "^10.9.1", "eslint-config-biome": "^2.1.3", From d38495b6cc5cf66c144a588e78c10ebd750d1999 Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:15:15 +0100 Subject: [PATCH 02/10] chore: adapt shared lint rules and require manual review --- .github/workflows/dependabot-automerge.yml | 9 ++-- eslint.config.mjs | 49 +++++++++++++++++++++- src/index.spec.ts | 5 ++- src/node.ts | 4 +- 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 629e86c2..3213a9db 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -18,10 +18,11 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs # Automatically merge semver-patch and semver-minor PRs - if: "${{ steps.metadata.outputs.update-type == - 'version-update:semver-minor' || - steps.metadata.outputs.update-type == - 'version-update:semver-patch' }}" + # Shared lint rule changes require manual review. + if: >- + ${{ !contains(steps.metadata.outputs.dependency-names, '@feedic/eslint-config') && + (steps.metadata.outputs.update-type == 'version-update:semver-minor' || + steps.metadata.outputs.update-type == 'version-update:semver-patch') }} run: gh pr merge --auto --squash "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} diff --git a/eslint.config.mjs b/eslint.config.mjs index 8ad4caa2..64258d58 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -44,5 +44,52 @@ export default defineConfig([ "n/no-unsupported-features/es-builtins": 0, }, }, - eslintConfigBiome + eslintConfigBiome, +// These nodes implement domhandler APIs, not the browser DOM. +{ + "files": [ + "**/*.ts" + ], + "rules": { + "unicorn/better-dom-traversing": "off" + } +}, +// Preserve class field initialization order and the public node layout. +{ + "files": [ + "src/index.ts", + "src/node.ts" + ], + "rules": { + "unicorn/consistent-class-member-order": "off" + } +}, +// Keep the public recursive clone parameter name. +{ + "files": [ + "src/node.ts" + ], + "rules": { + "unicorn/consistent-boolean-name": "off" + } +}, +// Fixture comparison checks dynamic keys, including inherited properties. +{ + "files": [ + "src/index.spec.ts" + ], + "rules": { + "unicorn/no-computed-property-existence-check": "off" + } +}, + +// Biome enforces the Number namespace for these constants. +{ + "files": [ + "**/*.ts" + ], + "rules": { + "unicorn/prefer-global-number-constants": "off" + } +}, ]); diff --git a/src/index.spec.ts b/src/index.spec.ts index f39f3d79..3b26972f 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -8,10 +8,11 @@ import Handler, { type DomHandlerOptions, type Node } from "./index.js"; const basePath = resolve(fileURLToPath(import.meta.url), "..", "__fixtures__"); describe("DomHandler", () => { - for (const { name, html, options = {}, expected } of readdirSync(basePath) + const fixtures = readdirSync(basePath) .filter((name) => name.endsWith(".json")) .map((name) => resolve(basePath, name)) - .map((path) => JSON.parse(readFileSync(path, "utf8")))) { + .map((path) => JSON.parse(readFileSync(path, "utf8"))); + for (const { name, html, options = {}, expected } of fixtures) { test(name, () => { const result = parse(html, options); diff --git a/src/node.ts b/src/node.ts index ccefb9c1..36bd1b64 100644 --- a/src/node.ts +++ b/src/node.ts @@ -335,9 +335,9 @@ export class Element extends NodeWithChildren { } get attributes(): Attribute[] { - return Object.keys(this.attribs).map((name) => ({ + return Object.entries(this.attribs).map(([name, value]) => ({ name, - value: this.attribs[name], + value, namespace: this["x-attribsNamespace"]?.[name], prefix: this["x-attribsPrefix"]?.[name], })); From 4d0b0414d1705d313c4ebcf759d53c6002a9bb90 Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:33:04 +0100 Subject: [PATCH 03/10] style: prefer surgical fixes for updated lint rules --- eslint.config.mjs | 68 +++++++++++++++-------------------------------- src/index.spec.ts | 2 +- src/node.ts | 18 ++++++------- 3 files changed, 32 insertions(+), 56 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 64258d58..5928d672 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -45,51 +45,27 @@ export default defineConfig([ }, }, eslintConfigBiome, -// These nodes implement domhandler APIs, not the browser DOM. -{ - "files": [ - "**/*.ts" - ], - "rules": { - "unicorn/better-dom-traversing": "off" - } -}, -// Preserve class field initialization order and the public node layout. -{ - "files": [ - "src/index.ts", - "src/node.ts" - ], - "rules": { - "unicorn/consistent-class-member-order": "off" - } -}, -// Keep the public recursive clone parameter name. -{ - "files": [ - "src/node.ts" - ], - "rules": { - "unicorn/consistent-boolean-name": "off" - } -}, -// Fixture comparison checks dynamic keys, including inherited properties. -{ - "files": [ - "src/index.spec.ts" - ], - "rules": { - "unicorn/no-computed-property-existence-check": "off" - } -}, + // Preserve the observable class field initialization order. + { + files: ["src/index.ts", "src/node.ts"], + rules: { + "unicorn/consistent-class-member-order": "off", + }, + }, -// Biome enforces the Number namespace for these constants. -{ - "files": [ - "**/*.ts" - ], - "rules": { - "unicorn/prefer-global-number-constants": "off" - } -}, + // These fixtures use domhandler nodes; browser querySelector and firstElementChild APIs do not apply. + { + files: ["src/node.spec.ts"], + rules: { + "unicorn/better-dom-traversing": "off", + }, + }, + + // Use the Number namespace required by the existing Biome configuration. + { + files: ["src/node.spec.ts"], + rules: { + "unicorn/prefer-global-number-constants": "off", + }, + }, ]); diff --git a/src/index.spec.ts b/src/index.spec.ts index 3b26972f..70892c92 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -60,7 +60,7 @@ function compare(actual: unknown, expected: unknown) { expect(actual).toBe(expected); } else { for (const property in expected) { - expect(property in actual).toBeTruthy(); + expect(Reflect.has(actual, property)).toBeTruthy(); compare(actual[property as never], expected[property as never]); } } diff --git a/src/node.ts b/src/node.ts index 36bd1b64..365fa76a 100644 --- a/src/node.ts +++ b/src/node.ts @@ -120,11 +120,11 @@ export abstract class Node { /** * Clone this node, and optionally its children. - * @param recursive Clone child nodes as well. + * @param isRecursive Clone child nodes as well. * @returns A clone of the node. */ - cloneNode(this: T, recursive = false): T { - return cloneNode(this, recursive); + cloneNode(this: T, isRecursive = false): T { + return cloneNode(this, isRecursive); } } @@ -218,7 +218,7 @@ export abstract class NodeWithChildren extends Node { // Aliases /** First child of the node. */ get firstChild(): ChildNode | null { - return this.children[0] ?? null; + return this.children.at(0) ?? null; } /** Last child of the node. */ @@ -417,10 +417,10 @@ export function hasChildren(node: Node): node is ParentNode { /** * Clone a node, and optionally its children. * @param node Node to clone. - * @param recursive Clone child nodes as well. + * @param isRecursive Clone child nodes as well. * @returns A clone of the node. */ -export function cloneNode(node: T, recursive = false): T { +export function cloneNode(node: T, isRecursive = false): T { let result: Node; if (isText(node)) { @@ -428,7 +428,7 @@ export function cloneNode(node: T, recursive = false): T { } else if (isComment(node)) { result = new Comment(node.data); } else if (isTag(node)) { - const children = recursive ? cloneChildren(node.children) : []; + const children = isRecursive ? cloneChildren(node.children) : []; const clone = new Element(node.name, { ...node.attribs }, children); for (const child of children) { child.parent = clone; @@ -446,14 +446,14 @@ export function cloneNode(node: T, recursive = false): T { result = clone; } else if (isCDATA(node)) { - const children = recursive ? cloneChildren(node.children) : []; + const children = isRecursive ? cloneChildren(node.children) : []; const clone = new CDATA(children); for (const child of children) { child.parent = clone; } result = clone; } else if (isDocument(node)) { - const children = recursive ? cloneChildren(node.children) : []; + const children = isRecursive ? cloneChildren(node.children) : []; const clone = new Document(children); for (const child of children) { child.parent = clone; From cd8278f96d0721f7c5b5d73d1b207d75ad17d04e Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Sat, 10 Oct 2026 01:00:30 +0100 Subject: [PATCH 04/10] ci: validate the supported development Node baseline --- .github/workflows/nodejs-test.yml | 7 ++++--- .node-version | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 .node-version diff --git a/.github/workflows/nodejs-test.yml b/.github/workflows/nodejs-test.yml index bb5e4cc4..a2c9e333 100644 --- a/.github/workflows/nodejs-test.yml +++ b/.github/workflows/nodejs-test.yml @@ -26,7 +26,7 @@ jobs: - run: npm run lint test: - name: Node ${{ matrix.node }} + name: Node ${{ matrix.node == '22.22.2' && '22' || matrix.node }} runs-on: ubuntu-latest strategy: @@ -34,8 +34,9 @@ jobs: matrix: node: - 20 - - 22 + - 22.22.2 # Minimum Node.js version for the development toolchain. - 24 + - 26 - lts/* steps: @@ -47,4 +48,4 @@ jobs: cache: npm - run: npm ci - run: npm run build --if-present - - run: npm run test:vi + - run: npm run ${{ matrix.node == '22.22.2' && 'test' || 'test:vi' }} diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..db49bb14 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +22.22.2 From b54e4111b987419d89fef7c29e5ccea433d3a9e1 Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Sat, 10 Oct 2026 08:49:56 +0100 Subject: [PATCH 05/10] chore: simplify CI versions and module lint exceptions --- .github/workflows/nodejs-test.yml | 6 +++--- .node-version | 2 +- eslint.config.mjs | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nodejs-test.yml b/.github/workflows/nodejs-test.yml index a2c9e333..79262bd9 100644 --- a/.github/workflows/nodejs-test.yml +++ b/.github/workflows/nodejs-test.yml @@ -26,7 +26,7 @@ jobs: - run: npm run lint test: - name: Node ${{ matrix.node == '22.22.2' && '22' || matrix.node }} + name: Node ${{ matrix.node }} runs-on: ubuntu-latest strategy: @@ -34,7 +34,7 @@ jobs: matrix: node: - 20 - - 22.22.2 # Minimum Node.js version for the development toolchain. + - 22 - 24 - 26 - lts/* @@ -48,4 +48,4 @@ jobs: cache: npm - run: npm ci - run: npm run build --if-present - - run: npm run ${{ matrix.node == '22.22.2' && 'test' || 'test:vi' }} + - run: npm run test:vi diff --git a/.node-version b/.node-version index db49bb14..2bd5a0a9 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -22.22.2 +22 diff --git a/eslint.config.mjs b/eslint.config.mjs index 5928d672..c79d2a0d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -53,9 +53,8 @@ export default defineConfig([ }, }, - // These fixtures use domhandler nodes; browser querySelector and firstElementChild APIs do not apply. + // This module uses parser DOM nodes, which do not implement browser traversal APIs. { - files: ["src/node.spec.ts"], rules: { "unicorn/better-dom-traversing": "off", }, From 25ba64c8e535c2138433ad6b128c6cfec0a7cbbc Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:25:48 +0100 Subject: [PATCH 06/10] ci: restore usual Dependabot auto-merge policy --- .github/workflows/dependabot-automerge.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 3213a9db..629e86c2 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -18,11 +18,10 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs # Automatically merge semver-patch and semver-minor PRs - # Shared lint rule changes require manual review. - if: >- - ${{ !contains(steps.metadata.outputs.dependency-names, '@feedic/eslint-config') && - (steps.metadata.outputs.update-type == 'version-update:semver-minor' || - steps.metadata.outputs.update-type == 'version-update:semver-patch') }} + if: "${{ steps.metadata.outputs.update-type == + 'version-update:semver-minor' || + steps.metadata.outputs.update-type == + 'version-update:semver-patch' }}" run: gh pr merge --auto --squash "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} From ebd5855fe862fb2a24605269cd37b114f3df70d8 Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:44:38 +0100 Subject: [PATCH 07/10] chore: address dependency PR review --- .node-version | 1 - eslint.config.mjs | 9 --------- src/index.ts | 18 +++++++++--------- src/node.ts | 48 +++++++++++++++++++++++------------------------ 4 files changed, 33 insertions(+), 43 deletions(-) delete mode 100644 .node-version diff --git a/.node-version b/.node-version deleted file mode 100644 index 2bd5a0a9..00000000 --- a/.node-version +++ /dev/null @@ -1 +0,0 @@ -22 diff --git a/eslint.config.mjs b/eslint.config.mjs index c79d2a0d..7739f845 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -45,14 +45,6 @@ export default defineConfig([ }, }, eslintConfigBiome, - // Preserve the observable class field initialization order. - { - files: ["src/index.ts", "src/node.ts"], - rules: { - "unicorn/consistent-class-member-order": "off", - }, - }, - // This module uses parser DOM nodes, which do not implement browser traversal APIs. { rules: { @@ -62,7 +54,6 @@ export default defineConfig([ // Use the Number namespace required by the existing Biome configuration. { - files: ["src/node.spec.ts"], rules: { "unicorn/prefer-global-number-constants": "off", }, diff --git a/src/index.ts b/src/index.ts index af79d3f0..527e2bfa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,12 +59,6 @@ type ElementCallback = (element: Element) => void; * Event-based handler that builds a DOM tree from parser callbacks. */ export class DomHandler { - /** The elements of the DOM */ - dom: ChildNode[] = []; - - /** The root element for the DOM */ - root: Document = new Document(this.dom); - /** Called once parsing has completed. */ private readonly callback: Callback | null; @@ -77,15 +71,21 @@ export class DomHandler { /** Indicated whether parsing has been completed. */ private done = false; + /** Reference to the parser instance. Used for location information. */ + private parser: ParserInterface | null = null; + + /** The elements of the DOM */ + dom: ChildNode[] = []; + + /** The root element for the DOM */ + root: Document = new Document(this.dom); + /** Stack of open tags. */ protected tagStack: ParentNode[] = [this.root]; /** A data node that is still being written to. */ protected lastNode: DataNode | null = null; - /** Reference to the parser instance. Used for location information. */ - private parser: ParserInterface | null = null; - /** * @param callback Called once parsing has completed. * @param options Settings for the handler. diff --git a/src/node.ts b/src/node.ts index 365fa76a..6dc6dd33 100644 --- a/src/node.ts +++ b/src/node.ts @@ -184,6 +184,13 @@ export class ProcessingInstruction extends DataNode { type: ElementType.Directive = ElementType.Directive; name: string; + /** If this is a doctype, the document type name (parse5 only). */ + "x-name"?: string; + /** If this is a doctype, the document type public identifier (parse5 only). */ + "x-publicId"?: string; + /** If this is a doctype, the document type system identifier (parse5 only). */ + "x-systemId"?: string; + constructor(name: string, data: string) { super(data); this.name = name; @@ -192,13 +199,6 @@ export class ProcessingInstruction extends DataNode { override get nodeType(): 1 { return 1; } - - /** If this is a doctype, the document type name (parse5 only). */ - "x-name"?: string; - /** If this is a doctype, the document type public identifier (parse5 only). */ - "x-publicId"?: string; - /** If this is a doctype, the document type system identifier (parse5 only). */ - "x-systemId"?: string; } /** @@ -258,12 +258,12 @@ export class CDATA extends NodeWithChildren { export class Document extends NodeWithChildren { type: ElementType.Root = ElementType.Root; + /** [Document mode](https://dom.spec.whatwg.org/#concept-document-limited-quirks) (parse5 only). */ + declare "x-mode"?: "no-quirks" | "quirks" | "limited-quirks"; + get nodeType(): 9 { return 9; } - - /** [Document mode](https://dom.spec.whatwg.org/#concept-document-limited-quirks) (parse5 only). */ - declare "x-mode"?: "no-quirks" | "quirks" | "limited-quirks"; } /** @@ -284,6 +284,20 @@ export class Element extends NodeWithChildren { attribs: { [name: string]: string }; type: ElementType.Tag | ElementType.Script | ElementType.Style; + /** + * `parse5` source code location info, with start & end tags. + * + * Available if parsing with parse5 and location info is enabled. + */ + declare sourceCodeLocation?: TagSourceCodeLocation | null; + + /** Element namespace (parse5 only). */ + namespace?: string; + /** Element attribute namespaces (parse5 only). */ + "x-attribsNamespace"?: Record; + /** Element attribute namespace-related prefixes (parse5 only). */ + "x-attribsPrefix"?: Record; + /** * @param name Name of the tag, eg. `div`, `span`. * @param attribs Object mapping attribute names to attribute values. @@ -313,13 +327,6 @@ export class Element extends NodeWithChildren { return 1; } - /** - * `parse5` source code location info, with start & end tags. - * - * Available if parsing with parse5 and location info is enabled. - */ - declare sourceCodeLocation?: TagSourceCodeLocation | null; - // DOM Level 1 aliases /** @@ -342,13 +349,6 @@ export class Element extends NodeWithChildren { prefix: this["x-attribsPrefix"]?.[name], })); } - - /** Element namespace (parse5 only). */ - namespace?: string; - /** Element attribute namespaces (parse5 only). */ - "x-attribsNamespace"?: Record; - /** Element attribute namespace-related prefixes (parse5 only). */ - "x-attribsPrefix"?: Record; } /** From a94b1354069a569e5c43001a070ff5a2897eb1ba Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Sun, 11 Oct 2026 01:09:21 +0100 Subject: [PATCH 08/10] chore: consolidate lint rule explanations --- eslint.config.mjs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 7739f845..6044b51c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -45,17 +45,12 @@ export default defineConfig([ }, }, eslintConfigBiome, - // This module uses parser DOM nodes, which do not implement browser traversal APIs. - { - rules: { - "unicorn/better-dom-traversing": "off", - }, - }, - - // Use the Number namespace required by the existing Biome configuration. - { - rules: { - "unicorn/prefer-global-number-constants": "off", - }, + { + rules: { + // This module uses parser DOM nodes, which do not implement browser traversal APIs. + "unicorn/better-dom-traversing": "off", + // Use the Number namespace required by the existing Biome configuration. + "unicorn/prefer-global-number-constants": "off", }, + }, ]); From 59c3d06b1cfaa58f0b220ced03a579879179d2fd Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Sun, 11 Oct 2026 01:18:48 +0100 Subject: [PATCH 09/10] refactor: clarify boolean and predicate names --- src/node.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/node.ts b/src/node.ts index 6dc6dd33..57669d37 100644 --- a/src/node.ts +++ b/src/node.ts @@ -120,11 +120,11 @@ export abstract class Node { /** * Clone this node, and optionally its children. - * @param isRecursive Clone child nodes as well. + * @param shouldRecurse Clone child nodes as well. * @returns A clone of the node. */ - cloneNode(this: T, isRecursive = false): T { - return cloneNode(this, isRecursive); + cloneNode(this: T, shouldRecurse = false): T { + return cloneNode(this, shouldRecurse); } } @@ -417,10 +417,10 @@ export function hasChildren(node: Node): node is ParentNode { /** * Clone a node, and optionally its children. * @param node Node to clone. - * @param isRecursive Clone child nodes as well. + * @param shouldRecurse Clone child nodes as well. * @returns A clone of the node. */ -export function cloneNode(node: T, isRecursive = false): T { +export function cloneNode(node: T, shouldRecurse = false): T { let result: Node; if (isText(node)) { @@ -428,7 +428,7 @@ export function cloneNode(node: T, isRecursive = false): T { } else if (isComment(node)) { result = new Comment(node.data); } else if (isTag(node)) { - const children = isRecursive ? cloneChildren(node.children) : []; + const children = shouldRecurse ? cloneChildren(node.children) : []; const clone = new Element(node.name, { ...node.attribs }, children); for (const child of children) { child.parent = clone; @@ -446,14 +446,14 @@ export function cloneNode(node: T, isRecursive = false): T { result = clone; } else if (isCDATA(node)) { - const children = isRecursive ? cloneChildren(node.children) : []; + const children = shouldRecurse ? cloneChildren(node.children) : []; const clone = new CDATA(children); for (const child of children) { child.parent = clone; } result = clone; } else if (isDocument(node)) { - const children = isRecursive ? cloneChildren(node.children) : []; + const children = shouldRecurse ? cloneChildren(node.children) : []; const clone = new Document(children); for (const child of children) { child.parent = clone; From a1481e1109773e98b8049588f82d7f449888e474 Mon Sep 17 00:00:00 2001 From: Felix Boehm <188768+fb55@users.noreply.github.com> Date: Sun, 11 Oct 2026 01:36:49 +0100 Subject: [PATCH 10/10] test: assert fixture properties with toHaveProperty --- src/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index 70892c92..b2b4084f 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -60,7 +60,7 @@ function compare(actual: unknown, expected: unknown) { expect(actual).toBe(expected); } else { for (const property in expected) { - expect(Reflect.has(actual, property)).toBeTruthy(); + expect(actual).toHaveProperty([property]); compare(actual[property as never], expected[property as never]); } }