function oxcTraverse(oxcAst, visitor, { needsAdvancedPaths = false } = {}) {
  // @ts-expect-error
  // Before traversing, normalize so that we're still compatible with both oxc and swc
  const ast = oxcAst.program || oxcAst;
  /**
   * For performance, the author of a visitor can call this to stop further traversal
   */
  let isStopped = false;
  const stop = () => {
    isStopped = true;
  };

  /**
   * @param {SwcNode} node
   * @param {SwcNode|null} parent
   * @param {SwcScope} scope
   * @param {boolean} hasPreparedTree
   * @param {SwcTraversalContext} traversalContext
   */
  const handlePathAndScope = (node, parent, scope, hasPreparedTree, traversalContext) => {
    if (hasPreparedTree) {
      const swcPath = /** @type {SwcPath} */ (swcPathCache.get(node));
      return {
        swcPath,
        newOrCurScope: getNewScope(swcPath, scope, traversalContext) || scope,
      };
    }
    // `needsAdvancedPaths` was false
    const swcPath = createSwcPath(node, parent, stop);
    // We create scopes ourselves, since paths are not prepared yet...
    const newOrCurScope = getNewScope(swcPath, scope, traversalContext) || scope;
    swcPath.scope = newOrCurScope;
    addPotentialBindingOrRefToScope(swcPath);
    return { swcPath, newOrCurScope };
  };

  /**
   * @param {SwcNode} node
   * @param {SwcNode|null} parent
   * @param {SwcScope} scope
   * @param {SwcTraversalContext} traversalContext
   * @param {{haltCondition?: (node: SwcNode) => boolean;}} [config]
   */
  const prepareTree = (node, parent, scope, traversalContext, { haltCondition } = {}) => {
    if (!node?.type) {
      return;
    }

    const { newOrCurScope } = handlePathAndScope(node, parent, scope, false, traversalContext);
    loopChildren({ node }, ({ child }) => {
      prepareTree(child, node, newOrCurScope, traversalContext, { haltCondition });
    });
  };

  /**
   * @param {SwcNode} node
   * @param {SwcNode|null} parent
   * @param {SwcScope} scope
   * @param {{hasPreparedTree?: boolean;}} config
   * @param {SwcTraversalContext} traversalContext
   */
  const visitTree = (node, parent, scope, config, traversalContext) => {
    if (!node?.type || isStopped) {
      return;
    }

    const { hasPreparedTree = false } = config || {};

    const { swcPath } = handlePathAndScope(node, parent, scope, hasPreparedTree, traversalContext);
    visit(swcPath, visitor, traversalContext);
    loopChildren({ node }, ({ child }) => {
      visitTree(child, node, swcPath.scope, config, traversalContext);
    });
  };

  const traversalContext = { visitOnExitFns: [], scopeId: 0 };
  // https://developer.mozilla.org/en-US/docs/Glossary/Scope
  /** @type {SwcScope} */
  const initialScope = {
    id: traversalContext.scopeId,
    bindings: {},
    path: null,
    getBinding(/** @type {string} */ identifierName) {
      return initialScope.bindings[identifierName];
    },
    _pendingRefsWithoutBinding: [],
    _isIsolatedBlockStatement: false,
  };
  if (needsAdvancedPaths) {
    // Do one full traversal to prepare advanced path functionality like path.get() and path.scope.bindings
    // TODO: improve with on the fly, partial tree traversal for best performance
    prepareTree(ast, null, initialScope, traversalContext);
  }
  visitTree(ast, null, initialScope, { hasPreparedTree: needsAdvancedPaths }, traversalContext);
  // @ts-expect-error
  traversalContext.visitOnExitFns.reverse().forEach(fn => fn());
}
Simple traversal for swc ast.
@param {OxcParseResult|SwcAstModule|SwcNode} oxcAst
@param {SwcVisitor} visitor
@param {object} config
@param {boolean} [config.needsAdvancedPaths] needs a full traversal before starting the visitor, which is less performant. Only enable when path.get() is used