diff --git a/apps/rundown/bin/rundown b/apps/rundown/bin/rundown index 783bb806fce..aee68e80224 100644 --- a/apps/rundown/bin/rundown +++ b/apps/rundown/bin/rundown @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/start.js') +require('../lib/start.js'); diff --git a/apps/rush-lib/src/api/PackageJsonEditor.ts b/apps/rush-lib/src/api/PackageJsonEditor.ts index 059811b0b83..629792c3c96 100644 --- a/apps/rush-lib/src/api/PackageJsonEditor.ts +++ b/apps/rush-lib/src/api/PackageJsonEditor.ts @@ -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); @@ -13,7 +13,8 @@ export const enum DependencyType { Regular = 'dependencies', Dev = 'devDependencies', Optional = 'optionalDependencies', - Peer = 'peerDependencies' + Peer = 'peerDependencies', + YarnResolutions = 'resolutions' } /** @@ -65,6 +66,9 @@ export class PackageJsonEditor { // and "peerDependencies" are mutually exclusive, but "devDependencies" is not. private readonly _devDependencies: Map; + // NOTE: The "resolutions" field is a yarn specific feature that controls package + // resolution override within yarn. + private readonly _resolutions: Map; private _modified: boolean; private _sourceData: IPackageJson; @@ -75,12 +79,14 @@ export class PackageJsonEditor { this._dependencies = new Map(); this._devDependencies = new Map(); + this._resolutions = new Map(); 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); @@ -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) { @@ -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 { + return [...this._resolutions.values()]; + } + public tryGetDependency(packageName: string): PackageJsonDependency | undefined { return this._dependencies.get(packageName); } @@ -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; } @@ -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'); } } @@ -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; } } diff --git a/apps/rush-lib/src/logic/installManager/RushInstallManager.ts b/apps/rush-lib/src/logic/installManager/RushInstallManager.ts index 133709aed3b..92a8d361a9a 100644 --- a/apps/rush-lib/src/logic/installManager/RushInstallManager.ts +++ b/apps/rush-lib/src/logic/installManager/RushInstallManager.ts @@ -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); diff --git a/common/changes/@microsoft/node-core-library/yarn-resolutions_2019-07-06-07-35.json b/common/changes/@microsoft/node-core-library/yarn-resolutions_2019-07-06-07-35.json new file mode 100644 index 00000000000..826aaeb75db --- /dev/null +++ b/common/changes/@microsoft/node-core-library/yarn-resolutions_2019-07-06-07-35.json @@ -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": "MasterLambaster@gmail.com" +} diff --git a/common/changes/@microsoft/rush-stack-compiler-3.8/yarn-resolutions_2021-05-14-05-05.json b/common/changes/@microsoft/rush-stack-compiler-3.8/yarn-resolutions_2021-05-14-05-05.json new file mode 100644 index 00000000000..e85748c5e9f --- /dev/null +++ b/common/changes/@microsoft/rush-stack-compiler-3.8/yarn-resolutions_2021-05-14-05-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush-stack-compiler-3.8", + "comment": "", + "type": "none" + } + ], + "packageName": "@microsoft/rush-stack-compiler-3.8", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/rush-stack-compiler-3.9/yarn-resolutions_2021-05-14-05-05.json b/common/changes/@microsoft/rush-stack-compiler-3.9/yarn-resolutions_2021-05-14-05-05.json new file mode 100644 index 00000000000..35751848f72 --- /dev/null +++ b/common/changes/@microsoft/rush-stack-compiler-3.9/yarn-resolutions_2021-05-14-05-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush-stack-compiler-3.9", + "comment": "", + "type": "none" + } + ], + "packageName": "@microsoft/rush-stack-compiler-3.9", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/rush/yarn-resolutions_2019-07-06-07-35.json b/common/changes/@microsoft/rush/yarn-resolutions_2019-07-06-07-35.json new file mode 100644 index 00000000000..709b8d3aacc --- /dev/null +++ b/common/changes/@microsoft/rush/yarn-resolutions_2019-07-06-07-35.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "Add support for the Yarn \"resolutions\" package.json feature.", + "packageName": "@microsoft/rush", + "type": "none" + } + ], + "packageName": "@microsoft/rush", + "email": "MasterLambaster@gmail.com" +} diff --git a/common/changes/@rushstack/node-core-library/yarn-resolutions_2021-05-14-05-05.json b/common/changes/@rushstack/node-core-library/yarn-resolutions_2021-05-14-05-05.json new file mode 100644 index 00000000000..a18f56bf958 --- /dev/null +++ b/common/changes/@rushstack/node-core-library/yarn-resolutions_2021-05-14-05-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@rushstack/node-core-library", + "comment": "", + "type": "none" + } + ], + "packageName": "@rushstack/node-core-library", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rundown/yarn-resolutions_2021-05-14-05-05.json b/common/changes/@rushstack/rundown/yarn-resolutions_2021-05-14-05-05.json new file mode 100644 index 00000000000..66ae345f9e7 --- /dev/null +++ b/common/changes/@rushstack/rundown/yarn-resolutions_2021-05-14-05-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rundown", + "comment": "", + "type": "none" + } + ], + "packageName": "@rushstack/rundown", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/reviews/api/node-core-library.api.md b/common/reviews/api/node-core-library.api.md index eeb24aee5d9..19ee114b8cb 100644 --- a/common/reviews/api/node-core-library.api.md +++ b/common/reviews/api/node-core-library.api.md @@ -465,6 +465,7 @@ export interface INodePackageJson { peerDependencies?: IPackageJsonDependencyTable; private?: boolean; repository?: string; + resolutions?: Record; scripts?: IPackageJsonScriptTable; // @beta tsdocMetadata?: string; diff --git a/common/reviews/api/rush-lib.api.md b/common/reviews/api/rush-lib.api.md index a8a31314447..fad59779419 100644 --- a/common/reviews/api/rush-lib.api.md +++ b/common/reviews/api/rush-lib.api.md @@ -87,7 +87,9 @@ export const enum DependencyType { // (undocumented) Peer = "peerDependencies", // (undocumented) - Regular = "dependencies" + Regular = "dependencies", + // (undocumented) + YarnResolutions = "resolutions" } // @public @@ -260,6 +262,7 @@ export class PackageJsonEditor { static load(filePath: string): PackageJsonEditor; // (undocumented) get name(): string; + get resolutionsList(): ReadonlyArray; // (undocumented) saveIfModified(): boolean; saveToObject(): IPackageJson; diff --git a/libraries/node-core-library/src/IPackageJson.ts b/libraries/node-core-library/src/IPackageJson.ts index e84116168c0..d215ff6e9bf 100644 --- a/libraries/node-core-library/src/IPackageJson.ts +++ b/libraries/node-core-library/src/IPackageJson.ts @@ -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; } /** diff --git a/stack/rush-stack-compiler-3.8/bin/rush-api-extractor b/stack/rush-stack-compiler-3.8/bin/rush-api-extractor index 5c9bcde4214..d6ece7d9298 100755 --- a/stack/rush-stack-compiler-3.8/bin/rush-api-extractor +++ b/stack/rush-stack-compiler-3.8/bin/rush-api-extractor @@ -1,2 +1,2 @@ #!/usr/bin/env node -require("@microsoft/api-extractor/bin/api-extractor"); +require('@microsoft/api-extractor/bin/api-extractor'); diff --git a/stack/rush-stack-compiler-3.8/bin/rush-eslint b/stack/rush-stack-compiler-3.8/bin/rush-eslint index 7c2ffc1b1c3..0ecf8039cfd 100755 --- a/stack/rush-stack-compiler-3.8/bin/rush-eslint +++ b/stack/rush-stack-compiler-3.8/bin/rush-eslint @@ -1,2 +1,2 @@ #!/usr/bin/env node -require("eslint/bin/eslint"); +require('eslint/bin/eslint'); diff --git a/stack/rush-stack-compiler-3.8/bin/rush-tslint b/stack/rush-stack-compiler-3.8/bin/rush-tslint index b76ee5d8dab..af77c6ef545 100755 --- a/stack/rush-stack-compiler-3.8/bin/rush-tslint +++ b/stack/rush-stack-compiler-3.8/bin/rush-tslint @@ -1,2 +1,2 @@ #!/usr/bin/env node -require("tslint/bin/tslint"); +require('tslint/bin/tslint'); diff --git a/stack/rush-stack-compiler-3.9/bin/rush-api-extractor b/stack/rush-stack-compiler-3.9/bin/rush-api-extractor index 5c9bcde4214..d6ece7d9298 100755 --- a/stack/rush-stack-compiler-3.9/bin/rush-api-extractor +++ b/stack/rush-stack-compiler-3.9/bin/rush-api-extractor @@ -1,2 +1,2 @@ #!/usr/bin/env node -require("@microsoft/api-extractor/bin/api-extractor"); +require('@microsoft/api-extractor/bin/api-extractor'); diff --git a/stack/rush-stack-compiler-3.9/bin/rush-eslint b/stack/rush-stack-compiler-3.9/bin/rush-eslint index 7c2ffc1b1c3..0ecf8039cfd 100755 --- a/stack/rush-stack-compiler-3.9/bin/rush-eslint +++ b/stack/rush-stack-compiler-3.9/bin/rush-eslint @@ -1,2 +1,2 @@ #!/usr/bin/env node -require("eslint/bin/eslint"); +require('eslint/bin/eslint'); diff --git a/stack/rush-stack-compiler-3.9/bin/rush-tslint b/stack/rush-stack-compiler-3.9/bin/rush-tslint index b76ee5d8dab..af77c6ef545 100755 --- a/stack/rush-stack-compiler-3.9/bin/rush-tslint +++ b/stack/rush-stack-compiler-3.9/bin/rush-tslint @@ -1,2 +1,2 @@ #!/usr/bin/env node -require("tslint/bin/tslint"); +require('tslint/bin/tslint');