@teambit/workspace 0.0.553 → 0.0.557

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.
Files changed (89) hide show
  1. package/dist/build-graph-from-fs.d.ts +30 -0
  2. package/dist/build-graph-from-fs.js +4 -1
  3. package/dist/build-graph-from-fs.js.map +1 -1
  4. package/dist/install.cmd.d.ts +17 -3
  5. package/dist/install.cmd.js.map +1 -1
  6. package/dist/link/link.cmd.d.ts +15 -1
  7. package/dist/types.d.ts +21 -0
  8. package/dist/ui/workspace/workspace-model.d.ts +32 -1
  9. package/dist/ui/workspace/workspace-provider.d.ts +9 -0
  10. package/dist/ui/workspace/workspace.d.ts +3 -0
  11. package/dist/watch/output-formatter.d.ts +6 -0
  12. package/dist/watch/watch.cmd.d.ts +22 -1
  13. package/dist/watch/watcher.d.ts +9 -0
  14. package/dist/workspace-component/component-status.d.ts +57 -1
  15. package/dist/workspace-component/workspace-component-loader.d.ts +7 -0
  16. package/dist/workspace-component/workspace-component.d.ts +36 -1
  17. package/dist/workspace.d.ts +236 -3
  18. package/dist/workspace.js +37 -5
  19. package/dist/workspace.js.map +1 -1
  20. package/dist/workspace.ui-root.d.ts +18 -1
  21. package/dist/workspace.ui.runtime.d.ts +32 -1
  22. package/package.json +63 -53
  23. package/tsconfig.json +1 -2
  24. package/build-graph-from-fs.ts +0 -160
  25. package/capsule.cmd.ts +0 -136
  26. package/component-config-file/component-config-file.ts +0 -98
  27. package/component-config-file/exceptions/already-exists.ts +0 -7
  28. package/component-config-file/exceptions/index.ts +0 -1
  29. package/component-config-file/index.ts +0 -2
  30. package/component-tree.widget.tsx +0 -16
  31. package/constants.ts +0 -1
  32. package/eject-conf.cmd.tsx +0 -62
  33. package/events/index.ts +0 -3
  34. package/events/on-component-add-event.ts +0 -14
  35. package/events/on-component-change-event.ts +0 -14
  36. package/events/on-component-removed-event.ts +0 -14
  37. package/exceptions/dependency-type-not-supported-in-policy.ts +0 -7
  38. package/exceptions/incorrect-env-aspect.ts +0 -8
  39. package/exceptions/index.ts +0 -1
  40. package/index.ts +0 -19
  41. package/install.cmd.tsx +0 -67
  42. package/link/component-list-links.ts +0 -50
  43. package/link/core-aspects-links.ts +0 -50
  44. package/link/get-package-name-from-target.ts +0 -5
  45. package/link/index.ts +0 -1
  46. package/link/link-row.ts +0 -20
  47. package/link/link-to-dir.ts +0 -13
  48. package/link/link.cmd.ts +0 -96
  49. package/link/nested-deps-in-nm-links.ts +0 -47
  50. package/link/rewire-row.ts +0 -17
  51. package/list.cmd.tsx +0 -30
  52. package/on-component-events.ts +0 -14
  53. package/package-tar/teambit-workspace-0.0.553.tgz +0 -0
  54. package/tag-cmd.ts +0 -247
  55. package/types/asset.d.ts +0 -29
  56. package/types/style.d.ts +0 -42
  57. package/types.ts +0 -40
  58. package/ui/index.ts +0 -1
  59. package/ui/workspace/default-component.ts +0 -7
  60. package/ui/workspace/index.ts +0 -1
  61. package/ui/workspace/use-workspace.ts +0 -193
  62. package/ui/workspace/workspace-context.tsx +0 -5
  63. package/ui/workspace/workspace-model.ts +0 -66
  64. package/ui/workspace/workspace-overview/index.ts +0 -1
  65. package/ui/workspace/workspace-overview/workspace-overview.module.scss +0 -6
  66. package/ui/workspace/workspace-overview/workspace-overview.tsx +0 -21
  67. package/ui/workspace/workspace-provider.tsx +0 -23
  68. package/ui/workspace/workspace.module.scss +0 -40
  69. package/ui/workspace/workspace.tsx +0 -99
  70. package/ui/workspace-components-drawer/index.ts +0 -1
  71. package/ui/workspace-components-drawer/workspace-components-drawer.module.scss +0 -4
  72. package/ui/workspace-components-drawer/workspace-components.drawer.tsx +0 -49
  73. package/watch/REFACTOR_GUIDE.md +0 -14
  74. package/watch/check-types.ts +0 -5
  75. package/watch/output-formatter.ts +0 -50
  76. package/watch/watch.cmd.tsx +0 -142
  77. package/watch/watcher.ts +0 -289
  78. package/workspace-component/component-status.ts +0 -67
  79. package/workspace-component/index.ts +0 -2
  80. package/workspace-component/workspace-component-loader.ts +0 -276
  81. package/workspace-component/workspace-component.ts +0 -51
  82. package/workspace-section.ts +0 -14
  83. package/workspace.aspect.ts +0 -9
  84. package/workspace.graphql.ts +0 -133
  85. package/workspace.main.runtime.ts +0 -50
  86. package/workspace.provider.ts +0 -218
  87. package/workspace.ts +0 -1517
  88. package/workspace.ui-root.ts +0 -64
  89. package/workspace.ui.runtime.tsx +0 -215
package/workspace.ts DELETED
@@ -1,1517 +0,0 @@
1
- import chalk from 'chalk';
2
- import mapSeries from 'p-map-series';
3
- import type { PubsubMain } from '@teambit/pubsub';
4
- import { IssuesList } from '@teambit/component-issues';
5
- import type { AspectLoaderMain, AspectDefinition } from '@teambit/aspect-loader';
6
- import { getAspectDef } from '@teambit/aspect-loader';
7
- import { MainRuntime } from '@teambit/cli';
8
- import {
9
- AspectEntry,
10
- ComponentMain,
11
- Component,
12
- ComponentFactory,
13
- ComponentID,
14
- ComponentMap,
15
- AspectList,
16
- AspectData,
17
- InvalidComponent,
18
- } from '@teambit/component';
19
- import { Importer } from '@teambit/importer';
20
- import { ComponentScopeDirMap, Config } from '@teambit/config';
21
- import {
22
- DependencyLifecycleType,
23
- DependencyResolverMain,
24
- PackageManagerInstallOptions,
25
- ComponentDependency,
26
- WorkspacePolicyEntry,
27
- LinkingOptions,
28
- LinkResults,
29
- DependencyList,
30
- } from '@teambit/dependency-resolver';
31
- import { EnvsMain, EnvsAspect, EnvServiceList, DEFAULT_ENV } from '@teambit/envs';
32
- import { GraphqlMain } from '@teambit/graphql';
33
- import { Harmony } from '@teambit/harmony';
34
- import { IsolatorMain } from '@teambit/isolator';
35
- import { Logger } from '@teambit/logger';
36
- import type { ScopeMain } from '@teambit/scope';
37
- import { isMatchNamespacePatternItem } from '@teambit/workspace.modules.match-pattern';
38
- import { RequireableComponent } from '@teambit/harmony.modules.requireable-component';
39
- import { ResolvedComponent } from '@teambit/harmony.modules.resolved-component';
40
- import type { VariantsMain } from '@teambit/variants';
41
- import { link } from '@teambit/legacy/dist/api/consumer';
42
- import LegacyGraph from '@teambit/legacy/dist/scope/graph/graph';
43
- import { ImportOptions } from '@teambit/legacy/dist/consumer/component-ops/import-components';
44
- import { NothingToImport } from '@teambit/legacy/dist/consumer/exceptions';
45
- import { BitIds } from '@teambit/legacy/dist/bit-id';
46
- import { BitId, InvalidScopeName, isValidScopeName } from '@teambit/legacy-bit-id';
47
- import { LocalLaneId, RemoteLaneId } from '@teambit/legacy/dist/lane-id/lane-id';
48
- import { Consumer, loadConsumer } from '@teambit/legacy/dist/consumer';
49
- import { GetBitMapComponentOptions } from '@teambit/legacy/dist/consumer/bit-map/bit-map';
50
- import AddComponents from '@teambit/legacy/dist/consumer/component-ops/add-components';
51
- import type {
52
- AddActionResults,
53
- Warnings,
54
- } from '@teambit/legacy/dist/consumer/component-ops/add-components/add-components';
55
- import { ComponentNotFound } from '@teambit/legacy/dist/scope/exceptions';
56
- import ComponentsList from '@teambit/legacy/dist/consumer/component/components-list';
57
- import { NoComponentDir } from '@teambit/legacy/dist/consumer/component/exceptions/no-component-dir';
58
- import { ExtensionDataList } from '@teambit/legacy/dist/consumer/config/extension-data';
59
- import { pathIsInside } from '@teambit/legacy/dist/utils';
60
- import componentIdToPackageName from '@teambit/legacy/dist/utils/bit/component-id-to-package-name';
61
- import { PathOsBased, PathOsBasedRelative, PathOsBasedAbsolute } from '@teambit/legacy/dist/utils/path';
62
- import { BitError } from '@teambit/bit-error';
63
- import fs from 'fs-extra';
64
- import { slice, uniqBy, difference, compact } from 'lodash';
65
- import path from 'path';
66
- import ConsumerComponent from '@teambit/legacy/dist/consumer/component';
67
- import type { ComponentLog } from '@teambit/legacy/dist/scope/models/model-component';
68
- import { CompilationInitiator } from '@teambit/compiler';
69
- import { ComponentConfigFile } from './component-config-file';
70
- import { DependencyTypeNotSupportedInPolicy } from './exceptions';
71
- import {
72
- OnComponentAdd,
73
- OnComponentChange,
74
- OnComponentEventResult,
75
- OnComponentLoad,
76
- OnComponentRemove,
77
- SerializableResults,
78
- } from './on-component-events';
79
- import { WorkspaceExtConfig } from './types';
80
- import { Watcher, WatchOptions } from './watch/watcher';
81
- import { ComponentStatus } from './workspace-component/component-status';
82
- import {
83
- OnComponentAddSlot,
84
- OnComponentChangeSlot,
85
- OnComponentLoadSlot,
86
- OnComponentRemoveSlot,
87
- OnPreWatch,
88
- OnPreWatchSlot,
89
- } from './workspace.provider';
90
- import { WorkspaceComponentLoader } from './workspace-component/workspace-component-loader';
91
- import { IncorrectEnvAspect } from './exceptions/incorrect-env-aspect';
92
- import { GraphFromFsBuilder, ShouldIgnoreFunc } from './build-graph-from-fs';
93
-
94
- export type EjectConfResult = {
95
- configPath: string;
96
- };
97
-
98
- export const ComponentAdded = 'componentAdded';
99
- export const ComponentChanged = 'componentChanged';
100
- export const ComponentRemoved = 'componentRemoved';
101
-
102
- export interface EjectConfOptions {
103
- propagate?: boolean;
104
- override?: boolean;
105
- }
106
-
107
- export type WorkspaceInstallOptions = {
108
- variants?: string;
109
- lifecycleType?: DependencyLifecycleType;
110
- dedupe: boolean;
111
- import: boolean;
112
- copyPeerToRuntimeOnRoot?: boolean;
113
- copyPeerToRuntimeOnComponents?: boolean;
114
- updateExisting: boolean;
115
- savePrefix?: string;
116
- };
117
-
118
- export type WorkspaceLinkOptions = LinkingOptions;
119
-
120
- export type TrackData = {
121
- rootDir: PathOsBasedRelative; // path relative to the workspace
122
- componentName?: string; // if empty, it'll be generated from the path
123
- mainFile?: string; // if empty, attempts will be made to guess the best candidate
124
- };
125
-
126
- export type TrackResult = { componentName: string; files: string[]; warnings: Warnings };
127
-
128
- const DEFAULT_VENDOR_DIR = 'vendor';
129
-
130
- /**
131
- * API of the Bit Workspace
132
- */
133
- export class Workspace implements ComponentFactory {
134
- priority = true;
135
- owner?: string;
136
- componentsScopeDirsMap: ComponentScopeDirMap;
137
- componentLoader: WorkspaceComponentLoader;
138
- constructor(
139
- /**
140
- * private pubsub.
141
- */
142
- private pubsub: PubsubMain,
143
-
144
- private config: WorkspaceExtConfig,
145
- /**
146
- * private access to the legacy consumer instance.
147
- */
148
- public consumer: Consumer,
149
-
150
- /**
151
- * access to the workspace `Scope` instance
152
- */
153
- readonly scope: ScopeMain,
154
-
155
- /**
156
- * access to the `ComponentProvider` instance
157
- */
158
- private componentAspect: ComponentMain,
159
-
160
- private isolator: IsolatorMain,
161
-
162
- private dependencyResolver: DependencyResolverMain,
163
-
164
- private variants: VariantsMain,
165
-
166
- private aspectLoader: AspectLoaderMain,
167
-
168
- private logger: Logger,
169
-
170
- private componentList: ComponentsList = new ComponentsList(consumer),
171
-
172
- /**
173
- * private reference to the instance of Harmony.
174
- */
175
- private harmony: Harmony,
176
-
177
- /**
178
- * on component load slot.
179
- */
180
- public onComponentLoadSlot: OnComponentLoadSlot,
181
-
182
- /**
183
- * on component change slot.
184
- */
185
- private onComponentChangeSlot: OnComponentChangeSlot,
186
-
187
- private envs: EnvsMain,
188
-
189
- /**
190
- * on component add slot.
191
- */
192
- private onComponentAddSlot: OnComponentAddSlot,
193
-
194
- private onComponentRemoveSlot: OnComponentRemoveSlot,
195
-
196
- private onPreWatchSlot: OnPreWatchSlot,
197
-
198
- private graphql: GraphqlMain
199
- ) {
200
- // TODO: refactor - prefer to avoid code inside the constructor.
201
- this.owner = this.config?.defaultOwner;
202
- this.componentLoader = new WorkspaceComponentLoader(this, logger, dependencyResolver, envs);
203
- this.validateConfig();
204
- }
205
-
206
- private validateConfig() {
207
- const defaultScope = this.config.defaultScope;
208
- if (this.consumer.isLegacy) return;
209
- if (!defaultScope) throw new Error('defaultScope is missing');
210
- if (!isValidScopeName(defaultScope)) throw new InvalidScopeName(defaultScope);
211
- }
212
-
213
- /**
214
- * watcher api.
215
- */
216
- readonly watcher = new Watcher(this, this.pubsub);
217
-
218
- /**
219
- * root path of the Workspace.
220
- */
221
- get path() {
222
- return this.consumer.getPath();
223
- }
224
-
225
- /** get the `node_modules` folder of this workspace */
226
- private get modulesPath() {
227
- return path.join(this.path, 'node_modules');
228
- }
229
-
230
- get isLegacy(): boolean {
231
- return this.consumer.isLegacy;
232
- }
233
-
234
- onComponentLoad(loadFn: OnComponentLoad) {
235
- this.onComponentLoadSlot.register(loadFn);
236
- return this;
237
- }
238
-
239
- registerOnComponentChange(onComponentChangeFunc: OnComponentChange) {
240
- this.onComponentChangeSlot.register(onComponentChangeFunc);
241
- return this;
242
- }
243
-
244
- registerOnComponentAdd(onComponentAddFunc: OnComponentAdd) {
245
- this.onComponentAddSlot.register(onComponentAddFunc);
246
- return this;
247
- }
248
-
249
- registerOnComponentRemove(onComponentRemoveFunc: OnComponentRemove) {
250
- this.onComponentRemoveSlot.register(onComponentRemoveFunc);
251
- return this;
252
- }
253
-
254
- registerOnPreWatch(onPreWatchFunc: OnPreWatch) {
255
- this.onPreWatchSlot.register(onPreWatchFunc);
256
- return this;
257
- }
258
-
259
- /**
260
- * name of the workspace as configured in either `workspace.json`.
261
- * defaults to workspace root directory name.
262
- */
263
- get name() {
264
- if (this.config.name) return this.config.name;
265
- const tokenizedPath = this.path.split('/');
266
- return tokenizedPath[tokenizedPath.length - 1];
267
- }
268
-
269
- get icon() {
270
- return this.config.icon;
271
- }
272
-
273
- async hasModifiedDependencies(component: Component) {
274
- const componentsList = new ComponentsList(this.consumer);
275
- const listAutoTagPendingComponents = await componentsList.listAutoTagPendingComponents();
276
- const isAutoTag = listAutoTagPendingComponents.find((consumerComponent) =>
277
- consumerComponent.id.isEqualWithoutVersion(component.id._legacy)
278
- );
279
- if (isAutoTag) return true;
280
- return false;
281
- }
282
-
283
- /**
284
- * get Component issues
285
- */
286
- getComponentIssues(component: Component): IssuesList | null {
287
- return component.state._consumer.issues || null;
288
- }
289
-
290
- /**
291
- * provides status of all components in the workspace.
292
- */
293
- async getComponentStatus(component: Component): Promise<ComponentStatus> {
294
- const status = await this.consumer.getComponentStatusById(component.id._legacy);
295
- const hasModifiedDependencies = await this.hasModifiedDependencies(component);
296
- return ComponentStatus.fromLegacy(status, hasModifiedDependencies, component.isOutdated());
297
- }
298
-
299
- /**
300
- * list all workspace components.
301
- */
302
- async list(filter?: { offset: number; limit: number }): Promise<Component[]> {
303
- const legacyIds = this.consumer.bitMap.getAllIdsAvailableOnLane();
304
- const ids = await this.resolveMultipleComponentIds(legacyIds);
305
- return this.getMany(filter && filter.limit ? slice(ids, filter.offset, filter.offset + filter.limit) : ids);
306
- }
307
-
308
- /**
309
- * list all invalid components.
310
- * (see the invalid criteria in ConsumerComponent.isComponentInvalidByErrorType())
311
- */
312
- async listInvalid(): Promise<InvalidComponent[]> {
313
- const legacyIds = this.consumer.bitMap.getAllIdsAvailableOnLane();
314
- const ids = await this.resolveMultipleComponentIds(legacyIds);
315
- return this.componentLoader.getInvalid(ids);
316
- }
317
-
318
- /**
319
- * get ids of all workspace components.
320
- */
321
- async listIds(): Promise<ComponentID[]> {
322
- return this.resolveMultipleComponentIds(this.consumer.bitmapIdsFromCurrentLane);
323
- }
324
-
325
- /**
326
- * Check if a specific id exist in the workspace
327
- * @param componentId
328
- */
329
- async hasId(componentId: ComponentID): Promise<boolean> {
330
- const ids = await this.listIds();
331
- const found = ids.find((id) => {
332
- return id.isEqual(componentId);
333
- });
334
- return !!found;
335
- }
336
-
337
- /**
338
- * whether or not a workspace has a component with the given name
339
- */
340
- async hasName(name: string): Promise<boolean> {
341
- const ids = await this.listIds();
342
- return Boolean(ids.find((id) => id.fullName === name));
343
- }
344
-
345
- /**
346
- * Check if a specific id exist in the workspace or in the scope
347
- * @param componentId
348
- */
349
- async hasIdNested(componentId: ComponentID, includeCache = true): Promise<boolean> {
350
- const found = await this.hasId(componentId);
351
- if (found) return found;
352
- return this.scope.hasIdNested(componentId, includeCache);
353
- }
354
-
355
- /**
356
- * list all modified components in the workspace.
357
- */
358
- async modified() {
359
- const ids: any = await this.componentList.listModifiedComponents(false);
360
- const componentIds = ids.map(ComponentID.fromLegacy);
361
- return this.getMany(componentIds);
362
- }
363
-
364
- /**
365
- * list all new components in the workspace.
366
- */
367
- async newComponents() {
368
- const ids: any = await this.componentList.listNewComponents(false);
369
- const componentIds = ids.map(ComponentID.fromLegacy);
370
- return this.getMany(componentIds);
371
- }
372
-
373
- /**
374
- * get all workspace component-ids, include vendor components.
375
- * (exclude nested dependencies in case dependencies are saved as components and not packages)
376
- */
377
- getAllComponentIds(): Promise<ComponentID[]> {
378
- const bitIds = this.consumer.bitMap.getAuthoredAndImportedBitIds();
379
- return this.resolveMultipleComponentIds(bitIds);
380
- }
381
-
382
- async getNewAndModifiedIds(): Promise<ComponentID[]> {
383
- const ids = await this.componentList.listTagPendingComponents();
384
- return this.resolveMultipleComponentIds(ids);
385
- }
386
-
387
- async getLogs(id: ComponentID): Promise<ComponentLog[]> {
388
- return this.scope.getLogs(id);
389
- }
390
-
391
- async getLegacyGraph(ids?: ComponentID[]): Promise<LegacyGraph> {
392
- if (!ids || ids.length < 1) ids = await this.listIds();
393
-
394
- const legacyIds = ids.map((id) => id._legacy);
395
-
396
- const legacyGraph = await this.buildOneGraphForComponents(legacyIds);
397
- return legacyGraph;
398
- }
399
-
400
- async loadCapsules(bitIds: string[]) {
401
- // throw new Error("Method not implemented.");
402
- const components = await this.load(bitIds);
403
- return components.map((comp) => comp.capsule);
404
- }
405
- /**
406
- * fully load components, including dependency resolution and prepare them for runtime.
407
- * @todo: remove the string option, use only BitId
408
- */
409
- async load(ids: Array<BitId | string>): Promise<ResolvedComponent[]> {
410
- const componentIds = await this.resolveMultipleComponentIds(ids);
411
- const components = await this.getMany(componentIds);
412
- const network = await this.isolator.isolateComponents(components.map((c) => c.id));
413
- const resolvedComponents = components.map((component) => {
414
- const capsule = network.graphCapsules.getCapsule(component.id);
415
- if (!capsule) throw new Error(`unable to find capsule for ${component.id.toString()}`);
416
- return new ResolvedComponent(component, capsule);
417
- });
418
- return resolvedComponents;
419
- }
420
-
421
- public async createAspectList(extensionDataList: ExtensionDataList) {
422
- const entiresP = extensionDataList.map(async (entry) => {
423
- return new AspectEntry(await this.resolveComponentId(entry.id), entry);
424
- });
425
-
426
- const entries: AspectEntry[] = await Promise.all(entiresP);
427
- return this.componentAspect.createAspectListFromEntries(entries);
428
- }
429
-
430
- /**
431
- * get a component from workspace
432
- * @param id component ID
433
- */
434
- async get(
435
- componentId: ComponentID,
436
- forCapsule = false,
437
- legacyComponent?: ConsumerComponent,
438
- useCache = true,
439
- storeInCache = true
440
- ): Promise<Component> {
441
- this.logger.debug(`get ${componentId.toString()}`);
442
- return this.componentLoader.get(componentId, forCapsule, legacyComponent, useCache, storeInCache);
443
- }
444
-
445
- // TODO: @gilad we should refactor this asap into to the envs aspect.
446
- async getEnvSystemDescriptor(component: Component): Promise<AspectData> {
447
- const env = this.envs.calculateEnv(component);
448
- if (env.env.__getDescriptor && typeof env.env.__getDescriptor === 'function') {
449
- const systemDescriptor = await env.env.__getDescriptor();
450
- // !important persist services only on the env itself.
451
- let services: undefined | EnvServiceList;
452
- if (this.envs.isEnvRegistered(component.id.toString())) services = this.envs.getServices(env);
453
- const icon = this.aspectLoader.getDescriptor(env.id).icon || env.env.icon;
454
-
455
- return {
456
- type: systemDescriptor.type,
457
- id: env.id,
458
- name: env.name,
459
- icon,
460
- description: env.description,
461
- services: services?.toObject(),
462
- };
463
- }
464
-
465
- return {};
466
- }
467
-
468
- clearCache() {
469
- this.logger.debug('clearing the workspace and scope caches');
470
- this.componentLoader.clearCache();
471
- this.scope.clearCache();
472
- this.componentList = new ComponentsList(this.consumer);
473
- }
474
-
475
- clearComponentCache(id: ComponentID) {
476
- this.componentLoader.clearComponentCache(id);
477
- this.consumer.componentLoader.clearOneComponentCache(id._legacy);
478
- this.componentList = new ComponentsList(this.consumer);
479
- }
480
-
481
- async triggerOnComponentChange(
482
- id: ComponentID,
483
- files: string[],
484
- initiator?: CompilationInitiator
485
- ): Promise<OnComponentEventResult[]> {
486
- const component = await this.get(id);
487
- // if a new file was added, upon component-load, its .bitmap entry is updated to include the
488
- // new file. write these changes to the .bitmap file so then other processes have access to
489
- // this new file. If the .bitmap wasn't change, it won't do anything.
490
- await this.writeBitMap();
491
- const onChangeEntries = this.onComponentChangeSlot.toArray(); // e.g. [ [ 'teambit.bit/compiler', [Function: bound onComponentChange] ] ]
492
- const results: Array<{ extensionId: string; results: SerializableResults }> = [];
493
- await mapSeries(onChangeEntries, async ([extension, onChangeFunc]) => {
494
- const onChangeResult = await onChangeFunc(component, files, initiator);
495
- results.push({ extensionId: extension, results: onChangeResult });
496
- });
497
-
498
- // TODO: find way to standardize event names.
499
- await this.graphql.pubsub.publish(ComponentChanged, { componentChanged: { component } });
500
- return results;
501
- }
502
-
503
- async triggerOnComponentAdd(id: ComponentID): Promise<OnComponentEventResult[]> {
504
- const component = await this.get(id);
505
- const onAddEntries = this.onComponentAddSlot.toArray(); // e.g. [ [ 'teambit.bit/compiler', [Function: bound onComponentChange] ] ]
506
- const results: Array<{ extensionId: string; results: SerializableResults }> = [];
507
- const files = component.state.filesystem.files.map((file) => file.path);
508
- await mapSeries(onAddEntries, async ([extension, onAddFunc]) => {
509
- const onAddResult = await onAddFunc(component, files);
510
- results.push({ extensionId: extension, results: onAddResult });
511
- });
512
-
513
- await this.graphql.pubsub.publish(ComponentAdded, { componentAdded: { component } });
514
- return results;
515
- }
516
-
517
- async triggerOnComponentRemove(id: ComponentID): Promise<OnComponentEventResult[]> {
518
- const onRemoveEntries = this.onComponentRemoveSlot.toArray(); // e.g. [ [ 'teambit.bit/compiler', [Function: bound onComponentChange] ] ]
519
- const results: Array<{ extensionId: string; results: SerializableResults }> = [];
520
- await mapSeries(onRemoveEntries, async ([extension, onRemoveFunc]) => {
521
- const onRemoveResult = await onRemoveFunc(id);
522
- results.push({ extensionId: extension, results: onRemoveResult });
523
- });
524
-
525
- await this.graphql.pubsub.publish(ComponentRemoved, { componentRemoved: { componentIds: [id.toObject()] } });
526
- return results;
527
- }
528
-
529
- getState(id: ComponentID, hash: string) {
530
- return this.scope.getState(id, hash);
531
- }
532
-
533
- getSnap(id: ComponentID, hash: string) {
534
- return this.scope.getSnap(id, hash);
535
- }
536
-
537
- getCurrentLaneId(): LocalLaneId {
538
- return this.consumer.getCurrentLaneId();
539
- }
540
-
541
- /**
542
- * if checked out to a lane, return the remote lane id (name+scope). otherwise, return null.
543
- */
544
- getCurrentRemoteLaneId(): RemoteLaneId | null {
545
- const currentLane = this.getCurrentLaneId();
546
- if (currentLane.isDefault()) {
547
- return null;
548
- }
549
- const trackData = this.scope.legacyScope.lanes.getRemoteTrackedDataByLocalLane(currentLane.name);
550
- const remoteLaneName = trackData?.remoteLane;
551
- if (!remoteLaneName) {
552
- return null;
553
- }
554
- return RemoteLaneId.from(remoteLaneName, trackData.remoteScope);
555
- }
556
-
557
- getDefaultExtensions(): ExtensionDataList {
558
- if (!this.config.extensions) {
559
- return new ExtensionDataList();
560
- }
561
- return ExtensionDataList.fromConfigObject(this.config.extensions);
562
- }
563
-
564
- async ejectConfig(id: ComponentID, options: EjectConfOptions): Promise<EjectConfResult> {
565
- const componentId = await this.resolveComponentId(id);
566
- const component = await this.scope.get(componentId);
567
- const aspects = component?.state.aspects
568
- ? await this.resolveScopeAspectListIds(component?.state.aspects)
569
- : await this.createAspectList(new ExtensionDataList());
570
-
571
- const componentDir = this.componentDir(id, { ignoreVersion: true });
572
- const componentConfigFile = new ComponentConfigFile(componentId, aspects, options.propagate);
573
- await componentConfigFile.write(componentDir, { override: options.override });
574
- return {
575
- configPath: ComponentConfigFile.composePath(componentDir),
576
- };
577
- }
578
-
579
- private async resolveScopeAspectListIds(aspectListFromScope: AspectList): Promise<AspectList> {
580
- const resolvedList = await aspectListFromScope.pmap(async (entry) => {
581
- if (entry.id.scope !== this.scope.name) {
582
- return entry;
583
- }
584
- const newId = await this.resolveComponentId(entry.id.fullName);
585
- const newEntry = new AspectEntry(newId, entry.legacy);
586
- return newEntry;
587
- });
588
- return resolvedList;
589
- }
590
-
591
- /**
592
- * load components into the workspace through a variants pattern.
593
- * @param pattern variants.
594
- * @param scope scope name.
595
- */
596
- async byPattern(pattern: string, scope = '**'): Promise<Component[]> {
597
- const ids = await this.listIds();
598
- const finalPattern = `${scope}/${pattern || '**'}`;
599
- const targetIds = ids.filter((id) => {
600
- const spec = isMatchNamespacePatternItem(id.toStringWithoutVersion(), finalPattern);
601
- return spec.match;
602
- });
603
-
604
- const components = await this.getMany(targetIds);
605
- return components;
606
- }
607
-
608
- async getMany(ids: Array<ComponentID>, forCapsule = false): Promise<Component[]> {
609
- return this.componentLoader.getMany(ids, forCapsule);
610
- }
611
-
612
- getManyByLegacy(components: ConsumerComponent[]): Promise<Component[]> {
613
- return mapSeries(components, async (component) => {
614
- const id = await this.resolveComponentId(component.id);
615
- return this.get(id, undefined, component);
616
- });
617
- }
618
-
619
- /**
620
- * This will make sure to fetch the objects prior to load them
621
- * do not use it if you are not sure you need it.
622
- * It will influence the performance
623
- * currently it used only for get many of aspects
624
- * @param ids
625
- * @param forCapsule
626
- */
627
- async importAndGetMany(ids: Array<ComponentID>, forCapsule = false): Promise<Component[]> {
628
- await this.scope.import(ids, { reFetchUnBuiltVersion: shouldReFetchUnBuiltVersion() });
629
- return this.componentLoader.getMany(ids, forCapsule);
630
- }
631
-
632
- /**
633
- * @deprecated use this.track() instead
634
- * track a new component. (practically, add it to .bitmap).
635
- *
636
- * @param componentPaths component paths relative to the workspace dir
637
- * @param id if not set, will be concluded from the filenames
638
- * @param main if not set, will try to guess according to some strategies and throws if failed
639
- * @param override whether add details to an existing component or re-define it
640
- */
641
- async add(
642
- componentPaths: PathOsBasedRelative[],
643
- id?: string,
644
- main?: string,
645
- override = false
646
- ): Promise<AddActionResults> {
647
- const addComponent = new AddComponents({ consumer: this.consumer }, { componentPaths, id, main, override });
648
- const addResults = await addComponent.add();
649
- // @todo: the legacy commands have `consumer.onDestroy()` on command completion, it writes the
650
- // .bitmap file. workspace needs a similar mechanism. once done, remove the next line.
651
- await this.writeBitMap();
652
- return addResults;
653
- }
654
-
655
- /**
656
- * add a new component to the .bitmap file.
657
- * this method only adds the records in memory but doesn't persist to the filesystem.
658
- * to write the .bitmap file once completed, run "await this.writeBitMap();"
659
- */
660
- async track(trackData: TrackData): Promise<TrackResult> {
661
- const addComponent = new AddComponents(
662
- { consumer: this.consumer },
663
- { componentPaths: [trackData.rootDir], id: trackData.componentName, main: trackData.mainFile, override: false }
664
- );
665
- const result = await addComponent.add();
666
- const addedComponent = result.addedComponents[0];
667
- const componentName = addedComponent?.id.name || (trackData.componentName as string);
668
- const files = addedComponent?.files.map((f) => f.relativePath) || [];
669
- return { componentName, files, warnings: result.warnings };
670
- }
671
-
672
- async write(rootPath: string, component: Component) {
673
- await Promise.all(
674
- component.filesystem.files.map(async (file) => {
675
- const pathToWrite = path.join(this.path, rootPath, file.path);
676
- await fs.outputFile(pathToWrite, file.contents);
677
- })
678
- );
679
- }
680
-
681
- async writeBitMap() {
682
- await this.consumer.writeBitMap();
683
- }
684
-
685
- /**
686
- * Get the component root dir in the file system (relative to workspace or full)
687
- * @param componentId
688
- * @param relative return the path relative to the workspace or full path
689
- */
690
- componentDir(
691
- componentId: ComponentID,
692
- bitMapOptions?: GetBitMapComponentOptions,
693
- options = { relative: false }
694
- ): PathOsBased {
695
- return this.componentDirFromLegacyId(componentId._legacy, bitMapOptions, options);
696
- }
697
-
698
- /**
699
- * component's files in the workspace are symlinked to the node_modules, and a package.json file is generated on that
700
- * package directory to simulate a valid node package.
701
- * @returns the package directory inside the node_module.
702
- * by default the absolute path, unless `options.relative` was set
703
- */
704
- componentPackageDir(component: Component, options = { relative: false }): string {
705
- const packageName = componentIdToPackageName(component.state._consumer);
706
- const packageDir = path.join('node_modules', packageName);
707
- return options.relative ? packageDir : this.consumer.toAbsolutePath(packageDir);
708
- }
709
-
710
- private componentDirFromLegacyId(
711
- bitId: BitId,
712
- bitMapOptions?: GetBitMapComponentOptions,
713
- options = { relative: false }
714
- ): PathOsBased {
715
- const componentMap = this.consumer.bitMap.getComponent(bitId, bitMapOptions);
716
- const relativeComponentDir = componentMap.getComponentDir();
717
- if (!relativeComponentDir) {
718
- throw new NoComponentDir(bitId.toString());
719
- }
720
- if (options.relative) {
721
- return relativeComponentDir;
722
- }
723
-
724
- return path.join(this.path, relativeComponentDir);
725
- }
726
-
727
- componentDirToAbsolute(relativeComponentDir: PathOsBasedRelative): PathOsBasedAbsolute {
728
- return path.join(this.path, relativeComponentDir);
729
- }
730
-
731
- async componentDefaultScope(componentId: ComponentID): Promise<string | undefined> {
732
- const relativeComponentDir = this.componentDir(componentId, { ignoreVersion: true }, { relative: true });
733
- return this.componentDefaultScopeFromComponentDirAndName(relativeComponentDir, componentId.fullName);
734
- }
735
-
736
- async componentDefaultScopeFromComponentDirAndName(
737
- relativeComponentDir: PathOsBasedRelative,
738
- name: string
739
- ): Promise<string | undefined> {
740
- const componentConfigFile = await this.componentConfigFileFromComponentDirAndName(relativeComponentDir, name);
741
- if (componentConfigFile && componentConfigFile.defaultScope) {
742
- return componentConfigFile.defaultScope;
743
- }
744
- return this.componentDefaultScopeFromComponentDirAndNameWithoutConfigFile(relativeComponentDir, name);
745
- }
746
-
747
- get defaultScope() {
748
- return this.config.defaultScope;
749
- }
750
-
751
- private async componentDefaultScopeFromComponentDirAndNameWithoutConfigFile(
752
- relativeComponentDir: PathOsBasedRelative,
753
- name: string
754
- ): Promise<string | undefined> {
755
- const variantConfig = this.variants.byRootDirAndName(relativeComponentDir, name);
756
- if (variantConfig && variantConfig.defaultScope) {
757
- return variantConfig.defaultScope;
758
- }
759
- const isVendor = this.isVendorComponentByComponentDir(relativeComponentDir);
760
- if (!isVendor) {
761
- return this.config.defaultScope;
762
- }
763
- return undefined;
764
- }
765
-
766
- /**
767
- * Calculate the component config based on:
768
- * the component.json file in the component folder
769
- * matching pattern in the variants config
770
- * defaults extensions from workspace config
771
- *
772
- * @param {ComponentID} componentId
773
- * @param {Component} [componentFromScope]
774
- * @returns {Promise<ExtensionDataList>}
775
- * @memberof Workspace
776
- */
777
- async componentExtensions(componentId: ComponentID, componentFromScope?: Component): Promise<ExtensionDataList> {
778
- // TODO: consider caching this result
779
- let configFileExtensions;
780
- let variantsExtensions;
781
- let wsDefaultExtensions;
782
- const mergeFromScope = true;
783
- const scopeExtensions = componentFromScope?.config?.extensions || new ExtensionDataList();
784
-
785
- const componentConfigFile = await this.componentConfigFile(componentId);
786
- if (componentConfigFile) {
787
- configFileExtensions = componentConfigFile.aspects.toLegacy();
788
- // do not merge from scope data when there is component config file
789
- // mergeFromScope = false;
790
- }
791
- const relativeComponentDir = this.componentDir(componentId, { ignoreVersion: true }, { relative: true });
792
- const variantConfig = this.variants.byRootDirAndName(relativeComponentDir, componentId.fullName);
793
- if (variantConfig) {
794
- variantsExtensions = variantConfig.extensions;
795
- // Do not merge from scope when there is specific variant (which is not *) that match the component
796
- // if (variantConfig.maxSpecificity > 0) {
797
- // mergeFromScope = false;
798
- // }
799
- }
800
- const isVendor = this.isVendorComponentByComponentDir(relativeComponentDir);
801
- if (!isVendor) {
802
- wsDefaultExtensions = this.getDefaultExtensions();
803
- }
804
- // We don't stop on each step because we want to merge the default scope even if propagate=false but the default scope is not defined
805
- const extensionsToMerge: ExtensionDataList[] = [];
806
- if (configFileExtensions) {
807
- extensionsToMerge.push(configFileExtensions);
808
- }
809
- let continuePropagating = componentConfigFile?.propagate ?? true;
810
- if (variantsExtensions && continuePropagating) {
811
- // Put it in the start to make sure the config file is stronger
812
- extensionsToMerge.push(variantsExtensions);
813
- }
814
- continuePropagating = continuePropagating && (variantConfig?.propagate ?? true);
815
- // Do not apply default extensions on the default extensions (it will create infinite loop when loading them)
816
- const isDefaultExtension = wsDefaultExtensions.findExtension(componentId.toString(), true, true);
817
- if (wsDefaultExtensions && continuePropagating && !isDefaultExtension) {
818
- // Put it in the start to make sure the config file is stronger
819
- extensionsToMerge.push(wsDefaultExtensions);
820
- }
821
-
822
- // It's before the scope extensions, since there is no need to resolve extensions from scope they are already resolved
823
- // await Promise.all(extensionsToMerge.map((extensions) => this.resolveExtensionsList(extensions)));
824
-
825
- if (mergeFromScope && continuePropagating) {
826
- extensionsToMerge.push(scopeExtensions);
827
- }
828
-
829
- // It's important to do this resolution before the merge, otherwise we have issues with extensions
830
- // coming from scope with local scope name, as opposed to the same extension comes from the workspace with default scope name
831
- const promises = extensionsToMerge.map((list) => this.resolveExtensionListIds(list));
832
- await Promise.all(promises);
833
-
834
- let mergedExtensions = ExtensionDataList.mergeConfigs(extensionsToMerge).filterRemovedExtensions();
835
-
836
- // remove self from merged extensions
837
- const selfInMergedExtensions = mergedExtensions.findExtension(
838
- componentId._legacy.toStringWithoutScopeAndVersion(),
839
- true,
840
- true
841
- );
842
- if (selfInMergedExtensions && selfInMergedExtensions.extensionId) {
843
- mergedExtensions = mergedExtensions.remove(selfInMergedExtensions.extensionId);
844
- }
845
-
846
- return mergedExtensions;
847
- }
848
-
849
- async triggerOnPreWatch(componentIds: ComponentID[], watchOpts: WatchOptions) {
850
- const components = await this.getMany(componentIds);
851
- const preWatchFunctions = this.onPreWatchSlot.values();
852
- await mapSeries(preWatchFunctions, async (func) => {
853
- await func(components, watchOpts);
854
- });
855
- }
856
-
857
- /**
858
- * This will mutate the entries with extensionId prop to have resolved legacy id
859
- * This should be worked on the extension data list not the new aspect list
860
- * @param extensionList
861
- */
862
- private async resolveExtensionListIds(extensionList: ExtensionDataList): Promise<ExtensionDataList> {
863
- const promises = extensionList.map(async (entry) => {
864
- if (entry.extensionId) {
865
- const id = await this.resolveComponentId(entry.extensionId);
866
- entry.extensionId = id._legacy;
867
- }
868
-
869
- return entry;
870
- });
871
- await Promise.all(promises);
872
- return extensionList;
873
- }
874
-
875
- private isVendorComponentByComponentDir(relativeComponentDir: PathOsBasedRelative): boolean {
876
- const vendorDir = this.config.vendor?.directory || DEFAULT_VENDOR_DIR;
877
- if (pathIsInside(relativeComponentDir, vendorDir)) {
878
- return true;
879
- }
880
- // TODO: implement
881
- return false;
882
- }
883
-
884
- /**
885
- * return the component config from its folder (component.json)
886
- * @param componentId
887
- */
888
- private async componentConfigFile(id: ComponentID): Promise<ComponentConfigFile | undefined> {
889
- const relativeComponentDir = this.componentDir(id, { ignoreVersion: true }, { relative: true });
890
- return this.componentConfigFileFromComponentDirAndName(relativeComponentDir, id.fullName);
891
- }
892
-
893
- private async componentConfigFileFromComponentDirAndName(
894
- relativeComponentDir: PathOsBasedRelative,
895
- name: string
896
- ): Promise<ComponentConfigFile | undefined> {
897
- let componentConfigFile;
898
- if (relativeComponentDir) {
899
- const absComponentDir = this.componentDirToAbsolute(relativeComponentDir);
900
- const defaultScopeFromVariantsOrWs = await this.componentDefaultScopeFromComponentDirAndNameWithoutConfigFile(
901
- relativeComponentDir,
902
- name
903
- );
904
- componentConfigFile = await ComponentConfigFile.load(
905
- absComponentDir,
906
- this.createAspectList.bind(this),
907
- defaultScopeFromVariantsOrWs
908
- );
909
- }
910
-
911
- return componentConfigFile;
912
- }
913
-
914
- async getAspectsGraphWithoutCore(components: Component[], isAspect?: ShouldIgnoreFunc) {
915
- const ids = components.map((component) => component.id._legacy);
916
- const coreAspectsStringIds = this.aspectLoader.getCoreAspectIds();
917
- const coreAspectsComponentIds = coreAspectsStringIds.map((id) => BitId.parse(id, true));
918
- const coreAspectsBitIds = BitIds.fromArray(coreAspectsComponentIds.map((id) => id.changeScope(null)));
919
- // const aspectsIds = components.reduce((acc, curr) => {
920
- // const currIds = curr.state.aspects.ids;
921
- // acc = acc.concat(currIds);
922
- // return acc;
923
- // }, [] as any);
924
- // const otherDependenciesMap = components.reduce((acc, curr) => {
925
- // // const currIds = curr.state.dependencies.dependencies.map(dep => dep.id.toString());
926
- // const currMap = curr.state.dependencies.getIdsMap();
927
- // Object.assign(acc, currMap);
928
- // return acc;
929
- // }, {});
930
-
931
- // const depsWhichAreNotAspects = difference(Object.keys(otherDependenciesMap), aspectsIds);
932
- // const depsWhichAreNotAspectsBitIds = depsWhichAreNotAspects.map((strId) => otherDependenciesMap[strId]);
933
- // We only want to load into the graph components which are aspects and not regular dependencies
934
- // This come to solve a circular loop when an env aspect use an aspect (as regular dep) and the aspect use the env aspect as its env
935
- // TODO: @gilad it causes many issues we need to find a better solution. removed for now.
936
- const ignoredIds = coreAspectsBitIds.concat([]);
937
- return this.buildOneGraphForComponents(ids, BitIds.fromArray(ignoredIds), isAspect);
938
- }
939
-
940
- /**
941
- * load aspects from the workspace and if not exists in the workspace, load from the scope.
942
- * keep in mind that the graph may have circles.
943
- */
944
- async loadAspects(ids: string[] = [], throwOnError = false): Promise<void> {
945
- this.logger.debug(`loading ${ids.length} aspects`);
946
- const notLoadedIds = ids.filter((id) => !this.aspectLoader.isAspectLoaded(id));
947
- if (!notLoadedIds.length) return;
948
- const coreAspectsStringIds = this.aspectLoader.getCoreAspectIds();
949
- const idsWithoutCore: string[] = difference(notLoadedIds, coreAspectsStringIds);
950
- const componentIds = await this.resolveMultipleComponentIds(idsWithoutCore);
951
- const components = await this.importAndGetAspects(componentIds);
952
-
953
- const isAspect = async (bitId: BitId) => {
954
- const id = await this.resolveComponentId(bitId);
955
- const component = await this.get(id);
956
- let data = component.config.extensions.findExtension(EnvsAspect.id)?.data;
957
- if (!data) {
958
- // TODO: remove this once we re-export old components used to store the data here
959
- data = component.state.aspects.get('teambit.workspace/workspace');
960
- }
961
-
962
- if (!data) return false;
963
- if (data.type !== 'aspect' && idsWithoutCore.includes(component.id.toString())) {
964
- const err = new IncorrectEnvAspect(component.id.toString(), data.type, data.id);
965
- if (data.id === DEFAULT_ENV) {
966
- // when cloning a project, or when the node-modules dir is deleted, nothing works and all
967
- // components are default to the DEFAULT_ENV, which is node-env. we must allow "bit
968
- // install" to prepare the workspace and let the proper the envs to be loaded
969
- this.logger.error(err.message);
970
- } else {
971
- throw err;
972
- }
973
- }
974
- return data.type === 'aspect';
975
- };
976
-
977
- const graph = await this.getAspectsGraphWithoutCore(components, isAspect);
978
- const idsStr = graph.nodes();
979
- const compIds = await this.resolveMultipleComponentIds(idsStr);
980
- const aspects = await this.getMany(compIds);
981
- const { workspaceComps, scopeComps } = await this.groupComponentsByWorkspaceAndScope(aspects);
982
- const scopeIds = scopeComps.map((aspect) => aspect.id.toString());
983
- const workspaceAspects = await this.requireComponents(workspaceComps);
984
- const workspaceManifests = await this.aspectLoader.getManifestsFromRequireableExtensions(
985
- workspaceAspects,
986
- throwOnError
987
- );
988
- const manifestsIds = workspaceManifests.map((m) => m.id);
989
-
990
- const scopeManifests = await this.scope.getManifestsGraphRecursively(scopeIds, compact(manifestsIds), throwOnError);
991
-
992
- const allManifests = [...scopeManifests, ...workspaceManifests];
993
- await this.aspectLoader.loadExtensionsByManifests(allManifests, throwOnError);
994
- }
995
-
996
- /**
997
- * Note - this gets called from Harmony only.
998
- * returns one graph that includes all dependencies types. each edge has a label of the dependency
999
- * type. the nodes content is the Component object.
1000
- */
1001
- async buildOneGraphForComponents(
1002
- ids: BitId[],
1003
- ignoreIds?: BitIds,
1004
- shouldIgnoreFunc?: ShouldIgnoreFunc
1005
- ): Promise<LegacyGraph> {
1006
- const graphFromFsBuilder = new GraphFromFsBuilder(this, this.logger, ignoreIds, shouldIgnoreFunc);
1007
- return graphFromFsBuilder.buildGraph(ids);
1008
- }
1009
-
1010
- async resolveAspects(runtimeName?: string, componentIds?: ComponentID[]): Promise<AspectDefinition[]> {
1011
- let missingPaths = false;
1012
- const stringIds: string[] = [];
1013
- const idsToResolve = componentIds ? componentIds.map((id) => id.toString()) : this.harmony.extensionsIds;
1014
- const coreAspectsIds = this.aspectLoader.getCoreAspectIds();
1015
- const userAspectsIds: string[] = difference(idsToResolve, coreAspectsIds);
1016
- const componentIdsToResolve = await this.resolveMultipleComponentIds(userAspectsIds);
1017
- const { workspaceIds, scopeIds } = await this.groupIdsByWorkspaceAndScope(componentIdsToResolve);
1018
- const wsComponents = await this.getMany(workspaceIds);
1019
- const aspectDefs = await this.aspectLoader.resolveAspects(wsComponents, async (component) => {
1020
- stringIds.push(component.id._legacy.toString());
1021
- const localPath = this.getComponentPackagePath(component.state._consumer);
1022
- const isExist = await fs.pathExists(localPath);
1023
- if (!isExist) {
1024
- missingPaths = true;
1025
- }
1026
-
1027
- return {
1028
- aspectPath: localPath,
1029
- runtimePath: runtimeName ? await this.aspectLoader.getRuntimePath(component, localPath, runtimeName) : null,
1030
- };
1031
- });
1032
-
1033
- let scopeAspectDefs: AspectDefinition[] = [];
1034
- if (scopeIds.length) {
1035
- scopeAspectDefs = await this.scope.resolveAspects(runtimeName, scopeIds);
1036
- }
1037
-
1038
- let coreAspectDefs = await Promise.all(
1039
- coreAspectsIds.map(async (coreId) => {
1040
- const rawDef = await getAspectDef(coreId, runtimeName);
1041
- return this.aspectLoader.loadDefinition(rawDef);
1042
- })
1043
- );
1044
-
1045
- // due to lack of workspace and scope runtimes. TODO: fix after adding them.
1046
- if (runtimeName) {
1047
- coreAspectDefs = coreAspectDefs.filter((coreAspect) => {
1048
- return coreAspect.runtimePath;
1049
- });
1050
- }
1051
-
1052
- if (missingPaths) {
1053
- await link(stringIds, false);
1054
- }
1055
-
1056
- const allDefs = aspectDefs.concat(coreAspectDefs).concat(scopeAspectDefs);
1057
- const uniqDefs = uniqBy(allDefs, (def) => `${def.aspectPath}-${def.runtimePath}`);
1058
- let defs = uniqDefs;
1059
- if (runtimeName) {
1060
- defs = defs.filter((def) => def.runtimePath);
1061
- }
1062
-
1063
- return defs;
1064
- }
1065
-
1066
- private async groupIdsByWorkspaceAndScope(
1067
- ids: ComponentID[]
1068
- ): Promise<{ workspaceIds: ComponentID[]; scopeIds: ComponentID[] }> {
1069
- const workspaceIds: ComponentID[] = [];
1070
- const scopeIds: ComponentID[] = [];
1071
- await Promise.all(
1072
- ids.map(async (id) => {
1073
- const existOnWorkspace = await this.hasId(id);
1074
- existOnWorkspace ? workspaceIds.push(id) : scopeIds.push(id);
1075
- })
1076
- );
1077
- return { workspaceIds, scopeIds };
1078
- }
1079
-
1080
- private async groupComponentsByWorkspaceAndScope(
1081
- components: Component[]
1082
- ): Promise<{ workspaceComps: Component[]; scopeComps: Component[] }> {
1083
- const workspaceComps: Component[] = [];
1084
- const scopeComps: Component[] = [];
1085
- await Promise.all(
1086
- components.map(async (component) => {
1087
- const existOnWorkspace = await this.hasId(component.id);
1088
- existOnWorkspace ? workspaceComps.push(component) : scopeComps.push(component);
1089
- })
1090
- );
1091
- return { workspaceComps, scopeComps };
1092
- }
1093
-
1094
- /**
1095
- * Load all unloaded extensions from a list
1096
- * @param extensions list of extensions with config to load
1097
- */
1098
- async loadExtensions(extensions: ExtensionDataList, throwOnError = false): Promise<void> {
1099
- const extensionsIdsP = extensions.map(async (extensionEntry) => {
1100
- // Core extension
1101
- if (!extensionEntry.extensionId) {
1102
- return extensionEntry.stringId as string;
1103
- }
1104
-
1105
- const id = await this.resolveComponentId(extensionEntry.extensionId);
1106
- // return this.resolveComponentId(extensionEntry.extensionId);
1107
- return id.toString();
1108
- });
1109
- const extensionsIds: string[] = await Promise.all(extensionsIdsP);
1110
- const loadedExtensions = this.harmony.extensionsIds;
1111
- const extensionsToLoad = difference(extensionsIds, loadedExtensions);
1112
- if (!extensionsToLoad.length) return;
1113
- await this.loadAspects(extensionsToLoad, throwOnError);
1114
- }
1115
-
1116
- /**
1117
- * Provides a cache folder, unique per key.
1118
- * Return value may be undefined, if workspace folder is unconventional (bare-scope, no node_modules, etc)
1119
- */
1120
- getTempDir(
1121
- /*
1122
- * unique key, i.e. aspect or component id
1123
- */
1124
- id: string
1125
- ) {
1126
- const PREFIX = 'bit';
1127
- const cacheDir = path.join(this.modulesPath, '.cache', PREFIX, id);
1128
-
1129
- // maybe should also check it's a folder and has write permissions
1130
- if (!fs.existsSync(cacheDir)) {
1131
- fs.mkdirSync(cacheDir, { recursive: true });
1132
- }
1133
-
1134
- return cacheDir;
1135
- }
1136
-
1137
- async requireComponents(components: Component[]): Promise<RequireableComponent[]> {
1138
- let missingPaths = false;
1139
- const stringIds: string[] = [];
1140
- const resolveP = components.map(async (component) => {
1141
- stringIds.push(component.id._legacy.toString());
1142
- const localPath = this.getComponentPackagePath(component);
1143
- const isExist = await fs.pathExists(localPath);
1144
- if (!isExist) {
1145
- missingPaths = true;
1146
- }
1147
-
1148
- const requireFunc = async () => {
1149
- const plugins = this.aspectLoader.getPlugins(component, localPath);
1150
- if (plugins.has()) {
1151
- return plugins.load(MainRuntime.name);
1152
- }
1153
-
1154
- // eslint-disable-next-line global-require, import/no-dynamic-require
1155
- const aspect = require(localPath);
1156
- // require aspect runtimes
1157
- const runtimePath = await this.aspectLoader.getRuntimePath(component, localPath, MainRuntime.name);
1158
- // eslint-disable-next-line global-require, import/no-dynamic-require
1159
- if (runtimePath) require(runtimePath);
1160
- return aspect;
1161
- };
1162
- return new RequireableComponent(component, requireFunc);
1163
- });
1164
- const resolved = await Promise.all(resolveP);
1165
- // Make sure to link missing components
1166
- if (missingPaths) {
1167
- await link(stringIds, false);
1168
- }
1169
- return resolved;
1170
- }
1171
-
1172
- private async getComponentsDirectory(ids: ComponentID[]) {
1173
- const components = ids.length ? await this.getMany(ids) : await this.list();
1174
- return ComponentMap.as<string>(components, (component) => this.componentDir(component.id));
1175
- }
1176
-
1177
- /**
1178
- * Install dependencies for all components in the workspace
1179
- *
1180
- * @returns
1181
- * @memberof Workspace
1182
- */
1183
- async install(packages?: string[], options?: WorkspaceInstallOptions) {
1184
- if (packages && packages.length) {
1185
- if (!options?.variants && options?.lifecycleType === 'dev') {
1186
- throw new DependencyTypeNotSupportedInPolicy(options?.lifecycleType);
1187
- }
1188
- this.logger.debug(`installing the following packages: ${packages.join()}`);
1189
- const resolver = this.dependencyResolver.getVersionResolver();
1190
- const resolvedPackagesP = packages.map((packageName) =>
1191
- resolver.resolveRemoteVersion(packageName, {
1192
- rootDir: this.path,
1193
- })
1194
- );
1195
- const resolvedPackages = await Promise.all(resolvedPackagesP);
1196
- const newWorkspacePolicyEntries: WorkspacePolicyEntry[] = [];
1197
- resolvedPackages.forEach((resolvedPackage) => {
1198
- if (resolvedPackage.version) {
1199
- const versionWithPrefix = this.dependencyResolver.getVersionWithSavePrefix(
1200
- resolvedPackage.version,
1201
- options?.savePrefix
1202
- );
1203
- newWorkspacePolicyEntries.push({
1204
- dependencyId: resolvedPackage.packageName,
1205
- value: {
1206
- version: versionWithPrefix,
1207
- },
1208
- lifecycleType: options?.lifecycleType || 'runtime',
1209
- });
1210
- }
1211
- });
1212
- if (!options?.variants) {
1213
- this.dependencyResolver.addToRootPolicy(newWorkspacePolicyEntries, {
1214
- updateExisting: options?.updateExisting ?? false,
1215
- });
1216
- } else {
1217
- // TODO: implement
1218
- }
1219
- await this.dependencyResolver.persistConfig(this.path);
1220
- }
1221
- if (options?.import) {
1222
- this.logger.setStatusLine('importing missing objects');
1223
- await this.importObjects();
1224
- this.logger.consoleSuccess();
1225
- }
1226
- this.logger.console(
1227
- `installing dependencies in workspace using ${chalk.cyan(this.dependencyResolver.getPackageManagerName())}`
1228
- );
1229
- this.logger.debug(`installing dependencies in workspace with options`, options);
1230
- this.clearCache();
1231
- // TODO: pass get install options
1232
- const installer = this.dependencyResolver.getInstaller({});
1233
- const compDirMap = await this.getComponentsDirectory([]);
1234
- const mergedRootPolicy = this.dependencyResolver.getWorkspacePolicy();
1235
-
1236
- const depsFilterFn = await this.generateFilterFnForDepsFromLocalRemote();
1237
-
1238
- const pmInstallOptions: PackageManagerInstallOptions = {
1239
- dedupe: options?.dedupe,
1240
- copyPeerToRuntimeOnRoot: options?.copyPeerToRuntimeOnRoot ?? true,
1241
- copyPeerToRuntimeOnComponents: options?.copyPeerToRuntimeOnComponents ?? false,
1242
- dependencyFilterFn: depsFilterFn,
1243
- };
1244
- await installer.install(this.path, mergedRootPolicy, compDirMap, { installTeambitBit: false }, pmInstallOptions);
1245
- // TODO: this make duplicate
1246
- // this.logger.consoleSuccess();
1247
- // TODO: add the links results to the output
1248
- await this.link({
1249
- linkTeambitBit: true,
1250
- legacyLink: true,
1251
- linkCoreAspects: true,
1252
- linkNestedDepsInNM: !this.isLegacy,
1253
- });
1254
- await this.consumer.componentFsCache.deleteAllDependenciesDataCache();
1255
- return compDirMap;
1256
- }
1257
-
1258
- async link(options?: WorkspaceLinkOptions): Promise<LinkResults> {
1259
- if (options?.fetchObject) {
1260
- await this.importObjects();
1261
- }
1262
- const compDirMap = await this.getComponentsDirectory([]);
1263
- const mergedRootPolicy = this.dependencyResolver.getWorkspacePolicy();
1264
- const linker = this.dependencyResolver.getLinker({
1265
- rootDir: this.path,
1266
- linkingOptions: options,
1267
- });
1268
- const res = await linker.link(this.path, mergedRootPolicy, compDirMap, options);
1269
- return res;
1270
- }
1271
-
1272
- /**
1273
- * Generate a filter to pass to the installer
1274
- * This will filter deps which are come from remotes which defined in scope.json
1275
- * those components comes from local remotes, usually doesn't have a package in a registry
1276
- * so no reason to try to install them (it will fail)
1277
- */
1278
- private async generateFilterFnForDepsFromLocalRemote() {
1279
- // TODO: once scope create a new API for this, replace it with the new one
1280
- const remotes = await this.scope._legacyRemotes();
1281
- return (dependencyList: DependencyList): DependencyList => {
1282
- const filtered = dependencyList.filter((dep) => {
1283
- if (!(dep instanceof ComponentDependency)) {
1284
- return true;
1285
- }
1286
- if (remotes.isHub(dep.componentId.scope)) {
1287
- return true;
1288
- }
1289
- return false;
1290
- });
1291
- return filtered;
1292
- };
1293
- }
1294
-
1295
- /**
1296
- * same as `this.importAndGetMany()` with a specific error handling of ComponentNotFound
1297
- */
1298
- private async importAndGetAspects(componentIds: ComponentID[]): Promise<Component[]> {
1299
- try {
1300
- return await this.importAndGetMany(componentIds);
1301
- } catch (err: any) {
1302
- if (err instanceof ComponentNotFound) {
1303
- const config = this.harmony.get<Config>('teambit.harmony/config');
1304
- const configStr = JSON.stringify(config.workspaceConfig?.raw || {});
1305
- if (configStr.includes(err.id)) {
1306
- throw new BitError(`error: a component "${err.id}" was not found
1307
- your workspace.jsonc has this component-id set. you might want to remove/change it.`);
1308
- }
1309
- }
1310
-
1311
- throw err;
1312
- }
1313
- }
1314
-
1315
- // TODO: replace with a proper import API on the workspace
1316
- private async importObjects() {
1317
- const importOptions: ImportOptions = {
1318
- ids: [],
1319
- verbose: false,
1320
- merge: false,
1321
- objectsOnly: true,
1322
- override: false,
1323
- writeDists: false,
1324
- writeConfig: false,
1325
- installNpmPackages: false,
1326
- importDependenciesDirectly: false,
1327
- importDependents: false,
1328
- };
1329
- const importer = new Importer(this, this.dependencyResolver);
1330
- try {
1331
- const res = await importer.import(importOptions, []);
1332
- return res;
1333
- } catch (err: any) {
1334
- // TODO: this is a hack since the legacy throw an error, we should provide a way to not throw this error from the legacy
1335
- if (err instanceof NothingToImport) {
1336
- // Do not write nothing to import warning
1337
- return undefined;
1338
- }
1339
- throw err;
1340
- }
1341
- }
1342
-
1343
- /**
1344
- * this should be rarely in-use.
1345
- * it's currently used by watch extension as a quick workaround to load .bitmap and the components
1346
- */
1347
- async _reloadConsumer() {
1348
- this.consumer = await loadConsumer(this.path, true);
1349
- this.clearCache();
1350
- }
1351
-
1352
- getComponentPackagePath(component: ConsumerComponent | Component) {
1353
- const packageName = componentIdToPackageName(
1354
- component instanceof ConsumerComponent ? component : component.state._consumer
1355
- );
1356
- return path.join(this.modulesPath, packageName);
1357
- }
1358
-
1359
- // TODO: should we return here the dir as it defined (aka components) or with /{name} prefix (as it used in legacy)
1360
- get defaultDirectory(): string {
1361
- return this.config.defaultDirectory;
1362
- }
1363
-
1364
- get legacyDefaultDirectory(): string {
1365
- if (this.defaultDirectory && !this.defaultDirectory.includes('{name}')) {
1366
- return `${this.defaultDirectory}/{name}`;
1367
- }
1368
- return this.defaultDirectory;
1369
- }
1370
-
1371
- /**
1372
- * Transform the id to ComponentId and get the exact id as appear in bitmap
1373
- *
1374
- * @param {(string | ComponentID | BitId)} id
1375
- * @returns {Promise<ComponentID>}
1376
- * @memberof Workspace
1377
- */
1378
- async resolveComponentId(id: string | ComponentID | BitId): Promise<ComponentID> {
1379
- // This is required in case where you have in your workspace a component with the same name as a core aspect
1380
- // let's say you have component called react-native (which is eventually my-org.my-scope/react-native)
1381
- // and you set teambit.react/react-native as your env
1382
- // bit will get here with the string teambit.react/react-native and will try to resolve it from the workspace
1383
- // during this it will find the my-org.my-scope/react-native which is incorrect as the core one doesn't exist in the
1384
- // workspace
1385
- if (this.aspectLoader.isCoreAspect(id.toString())) {
1386
- return ComponentID.fromString(id.toString());
1387
- }
1388
- let legacyId = this.consumer.getParsedIdIfExist(id.toString(), true, true);
1389
- if (!legacyId) {
1390
- try {
1391
- const idWithVersion = id.toString();
1392
- const [idWithoutVersion, version] = id.toString().split('@');
1393
- const _bitMapId = this.consumer.getParsedIdIfExist(idWithoutVersion, false, true);
1394
- // This logic is very specific, and very sensitive for changes please do not touch this without consulting with @ran or @gilad
1395
- // example (partial list) cases which should be handled are:
1396
- // use case 1 - ws component provided with the local scope name:
1397
- // source id : my-scope1/my-name1
1398
- // bitmap res (_id) : my-name1 (comp is tagged but not exported)
1399
- // local scope name : my-scope1
1400
- // scope content : my-name1
1401
- // expected result : my-name1
1402
- // use case 2 - component with same name exist in ws and scope (but with different scope name)
1403
- // source id : my-scope2/my-name1
1404
- // bitmap res (_id) : my-name1 (comp exist in ws but it's actually different component)
1405
- // local scope name : my-scope1
1406
- // scope content : my-scope2/my-name1
1407
- // expected result : my-scope2/my-name1
1408
- // use case 3 - component with same name exist in ws and scope (but with different scope name) - source provided without scope name
1409
- // source id : my-name1
1410
- // bitmap res (_id) : my-name1 (comp exist in ws but it's actually different component)
1411
- // local scope name : my-scope1
1412
- // scope content : my-scope1/my-name1 and my-scope2/my-name1
1413
- // expected result : my-name1 (get the name from the bitmap)
1414
- // use case 4 - component with the same name and different scope are imported into the ws
1415
- // source id : my-name1
1416
- // bitmap res (_id) : my-scope2/my-name1 (comp exist in ws from different scope (imported))
1417
- // local scope name : my-scope1
1418
- // scope content : my-scope2/my-name1
1419
- // expected result : my-scope2/my-name1 (get the name from the bitmap)
1420
-
1421
- // No entry in bitmap at all, search for the original input id
1422
- if (!_bitMapId) {
1423
- return await this.scope.resolveComponentId(id.toString());
1424
- }
1425
- const _bitMapIdWithoutVersion = _bitMapId.toStringWithoutVersion();
1426
- const _bitMapIdWithVersion = _bitMapId.changeVersion(version).toString();
1427
- // The id in the bitmap has prefix which is not in the source id - the bitmap entry has scope name
1428
- // Handle use case 4
1429
- if (_bitMapIdWithoutVersion.endsWith(idWithoutVersion) && _bitMapIdWithoutVersion !== idWithoutVersion) {
1430
- return await this.scope.resolveComponentId(_bitMapIdWithVersion);
1431
- }
1432
- // The id in the bitmap doesn't have scope, the source id has scope
1433
- // Handle use case 2 and use case 1
1434
- if (idWithoutVersion.endsWith(_bitMapIdWithoutVersion) && _bitMapIdWithoutVersion !== idWithoutVersion) {
1435
- if (id.toString().startsWith(this.scope.name)) {
1436
- // Handle use case 1 - the provided id has scope name same as the local scope name
1437
- // we want to send it as it appear in the bitmap
1438
- return await this.scope.resolveComponentId(_bitMapIdWithVersion);
1439
- }
1440
- // Handle use case 2 - the provided id has scope which is not the local scope
1441
- // we want to search by the source id
1442
- return await this.scope.resolveComponentId(idWithVersion);
1443
- }
1444
- // Handle use case 3
1445
- return await this.scope.resolveComponentId(idWithVersion);
1446
- } catch (error: any) {
1447
- legacyId = BitId.parse(id.toString(), true);
1448
- return ComponentID.fromLegacy(legacyId);
1449
- }
1450
- }
1451
- const relativeComponentDir = this.componentDirFromLegacyId(legacyId, undefined, { relative: true });
1452
- const defaultScope = await this.componentDefaultScopeFromComponentDirAndName(
1453
- relativeComponentDir,
1454
- legacyId.toStringWithoutScopeAndVersion()
1455
- );
1456
- return ComponentID.fromLegacy(legacyId, defaultScope);
1457
- }
1458
-
1459
- async resolveMultipleComponentIds(ids: Array<string | ComponentID | BitId>): Promise<ComponentID[]> {
1460
- return Promise.all(ids.map(async (id) => this.resolveComponentId(id)));
1461
- }
1462
-
1463
- /**
1464
- * This will mutate the original extensions list and resolve it's ids
1465
- *
1466
- * @param {ExtensionDataList} extensions
1467
- * @returns {Promise<void[]>}
1468
- * @memberof Workspace
1469
- */
1470
- resolveExtensionsList(extensions: ExtensionDataList): Promise<void[]> {
1471
- const resolveMergedExtensionsP = extensions.map(async (extensionEntry) => {
1472
- if (extensionEntry.extensionId) {
1473
- // const hasVersion = extensionEntry.extensionId.hasVersion();
1474
- // const useBitmapVersion = !hasVersion;
1475
- // const resolvedId = await this.resolveComponentId(extensionEntry.extensionId, true, useBitmapVersion);
1476
-
1477
- // Assuming extensionId always has scope - do not allow extension id without scope
1478
- const resolvedId = await this.resolveComponentId(extensionEntry.extensionId);
1479
- extensionEntry.extensionId = resolvedId._legacy;
1480
- }
1481
- });
1482
- return Promise.all(resolveMergedExtensionsP);
1483
- }
1484
-
1485
- /**
1486
- * This will mutate the original extensions list and make sure all extensions has the ids with the scope / default scope
1487
- *
1488
- * @param {ExtensionDataList} extensions
1489
- * @returns {Promise<void[]>}
1490
- * @memberof Workspace
1491
- */
1492
- addDefaultScopeToExtensionsList(extensions: ExtensionDataList): Promise<void[]> {
1493
- const resolveMergedExtensionsP = extensions.map(async (extensionEntry) => {
1494
- if (extensionEntry.extensionId && !extensionEntry.extensionId.hasScope()) {
1495
- const componentId = ComponentID.fromLegacy(extensionEntry.extensionId);
1496
- const defaultScope = await this.componentDefaultScope(componentId);
1497
- extensionEntry.extensionId = extensionEntry.extensionId.changeScope(defaultScope);
1498
- }
1499
- });
1500
- return Promise.all(resolveMergedExtensionsP);
1501
- }
1502
- }
1503
-
1504
- /**
1505
- * this is a super hacky way to do it. problem is that loadAspect is running as onStart hook, where we don't
1506
- * have the CLI fully loaded yet, so we can't get the command from the CLI aspect, we have to retrieve it from
1507
- * process.argv.
1508
- * in general, we don't want every command to try again and again fetching un-built versions. otherwise, every time
1509
- * Bit loads (even bit --help), it'll fetch them and slow down everything.
1510
- * instead, long-running commands and those that need the artifacts from the Version objects, should try to re-fetch.
1511
- */
1512
- function shouldReFetchUnBuiltVersion() {
1513
- const commandsToReFetch = ['build', 'show', 'start', 'tag', 'install', 'link', 'import'];
1514
- return commandsToReFetch.includes(process.argv[2]);
1515
- }
1516
-
1517
- export default Workspace;