diff --git a/.changeset/tame-eyes-yawn.md b/.changeset/tame-eyes-yawn.md new file mode 100644 index 000000000..31196df1d --- /dev/null +++ b/.changeset/tame-eyes-yawn.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/dep-check": patch +--- + +Keep version ranges defined in `peerDependencies` if they are a superset diff --git a/packages/dep-check/src/capabilities.ts b/packages/dep-check/src/capabilities.ts index ee58fd8ce..e85696f4e 100644 --- a/packages/dep-check/src/capabilities.ts +++ b/packages/dep-check/src/capabilities.ts @@ -1,6 +1,6 @@ import type { Capability, KitCapabilities } from "@rnx-kit/config"; import type { PackageManifest } from "@rnx-kit/tools-node/package"; -import semver from "semver"; +import semverMinVersion from "semver/ranges/min-version"; import { getProfilesFor, getProfileVersionsFor } from "./profiles"; import { concatVersionRanges, keysOf } from "./helpers"; import type { @@ -46,7 +46,7 @@ export function capabilitiesFor( ? { reactNativeDevVersion: devDependencies?.["react-native"] || - semver.minVersion(reactNativeVersion)?.version, + semverMinVersion(reactNativeVersion)?.version, } : undefined), kitType, diff --git a/packages/dep-check/src/dependencies.ts b/packages/dep-check/src/dependencies.ts index 7641d8886..e5449c628 100644 --- a/packages/dep-check/src/dependencies.ts +++ b/packages/dep-check/src/dependencies.ts @@ -1,13 +1,8 @@ -import { - Capability, - getKitCapabilities, - getKitConfig, - KitConfig, - KitType, -} from "@rnx-kit/config"; +import type { Capability, KitConfig, KitType } from "@rnx-kit/config"; +import { getKitCapabilities, getKitConfig } from "@rnx-kit/config"; import { error, warn } from "@rnx-kit/console"; +import type { PackageManifest } from "@rnx-kit/tools-node/package"; import { - PackageManifest, findPackageDependencyDir, parsePackageRef, readPackage, @@ -101,7 +96,7 @@ export function getRequirements( visitDependencies(targetManifest, projectRoot, (module, modulePath) => { const kitConfig = getKitConfig({ cwd: modulePath }); - if (!kitConfig) { + if (!kitConfig?.reactNativeVersion) { return; } diff --git a/packages/dep-check/src/findBadPackages.ts b/packages/dep-check/src/findBadPackages.ts index c3566f192..6155495f8 100644 --- a/packages/dep-check/src/findBadPackages.ts +++ b/packages/dep-check/src/findBadPackages.ts @@ -1,11 +1,11 @@ import type { PackageManifest } from "@rnx-kit/tools-node/package"; -import semver from "semver"; +import semverSubset from "semver/ranges/subset"; import banned from "./profiles/banned"; import type { ExcludedPackage } from "./types"; function isBanned(name: string, version: string): ExcludedPackage | undefined { const info = banned.find((pkg) => pkg.name === name); - return info && semver.subset(version, info.version) ? info : undefined; + return info && semverSubset(version, info.version) ? info : undefined; } export function findBadPackages({ diff --git a/packages/dep-check/src/manifest.ts b/packages/dep-check/src/manifest.ts index d34dd2868..096a717d0 100644 --- a/packages/dep-check/src/manifest.ts +++ b/packages/dep-check/src/manifest.ts @@ -1,7 +1,7 @@ import type { Capability, KitType } from "@rnx-kit/config"; import type { PackageManifest } from "@rnx-kit/tools-node/package"; import omit from "lodash/omit"; -import semver from "semver"; +import semverSatisfies from "semver/functions/satisfies"; import { resolveCapabilities } from "./capabilities"; import type { DependencyType, Package, Profile } from "./types"; @@ -58,7 +58,7 @@ export function updateDependencies( // `Array.prototype.sort` is stable as of V8 7.0 (or Node 11). For users still // on Node 10 or below, we'll use a solution that throws away one more object. - if (semver.satisfies(nodeVersion, ">=11")) { + if (semverSatisfies(nodeVersion, ">=11")) { const entries = packageNames.reduce((result, dependency) => { const packageRange = packages[dependency]; if (shouldBeAdded(packageRange[0])) { diff --git a/packages/dep-check/src/profiles.ts b/packages/dep-check/src/profiles.ts index edb4713a4..dab49e000 100644 --- a/packages/dep-check/src/profiles.ts +++ b/packages/dep-check/src/profiles.ts @@ -1,6 +1,10 @@ import { error } from "@rnx-kit/console"; import isString from "lodash/isString"; -import semver from "semver"; +import semverCoerce from "semver/functions/coerce"; +import semverSatisfies from "semver/functions/satisfies"; +import semverValid from "semver/functions/valid"; +import semverIntersects from "semver/ranges/intersects"; +import semverValidRange from "semver/ranges/valid"; import { keysOf } from "./helpers"; import profile_0_61 from "./profiles/profile-0.61"; import profile_0_62 from "./profiles/profile-0.62"; @@ -37,16 +41,16 @@ function getVersionComparator( ): (profileVersion: ProfileVersion) => boolean { const includePrerelease = { includePrerelease: true }; - const version = semver.valid(versionOrRange); + const version = semverValid(versionOrRange); if (version) { return (profileVersion: ProfileVersion) => - semver.satisfies(version, "^" + profileVersion, includePrerelease); + semverSatisfies(version, "^" + profileVersion, includePrerelease); } - const range = semver.validRange(versionOrRange); + const range = semverValidRange(versionOrRange); if (range) { return (profileVersion: ProfileVersion) => - semver.intersects("^" + profileVersion, range, includePrerelease); + semverIntersects("^" + profileVersion, range, includePrerelease); } throw new Error(`Invalid 'react-native' version range: ${versionOrRange}`); @@ -170,7 +174,7 @@ export function parseProfilesString( const profileVersions = versions .toString() .split(",") - .map((value) => "^" + semver.coerce(value)); + .map((value) => "^" + semverCoerce(value)); const targetVersion = profileVersions[0]; // Note: `.sort()` mutates the array diff --git a/packages/dep-check/src/vigilant.ts b/packages/dep-check/src/vigilant.ts index 32327eadc..0e4a31799 100644 --- a/packages/dep-check/src/vigilant.ts +++ b/packages/dep-check/src/vigilant.ts @@ -2,6 +2,7 @@ import { Capability } from "@rnx-kit/config"; import { error } from "@rnx-kit/console"; import { PackageManifest, readPackage } from "@rnx-kit/tools-node/package"; import isString from "lodash/isString"; +import semverSubset from "semver/ranges/subset"; import { resolveCapabilities } from "./capabilities"; import { checkPackageManifest, getCheckConfig } from "./check"; import { keysOf, modifyManifest } from "./helpers"; @@ -105,6 +106,10 @@ export function inspect( profile: ManifestProfile, write: boolean ): Change[] { + const isMisalignedDirect = (from: string, to: string) => from !== to; + const isMisalignedPeer = (from: string, to: string) => + from !== to && !semverSubset(to, from, { includePrerelease: true }); + const changes: Change[] = []; allSections.forEach((section) => { const dependencies = manifest[section]; @@ -112,12 +117,14 @@ export function inspect( return; } + const isMisaligned = + section === "peerDependencies" ? isMisalignedPeer : isMisalignedDirect; const desiredDependencies = profile[section]; Object.keys(dependencies).forEach((name) => { if (name in desiredDependencies) { const from = dependencies[name]; const to = desiredDependencies[name]; - if (from !== to) { + if (isMisaligned(from, to)) { changes.push({ name, from, to, section }); if (write) { dependencies[name] = to; diff --git a/packages/dep-check/test/vigilant.test.ts b/packages/dep-check/test/vigilant.test.ts index 992c6d259..2651a6b5e 100644 --- a/packages/dep-check/test/vigilant.test.ts +++ b/packages/dep-check/test/vigilant.test.ts @@ -218,6 +218,45 @@ describe("inspect()", () => { ); expect(manifest.dependencies).not.toEqual(dependencies); }); + + test("does not rewrite peerDependencies if superset", () => { + const manifest = { + name: "@rnx-kit/dep-check", + version: "1.0.0", + dependencies: { + react: "^16.8.1", + }, + peerDependencies: { + metro: "*", + react: ">=16.8.0 <18.0.0", + "react-native": ">=0.64", + }, + devDependencies: {}, + }; + const profile = { + name: "@rnx-kit/dep-check", + version: "1.0.0", + dependencies: { + react: "~17.0.1", + }, + peerDependencies: { + metro: "^0.66.2", + react: "~17.0.1", + "react-native": "^0.66.0-0", + }, + devDependencies: {}, + }; + const expectedChanges = [ + { + name: "react", + from: manifest.dependencies["react"], + to: profile.dependencies["react"], + section: "dependencies", + }, + ]; + + expect(inspect(manifest, profile, false)).toEqual(expectedChanges); + }); }); describe("makeVigilantCommand()", () => { diff --git a/packages/jest-preset/package.json b/packages/jest-preset/package.json index 89afa9b85..4216162cc 100644 --- a/packages/jest-preset/package.json +++ b/packages/jest-preset/package.json @@ -55,5 +55,8 @@ }, "jest": { "preset": "@rnx-kit/scripts" + }, + "rnx-kit": { + "customProfiles": "@rnx-kit/scripts/rnx-dep-check.js" } } diff --git a/packages/metro-config/package.json b/packages/metro-config/package.json index ec121bdef..a006e4810 100644 --- a/packages/metro-config/package.json +++ b/packages/metro-config/package.json @@ -51,5 +51,8 @@ }, "jest": { "preset": "@rnx-kit/scripts" + }, + "rnx-kit": { + "customProfiles": "@rnx-kit/scripts/rnx-dep-check.js" } } diff --git a/scripts/rnx-dep-check.js b/scripts/rnx-dep-check.js index 17f83d5b9..10d88a1ee 100755 --- a/scripts/rnx-dep-check.js +++ b/scripts/rnx-dep-check.js @@ -68,8 +68,6 @@ module.exports = { if (require.main === module) { require("@rnx-kit/dep-check").cli({ "custom-profiles": __filename, - // the following packages support multiple versions of react-native - "exclude-packages": "@rnx-kit/jest-preset,@rnx-kit/metro-config", vigilant: "0.66", write: process.argv.includes("--write"), });