content: import React from 'react';
  import { enterHotUpdate, get as getGeneration, hotComparisonOpen, setComparisonHooks } from '../global/generation';
  import { getProxyByType, setStandInOptions } from './proxies';
  import reconcileHotReplacement, { flushScheduledUpdates, unscheduleUpdate } from './index';
  import configuration, { internalConfiguration } from '../configuration';
  import { EmptyErrorPlaceholder, logException } from '../errorReporter';
  import { RENDERED_GENERATION } from '../proxy';
  
  export const renderReconciler = (target, force) => {
    // we are not inside parent reconcilation
    const currentGeneration = getGeneration();
    const componentGeneration = target[RENDERED_GENERATION];
  
    target[RENDERED_GENERATION] = currentGeneration;
  
    if (!internalConfiguration.disableProxyCreation) {
      if ((componentGeneration || force) && componentGeneration !== currentGeneration) {
        enterHotUpdate();
        reconcileHotReplacement(target);
        return true;
      }
    }
    return false;
  };
  
  function asyncReconciledRender(target) {
    renderReconciler(target, false);
  }
  
  export function proxyWrapper(element) {
    // post wrap on post render
    if (!internalConfiguration.disableProxyCreation) {
      unscheduleUpdate(this);
    }
  
    if (!element) {
      return element;
    }
    if (Array.isArray(element)) {
      return element.map(proxyWrapper);
    }
    if (typeof element.type === 'function') {
      const proxy = getProxyByType(element.type);
      if (proxy) {
        return {
          ...element,
          type: proxy.get(),
        };
      }
    }
    return element;
  }
  
  const ERROR_STATE = 'react_hot_loader_catched_error';
  const ERROR_STATE_PROTO = 'react_hot_loader_catched_error-prototype';
  const OLD_RENDER = 'react_hot_loader_original_render';
  
  function componentDidCatch(error, errorInfo) {
    this[ERROR_STATE] = {
      location: 'boundary',
      error,
      errorInfo,
      generation: getGeneration(),
    };
    Object.getPrototypeOf(this)[ERROR_STATE_PROTO] = this[ERROR_STATE];
    if (!configuration.errorReporter) {
      logException(error, errorInfo, this);
    }
    this.forceUpdate();
  }
  
  function componentRender(...args) {
    const { error, errorInfo, generation } = this[ERROR_STATE] || {};
  
    if (error && generation === getGeneration()) {
      return React.createElement(configuration.errorReporter || EmptyErrorPlaceholder, {
        error,
        errorInfo,
        component: this,
      });
    }
  
    if (this.hotComponentUpdate) {
      this.hotComponentUpdate();
    }
    try {
      return this[OLD_RENDER].render.call(this, ...args);
    } catch (renderError) {
      this[ERROR_STATE] = {
        location: 'render',
        error: renderError,
        generation: getGeneration(),
      };
      if (!configuration.errorReporter) {
        logException(renderError, undefined, this);
      }
      return componentRender.call(this);
    }
  }
  
  export function retryHotLoaderError() {
    delete this[ERROR_STATE];
    this.forceUpdate();
  }
  
  setComparisonHooks(
    () => ({}),
    component => {
      if (!hotComparisonOpen()) {
        return;
      }
      const { prototype } = component;
      if (!prototype[OLD_RENDER]) {
        const renderDescriptior = Object.getOwnPropertyDescriptor(prototype, 'render');
        prototype[OLD_RENDER] = {
          descriptor: renderDescriptior ? renderDescriptior.value : undefined,
          render: prototype.render,
        };
        prototype.componentDidCatch = componentDidCatch;
        prototype.retryHotLoaderError = retryHotLoaderError;
  
        prototype.render = componentRender;
      }
      delete prototype[ERROR_STATE];
    },
    ({ prototype }) => {
      if (prototype[OLD_RENDER]) {
        const { generation } = prototype[ERROR_STATE_PROTO] || {};
  
        if (generation === getGeneration()) {
          // still in error.
          // keep render hooked
        } else {
          delete prototype.componentDidCatch;
          delete prototype.retryHotLoaderError;
  
          // undo only what we did
          if (prototype.render === componentRender) {
            if (!prototype[OLD_RENDER].descriptor) {
              delete prototype.render;
            } else {
              prototype.render = prototype[OLD_RENDER].descriptor;
            }
          } else {
            console.error('React-Hot-Loader: something unexpectedly mutated Component', prototype);
          }
          delete prototype[ERROR_STATE_PROTO];
          delete prototype[OLD_RENDER];
        }
      }
    },
  );
  
  setStandInOptions({
    componentWillRender: asyncReconciledRender,
    componentDidRender: proxyWrapper,
    componentDidUpdate: component => {
      component[RENDERED_GENERATION] = getGeneration();
      flushScheduledUpdates();
    },
  });
  