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

[rush] Introduce a "rush add" command #843

Merged
merged 32 commits into from
Sep 28, 2018
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0f4ae68
introduce the new command - incomplete
nickpape-msft Sep 21, 2018
7260861
API Change
nickpape-msft Sep 24, 2018
60f2fa9
Fill out most of the rest of the logic for the add command
nickpape-msft Sep 24, 2018
0ae6ccf
Fix lint issues
nickpape-msft Sep 24, 2018
a66230d
Some small change4s
nickpape-msft Sep 25, 2018
9c6f76f
Small changes
nickpape-msft Sep 25, 2018
30df582
Move add code into a seperate class
nickpape-msft Sep 26, 2018
caf6983
Fix bugs
nickpape-msft Sep 26, 2018
0a8a922
Add logging
nickpape-msft Sep 26, 2018
f094bb8
Merge branch 'master' into nickpape/rush-add
nickpape-msft Sep 26, 2018
8add64f
Fix wording
nickpape-msft Sep 26, 2018
8177fec
Merge branch 'master' into nickpape/rush-add
nickpape-msft Sep 26, 2018
bc43560
PR Feedback
nickpape-msft Sep 26, 2018
56a6ab2
Refactor
nickpape-msft Sep 27, 2018
ee299b1
Random comments
nickpape-msft Sep 28, 2018
1c8e54a
Some PR feedback
nickpape-msft Sep 28, 2018
abfabf2
PR Feedback
nickpape-msft Sep 28, 2018
9d66f66
More feedback
nickpape-msft Sep 28, 2018
14e448f
More feedback
nickpape-msft Sep 28, 2018
6e62fdc
Snapshot
nickpape-msft Sep 28, 2018
836c9a2
Updating strings
pgonzal Sep 28, 2018
2909608
Merge branch 'nickpape/rush-add' of https://github.com/Microsoft/web-…
pgonzal Sep 28, 2018
e0793d0
Add change log
pgonzal Sep 28, 2018
ddf3e0b
Some fixes
nickpape-msft Sep 28, 2018
d48c7eb
Fix small bug
nickpape-msft Sep 28, 2018
2d81c46
Merge branch 'nickpape/rush-add' of https://github.com/Microsoft/web-…
nickpape-msft Sep 28, 2018
128da2f
Minor changes
nickpape-msft Sep 28, 2018
daf87a2
more bugs
nickpape-msft Sep 28, 2018
91b5592
small bug
nickpape-msft Sep 28, 2018
a115ad5
remove debugging:
nickpape-msft Sep 28, 2018
2ac1044
Missing changefile
nickpape-msft Sep 28, 2018
612e9bf
Merge branch 'master' into nickpape/rush-add
nickpape-msft Sep 28, 2018
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
219 changes: 219 additions & 0 deletions apps/rush-lib/src/api/PackageJsonEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import * as semver from 'semver';
Copy link
Collaborator

@octogonz octogonz Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import [](start = 0, length = 7)

copyright banner #Resolved


import {
IPackageJson,
JsonFile
} from '@microsoft/node-core-library';

export const enum DependencyType {
Dependency = 'dependency',
DevDependency = 'devDependency',
OptionalDependency = 'optionalDependency',
PeerOnly = 'peerDependency'
Copy link
Collaborator

@octogonz octogonz Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normal, dev, optional, peerOnly?

If it's DependencyType.peerOnly #Resolved

}

export class Dependency {
Copy link
Collaborator

@octogonz octogonz Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependency [](start = 13, length = 10)

PackageJsonDependency? #Resolved

private _type: DependencyType;
private _name: string;
private _version: string | undefined;
private _peerVersion: string | undefined;
private _onChange: () => void;

public constructor(name: string,
version: string | undefined,
type: DependencyType,
peerVersion: string | undefined,
onChange: () => void) {
this._name = name;
this._version = version;
this._type = type;
this._peerVersion = peerVersion;
this._onChange = onChange;

if (this._version && this._type === DependencyType.PeerOnly) {
throw new Error(`Cannot specify a primary version if the dependency type is peer-only.`);
}
if (!this._peerVersion && this._type === DependencyType.PeerOnly) {
throw new Error(`Must specify a peer version if the dependency type if peer-only.`);
}
}

public get name(): string {
return this._name;
}

public get version(): string | undefined {
return this._version;
}

public setVersion(newVersion: string): void {
if (!semver.valid(newVersion) && !semver.validRange(newVersion)) {
throw new Error(`Cannot set version to invalid value: "${newVersion}"`);
}
this._version = newVersion;
this._onChange();
}

public get dependencyType(): DependencyType {
return this._type;
}

public setDependencyType(newType: DependencyType): void {
this._type = newType;
this._onChange();
}

public get peerVersion(): string | undefined {
return this._peerVersion;
}
}

export class PackageJsonEditor {
private readonly _filepath: string;
private readonly _data: IPackageJson;
private readonly _dependencies: Map<string, Dependency>;

private _onChange: () => void;
private _modified: boolean;

public static load(filepath: string): PackageJsonEditor {
return new PackageJsonEditor(filepath, JsonFile.load(filepath));
}

public static fromObject(object: IPackageJson, filename: string): PackageJsonEditor {
return new PackageJsonEditor(filename, object);
}

public get name(): string {
return this._data.name;
}

public get version(): string {
return this._data.version;
}

public get filePath(): string {
return this._filepath;
}

public getDependency(packageName: string): Dependency | undefined {
return this._dependencies.get(packageName);
}

public forEachDependency(cb: (dependency: Dependency) => void): void {
this._dependencies.forEach(cb);
}

public addOrUpdateDependency(packageName: string, newVersion: string, dependencyType: DependencyType): void {
if (this._dependencies.has(packageName)) {
const dependency: Dependency = this._dependencies.get(packageName)!;
dependency.setVersion(newVersion);
dependency.setDependencyType(dependencyType);
} else {
const dependency: Dependency
= new Dependency(packageName, newVersion, dependencyType, undefined, this._onChange);
this._dependencies.set(packageName, dependency);
}
}

public saveIfModified(): boolean {
if (this._modified) {
JsonFile.save(this._normalize(), this._filepath);
this._modified = false;
return true;
}
return false;
}

private constructor(filepath: string, data: IPackageJson) {
this._filepath = filepath;
this._data = data;

this._dependencies = new Map<string, Dependency>();

const dependencies: { [key: string]: string } = data.dependencies || {};
const devDependencies: { [key: string]: string } = data.devDependencies || {};
const optionalDependencies: { [key: string]: string } = data.optionalDependencies || {};
const peerDependencies: { [key: string]: string } = data.peerDependencies || {};

this._onChange = () => {
this._modified = true;
};

Object.keys(dependencies || {}).forEach((dependency: string) => {
if (devDependencies[dependency]) {
throw new Error(`The package "${dependency}" is listed as both a dev and a regular dependency`);
}
if (optionalDependencies[dependency]) {
throw new Error(`The package "${dependency}" is listed as both a dev and a regular dependency`);
}

this._dependencies.set(dependency, new Dependency(dependency, dependencies[dependency],
DependencyType.Dependency, peerDependencies[dependency], this._onChange));
});

Object.keys(devDependencies || {}).forEach((dependency: string) => {
if (optionalDependencies[dependency]) {
throw new Error(`The package "${dependency}" is listed as both a dev and an optional dependency`);
}

this._dependencies.set(dependency, new Dependency(dependency, devDependencies[dependency],
DependencyType.Dependency, peerDependencies[dependency], this._onChange));
});

Object.keys(optionalDependencies || {}).forEach((dependency: string) => {
this._dependencies.set(dependency, new Dependency(dependency, optionalDependencies[dependency],
DependencyType.OptionalDependency, peerDependencies[dependency], this._onChange));
});

Object.keys(peerDependencies || {}).forEach((dependency: string) => {
if (!this._dependencies.has(dependency)) {
this._dependencies.set(dependency, new Dependency(dependency, undefined,
DependencyType.PeerOnly, peerDependencies[dependency], this._onChange));
}
});
}

private _normalize(): IPackageJson {
delete this._data.dependencies;
delete this._data.devDependencies;
delete this._data.peerDependencies;
delete this._data.optionalDependencies;

const keys: Array<string> = [...this._dependencies.keys()].sort();

for (const packageName of keys) {
const dependency: Dependency = this._dependencies.get(packageName)!;

if (dependency.dependencyType === DependencyType.Dependency) {
Copy link
Collaborator

@octogonz octogonz Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(dependency.dependencyType [](start = 9, length = 26)

switch? #WontFix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh


In reply to: 221123902 [](ancestors = 221123902)

if (!this._data.dependencies) {
this._data.dependencies = {};
}
this._data.dependencies[dependency.name] = dependency.version!;
}

if (dependency.dependencyType === DependencyType.DevDependency) {
if (!this._data.devDependencies) {
this._data.devDependencies = {};
}
this._data.devDependencies[dependency.name] = dependency.version!;
}

if (dependency.dependencyType === DependencyType.OptionalDependency) {
if (!this._data.optionalDependencies) {
this._data.optionalDependencies = {};
}
this._data.optionalDependencies[dependency.name] = dependency.version!;
}

if (dependency.peerVersion) {
if (!this._data.peerDependencies) {
this._data.peerDependencies = {};
}
this._data.peerDependencies[dependency.name] = dependency.peerVersion;
}
}

return this._data;
}
}
22 changes: 21 additions & 1 deletion apps/rush-lib/src/api/RushConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
import * as path from 'path';
import * as fs from 'fs';
import * as semver from 'semver';
import { JsonFile, JsonSchema, PackageName, FileSystem } from '@microsoft/node-core-library';
import {
JsonFile,
JsonSchema,
Path,
PackageName,
FileSystem
} from '@microsoft/node-core-library';

import { Rush } from '../api/Rush';
import { RushConfigurationProject, IRushConfigurationProjectJson } from './RushConfigurationProject';
Expand Down Expand Up @@ -743,6 +749,20 @@ export class RushConfiguration {
return this._versionPolicyConfiguration;
}

/**
* Returns the project for which the specified path is underneath that project's folder.
* If the path is not under any project's folder, returns undefined.
*/
public getProjectForPath(currentFolderPath: string): RushConfigurationProject | undefined {
Copy link
Collaborator

@octogonz octogonz Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tryGet? #Resolved

const resolvedPath: string = path.resolve(currentFolderPath);
for (const project of this.projects) {
if (Path.isUnder(project.projectFolder, resolvedPath)) {
return project;
}
}
return undefined;
}

/**
* Use RushConfiguration.loadFromConfigurationFile() or Use RushConfiguration.loadFromDefaultLocation()
* instead.
Expand Down
13 changes: 13 additions & 0 deletions apps/rush-lib/src/api/RushConfigurationProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {

import { RushConfiguration } from '../api/RushConfiguration';
import { VersionPolicy, LockStepVersionPolicy } from './VersionPolicy';
import { PackageJsonEditor } from './PackageJsonEditor';

/**
* This represents the JSON data object for a project entry in the rush.json configuration file.
Expand All @@ -37,6 +38,7 @@ export class RushConfigurationProject {
private _projectRelativeFolder: string;
private _reviewCategory: string;
private _packageJson: IPackageJson;
private _packageJsonEditor: PackageJsonEditor;
private _tempProjectName: string;
private _unscopedTempProjectName: string;
private _cyclicDependencyProjects: Set<string>;
Expand Down Expand Up @@ -97,6 +99,8 @@ export class RushConfigurationProject {
+ ` match the name "${this._packageJson.name}" from package.json`);
}

this._packageJsonEditor = PackageJsonEditor.load(packageJsonFilename);

this._tempProjectName = tempProjectName;

// The "rushProject.tempProjectName" is guaranteed to be unique name (e.g. by adding the "-2"
Expand Down Expand Up @@ -172,11 +176,20 @@ export class RushConfigurationProject {

/**
* The parsed NPM "package.json" file from projectFolder.
* Will be deprecated soon in favor of packageJsonEditor
Copy link
Collaborator

@octogonz octogonz Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will [](start = 5, length = 4)

@deprecated Use packageJsonEditor instead #Resolved

*/
public get packageJson(): IPackageJson {
return this._packageJson;
}

/**
* A useful wrapper around the package.json file for making modifications
* @alpha
Copy link
Collaborator

@octogonz octogonz Sep 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@beta -- we don't use alpha in WBT #Resolved

*/
public get packageJsonEditor(): PackageJsonEditor {
return this._packageJsonEditor;
}

/**
* The unique name for the temporary project that will be generated in the Common folder.
* For example, if the project name is "@scope/MyProject", the temporary project name
Expand Down
Loading