Repository: babel/babel
File: packages/babel-traverse/src/scope/index.ts (TypeScript)

Code before:
  getOwnBindingIdentifier(name: string): t.Identifier {
    const binding = this.bindings[name];
    return binding?.identifier;
  }

  hasOwnBinding(name: string) {
    return !!this.getOwnBinding(name);
  }

  // By default, we consider generated UIDs as bindings.
  // This is because they are almost always used to declare variables,
  // and since the scope isn't always up-to-date it's better to assume that
  // there is a variable with that name. The `noUids` option can be used to
  // turn off this behavior, for example if you know that the generate UID
  // was used to declare a variable in a different scope.
  hasBinding(
    name: string,
    opts?: boolean | { noGlobals?: boolean; noUids?: boolean },
  ) {
    if (!name) return false;
    if (this.hasOwnBinding(name)) return true;
    {
      // TODO: Only accept the object form.
      if (typeof opts === "boolean") opts = { noGlobals: opts };
    }
    if (this.parentHasBinding(name, opts)) return true;
    if (!opts?.noUids && this.hasUid(name)) return true;
    if (!opts?.noGlobals && Scope.globals.includes(name)) return true;
    if (!opts?.noGlobals && Scope.contextVariables.includes(name)) return true;
    return false;
  }

  parentHasBinding(
    name: string,
    opts?: { noGlobals?: boolean; noUids?: boolean },
  ) {
    return this.parent?.hasBinding(name, opts);
  }

  /**
   * Move a binding of `name` to another `scope`.
   */

  moveBindingTo(name: string, scope: Scope) {
    const info = this.getBinding(name);
    if (info) {
      info.scope.removeOwnBinding(name);
      info.scope = scope;
      scope.bindings[name] = info;
    }
  }

Review comment (refactor):
Do we have other `parentHasBinding` usage? If not maybe we can deprecate and remove this method in Babel 8, as it is trivial to implement.

Code after:
  getOwnBindingIdentifier(name: string): t.Identifier {
    const binding = this.bindings[name];
    return binding?.identifier;
  }

  hasOwnBinding(name: string) {
    return !!this.getOwnBinding(name);
  }

  // By default, we consider generated UIDs as bindings.
  // This is because they are almost always used to declare variables,
  // and since the scope isn't always up-to-date it's better to assume that
  // there is a variable with that name. The `noUids` option can be used to
  // turn off this behavior, for example if you know that the generate UID
  // was used to declare a variable in a different scope.
  hasBinding(
    name: string,
    opts?: boolean | { noGlobals?: boolean; noUids?: boolean },
  ) {
    if (!name) return false;
    let scope: Scope = this;
    do {
      if (scope.hasOwnBinding(name)) {
        return true;
      }
    } while ((scope = scope.parent));

    // TODO: Only accept the object form.
    let noGlobals;
    let noUids;
    if (typeof opts === "object") {
      noGlobals = opts.noGlobals;
      noUids = opts.noUids;
    } else if (typeof opts === "boolean") {
      noGlobals = opts;
    }

    if (!noUids && this.hasUid(name)) return true;
    if (!noGlobals && Scope.globals.includes(name)) return true;
    if (!noGlobals && Scope.contextVariables.includes(name)) return true;
    return false;
  }

  parentHasBinding(
    name: string,
    opts?: { noGlobals?: boolean; noUids?: boolean },
  ) {
    return this.parent?.hasBinding(name, opts);
  }

  /**