From ca43568fe9a60ffa9f7e0bdb4d1215ff1f48df8f Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:05:21 +0200 Subject: [PATCH] feat(align-deps): notify users when a newer version of a preset is available --- .changeset/chatty-pots-read.md | 5 ++++ packages/align-deps/src/preset.ts | 44 +++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 .changeset/chatty-pots-read.md diff --git a/.changeset/chatty-pots-read.md b/.changeset/chatty-pots-read.md new file mode 100644 index 0000000000..7c7f1281c8 --- /dev/null +++ b/.changeset/chatty-pots-read.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/align-deps": minor +--- + +Notify users when a newer version of a preset is available diff --git a/packages/align-deps/src/preset.ts b/packages/align-deps/src/preset.ts index eafb335042..39c2486c4c 100644 --- a/packages/align-deps/src/preset.ts +++ b/packages/align-deps/src/preset.ts @@ -1,5 +1,12 @@ +import { info } from "@rnx-kit/console"; +import { readPackage } from "@rnx-kit/tools-node/package"; import type { Capability } from "@rnx-kit/types-kit-config"; +import { spawn } from "node:child_process"; +import * as nodefs from "node:fs"; +import { findPackageJSON } from "node:module"; +import { pathToFileURL } from "node:url"; import semverCoerce from "semver/functions/coerce.js"; +import semverGreater from "semver/functions/gt.js"; import semverSatisfies from "semver/functions/satisfies.js"; import semverValidRange from "semver/ranges/valid.js"; import { gatherRequirements } from "./dependencies.ts"; @@ -12,6 +19,36 @@ type Resolution = { capabilities: Capability[]; }; +const notifyLatestVersion = (() => { + const cache = new Set(); + return (preset: string, fs = nodefs): void => { + if (cache.has(preset)) { + return; + } + + cache.add(preset); + + const manifestPath = findPackageJSON(pathToFileURL(preset)); + if (!manifestPath) { + return; + } + + const { name, version } = readPackage(manifestPath, fs); + + const shell = process.platform === "win32"; + const opts = { shell, windowsVerbatimArguments: true }; + + spawn("npm", ["view", name, "version"], opts).stdout.on("data", (data) => { + const latestVersion = data.toString().trim(); + if (semverGreater(latestVersion, version)) { + info( + `A newer version of '${name}' was found: ${latestVersion} (current: ${version})` + ); + } + }); + }; +})(); + function loadPreset( preset: string, projectRoot: string, @@ -20,8 +57,11 @@ function loadPreset( switch (preset) { case "microsoft/react-native": return reactNativePreset; - default: - return require(resolve(preset, { paths: [projectRoot] })); + default: { + const spec = resolve(preset, { paths: [projectRoot] }); + notifyLatestVersion(spec); + return require(spec); + } } }