Skip to content

Commit

Permalink
feat(traversal): add traverse function
Browse files Browse the repository at this point in the history
  • Loading branch information
char0n committed Oct 28, 2020
1 parent fe2a4e2 commit 3d1c64d
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 8 deletions.
119 changes: 119 additions & 0 deletions apidom/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apidom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"prettier": "=2.0.5",
"regenerator-runtime": "=0.13.5",
"rimraf": "=3.0.2",
"sinon": "=9.2.1",
"terser-webpack-plugin": "=3.0.2",
"tree-sitter-cli": "=0.17.0",
"typescript": "=3.9.3",
Expand Down
2 changes: 1 addition & 1 deletion apidom/packages/apidom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export {
export { default as createPredicate } from './predicates/helpers';

export { ArraySlice } from 'minim';
export { filter, reject, find, some } from './traversal';
export { filter, reject, find, some, traverse } from './traversal';

export const createNamespace = (namespacePlugin?: NamespacePlugin): ApiDOMNamespace => {
const namespace = new ApiDOMNamespace();
Expand Down
64 changes: 57 additions & 7 deletions apidom/packages/apidom/src/traversal.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { visit, BREAK } from 'apidom-ast';
import stampit from 'stampit';
import { ArraySlice } from 'minim';
import { Pred, curry, curryN, pipe, F as stubFalse, complement, pathOr } from 'ramda';
import { isString, isNotUndefined } from 'ramda-adjunct';
import { Pred, curry, curryN, pipe, F as stubFalse, complement, pathOr, defaultTo } from 'ramda';
import { isString, isFunction, isNotUndefined, noop } from 'ramda-adjunct';
import { visit, BREAK } from 'apidom-ast';

import {
isElement,
isObjectElement,
isArrayElement,
isNumberElement,
Expand Down Expand Up @@ -48,13 +49,14 @@ const keyMap = {
member: ['key', 'value'],
};

const Visitor = stampit({
const PredicateVisitor = stampit({
props: {
result: [],
predicate: stubFalse,
return: undefined,
},
init({ predicate }) {
// @ts-ignore
init({ predicate = this.predicate } = {}) {
this.result = [];
this.predicate = predicate;
},
Expand All @@ -69,11 +71,30 @@ const Visitor = stampit({
},
});

const CallbackVisitor = stampit(PredicateVisitor, {
props: {
callback: noop,
},
// @ts-ignore
init({ callback = this.callback } = {}) {
this.callback = callback;
},
methods: {
enter(element) {
if (this.predicate(element)) {
this.callback(element);
return this.return;
}
return undefined;
},
},
});

// finds all elements matching the predicate
// filter :: Pred -> Element -> ArraySlice
export const filter = curry(
<T extends Element>(predicate: Pred, element: T): ArraySlice => {
const visitor = Visitor({ predicate });
const visitor = PredicateVisitor({ predicate });

// @ts-ignore
visit(element, visitor, { keyMap, nodeTypeGetter: getNodeType, nodePredicate: isNode });
Expand All @@ -93,7 +114,7 @@ export const reject = curry(
// first first element in that satisfies the provided predicate
// find :: Pred -> Element -> Element | Undefined
export const find = curry(<T extends Element>(predicate: Pred, element: T): T | undefined => {
const visitor = Visitor({ predicate, return: BREAK });
const visitor = PredicateVisitor({ predicate, return: BREAK });

// @ts-ignore
visit(element, visitor, { keyMap, nodeTypeGetter: getNodeType, nodePredicate: isNode });
Expand All @@ -106,3 +127,32 @@ export const find = curry(<T extends Element>(predicate: Pred, element: T): T |
export const some = curry(<T extends Element>(predicate: Pred, element: T): boolean => {
return isNotUndefined(find(predicate)(element));
});

type Callback = <T extends Element>(element: T) => void;
interface TraverseOptions {
callback?: Callback;
predicate?: Pred;
}

// executes the callback on this object and all descendants
// traverse :: Callback | { predicate: Pred, callback: Callback } -> Element -> Undefined
export const traverse = curry(
<T extends Element>(options: Callback | TraverseOptions, element: T): void => {
let callback;
let predicate;

if (isFunction(options)) {
callback = options;
} else {
({ callback, predicate } = options);
}

const visitor = CallbackVisitor({
callback: defaultTo(noop, callback),
predicate: defaultTo(isElement, predicate),
});

// @ts-ignore
visit(element, visitor, { keyMap, nodeTypeGetter: getNodeType, nodePredicate: isNode });
},
);
Loading

0 comments on commit 3d1c64d

Please sign in to comment.