tree-visit
Advanced tools
Comparing version 0.2.3 to 0.3.0
@@ -96,2 +96,25 @@ "use strict"; | ||
}); | ||
describe('reuseIndexPath option', () => { | ||
it('allocates new index paths', () => { | ||
let indexPaths = []; | ||
(0, index_1.visit)(example, { | ||
onEnter: (_child, indexPath) => { | ||
indexPaths.push(indexPath); | ||
}, | ||
getChildren, | ||
}); | ||
expect(indexPaths).toEqual([[], [0], [0, 0], [0, 1], [1], [1, 0], [1, 1]]); | ||
}); | ||
it('reuses index paths', () => { | ||
let indexPaths = []; | ||
(0, index_1.visit)(example, { | ||
onEnter: (_child, indexPath) => { | ||
indexPaths.push(indexPath); | ||
}, | ||
getChildren, | ||
reuseIndexPath: true, | ||
}); | ||
expect(indexPaths).toEqual([[], [], [], [], [], [], []]); | ||
}); | ||
}); | ||
it('traverses deeply nested nodes', () => { | ||
@@ -98,0 +121,0 @@ let enterNames = []; |
@@ -8,2 +8,3 @@ "use strict"; | ||
const remove_1 = require("../remove"); | ||
const replace_1 = require("../replace"); | ||
const withOptions_1 = require("../withOptions"); | ||
@@ -111,2 +112,31 @@ describe('insert', () => { | ||
}); | ||
describe('replace', () => { | ||
it('replaces root', () => { | ||
const result = (0, replace_1.replace)(node_1.example, { | ||
at: [], | ||
node: { name: 'x', indexPath: [] }, | ||
create: node_1.createNode, | ||
getChildren: node_1.getChildren, | ||
}); | ||
expect((0, diagram_1.diagram)(result, { getChildren: node_1.getChildren, getLabel: node_1.getLabel })).toMatchSnapshot(); | ||
}); | ||
it('replaces child', () => { | ||
const result = (0, replace_1.replace)(node_1.example, { | ||
at: [1], | ||
node: { name: 'x', indexPath: [] }, | ||
create: node_1.createNode, | ||
getChildren: node_1.getChildren, | ||
}); | ||
expect((0, diagram_1.diagram)(result, { getChildren: node_1.getChildren, getLabel: node_1.getLabel })).toMatchSnapshot(); | ||
}); | ||
it('replaces nested', () => { | ||
const result = (0, replace_1.replace)(node_1.example, { | ||
at: [0, 1], | ||
node: { name: 'x', indexPath: [] }, | ||
create: node_1.createNode, | ||
getChildren: node_1.getChildren, | ||
}); | ||
expect((0, diagram_1.diagram)(result, { getChildren: node_1.getChildren, getLabel: node_1.getLabel })).toMatchSnapshot(); | ||
}); | ||
}); | ||
describe('move', () => { | ||
@@ -202,2 +232,11 @@ it('moves node to the same place', () => { | ||
}); | ||
it('moves node to non-existent index', () => { | ||
const result = (0, move_1.move)(node_1.example, { | ||
indexPaths: [[0, 1]], | ||
to: [1, 7], | ||
create: node_1.createNode, | ||
getChildren: node_1.getChildren, | ||
}); | ||
expect((0, diagram_1.diagram)(result, { getChildren: node_1.getChildren, getLabel: node_1.getLabel })).toMatchSnapshot(); | ||
}); | ||
}); | ||
@@ -229,2 +268,9 @@ describe('partially applied', () => { | ||
}); | ||
it('replaces node', () => { | ||
const result = Tree.replace(node_1.example, { | ||
at: [1], | ||
node: { name: 'x', indexPath: [] }, | ||
}); | ||
expect((0, diagram_1.diagram)(result, { getChildren: node_1.getChildren, getLabel: node_1.getLabel })).toMatchSnapshot(); | ||
}); | ||
it('mutate in place', () => { | ||
@@ -231,0 +277,0 @@ // Clone the example mock so we can mutate it |
@@ -9,2 +9,3 @@ export * from './access'; | ||
export * from './insert'; | ||
export * from './map'; | ||
export * from './move'; | ||
@@ -14,4 +15,5 @@ export * from './options'; | ||
export * from './remove'; | ||
export * from './replace'; | ||
export * from './sort'; | ||
export * from './visit'; | ||
export * from './withOptions'; |
@@ -21,2 +21,3 @@ "use strict"; | ||
__exportStar(require("./insert"), exports); | ||
__exportStar(require("./map"), exports); | ||
__exportStar(require("./move"), exports); | ||
@@ -26,4 +27,5 @@ __exportStar(require("./options"), exports); | ||
__exportStar(require("./remove"), exports); | ||
__exportStar(require("./replace"), exports); | ||
__exportStar(require("./sort"), exports); | ||
__exportStar(require("./visit"), exports); | ||
__exportStar(require("./withOptions"), exports); |
@@ -24,4 +24,5 @@ import { IndexPath } from './indexPath'; | ||
export declare function getRemovalOperations<T>(indexPaths: IndexPath[]): OperationMap<T>; | ||
export declare function getReplaceOperations<T>(indexPath: IndexPath, node: T): OperationMap<T>; | ||
export declare function applyOperations<T>(node: T, operations: OperationMap<T>, options: MutationBaseOptions<T>): T; | ||
export declare function splice<T>(array: T[], start: number, deleteCount: number, ...items: T[]): T[]; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.splice = exports.applyOperations = exports.getRemovalOperations = exports.getInsertionOperations = exports.replaceOperation = exports.removeOperation = exports.insertOperation = void 0; | ||
exports.splice = exports.applyOperations = exports.getReplaceOperations = exports.getRemovalOperations = exports.getInsertionOperations = exports.replaceOperation = exports.removeOperation = exports.insertOperation = void 0; | ||
const ancestors_1 = require("./ancestors"); | ||
@@ -27,6 +27,8 @@ const map_1 = require("./map"); | ||
exports.replaceOperation = replaceOperation; | ||
function splitIndexPath(indexPath) { | ||
return [indexPath.slice(0, -1), indexPath[indexPath.length - 1]]; | ||
} | ||
function getInsertionOperations(indexPath, nodes, operations = new Map()) { | ||
var _a; | ||
const parentIndexPath = indexPath.slice(0, -1); | ||
const index = indexPath.at(-1); | ||
const [parentIndexPath, index] = splitIndexPath(indexPath); | ||
// Mark all parents for replacing | ||
@@ -84,2 +86,19 @@ for (let i = parentIndexPath.length - 1; i >= 0; i--) { | ||
exports.getRemovalOperations = getRemovalOperations; | ||
function getReplaceOperations(indexPath, node) { | ||
const operations = new Map(); | ||
const [parentIndexPath, index] = splitIndexPath(indexPath); | ||
// Mark all parents for replacing | ||
for (let i = parentIndexPath.length - 1; i >= 0; i--) { | ||
const parentKey = parentIndexPath.slice(0, i).join(); | ||
operations.set(parentKey, replaceOperation()); | ||
} | ||
operations.set(parentIndexPath.join(), { | ||
type: 'removeThenInsert', | ||
removeIndexes: [index], | ||
insertIndex: index, | ||
insertNodes: [node], | ||
}); | ||
return operations; | ||
} | ||
exports.getReplaceOperations = getReplaceOperations; | ||
function applyOperations(node, operations, options) { | ||
@@ -86,0 +105,0 @@ return (0, map_1.map)(node, Object.assign(Object.assign({}, options), { |
import { IndexPath } from './indexPath'; | ||
export declare type BaseOptions<T> = { | ||
getChildren: (node: T, indexPath: IndexPath) => T[]; | ||
/** | ||
* By default, a new IndexPath array is allocated on every callback. | ||
* | ||
* For maximum performance, you can reuse the same IndexPath array | ||
* by setting this option to `true`. If you do this, make sure to | ||
* clone the IndexPath array if you store it for later use. | ||
*/ | ||
reuseIndexPath?: boolean; | ||
}; | ||
@@ -5,0 +13,0 @@ export declare type MutationBaseOptions<T> = BaseOptions<T> & { |
@@ -21,11 +21,8 @@ "use strict"; | ||
function visit(node, options) { | ||
var _a, _b; | ||
const normalizedOptions = Object.assign(Object.assign({}, options), { onEnter: (_a = options.onEnter) !== null && _a !== void 0 ? _a : (() => { }), onLeave: (_b = options.onLeave) !== null && _b !== void 0 ? _b : (() => { }) }); | ||
visitInternal(node, normalizedOptions); | ||
} | ||
exports.visit = visit; | ||
function visitInternal(root, options) { | ||
const { onEnter, onLeave, getChildren } = options; | ||
let indexPath = []; | ||
let stack = [{ node: root }]; | ||
let stack = [{ node }]; | ||
const getIndexPath = options.reuseIndexPath | ||
? () => indexPath | ||
: () => indexPath.slice(); | ||
while (stack.length > 0) { | ||
@@ -35,8 +32,8 @@ let wrapper = stack[stack.length - 1]; | ||
if (wrapper.state === undefined) { | ||
const enterResult = onEnter(wrapper.node, indexPath); | ||
const enterResult = onEnter === null || onEnter === void 0 ? void 0 : onEnter(wrapper.node, getIndexPath()); | ||
if (enterResult === exports.STOP) | ||
return enterResult; | ||
return; | ||
wrapper.state = enterResult === exports.SKIP ? -1 : 0; | ||
} | ||
const children = wrapper.children || getChildren(wrapper.node, indexPath); | ||
const children = wrapper.children || getChildren(wrapper.node, getIndexPath()); | ||
if (!wrapper.children) { | ||
@@ -55,5 +52,5 @@ wrapper.children = children; | ||
} | ||
const leaveResult = onLeave(wrapper.node, indexPath); | ||
const leaveResult = onLeave === null || onLeave === void 0 ? void 0 : onLeave(wrapper.node, getIndexPath()); | ||
if (leaveResult === exports.STOP) | ||
return leaveResult; | ||
return; | ||
} | ||
@@ -64,1 +61,2 @@ indexPath.pop(); | ||
} | ||
exports.visit = visit; |
@@ -11,2 +11,3 @@ import { DiagramOptions } from './diagram'; | ||
import { RemoveOptions } from './remove'; | ||
import { ReplaceOptions } from './replace'; | ||
import { VisitOptions } from './visit'; | ||
@@ -96,8 +97,13 @@ declare type WithoutBase<O> = Omit<O, keyof BaseOptions<unknown>>; | ||
move(node: T, options: WithoutBase<MoveOptions<T>>): T; | ||
/** | ||
* Replace the node at the given `IndexPath` with another | ||
*/ | ||
replace(node: T, options: WithoutBase<ReplaceOptions<T>>): T; | ||
}; | ||
declare type WithoutMutationBase<O> = Omit<O, keyof MutationBaseOptions<unknown>>; | ||
export declare type WithMutationOptions<T> = Omit<WithOptions<T>, 'insert' | 'remove' | 'move'> & { | ||
export declare type WithMutationOptions<T> = Omit<WithOptions<T>, 'insert' | 'remove' | 'move' | 'replace'> & { | ||
insert: (node: T, options: WithoutMutationBase<InsertOptions<T>>) => T; | ||
remove: (node: T, options: WithoutMutationBase<RemoveOptions<T>>) => T; | ||
move: (node: T, options: WithoutMutationBase<MoveOptions<T>>) => T; | ||
replace: (node: T, options: WithoutMutationBase<ReplaceOptions<T>>) => T; | ||
}; | ||
@@ -104,0 +110,0 @@ /** |
@@ -14,2 +14,3 @@ "use strict"; | ||
const remove_1 = require("./remove"); | ||
const replace_1 = require("./replace"); | ||
const visit_1 = require("./visit"); | ||
@@ -46,2 +47,3 @@ function withOptionsBase(baseOptions) { | ||
move: (node, options) => (0, move_1.move)(node, Object.assign(Object.assign({}, baseOptions), options)), | ||
replace: (node, options) => (0, replace_1.replace)(node, Object.assign(Object.assign({}, baseOptions), options)), | ||
}; | ||
@@ -51,3 +53,3 @@ } | ||
const tree = withOptionsBase(baseOptions); | ||
return Object.assign(Object.assign({}, tree), { insert: (node, options) => (0, insert_1.insert)(node, Object.assign(Object.assign({}, baseOptions), options)), remove: (node, options) => (0, remove_1.remove)(node, Object.assign(Object.assign({}, baseOptions), options)), move: (node, options) => (0, move_1.move)(node, Object.assign(Object.assign({}, baseOptions), options)) }); | ||
return Object.assign(Object.assign({}, tree), { insert: (node, options) => (0, insert_1.insert)(node, Object.assign(Object.assign({}, baseOptions), options)), remove: (node, options) => (0, remove_1.remove)(node, Object.assign(Object.assign({}, baseOptions), options)), move: (node, options) => (0, move_1.move)(node, Object.assign(Object.assign({}, baseOptions), options)), replace: (node, options) => (0, replace_1.replace)(node, Object.assign(Object.assign({}, baseOptions), options)) }); | ||
} | ||
@@ -54,0 +56,0 @@ function withOptions(baseOptions) { |
{ | ||
"name": "tree-visit", | ||
"version": "0.2.3", | ||
"version": "0.3.0", | ||
"description": "A tree traversal library.", | ||
@@ -15,3 +15,4 @@ "main": "lib/index.js", | ||
"test:watch": "jest --watch", | ||
"prepublishOnly": "npm run build" | ||
"prepublishOnly": "npm run build", | ||
"clean": "rm -rf ./lib" | ||
}, | ||
@@ -18,0 +19,0 @@ "repository": "https://github.com/dabbott/tree-visit", |
90296
53
2095