text: /**
   * 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.
   *
   * @flow
   */
  
  import {
    ElementTypeForwardRef,
    ElementTypeMemo,
  } from 'react-devtools-shared/src/types';
  
  import type {Element} from './views/Components/types';
  import type Store from './store';
  
  export function printElement(element: Element, includeWeight: boolean = false) {
    let prefix = ' ';
    if (element.children.length > 0) {
      prefix = element.isCollapsed ? '▸' : '▾';
    }
  
    let key = '';
    if (element.key !== null) {
      key = ` key="${element.key}"`;
    }
  
    let hocDisplayNames = null;
    if (element.hocDisplayNames !== null) {
      hocDisplayNames = [...element.hocDisplayNames];
    }
    if (element.type === ElementTypeMemo) {
      if (hocDisplayNames === null) {
        hocDisplayNames = ['Memo'];
      } else {
        hocDisplayNames.push('Memo');
      }
    } else if (element.type === ElementTypeForwardRef) {
      if (hocDisplayNames === null) {
        hocDisplayNames = ['ForwardRef'];
      } else {
        hocDisplayNames.push('ForwardRef');
      }
    }
  
    let hocs = hocDisplayNames === null ? '' : ` [${hocDisplayNames.join('][')}]`;
  
    let suffix = '';
    if (includeWeight) {
      suffix = ` (${element.isCollapsed ? 1 : element.weight})`;
    }
  
    return `${'  '.repeat(element.depth + 1)}${prefix} <${element.displayName ||
      'null'}${key}>${hocs}${suffix}`;
  }
  
  export function printOwnersList(
    elements: Array<Element>,
    includeWeight: boolean = false,
  ) {
    return elements
      .map(element => printElement(element, includeWeight))
      .join('\n');
  }
  
  export function printStore(store: Store, includeWeight: boolean = false) {
    const snapshotLines = [];
  
    let rootWeight = 0;
  
    store.roots.forEach(rootID => {
      const {weight} = ((store.getElementByID(rootID): any): Element);
  
      snapshotLines.push('[root]' + (includeWeight ? ` (${weight})` : ''));
  
      for (let i = rootWeight; i < rootWeight + weight; i++) {
        const element = store.getElementAtIndex(i);
  
        if (element == null) {
          throw Error(`Could not find element at index ${i}`);
        }
  
        snapshotLines.push(printElement(element, includeWeight));
      }
  
      rootWeight += weight;
    });
  
    // Make sure the pretty-printed test align with the Store's reported number of total rows.
    if (rootWeight !== store.numElements) {
      throw Error(
        `Inconsistent Store state. Individual root weights (${rootWeight}) do not match total weight (${
          store.numElements
        })`,
      );
    }
  
    // If roots have been unmounted, verify that they've been removed from maps.
    // This helps ensure the Store doesn't leak memory.
    store.assertExpectedRootMapSizes();
  
    return snapshotLines.join('\n');
  }
  
  // We use JSON.parse to parse string values
  // e.g. 'foo' is not valid JSON but it is a valid string
  // so this method replaces e.g. 'foo' with "foo"
  export function sanitizeForParse(value: any) {
    if (typeof value === 'string') {
      if (
        value.length >= 2 &&
        value.charAt(0) === "'" &&
        value.charAt(value.length - 1) === "'"
      ) {
        return '"' + value.substr(1, value.length - 2) + '"';
      }
    }
    return value;
  }
  
  export function smartParse(value: any) {
    switch (value) {
      case 'Infinity':
        return Infinity;
      case 'NaN':
        return NaN;
      case 'undefined':
        return undefined;
      default:
        return JSON.parse(sanitizeForParse(value));
    }
  }
  
  export function smartStringify(value: any) {
    if (typeof value === 'number') {
      if (Number.isNaN(value)) {
        return 'NaN';
      } else if (!Number.isFinite(value)) {
        return 'Infinity';
      }
    } else if (value === undefined) {
      return 'undefined';
    }
  
    return JSON.stringify(value);
  }
  