From 471e12af0d58705362c38359a911b123710d0ee8 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Tue, 19 Mar 2019 22:48:14 +0100 Subject: [PATCH 01/20] Add getLatestRelease `getLatestRelease` uses the GitHub API to retrieve the most recent React Native release. The response data can be used to inform the user of a newer RN version to upgrade to. --- packages/cli/src/tools/getLatestRelease.js | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 packages/cli/src/tools/getLatestRelease.js diff --git a/packages/cli/src/tools/getLatestRelease.js b/packages/cli/src/tools/getLatestRelease.js new file mode 100644 index 000000000..dc147785a --- /dev/null +++ b/packages/cli/src/tools/getLatestRelease.js @@ -0,0 +1,43 @@ +/** + * @flow + */ +import https from 'https'; + +type Release = { + tag_name: string, + html_url: string, + draft: boolean, + prerelease: boolean, + created_at: string, + published_at: string, + body: string, +}; + +export function getLatestRelease() { + // Replace with promisify.js + return new Promise((resolve, reject) => { + const options = { + hostname: 'api.github.com', + path: '/repos/facebook/react-native/releases/latest', + // https://developer.github.com/v3/#user-agent-required + headers: {'User-Agent': 'React-Native-CLI'}, + }; + + https + .get(options, result => { + let body = ''; + + result.setEncoding('utf8'); + result.on('data', data => { + body += data; + }); + + result.on('end', () => { + resolve(JSON.parse(body)); + }); + + result.on('error', error => reject(error)); + }) + .on('error', error => reject(error)); + }); +} From cdeda7067aa18827d75b0b3f43b421ede96efdd1 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Tue, 19 Mar 2019 23:21:26 +0100 Subject: [PATCH 02/20] Add getNewerReleaseDataIfAvailable This function is a wrapper around the previouslt introduced `getLatestRelease` and will return the release data only if it is newer that the currently used React Native version and if it is not a prerelease. --- packages/cli/src/tools/getLatestRelease.js | 43 ----- .../getLatestRelease/getLatestRelease.js | 167 ++++++++++++++++++ 2 files changed, 167 insertions(+), 43 deletions(-) delete mode 100644 packages/cli/src/tools/getLatestRelease.js create mode 100644 packages/cli/src/tools/getLatestRelease/getLatestRelease.js diff --git a/packages/cli/src/tools/getLatestRelease.js b/packages/cli/src/tools/getLatestRelease.js deleted file mode 100644 index dc147785a..000000000 --- a/packages/cli/src/tools/getLatestRelease.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @flow - */ -import https from 'https'; - -type Release = { - tag_name: string, - html_url: string, - draft: boolean, - prerelease: boolean, - created_at: string, - published_at: string, - body: string, -}; - -export function getLatestRelease() { - // Replace with promisify.js - return new Promise((resolve, reject) => { - const options = { - hostname: 'api.github.com', - path: '/repos/facebook/react-native/releases/latest', - // https://developer.github.com/v3/#user-agent-required - headers: {'User-Agent': 'React-Native-CLI'}, - }; - - https - .get(options, result => { - let body = ''; - - result.setEncoding('utf8'); - result.on('data', data => { - body += data; - }); - - result.on('end', () => { - resolve(JSON.parse(body)); - }); - - result.on('error', error => reject(error)); - }) - .on('error', error => reject(error)); - }); -} diff --git a/packages/cli/src/tools/getLatestRelease/getLatestRelease.js b/packages/cli/src/tools/getLatestRelease/getLatestRelease.js new file mode 100644 index 000000000..dcf9a38e8 --- /dev/null +++ b/packages/cli/src/tools/getLatestRelease/getLatestRelease.js @@ -0,0 +1,167 @@ +/** + * @flow + */ +import path from 'path'; +import https from 'https'; +import semver from 'semver'; +import fs from 'fs'; +import logger from '../logger'; + +type Release = { + tag_name: string, + html_url: string, + draft: boolean, + prerelease: boolean, + created_at: string, + published_at: string, + body: string, +}; + +/** + * Checks via GitHub API if there is a newer stable React Native release and, + * if it exists, returns the release data. + * + * If the latest release is not newer or if it's a prerelease, the function + * will return null. + */ +export async function getNewerReleaseDataIfAvailable() { + logger.debug('Checking for a newer version of React Native'); + try { + const currentVersion = require(getRNPkgJsonPath()).version; + logger.debug(`Current version: ${currentVersion}`); + + const cache = cacheManager.get(); + let eTag; + if (cache) { + eTag = cache.eTag; + logger.debug( + `Cached release version: ${semver.coerce(cache.release.tag_name)}`, + ); + } + + logger.debug('Checking for newer releases on GitHub'); + const response: Response = await getLatestRelease(eTag); + + let latestRelease; + if ( + response.statusCode === 200 && + response.release && + !response.release.prerelease + ) { + latestRelease = response.release; + logger.debug( + `Remote release version: ${semver.coerce(latestRelease.tag_name)}`, + ); + // Update the cache. + cacheManager.set({ + release: latestRelease, + eTag: response.eTag, + }); + } else { + // Response status is 304, meaning that our cached release data + // is the most recent release available. + logger.debug('Cache is up-to-date with GitHub releases'); + // $FlowFixMe: We know at this point that cache is not null + latestRelease = cache.release; + } + + const latestVersion = semver.coerce(latestRelease.tag_name); + logger.debug(`Latest stable release: ${latestVersion}`); + if (semver.compare(latestVersion, currentVersion) === 1) { + return latestRelease; + } else { + return null; + } + } catch (e) { + logger.debug( + 'Something went wrong with remote version checking, moving on', + ); + return null; + } +} + +type Headers = { + 'User-Agent': string, + [header: string]: string, +}; + +type Response = { + eTag: string, + release: ?Release, + statusCode: number, +}; + +function getLatestRelease(eTag: ?string) { + return new Promise((resolve, reject) => { + const options = { + hostname: 'api.github.com', + path: '/repos/facebook/react-native/releases/latest', + // https://developer.github.com/v3/#user-agent-required + headers: ({'User-Agent': 'React-Native-CLI'}: Headers), + }; + + if (eTag) { + options.headers['If-None-Match'] = eTag; + } + + https + .get(options, result => { + let body = ''; + + result.setEncoding('utf8'); + result.on('data', data => { + body += data; + }); + + result.on('end', () => { + resolve({ + // If status code is 304, then body will be empty. + release: body ? JSON.parse(body) : undefined, + eTag: result.headers.etag, + statusCode: result.statusCode, + }); + }); + + result.on('error', error => reject(error)); + }) + .on('error', error => reject(error)); + }); +} + +const getRNPkgJsonPath = function() { + return path.resolve( + process.cwd(), + 'node_modules', + 'react-native', + 'package.json', + ); +}; + +const cacheManager = { + get: (): ?{release: Release, eTag: string} => { + try { + const cacheRaw = fs.readFileSync( + path.resolve(__dirname, '.cache'), + 'utf8', + ); + const cache = JSON.parse(cacheRaw); + logger.debug(`Found release cache ${cache.eTag}`); + return cache; + } catch (e) { + logger.debug('No release cache found'); + return null; + } + }, + + set: (releaseData: {release: Release, eTag: string}) => { + logger.debug( + `Saving release ${semver.coerce(releaseData.release.tag_name)} as ${ + releaseData.eTag + } to cache`, + ); + fs.writeFileSync( + path.resolve(__dirname, '.cache'), + JSON.stringify(releaseData), + ); + }, +}; From cdfd8bebde7cdb1d79478acf2c1ef492c0b02e36 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Fri, 22 Mar 2019 11:41:11 +0100 Subject: [PATCH 03/20] Check for newer release in cliEntry --- packages/cli/src/cliEntry.js | 12 ++++++++++++ .../src/tools/getLatestRelease/getLatestRelease.js | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index c22bc0573..278191931 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -19,6 +19,8 @@ import init from './commands/init/initCompat'; import assertRequiredOptions from './tools/assertRequiredOptions'; import {logger} from '@react-native-community/cli-tools'; import {setProjectDir} from './tools/packageManager'; +import findPlugins from './tools/findPlugins'; +import getNewerReleaseDataIfAvailable from './tools/getLatestRelease/getLatestRelease'; import pkgJson from '../package.json'; import loadConfig from './tools/config'; @@ -184,6 +186,16 @@ async function setupAndRun() { } logger.setVerbose(commander.verbose); + + const newerRelease = await getNewerReleaseDataIfAvailable(); + if (newerRelease) { + logger.info('A newer version of React Native is available!'); + logger.info(''); + logger.info(`Version: ${newerRelease.tag_name}`); + logger.info(`Changelog: ${newerRelease.html_url}`); + logger.info(''); + logger.info('Upgrade to the newer version by running react-native upgrade'); + } } export default { diff --git a/packages/cli/src/tools/getLatestRelease/getLatestRelease.js b/packages/cli/src/tools/getLatestRelease/getLatestRelease.js index dcf9a38e8..b0247d72f 100644 --- a/packages/cli/src/tools/getLatestRelease/getLatestRelease.js +++ b/packages/cli/src/tools/getLatestRelease/getLatestRelease.js @@ -24,7 +24,7 @@ type Release = { * If the latest release is not newer or if it's a prerelease, the function * will return null. */ -export async function getNewerReleaseDataIfAvailable() { +export default (async function() { logger.debug('Checking for a newer version of React Native'); try { const currentVersion = require(getRNPkgJsonPath()).version; @@ -78,7 +78,7 @@ export async function getNewerReleaseDataIfAvailable() { ); return null; } -} +}); type Headers = { 'User-Agent': string, From 535d111c8c7e1f74e40bfc26c88b3e298837f9d3 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Fri, 22 Mar 2019 11:46:27 +0100 Subject: [PATCH 04/20] Rename some parts of the release checker --- packages/cli/src/cliEntry.js | 10 +++++----- .../getLatestRelease.js | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename packages/cli/src/tools/{getLatestRelease => releaseChecker}/getLatestRelease.js (100%) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index 278191931..a9c4d7d41 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -20,7 +20,7 @@ import assertRequiredOptions from './tools/assertRequiredOptions'; import {logger} from '@react-native-community/cli-tools'; import {setProjectDir} from './tools/packageManager'; import findPlugins from './tools/findPlugins'; -import getNewerReleaseDataIfAvailable from './tools/getLatestRelease/getLatestRelease'; +import getLatestRelease from './tools/releaseChecker/getLatestRelease'; import pkgJson from '../package.json'; import loadConfig from './tools/config'; @@ -187,12 +187,12 @@ async function setupAndRun() { logger.setVerbose(commander.verbose); - const newerRelease = await getNewerReleaseDataIfAvailable(); - if (newerRelease) { + const latestRelease = await getLatestRelease(); + if (latestRelease) { logger.info('A newer version of React Native is available!'); logger.info(''); - logger.info(`Version: ${newerRelease.tag_name}`); - logger.info(`Changelog: ${newerRelease.html_url}`); + logger.info(`Version: ${latestRelease.tag_name}`); + logger.info(`Changelog: ${latestRelease.html_url}`); logger.info(''); logger.info('Upgrade to the newer version by running react-native upgrade'); } diff --git a/packages/cli/src/tools/getLatestRelease/getLatestRelease.js b/packages/cli/src/tools/releaseChecker/getLatestRelease.js similarity index 100% rename from packages/cli/src/tools/getLatestRelease/getLatestRelease.js rename to packages/cli/src/tools/releaseChecker/getLatestRelease.js From 02f275a7974586e6d03e61505e6bb5a8af425d22 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Mon, 25 Mar 2019 18:52:19 +0100 Subject: [PATCH 05/20] Add new RN printing utility `printNewRelease` will output to console a notification when a newer version of React Native is available. --- packages/cli/src/cliEntry.js | 15 +++-- .../tools/releaseChecker/getLatestRelease.js | 14 +--- .../tools/releaseChecker/printNewRelease.js | 23 +++++++ yarn.lock | 66 +++++++++++++++++++ 4 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 packages/cli/src/tools/releaseChecker/printNewRelease.js diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index a9c4d7d41..222a15951 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -21,6 +21,7 @@ import {logger} from '@react-native-community/cli-tools'; import {setProjectDir} from './tools/packageManager'; import findPlugins from './tools/findPlugins'; import getLatestRelease from './tools/releaseChecker/getLatestRelease'; +import printNewRelease from './tools/releaseChecker/printNewRelease'; import pkgJson from '../package.json'; import loadConfig from './tools/config'; @@ -187,14 +188,14 @@ async function setupAndRun() { logger.setVerbose(commander.verbose); - const latestRelease = await getLatestRelease(); + const {version: currentVersion} = require(path.join( + ctx.root, + 'node_modules/react-native/package.json', + )); + const latestRelease = await getLatestRelease(currentVersion); + if (latestRelease) { - logger.info('A newer version of React Native is available!'); - logger.info(''); - logger.info(`Version: ${latestRelease.tag_name}`); - logger.info(`Changelog: ${latestRelease.html_url}`); - logger.info(''); - logger.info('Upgrade to the newer version by running react-native upgrade'); + printNewRelease(latestRelease, currentVersion); } } diff --git a/packages/cli/src/tools/releaseChecker/getLatestRelease.js b/packages/cli/src/tools/releaseChecker/getLatestRelease.js index b0247d72f..fb08af9a3 100644 --- a/packages/cli/src/tools/releaseChecker/getLatestRelease.js +++ b/packages/cli/src/tools/releaseChecker/getLatestRelease.js @@ -7,7 +7,7 @@ import semver from 'semver'; import fs from 'fs'; import logger from '../logger'; -type Release = { +export type Release = { tag_name: string, html_url: string, draft: boolean, @@ -24,10 +24,9 @@ type Release = { * If the latest release is not newer or if it's a prerelease, the function * will return null. */ -export default (async function() { +export default (async function(currentVersion: string) { logger.debug('Checking for a newer version of React Native'); try { - const currentVersion = require(getRNPkgJsonPath()).version; logger.debug(`Current version: ${currentVersion}`); const cache = cacheManager.get(); @@ -128,15 +127,6 @@ function getLatestRelease(eTag: ?string) { }); } -const getRNPkgJsonPath = function() { - return path.resolve( - process.cwd(), - 'node_modules', - 'react-native', - 'package.json', - ); -}; - const cacheManager = { get: (): ?{release: Release, eTag: string} => { try { diff --git a/packages/cli/src/tools/releaseChecker/printNewRelease.js b/packages/cli/src/tools/releaseChecker/printNewRelease.js new file mode 100644 index 000000000..88e6467f2 --- /dev/null +++ b/packages/cli/src/tools/releaseChecker/printNewRelease.js @@ -0,0 +1,23 @@ +/** + * @flow + */ +import boxen from 'boxen'; +import chalk from 'chalk'; +import type {Release} from './getLatestRelease'; + +/** + * Notifies the user that a newer version of React Native is available. + */ +export default function(latestRelease: Release, currentVersion: string) { + console.log( + boxen( + 'A newer release of React Native is available!\n\n' + + `Current: v${currentVersion}\n` + + `Latest: ${chalk.green(latestRelease.tag_name)}\n` + + `Changelog: ${chalk.underline(latestRelease.html_url)}\n\n` + + `Run ${chalk.blue('react-native upgrade')} to ` + + 'upgrade to the latest version', + {padding: 1, margin: 1, borderStyle: 'round', borderColor: 'yellow'}, + ), + ); +} diff --git a/yarn.lock b/yarn.lock index a72c5c0fb..3d7cdd45a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2119,6 +2119,13 @@ ajv@^6.5.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== + dependencies: + string-width "^3.0.0" + ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -2148,6 +2155,11 @@ ansi-regex@^4.0.0, ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -2518,6 +2530,18 @@ bluebird@~3.4.1: version "3.4.7" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= +boxen@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-3.0.0.tgz#2e229f603c9c1da9d2966b7e9a5681eb692eca23" + integrity sha512-6BI51DCC62Ylgv78Kfn+MHkyPwSlhulks+b+wz7bK1vsTFgbSEy/E1DOxx1wjf/0YdkrfPUMh9NoaW419M7csQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.0.0" + chalk "^2.4.2" + cli-boxes "^2.0.0" + string-width "^3.0.0" + term-size "^1.2.0" + widest-line "^2.0.0" bplist-creator@0.0.7: version "0.0.7" @@ -2843,6 +2867,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +cli-boxes@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.0.0.tgz#de5eb5ce7462833133e85f5710fabb38377e9333" + integrity sha512-P46J1Wf3BVD0E5plybtf6g/NtHYAUlOIt7w3ou/Ova/p7dJPdukPV4yp+BF8dpmnnk45XlMzn+x9kfzyucKzrg== + cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -3551,6 +3580,11 @@ electron-to-chromium@^1.3.103: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.103.tgz#a695777efdbc419cad6cbb0e58458251302cd52f" integrity sha512-tObPqGmY9X8MUM8i3MEimYmbnLLf05/QV5gPlkR8MQ3Uj8G8B2govE1U4cQcBYtv3ymck9Y8cIOu4waoiykMZQ== +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + encodeurl@~1.0.1, encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -8559,6 +8593,14 @@ string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" string_decoder@~1.1.1: version "1.1.1" @@ -8588,6 +8630,17 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-bom@3.0.0, strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -8728,6 +8781,13 @@ temp@0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" +term-size@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= + dependencies: + execa "^0.7.0" + test-exclude@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.1.0.tgz#6ba6b25179d2d38724824661323b73e03c0c1de1" @@ -9213,6 +9273,12 @@ windows-release@^3.1.0: integrity sha512-hBb7m7acFgQPQc222uEQTmdcGLeBmQLNLFIh0rDk3CwFOBrfjefLzEfEfmpMq8Af/n/GnFf3eYf203FY1PmudA== dependencies: execa "^0.10.0" +widest-line@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" + integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== + dependencies: + string-width "^2.1.1" winston@0.8.x: version "0.8.3" From e589b172f79bb55d1a5a6f84c2c0c26efca7661c Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 30 Mar 2019 16:22:59 +0100 Subject: [PATCH 06/20] Replace boxen with logger, simplify message --- .../tools/releaseChecker/printNewRelease.js | 18 ++--- yarn.lock | 66 ------------------- 2 files changed, 7 insertions(+), 77 deletions(-) diff --git a/packages/cli/src/tools/releaseChecker/printNewRelease.js b/packages/cli/src/tools/releaseChecker/printNewRelease.js index 88e6467f2..f5758a233 100644 --- a/packages/cli/src/tools/releaseChecker/printNewRelease.js +++ b/packages/cli/src/tools/releaseChecker/printNewRelease.js @@ -1,23 +1,19 @@ /** * @flow */ -import boxen from 'boxen'; import chalk from 'chalk'; +import logger from '../logger'; import type {Release} from './getLatestRelease'; /** * Notifies the user that a newer version of React Native is available. */ export default function(latestRelease: Release, currentVersion: string) { - console.log( - boxen( - 'A newer release of React Native is available!\n\n' + - `Current: v${currentVersion}\n` + - `Latest: ${chalk.green(latestRelease.tag_name)}\n` + - `Changelog: ${chalk.underline(latestRelease.html_url)}\n\n` + - `Run ${chalk.blue('react-native upgrade')} to ` + - 'upgrade to the latest version', - {padding: 1, margin: 1, borderStyle: 'round', borderColor: 'yellow'}, - ), + logger.info( + 'Your current version of React Native is out of date. ' + + `The latest version is ${latestRelease.tag_name}, ` + + `while you're on v${currentVersion}`, ); + logger.info(`Changelog: ${chalk.underline(latestRelease.html_url)}`); + logger.info(`To upgrade, run ${chalk.bold('react-native upgrade')}`); } diff --git a/yarn.lock b/yarn.lock index 3d7cdd45a..a72c5c0fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2119,13 +2119,6 @@ ajv@^6.5.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-align@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" - integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== - dependencies: - string-width "^3.0.0" - ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -2155,11 +2148,6 @@ ansi-regex@^4.0.0, ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -2530,18 +2518,6 @@ bluebird@~3.4.1: version "3.4.7" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= -boxen@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-3.0.0.tgz#2e229f603c9c1da9d2966b7e9a5681eb692eca23" - integrity sha512-6BI51DCC62Ylgv78Kfn+MHkyPwSlhulks+b+wz7bK1vsTFgbSEy/E1DOxx1wjf/0YdkrfPUMh9NoaW419M7csQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^5.0.0" - chalk "^2.4.2" - cli-boxes "^2.0.0" - string-width "^3.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" bplist-creator@0.0.7: version "0.0.7" @@ -2867,11 +2843,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -cli-boxes@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.0.0.tgz#de5eb5ce7462833133e85f5710fabb38377e9333" - integrity sha512-P46J1Wf3BVD0E5plybtf6g/NtHYAUlOIt7w3ou/Ova/p7dJPdukPV4yp+BF8dpmnnk45XlMzn+x9kfzyucKzrg== - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -3580,11 +3551,6 @@ electron-to-chromium@^1.3.103: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.103.tgz#a695777efdbc419cad6cbb0e58458251302cd52f" integrity sha512-tObPqGmY9X8MUM8i3MEimYmbnLLf05/QV5gPlkR8MQ3Uj8G8B2govE1U4cQcBYtv3ymck9Y8cIOu4waoiykMZQ== -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - encodeurl@~1.0.1, encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -8593,14 +8559,6 @@ string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" string_decoder@~1.1.1: version "1.1.1" @@ -8630,17 +8588,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-bom@3.0.0, strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -8781,13 +8728,6 @@ temp@0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= - dependencies: - execa "^0.7.0" - test-exclude@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.1.0.tgz#6ba6b25179d2d38724824661323b73e03c0c1de1" @@ -9273,12 +9213,6 @@ windows-release@^3.1.0: integrity sha512-hBb7m7acFgQPQc222uEQTmdcGLeBmQLNLFIh0rDk3CwFOBrfjefLzEfEfmpMq8Af/n/GnFf3eYf203FY1PmudA== dependencies: execa "^0.10.0" -widest-line@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== - dependencies: - string-width "^2.1.1" winston@0.8.x: version "0.8.3" From 76dd8cc57ae4ae60492c5458a152edb4b7ed78f3 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 30 Mar 2019 17:25:00 +0100 Subject: [PATCH 07/20] Extract release cache manager to own file --- .../tools/releaseChecker/getLatestRelease.js | 51 ++++--------------- .../releaseChecker/releaseCacheManager.js | 49 ++++++++++++++++++ 2 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 packages/cli/src/tools/releaseChecker/releaseCacheManager.js diff --git a/packages/cli/src/tools/releaseChecker/getLatestRelease.js b/packages/cli/src/tools/releaseChecker/getLatestRelease.js index fb08af9a3..16f85db24 100644 --- a/packages/cli/src/tools/releaseChecker/getLatestRelease.js +++ b/packages/cli/src/tools/releaseChecker/getLatestRelease.js @@ -1,11 +1,10 @@ /** * @flow */ -import path from 'path'; import https from 'https'; import semver from 'semver'; -import fs from 'fs'; import logger from '../logger'; +import cacheManager from './releaseCacheManager'; export type Release = { tag_name: string, @@ -29,12 +28,12 @@ export default (async function(currentVersion: string) { try { logger.debug(`Current version: ${currentVersion}`); - const cache = cacheManager.get(); - let eTag; - if (cache) { - eTag = cache.eTag; + const eTag = cacheManager.get('eTag'); + const release: Release = cacheManager.get('release'); + + if (release) { logger.debug( - `Cached release version: ${semver.coerce(cache.release.tag_name)}`, + `Cached release version: ${semver.coerce(release.tag_name)}`, ); } @@ -52,16 +51,13 @@ export default (async function(currentVersion: string) { `Remote release version: ${semver.coerce(latestRelease.tag_name)}`, ); // Update the cache. - cacheManager.set({ - release: latestRelease, - eTag: response.eTag, - }); + cacheManager.set('eTag', response.eTag); + cacheManager.set('release', latestRelease); } else { // Response status is 304, meaning that our cached release data // is the most recent release available. logger.debug('Cache is up-to-date with GitHub releases'); - // $FlowFixMe: We know at this point that cache is not null - latestRelease = cache.release; + latestRelease = release; } const latestVersion = semver.coerce(latestRelease.tag_name); @@ -126,32 +122,3 @@ function getLatestRelease(eTag: ?string) { .on('error', error => reject(error)); }); } - -const cacheManager = { - get: (): ?{release: Release, eTag: string} => { - try { - const cacheRaw = fs.readFileSync( - path.resolve(__dirname, '.cache'), - 'utf8', - ); - const cache = JSON.parse(cacheRaw); - logger.debug(`Found release cache ${cache.eTag}`); - return cache; - } catch (e) { - logger.debug('No release cache found'); - return null; - } - }, - - set: (releaseData: {release: Release, eTag: string}) => { - logger.debug( - `Saving release ${semver.coerce(releaseData.release.tag_name)} as ${ - releaseData.eTag - } to cache`, - ); - fs.writeFileSync( - path.resolve(__dirname, '.cache'), - JSON.stringify(releaseData), - ); - }, -}; diff --git a/packages/cli/src/tools/releaseChecker/releaseCacheManager.js b/packages/cli/src/tools/releaseChecker/releaseCacheManager.js new file mode 100644 index 000000000..e349f2252 --- /dev/null +++ b/packages/cli/src/tools/releaseChecker/releaseCacheManager.js @@ -0,0 +1,49 @@ +/** + * @flow + */ +import path from 'path'; +import fs from 'fs'; +import logger from '../logger'; + +type ReleaseCacheKey = 'eTag' | 'release' | 'lastChecked'; + +function loadCache(): ?Object { + try { + const cacheRaw = fs.readFileSync(path.resolve(__dirname, '.cache'), 'utf8'); + const cache = JSON.parse(cacheRaw); + return cache; + } catch (e) { + if (e.code === 'ENOENT') { + // Create cache file since it doesn't exist. + saveCache({}); + } + logger.debug('No release cache found'); + } +} + +function saveCache(cache: Object) { + fs.writeFileSync( + path.resolve(__dirname, '.cache'), + JSON.stringify(cache, null, 2), + ); +} + +function get(key: ReleaseCacheKey): any { + const cache = loadCache(); + if (cache) { + return cache[key]; + } +} + +function set(key: ReleaseCacheKey, value: any) { + const cache = loadCache(); + if (cache) { + cache[key] = value; + saveCache(cache); + } +} + +export default { + get, + set, +}; From 8146945fe4242b153f38e988d1beaf0872573a15 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 30 Mar 2019 17:25:37 +0100 Subject: [PATCH 08/20] Add one week interval to new printNewRelease --- .../tools/releaseChecker/printNewRelease.js | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/tools/releaseChecker/printNewRelease.js b/packages/cli/src/tools/releaseChecker/printNewRelease.js index f5758a233..8311fd14d 100644 --- a/packages/cli/src/tools/releaseChecker/printNewRelease.js +++ b/packages/cli/src/tools/releaseChecker/printNewRelease.js @@ -4,16 +4,25 @@ import chalk from 'chalk'; import logger from '../logger'; import type {Release} from './getLatestRelease'; +import cacheManager from './releaseCacheManager'; /** * Notifies the user that a newer version of React Native is available. */ export default function(latestRelease: Release, currentVersion: string) { - logger.info( - 'Your current version of React Native is out of date. ' + - `The latest version is ${latestRelease.tag_name}, ` + - `while you're on v${currentVersion}`, - ); - logger.info(`Changelog: ${chalk.underline(latestRelease.html_url)}`); - logger.info(`To upgrade, run ${chalk.bold('react-native upgrade')}`); + const aWeek = 7 * 24 * 60 * 60 * 1000; // 7 days. + + const lastChecked = cacheManager.get('lastChecked'); + const now = new Date(); + if (!lastChecked || now - new Date(lastChecked) > aWeek) { + logger.info( + 'Your current version of React Native is out of date. ' + + `The latest version is ${latestRelease.tag_name}, ` + + `while you're on v${currentVersion}`, + ); + logger.info(`Changelog: ${chalk.underline(latestRelease.html_url)}`); + logger.info(`To upgrade, run ${chalk.bold('react-native upgrade')}`); + + cacheManager.set('lastChecked', now.toISOString()); + } } From 060ceceacd46819edbc4a25dc5884d198da4260f Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 30 Mar 2019 18:17:32 +0100 Subject: [PATCH 09/20] Force RN version checking before command execution --- packages/cli/src/cliEntry.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index 222a15951..1d0c9a41c 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -171,6 +171,18 @@ async function setupAndRun() { setProjectDir(ctx.root); + // New version check must occur before `commander.parse` to ensure that + // the message of a new release happens before anything else. + const {version: currentVersion} = require(path.join( + ctx.root, + 'node_modules/react-native/package.json', + )); + const latestRelease = await getLatestRelease(currentVersion); + + if (latestRelease) { + printNewRelease(latestRelease, currentVersion); + } + [...commands, ...ctx.commands].forEach(command => addCommand(command, ctx)); commander.parse(process.argv); @@ -187,16 +199,6 @@ async function setupAndRun() { } logger.setVerbose(commander.verbose); - - const {version: currentVersion} = require(path.join( - ctx.root, - 'node_modules/react-native/package.json', - )); - const latestRelease = await getLatestRelease(currentVersion); - - if (latestRelease) { - printNewRelease(latestRelease, currentVersion); - } } export default { From 8956aaa8446c9de698cc58dffb159824754f2f43 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 30 Mar 2019 20:50:21 +0100 Subject: [PATCH 10/20] Check for new releases against rn-diff-purge --- .../tools/releaseChecker/getLatestRelease.js | 123 +++++++++--------- .../tools/releaseChecker/printNewRelease.js | 8 +- .../releaseChecker/releaseCacheManager.js | 2 +- 3 files changed, 67 insertions(+), 66 deletions(-) diff --git a/packages/cli/src/tools/releaseChecker/getLatestRelease.js b/packages/cli/src/tools/releaseChecker/getLatestRelease.js index 16f85db24..93d43e508 100644 --- a/packages/cli/src/tools/releaseChecker/getLatestRelease.js +++ b/packages/cli/src/tools/releaseChecker/getLatestRelease.js @@ -7,13 +7,8 @@ import logger from '../logger'; import cacheManager from './releaseCacheManager'; export type Release = { - tag_name: string, - html_url: string, - draft: boolean, - prerelease: boolean, - created_at: string, - published_at: string, - body: string, + version: string, + changelogUrl: string, }; /** @@ -21,84 +16,91 @@ export type Release = { * if it exists, returns the release data. * * If the latest release is not newer or if it's a prerelease, the function - * will return null. + * will return undefined. */ export default (async function(currentVersion: string) { logger.debug('Checking for a newer version of React Native'); try { logger.debug(`Current version: ${currentVersion}`); - const eTag = cacheManager.get('eTag'); - const release: Release = cacheManager.get('release'); + const cachedLatest: string = cacheManager.get('latestVersion'); - if (release) { - logger.debug( - `Cached release version: ${semver.coerce(release.tag_name)}`, - ); + if (cachedLatest) { + logger.debug(`Cached release version: ${cachedLatest}`); } logger.debug('Checking for newer releases on GitHub'); - const response: Response = await getLatestRelease(eTag); + const eTag = cacheManager.get('eTag'); + const latestVersion = await getLatestRnDiffPurgeVersion(eTag); + logger.debug(`Latest release: ${latestVersion}`); - let latestRelease; if ( - response.statusCode === 200 && - response.release && - !response.release.prerelease + semver.compare(latestVersion, currentVersion) === 1 && + !semver.prerelease(latestVersion) ) { - latestRelease = response.release; - logger.debug( - `Remote release version: ${semver.coerce(latestRelease.tag_name)}`, - ); - // Update the cache. - cacheManager.set('eTag', response.eTag); - cacheManager.set('release', latestRelease); - } else { - // Response status is 304, meaning that our cached release data - // is the most recent release available. - logger.debug('Cache is up-to-date with GitHub releases'); - latestRelease = release; - } - - const latestVersion = semver.coerce(latestRelease.tag_name); - logger.debug(`Latest stable release: ${latestVersion}`); - if (semver.compare(latestVersion, currentVersion) === 1) { - return latestRelease; - } else { - return null; + return { + version: latestVersion, + changelogUrl: buildChangelogUrl(latestVersion), + }; } } catch (e) { logger.debug( 'Something went wrong with remote version checking, moving on', ); - return null; } }); +function buildChangelogUrl(version: string) { + return `https://github.com/facebook/react-native/releases/tag/v${version}`; +} + +/** + * Returns the most recent React Native version available to upgrade to. + */ +async function getLatestRnDiffPurgeVersion(eTag: ?string): Promise { + const options = { + hostname: 'api.github.com', + path: '/repos/react-native-community/rn-diff-purge/tags', + // https://developer.github.com/v3/#user-agent-required + headers: ({'User-Agent': 'React-Native-CLI'}: Headers), + }; + + if (eTag) { + options.headers['If-None-Match'] = eTag; + } + + const response = await httpsGet(options); + + // Remote is newer. + if (response.statusCode === 200) { + const latestVersion = JSON.parse(response.body)[0].name.substring(8); + + // Update cache only if newer release is stable. + if (!semver.prerelease(latestVersion)) { + logger.debug(`Saving ${response.eTag} to cache`); + cacheManager.set('eTag', response.eTag); + cacheManager.set('latestVersion', latestVersion); + } + + return latestVersion; + } + + // Cache is still valid. + if (response.statusCode === 304) { + return cacheManager.get('latestVersion'); + } + + // Should be returned only if something went wrong. + return '0.0.0'; +} + type Headers = { 'User-Agent': string, [header: string]: string, }; -type Response = { - eTag: string, - release: ?Release, - statusCode: number, -}; - -function getLatestRelease(eTag: ?string) { - return new Promise((resolve, reject) => { - const options = { - hostname: 'api.github.com', - path: '/repos/facebook/react-native/releases/latest', - // https://developer.github.com/v3/#user-agent-required - headers: ({'User-Agent': 'React-Native-CLI'}: Headers), - }; - - if (eTag) { - options.headers['If-None-Match'] = eTag; - } - +function httpsGet(options: any) { + return new Promise((resolve, reject) => { https .get(options, result => { let body = ''; @@ -110,8 +112,7 @@ function getLatestRelease(eTag: ?string) { result.on('end', () => { resolve({ - // If status code is 304, then body will be empty. - release: body ? JSON.parse(body) : undefined, + body, eTag: result.headers.etag, statusCode: result.statusCode, }); diff --git a/packages/cli/src/tools/releaseChecker/printNewRelease.js b/packages/cli/src/tools/releaseChecker/printNewRelease.js index 8311fd14d..2d1ec9bc5 100644 --- a/packages/cli/src/tools/releaseChecker/printNewRelease.js +++ b/packages/cli/src/tools/releaseChecker/printNewRelease.js @@ -10,17 +10,17 @@ import cacheManager from './releaseCacheManager'; * Notifies the user that a newer version of React Native is available. */ export default function(latestRelease: Release, currentVersion: string) { - const aWeek = 7 * 24 * 60 * 60 * 1000; // 7 days. + const aWeek = 7 * 24 * 60 * 60 * 1000; const lastChecked = cacheManager.get('lastChecked'); const now = new Date(); if (!lastChecked || now - new Date(lastChecked) > aWeek) { logger.info( 'Your current version of React Native is out of date. ' + - `The latest version is ${latestRelease.tag_name}, ` + - `while you're on v${currentVersion}`, + `The latest version is ${latestRelease.version}, ` + + `while you're on ${currentVersion}`, ); - logger.info(`Changelog: ${chalk.underline(latestRelease.html_url)}`); + logger.info(`Changelog: ${chalk.underline(latestRelease.changelogUrl)}`); logger.info(`To upgrade, run ${chalk.bold('react-native upgrade')}`); cacheManager.set('lastChecked', now.toISOString()); diff --git a/packages/cli/src/tools/releaseChecker/releaseCacheManager.js b/packages/cli/src/tools/releaseChecker/releaseCacheManager.js index e349f2252..8ec486088 100644 --- a/packages/cli/src/tools/releaseChecker/releaseCacheManager.js +++ b/packages/cli/src/tools/releaseChecker/releaseCacheManager.js @@ -5,7 +5,7 @@ import path from 'path'; import fs from 'fs'; import logger from '../logger'; -type ReleaseCacheKey = 'eTag' | 'release' | 'lastChecked'; +type ReleaseCacheKey = 'eTag' | 'lastChecked' | 'latestVersion'; function loadCache(): ?Object { try { From 2b923ad976378842f8a799486fd247ed93013f1a Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 30 Mar 2019 20:56:22 +0100 Subject: [PATCH 11/20] Improve logging for printNewRelease --- packages/cli/src/tools/releaseChecker/printNewRelease.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/cli/src/tools/releaseChecker/printNewRelease.js b/packages/cli/src/tools/releaseChecker/printNewRelease.js index 2d1ec9bc5..cc98d88f3 100644 --- a/packages/cli/src/tools/releaseChecker/printNewRelease.js +++ b/packages/cli/src/tools/releaseChecker/printNewRelease.js @@ -24,5 +24,10 @@ export default function(latestRelease: Release, currentVersion: string) { logger.info(`To upgrade, run ${chalk.bold('react-native upgrade')}`); cacheManager.set('lastChecked', now.toISOString()); + } else { + logger.debug( + `Last time notified a newer version: ${now.toDateString()}, ` + + 'skipping this time', + ); } } From 34accf0ae50a114e4a4a34222ca883e608a1f9c3 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 30 Mar 2019 21:03:27 +0100 Subject: [PATCH 12/20] Safeguard new release check in cliEntry --- packages/cli/src/cliEntry.js | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index 1d0c9a41c..7ea9371df 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -19,7 +19,6 @@ import init from './commands/init/initCompat'; import assertRequiredOptions from './tools/assertRequiredOptions'; import {logger} from '@react-native-community/cli-tools'; import {setProjectDir} from './tools/packageManager'; -import findPlugins from './tools/findPlugins'; import getLatestRelease from './tools/releaseChecker/getLatestRelease'; import printNewRelease from './tools/releaseChecker/printNewRelease'; import pkgJson from '../package.json'; @@ -171,16 +170,25 @@ async function setupAndRun() { setProjectDir(ctx.root); - // New version check must occur before `commander.parse` to ensure that - // the message of a new release happens before anything else. - const {version: currentVersion} = require(path.join( - ctx.root, - 'node_modules/react-native/package.json', - )); - const latestRelease = await getLatestRelease(currentVersion); - - if (latestRelease) { - printNewRelease(latestRelease, currentVersion); + try { + // New version check must occur before `commander.parse` to ensure that + // the message of a new release happens before anything else. + const {version: currentVersion} = require(path.join( + ctx.root, + 'node_modules/react-native/package.json', + )); + const latestRelease = await getLatestRelease(currentVersion); + + if (latestRelease) { + printNewRelease(latestRelease, currentVersion); + } + } catch (_ignored) { + // We let the flow continue as this component is not vital for the rest of + // the CLI. + logger.debug( + 'Cannot detect current version of React Native, ' + + 'skipping check for a newer release', + ); } [...commands, ...ctx.commands].forEach(command => addCommand(command, ctx)); From dc1c4961010b93325b865415ff7bced971f00a63 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Tue, 14 May 2019 11:55:44 +0200 Subject: [PATCH 13/20] Log getLatestRelease error in verbose mode --- packages/cli/src/tools/releaseChecker/getLatestRelease.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/tools/releaseChecker/getLatestRelease.js b/packages/cli/src/tools/releaseChecker/getLatestRelease.js index 93d43e508..51663699a 100644 --- a/packages/cli/src/tools/releaseChecker/getLatestRelease.js +++ b/packages/cli/src/tools/releaseChecker/getLatestRelease.js @@ -47,6 +47,7 @@ export default (async function(currentVersion: string) { logger.debug( 'Something went wrong with remote version checking, moving on', ); + logger.debug(e); } }); From 83463fac46d3b9d808b9e7d1a6dc4b9038c710e5 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Tue, 14 May 2019 12:00:00 +0200 Subject: [PATCH 14/20] Name printNewRelease function --- packages/cli/src/tools/releaseChecker/printNewRelease.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/tools/releaseChecker/printNewRelease.js b/packages/cli/src/tools/releaseChecker/printNewRelease.js index cc98d88f3..322fbde0d 100644 --- a/packages/cli/src/tools/releaseChecker/printNewRelease.js +++ b/packages/cli/src/tools/releaseChecker/printNewRelease.js @@ -9,7 +9,10 @@ import cacheManager from './releaseCacheManager'; /** * Notifies the user that a newer version of React Native is available. */ -export default function(latestRelease: Release, currentVersion: string) { +export default function printNewRelease( + latestRelease: Release, + currentVersion: string, +) { const aWeek = 7 * 24 * 60 * 60 * 1000; const lastChecked = cacheManager.get('lastChecked'); From 733c1dca2e0e852c63e153d3afbdb3d085f9275f Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Tue, 14 May 2019 12:07:20 +0200 Subject: [PATCH 15/20] Use resolveNodeModuleDir helper --- packages/cli/src/cliEntry.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index 7ea9371df..63f21bd65 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -19,6 +19,7 @@ import init from './commands/init/initCompat'; import assertRequiredOptions from './tools/assertRequiredOptions'; import {logger} from '@react-native-community/cli-tools'; import {setProjectDir} from './tools/packageManager'; +import resolveNodeModuleDir from './tools/config/resolveNodeModuleDir'; import getLatestRelease from './tools/releaseChecker/getLatestRelease'; import printNewRelease from './tools/releaseChecker/printNewRelease'; import pkgJson from '../package.json'; @@ -174,8 +175,8 @@ async function setupAndRun() { // New version check must occur before `commander.parse` to ensure that // the message of a new release happens before anything else. const {version: currentVersion} = require(path.join( - ctx.root, - 'node_modules/react-native/package.json', + resolveNodeModuleDir(ctx.root, 'react-native'), + 'package.json', )); const latestRelease = await getLatestRelease(currentVersion); From 953a5c0fb14c7852b9a9d22e97c7984099f47c2b Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Tue, 14 May 2019 15:13:10 +0200 Subject: [PATCH 16/20] Check for new release after setupAndRun --- packages/cli/src/cliEntry.js | 43 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index 63f21bd65..3aff3e471 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -142,6 +142,7 @@ const addCommand = (command: CommandT, ctx: ConfigT) => { async function run() { try { await setupAndRun(); + checkForNewRelease(); } catch (e) { handleError(e); } @@ -171,9 +172,27 @@ async function setupAndRun() { setProjectDir(ctx.root); + [...commands, ...ctx.commands].forEach(command => addCommand(command, ctx)); + + commander.parse(process.argv); + + if (commander.rawArgs.length === 2) { + commander.outputHelp(); + } + + // We handle --version as a special case like this because both `commander` + // and `yargs` append it to every command and we don't want to do that. + // E.g. outside command `init` has --version flag and we want to preserve it. + if (commander.args.length === 0 && commander.version === true) { + console.log(pkgJson.version); + } + + logger.setVerbose(commander.verbose); +} + +async function checkForNewRelease() { try { - // New version check must occur before `commander.parse` to ensure that - // the message of a new release happens before anything else. + const ctx = loadConfig(); const {version: currentVersion} = require(path.join( resolveNodeModuleDir(ctx.root, 'react-native'), 'package.json', @@ -183,31 +202,15 @@ async function setupAndRun() { if (latestRelease) { printNewRelease(latestRelease, currentVersion); } - } catch (_ignored) { + } catch (e) { // We let the flow continue as this component is not vital for the rest of // the CLI. logger.debug( 'Cannot detect current version of React Native, ' + 'skipping check for a newer release', ); + logger.debug(e); } - - [...commands, ...ctx.commands].forEach(command => addCommand(command, ctx)); - - commander.parse(process.argv); - - if (commander.rawArgs.length === 2) { - commander.outputHelp(); - } - - // We handle --version as a special case like this because both `commander` - // and `yargs` append it to every command and we don't want to do that. - // E.g. outside command `init` has --version flag and we want to preserve it. - if (commander.args.length === 0 && commander.version === true) { - console.log(pkgJson.version); - } - - logger.setVerbose(commander.verbose); } export default { From 56cee5a79ed167effdc581f54f4bc99e5c3034b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Tue, 14 May 2019 18:34:45 +0200 Subject: [PATCH 17/20] make it run after the command --- packages/cli/src/cliEntry.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index 3aff3e471..764c06a2c 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -110,16 +110,18 @@ const addCommand = (command: CommandT, ctx: ConfigT) => { const cmd = commander .command(command.name) .description(command.description) - .action(function handleAction(...args) { + .action(async function handleAction(...args) { const passedOptions = this.opts(); const argv: Array = Array.from(args).slice(0, -1); - Promise.resolve() - .then(() => { - assertRequiredOptions(options, passedOptions); - return command.func(argv, ctx, passedOptions); - }) - .catch(handleError); + try { + assertRequiredOptions(options, passedOptions); + await command.func(argv, ctx, passedOptions); + } catch (error) { + handleError(error); + } finally { + checkForNewRelease(); + } }); cmd.helpInformation = printHelpInformation.bind( @@ -142,7 +144,6 @@ const addCommand = (command: CommandT, ctx: ConfigT) => { async function run() { try { await setupAndRun(); - checkForNewRelease(); } catch (e) { handleError(e); } From 41e316ceb2b7eb6d4e7a6f5c6d8a6f121383172a Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 25 May 2019 09:35:50 +0200 Subject: [PATCH 18/20] Move cache age check before network fetch --- .../tools/releaseChecker/getLatestRelease.js | 8 ++++++ .../tools/releaseChecker/printNewRelease.js | 27 ++++++------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/tools/releaseChecker/getLatestRelease.js b/packages/cli/src/tools/releaseChecker/getLatestRelease.js index 51663699a..38cf1b1c9 100644 --- a/packages/cli/src/tools/releaseChecker/getLatestRelease.js +++ b/packages/cli/src/tools/releaseChecker/getLatestRelease.js @@ -29,6 +29,14 @@ export default (async function(currentVersion: string) { logger.debug(`Cached release version: ${cachedLatest}`); } + const aWeek = 7 * 24 * 60 * 60 * 1000; + const lastChecked = cacheManager.get('lastChecked'); + const now = new Date(); + if (lastChecked && now - new Date(lastChecked) < aWeek) { + logger.debug('Cached release is still recent, skipping remote check'); + return; + } + logger.debug('Checking for newer releases on GitHub'); const eTag = cacheManager.get('eTag'); const latestVersion = await getLatestRnDiffPurgeVersion(eTag); diff --git a/packages/cli/src/tools/releaseChecker/printNewRelease.js b/packages/cli/src/tools/releaseChecker/printNewRelease.js index 322fbde0d..aa56c91d8 100644 --- a/packages/cli/src/tools/releaseChecker/printNewRelease.js +++ b/packages/cli/src/tools/releaseChecker/printNewRelease.js @@ -13,24 +13,13 @@ export default function printNewRelease( latestRelease: Release, currentVersion: string, ) { - const aWeek = 7 * 24 * 60 * 60 * 1000; + logger.info( + 'Your current version of React Native is out of date. ' + + `The latest version is ${latestRelease.version}, ` + + `while you're on ${currentVersion}`, + ); + logger.info(`Changelog: ${chalk.underline(latestRelease.changelogUrl)}`); + logger.info(`To upgrade, run ${chalk.bold('react-native upgrade')}`); - const lastChecked = cacheManager.get('lastChecked'); - const now = new Date(); - if (!lastChecked || now - new Date(lastChecked) > aWeek) { - logger.info( - 'Your current version of React Native is out of date. ' + - `The latest version is ${latestRelease.version}, ` + - `while you're on ${currentVersion}`, - ); - logger.info(`Changelog: ${chalk.underline(latestRelease.changelogUrl)}`); - logger.info(`To upgrade, run ${chalk.bold('react-native upgrade')}`); - - cacheManager.set('lastChecked', now.toISOString()); - } else { - logger.debug( - `Last time notified a newer version: ${now.toDateString()}, ` + - 'skipping this time', - ); - } + cacheManager.set('lastChecked', new Date().toISOString()); } From 043640f1f875aada2103ff9e6a69aac3732dac29 Mon Sep 17 00:00:00 2001 From: Matei Radu Date: Sat, 25 May 2019 10:13:19 +0200 Subject: [PATCH 19/20] Create cache dir in os.homedir() --- .../releaseChecker/releaseCacheManager.js | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/tools/releaseChecker/releaseCacheManager.js b/packages/cli/src/tools/releaseChecker/releaseCacheManager.js index 8ec486088..6a90951e4 100644 --- a/packages/cli/src/tools/releaseChecker/releaseCacheManager.js +++ b/packages/cli/src/tools/releaseChecker/releaseCacheManager.js @@ -3,13 +3,19 @@ */ import path from 'path'; import fs from 'fs'; +import os from 'os'; import logger from '../logger'; +import loadConfig from '../config'; type ReleaseCacheKey = 'eTag' | 'lastChecked' | 'latestVersion'; function loadCache(): ?Object { try { - const cacheRaw = fs.readFileSync(path.resolve(__dirname, '.cache'), 'utf8'); + const {name} = require(path.join(loadConfig().root, 'package.json')); + const cacheRaw = fs.readFileSync( + path.resolve(getCacheRootPath(), name), + 'utf8', + ); const cache = JSON.parse(cacheRaw); return cache; } catch (e) { @@ -22,12 +28,27 @@ function loadCache(): ?Object { } function saveCache(cache: Object) { + const {name} = require(path.join(loadConfig().root, 'package.json')); fs.writeFileSync( - path.resolve(__dirname, '.cache'), + path.resolve(getCacheRootPath(), name), JSON.stringify(cache, null, 2), ); } +/** + * Returns the path string of `$HOME/.react-native-cli`. + * + * In case it doesn't exist, it will be created. + */ +function getCacheRootPath() { + const cachePath = path.resolve(os.homedir(), '.react-native-cli', 'cache'); + if (!fs.existsSync(cachePath)) { + fs.mkdirSync(cachePath); + } + + return cachePath; +} + function get(key: ReleaseCacheKey): any { const cache = loadCache(); if (cache) { From 7e50deed7fd2368094a61bee5f915a3b011bd5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 6 Jun 2019 12:45:50 +0200 Subject: [PATCH 20/20] avoid loading config twice --- packages/cli/src/cliEntry.js | 12 ++++----- .../tools/releaseChecker/getLatestRelease.js | 26 ++++++++++++------- .../tools/releaseChecker/printNewRelease.js | 13 +++++----- .../releaseChecker/releaseCacheManager.js | 23 ++++++++-------- packages/cli/src/tools/walk.js | 4 +-- 5 files changed, 42 insertions(+), 36 deletions(-) diff --git a/packages/cli/src/cliEntry.js b/packages/cli/src/cliEntry.js index 764c06a2c..1798f391e 100644 --- a/packages/cli/src/cliEntry.js +++ b/packages/cli/src/cliEntry.js @@ -120,7 +120,7 @@ const addCommand = (command: CommandT, ctx: ConfigT) => { } catch (error) { handleError(error); } finally { - checkForNewRelease(); + checkForNewRelease(ctx.root); } }); @@ -191,17 +191,17 @@ async function setupAndRun() { logger.setVerbose(commander.verbose); } -async function checkForNewRelease() { +async function checkForNewRelease(root: string) { try { - const ctx = loadConfig(); const {version: currentVersion} = require(path.join( - resolveNodeModuleDir(ctx.root, 'react-native'), + resolveNodeModuleDir(root, 'react-native'), 'package.json', )); - const latestRelease = await getLatestRelease(currentVersion); + const {name} = require(path.join(root, 'package.json')); + const latestRelease = await getLatestRelease(name, currentVersion); if (latestRelease) { - printNewRelease(latestRelease, currentVersion); + printNewRelease(name, latestRelease, currentVersion); } } catch (e) { // We let the flow continue as this component is not vital for the rest of diff --git a/packages/cli/src/tools/releaseChecker/getLatestRelease.js b/packages/cli/src/tools/releaseChecker/getLatestRelease.js index 38cf1b1c9..debf37f0e 100644 --- a/packages/cli/src/tools/releaseChecker/getLatestRelease.js +++ b/packages/cli/src/tools/releaseChecker/getLatestRelease.js @@ -18,19 +18,22 @@ export type Release = { * If the latest release is not newer or if it's a prerelease, the function * will return undefined. */ -export default (async function(currentVersion: string) { +export default async function getLatestRelease( + name: string, + currentVersion: string, +) { logger.debug('Checking for a newer version of React Native'); try { logger.debug(`Current version: ${currentVersion}`); - const cachedLatest: string = cacheManager.get('latestVersion'); + const cachedLatest = cacheManager.get(name, 'latestVersion'); if (cachedLatest) { logger.debug(`Cached release version: ${cachedLatest}`); } const aWeek = 7 * 24 * 60 * 60 * 1000; - const lastChecked = cacheManager.get('lastChecked'); + const lastChecked = cacheManager.get(name, 'lastChecked'); const now = new Date(); if (lastChecked && now - new Date(lastChecked) < aWeek) { logger.debug('Cached release is still recent, skipping remote check'); @@ -38,8 +41,8 @@ export default (async function(currentVersion: string) { } logger.debug('Checking for newer releases on GitHub'); - const eTag = cacheManager.get('eTag'); - const latestVersion = await getLatestRnDiffPurgeVersion(eTag); + const eTag = cacheManager.get(name, 'eTag'); + const latestVersion = await getLatestRnDiffPurgeVersion(name, eTag); logger.debug(`Latest release: ${latestVersion}`); if ( @@ -57,7 +60,7 @@ export default (async function(currentVersion: string) { ); logger.debug(e); } -}); +} function buildChangelogUrl(version: string) { return `https://github.com/facebook/react-native/releases/tag/v${version}`; @@ -66,7 +69,7 @@ function buildChangelogUrl(version: string) { /** * Returns the most recent React Native version available to upgrade to. */ -async function getLatestRnDiffPurgeVersion(eTag: ?string): Promise { +async function getLatestRnDiffPurgeVersion(name: string, eTag: ?string) { const options = { hostname: 'api.github.com', path: '/repos/react-native-community/rn-diff-purge/tags', @@ -87,8 +90,8 @@ async function getLatestRnDiffPurgeVersion(eTag: ?string): Promise { // Update cache only if newer release is stable. if (!semver.prerelease(latestVersion)) { logger.debug(`Saving ${response.eTag} to cache`); - cacheManager.set('eTag', response.eTag); - cacheManager.set('latestVersion', latestVersion); + cacheManager.set(name, 'eTag', response.eTag); + cacheManager.set(name, 'latestVersion', latestVersion); } return latestVersion; @@ -96,7 +99,10 @@ async function getLatestRnDiffPurgeVersion(eTag: ?string): Promise { // Cache is still valid. if (response.statusCode === 304) { - return cacheManager.get('latestVersion'); + const latestVersion = cacheManager.get(name, 'latestVersion'); + if (latestVersion) { + return latestVersion; + } } // Should be returned only if something went wrong. diff --git a/packages/cli/src/tools/releaseChecker/printNewRelease.js b/packages/cli/src/tools/releaseChecker/printNewRelease.js index aa56c91d8..cfababab7 100644 --- a/packages/cli/src/tools/releaseChecker/printNewRelease.js +++ b/packages/cli/src/tools/releaseChecker/printNewRelease.js @@ -10,16 +10,17 @@ import cacheManager from './releaseCacheManager'; * Notifies the user that a newer version of React Native is available. */ export default function printNewRelease( + name: string, latestRelease: Release, currentVersion: string, ) { logger.info( - 'Your current version of React Native is out of date. ' + - `The latest version is ${latestRelease.version}, ` + - `while you're on ${currentVersion}`, + `React Native v${ + latestRelease.version + } is now available (your project is running on v${currentVersion}).`, ); - logger.info(`Changelog: ${chalk.underline(latestRelease.changelogUrl)}`); - logger.info(`To upgrade, run ${chalk.bold('react-native upgrade')}`); + logger.info(`Changelog: ${chalk.dim.underline(latestRelease.changelogUrl)}.`); + logger.info(`To upgrade, run "${chalk.bold('react-native upgrade')}".`); - cacheManager.set('lastChecked', new Date().toISOString()); + cacheManager.set(name, 'lastChecked', new Date().toISOString()); } diff --git a/packages/cli/src/tools/releaseChecker/releaseCacheManager.js b/packages/cli/src/tools/releaseChecker/releaseCacheManager.js index 6a90951e4..193af6c33 100644 --- a/packages/cli/src/tools/releaseChecker/releaseCacheManager.js +++ b/packages/cli/src/tools/releaseChecker/releaseCacheManager.js @@ -4,14 +4,14 @@ import path from 'path'; import fs from 'fs'; import os from 'os'; +import mkdirp from 'mkdirp'; import logger from '../logger'; -import loadConfig from '../config'; type ReleaseCacheKey = 'eTag' | 'lastChecked' | 'latestVersion'; +type Cache = {[key: ReleaseCacheKey]: string}; -function loadCache(): ?Object { +function loadCache(name: string): ?Cache { try { - const {name} = require(path.join(loadConfig().root, 'package.json')); const cacheRaw = fs.readFileSync( path.resolve(getCacheRootPath(), name), 'utf8', @@ -21,14 +21,13 @@ function loadCache(): ?Object { } catch (e) { if (e.code === 'ENOENT') { // Create cache file since it doesn't exist. - saveCache({}); + saveCache(name, {}); } logger.debug('No release cache found'); } } -function saveCache(cache: Object) { - const {name} = require(path.join(loadConfig().root, 'package.json')); +function saveCache(name: string, cache: Cache) { fs.writeFileSync( path.resolve(getCacheRootPath(), name), JSON.stringify(cache, null, 2), @@ -43,24 +42,24 @@ function saveCache(cache: Object) { function getCacheRootPath() { const cachePath = path.resolve(os.homedir(), '.react-native-cli', 'cache'); if (!fs.existsSync(cachePath)) { - fs.mkdirSync(cachePath); + mkdirp(cachePath); } return cachePath; } -function get(key: ReleaseCacheKey): any { - const cache = loadCache(); +function get(name: string, key: ReleaseCacheKey): ?string { + const cache = loadCache(name); if (cache) { return cache[key]; } } -function set(key: ReleaseCacheKey, value: any) { - const cache = loadCache(); +function set(name: string, key: ReleaseCacheKey, value: string) { + const cache = loadCache(name); if (cache) { cache[key] = value; - saveCache(cache); + saveCache(name, cache); } } diff --git a/packages/cli/src/tools/walk.js b/packages/cli/src/tools/walk.js index 5fdc88848..fb0a81eba 100644 --- a/packages/cli/src/tools/walk.js +++ b/packages/cli/src/tools/walk.js @@ -4,13 +4,13 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @flow */ import fs from 'fs'; import path from 'path'; -function walk(current) { +function walk(current: string) { if (!fs.lstatSync(current).isDirectory()) { return [current]; }