Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduced a new component-issue "DeprecatedDependencies" #8854

Merged
merged 2 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions components/component-issues/deprecated-dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ComponentIssue, ISSUE_FORMAT_SPACE } from './component-issue';

export class DeprecatedDependencies extends ComponentIssue {
description = 'dependencies used in the code were deprecated';
solution = 'replace them with alternative dependencies. or remove them if not needed anymore';
data: string[]; // deps ids
isTagBlocker = false;
dataToString() {
return ISSUE_FORMAT_SPACE + this.data.join(', ');
}
}
6 changes: 6 additions & 0 deletions components/component-issues/issues-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ExternalEnvWithoutVersion } from './external-env-without-version';
import { RemovedDependencies } from './removed-dependencies';
import { SelfReference } from './self-reference';
import { ImportFromDirectory } from './import-from-directory';
import { DeprecatedDependencies } from './deprecated-dependencies';

export const IssuesClasses = {
MissingPackagesDependenciesOnFs,
Expand All @@ -41,6 +42,7 @@ export const IssuesClasses = {
NonLoadedEnv,
ExternalEnvWithoutVersion,
RemovedDependencies,
DeprecatedDependencies,
SelfReference,
ImportFromDirectory,
};
Expand Down Expand Up @@ -92,6 +94,10 @@ export class IssuesList {
this._issues = this._issues.filter((issue) => issue.constructor.name !== IssueClass.name);
}

hasTagBlockerIssues(): boolean {
return this._issues.some((issue) => issue.isTagBlocker);
}

getIssue<T extends ComponentIssue>(IssueClass: { new (): T }): T | undefined {
// don't use instanceof, e.g. `this._issues.find((issue) => issue instanceof IssueClass)`
// the "component-issues" package can come from different sources, so the "instanceof" won't work.
Expand Down
14 changes: 14 additions & 0 deletions e2e/harmony/deprecate.e2e.1.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { Extensions } from '../../src/constants';
import Helper from '../../src/e2e-helper/e2e-helper';
import { IssuesClasses } from '@teambit/component-issues';

describe('bit deprecate and undeprecate commands', function () {
this.timeout(0);
Expand Down Expand Up @@ -198,4 +199,17 @@ describe('bit deprecate and undeprecate commands', function () {
expect(deprecationData.isDeprecate).to.be.false;
});
});
describe('using deprecated components', () => {
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopes();
helper.fixtures.populateComponents(2);
helper.command.tagAllWithoutBuild();
helper.command.deprecateComponent('comp2');
helper.command.tagAllWithoutBuild();
helper.command.export();
});
it('bit status should show the DeprecatedDependencies component-issue', () => {
helper.command.expectStatusToHaveIssue(IssuesClasses.DeprecatedDependencies.name);
});
});
});
63 changes: 57 additions & 6 deletions scopes/component/deprecation/deprecation.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import { deprecationSchema } from './deprecation.graphql';
import { DeprecationFragment } from './deprecation.fragment';
import { DeprecateCmd } from './deprecate-cmd';
import { UndeprecateCmd } from './undeprecate-cmd';
import IssuesAspect, { IssuesMain } from '@teambit/issues';
import pMapSeries from 'p-map-series';
import DependencyResolverAspect, { DependencyResolverMain } from '@teambit/dependency-resolver';
import { compact } from 'lodash';
import { IssuesClasses } from '@teambit/component-issues';

export type DeprecationInfo = {
isDeprecate: boolean;
Expand All @@ -33,9 +38,7 @@ export type DeprecationMetadata = {
};

export class DeprecationMain {
constructor(private scope: ScopeMain, private workspace: Workspace) {}
static runtime = MainRuntime;
static dependencies = [GraphqlAspect, ScopeAspect, ComponentAspect, WorkspaceAspect, CLIAspect];
constructor(private scope: ScopeMain, private workspace: Workspace, private depsResolver: DependencyResolverMain) {}

async getDeprecationInfo(component: Component): Promise<DeprecationInfo> {
const headComponent = await this.getHeadComponent(component);
Expand Down Expand Up @@ -100,14 +103,62 @@ export class DeprecationMain {
return results;
}

static async provider([graphql, scope, componentAspect, workspace, cli]: [
async addDeprecatedDependenciesIssues(components: Component[]) {
await pMapSeries(components, async (component) => {
await this.addDeprecatedDepIssue(component);
});
}

private async addDeprecatedDepIssue(component: Component) {
const dependencies = this.depsResolver.getComponentDependencies(component);
const removedWithUndefined = await Promise.all(
dependencies.map(async (dep) => {
const isRemoved = await this.isDeprecatedByIdWithoutLoadingComponent(dep.componentId);
if (isRemoved) return dep.componentId;
return undefined;
})
);
const removed = compact(removedWithUndefined).map((id) => id.toString());
if (removed.length) {
component.state.issues.getOrCreate(IssuesClasses.DeprecatedDependencies).data = removed;
}
}

/**
* performant version of isDeprecated() in case the component object is not available and loading it is expensive.
*/
private async isDeprecatedByIdWithoutLoadingComponent(componentId: ComponentID): Promise<boolean> {
if (!componentId.hasVersion()) return false;
const modelComp = await this.workspace.scope.getBitObjectModelComponent(componentId);
if (!modelComp) return false;
const isDeprecated = await modelComp.isDeprecated(
this.workspace.scope.legacyScope.objects,
componentId.version as string
);
return Boolean(isDeprecated);
}

static runtime = MainRuntime;
static dependencies = [
GraphqlAspect,
ScopeAspect,
ComponentAspect,
WorkspaceAspect,
CLIAspect,
DependencyResolverAspect,
IssuesAspect,
];
static async provider([graphql, scope, componentAspect, workspace, cli, depsResolver, issues]: [
GraphqlMain,
ScopeMain,
ComponentMain,
Workspace,
CLIMain
CLIMain,
DependencyResolverMain,
IssuesMain
]) {
const deprecation = new DeprecationMain(scope, workspace);
const deprecation = new DeprecationMain(scope, workspace, depsResolver);
issues.registerAddComponentsIssues(deprecation.addDeprecatedDependenciesIssues.bind(deprecation));
cli.register(new DeprecateCmd(deprecation, workspace), new UndeprecateCmd(deprecation, workspace));
componentAspect.registerShowFragments([new DeprecationFragment(deprecation)]);
graphql.register(deprecationSchema(deprecation));
Expand Down
2 changes: 1 addition & 1 deletion scopes/component/remove/remove.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ ${mainComps.map((c) => c.id.toString()).join('\n')}`);
}

private async addRemovedDepIssue(component: Component) {
const dependencies = await this.depResolver.getComponentDependencies(component);
const dependencies = this.depResolver.getComponentDependencies(component);
const removedWithUndefined = await Promise.all(
dependencies.map(async (dep) => {
const isRemoved = await this.isRemovedByIdWithoutLoadingComponent(dep.componentId);
Expand Down
8 changes: 6 additions & 2 deletions scopes/component/status/status-cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getInvalidComponentLabel } from '@teambit/legacy/dist/cli/templates/com
import {
IMPORT_PENDING_MSG,
statusFailureMsg,
statusWarningsMsg,
statusInvalidComponentsMsg,
statusWorkspaceIsCleanMsg,
BASE_DOCS_DOMAIN,
Expand Down Expand Up @@ -169,7 +170,9 @@ export class StatusCmd implements Command {
const isSoftTagged = Boolean(softTaggedComponents.find((softTaggedId) => softTaggedId.isEqual(id)));
const getStatusText = () => {
if (message) return message;
if (idWithIssues) return statusFailureMsg;
if (idWithIssues) {
return idWithIssues.issues.hasTagBlockerIssues() ? statusFailureMsg : statusWarningsMsg;
}
return 'ok';
};
const getColor = () => {
Expand Down Expand Up @@ -200,7 +203,8 @@ export class StatusCmd implements Command {
idFormatted += ' ... ';
if (showIssues && idWithIssues) {
showTroubleshootingLink = true;
return `${idFormatted} ${chalk.red(statusFailureMsg)}${formatIssues(idWithIssues.issues)}`;
const issuesTxt = idWithIssues.issues.hasTagBlockerIssues() ? statusFailureMsg : statusWarningsMsg;
return `${idFormatted} ${chalk.red(issuesTxt)}${formatIssues(idWithIssues.issues)}`;
}
return `${idFormatted}${messageStatus}`;
}
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ export const PREVIOUS_DEFAULT_LANE = 'master';

export const statusInvalidComponentsMsg = 'invalid components';
export const statusFailureMsg = 'issues found';
export const statusWarningsMsg = 'warnings found';
export const statusWorkspaceIsCleanMsg =
'nothing to tag or export (use "bit create <template> <component>" to generate a new component)';

Expand Down