@teambit/dependency-resolver 1.0.912 → 1.0.913
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dependency-installer.d.ts +5 -2
- package/dist/dependency-installer.js +10 -3
- package/dist/dependency-installer.js.map +1 -1
- package/dist/dependency-resolver-workspace-config.d.ts +17 -0
- package/dist/dependency-resolver-workspace-config.js.map +1 -1
- package/dist/dependency-resolver.main.runtime.js +9 -4
- package/dist/dependency-resolver.main.runtime.js.map +1 -1
- package/dist/manifest/workspace-manifest-factory.d.ts +20 -1
- package/dist/manifest/workspace-manifest-factory.js +223 -30
- package/dist/manifest/workspace-manifest-factory.js.map +1 -1
- package/dist/manifest/workspace-manifest.d.ts +3 -1
- package/dist/manifest/workspace-manifest.js +10 -3
- package/dist/manifest/workspace-manifest.js.map +1 -1
- package/dist/package-manager.d.ts +1 -0
- package/dist/package-manager.js.map +1 -1
- package/dist/policy/env-policy/env-policy.d.ts +16 -2
- package/dist/policy/env-policy/env-policy.js +12 -4
- package/dist/policy/env-policy/env-policy.js.map +1 -1
- package/dist/policy/variant-policy/variant-policy.d.ts +2 -0
- package/dist/policy/variant-policy/variant-policy.js +5 -1
- package/dist/policy/variant-policy/variant-policy.js.map +1 -1
- package/dist/{preview-1773927560597.js → preview-1773942498396.js} +2 -2
- package/manifest/workspace-manifest-factory.ts +277 -34
- package/manifest/workspace-manifest.ts +14 -3
- package/package.json +8 -8
- package/policy/env-policy/env-policy.ts +24 -4
- package/policy/variant-policy/variant-policy.ts +6 -0
|
@@ -4,13 +4,16 @@ import { IssuesClasses } from '@teambit/component-issues';
|
|
|
4
4
|
import type { Component } from '@teambit/component';
|
|
5
5
|
import { componentIdToPackageName } from '@teambit/pkg.modules.component-package-name';
|
|
6
6
|
import { fromPairs, pickBy, mapValues, uniq, difference } from 'lodash';
|
|
7
|
-
import { SemVer } from 'semver';
|
|
7
|
+
import semver, { SemVer } from 'semver';
|
|
8
8
|
import pMapSeries from 'p-map-series';
|
|
9
9
|
import { snapToSemver } from '@teambit/component-package-version';
|
|
10
|
+
import type { Logger } from '@teambit/logger';
|
|
10
11
|
import type { DependencyList, PackageName } from '../dependencies';
|
|
11
12
|
import { ComponentDependency } from '../dependencies';
|
|
12
13
|
import type { WorkspacePolicy, EnvPolicy } from '../policy';
|
|
13
14
|
import { VariantPolicy } from '../policy';
|
|
15
|
+
import type { VariantPolicyEntry } from '../policy/variant-policy';
|
|
16
|
+
import { createVariantPolicyEntry } from '../policy/variant-policy';
|
|
14
17
|
import type { DependencyResolverMain } from '../dependency-resolver.main.runtime';
|
|
15
18
|
import type { ComponentsManifestsMap } from '../types';
|
|
16
19
|
import { ComponentManifest } from './component-manifest';
|
|
@@ -49,7 +52,10 @@ const DEFAULT_CREATE_OPTIONS: CreateFromComponentsOptions = {
|
|
|
49
52
|
export class WorkspaceManifestFactory {
|
|
50
53
|
constructor(
|
|
51
54
|
private dependencyResolver: DependencyResolverMain,
|
|
52
|
-
private aspectLoader: AspectLoaderMain
|
|
55
|
+
private aspectLoader: AspectLoaderMain,
|
|
56
|
+
private logger?: Logger,
|
|
57
|
+
private resolveEnvPeersFromRoot: boolean = true,
|
|
58
|
+
private forceEnvPeersToRoot: boolean = false
|
|
53
59
|
) {}
|
|
54
60
|
|
|
55
61
|
async createFromComponents(
|
|
@@ -97,14 +103,29 @@ export class WorkspaceManifestFactory {
|
|
|
97
103
|
components,
|
|
98
104
|
optsWithDefaults.createManifestForComponentsWithoutDependencies
|
|
99
105
|
);
|
|
100
|
-
|
|
106
|
+
let envSelfPeers: VariantPolicy;
|
|
107
|
+
let peerOverrides: Record<string, string> = {};
|
|
108
|
+
if (this.resolveEnvPeersFromRoot) {
|
|
109
|
+
const workspacePackageNames = new Set(
|
|
110
|
+
components.map((component) => this.dependencyResolver.getPackageName(component))
|
|
111
|
+
);
|
|
112
|
+
const result = this.mergeEnvPeersToRoot(componentsManifestsMap, workspacePackageNames);
|
|
113
|
+
envSelfPeers = result.rootPolicy;
|
|
114
|
+
peerOverrides = result.peerOverrides;
|
|
115
|
+
if (result.componentPeerOverrides.size > 0) {
|
|
116
|
+
this.injectConflictingPeersToComponents(componentsManifestsMap, result.componentPeerOverrides);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
envSelfPeers = this.getEnvsSelfPeersPolicy(componentsManifestsMap);
|
|
120
|
+
}
|
|
101
121
|
const workspaceManifest = new WorkspaceManifest(
|
|
102
122
|
name,
|
|
103
123
|
version,
|
|
104
124
|
dedupedDependencies.rootDependencies,
|
|
105
125
|
envSelfPeers,
|
|
106
126
|
rootDir,
|
|
107
|
-
componentsManifestsMap
|
|
127
|
+
componentsManifestsMap,
|
|
128
|
+
peerOverrides
|
|
108
129
|
);
|
|
109
130
|
return workspaceManifest;
|
|
110
131
|
}
|
|
@@ -116,6 +137,230 @@ export class WorkspaceManifestFactory {
|
|
|
116
137
|
return rootPolicy.filter((dep) => !coreAspectPkgNames.has(dep.dependencyId));
|
|
117
138
|
}
|
|
118
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Merge env peer dependencies to the workspace root.
|
|
142
|
+
* - Non-conflicting peers: always merge to root
|
|
143
|
+
* - Conflicting peers with workspaceSingleton: merge majority to root, warn about unsatisfied envs
|
|
144
|
+
* - Conflicting peers without workspaceSingleton: skip root, return as componentPeerOverrides for per-component injection
|
|
145
|
+
*/
|
|
146
|
+
private mergeEnvPeersToRoot(
|
|
147
|
+
componentsManifestsMap: ComponentsManifestsMap,
|
|
148
|
+
workspacePackageNames: Set<string>
|
|
149
|
+
): {
|
|
150
|
+
rootPolicy: VariantPolicy;
|
|
151
|
+
componentPeerOverrides: Map<string, Record<string, string>>;
|
|
152
|
+
peerOverrides: Record<string, string>;
|
|
153
|
+
} {
|
|
154
|
+
// Collect all env selfPolicies grouped by package name
|
|
155
|
+
// Map<packageName, Map<version, Set<envId>>>
|
|
156
|
+
const peerVersionsMap = new Map<string, Map<string, Set<string>>>();
|
|
157
|
+
// Track workspaceSingleton flag per package (true if ANY env marks it)
|
|
158
|
+
const singletonFlags = new Map<string, boolean>();
|
|
159
|
+
// Track overrides flag per package (true if ANY env marks it)
|
|
160
|
+
const overridesFlags = new Map<string, boolean>();
|
|
161
|
+
|
|
162
|
+
for (const manifest of componentsManifestsMap.values()) {
|
|
163
|
+
const envId = manifest.envPolicy.envId || 'unknown';
|
|
164
|
+
const selfPolicy = manifest.envPolicy.selfPolicy;
|
|
165
|
+
for (const entry of selfPolicy.entries) {
|
|
166
|
+
// Skip peers that are workspace components — they're linked, not installed
|
|
167
|
+
if (workspacePackageNames.has(entry.dependencyId)) continue;
|
|
168
|
+
const pkgName = entry.dependencyId;
|
|
169
|
+
const version = entry.value.version;
|
|
170
|
+
if (!peerVersionsMap.has(pkgName)) {
|
|
171
|
+
peerVersionsMap.set(pkgName, new Map());
|
|
172
|
+
}
|
|
173
|
+
const versionsMap = peerVersionsMap.get(pkgName)!;
|
|
174
|
+
if (!versionsMap.has(version)) {
|
|
175
|
+
versionsMap.set(version, new Set());
|
|
176
|
+
}
|
|
177
|
+
versionsMap.get(version)!.add(envId);
|
|
178
|
+
if (entry.value.workspaceSingleton) {
|
|
179
|
+
singletonFlags.set(pkgName, true);
|
|
180
|
+
}
|
|
181
|
+
if (entry.value.override) {
|
|
182
|
+
overridesFlags.set(pkgName, true);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Resolve each peer dependency
|
|
188
|
+
const resolvedEntries: VariantPolicyEntry[] = [];
|
|
189
|
+
// Map<envId, Record<pkgName, version>> for per-component injection
|
|
190
|
+
const componentPeerOverrides = new Map<string, Record<string, string>>();
|
|
191
|
+
// Collect pnpm overrides from entries with overrides: true
|
|
192
|
+
const peerOverrides: Record<string, string> = {};
|
|
193
|
+
// Track packages with conflicts for the hint message
|
|
194
|
+
const conflictingPackages: string[] = [];
|
|
195
|
+
|
|
196
|
+
for (const [pkgName, versionsMap] of peerVersionsMap.entries()) {
|
|
197
|
+
const versions = Array.from(versionsMap.keys());
|
|
198
|
+
|
|
199
|
+
if (versions.length === 1) {
|
|
200
|
+
// No conflict — merge to root
|
|
201
|
+
const entry = this.createResolvedEntry(componentsManifestsMap, pkgName, versions[0]);
|
|
202
|
+
resolvedEntries.push(entry);
|
|
203
|
+
if (overridesFlags.get(pkgName) && versions[0] !== '+') {
|
|
204
|
+
peerOverrides[pkgName] = versions[0];
|
|
205
|
+
}
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// overrides:true implies singleton — an override forces a single version across the workspace
|
|
210
|
+
const isSingleton =
|
|
211
|
+
this.forceEnvPeersToRoot || (singletonFlags.get(pkgName) ?? false) || (overridesFlags.get(pkgName) ?? false);
|
|
212
|
+
|
|
213
|
+
if (isSingleton) {
|
|
214
|
+
// Conflict + workspaceSingleton: merge majority to root, warn about unsatisfied
|
|
215
|
+
const chosenVersion = this.resolveConflictingPeerVersions(pkgName, versionsMap);
|
|
216
|
+
resolvedEntries.push(this.createResolvedEntry(componentsManifestsMap, pkgName, chosenVersion));
|
|
217
|
+
conflictingPackages.push(pkgName);
|
|
218
|
+
if (overridesFlags.get(pkgName) && chosenVersion !== '+') {
|
|
219
|
+
peerOverrides[pkgName] = chosenVersion;
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
// Conflict + no workspaceSingleton: per-component injection
|
|
223
|
+
// Each env gets its own version injected into its components
|
|
224
|
+
for (const [version, envIds] of versionsMap.entries()) {
|
|
225
|
+
// Skip "+" placeholder — it's resolved at the workspace root level
|
|
226
|
+
if (version === '+') continue;
|
|
227
|
+
for (const envId of envIds) {
|
|
228
|
+
if (!componentPeerOverrides.has(envId)) {
|
|
229
|
+
componentPeerOverrides.set(envId, {});
|
|
230
|
+
}
|
|
231
|
+
componentPeerOverrides.get(envId)![pkgName] = version;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (conflictingPackages.length > 0) {
|
|
238
|
+
this.logger?.console?.(
|
|
239
|
+
`\n💡 To resolve workspace peer version conflicts, pin a version in workspace.jsonc under "teambit.dependencies/dependency-resolver" > "overrides", ` +
|
|
240
|
+
`e.g. "overrides": { "${conflictingPackages[0]}": "<version>" }\n`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
rootPolicy: VariantPolicy.fromArray(resolvedEntries),
|
|
246
|
+
componentPeerOverrides,
|
|
247
|
+
peerOverrides,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private createResolvedEntry(
|
|
252
|
+
componentsManifestsMap: ComponentsManifestsMap,
|
|
253
|
+
pkgName: string,
|
|
254
|
+
version: string
|
|
255
|
+
): VariantPolicyEntry {
|
|
256
|
+
const existingEntry = this.findEntryWithVersion(componentsManifestsMap, pkgName, version);
|
|
257
|
+
if (existingEntry) return existingEntry;
|
|
258
|
+
return createVariantPolicyEntry(pkgName, version, 'runtime', {
|
|
259
|
+
source: 'env-own',
|
|
260
|
+
force: true,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Inject conflicting non-singleton peer deps into component manifests.
|
|
266
|
+
* Each component gets the version specified by its env.
|
|
267
|
+
*/
|
|
268
|
+
private injectConflictingPeersToComponents(
|
|
269
|
+
componentsManifestsMap: ComponentsManifestsMap,
|
|
270
|
+
componentPeerOverrides: Map<string, Record<string, string>>
|
|
271
|
+
): void {
|
|
272
|
+
for (const manifest of componentsManifestsMap.values()) {
|
|
273
|
+
const envId = manifest.envPolicy.envId || 'unknown';
|
|
274
|
+
const overrides = componentPeerOverrides.get(envId);
|
|
275
|
+
if (!overrides) continue;
|
|
276
|
+
manifest.dependencies.dependencies = {
|
|
277
|
+
...overrides,
|
|
278
|
+
...manifest.dependencies.dependencies,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private resolveConflictingPeerVersions(pkgName: string, versionsMap: Map<string, Set<string>>): string {
|
|
284
|
+
const versions = Array.from(versionsMap.keys());
|
|
285
|
+
const envsByVersion = Array.from(versionsMap.entries());
|
|
286
|
+
|
|
287
|
+
// Filter out non-semver versions (like '+') for comparison
|
|
288
|
+
const semverVersions = versions.filter((v) => v !== '+' && semver.coerce(v) !== null);
|
|
289
|
+
|
|
290
|
+
if (semverVersions.length === 0) {
|
|
291
|
+
// All versions are non-semver (e.g., '+'), just pick the first
|
|
292
|
+
return versions[0];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// For each version, count how many envs it could satisfy
|
|
296
|
+
let bestVersion = semverVersions[0];
|
|
297
|
+
let bestCount = 0;
|
|
298
|
+
|
|
299
|
+
for (const candidateVersion of semverVersions) {
|
|
300
|
+
const coercedCandidate = semver.coerce(candidateVersion);
|
|
301
|
+
if (!coercedCandidate) continue;
|
|
302
|
+
let satisfiedCount = 0;
|
|
303
|
+
for (const [version, envIds] of envsByVersion) {
|
|
304
|
+
if (version === candidateVersion || version === '+') {
|
|
305
|
+
satisfiedCount += envIds.size;
|
|
306
|
+
} else if (semver.validRange(version)) {
|
|
307
|
+
// Check if the candidate could satisfy the range
|
|
308
|
+
if (semver.satisfies(coercedCandidate.version, version)) {
|
|
309
|
+
satisfiedCount += envIds.size;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const coercedBest = semver.coerce(bestVersion);
|
|
314
|
+
if (
|
|
315
|
+
satisfiedCount > bestCount ||
|
|
316
|
+
(satisfiedCount === bestCount && coercedBest && semver.gt(coercedCandidate.version, coercedBest.version))
|
|
317
|
+
) {
|
|
318
|
+
bestVersion = candidateVersion;
|
|
319
|
+
bestCount = satisfiedCount;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Only warn about envs whose version is NOT satisfied by the chosen version
|
|
324
|
+
const coercedBestForCheck = semver.coerce(bestVersion);
|
|
325
|
+
const unsatisfiedEnvs = envsByVersion.filter(([version]) => {
|
|
326
|
+
if (version === bestVersion || version === '+') return false;
|
|
327
|
+
if (coercedBestForCheck && semver.validRange(version) && semver.satisfies(coercedBestForCheck.version, version))
|
|
328
|
+
return false;
|
|
329
|
+
return true;
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (unsatisfiedEnvs.length > 0) {
|
|
333
|
+
const allDetails = envsByVersion
|
|
334
|
+
.map(([version, envIds]) => ` ${version} (from envs: ${Array.from(envIds).join(', ')})`)
|
|
335
|
+
.join('\n');
|
|
336
|
+
this.logger?.warn(
|
|
337
|
+
`Conflicting env peer dependency versions for "${pkgName}":\n${allDetails}\n → Resolved to: ${bestVersion}`
|
|
338
|
+
);
|
|
339
|
+
const unsatisfiedDetails = unsatisfiedEnvs
|
|
340
|
+
.map(([version, envIds]) => `${version} (${Array.from(envIds).join(', ')})`)
|
|
341
|
+
.join(', ');
|
|
342
|
+
this.logger?.consoleWarning?.(
|
|
343
|
+
`Conflicting env peer versions for "${pkgName}": using ${bestVersion}, but not compatible with: ${unsatisfiedDetails}`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return bestVersion;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private findEntryWithVersion(
|
|
351
|
+
componentsManifestsMap: ComponentsManifestsMap,
|
|
352
|
+
pkgName: string,
|
|
353
|
+
version: string
|
|
354
|
+
): VariantPolicyEntry | undefined {
|
|
355
|
+
for (const manifest of componentsManifestsMap.values()) {
|
|
356
|
+
const entry = manifest.envPolicy.selfPolicy.entries.find(
|
|
357
|
+
(e) => e.dependencyId === pkgName && e.value.version === version
|
|
358
|
+
);
|
|
359
|
+
if (entry) return entry;
|
|
360
|
+
}
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
|
|
119
364
|
private getEnvsSelfPeersPolicy(componentsManifestsMap: ComponentsManifestsMap) {
|
|
120
365
|
const foundEnvs: EnvPolicy[] = [];
|
|
121
366
|
for (const component of componentsManifestsMap.values()) {
|
|
@@ -215,36 +460,34 @@ export class WorkspaceManifestFactory {
|
|
|
215
460
|
);
|
|
216
461
|
});
|
|
217
462
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (peerDepsForManifest.react && envPeerDependencies['react-dom']) {
|
|
247
|
-
peerDepsForManifest['react-dom'] = envPeerDependencies['react-dom'];
|
|
463
|
+
let peerDepsForManifest: Record<string, string> = {};
|
|
464
|
+
if (!this.resolveEnvPeersFromRoot) {
|
|
465
|
+
// Legacy behavior: inject env peer deps into each component's manifest
|
|
466
|
+
const envPeerDependencies = await this._getEnvPeerDependencies(component, packageNames);
|
|
467
|
+
if (includeAllEnvPeers ?? true) {
|
|
468
|
+
peerDepsForManifest = envPeerDependencies;
|
|
469
|
+
} else {
|
|
470
|
+
peerDepsForManifest = pickBy(envPeerDependencies, (_val, pkgName) => {
|
|
471
|
+
return (
|
|
472
|
+
depManifestBeforeFiltering.dependencies[pkgName] ||
|
|
473
|
+
depManifestBeforeFiltering.devDependencies[pkgName] ||
|
|
474
|
+
depManifestBeforeFiltering.peerDependencies[pkgName]
|
|
475
|
+
);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// In case the env has peer dependencies on both react and react-dom, we want to make sure to keep the versions
|
|
479
|
+
// in sync with each other, otherwise it may cause issues in the workspace
|
|
480
|
+
// This is a special case for react and react-dom, as most component do import from react, making it a peer dependency,
|
|
481
|
+
// but not necessarily import from react-dom, which from env.jsonc peers in that case not added to the peers of the component.
|
|
482
|
+
// and if the versions are not in sync, it may cause issues in the workspace
|
|
483
|
+
// an example:
|
|
484
|
+
// my-comp depend on react, and using @testing-library/react which depend on react-dom (as peer),
|
|
485
|
+
// the component don't have react-dom as peer dependency, but when we install the dependencies in the workspace,
|
|
486
|
+
// it will install the latest version of react-dom which may not be compatible with the version of react that my-comp
|
|
487
|
+
// is using, and it may cause issues in the workspace.
|
|
488
|
+
if (peerDepsForManifest.react && envPeerDependencies['react-dom']) {
|
|
489
|
+
peerDepsForManifest['react-dom'] = envPeerDependencies['react-dom'];
|
|
490
|
+
}
|
|
248
491
|
}
|
|
249
492
|
}
|
|
250
493
|
|
|
@@ -7,6 +7,7 @@ import { Manifest } from './manifest';
|
|
|
7
7
|
|
|
8
8
|
export interface WorkspaceManifestToJsonOptions extends ManifestToJsonOptions {
|
|
9
9
|
installPeersFromEnvs?: boolean;
|
|
10
|
+
resolveEnvPeersFromRoot?: boolean;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export class WorkspaceManifest extends Manifest {
|
|
@@ -17,7 +18,8 @@ export class WorkspaceManifest extends Manifest {
|
|
|
17
18
|
public dependencies: ManifestDependenciesObject,
|
|
18
19
|
private envSelfPeersPolicy: VariantPolicy | undefined,
|
|
19
20
|
private rootDir: string,
|
|
20
|
-
public componentsManifestsMap: ComponentsManifestsMap
|
|
21
|
+
public componentsManifestsMap: ComponentsManifestsMap,
|
|
22
|
+
public peerOverrides: Record<string, string> = {}
|
|
21
23
|
) {
|
|
22
24
|
super(name, version, dependencies);
|
|
23
25
|
}
|
|
@@ -30,12 +32,21 @@ export class WorkspaceManifest extends Manifest {
|
|
|
30
32
|
|
|
31
33
|
toJson(options: WorkspaceManifestToJsonOptions = {}): Record<string, any> {
|
|
32
34
|
const manifest = super.toJson(options);
|
|
33
|
-
if (options.installPeersFromEnvs) {
|
|
35
|
+
if (options.installPeersFromEnvs || options.resolveEnvPeersFromRoot) {
|
|
34
36
|
const peersManifest = this.envSelfPeersPolicy?.toVersionManifest();
|
|
35
37
|
// Resolve "+" version placeholders from peersManifest
|
|
36
38
|
const resolvedPeersManifest = this._resolvePlusVersions(peersManifest || {});
|
|
37
39
|
manifest.dependencies = manifest.dependencies || {};
|
|
38
|
-
|
|
40
|
+
// Env peers are added as defaults — workspace.jsonc policy takes priority across all dep sections
|
|
41
|
+
for (const [pkgName, version] of Object.entries(resolvedPeersManifest)) {
|
|
42
|
+
const alreadyDefined = manifest.dependencies?.[pkgName] ||
|
|
43
|
+
manifest.devDependencies?.[pkgName] ||
|
|
44
|
+
manifest.peerDependencies?.[pkgName] ||
|
|
45
|
+
manifest.optionalDependencies?.[pkgName];
|
|
46
|
+
if (!alreadyDefined) {
|
|
47
|
+
manifest.dependencies[pkgName] = version;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
39
50
|
}
|
|
40
51
|
return manifest;
|
|
41
52
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teambit/dependency-resolver",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.913",
|
|
4
4
|
"homepage": "https://bit.cloud/teambit/dependencies/dependency-resolver",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"componentId": {
|
|
7
7
|
"scope": "teambit.dependencies",
|
|
8
8
|
"name": "dependency-resolver",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.913"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"chalk": "4.1.2",
|
|
@@ -52,14 +52,14 @@
|
|
|
52
52
|
"@teambit/component-issues": "0.0.170",
|
|
53
53
|
"@teambit/component-package-version": "0.0.449",
|
|
54
54
|
"@teambit/legacy-bit-id": "1.1.3",
|
|
55
|
-
"@teambit/toolbox.object.sorter": "0.0.2",
|
|
56
55
|
"@teambit/legacy.consumer-config": "0.0.101",
|
|
57
56
|
"@teambit/toolbox.crypto.sha1": "0.0.15",
|
|
58
|
-
"@teambit/
|
|
59
|
-
"@teambit/
|
|
60
|
-
"@teambit/
|
|
61
|
-
"@teambit/
|
|
62
|
-
"@teambit/
|
|
57
|
+
"@teambit/toolbox.object.sorter": "0.0.2",
|
|
58
|
+
"@teambit/component": "1.0.913",
|
|
59
|
+
"@teambit/envs": "1.0.913",
|
|
60
|
+
"@teambit/aspect-loader": "1.0.913",
|
|
61
|
+
"@teambit/objects": "0.0.420",
|
|
62
|
+
"@teambit/graphql": "1.0.913"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@types/fs-extra": "9.0.7",
|
|
@@ -23,6 +23,19 @@ export type EnvJsoncPolicyEntry = {
|
|
|
23
23
|
|
|
24
24
|
export type EnvJsoncPolicyPeerEntry = EnvJsoncPolicyEntry & {
|
|
25
25
|
supportedRange: string;
|
|
26
|
+
/**
|
|
27
|
+
* When true, this peer dependency will be resolved as a single version at the workspace root,
|
|
28
|
+
* even if different envs specify different versions. Useful for @types packages and workspace-level
|
|
29
|
+
* tools (eslint, prettier) that must resolve from the workspace root.
|
|
30
|
+
* When false (default), conflicts are resolved per-component via env roots.
|
|
31
|
+
*/
|
|
32
|
+
workspaceSingleton?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* When true, generates a pnpm override for this peer using its version,
|
|
35
|
+
* forcing all transitive dependencies to use the same version.
|
|
36
|
+
* Useful to prevent old versions from being pulled by published packages.
|
|
37
|
+
*/
|
|
38
|
+
override?: boolean;
|
|
26
39
|
};
|
|
27
40
|
|
|
28
41
|
export type VersionKeyName = 'version' | 'supportedRange';
|
|
@@ -45,14 +58,16 @@ export type EnvPolicyConfigObject = EnvPolicyEnvJsoncConfigObject | EnvPolicyLeg
|
|
|
45
58
|
export class EnvPolicy extends VariantPolicy {
|
|
46
59
|
constructor(
|
|
47
60
|
_policiesEntries: VariantPolicyEntry[],
|
|
48
|
-
readonly selfPolicy: VariantPolicy
|
|
61
|
+
readonly selfPolicy: VariantPolicy,
|
|
62
|
+
readonly envId?: string
|
|
49
63
|
) {
|
|
50
64
|
super(_policiesEntries);
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
static fromConfigObject(
|
|
54
68
|
configObject: EnvPolicyConfigObject,
|
|
55
|
-
{ includeLegacyPeersInSelfPolicy }: VariantPolicyFromConfigObjectOptions = {}
|
|
69
|
+
{ includeLegacyPeersInSelfPolicy }: VariantPolicyFromConfigObjectOptions = {},
|
|
70
|
+
envId?: string
|
|
56
71
|
): EnvPolicy {
|
|
57
72
|
validateEnvPolicyConfigObject(configObject);
|
|
58
73
|
|
|
@@ -95,7 +110,7 @@ export class EnvPolicy extends VariantPolicy {
|
|
|
95
110
|
);
|
|
96
111
|
const newPolicy = VariantPolicy.fromArray(componentPeersEntries.concat(otherEntries));
|
|
97
112
|
const finalComponentPolicy = VariantPolicy.mergePolices([legacyPolicy, newPolicy]);
|
|
98
|
-
return new EnvPolicy(finalComponentPolicy.entries, selfPolicy);
|
|
113
|
+
return new EnvPolicy(finalComponentPolicy.entries, selfPolicy, envId);
|
|
99
114
|
}
|
|
100
115
|
|
|
101
116
|
static getEmpty(): EnvPolicy {
|
|
@@ -134,7 +149,12 @@ function entriesFromKey(
|
|
|
134
149
|
`env.jsonc: "policy.${keyName}" entry must be a property with a "${versionKey}" field. got "${entry}"`
|
|
135
150
|
);
|
|
136
151
|
}
|
|
137
|
-
|
|
152
|
+
const peerEntry = entry as EnvJsoncPolicyPeerEntry;
|
|
153
|
+
const hasPeerProps = 'workspaceSingleton' in entry || 'override' in entry;
|
|
154
|
+
const value = hasPeerProps
|
|
155
|
+
? { version: entry[versionKey], workspaceSingleton: peerEntry.workspaceSingleton, override: peerEntry.override }
|
|
156
|
+
: entry[versionKey];
|
|
157
|
+
return createVariantPolicyEntry(entry.name, value, lifecycleType, {
|
|
138
158
|
...options,
|
|
139
159
|
source: options.source ?? 'env',
|
|
140
160
|
hidden: entry.hidden,
|
|
@@ -40,6 +40,8 @@ export type VariantPolicyEntryValue = {
|
|
|
40
40
|
version: VariantPolicyEntryVersion;
|
|
41
41
|
resolveFromEnv?: boolean;
|
|
42
42
|
optional?: boolean;
|
|
43
|
+
workspaceSingleton?: boolean;
|
|
44
|
+
override?: boolean;
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
export type DependencySource = 'auto' | 'env' | 'env-own' | 'slots' | 'config';
|
|
@@ -343,10 +345,14 @@ export function createVariantPolicyEntry(
|
|
|
343
345
|
const version = typeof value === 'string' ? value : value.version;
|
|
344
346
|
const resolveFromEnv = typeof value === 'string' ? false : value.resolveFromEnv;
|
|
345
347
|
const optional = typeof value === 'string' ? undefined : value.optional;
|
|
348
|
+
const workspaceSingleton = typeof value === 'string' ? undefined : value.workspaceSingleton;
|
|
349
|
+
const override = typeof value === 'string' ? undefined : value.override;
|
|
346
350
|
|
|
347
351
|
const entryValue: VariantPolicyEntryValue = {
|
|
348
352
|
version,
|
|
349
353
|
resolveFromEnv,
|
|
354
|
+
workspaceSingleton,
|
|
355
|
+
override,
|
|
350
356
|
};
|
|
351
357
|
const entry: VariantPolicyEntry = {
|
|
352
358
|
...opts,
|