@teambit/dependencies 1.0.1233 → 1.0.1235

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.
@@ -0,0 +1,85 @@
1
+ import { expect } from 'chai';
2
+ import { ComponentID } from '@teambit/component-id';
3
+ import { Dependency } from '@teambit/legacy.consumer-component';
4
+ import { ComponentOverrides } from '@teambit/legacy.consumer-config';
5
+ import type { DependencyResolverMain } from '@teambit/dependency-resolver';
6
+ import type { Logger } from '@teambit/logger';
7
+ import { ApplyOverrides } from './apply-overrides';
8
+
9
+ const noopLogger = { debug() {}, warn() {}, trace() {} } as unknown as Logger;
10
+
11
+ function createApplyOverrides(forcedDeps: Record<string, Record<string, string>>) {
12
+ const id = ComponentID.fromString('my-scope/my-env@0.0.1');
13
+ const component = { id, componentId: id, issues: undefined, overrides: new ComponentOverrides(forcedDeps) };
14
+ // no workspace, as in a bare scope
15
+ return new ApplyOverrides(component as any, {} as DependencyResolverMain, noopLogger);
16
+ }
17
+
18
+ describe('ApplyOverrides', () => {
19
+ describe('outside a workspace, a forced dependency that is a component dependency in another field', () => {
20
+ // e.g. a peer that an env sets for itself (in env.jsonc) is a runtime dependency of the env. the env of the env
21
+ // may set it as a peer, which moves the component dependency to the peers before the forced deps are added.
22
+ const peerDep = new Dependency(
23
+ ComponentID.fromString('my-scope/ui/button@1.0.0'),
24
+ [],
25
+ '@my-scope/ui.button',
26
+ '^1.0.0'
27
+ );
28
+ let result: ReturnType<ApplyOverrides['getDependenciesToAddManually']>;
29
+ before(() => {
30
+ const applyOverrides = createApplyOverrides({ dependencies: { '@my-scope/ui.button': '1.0.0' } });
31
+ applyOverrides.allDependencies = { dependencies: [], devDependencies: [], peerDependencies: [peerDep] };
32
+ result = applyOverrides.getDependenciesToAddManually(undefined, applyOverrides.allDependencies);
33
+ });
34
+ it('should resolve the package to the component, as the node_modules of a workspace would', () => {
35
+ expect(result?.components.dependencies).to.have.lengthOf(1);
36
+ expect(result?.components.dependencies[0].componentId.toString()).to.equal('my-scope/ui/button@1.0.0');
37
+ expect(result?.packages).to.deep.equal({});
38
+ });
39
+ it('should not set a version range on a runtime component dependency', () => {
40
+ expect(result?.components.dependencies[0].versionRange).to.be.undefined;
41
+ });
42
+ });
43
+ });
44
+
45
+ function createApplyOverridesWithPolicy({
46
+ installed,
47
+ inWorkspacePolicy,
48
+ }: {
49
+ installed?: string;
50
+ inWorkspacePolicy?: string;
51
+ }) {
52
+ const applyOverrides = Object.create(ApplyOverrides.prototype);
53
+ applyOverrides.depsResolver = {
54
+ getWorkspacePolicyManifest: () => ({ dependencies: inWorkspacePolicy ? { 'my-pkg': inWorkspacePolicy } : {} }),
55
+ };
56
+ applyOverrides._resolvePackageData = () => (installed ? { concreteVersion: installed } : undefined);
57
+ return applyOverrides;
58
+ }
59
+
60
+ describe('ApplyOverrides.resolveEnvPeerDepVersion()', () => {
61
+ it('should prefer the installed version by default', () => {
62
+ const applyOverrides = createApplyOverridesWithPolicy({ installed: '2.0.0', inWorkspacePolicy: '3.0.1' });
63
+ expect(applyOverrides.resolveEnvPeerDepVersion('my-pkg')).to.equal('2.0.0');
64
+ });
65
+ it('should prefer the workspace policy when asked to, as the installed version could be outdated', () => {
66
+ const applyOverrides = createApplyOverridesWithPolicy({ installed: '2.0.0', inWorkspacePolicy: '3.0.1' });
67
+ expect(applyOverrides.resolveEnvPeerDepVersion('my-pkg', true)).to.equal('3.0.1');
68
+ });
69
+ it('should prefer the installed version when it satisfies the workspace policy range', () => {
70
+ const applyOverrides = createApplyOverridesWithPolicy({ installed: '18.2.0', inWorkspacePolicy: '^18.0.0' });
71
+ expect(applyOverrides.resolveEnvPeerDepVersion('my-pkg', true)).to.equal('18.2.0');
72
+ });
73
+ it('should take the minimum version of the workspace policy range when the installed one is outdated', () => {
74
+ const applyOverrides = createApplyOverridesWithPolicy({ installed: '17.0.0', inWorkspacePolicy: '^18.1.0' });
75
+ expect(applyOverrides.resolveEnvPeerDepVersion('my-pkg', true)).to.equal('18.1.0');
76
+ });
77
+ it('should keep an unconstrained workspace policy range as is', () => {
78
+ const applyOverrides = createApplyOverridesWithPolicy({ inWorkspacePolicy: '*' });
79
+ expect(applyOverrides.resolveEnvPeerDepVersion('my-pkg', true)).to.equal('*');
80
+ });
81
+ it('should fall back to the installed version when the workspace policy does not have the package', () => {
82
+ const applyOverrides = createApplyOverridesWithPolicy({ installed: '2.0.0' });
83
+ expect(applyOverrides.resolveEnvPeerDepVersion('my-pkg', true)).to.equal('2.0.0');
84
+ });
85
+ });
@@ -1,4 +1,5 @@
1
1
  import path from 'path';
2
+ import semver from 'semver';
2
3
  import type { ComponentID } from '@teambit/component-id';
3
4
  import { ComponentIdList } from '@teambit/component-id';
4
5
  import { cloneDeep, difference, forEach, isEmpty, pick, pickBy, uniq } from 'lodash';
@@ -14,6 +15,7 @@ import type { ResolvedPackageData } from '../resolve-pkg-data';
14
15
  import { resolvePackageData } from '../resolve-pkg-data';
15
16
  import type { Workspace } from '@teambit/workspace';
16
17
  import type { DependencyResolverMain } from '@teambit/dependency-resolver';
18
+ import { isPlusVersion, resolvePlusVersion } from '@teambit/dependency-resolver';
17
19
  import type { Consumer } from '@teambit/legacy.consumer';
18
20
  import type { ComponentMap } from '@teambit/legacy.bit-map';
19
21
  import type { Logger } from '@teambit/logger';
@@ -245,7 +247,9 @@ export class ApplyOverrides {
245
247
  }
246
248
 
247
249
  private async loadAutoDetectOverrides() {
248
- this.autoDetectOverrides = await this.workspace?.getAutoDetectOverrides(
250
+ // outside a workspace, they're calculated by the caller (if at all) and set before this runs
251
+ if (!this.workspace) return;
252
+ this.autoDetectOverrides = await this.workspace.getAutoDetectOverrides(
249
253
  this.component.extensions,
250
254
  this.component.id,
251
255
  this.component.files,
@@ -290,12 +294,30 @@ export class ApplyOverrides {
290
294
 
291
295
  private _getComponentIdToAdd(
292
296
  dependency: string,
293
- versionRange: string
294
- ): { componentId?: ComponentID; packageName?: string; versionRange: string } {
297
+ versionRange: string,
298
+ depField: string
299
+ ): { componentId?: ComponentID; packageName?: string; versionRange?: string } {
300
+ if (!this.consumer) {
301
+ // a scope has no node_modules to resolve the package from. if it's a component dependency, use it. otherwise, a
302
+ // forced component dependency (e.g. an env's own peer, which is a runtime dep of the env) is added as a package
303
+ // and then dropped in favor of the component dependency, which may be in a different field.
304
+ const compDep = this.findComponentDepByPackageName(dependency);
305
+ if (compDep) {
306
+ // same as the workspace (see updateDependenciesVersions), only a peer component dependency keeps its range
307
+ const range = depField === 'peerDependencies' ? versionRange : undefined;
308
+ return { componentId: compDep.id, packageName: dependency, versionRange: range };
309
+ }
310
+ }
295
311
  const packageData = this._resolvePackageData(dependency);
296
312
  return { componentId: packageData?.componentId, packageName: packageData?.name, versionRange };
297
313
  }
298
314
 
315
+ private findComponentDepByPackageName(packageName: string): Dependency | undefined {
316
+ const { dependencies, devDependencies, peerDependencies } = this.allDependencies;
317
+ const isMatch = (dep: Dependency) => dep.packageName === packageName;
318
+ return dependencies.find(isMatch) || devDependencies.find(isMatch) || peerDependencies.find(isMatch);
319
+ }
320
+
299
321
  getDependenciesToAddManually(
300
322
  packageJson: Record<string, any> | null | undefined,
301
323
  existingDependencies: AllDependencies
@@ -309,7 +331,7 @@ export class ApplyOverrides {
309
331
  if (!overrides[depField]) return;
310
332
  Object.keys(overrides[depField]).forEach((dependency) => {
311
333
  const dependencyValue = overrides[depField][dependency];
312
- const componentData = this._getComponentIdToAdd(dependency, dependencyValue);
334
+ const componentData = this._getComponentIdToAdd(dependency, dependencyValue, depField);
313
335
  if (componentData?.componentId) {
314
336
  if (componentData.componentId.isEqualWithoutVersion(this.componentId)) {
315
337
  this.logger.warn(
@@ -589,10 +611,7 @@ export class ApplyOverrides {
589
611
 
590
612
  private isPackageAutoDetected(pkgName: string, originallyExists: string[], missingPackages: string[]): boolean {
591
613
  // Check component dependencies
592
- const existsInAnyCompDeps =
593
- this.allDependencies.dependencies.some((dep) => dep.packageName === pkgName) ||
594
- this.allDependencies.devDependencies.some((dep) => dep.packageName === pkgName) ||
595
- this.allDependencies.peerDependencies.some((dep) => dep.packageName === pkgName);
614
+ const existsInAnyCompDeps = Boolean(this.findComponentDepByPackageName(pkgName));
596
615
 
597
616
  // Check originAllPackagesDependencies instead of allPackagesDependencies
598
617
  // as it might be already removed at this point if it was set with "-" in runtime/dev
@@ -716,7 +735,11 @@ export class ApplyOverrides {
716
735
  comp.versionRange = pkgVal;
717
736
  this.allDependencies.peerDependencies.push(comp);
718
737
  } else {
719
- this.allPackagesDependencies[key][pkgName] = pkgVal;
738
+ // a package has no separate version and range, so a "+" placeholder is resolved here.
739
+ // a component dep keeps it in the versionRange, and it's resolved when generating the package.json
740
+ this.allPackagesDependencies[key][pkgName] = isPlusVersion(pkgVal)
741
+ ? resolvePlusVersion(pkgVal, this.resolveEnvPeerDepVersion(pkgName, true))
742
+ : pkgVal;
720
743
  }
721
744
 
722
745
  if (existsInCompsPeerDeps) {
@@ -750,11 +773,11 @@ export class ApplyOverrides {
750
773
  });
751
774
  const resolvedEnvPolicyManifest = Object.keys(envPolicyManifest).reduce((acc, pkgName) => {
752
775
  const version = envPolicyManifest[pkgName];
753
- if (version !== '+') {
776
+ if (!isPlusVersion(version)) {
754
777
  acc[pkgName] = version;
755
778
  return acc;
756
779
  }
757
- acc[pkgName] = this.resolveEnvPeerDepVersion(pkgName);
780
+ acc[pkgName] = resolvePlusVersion(version, this.resolveEnvPeerDepVersion(pkgName));
758
781
  return acc;
759
782
  }, {});
760
783
  // TODO: handle component deps once we support peers between components
@@ -765,21 +788,34 @@ export class ApplyOverrides {
765
788
  }
766
789
 
767
790
  /**
768
- * in the env.jsonc file, a policy-peer package can have `+` sign in the version. it means that it should be resolved
791
+ * in the env.jsonc file, a policy-peer package can have `+` sign (or `+^`/`+~`) in the version. it means that it should be resolved
769
792
  * from the workspace. whatever version is installed/imported in the workspace, it should be used here.
770
793
  * in some cases, the package is not installed in the workspace, for example, the env is now imported without the
771
794
  * dep. so the dep is not in the node_modules.
772
795
  * strategy should be: .bitmap, workspace.jsonc, then model.
773
796
  * it's not in .bitmap, otherwise, it was linked and `_resolvePackageData` would have found it.
774
797
  * so either, it's in the workspace.jsonc or in the model.
798
+ * @param preferWorkspacePolicy check the workspace.jsonc before what's installed. needed for a package that the
799
+ * component gets with a plus version. what's installed for the component was installed according to the version it
800
+ * had before, so it doesn't follow an upgrade in the workspace.jsonc (e.g. by "bit update").
775
801
  */
776
- private resolveEnvPeerDepVersion(pkgName: string): string {
802
+ private resolveEnvPeerDepVersion(pkgName: string, preferWorkspacePolicy = false): string {
803
+ const wsPolicy = this.depsResolver.getWorkspacePolicyManifest();
804
+ const wsVersion = wsPolicy?.dependencies?.[pkgName] || wsPolicy?.peerDependencies?.[pkgName];
777
805
  const resolved = this._resolvePackageData(pkgName);
806
+ if (preferWorkspacePolicy && wsVersion) {
807
+ // the workspace.jsonc has usually a range. the plus version needs a concrete version to add its own prefix to
808
+ if (resolved?.concreteVersion && semver.satisfies(resolved.concreteVersion, wsVersion)) {
809
+ return resolved.concreteVersion;
810
+ }
811
+ const validRange = semver.validRange(wsVersion);
812
+ // an unconstrained range (e.g. "*") has no meaningful minimum, keep it as is
813
+ if (!validRange || validRange === '*') return wsVersion;
814
+ return semver.minVersion(wsVersion)?.version || wsVersion;
815
+ }
778
816
  if (resolved && resolved.concreteVersion) {
779
817
  return resolved.concreteVersion;
780
818
  }
781
- const wsPolicy = this.depsResolver.getWorkspacePolicyManifest();
782
- const wsVersion = wsPolicy?.dependencies?.[pkgName] || wsPolicy?.peerDependencies?.[pkgName];
783
819
  if (wsVersion) {
784
820
  return wsVersion;
785
821
  }
@@ -15,6 +15,7 @@ import { DependencyResolverAspect } from '@teambit/dependency-resolver';
15
15
  import type { DevFilesMain } from '@teambit/dev-files';
16
16
  import type { Logger } from '@teambit/logger';
17
17
  import type { AspectLoaderMain } from '@teambit/aspect-loader';
18
+ import type { DependenciesOverridesData } from '@teambit/legacy.consumer-config';
18
19
  import { DependenciesData } from './dependencies-data';
19
20
  import { updateDependenciesVersions } from './dependencies-versions-resolver';
20
21
  import type { DebugDependencies } from './auto-detect-deps';
@@ -62,12 +63,17 @@ export class DependenciesLoader {
62
63
  };
63
64
  }
64
65
 
65
- async loadFromScope(dependenciesData: Partial<DependenciesData>) {
66
+ /**
67
+ * `autoDetectOverrides` are the non-force policy entries (versions for dependencies that were detected). a workspace
68
+ * calculates them on its own, a scope gets them from the caller.
69
+ */
70
+ async loadFromScope(dependenciesData: Partial<DependenciesData>, autoDetectOverrides?: DependenciesOverridesData) {
66
71
  const applyOverrides = new ApplyOverrides(this.component, this.depsResolver, this.logger);
67
72
  const { allDependencies, allPackagesDependencies, issues } = dependenciesData;
68
73
  if (allDependencies) applyOverrides.allDependencies = allDependencies;
69
74
  if (allPackagesDependencies) applyOverrides.allPackagesDependencies = allPackagesDependencies;
70
75
  if (issues) applyOverrides.issues = issues;
76
+ if (autoDetectOverrides) applyOverrides.autoDetectOverrides = autoDetectOverrides;
71
77
  const results = await applyOverrides.getDependenciesData();
72
78
  this.setDependenciesDataOnComponent(results.dependenciesData, results.overridesDependencies);
73
79
  }
@@ -8,6 +8,7 @@ import { isEmpty } from 'lodash';
8
8
  import type { Dependency, Dependencies, ConsumerComponent as Component } from '@teambit/legacy.consumer-component';
9
9
  import type { ExtensionDataEntry, ExtensionDataList } from '@teambit/legacy.extension-data';
10
10
  import type { DependencyResolverMain } from '@teambit/dependency-resolver';
11
+ import { isPlusVersion } from '@teambit/dependency-resolver';
11
12
  import { DEPENDENCIES_FIELDS } from '@teambit/legacy.constants';
12
13
  import type OverridesDependencies from './overrides-dependencies';
13
14
  import type { DebugComponentsDependency } from './auto-detect-deps';
@@ -126,7 +127,7 @@ export async function updateDependenciesVersions(
126
127
  ) {
127
128
  dependency.versionRange = `${componentRangePrefix}${resolvedVersion}`;
128
129
  }
129
- } else if (dependency.versionRange && depType !== 'peerDependencies' && dependency.versionRange !== '+') {
130
+ } else if (dependency.versionRange && depType !== 'peerDependencies' && !isPlusVersion(dependency.versionRange)) {
130
131
  dependency.versionRange = undefined;
131
132
  }
132
133
  }
@@ -1,5 +1,6 @@
1
- import { DEPENDENCIES_FIELDS, MANUALLY_ADD_DEPENDENCY, MANUALLY_REMOVE_DEPENDENCY } from '@teambit/legacy.constants';
1
+ import { DEPENDENCIES_FIELDS, MANUALLY_REMOVE_DEPENDENCY } from '@teambit/legacy.constants';
2
2
  import { logger } from '@teambit/legacy.logger';
3
+ import { isPlusVersion, resolvePlusVersion } from '@teambit/dependency-resolver';
3
4
  import type { ConsumerComponent as Component, ManuallyChangedDependencies } from '@teambit/legacy.consumer-component';
4
5
  import type { FileType } from './auto-detect-deps';
5
6
 
@@ -75,14 +76,15 @@ export default class OverridesDependencies {
75
76
  packageJson: Record<string, any> | null | undefined
76
77
  ): Record<string, any> | null | undefined {
77
78
  const packageVersionToAdd = (): string | null | undefined => {
78
- if (dependencyValue !== MANUALLY_ADD_DEPENDENCY) {
79
+ // "+", "+^" and "+~" take the version from the package.json
80
+ if (!isPlusVersion(dependencyValue)) {
79
81
  return dependencyValue;
80
82
  }
81
83
  if (!packageJson) return null;
82
84
  for (const depField of DEPENDENCIES_FIELDS) {
83
85
  if (packageJson[depField]) {
84
86
  const found = Object.keys(packageJson[depField]).find((pkg) => pkg === dependency);
85
- if (found) return packageJson[depField][dependency];
87
+ if (found) return resolvePlusVersion(dependencyValue, packageJson[depField][dependency]);
86
88
  }
87
89
  }
88
90
  return null;
@@ -93,6 +93,7 @@ export declare class ApplyOverrides {
93
93
  private removeIgnoredPackagesByOverrides;
94
94
  private _resolvePackageData;
95
95
  private _getComponentIdToAdd;
96
+ private findComponentDepByPackageName;
96
97
  getDependenciesToAddManually(packageJson: Record<string, any> | null | undefined, existingDependencies: AllDependencies): {
97
98
  components: Record<string, any>;
98
99
  packages: Record<string, any>;
@@ -129,13 +130,16 @@ export declare class ApplyOverrides {
129
130
  private applyPackageVersion;
130
131
  private applyAutoDetectedPeersFromEnvOnEnvItSelf;
131
132
  /**
132
- * in the env.jsonc file, a policy-peer package can have `+` sign in the version. it means that it should be resolved
133
+ * in the env.jsonc file, a policy-peer package can have `+` sign (or `+^`/`+~`) in the version. it means that it should be resolved
133
134
  * from the workspace. whatever version is installed/imported in the workspace, it should be used here.
134
135
  * in some cases, the package is not installed in the workspace, for example, the env is now imported without the
135
136
  * dep. so the dep is not in the node_modules.
136
137
  * strategy should be: .bitmap, workspace.jsonc, then model.
137
138
  * it's not in .bitmap, otherwise, it was linked and `_resolvePackageData` would have found it.
138
139
  * so either, it's in the workspace.jsonc or in the model.
140
+ * @param preferWorkspacePolicy check the workspace.jsonc before what's installed. needed for a package that the
141
+ * component gets with a plus version. what's installed for the component was installed according to the version it
142
+ * had before, so it doesn't follow an upgrade in the workspace.jsonc (e.g. by "bit update").
139
143
  */
140
144
  private resolveEnvPeerDepVersion;
141
145
  /**
@@ -11,6 +11,13 @@ function _path() {
11
11
  };
12
12
  return data;
13
13
  }
14
+ function _semver() {
15
+ const data = _interopRequireDefault(require("semver"));
16
+ _semver = function () {
17
+ return data;
18
+ };
19
+ return data;
20
+ }
14
21
  function _componentId() {
15
22
  const data = require("@teambit/component-id");
16
23
  _componentId = function () {
@@ -67,6 +74,13 @@ function _resolvePkgData() {
67
74
  };
68
75
  return data;
69
76
  }
77
+ function _dependencyResolver() {
78
+ const data = require("@teambit/dependency-resolver");
79
+ _dependencyResolver = function () {
80
+ return data;
81
+ };
82
+ return data;
83
+ }
70
84
  function _legacy4() {
71
85
  const data = require("@teambit/legacy.consumer-config");
72
86
  _legacy4 = function () {
@@ -285,7 +299,9 @@ class ApplyOverrides {
285
299
  if (!missingIssue.data.length) this.issues.delete(_componentIssues().IssuesClasses.MissingPackagesDependenciesOnFs);
286
300
  }
287
301
  async loadAutoDetectOverrides() {
288
- this.autoDetectOverrides = await this.workspace?.getAutoDetectOverrides(this.component.extensions, this.component.id, this.component.files, this.getEnvExtendsDeps());
302
+ // outside a workspace, they're calculated by the caller (if at all) and set before this runs
303
+ if (!this.workspace) return;
304
+ this.autoDetectOverrides = await this.workspace.getAutoDetectOverrides(this.component.extensions, this.component.id, this.component.files, this.getEnvExtendsDeps());
289
305
  }
290
306
  cloneAllPackagesDependencies() {
291
307
  this.originAllPackagesDependencies = (0, _lodash().cloneDeep)(this.allPackagesDependencies);
@@ -311,7 +327,22 @@ class ApplyOverrides {
311
327
  const packageObject = (0, _resolvePkgData().resolvePackageData)(basePath, modulePath);
312
328
  return packageObject;
313
329
  }
314
- _getComponentIdToAdd(dependency, versionRange) {
330
+ _getComponentIdToAdd(dependency, versionRange, depField) {
331
+ if (!this.consumer) {
332
+ // a scope has no node_modules to resolve the package from. if it's a component dependency, use it. otherwise, a
333
+ // forced component dependency (e.g. an env's own peer, which is a runtime dep of the env) is added as a package
334
+ // and then dropped in favor of the component dependency, which may be in a different field.
335
+ const compDep = this.findComponentDepByPackageName(dependency);
336
+ if (compDep) {
337
+ // same as the workspace (see updateDependenciesVersions), only a peer component dependency keeps its range
338
+ const range = depField === 'peerDependencies' ? versionRange : undefined;
339
+ return {
340
+ componentId: compDep.id,
341
+ packageName: dependency,
342
+ versionRange: range
343
+ };
344
+ }
345
+ }
315
346
  const packageData = this._resolvePackageData(dependency);
316
347
  return {
317
348
  componentId: packageData?.componentId,
@@ -319,6 +350,15 @@ class ApplyOverrides {
319
350
  versionRange
320
351
  };
321
352
  }
353
+ findComponentDepByPackageName(packageName) {
354
+ const {
355
+ dependencies,
356
+ devDependencies,
357
+ peerDependencies
358
+ } = this.allDependencies;
359
+ const isMatch = dep => dep.packageName === packageName;
360
+ return dependencies.find(isMatch) || devDependencies.find(isMatch) || peerDependencies.find(isMatch);
361
+ }
322
362
  getDependenciesToAddManually(packageJson, existingDependencies) {
323
363
  const overrides = this.overridesDependencies.getDependenciesToAddManually();
324
364
  if (!overrides) return undefined;
@@ -329,7 +369,7 @@ class ApplyOverrides {
329
369
  if (!overrides[depField]) return;
330
370
  Object.keys(overrides[depField]).forEach(dependency => {
331
371
  const dependencyValue = overrides[depField][dependency];
332
- const componentData = this._getComponentIdToAdd(dependency, dependencyValue);
372
+ const componentData = this._getComponentIdToAdd(dependency, dependencyValue, depField);
333
373
  if (componentData?.componentId) {
334
374
  if (componentData.componentId.isEqualWithoutVersion(this.componentId)) {
335
375
  this.logger.warn(`component ${this.componentId.toString()} depends on itself ${componentData.componentId.toString()}. ignoring it.`);
@@ -577,7 +617,7 @@ class ApplyOverrides {
577
617
  }
578
618
  isPackageAutoDetected(pkgName, originallyExists, missingPackages) {
579
619
  // Check component dependencies
580
- const existsInAnyCompDeps = this.allDependencies.dependencies.some(dep => dep.packageName === pkgName) || this.allDependencies.devDependencies.some(dep => dep.packageName === pkgName) || this.allDependencies.peerDependencies.some(dep => dep.packageName === pkgName);
620
+ const existsInAnyCompDeps = Boolean(this.findComponentDepByPackageName(pkgName));
581
621
 
582
622
  // Check originAllPackagesDependencies instead of allPackagesDependencies
583
623
  // as it might be already removed at this point if it was set with "-" in runtime/dev
@@ -658,7 +698,9 @@ class ApplyOverrides {
658
698
  comp.versionRange = pkgVal;
659
699
  this.allDependencies.peerDependencies.push(comp);
660
700
  } else {
661
- this.allPackagesDependencies[key][pkgName] = pkgVal;
701
+ // a package has no separate version and range, so a "+" placeholder is resolved here.
702
+ // a component dep keeps it in the versionRange, and it's resolved when generating the package.json
703
+ this.allPackagesDependencies[key][pkgName] = (0, _dependencyResolver().isPlusVersion)(pkgVal) ? (0, _dependencyResolver().resolvePlusVersion)(pkgVal, this.resolveEnvPeerDepVersion(pkgName, true)) : pkgVal;
662
704
  }
663
705
  if (existsInCompsPeerDeps) {
664
706
  existsInCompsPeerDeps.versionRange = pkgVal;
@@ -685,11 +727,11 @@ class ApplyOverrides {
685
727
  });
686
728
  const resolvedEnvPolicyManifest = Object.keys(envPolicyManifest).reduce((acc, pkgName) => {
687
729
  const version = envPolicyManifest[pkgName];
688
- if (version !== '+') {
730
+ if (!(0, _dependencyResolver().isPlusVersion)(version)) {
689
731
  acc[pkgName] = version;
690
732
  return acc;
691
733
  }
692
- acc[pkgName] = this.resolveEnvPeerDepVersion(pkgName);
734
+ acc[pkgName] = (0, _dependencyResolver().resolvePlusVersion)(version, this.resolveEnvPeerDepVersion(pkgName));
693
735
  return acc;
694
736
  }, {});
695
737
  // TODO: handle component deps once we support peers between components
@@ -697,21 +739,34 @@ class ApplyOverrides {
697
739
  }
698
740
 
699
741
  /**
700
- * in the env.jsonc file, a policy-peer package can have `+` sign in the version. it means that it should be resolved
742
+ * in the env.jsonc file, a policy-peer package can have `+` sign (or `+^`/`+~`) in the version. it means that it should be resolved
701
743
  * from the workspace. whatever version is installed/imported in the workspace, it should be used here.
702
744
  * in some cases, the package is not installed in the workspace, for example, the env is now imported without the
703
745
  * dep. so the dep is not in the node_modules.
704
746
  * strategy should be: .bitmap, workspace.jsonc, then model.
705
747
  * it's not in .bitmap, otherwise, it was linked and `_resolvePackageData` would have found it.
706
748
  * so either, it's in the workspace.jsonc or in the model.
749
+ * @param preferWorkspacePolicy check the workspace.jsonc before what's installed. needed for a package that the
750
+ * component gets with a plus version. what's installed for the component was installed according to the version it
751
+ * had before, so it doesn't follow an upgrade in the workspace.jsonc (e.g. by "bit update").
707
752
  */
708
- resolveEnvPeerDepVersion(pkgName) {
753
+ resolveEnvPeerDepVersion(pkgName, preferWorkspacePolicy = false) {
754
+ const wsPolicy = this.depsResolver.getWorkspacePolicyManifest();
755
+ const wsVersion = wsPolicy?.dependencies?.[pkgName] || wsPolicy?.peerDependencies?.[pkgName];
709
756
  const resolved = this._resolvePackageData(pkgName);
757
+ if (preferWorkspacePolicy && wsVersion) {
758
+ // the workspace.jsonc has usually a range. the plus version needs a concrete version to add its own prefix to
759
+ if (resolved?.concreteVersion && _semver().default.satisfies(resolved.concreteVersion, wsVersion)) {
760
+ return resolved.concreteVersion;
761
+ }
762
+ const validRange = _semver().default.validRange(wsVersion);
763
+ // an unconstrained range (e.g. "*") has no meaningful minimum, keep it as is
764
+ if (!validRange || validRange === '*') return wsVersion;
765
+ return _semver().default.minVersion(wsVersion)?.version || wsVersion;
766
+ }
710
767
  if (resolved && resolved.concreteVersion) {
711
768
  return resolved.concreteVersion;
712
769
  }
713
- const wsPolicy = this.depsResolver.getWorkspacePolicyManifest();
714
- const wsVersion = wsPolicy?.dependencies?.[pkgName] || wsPolicy?.peerDependencies?.[pkgName];
715
770
  if (wsVersion) {
716
771
  return wsVersion;
717
772
  }