diff --git a/package.json b/package.json index a35853c734..60f4c42e81 100644 --- a/package.json +++ b/package.json @@ -236,6 +236,7 @@ "json-text", "json-type", "json-type-value", + "json-walk", "reactive-rpc", "util" ] diff --git a/src/json-walk/__tests__/index.spec.ts b/src/json-walk/__tests__/index.spec.ts new file mode 100644 index 0000000000..94e4d1e6d8 --- /dev/null +++ b/src/json-walk/__tests__/index.spec.ts @@ -0,0 +1,55 @@ +import {walk} from '..'; + +test('can walk through a value', () => { + const value = { + a: 1, + foo: [2, 'a', null, 3, true, false, new Set([1, 2]), new Map([['b', 3]])], + }; + const nodes: unknown[] = []; + walk(value, (node) => { + nodes.push(node); + }); + expect(nodes).toEqual([ + value, + 1, + value.foo, + 2, + 'a', + null, + 3, + true, + false, + new Set([1, 2]), + 1, + 2, + new Map([['b', 3]]), + 3, + ]); +}); + +test('can walk through null', () => { + const value = null; + const nodes: unknown[] = []; + walk(value, (node) => { + nodes.push(node); + }); + expect(nodes).toEqual([null]); +}); + +test('can walk empty object', () => { + const value = {}; + const nodes: unknown[] = []; + walk(value, (node) => { + nodes.push(node); + }); + expect(nodes).toEqual([{}]); +}); + +test('can walk empty array', () => { + const value: any[] = []; + const nodes: unknown[] = []; + walk(value, (node) => { + nodes.push(node); + }); + expect(nodes).toEqual([[]]); +}); diff --git a/src/json-walk/index.ts b/src/json-walk/index.ts new file mode 100644 index 0000000000..2a675b3594 --- /dev/null +++ b/src/json-walk/index.ts @@ -0,0 +1,25 @@ +export const walk = (value: unknown, callback: (value: unknown) => void): void => { + callback(value); + if (typeof value === 'object') { + if (!value) return; + switch (value.constructor) { + case Array: { + const arr = value as unknown[]; + const length = arr.length; + for (let i = 0; i < length; i++) walk(arr[i], callback); + break; + } + case Object: { + const obj = value as Record; + for (const key in obj) walk(obj[key], callback); + break; + } + case Map: + case Set: { + const mapOrSet = value as Set | Map; + mapOrSet.forEach((val) => walk(val, callback)); + break; + } + } + } +};