@teambit/remove 1.0.106 → 1.0.108

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.
@@ -0,0 +1,367 @@
1
+ import { CLIAspect, CLIMain, MainRuntime } from '@teambit/cli';
2
+ import { Logger, LoggerAspect, LoggerMain } from '@teambit/logger';
3
+ import WorkspaceAspect, { OutsideWorkspaceError, Workspace } from '@teambit/workspace';
4
+ import { ComponentID, ComponentIdList } from '@teambit/component-id';
5
+ import { ConsumerNotFound } from '@teambit/legacy/dist/consumer/exceptions';
6
+ import ImporterAspect, { ImporterMain } from '@teambit/importer';
7
+ import { compact } from 'lodash';
8
+ import hasWildcard from '@teambit/legacy/dist/utils/string/has-wildcard';
9
+ import { getRemoteBitIdsByWildcards } from '@teambit/legacy/dist/api/consumer/lib/list-scope';
10
+ import { BitError } from '@teambit/bit-error';
11
+ import deleteComponentsFiles from '@teambit/legacy/dist/consumer/component-ops/delete-component-files';
12
+ import { DependencyResolverAspect, DependencyResolverMain } from '@teambit/dependency-resolver';
13
+ import { IssuesClasses } from '@teambit/component-issues';
14
+ import IssuesAspect, { IssuesMain } from '@teambit/issues';
15
+ import pMapSeries from 'p-map-series';
16
+ import { NoHeadNoVersion } from '@teambit/legacy/dist/scope/exceptions/no-head-no-version';
17
+ import ComponentAspect, { Component, ComponentMain } from '@teambit/component';
18
+ import { removeComponentsFromNodeModules } from '@teambit/legacy/dist/consumer/component/package-json-utils';
19
+ import { RemoveCmd } from './remove-cmd';
20
+ import { RemoveComponentsResult, removeComponents } from './remove-components';
21
+ import { RemoveAspect } from './remove.aspect';
22
+ import { RemoveFragment } from './remove.fragment';
23
+ import { RecoverCmd, RecoverOptions } from './recover-cmd';
24
+ import { DeleteCmd } from './delete-cmd';
25
+
26
+ const BEFORE_REMOVE = 'removing components';
27
+
28
+ export type RemoveInfo = {
29
+ removed: boolean;
30
+ /**
31
+ * whether to remove the component from default lane once merged
32
+ */
33
+ removeOnMain?: boolean;
34
+ };
35
+
36
+ export class RemoveMain {
37
+ constructor(
38
+ private workspace: Workspace,
39
+ public logger: Logger,
40
+ private importer: ImporterMain,
41
+ private depResolver: DependencyResolverMain
42
+ ) {}
43
+
44
+ async remove({
45
+ componentsPattern,
46
+ force = false,
47
+ remote = false,
48
+ track = false,
49
+ deleteFiles = true,
50
+ }: {
51
+ componentsPattern: string;
52
+ force?: boolean;
53
+ remote?: boolean;
54
+ track?: boolean;
55
+ deleteFiles?: boolean;
56
+ }): Promise<RemoveComponentsResult> {
57
+ this.logger.setStatusLine(BEFORE_REMOVE);
58
+ const bitIds = remote
59
+ ? await this.getRemoteBitIdsToRemove(componentsPattern)
60
+ : await this.getLocalBitIdsToRemove(componentsPattern);
61
+ this.logger.setStatusLine(BEFORE_REMOVE); // again because the loader might changed when talking to the remote
62
+ const consumer = this.workspace?.consumer;
63
+ const removeResults = await removeComponents({
64
+ consumer,
65
+ ids: ComponentIdList.fromArray(bitIds),
66
+ force,
67
+ remote,
68
+ track,
69
+ deleteFiles,
70
+ });
71
+ if (consumer) await consumer.onDestroy('remove');
72
+ return removeResults;
73
+ }
74
+
75
+ /**
76
+ * remove components from the workspace.
77
+ */
78
+ async removeLocallyByIds(ids: ComponentID[], { force = false }: { force?: boolean } = {}) {
79
+ if (!this.workspace) throw new OutsideWorkspaceError();
80
+ const results = await removeComponents({
81
+ consumer: this.workspace.consumer,
82
+ ids: ComponentIdList.fromArray(ids),
83
+ force,
84
+ remote: false,
85
+ track: false,
86
+ deleteFiles: true,
87
+ });
88
+ await this.workspace.bitMap.write('remove');
89
+
90
+ return results;
91
+ }
92
+
93
+ async markRemoveComps(componentIds: ComponentID[], shouldUpdateMain = false) {
94
+ const components = await this.workspace.getMany(componentIds);
95
+ await removeComponentsFromNodeModules(
96
+ this.workspace.consumer,
97
+ components.map((c) => c.state._consumer)
98
+ );
99
+ // don't use `this.workspace.addSpecificComponentConfig`, if the component has component.json it will be deleted
100
+ // during this removal along with the entire component dir.
101
+ const config: RemoveInfo = { removed: true };
102
+ if (shouldUpdateMain) config.removeOnMain = true;
103
+ componentIds.map((compId) => this.workspace.bitMap.addComponentConfig(compId, RemoveAspect.id, config));
104
+ await this.workspace.bitMap.write('delete');
105
+ const bitIds = ComponentIdList.fromArray(componentIds.map((id) => id));
106
+ await deleteComponentsFiles(this.workspace.consumer, bitIds);
107
+
108
+ return componentIds;
109
+ }
110
+
111
+ async deleteComps(componentsPattern: string, opts: { updateMain?: boolean } = {}): Promise<ComponentID[]> {
112
+ if (!this.workspace) throw new ConsumerNotFound();
113
+ const componentIds = await this.workspace.idsByPattern(componentsPattern);
114
+ const newComps = componentIds.filter((id) => !id.hasVersion());
115
+ if (newComps.length) {
116
+ throw new BitError(
117
+ `no need to delete the following new component(s), please remove them by "bit remove"\n${newComps
118
+ .map((id) => id.toString())
119
+ .join('\n')}`
120
+ );
121
+ }
122
+ const currentLane = await this.workspace.getCurrentLaneObject();
123
+ const { updateMain } = opts;
124
+ if (!updateMain && currentLane?.isNew) {
125
+ throw new Error(
126
+ 'no need to delete components from an un-exported lane, you can remove them by running "bit remove"'
127
+ );
128
+ }
129
+
130
+ return this.markRemoveComps(componentIds, updateMain);
131
+ }
132
+
133
+ /**
134
+ * recover a soft-removed component.
135
+ * there are 4 different scenarios.
136
+ * 1. a component was just soft-removed, it wasn't snapped yet. so it's now in .bitmap with the "removed" aspect entry.
137
+ * 1.a. the component still exists in the local scope. no need to import. write it from there.
138
+ * 1.b. the component doesn't exist in the local scope. import it.
139
+ * 2. soft-removed and then snapped. It's not in .bitmap now.
140
+ * 3. soft-removed, snapped, exported. it's not in .bitmap now.
141
+ * 4. a soft-removed components was imported, so it's now in .bitmap without the "removed" aspect entry.
142
+ * 5. workspace is empty. the soft-removed component is on the remote.
143
+ * returns `true` if it was recovered. `false` if the component wasn't soft-removed, so nothing to recover from.
144
+ */
145
+ async recover(compIdStr: string, options: RecoverOptions): Promise<boolean> {
146
+ if (!this.workspace) throw new ConsumerNotFound();
147
+ const bitMapEntry = this.workspace.consumer.bitMap.components.find((compMap) => {
148
+ return compMap.id.fullName === compIdStr || compMap.id.toStringWithoutVersion() === compIdStr;
149
+ });
150
+ const importComp = async (idStr: string) => {
151
+ await this.importer.import({
152
+ ids: [idStr],
153
+ installNpmPackages: !options.skipDependencyInstallation,
154
+ writeConfigFiles: !options.skipWriteConfigFiles,
155
+ override: true,
156
+ });
157
+ };
158
+ const setAsRemovedFalse = async (compId: ComponentID) => {
159
+ await this.workspace.addSpecificComponentConfig(compId, RemoveAspect.id, { removed: false });
160
+ await this.workspace.bitMap.write('recover');
161
+ };
162
+ if (bitMapEntry) {
163
+ if (bitMapEntry.config?.[RemoveAspect.id]) {
164
+ // case #1
165
+ const compFromScope = await this.workspace.scope.get(bitMapEntry.id);
166
+ if (compFromScope) {
167
+ // in the case the component is in the scope, we prefer to write it from the scope rather than import it.
168
+ // because in some cases the "import" throws an error, e.g. when the component is diverged.
169
+ await this.workspace.write(compFromScope, bitMapEntry.rootDir);
170
+ this.workspace.bitMap.removeComponentConfig(bitMapEntry.id, RemoveAspect.id, false);
171
+ await this.workspace.bitMap.write('recover');
172
+ } else {
173
+ delete bitMapEntry.config?.[RemoveAspect.id];
174
+ await importComp(bitMapEntry.id.toString());
175
+ }
176
+ return true;
177
+ }
178
+ // case #4
179
+ const compId = await this.workspace.resolveComponentId(bitMapEntry.id);
180
+ const comp = await this.workspace.get(compId);
181
+ if (!this.isRemoved(comp)) {
182
+ return false;
183
+ }
184
+ await setAsRemovedFalse(compId);
185
+ return true;
186
+ }
187
+ const compId = await this.workspace.scope.resolveComponentId(compIdStr);
188
+ const currentLane = await this.workspace.getCurrentLaneObject();
189
+ const idOnLane = currentLane?.getComponent(compId);
190
+ const compIdWithPossibleVer = idOnLane ? compId.changeVersion(idOnLane.head.toString()) : compId;
191
+ const compFromScope = await this.workspace.scope.get(compIdWithPossibleVer);
192
+ if (compFromScope && this.isRemoved(compFromScope)) {
193
+ // case #2 and #3
194
+ await importComp(compIdWithPossibleVer._legacy.toString());
195
+ await setAsRemovedFalse(compIdWithPossibleVer);
196
+ return true;
197
+ }
198
+ // case #5
199
+ let comp: Component | undefined;
200
+ try {
201
+ comp = await this.workspace.scope.getRemoteComponent(compId);
202
+ } catch (err: any) {
203
+ if (err instanceof NoHeadNoVersion) {
204
+ throw new BitError(
205
+ `unable to find the component ${compIdWithPossibleVer.toString()} in the current lane or main`
206
+ );
207
+ }
208
+ throw err;
209
+ }
210
+ if (!this.isRemoved(comp)) {
211
+ return false;
212
+ }
213
+ await importComp(compId._legacy.toString());
214
+ await setAsRemovedFalse(compId);
215
+
216
+ return true;
217
+ }
218
+
219
+ private async throwForMainComponentWhenOnLane(components: Component[]) {
220
+ const currentLane = await this.workspace.getCurrentLaneObject();
221
+ if (!currentLane) return; // user on main
222
+ const laneComps = currentLane.toBitIds();
223
+ const mainComps = components.filter((comp) => !laneComps.hasWithoutVersion(comp.id));
224
+ if (mainComps.length) {
225
+ throw new BitError(`the following components belong to main, they cannot be soft-removed when on a lane. consider removing them without --soft.
226
+ ${mainComps.map((c) => c.id.toString()).join('\n')}`);
227
+ }
228
+ }
229
+
230
+ getRemoveInfo(component: Component): RemoveInfo {
231
+ const data = component.config.extensions.findExtension(RemoveAspect.id)?.config as RemoveInfo | undefined;
232
+ return {
233
+ removed: data?.removed || false,
234
+ };
235
+ }
236
+
237
+ isRemoved(component: Component): boolean {
238
+ return this.getRemoveInfo(component).removed;
239
+ }
240
+
241
+ /**
242
+ * performant version of isRemoved() in case the component object is not available and loading it is expensive.
243
+ */
244
+ async isRemovedByIdWithoutLoadingComponent(componentId: ComponentID): Promise<boolean> {
245
+ if (!componentId.hasVersion()) return false;
246
+ const bitmapEntry = this.workspace.bitMap.getBitmapEntryIfExist(componentId);
247
+ if (bitmapEntry && bitmapEntry.isRemoved()) return true;
248
+ if (bitmapEntry && bitmapEntry.isRecovered()) return false;
249
+ const modelComp = await this.workspace.scope.getBitObjectModelComponent(componentId);
250
+ if (!modelComp) return false;
251
+ const versionObj = await this.workspace.scope.getBitObjectVersion(modelComp, componentId.version as string);
252
+ if (!versionObj) return false;
253
+ return versionObj.isRemoved();
254
+ }
255
+
256
+ /**
257
+ * get components that were soft-removed and tagged/snapped/merged but not exported yet.
258
+ */
259
+ async getRemovedStaged(): Promise<ComponentID[]> {
260
+ return this.workspace.isOnMain() ? this.getRemovedStagedFromMain() : this.getRemovedStagedFromLane();
261
+ }
262
+
263
+ async addRemovedDependenciesIssues(components: Component[]) {
264
+ await pMapSeries(components, async (component) => {
265
+ await this.addRemovedDepIssue(component);
266
+ });
267
+ }
268
+
269
+ private async addRemovedDepIssue(component: Component) {
270
+ const dependencies = await this.depResolver.getComponentDependencies(component);
271
+ const removedWithUndefined = await Promise.all(
272
+ dependencies.map(async (dep) => {
273
+ const isRemoved = await this.isRemovedByIdWithoutLoadingComponent(dep.componentId);
274
+ if (isRemoved) return dep.componentId;
275
+ return undefined;
276
+ })
277
+ );
278
+ const removed = compact(removedWithUndefined).map((id) => id.toString());
279
+ if (removed.length) {
280
+ component.state.issues.getOrCreate(IssuesClasses.RemovedDependencies).data = removed;
281
+ }
282
+ }
283
+
284
+ private async getRemovedStagedFromMain(): Promise<ComponentID[]> {
285
+ const stagedConfig = await this.workspace.scope.getStagedConfig();
286
+ return stagedConfig
287
+ .getAll()
288
+ .filter((compConfig) => compConfig.config?.[RemoveAspect.id]?.removed)
289
+ .map((compConfig) => compConfig.id);
290
+ }
291
+
292
+ private async getRemovedStagedFromLane(): Promise<ComponentID[]> {
293
+ const currentLane = await this.workspace.getCurrentLaneObject();
294
+ if (!currentLane) return [];
295
+ const laneIds = currentLane.toBitIds();
296
+ const workspaceIds = await this.workspace.listIds();
297
+ const laneIdsNotInWorkspace = laneIds.filter((id) => !workspaceIds.find((wId) => wId.isEqualWithoutVersion(id)));
298
+ if (!laneIdsNotInWorkspace.length) return [];
299
+ const laneCompIdsNotInWorkspace = await this.workspace.scope.resolveMultipleComponentIds(laneIdsNotInWorkspace);
300
+ const comps = await this.workspace.scope.getMany(laneCompIdsNotInWorkspace);
301
+ const removed = comps.filter((c) => this.isRemoved(c));
302
+ const staged = await Promise.all(
303
+ removed.map(async (c) => {
304
+ const snapDistance = await this.workspace.scope.getSnapDistance(c.id, false);
305
+ if (snapDistance.err) {
306
+ this.logger.warn(
307
+ `getRemovedStagedFromLane unable to get snapDistance for ${c.id.toString()} due to ${snapDistance.err.name}`
308
+ );
309
+ // todo: not clear what should be done here. should we consider it as removed-staged or not.
310
+ }
311
+ if (snapDistance.isSourceAhead()) return c.id;
312
+ return undefined;
313
+ })
314
+ );
315
+ return compact(staged);
316
+ }
317
+
318
+ private async getLocalBitIdsToRemove(componentsPattern: string): Promise<ComponentID[]> {
319
+ if (!this.workspace) throw new ConsumerNotFound();
320
+ const componentIds = await this.workspace.idsByPattern(componentsPattern);
321
+ return componentIds.map((id) => id);
322
+ }
323
+
324
+ private async getRemoteBitIdsToRemove(componentsPattern: string): Promise<ComponentID[]> {
325
+ if (hasWildcard(componentsPattern)) {
326
+ return getRemoteBitIdsByWildcards(componentsPattern);
327
+ }
328
+ return [ComponentID.fromString(componentsPattern)];
329
+ }
330
+
331
+ static slots = [];
332
+ static dependencies = [
333
+ WorkspaceAspect,
334
+ CLIAspect,
335
+ LoggerAspect,
336
+ ComponentAspect,
337
+ ImporterAspect,
338
+ DependencyResolverAspect,
339
+ IssuesAspect,
340
+ ];
341
+ static runtime = MainRuntime;
342
+
343
+ static async provider([workspace, cli, loggerMain, componentAspect, importerMain, depResolver, issues]: [
344
+ Workspace,
345
+ CLIMain,
346
+ LoggerMain,
347
+ ComponentMain,
348
+ ImporterMain,
349
+ DependencyResolverMain,
350
+ IssuesMain
351
+ ]) {
352
+ const logger = loggerMain.createLogger(RemoveAspect.id);
353
+ const removeMain = new RemoveMain(workspace, logger, importerMain, depResolver);
354
+ issues.registerAddComponentsIssues(removeMain.addRemovedDependenciesIssues.bind(removeMain));
355
+ componentAspect.registerShowFragments([new RemoveFragment(removeMain)]);
356
+ cli.register(
357
+ new RemoveCmd(removeMain, workspace),
358
+ new DeleteCmd(removeMain, workspace),
359
+ new RecoverCmd(removeMain)
360
+ );
361
+ return removeMain;
362
+ }
363
+ }
364
+
365
+ RemoveAspect.addRuntime(RemoveMain);
366
+
367
+ export default RemoveMain;
@@ -0,0 +1,17 @@
1
+ import { ComponentIdList } from '@teambit/component-id';
2
+ import RemovedObjects from '@teambit/legacy/dist/scope/removed-components';
3
+
4
+ export class RemovedLocalObjects extends RemovedObjects {
5
+ modifiedComponents: ComponentIdList;
6
+ constructor(
7
+ removedComponentIds?: ComponentIdList,
8
+ missingComponents?: ComponentIdList,
9
+ modifiedComponents?: ComponentIdList,
10
+ dependentBits?: Record<string, any>,
11
+ removedFromLane?: ComponentIdList
12
+ ) {
13
+ super({ removedComponentIds, missingComponents, dependentBits, removedFromLane });
14
+ // @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
15
+ this.modifiedComponents = modifiedComponents;
16
+ }
17
+ }
package/tsconfig.json CHANGED
@@ -1,38 +1,33 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "lib": [
4
- "es2019",
5
- "DOM",
6
- "ES6",
7
- "DOM.Iterable",
8
- "ScriptHost"
4
+ "esnext",
5
+ "dom",
6
+ "dom.Iterable"
9
7
  ],
10
- "target": "es2015",
11
- "module": "CommonJS",
12
- "jsx": "react",
13
- "allowJs": true,
14
- "composite": true,
8
+ "target": "es2020",
9
+ "module": "es2020",
10
+ "jsx": "react-jsx",
15
11
  "declaration": true,
16
12
  "sourceMap": true,
17
- "skipLibCheck": true,
18
13
  "experimentalDecorators": true,
19
- "outDir": "dist",
14
+ "skipLibCheck": true,
20
15
  "moduleResolution": "node",
21
16
  "esModuleInterop": true,
22
- "rootDir": ".",
23
17
  "resolveJsonModule": true,
24
- "emitDeclarationOnly": true,
25
- "emitDecoratorMetadata": true,
26
- "allowSyntheticDefaultImports": true,
27
- "strictPropertyInitialization": false,
28
- "strict": true,
29
- "noImplicitAny": false,
30
- "preserveConstEnums": true
18
+ "allowJs": true,
19
+ "outDir": "dist",
20
+ "emitDeclarationOnly": true
31
21
  },
32
22
  "exclude": [
23
+ "artifacts",
24
+ "public",
33
25
  "dist",
26
+ "node_modules",
27
+ "package.json",
34
28
  "esm.mjs",
35
- "package.json"
29
+ "**/*.cjs",
30
+ "./dist"
36
31
  ],
37
32
  "include": [
38
33
  "**/*",
package/types/asset.d.ts CHANGED
@@ -5,12 +5,12 @@ declare module '*.png' {
5
5
  declare module '*.svg' {
6
6
  import type { FunctionComponent, SVGProps } from 'react';
7
7
 
8
- export const ReactComponent: FunctionComponent<SVGProps<SVGSVGElement> & { title?: string }>;
8
+ export const ReactComponent: FunctionComponent<
9
+ SVGProps<SVGSVGElement> & { title?: string }
10
+ >;
9
11
  const src: string;
10
12
  export default src;
11
13
  }
12
-
13
- // @TODO Gilad
14
14
  declare module '*.jpg' {
15
15
  const value: any;
16
16
  export = value;
@@ -27,3 +27,15 @@ declare module '*.bmp' {
27
27
  const value: any;
28
28
  export = value;
29
29
  }
30
+ declare module '*.otf' {
31
+ const value: any;
32
+ export = value;
33
+ }
34
+ declare module '*.woff' {
35
+ const value: any;
36
+ export = value;
37
+ }
38
+ declare module '*.woff2' {
39
+ const value: any;
40
+ export = value;
41
+ }