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] Support PNPM workspaces #1938

Merged
merged 39 commits into from
Jul 3, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d44931f
Move to installManager
D4N14L Jun 15, 2020
65c1c44
Add initial implementation of workspaces
D4N14L Jun 17, 2020
824c01d
Merge branch 'master' of https://github.com/D4N14L/web-build-tools in…
D4N14L Jun 17, 2020
e6d78e7
Rush update
D4N14L Jun 17, 2020
cadb3f6
Rush change
D4N14L Jun 17, 2020
7acf1ed
Lint
D4N14L Jun 17, 2020
f35d08b
Add new test files
D4N14L Jun 19, 2020
5b49dd8
Add 'rush add' support for workspace projects
D4N14L Jun 19, 2020
078d7b0
Add workspaces versioning support and tests
D4N14L Jun 19, 2020
f73c79d
Make pnpmfile shim workspace-only
D4N14L Jun 24, 2020
85885a7
Allow exact ranges to be used for workspace range conversion
D4N14L Jun 24, 2020
debf80f
Add repo-state.json to be used by workspaces to validate common versions
D4N14L Jun 24, 2020
2301d3b
Merge branch 'master' of https://github.com/D4N14L/web-build-tools in…
D4N14L Jun 24, 2020
9b738c8
Rush update
D4N14L Jun 24, 2020
e841136
Small modification to avoid work when using DeployManager
D4N14L Jun 24, 2020
48bcca5
Fix changes when workspace dependencies should be tracked
D4N14L Jun 25, 2020
b0984b7
Merge branch 'master' of https://github.com/microsoft/rushstack into …
D4N14L Jun 27, 2020
6ce1e86
Rush update
D4N14L Jun 27, 2020
d8a7b2c
PR feedback
D4N14L Jun 27, 2020
f7f7605
Updated snapshot
D4N14L Jun 29, 2020
8b43f8e
Fix local project references for RushInstallManager
D4N14L Jun 29, 2020
495699a
Add support for reverting workspace notation when going back to legac…
D4N14L Jun 29, 2020
3ea3fbd
Fix per-project shrinkwrap when encountering duplicate dev dependencies
D4N14L Jun 29, 2020
3fad71e
Use repo-state.json for shrinkwrap hash validation
D4N14L Jun 30, 2020
a886fb1
PR feedback
D4N14L Jun 30, 2020
e94bfa1
Fix RepoStateFile serialization error
D4N14L Jun 30, 2020
c83abf7
Add "workspaces" property to the last-install.flag file
D4N14L Jul 1, 2020
5e92471
Merge branch 'master' of https://github.com/microsoft/rushstack into …
D4N14L Jul 1, 2020
952b0bc
Add SpecifierType enum
D4N14L Jul 3, 2020
46a7d3d
PR feedback
D4N14L Jul 3, 2020
58eb273
Remove 'generated' comment from checked in file
D4N14L Jul 3, 2020
cb7aad3
Merge branch 'master' of https://github.com/microsoft/rushstack into …
D4N14L Jul 3, 2020
c225f18
Bump semver
D4N14L Jul 3, 2020
e6da401
rush change
D4N14L Jul 3, 2020
3d9ec44
Merge branch 'master' of https://github.com/microsoft/rushstack into …
D4N14L Jul 3, 2020
2aaf287
PR feedback
D4N14L Jul 3, 2020
5e3c3ea
Move dynamic data to pnpmfileSettings.json
D4N14L Jul 3, 2020
1771839
Upgrade semver types to match package version.
iclanton Jul 3, 2020
0e8d05a
Improve typesafety in PnpmfileShim.
iclanton Jul 3, 2020
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
108 changes: 86 additions & 22 deletions apps/rush-lib/src/logic/DependencySpecifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,56 @@
import npmPackageArg = require('npm-package-arg');
import { InternalError } from '@rushstack/node-core-library';

/**
* The parsed format of a provided version specifier.
*/
export const enum SpecifierType {
D4N14L marked this conversation as resolved.
Show resolved Hide resolved
/**
* A git repository
*/
Git = 'Git',

/**
* A tagged version, e.g. "example@latest"
*/
Tag = 'Tag',

/**
* A specific version number, e.g. "example@1.2.3"
*/
Version = 'Version',

/**
* A version range, e.g. "example@2.x"
*/
Range = 'Range',

/**
* A local .tar.gz, .tar or .tgz file
*/
File = 'File',

/**
* A local directory
*/
Directory = 'Directory',

/**
* An HTTP url to a .tar.gz, .tar or .tgz file
*/
Remote = 'Remote',

/**
* A package alias, e.g. "npm:other-package@^1.2.3"
*/
Alias = 'Alias',

/**
* A package specified using workspace protocol, e.g. "workspace:^1.2.3"
*/
Workspace = 'Workspace'
}

/**
* An NPM "version specifier" is a string that can appear as a package.json "dependencies" value.
* Example version specifiers: `^1.2.3`, `file:./blah.tgz`, `npm:other-package@~1.2.3`, and so forth.
Expand All @@ -22,19 +72,9 @@ export class DependencySpecifier {
public readonly versionSpecifier: string;

/**
* The type of `versionSpecifier`:
*
* git - a git repository
* tag - a tagged version, e.g. "example@latest"
* version - A specific version number, e.g. "example@1.2.3"
* range - A version range, e.g. "example@2.x"
* file - A local .tar.gz, .tar or .tgz file
* directory - A local directory
* remote - An HTTP url to a .tar.gz, .tar or .tgz file
* alias - A package alias such as "npm:other-package@^1.2.3"
* workspace - A package specified using workspace protocol such as "workspace:^1.2.3"
* The type of the `versionSpecifier`.
*/
public readonly specifierType: string;
public readonly specifierType: SpecifierType;

/**
* If `specifierType` is `alias`, then this is the parsed target dependency.
Expand All @@ -50,24 +90,48 @@ export class DependencySpecifier {
// Workspace ranges are a feature from PNPM and Yarn. Set the version specifier
// to the trimmed version range.
if (versionSpecifier.startsWith('workspace:')) {
this.specifierType = 'workspace';
this.specifierType = SpecifierType.Workspace;
this.versionSpecifier = versionSpecifier.slice(this.specifierType.length + 1).trim();
this.aliasTarget = undefined;
return;
}

const result: npmPackageArg.AliasResult = npmPackageArg.resolve(
packageName,
versionSpecifier
) as npmPackageArg.AliasResult;

this.specifierType = result.type;
const result: npmPackageArg.Result = npmPackageArg.resolve(packageName, versionSpecifier);
switch (result.type) {
case 'git':
this.specifierType = SpecifierType.Git;
break;
case 'tag':
this.specifierType = SpecifierType.Tag;
break;
case 'version':
this.specifierType = SpecifierType.Version;
break;
case 'range':
this.specifierType = SpecifierType.Range;
break;
case 'file':
this.specifierType = SpecifierType.File;
break;
case 'directory':
this.specifierType = SpecifierType.Directory;
break;
case 'remote':
this.specifierType = SpecifierType.Remote;
break;
case 'alias':
this.specifierType = SpecifierType.Alias;
break;
default:
throw new InternalError(`Unexpected npm-package-arg result type "${result.type}"`);
}
D4N14L marked this conversation as resolved.
Show resolved Hide resolved

if (result.type === 'alias') {
if (!result.subSpec || !result.subSpec.name) {
if (this.specifierType === SpecifierType.Alias) {
const aliasResult: npmPackageArg.AliasResult = result as npmPackageArg.AliasResult;
if (!aliasResult.subSpec || !aliasResult.subSpec.name) {
throw new InternalError('Unexpected result from npm-package-arg');
}
this.aliasTarget = new DependencySpecifier(result.subSpec.name, result.subSpec.rawSpec);
this.aliasTarget = new DependencySpecifier(aliasResult.subSpec.name, aliasResult.subSpec.rawSpec);
} else {
this.aliasTarget = undefined;
}
Expand Down
13 changes: 8 additions & 5 deletions apps/rush-lib/src/logic/PublishUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { execSync } from 'child_process';
import { PrereleaseToken } from './PrereleaseToken';
import { ChangeFiles } from './ChangeFiles';
import { RushConfiguration } from '../api/RushConfiguration';
import { DependencySpecifier } from './DependencySpecifier';
import { DependencySpecifier, SpecifierType } from './DependencySpecifier';

export interface IChangeInfoHash {
[key: string]: IChangeInfo;
Expand Down Expand Up @@ -246,7 +246,7 @@ export class PublishUtilities {
} else {
newDependencyVersion = newProjectVersion;
}
return currentDependencySpecifier.specifierType === 'workspace'
return currentDependencySpecifier.specifierType === SpecifierType.Workspace
? `workspace:${newDependencyVersion}`
: newDependencyVersion;
}
Expand Down Expand Up @@ -434,7 +434,9 @@ export class PublishUtilities {
);
const newVersion: string = PublishUtilities._getChangeInfoNewVersion(depChange, prereleaseToken);
dependencies[depName] =
currentSpecifier.specifierType === 'workspace' ? `workspace:${newVersion}` : newVersion;
currentSpecifier.specifierType === SpecifierType.Workspace
? `workspace:${newVersion}`
: newVersion;
} else if (depChange && depChange.changeType! >= ChangeType.hotfix) {
PublishUtilities._updateDependencyVersion(
packageName,
Expand Down Expand Up @@ -712,7 +714,7 @@ export class PublishUtilities {
currentDependencyVersion
);
currentDependencyVersion =
currentDependencySpecifier.specifierType === 'workspace' &&
currentDependencySpecifier.specifierType === SpecifierType.Workspace &&
currentDependencySpecifier.versionSpecifier === '*'
? undefined
: currentDependencySpecifier.versionSpecifier;
Expand All @@ -722,7 +724,8 @@ export class PublishUtilities {
newDependencyVersion
);
newDependencyVersion =
newDependencySpecifier.specifierType === 'workspace' && newDependencySpecifier.versionSpecifier === '*'
newDependencySpecifier.specifierType === SpecifierType.Workspace &&
newDependencySpecifier.versionSpecifier === '*'
? dependencyChange.newVersion!
: newDependencySpecifier.versionSpecifier;

Expand Down
10 changes: 5 additions & 5 deletions apps/rush-lib/src/logic/base/BaseShrinkwrapFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as semver from 'semver';
import { FileSystem } from '@rushstack/node-core-library';

import { RushConstants } from '../../logic/RushConstants';
import { DependencySpecifier } from '../DependencySpecifier';
import { DependencySpecifier, SpecifierType } from '../DependencySpecifier';
import { IShrinkwrapFilePolicyValidatorOptions } from '../policy/ShrinkwrapFilePolicy';
import { PackageManagerOptionsConfigurationBase } from '../../api/RushConfiguration';
import { PackageNameParsers } from '../../api/PackageNameParsers';
Expand Down Expand Up @@ -193,9 +193,9 @@ export abstract class BaseShrinkwrapFile {
//
// In this case, the shrinkwrap file will have a key equivalent to "npm:target-name@1.2.5",
// and so we need to unwrap the target and compare "1.2.5" with "^1.2.3".
if (projectDependency.specifierType === 'alias') {
if (projectDependency.specifierType === SpecifierType.Alias) {
// Does the shrinkwrap install it as an alias?
if (shrinkwrapDependency.specifierType === 'alias') {
if (shrinkwrapDependency.specifierType === SpecifierType.Alias) {
// Does the shrinkwrap have the right package name?
if (projectDependency.packageName === shrinkwrapDependency.packageName) {
// Yes, the aliases match, so let's compare their targets in the logic below
Expand All @@ -212,8 +212,8 @@ export abstract class BaseShrinkwrapFile {
}

switch (normalizedProjectDependency.specifierType) {
case 'version':
case 'range':
case SpecifierType.Version:
case SpecifierType.Range:
return semver.satisfies(
normalizedShrinkwrapDependency.versionSpecifier,
normalizedProjectDependency.versionSpecifier
Expand Down
4 changes: 2 additions & 2 deletions apps/rush-lib/src/logic/installManager/RushInstallManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { RushConstants } from '../../logic/RushConstants';
import { Stopwatch } from '../../utilities/Stopwatch';
import { Utilities } from '../../utilities/Utilities';
import { PackageJsonEditor, DependencyType, PackageJsonDependency } from '../../api/PackageJsonEditor';
import { DependencySpecifier } from '../DependencySpecifier';
import { DependencySpecifier, SpecifierType } from '../DependencySpecifier';
import { InstallHelpers } from './InstallHelpers';
import { AlreadyReportedError } from '../../utilities/AlreadyReportedError';

Expand Down Expand Up @@ -374,7 +374,7 @@ export class RushInstallManager extends BaseInstallManager {

private _revertWorkspaceNotation(dependency: PackageJsonDependency): boolean {
const specifier: DependencySpecifier = new DependencySpecifier(dependency.name, dependency.version);
if (specifier.specifierType !== 'workspace') {
if (specifier.specifierType !== SpecifierType.Workspace) {
return false;
}
// Replace workspace notation with the supplied version range
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { FileSystem, InternalError, MapExtensions, NewlineKind } from '@rushstac
import { AlreadyReportedError } from '../../utilities/AlreadyReportedError';
import { BaseInstallManager, IInstallManagerOptions } from '../base/BaseInstallManager';
import { BaseShrinkwrapFile } from '../../logic/base/BaseShrinkwrapFile';
import { DependencySpecifier } from '../DependencySpecifier';
import { DependencySpecifier, SpecifierType } from '../DependencySpecifier';
import { PackageJsonEditor, DependencyType } from '../../api/PackageJsonEditor';
import { PnpmWorkspaceFile } from '../pnpm/PnpmWorkspaceFile';
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
Expand Down Expand Up @@ -143,8 +143,8 @@ export class WorkspaceInstallManager extends BaseInstallManager {
// cyclic dependency, then it needs to be updated to specify `workspace:*` explicitly. Currently only
// supporting versions and version ranges for specifying a local project.
if (
(dependencySpecifier.specifierType === 'version' ||
dependencySpecifier.specifierType === 'range') &&
(dependencySpecifier.specifierType === SpecifierType.Version ||
dependencySpecifier.specifierType === SpecifierType.Range) &&
referencedLocalProject &&
!rushProject.cyclicDependencyProjects.has(name)
) {
Expand Down Expand Up @@ -190,7 +190,7 @@ export class WorkspaceInstallManager extends BaseInstallManager {
shrinkwrapIsUpToDate = false;
continue;
}
} else if (dependencySpecifier.specifierType === 'workspace') {
} else if (dependencySpecifier.specifierType === SpecifierType.Workspace) {
// Already specified as a local project. Allow the package manager to validate this
continue;
}
Expand Down
6 changes: 3 additions & 3 deletions apps/rush-lib/src/logic/pnpm/PnpmLinkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from './PnpmShrinkwrapFile';
import { PnpmProjectDependencyManifest } from './PnpmProjectDependencyManifest';
import { PackageJsonDependency, DependencyType } from '../../api/PackageJsonEditor';
import { DependencySpecifier } from '../DependencySpecifier';
import { DependencySpecifier, SpecifierType } from '../DependencySpecifier';

// special flag for debugging, will print extra diagnostic information,
// but comes with performance cost
Expand Down Expand Up @@ -324,7 +324,7 @@ export class PnpmLinkManager extends BaseLinkManager {
const localDependencies: PackageJsonDependency[] = [
...project.packageJsonEditor.dependencyList,
...project.packageJsonEditor.devDependencyList
].filter((x) => new DependencySpecifier(x.name, x.version).specifierType === 'workspace');
].filter((x) => new DependencySpecifier(x.name, x.version).specifierType === SpecifierType.Workspace);

for (const { name } of localDependencies) {
const matchedRushPackage:
Expand Down Expand Up @@ -369,7 +369,7 @@ export class PnpmLinkManager extends BaseLinkManager {
const dependencies: PackageJsonDependency[] = [
...project.packageJsonEditor.dependencyList,
...project.packageJsonEditor.devDependencyList
].filter((x) => new DependencySpecifier(x.name, x.version).specifierType !== 'workspace');
].filter((x) => new DependencySpecifier(x.name, x.version).specifierType !== SpecifierType.Workspace);

for (const { name, dependencyType } of dependencies) {
// read the version number from the shrinkwrap entry
Expand Down