Skip to content

Commit

Permalink
Merge pull request #1360 from MasterLambaster/yarn-resolutions
Browse files Browse the repository at this point in the history
[rush] Copy resolutions section from source package.json for yarn
  • Loading branch information
octogonz authored May 14, 2021
2 parents b3aa0f3 + 2db822e commit b0b9804
Show file tree
Hide file tree
Showing 18 changed files with 176 additions and 38 deletions.
2 changes: 1 addition & 1 deletion apps/rundown/bin/rundown
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require('../lib/start.js')
require('../lib/start.js');
112 changes: 82 additions & 30 deletions apps/rush-lib/src/api/PackageJsonEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// See LICENSE in the project root for license information.

import * as semver from 'semver';
import { Import, IPackageJson, JsonFile, Sort } from '@rushstack/node-core-library';
import { Import, InternalError, IPackageJson, JsonFile, Sort } from '@rushstack/node-core-library';

const lodash: typeof import('lodash') = Import.lazy('lodash', require);

Expand All @@ -13,7 +13,8 @@ export const enum DependencyType {
Regular = 'dependencies',
Dev = 'devDependencies',
Optional = 'optionalDependencies',
Peer = 'peerDependencies'
Peer = 'peerDependencies',
YarnResolutions = 'resolutions'
}

/**
Expand Down Expand Up @@ -65,6 +66,9 @@ export class PackageJsonEditor {
// and "peerDependencies" are mutually exclusive, but "devDependencies" is not.
private readonly _devDependencies: Map<string, PackageJsonDependency>;

// NOTE: The "resolutions" field is a yarn specific feature that controls package
// resolution override within yarn.
private readonly _resolutions: Map<string, PackageJsonDependency>;
private _modified: boolean;
private _sourceData: IPackageJson;

Expand All @@ -75,12 +79,14 @@ export class PackageJsonEditor {

this._dependencies = new Map<string, PackageJsonDependency>();
this._devDependencies = new Map<string, PackageJsonDependency>();
this._resolutions = new Map<string, PackageJsonDependency>();

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

const devDependencies: { [key: string]: string } = data.devDependencies || {};
const resolutions: { [key: string]: string } = data.resolutions || {};

const _onChange: () => void = this._onChange.bind(this);

Expand Down Expand Up @@ -141,6 +147,19 @@ export class PackageJsonEditor {
);
});

Object.keys(resolutions || {}).forEach((packageName: string) => {
this._resolutions.set(
packageName,
new PackageJsonDependency(
packageName,
resolutions[packageName],
DependencyType.YarnResolutions,
_onChange
)
);
});

// (Do not sort this._resolutions because order may be significant; the RFC is unclear about that.)
Sort.sortMapKeys(this._dependencies);
Sort.sortMapKeys(this._devDependencies);
} catch (e) {
Expand Down Expand Up @@ -182,6 +201,17 @@ export class PackageJsonEditor {
return [...this._devDependencies.values()];
}

/**
* This field is a Yarn-specific feature that allows overriding of package resolution.
*
* @remarks
* See the {@link https://github.com/yarnpkg/rfcs/blob/master/implemented/0000-selective-versions-resolutions.md
* | 0000-selective-versions-resolutions.md RFC} for details.
*/
public get resolutionsList(): ReadonlyArray<PackageJsonDependency> {
return [...this._resolutions.values()];
}

public tryGetDependency(packageName: string): PackageJsonDependency | undefined {
return this._dependencies.get(packageName);
}
Expand All @@ -203,16 +233,23 @@ export class PackageJsonEditor {
);

// Rush collapses everything that isn't a devDependency into the dependencies
// field, so we need to set the value dependening on dependency type
if (
dependencyType === DependencyType.Regular ||
dependencyType === DependencyType.Optional ||
dependencyType === DependencyType.Peer
) {
this._dependencies.set(packageName, dependency);
} else {
this._devDependencies.set(packageName, dependency);
// field, so we need to set the value depending on dependency type
switch (dependencyType) {
case DependencyType.Regular:
case DependencyType.Optional:
case DependencyType.Peer:
this._dependencies.set(packageName, dependency);
break;
case DependencyType.Dev:
this._devDependencies.set(packageName, dependency);
break;
case DependencyType.YarnResolutions:
this._resolutions.set(packageName, dependency);
break;
default:
throw new InternalError('Unsupported DependencyType');
}

this._modified = true;
}

Expand Down Expand Up @@ -255,31 +292,36 @@ export class PackageJsonEditor {
delete normalizedData.optionalDependencies;
delete normalizedData.peerDependencies;
delete normalizedData.devDependencies;
delete normalizedData.resolutions;

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

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

if (dependency.dependencyType === DependencyType.Regular) {
if (!normalizedData.dependencies) {
normalizedData.dependencies = {};
}
normalizedData.dependencies[dependency.name] = dependency.version;
}

if (dependency.dependencyType === DependencyType.Optional) {
if (!normalizedData.optionalDependencies) {
normalizedData.optionalDependencies = {};
}
normalizedData.optionalDependencies[dependency.name] = dependency.version;
}

if (dependency.dependencyType === DependencyType.Peer) {
if (!normalizedData.peerDependencies) {
normalizedData.peerDependencies = {};
}
normalizedData.peerDependencies[dependency.name] = dependency.version;
switch (dependency.dependencyType) {
case DependencyType.Regular:
if (!normalizedData.dependencies) {
normalizedData.dependencies = {};
}
normalizedData.dependencies[dependency.name] = dependency.version;
break;
case DependencyType.Optional:
if (!normalizedData.optionalDependencies) {
normalizedData.optionalDependencies = {};
}
normalizedData.optionalDependencies[dependency.name] = dependency.version;
break;
case DependencyType.Peer:
if (!normalizedData.peerDependencies) {
normalizedData.peerDependencies = {};
}
normalizedData.peerDependencies[dependency.name] = dependency.version;
break;
case DependencyType.Dev: // uses this._devDependencies instead
case DependencyType.YarnResolutions: // uses this._resolutions instead
default:
throw new InternalError('Unsupported DependencyType');
}
}

Expand All @@ -294,6 +336,16 @@ export class PackageJsonEditor {
normalizedData.devDependencies[dependency.name] = dependency.version;
}

// (Do not sort this._resolutions because order may be significant; the RFC is unclear about that.)
for (const packageName of this._resolutions.keys()) {
const dependency: PackageJsonDependency = this._resolutions.get(packageName)!;

if (!normalizedData.resolutions) {
normalizedData.resolutions = {};
}
normalizedData.resolutions[dependency.name] = dependency.version;
}

return normalizedData;
}
}
7 changes: 7 additions & 0 deletions apps/rush-lib/src/logic/installManager/RushInstallManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,13 @@ export class RushInstallManager extends BaseInstallManager {
}
}

if (this.rushConfiguration.packageManager === 'yarn') {
// This feature is only implemented by the Yarn package manager
if (packageJson.resolutionsList.length > 0) {
tempPackageJson.resolutions = packageJson.saveToObject().resolutions;
}
}

// Example: "C:\MyRepo\common\temp\projects\my-project-2"
const tempProjectFolder: string = this._tempProjectHelper.getTempProjectFolder(rushProject);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@microsoft/node-core-library",
"comment": "Include the Yarn \"resolutions\" field in \"IPackageJson\".",
"type": "minor"
}
],
"packageName": "@microsoft/node-core-library",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@microsoft/rush-stack-compiler-3.8",
"comment": "",
"type": "none"
}
],
"packageName": "@microsoft/rush-stack-compiler-3.8",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@microsoft/rush-stack-compiler-3.9",
"comment": "",
"type": "none"
}
],
"packageName": "@microsoft/rush-stack-compiler-3.9",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "Add support for the Yarn \"resolutions\" package.json feature.",
"packageName": "@microsoft/rush",
"type": "none"
}
],
"packageName": "@microsoft/rush",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@rushstack/node-core-library",
"comment": "",
"type": "none"
}
],
"packageName": "@rushstack/node-core-library",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@rushstack/rundown",
"comment": "",
"type": "none"
}
],
"packageName": "@rushstack/rundown",
"email": "[email protected]"
}
1 change: 1 addition & 0 deletions common/reviews/api/node-core-library.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ export interface INodePackageJson {
peerDependencies?: IPackageJsonDependencyTable;
private?: boolean;
repository?: string;
resolutions?: Record<string, string>;
scripts?: IPackageJsonScriptTable;
// @beta
tsdocMetadata?: string;
Expand Down
5 changes: 4 additions & 1 deletion common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ export const enum DependencyType {
// (undocumented)
Peer = "peerDependencies",
// (undocumented)
Regular = "dependencies"
Regular = "dependencies",
// (undocumented)
YarnResolutions = "resolutions"
}

// @public
Expand Down Expand Up @@ -260,6 +262,7 @@ export class PackageJsonEditor {
static load(filePath: string): PackageJsonEditor;
// (undocumented)
get name(): string;
get resolutionsList(): ReadonlyArray<PackageJsonDependency>;
// (undocumented)
saveIfModified(): boolean;
saveToObject(): IPackageJson;
Expand Down
9 changes: 9 additions & 0 deletions libraries/node-core-library/src/IPackageJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ export interface INodePackageJson {
* A table of script hooks that a package manager or build tool may invoke.
*/
scripts?: IPackageJsonScriptTable;

/**
* A table of package version resolutions. This feature is only implemented by the Yarn package manager.
*
* @remarks
* See the {@link https://github.com/yarnpkg/rfcs/blob/master/implemented/0000-selective-versions-resolutions.md
* | 0000-selective-versions-resolutions.md RFC} for details.
*/
resolutions?: Record<string, string>;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion stack/rush-stack-compiler-3.8/bin/rush-api-extractor
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require("@microsoft/api-extractor/bin/api-extractor");
require('@microsoft/api-extractor/bin/api-extractor');
2 changes: 1 addition & 1 deletion stack/rush-stack-compiler-3.8/bin/rush-eslint
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require("eslint/bin/eslint");
require('eslint/bin/eslint');
2 changes: 1 addition & 1 deletion stack/rush-stack-compiler-3.8/bin/rush-tslint
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require("tslint/bin/tslint");
require('tslint/bin/tslint');
2 changes: 1 addition & 1 deletion stack/rush-stack-compiler-3.9/bin/rush-api-extractor
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require("@microsoft/api-extractor/bin/api-extractor");
require('@microsoft/api-extractor/bin/api-extractor');
2 changes: 1 addition & 1 deletion stack/rush-stack-compiler-3.9/bin/rush-eslint
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require("eslint/bin/eslint");
require('eslint/bin/eslint');
2 changes: 1 addition & 1 deletion stack/rush-stack-compiler-3.9/bin/rush-tslint
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require("tslint/bin/tslint");
require('tslint/bin/tslint');

0 comments on commit b0b9804

Please sign in to comment.