Skip to content

Commit

Permalink
feat(align-deps): allow version range subsets
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 committed Jun 6, 2024
1 parent 97a9925 commit 4e886cb
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 6 deletions.
6 changes: 6 additions & 0 deletions .changeset/quiet-houses-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rnx-kit/align-deps": minor
"@rnx-kit/cli": patch
---

Allow exact and version range subsets with `--diff-mode allow-subset`
25 changes: 23 additions & 2 deletions packages/align-deps/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

import { error } from "@rnx-kit/console";
import { error, warn } from "@rnx-kit/console";
import { hasProperty } from "@rnx-kit/tools-language/properties";
import { findPackageDir } from "@rnx-kit/tools-node/package";
import {
Expand All @@ -15,9 +15,15 @@ import { makeSetVersionCommand } from "./commands/setVersion";
import { defaultConfig } from "./config";
import { printError, printInfo } from "./errors";
import { isString } from "./helpers";
import type { Args, Command } from "./types";
import type { Args, Command, DiffMode } from "./types";

export const cliOptions = {
"diff-mode": {
default: "strict",
description:
"Determines what align-deps should do when versions differ. Valid values are 'strict' (version strings must be equal) and 'allow-subset' (allow version ranges that are a subset).",
choices: ["strict", "allow-subset"],
},
"exclude-packages": {
description:
"Comma-separated list of package names to exclude from inspection.",
Expand Down Expand Up @@ -148,6 +154,19 @@ function reportConflicts(conflicts: [string, string][], args: Args): boolean {
}, false);
}

function validateDiffMode(mode: string | undefined): DiffMode {
switch (mode) {
case "allow-subset":
case "strict":
return mode;
default:
if (mode) {
warn("Unknown diff mode:", mode);
}
return "strict";
}
}

async function makeCommand(args: Args): Promise<Command | undefined> {
const conflicts: [string, string][] = [
["init", "set-version"],
Expand All @@ -159,6 +178,7 @@ async function makeCommand(args: Args): Promise<Command | undefined> {
}

const {
"diff-mode": diffMode,
"exclude-packages": excludePackages,
init,
loose,
Expand All @@ -178,6 +198,7 @@ async function makeCommand(args: Args): Promise<Command | undefined> {
noUnmanaged,
verbose,
write,
diffMode: validateDiffMode(diffMode),
excludePackages: excludePackages?.toString()?.split(","),
requirements: requirements?.toString()?.split(","),
};
Expand Down
2 changes: 1 addition & 1 deletion packages/align-deps/src/commands/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function checkPackageManifest(
kitType
);

const allChanges = diff(manifest, updatedManifest);
const allChanges = diff(manifest, updatedManifest, options);
if (allChanges) {
if (options.write) {
// The config object may be passed to other commands, so we need to
Expand Down
15 changes: 12 additions & 3 deletions packages/align-deps/src/diff.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import type { PackageManifest } from "@rnx-kit/tools-node/package";
import semverRangeSubset from "semver/ranges/subset";
import { dependencySections } from "./helpers";
import type { Changes } from "./types";
import type { Changes, Options } from "./types";

function isStrictlyEqual(version: string, range: string) {
return version === range;
}

export function diff(
manifest: PackageManifest,
updatedManifest: PackageManifest
updatedManifest: PackageManifest,
{ diffMode }: Pick<Options, "diffMode">
): Changes | undefined {
const allChanges: Changes = {
dependencies: [],
Expand All @@ -13,6 +19,9 @@ export function diff(
capabilities: [],
};

const satisfies =
diffMode === "allow-subset" ? semverRangeSubset : isStrictlyEqual;

const numChanges = dependencySections.reduce((count, section) => {
const changes = allChanges[section];
const currentDeps = manifest[section] ?? {};
Expand All @@ -22,7 +31,7 @@ export function diff(
const current = currentDeps[dependency];
if (!current) {
changes.push({ type: "added", dependency, target });
} else if (current !== target) {
} else if (!satisfies(current, target)) {
changes.push({ type: "changed", dependency, target, current });
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/align-deps/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,22 @@ export type Changes = {
capabilities: { type: "unmanaged"; dependency: string; capability: string }[];
};

export type DiffMode = "strict" | "allow-subset";

export type Options = {
presets: string[];
loose: boolean;
migrateConfig: boolean;
noUnmanaged: boolean;
verbose: boolean;
write: boolean;
diffMode?: DiffMode;
excludePackages?: string[];
requirements?: string[];
};

export type Args = Pick<Options, "loose" | "verbose" | "write"> & {
"diff-mode"?: string;
"exclude-packages"?: string | number;
"migrate-config": boolean;
"no-unmanaged": boolean;
Expand Down
71 changes: 71 additions & 0 deletions packages/align-deps/test/check.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,77 @@ describe("checkPackageManifest({ kitType: 'library' })", () => {
expect(consoleWarnSpy).not.toHaveBeenCalled();
expect(consoleErrorSpy).not.toHaveBeenCalled();
});

test("allows exact versions", () => {
const mods = /^[^\d]*/;
fs.__setMockContent({
...mockManifest,
peerDependencies: {
react: react_v68_v69_v70,
"react-native": v68_v69_v70,
},
devDependencies: {
react: packageVersion(profile_0_69, "react").replace(mods, ""),
"react-native": packageVersion(profile_0_69, "core").replace(mods, ""),
},
});
rnxKitConfig.__setMockConfig({
alignDeps: {
requirements: {
development: ["react-native@0.69"],
production: ["react-native@0.68 || 0.69 || 0.70"],
},
capabilities: ["core-ios"],
},
});

const result = checkPackageManifest("package.json", {
...defaultOptions,
diffMode: "allow-subset",
});

expect(result).toBe("success");
expect(consoleLogSpy).not.toHaveBeenCalled();
expect(consoleWarnSpy).not.toHaveBeenCalled();
expect(consoleErrorSpy).not.toHaveBeenCalled();
});

test("allows version range subsets", () => {
const patch = /\.\d*$/;
fs.__setMockContent({
...mockManifest,
peerDependencies: {
react: react_v68_v69_v70,
"react-native": v68_v69_v70,
},
devDependencies: {
react: packageVersion(profile_0_69, "react"),
"react-native": packageVersion(profile_0_69, "core").replace(
patch,
".9999"
),
},
});
rnxKitConfig.__setMockConfig({
alignDeps: {
requirements: {
development: ["react-native@0.69"],
production: ["react-native@0.68 || 0.69 || 0.70"],
},
capabilities: ["core-ios"],
},
});

const result = checkPackageManifest("package.json", {
...defaultOptions,
diffMode: "allow-subset",
});

expect(result).toBe("success");
expect(consoleLogSpy).not.toHaveBeenCalled();
expect(consoleWarnSpy).not.toHaveBeenCalled();
expect(consoleErrorSpy).not.toHaveBeenCalled();
});
});

describe("checkPackageManifest({ kitType: 'library' }) (backwards compatibility)", () => {
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/align-deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export function rnxAlignDeps(
): void {
cli({
...pickValues(args, Object.values(optionsMap), Object.keys(optionsMap)),
"diff-mode": args.diffMode?.toString(),
loose: Boolean(args.loose),
"migrate-config": Boolean(args.migrateConfig),
"no-unmanaged": Boolean(args.noUnmanaged),
Expand All @@ -34,6 +35,10 @@ export const rnxAlignDepsCommand = {
"Manage dependencies within a repository and across many repositories",
func: rnxAlignDeps,
options: [
{
name: `--diff-mode [${cliOptions["diff-mode"].choices.join("|")}]`,
description: cliOptions["diff-mode"].description,
},
{
name: "--exclude-packages [packages]",
description: cliOptions["exclude-packages"].description,
Expand Down

0 comments on commit 4e886cb

Please sign in to comment.