Skip to content

feat(deps): resolve env peer dependencies from workspace root#10227

Open
ranm8 wants to merge 10 commits intomasterfrom
feat/resolve-env-peers-from-root
Open

feat(deps): resolve env peer dependencies from workspace root#10227
ranm8 wants to merge 10 commits intomasterfrom
feat/resolve-env-peers-from-root

Conversation

@ranm8
Copy link
Member

@ranm8 ranm8 commented Mar 16, 2026

Summary

  • Merge env peers to workspace root: Non-conflicting env peer dependencies are resolved once and merged into the workspace root manifest, instead of being injected into each component's manifest individually. This reduces manifest bloat and improves IDE resolution.
  • workspaceSingleton support: New per-peer flag in env.jsonc that forces single-version resolution at workspace root even when envs conflict. Useful for @types/* packages and workspace-level tools (eslint, prettier).
  • overrides support: New per-peer boolean in env.jsonc that generates a pnpm override for that peer, forcing all transitive dependencies to use the same version. workspace.jsonc overrides take precedence.
  • Conflict handling: Conflicting peers without workspaceSingleton are injected per-component, allowing different envs to use different versions (e.g. React 18 and React 19 in the same workspace).
  • workspace.jsonc priority: workspace.jsonc policy always takes priority over env peer dependencies.
  • Better error messages: env.jsonc parse errors now include the file path and env name.

New workspace.jsonc config options

Option Default Description
resolveEnvPeersFromRoot true Merge env peers to workspace root. Set false for legacy per-component injection.
forceEnvPeersToRoot false Force ALL env peers to root, even conflicting ones.

New env.jsonc peer entry properties

{
  "policy": {
    "peers": [
      {
        "name": "@types/react",
        "version": "18.3.1",
        "supportedRange": "^18.0.0",
        "workspaceSingleton": true  // force single version at workspace root
      },
      {
        "name": "react",
        "version": "19.0.0",
        "supportedRange": "^19.0.0",
        "overrides": true  // generate pnpm override to pin transitive deps
      }
    ]
  }
}

Tested

  • bit install works on complex workspaces with multiple envs — env peers merged to root correctly
  • workspaceSingleton: true resolves conflicts to a single version at root
  • overrides: true generates pnpm overrides
  • workspace.jsonc policy takes priority over env peers
  • Improved error messages for broken env.jsonc files show file path and env name

🤖 Generated with Claude Code

Redesign how Bit resolves env peer dependencies in workspaces:

- Merge non-conflicting env peers to workspace root manifest instead of
  injecting per-component, reducing manifest bloat
- Support `workspaceSingleton` property on env.jsonc peer entries to force
  single-version resolution at root for packages like @types/* and eslint
- Support `overrides` boolean on env.jsonc peer entries to generate pnpm
  overrides forcing transitive deps to use the same version
- workspace.jsonc policy always takes priority over env peers
- Add `resolveEnvPeersFromRoot` config (default: true) to control the behavior
- Add `forceEnvPeersToRoot` config (default: false) to force all peers to root
- Improve env.jsonc parse error messages to include file path and env name

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 16, 2026 13:34
Copy link
Contributor

Copilot AI left a comment

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 changes how env peer dependencies (from env.jsonc) are resolved during installation by preferring a single resolution at the workspace root, with new controls for handling conflicts and pinning via overrides.

Changes:

  • Add workspace config/schema support for resolveEnvPeersFromRoot and forceEnvPeersToRoot.
  • Extend env peer policy entries with workspaceSingleton and overrides flags and propagate them through policy objects.
  • Update manifest generation + install flow to merge env peers to the root manifest (and collect env-derived pnpm overrides), with improved env.jsonc parse errors.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
workspace-jsonc-schema.json Documents new workspace.jsonc options for env peer resolution behavior.
scopes/workspace/workspace/workspace.ts Wraps env.jsonc parsing with a clearer syntax error.
scopes/workspace/install/install.main.runtime.ts Threads resolveEnvPeersFromRoot into manifest calc and merges env-derived overrides with workspace overrides.
scopes/envs/envs/environments.main.runtime.ts Improves env.jsonc parse error reporting with env name + file path.
scopes/envs/envs/env-jsonc.detector.ts Adds parse error wrapping for env.jsonc dependency detection.
scopes/dependencies/dependency-resolver/policy/variant-policy/variant-policy.ts Adds workspaceSingleton/overrides to policy entry values.
scopes/dependencies/dependency-resolver/policy/env-policy/env-policy.ts Adds new env.jsonc peer entry fields and passes them into variant policy entries; tracks envId.
scopes/dependencies/dependency-resolver/package-manager.ts Adds resolveEnvPeersFromRoot option to install options typing.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest.ts Treats env peers as “defaults” when writing the root manifest.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts Implements root merging, conflict handling, per-component injection, and env-derived overrides collection.
scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts Wires workspace-level config into WorkspaceManifestFactory and passes envId into EnvPolicy.
scopes/dependencies/dependency-resolver/dependency-resolver-workspace-config.ts Adds config interface docs for the new options.
scopes/dependencies/dependency-resolver/dependency-installer.ts Returns { manifests, peerOverrides } from manifest calculation and threads resolveEnvPeersFromRoot to workspace manifest writing.

You can also share your feedback on Copilot code review. Take the survey.

ranm8 and others added 2 commits March 16, 2026 15:48
Env peer dependencies that are also workspace components should not be
added to the root manifest — they are linked locally, not installed.
This matches the existing filter in _getEnvPeerDependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of repeating the hint per conflict, show a single message
after all warnings explaining how to pin a version in workspace.jsonc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 16, 2026 13:54
Copy link
Contributor

Copilot AI left a comment

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 changes how env peer dependencies (from env.jsonc) are resolved and applied during installation, prioritizing a single workspace-root resolution path to reduce per-component manifest bloat while adding opt-in knobs for singleton resolution and pnpm overrides.

Changes:

  • Add workspace config + schema options to control resolving env peers from the workspace root and forcing conflicts to root.
  • Extend env policy/variant policy entries to carry workspaceSingleton and overrides flags, and implement root-merge + per-component fallback for conflicting peers.
  • Thread env-derived pnpm overrides into install options, and improve env.jsonc parse error handling.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
workspace-jsonc-schema.json Documents new workspace config options for env peer resolution behavior.
scopes/workspace/workspace/workspace.ts Adds try/catch around env.jsonc parsing and improves error reporting.
scopes/workspace/install/install.main.runtime.ts Passes resolveEnvPeersFromRoot to manifest calculation and merges env-derived pnpm overrides with workspace overrides.
scopes/envs/envs/environments.main.runtime.ts Adds contextualized parse errors when calculating env manifests.
scopes/envs/envs/env-jsonc.detector.ts Adds explicit parse failure error for env.jsonc detector.
scopes/dependencies/dependency-resolver/policy/variant-policy/variant-policy.ts Adds workspaceSingleton / overrides to policy entry values and wires them into entry creation.
scopes/dependencies/dependency-resolver/policy/env-policy/env-policy.ts Adds peer entry flags and carries envId on EnvPolicy for later resolution.
scopes/dependencies/dependency-resolver/package-manager.ts Adds resolveEnvPeersFromRoot to install options type plumbing.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest.ts Ensures env peers can be merged into the root manifest (as defaults) and stores env-derived overrides.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts Implements env peer merge-to-root logic, conflict resolution, per-component injection for non-singleton conflicts, and override collection.
scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts Instantiates manifest factory with new flags and passes envId into env policy creation.
scopes/dependencies/dependency-resolver/dependency-resolver-workspace-config.ts Adds workspace config interface fields + docs for the new behavior.
scopes/dependencies/dependency-resolver/dependency-installer.ts Updates manifests computation to return env-derived overrides alongside manifests and passes new option into root manifest generation.

You can also share your feedback on Copilot code review. Take the survey.

ranm8 and others added 2 commits March 16, 2026 16:11
…sonc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use console log with emoji and newlines so the hint stands out
from the yellow warning messages above it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 16, 2026 14:37
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements a new dependency-resolution flow where env peer dependencies from env.jsonc are (by default) resolved once and merged into the workspace root manifest, with new configuration knobs and metadata (workspaceSingleton, overrides) to control conflict handling and pnpm overrides.

Changes:

  • Add resolveEnvPeersFromRoot / forceEnvPeersToRoot workspace configuration and schema support.
  • Extend env peer entries with workspaceSingleton and overrides, and propagate these through env/variant policy types.
  • Update install + manifest generation pipeline to (a) merge non-conflicting env peers to root, (b) inject conflicting peers per-component, and (c) generate pnpm overrides derived from env peers.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
workspace-jsonc-schema.json Adds schema entries for the new workspace config flags.
scopes/workspace/workspace/workspace.ts Wraps env.jsonc parsing with improved error handling.
scopes/workspace/install/install.main.runtime.ts Passes resolveEnvPeersFromRoot into manifest calc and merges env-derived pnpm overrides.
scopes/envs/envs/environments.main.runtime.ts Adds file/env-contextual env.jsonc parse error message.
scopes/envs/envs/env-jsonc.detector.ts Adds parse error handling for env.jsonc detector.
scopes/dependencies/dependency-resolver/policy/variant-policy/variant-policy.ts Extends policy entry value with workspaceSingleton and overrides.
scopes/dependencies/dependency-resolver/policy/env-policy/env-policy.ts Adds peer entry flags + threads env id through EnvPolicy.
scopes/dependencies/dependency-resolver/package-manager.ts Adds resolveEnvPeersFromRoot to install options typing.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest.ts Changes root manifest env-peer merge behavior to respect workspace policy precedence.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts Implements root-merge vs per-component injection + override generation + conflict resolution logic.
scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts Wires new config flags into WorkspaceManifestFactory; passes env id into EnvPolicy.
scopes/dependencies/dependency-resolver/dependency-resolver-workspace-config.ts Documents new config flags in the workspace config interface.
scopes/dependencies/dependency-resolver/dependency-installer.ts Returns { manifests, peerOverrides } from getComponentManifests() and passes new option through.

You can also share your feedback on Copilot code review. Take the survey.

- overrides:true now implies singleton (forces single version at root)
- Fix semver.coerce bug: use .version string instead of SemVer object
- Guard semver.satisfies with semver.validRange to prevent crashes
- Skip "+" version placeholders from peerOverrides and component injection
- Check all dep sections (dev/peer/optional) before adding env peers to root
- Use BitError instead of plain Error for consistent CLI error handling
- Include file path in env.jsonc parse error messages
- Fix error messages to say "JSONC" instead of "JSON"
- Fix docs to say "envs" instead of "components" for conflict resolution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
envSelfPeers = result.rootPolicy;
peerOverrides = result.peerOverrides;
if (result.componentPeerOverrides.size > 0) {
this.injectConflictingPeersToComponents(componentsManifestsMap, result.componentPeerOverrides);
Copy link
Member

Choose a reason for hiding this comment

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

we were talking with Gilad about possibly deprecating root components, if this approach works. But if we are injecting conflicting peers to components, then we do need root components to remain. Could we possibly be more strict and only allow singletons? If someone needs to work with a different set of peer dependencies, they could create a new workspace.

* forcing all transitive dependencies to use the same version.
* Useful to prevent old versions from being pulled by published packages.
*/
overrides?: boolean;
Copy link
Member

Choose a reason for hiding this comment

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

"override" might be a better name as "overrides" is used for the map of overrides. Or something more descriptive to make the difference between the two fields clear. Like "overrideSubdeps" and "overrideRootDeps".

Copilot AI review requested due to automatic review settings March 18, 2026 13:31
Copy link
Contributor

Copilot AI left a comment

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 changes how env (env.jsonc) peer dependencies are resolved during installation by defaulting to a workspace-root merge strategy (with conflict handling, optional forced-root behavior, and pnpm overrides support), while also improving env.jsonc JSONC parse error reporting.

Changes:

  • Add workspace-level config (resolveEnvPeersFromRoot, forceEnvPeersToRoot) and implement root-merge + conflict handling for env peer dependencies, with per-component fallback when conflicts aren’t forced/singleton.
  • Add per-peer flags (workspaceSingleton, overrides) to env peer policy entries and propagate them through the policy model to drive root resolution + pnpm overrides generation.
  • Improve env.jsonc parse error messages to include env identity + file path for faster debugging.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
workspace-jsonc-schema.json Documents new workspace.jsonc dependency-resolver options for env peer resolution behavior.
scopes/workspace/workspace/workspace.ts Adds env.jsonc JSONC parse error handling that includes env id + file path.
scopes/workspace/install/install.main.runtime.ts Threads resolveEnvPeersFromRoot into manifest calculation and merges env-generated peer overrides with workspace overrides.
scopes/envs/envs/environments.main.runtime.ts Improves env.jsonc parse errors with env name/path context and consistent BitError formatting.
scopes/envs/envs/env-jsonc.detector.ts Adds guarded parsing with a clearer error when env.jsonc cannot be parsed.
scopes/dependencies/dependency-resolver/policy/variant-policy/variant-policy.ts Extends policy entry value to carry workspaceSingleton / overrides metadata.
scopes/dependencies/dependency-resolver/policy/env-policy/env-policy.ts Adds envId tracking to EnvPolicy and parses new peer flags from env.jsonc.
scopes/dependencies/dependency-resolver/package-manager.ts Adds resolveEnvPeersFromRoot to install options shape for plumbing.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest.ts Emits env peers to root manifest as “defaults” while respecting existing workspace policy across all dep sections; resolves + placeholders.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts Implements env peer root-merge algorithm, conflict resolution, per-component injection for non-singleton conflicts, and pnpm overrides collection.
scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts Instantiates manifest factory with root-merge flags; passes envId into EnvPolicy creation.
scopes/dependencies/dependency-resolver/dependency-resolver-workspace-config.ts Adds new workspace config options to the typed config interface.
scopes/dependencies/dependency-resolver/dependency-installer.ts Changes manifests computation to return { manifests, peerOverrides } and plumbs resolveEnvPeersFromRoot into root manifest generation.

Comment on lines 342 to +345
manifests[rootDir] = workspaceManifest.toJson({
copyPeerToRuntime: copyPeerToRuntimeOnRoot,
installPeersFromEnvs,
resolveEnvPeersFromRoot,
Copilot AI review requested due to automatic review settings March 18, 2026 19:25
Copy link
Contributor

Copilot AI left a comment

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 changes Bit’s dependency resolution so env peer dependencies from env.jsonc are generally resolved once and merged into the workspace root manifest (instead of being injected into every component manifest), with new config flags to control conflict handling and new per-peer workspaceSingleton / overrides behaviors.

Changes:

  • Add workspace-level config to control root-merging of env peers (resolveEnvPeersFromRoot, forceEnvPeersToRoot) and implement root-merge + conflict handling logic.
  • Add per-peer flags in env policy (workspaceSingleton, overrides) and plumb through to resolution/installation (including generating pnpm overrides).
  • Improve env.jsonc JSONC parse errors with env name + file path context.

Reviewed changes

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

Show a summary per file
File Description
workspace-jsonc-schema.json Documents the new workspace.jsonc config flags in the schema.
scopes/workspace/workspace/workspace.ts Improves env.jsonc parse error messaging (env id + file path).
scopes/workspace/install/install.main.runtime.ts Threads resolveEnvPeersFromRoot into manifest calculation and merges env-generated overrides with workspace overrides.
scopes/envs/envs/environments.main.runtime.ts Improves env.jsonc parse error messaging (env name + file path) using BitError.
scopes/envs/envs/env-jsonc.detector.ts Wraps env.jsonc parsing with a clearer error message.
scopes/dependencies/dependency-resolver/policy/variant-policy/variant-policy.ts Extends policy entry values to carry workspaceSingleton / overrides.
scopes/dependencies/dependency-resolver/policy/env-policy/env-policy.ts Adds env peer entry flags + threads envId for better reporting/merging.
scopes/dependencies/dependency-resolver/package-manager.ts Adds resolveEnvPeersFromRoot to install options plumbing.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest.ts Merges env peers into root manifest as “defaults” while preserving workspace policy priority; carries peerOverrides.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts Implements root-merge algorithm, conflict resolution, per-component injection for conflicts, and override generation.
scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts Wires the new config flags into workspace manifest creation and passes envId into EnvPolicy.
scopes/dependencies/dependency-resolver/dependency-resolver-workspace-config.ts Adds the two new workspace config options with documentation comments.
scopes/dependencies/dependency-resolver/dependency-installer.ts Updates manifest generation API to return peerOverrides alongside manifests and passes the new flag through.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +225 to +231
// Skip "+" placeholder — it's resolved at the workspace root level
if (version === '+') continue;
for (const envId of envIds) {
if (!componentPeerOverrides.has(envId)) {
componentPeerOverrides.set(envId, {});
}
componentPeerOverrides.get(envId)![pkgName] = version;
Comment on lines +287 to +311
// Filter out non-semver versions (like '+') for comparison
const semverVersions = versions.filter((v) => v !== '+' && semver.coerce(v) !== null);

if (semverVersions.length === 0) {
// All versions are non-semver (e.g., '+'), just pick the first
return versions[0];
}

// For each version, count how many envs it could satisfy
let bestVersion = semverVersions[0];
let bestCount = 0;

for (const candidateVersion of semverVersions) {
const coercedCandidate = semver.coerce(candidateVersion);
if (!coercedCandidate) continue;
let satisfiedCount = 0;
for (const [version, envIds] of envsByVersion) {
if (version === candidateVersion || version === '+') {
satisfiedCount += envIds.size;
} else if (semver.validRange(version)) {
// Check if the candidate could satisfy the range
if (semver.satisfies(coercedCandidate.version, version)) {
satisfiedCount += envIds.size;
}
}
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.

5 participants