# Visitor pattern

*Visitor* is a design pattern that allows operations to be performed on individual elements in a more complex structure. Redocly uses it as the basis for custom plugins where every node in a document must be evaluated, for example when applying rules or decorators.

To understand how this applies to your API description, think of the document as a tree structure. The top level elements are entries like `info` and `components`. To examine the `description` field in the `info` section, the visitor goes to the `info` node first, then on to the `description` node. This pattern is repeated all over the document as the visitor pattern works its way around the document tree.

## Structure of the visitor object

In your plugin, create a JavaScript visitor object, and describe the functionality required for each type of node.

Redocly CLI calls `enter()` while going down the tree and `leave()` going up the tree after processing the node and its children.
If the `skip()` predicate is defined and returns `true` the node is ignored for this visitor.


```js
function ExampleRule() {
  const seen = {};
  return {
    Root: {
      leave() {
        // check something and report
      }
    }
    Operation: {
      enter(operation, ctx) {
        seen[operation.operationId] = true;
      },
    }
  };
}
```

Keys of the object are one of the following:

- document-specific node types, such as the [OpenAPI node types](https://redocly.com/docs/openapi-visual-reference/openapi-node-types/).
- `any` - visitor is called on every node.
- `ref` - visitor is called on $ref nodes.


## Visitors execution and $ref

Top level **visitor functions** run only once for each node.
If the same node is referenced by the $ref multiple times,
top-level **visitor functions** are executed only once for this node.

This works fine for most context-free rules. If you need contextual info you should use [nested visitors](#nested-visitors).

## Nested visitors

The visitor object (if it is not `any` or `ref`) can define nested visitors.

Here is a basic example of a nested visitor:


```js
function ExampleRule() {
  return {
    Operation: {
      Schema: {
        enter(schema, ctx, parents) {
          console.log(`type ${schema.type} from ${parents.Operation.operationId}`)
        }
      }
    }
  };
}
```

The `Schema` **visitor function** is called by Redocly CLI only if the Schema Object is encountered while traversing a tree while the Operation Object is **entered**.

As the third argument, `enter()` in a **nested visitor object** accepts the `parents` object with corresponding parent nodes as defined in the **visitor object**.

It is executed only for the first level of the Schema Object.

For the example document below:


```yaml
get:
  operationId: getOp
  parameters:
    - name: a
      in: path
      schema:
        type: string
  requestBody:
    content:
      application/json:
        schema:
          type: object
          properties:
            a:
              type: boolean
put:
  operationId: putOp
  parameters:
    - name: a
      in: path
      schema:
        type: number
```

The visitor above logs the following:


```sh
type string from getOp
type object from getOp
type number from putOp
```