@teambit/dependencies 1.0.1251 → 1.0.1252

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.
@@ -1,5 +1,5 @@
1
1
  import { expect } from 'chai';
2
- import { ComponentID } from '@teambit/component-id';
2
+ import { ComponentID, ComponentIdList } from '@teambit/component-id';
3
3
  import { Dependency } from '@teambit/legacy.consumer-component';
4
4
  import { ComponentOverrides } from '@teambit/legacy.consumer-config';
5
5
  import type { DependencyResolverMain } from '@teambit/dependency-resolver';
@@ -42,6 +42,63 @@ describe('ApplyOverrides', () => {
42
42
  });
43
43
  });
44
44
 
45
+ describe('ApplyOverrides outside a workspace, when the dependencies are calculated from a new policy', () => {
46
+ // e.g. a lane merge from a bare scope. a workspace takes these versions from the policy, .bitmap and node_modules.
47
+ const buttonId = ComponentID.fromString('my-scope/ui/button@1.0.0');
48
+ const buttonPkg = '@my-scope/ui.button';
49
+ function loadForcedDep({ inWorkspace = false, envOwn = false, newPolicy = true } = {}) {
50
+ const applyOverrides = createApplyOverrides({ dependencies: { [buttonPkg]: '2.0.0' } });
51
+ const dep = new Dependency(buttonId, [], buttonPkg);
52
+ applyOverrides.allDependencies = { dependencies: [dep], devDependencies: [], peerDependencies: [] };
53
+ if (newPolicy) applyOverrides.workspaceComponentIds = ComponentIdList.fromArray(inWorkspace ? [buttonId] : []);
54
+ if (envOwn) applyOverrides.overridesDependencies.envOwnPkgNames = new Set([buttonPkg]);
55
+ applyOverrides.getDependenciesToAddManually(undefined, applyOverrides.allDependencies);
56
+ return { dep, component: (applyOverrides as any).component };
57
+ }
58
+ function applyPeerRange(range: string, { inWorkspace = false, version = '1.0.0' } = {}) {
59
+ const applyOverrides = createApplyOverrides({});
60
+ const peer = new Dependency(buttonId.changeVersion(version), [], buttonPkg, '1.0.0');
61
+ applyOverrides.allDependencies = { dependencies: [], devDependencies: [], peerDependencies: [peer] };
62
+ applyOverrides.workspaceComponentIds = ComponentIdList.fromArray(inWorkspace ? [buttonId] : []);
63
+ (applyOverrides as any).applyPackageVersion('peerDependencies', buttonPkg, range, undefined, undefined, peer);
64
+ return peer;
65
+ }
66
+ it('should set the version of a forced component dependency and keep it when a new version is snapped along', () => {
67
+ const { dep, component } = loadForcedDep();
68
+ expect(dep.id.version).to.equal('2.0.0');
69
+ expect(component.dependenciesWithVersionFromConfig).to.deep.equal(['my-scope/ui/button']);
70
+ });
71
+ it('should set the version of a forced dependency on a workspace component', () => {
72
+ expect(loadForcedDep({ inWorkspace: true }).dep.id.version).to.equal('2.0.0');
73
+ });
74
+ it('should keep the version of an env own dependency on a workspace component, which follows the workspace', () => {
75
+ const { dep, component } = loadForcedDep({ inWorkspace: true, envOwn: true });
76
+ expect(dep.id.version).to.equal('1.0.0');
77
+ expect(component.dependenciesWithVersionFromConfig).to.be.undefined;
78
+ });
79
+ it('should keep the version when the policy is not new (e.g. a tag from a scope)', () => {
80
+ expect(loadForcedDep({ newPolicy: false }).dep.id.version).to.equal('1.0.0');
81
+ });
82
+ it('should set the version of a component peer that is out of the range of the policy', () => {
83
+ const peer = applyPeerRange('2.0.0');
84
+ expect(peer.id.version).to.equal('2.0.0');
85
+ expect(peer.versionRange).to.equal('2.0.0');
86
+ });
87
+ it('should keep the version of a component peer that is in the range of the policy', () => {
88
+ expect(applyPeerRange('^1.0.0').id.version).to.equal('1.0.0');
89
+ });
90
+ it('should keep the version of a component peer when the range has no minimum version (e.g. an upper bound)', () => {
91
+ expect(applyPeerRange('<1.0.0').id.version).to.equal('1.0.0');
92
+ });
93
+ it('should set the version of a component peer that is a snap, which is out of any semver range', () => {
94
+ const snap = 'a'.repeat(40);
95
+ expect(applyPeerRange('^2.0.0', { version: snap }).id.version).to.equal('2.0.0');
96
+ });
97
+ it('should keep the version of a component peer that is a workspace component', () => {
98
+ expect(applyPeerRange('2.0.0', { inWorkspace: true }).id.version).to.equal('1.0.0');
99
+ });
100
+ });
101
+
45
102
  function createApplyOverridesWithPolicy({
46
103
  installed,
47
104
  inWorkspacePolicy,
@@ -23,6 +23,7 @@ import { ComponentOverrides } from '@teambit/legacy.consumer-config';
23
23
  import OverridesDependencies from './overrides-dependencies';
24
24
  import { DependenciesData } from './dependencies-data';
25
25
  import type { DebugDependencies, FileType } from './auto-detect-deps';
26
+ import { getValidComponentVersion } from './auto-detect-deps';
26
27
 
27
28
  export type AllDependencies = {
28
29
  dependencies: Dependency[];
@@ -62,6 +63,14 @@ export class ApplyOverrides {
62
63
  * these overrides are from env/variants/merge-config. not ones with "force: true".
63
64
  */
64
65
  public autoDetectOverrides: Record<string, any> | undefined;
66
+ /**
67
+ * outside a workspace, when the dependencies are calculated from a new policy (e.g. a lane merge from a bare scope):
68
+ * the components that a workspace would have in its .bitmap, e.g. the components of the lane.
69
+ * a workspace takes the version of a component dependency from a forced policy, the .bitmap or the installed package
70
+ * (see updateDependenciesVersions). a scope has no .bitmap and no node_modules, so when this is set, the versions of
71
+ * the policy are applied here. otherwise, a component dependency keeps its version.
72
+ */
73
+ public workspaceComponentIds?: ComponentIdList;
65
74
  constructor(
66
75
  private component: Component,
67
76
  private depsResolver: DependencyResolverMain,
@@ -295,7 +304,7 @@ export class ApplyOverrides {
295
304
  dependency: string,
296
305
  versionRange: string,
297
306
  depField: string
298
- ): { componentId?: ComponentID; packageName?: string; versionRange?: string } {
307
+ ): { componentId?: ComponentID; packageName?: string; versionRange?: string; versionFromConfig?: boolean } {
299
308
  if (!this.consumer) {
300
309
  // a scope has no node_modules to resolve the package from. if it's a component dependency, use it. otherwise, a
301
310
  // forced component dependency (e.g. an env's own peer, which is a runtime dep of the env) is added as a package
@@ -304,13 +313,59 @@ export class ApplyOverrides {
304
313
  if (compDep) {
305
314
  // same as the workspace (see updateDependenciesVersions), only a peer component dependency keeps its range
306
315
  const range = depField === 'peerDependencies' ? versionRange : undefined;
307
- return { componentId: compDep.id, packageName: dependency, versionRange: range };
316
+ const idFromConfig = this.getComponentIdFromForcedVersion(compDep.id, dependency, versionRange);
317
+ return {
318
+ componentId: idFromConfig || compDep.id,
319
+ packageName: dependency,
320
+ versionRange: range,
321
+ versionFromConfig: Boolean(idFromConfig),
322
+ };
308
323
  }
309
324
  }
310
325
  const packageData = this._resolvePackageData(dependency);
311
326
  return { componentId: packageData?.componentId, packageName: packageData?.name, versionRange };
312
327
  }
313
328
 
329
+ /**
330
+ * the scope side of resolveFromOverrides() in updateDependenciesVersions(): a forced policy sets the version of a
331
+ * component dependency. the version of an env's own dependency on a workspace component follows the workspace.
332
+ */
333
+ private getComponentIdFromForcedVersion(
334
+ id: ComponentID,
335
+ packageName: string,
336
+ forcedVersion: string
337
+ ): ComponentID | undefined {
338
+ if (!this.workspaceComponentIds) return undefined;
339
+ const isEnvOwn = this.overridesDependencies.envOwnPkgNames.has(packageName);
340
+ if (isEnvOwn && this.workspaceComponentIds.hasWithoutVersion(id)) return undefined;
341
+ const { version } = getValidComponentVersion(forcedVersion);
342
+ return version ? id.changeVersion(version) : undefined;
343
+ }
344
+
345
+ /**
346
+ * the scope side of the installed version in updateDependenciesVersions(): a workspace installs a component peer in
347
+ * the range of the policy, and records the installed version. so when the version is out of the range, it's changed
348
+ * to the version of the range. (when it's in the range, it's kept, as a workspace that has it installed would do)
349
+ */
350
+ private setComponentPeerVersionInRange(dependency: Dependency, range: string) {
351
+ if (!this.workspaceComponentIds || this.workspaceComponentIds.hasWithoutVersion(dependency.id)) return;
352
+ const version = dependency.id.version;
353
+ if (!version || !semver.validRange(range)) return;
354
+ // a snap is not in any semver range, a workspace would install a version of the range instead
355
+ if (semver.valid(version) && semver.satisfies(version, range)) return;
356
+ const { version: versionInRange } = getValidComponentVersion(range);
357
+ // e.g. "<2.0.0" is coerced to "2.0.0", which is out of the range. there's no version to take for such a range.
358
+ if (versionInRange && semver.satisfies(versionInRange, range)) {
359
+ dependency.id = dependency.id.changeVersion(versionInRange);
360
+ }
361
+ }
362
+
363
+ private setVersionFromConfig(id: ComponentID) {
364
+ const idStr = id.toStringWithoutVersion();
365
+ const dependenciesWithVersionFromConfig = (this.component.dependenciesWithVersionFromConfig ||= []);
366
+ if (!dependenciesWithVersionFromConfig.includes(idStr)) dependenciesWithVersionFromConfig.push(idStr);
367
+ }
368
+
314
369
  private findComponentDepByPackageName(packageName: string): Dependency | undefined {
315
370
  const { dependencies, devDependencies, peerDependencies } = this.allDependencies;
316
371
  const isMatch = (dep: Dependency) => dep.packageName === packageName;
@@ -341,6 +396,9 @@ export class ApplyOverrides {
341
396
  const dependencyExist = existingDependencies[depField].find((d) =>
342
397
  d.id.isEqualWithoutVersion(componentData.componentId)
343
398
  );
399
+ // the version-maker keeps it, rather than changing it to a version snapped along (see updateDependenciesVersions)
400
+ if (componentData.versionFromConfig) this.setVersionFromConfig(componentData.componentId);
401
+ if (dependencyExist && componentData.versionFromConfig) dependencyExist.id = componentData.componentId;
344
402
  if (!dependencyExist) {
345
403
  this.overridesDependencies._addManuallyAddedDep(depField, componentData.componentId.toString());
346
404
  components[depField] ? components[depField].push(componentData) : (components[depField] = [componentData]);
@@ -732,6 +790,7 @@ export class ApplyOverrides {
732
790
  if ((existsInCompsDeps || existsInCompsDevDeps) && field === 'peerDependencies') {
733
791
  const comp = (existsInCompsDeps ?? existsInCompsDevDeps) as Dependency;
734
792
  comp.versionRange = pkgVal;
793
+ this.setComponentPeerVersionInRange(comp, pkgVal);
735
794
  this.allDependencies.peerDependencies.push(comp);
736
795
  } else {
737
796
  // a package has no separate version and range, so a "+" placeholder is resolved here.
@@ -743,6 +802,7 @@ export class ApplyOverrides {
743
802
 
744
803
  if (existsInCompsPeerDeps) {
745
804
  existsInCompsPeerDeps.versionRange = pkgVal;
805
+ this.setComponentPeerVersionInRange(existsInCompsPeerDeps, pkgVal);
746
806
  }
747
807
  }
748
808
 
@@ -16,6 +16,7 @@ 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
18
  import type { DependenciesOverridesData } from '@teambit/legacy.consumer-config';
19
+ import type { ComponentIdList } from '@teambit/component-id';
19
20
  import { DependenciesData } from './dependencies-data';
20
21
  import { updateDependenciesVersions } from './dependencies-versions-resolver';
21
22
  import type { DebugDependencies } from './auto-detect-deps';
@@ -67,8 +68,13 @@ export class DependenciesLoader {
67
68
  * `autoDetectOverrides` are the non-force policy entries (versions for dependencies that were detected). a workspace
68
69
  * calculates them on its own, a scope gets them from the caller.
69
70
  */
70
- async loadFromScope(dependenciesData: Partial<DependenciesData>, autoDetectOverrides?: DependenciesOverridesData) {
71
+ async loadFromScope(
72
+ dependenciesData: Partial<DependenciesData>,
73
+ autoDetectOverrides?: DependenciesOverridesData,
74
+ workspaceComponentIds?: ComponentIdList
75
+ ) {
71
76
  const applyOverrides = new ApplyOverrides(this.component, this.depsResolver, this.logger);
77
+ applyOverrides.workspaceComponentIds = workspaceComponentIds;
72
78
  const { allDependencies, allPackagesDependencies, issues } = dependenciesData;
73
79
  if (allDependencies) applyOverrides.allDependencies = allDependencies;
74
80
  if (allPackagesDependencies) applyOverrides.allPackagesDependencies = allPackagesDependencies;
@@ -1,4 +1,5 @@
1
1
  import type { ComponentID } from '@teambit/component-id';
2
+ import { ComponentIdList } from '@teambit/component-id';
2
3
  import type { IssuesList } from '@teambit/component-issues';
3
4
  import type { ConsumerComponent as Component } from '@teambit/legacy.consumer-component';
4
5
  import { Dependency } from '@teambit/legacy.consumer-component';
@@ -43,6 +44,14 @@ export declare class ApplyOverrides {
43
44
  * these overrides are from env/variants/merge-config. not ones with "force: true".
44
45
  */
45
46
  autoDetectOverrides: Record<string, any> | undefined;
47
+ /**
48
+ * outside a workspace, when the dependencies are calculated from a new policy (e.g. a lane merge from a bare scope):
49
+ * the components that a workspace would have in its .bitmap, e.g. the components of the lane.
50
+ * a workspace takes the version of a component dependency from a forced policy, the .bitmap or the installed package
51
+ * (see updateDependenciesVersions). a scope has no .bitmap and no node_modules, so when this is set, the versions of
52
+ * the policy are applied here. otherwise, a component dependency keeps its version.
53
+ */
54
+ workspaceComponentIds?: ComponentIdList;
46
55
  constructor(component: Component, depsResolver: DependencyResolverMain, logger: Logger, workspace?: Workspace | undefined);
47
56
  private setOverridesDependencies;
48
57
  private getEnvExtendsDeps;
@@ -93,6 +102,18 @@ export declare class ApplyOverrides {
93
102
  private removeIgnoredPackagesByOverrides;
94
103
  private _resolvePackageData;
95
104
  private _getComponentIdToAdd;
105
+ /**
106
+ * the scope side of resolveFromOverrides() in updateDependenciesVersions(): a forced policy sets the version of a
107
+ * component dependency. the version of an env's own dependency on a workspace component follows the workspace.
108
+ */
109
+ private getComponentIdFromForcedVersion;
110
+ /**
111
+ * the scope side of the installed version in updateDependenciesVersions(): a workspace installs a component peer in
112
+ * the range of the policy, and records the installed version. so when the version is out of the range, it's changed
113
+ * to the version of the range. (when it's in the range, it's kept, as a workspace that has it installed would do)
114
+ */
115
+ private setComponentPeerVersionInRange;
116
+ private setVersionFromConfig;
96
117
  private findComponentDepByPackageName;
97
118
  getDependenciesToAddManually(packageJson: Record<string, any> | null | undefined, existingDependencies: AllDependencies): {
98
119
  components: Record<string, any>;
@@ -102,6 +102,13 @@ function _dependenciesData() {
102
102
  };
103
103
  return data;
104
104
  }
105
+ function _autoDetectDeps() {
106
+ const data = require("./auto-detect-deps");
107
+ _autoDetectDeps = function () {
108
+ return data;
109
+ };
110
+ return data;
111
+ }
105
112
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
106
113
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
107
114
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -138,6 +145,14 @@ class ApplyOverrides {
138
145
  * these overrides are from env/variants/merge-config. not ones with "force: true".
139
146
  */
140
147
  _defineProperty(this, "autoDetectOverrides", void 0);
148
+ /**
149
+ * outside a workspace, when the dependencies are calculated from a new policy (e.g. a lane merge from a bare scope):
150
+ * the components that a workspace would have in its .bitmap, e.g. the components of the lane.
151
+ * a workspace takes the version of a component dependency from a forced policy, the .bitmap or the installed package
152
+ * (see updateDependenciesVersions). a scope has no .bitmap and no node_modules, so when this is set, the versions of
153
+ * the policy are applied here. otherwise, a component dependency keeps its version.
154
+ */
155
+ _defineProperty(this, "workspaceComponentIds", void 0);
141
156
  this.componentId = component.componentId;
142
157
  this.componentFromModel = this.component.componentFromModel;
143
158
  this.allDependencies = {
@@ -335,10 +350,12 @@ class ApplyOverrides {
335
350
  if (compDep) {
336
351
  // same as the workspace (see updateDependenciesVersions), only a peer component dependency keeps its range
337
352
  const range = depField === 'peerDependencies' ? versionRange : undefined;
353
+ const idFromConfig = this.getComponentIdFromForcedVersion(compDep.id, dependency, versionRange);
338
354
  return {
339
- componentId: compDep.id,
355
+ componentId: idFromConfig || compDep.id,
340
356
  packageName: dependency,
341
- versionRange: range
357
+ versionRange: range,
358
+ versionFromConfig: Boolean(idFromConfig)
342
359
  };
343
360
  }
344
361
  }
@@ -349,6 +366,45 @@ class ApplyOverrides {
349
366
  versionRange
350
367
  };
351
368
  }
369
+
370
+ /**
371
+ * the scope side of resolveFromOverrides() in updateDependenciesVersions(): a forced policy sets the version of a
372
+ * component dependency. the version of an env's own dependency on a workspace component follows the workspace.
373
+ */
374
+ getComponentIdFromForcedVersion(id, packageName, forcedVersion) {
375
+ if (!this.workspaceComponentIds) return undefined;
376
+ const isEnvOwn = this.overridesDependencies.envOwnPkgNames.has(packageName);
377
+ if (isEnvOwn && this.workspaceComponentIds.hasWithoutVersion(id)) return undefined;
378
+ const {
379
+ version
380
+ } = (0, _autoDetectDeps().getValidComponentVersion)(forcedVersion);
381
+ return version ? id.changeVersion(version) : undefined;
382
+ }
383
+
384
+ /**
385
+ * the scope side of the installed version in updateDependenciesVersions(): a workspace installs a component peer in
386
+ * the range of the policy, and records the installed version. so when the version is out of the range, it's changed
387
+ * to the version of the range. (when it's in the range, it's kept, as a workspace that has it installed would do)
388
+ */
389
+ setComponentPeerVersionInRange(dependency, range) {
390
+ if (!this.workspaceComponentIds || this.workspaceComponentIds.hasWithoutVersion(dependency.id)) return;
391
+ const version = dependency.id.version;
392
+ if (!version || !_semver().default.validRange(range)) return;
393
+ // a snap is not in any semver range, a workspace would install a version of the range instead
394
+ if (_semver().default.valid(version) && _semver().default.satisfies(version, range)) return;
395
+ const {
396
+ version: versionInRange
397
+ } = (0, _autoDetectDeps().getValidComponentVersion)(range);
398
+ // e.g. "<2.0.0" is coerced to "2.0.0", which is out of the range. there's no version to take for such a range.
399
+ if (versionInRange && _semver().default.satisfies(versionInRange, range)) {
400
+ dependency.id = dependency.id.changeVersion(versionInRange);
401
+ }
402
+ }
403
+ setVersionFromConfig(id) {
404
+ const idStr = id.toStringWithoutVersion();
405
+ const dependenciesWithVersionFromConfig = this.component.dependenciesWithVersionFromConfig ||= [];
406
+ if (!dependenciesWithVersionFromConfig.includes(idStr)) dependenciesWithVersionFromConfig.push(idStr);
407
+ }
352
408
  findComponentDepByPackageName(packageName) {
353
409
  const {
354
410
  dependencies,
@@ -375,6 +431,9 @@ class ApplyOverrides {
375
431
  return;
376
432
  }
377
433
  const dependencyExist = existingDependencies[depField].find(d => d.id.isEqualWithoutVersion(componentData.componentId));
434
+ // the version-maker keeps it, rather than changing it to a version snapped along (see updateDependenciesVersions)
435
+ if (componentData.versionFromConfig) this.setVersionFromConfig(componentData.componentId);
436
+ if (dependencyExist && componentData.versionFromConfig) dependencyExist.id = componentData.componentId;
378
437
  if (!dependencyExist) {
379
438
  this.overridesDependencies._addManuallyAddedDep(depField, componentData.componentId.toString());
380
439
  components[depField] ? components[depField].push(componentData) : components[depField] = [componentData];
@@ -695,6 +754,7 @@ class ApplyOverrides {
695
754
  if ((existsInCompsDeps || existsInCompsDevDeps) && field === 'peerDependencies') {
696
755
  const comp = existsInCompsDeps ?? existsInCompsDevDeps;
697
756
  comp.versionRange = pkgVal;
757
+ this.setComponentPeerVersionInRange(comp, pkgVal);
698
758
  this.allDependencies.peerDependencies.push(comp);
699
759
  } else {
700
760
  // a package has no separate version and range, so a "+" placeholder is resolved here.
@@ -703,6 +763,7 @@ class ApplyOverrides {
703
763
  }
704
764
  if (existsInCompsPeerDeps) {
705
765
  existsInCompsPeerDeps.versionRange = pkgVal;
766
+ this.setComponentPeerVersionInRange(existsInCompsPeerDeps, pkgVal);
706
767
  }
707
768
  }
708
769
  async applyAutoDetectedPeersFromEnvOnEnvItSelf() {