From 6f8b97f7991cb13d16556681a7e3dbb6923e79e2 Mon Sep 17 00:00:00 2001 From: escapedcat Date: Tue, 30 Jun 2026 11:28:16 +0200 Subject: [PATCH 1/4] feat(resolve-extends): resolve pure-ESM parser presets Parser presets published as pure ESM expose an exports map with only the import condition (no require/default/main). The CommonJS preset resolver could not read these and threw "No exports main defined". Add an ESM-aware node_modules fallback that runs only after CommonJS resolution fails, plus the same fallback in the test bootstrap. Co-Authored-By: Claude Opus 4.8 (1M context) --- @commitlint/resolve-extends/src/index.test.ts | 77 +++++++++++++++- @commitlint/resolve-extends/src/index.ts | 88 +++++++++++++++++++ @packages/test/src/npm.ts | 34 +++++++ 3 files changed, 197 insertions(+), 2 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.test.ts b/@commitlint/resolve-extends/src/index.test.ts index 145f420b4c..e6fc679c71 100644 --- a/@commitlint/resolve-extends/src/index.test.ts +++ b/@commitlint/resolve-extends/src/index.test.ts @@ -1,8 +1,11 @@ -import { test, expect, vi } from "vitest"; +import { test, expect, vi, afterEach } from "vitest"; import { createRequire } from "node:module"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; import { RuleConfigSeverity, UserConfig } from "@commitlint/types"; -import resolveExtends, { ResolveExtendsContext } from "./index.js"; +import resolveExtends, { ResolveExtendsContext, resolveFrom } from "./index.js"; const require = createRequire(import.meta.url); @@ -628,3 +631,73 @@ test("should correctly merge nested configs", async () => { expect(actual).toEqual(expected); }); + +const scaffoldedRoots: string[] = []; + +afterEach(() => { + for (const root of scaffoldedRoots.splice(0)) { + fs.rmSync(root, { recursive: true, force: true }); + } +}); + +// Create a throwaway `/node_modules/` layout and return . +const scaffoldModule = ( + pkg: string, + manifest: Record, + files: Record, +): string => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "resolve-from-")); + scaffoldedRoots.push(root); + const pkgDir = path.join(root, "node_modules", pkg); + fs.mkdirSync(pkgDir, { recursive: true }); + fs.writeFileSync(path.join(pkgDir, "package.json"), JSON.stringify(manifest)); + for (const [relative, content] of Object.entries(files)) { + const file = path.join(pkgDir, relative); + fs.mkdirSync(path.dirname(file), { recursive: true }); + fs.writeFileSync(file, content); + } + return root; +}; + +// CommonJS resolvers cannot read an `exports` map that only declares the `import` +// condition (no require/default/main), so resolveFrom must fall back to reading the +// manifest itself. See conventional-changelog-angular@>=9 / conventionalcommits@>=10. +test("resolveFrom resolves a pure-ESM package exposing only the import condition", () => { + const root = scaffoldModule( + "esm-only-preset", + { + name: "esm-only-preset", + type: "module", + exports: { types: "./index.d.ts", import: "./index.js" }, + }, + { "index.js": "export default {};" }, + ); + + expect(resolveFrom("esm-only-preset", root)).toBe( + path.join(root, "node_modules", "esm-only-preset", "index.js"), + ); +}); + +test("resolveFrom resolves a subpath of a pure-ESM package", () => { + const root = scaffoldModule( + "esm-only-preset", + { name: "esm-only-preset", type: "module", exports: { import: "./index.js" } }, + { "index.js": "export default {};", "feature.js": "export default {};" }, + ); + + expect(resolveFrom("esm-only-preset/feature", root)).toBe( + path.join(root, "node_modules", "esm-only-preset", "feature.js"), + ); +}); + +test("resolveFrom falls back to main when the exports map has no runtime condition", () => { + const root = scaffoldModule( + "types-only-preset", + { name: "types-only-preset", exports: { types: "./index.d.ts" }, main: "./main.js" }, + { "main.js": "module.exports = {};" }, + ); + + expect(resolveFrom("types-only-preset", root)).toBe( + path.join(root, "node_modules", "types-only-preset", "main.js"), + ); +}); diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index c4be7afc36..a0cbf7d222 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -25,6 +25,90 @@ const pathSuffixes = ["", ".js", ".json", `${path.sep}index.js`, `${path.sep}ind const specifierSuffixes = ["", ".js", ".json", "/index.js", "/index.json"]; +/** + * Recover the entry point from a package manifest, preferring the ESM + * (`import`) condition of the `exports` map and falling back to `module`/`main`. + */ +const resolveExportsEntry = (manifest: Record): string => { + const fromConditions = (value: unknown): string | undefined => { + if (typeof value === "string") { + return value; + } + if (value && typeof value === "object" && !Array.isArray(value)) { + const conditions = value as Record; + for (const key of ["import", "module", "node", "default"]) { + if (key in conditions) { + const resolved = fromConditions(conditions[key]); + if (resolved) { + return resolved; + } + } + } + } + return undefined; + }; + + let exp = manifest.exports; + if (exp && typeof exp === "object" && !Array.isArray(exp) && "." in (exp as object)) { + exp = (exp as Record)["."]; + } + + return ( + fromConditions(exp) || + (typeof manifest.module === "string" ? manifest.module : undefined) || + (typeof manifest.main === "string" ? manifest.main : undefined) || + "index.js" + ); +}; + +/** + * Pure-ESM presets such as conventional-changelog-angular@>=9 and + * conventional-changelog-conventionalcommits@>=10 declare only the `import` + * condition in their `exports` map (no `require`/`default`/`main`), so the + * CommonJS resolvers above fail with ERR_PACKAGE_PATH_NOT_EXPORTED. Walk + * node_modules from the requesting location and read the manifest ourselves. + */ +const resolveEsmOnly = (lookup: string, fromDir: string): string | undefined => { + if (path.isAbsolute(lookup) || lookup.startsWith(".")) { + return undefined; + } + + const segments = lookup.split("/"); + const pkgName = lookup.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0]; + const subpath = lookup.slice(pkgName.length).replace(/^\//, ""); + + let dir = fromDir; + for (;;) { + const pkgDir = path.join(dir, "node_modules", pkgName); + const manifestPath = path.join(pkgDir, "package.json"); + + if (fs.existsSync(manifestPath)) { + if (subpath) { + for (const suffix of pathSuffixes) { + const filename = path.join(pkgDir, subpath) + suffix; + if (fs.existsSync(filename)) { + return filename; + } + } + return undefined; + } + + const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8")); + const resolved = path.join(pkgDir, resolveExportsEntry(manifest)); + if (fs.existsSync(resolved)) { + return resolved; + } + return undefined; + } + + const parentDir = path.dirname(dir); + if (parentDir === dir) { + return undefined; + } + dir = parentDir; + } +}; + export const resolveFrom = (lookup: string, parent?: string): string => { if (path.isAbsolute(lookup)) { for (const suffix of pathSuffixes) { @@ -62,6 +146,10 @@ export const resolveFrom = (lookup: string, parent?: string): string => { */ return resolveFrom_(path.dirname(parentPath), lookup); } catch { + const esmResolved = resolveEsmOnly(lookup, path.dirname(parentPath)); + if (esmResolved) { + return esmResolved; + } throw resolveError; } }; diff --git a/@packages/test/src/npm.ts b/@packages/test/src/npm.ts index 1592e533fe..2d87410273 100644 --- a/@packages/test/src/npm.ts +++ b/@packages/test/src/npm.ts @@ -1,5 +1,7 @@ import path from "node:path"; import { createRequire } from "node:module"; +import { existsSync, realpathSync } from "node:fs"; +import { fileURLToPath } from "node:url"; import fs from "node:fs/promises"; import resolvePkg from "resolve-pkg"; @@ -8,6 +10,34 @@ import * as git from "./git.js"; const require = createRequire(import.meta.url); +/** + * Pure-ESM packages (e.g. conventional-changelog-angular@>=9, + * conventional-changelog-conventionalcommits@>=10) ship an `exports` map with + * only the `import` condition, which the CommonJS resolvers (`resolve-pkg`, + * `require.resolve`) cannot read. Locate the package directory by walking up + * from this module and checking both the regular and the pnpm-virtual-store + * (`node_modules/.pnpm/node_modules/`) layouts. + */ +function resolveModuleDir(dependency: string): string | undefined { + let dir = path.dirname(fileURLToPath(import.meta.url)); + for (;;) { + for (const rel of [ + path.join("node_modules", dependency), + path.join("node_modules", ".pnpm", "node_modules", dependency), + ]) { + const candidate = path.join(dir, rel); + if (existsSync(path.join(candidate, "package.json"))) { + return realpathSync(candidate); + } + } + const parent = path.dirname(dir); + if (parent === dir) { + return undefined; + } + dir = parent; + } +} + export async function installModules(cwd: string) { const manifestPath = path.join(cwd, "package.json"); const targetModulesPath = path.join(cwd, "node_modules"); @@ -54,6 +84,10 @@ export async function installModules(cwd: string) { } } + if (!sourcePath) { + sourcePath = resolveModuleDir(dependency); + } + if (!sourcePath) { throw new Error(`Could not resolve dependency ${dependency}`); } From fdeb0a0e5d6ef7bbe9a423b99530ada64aabd2ac Mon Sep 17 00:00:00 2001 From: escapedcat Date: Tue, 30 Jun 2026 11:29:12 +0200 Subject: [PATCH 2/4] fix: update dependency conventional-commits-parser to v7 Co-Authored-By: Claude Opus 4.8 (1M context) --- @commitlint/cli/package.json | 2 +- @commitlint/parse/package.json | 2 +- @commitlint/rules/package.json | 2 +- @commitlint/types/package.json | 2 +- pnpm-lock.yaml | 39 +++++++++++++++++++++++++++------- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/@commitlint/cli/package.json b/@commitlint/cli/package.json index da63abde9a..a29623a33c 100644 --- a/@commitlint/cli/package.json +++ b/@commitlint/cli/package.json @@ -51,7 +51,7 @@ "@commitlint/utils": "workspace:^", "@types/node": "^22.0.0", "@types/yargs": "^17.0.29", - "conventional-commits-parser": "^6.3.0", + "conventional-commits-parser": "^7.0.0", "es-toolkit": "^1.46.0" }, "engines": { diff --git a/@commitlint/parse/package.json b/@commitlint/parse/package.json index b81a2f04df..f12d471e18 100644 --- a/@commitlint/parse/package.json +++ b/@commitlint/parse/package.json @@ -36,7 +36,7 @@ "dependencies": { "@commitlint/types": "workspace:^", "conventional-changelog-angular": "^8.2.0", - "conventional-commits-parser": "^6.3.0" + "conventional-commits-parser": "^7.0.0" }, "devDependencies": { "@commitlint/test": "workspace:^", diff --git a/@commitlint/rules/package.json b/@commitlint/rules/package.json index 25634288c3..600b8d7d0e 100644 --- a/@commitlint/rules/package.json +++ b/@commitlint/rules/package.json @@ -45,7 +45,7 @@ "@commitlint/utils": "workspace:^", "@types/node": "^22.0.0", "conventional-changelog-angular": "^8.2.0", - "conventional-commits-parser": "^6.3.0" + "conventional-commits-parser": "^7.0.0" }, "engines": { "node": ">=22.12.0" diff --git a/@commitlint/types/package.json b/@commitlint/types/package.json index 276fe3a377..4f72fdd237 100644 --- a/@commitlint/types/package.json +++ b/@commitlint/types/package.json @@ -27,7 +27,7 @@ "pkg": "pkg-check" }, "dependencies": { - "conventional-commits-parser": "^6.3.0", + "conventional-commits-parser": "^7.0.0", "picocolors": "^1.1.1" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7d1216730..861b3cc062 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,8 +162,8 @@ importers: specifier: ^17.0.29 version: 17.0.35 conventional-commits-parser: - specifier: ^6.3.0 - version: 6.4.0 + specifier: ^7.0.0 + version: 7.0.0 es-toolkit: specifier: ^1.46.0 version: 1.49.0 @@ -501,8 +501,8 @@ importers: specifier: ^8.2.0 version: 8.3.1 conventional-commits-parser: - specifier: ^6.3.0 - version: 6.4.0 + specifier: ^7.0.0 + version: 7.0.0 devDependencies: '@commitlint/test': specifier: workspace:^ @@ -648,8 +648,8 @@ importers: specifier: ^8.2.0 version: 8.3.1 conventional-commits-parser: - specifier: ^6.3.0 - version: 6.4.0 + specifier: ^7.0.0 + version: 7.0.0 '@commitlint/to-lines': devDependencies: @@ -695,8 +695,8 @@ importers: '@commitlint/types': dependencies: conventional-commits-parser: - specifier: ^6.3.0 - version: 6.4.0 + specifier: ^7.0.0 + version: 7.0.0 picocolors: specifier: ^1.1.1 version: 1.1.1 @@ -2149,6 +2149,10 @@ packages: resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} engines: {node: '>=18'} + '@simple-libs/stream-utils@2.0.0': + resolution: {integrity: sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==} + engines: {node: '>=22'} + '@sinclair/typebox@0.34.49': resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} @@ -2896,6 +2900,11 @@ packages: engines: {node: '>=18'} hasBin: true + conventional-commits-parser@7.0.0: + resolution: {integrity: sha512-dZe/p+FgGQLNJFqaCgNdl8j6a7gI8xuaN30Wy5g7nvyK3jqOpNUEUZ3pGJ5D68h89uRh038FtkeOk/bnGmYkmg==} + engines: {node: '>=22'} + hasBin: true + conventional-recommended-bump@7.0.1: resolution: {integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==} engines: {node: '>=14'} @@ -3981,6 +3990,10 @@ packages: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} + meow@14.1.0: + resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==} + engines: {node: '>=20'} + meow@8.1.2: resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} engines: {node: '>=10'} @@ -6643,6 +6656,8 @@ snapshots: '@simple-libs/stream-utils@1.2.0': {} + '@simple-libs/stream-utils@2.0.0': {} + '@sinclair/typebox@0.34.49': {} '@standard-schema/spec@1.1.0': {} @@ -7390,6 +7405,12 @@ snapshots: dependencies: '@simple-libs/stream-utils': 1.2.0 meow: 13.2.0 + optional: true + + conventional-commits-parser@7.0.0: + dependencies: + '@simple-libs/stream-utils': 2.0.0 + meow: 14.1.0 conventional-recommended-bump@7.0.1: dependencies: @@ -8587,6 +8608,8 @@ snapshots: meow@13.2.0: {} + meow@14.1.0: {} + meow@8.1.2: dependencies: '@types/minimist': 1.2.5 From ce67d3b33174cee2c58be25f9cf68fee916a97c6 Mon Sep 17 00:00:00 2001 From: escapedcat Date: Tue, 30 Jun 2026 11:29:45 +0200 Subject: [PATCH 3/4] fix: update dependency conventional-changelog-conventionalcommits to v10 Co-Authored-By: Claude Opus 4.8 (1M context) --- @commitlint/config-conventional/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++++------- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/@commitlint/config-conventional/package.json b/@commitlint/config-conventional/package.json index 206c87680b..ea4c8e2c45 100644 --- a/@commitlint/config-conventional/package.json +++ b/@commitlint/config-conventional/package.json @@ -34,7 +34,7 @@ }, "dependencies": { "@commitlint/types": "workspace:^", - "conventional-changelog-conventionalcommits": "^9.2.0" + "conventional-changelog-conventionalcommits": "^10.0.0" }, "devDependencies": { "@commitlint/lint": "workspace:^", diff --git a/@commitlint/load/fixtures/parser-preset-conventional-without-factory/package.json b/@commitlint/load/fixtures/parser-preset-conventional-without-factory/package.json index cff2ccd455..75a2692705 100644 --- a/@commitlint/load/fixtures/parser-preset-conventional-without-factory/package.json +++ b/@commitlint/load/fixtures/parser-preset-conventional-without-factory/package.json @@ -2,6 +2,6 @@ "name": "parser-preset-conventional-without-factory", "version": "1.0.0", "devDependencies": { - "conventional-changelog-conventionalcommits": "^9.2.0" + "conventional-changelog-conventionalcommits": "^10.0.0" } } diff --git a/@commitlint/load/fixtures/parser-preset-conventionalcommits/package.json b/@commitlint/load/fixtures/parser-preset-conventionalcommits/package.json index 3775c8e10e..76407371e3 100644 --- a/@commitlint/load/fixtures/parser-preset-conventionalcommits/package.json +++ b/@commitlint/load/fixtures/parser-preset-conventionalcommits/package.json @@ -2,6 +2,6 @@ "name": "parser-preset-conventionalcommits", "version": "1.0.0", "devDependencies": { - "conventional-changelog-conventionalcommits": "^9.2.0" + "conventional-changelog-conventionalcommits": "^10.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 861b3cc062..fb2e043b39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,8 +193,8 @@ importers: specifier: workspace:^ version: link:../types conventional-changelog-conventionalcommits: - specifier: ^9.2.0 - version: 9.3.1 + specifier: ^10.0.0 + version: 10.2.0 devDependencies: '@commitlint/lint': specifier: workspace:^ @@ -940,6 +940,10 @@ packages: conventional-commits-parser: optional: true + '@conventional-changelog/template@1.2.0': + resolution: {integrity: sha512-12qHxvlKjHmP0PQ+17EREgC7lWyLwbph1RKcZQZ7k7ZWGmrxfxC9gadHGfvzr0g0u8BhiBGg3tks93txodlyRQ==} + engines: {node: '>=22'} + '@docsearch/css@3.8.2': resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} @@ -2865,9 +2869,9 @@ packages: engines: {node: '>=18'} deprecated: This preset is deprecated. Please use conventional-changelog-conventionalcommits or conventional-changelog-angular instead. - conventional-changelog-conventionalcommits@9.3.1: - resolution: {integrity: sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==} - engines: {node: '>=18'} + conventional-changelog-conventionalcommits@10.2.0: + resolution: {integrity: sha512-UtlM9GqolY7OmlQh5L/UEVoKsTUpTgUVy1PU8JN5gl5Ydaejb7WRklGliG1SKPxxj7hzA173eG3Kt5fYWE2pmg==} + engines: {node: '>=22'} conventional-changelog-core@5.0.1: resolution: {integrity: sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==} @@ -5700,6 +5704,8 @@ snapshots: optionalDependencies: conventional-commits-parser: 6.4.0 + '@conventional-changelog/template@1.2.0': {} + '@docsearch/css@3.8.2': {} '@docsearch/js@3.8.2(@algolia/client-search@5.55.1)(search-insights@2.17.3)': @@ -7357,9 +7363,9 @@ snapshots: conventional-changelog-atom@5.1.0: {} - conventional-changelog-conventionalcommits@9.3.1: + conventional-changelog-conventionalcommits@10.2.0: dependencies: - compare-func: 2.0.0 + '@conventional-changelog/template': 1.2.0 conventional-changelog-core@5.0.1: dependencies: From edb60052f4c9928d50ef9df505ad9401e0199263 Mon Sep 17 00:00:00 2001 From: escapedcat Date: Tue, 30 Jun 2026 11:30:57 +0200 Subject: [PATCH 4/4] fix: update dependency conventional-changelog-angular to v9 v9 ships its own typings: createPreset() is typed as {} and the named parser export was dropped (still present at runtime). Read the parser options via createPreset().parser and drop the now-unused @ts-expect-error directives. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../parser-preset-angular/package.json | 2 +- @commitlint/parse/package.json | 2 +- @commitlint/parse/src/index.test.ts | 26 ++++++++++--------- @commitlint/parse/src/index.ts | 8 ++++-- @commitlint/rules/package.json | 2 +- .../rules/src/references-empty.test.ts | 4 +-- .../src/subject-exclamation-mark.test.ts | 6 ++--- pnpm-lock.yaml | 18 ++++++------- 8 files changed, 37 insertions(+), 31 deletions(-) diff --git a/@commitlint/load/fixtures/parser-preset-angular/package.json b/@commitlint/load/fixtures/parser-preset-angular/package.json index 5bc9d3e3a7..dac067300a 100644 --- a/@commitlint/load/fixtures/parser-preset-angular/package.json +++ b/@commitlint/load/fixtures/parser-preset-angular/package.json @@ -2,6 +2,6 @@ "name": "parser-preset-angular", "version": "1.0.0", "devDependencies": { - "conventional-changelog-angular": "^7.0.0" + "conventional-changelog-angular": "^9.0.0" } } diff --git a/@commitlint/parse/package.json b/@commitlint/parse/package.json index f12d471e18..bc0488f6dd 100644 --- a/@commitlint/parse/package.json +++ b/@commitlint/parse/package.json @@ -35,7 +35,7 @@ }, "dependencies": { "@commitlint/types": "workspace:^", - "conventional-changelog-angular": "^8.2.0", + "conventional-changelog-angular": "^9.0.0", "conventional-commits-parser": "^7.0.0" }, "devDependencies": { diff --git a/@commitlint/parse/src/index.test.ts b/@commitlint/parse/src/index.test.ts index c68bddffc3..d24c39b2ba 100644 --- a/@commitlint/parse/src/index.test.ts +++ b/@commitlint/parse/src/index.test.ts @@ -1,6 +1,16 @@ import { test, expect } from "vitest"; +import type { ParserOptions } from "conventional-commits-parser"; + import parse from "./index.js"; +// conventional-changelog-angular@>=9 exposes a single default `createPreset()` +// whose return is typed as `{}`; the parser options live under `.parser`. +const angularParserOpts = async (): Promise => { + const { default: createPreset } = await import("conventional-changelog-angular"); + const { parser } = (await createPreset()) as { parser: ParserOptions }; + return parser; +}; + test("throws when called without params", async () => { await expect((parse as any)()).rejects.toThrow("Expected a raw commit"); }); @@ -143,10 +153,8 @@ test("supports scopes with / and empty parserOpts", async () => { test("ignores comments", async () => { const message = "type(some/scope): subject\n# some comment"; - // @ts-expect-error -- no typings - const changelogOpts = await import("conventional-changelog-angular"); const opts = { - ...changelogOpts.parser, + ...(await angularParserOpts()), commentChar: "#", }; const actual = await parse(message, undefined, opts); @@ -158,9 +166,7 @@ test("ignores comments", async () => { test("parses inline references in subject and body", async () => { const message = "type(some/scope): subject #reference\n\nthings #reference"; - // @ts-expect-error -- no typings - const changelogOpts = await import("conventional-changelog-angular"); - const actual = await parse(message, undefined, changelogOpts.parser); + const actual = await parse(message, undefined, await angularParserOpts()); expect(actual.subject).toBe("subject #reference"); expect(actual.body).toBe(""); @@ -186,10 +192,8 @@ test("parses inline references in subject and body", async () => { test("filters comment lines when commentChar is set", async () => { const message = "type(scope): subject\n# this is a comment\nbody content"; - // @ts-expect-error -- no typings - const changelogOpts = await import("conventional-changelog-angular"); const opts = { - ...changelogOpts.parser, + ...(await angularParserOpts()), commentChar: "#", }; const actual = await parse(message, undefined, opts); @@ -235,11 +239,9 @@ test("allows separating -side nodes- by setting parserOpts.fieldPattern", async test("parses references leading subject", async () => { const message = "#1 some subject"; - // @ts-expect-error -- no typings - const opts = await import("conventional-changelog-angular"); const { references: [actual], - } = await parse(message, undefined, opts); + } = await parse(message, undefined, await angularParserOpts()); expect(actual.issue).toBe("1"); }); diff --git a/@commitlint/parse/src/index.ts b/@commitlint/parse/src/index.ts index d005b95fca..470a03a6dd 100644 --- a/@commitlint/parse/src/index.ts +++ b/@commitlint/parse/src/index.ts @@ -1,7 +1,6 @@ import type { Parser } from "@commitlint/types"; import { type Commit, type ParserOptions, CommitParser } from "conventional-commits-parser"; -// @ts-expect-error -- no typings import defaultChangelogOpts from "conventional-changelog-angular"; const defaultParser: Parser = (message, options) => { @@ -21,7 +20,12 @@ export async function parse( parser: Parser = defaultParser, parserOpts?: ParserOptions, ): Promise { - const preset = await defaultChangelogOpts(); + // conventional-changelog-angular@>=9 ships typings that declare the preset as + // `{}`; the parser options live under `.parser` at runtime. + const preset = (await defaultChangelogOpts()) as { + parser?: ParserOptions; + parserOpts?: ParserOptions; + }; const defaultOpts = preset.parser || preset.parserOpts; // Support user-provided parser options passed either flat or nested under a 'parser' key const userOpts = (parserOpts as any)?.parser || parserOpts || {}; diff --git a/@commitlint/rules/package.json b/@commitlint/rules/package.json index 600b8d7d0e..9da7170074 100644 --- a/@commitlint/rules/package.json +++ b/@commitlint/rules/package.json @@ -44,7 +44,7 @@ "@commitlint/test": "workspace:^", "@commitlint/utils": "workspace:^", "@types/node": "^22.0.0", - "conventional-changelog-angular": "^8.2.0", + "conventional-changelog-angular": "^9.0.0", "conventional-commits-parser": "^7.0.0" }, "engines": { diff --git a/@commitlint/rules/src/references-empty.test.ts b/@commitlint/rules/src/references-empty.test.ts index a503877723..70e6c3099a 100644 --- a/@commitlint/rules/src/references-empty.test.ts +++ b/@commitlint/rules/src/references-empty.test.ts @@ -2,7 +2,7 @@ import { test, expect } from "vitest"; import parse from "@commitlint/parse"; import { referencesEmpty } from "./references-empty.js"; -// @ts-expect-error -- no typings +import type { ParserOptions } from "conventional-commits-parser"; import preset from "conventional-changelog-angular"; const messages = { @@ -14,7 +14,7 @@ const messages = { }; const opts = (async () => { - const o = await preset(); + const o = (await preset()) as { parser: ParserOptions }; o.parser.commentChar = "#"; return o; })(); diff --git a/@commitlint/rules/src/subject-exclamation-mark.test.ts b/@commitlint/rules/src/subject-exclamation-mark.test.ts index 55e47d0e7a..cef2820bdc 100644 --- a/@commitlint/rules/src/subject-exclamation-mark.test.ts +++ b/@commitlint/rules/src/subject-exclamation-mark.test.ts @@ -1,13 +1,13 @@ import { test, expect } from "vitest"; import parse from "@commitlint/parse"; -// @ts-expect-error -- no typings +import type { ParserOptions } from "conventional-commits-parser"; import preset from "conventional-changelog-angular"; import { subjectExclamationMark } from "./subject-exclamation-mark.js"; const parseMessage = async (str: string) => { - const { parserOpts } = await preset(); - return parse(str, undefined, parserOpts); + const { parser } = (await preset()) as { parser: ParserOptions }; + return parse(str, undefined, parser); }; const messages = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb2e043b39..23b699520e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -498,8 +498,8 @@ importers: specifier: workspace:^ version: link:../types conventional-changelog-angular: - specifier: ^8.2.0 - version: 8.3.1 + specifier: ^9.0.0 + version: 9.2.0 conventional-commits-parser: specifier: ^7.0.0 version: 7.0.0 @@ -645,8 +645,8 @@ importers: specifier: ^22.0.0 version: 22.20.0 conventional-changelog-angular: - specifier: ^8.2.0 - version: 8.3.1 + specifier: ^9.0.0 + version: 9.2.0 conventional-commits-parser: specifier: ^7.0.0 version: 7.0.0 @@ -2860,9 +2860,9 @@ packages: resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} engines: {node: '>=16'} - conventional-changelog-angular@8.3.1: - resolution: {integrity: sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==} - engines: {node: '>=18'} + conventional-changelog-angular@9.2.0: + resolution: {integrity: sha512-2EihZvM1KmbBGwuUow8lNXLe+ECRFsyOcV8X0197/WI7Rvvgfi55Oicfw0wxJTXGGIVXSfj+xBoNj+cxLlIiVw==} + engines: {node: '>=22'} conventional-changelog-atom@5.1.0: resolution: {integrity: sha512-fw7GpI9jHNCWGBnTsPRI452ypQbNupGwsjrXfozvRNE0c92pJRpoj9rXfzDKUYJcsmk0H4XKaQjhjelwI9z27w==} @@ -7357,9 +7357,9 @@ snapshots: dependencies: compare-func: 2.0.0 - conventional-changelog-angular@8.3.1: + conventional-changelog-angular@9.2.0: dependencies: - compare-func: 2.0.0 + '@conventional-changelog/template': 1.2.0 conventional-changelog-atom@5.1.0: {}