mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
feat(cli) check and warn if npx react-native version is using an old cached version (#37510)
Summary:
Currently npx has a variety of caching strategies to avoid having to pull a version of the package from a registry. These are often unexpected to our users, who may fall behind. After looking at a variety of fancy approaches to dealing with this (the high end of which was intelligently forking npx to run `npx react-native@latest <args>`, the best possible tradeoff for time and simplicity was to warn the user when they weren't running the latest release:
{F999520817}
### Problem Details
On my laptop when you run `npx <package> <arguments>` this it eventually calls [libnpmexec](https://github.com/npm/cli/tree/0783cff9653928359a6c68c8fdf30b9fd02130c9/workspaces/libnpmexec), which applies this lookup [algorithm](https://github.com/npm/cli/blob/0783cff9653928359a6c68c8fdf30b9fd02130c9/workspaces/libnpmexec/lib/index.js#L39-L41) for `package@version`:
- is package available in local modules (npm root → `~/project/node_modules/<package>`)?. **Importantly it will walk all the way down to `/` looking for `node_modules/<package>`**.
- is package available in global modules (npm root -g → `/Users/blakef/.nvm/versions/node/v17.9.0/lib/node_modules`)?
- is package available in npx cache (`~/.npm/_npx`)?
- is package available in your registry? Download to the npx cache `~/.npm/_npx/<hash>/`
At this point you'll have a cached copy, which then has its bin script run with the arguments you originally provided.
### How this works against React-Native users
Users can get their development environment into a **persistent** pickle with a bunch of unintended side-effects of npx / npm exec’s caching model:
- **It matters where you run `npx react-native`**, since it’ll default to the version of react-native in a node package's folder. This works well for us in a React Native project, but not when initializing a project outside of a package folder.
- **Global and relative node_modules really matter**. If your users runs npx react-native init and they have a version of react-native installed globally, it’ll use that version.
- If the user has a `node_modules/react-native` installation anywhere in the directory hierarchy it’ll be used. For example if I run `npx react-native init Foobar` in `/home/blakef/src/example` , npx will look for versions of react-native like this before searching globals or the npx cache:
- /home/blakef/src/example/node_modules
- /home/blakef/src/node_modules
- /home/blakef/node_modules
- /home/node_modules
- /node_modules
**nvm just makes things harder** if your user switches between versions of node it can be hard to determine if they're affected by a globally installed version. Examples include having a `.nvmrc` file in the directory they run the command which transparently switches node version (and globals location).
## Changelog:
[General][Added] - Log a warning if npx react-native uses old cached version
Pull Request resolved: https://github.com/facebook/react-native/pull/37510
Test Plan: Ran this directly from the project, defining the `npm_lifecycle_event=npx` to mock directly running using `npx`.
Reviewed By: Andjeliko
Differential Revision: D46069419
Pulled By: blakef
fbshipit-source-id: 1c1af7f639c5312760a39a0828b89b7ddf2b5fda
This commit is contained in:
committed by
Facebook GitHub Bot
parent
b6d9217077
commit
bfca23a25d
Vendored
+60
-2
@@ -10,10 +10,68 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var cli = require('@react-native-community/cli');
|
||||
const {get} = require('https');
|
||||
const {URL} = require('url');
|
||||
const chalk = require('chalk');
|
||||
const cli = require('@react-native-community/cli');
|
||||
|
||||
const {version: currentVersion} = require('./package.json');
|
||||
|
||||
const isNpxRuntime = process.env.npm_lifecycle_event === 'npx';
|
||||
const DEFAULT_REGISTRY_HOST =
|
||||
process.env.npm_config_registry ?? 'https://registry.npmjs.org/';
|
||||
const HEAD = '1000.0.0';
|
||||
|
||||
async function getLatestVersion(registryHost = DEFAULT_REGISTRY_HOST) {
|
||||
return new Promise((res, rej) => {
|
||||
const url = new URL(registryHost);
|
||||
url.pathname = 'react-native/latest';
|
||||
get(url.toString(), resp => {
|
||||
const buffer = [];
|
||||
resp.on('data', data => buffer.push(data));
|
||||
resp.on('end', () => {
|
||||
try {
|
||||
res(JSON.parse(Buffer.concat(buffer).toString('utf8')).version);
|
||||
} catch (e) {
|
||||
rej(e);
|
||||
}
|
||||
});
|
||||
}).on('error', e => rej(e));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* npx react-native -> @react-native-comminity/cli
|
||||
*
|
||||
* Will perform a version check and warning if you're not running the latest community cli when executed using npx. If
|
||||
* you know what you're doing, you can skip this check:
|
||||
*
|
||||
* SKIP=true npx react-native ...
|
||||
*
|
||||
*/
|
||||
async function main() {
|
||||
if (isNpxRuntime && !process.env.SKIP && currentVersion !== HEAD) {
|
||||
try {
|
||||
const latest = await getLatestVersion();
|
||||
if (latest !== currentVersion) {
|
||||
const msg = `
|
||||
${chalk.bold.yellow('WARNING:')} You should run ${chalk.white.bold(
|
||||
'npx react-native@latest',
|
||||
)} to ensure you're always using the most current version of the CLI. NPX has cached version (${chalk.bold.yellow(
|
||||
currentVersion,
|
||||
)}) != current release (${chalk.bold.green(latest)})
|
||||
`;
|
||||
console.warn(msg);
|
||||
}
|
||||
} catch (_) {
|
||||
// Ignore errors, since it's a nice to have warning
|
||||
}
|
||||
}
|
||||
return cli.run();
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
cli.run();
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = cli;
|
||||
|
||||
Reference in New Issue
Block a user