react-native-macos/Libraries/Utilities/HMRClient.js

315 строки
8.9 KiB
JavaScript
Исходник Обычный вид История

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const Platform = require('./Platform');
const invariant = require('invariant');
const MetroHMRClient = require('metro/src/lib/bundle-modules/HMRClient');
import NativeRedBox from '../NativeModules/specs/NativeRedBox';
import type {ExtendedError} from '../Core/Devtools/parseErrorStack';
const pendingEntryPoints = [];
let hmrClient = null;
let hmrUnavailableReason: string | null = null;
let currentCompileErrorMessage: string | null = null;
let didConnect: boolean = false;
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
type LogLevel =
| 'trace'
| 'info'
| 'warn'
| 'log'
| 'group'
| 'groupCollapsed'
| 'groupEnd'
| 'debug';
export type HMRClientNativeInterface = {|
enable(): void,
disable(): void,
registerBundle(requestUrl: string): void,
log(level: LogLevel, data: Array<mixed>): void,
setup(
platform: string,
bundleEntry: string,
host: string,
port: number | string,
isEnabled: boolean,
): void,
|};
/**
* HMR Client that receives from the server HMR updates and propagates them
* runtime to reflects those changes.
*/
const HMRClient: HMRClientNativeInterface = {
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
enable() {
if (hmrUnavailableReason !== null) {
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
// If HMR became unavailable while you weren't using it,
// explain why when you try to turn it on.
// This is an error (and not a warning) because it is shown
// in response to a direct user action.
throw new Error(hmrUnavailableReason);
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
}
invariant(hmrClient, 'Expected HMRClient.setup() call at startup.');
const LoadingView = require('./LoadingView');
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
// We use this for internal logging only.
// It doesn't affect the logic.
hmrClient.send(JSON.stringify({type: 'log-opt-in'}));
// When toggling Fast Refresh on, we might already have some stashed updates.
// Since they'll get applied now, we'll show a banner.
const hasUpdates = hmrClient.hasPendingUpdates();
if (hasUpdates) {
LoadingView.showMessage('Refreshing...', 'refresh');
}
try {
hmrClient.enable();
} finally {
if (hasUpdates) {
LoadingView.hide();
}
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
}
// There could be a compile error while Fast Refresh was off,
// but we ignored it at the time. Show it now.
showCompileError();
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
},
disable() {
invariant(hmrClient, 'Expected HMRClient.setup() call at startup.');
hmrClient.disable();
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
},
registerBundle(requestUrl: string) {
invariant(hmrClient, 'Expected HMRClient.setup() call at startup.');
pendingEntryPoints.push(requestUrl);
registerBundleEntryPoints(hmrClient);
},
log(level: LogLevel, data: Array<mixed>) {
try {
if (hmrClient) {
let message;
if (global.Symbol) {
message = JSON.stringify({
type: 'log',
level,
data: data.map(item =>
typeof item === 'string'
? item
: require('pretty-format')(item, {
escapeString: true,
highlight: true,
maxDepth: 3,
min: true,
plugins: [require('pretty-format').plugins.ReactElement],
}),
),
});
} else {
try {
message = JSON.stringify({type: 'log', level, data});
} catch (error) {
message = JSON.stringify({
type: 'log',
level,
data: [error.message],
});
}
}
hmrClient.send(message);
}
} catch (error) {
// If sending logs causes any failures we want to silently ignore them
// to ensure we do not cause infinite-logging loops.
}
},
// Called once by the bridge on startup, even if Fast Refresh is off.
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
// It creates the HMR client but doesn't actually set up the socket yet.
setup(
platform: string,
bundleEntry: string,
host: string,
port: number | string,
isEnabled: boolean,
) {
invariant(platform, 'Missing required parameter `platform`');
invariant(bundleEntry, 'Missing required paramenter `bundleEntry`');
invariant(host, 'Missing required paramenter `host`');
invariant(!hmrClient, 'Cannot initialize hmrClient twice');
// Moving to top gives errors due to NativeModules not being initialized
const LoadingView = require('./LoadingView');
const wsHost = port !== null && port !== '' ? `${host}:${port}` : host;
const client = new MetroHMRClient(`ws://${wsHost}/hot`);
hmrClient = client;
pendingEntryPoints.push(
`ws://${wsHost}/hot?bundleEntry=${bundleEntry}&platform=${platform}`,
);
client.on('connection-error', e => {
let error = `Cannot connect to the Metro server.
Try the following to fix the issue:
- Ensure that the Metro server is running and available on the same network`;
if (Platform.OS === 'ios') {
error += `
- Ensure that the Metro server URL is correctly set in AppDelegate`;
} else {
error += `
- Ensure that your device/emulator is connected to your machine and has USB debugging enabled - run 'adb devices' to see a list of connected devices
- If you're on a physical device connected to the same machine, run 'adb reverse tcp:8081 tcp:8081' to forward requests from your device
- If your device is on the same Wi-Fi network, set 'Debug server host & port for device' in 'Dev settings' to your machine's IP address and the port of the local dev server - e.g. 10.0.1.1:8081`;
}
error += `
URL: ${host}:${port}
Error: ${e.message}`;
setHMRUnavailableReason(error);
});
client.on('update-start', ({isInitialUpdate}) => {
currentCompileErrorMessage = null;
didConnect = true;
if (client.isEnabled() && !isInitialUpdate) {
LoadingView.showMessage('Refreshing...', 'refresh');
}
});
client.on('update', ({isInitialUpdate}) => {
if (client.isEnabled() && !isInitialUpdate) {
dismissRedbox();
}
});
client.on('update-done', () => {
LoadingView.hide();
});
client.on('error', data => {
LoadingView.hide();
if (data.type === 'GraphNotFoundError') {
client.close();
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
setHMRUnavailableReason(
'The Metro server has restarted since the last edit. Reload to reconnect.',
);
} else if (data.type === 'RevisionNotFoundError') {
client.close();
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
setHMRUnavailableReason(
'The Metro server and the client are out of sync. Reload to reconnect.',
);
} else {
currentCompileErrorMessage = `${data.type} ${data.message}`;
if (client.isEnabled()) {
showCompileError();
}
}
});
client.on('close', data => {
LoadingView.hide();
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
setHMRUnavailableReason(
'Disconnected from the Metro server. Reload to reconnect.',
);
});
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
if (isEnabled) {
HMRClient.enable();
} else {
HMRClient.disable();
}
registerBundleEntryPoints(hmrClient);
},
};
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
function setHMRUnavailableReason(reason) {
invariant(hmrClient, 'Expected HMRClient.setup() call at startup.');
if (hmrUnavailableReason !== null) {
// Don't show more than one warning.
return;
}
hmrUnavailableReason = reason;
// We only want to show a warning if Fast Refresh is on *and* if we ever
// previously managed to connect successfully. We don't want to show
// the warning to native engineers who use cached bundles without Metro.
if (hmrClient.isEnabled() && didConnect) {
console.warn(reason);
// (Not using the `warning` module to prevent a Buck cycle.)
Make "Enable Hot Reloading" Instant Summary: As we saw in D15947985, and later traced down to D5623623, the `hot` option isn't used by Metro anymore. The relevant transforms _always_ run in DEV regardless of the option. Given that, it doesn't make sense that enabling or disabling Hot Reloading forces a full refresh. This significantly raises the usage barrier because **currently, you might have to wait ~20 seconds (on a large app) to just start using Hot Reloading when you're already in the middle of some screen.** So you just end up not using it. This diff changes enabling/disabling Hot Reloading to be _instant_. Here's how it works: 1. Now we always send the necessary info to the client via the new `HMRClient.setup()` function. It creates a Metro HMR client instance, but only actually sets up the socket if Hot Reloading is on. 2. The "Enable Hot Reloading" menu no longer forces a reload. Instead, it calls `HMRClient.enable()` which lazily sets up a socket (at most once). 3. The "Disable Hot Reloading" menu also doesn't trigger a refresh now. Instead, it calls `HMRClient.disable()`. We don't actually tear down the socket here because it's a pain to deal with race conditions and such. Instead, we keep the connection — but we _ignore the updates_ that come in while we're disabled. 4. As a result, it is possible to enable and disable it many times during a single session. (Updates while disabled would be ignored — which has a risk of making your running app inconsistent — but I'd argue it's expected and is worth it. You can always save a particular file to force it to update once the mode is on.) 5. In order to support "ignoring" updates, Metro's `HMRClient` (not to be confused with RN's module) now supports a `shouldApplyUpdates` field. The RN module uses it to disable handling updates when the mode is off. 6. In case there is an error that makes hot reloading unavailable (such as the server disconnecting), we surface the error only if the mode is on. If the mode is off, we stash the error message in the `_hmrUnavailableReason` variable, and display it next time you try to enable Hot Reloading. Reviewed By: rickhanlonii Differential Revision: D15958160 fbshipit-source-id: 8256fc4d5c2c3f653a78edf13b8515a5671953e4
2019-06-24 19:45:29 +03:00
}
}
function registerBundleEntryPoints(client) {
if (pendingEntryPoints.length > 0) {
client.send(
JSON.stringify({
type: 'register-entrypoints',
entryPoints: pendingEntryPoints,
}),
);
pendingEntryPoints.length = 0;
}
}
function dismissRedbox() {
if (
Platform.OS === 'ios' &&
NativeRedBox != null &&
NativeRedBox.dismiss != null
) {
NativeRedBox.dismiss();
} else {
const NativeExceptionsManager = require('../Core/NativeExceptionsManager')
.default;
NativeExceptionsManager &&
NativeExceptionsManager.dismissRedbox &&
NativeExceptionsManager.dismissRedbox();
}
}
function showCompileError() {
if (currentCompileErrorMessage === null) {
return;
}
// Even if there is already a redbox, syntax errors are more important.
// Otherwise you risk seeing a stale runtime error while a syntax error is more recent.
dismissRedbox();
const message = currentCompileErrorMessage;
currentCompileErrorMessage = null;
const error: ExtendedError = new Error(message);
// Symbolicating compile errors is wasted effort
// because the stack trace is meaningless:
error.preventSymbolication = true;
throw error;
}
module.exports = HMRClient;