@teambit/workspace 0.0.566 → 0.0.570

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 (64) hide show
  1. package/__snapshots__/pick-outdated-pkgs.spec.ts.snap +88 -0
  2. package/component-config-file/component-config-file.ts +98 -0
  3. package/component-config-file/exceptions/already-exists.ts +7 -0
  4. package/component-config-file/exceptions/index.ts +1 -0
  5. package/component-config-file/index.ts +2 -0
  6. package/component-tree.widget.tsx +16 -0
  7. package/dist/__snapshots__/pick-outdated-pkgs.spec.js.snap +88 -0
  8. package/dist/__snapshots__/pick-outdated-pkgs.spec.ts.snap +88 -0
  9. package/dist/pick-outdated-pkgs.d.ts +12 -0
  10. package/dist/pick-outdated-pkgs.js +201 -0
  11. package/dist/pick-outdated-pkgs.js.map +1 -0
  12. package/dist/pick-outdated-pkgs.spec.d.ts +1 -0
  13. package/dist/pick-outdated-pkgs.spec.js +64 -0
  14. package/dist/pick-outdated-pkgs.spec.js.map +1 -0
  15. package/dist/update.cmd.js +3 -5
  16. package/dist/update.cmd.js.map +1 -1
  17. package/dist/workspace.d.ts +8 -1
  18. package/dist/workspace.js +28 -2
  19. package/dist/workspace.js.map +1 -1
  20. package/eject-conf.cmd.tsx +62 -0
  21. package/events/index.ts +3 -0
  22. package/events/on-component-add-event.ts +14 -0
  23. package/events/on-component-change-event.ts +14 -0
  24. package/events/on-component-removed-event.ts +14 -0
  25. package/exceptions/dependency-type-not-supported-in-policy.ts +7 -0
  26. package/exceptions/incorrect-env-aspect.ts +8 -0
  27. package/exceptions/index.ts +1 -0
  28. package/install.cmd.tsx +67 -0
  29. package/link/component-list-links.ts +50 -0
  30. package/link/core-aspects-links.ts +50 -0
  31. package/link/get-package-name-from-target.ts +5 -0
  32. package/link/index.ts +1 -0
  33. package/link/link-row.ts +20 -0
  34. package/link/link-to-dir.ts +13 -0
  35. package/link/link.cmd.ts +96 -0
  36. package/link/nested-deps-in-nm-links.ts +47 -0
  37. package/link/rewire-row.ts +17 -0
  38. package/list.cmd.tsx +30 -0
  39. package/package-tar/teambit-workspace-0.0.570.tgz +0 -0
  40. package/package.json +54 -65
  41. package/types/asset.d.ts +29 -0
  42. package/types/style.d.ts +42 -0
  43. package/ui/index.ts +1 -0
  44. package/ui/workspace/default-component.ts +7 -0
  45. package/ui/workspace/index.ts +1 -0
  46. package/ui/workspace/use-workspace.ts +193 -0
  47. package/ui/workspace/workspace-context.tsx +5 -0
  48. package/ui/workspace/workspace-model.ts +66 -0
  49. package/ui/workspace/workspace-overview/index.ts +1 -0
  50. package/ui/workspace/workspace-overview/workspace-overview.tsx +21 -0
  51. package/ui/workspace/workspace-provider.tsx +23 -0
  52. package/ui/workspace/workspace.tsx +99 -0
  53. package/ui/workspace-components-drawer/index.ts +1 -0
  54. package/ui/workspace-components-drawer/workspace-components.drawer.tsx +49 -0
  55. package/update.cmd.tsx +30 -0
  56. package/watch/check-types.ts +5 -0
  57. package/watch/output-formatter.ts +50 -0
  58. package/watch/watch.cmd.tsx +142 -0
  59. package/watch/watcher.ts +289 -0
  60. package/workspace-component/component-status.ts +67 -0
  61. package/workspace-component/index.ts +2 -0
  62. package/workspace-component/workspace-component-loader.ts +276 -0
  63. package/workspace-component/workspace-component.ts +51 -0
  64. package/workspace.ui.runtime.tsx +215 -0
@@ -0,0 +1,49 @@
1
+ import type { DrawerType } from '@teambit/ui-foundation.ui.tree.drawer';
2
+ import { FullLoader } from '@teambit/ui-foundation.ui.full-loader';
3
+ import {
4
+ ComponentTree,
5
+ PayloadType,
6
+ ComponentView,
7
+ ScopeTreeNode,
8
+ NamespaceTreeNode,
9
+ ScopePayload,
10
+ } from '@teambit/ui-foundation.ui.side-bar';
11
+
12
+ import type { TreeNodeProps } from '@teambit/base-ui.graph.tree.recursive-tree';
13
+
14
+ import React, { useCallback, useContext } from 'react';
15
+ import classNames from 'classnames';
16
+ import { ComponentTreeSlot } from '@teambit/component-tree';
17
+ import { mutedItalic } from '@teambit/design.ui.styles.muted-italic';
18
+ import { ellipsis } from '@teambit/design.ui.styles.ellipsis';
19
+ import { WorkspaceContext } from '../workspace/workspace-context';
20
+ import styles from './workspace-components-drawer.module.scss';
21
+
22
+ export class WorkspaceComponentsDrawer implements DrawerType {
23
+ constructor(private treeNodeSlot: ComponentTreeSlot) {}
24
+
25
+ name = 'COMPONENTS';
26
+
27
+ render = () => {
28
+ const workspace = useContext(WorkspaceContext);
29
+ const { treeNodeSlot } = this;
30
+
31
+ const TreeNodeRenderer = useCallback(
32
+ function TreeNode(props: TreeNodeProps<PayloadType>) {
33
+ const children = props.node.children;
34
+
35
+ if (!children) return <ComponentView {...props} treeNodeSlot={treeNodeSlot} />;
36
+
37
+ if (props.node.payload instanceof ScopePayload) return <ScopeTreeNode {...props} />;
38
+
39
+ return <NamespaceTreeNode {...props} />;
40
+ },
41
+ [treeNodeSlot]
42
+ );
43
+
44
+ if (!workspace) return <FullLoader />;
45
+ if (workspace.components.length === 0)
46
+ return <span className={classNames(mutedItalic, ellipsis, styles.emptyWorkspace)}>Workspace is empty</span>;
47
+ return <ComponentTree components={workspace.components} TreeNode={TreeNodeRenderer} />;
48
+ };
49
+ }
package/update.cmd.tsx ADDED
@@ -0,0 +1,30 @@
1
+ import { Command, CommandOptions } from '@teambit/cli';
2
+
3
+ import { Workspace } from './workspace';
4
+
5
+ type UpdateCmdOptions = {
6
+ yes?: boolean;
7
+ };
8
+
9
+ export default class UpdateCmd implements Command {
10
+ name = 'update';
11
+ description = 'update dependencies';
12
+ alias = 'up';
13
+ group = 'development';
14
+ shortDescription = '';
15
+ options = [['y', 'yes', 'automatically update all outdated packages']] as CommandOptions;
16
+
17
+ constructor(
18
+ /**
19
+ * workspace extension.
20
+ */
21
+ private workspace: Workspace
22
+ ) {}
23
+
24
+ async report(args: [string[]], options: UpdateCmdOptions) {
25
+ await this.workspace.updateDependencies({
26
+ all: options.yes === true,
27
+ });
28
+ return '';
29
+ }
30
+ }
@@ -0,0 +1,5 @@
1
+ export enum CheckTypes {
2
+ None, // keep this. it equals zero. this way we can do "if checkTypes() ... "
3
+ EntireProject,
4
+ ChangedFile,
5
+ }
@@ -0,0 +1,50 @@
1
+ import chalk from 'chalk';
2
+ import { OnComponentEventResult } from '../on-component-events';
3
+
4
+ const verboseComponentFilesArrayToString = (componentFiles) => {
5
+ return componentFiles.reduce((outputString, filePath) => `${outputString} \t - ${filePath}\n`, ``);
6
+ };
7
+
8
+ const resultsForExtensionArrayToString = (resultsForExtension, verbose) => {
9
+ return resultsForExtension.reduce(
10
+ (outputString, resultForExtension) =>
11
+ `${outputString}${chalk.green('√')}SUCCESS\t${resultForExtension.component}\n
12
+ ${verbose ? resultForExtension.componentFilesAsString : ''}\n`,
13
+ ''
14
+ );
15
+ };
16
+
17
+ export const formatWatchPathsSortByComponent = (trackDirs) => {
18
+ return Object.keys(trackDirs).reduce(
19
+ (outputString, watchPath) =>
20
+ `${outputString}
21
+ ${chalk.green('√')} SUCCESS\t${trackDirs[watchPath]}\n
22
+ \t - ${watchPath}\n\n`,
23
+ ` ${chalk.underline('STATUS\t\tCOMPONENT ID')}\n`
24
+ );
25
+ };
26
+
27
+ /**
28
+ * todo: this was implemented incorrectly.
29
+ * the original idea of `SerializableResults` was to have each one of the aspects registered to the slot, the
30
+ * ability to have their own formatting to their results, and then `toString()` method to print them.
31
+ * Here, the printing is specifically to the Compiler aspect. It should move to where it belongs.
32
+ */
33
+ export function formatCompileResults(compileResults: OnComponentEventResult[], verbose: boolean) {
34
+ if (!compileResults.length || !Array.isArray(compileResults)) return '';
35
+ return compileResults
36
+ .filter((compileResult) => compileResult.results?.results && Array.isArray(compileResult.results?.results))
37
+ .map((compileResult) => ({
38
+ extensionId: compileResult.extensionId,
39
+ resultsForExtension: compileResult.results?.results?.map((resultForExtension) => ({
40
+ component: resultForExtension.component,
41
+ componentFilesAsString: verboseComponentFilesArrayToString(resultForExtension.buildResults),
42
+ })),
43
+ }))
44
+ .reduce(
45
+ (outputString, compileResult) =>
46
+ `${outputString}
47
+ ${resultsForExtensionArrayToString(compileResult.resultsForExtension, verbose)}`,
48
+ ` ${chalk.underline('STATUS\tCOMPONENT ID')}`
49
+ );
50
+ }
@@ -0,0 +1,142 @@
1
+ import chalk from 'chalk';
2
+ import moment from 'moment';
3
+ import { Command, CommandOptions } from '@teambit/cli';
4
+ import type { Logger } from '@teambit/logger';
5
+ import type { BitBaseEvent, PubsubMain } from '@teambit/pubsub';
6
+
7
+ // import IDs and events
8
+ import { CompilerAspect, CompilerErrorEvent } from '@teambit/compiler';
9
+
10
+ import { Watcher, WatchOptions } from './watcher';
11
+ import { formatCompileResults, formatWatchPathsSortByComponent } from './output-formatter';
12
+ import { OnComponentEventResult } from '../on-component-events';
13
+ import { CheckTypes } from './check-types';
14
+
15
+ export type WatchCmdOpts = {
16
+ verbose?: boolean;
17
+ skipPreCompilation?: boolean;
18
+ checkTypes?: string | boolean;
19
+ };
20
+
21
+ export class WatchCommand implements Command {
22
+ msgs = {
23
+ onAll: (event, path) => this.logger.console(`Event: "${event}". Path: ${path}`),
24
+ onStart: () => {},
25
+ onReady: (workspace, watchPathsSortByComponent, verbose) => {
26
+ clearOutdatedData();
27
+ if (verbose) {
28
+ this.logger.console(formatWatchPathsSortByComponent(watchPathsSortByComponent));
29
+ }
30
+ this.logger.console(
31
+ chalk.yellow(
32
+ `Watching for component changes in workspace ${workspace.config.name} (${moment().format('HH:mm:ss')})...\n`
33
+ )
34
+ );
35
+ },
36
+ onChange: (filePath: string, buildResults: OnComponentEventResult[], verbose: boolean, duration) => {
37
+ clearOutdatedData();
38
+ if (!buildResults.length) {
39
+ this.logger.console(`The file ${filePath} has been changed, but nothing to compile.\n\n`);
40
+ return;
41
+ }
42
+ this.logger.console(`The file ${filePath} has been changed.\n\n`);
43
+ this.logger.console(formatCompileResults(buildResults, verbose));
44
+ this.logger.console(`Finished. (${duration}ms)`);
45
+ this.logger.console(chalk.yellow(`Watching for component changes (${moment().format('HH:mm:ss')})...`));
46
+ },
47
+ onAdd: (filePath: string, buildResults: OnComponentEventResult[], verbose: boolean, duration) => {
48
+ clearOutdatedData();
49
+ this.logger.console(`The file ${filePath} has been added.\n\n`);
50
+ this.logger.console(formatCompileResults(buildResults, verbose));
51
+ this.logger.console(`Finished. (${duration}ms)`);
52
+ this.logger.console(chalk.yellow(`Watching for component changes (${moment().format('HH:mm:ss')})...`));
53
+ },
54
+ onUnlink: (p) => {
55
+ this.logger.console(`file ${p} has been removed`);
56
+ },
57
+ onError: (err) => {
58
+ this.logger.console(`Watcher error ${err}`);
59
+ },
60
+ };
61
+
62
+ name = 'watch';
63
+ description = 'watch a set of components';
64
+ alias = '';
65
+ group = 'development';
66
+ shortDescription = '';
67
+ options = [
68
+ ['v', 'verbose', 'showing npm verbose output for inspection and prints stack trace'],
69
+ ['', 'skip-pre-compilation', 'skip the compilation step before starting to watch'],
70
+ [
71
+ 't',
72
+ 'check-types [string]',
73
+ 'EXPERIMENTAL. show errors/warnings for types. options are [file, project] to investigate only changed file or entire project. defaults to project',
74
+ ],
75
+ ] as CommandOptions;
76
+
77
+ constructor(
78
+ /**
79
+ * logger extension.
80
+ */
81
+ private pubsub: PubsubMain,
82
+
83
+ /**
84
+ * logger extension.
85
+ */
86
+ private logger: Logger,
87
+
88
+ /**
89
+ * watcher extension.
90
+ */
91
+ private watcher: Watcher
92
+ ) {
93
+ this.registerToEvents();
94
+ }
95
+
96
+ private registerToEvents() {
97
+ this.pubsub.sub(CompilerAspect.id, this.eventsListener);
98
+ }
99
+
100
+ private eventsListener = (event: BitBaseEvent<any>) => {
101
+ switch (event.type) {
102
+ case CompilerErrorEvent.TYPE:
103
+ this.logger.console(`Watcher error ${event.data.error}, 'error'`);
104
+ break;
105
+ default:
106
+ }
107
+ };
108
+
109
+ async report(cliArgs: [], watchCmdOpts: WatchCmdOpts) {
110
+ const { verbose, checkTypes } = watchCmdOpts;
111
+ const getCheckTypesEnum = () => {
112
+ switch (checkTypes) {
113
+ case undefined:
114
+ case false:
115
+ return CheckTypes.None;
116
+ case 'project':
117
+ case true: // project is the default
118
+ return CheckTypes.EntireProject;
119
+ case 'file':
120
+ return CheckTypes.ChangedFile;
121
+ default:
122
+ throw new Error(`check-types can be either "file" or "project". got "${checkTypes}"`);
123
+ }
124
+ };
125
+ const watchOpts: WatchOptions = {
126
+ msgs: this.msgs,
127
+ verbose,
128
+ preCompile: !watchCmdOpts.skipPreCompilation,
129
+ spawnTSServer: Boolean(checkTypes), // if check-types is enabled, it must spawn the tsserver.
130
+ checkTypes: getCheckTypesEnum(),
131
+ };
132
+ await this.watcher.watchAll(watchOpts);
133
+ return 'watcher terminated';
134
+ }
135
+ }
136
+
137
+ /**
138
+ * with console.clear() all history is deleted from the console. this function preserver the history.
139
+ */
140
+ function clearOutdatedData() {
141
+ process.stdout.write('\x1Bc');
142
+ }
@@ -0,0 +1,289 @@
1
+ import { PubsubMain } from '@teambit/pubsub';
2
+ import { dirname, sep } from 'path';
3
+ import { difference } from 'lodash';
4
+ import { ComponentID } from '@teambit/component';
5
+ import { BitId } from '@teambit/legacy-bit-id';
6
+ import loader from '@teambit/legacy/dist/cli/loader';
7
+ import { BIT_MAP, COMPONENT_ORIGINS } from '@teambit/legacy/dist/constants';
8
+ import { Consumer } from '@teambit/legacy/dist/consumer';
9
+ import logger from '@teambit/legacy/dist/logger/logger';
10
+ import { pathNormalizeToLinux } from '@teambit/legacy/dist/utils';
11
+ import mapSeries from 'p-map-series';
12
+ import chalk from 'chalk';
13
+ import { ChildProcess } from 'child_process';
14
+ import chokidar, { FSWatcher } from 'chokidar';
15
+ import ComponentMap from '@teambit/legacy/dist/consumer/bit-map/component-map';
16
+ import { PathLinux, PathOsBasedAbsolute } from '@teambit/legacy/dist/utils/path';
17
+ import { CompilationInitiator } from '@teambit/compiler';
18
+ import { WorkspaceAspect } from '../';
19
+ import { OnComponentChangeEvent, OnComponentAddEvent, OnComponentRemovedEvent } from '../events';
20
+ import { Workspace } from '../workspace';
21
+ import { OnComponentEventResult } from '../on-component-events';
22
+ import { CheckTypes } from './check-types';
23
+
24
+ export type WatcherProcessData = { watchProcess: ChildProcess; compilerId: BitId; componentIds: BitId[] };
25
+
26
+ export type WatchOptions = {
27
+ msgs?: any;
28
+ initiator?: CompilationInitiator;
29
+ verbose?: boolean; // print watch events to the console. (also ts-server events if spawnTSServer is true)
30
+ spawnTSServer?: boolean; // needed for check types and extract API/docs.
31
+ checkTypes?: CheckTypes; // if enabled, the spawnTSServer becomes true.
32
+ preCompile?: boolean; // whether compile all components before start watching
33
+ };
34
+
35
+ export class Watcher {
36
+ private fsWatcher: FSWatcher;
37
+ constructor(
38
+ private workspace: Workspace,
39
+ private pubsub: PubsubMain,
40
+ private trackDirs: { [dir: PathLinux]: ComponentID } = {},
41
+ private verbose = false,
42
+ private multipleWatchers: WatcherProcessData[] = []
43
+ ) {}
44
+
45
+ get consumer(): Consumer {
46
+ return this.workspace.consumer;
47
+ }
48
+
49
+ async watchAll(opts: WatchOptions) {
50
+ const { msgs, ...watchOpts } = opts;
51
+ // TODO: run build in the beginning of process (it's work like this in other envs)
52
+ const pathsToWatch = await this.getPathsToWatch();
53
+ const componentIds = Object.values(this.trackDirs);
54
+ await this.workspace.triggerOnPreWatch(componentIds, watchOpts);
55
+ await this.createWatcher(pathsToWatch);
56
+ const watcher = this.fsWatcher;
57
+ msgs?.onStart(this.workspace);
58
+
59
+ return new Promise((resolve, reject) => {
60
+ // prefix your command with "BIT_LOG=*" to see all watch events
61
+ if (process.env.BIT_LOG) {
62
+ if (msgs?.onAll) watcher.on('all', msgs?.onAll);
63
+ }
64
+ watcher.on('ready', () => {
65
+ msgs?.onReady(this.workspace, this.trackDirs, this.verbose);
66
+ });
67
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
68
+ watcher.on('change', async (filePath) => {
69
+ const startTime = new Date().getTime();
70
+ const buildResults = (await this.handleChange(filePath, opts?.initiator)) || [];
71
+ const duration = new Date().getTime() - startTime;
72
+ msgs?.onChange(filePath, buildResults, this.verbose, duration);
73
+ });
74
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
75
+ watcher.on('add', async (filePath) => {
76
+ const startTime = new Date().getTime();
77
+ const buildResults = (await this.handleChange(filePath)) || [];
78
+ const duration = new Date().getTime() - startTime;
79
+ msgs?.onAdd(filePath, buildResults, this.verbose, duration);
80
+ });
81
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
82
+ watcher.on('unlink', async (p) => {
83
+ msgs?.onUnlink(p);
84
+ await this.handleChange(p);
85
+ });
86
+ watcher.on('error', (err) => {
87
+ msgs?.onError(err);
88
+ reject(err);
89
+ });
90
+ });
91
+ }
92
+
93
+ private async handleChange(filePath: string, initiator?: CompilationInitiator): Promise<OnComponentEventResult[]> {
94
+ try {
95
+ if (filePath.endsWith(BIT_MAP)) {
96
+ const buildResults = await this.handleBitmapChanges();
97
+ this.completeWatch();
98
+ return buildResults;
99
+ }
100
+ const componentId = await this.getComponentIdAndClearItsCache(filePath);
101
+ if (!componentId) {
102
+ logger.debug(`file ${filePath} is not part of any component, ignoring it`);
103
+ return this.completeWatch();
104
+ }
105
+
106
+ const buildResults = await this.executeWatchOperationsOnComponent(componentId, [filePath], true, initiator);
107
+ this.completeWatch();
108
+ return buildResults;
109
+ } catch (err: any) {
110
+ const msg = `watcher found an error while handling ${filePath}`;
111
+ logger.error(msg, err);
112
+ logger.console(`${msg}, ${err.message}`);
113
+ return [];
114
+ }
115
+ }
116
+
117
+ /**
118
+ * if .bitmap changed, it's possible that a new component has been added. trigger onComponentAdd.
119
+ */
120
+ private async handleBitmapChanges(): Promise<OnComponentEventResult[]> {
121
+ const previewsTrackDirs = { ...this.trackDirs };
122
+ await this.workspace._reloadConsumer();
123
+ await this.setTrackDirs();
124
+ const newDirs: string[] = difference(Object.keys(this.trackDirs), Object.keys(previewsTrackDirs));
125
+ const removedDirs: string[] = difference(Object.keys(previewsTrackDirs), Object.keys(this.trackDirs));
126
+ const results: OnComponentEventResult[] = [];
127
+ if (newDirs.length) {
128
+ this.fsWatcher.add(newDirs);
129
+ const addResults = await mapSeries(newDirs, async (dir) =>
130
+ this.executeWatchOperationsOnComponent(this.trackDirs[dir], [], false)
131
+ );
132
+ results.push(...addResults.flat());
133
+ }
134
+ if (removedDirs.length) {
135
+ await this.fsWatcher.unwatch(removedDirs);
136
+ await mapSeries(removedDirs, (dir) => this.executeWatchOperationsOnRemove(previewsTrackDirs[dir]));
137
+ }
138
+ return results;
139
+ }
140
+
141
+ private async executeWatchOperationsOnRemove(componentId: ComponentID) {
142
+ logger.debug(`running OnComponentRemove hook for ${chalk.bold(componentId.toString())}`);
143
+ this.pubsub.pub(WorkspaceAspect.id, this.creatOnComponentRemovedEvent(componentId.toString()));
144
+ await this.workspace.triggerOnComponentRemove(componentId);
145
+ }
146
+
147
+ private async executeWatchOperationsOnComponent(
148
+ componentId: ComponentID,
149
+ files: string[],
150
+ isChange = true,
151
+ initiator?: CompilationInitiator
152
+ ): Promise<OnComponentEventResult[]> {
153
+ if (this.isComponentWatchedExternally(componentId)) {
154
+ // update capsule, once done, it automatically triggers the external watcher
155
+ await this.workspace.get(componentId);
156
+ return [];
157
+ }
158
+ const idStr = componentId.toString();
159
+
160
+ if (isChange) {
161
+ logger.debug(`running OnComponentChange hook for ${chalk.bold(idStr)}`);
162
+ this.pubsub.pub(WorkspaceAspect.id, this.creatOnComponentChangeEvent(idStr, 'OnComponentChange'));
163
+ } else {
164
+ logger.debug(`running OnComponentAdd hook for ${chalk.bold(idStr)}`);
165
+ this.pubsub.pub(WorkspaceAspect.id, this.creatOnComponentAddEvent(idStr, 'OnComponentAdd'));
166
+ }
167
+
168
+ let buildResults: OnComponentEventResult[];
169
+ try {
170
+ buildResults = isChange
171
+ ? await this.workspace.triggerOnComponentChange(componentId, files, initiator)
172
+ : await this.workspace.triggerOnComponentAdd(componentId);
173
+ } catch (err: any) {
174
+ // do not exit the watch process on errors, just print them
175
+ const msg = `found an issue during onComponentChange or onComponentAdd hooks`;
176
+ logger.error(msg, err);
177
+ logger.console(`\n${msg}: ${err.message || err}`);
178
+ return [];
179
+ }
180
+ if (buildResults && buildResults.length) {
181
+ return buildResults;
182
+ }
183
+ logger.console(`${idStr} doesn't have a compiler, nothing to build`);
184
+ return [];
185
+ }
186
+
187
+ private creatOnComponentRemovedEvent(idStr) {
188
+ return new OnComponentRemovedEvent(Date.now(), idStr);
189
+ }
190
+
191
+ private creatOnComponentChangeEvent(idStr, hook) {
192
+ return new OnComponentChangeEvent(Date.now(), idStr, hook);
193
+ }
194
+
195
+ private creatOnComponentAddEvent(idStr, hook) {
196
+ return new OnComponentAddEvent(Date.now(), idStr, hook);
197
+ }
198
+
199
+ private completeWatch() {
200
+ loader.stop();
201
+ return [];
202
+ }
203
+
204
+ private isComponentWatchedExternally(componentId: ComponentID) {
205
+ const watcherData = this.multipleWatchers.find((m) => m.componentIds.find((id) => id.isEqual(componentId._legacy)));
206
+ if (watcherData) {
207
+ logger.debug(`${componentId.toString()} is watched by ${watcherData.compilerId.toString()}`);
208
+ return true;
209
+ }
210
+ return false;
211
+ }
212
+
213
+ /**
214
+ * if a file was added/remove, once the component is loaded, it changes .bitmap, and then the
215
+ * entire cache is invalidated and the consumer is reloaded.
216
+ * when a file just changed, no need to reload the consumer, it is enough to just delete the
217
+ * component from the cache (both, workspace and consumer)
218
+ */
219
+ private async getComponentIdAndClearItsCache(filePath: string): Promise<ComponentID | null> {
220
+ const relativeFile = pathNormalizeToLinux(this.consumer.getPathRelativeToConsumer(filePath));
221
+ const trackDir = this.findTrackDirByFilePathRecursively(relativeFile);
222
+ if (!trackDir) {
223
+ // the file is not part of any component. If it was a new component, or a new file of
224
+ // existing component, then, handleBitmapChanges() should deal with it.
225
+ return null;
226
+ }
227
+ const componentId = this.trackDirs[trackDir];
228
+ this.workspace.clearComponentCache(componentId);
229
+ const component = await this.workspace.get(componentId);
230
+ const componentMap: ComponentMap = component.state._consumer.componentMap;
231
+ if (componentMap.getFilesRelativeToConsumer().find((p) => p === relativeFile)) {
232
+ return componentId;
233
+ }
234
+ // the file is inside the component dir but it's ignored. (e.g. it's in IGNORE_LIST)
235
+ return null;
236
+ }
237
+
238
+ private findTrackDirByFilePathRecursively(filePath: string): string | null {
239
+ if (this.trackDirs[filePath]) return filePath;
240
+ const parentDir = dirname(filePath);
241
+ if (parentDir === filePath) return null;
242
+ return this.findTrackDirByFilePathRecursively(parentDir);
243
+ }
244
+
245
+ private async createWatcher(pathsToWatch: string[]) {
246
+ this.fsWatcher = chokidar.watch(pathsToWatch, {
247
+ ignoreInitial: true,
248
+ // Using the function way since the regular way not working as expected
249
+ // It might be solved when upgrading to chokidar > 3.0.0
250
+ // See:
251
+ // https://github.com/paulmillr/chokidar/issues/773
252
+ // https://github.com/paulmillr/chokidar/issues/492
253
+ // https://github.com/paulmillr/chokidar/issues/724
254
+ ignored: (path) => {
255
+ // Ignore package.json temporarily since it cerates endless loop since it's re-written after each build
256
+ if (path.includes(`${sep}node_modules${sep}`) || path.includes(`${sep}package.json`)) {
257
+ return true;
258
+ }
259
+ return false;
260
+ },
261
+ persistent: true,
262
+ useFsEvents: false,
263
+ });
264
+ }
265
+
266
+ async setTrackDirs() {
267
+ this.trackDirs = {};
268
+ const componentsFromBitMap = this.consumer.bitMap.getAllComponents([
269
+ COMPONENT_ORIGINS.AUTHORED,
270
+ COMPONENT_ORIGINS.IMPORTED,
271
+ ]);
272
+ await Promise.all(
273
+ componentsFromBitMap.map(async (componentMap) => {
274
+ const bitId = componentMap.id;
275
+ const trackDir = componentMap.getTrackDir();
276
+ if (!trackDir) throw new Error(`${bitId.toString()} has no rootDir, which is invalid in Harmony`);
277
+ const componentId = await this.workspace.resolveComponentId(bitId);
278
+ this.trackDirs[trackDir] = componentId;
279
+ })
280
+ );
281
+ }
282
+
283
+ private async getPathsToWatch(): Promise<PathOsBasedAbsolute[]> {
284
+ await this.setTrackDirs();
285
+ const paths = [...Object.keys(this.trackDirs), BIT_MAP];
286
+ const pathsAbsolute = paths.map((dir) => this.consumer.toAbsolutePath(dir));
287
+ return pathsAbsolute;
288
+ }
289
+ }
@@ -0,0 +1,67 @@
1
+ import { ComponentStatus as LegacyComponentStatus } from '@teambit/legacy/dist/consumer/component-ops/component-status-loader';
2
+
3
+ export type ModifyInfo = {
4
+ hasModifiedFiles: boolean;
5
+ hasModifiedDependencies: boolean;
6
+ };
7
+
8
+ export class ComponentStatus {
9
+ constructor(
10
+ /**
11
+ * is the component modified.
12
+ */
13
+ readonly modifyInfo: ModifyInfo,
14
+
15
+ /**
16
+ * is the new component new.
17
+ */
18
+ readonly isNew: boolean,
19
+
20
+ /**
21
+ * is the component deleted from the workspace.
22
+ */
23
+ readonly isDeleted: boolean,
24
+
25
+ /**
26
+ * is the component staged.
27
+ */
28
+ readonly isStaged: boolean,
29
+
30
+ /**
31
+ * does the component exists in the workspace.
32
+ */
33
+ readonly isInWorkspace: boolean,
34
+
35
+ /**
36
+ * does the component exists in the scope.
37
+ */
38
+ readonly isInScope: boolean,
39
+
40
+ /**
41
+ * does the component is outdated (pending for update).
42
+ */
43
+ readonly isOutdated: boolean,
44
+
45
+ /**
46
+ * the component is not authored and not imported.
47
+ */
48
+ readonly nested?: boolean
49
+ ) {}
50
+
51
+ static fromLegacy(status: LegacyComponentStatus, hasModifiedDependencies: boolean, isOutdated: boolean) {
52
+ const modify: ModifyInfo = {
53
+ hasModifiedFiles: !!status.modified,
54
+ hasModifiedDependencies,
55
+ };
56
+ return new ComponentStatus(
57
+ modify,
58
+ !!status.newlyCreated,
59
+ !!status.deleted,
60
+ !!status.staged,
61
+ !status.notExist,
62
+ !status.missingFromScope,
63
+ isOutdated,
64
+ !!status.nested
65
+ );
66
+ }
67
+ }
@@ -0,0 +1,2 @@
1
+ export { WorkspaceComponent } from './workspace-component';
2
+ export { ComponentStatus } from './component-status';