Skip to content

Commit 41d900a

Browse files
escapedcatclaude
andcommitted
fix: update dependency conventional-changelog-conventionalcommits to v10
v10 is pure ESM whose exports map exposes only the `import` condition, so the CommonJS preset resolver in resolve-extends (and the test bootstrap) can no longer resolve it. Add an ESM-aware node_modules fallback that runs only after CommonJS resolution fails. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4e9eb8e commit 41d900a

6 files changed

Lines changed: 141 additions & 10 deletions

File tree

@commitlint/config-conventional/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
},
3535
"dependencies": {
3636
"@commitlint/types": "workspace:^",
37-
"conventional-changelog-conventionalcommits": "^9.2.0"
37+
"conventional-changelog-conventionalcommits": "^10.0.0"
3838
},
3939
"devDependencies": {
4040
"@commitlint/lint": "workspace:^",

@commitlint/load/fixtures/parser-preset-conventional-without-factory/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"name": "parser-preset-conventional-without-factory",
33
"version": "1.0.0",
44
"devDependencies": {
5-
"conventional-changelog-conventionalcommits": "^9.2.0"
5+
"conventional-changelog-conventionalcommits": "^10.0.0"
66
}
77
}

@commitlint/load/fixtures/parser-preset-conventionalcommits/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"name": "parser-preset-conventionalcommits",
33
"version": "1.0.0",
44
"devDependencies": {
5-
"conventional-changelog-conventionalcommits": "^9.2.0"
5+
"conventional-changelog-conventionalcommits": "^10.0.0"
66
}
77
}

@commitlint/resolve-extends/src/index.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,93 @@ const pathSuffixes = ["", ".js", ".json", `${path.sep}index.js`, `${path.sep}ind
2525

2626
const specifierSuffixes = ["", ".js", ".json", "/index.js", "/index.json"];
2727

28+
/**
29+
* Recover the entry point from a package manifest, preferring the ESM
30+
* (`import`) condition of the `exports` map and falling back to `module`/`main`.
31+
*/
32+
const resolveExportsEntry = (manifest: Record<string, unknown>): string | undefined => {
33+
const fromConditions = (value: unknown): string | undefined => {
34+
if (typeof value === "string") {
35+
return value;
36+
}
37+
if (value && typeof value === "object" && !Array.isArray(value)) {
38+
const conditions = value as Record<string, unknown>;
39+
for (const key of ["import", "module", "node", "default"]) {
40+
if (key in conditions) {
41+
const resolved = fromConditions(conditions[key]);
42+
if (resolved) {
43+
return resolved;
44+
}
45+
}
46+
}
47+
}
48+
return undefined;
49+
};
50+
51+
let exp = manifest.exports;
52+
if (exp && typeof exp === "object" && !Array.isArray(exp) && "." in (exp as object)) {
53+
exp = (exp as Record<string, unknown>)["."];
54+
}
55+
56+
return (
57+
fromConditions(exp) ||
58+
(typeof manifest.module === "string" ? manifest.module : undefined) ||
59+
(typeof manifest.main === "string" ? manifest.main : undefined) ||
60+
"index.js"
61+
);
62+
};
63+
64+
/**
65+
* Pure-ESM presets such as conventional-changelog-angular@>=9 and
66+
* conventional-changelog-conventionalcommits@>=10 declare only the `import`
67+
* condition in their `exports` map (no `require`/`default`/`main`), so the
68+
* CommonJS resolvers above fail with ERR_PACKAGE_PATH_NOT_EXPORTED. Walk
69+
* node_modules from the requesting location and read the manifest ourselves.
70+
*/
71+
const resolveEsmOnly = (lookup: string, fromDir: string): string | undefined => {
72+
if (path.isAbsolute(lookup) || lookup.startsWith(".")) {
73+
return undefined;
74+
}
75+
76+
const segments = lookup.split("/");
77+
const pkgName = lookup.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
78+
const subpath = lookup.slice(pkgName.length).replace(/^\//, "");
79+
80+
let dir = fromDir;
81+
for (;;) {
82+
const pkgDir = path.join(dir, "node_modules", pkgName);
83+
const manifestPath = path.join(pkgDir, "package.json");
84+
85+
if (fs.existsSync(manifestPath)) {
86+
if (subpath) {
87+
for (const suffix of pathSuffixes) {
88+
const filename = path.join(pkgDir, subpath) + suffix;
89+
if (fs.existsSync(filename)) {
90+
return filename;
91+
}
92+
}
93+
return undefined;
94+
}
95+
96+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
97+
const entry = resolveExportsEntry(manifest);
98+
if (entry) {
99+
const resolved = path.join(pkgDir, entry);
100+
if (fs.existsSync(resolved)) {
101+
return resolved;
102+
}
103+
}
104+
return undefined;
105+
}
106+
107+
const parentDir = path.dirname(dir);
108+
if (parentDir === dir) {
109+
return undefined;
110+
}
111+
dir = parentDir;
112+
}
113+
};
114+
28115
export const resolveFrom = (lookup: string, parent?: string): string => {
29116
if (path.isAbsolute(lookup)) {
30117
for (const suffix of pathSuffixes) {
@@ -62,6 +149,10 @@ export const resolveFrom = (lookup: string, parent?: string): string => {
62149
*/
63150
return resolveFrom_(path.dirname(parentPath), lookup);
64151
} catch {
152+
const esmResolved = resolveEsmOnly(lookup, path.dirname(parentPath));
153+
if (esmResolved) {
154+
return esmResolved;
155+
}
65156
throw resolveError;
66157
}
67158
};

@packages/test/src/npm.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import path from "node:path";
22
import { createRequire } from "node:module";
3+
import { existsSync, realpathSync } from "node:fs";
4+
import { fileURLToPath } from "node:url";
35

46
import fs from "node:fs/promises";
57
import resolvePkg from "resolve-pkg";
@@ -8,6 +10,34 @@ import * as git from "./git.js";
810

911
const require = createRequire(import.meta.url);
1012

13+
/**
14+
* Pure-ESM packages (e.g. conventional-changelog-angular@>=9,
15+
* conventional-changelog-conventionalcommits@>=10) ship an `exports` map with
16+
* only the `import` condition, which the CommonJS resolvers (`resolve-pkg`,
17+
* `require.resolve`) cannot read. Locate the package directory by walking up
18+
* from this module and checking both the regular and the pnpm-virtual-store
19+
* (`node_modules/.pnpm/node_modules/<name>`) layouts.
20+
*/
21+
function resolveModuleDir(dependency: string): string | undefined {
22+
let dir = path.dirname(fileURLToPath(import.meta.url));
23+
for (;;) {
24+
for (const rel of [
25+
path.join("node_modules", dependency),
26+
path.join("node_modules", ".pnpm", "node_modules", dependency),
27+
]) {
28+
const candidate = path.join(dir, rel);
29+
if (existsSync(path.join(candidate, "package.json"))) {
30+
return realpathSync(candidate);
31+
}
32+
}
33+
const parent = path.dirname(dir);
34+
if (parent === dir) {
35+
return undefined;
36+
}
37+
dir = parent;
38+
}
39+
}
40+
1141
export async function installModules(cwd: string) {
1242
const manifestPath = path.join(cwd, "package.json");
1343
const targetModulesPath = path.join(cwd, "node_modules");
@@ -54,6 +84,10 @@ export async function installModules(cwd: string) {
5484
}
5585
}
5686

87+
if (!sourcePath) {
88+
sourcePath = resolveModuleDir(dependency);
89+
}
90+
5791
if (!sourcePath) {
5892
throw new Error(`Could not resolve dependency ${dependency}`);
5993
}

pnpm-lock.yaml

Lines changed: 13 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)