@codama/visitors-core
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -5,8 +5,11 @@ import { Node, NodeKind } from '@codama/nodes'; | ||
import { Visitor } from './visitor'; | ||
export type BottomUpNodeTransformer<TNode extends Node = Node> = (node: TNode, stack: NodeStack) => Node | null; | ||
export type BottomUpNodeTransformerWithSelector<TNode extends Node = Node> = { | ||
export type BottomUpNodeTransformer = (node: Node, stack: NodeStack) => Node | null; | ||
export type BottomUpNodeTransformerWithSelector = { | ||
select: NodeSelector | NodeSelector[]; | ||
transform: BottomUpNodeTransformer<TNode>; | ||
transform: BottomUpNodeTransformer; | ||
}; | ||
export declare function bottomUpTransformerVisitor<TNodeKind extends NodeKind = NodeKind>(transformers: (BottomUpNodeTransformer | BottomUpNodeTransformerWithSelector)[], nodeKeys?: TNodeKind[]): Visitor<Node | null, TNodeKind>; | ||
export declare function bottomUpTransformerVisitor<TNodeKind extends NodeKind = NodeKind>(transformers: (BottomUpNodeTransformer | BottomUpNodeTransformerWithSelector)[], options?: { | ||
keys?: TNodeKind[]; | ||
stack?: NodeStack; | ||
}): Visitor<Node | null, TNodeKind>; | ||
//# sourceMappingURL=bottomUpTransformerVisitor.d.ts.map |
import { NodeKind } from '@codama/nodes'; | ||
import { NodeSelector } from './NodeSelector'; | ||
export declare function deleteNodesVisitor<TNodeKind extends NodeKind = NodeKind>(selectors: NodeSelector[], nodeKeys?: TNodeKind[]): import("./visitor").Visitor<import("@codama/nodes").Node | null, TNodeKind>; | ||
import { topDownTransformerVisitor } from './topDownTransformerVisitor'; | ||
export declare function deleteNodesVisitor<TNodeKind extends NodeKind = NodeKind>(selectors: NodeSelector[], options?: Parameters<typeof topDownTransformerVisitor<TNodeKind>>[1]): import("./visitor").Visitor<import("@codama/nodes").Node | null, TNodeKind>; | ||
//# sourceMappingURL=deleteNodesVisitor.d.ts.map |
import { RegisteredTypeNode } from '@codama/nodes'; | ||
import { LinkableDictionary } from './LinkableDictionary'; | ||
import { NodeStack } from './NodeStack'; | ||
import { Visitor } from './visitor'; | ||
export type ByteSizeVisitorKeys = RegisteredTypeNode['kind'] | 'accountNode' | 'definedTypeLinkNode' | 'definedTypeNode' | 'instructionArgumentNode' | 'instructionNode'; | ||
export declare function getByteSizeVisitor(linkables: LinkableDictionary): Visitor<number | null, ByteSizeVisitorKeys>; | ||
export declare function getByteSizeVisitor(linkables: LinkableDictionary, options?: { | ||
stack?: NodeStack; | ||
}): Visitor<number | null, ByteSizeVisitorKeys>; | ||
//# sourceMappingURL=getByteSizeVisitor.d.ts.map |
import { Node, NodeKind } from '@codama/nodes'; | ||
import { Visitor } from './visitor'; | ||
export declare function identityVisitor<TNodeKind extends NodeKind = NodeKind>(nodeKeys?: TNodeKind[]): Visitor<Node | null, TNodeKind>; | ||
export declare function identityVisitor<TNodeKind extends NodeKind = NodeKind>(options?: { | ||
keys?: TNodeKind[]; | ||
}): Visitor<Node | null, TNodeKind>; | ||
//# sourceMappingURL=identityVisitor.d.ts.map |
@@ -1,4 +0,1 @@ | ||
export * from './LinkableDictionary'; | ||
export * from './NodeSelector'; | ||
export * from './NodeStack'; | ||
export * from './bottomUpTransformerVisitor'; | ||
@@ -13,6 +10,10 @@ export * from './consoleLogVisitor'; | ||
export * from './identityVisitor'; | ||
export * from './interceptFirstVisitVisitor'; | ||
export * from './interceptVisitor'; | ||
export * from './interceptFirstVisitVisitor'; | ||
export * from './LinkableDictionary'; | ||
export * from './mapVisitor'; | ||
export * from './mergeVisitor'; | ||
export * from './NodePath'; | ||
export * from './NodeSelector'; | ||
export * from './NodeStack'; | ||
export * from './nonNullableIdentityVisitor'; | ||
@@ -19,0 +20,0 @@ export * from './pipe'; |
@@ -1,36 +0,34 @@ | ||
import { AccountLinkNode, AccountNode, DefinedTypeLinkNode, DefinedTypeNode, InstructionAccountLinkNode, InstructionAccountNode, InstructionArgumentLinkNode, InstructionArgumentNode, InstructionLinkNode, InstructionNode, LinkNode, PdaLinkNode, PdaNode, ProgramLinkNode, ProgramNode } from '@codama/nodes'; | ||
import { NodeStack } from './NodeStack'; | ||
import { AccountNode, DefinedTypeNode, InstructionAccountNode, InstructionArgumentNode, InstructionNode, LinkNode, PdaNode, ProgramNode } from '@codama/nodes'; | ||
import { NodePath } from './NodePath'; | ||
export type LinkableNode = AccountNode | DefinedTypeNode | InstructionAccountNode | InstructionArgumentNode | InstructionNode | PdaNode | ProgramNode; | ||
export declare const LINKABLE_NODES: LinkableNode['kind'][]; | ||
export type GetLinkableFromLinkNode<TLinkNode extends LinkNode> = { | ||
accountLinkNode: AccountNode; | ||
definedTypeLinkNode: DefinedTypeNode; | ||
instructionAccountLinkNode: InstructionAccountNode; | ||
instructionArgumentLinkNode: InstructionArgumentNode; | ||
instructionLinkNode: InstructionNode; | ||
pdaLinkNode: PdaNode; | ||
programLinkNode: ProgramNode; | ||
}[TLinkNode['kind']]; | ||
type ProgramDictionary = { | ||
accounts: Map<string, AccountNode>; | ||
definedTypes: Map<string, DefinedTypeNode>; | ||
accounts: Map<string, NodePath<AccountNode>>; | ||
definedTypes: Map<string, NodePath<DefinedTypeNode>>; | ||
instructions: Map<string, InstructionDictionary>; | ||
pdas: Map<string, PdaNode>; | ||
program: ProgramNode; | ||
pdas: Map<string, NodePath<PdaNode>>; | ||
program: NodePath<ProgramNode>; | ||
}; | ||
type InstructionDictionary = { | ||
accounts: Map<string, InstructionAccountNode>; | ||
arguments: Map<string, InstructionArgumentNode>; | ||
instruction: InstructionNode; | ||
accounts: Map<string, NodePath<InstructionAccountNode>>; | ||
arguments: Map<string, NodePath<InstructionArgumentNode>>; | ||
instruction: NodePath<InstructionNode>; | ||
}; | ||
export declare class LinkableDictionary { | ||
readonly programs: Map<string, ProgramDictionary>; | ||
readonly stack: NodeStack; | ||
record(node: LinkableNode): this; | ||
getOrThrow(linkNode: AccountLinkNode): AccountNode; | ||
getOrThrow(linkNode: DefinedTypeLinkNode): DefinedTypeNode; | ||
getOrThrow(linkNode: InstructionAccountLinkNode): InstructionAccountNode; | ||
getOrThrow(linkNode: InstructionArgumentLinkNode): InstructionArgumentNode; | ||
getOrThrow(linkNode: InstructionLinkNode): InstructionNode; | ||
getOrThrow(linkNode: PdaLinkNode): PdaNode; | ||
getOrThrow(linkNode: ProgramLinkNode): ProgramNode; | ||
get(linkNode: AccountLinkNode): AccountNode | undefined; | ||
get(linkNode: DefinedTypeLinkNode): DefinedTypeNode | undefined; | ||
get(linkNode: InstructionAccountLinkNode): InstructionAccountNode | undefined; | ||
get(linkNode: InstructionArgumentLinkNode): InstructionArgumentNode | undefined; | ||
get(linkNode: InstructionLinkNode): InstructionNode | undefined; | ||
get(linkNode: PdaLinkNode): PdaNode | undefined; | ||
get(linkNode: ProgramLinkNode): ProgramNode | undefined; | ||
has(linkNode: LinkNode): boolean; | ||
recordPath(linkablePath: NodePath<LinkableNode>): this; | ||
getPathOrThrow<TLinkNode extends LinkNode>(linkPath: NodePath<TLinkNode>): NodePath<GetLinkableFromLinkNode<TLinkNode>>; | ||
getPath<TLinkNode extends LinkNode>(linkPath: NodePath<TLinkNode>): NodePath<GetLinkableFromLinkNode<TLinkNode>> | undefined; | ||
getOrThrow<TLinkNode extends LinkNode>(linkPath: NodePath<TLinkNode>): GetLinkableFromLinkNode<TLinkNode>; | ||
get<TLinkNode extends LinkNode>(linkPath: NodePath<TLinkNode>): GetLinkableFromLinkNode<TLinkNode> | undefined; | ||
has(linkPath: NodePath<LinkNode>): boolean; | ||
private getOrCreateProgramDictionary; | ||
@@ -37,0 +35,0 @@ private getOrCreateInstructionDictionary; |
import { Node, NodeKind } from '@codama/nodes'; | ||
import { Visitor } from './visitor'; | ||
export declare function mergeVisitor<TReturn, TNodeKind extends NodeKind = NodeKind>(leafValue: (node: Node) => TReturn, merge: (node: Node, values: TReturn[]) => TReturn, nodeKeys?: TNodeKind[]): Visitor<TReturn, TNodeKind>; | ||
export declare function mergeVisitor<TReturn, TNodeKind extends NodeKind = NodeKind>(leafValue: (node: Node) => TReturn, merge: (node: Node, values: TReturn[]) => TReturn, options?: { | ||
keys?: TNodeKind[]; | ||
}): Visitor<TReturn, TNodeKind>; | ||
//# sourceMappingURL=mergeVisitor.d.ts.map |
@@ -1,3 +0,2 @@ | ||
import { Node } from '@codama/nodes'; | ||
import type { NodeStack } from './NodeStack'; | ||
import { NodePath } from './NodePath'; | ||
export type NodeSelector = NodeSelectorFunction | NodeSelectorPath; | ||
@@ -11,8 +10,8 @@ /** | ||
* - `[someNode]someText` matches both the kind and the name of a node. | ||
* - `a.b.c` matches a node `c` such that its parent stack contains `a` and `b` in order (but not necessarily subsequent). | ||
* - `a.b.c` matches a node `c` such that its ancestors contains `a` and `b` in order (but not necessarily subsequent). | ||
*/ | ||
export type NodeSelectorPath = string; | ||
export type NodeSelectorFunction = (node: Node, stack: NodeStack) => boolean; | ||
export type NodeSelectorFunction = (path: NodePath) => boolean; | ||
export declare const getNodeSelectorFunction: (selector: NodeSelector) => NodeSelectorFunction; | ||
export declare const getConjunctiveNodeSelectorFunction: (selector: NodeSelector | NodeSelector[]) => NodeSelectorFunction; | ||
//# sourceMappingURL=NodeSelector.d.ts.map |
@@ -1,17 +0,27 @@ | ||
import { GetNodeFromKind, InstructionNode, Node, NodeKind, ProgramNode } from '@codama/nodes'; | ||
import { GetNodeFromKind, Node, NodeKind } from '@codama/nodes'; | ||
import { NodePath } from './NodePath'; | ||
export declare class NodeStack { | ||
/** | ||
* Contains all the node paths saved during the traversal. | ||
* | ||
* - The very last path is the current path which is being | ||
* used during the traversal. | ||
* - The other paths can be used to save and restore the | ||
* current path when jumping to different parts of the tree. | ||
* | ||
* There must at least be one path in the stack at all times. | ||
*/ | ||
private readonly stack; | ||
constructor(stack?: Node[]); | ||
constructor(...stack: readonly [...(readonly NodePath[]), NodePath] | readonly []); | ||
private get currentPath(); | ||
push(node: Node): void; | ||
pop(): Node | undefined; | ||
peek(): Node | undefined; | ||
find<TKind extends NodeKind>(kind: TKind | TKind[]): GetNodeFromKind<TKind> | undefined; | ||
getProgram(): ProgramNode | undefined; | ||
getInstruction(): InstructionNode | undefined; | ||
all(): readonly Node[]; | ||
pushPath(newPath?: NodePath): void; | ||
popPath(): NodePath; | ||
getPath(): NodePath; | ||
getPath<TKind extends NodeKind>(kind: TKind | TKind[]): NodePath<GetNodeFromKind<TKind>>; | ||
isEmpty(): boolean; | ||
clone(): NodeStack; | ||
toString(): string; | ||
toStringArray(): string[]; | ||
} | ||
//# sourceMappingURL=NodeStack.d.ts.map |
import { Node, NodeKind } from '@codama/nodes'; | ||
import { Visitor } from './visitor'; | ||
export declare function nonNullableIdentityVisitor<TNodeKind extends NodeKind = NodeKind>(nodeKeys?: TNodeKind[]): Visitor<Node, TNodeKind>; | ||
export declare function nonNullableIdentityVisitor<TNodeKind extends NodeKind = NodeKind>(options?: { | ||
keys?: TNodeKind[]; | ||
}): Visitor<Node, TNodeKind>; | ||
//# sourceMappingURL=nonNullableIdentityVisitor.d.ts.map |
import { type NodeKind } from '@codama/nodes'; | ||
import { LinkableDictionary } from './LinkableDictionary'; | ||
import { Visitor } from './visitor'; | ||
export declare function recordLinkablesVisitor<TReturn, TNodeKind extends NodeKind>(visitor: Visitor<TReturn, TNodeKind>, linkables: LinkableDictionary): Visitor<TReturn, TNodeKind>; | ||
export declare function getRecordLinkablesVisitor<TNodeKind extends NodeKind>(linkables: LinkableDictionary): Visitor<void, TNodeKind>; | ||
export declare function recordLinkablesOnFirstVisitVisitor<TReturn, TNodeKind extends NodeKind>(visitor: Visitor<TReturn, TNodeKind>, linkables: LinkableDictionary): Visitor<TReturn, TNodeKind>; | ||
//# sourceMappingURL=recordLinkablesVisitor.d.ts.map |
import { NodeKind } from '@codama/nodes'; | ||
export declare function removeDocsVisitor<TNodeKind extends NodeKind = NodeKind>(nodeKeys?: TNodeKind[]): import("./visitor").Visitor<import("@codama/nodes").Node, TNodeKind>; | ||
export declare function removeDocsVisitor<TNodeKind extends NodeKind = NodeKind>(options?: { | ||
keys?: TNodeKind[]; | ||
}): import("./visitor").Visitor<import("@codama/nodes").Node, TNodeKind>; | ||
//# sourceMappingURL=removeDocsVisitor.d.ts.map |
import { Node, NodeKind } from '@codama/nodes'; | ||
import { Visitor } from './visitor'; | ||
export declare function staticVisitor<TReturn, TNodeKind extends NodeKind = NodeKind>(fn: (node: Node) => TReturn, nodeKeys?: TNodeKind[]): Visitor<TReturn, TNodeKind>; | ||
export declare function staticVisitor<TReturn, TNodeKind extends NodeKind = NodeKind>(fn: (node: Node) => TReturn, options?: { | ||
keys?: TNodeKind[]; | ||
}): Visitor<TReturn, TNodeKind>; | ||
//# sourceMappingURL=staticVisitor.d.ts.map |
@@ -5,8 +5,11 @@ import { Node, NodeKind } from '@codama/nodes'; | ||
import { Visitor } from './visitor'; | ||
export type TopDownNodeTransformer<TNode extends Node = Node> = <T extends TNode = TNode>(node: T, stack: NodeStack) => T | null; | ||
export type TopDownNodeTransformerWithSelector<TNode extends Node = Node> = { | ||
export type TopDownNodeTransformer = <TNode extends Node>(node: TNode, stack: NodeStack) => TNode | null; | ||
export type TopDownNodeTransformerWithSelector = { | ||
select: NodeSelector | NodeSelector[]; | ||
transform: TopDownNodeTransformer<TNode>; | ||
transform: TopDownNodeTransformer; | ||
}; | ||
export declare function topDownTransformerVisitor<TNodeKind extends NodeKind = NodeKind>(transformers: (TopDownNodeTransformer | TopDownNodeTransformerWithSelector)[], nodeKeys?: TNodeKind[]): Visitor<Node | null, TNodeKind>; | ||
export declare function topDownTransformerVisitor<TNodeKind extends NodeKind = NodeKind>(transformers: (TopDownNodeTransformer | TopDownNodeTransformerWithSelector)[], options?: { | ||
keys?: TNodeKind[]; | ||
stack?: NodeStack; | ||
}): Visitor<Node | null, TNodeKind>; | ||
//# sourceMappingURL=topDownTransformerVisitor.d.ts.map |
import type { NodeKind } from '@codama/nodes'; | ||
import { Visitor } from './visitor'; | ||
export declare function voidVisitor<TNodeKind extends NodeKind = NodeKind>(nodeKeys?: TNodeKind[]): Visitor<void, TNodeKind>; | ||
export declare function voidVisitor<TNodeKind extends NodeKind = NodeKind>(options?: { | ||
keys?: TNodeKind[]; | ||
}): Visitor<void, TNodeKind>; | ||
//# sourceMappingURL=voidVisitor.d.ts.map |
{ | ||
"name": "@codama/visitors-core", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Core visitors for the Codama framework", | ||
@@ -39,7 +39,7 @@ "exports": { | ||
"json-stable-stringify": "^1.1.1", | ||
"@codama/errors": "1.0.0", | ||
"@codama/nodes": "1.0.0" | ||
"@codama/errors": "1.1.0", | ||
"@codama/nodes": "1.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/json-stable-stringify": "^1.0.36" | ||
"@types/json-stable-stringify": "^1.1.0" | ||
}, | ||
@@ -46,0 +46,0 @@ "license": "MIT", |
183
README.md
@@ -99,3 +99,3 @@ # Codama ➤ Visitors ➤ Core | ||
Before we list each available core visitor, it is important to know that each of these functions optionally accepts a node kind or an array of node kinds **as their last argument**. This allows us to restrict the visitor to a specific set of nodes and will return a `Visitor<T, U>` instance where `U` is the union of the provided node kinds. | ||
Before we list each available core visitor, it is important to know that each of these functions optionally accepts a node kind or an array of node kinds **as a `keys` attribute in their `options`**. This allows us to restrict the visitor to a specific set of nodes and will return a `Visitor<T, U>` instance where `U` is the union of the provided node kinds. | ||
@@ -106,18 +106,17 @@ Here are some examples: | ||
// This visitor only accepts `ProgramNodes`. | ||
const visitor: Visitor<number, 'programNode'> = myNumberVisitor('programNode'); | ||
const visitor: Visitor<number, 'programNode'> = voidVisitor({ keys: 'programNode' }); | ||
// This visitor accepts both `NumberTypeNodes` and `StringTypeNodes`. | ||
const visitor: Visitor<number, 'numberTypeNode' | 'stringTypeNode'> = myNumberVisitor([ | ||
'numberTypeNode', | ||
'stringTypeNode', | ||
]); | ||
const visitor: Visitor<number, 'numberTypeNode' | 'stringTypeNode'> = voidVisitor({ | ||
keys: ['numberTypeNode', 'stringTypeNode'], | ||
}); | ||
// This visitor accepts all type nodes. | ||
const visitor: Visitor<number, TypeNode['kind']> = myNumberVisitor(TYPE_NODES); | ||
const visitor: Visitor<number, TypeNode['kind']> = voidVisitor({ keys: TYPE_NODES }); | ||
// This visitor accepts all nodes. | ||
const visitor: Visitor<number> = myNumberVisitor(); | ||
const visitor: Visitor<number> = voidVisitor(); | ||
``` | ||
In the following sections describing the core visitors, this exact pattern can be used to restrict the visitors to specific node kinds. We won't cover this for each visitor but know that you can achieve this via the last argument of each function. | ||
In the following sections describing the core visitors, this exact pattern can be used to restrict the visitors to specific node kinds. We won't cover this for each visitor but know that you can achieve this via the `keys` option of each function. | ||
@@ -413,7 +412,51 @@ ### `voidVisitor` | ||
## Recording node stacks | ||
## Recording node paths | ||
### `NodePath` | ||
The `NodePath` type defines an immutable array of `Nodes` that represents any path of nodes in the Codama IDL. It accepts an optional type parameter that tells us the type of the last node in the path. For instance `NodePath<NumberTypeNode>` represents a path of node ending with a `NumberTypeNode`. | ||
Additionally, there are several utility functions to use with `NodePath` instances: | ||
```ts | ||
// An example of a node path. | ||
const path: NodePath<AccountNode> = [rootNode, programNode, accountNode]; | ||
// Access the last node in the path. I.e. given NodePath<T>, returns T. | ||
const lastNode: AccountNode = getLastNodeFromPath(path); | ||
// Access the first/last node of a specific kind in the path. | ||
const firstProgramNode: ProgramNode | undefined = findFirstNodeFromPath(path, 'programNode'); | ||
const lastProgramNode: ProgramNode | undefined = findLastNodeFromPath(path, 'programNode'); | ||
// Access the last program/instruction node in the path (sugar for `findLastNodeFromPath`). | ||
const programNode: ProgramNode | undefined = findProgramNodeFromPath(path); | ||
const instructionNode: InstructionNode | undefined = findInstructionNodeFromPath(path); | ||
// Get the subpath of a path from the beginning to the last node matching a specific kind. | ||
const subpath: NodePath = getNodePathUntilLastNode(path, 'programNode'); | ||
// ^ [rootNode, programNode] | ||
// Check that a path is not empty. | ||
if (isFilledNodePath(path as NodePath)) { | ||
path satisfies NodePath<Node>; | ||
} | ||
// Check that a path finishes with a node matching the provided kind or kinds. | ||
if (isNodePath(path as NodePath, ['AccountNode', 'InstructionNode'])) { | ||
path satisfies NodePath<AccountNode | InstructionNode>; | ||
} | ||
// Assert that a path finishes with a node matching the provided kind or kinds. | ||
assertIsNodePath(path as NodePath, ['AccountNode', 'InstructionNode']); | ||
path satisfies NodePath<AccountNode | InstructionNode>; | ||
// Display paths as strings or arrays of strings. | ||
nodePathToStringArray(path); // -> ['[rootNode]', '[programNode]token', '[accountNode]mint'] | ||
nodePathToString(path); // -> "[rootNode] > [programNode]token > [accountNode]mint" | ||
``` | ||
### `NodeStack` | ||
The `NodeStack` class is a utility that allows us to record the stack of nodes that led to a specific node. | ||
The `NodeStack` class is a utility that allows us to record the path of nodes that led to the node being currently visited. It is essentially a mutable version of `NodePath` that pushes and pops `Nodes` as we go down and up the tree of nodes. | ||
@@ -438,3 +481,3 @@ For instance, consider the following node: | ||
Once you have access to a `NodeStack` instance — provided by various utility visitors — you may use the following methods: | ||
Once you have access to a `NodeStack` instance, you may use the following methods: | ||
@@ -444,22 +487,32 @@ ```ts | ||
nodeStack.push(node); | ||
// Pop the last node out of the stack. | ||
const lastNode = nodeStack.pop(); | ||
// Peek at the last node in the stack. | ||
const lastNode = nodeStack.peek(); | ||
// Get all the nodes in the stack as an array. | ||
const nodes = nodeStack.all(); | ||
// Get the closest node in the stack matching one or several node kinds. | ||
const nodes = nodeStack.find('accountNode'); | ||
// Get the closest program node in the stack. | ||
const nodes = nodeStack.getProgram(); | ||
// Get the closest instruction node in the stack. | ||
const nodes = nodeStack.getInstruction(); | ||
// Get all the nodes in the stack as an immutable `NodePath` array. | ||
const path: NodePath = nodeStack.getPath(); | ||
// Get a `NodePath` whilst asserting on the last node kind. | ||
const path: NodePath<AccountNode> = nodeStack.getPath('accountNode'); | ||
// Check if the stack is empty. | ||
const isEmpty = nodeStack.isEmpty(); | ||
// Clone the stack. | ||
const clonedStack = nodeStack.clone(); | ||
// Get a string representation of the stack. | ||
const stackString = nodeStack.toString(); | ||
``` | ||
Additionally, it is possible to save and restore multiple node paths within a `NodeStack` by using the `pushPath` and `popPath` methods. This is for more advanced uses cases where you need to jump from one part of the tree, to a different part of the tree, and back — without loosing the context of the original path. An application of this is when we need to follow a node from a `LinkNode` (see ["Resolving link nodes"](#resolving-link-nodes) below for more details). | ||
```ts | ||
// Save the current path and push a new path. | ||
stack.pushPath([rootNode, programNode, linkableNode]); | ||
// Pop the current path and restore the previous path. | ||
const previousPath = stack.popPath(); | ||
``` | ||
### `recordNodeStackVisitor` | ||
@@ -469,2 +522,4 @@ | ||
Note that the `recordNodeStackVisitor` **should be the last visitor** in the pipe to ensure that the stack is correctly recorded and that the current node visited is part of the stack. | ||
For instance, here's how we can log the `NodeStack` of any base visitor as we visit the nodes. | ||
@@ -476,11 +531,27 @@ | ||
baseVisitor, | ||
v => recordNodeStackVisitor(v, stack), | ||
v => | ||
interceptVisitor(v, (node, next) => { | ||
console.log(stack.clone().toString()); | ||
console.log(nodePathToString(stack.getPath())); | ||
return next(node); | ||
}), | ||
v => recordNodeStackVisitor(v, stack), | ||
); | ||
``` | ||
Also note that some core visitors such as the `bottomUpTransformerVisitor` or the `getByteSizeVisitor` use a `NodeStack` internally to keep track of the current path. If you use these visitor within another visitor, you may wish to provide your own `NodeStack` instance as an option so that the same `NodeStack` is used across all visitors throughout the traversal. | ||
```ts | ||
const stack = new NodeStack(); | ||
const byteSizeVisitor = getByteSizeVisitor(..., { stack }); | ||
const visitor = pipe( | ||
voidVisitor(), | ||
v => tapVisitor(v, 'definedTypeNode', node => { | ||
const byteSize = visit(node, byteSizeVisitor); | ||
console.log(`The byte size of ${node.name} is ${byteSize}`); | ||
}), | ||
v => recordNodeStackVisitor(v, stack), | ||
); | ||
``` | ||
## Selecting nodes | ||
@@ -490,5 +561,5 @@ | ||
To take end, the `NodeSelector` type represents a node selection that can take two forms: | ||
To that end, the `NodeSelector` type represents a node selection that can take two forms: | ||
- A `NodeSelectorFunction` of type `(node: Node, stack: NodeStack) => boolean`. In this case, the provided function is used to determine if the node should be selected. | ||
- A `NodeSelectorFunction` of type `(path: NodePath) => boolean`. In this case, the provided function is used to determine if the last node in the provided `NodePath` should be selected. | ||
- A `NodeSelectorPath` of type `string`. In this case, the provided string uses a simple syntax to select nodes. | ||
@@ -579,2 +650,4 @@ | ||
By default, this visitor will keep track of its own `NodeStack` but you may provide your own via the `stack` option in order to share the same `NodeStack` across multiple visitors. | ||
### `topDownTransformerVisitor` | ||
@@ -600,2 +673,4 @@ | ||
Here as well, you may use the `stack` option to provide your own `NodeStack` instance. | ||
### `deleteNodesVisitor` | ||
@@ -610,2 +685,4 @@ | ||
Here as well, you may use the `stack` option to provide your own `NodeStack` instance. | ||
## String representations | ||
@@ -667,23 +744,43 @@ | ||
// Record program nodes. | ||
linkables.record(programNode); | ||
// Record linkable nodes via their full path. | ||
linkables.recordPath([rootNode, programNode, accountNode]); | ||
// Record other linkable nodes with their associated program node. | ||
linkables.record(accountNode); | ||
// Get a linkable node using the full path of a link node, or return undefined if it is not found. | ||
const programNode: ProgramNode | undefined = linkables.get([...somePath, programLinkNode]); | ||
// Get a linkable node using a link node, or throw an error if it is not found. | ||
const programNode = linkables.getOrThrow(programLinkNode); | ||
// Get a linkable node using the full path of a link node, or throw an error if it is not found. | ||
const programNode: ProgramNode = linkables.getOrThrow([...somePath, programLinkNode]); | ||
// Get a linkable node using a link node, or return undefined if it is not found. | ||
const accountNode = linkables.get(accountLinkNode); | ||
// Get the path of a linkable node using the full path of a link node, or return undefined if it is not found. | ||
const accountPath: NodePath<AccountNode> | undefined = linkables.getPath([...somePath, accountLinkNode]); | ||
// Get the path of a linkable node using the full path of a link node, or throw an error if it is not found. | ||
const accountPath: NodePath<AccountNode> = linkables.getPathOrThrow([...somePath, accountLinkNode]); | ||
``` | ||
Note that this API must be used in conjunction with the `recordLinkablesVisitor` to record the linkable nodes and, later on, resolve the link nodes as we traverse the nodes. This is because the `LinkableDictionary` instance keeps track of its own internal `NodeStack` in order to understand which program node should be used for a given link node. | ||
Note that: | ||
### `recordLinkablesVisitor` | ||
- The path of the recorded node must be provided when recording a linkable node. | ||
- The path of the link node must be provided when getting a linkable node (or its path) from it. | ||
Much like the `recordNodeStackVisitor`, the `recordLinkablesVisitor` allows us to record linkable nodes as we traverse the tree of nodes. It accepts a base visitor and `LinkableDictionary` instance; and records any linkable node it encounters. | ||
This API may be used with the `recordLinkablesOnFirstVisitVisitor` to record the linkable nodes before the first node visit; as well as the `recordNodeStackVisitor` to keep track of the current node path when accessing the linkable nodes. | ||
This means that we can inject the `LinkableDictionary` instance into another extension of the base visitor to resolve any link node we encounter. | ||
### `getRecordLinkablesVisitor` | ||
This visitor accepts a `LinkableDictionary` instance and records all linkable nodes it encounters when visiting the nodes. | ||
```ts | ||
const linkables = new LinkableDictionary(); | ||
visit(rootNode, getRecordLinkablesVisitor(linkables)); | ||
// Now, all linkable nodes are recorded in the `linkables` dictionary. | ||
``` | ||
### `recordLinkablesOnFirstVisitVisitor` | ||
This visitor is a utility that combines `interceptFirstVisitVisitor` and `getRecordLinkablesVisitor` to record all linkable nodes before the first visit of any node. | ||
It accepts a base visitor and a `LinkableDictionary` instance; and returns a new visitor that records all linkable nodes it encounters before the very first visit of the provided base visitor. This means that we can inject the `LinkableDictionary` instance into other extensions of the base visitor to resolve any link node we encounter. | ||
Note that the `recordLinkablesOnFirstVisitVisitor` **should be the last visitor** in the pipe to ensure that all linkable nodes are recorded before being used. | ||
Here's an example that records a `LinkableDictionary` and uses it to log the amount of seeds in each linked PDA node. | ||
@@ -693,2 +790,3 @@ | ||
const linkables = new LinkableDictionary(); | ||
const stack = new NodeStack(); | ||
const visitor = pipe( | ||
@@ -698,11 +796,10 @@ baseVisitor, | ||
tapVisitor(v, 'pdaLinkNode', node => { | ||
const pdaNode = linkables.getOrThrow(node); | ||
const pdaNode = linkables.getOrThrow(stack.getPath(node.kind)); | ||
console.log(`${pdaNode.seeds.length} seeds`); | ||
}), | ||
v => recordLinkablesVisitor(v, linkables), | ||
v => recordNodeStackVisitor(v, stack), | ||
v => recordLinkablesOnFirstVisitVisitor(v, linkables), | ||
); | ||
``` | ||
Note that the `recordLinkablesVisitor` should always be the last visitor in the pipe to ensure that all linkable nodes are recorded before being used. | ||
## Other useful visitors | ||
@@ -722,2 +819,4 @@ | ||
By default, this visitor will keep track of its own `NodeStack` but you may provide your own via the `stack` option in order to share the same `NodeStack` across multiple visitors. | ||
### `getResolvedInstructionInputsVisitor` | ||
@@ -724,0 +823,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1349156
71
9338
830
+ Added@codama/errors@1.1.0(transitive)
+ Added@codama/node-types@1.1.0(transitive)
+ Added@codama/nodes@1.1.0(transitive)
- Removed@codama/errors@1.0.0(transitive)
- Removed@codama/node-types@1.0.0(transitive)
- Removed@codama/nodes@1.0.0(transitive)
Updated@codama/errors@1.1.0
Updated@codama/nodes@1.1.0