mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
[0.76] Simplify key handling in start command (#46645)
Co-authored-by: Blake Friedman <blakef@meta.com>
This commit is contained in:
co-authored by
Blake Friedman
parent
6a24df7eaa
commit
5a0df6d0bf
@@ -26,6 +26,7 @@
|
||||
"@react-native/metro-babel-transformer": "0.76.0-rc.2",
|
||||
"chalk": "^4.0.0",
|
||||
"execa": "^5.1.1",
|
||||
"invariant": "^2.2.4",
|
||||
"metro": "^0.81.0-alpha.2",
|
||||
"metro-config": "^0.81.0-alpha.2",
|
||||
"metro-core": "^0.81.0-alpha.2",
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
import type {Config} from '@react-native-community/cli-types';
|
||||
import type TerminalReporter from 'metro/src/lib/TerminalReporter';
|
||||
|
||||
import {KeyPressHandler} from '../../utils/KeyPressHandler';
|
||||
import {logger} from '../../utils/logger';
|
||||
import OpenDebuggerKeyboardHandler from './OpenDebuggerKeyboardHandler';
|
||||
import chalk from 'chalk';
|
||||
import execa from 'execa';
|
||||
import invariant from 'invariant';
|
||||
import readline from 'readline';
|
||||
import {ReadStream} from 'tty';
|
||||
|
||||
const CTRL_C = '\u0003';
|
||||
const CTRL_D = '\u0004';
|
||||
@@ -33,6 +35,14 @@ const throttle = (callback: () => void, timeout: number) => {
|
||||
};
|
||||
};
|
||||
|
||||
type KeyEvent = {
|
||||
sequence: string,
|
||||
name: string,
|
||||
ctrl: boolean,
|
||||
meta: boolean,
|
||||
shift: boolean,
|
||||
};
|
||||
|
||||
export default function attachKeyHandlers({
|
||||
cliConfig,
|
||||
devServerUrl,
|
||||
@@ -52,6 +62,9 @@ export default function attachKeyHandlers({
|
||||
return;
|
||||
}
|
||||
|
||||
readline.emitKeypressEvents(process.stdin);
|
||||
setRawMode(true);
|
||||
|
||||
const execaOptions = {
|
||||
env: {FORCE_COLOR: chalk.supportsColor ? 'true' : 'false'},
|
||||
};
|
||||
@@ -66,12 +79,14 @@ export default function attachKeyHandlers({
|
||||
devServerUrl,
|
||||
});
|
||||
|
||||
const onPress = async (key: string) => {
|
||||
if (openDebuggerKeyboardHandler.maybeHandleTargetSelection(key)) {
|
||||
process.stdin.on('keypress', (str: string, key: KeyEvent) => {
|
||||
logger.debug(`Key pressed: ${key.sequence}`);
|
||||
|
||||
if (openDebuggerKeyboardHandler.maybeHandleTargetSelection(key.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (key.toLowerCase()) {
|
||||
switch (key.sequence) {
|
||||
case 'r':
|
||||
reload();
|
||||
break;
|
||||
@@ -104,21 +119,19 @@ export default function attachKeyHandlers({
|
||||
).stdout?.pipe(process.stdout);
|
||||
break;
|
||||
case 'j':
|
||||
await openDebuggerKeyboardHandler.handleOpenDebugger();
|
||||
// eslint-disable-next-line no-void
|
||||
void openDebuggerKeyboardHandler.handleOpenDebugger();
|
||||
break;
|
||||
case CTRL_C:
|
||||
case CTRL_D:
|
||||
openDebuggerKeyboardHandler.dismiss();
|
||||
logger.info('Stopping server');
|
||||
keyPressHandler.stopInterceptingKeyStrokes();
|
||||
setRawMode(false);
|
||||
process.stdin.pause();
|
||||
process.emit('SIGINT');
|
||||
process.exit();
|
||||
}
|
||||
};
|
||||
|
||||
const keyPressHandler = new KeyPressHandler(onPress);
|
||||
keyPressHandler.createInteractionListener();
|
||||
keyPressHandler.startInterceptingKeyStrokes();
|
||||
});
|
||||
|
||||
logger.log(
|
||||
[
|
||||
@@ -132,3 +145,11 @@ export default function attachKeyHandlers({
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
function setRawMode(enable: boolean) {
|
||||
invariant(
|
||||
process.stdin instanceof ReadStream,
|
||||
'process.stdin must be a readable stream to modify raw mode',
|
||||
);
|
||||
process.stdin.setRawMode(enable);
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import {CLIError} from './errors';
|
||||
import {logger} from './logger';
|
||||
|
||||
const CTRL_C = '\u0003';
|
||||
|
||||
/** An abstract key stroke interceptor. */
|
||||
export class KeyPressHandler {
|
||||
_isInterceptingKeyStrokes = false;
|
||||
_isHandlingKeyPress = false;
|
||||
_onPress: (key: string) => Promise<void>;
|
||||
|
||||
constructor(onPress: (key: string) => Promise<void>) {
|
||||
this._onPress = onPress;
|
||||
}
|
||||
|
||||
/** Start observing interaction pause listeners. */
|
||||
createInteractionListener(): ({pause: boolean, ...}) => void {
|
||||
// Support observing prompts.
|
||||
let wasIntercepting = false;
|
||||
|
||||
const listener = ({pause}: {pause: boolean, ...}) => {
|
||||
if (pause) {
|
||||
// Track if we were already intercepting key strokes before pausing, so we can
|
||||
// resume after pausing.
|
||||
wasIntercepting = this._isInterceptingKeyStrokes;
|
||||
this.stopInterceptingKeyStrokes();
|
||||
} else if (wasIntercepting) {
|
||||
// Only start if we were previously intercepting.
|
||||
this.startInterceptingKeyStrokes();
|
||||
}
|
||||
};
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
_handleKeypress = async (key: string): Promise<CLIError | void> => {
|
||||
// Prevent sending another event until the previous event has finished.
|
||||
if (this._isHandlingKeyPress && key !== CTRL_C) {
|
||||
return;
|
||||
}
|
||||
this._isHandlingKeyPress = true;
|
||||
try {
|
||||
logger.debug(`Key pressed: ${key}`);
|
||||
await this._onPress(key);
|
||||
} catch (error) {
|
||||
return new CLIError('There was an error with the key press handler.');
|
||||
} finally {
|
||||
this._isHandlingKeyPress = false;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/** Start intercepting all key strokes and passing them to the input `onPress` method. */
|
||||
startInterceptingKeyStrokes() {
|
||||
if (this._isInterceptingKeyStrokes) {
|
||||
return;
|
||||
}
|
||||
this._isInterceptingKeyStrokes = true;
|
||||
const {stdin} = process;
|
||||
// $FlowFixMe[prop-missing]
|
||||
stdin.setRawMode(true);
|
||||
stdin.resume();
|
||||
stdin.setEncoding('utf8');
|
||||
stdin.on('data', this._handleKeypress);
|
||||
}
|
||||
|
||||
/** Stop intercepting all key strokes. */
|
||||
stopInterceptingKeyStrokes() {
|
||||
if (!this._isInterceptingKeyStrokes) {
|
||||
return;
|
||||
}
|
||||
this._isInterceptingKeyStrokes = false;
|
||||
const {stdin} = process;
|
||||
stdin.removeListener('data', this._handleKeypress);
|
||||
// $FlowFixMe[prop-missing]
|
||||
stdin.setRawMode(false);
|
||||
stdin.resume();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user