Skip to content

Commit

Permalink
refactor: decouple our version checks
Browse files Browse the repository at this point in the history
There was a single version check that would log an available stable
version of React Native that was greater than the current version in a
project.

The change breaks that up into:
- what is the current version
- what is the next stable upgrade if available
- log any stable upgrade if available
  • Loading branch information
blakef committed Mar 20, 2023
1 parent bde1e1f commit f5477f2
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 28 deletions.
2 changes: 2 additions & 0 deletions packages/cli-config/src/__tests__/index-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ test('should merge project configuration with default values', () => {
test('should load commands from "react-native-foo" and "react-native-bar" packages', () => {
DIR = getTempDirectory('config_test_packages');
writeFiles(DIR, {
'react-native.config.js': 'module.exports = { reactNativePath: "." }',
'node_modules/react-native-foo/package.json': '{}',
'node_modules/react-native-foo/react-native.config.js': `module.exports = {
commands: [
Expand Down Expand Up @@ -220,6 +221,7 @@ test('does not use restricted "react-native" key to resolve config from package.
'node_modules/react-native-netinfo/package.json': `{
"react-native": "src/index.js"
}`,
'react-native.config.js': 'module.exports = { reactNativePath: "." }',
'package.json': `{
"dependencies": {
"react-native-netinfo": "0.0.1"
Expand Down
23 changes: 10 additions & 13 deletions packages/cli-config/src/loadConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import path from 'path';
import fs from 'fs';
import semver from 'semver';
import {
UserDependencyConfig,
ProjectConfig,
Expand All @@ -10,7 +8,9 @@ import {
} from '@react-native-community/cli-types';
import {
findProjectRoot,
version,
resolveNodeModuleDir,
UnknownProjectError,
} from '@react-native-community/cli-tools';
import findDependencies from './findDependencies';
import resolveReactNativePath from './resolveReactNativePath';
Expand Down Expand Up @@ -96,19 +96,16 @@ function loadConfig(projectRoot: string = findProjectRoot()): Config {
// currently being used to get our deeplinks working, so it's only worried with
// the major and minor version.
try {
const {version} = JSON.parse(
fs.readFileSync(
path.join(initialConfig.reactNativePath, 'package.json'),
{encoding: 'utf8'},
),
);
const out = semver.parse(version);
if (out) {
let semver = version.current(initialConfig.reactNativePath);
if (semver) {
// Retain only these version, since they correspond with our documentation.
initialConfig.reactNativeVersion = `${out.major}.${out.minor}`;
initialConfig.reactNativeVersion = `${semver.major}.${semver.minor}`;
}
} catch (e) {
// If we don't seem to be in a well formed project, give up quietly.
if (!(e instanceof UnknownProjectError)) {
throw e;
}
} catch (_) {
// We don't seem to be in a well formed project, give up quietly.
}

const finalConfig = Array.from(
Expand Down
4 changes: 2 additions & 2 deletions packages/cli-doctor/src/commands/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import getEnvironmentInfo from '../tools/envinfo';
import {logger, releaseChecker} from '@react-native-community/cli-tools';
import {logger, version} from '@react-native-community/cli-tools';
import {Config} from '@react-native-community/cli-types';

const info = async function getInfo(_argv: Array<string>, ctx: Config) {
Expand All @@ -17,7 +17,7 @@ const info = async function getInfo(_argv: Array<string>, ctx: Config) {
} catch (err) {
logger.error(`Unable to print environment info.\n${err}`);
} finally {
await releaseChecker(ctx.root);
await version.logIfUpdateAvailable(ctx.root);
}
};

Expand Down
4 changes: 2 additions & 2 deletions packages/cli-plugin-metro/src/commands/start/runServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import {Config} from '@react-native-community/cli-types';

import loadMetroConfig from '../../tools/loadMetroConfig';
import {releaseChecker} from '@react-native-community/cli-tools';
import {version} from '@react-native-community/cli-tools';
import enableWatchMode from './watchMode';

export type Args = {
Expand Down Expand Up @@ -124,7 +124,7 @@ async function runServer(_argv: Array<string>, ctx: Config, args: Args) {
//
serverInstance.keepAliveTimeout = 30000;

await releaseChecker(ctx.root);
await version.logIfUpdateAvailable(ctx.root);
}

function getReporterImpl(customLogReporterPath: string | undefined) {
Expand Down
5 changes: 5 additions & 0 deletions packages/cli-tools/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,10 @@ export class CLIError extends Error {
}
}

/**
* Raised when we're unable to find a package.json
*/
export class UnknownProjectError extends Error {}

export const inlineString = (str: string) =>
str.replace(/(\s{2,})/gm, ' ').trim();
2 changes: 1 addition & 1 deletion packages/cli-tools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export {fetch, fetchToTemp} from './fetch';
export {default as launchDefaultBrowser} from './launchDefaultBrowser';
export {default as launchDebugger} from './launchDebugger';
export {default as launchEditor} from './launchEditor';
export {default as releaseChecker} from './releaseChecker';
export * as version from './releaseChecker';
export {default as resolveNodeModuleDir} from './resolveNodeModuleDir';
export {default as hookStdout} from './hookStdout';
export {getLoader, NoopLoader, Loader} from './loader';
Expand Down
3 changes: 2 additions & 1 deletion packages/cli-tools/src/releaseChecker/getLatestRelease.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type Release = {
export default async function getLatestRelease(
name: string,
currentVersion: string,
): Promise<Release | void> {
): Promise<Release | undefined> {
logger.debug('Checking for a newer version of React Native');
try {
logger.debug(`Current version: ${currentVersion}`);
Expand Down Expand Up @@ -68,6 +68,7 @@ export default async function getLatestRelease(
);
logger.debug(e as any);
}
return undefined;
}

function buildChangelogUrl(version: string) {
Expand Down
65 changes: 56 additions & 9 deletions packages/cli-tools/src/releaseChecker/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
import path from 'path';
import semver, {SemVer} from 'semver';

import {UnknownProjectError} from '../errors';
import logger from '../logger';
// @ts-ignore - JS file
import resolveNodeModuleDir from '../resolveNodeModuleDir';
import getLatestRelease from './getLatestRelease';
import getLatestRelease, {Release} from './getLatestRelease';
import printNewRelease from './printNewRelease';

export default async function releaseChecker(root: string) {
const getReactNativeVersion = (projectRoot: string): string | undefined =>
require(path.join(
resolveNodeModuleDir(projectRoot, 'react-native'),
'package.json',
))?.version;

/**
* Logs out a message if the user's version is behind a stable version of React Native
*/
export async function logIfUpdateAvailable(projectRoot: string): Promise<void> {
const hasUpdate = await latest(projectRoot);
if (hasUpdate) {
printNewRelease(hasUpdate.name, hasUpdate.upgrade, hasUpdate.current);
}
}

type Update = {
upgrade: Release;
current: string;
name: string;
};

/**
* Finds the latest stables version of React Native > current version
*/
export async function latest(projectRoot: string): Promise<Update | undefined> {
try {
const {version: currentVersion} = require(path.join(
resolveNodeModuleDir(root, 'react-native'),
'package.json',
));
const {name} = require(path.join(root, 'package.json'));
const currentVersion = getReactNativeVersion(projectRoot);
if (!currentVersion) {
return;
}
const {name} = require(path.join(projectRoot, 'package.json'));
const latestRelease = await getLatestRelease(name, currentVersion);

if (latestRelease) {
printNewRelease(name, latestRelease, currentVersion);
return {
name,
current: currentVersion,
upgrade: latestRelease,
};
}
} catch (e) {
// We let the flow continue as this component is not vital for the rest of
Expand All @@ -26,4 +57,20 @@ export default async function releaseChecker(root: string) {
);
logger.debug(e as any);
}
return undefined;
}

/**
* Gets the current project's version parsed as Semver
*/
export function current(projectRoot: string): SemVer | undefined {
try {
const found = semver.parse(getReactNativeVersion(projectRoot));
if (found) {
return found;
}
} catch (_) {
throw new UnknownProjectError(projectRoot);
}
return undefined;
}

0 comments on commit f5477f2

Please sign in to comment.