Repository: facebook/react
File: packages/react-debug-tools/src/ReactDebugHooks.js (JavaScript)

Code before:
  }
  if (functionName.slice(startIndex, startIndex + 3) === 'use') {
    if (functionName.length - startIndex === 3) {
      return 'Use';
    }
    startIndex += 3;
  }
  return functionName.slice(startIndex);
}

function buildTree(
  rootStack: any,
  readHookLog: Array<HookLogEntry>,
): HooksTree {
  const rootChildren: Array<HooksNode> = [];
  let prevStack = null;
  let levelChildren = rootChildren;
  let nativeHookID = 0;
  const stackOfChildren = [];
  for (let i = 0; i < readHookLog.length; i++) {
    const hook = readHookLog[i];
    const parseResult = parseTrimmedStack(rootStack, hook);
    let displayName = hook.displayName;
    if (parseResult !== null) {
      const [primitiveFrame, stack] = parseResult;
      if (hook.displayName === null) {
        displayName =
          parseHookName(primitiveFrame.functionName) ||
          // Older versions of React do not have sourcemaps.
          // In those versions there was always a 1:1 mapping between wrapper and dispatcher method.
          parseHookName(hook.dispatcherMethodName);
      }
      // Note: The indices 0 <= n < length-1 will contain the names.
      // The indices 1 <= n < length will contain the source locations.
      // That's why we get the name from n - 1 and don't check the source
      // of index 0.
      let commonSteps = 0;
      if (prevStack !== null) {
        // Compare the current level's stack to the new stack.
        while (commonSteps < stack.length && commonSteps < prevStack.length) {
          const stackSource = stack[stack.length - commonSteps - 1].source;
          const prevSource =
            prevStack[prevStack.length - commonSteps - 1].source;
          if (stackSource !== prevSource) {
            break;
          }
          commonSteps++;
        }
        // Pop back the stack as many steps as were not common.
        for (let j = prevStack.length - 1; j > commonSteps; j--) {
          levelChildren = stackOfChildren.pop();

Review comment (suggestion):
```suggestion
      if (displayName == null) {
```

Code after:
  }
  return -1;
}

function isReactWrapper(functionName: any, wrapperName: string) {
  return parseHookName(functionName) === wrapperName;
}

function findPrimitiveIndex(hookStack: any, hook: HookLogEntry) {
  const stackCache = getPrimitiveStackCache();
  const primitiveStack = stackCache.get(hook.primitive);
  if (primitiveStack === undefined) {
    return -1;
  }
  for (let i = 0; i < primitiveStack.length && i < hookStack.length; i++) {
    if (primitiveStack[i].source !== hookStack[i].source) {
      // If the next frame is a method from the dispatcher, we
      // assume that the next frame after that is the actual public API call.
      // This prohibits nesting dispatcher calls in hooks.
      if (
        i < hookStack.length - 1 &&
        isReactWrapper(hookStack[i].functionName, hook.dispatcherHookName)
      ) {
        i++;
      }
      if (
        i < hookStack.length - 1 &&
        isReactWrapper(hookStack[i].functionName, hook.dispatcherHookName)
      ) {
        i++;
      }
      return i;
    }
  }
  return -1;
}

function parseTrimmedStack(rootStack: any, hook: HookLogEntry) {
  // Get the stack trace between the primitive hook function and
  // the root function call. I.e. the stack frames of custom hooks.
  const hookStack = ErrorStackParser.parse(hook.stackError);
  const rootIndex = findCommonAncestorIndex(rootStack, hookStack);
  const primitiveIndex = findPrimitiveIndex(hookStack, hook);
  if (
    rootIndex === -1 ||
    primitiveIndex === -1 ||
    rootIndex - primitiveIndex < 2
  ) {
    if (primitiveIndex === -1) {
      // Something went wrong. Give up.
      return [null, null];