Skip to content

Commit

Permalink
Merge pull request #17 from Rich-Harris/return-node-from-next
Browse files Browse the repository at this point in the history
return result from `context.next()`
  • Loading branch information
Rich-Harris authored Oct 28, 2023
2 parents 5c7c1f2 + 9581c89 commit aaf2ce2
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ walk(node, state, visitors);

Each visitor receives a second argument, `context`, which is an object with the following properties and methods:

- `next(state?: State): void` — a function that allows you to control when child nodes are visited, and which state they are visited with
- `next(state?: State): void` — a function that allows you to control when child nodes are visited, and which state they are visited with. If child visitors transform their inputs, this will return the transformed node (if not, returns `undefined`)
- `path: Node[]` — an array of parent nodes. For example, to get the root node you would do `path.at(0)`; to get the current node's immediate parent you would do `path.at(-1)`
- `state: State` — an object of the same type as the second argument to `walk`. Visitors can pass new state objects to their children with `next(childState)` or `visit(node, childState)`
- `stop(): void` — prevents any subsequent traversal
Expand Down
2 changes: 1 addition & 1 deletion src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type Visitors<T extends BaseNode, U> = T['type'] extends '_'
: SpecialisedVisitors<T, U> & { _?: Visitor<T, U, T> };

export interface Context<T, U> {
next: (state?: U) => void;
next: (state?: U) => T | void;
path: T[];
state: U;
stop: () => void;
Expand Down
6 changes: 6 additions & 0 deletions src/walk.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ export function walk(node, state, visitors) {
}
}
path.pop();

if (Object.keys(mutations).length > 0) {
return { ...node, ...mutations };
}
},
stop: () => {
stopped = true;
Expand Down Expand Up @@ -103,6 +107,8 @@ export function walk(node, state, visitors) {
...context,
state: next_state
});

return inner_result;
}
});

Expand Down
71 changes: 71 additions & 0 deletions test/transformation.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,74 @@ test('respects individual visitors if universal visitor calls next()', () => {
children: [{ type: 'TransformedA' }, { type: 'B' }, { type: 'C' }]
});
});

test('returns the result of child transforms when calling next', () => {
/** @type {import('./types').TestNode} */
const tree = {
type: 'Root',
children: [{ type: 'A' }, { type: 'B' }, { type: 'C' }]
};

let count = 0;
let children;

const transformed = /** @type {import('./types').TestNode} */ (
walk(/** @type {import('./types').TestNode} */ (tree), null, {
Root: (node, { next }) => {
const result = /** @type {import('./types').Root} */ (next());
children = result.children;
return node;
},
A: (node) => {
count += 1;
return {
type: 'TransformedA'
};
},
C: (node) => {
count += 1;
return {
type: 'TransformedC'
};
}
})
);

expect(count).toBe(2);

// check that `tree` wasn't mutated
expect(tree).toEqual({
type: 'Root',
children: [{ type: 'A' }, { type: 'B' }, { type: 'C' }]
});

expect(transformed).toBe(tree);

expect(children).toEqual([
{ type: 'TransformedA' },
{ type: 'B' },
{ type: 'TransformedC' }
]);
});

test('returns undefined if there are no child transformations', () => {
/** @type {import('./types').TestNode} */
const tree = {
type: 'Root',
children: [{ type: 'A' }, { type: 'B' }, { type: 'C' }]
};

let result;

const transformed = /** @type {import('./types').TestNode} */ (
walk(/** @type {import('./types').TestNode} */ (tree), null, {
Root: (node, { next }) => {
result = next();
}
})
);

expect(transformed).toBe(tree);

expect(result).toBe(undefined);
});

0 comments on commit aaf2ce2

Please sign in to comment.