Skip to content

deps: migrate pnpm internals to v11 (1100.x)#10293

Open
zkochan wants to merge 2 commits into
teambit:masterfrom
zkochan:pnpm11
Open

deps: migrate pnpm internals to v11 (1100.x)#10293
zkochan wants to merge 2 commits into
teambit:masterfrom
zkochan:pnpm11

Conversation

@zkochan

@zkochan zkochan commented Apr 14, 2026

Copy link
Copy Markdown
Member

Summary

  • Rename 17 @pnpm/* deps in workspace.jsonc to v11's @pnpm/<domain>.<leaf> naming (e.g. @pnpm/core@pnpm/installing.deps-installer, @pnpm/config@pnpm/config.reader), bump in-monorepo packages to 1100.0.0.
  • Adapt to v11 breaking API changes: mutateModules now uses allowBuilds: Record<string, boolean | string> (plus dangerouslyAllowAllBuilds) in place of the removed neverBuiltDependencies / onlyBuiltDependencies / ignoredBuiltDependencies trio.
  • Config.rawConfigConfig.authConfig rename; read network settings (maxSockets, fetchRetries, etc.) from typed Config fields instead of rawConfig ini lookups.
  • Convert flat auth rawConfig to the new configByUri: Record<string, RegistryConfig> shape for CreateStoreControllerOptions; switch generateResolverAndFetcher to createResolver since both callers only use .resolve (avoids the now-required storeIndex on createClient).
  • Rename sortPackagessortProjects, createPkgGraphcreateProjectsGraph, createOrConnectStoreControllercreateStoreController; supply now-required globalVirtualStoreDir to getPeerDependencyIssues.

Test plan

  • npm run check-types passes
  • bit compile — 310/310 components compile
  • install new dependencies (using pnpm) e2e — 5/5 pass
  • install missing dependencies (when all packages exist) e2e — 2/2 pass (covers add/tag/export/import/reinstall)
  • skipping compilation on install e2e — 2/2 pass
  • Broader e2e suite
  • CI green

@zkochan zkochan force-pushed the pnpm11 branch 2 times, most recently from 3d42363 to 37f4352 Compare May 5, 2026 13:41
@zkochan zkochan marked this pull request as ready for review May 6, 2026 08:20
Copilot AI review requested due to automatic review settings May 6, 2026 08:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates Bit’s internal pnpm integration to pnpm v11-era packages (1100.x), updating dependency names and adapting Bit’s pnpm/yarn package-manager glue code to pnpm’s breaking API and module-format (ESM) changes.

Changes:

  • Renames and bumps @pnpm/* dependencies in workspace.jsonc to pnpm v11 package naming and versions.
  • Updates pnpm/yarn integration code for pnpm v11 APIs (config/auth shape, resolver creation, store controller creation, peer issues options, project graph/sorting).
  • Adds CJS shims + adjusts Babel config to safely load ESM-only pnpm modules in test/build environments without breaking mocha/Babel require chains.

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
workspace.jsonc Renames pnpm internal deps to v11 package names and bumps versions.
scopes/workspace/workspace/workspace-aspects-loader.ts Removes unreachable code in error handling path.
scopes/typescript/typescript/schema-extractor-context.ts Removes unreachable return after throwing.
scopes/scope/objects/models/dependencies-graph.ts Updates pnpm dep-path import to new package name.
scopes/pkg/pkg/packer.ts Switches pnpm pack command loading to ESM-safe dynamic loader shim.
scopes/pkg/pkg/load-pnpm-pack.cjs New CJS shim to import() ESM-only pnpm releasing commands for packing.
scopes/harmony/aspect/babel/babel-config.ts Adjusts preset-env to avoid transforming dynamic import().
scopes/dependencies/yarn/yarn.package-manager.ts Updates pnpm overrides parser import to v11 package.
scopes/dependencies/pnpm/read-config.ts Switches pnpm config reader import to v11 package.
scopes/dependencies/pnpm/pnpm.package-manager.ts Adapts network config reading + lockfile graph converter init + updated pnpm module imports.
scopes/dependencies/pnpm/pnpm-error-to-bit-error.spec.ts Avoids importing ESM-only @pnpm/error by stubbing a PnpmError-like object.
scopes/dependencies/pnpm/lynx.ts Migrates pnpm install/resolver/store controller APIs and script/build policy wiring to v11.
scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts Adds async init + ESM loading shim for ESM-only pnpm modules; adjusts types.
scopes/dependencies/pnpm/lockfile-deps-graph-converter.spec.ts Ensures converter init runs before tests; minor structure tweaks.
scopes/dependencies/pnpm/load-pnpm-esm.cjs New CJS shim to import() ESM-only @pnpm/deps.path and @pnpm/lockfile.fs.
scopes/dependencies/pnpm/get-registries.ts Uses config.authConfig in place of rawConfig for credential extraction.
scopes/dependencies/pnpm/get-proxy-config.ts Updates config type import and no-proxy config source.
scopes/dependencies/dependency-resolver/package-manager.ts Updates pnpm peer issues type import to v11 package.
scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts Updates npm resolver import to v11 package.
scopes/component/modules/merge-helper/merge-files.ts Removes unreachable throw err after throwing a new error.
e2e/harmony/pnpm-default-hoisting.e2e.ts Uses ESM-safe dynamic import for modules manifest reading.
e2e/harmony/pkg-manager-config.e2e.ts Removes pnpm-specific capsule rc test section; keeps Yarn case.
e2e/harmony/dependency-resolver.e2e.ts Uses ESM-safe dynamic import for modules manifest reading.
components/legacy/e2e-helper/npm-ci-registry.ts Switches pnpm fetch import to dynamic ESM import (@pnpm/network.fetch).
babel.config.js Ensures Babel doesn’t transform dynamic import() (needed for ESM-only pnpm deps).

Comment on lines +33 to +41
export async function init(): Promise<void> {
if (loaded) return;
const { loadEsm } = require('./load-pnpm-esm.cjs') as {
loadEsm: () => Promise<{ dp: typeof Dp; getLockfileImporterId: typeof GetLockfileImporterId }>;
};
const m = await loadEsm();
dp = m.dp;
getLockfileImporterId = m.getLockfileImporterId;
loaded = true;
Comment on lines 68 to 70
after(() => {
npmCiRegistry.destroy();
});
Copilot AI review requested due to automatic review settings May 7, 2026 12:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 3 comments.

Comment on lines 1 to 4
import semver from 'semver';
import type { LockfilePackageInfo } from '@pnpm/lockfile.types';
import * as dp from '@pnpm/dependency-path';
import * as dp from '@pnpm/deps.path';

Comment on lines 10 to 13
(supportNpmCiRegistryTesting ? describe : describe.skip)(
'package manager rc file is read from the workspace directory when installation is in a capsule',
'workspace .yarnrc.yml is read by Yarn when installation is in a capsule',
function () {
this.timeout(0);
import { buildDependentsTree } from '@pnpm/reviewing.dependencies-hierarchy';
import { renderDependentsTree } from '@pnpm/list';
import type { Modules } from '@pnpm/installing.modules-yaml';
import { readModulesManifest } from '@pnpm/installing.modules-yaml';
Copilot AI review requested due to automatic review settings June 1, 2026 12:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 4 comments.

Comment on lines 1 to 4
import semver from 'semver';
import type { LockfilePackageInfo } from '@pnpm/lockfile.types';
import * as dp from '@pnpm/dependency-path';
import * as dp from '@pnpm/deps.path';

Comment on lines 10 to 12
(supportNpmCiRegistryTesting ? describe : describe.skip)(
'package manager rc file is read from the workspace directory when installation is in a capsule',
'workspace .yarnrc.yml is read by Yarn when installation is in a capsule',
function () {
Comment on lines 1 to 4
import semver from 'semver';
import type { LockfilePackageInfo } from '@pnpm/lockfile.types';
import * as dp from '@pnpm/dependency-path';
import * as dp from '@pnpm/deps.path';

Comment on lines 10 to 12
(supportNpmCiRegistryTesting ? describe : describe.skip)(
'package manager rc file is read from the workspace directory when installation is in a capsule',
'workspace .yarnrc.yml is read by Yarn when installation is in a capsule',
function () {
Copilot AI review requested due to automatic review settings June 1, 2026 15:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 2 comments.

Comment on lines +25 to +31
// @pnpm/deps.path and @pnpm/lockfile.fs are ESM-only; load them through a .cjs
// shim so the require() chain in the build capsule's mocha runner doesn't trip
// on the transitive ESM import. Call `init()` once before invoking the public
// converters; helpers reach for these module-level slots synchronously.
let dp!: typeof Dp;
let getLockfileImporterId!: typeof GetLockfileImporterId;
let loading: Promise<void> | undefined;
Comment on lines 10 to 12
(supportNpmCiRegistryTesting ? describe : describe.skip)(
'package manager rc file is read from the workspace directory when installation is in a capsule',
'workspace .yarnrc.yml is read by Yarn when installation is in a capsule',
function () {
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 15, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. ESM import in CJS 🐞 Bug ☼ Reliability
Description
DependenciesGraph now statically imports @pnpm/deps.path, which this PR itself documents as
ESM-only; when Babel transpiles this file to CommonJS, it will become a require() and can crash at
runtime with ERR_REQUIRE_ESM. This can break dependency graph operations anywhere
@teambit/objects is loaded in the CJS-transpiled execution path (e.g. mocha via babel-register).
Code

scopes/scope/objects/models/dependencies-graph.ts[3]

+import * as dp from '@pnpm/deps.path';
Evidence
The modified file now imports @pnpm/deps.path statically. The PR-added shim explicitly states
@pnpm/deps.path is ESM-only and must be loaded via Node’s ESM loader (dynamic import()), while
the Babel configuration used by babel-register transforms TS modules to CommonJS, turning the
static import into a require() which is incompatible with ESM-only packages.

scopes/scope/objects/models/dependencies-graph.ts[1-4]
scopes/dependencies/pnpm/load-pnpm-esm.cjs[3-10]
babel.config.js[19-26]
babel-register.js[1-1]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scopes/scope/objects/models/dependencies-graph.ts` now statically imports `@pnpm/deps.path`. The PR also states `@pnpm/deps.path` is ESM-only, while the repo’s Babel setup transpiles TS modules to CommonJS (turning `import` into `require()`), which can throw `ERR_REQUIRE_ESM` at runtime.
### Issue Context
- This repo uses `@babel/plugin-transform-modules-commonjs` (via `babel-register`) in at least some execution paths.
- The PR already introduced a CJS shim + dynamic `import()` loader for ESM-only pnpm modules elsewhere, which suggests the same problem applies here.
### Fix Focus Areas
- scopes/scope/objects/models/dependencies-graph.ts[1-120]
- scopes/dependencies/pnpm/load-pnpm-esm.cjs[1-12]
- babel.config.js[1-31]
- babel-register.js[1-1]
### Suggested fix directions
Pick one:
1) **Remove the dependency on `@pnpm/deps.path` here** by implementing a small local parser for the *specific* need (extracting `version` from a depPath string) so this code stays synchronous and CJS-safe.
2) **Mirror the shim approach** used in `lockfile-deps-graph-converter.ts`: add a local loader wrapper for `@pnpm/deps.path` and ensure it is initialized before any `DependenciesGraph` code path that calls `dp.parse()`. If you choose this route, add a clear runtime guard/error message when not initialized (instead of an opaque TypeError).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Core-js deny ignored 🐞 Bug ⛨ Security
Description
In resolveScriptPolicies(), the branch that sets a default deny for "core-js" also returns
dangerouslyAllowAllBuilds: true even though the code comment states pnpm ignores allowBuilds when
dangerouslyAllowAllBuilds is set. This makes the default deny ineffective and can unintentionally
allow core-js build scripts to run when dangerouslyAllowAllScripts is enabled without an explicit
neverBuiltDependencies list.
Code

scopes/dependencies/pnpm/lynx.ts[R428-434]

if (dangerouslyAllowAllScripts) {
-    if (resolvedNeverBuilt == null) {
-      // If neverBuiltDependencies is not explicitly set, use a default list
-      // we tell pnpm to allow all scripts to be executed, except the packages listed below.
-      resolvedNeverBuilt = ['core-js'];
+    // pnpm v11's createAllowBuildFunction ignores allowBuilds when dangerouslyAllowAllBuilds is
+    // set, so emit allowBuilds with just the deny entries whenever a deny list exists.
+    if (!neverBuiltDependencies?.length) {
+      allowBuilds['core-js'] = false;
+      return { dangerouslyAllowAllBuilds: true, allowBuilds };
}
-  } else {
-    onlyBuiltDependencies = [];
-    ignoredBuiltDependencies = [];
-    for (const [packageDescriptor, allowedScript] of Object.entries(allowScripts ?? {})) {
-      switch (allowedScript) {
-        case true:
-          onlyBuiltDependencies.push(packageDescriptor);
-          break;
-        case false:
-          ignoredBuiltDependencies.push(packageDescriptor);
-          break;
-        default:
-          // Ignore any non-boolean values. String values are placeholders that the user
-          // should replace with booleans.
-          // pnpm will print a warning about these during installation.
-          break;
-      }
Evidence
The function comment explicitly states allowBuilds is ignored when dangerouslyAllowAllBuilds is set,
yet the code sets a deny entry in allowBuilds and returns dangerouslyAllowAllBuilds: true in that
same branch; therefore the deny entry will not take effect under the stated behavior.

scopes/dependencies/pnpm/lynx.ts[422-434]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`resolveScriptPolicies()` returns `{ dangerouslyAllowAllBuilds: true, allowBuilds }` in the same branch where it sets `allowBuilds['core-js'] = false`, but the adjacent comment states that pnpm ignores `allowBuilds` when `dangerouslyAllowAllBuilds` is set. This makes the default deny entry ineffective.
### Issue Context
The intent (based on existing policy and comments) is to keep a safe default deny list (at least `core-js`) even when `dangerouslyAllowAllScripts` is enabled.
### Fix Focus Areas
- scopes/dependencies/pnpm/lynx.ts[422-439]
### Suggested fix
Adjust the returned options so that the deny entry is actually honored:
- Do **not** set `dangerouslyAllowAllBuilds` in the code path where you need `allowBuilds` to enforce denies (e.g. for `core-js`).
- Alternatively, if pnpm v11 provides a supported way to express “allow all except these denies”, use that API and add/adjust a regression test for the default `core-js` behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Init precondition unguarded 🐞 Bug ☼ Reliability
Description
lockfile-deps-graph-converter.ts now relies on module-level dp/getLockfileImporterId variables that
are only populated via an async init(), but converter functions use dp synchronously without a
runtime guard. If any caller misses/forgets the init() precondition, this will crash at runtime with
an opaque TypeError rather than a clear error.
Code

scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[R25-43]

+// @pnpm/deps.path and @pnpm/lockfile.fs are ESM-only; load them through a .cjs
+// shim so the require() chain in the build capsule's mocha runner doesn't trip
+// on the transitive ESM import. Call `init()` once before invoking the public
+// converters; helpers reach for these module-level slots synchronously.
+let dp!: typeof Dp;
+let getLockfileImporterId!: typeof GetLockfileImporterId;
+let loading: Promise<void> | undefined;
+
+export function init(): Promise<void> {
+  loading ??= (async () => {
+    const { loadEsm } = require('./load-pnpm-esm.cjs') as {
+      loadEsm: () => Promise<{ dp: typeof Dp; getLockfileImporterId: typeof GetLockfileImporterId }>;
+    };
+    const m = await loadEsm();
+    dp = m.dp;
+    getLockfileImporterId = m.getLockfileImporterId;
+  })();
+  return loading;
+}
Evidence
The module-level slots are declared uninitialized and assigned only inside init(); later code uses
dp.refToRelative synchronously, so calling converters before init() will dereference an unset dp
variable.

scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[25-43]
scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[64-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The module introduces an `init()` that asynchronously sets module-level `dp` and `getLockfileImporterId`, but exported converter functions access `dp.*` synchronously and assume `init()` has been awaited.
### Issue Context
Current call sites appear to call `await initLockfileDepsGraphConverter()`, but this is a fragile contract for exported utilities and will be easy to violate in future usages.
### Fix Focus Areas
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[25-43]
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[64-75]
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[77-110]
### Suggested fix
- Add a small runtime guard (e.g. `assertInitialized()`) that checks `dp`/`getLockfileImporterId` are set and throws a clear, actionable error message ("call and await init() before convert*").
- Optionally, in async entry points (e.g. `convertGraphToLockfile`) call `await init()` internally as a safety net.
- Keep `convertLockfileToGraph` sync, but guard it with the explicit error to avoid a cryptic crash.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 767e5ae

Results up to commit 637e002


🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. ESM import in CJS 🐞 Bug ☼ Reliability
Description
DependenciesGraph now statically imports @pnpm/deps.path, which this PR itself documents as
ESM-only; when Babel transpiles this file to CommonJS, it will become a require() and can crash at
runtime with ERR_REQUIRE_ESM. This can break dependency graph operations anywhere
@teambit/objects is loaded in the CJS-transpiled execution path (e.g. mocha via babel-register).
Code

scopes/scope/objects/models/dependencies-graph.ts[3]

+import * as dp from '@pnpm/deps.path';
Evidence
The modified file now imports @pnpm/deps.path statically. The PR-added shim explicitly states
@pnpm/deps.path is ESM-only and must be loaded via Node’s ESM loader (dynamic import()), while
the Babel configuration used by babel-register transforms TS modules to CommonJS, turning the
static import into a require() which is incompatible with ESM-only packages.

scopes/scope/objects/models/dependencies-graph.ts[1-4]
scopes/dependencies/pnpm/load-pnpm-esm.cjs[3-10]
babel.config.js[19-26]
babel-register.js[1-1]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scopes/scope/objects/models/dependencies-graph.ts` now statically imports `@pnpm/deps.path`. The PR also states `@pnpm/deps.path` is ESM-only, while the repo’s Babel setup transpiles TS modules to CommonJS (turning `import` into `require()`), which can throw `ERR_REQUIRE_ESM` at runtime.
### Issue Context
- This repo uses `@babel/plugin-transform-modules-commonjs` (via `babel-register`) in at least some execution paths.
- The PR already introduced a CJS shim + dynamic `import()` loader for ESM-only pnpm modules elsewhere, which suggests the same problem applies here.
### Fix Focus Areas
- scopes/scope/objects/models/dependencies-graph.ts[1-120]
- scopes/dependencies/pnpm/load-pnpm-esm.cjs[1-12]
- babel.config.js[1-31]
- babel-register.js[1-1]
### Suggested fix directions
Pick one:
1) **Remove the dependency on `@pnpm/deps.path` here** by implementing a small local parser for the *specific* need (extracting `version` from a depPath string) so this code stays synchronous and CJS-safe.
2) **Mirror the shim approach** used in `lockfile-deps-graph-converter.ts`: add a local loader wrapper for `@pnpm/deps.path` and ensure it is initialized before any `DependenciesGraph` code path that calls `dp.parse()`. If you choose this route, add a clear runtime guard/error message when not initialized (instead of an opaque TypeError).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Core-js deny ignored 🐞 Bug ⛨ Security
Description
In resolveScriptPolicies(), the branch that sets a default deny for "core-js" also returns
dangerouslyAllowAllBuilds: true even though the code comment states pnpm ignores allowBuilds when
dangerouslyAllowAllBuilds is set. This makes the default deny ineffective and can unintentionally
allow core-js build scripts to run when dangerouslyAllowAllScripts is enabled without an explicit
neverBuiltDependencies list.
Code

scopes/dependencies/pnpm/lynx.ts[R428-434]

if (dangerouslyAllowAllScripts) {
-    if (resolvedNeverBuilt == null) {
-      // If neverBuiltDependencies is not explicitly set, use a default list
-      // we tell pnpm to allow all scripts to be executed, except the packages listed below.
-      resolvedNeverBuilt = ['core-js'];
+    // pnpm v11's createAllowBuildFunction ignores allowBuilds when dangerouslyAllowAllBuilds is
+    // set, so emit allowBuilds with just the deny entries whenever a deny list exists.
+    if (!neverBuiltDependencies?.length) {
+      allowBuilds['core-js'] = false;
+      return { dangerouslyAllowAllBuilds: true, allowBuilds };
}
-  } else {
-    onlyBuiltDependencies = [];
-    ignoredBuiltDependencies = [];
-    for (const [packageDescriptor, allowedScript] of Object.entries(allowScripts ?? {})) {
-      switch (allowedScript) {
-        case true:
-          onlyBuiltDependencies.push(packageDescriptor);
-          break;
-        case false:
-          ignoredBuiltDependencies.push(packageDescriptor);
-          break;
-        default:
-          // Ignore any non-boolean values. String values are placeholders that the user
-          // should replace with booleans.
-          // pnpm will print a warning about these during installation.
-          break;
-      }
Evidence
The function comment explicitly states allowBuilds is ignored when dangerouslyAllowAllBuilds is set,
yet the code sets a deny entry in allowBuilds and returns dangerouslyAllowAllBuilds: true in that
same branch; therefore the deny entry will not take effect under the stated behavior.

scopes/dependencies/pnpm/lynx.ts[422-434]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`resolveScriptPolicies()` returns `{ dangerouslyAllowAllBuilds: true, allowBuilds }` in the same branch where it sets `allowBuilds['core-js'] = false`, but the adjacent comment states that pnpm ignores `allowBuilds` when `dangerouslyAllowAllBuilds` is set. This makes the default deny entry ineffective.
### Issue Context
The intent (based on existing policy and comments) is to keep a safe default deny list (at least `core-js`) even when `dangerouslyAllowAllScripts` is enabled.
### Fix Focus Areas
- scopes/dependencies/pnpm/lynx.ts[422-439]
### Suggested fix
Adjust the returned options so that the deny entry is actually honored:
- Do **not** set `dangerouslyAllowAllBuilds` in the code path where you need `allowBuilds` to enforce denies (e.g. for `core-js`).
- Alternatively, if pnpm v11 provides a supported way to express “allow all except these denies”, use that API and add/adjust a regression test for the default `core-js` behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
3. Init precondition unguarded 🐞 Bug ☼ Reliability
Description
lockfile-deps-graph-converter.ts now relies on module-level dp/getLockfileImporterId variables that
are only populated via an async init(), but converter functions use dp synchronously without a
runtime guard. If any caller misses/forgets the init() precondition, this will crash at runtime with
an opaque TypeError rather than a clear error.
Code

scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[R25-43]

+// @pnpm/deps.path and @pnpm/lockfile.fs are ESM-only; load them through a .cjs
+// shim so the require() chain in the build capsule's mocha runner doesn't trip
+// on the transitive ESM import. Call `init()` once before invoking the public
+// converters; helpers reach for these module-level slots synchronously.
+let dp!: typeof Dp;
+let getLockfileImporterId!: typeof GetLockfileImporterId;
+let loading: Promise<void> | undefined;
+
+export function init(): Promise<void> {
+  loading ??= (async () => {
+    const { loadEsm } = require('./load-pnpm-esm.cjs') as {
+      loadEsm: () => Promise<{ dp: typeof Dp; getLockfileImporterId: typeof GetLockfileImporterId }>;
+    };
+    const m = await loadEsm();
+    dp = m.dp;
+    getLockfileImporterId = m.getLockfileImporterId;
+  })();
+  return loading;
+}
Evidence
The module-level slots are declared uninitialized and assigned only inside init(); later code uses
dp.refToRelative synchronously, so calling converters before init() will dereference an unset dp
variable.

scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[25-43]
scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[64-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The module introduces an `init()` that asynchronously sets module-level `dp` and `getLockfileImporterId`, but exported converter functions access `dp.*` synchronously and assume `init()` has been awaited.
### Issue Context
Current call sites appear to call `await initLockfileDepsGraphConverter()`, but this is a fragile contract for exported utilities and will be easy to violate in future usages.
### Fix Focus Areas
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[25-43]
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[64-75]
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[77-110]
### Suggested fix
- Add a small runtime guard (e.g. `assertInitialized()`) that checks `dp`/`getLockfileImporterId` are set and throws a clear, actionable error message ("call and await init() before convert*").
- Optionally, in async entry points (e.g. `convertGraphToLockfile`) call `await init()` internally as a safety net.
- Keep `convertLockfileToGraph` sync, but guard it with the explicit error to avoid a cryptic crash.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit N/A


🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. ESM import in CJS 🐞 Bug ☼ Reliability
Description
DependenciesGraph now statically imports @pnpm/deps.path, which this PR itself documents as
ESM-only; when Babel transpiles this file to CommonJS, it will become a require() and can crash at
runtime with ERR_REQUIRE_ESM. This can break dependency graph operations anywhere
@teambit/objects is loaded in the CJS-transpiled execution path (e.g. mocha via babel-register).
Code

scopes/scope/objects/models/dependencies-graph.ts[3]

+import * as dp from '@pnpm/deps.path';
Evidence
The modified file now imports @pnpm/deps.path statically. The PR-added shim explicitly states
@pnpm/deps.path is ESM-only and must be loaded via Node’s ESM loader (dynamic import()), while
the Babel configuration used by babel-register transforms TS modules to CommonJS, turning the
static import into a require() which is incompatible with ESM-only packages.

scopes/scope/objects/models/dependencies-graph.ts[1-4]
scopes/dependencies/pnpm/load-pnpm-esm.cjs[3-10]
babel.config.js[19-26]
babel-register.js[1-1]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scopes/scope/objects/models/dependencies-graph.ts` now statically imports `@pnpm/deps.path`. The PR also states `@pnpm/deps.path` is ESM-only, while the repo’s Babel setup transpiles TS modules to CommonJS (turning `import` into `require()`), which can throw `ERR_REQUIRE_ESM` at runtime.
### Issue Context
- This repo uses `@babel/plugin-transform-modules-commonjs` (via `babel-register`) in at least some execution paths.
- The PR already introduced a CJS shim + dynamic `import()` loader for ESM-only pnpm modules elsewhere, which suggests the same problem applies here.
### Fix Focus Areas
- scopes/scope/objects/models/dependencies-graph.ts[1-120]
- scopes/dependencies/pnpm/load-pnpm-esm.cjs[1-12]
- babel.config.js[1-31]
- babel-register.js[1-1]
### Suggested fix directions
Pick one:
1) **Remove the dependency on `@pnpm/deps.path` here** by implementing a small local parser for the *specific* need (extracting `version` from a depPath string) so this code stays synchronous and CJS-safe.
2) **Mirror the shim approach** used in `lockfile-deps-graph-converter.ts`: add a local loader wrapper for `@pnpm/deps.path` and ensure it is initialized before any `DependenciesGraph` code path that calls `dp.parse()`. If you choose this route, add a clear runtime guard/error message when not initialized (instead of an opaque TypeError).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Core-js deny ignored 🐞 Bug ⛨ Security
Description
In resolveScriptPolicies(), the branch that sets a default deny for "core-js" also returns
dangerouslyAllowAllBuilds: true even though the code comment states pnpm ignores allowBuilds when
dangerouslyAllowAllBuilds is set. This makes the default deny ineffective and can unintentionally
allow core-js build scripts to run when dangerouslyAllowAllScripts is enabled without an explicit
neverBuiltDependencies list.
Code

scopes/dependencies/pnpm/lynx.ts[R428-434]

if (dangerouslyAllowAllScripts) {
-    if (resolvedNeverBuilt == null) {
-      // If neverBuiltDependencies is not explicitly set, use a default list
-      // we tell pnpm to allow all scripts to be executed, except the packages listed below.
-      resolvedNeverBuilt = ['core-js'];
+    // pnpm v11's createAllowBuildFunction ignores allowBuilds when dangerouslyAllowAllBuilds is
+    // set, so emit allowBuilds with just the deny entries whenever a deny list exists.
+    if (!neverBuiltDependencies?.length) {
+      allowBuilds['core-js'] = false;
+      return { dangerouslyAllowAllBuilds: true, allowBuilds };
}
-  } else {
-    onlyBuiltDependencies = [];
-    ignoredBuiltDependencies = [];
-    for (const [packageDescriptor, allowedScript] of Object.entries(allowScripts ?? {})) {
-      switch (allowedScript) {
-        case true:
-          onlyBuiltDependencies.push(packageDescriptor);
-          break;
-        case false:
-          ignoredBuiltDependencies.push(packageDescriptor);
-          break;
-        default:
-          // Ignore any non-boolean values. String values are placeholders that the user
-          // should replace with booleans.
-          // pnpm will print a warning about these during installation.
-          break;
-      }
Evidence
The function comment explicitly states allowBuilds is ignored when dangerouslyAllowAllBuilds is set,
yet the code sets a deny entry in allowBuilds and returns dangerouslyAllowAllBuilds: true in that
same branch; therefore the deny entry will not take effect under the stated behavior.

scopes/dependencies/pnpm/lynx.ts[422-434]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`resolveScriptPolicies()` returns `{ dangerouslyAllowAllBuilds: true, allowBuilds }` in the same branch where it sets `allowBuilds['core-js'] = false`, but the adjacent comment states that pnpm ignores `allowBuilds` when `dangerouslyAllowAllBuilds` is set. This makes the default deny entry ineffective.
### Issue Context
The intent (based on existing policy and comments) is to keep a safe default deny list (at least `core-js`) even when `dangerouslyAllowAllScripts` is enabled.
### Fix Focus Areas
- scopes/dependencies/pnpm/lynx.ts[422-439]
### Suggested fix
Adjust the returned options so that the deny entry is actually honored:
- Do **not** set `dangerouslyAllowAllBuilds` in the code path where you need `allowBuilds` to enforce denies (e.g. for `core-js`).
- Alternatively, if pnpm v11 provides a supported way to express “allow all except these denies”, use that API and add/adjust a regression test for the default `core-js` behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
3. Init precondition unguarded 🐞 Bug ☼ Reliability
Description
lockfile-deps-graph-converter.ts now relies on module-level dp/getLockfileImporterId variables that
are only populated via an async init(), but converter functions use dp synchronously without a
runtime guard. If any caller misses/forgets the init() precondition, this will crash at runtime with
an opaque TypeError rather than a clear error.
Code

scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[R25-43]

+// @pnpm/deps.path and @pnpm/lockfile.fs are ESM-only; load them through a .cjs
+// shim so the require() chain in the build capsule's mocha runner doesn't trip
+// on the transitive ESM import. Call `init()` once before invoking the public
+// converters; helpers reach for these module-level slots synchronously.
+let dp!: typeof Dp;
+let getLockfileImporterId!: typeof GetLockfileImporterId;
+let loading: Promise<void> | undefined;
+
+export function init(): Promise<void> {
+  loading ??= (async () => {
+    const { loadEsm } = require('./load-pnpm-esm.cjs') as {
+      loadEsm: () => Promise<{ dp: typeof Dp; getLockfileImporterId: typeof GetLockfileImporterId }>;
+    };
+    const m = await loadEsm();
+    dp = m.dp;
+    getLockfileImporterId = m.getLockfileImporterId;
+  })();
+  return loading;
+}
Evidence
The module-level slots are declared uninitialized and assigned only inside init(); later code uses
dp.refToRelative synchronously, so calling converters before init() will dereference an unset dp
variable.

scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[25-43]
scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[64-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The module introduces an `init()` that asynchronously sets module-level `dp` and `getLockfileImporterId`, but exported converter functions access `dp.*` synchronously and assume `init()` has been awaited.
### Issue Context
Current call sites appear to call `await initLockfileDepsGraphConverter()`, but this is a fragile contract for exported utilities and will be easy to violate in future usages.
### Fix Focus Areas
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[25-43]
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[64-75]
- scopes/dependencies/pnpm/lockfile-deps-graph-converter.ts[77-110]
### Suggested fix
- Add a small runtime guard (e.g. `assertInitialized()`) that checks `dp`/`getLockfileImporterId` are set and throws a clear, actionable error message ("call and await init() before convert*").
- Optionally, in async entry points (e.g. `convertGraphToLockfile`) call `await init()` internally as a safety net.
- Keep `convertLockfileToGraph` sync, but guard it with the explicit error to avoid a cryptic crash.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment on lines 428 to 434
if (dangerouslyAllowAllScripts) {
if (resolvedNeverBuilt == null) {
// If neverBuiltDependencies is not explicitly set, use a default list
// we tell pnpm to allow all scripts to be executed, except the packages listed below.
resolvedNeverBuilt = ['core-js'];
// pnpm v11's createAllowBuildFunction ignores allowBuilds when dangerouslyAllowAllBuilds is
// set, so emit allowBuilds with just the deny entries whenever a deny list exists.
if (!neverBuiltDependencies?.length) {
allowBuilds['core-js'] = false;
return { dangerouslyAllowAllBuilds: true, allowBuilds };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Core-js deny ignored 🐞 Bug ⛨ Security

In resolveScriptPolicies(), the branch that sets a default deny for "core-js" also returns
dangerouslyAllowAllBuilds: true even though the code comment states pnpm ignores allowBuilds when
dangerouslyAllowAllBuilds is set. This makes the default deny ineffective and can unintentionally
allow core-js build scripts to run when dangerouslyAllowAllScripts is enabled without an explicit
neverBuiltDependencies list.
Agent Prompt
### Issue description
`resolveScriptPolicies()` returns `{ dangerouslyAllowAllBuilds: true, allowBuilds }` in the same branch where it sets `allowBuilds['core-js'] = false`, but the adjacent comment states that pnpm ignores `allowBuilds` when `dangerouslyAllowAllBuilds` is set. This makes the default deny entry ineffective.

### Issue Context
The intent (based on existing policy and comments) is to keep a safe default deny list (at least `core-js`) even when `dangerouslyAllowAllScripts` is enabled.

### Fix Focus Areas
- scopes/dependencies/pnpm/lynx.ts[422-439]

### Suggested fix
Adjust the returned options so that the deny entry is actually honored:
- Do **not** set `dangerouslyAllowAllBuilds` in the code path where you need `allowBuilds` to enforce denies (e.g. for `core-js`).
- Alternatively, if pnpm v11 provides a supported way to express “allow all except these denies”, use that API and add/adjust a regression test for the default `core-js` behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 16, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 03d4bc6

import semver from 'semver';
import type { LockfilePackageInfo } from '@pnpm/lockfile.types';
import * as dp from '@pnpm/dependency-path';
import * as dp from '@pnpm/deps.path';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Esm import in cjs 🐞 Bug ☼ Reliability

DependenciesGraph now statically imports @pnpm/deps.path, which this PR itself documents as
ESM-only; when Babel transpiles this file to CommonJS, it will become a require() and can crash at
runtime with ERR_REQUIRE_ESM. This can break dependency graph operations anywhere
@teambit/objects is loaded in the CJS-transpiled execution path (e.g. mocha via babel-register).
Agent Prompt
### Issue description
`scopes/scope/objects/models/dependencies-graph.ts` now statically imports `@pnpm/deps.path`. The PR also states `@pnpm/deps.path` is ESM-only, while the repo’s Babel setup transpiles TS modules to CommonJS (turning `import` into `require()`), which can throw `ERR_REQUIRE_ESM` at runtime.

### Issue Context
- This repo uses `@babel/plugin-transform-modules-commonjs` (via `babel-register`) in at least some execution paths.
- The PR already introduced a CJS shim + dynamic `import()` loader for ESM-only pnpm modules elsewhere, which suggests the same problem applies here.

### Fix Focus Areas
- scopes/scope/objects/models/dependencies-graph.ts[1-120]
- scopes/dependencies/pnpm/load-pnpm-esm.cjs[1-12]
- babel.config.js[1-31]
- babel-register.js[1-1]

### Suggested fix directions
Pick one:
1) **Remove the dependency on `@pnpm/deps.path` here** by implementing a small local parser for the *specific* need (extracting `version` from a depPath string) so this code stays synchronous and CJS-safe.
2) **Mirror the shim approach** used in `lockfile-deps-graph-converter.ts`: add a local loader wrapper for `@pnpm/deps.path` and ensure it is initialized before any `DependenciesGraph` code path that calls `dp.parse()`. If you choose this route, add a clear runtime guard/error message when not initialized (instead of an opaque TypeError).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 16, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 93dcf91

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

Sorry, something went wrong

We weren't able to complete the code review on our side. Please try again

Grey Divider

Qodo Logo

Migrate Bit's pnpm integration to the latest v11 (11xx) line of @pnpm/*
packages. Adapts the dependency-resolver and pnpm aspects to upstream
API changes, loads ESM-only @pnpm modules via .cjs shims, and refreshes
the lockfile and workspace.jsonc pins accordingly.

All @pnpm/* deps are bumped to their newest 11xx release, including the
1102.x store/resolver/installer line and worker 1100.2.2 (the new
content-addressable store generation). lynx.ts therefore keeps the
frozenStore flag and the scope-keyed auth translation that the new
generation's store.controller / network.auth-header expect.

Exception: @pnpm/config.reader is held at 1101.3.1, the last release
that depends on @pnpm/config.env-replace 3.x. Newer config.reader needs
env-replace ^4.1.0, which bit.cloud's registry mirror does not carry.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 637e002

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 767e5ae

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants