@teambit/workspace 1.0.1135 → 1.0.1136

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/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@teambit/workspace",
3
- "version": "1.0.1135",
3
+ "version": "1.0.1136",
4
4
  "homepage": "https://bit.cloud/teambit/workspace/workspace",
5
5
  "main": "dist/index.js",
6
6
  "componentId": {
7
7
  "scope": "teambit.workspace",
8
8
  "name": "workspace",
9
- "version": "1.0.1135"
9
+ "version": "1.0.1136"
10
10
  },
11
11
  "dependencies": {
12
12
  "lodash": "4.17.21",
@@ -30,41 +30,24 @@
30
30
  "reset-css": "5.0.1",
31
31
  "@teambit/component-id": "1.2.4",
32
32
  "@teambit/harmony": "0.4.12",
33
- "@teambit/legacy.extension-data": "0.0.152",
34
- "@teambit/legacy.scope": "0.0.150",
35
33
  "@teambit/component-version": "1.0.4",
36
- "@teambit/legacy.consumer-component": "0.0.151",
37
- "@teambit/legacy.consumer": "0.0.150",
38
34
  "@teambit/bit-error": "0.0.404",
39
35
  "@teambit/lane-id": "0.0.312",
40
- "@teambit/legacy.bit-map": "0.0.207",
41
36
  "@teambit/toolbox.fs.last-modified": "0.0.19",
42
37
  "@teambit/toolbox.path.path": "0.0.21",
43
38
  "@teambit/graph.cleargraph": "0.0.11",
44
- "@teambit/logger": "0.0.1470",
45
- "@teambit/cli": "0.0.1377",
46
- "@teambit/config-store": "0.0.258",
47
39
  "@teambit/legacy.constants": "0.0.42",
48
40
  "@teambit/component.ui.component-status-resolver": "0.0.510",
49
41
  "@teambit/harmony.modules.load-trace": "0.0.13",
50
42
  "@teambit/harmony.modules.resolved-component": "0.0.525",
51
43
  "@teambit/legacy.utils": "0.0.49",
52
- "@teambit/scope.remotes": "0.0.150",
53
- "@teambit/config": "0.0.1552",
54
44
  "@teambit/harmony.modules.requireable-component": "0.0.525",
55
45
  "@teambit/toolbox.modules.module-resolver": "0.0.24",
56
- "@teambit/workspace.modules.node-modules-linker": "0.0.381",
57
- "@teambit/global-config": "0.0.1381",
58
- "@teambit/legacy.consumer-config": "0.0.150",
59
- "@teambit/variants": "0.0.1645",
60
46
  "@teambit/component-issues": "0.0.184",
61
- "@teambit/component.sources": "0.0.202",
62
47
  "@teambit/dependencies.modules.packages-excluder": "1.0.8",
63
48
  "@teambit/git.modules.git-executable": "0.0.43",
64
49
  "@teambit/harmony.modules.in-memory-cache": "0.0.46",
65
50
  "@teambit/legacy-bit-id": "1.1.3",
66
- "@teambit/legacy.component-list": "0.0.204",
67
- "@teambit/legacy.scope-api": "0.0.205",
68
51
  "@teambit/toolbox.path.is-path-inside": "0.0.513",
69
52
  "@teambit/workspace.modules.match-pattern": "0.0.533",
70
53
  "@teambit/component.ui.component-drawer": "0.0.503",
@@ -101,23 +84,40 @@
101
84
  "@teambit/workspace.hooks.use-url-change-broadcaster": "0.0.2",
102
85
  "@teambit/workspace.ui.preserve-workspace-mode": "0.0.3",
103
86
  "@teambit/evangelist.elements.icon": "1.0.5",
104
- "@teambit/component": "1.0.1135",
105
- "@teambit/dependency-resolver": "1.0.1135",
106
- "@teambit/envs": "1.0.1135",
107
- "@teambit/objects": "0.0.642",
108
- "@teambit/scope": "1.0.1135",
109
- "@teambit/graph": "1.0.1135",
110
- "@teambit/isolator": "1.0.1135",
111
- "@teambit/component-tree": "1.0.1135",
112
- "@teambit/watcher": "1.0.1135",
113
- "@teambit/aspect-loader": "1.0.1135",
114
- "@teambit/graphql": "1.0.1135",
115
- "@teambit/bundler": "1.0.1135",
116
- "@teambit/ui": "1.0.1135",
117
- "@teambit/command-bar": "1.0.1135",
118
- "@teambit/sidebar": "1.0.1135",
119
- "@teambit/pubsub": "1.0.1135",
120
- "@teambit/deprecation": "1.0.1135"
87
+ "@teambit/component": "1.0.1136",
88
+ "@teambit/dependency-resolver": "1.0.1136",
89
+ "@teambit/envs": "1.0.1136",
90
+ "@teambit/legacy.extension-data": "0.0.153",
91
+ "@teambit/legacy.scope": "0.0.151",
92
+ "@teambit/legacy.consumer-component": "0.0.152",
93
+ "@teambit/legacy.consumer": "0.0.151",
94
+ "@teambit/legacy.bit-map": "0.0.208",
95
+ "@teambit/logger": "0.0.1471",
96
+ "@teambit/objects": "0.0.643",
97
+ "@teambit/scope": "1.0.1136",
98
+ "@teambit/graph": "1.0.1136",
99
+ "@teambit/cli": "0.0.1378",
100
+ "@teambit/config-store": "0.0.259",
101
+ "@teambit/isolator": "1.0.1136",
102
+ "@teambit/component-tree": "1.0.1136",
103
+ "@teambit/watcher": "1.0.1136",
104
+ "@teambit/scope.remotes": "0.0.151",
105
+ "@teambit/aspect-loader": "1.0.1136",
106
+ "@teambit/config": "0.0.1553",
107
+ "@teambit/workspace.modules.node-modules-linker": "0.0.382",
108
+ "@teambit/graphql": "1.0.1136",
109
+ "@teambit/bundler": "1.0.1136",
110
+ "@teambit/global-config": "0.0.1382",
111
+ "@teambit/legacy.consumer-config": "0.0.151",
112
+ "@teambit/ui": "1.0.1136",
113
+ "@teambit/variants": "0.0.1646",
114
+ "@teambit/component.sources": "0.0.203",
115
+ "@teambit/legacy.component-list": "0.0.205",
116
+ "@teambit/legacy.scope-api": "0.0.206",
117
+ "@teambit/command-bar": "1.0.1136",
118
+ "@teambit/sidebar": "1.0.1136",
119
+ "@teambit/pubsub": "1.0.1136",
120
+ "@teambit/deprecation": "1.0.1136"
121
121
  },
122
122
  "devDependencies": {
123
123
  "@types/lodash": "4.14.165",
@@ -29,6 +29,7 @@ import {
29
29
  reportLoadFailure,
30
30
  } from '@teambit/harmony.modules.load-trace';
31
31
  import type { AspectLoaderMain } from '@teambit/aspect-loader';
32
+ import { AsyncLocalStorage } from 'async_hooks';
32
33
  import type { Workspace } from '../workspace';
33
34
  import { WorkspaceComponent } from './workspace-component';
34
35
  import { MergeConfigConflict } from '../exceptions/merge-config-conflict';
@@ -110,6 +111,17 @@ export class WorkspaceComponentLoader {
110
111
  }>;
111
112
 
112
113
  private componentLoadedSelfAsAspects: InMemoryCache<boolean>; // cache loaded components
114
+
115
+ /**
116
+ * ids being loaded through the getMany pipeline within the current async call-chain, on behalf
117
+ * of a single `get` call. a re-entrant `get` of the same id from inside that pipeline (e.g. an
118
+ * onLoad subscriber asking for the component it's calculating) must use the direct flow,
119
+ * otherwise it recurses forever. an unrelated concurrent `get` of the same id is not part of
120
+ * the chain and awaits the in-flight load (singleLoadsInFlight) instead.
121
+ * see shouldDelegateToGetMany and loadOneThroughGetMany.
122
+ */
123
+ private singleLoadChainContext = new AsyncLocalStorage<Set<string>>();
124
+ private singleLoadsInFlight = new Map<string, Promise<Component | undefined>>();
113
125
  constructor(
114
126
  private workspace: Workspace,
115
127
  private logger: Logger,
@@ -849,8 +861,14 @@ export class WorkspaceComponentLoader {
849
861
  return fromCache;
850
862
  }
851
863
  span.setAttribute('componentsCache', 'miss');
852
- let consumerComponent = legacyComponent;
853
864
  const inWs = await this.isInWsIncludeDeleted(id);
865
+ if (!legacyComponent && this.shouldDelegateToGetMany(id, inWs, useCache, storeInCache, loadOpts)) {
866
+ const fromGetMany = await this.loadOneThroughGetMany(id);
867
+ if (fromGetMany) return fromGetMany;
868
+ // the grouped pipeline didn't yield the component (e.g. an out-of-sync id changed during
869
+ // the load). fall through so the flow below reports the precise error for the requested id.
870
+ }
871
+ let consumerComponent = legacyComponent;
854
872
  if (inWs && !consumerComponent) {
855
873
  consumerComponent = await loadSpan('consumer-fs-load', { id: id.toString() }, () =>
856
874
  this.getConsumerComponent(id, loadOptsWithDefaults)
@@ -868,6 +886,67 @@ export class WorkspaceComponentLoader {
868
886
  return component;
869
887
  }
870
888
 
889
+ /**
890
+ * a cold single-component load resolves the component's env lazily in the middle of its own
891
+ * load, without the env-first group ordering of the getMany pipeline (see buildLoadGroups).
892
+ * when the env fails to load at that point, dependency policies silently fall back to defaults,
893
+ * producing a component with wrong dependency data (which then surfaces as phantom
894
+ * modifications in status/diff/merge). to keep one source of truth, a plain cache-miss load of
895
+ * a workspace component is delegated to the grouped pipeline.
896
+ */
897
+ private shouldDelegateToGetMany(
898
+ id: ComponentID,
899
+ inWs: boolean,
900
+ useCache: boolean,
901
+ storeInCache: boolean,
902
+ loadOpts?: ComponentLoadOptions
903
+ ): boolean {
904
+ if (!inWs) return false; // scope components load from the model, no dependency-policy calculation
905
+ if (!useCache || !storeInCache) return false; // getMany always uses and populates the cache
906
+ // any custom load-option means a caller-specific contract (partial load, docs/compositions
907
+ // flags, etc.). the getMany pipeline manages these options itself per load-stage, so only a
908
+ // plain full load can be delegated.
909
+ const isDefaultLoadOpts =
910
+ !loadOpts ||
911
+ Object.entries(loadOpts).every(
912
+ ([key, value]) =>
913
+ value === undefined || ((key === 'loadExtensions' || key === 'executeLoadSlot') && value === true)
914
+ );
915
+ if (!isDefaultLoadOpts) return false;
916
+ // re-entrance from within the pipeline of the same id (e.g. an onLoad subscriber asking for
917
+ // the component it's calculating) must use the direct flow, otherwise it recurses forever.
918
+ return !this.singleLoadChainContext.getStore()?.has(id.toString());
919
+ }
920
+
921
+ private async loadOneThroughGetMany(id: ComponentID): Promise<Component | undefined> {
922
+ const idStr = id.toString();
923
+ // an unrelated concurrent load of the same id joins the in-flight grouped load, rather than
924
+ // being mistaken for pipeline re-entrance and sent through the direct flow.
925
+ const inFlight = this.singleLoadsInFlight.get(idStr);
926
+ if (inFlight) return inFlight;
927
+ const chainIds = this.singleLoadChainContext.getStore() ?? new Set<string>();
928
+ // the promise is created inside run() so the whole load executes within the chain context.
929
+ // (assigned via `let` because some @types/node versions type `run` as returning void)
930
+ let loadPromise!: Promise<Component | undefined>;
931
+ this.singleLoadChainContext.run(new Set([...chainIds, idStr]), () => {
932
+ loadPromise = this.getMany([id]).then(({ components }) => {
933
+ if (components.length === 1) return components[0];
934
+ // the load may have fixed an out-of-sync .bitmap and changed the component-id in the
935
+ // process (e.g. a stale version was removed or bumped). in that case getMany filtered the
936
+ // component out because it no longer matches the requested id. look it up by its updated
937
+ // .bitmap id.
938
+ const updatedBitmapId = this.workspace.consumer.bitmapIdsFromCurrentLaneIncludeRemoved.searchWithoutVersion(id);
939
+ if (updatedBitmapId && updatedBitmapId.version !== id.version) {
940
+ return this.getFromCache(updatedBitmapId, { loadExtensions: true, executeLoadSlot: true });
941
+ }
942
+ return undefined;
943
+ });
944
+ });
945
+ const withCleanup = loadPromise.finally(() => this.singleLoadsInFlight.delete(idStr));
946
+ this.singleLoadsInFlight.set(idStr, withCleanup);
947
+ return withCleanup;
948
+ }
949
+
871
950
  async getIfExist(componentId: ComponentID) {
872
951
  try {
873
952
  return await this.get(componentId);