Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

return result from context.next() #17

Merged
merged 3 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});
Loading