Skip to content

Commit

Permalink
Merge pull request #2422 from dmichon-msft/dmichon/rework-deps
Browse files Browse the repository at this point in the history
[rush] Revise command line selection operations
  • Loading branch information
octogonz authored Feb 1, 2021
2 parents cb2e6f9 + 2605f50 commit 52d680e
Show file tree
Hide file tree
Showing 20 changed files with 883 additions and 256 deletions.
3 changes: 2 additions & 1 deletion apps/rush-lib/src/api/RushConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ export class RushConfiguration {
// Compute the downstream dependencies within the list of Rush projects.
this._populateDownstreamDependencies(project.packageJson.dependencies, project.packageName);
this._populateDownstreamDependencies(project.packageJson.devDependencies, project.packageName);
this._populateDownstreamDependencies(project.packageJson.optionalDependencies, project.packageName);
this._versionPolicyConfiguration.validate(this.projectsByName);
}
}
Expand Down Expand Up @@ -1668,7 +1669,7 @@ export class RushConfiguration {
const depProject: RushConfigurationProject | undefined = this.projectsByName.get(dependencyName);

if (depProject) {
depProject.downstreamDependencyProjects.push(packageName);
depProject._consumingProjectNames.add(packageName);
}
});
}
Expand Down
100 changes: 80 additions & 20 deletions apps/rush-lib/src/api/RushConfigurationProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { PackageJsonEditor } from './PackageJsonEditor';
import { RushConstants } from '../logic/RushConstants';
import { PackageNameParsers } from './PackageNameParsers';
import { DependencySpecifier, DependencySpecifierType } from '../logic/DependencySpecifier';
import { Selection } from '../logic/Selection';

/**
* This represents the JSON data object for a project entry in the rush.json configuration file.
Expand Down Expand Up @@ -54,10 +55,19 @@ export class RushConfigurationProject {
private _shouldPublish: boolean;
private _skipRushCheck: boolean;
private _publishFolder: string;
private _downstreamDependencyProjects: string[];
private _localDependencyProjects: ReadonlyArray<RushConfigurationProject> | undefined;
private _dependencyProjects: ReadonlySet<RushConfigurationProject> | undefined;
private _consumingProjects: ReadonlySet<RushConfigurationProject> | undefined;
private readonly _rushConfiguration: RushConfiguration;

/**
* A set of projects within the Rush configuration which directly consume this package.
*
* @remarks
* Writable because it is mutated by RushConfiguration during initialization.
* @internal
*/
public readonly _consumingProjectNames: Set<string>;

/** @internal */
public constructor(
projectJson: IRushConfigurationProjectJson,
Expand Down Expand Up @@ -144,7 +154,7 @@ export class RushConfigurationProject {
}
this._shouldPublish = !!projectJson.shouldPublish;
this._skipRushCheck = !!projectJson.skipRushCheck;
this._downstreamDependencyProjects = [];
this._consumingProjectNames = new Set();
this._versionPolicyName = projectJson.versionPolicyName;

this._publishFolder = this._projectFolder;
Expand Down Expand Up @@ -175,7 +185,7 @@ export class RushConfigurationProject {
/**
* The relative path of the folder that contains the project to be built by Rush.
*
* Example: `libraries\my-project`
* Example: `libraries/my-project`
*/
public get projectRelativeFolder(): string {
return this._projectRelativeFolder;
Expand Down Expand Up @@ -226,24 +236,54 @@ export class RushConfigurationProject {
}

/**
* A list of projects within the Rush configuration which directly depend on this package.
* An array of projects within the Rush configuration which directly depend on this package.
* @deprecated Use `consumingProjectNames` instead, as it has Set semantics, which better reflect the nature
* of the data.
*/
public get downstreamDependencyProjects(): string[] {
return this._downstreamDependencyProjects;
return [...this._consumingProjectNames];
}

/**
* A map of projects within the Rush configuration which are directly depended on by this project
* An array of projects within the Rush configuration which this project declares as dependencies.
* @deprecated Use `dependencyProjects` instead, as it has Set semantics, which better reflect the nature
* of the data.
*/
public get localDependencyProjects(): ReadonlyArray<RushConfigurationProject> {
if (!this._localDependencyProjects) {
this._localDependencyProjects = [
...this._getLocalDependencyProjects(this.packageJson.dependencies),
...this._getLocalDependencyProjects(this.packageJson.devDependencies),
...this._getLocalDependencyProjects(this.packageJson.optionalDependencies)
];
return [...this.dependencyProjects];
}

/**
* The set of projects within the Rush configuration which this project declares as dependencies.
*
* @remarks
* Can be used recursively to walk the project dependency graph to find all projects that are directly or indirectly
* referenced from this project.
*/
public get dependencyProjects(): ReadonlySet<RushConfigurationProject> {
if (!this._dependencyProjects) {
this._dependencyProjects = Selection.union(
this._getDependencyProjects(this.packageJson.dependencies),
this._getDependencyProjects(this.packageJson.devDependencies),
this._getDependencyProjects(this.packageJson.optionalDependencies)
);
}
return this._localDependencyProjects;
return this._dependencyProjects;
}

/**
* The set of projects within the Rush configuration which declare this project as a dependency.
* Excludes those that declare this project as a `cyclicDependencyProject`.
*
* @remarks
* This field is the counterpart to `dependencyProjects`, and can be used recursively to walk the project dependency
* graph to find all projects which will be impacted by changes to this project.
*/
public get consumingProjects(): ReadonlySet<RushConfigurationProject> {
if (!this._consumingProjects) {
this._consumingProjects = this._getConsumingProjects();
}
return this._consumingProjects;
}

/**
Expand Down Expand Up @@ -358,10 +398,14 @@ export class RushConfigurationProject {
return isMain;
}

private _getLocalDependencyProjects(
/**
* Compute the local rush projects that this project immediately depends on,
* according to the specific dependency group from package.json
*/
private _getDependencyProjects(
dependencies: IPackageJsonDependencyTable = {}
): RushConfigurationProject[] {
const localDependencyProjects: RushConfigurationProject[] = [];
): Set<RushConfigurationProject> {
const dependencyProjects: Set<RushConfigurationProject> = new Set();
for (const dependency of Object.keys(dependencies)) {
// Skip if we can't find the local project or it's a cyclic dependency
const localProject: RushConfigurationProject | undefined = this._rushConfiguration.getProjectByName(
Expand All @@ -377,15 +421,31 @@ export class RushConfigurationProject {
case DependencySpecifierType.Version:
case DependencySpecifierType.Range:
if (semver.satisfies(localProject.packageJson.version, dependencySpecifier.versionSpecifier)) {
localDependencyProjects.push(localProject);
dependencyProjects.add(localProject);
}
break;
case DependencySpecifierType.Workspace:
localDependencyProjects.push(localProject);
dependencyProjects.add(localProject);
break;
}
}
}
return localDependencyProjects;
return dependencyProjects;
}

/**
* Compute the local rush projects that declare this project as a dependency
*/
private _getConsumingProjects(): Set<RushConfigurationProject> {
const consumingProjects: Set<RushConfigurationProject> = new Set();
for (const projectName of this._consumingProjectNames) {
const localProject: RushConfigurationProject | undefined = this._rushConfiguration.getProjectByName(
projectName
);
if (localProject && localProject.dependencyProjects.has(this)) {
consumingProjects.add(localProject);
}
}
return consumingProjects;
}
}
24 changes: 12 additions & 12 deletions apps/rush-lib/src/cli/actions/BaseRushAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,11 @@ export abstract class BaseRushAction extends BaseConfiglessRushAction {
return super.onExecute();
}

protected mergeProjectsWithVersionPolicy(
projectsParameters: CommandLineStringListParameter,
versionPoliciesParameters: CommandLineStringListParameter
): RushConfigurationProject[] {
protected *evaluateProjectParameter(
projectsParameters: CommandLineStringListParameter
): Iterable<RushConfigurationProject> {
const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup();

const projects: RushConfigurationProject[] = [];
for (const projectParameter of projectsParameters.values) {
if (projectParameter === '.') {
const packageJson: IPackageJson | undefined = packageJsonLookup.tryLoadPackageJsonFor(process.cwd());
Expand All @@ -146,7 +144,7 @@ export abstract class BaseRushAction extends BaseConfiglessRushAction {
packageJson.name
);
if (project) {
projects.push(project);
yield project;
} else {
console.log(
colors.red(
Expand Down Expand Up @@ -174,21 +172,23 @@ export abstract class BaseRushAction extends BaseConfiglessRushAction {
throw new AlreadyReportedError();
}

projects.push(project);
yield project;
}
}
}

protected *evaluateVersionPolicyProjects(
versionPoliciesParameters: CommandLineStringListParameter
): Iterable<RushConfigurationProject> {
if (versionPoliciesParameters.values && versionPoliciesParameters.values.length > 0) {
this.rushConfiguration.projects.forEach((project) => {
for (const project of this.rushConfiguration.projects) {
const matches: boolean = versionPoliciesParameters.values.some((policyName) => {
return project.versionPolicyName === policyName;
});
if (matches) {
projects.push(project);
yield project;
}
});
}
}

return projects;
}
}
15 changes: 13 additions & 2 deletions apps/rush-lib/src/cli/actions/InstallAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { CommandLineStringListParameter } from '@rushstack/ts-command-line';
import { BaseInstallAction } from './BaseInstallAction';
import { IInstallManagerOptions } from '../../logic/base/BaseInstallManager';
import { RushCommandLineParser } from '../RushCommandLineParser';
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
import { Selection } from '../../logic/Selection';

export class InstallAction extends BaseInstallAction {
protected _toFlag!: CommandLineStringListParameter;
Expand Down Expand Up @@ -73,6 +75,15 @@ export class InstallAction extends BaseInstallAction {
}

protected buildInstallOptions(): IInstallManagerOptions {
const toProjects: Set<RushConfigurationProject> = Selection.union<RushConfigurationProject>(
this.evaluateProjectParameter(this._toFlag),
this.evaluateVersionPolicyProjects(this._toVersionPolicy)
);
const fromProjects: Set<RushConfigurationProject> = Selection.union<RushConfigurationProject>(
this.evaluateProjectParameter(this._fromFlag),
this.evaluateVersionPolicyProjects(this._fromVersionPolicy)
);

return {
debug: this.parser.isDebug,
allowShrinkwrapUpdates: false,
Expand All @@ -86,8 +97,8 @@ export class InstallAction extends BaseInstallAction {
// Because the 'defaultValue' option on the _maxInstallAttempts parameter is set,
// it is safe to assume that the value is not null
maxInstallAttempts: this._maxInstallAttempts.value!,
toProjects: this.mergeProjectsWithVersionPolicy(this._toFlag, this._toVersionPolicy),
fromProjects: this.mergeProjectsWithVersionPolicy(this._fromFlag, this._fromVersionPolicy)
toProjects,
fromProjects
};
}
}
4 changes: 2 additions & 2 deletions apps/rush-lib/src/cli/actions/UpdateAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ export class UpdateAction extends BaseInstallAction {
// Because the 'defaultValue' option on the _maxInstallAttempts parameter is set,
// it is safe to assume that the value is not null
maxInstallAttempts: this._maxInstallAttempts.value!,
toProjects: [],
fromProjects: []
toProjects: new Set(),
fromProjects: new Set()
};
}
}
Loading

0 comments on commit 52d680e

Please sign in to comment.