@teambit/workspace 1.0.1018 → 1.0.1020

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.
@@ -116,6 +116,13 @@ function _harmonyModules2() {
116
116
  };
117
117
  return data;
118
118
  }
119
+ function _harmonyModules3() {
120
+ const data = require("@teambit/harmony.modules.load-trace");
121
+ _harmonyModules3 = function () {
122
+ return data;
123
+ };
124
+ return data;
125
+ }
119
126
  function _workspaceComponent() {
120
127
  const data = require("./workspace-component");
121
128
  _workspaceComponent = function () {
@@ -177,9 +184,14 @@ class WorkspaceComponentLoader {
177
184
  invalidComponents: []
178
185
  };
179
186
  }
187
+ return (0, _harmonyModules3().startOrJoinLoadTrace)('workspace.getMany', {
188
+ ids: idsWithoutEmpty.length
189
+ }, span => this.getManyWithSpan(idsWithoutEmpty, span, loadOpts, throwOnFailure));
190
+ }
191
+ async getManyWithSpan(idsWithoutEmpty, span, loadOpts, throwOnFailure = true) {
180
192
  const callId = Math.floor(Math.random() * 1000); // generate a random callId to be able to identify the call from the logs
181
193
  this.logger.profileTrace(`getMany-${callId}`);
182
- this.logger.setStatusLine(`loading ${ids.length} component(s)`);
194
+ this.logger.setStatusLine(`loading ${idsWithoutEmpty.length} component(s)`);
183
195
  const loadOptsWithDefaults = Object.assign(
184
196
  // We don't want to load extension or execute the load slot at this step
185
197
  // we will do it later
@@ -205,6 +217,8 @@ class WorkspaceComponentLoader {
205
217
  loadOrCached.idsToLoad.push(id);
206
218
  }
207
219
  }, loadOrCached);
220
+ span.setAttribute('componentsCache:hits', loadOrCached.fromCache.length);
221
+ span.setAttribute('componentsCache:misses', loadOrCached.idsToLoad.length);
208
222
  const {
209
223
  components: loadedComponents,
210
224
  invalidComponents
@@ -225,7 +239,7 @@ class WorkspaceComponentLoader {
225
239
  executeLoadSlot: true
226
240
  });
227
241
  });
228
- const idsWithEmptyStrs = ids.map(id => id.toString());
242
+ const idsWithEmptyStrs = idsWithoutEmpty.map(id => id.toString());
229
243
  const requestedComponents = components.filter(comp => idsWithEmptyStrs.includes(comp.id.toString()) || idsWithEmptyStrs.includes(comp.id.toStringWithoutVersion()));
230
244
  this.logger.profileTrace(`getMany-${callId}`);
231
245
  this.logger.clearStatusLine();
@@ -239,9 +253,11 @@ class WorkspaceComponentLoader {
239
253
  components: [],
240
254
  invalidComponents: []
241
255
  };
242
- const workspaceScopeIdsMap = await this.groupAndUpdateIds(ids);
256
+ const workspaceScopeIdsMap = await (0, _harmonyModules3().loadSpan)('id-resolution', {
257
+ ids: ids.length
258
+ }, () => this.groupAndUpdateIds(ids));
243
259
  this.logger.profileTrace('buildLoadGroups');
244
- const groupsToHandle = await this.buildLoadGroups(workspaceScopeIdsMap);
260
+ const groupsToHandle = await (0, _harmonyModules3().loadSpan)('build-load-groups', {}, () => this.buildLoadGroups(workspaceScopeIdsMap));
245
261
  this.logger.profileTrace('buildLoadGroups');
246
262
  // prefix your command with "BIT_LOG=*" to see the detailed groups
247
263
  if (process.env.BIT_LOG) {
@@ -256,17 +272,21 @@ class WorkspaceComponentLoader {
256
272
  seeders,
257
273
  envs
258
274
  } = group;
259
- const groupDesc = `getMany-${callId} group ${index + 1}/${groupsToHandle.length} - ${loadGroupToStr(group)}`;
275
+ const groupStr = loadGroupToStr(group);
276
+ const groupDesc = `getMany-${callId} group ${index + 1}/${groupsToHandle.length} - ${groupStr}`;
260
277
  this.logger.profileTrace(groupDesc);
261
278
  if (!workspaceIds.length && !scopeIds.length) {
262
279
  throw new Error('getAndLoadSlotOrdered - group has no ids to load');
263
280
  }
264
- const res = await this.getAndLoadSlot(workspaceIds, scopeIds, _objectSpread(_objectSpread({}, loadOpts), {}, {
281
+ const res = await (0, _harmonyModules3().loadSpan)('load-group', {
282
+ group: `${index + 1}/${groupsToHandle.length}`,
283
+ desc: groupStr
284
+ }, () => this.getAndLoadSlot(workspaceIds, scopeIds, _objectSpread(_objectSpread({}, loadOpts), {}, {
265
285
  core,
266
286
  seeders,
267
287
  aspects,
268
288
  envs
269
- }));
289
+ })));
270
290
  this.logger.profileTrace(groupDesc);
271
291
  // We don't want to return components that were not asked originally (we do want to load them)
272
292
  if (!group.seeders) return undefined;
@@ -485,7 +505,9 @@ class WorkspaceComponentLoader {
485
505
  });
486
506
  if (loadOpts.loadExtensions) {
487
507
  this.logger.profileTrace('loadComponentsExtensions');
488
- await this.workspace.loadComponentsExtensions(filteredMergeExtensions);
508
+ await (0, _harmonyModules3().loadSpan)('load-components-extensions', {
509
+ extensions: filteredMergeExtensions.length
510
+ }, () => this.workspace.loadComponentsExtensions(filteredMergeExtensions));
489
511
  this.logger.profileTrace('loadComponentsExtensions');
490
512
  }
491
513
  let wsComponentsWithAspects = workspaceComponents;
@@ -505,16 +527,19 @@ class WorkspaceComponentLoader {
505
527
  // so we will get wrong dependencies from component who uses envs from the workspace
506
528
  this.logger.profileTrace('loadCompsAsAspects');
507
529
  if (loadOpts.loadSeedersAsAspects || loadOpts.core && loadOpts.aspects) {
508
- await this.loadCompsAsAspects(workspaceComponents.concat(scopeComponents), {
530
+ await (0, _harmonyModules3().loadSpan)('load-comps-as-aspects', {
531
+ components: workspaceComponents.length + scopeComponents.length
532
+ }, () => this.loadCompsAsAspects(workspaceComponents.concat(scopeComponents), {
509
533
  loadApps: true,
510
534
  loadEnvs: true,
511
535
  loadAspects: loadOpts.aspects,
512
536
  core: loadOpts.core,
513
537
  seeders: loadOpts.seeders,
514
538
  idsToNotLoadAsAspects: loadOpts.idsToNotLoadAsAspects
515
- });
539
+ }));
516
540
  }
517
541
  this.logger.profileTrace('loadCompsAsAspects');
542
+ this.attachReportedLoadFailures(withAspects);
518
543
  return {
519
544
  components: withAspects,
520
545
  invalidComponents
@@ -560,13 +585,80 @@ class WorkspaceComponentLoader {
560
585
  });
561
586
  } catch (err) {
562
587
  this.logger.warn(`failed loading components as aspects for components ${aspectIds.join(', ')}`, err);
563
- // we ignore that errors at the moment
588
+ // the error is not thrown (best-effort loading), but it's surfaced as a component issue
589
+ // so it shows up in "bit status" instead of disappearing.
590
+ if (!this.workspace.inInstallContext) {
591
+ const errMsg = err.message || String(err);
592
+ aspectIds.forEach(aspectId => {
593
+ (0, _harmonyModules3().reportLoadFailure)({
594
+ failedId: aspectId,
595
+ phase: 'load-comps-as-aspects',
596
+ error: errMsg
597
+ });
598
+ });
599
+ components.forEach(component => {
600
+ if (!aspectIds.includes(component.id.toString())) return;
601
+ this.addLoadFailureIssue(component, component.id.toString(), 'load-comps-as-aspects', errMsg);
602
+ });
603
+ }
564
604
  }
565
605
  }
606
+ addLoadFailureIssue(component, failedId, phase, error) {
607
+ const consumerComponent = component.state._consumer;
608
+ if (!consumerComponent) return;
609
+ const issue = component.state.issues.getOrCreate(_componentIssues().IssuesClasses.LoadFailures);
610
+ const alreadyReported = issue.data.some(entry => entry.failedId === failedId && entry.phase === phase);
611
+ if (!alreadyReported) issue.data.push({
612
+ failedId,
613
+ phase,
614
+ error
615
+ });
616
+ }
617
+
618
+ /**
619
+ * surface load failures that were reported to the active trace by deep loader code (e.g. aspect
620
+ * require failures in the scope loader). the failing component itself gets a per-component
621
+ * issue. components merely *using* the failed aspect/env are aggregated into a single
622
+ * workspace-level issue — one failing env may be used by 100 components, and attaching the
623
+ * issue to each of them would flood "bit status".
624
+ */
625
+ attachReportedLoadFailures(components) {
626
+ if (this.workspace.inInstallContext) return;
627
+ const failures = (0, _harmonyModules3().currentLoadTrace)()?.loadFailures;
628
+ if (!failures?.length) return;
629
+ const noVersion = id => id.split('@')[0];
630
+ components.forEach(component => {
631
+ const componentIdStr = component.id.toString();
632
+ const usedIds = component.state._consumer ? component.state._consumer.extensions.map(ext => ext.stringId) : [];
633
+ // the env may be set via the EnvsAspect config rather than as a direct extension entry, in
634
+ // which case its id won't appear in the extensions list above. include it explicitly so a
635
+ // failure loading that env still gets aggregated to this component.
636
+ const envId = component.state.aspects.get(_envs().EnvsAspect.id)?.data?.id;
637
+ if (envId) usedIds.push(envId);
638
+ failures.forEach(failure => {
639
+ if (noVersion(failure.failedId) === noVersion(componentIdStr)) {
640
+ this.addLoadFailureIssue(component, failure.failedId, failure.phase, failure.error);
641
+ return;
642
+ }
643
+ if (usedIds.some(usedId => noVersion(usedId) === noVersion(failure.failedId))) {
644
+ this.workspace.registerAggregatedLoadFailure(failure, componentIdStr);
645
+ }
646
+ });
647
+ });
648
+ }
566
649
  async populateScopeAndExtensionsCache(ids, workspaceScopeIdsMap) {
567
- return (0, _pMapSeries().default)(ids, async id => {
650
+ return (0, _harmonyModules3().loadSpan)('populate-scope-and-extensions-cache', {
651
+ ids: ids.length
652
+ }, span => this.populateScopeAndExtensionsCacheWithSpan(ids, workspaceScopeIdsMap, span));
653
+ }
654
+ async populateScopeAndExtensionsCacheWithSpan(ids, workspaceScopeIdsMap, span) {
655
+ let scopeCacheHits = 0;
656
+ let extensionsCacheHits = 0;
657
+ const result = await (0, _pMapSeries().default)(ids, async id => {
568
658
  const idStr = id.toString();
569
659
  let componentFromScope;
660
+ if (this.scopeComponentsCache.has(idStr)) scopeCacheHits += 1;
661
+ if (this.componentsExtensionsCache.has(idStr)) extensionsCacheHits += 1;
570
662
  if (!this.scopeComponentsCache.has(idStr)) {
571
663
  try {
572
664
  // Do not import automatically if it's missing, it will throw an error later
@@ -597,6 +689,10 @@ class WorkspaceComponentLoader {
597
689
  });
598
690
  }
599
691
  });
692
+ span.setAttribute('scopeComponentsCache:hits', scopeCacheHits);
693
+ span.setAttribute('scopeComponentsCache:misses', ids.length - scopeCacheHits);
694
+ span.setAttribute('componentsExtensionsCache:hits', extensionsCacheHits);
695
+ return result;
600
696
  }
601
697
  async warnAboutMisconfiguredEnvs(components) {
602
698
  const allIds = (0, _lodash().uniq)(components.map(component => this.envs.getEnvId(component)));
@@ -650,7 +746,9 @@ class WorkspaceComponentLoader {
650
746
  components: legacyComponents,
651
747
  invalidComponents: legacyInvalidComponents,
652
748
  removedComponents
653
- } = await this.workspace.consumer.loadComponents(_componentId().ComponentIdList.fromArray(workspaceIds), false, loadOptsWithDefaults);
749
+ } = await (0, _harmonyModules3().loadSpan)('consumer-fs-load', {
750
+ ids: workspaceIds.length
751
+ }, () => this.workspace.consumer.loadComponents(_componentId().ComponentIdList.fromArray(workspaceIds), false, loadOptsWithDefaults));
654
752
  this.logger.profileTrace('consumer.loadComponents');
655
753
  const allLegacyComponents = legacyComponents.concat(removedComponents);
656
754
  legacyInvalidComponents.forEach(invalidComponent => {
@@ -712,7 +810,9 @@ class WorkspaceComponentLoader {
712
810
  // as when loading the next batch of components (next group) we won't have the envs loaded
713
811
 
714
812
  try {
715
- const scopeComponents = await this.workspace.scope.getMany(scopeIds);
813
+ const scopeComponents = await (0, _harmonyModules3().loadSpan)('scope-load', {
814
+ ids: scopeIds.length
815
+ }, () => this.workspace.scope.getMany(scopeIds));
716
816
 
717
817
  // We don't want to load envs as part of this step, they will be loaded later
718
818
  // const scopeComponents = await this.workspace.scope.loadMany(scopeIds, undefined, {
@@ -754,6 +854,13 @@ class WorkspaceComponentLoader {
754
854
  }
755
855
  async get(componentId, legacyComponent, useCache = true, storeInCache = true, loadOpts, getOpts = {
756
856
  resolveIdVersion: true
857
+ }) {
858
+ return (0, _harmonyModules3().startOrJoinLoadTrace)('workspace.get', {
859
+ id: componentId.toString()
860
+ }, span => this.getWithSpan(componentId, span, legacyComponent, useCache, storeInCache, loadOpts, getOpts));
861
+ }
862
+ async getWithSpan(componentId, span, legacyComponent, useCache = true, storeInCache = true, loadOpts, getOpts = {
863
+ resolveIdVersion: true
757
864
  }) {
758
865
  const loadOptsWithDefaults = Object.assign({
759
866
  loadExtensions: true,
@@ -762,17 +869,22 @@ class WorkspaceComponentLoader {
762
869
  const id = getOpts?.resolveIdVersion ? this.resolveVersion(componentId) : componentId;
763
870
  const fromCache = this.getFromCache(id, loadOptsWithDefaults);
764
871
  if (fromCache && useCache) {
872
+ span.setAttribute('componentsCache', 'hit');
765
873
  return fromCache;
766
874
  }
875
+ span.setAttribute('componentsCache', 'miss');
767
876
  let consumerComponent = legacyComponent;
768
877
  const inWs = await this.isInWsIncludeDeleted(id);
769
878
  if (inWs && !consumerComponent) {
770
- consumerComponent = await this.getConsumerComponent(id, loadOptsWithDefaults);
879
+ consumerComponent = await (0, _harmonyModules3().loadSpan)('consumer-fs-load', {
880
+ id: id.toString()
881
+ }, () => this.getConsumerComponent(id, loadOptsWithDefaults));
771
882
  }
772
883
 
773
884
  // in case of out-of-sync, the id may changed during the load process
774
885
  const updatedId = consumerComponent ? consumerComponent.id : id;
775
886
  const component = await this.loadOne(updatedId, consumerComponent, loadOptsWithDefaults);
887
+ this.attachReportedLoadFailures([component]);
776
888
  if (storeInCache) {
777
889
  this.addMultipleEnvsIssueIfNeeded(component); // it's in storeInCache block, otherwise, it wasn't fully loaded
778
890
  this.saveInCache(component, loadOptsWithDefaults);
@@ -821,7 +933,10 @@ class WorkspaceComponentLoader {
821
933
  }
822
934
  async loadOne(id, consumerComponent, loadOpts) {
823
935
  const idStr = id.toString();
824
- const componentFromScope = this.scopeComponentsCache.has(idStr) ? this.scopeComponentsCache.get(idStr) : await this.workspace.scope.get(id);
936
+ const scopeCacheHit = this.scopeComponentsCache.has(idStr);
937
+ const componentFromScope = scopeCacheHit ? this.scopeComponentsCache.get(idStr) : await (0, _harmonyModules3().loadSpan)('scope-load', {
938
+ id: idStr
939
+ }, () => this.workspace.scope.get(id));
825
940
  if (!consumerComponent) {
826
941
  if (!componentFromScope) throw new (_legacy3().MissingBitMapComponent)(id.toString());
827
942
  return componentFromScope;
@@ -830,9 +945,13 @@ class WorkspaceComponentLoader {
830
945
  const {
831
946
  extensions,
832
947
  errors
833
- } = extErrorsFromCache || (await this.workspace.componentExtensions(id, componentFromScope, undefined, {
948
+ } = extErrorsFromCache || (await (0, _harmonyModules3().loadSpan)('extension-merge', {
949
+ id: idStr,
950
+ scopeComponentsCache: scopeCacheHit ? 'hit' : 'miss',
951
+ componentsExtensionsCache: 'miss'
952
+ }, () => this.workspace.componentExtensions(id, componentFromScope, undefined, {
834
953
  loadExtensions: loadOpts?.loadExtensions
835
- }));
954
+ })));
836
955
  if (errors?.some(err => err instanceof _mergeConfigConflict().MergeConfigConflict)) {
837
956
  consumerComponent.issues.getOrCreate(_componentIssues().IssuesClasses.MergeConfigHasConflict).data = true;
838
957
  }
@@ -911,6 +1030,11 @@ class WorkspaceComponentLoader {
911
1030
  return err instanceof _legacy4().ComponentNotFound || err instanceof _legacy3().MissingBitMapComponent;
912
1031
  }
913
1032
  async executeLoadSlot(component, loadOpts) {
1033
+ return (0, _harmonyModules3().loadSpan)('execute-load-slot', {
1034
+ id: component.id.toString()
1035
+ }, () => this.executeLoadSlotWithSpan(component, loadOpts));
1036
+ }
1037
+ async executeLoadSlotWithSpan(component, loadOpts) {
914
1038
  if (component.state._consumer.removed) {
915
1039
  // if it was soft-removed now, the component is not in the FS. loading aspects such as composition ends up with
916
1040
  // errors as they try to read component files from the filesystem.
@@ -919,18 +1043,26 @@ class WorkspaceComponentLoader {
919
1043
 
920
1044
  // Special load events which runs from the workspace but should run from the correct aspect
921
1045
  // TODO: remove this once those extensions dependent on workspace
922
- const envsData = await this.envs.calcDescriptor(component, {
1046
+ const envsData = await (0, _harmonyModules3().loadSpan)('env-calc', {}, () => this.envs.calcDescriptor(component, {
923
1047
  skipWarnings: !!this.workspace.inInstallContext
924
- });
1048
+ }));
925
1049
  const wsDeps = component.state._consumer.dependencies.dependencies || [];
926
1050
  const modelDeps = component.state._consumer.componentFromModel?.dependencies.dependencies || [];
927
1051
  const merged = _legacy2().Dependencies.merge([wsDeps, modelDeps]);
928
1052
  const envExtendsDeps = merged.get();
929
1053
 
930
1054
  // Move to deps resolver main runtime once we switch ws<> deps resolver direction
931
- const policy = await this.dependencyResolver.mergeVariantPolicies(component.config.extensions, component.id, component.state._consumer.files, envExtendsDeps);
932
- const dependenciesList = await this.dependencyResolver.extractDepsFromLegacy(component, policy);
933
- const resolvedEnvJsonc = await this.envs.calculateEnvManifest(component, component.state._consumer.files, envExtendsDeps);
1055
+ const {
1056
+ policy,
1057
+ dependenciesList
1058
+ } = await (0, _harmonyModules3().loadSpan)('dependency-resolution', {}, async () => {
1059
+ const mergedPolicy = await this.dependencyResolver.mergeVariantPolicies(component.config.extensions, component.id, component.state._consumer.files, envExtendsDeps);
1060
+ return {
1061
+ policy: mergedPolicy,
1062
+ dependenciesList: await this.dependencyResolver.extractDepsFromLegacy(component, mergedPolicy)
1063
+ };
1064
+ });
1065
+ const resolvedEnvJsonc = await (0, _harmonyModules3().loadSpan)('env-manifest', {}, () => this.envs.calculateEnvManifest(component, component.state._consumer.files, envExtendsDeps));
934
1066
  if (resolvedEnvJsonc) {
935
1067
  // @ts-ignore
936
1068
  envsData.resolvedEnvJsonc = resolvedEnvJsonc;
@@ -951,7 +1083,9 @@ class WorkspaceComponentLoader {
951
1083
  component.state.aspects = aspectListWithEnvsAndDeps;
952
1084
  const entries = this.workspace.onComponentLoadSlot.toArray();
953
1085
  await (0, _pMapSeries().default)(entries, async ([extension, onLoad]) => {
954
- const data = await onLoad(component, loadOpts);
1086
+ const data = await (0, _harmonyModules3().loadSpan)('on-load', {
1087
+ aspect: extension
1088
+ }, () => onLoad(component, loadOpts));
955
1089
  await this.upsertExtensionData(component, extension, data);
956
1090
  // Update the aspect list to have changes happened during the on load slot (new data added above)
957
1091
  component.state.aspects.upsertEntry(await this.workspace.resolveComponentId(extension), data);