mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53927 Changelog: [Internal] Fixes a bug that broke `unstable_prepareDebuggerShell` (and therefore the RNDT shell) in OSS. Reviewed By: vzaidman Differential Revision: D83178777 fbshipit-source-id: cfd93b14e2a94e36d8a3ee9f4605036a4279ef8c
184 lines
5.2 KiB
JavaScript
184 lines
5.2 KiB
JavaScript
/**
|
|
* 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
|
|
*/
|
|
|
|
import {
|
|
prepareDebuggerShellFromDotSlashFile,
|
|
spawnAndGetStderr,
|
|
} from './private/LaunchUtils';
|
|
|
|
const {spawn} = require('cross-spawn');
|
|
const path = require('path');
|
|
|
|
// The 'prebuilt' flavor will use the prebuilt shell binary (and the JavaScript embedded in it).
|
|
// The 'dev' flavor will use a stock Electron binary and run the shell code from the `electron/` directory.
|
|
type DebuggerShellFlavor = 'prebuilt' | 'dev';
|
|
|
|
const DEVTOOLS_BINARY_DOTSLASH_FILE = path.join(
|
|
__dirname,
|
|
'../../bin/react-native-devtools',
|
|
);
|
|
|
|
async function unstable_spawnDebuggerShellWithArgs(
|
|
args: string[],
|
|
{
|
|
mode = 'detached',
|
|
flavor = 'prebuilt',
|
|
prebuiltBinaryPath,
|
|
}: $ReadOnly<{
|
|
// In 'syncAndExit' mode, the current process will block until the spawned process exits, and then it will exit
|
|
// with the same exit code as the spawned process.
|
|
// In 'detached' mode, the spawned process will be detached from the current process and the current process will
|
|
// continue to run normally.
|
|
mode?: 'syncThenExit' | 'detached',
|
|
flavor?: DebuggerShellFlavor,
|
|
prebuiltBinaryPath?: string,
|
|
}> = {},
|
|
): Promise<void> {
|
|
const [binaryPath, baseArgs] = getShellBinaryAndArgs(
|
|
flavor,
|
|
prebuiltBinaryPath,
|
|
);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(binaryPath, [...baseArgs, ...args], {
|
|
stdio: 'inherit',
|
|
windowsHide: true,
|
|
detached: mode === 'detached',
|
|
});
|
|
if (mode === 'detached') {
|
|
child.on('spawn', () => {
|
|
resolve();
|
|
});
|
|
child.on('close', (code: number) => {
|
|
if (code !== 0) {
|
|
reject(
|
|
new Error(
|
|
`Failed to open debugger shell: exited with code ${code}`,
|
|
),
|
|
);
|
|
}
|
|
});
|
|
child.unref();
|
|
} else if (mode === 'syncThenExit') {
|
|
child.on('close', function (code, signal) {
|
|
if (code === null) {
|
|
console.error('Debugger shell exited with signal', signal);
|
|
process.exit(1);
|
|
}
|
|
process.exit(code);
|
|
});
|
|
|
|
const handleTerminationSignal = function (signal: string) {
|
|
process.on(signal, function signalHandler() {
|
|
if (!child.killed) {
|
|
child.kill(signal);
|
|
}
|
|
});
|
|
};
|
|
|
|
handleTerminationSignal('SIGINT');
|
|
handleTerminationSignal('SIGTERM');
|
|
}
|
|
});
|
|
}
|
|
|
|
export type DebuggerShellPreparationResult = $ReadOnly<{
|
|
code:
|
|
| 'success'
|
|
| 'not_implemented'
|
|
| 'likely_offline'
|
|
| 'platform_not_supported'
|
|
| 'possible_corruption'
|
|
| 'unexpected_error',
|
|
humanReadableMessage?: string,
|
|
verboseInfo?: string,
|
|
}>;
|
|
|
|
/**
|
|
* Attempts to prepare the debugger shell for use and returns a coded result
|
|
* that can be used to advise the user on how to proceed in case of failure.
|
|
* In particular, this function will attempt to download and extract an
|
|
* appropriate binary for the "prebuilt" flavor.
|
|
*
|
|
* This function should be called early during dev server startup, in parallel
|
|
* with other initialization steps, so that the debugger shell is ready to use
|
|
* instantly when the user tries to open it (and conversely, the user is
|
|
* informed ASAP if it is not ready to use).
|
|
*/
|
|
async function unstable_prepareDebuggerShell(
|
|
flavor: DebuggerShellFlavor,
|
|
{prebuiltBinaryPath}: {prebuiltBinaryPath?: string} = {},
|
|
): Promise<DebuggerShellPreparationResult> {
|
|
try {
|
|
switch (flavor) {
|
|
case 'prebuilt':
|
|
const prebuiltResult = await prepareDebuggerShellFromDotSlashFile(
|
|
prebuiltBinaryPath ?? DEVTOOLS_BINARY_DOTSLASH_FILE,
|
|
);
|
|
if (prebuiltResult.code !== 'success') {
|
|
return prebuiltResult;
|
|
}
|
|
break;
|
|
case 'dev':
|
|
break;
|
|
default:
|
|
flavor as empty;
|
|
throw new Error(`Unknown flavor: ${flavor}`);
|
|
}
|
|
|
|
const [binaryPath, baseArgs] = getShellBinaryAndArgs(
|
|
flavor,
|
|
prebuiltBinaryPath,
|
|
);
|
|
const {code, stderr} = await spawnAndGetStderr(binaryPath, [
|
|
...baseArgs,
|
|
'--version',
|
|
]);
|
|
if (code !== 0) {
|
|
return {
|
|
code: 'unexpected_error',
|
|
verboseInfo: stderr,
|
|
};
|
|
}
|
|
return {code: 'success'};
|
|
} catch (e) {
|
|
return {
|
|
code: 'unexpected_error',
|
|
verboseInfo: e.message,
|
|
};
|
|
}
|
|
}
|
|
|
|
function getShellBinaryAndArgs(
|
|
flavor: DebuggerShellFlavor,
|
|
prebuiltBinaryPath: ?string,
|
|
): [string, Array<string>] {
|
|
switch (flavor) {
|
|
case 'prebuilt':
|
|
return [
|
|
require('fb-dotslash'),
|
|
[prebuiltBinaryPath ?? DEVTOOLS_BINARY_DOTSLASH_FILE],
|
|
];
|
|
case 'dev':
|
|
return [
|
|
// NOTE: Internally at Meta, this is aliased to a workspace that is
|
|
// API-compatible with the 'electron' package, but contains prebuilt binaries
|
|
// that do not need to be downloaded in a postinstall action.
|
|
require('electron'),
|
|
[require.resolve('../electron')],
|
|
];
|
|
default:
|
|
flavor as empty;
|
|
throw new Error(`Unknown flavor: ${flavor}`);
|
|
}
|
|
}
|
|
|
|
export {unstable_spawnDebuggerShellWithArgs, unstable_prepareDebuggerShell};
|