@teambit/scope 1.0.108 → 1.0.109

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,1263 +0,0 @@
1
- import GlobalConfigAspect, { GlobalConfigMain } from '@teambit/global-config';
2
- import { ExternalActions } from '@teambit/legacy/dist/api/scope/lib/action';
3
- import mapSeries from 'p-map-series';
4
- import path from 'path';
5
- import { Graph, Node, Edge } from '@teambit/graph.cleargraph';
6
- import semver from 'semver';
7
- import multimatch from 'multimatch';
8
- import type { AspectLoaderMain } from '@teambit/aspect-loader';
9
- import { RawBuilderData, BuilderAspect } from '@teambit/builder';
10
- import { AspectLoaderAspect, AspectDefinition } from '@teambit/aspect-loader';
11
- import { CLIAspect, CLIMain, MainRuntime } from '@teambit/cli';
12
- import type { AspectData, ComponentMain, ComponentMap, ResolveAspectsOptions } from '@teambit/component';
13
- import { Component, ComponentAspect, ComponentFactory, Snap, State, AspectEntry } from '@teambit/component';
14
- import type { GraphqlMain } from '@teambit/graphql';
15
- import { GraphqlAspect } from '@teambit/graphql';
16
- import { Harmony, Slot, SlotRegistry } from '@teambit/harmony';
17
- import { IsolateComponentsOptions, IsolatorAspect, IsolatorMain } from '@teambit/isolator';
18
- import { LoggerAspect, LoggerMain, Logger } from '@teambit/logger';
19
- import { ExpressAspect, ExpressMain } from '@teambit/express';
20
- import type { UiMain } from '@teambit/ui';
21
- import { UIAspect } from '@teambit/ui';
22
- import { ComponentIdList, ComponentID } from '@teambit/component-id';
23
- import { ModelComponent, Lane, Version } from '@teambit/legacy/dist/scope/models';
24
- import { Ref, Repository } from '@teambit/legacy/dist/scope/objects';
25
- import LegacyScope, { LegacyOnTagResult } from '@teambit/legacy/dist/scope/scope';
26
- import { LegacyComponentLog as ComponentLog } from '@teambit/legacy-component-log';
27
- import { loadScopeIfExist } from '@teambit/legacy/dist/scope/scope-loader';
28
- import { getDivergeData } from '@teambit/legacy/dist/scope/component-ops/get-diverge-data';
29
- import { PersistOptions } from '@teambit/legacy/dist/scope/types';
30
- import { ExportPersist, PostSign } from '@teambit/legacy/dist/scope/actions';
31
- import { DependencyResolverAspect, DependencyResolverMain, NodeLinker } from '@teambit/dependency-resolver';
32
- import { getScopeRemotes } from '@teambit/legacy/dist/scope/scope-remotes';
33
- import { Remotes } from '@teambit/legacy/dist/remotes';
34
- import { isMatchNamespacePatternItem } from '@teambit/workspace.modules.match-pattern';
35
- import { Scope } from '@teambit/legacy/dist/scope';
36
- import { CompIdGraph, DepEdgeType } from '@teambit/graph';
37
- import chokidar from '@teambit/chokidar';
38
- import { Types } from '@teambit/legacy/dist/scope/object-registrar';
39
- import { FETCH_OPTIONS } from '@teambit/legacy/dist/api/scope/lib/fetch';
40
- import { ObjectList } from '@teambit/legacy/dist/scope/objects/object-list';
41
- import { RequireableComponent } from '@teambit/harmony.modules.requireable-component';
42
- import { SnapsDistance } from '@teambit/legacy/dist/scope/component-ops/snaps-distance';
43
- import { Http, DEFAULT_AUTH_TYPE, AuthData, getAuthDataFromHeader } from '@teambit/legacy/dist/scope/network/http/http';
44
- import { remove } from '@teambit/legacy/dist/api/scope';
45
- import { BitError } from '@teambit/bit-error';
46
- import ConsumerComponent from '@teambit/legacy/dist/consumer/component';
47
- import { resumeExport } from '@teambit/legacy/dist/scope/component-ops/export-scope-components';
48
- import { GLOBAL_SCOPE } from '@teambit/legacy/dist/constants';
49
- import { BitId } from '@teambit/legacy-bit-id';
50
- import { ExtensionDataEntry, ExtensionDataList } from '@teambit/legacy/dist/consumer/config';
51
- import EnvsAspect, { EnvsMain } from '@teambit/envs';
52
- import { compact, slice, difference, partition } from 'lodash';
53
- import { ComponentNotFound } from './exceptions';
54
- import { ScopeAspect } from './scope.aspect';
55
- import { scopeSchema } from './scope.graphql';
56
- import { ScopeUIRoot } from './scope.ui-root';
57
- import { PutRoute, FetchRoute, ActionRoute, DeleteRoute } from './routes';
58
- import { ScopeComponentLoader } from './scope-component-loader';
59
- import { ScopeCmd } from './scope-cmd';
60
- import { StagedConfig } from './staged-config';
61
- import { NoIdMatchPattern } from './exceptions/no-id-match-pattern';
62
- import { ScopeAspectsLoader, ScopeLoadAspectsOptions } from './scope-aspects-loader';
63
- import { ClearCacheAction } from './clear-cache-action';
64
-
65
- type RemoteEventMetadata = { auth?: AuthData; headers?: {} };
66
- type RemoteEvent<Data> = (data: Data, metadata: RemoteEventMetadata, errors?: Array<string | Error>) => Promise<void>;
67
- type OnPostPutData = { ids: ComponentID[]; lanes: Lane[] };
68
- type OnPostDeleteData = { ids: ComponentID[] };
69
- type OnPreFetchObjectData = { ids: string[]; fetchOptions: FETCH_OPTIONS };
70
-
71
- type OnPostPut = RemoteEvent<OnPostPutData>;
72
- type OnPostExport = RemoteEvent<OnPostPutData>;
73
- type OnPostDelete = RemoteEvent<OnPostDeleteData>;
74
- type OnPostObjectsPersist = RemoteEvent<undefined>;
75
- type OnPreFetchObjects = RemoteEvent<OnPreFetchObjectData>;
76
- type OnCompAspectReCalc = (component: Component) => Promise<AspectData | undefined>;
77
-
78
- export type OnPostPutSlot = SlotRegistry<OnPostPut>;
79
- export type OnPostDeleteSlot = SlotRegistry<OnPostDelete>;
80
- export type OnPostExportSlot = SlotRegistry<OnPostExport>;
81
- export type OnPostObjectsPersistSlot = SlotRegistry<OnPostObjectsPersist>;
82
- export type OnPreFetchObjectsSlot = SlotRegistry<OnPreFetchObjects>;
83
- export type OnCompAspectReCalcSlot = SlotRegistry<OnCompAspectReCalc>;
84
- export type LoadOptions = {
85
- /**
86
- * In case the component we are loading is app, whether to load it as app (in a scope aspects capsule)
87
- */
88
- loadApps?: boolean;
89
- /**
90
- * In case the component we are loading is env, whether to load it as env (in a scope aspects capsule)
91
- */
92
- loadEnvs?: boolean;
93
- };
94
-
95
- export type ScopeConfig = {
96
- httpTimeOut: number;
97
- description?: string;
98
- icon?: string;
99
- backgroundIconColor?: string;
100
- /**
101
- * Set a different package manager for the aspects capsules
102
- */
103
- aspectsPackageManager?: string;
104
- /**
105
- * Set a different node linker for the aspects capsules
106
- */
107
- aspectsNodeLinker?: NodeLinker;
108
- };
109
-
110
- export class ScopeMain implements ComponentFactory {
111
- componentLoader: ScopeComponentLoader;
112
- constructor(
113
- /**
114
- * private reference to the instance of Harmony.
115
- */
116
- private harmony: Harmony,
117
- /**
118
- * legacy scope
119
- */
120
- readonly legacyScope: LegacyScope,
121
-
122
- /**
123
- * component extension.
124
- */
125
- readonly componentExtension: ComponentMain,
126
-
127
- /**
128
- * slot registry for subscribing to build
129
- */
130
-
131
- readonly config: ScopeConfig,
132
-
133
- private postPutSlot: OnPostPutSlot,
134
-
135
- private postDeleteSlot: OnPostDeleteSlot,
136
-
137
- private postExportSlot: OnPostExportSlot,
138
-
139
- private postObjectsPersist: OnPostObjectsPersistSlot,
140
-
141
- public preFetchObjects: OnPreFetchObjectsSlot,
142
-
143
- private OnCompAspectReCalcSlot: OnCompAspectReCalcSlot,
144
-
145
- private isolator: IsolatorMain,
146
-
147
- private aspectLoader: AspectLoaderMain,
148
-
149
- private logger: Logger,
150
-
151
- private envs: EnvsMain,
152
-
153
- private dependencyResolver: DependencyResolverMain,
154
-
155
- private globalConfig: GlobalConfigMain
156
- ) {
157
- this.componentLoader = new ScopeComponentLoader(this, this.logger);
158
- }
159
-
160
- priority?: boolean | undefined;
161
-
162
- localAspects: string[] = [];
163
-
164
- /**
165
- * name of the scope
166
- */
167
- get name(): string {
168
- return this.legacyScope.name;
169
- }
170
-
171
- get icon(): string | undefined {
172
- return this.config.icon;
173
- }
174
-
175
- get backgroundIconColor(): string | undefined {
176
- return this.config.backgroundIconColor;
177
- }
178
-
179
- get description(): string | undefined {
180
- return this.config.description;
181
- }
182
-
183
- get path(): string {
184
- return this.legacyScope.path;
185
- }
186
-
187
- get isLegacy(): boolean {
188
- return this.legacyScope.isLegacy;
189
- }
190
-
191
- get isGlobalScope(): boolean {
192
- return this.path === GLOBAL_SCOPE;
193
- }
194
-
195
- get aspectsPackageManager(): string | undefined {
196
- return this.config.aspectsPackageManager;
197
- }
198
-
199
- get aspectsNodeLinker(): NodeLinker | undefined {
200
- return this.config.aspectsNodeLinker;
201
- }
202
-
203
- // We need to reload the aspects with their new version since:
204
- // during get many by legacy, we go load component which in turn go to getEnv
205
- // get env validates that the env written on the component is really exist by checking the envs slot registry
206
- // when we load here, it's env version in the aspect list already has the new version in case the env itself is being tagged
207
- // so we are search for the env in the registry with the new version number
208
- // but since the env only registered during the on load of the bit process (before the tag) it's version in the registry is only the old one
209
- // once we reload them we will have it registered with the new version as well
210
- async reloadAspectsWithNewVersion(components: ConsumerComponent[]): Promise<void> {
211
- const host = this.componentExtension.getHost();
212
-
213
- // Return only aspects that defined on components but not in the root config file (workspace.jsonc/scope.jsonc)
214
- const getUserAspectsIdsWithoutRootIds = (): string[] => {
215
- const allUserAspectIds = this.aspectLoader.getUserAspects();
216
- const rootIds = Object.keys(this.harmony.config.toObject());
217
- const diffIds = difference(allUserAspectIds, rootIds);
218
- return diffIds;
219
- };
220
-
221
- // Based on the list of components to be tagged return those who are loaded to harmony with their used version
222
- const getAspectsByPreviouslyUsedVersion = async (): Promise<string[]> => {
223
- const harmonyIds = getUserAspectsIdsWithoutRootIds();
224
- const aspectsIds: string[] = [];
225
- const aspectsP = components.map(async (component) => {
226
- const newId = await host.resolveComponentId(component.id);
227
- if (
228
- component.previouslyUsedVersion &&
229
- component.version &&
230
- component.previouslyUsedVersion !== component.version
231
- ) {
232
- const newIdWithPreviouslyUsedVersion = newId.changeVersion(component.previouslyUsedVersion);
233
- if (harmonyIds.includes(newIdWithPreviouslyUsedVersion.toString())) {
234
- aspectsIds.push(newId.toString());
235
- }
236
- }
237
- });
238
- await Promise.all(aspectsP);
239
- return aspectsIds;
240
- };
241
-
242
- const idsToLoad = await getAspectsByPreviouslyUsedVersion();
243
- await host.loadAspects(idsToLoad, false, 'scope.reloadAspectsWithNewVersion');
244
- }
245
-
246
- loadAspects(
247
- ids: string[],
248
- throwOnError?: boolean | undefined,
249
- neededFor?: string | undefined,
250
- lane?: Lane,
251
- opts?: ScopeLoadAspectsOptions
252
- ): Promise<string[]> {
253
- const scopeAspectsLoader = this.getScopeAspectsLoader();
254
- return scopeAspectsLoader.loadAspects(ids, throwOnError, neededFor, lane, opts);
255
- }
256
- resolveAspects(
257
- runtimeName?: string | undefined,
258
- componentIds?: ComponentID[] | undefined,
259
- opts?: ResolveAspectsOptions | undefined
260
- ): Promise<AspectDefinition[]> {
261
- const scopeAspectsLoader = this.getScopeAspectsLoader();
262
- return scopeAspectsLoader.resolveAspects(runtimeName, componentIds, opts);
263
- }
264
-
265
- async getResolvedAspects(
266
- components: Component[],
267
- opts?: { skipIfExists?: boolean; packageManagerConfigRootDir?: string; workspaceName?: string }
268
- ): Promise<RequireableComponent[]> {
269
- const scopeAspectsLoader = this.getScopeAspectsLoader();
270
- return scopeAspectsLoader.getResolvedAspects(components, opts);
271
- }
272
-
273
- async executeOnCompAspectReCalcSlot(component: Component) {
274
- const envsData = await this.envs.calcDescriptor(component, { skipWarnings: false });
275
- const policy = await this.dependencyResolver.mergeVariantPolicies(
276
- component.config.extensions,
277
- component.id,
278
- component.state._consumer.files
279
- );
280
- const dependenciesList = await this.dependencyResolver.extractDepsFromLegacy(component, policy);
281
-
282
- const depResolverData = {
283
- packageName: this.dependencyResolver.calcPackageName(component),
284
- dependencies: dependenciesList.serialize(),
285
- policy: policy.serialize(),
286
- };
287
- // Make sure we are adding the envs / deps data first because other on load events might depend on it
288
- await Promise.all([
289
- this.upsertExtensionData(component, EnvsAspect.id, envsData),
290
- this.upsertExtensionData(component, DependencyResolverAspect.id, depResolverData),
291
- ]);
292
-
293
- // We are updating the component state with the envs and deps data here, so in case we have other slots that depend on this data
294
- // they will be able to get it, as it's very common use case that during on load someone want to access to the component env for example
295
- const aspectListWithEnvsAndDeps = await this.createAspectListFromExtensionDataList(
296
- component.state._consumer.extensions
297
- );
298
- component.state.aspects = aspectListWithEnvsAndDeps;
299
-
300
- const entries = this.OnCompAspectReCalcSlot.toArray();
301
- await mapSeries(entries, async ([extension, onLoad]) => {
302
- const data = await onLoad(component);
303
- await this.upsertExtensionData(component, extension, data);
304
- // Update the aspect list to have changes happened during the on load slot (new data added above)
305
- component.state.aspects.upsertEntry(await this.resolveComponentId(extension), data);
306
- });
307
-
308
- return component;
309
- }
310
-
311
- private async upsertExtensionData(component: Component, extension: string, data: any) {
312
- if (!data) return;
313
- const existingExtension = component.state._consumer.extensions.findExtension(extension);
314
- if (existingExtension) {
315
- // Only merge top level of extension data
316
- Object.assign(existingExtension.data, data);
317
- return;
318
- }
319
- component.state._consumer.extensions.push(await this.getDataEntry(extension, data));
320
- }
321
-
322
- private async getDataEntry(extension: string, data: { [key: string]: any }): Promise<ExtensionDataEntry> {
323
- // TODO: @gilad we need to refactor the extension data entry api.
324
- return new ExtensionDataEntry(undefined, undefined, extension, undefined, data);
325
- }
326
-
327
- getIsolateAspectsOpts(opts?: {
328
- skipIfExists?: boolean;
329
- packageManagerConfigRootDir?: string;
330
- workspaceName?: string;
331
- }): IsolateComponentsOptions {
332
- const scopeAspectsLoader = this.getScopeAspectsLoader();
333
- return scopeAspectsLoader.getIsolateOpts(opts);
334
- }
335
-
336
- getAspectCapsulePath() {
337
- const scopeAspectsLoader = this.getScopeAspectsLoader();
338
- return scopeAspectsLoader.getAspectCapsulePath();
339
- }
340
-
341
- shouldUseHashForCapsules() {
342
- const scopeAspectsLoader = this.getScopeAspectsLoader();
343
- return scopeAspectsLoader.shouldUseHashForCapsules();
344
- }
345
-
346
- getManyByLegacy(components: ConsumerComponent[]): Promise<Component[]> {
347
- return mapSeries(components, async (component) => this.getFromConsumerComponent(component));
348
- }
349
-
350
- getScopeAspectsLoader(): ScopeAspectsLoader {
351
- const scopeAspectsLoader = new ScopeAspectsLoader(
352
- this,
353
- this.aspectLoader,
354
- this.envs,
355
- this.isolator,
356
- this.logger,
357
- this.globalConfig
358
- );
359
- return scopeAspectsLoader;
360
- }
361
-
362
- async clearCache() {
363
- this.logger.debug('clearing the components and the legacy cache');
364
- this.componentLoader.clearCache();
365
- await this.legacyScope.objects.clearCache();
366
- }
367
-
368
- builderDataMapToLegacyOnTagResults(builderDataComponentMap: ComponentMap<RawBuilderData>): LegacyOnTagResult[] {
369
- const builderDataToLegacyExtension = (component: Component, builderData: RawBuilderData) => {
370
- const existingBuilder = component.state.aspects.get(BuilderAspect.id)?.legacy;
371
- const builderExtension = existingBuilder || new ExtensionDataEntry(undefined, undefined, BuilderAspect.id);
372
- builderExtension.data = builderData;
373
- return builderExtension;
374
- };
375
- return builderDataComponentMap.toArray().map(([component, builderData]) => ({
376
- id: component.id,
377
- builderData: builderDataToLegacyExtension(component, builderData),
378
- }));
379
- }
380
-
381
- /**
382
- * register to the post-export slot.
383
- */
384
- onPostPut(postPutFn: OnPostPut) {
385
- this.postPutSlot.register(postPutFn);
386
- return this;
387
- }
388
-
389
- /**
390
- * register to the post-delete slot.
391
- */
392
- onPostDelete(postDeleteFn: OnPostDelete) {
393
- this.postDeleteSlot.register(postDeleteFn);
394
- return this;
395
- }
396
-
397
- /**
398
- * register to the post-export slot.
399
- */
400
- registerOnPostExport(postExportFn: OnPostExport) {
401
- this.postExportSlot.register(postExportFn);
402
- return this;
403
- }
404
-
405
- registerOnPreFetchObjects(preFetchObjectsFn: OnPreFetchObjects) {
406
- this.preFetchObjects.register(preFetchObjectsFn);
407
- return this;
408
- }
409
-
410
- registerOnCompAspectReCalc(compAspectReCalcSlotFn: OnCompAspectReCalc) {
411
- this.OnCompAspectReCalcSlot.register(compAspectReCalcSlotFn);
412
- }
413
-
414
- registerOnPostObjectsPersist(postObjectsPersistFn: OnPostObjectsPersist) {
415
- this.postObjectsPersist.register(postObjectsPersistFn);
416
- return this;
417
- }
418
-
419
- /**
420
- * Will fetch a list of components into the current scope.
421
- * This will only fetch the object and won't write the files to the actual FS
422
- */
423
- fetch(ids: ComponentIdList) {} // eslint-disable-line @typescript-eslint/no-unused-vars
424
-
425
- /**
426
- * This function will get a component and sealed it's current state into the scope
427
- *
428
- * @param {Component[]} components A list of components to seal with specific persist options (such as message and version number)
429
- * @param {PersistOptions} persistGeneralOptions General persistence options such as verbose
430
- */
431
- persist(components: Component[], options: PersistOptions) {} // eslint-disable-line @typescript-eslint/no-unused-vars
432
-
433
- async delete(
434
- { ids, force, lanes }: { ids: string[]; force: boolean; lanes: boolean },
435
- headers?: Record<string, any>
436
- ) {
437
- const authData = getAuthDataFromHeader(headers?.authorization);
438
- const result = await remove({
439
- path: this.path,
440
- ids,
441
- force,
442
- lanes,
443
- });
444
-
445
- const fns = this.postDeleteSlot.values();
446
- const metadata = { auth: authData, headers };
447
- const componentIds = lanes ? [] : ids.map((id) => ComponentID.fromString(id));
448
- await mapSeries(fns, async (fn) => {
449
- try {
450
- await fn({ ids: componentIds }, metadata);
451
- } catch (err: any) {
452
- this.logger.error('failed to run delete slot', err);
453
- }
454
- });
455
- return result;
456
- }
457
-
458
- isExported(id: ComponentID): boolean {
459
- return this.legacyScope.isExported(id);
460
- }
461
-
462
- /**
463
- * for long-running processes, such as `bit start` or `bit watch`, it's important to keep the following data up to date:
464
- * 1. scope-index (.bit/index.json file).
465
- * 2. remote-refs (.bit/refs/*).
466
- * it's possible that other commands (e.g. `bit import`) modified these files, while these processes are running.
467
- * Because these data are kept in memory, they're not up to date anymore.
468
- */
469
- async watchScopeInternalFiles() {
470
- const scopeIndexFile = this.legacyScope.objects.scopeIndex.getPath();
471
- const remoteLanesDir = this.legacyScope.objects.remoteLanes.basePath;
472
- const pathsToWatch = [scopeIndexFile, remoteLanesDir];
473
- const watcher = chokidar.watch(pathsToWatch);
474
- watcher.on('ready', () => {
475
- this.logger.debug(`watchSystemFiles has started, watching ${pathsToWatch.join(', ')}`);
476
- });
477
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
478
- watcher.on('change', async (filePath) => {
479
- if (filePath === scopeIndexFile) {
480
- this.logger.debug('scope index file has been changed, reloading it');
481
- await this.legacyScope.objects.reLoadScopeIndex();
482
- } else if (filePath.startsWith(remoteLanesDir)) {
483
- this.legacyScope.objects.remoteLanes.removeFromCacheByFilePath(filePath);
484
- } else {
485
- this.logger.error(
486
- 'unknown file has been changed, please check why it is watched by scope.watchSystemFiles',
487
- filePath
488
- );
489
- }
490
- });
491
- }
492
-
493
- async toObjectList(types: Types): Promise<ObjectList> {
494
- const objects = await this.legacyScope.objects.list(types);
495
- return ObjectList.fromBitObjects(objects);
496
- }
497
-
498
- // TODO: temporary compiler workaround - discuss this with david.
499
- private toJs(str: string) {
500
- if (str.endsWith('.ts')) return str.replace('.ts', '.js');
501
- return str;
502
- }
503
-
504
- async getGraph(ids?: ComponentID[]): Promise<Graph<Component, string>> {
505
- if (!ids || !ids.length) ids = (await this.list()).map((comp) => comp.id) || [];
506
- const components = await this.getMany(ids);
507
- const allFlattened = components.map((component) => component.state._consumer.getAllFlattenedDependencies()).flat();
508
- const allFlattenedUniq = ComponentIdList.uniqFromArray(allFlattened);
509
- await this.legacyScope.scopeImporter.importWithoutDeps(allFlattenedUniq, {
510
- cache: true,
511
- reason: `which are unique flattened dependencies to get the graph of ${ids.length} ids`,
512
- });
513
- const allFlattenedCompIds = await this.resolveMultipleComponentIds(allFlattenedUniq);
514
- const dependencies = await this.getMany(allFlattenedCompIds);
515
- const allComponents: Component[] = [...components, ...dependencies];
516
-
517
- // build the graph
518
- const graph = new Graph<Component, string>();
519
- allComponents.forEach((comp) => graph.setNode(new Node(comp.id.toString(), comp)));
520
- await Promise.all(
521
- allComponents.map(async (comp) => {
522
- const deps = await this.dependencyResolver.getComponentDependencies(comp);
523
- deps.forEach((dep) => {
524
- const depCompId = dep.componentId;
525
- if (!graph.hasNode(depCompId.toString())) {
526
- throw new Error(`scope.getGraph: missing node of ${depCompId.toString()}`);
527
- }
528
- graph.setEdge(new Edge(comp.id.toString(), depCompId.toString(), dep.lifecycle));
529
- });
530
- })
531
- );
532
- return graph;
533
- }
534
-
535
- async getGraphIds(ids?: ComponentID[]): Promise<CompIdGraph> {
536
- if (!ids || !ids.length) ids = (await this.list()).map((comp) => comp.id) || [];
537
- const components = await this.getMany(ids);
538
- const graph = new Graph<ComponentID, DepEdgeType>();
539
- const componentsWithoutSavedGraph: Component[] = [];
540
-
541
- // try to add from saved graph
542
- components.forEach((component) => {
543
- const compGraph = this.getSavedGraphOfComponentIfExist(component);
544
- if (!compGraph) {
545
- componentsWithoutSavedGraph.push(component);
546
- return;
547
- }
548
- graph.merge([graph]);
549
- });
550
- if (!componentsWithoutSavedGraph.length) {
551
- return graph;
552
- }
553
-
554
- // there are components that don't have the graph saved, create the graph by using Version objects of all flattened
555
- const lane = (await this.legacyScope.getCurrentLaneObject()) || undefined;
556
- await this.import(
557
- componentsWithoutSavedGraph.map((c) => c.id),
558
- { reFetchUnBuiltVersion: false, lane, reason: `to build graph-ids from the scope` }
559
- );
560
-
561
- const allFlattened = componentsWithoutSavedGraph
562
- .map((component) => component.state._consumer.getAllFlattenedDependencies())
563
- .flat();
564
- const allFlattenedUniq = ComponentIdList.uniqFromArray(allFlattened);
565
-
566
- const addEdges = (compId: ComponentID, dependencies: ConsumerComponent['dependencies'], label: DepEdgeType) => {
567
- dependencies.get().forEach((dep) => {
568
- const depId = dep.id;
569
- graph.setNode(new Node(depId.toString(), depId));
570
- graph.setEdge(new Edge(compId.toString(), depId.toString(), label));
571
- });
572
- };
573
- const componentsAndVersions = await this.legacyScope.getComponentsAndVersions(allFlattenedUniq);
574
- componentsAndVersions.forEach(({ component, version, versionStr }) => {
575
- const compId = component.toComponentId().changeVersion(versionStr);
576
- graph.setNode(new Node(compId.toString(), compId));
577
- addEdges(compId, version.dependencies, 'prod');
578
- addEdges(compId, version.devDependencies, 'dev');
579
- addEdges(compId, version.extensionDependencies, 'ext');
580
- });
581
-
582
- return graph;
583
- }
584
-
585
- private getSavedGraphOfComponentIfExist(component: Component): Graph<ComponentID, DepEdgeType> | null {
586
- const consumerComponent = component.state._consumer as ConsumerComponent;
587
- const flattenedEdges = consumerComponent.flattenedEdges;
588
- if (!flattenedEdges.length && consumerComponent.flattenedDependencies.length) {
589
- // there are flattenedDependencies, so must be edges, if they're empty, it's because the component was tagged
590
- // with a version < ~0.0.901, so this flattenedEdges wasn't exist.
591
- return null;
592
- }
593
- const edges = flattenedEdges.map((edge) => ({
594
- ...edge,
595
- source: edge.source,
596
- target: edge.target,
597
- }));
598
- const nodes = consumerComponent.flattenedDependencies;
599
-
600
- const graph = new Graph<ComponentID, DepEdgeType>();
601
- nodes.forEach((node) => graph.setNode(new Node(node.toString(), node)));
602
- edges.forEach((edge) => graph.setEdge(new Edge(edge.source.toString(), edge.target.toString(), edge.type)));
603
- return graph;
604
- }
605
-
606
- /**
607
- * import components into the scope.
608
- */
609
- async import(
610
- ids: ComponentID[],
611
- {
612
- useCache = true,
613
- reFetchUnBuiltVersion = true,
614
- preferDependencyGraph = true,
615
- lane,
616
- reason,
617
- }: {
618
- /**
619
- * if the component exists locally, don't go to the server to search for updates.
620
- */
621
- useCache?: boolean;
622
- /**
623
- * if the Version objects exists locally, but its `buildStatus` is Pending or Failed, reach the remote to find
624
- * whether the version was already built there.
625
- */
626
- reFetchUnBuiltVersion?: boolean;
627
- /**
628
- * if the component is on a lane, provide the lane object. the component will be fetched from the lane-scope and
629
- * not from the component-scope.
630
- */
631
- lane?: Lane;
632
- /**
633
- * if an external is missing and the remote has it with the dependency graph, don't fetch all its dependencies
634
- */
635
- preferDependencyGraph?: boolean;
636
- /**
637
- * reason why this import is needed (to show in the terminal)
638
- */
639
- reason?: string;
640
- } = {}
641
- ): Promise<void> {
642
- const withoutOwnScopeAndLocals = ids.filter((id) => {
643
- return id.scope !== this.name && id._legacy.hasScope();
644
- });
645
- await this.legacyScope.scopeImporter.importMany({
646
- ids: ComponentIdList.fromArray(withoutOwnScopeAndLocals),
647
- cache: useCache,
648
- throwForDependencyNotFound: false,
649
- reFetchUnBuiltVersion,
650
- lane,
651
- preferDependencyGraph,
652
- reason,
653
- });
654
- }
655
-
656
- async get(id: ComponentID, useCache = true, importIfMissing = true): Promise<Component | undefined> {
657
- return this.componentLoader.get(id, importIfMissing, useCache);
658
- }
659
-
660
- async getFromConsumerComponent(consumerComponent: ConsumerComponent): Promise<Component> {
661
- return this.componentLoader.getFromConsumerComponent(consumerComponent);
662
- }
663
-
664
- /**
665
- * get a component from a remote without importing it
666
- */
667
- async getRemoteComponent(id: ComponentID): Promise<Component> {
668
- return this.componentLoader.getRemoteComponent(id);
669
- }
670
-
671
- /**
672
- * get a component from a remote without importing it
673
- */
674
- async getManyRemoteComponents(ids: ComponentID[]): Promise<Component[]> {
675
- return this.componentLoader.getManyRemoteComponents(ids);
676
- }
677
-
678
- /**
679
- * list all components in the scope.
680
- */
681
- async list(
682
- filter?: { offset: number; limit: number; namespaces?: string[] },
683
- includeCache = false,
684
- includeFromLanes = false,
685
- includeDeleted = false
686
- ): Promise<Component[]> {
687
- const patternsWithScope =
688
- (filter?.namespaces && filter?.namespaces.map((pattern) => `**/${pattern || '**'}`)) || undefined;
689
- const componentsIds = await this.listIds(includeCache, includeFromLanes, patternsWithScope);
690
-
691
- const comps = await this.getMany(
692
- filter && filter.limit ? slice(componentsIds, filter.offset, filter.offset + filter.limit) : componentsIds
693
- );
694
-
695
- return includeDeleted ? comps : comps.filter((comp) => !comp.isDeleted());
696
- }
697
-
698
- /**
699
- * for now, list of invalid components are mostly useful for the workspace.
700
- * in the future, this can return components that failed to load in the scope due to objects file
701
- * corruption or similar issues.
702
- */
703
- async listInvalid() {
704
- return [];
705
- }
706
-
707
- /**
708
- * get ids of all scope components.
709
- * @param includeCache whether or not include components that their scope-name is different than the current scope-name
710
- */
711
- async listIds(includeCache = false, includeFromLanes = false, patterns?: string[]): Promise<ComponentID[]> {
712
- const allModelComponents = await this.legacyScope.list();
713
- const filterByCacheAndLanes = (modelComponent: ModelComponent) => {
714
- const cacheFilter = includeCache ? true : this.exists(modelComponent);
715
- const lanesFilter = includeFromLanes ? true : modelComponent.hasHead();
716
-
717
- return cacheFilter && lanesFilter;
718
- };
719
- const modelComponentsToList = allModelComponents.filter(filterByCacheAndLanes);
720
- let ids = modelComponentsToList.map((component) =>
721
- ComponentID.fromLegacy(component.toBitIdWithLatestVersion(), component.scope || this.name)
722
- );
723
- if (patterns && patterns.length > 0) {
724
- ids = ids.filter((id) =>
725
- patterns?.some((pattern) => isMatchNamespacePatternItem(id.toStringWithoutVersion(), pattern).match)
726
- );
727
- }
728
- this.logger.debug(`scope listIds: total ${ids.length} components after filter scope`);
729
- return ids;
730
- }
731
-
732
- /**
733
- * Check if a specific id exist in the scope
734
- * @param componentId
735
- */
736
- async hasId(componentId: ComponentID, includeCache = false): Promise<boolean> {
737
- if (!includeCache && componentId.scope !== this.name) return false;
738
- const opts = {
739
- includeVersion: true,
740
- };
741
-
742
- return this.legacyScope.hasId(componentId, opts);
743
- }
744
-
745
- async hasIdNested(componentId: ComponentID, includeCache = false): Promise<boolean> {
746
- return this.hasId(componentId, includeCache);
747
- }
748
-
749
- /**
750
- * determine whether a component exists in the scope.
751
- */
752
- exists(modelComponent: ModelComponent) {
753
- return modelComponent.scope === this.name;
754
- }
755
-
756
- async getMany(ids: ComponentID[], throwIfNotExist = false): Promise<Component[]> {
757
- const idsWithoutEmpty = compact(ids);
758
- const componentsP = mapSeries(idsWithoutEmpty, async (id: ComponentID) => {
759
- return throwIfNotExist ? this.getOrThrow(id) : this.get(id);
760
- });
761
- const components = await componentsP;
762
- return compact(components);
763
- }
764
-
765
- /**
766
- * important! you probably want to use `getMany`, which returns the components from the scope.
767
- * this method loads all aspects of the loaded components. (which hurts performance)
768
- */
769
- async loadMany(
770
- ids: ComponentID[],
771
- lane?: Lane,
772
- opts: LoadOptions = { loadApps: true, loadEnvs: true }
773
- ): Promise<Component[]> {
774
- const components = await mapSeries(ids, (id) => this.load(id, lane, opts));
775
- return compact(components);
776
- }
777
-
778
- async loadManyCompsAspects(components: Component[], lane?: Lane): Promise<Component[]> {
779
- const loadedComponents = await mapSeries(components, (id) => this.loadCompAspects(id, lane));
780
- return compact(loadedComponents);
781
- }
782
-
783
- /**
784
- * get a component and throw an exception if not found.
785
- * @param id component id
786
- */
787
- async getOrThrow(id: ComponentID): Promise<Component> {
788
- const component = await this.get(id);
789
- if (!component) throw new ComponentNotFound(id);
790
- return component;
791
- }
792
-
793
- /**
794
- * returns a specific state of a component.
795
- * @param id component ID.
796
- * @param hash state hash.
797
- */
798
- async getState(id: ComponentID, hash: string): Promise<State> {
799
- return this.componentLoader.getState(id, hash);
800
- }
801
-
802
- async getSnap(id: ComponentID, hash: string): Promise<Snap> {
803
- const modelComponent = await this.legacyScope.getModelComponent(id);
804
- const ref = modelComponent.getRef(hash);
805
- if (!ref) throw new Error(`ref was not found: ${id.toString()} with tag ${hash}`);
806
- return this.componentLoader.getSnap(id, ref.toString());
807
- }
808
-
809
- /**
810
- * get component log sorted by the timestamp in ascending order (from the earliest to the latest)
811
- */
812
- async getLogs(id: ComponentID, shortHash = false, startsFrom?: string): Promise<ComponentLog[]> {
813
- return this.legacyScope.loadComponentLogs(id, shortHash, startsFrom);
814
- }
815
-
816
- async getStagedConfig() {
817
- const currentLaneId = this.legacyScope.getCurrentLaneId();
818
- return StagedConfig.load(this.path, this.logger, currentLaneId);
819
- }
820
-
821
- /**
822
- * wether a component is soft-removed.
823
- * the version is required as it can be removed on a lane. in which case, the version is the head in the lane.
824
- */
825
- async isComponentRemoved(id: ComponentID): Promise<Boolean> {
826
- const version = id.version;
827
- if (!version) throw new Error(`isComponentRemoved expect to get version, got ${id.toString()}`);
828
- const modelComponent = await this.legacyScope.getModelComponent(id);
829
- const versionObj = await modelComponent.loadVersion(version, this.legacyScope.objects);
830
- return versionObj.isRemoved();
831
- }
832
-
833
- /**
834
- * resolve a component ID.
835
- * @param id component ID.
836
- */
837
- async resolveComponentId(id: string | BitId | ComponentID): Promise<ComponentID> {
838
- if (id instanceof BitId) return this.resolveComponentIdFromBitId(id);
839
- const idStr = id.toString();
840
- const component = await this.legacyScope.loadModelComponentByIdStr(idStr);
841
- const getIdToCheck = () => {
842
- if (component) return idStr; // component exists in the scope with the scope-name.
843
- if (idStr.startsWith(`${this.name}/`)) {
844
- // component with the full name doesn't exist in the scope, it might be locally tagged
845
- return idStr.replace(`${this.name}/`, '');
846
- }
847
- return idStr;
848
- };
849
- const IdToCheck = getIdToCheck();
850
- const legacyId = await this.legacyScope.getParsedId(IdToCheck);
851
- return legacyId;
852
- }
853
-
854
- private resolveComponentIdFromBitId(id: BitId) {
855
- return id.hasScope() ? ComponentID.fromLegacy(id) : ComponentID.fromLegacy(id, this.name);
856
- }
857
-
858
- async resolveMultipleComponentIds(ids: Array<string | ComponentID | ComponentID>) {
859
- return Promise.all(ids.map(async (id) => this.resolveComponentId(id)));
860
- }
861
-
862
- /**
863
- * @deprecated use `this.idsByPattern` instead for consistency, which supports also negation and list of patterns.
864
- */
865
- async byPattern(patterns: string[], scope = '**'): Promise<Component[]> {
866
- const patternsWithScope = patterns.map((pattern) => `${scope}/${pattern || '**'}`);
867
-
868
- const ids = await this.listIds(true, false, patternsWithScope);
869
-
870
- const components = await this.getMany(ids);
871
- return components;
872
- }
873
-
874
- /**
875
- * get component-ids matching the given pattern. a pattern can have multiple patterns separated by a comma.
876
- * it uses multimatch (https://www.npmjs.com/package/multimatch) package for the matching algorithm, which supports
877
- * (among others) negate character "!" to exclude ids. See the package page for more supported characters.
878
- */
879
- async idsByPattern(pattern: string, throwForNoMatch = true): Promise<ComponentID[]> {
880
- if (!pattern.includes('*') && !pattern.includes(',')) {
881
- // if it's not a pattern but just id, resolve it without multimatch to support specifying id without scope-name
882
- const id = await this.resolveComponentId(pattern);
883
- const exists = await this.hasId(id, true);
884
- if (exists) return [id];
885
- if (throwForNoMatch) throw new BitError(`unable to find "${pattern}" in the scope`);
886
- return [];
887
- }
888
- const ids = await this.listIds(true);
889
- return this.filterIdsFromPoolIdsByPattern(pattern, ids, throwForNoMatch);
890
- }
891
-
892
- // todo: move this to somewhere else (where?)
893
- async filterIdsFromPoolIdsByPattern(
894
- pattern: string,
895
- ids: ComponentID[],
896
- throwForNoMatch = true,
897
- filterByStateFunc?: (state: any, poolIds: ComponentID[]) => Promise<ComponentID[]>
898
- ) {
899
- const patterns = pattern
900
- .split(',')
901
- .map((p) => p.trim())
902
- .map((p) => p.split('@')[0]); // no need for the version
903
- if (patterns.every((p) => p.startsWith('!'))) {
904
- // otherwise it'll never match anything. don't use ".push()". it must be the first item in the array.
905
- patterns.unshift('**');
906
- }
907
- // check also as legacyId.toString, as it doesn't have the defaultScope
908
- const idsToCheck = (id: ComponentID) => [id._legacy.toStringWithoutVersion(), id.toStringWithoutVersion()];
909
- const [statePatterns, nonStatePatterns] = partition(patterns, (p) => p.startsWith('$'));
910
- const idsFiltered = nonStatePatterns.length
911
- ? ids.filter((id) => multimatch(idsToCheck(id), nonStatePatterns).length)
912
- : [];
913
-
914
- const idsStateFiltered = await mapSeries(statePatterns, async (statePattern) => {
915
- if (!filterByStateFunc) {
916
- throw new Error(`filter by a state (${statePattern}) is currently supported on the workspace only`);
917
- }
918
- statePattern = statePattern.replace('$', '');
919
- if (statePattern.includes(' AND ')) {
920
- const statePatternSplit = statePattern.split(' AND ').map((p) => p.trim());
921
- if (statePatternSplit.length !== 2) throw new Error('invalid state pattern, can have only one "AND"');
922
- const [stateF, nonStateF] = statePatternSplit;
923
- const filteredByNonState = ids.filter((id) => multimatch(idsToCheck(id), [nonStateF]).length);
924
- return filterByStateFunc(stateF, filteredByNonState);
925
- }
926
- return filterByStateFunc(statePattern, ids);
927
- });
928
- const idsStateFilteredFlat = idsStateFiltered.flat();
929
- const combineFilteredIds = () => {
930
- if (!idsStateFiltered) return idsFiltered;
931
- const allIds = [...idsFiltered, ...idsStateFilteredFlat];
932
- return ComponentIdList.uniqFromArray(allIds);
933
- };
934
- const allIdsFiltered = combineFilteredIds();
935
- if (throwForNoMatch && !allIdsFiltered.length) {
936
- throw new NoIdMatchPattern(pattern);
937
- }
938
- return allIdsFiltered;
939
- }
940
-
941
- async getSnapDistance(id: ComponentID, throws = true): Promise<SnapsDistance> {
942
- const modelComp = await this.legacyScope.getModelComponent(id);
943
- await modelComp.setDivergeData(this.legacyScope.objects, throws);
944
- return modelComp.getDivergeData();
945
- }
946
- /**
947
- * get the distance for a component between two lanes. for example, lane-b forked from lane-a and lane-b added some new snaps
948
- * @param componentId
949
- * @param sourceHead head on the source lane. leave empty if the source is main
950
- * @param targetHead head on the target lane. leave empty if the target is main
951
- * @returns
952
- */
953
- async getSnapsDistanceBetweenTwoSnaps(
954
- componentId: ComponentID,
955
- sourceHead?: string,
956
- targetHead?: string,
957
- throws?: boolean
958
- ): Promise<SnapsDistance> {
959
- if (!sourceHead && !targetHead) {
960
- throw new Error(`getDivergeData got sourceHead and targetHead empty. at least one of them should be populated`);
961
- }
962
- const modelComponent = await this.legacyScope.getModelComponent(componentId);
963
- return getDivergeData({
964
- modelComponent,
965
- repo: this.legacyScope.objects,
966
- sourceHead: sourceHead ? Ref.from(sourceHead) : modelComponent.head || null,
967
- targetHead: targetHead ? Ref.from(targetHead) : modelComponent.head || null,
968
- throws,
969
- });
970
- }
971
-
972
- async getExactVersionBySemverRange(id: ComponentID, range: string): Promise<string | undefined> {
973
- const modelComponent = await this.legacyScope.getModelComponent(id);
974
- const versions = modelComponent.listVersions();
975
- return semver.maxSatisfying<string>(versions, range, { includePrerelease: true })?.toString();
976
- }
977
-
978
- async resumeExport(exportId: string, remotes: string[]): Promise<string[]> {
979
- return resumeExport(this.legacyScope, exportId, remotes);
980
- }
981
-
982
- /**
983
- * @deprecated use `this.resolveComponentId` instead.
984
- */
985
- async resolveId(id: string): Promise<ComponentID> {
986
- return this.resolveComponentId(id);
987
- }
988
-
989
- // TODO: add new API for this
990
- async _legacyRemotes(): Promise<Remotes> {
991
- return getScopeRemotes(this.legacyScope);
992
- }
993
-
994
- /**
995
- * list all component ids from a remote-scope
996
- */
997
- async listRemoteScope(scopeName: string): Promise<ComponentID[]> {
998
- const remotes = await this._legacyRemotes();
999
- const remote = await remotes.resolve(scopeName, this.legacyScope);
1000
- const results = await remote.list();
1001
- return results.map(({ id }) => id);
1002
- }
1003
-
1004
- async getLegacyMinimal(id: ComponentID): Promise<ConsumerComponent | undefined> {
1005
- try {
1006
- return await this.legacyScope.getConsumerComponent(id);
1007
- } catch (err) {
1008
- // in case the component is missing locally, this.get imports it.
1009
- return (await this.get(id))?.state._consumer;
1010
- }
1011
- }
1012
-
1013
- /**
1014
- * ModelComponent is of type `BitObject` which gets saved into the local scope as an object file.
1015
- * It has data about the tags and the component head. It doesn't have any data about the source-files/artifacts etc.
1016
- */
1017
- async getBitObjectModelComponent(id: ComponentID, throwIfNotExist = false): Promise<ModelComponent | undefined> {
1018
- return throwIfNotExist ? this.legacyScope.getModelComponent(id) : this.legacyScope.getModelComponentIfExist(id);
1019
- }
1020
-
1021
- /**
1022
- * Version BitObject holds the data of the source files and build artifacts of a specific snap/tag.
1023
- */
1024
- async getBitObjectVersion(
1025
- modelComponent: ModelComponent,
1026
- version: string,
1027
- throwIfNotExist = false
1028
- ): Promise<Version | undefined> {
1029
- return modelComponent.loadVersion(version, this.legacyScope.objects, throwIfNotExist);
1030
- }
1031
-
1032
- /**
1033
- * get a component and load its aspect
1034
- */
1035
- async load(
1036
- id: ComponentID,
1037
- lane?: Lane,
1038
- opts: LoadOptions = { loadApps: true, loadEnvs: true }
1039
- ): Promise<Component | undefined> {
1040
- const component = await this.get(id);
1041
- if (!component) return undefined;
1042
- return this.loadCompAspects(component, lane, opts);
1043
- }
1044
-
1045
- async loadCompAspects(
1046
- component: Component,
1047
- lane?: Lane,
1048
- opts: LoadOptions = { loadApps: true, loadEnvs: true }
1049
- ): Promise<Component> {
1050
- const aspectIds = component.state.aspects.ids;
1051
- // load components from type aspects as aspects.
1052
- // important! previously, this was running for any aspect, not only apps. (the if statement was `this.aspectLoader.isAspectComponent(component)`)
1053
- // Ran suggests changing it and if it breaks something, we'll document is and fix it.
1054
- if (opts.loadApps) {
1055
- const appData = component.state.aspects.get('teambit.harmony/application');
1056
- if (appData?.data?.appName) {
1057
- aspectIds.push(component.id.toString());
1058
- }
1059
- }
1060
- if (opts.loadEnvs) {
1061
- const envsData = component.state.aspects.get(EnvsAspect.id);
1062
- if (envsData?.data?.services || envsData?.data?.self || envsData?.data?.type === 'env') {
1063
- aspectIds.push(component.id.toString());
1064
- }
1065
- }
1066
- if (aspectIds && aspectIds.length) {
1067
- await this.loadAspects(aspectIds, true, component.id.toString(), lane);
1068
- }
1069
-
1070
- return component;
1071
- }
1072
-
1073
- async loadComponentsAspect(component: Component) {
1074
- const aspectIds = component.state.aspects.ids;
1075
- await this.loadAspects(aspectIds, true, component.id.toString());
1076
- }
1077
-
1078
- async addAspectsFromConfigObject(component: Component, configObject: Record<string, any>) {
1079
- const extensionsFromConfigObject = ExtensionDataList.fromConfigObject(configObject);
1080
- const extensionDataList = ExtensionDataList.mergeConfigs([
1081
- extensionsFromConfigObject,
1082
- component.state._consumer.extensions,
1083
- ]).filterRemovedExtensions();
1084
- component.state._consumer.extensions = extensionDataList;
1085
- }
1086
-
1087
- public async createAspectListFromExtensionDataList(extensionDataList: ExtensionDataList) {
1088
- const entries = await Promise.all(extensionDataList.map((entry) => this.extensionDataEntryToAspectEntry(entry)));
1089
- return this.componentExtension.createAspectListFromEntries(entries);
1090
- }
1091
-
1092
- private async extensionDataEntryToAspectEntry(dataEntry: ExtensionDataEntry): Promise<AspectEntry> {
1093
- return new AspectEntry(await this.resolveComponentId(dataEntry.id), dataEntry);
1094
- }
1095
-
1096
- getLastMergedPath() {
1097
- return path.join(this.path, 'last-merged');
1098
- }
1099
-
1100
- async isModified(): Promise<boolean> {
1101
- return false;
1102
- }
1103
-
1104
- async write() {
1105
- // no-op (it's relevant for the workspace only)
1106
- }
1107
-
1108
- /**
1109
- * declare the slots of scope extension.
1110
- */
1111
- static slots = [
1112
- Slot.withType<OnPostPut>(),
1113
- Slot.withType<OnPostDelete>(),
1114
- Slot.withType<OnPostExport>(),
1115
- Slot.withType<OnPostObjectsPersist>(),
1116
- Slot.withType<OnPreFetchObjects>(),
1117
- Slot.withType<OnCompAspectReCalc>(),
1118
- ];
1119
- static runtime = MainRuntime;
1120
-
1121
- static dependencies = [
1122
- ComponentAspect,
1123
- UIAspect,
1124
- GraphqlAspect,
1125
- CLIAspect,
1126
- IsolatorAspect,
1127
- AspectLoaderAspect,
1128
- ExpressAspect,
1129
- LoggerAspect,
1130
- EnvsAspect,
1131
- DependencyResolverAspect,
1132
- GlobalConfigAspect,
1133
- ];
1134
-
1135
- static defaultConfig: ScopeConfig = {
1136
- httpTimeOut: 600000,
1137
- };
1138
-
1139
- static async provider(
1140
- [componentExt, ui, graphql, cli, isolator, aspectLoader, express, loggerMain, envs, depsResolver, globalConfig]: [
1141
- ComponentMain,
1142
- UiMain,
1143
- GraphqlMain,
1144
- CLIMain,
1145
- IsolatorMain,
1146
- AspectLoaderMain,
1147
- ExpressMain,
1148
- LoggerMain,
1149
- EnvsMain,
1150
- DependencyResolverMain,
1151
- GlobalConfigMain
1152
- ],
1153
- config: ScopeConfig,
1154
- [
1155
- postPutSlot,
1156
- postDeleteSlot,
1157
- postExportSlot,
1158
- postObjectsPersistSlot,
1159
- preFetchObjectsSlot,
1160
- OnCompAspectReCalcSlot,
1161
- ]: [
1162
- OnPostPutSlot,
1163
- OnPostDeleteSlot,
1164
- OnPostExportSlot,
1165
- OnPostObjectsPersistSlot,
1166
- OnPreFetchObjectsSlot,
1167
- OnCompAspectReCalcSlot
1168
- ],
1169
- harmony: Harmony
1170
- ) {
1171
- const bitConfig: any = harmony.config.get('teambit.harmony/bit');
1172
- const legacyScope = await loadScopeIfExist(bitConfig?.cwd);
1173
- if (!legacyScope) {
1174
- return undefined;
1175
- }
1176
-
1177
- const logger = loggerMain.createLogger(ScopeAspect.id);
1178
- const scope = new ScopeMain(
1179
- harmony,
1180
- legacyScope,
1181
- componentExt,
1182
- config,
1183
- postPutSlot,
1184
- postDeleteSlot,
1185
- postExportSlot,
1186
- postObjectsPersistSlot,
1187
- preFetchObjectsSlot,
1188
- OnCompAspectReCalcSlot,
1189
- isolator,
1190
- aspectLoader,
1191
- logger,
1192
- envs,
1193
- depsResolver,
1194
- globalConfig
1195
- );
1196
- cli.registerOnStart(async (hasWorkspace: boolean) => {
1197
- if (hasWorkspace) return;
1198
- await scope.loadAspects(aspectLoader.getNotLoadedConfiguredExtensions(), undefined, 'scope.cli.registerOnStart');
1199
- });
1200
- cli.register(new ScopeCmd());
1201
-
1202
- const onPutHook = async (ids: string[], lanes: Lane[], authData?: AuthData): Promise<void> => {
1203
- logger.debug(`onPutHook, started. (${ids.length} components)`);
1204
- scope.componentLoader.clearCache();
1205
- const componentIds = await scope.resolveMultipleComponentIds(ids);
1206
- const fns = postPutSlot.values();
1207
- const data = {
1208
- ids: componentIds,
1209
- lanes,
1210
- };
1211
- const metadata = { auth: authData };
1212
- await Promise.all(fns.map(async (fn) => fn(data, metadata)));
1213
- logger.debug(`onPutHook, completed. (${ids.length} components)`);
1214
- };
1215
-
1216
- const getAuthData = (): AuthData | undefined => {
1217
- const token = Http.getToken();
1218
- return token ? { type: DEFAULT_AUTH_TYPE, credentials: token } : undefined;
1219
- };
1220
-
1221
- const onPostExportHook = async (ids: ComponentID[], lanes: Lane[]): Promise<void> => {
1222
- logger.debug(`onPostExportHook, started. (${ids.length} components)`);
1223
- const componentIds = await scope.resolveMultipleComponentIds(ids);
1224
- const fns = postExportSlot.values();
1225
- const data = {
1226
- ids: componentIds,
1227
- lanes,
1228
- };
1229
- const metadata = { auth: getAuthData() };
1230
- await Promise.all(fns.map(async (fn) => fn(data, metadata)));
1231
- logger.debug(`onPostExportHook, completed. (${ids.length} components)`);
1232
- };
1233
-
1234
- const onPostObjectsPersistHook = async (): Promise<void> => {
1235
- logger.debug(`onPostObjectsPersistHook, started`);
1236
- const fns = postObjectsPersistSlot.values();
1237
- const metadata = { auth: getAuthData() };
1238
- await Promise.all(fns.map(async (fn) => fn(undefined, metadata)));
1239
- logger.debug(`onPostObjectsPersistHook, completed`);
1240
- };
1241
-
1242
- ExportPersist.onPutHook = onPutHook;
1243
- PostSign.onPutHook = onPutHook;
1244
- Scope.onPostExport = onPostExportHook;
1245
- Repository.onPostObjectsPersist = onPostObjectsPersistHook;
1246
- ExternalActions.externalActions.push(new ClearCacheAction(scope));
1247
-
1248
- express.register([
1249
- new PutRoute(scope, postPutSlot),
1250
- new FetchRoute(scope, logger),
1251
- new ActionRoute(scope),
1252
- new DeleteRoute(scope),
1253
- ]);
1254
- // @ts-ignore - @ran to implement the missing functions and remove it
1255
- ui.registerUiRoot(new ScopeUIRoot(scope));
1256
- graphql.register(scopeSchema(scope));
1257
- componentExt.registerHost(scope);
1258
-
1259
- return scope;
1260
- }
1261
- }
1262
-
1263
- ScopeAspect.addRuntime(ScopeMain);