From 9181922129ae06cfaa097bbebda9ff1ae51298a1 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 18:39:18 +0100 Subject: [PATCH 1/3] build(deps): adopt @exadev/eslint-config and bump to npm 12.0.2 Adds @exadev/eslint-config as an exact-pinned dev dependency, and bumps the pinned package manager to npm 12.0.2. Also approves @anthropic-ai/claude-code's postinstall script, which npm 12's lifecycle-script gate now blocks by default -- the dependabot investigate job invokes this CLI directly via npx, so the script genuinely needs to run. --- .npmrc | 1 + package-lock.json | 695 ++++++++++++++++++ package.json | 6 +- ...mat-changelog.mjs => format-changelog.mts} | 0 ...{move-major-tag.mjs => move-major-tag.mts} | 0 5 files changed, 701 insertions(+), 1 deletion(-) create mode 100644 .npmrc rename scripts/{format-changelog.mjs => format-changelog.mts} (100%) rename scripts/{move-major-tag.mjs => move-major-tag.mts} (100%) diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..cffe8cd --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/package-lock.json b/package-lock.json index 5224a1c..96a4a2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@commitlint/config-conventional": "21.2.2", "@eslint/json": "2.1.0", "@eslint/markdown": "8.0.3", + "@exadev/eslint-config": "2.12.3", "@semantic-release/changelog": "7.0.0", "@semantic-release/git": "11.0.1", "@types/node": "26.4.1", @@ -28,6 +29,9 @@ "turbo": "2.10.12", "typescript": "6.0.3", "typescript-eslint": "8.69.0" + }, + "engines": { + "node": ">=22.22.1 <23.0.0 || >=23.6.0" } }, "node_modules/@actions/core": { @@ -551,6 +555,33 @@ "node": ">=22" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.97.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.97.0.tgz", + "integrity": "sha512-EP8uoFfh6+GsdGCduYtmWAW0h7AO+Ayik9Vh5YbA2r/3N6lmJKkCNZX+q3QBXC1K6ixjQ/9igF2b7WVvLm063g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.9", + "@typescript-eslint/types": "^8.69.0", + "comment-parser": "1.4.8", + "esquery": "^1.7.0", + "jsdoc-type-pratt-parser": "~9.2.1" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + } + }, + "node_modules/@es-joy/resolve.exports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", + "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", @@ -634,6 +665,27 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, "node_modules/@eslint/json": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@eslint/json/-/json-2.1.0.tgz", @@ -700,6 +752,187 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@exadev/eslint-config": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/@exadev/eslint-config/-/eslint-config-2.12.3.tgz", + "integrity": "sha512-gs6yKJhngCnC8AJtthvr8Tq0pNzmNzIKnFEJLaoh24sw8PK0WntpLUB+0Fxv53Soos4Ju0pSBNCufmYljGwaVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint/js": "^10.0.1", + "@typescript-eslint/utils": "8.67.0", + "eslint-plugin-jsdoc": "64.3.6", + "eslint-plugin-tsdoc": "^0.5.2", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@next/eslint-plugin-next": "^16.3.2", + "eslint": ">=10.0.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.1.1", + "typescript": ">=4.8.4", + "typescript-eslint": ">=8.0.0" + }, + "peerDependenciesMeta": { + "@next/eslint-plugin-next": { + "optional": true + }, + "eslint-plugin-jsx-a11y": { + "optional": true + }, + "eslint-plugin-react": { + "optional": true + }, + "eslint-plugin-react-hooks": { + "optional": true + } + } + }, + "node_modules/@exadev/eslint-config/node_modules/@typescript-eslint/project-service": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@exadev/eslint-config/node_modules/@typescript-eslint/scope-manager": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@exadev/eslint-config/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@exadev/eslint-config/node_modules/@typescript-eslint/types": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@exadev/eslint-config/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@exadev/eslint-config/node_modules/@typescript-eslint/utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@exadev/eslint-config/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -776,6 +1009,43 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz", + "integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "ajv": "~8.18.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@octokit/auth-token": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", @@ -1500,6 +1770,19 @@ "url": "https://ko-fi.com/dangreen" } }, + "node_modules/@sindresorhus/base62": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz", + "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -2047,6 +2330,16 @@ "dev": true, "license": "MIT" }, + "node_modules/are-docs-informative": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.1.1.tgz", + "integrity": "sha512-sqRsNQBwbKLRX0jV5Cu5uzmtflf892n4Vukz7T659ebL4pz3mpOqCMU7lxMoBTFwnp10E3YB5ZcyHM41W5bcDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2418,6 +2711,16 @@ "node": ">= 12" } }, + "node_modules/comment-parser": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.8.tgz", + "integrity": "sha512-rKZTGo4fzKYna8UcL0isTg5wkBNla7bxTypLwZQXjIdi++IdP1OJ41rI5Mti3/jltkPujbu4i9LIARYA+zpotQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -2948,6 +3251,16 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-toolkit": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", @@ -3059,6 +3372,47 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "64.3.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-64.3.6.tgz", + "integrity": "sha512-lo7IXmgUUNy88SxW7KnJmmD2iPQBIRMomfCFPHidW1M3zNNVuzxlB2uoNrNyE1g4v2/L+RtYmiROPwQhSNzL8Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.97.0", + "@es-joy/resolve.exports": "1.2.0", + "@typescript-eslint/utils": "^8.69.0", + "are-docs-informative": "^0.1.1", + "comment-parser": "1.4.8", + "debug": "^4.4.3", + "escape-string-regexp": "^5.0.0", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "html-entities": "^2.6.0", + "object-deep-merge": "^2.0.1", + "parse-imports-exports": "^0.2.4", + "semver": "^7.8.5", + "spdx-expression-parse": "^5.0.0", + "to-valid-identifier": "^1.0.0" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/spdx-expression-parse": { + "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": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/eslint-plugin-prettier": { "version": "5.5.6", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz", @@ -3090,6 +3444,174 @@ } } }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.5.2.tgz", + "integrity": "sha512-BlvqjWZdBJDIPO/YU3zcPCF23CvjYT3gyu63yo6b609NNV3D1b6zceAREy2xnweuBoDpZcLNuPyAUq9cvx6bbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "@microsoft/tsdoc-config": "0.18.1", + "@typescript-eslint/utils": "~8.56.0" + } + }, + "node_modules/eslint-plugin-tsdoc/node_modules/@typescript-eslint/project-service": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-plugin-tsdoc/node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-tsdoc/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-plugin-tsdoc/node_modules/@typescript-eslint/types": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-tsdoc/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-plugin-tsdoc/node_modules/@typescript-eslint/utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-plugin-tsdoc/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-tsdoc/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/eslint-plugin-yml": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/eslint-plugin-yml/-/eslint-plugin-yml-3.8.1.tgz", @@ -3486,6 +4008,16 @@ "node": ">=14.14" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/function-timeout": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", @@ -3629,6 +4161,19 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -3665,6 +4210,23 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, "node_modules/http-proxy-agent": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz", @@ -3843,6 +4405,22 @@ "dev": true, "license": "MIT" }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3986,6 +4564,13 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4016,6 +4601,20 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-9.2.1.tgz", + "integrity": "sha512-V4Ww4EHnTcTLSOMoB0FsF72JhQvcAsriCm/LWnxJeGWoxIjEL2l9na11abQok5SYShq8m0Gl02el/xAbTCulvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.9", + "@types/node": "^26.4.0" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -7329,6 +7928,13 @@ "node": ">=0.10.0" } }, + "node_modules/object-deep-merge": { + "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" + }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -7502,6 +8108,16 @@ "node": ">=6" } }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -7534,6 +8150,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, "node_modules/parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", @@ -7578,6 +8201,13 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -7946,6 +8576,41 @@ "node": ">=0.10.0" } }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -8457,6 +9122,19 @@ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/synckit": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", @@ -8631,6 +9309,23 @@ "node": ">=8.0" } }, + "node_modules/to-valid-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", + "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/base62": "^1.0.0", + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/traverse": { "version": "0.6.8", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", diff --git a/package.json b/package.json index 23c86e1..dbf56ba 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0-managed-by-semantic-release", "private": true, "type": "module", - "packageManager": "npm@11.19.0", + "packageManager": "npm@12.0.2+sha512.b885e890b9418fa1693544d05f53e64f9a73ec194837d4258b15fecdd692347b1dd2a517b1b0cbaf9d31cd8e92c3b70956bd2ecc72833a57b4b3098f5bfa7943", "engines": { "node": ">=22.22.1 <23.0.0 || >=23.6.0" }, @@ -22,6 +22,7 @@ "@commitlint/config-conventional": "21.2.2", "@eslint/json": "2.1.0", "@eslint/markdown": "8.0.3", + "@exadev/eslint-config": "2.12.3", "@semantic-release/changelog": "7.0.0", "@semantic-release/git": "11.0.1", "@types/node": "26.4.1", @@ -37,5 +38,8 @@ "turbo": "2.10.12", "typescript": "6.0.3", "typescript-eslint": "8.69.0" + }, + "allowScripts": { + "@anthropic-ai/claude-code@2.1.259": true } } diff --git a/scripts/format-changelog.mjs b/scripts/format-changelog.mts similarity index 100% rename from scripts/format-changelog.mjs rename to scripts/format-changelog.mts diff --git a/scripts/move-major-tag.mjs b/scripts/move-major-tag.mts similarity index 100% rename from scripts/move-major-tag.mjs rename to scripts/move-major-tag.mts From a7d50161ea60f0ebc0f2858faa59b42e67fd16eb Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 18:39:43 +0100 Subject: [PATCH 2/3] fix: convert semantic-release plugin scripts to TypeScript Adopts @exadev/eslint-config's type-checked ruleset (replacing the previous tseslint.configs.recommended, which carried no type information at all) and fixes the resulting fallout in the two semantic-release plugin scripts it referenced by path, format-changelog.mjs and move-major-tag.mjs: both had no type annotations, and eslint's type-checked rules crashed outright on a plain .mjs file with no parserOptions/project configured for it. Converts both to real TypeScript (.mts), typed against semantic-release's own exported PrepareContext/SuccessContext, and adds scripts/**/*.mts to tsconfig.json's include so they get real type-checking. Both types' logger field resolves unsafely, since the signale package they depend on ships no type declarations of its own and skipLibCheck hides the resulting gap from tsc -- overridden with a narrow, honest local type for just the log method each script actually calls, rather than accepting the effectively-any upstream type. Also fixes a genuine noUncheckedIndexedAccess gap in move-major-tag.mts (nextRelease.version.split(".")[0] is string | undefined) with an explicit guard, and drops the unneeded async from a function that never awaits. Updates every reference to the old .mjs paths (release.config.ts, the CI workflow, README.md). --- .github/workflows/ci.yml | 4 ++-- README.md | 4 ++-- eslint.config.ts | 12 +++++++++--- release.config.ts | 6 +++--- scripts/format-changelog.mts | 17 +++++++++++++---- scripts/move-major-tag.mts | 21 ++++++++++++++++++--- tsconfig.json | 2 +- 7 files changed, 48 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9d9d2d..75a2049 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,7 +114,7 @@ jobs: fi done - # Computes the next version from commits since the last tag, generates release notes and a CHANGELOG.md entry, creates the GitHub Release and version tag, commits CHANGELOG.md back to main, and (via the move-major-tag plugin in release.config.ts) moves the moving vN tag to point at the new release -- see release.config.ts and scripts/move-major-tag.mjs. There is no npm-publish step and therefore no id-token: write permission -- this repository ships a GitHub Action, not an npm package. + # Computes the next version from commits since the last tag, generates release notes and a CHANGELOG.md entry, creates the GitHub Release and version tag, commits CHANGELOG.md back to main, and (via the move-major-tag plugin in release.config.ts) moves the moving vN tag to point at the new release -- see release.config.ts and scripts/move-major-tag.mts. There is no npm-publish step and therefore no id-token: write permission -- this repository ships a GitHub Action, not an npm package. release: name: Release needs: [required-checks] @@ -138,7 +138,7 @@ jobs: with: # semantic-release analyses the full commit history since the last tag. fetch-depth: 0 - # actions/checkout wires this into git's own credential config, so every subsequent plain `git push` in this job (the version tag, the CHANGELOG.md commit, and move-major-tag.mjs's tag move) authenticates as the App, not just this one checkout. + # actions/checkout wires this into git's own credential config, so every subsequent plain `git push` in this job (the version tag, the CHANGELOG.md commit, and move-major-tag.mts's tag move) authenticates as the App, not just this one checkout. token: ${{ steps.app-token.outputs.token }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/README.md b/README.md index 7d9ae48..ef56fb9 100644 --- a/README.md +++ b/README.md @@ -388,7 +388,7 @@ These native-metadata capabilities widen triage's allowlist with `Bash(gh api:*) ## Versioning -Releases are automated by [`semantic-release`](https://semantic-release.gitbook.io/) (`release.config.ts`), triggered by the `release` job in `.github/workflows/ci.yml` on every push to `main` that passes `required-checks` — the single gate job aggregating commitlint, actionlint, typecheck, lint, and format, so adding, renaming, or splitting a check only ever means editing that one job's `needs:` list, never touching branch protection settings. It computes the next version from the conventional-commit types of the commits since the last release, writes `CHANGELOG.md`, creates the GitHub Release and the version tag, and moves the moving `v1` tag to it (`scripts/move-major-tag.mjs`, a local plugin — semantic-release has no concept of a moving major tag, since that's a GitHub-Actions-consumer convention, not a package-semver one). Consuming repositories reference `@v1`; a breaking change would ship as `v2` and require callers to opt in. +Releases are automated by [`semantic-release`](https://semantic-release.gitbook.io/) (`release.config.ts`), triggered by the `release` job in `.github/workflows/ci.yml` on every push to `main` that passes `required-checks` — the single gate job aggregating commitlint, actionlint, typecheck, lint, and format, so adding, renaming, or splitting a check only ever means editing that one job's `needs:` list, never touching branch protection settings. It computes the next version from the conventional-commit types of the commits since the last release, writes `CHANGELOG.md`, creates the GitHub Release and the version tag, and moves the moving `v1` tag to it (`scripts/move-major-tag.mts`, a local plugin — semantic-release has no concept of a moving major tag, since that's a GitHub-Actions-consumer convention, not a package-semver one). Consuming repositories reference `@v1`; a breaking change would ship as `v2` and require callers to opt in. **The commit type you use is the release decision — get it right.** `commitlint.config.ts` and `release.config.ts` share one list (`commitTypes` in `release.config.ts`) mapping each conventional-commit type to a release level: @@ -456,7 +456,7 @@ action.yml Composite action: input validation, prompt compos call, automatic fixes turbo.json Turborepo task cache config (lint/typecheck/format:check) commitlint.config.ts Conventional-commit enforcement (local hook + CI) -release.config.ts semantic-release config; scripts/move-major-tag.mjs is its +release.config.ts semantic-release config; scripts/move-major-tag.mts is its local plugin that moves the moving vN tag after each release lint-staged.config.js Deliberately .js, not .ts -- see its own header comment .github/dependabot.yml Dependabot config: daily npm + github-actions scan, 7-day cooldown diff --git a/eslint.config.ts b/eslint.config.ts index b8489d9..3812c6a 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1,5 +1,5 @@ import { defineConfig } from "eslint/config"; -import tseslint from "typescript-eslint"; +import exadev from "@exadev/eslint-config"; import json from "@eslint/json"; import markdown from "@eslint/markdown"; // Recommended config, not the plugin itself: this both enables the prettier/prettier rule (reports formatting differences as lint errors, reading prettier.config.ts the same way `prettier --check` does) and applies eslint-config-prettier, which turns off every ESLint stylistic rule that could otherwise disagree with Prettier. It carries no `files:` restriction of its own, so it applies to everything ESLint actually lints below — YAML is untouched here (no ESLint language plugin for it), so `npm run format:check` remains the one authoritative formatting check that also covers the workflow/action YAML files. @@ -7,9 +7,15 @@ import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended" export default defineConfig([ { - files: ["**/*.ts"], - extends: [tseslint.configs.recommended], + files: ["**/*.{ts,mts}"], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, }, + ...exadev, { files: ["**/*.json"], ignores: ["package-lock.json"], // generated, not hand-maintained diff --git a/release.config.ts b/release.config.ts index 0ac2855..f6cf3c9 100644 --- a/release.config.ts +++ b/release.config.ts @@ -24,7 +24,7 @@ export const commitTypes: readonly CommitType[] = [ ]; /** - * Runs on push to main. Analyses commits since the last v* tag, computes the next version, generates release notes and a CHANGELOG.md entry, creates the GitHub Release and the version tag, and commits CHANGELOG.md back to main. There is no npm-publish step: this repository ships a GitHub Action, not an npm package. The final plugin, ./scripts/move-major-tag.mjs, moves the moving `v1` tag to the new release afterwards, since semantic-release has no concept of that and consuming repositories already pin to `@v1`. + * Runs on push to main. Analyses commits since the last v* tag, computes the next version, generates release notes and a CHANGELOG.md entry, creates the GitHub Release and the version tag, and commits CHANGELOG.md back to main. There is no npm-publish step: this repository ships a GitHub Action, not an npm package. The final plugin, ./scripts/move-major-tag.mts, moves the moving `v1` tag to the new release afterwards, since semantic-release has no concept of that and consuming repositories already pin to `@v1`. */ const config: Options = { branches: ["main"], @@ -73,7 +73,7 @@ const config: Options = { ], "@semantic-release/changelog", // A plugin file, not an npm package: runs in the prepare step, which executes in plugin-list order, so placing it here -- after @semantic-release/changelog writes CHANGELOG.md and before @semantic-release/git commits it -- formats the generated file so it passes format:check. See the plugin's own docstring for why this is a local plugin rather than a flag on @semantic-release/changelog (which has none). - "./scripts/format-changelog.mjs", + "./scripts/format-changelog.mts", "@semantic-release/github", [ "@semantic-release/git", @@ -83,7 +83,7 @@ const config: Options = { }, ], // A plugin file, not an npm dependency: referenced by path so Options.plugins' own PluginSpec type (string | [string, T]) is satisfied without a cast. See the plugin's own docstring for why this exists as local code rather than a package. - "./scripts/move-major-tag.mjs", + "./scripts/move-major-tag.mts", ], }; diff --git a/scripts/format-changelog.mts b/scripts/format-changelog.mts index 2e43821..22fc4d7 100644 --- a/scripts/format-changelog.mts +++ b/scripts/format-changelog.mts @@ -1,19 +1,28 @@ import { readFile, writeFile } from "node:fs/promises"; import * as prettier from "prettier"; import { join } from "node:path"; +import type { PrepareContext } from "semantic-release"; + +/** semantic-release's own PrepareContext types `logger` as `Signale<...>`, but the `signale` package ships no type declarations of its own (no `main`/`types` entry, no separate `@types/signale`) -- `skipLibCheck` hides the resulting unresolved type from `tsc`, but it still surfaces as an unsafe `any` wherever `context.logger` is actually used. Overriding just that one field with the narrow, honest shape this file actually calls avoids relying on the broken upstream type at all. */ +type TypedPrepareContext = Omit & { + readonly logger: { readonly log: (message: string) => void }; +}; /** - * semantic-release plugin, referenced by path from release.config.ts. Runs Prettier over CHANGELOG.md after @semantic-release/changelog has generated it and before @semantic-release/git commits it, so the committed changelog always satisfies this repository's format:check job. + * semantic-release plugin, referenced by path from release.config.ts. Runs Prettier over CHANGELOG.md after `@semantic-release/changelog` has generated it and before `@semantic-release/git` commits it, so the committed changelog always satisfies this repository's format:check job. * * Without this, conventional-changelog-writer's output (asterisk bullets, double blank lines between commit groups) fails `prettier --check CHANGELOG.md`, and since the release commit is `[skip ci]` the failure surfaces only on the *next* push to main — by which point a broken CHANGELOG is already on the branch and blocking the subsequent release. Formatting at generation time is the root-cause fix; excluding CHANGELOG.md from Prettier would only hide the mismatch. * - * Runs in the `prepare` step, so it must be listed after @semantic-release/changelog (which writes the file) and before @semantic-release/git (which commits it) in release.config.ts's plugin order. + * Runs in the `prepare` step, so it must be listed after `@semantic-release/changelog` (which writes the file) and before `@semantic-release/git` (which commits it) in release.config.ts's plugin order. * * Uses Prettier's programmatic API with the `filepath` option so it resolves and applies prettier.config.ts exactly as the CLI `prettier --write` would, including the double-quote setting. */ -export async function prepare(_pluginConfig, context) { +export async function prepare( + _pluginConfig: unknown, + context: TypedPrepareContext, +): Promise { const { cwd, logger } = context; - const filePath = join(cwd, "CHANGELOG.md"); + const filePath = join(cwd ?? process.cwd(), "CHANGELOG.md"); const text = await readFile(filePath, "utf8"); const formatted = await prettier.format(text, { filepath: "CHANGELOG.md" }); await writeFile(filePath, formatted); diff --git a/scripts/move-major-tag.mts b/scripts/move-major-tag.mts index 88ea2a8..acba83a 100644 --- a/scripts/move-major-tag.mts +++ b/scripts/move-major-tag.mts @@ -1,15 +1,30 @@ import { execFileSync } from "node:child_process"; +import type { SuccessContext } from "semantic-release"; + +/** semantic-release's own SuccessContext types `logger` as `Signale<...>`, but the `signale` package ships no type declarations of its own (no `main`/`types` entry, no separate `@types/signale`) -- `skipLibCheck` hides the resulting unresolved type from `tsc`, but it still surfaces as an unsafe `any` wherever `context.logger` is actually used. Overriding just that one field with the narrow, honest shape this file actually calls avoids relying on the broken upstream type at all. */ +type TypedSuccessContext = Omit & { + readonly logger: { readonly log: (message: string) => void }; +}; /** * semantic-release plugin, referenced by path from release.config.ts. Moves the moving major-version tag (v1, v2, ...) to the commit semantic-release just tagged. Consuming repositories pin `@v1` rather than an exact `v1.x.y`, so every release within v1 needs this. No official or third-party semantic-release plugin does this — it is specific to a GitHub-Actions-consumer convention, not the npm/package-semver versioning semantic-release itself models — so it is a small local plugin rather than a dependency. * * Runs in the `success` step, after every publish plugin has completed, so `nextRelease.gitTag` is already created and pushed by the time this runs. */ -export async function success(_pluginConfig, context) { +export function success( + _pluginConfig: unknown, + context: TypedSuccessContext, +): void { const { nextRelease, logger, cwd, env } = context; - const major = `v${nextRelease.version.split(".")[0]}`; + const majorVersionSegment = nextRelease.version.split(".")[0]; + if (majorVersionSegment === undefined) { + throw new Error( + `Could not parse a major version from "${nextRelease.version}".`, + ); + } + const major = `v${majorVersionSegment}`; - const run = (...args) => + const run = (...args: readonly string[]): Buffer => execFileSync("git", args, { cwd, env, stdio: "inherit" }); // -c user.name/user.email rather than relying on the checkout having git identity configured: creating an annotated tag object requires one, and a fresh CI checkout has none by default. Scoped to this one command rather than writing global git config. diff --git a/tsconfig.json b/tsconfig.json index 817453b..8539e07 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,5 +14,5 @@ "forceConsistentCasingInFileNames": true, "types": ["node"] }, - "include": ["*.ts"] + "include": ["*.ts", "scripts/**/*.mts"] } From 32e169eb08eaa3fe27129e2c9a57eedc1a78df7e Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Mon, 14 Sep 2026 18:40:22 +0100 Subject: [PATCH 3/3] fix: cover .mts files in the pre-commit lint-staged glob lint-staged.config.ts's "*.{ts,md}" pattern never matched a .mts file, so the two semantic-release plugin scripts just converted from .mjs would have silently bypassed pre-commit eslint/prettier on every future edit. Widens the pattern to "*.{ts,mts,md}". --- lint-staged.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lint-staged.config.ts b/lint-staged.config.ts index 9b1ee8f..104c0f5 100644 --- a/lint-staged.config.ts +++ b/lint-staged.config.ts @@ -4,7 +4,7 @@ import { defineConfig } from "lint-staged/config"; export default defineConfig({ - "*.{ts,md}": [ + "*.{ts,mts,md}": [ "eslint --fix --cache --cache-location node_modules/.cache/eslint/.eslintcache", "prettier --write --cache", ],