feat(deps): resolve env peer dependencies from workspace root#10227
feat(deps): resolve env peer dependencies from workspace root#10227
Conversation
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>
There was a problem hiding this comment.
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
resolveEnvPeersFromRootandforceEnvPeersToRoot. - Extend env peer policy entries with
workspaceSingletonandoverridesflags 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.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts
Show resolved
Hide resolved
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts
Outdated
Show resolved
Hide resolved
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts
Show resolved
Hide resolved
scopes/dependencies/dependency-resolver/manifest/workspace-manifest.ts
Outdated
Show resolved
Hide resolved
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts
Show resolved
Hide resolved
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>
There was a problem hiding this comment.
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
workspaceSingletonandoverridesflags, and implement root-merge + per-component fallback for conflicting peers. - Thread env-derived pnpm overrides into install options, and improve
env.jsoncparse 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.
scopes/dependencies/dependency-resolver/dependency-resolver-workspace-config.ts
Show resolved
Hide resolved
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts
Show resolved
Hide resolved
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts
Show resolved
Hide resolved
…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>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
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/forceEnvPeersToRootworkspace configuration and schema support. - Extend env peer entries with
workspaceSingletonandoverrides, 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.
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts
Show resolved
Hide resolved
scopes/dependencies/dependency-resolver/manifest/workspace-manifest.ts
Outdated
Show resolved
Hide resolved
- 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); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
"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".
There was a problem hiding this comment.
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. |
| manifests[rootDir] = workspaceManifest.toJson({ | ||
| copyPeerToRuntime: copyPeerToRuntimeOnRoot, | ||
| installPeersFromEnvs, | ||
| resolveEnvPeersFromRoot, |
There was a problem hiding this comment.
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.jsoncJSONC 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.
| // 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; |
| // 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; | ||
| } | ||
| } |
Summary
workspaceSingletonsupport: 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).overridessupport: 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.workspaceSingletonare injected per-component, allowing different envs to use different versions (e.g. React 18 and React 19 in the same workspace).New workspace.jsonc config options
resolveEnvPeersFromRoottruefalsefor legacy per-component injection.forceEnvPeersToRootfalseNew 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 installworks on complex workspaces with multiple envs — env peers merged to root correctlyworkspaceSingleton: trueresolves conflicts to a single version at rootoverrides: truegenerates pnpm overrides🤖 Generated with Claude Code