1{"version":3,"file":"HMRClient.js","sourceRoot":"","sources":["../src/HMRClient.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Copyright (c) 650 Industries.\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * Based on this but with web support:\n * https://github.com/facebook/react-native/blob/086714b02b0fb838dee5a66c5bcefe73b53cf3df/Libraries/Utilities/HMRClient.js\n */\nimport prettyFormat, { plugins } from 'pretty-format';\n\nimport LoadingView from './LoadingView';\nimport LogBox from './error-overlay/LogBox';\nimport getDevServer from './getDevServer';\n\nconst MetroHMRClient = require('metro-runtime/src/modules/HMRClient');\nconst pendingEntryPoints: string[] = [];\n\ntype HMRClientType = {\n  send: (msg: string) => void;\n  isEnabled: () => boolean;\n  disable: () => void;\n  enable: () => void;\n  hasPendingUpdates: () => boolean;\n};\n\nlet hmrClient: HMRClientType | null = null;\nlet hmrUnavailableReason: string | null = null;\nlet currentCompileErrorMessage: string | null = null;\nlet didConnect: boolean = false;\nconst pendingLogs: [LogLevel, any[]][] = [];\n\ntype LogLevel =\n  | 'trace'\n  | 'info'\n  | 'warn'\n  | 'error'\n  | 'log'\n  | 'group'\n  | 'groupCollapsed'\n  | 'groupEnd'\n  | 'debug';\n\nexport type HMRClientNativeInterface = {\n  enable(): void;\n  disable(): void;\n  registerBundle(requestUrl: string): void;\n  log(level: LogLevel, data: any[]): void;\n  setup(props: { isEnabled: boolean }): void;\n};\n\nfunction assert(foo: any, msg: string): asserts foo {\n  if (!foo) throw new Error(msg);\n}\n\n/**\n * HMR Client that receives from the server HMR updates and propagates them\n * runtime to reflects those changes.\n */\nconst HMRClient: HMRClientNativeInterface = {\n  enable() {\n    if (hmrUnavailableReason !== null) {\n      // If HMR became unavailable while you weren't using it,\n      // explain why when you try to turn it on.\n      // This is an error (and not a warning) because it is shown\n      // in response to a direct user action.\n      throw new Error(hmrUnavailableReason);\n    }\n\n    assert(hmrClient, 'Expected HMRClient.setup() call at startup.');\n\n    // We use this for internal logging only.\n    // It doesn't affect the logic.\n    hmrClient.send(JSON.stringify({ type: 'log-opt-in' }));\n\n    // When toggling Fast Refresh on, we might already have some stashed updates.\n    // Since they'll get applied now, we'll show a banner.\n    const hasUpdates = hmrClient!.hasPendingUpdates();\n\n    if (hasUpdates) {\n      LoadingView.showMessage('Refreshing...', 'refresh');\n    }\n    try {\n      hmrClient.enable();\n    } finally {\n      if (hasUpdates) {\n        LoadingView.hide();\n      }\n    }\n\n    // There could be a compile error while Fast Refresh was off,\n    // but we ignored it at the time. Show it now.\n    showCompileError();\n  },\n\n  disable() {\n    assert(hmrClient, 'Expected HMRClient.setup() call at startup.');\n    hmrClient.disable();\n  },\n\n  registerBundle(requestUrl: string) {\n    assert(hmrClient, 'Expected HMRClient.setup() call at startup.');\n    pendingEntryPoints.push(requestUrl);\n    registerBundleEntryPoints(hmrClient);\n  },\n\n  log(level: LogLevel, data: any[]) {\n    if (!hmrClient) {\n      // Catch a reasonable number of early logs\n      // in case hmrClient gets initialized later.\n      pendingLogs.push([level, data]);\n      if (pendingLogs.length > 100) {\n        pendingLogs.shift();\n      }\n      return;\n    }\n    try {\n      hmrClient.send(\n        JSON.stringify({\n          type: 'log',\n          level,\n          mode: 'BRIDGE',\n          data: data.map((item) =>\n            typeof item === 'string'\n              ? item\n              : prettyFormat(item, {\n                  escapeString: true,\n                  highlight: true,\n                  maxDepth: 3,\n                  min: true,\n                  plugins: [plugins.ReactElement],\n                })\n          ),\n        })\n      );\n    } catch {\n      // If sending logs causes any failures we want to silently ignore them\n      // to ensure we do not cause infinite-logging loops.\n    }\n  },\n\n  // Called once by the bridge on startup, even if Fast Refresh is off.\n  // It creates the HMR client but doesn't actually set up the socket yet.\n  setup({ isEnabled }: { isEnabled: boolean }) {\n    assert(!hmrClient, 'Cannot initialize hmrClient twice');\n\n    const serverScheme = window.location.protocol === 'https:' ? 'wss' : 'ws';\n    const client = new MetroHMRClient(`${serverScheme}://${window.location.host}/hot`);\n    hmrClient = client;\n\n    const { fullBundleUrl } = getDevServer();\n    pendingEntryPoints.push(\n      // HMRServer understands regular bundle URLs, so prefer that in case\n      // there are any important URL parameters we can't reconstruct from\n      // `setup()`'s arguments.\n      fullBundleUrl\n    );\n\n    client.on('connection-error', (e: Error) => {\n      let error = `Cannot connect to Metro.\n \n Try the following to fix the issue:\n - Ensure the Metro dev server is running and available on the same network as this device`;\n      error += `\n \n URL: ${window.location.host}\n \n Error: ${e.message}`;\n\n      setHMRUnavailableReason(error);\n    });\n\n    client.on('update-start', ({ isInitialUpdate }: { isInitialUpdate?: boolean }) => {\n      currentCompileErrorMessage = null;\n      didConnect = true;\n\n      if (client.isEnabled() && !isInitialUpdate) {\n        LoadingView.showMessage('Refreshing...', 'refresh');\n      }\n    });\n\n    client.on('update', ({ isInitialUpdate }: { isInitialUpdate?: boolean }) => {\n      if (client.isEnabled() && !isInitialUpdate) {\n        dismissRedbox();\n        LogBox.clearAllLogs();\n      }\n    });\n\n    client.on('update-done', () => {\n      LoadingView.hide();\n    });\n\n    client.on('error', (data: { type: string; message: string }) => {\n      LoadingView.hide();\n\n      if (data.type === 'GraphNotFoundError') {\n        client.close();\n        setHMRUnavailableReason('Metro has restarted since the last edit. Reload to reconnect.');\n      } else if (data.type === 'RevisionNotFoundError') {\n        client.close();\n        setHMRUnavailableReason('Metro and the client are out of sync. Reload to reconnect.');\n      } else {\n        currentCompileErrorMessage = `${data.type} ${data.message}`;\n        if (client.isEnabled()) {\n          showCompileError();\n        }\n      }\n    });\n\n    client.on('close', (closeEvent: { code: number; reason: string }) => {\n      LoadingView.hide();\n\n      // https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1\n      // https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.5\n      const isNormalOrUnsetCloseReason =\n        closeEvent == null ||\n        closeEvent.code === 1000 ||\n        closeEvent.code === 1005 ||\n        closeEvent.code == null;\n\n      setHMRUnavailableReason(\n        `${\n          isNormalOrUnsetCloseReason\n            ? 'Disconnected from Metro.'\n            : `Disconnected from Metro (${closeEvent.code}: \"${closeEvent.reason}\").`\n        }\n\nTo reconnect:\n- Ensure that Metro is running and available on the same network\n- Reload this app (will trigger further help if Metro cannot be connected to)\n      `\n      );\n    });\n\n    if (isEnabled) {\n      HMRClient.enable();\n    } else {\n      HMRClient.disable();\n    }\n\n    registerBundleEntryPoints(hmrClient);\n    flushEarlyLogs();\n  },\n};\n\nfunction setHMRUnavailableReason(reason: string) {\n  assert(hmrClient, 'Expected HMRClient.setup() call at startup.');\n  if (hmrUnavailableReason !== null) {\n    // Don't show more than one warning.\n    return;\n  }\n  hmrUnavailableReason = reason;\n\n  // We only want to show a warning if Fast Refresh is on *and* if we ever\n  // previously managed to connect successfully. We don't want to show\n  // the warning to native engineers who use cached bundles without Metro.\n  if (hmrClient.isEnabled() && didConnect) {\n    console.warn(reason);\n    // (Not using the `warning` module to prevent a Buck cycle.)\n  }\n}\n\nfunction registerBundleEntryPoints(client: HMRClientType | null) {\n  if (hmrUnavailableReason != null) {\n    // \"Bundle Splitting – Metro disconnected\"\n    window.location.reload();\n    return;\n  }\n\n  if (pendingEntryPoints.length > 0) {\n    client?.send(\n      JSON.stringify({\n        type: 'register-entrypoints',\n        entryPoints: pendingEntryPoints,\n      })\n    );\n    pendingEntryPoints.length = 0;\n  }\n}\n\nfunction flushEarlyLogs() {\n  try {\n    pendingLogs.forEach(([level, data]) => {\n      HMRClient.log(level, data);\n    });\n  } finally {\n    pendingLogs.length = 0;\n  }\n}\n\nfunction dismissRedbox() {\n  // TODO(EvanBacon): Error overlay for web.\n}\n\nfunction showCompileError() {\n  if (currentCompileErrorMessage === null) {\n    return;\n  }\n\n  // Even if there is already a redbox, syntax errors are more important.\n  // Otherwise you risk seeing a stale runtime error while a syntax error is more recent.\n  dismissRedbox();\n\n  const message = currentCompileErrorMessage;\n  currentCompileErrorMessage = null;\n\n  const error = new Error(message);\n  // Symbolicating compile errors is wasted effort\n  // because the stack trace is meaningless:\n  // @ts-expect-error\n  error.preventSymbolication = true;\n  throw error;\n}\n\nexport default HMRClient;\n"]}