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] Copy resolutions section from source package.json for yarn #1360

Merged
merged 12 commits into from
May 14, 2021
Merged
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');
111 changes: 81 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,6 +79,7 @@ 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 || {};
Expand Down Expand Up @@ -141,6 +146,19 @@ export class PackageJsonEditor {
);
});

Object.keys(data.resolutions || {}).forEach((packageName: string) => {
this._resolutions.set(
packageName,
new PackageJsonDependency(
packageName,
devDependencies[packageName],
D4N14L marked this conversation as resolved.
Show resolved Hide resolved
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 +200,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 +232,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 +291,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 +335,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;
}
}
11 changes: 11 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,17 @@ export class RushInstallManager extends BaseInstallManager {
}
}

if (packageJson.resolutionsList.length > 0) {
// We do not expect resolutions key to be provided for package managers other than yarn
if (this.rushConfiguration.packageManager !== 'yarn') {
throw new Error(
"Unexpected 'resolutions' section found in package.json. Only Yarn supports this feature."
);
}

tempPackageJson.resolutions = packageJson.saveToObject().resolutions;
D4N14L marked this conversation as resolved.
Show resolved Hide resolved
}

// 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');