npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

declarative-tree

v1.0.2

Published

Generalized way to convert a string to tree, and vice versa. Useful for creating declarative tree tests.

Downloads

16

Readme

Table of contents

Installation

npm install declarative-tree

Description

Generalized way to convert a string to tree, and vice versa. Useful for creating declarative tree tests.

Code coverage

The testing code coverage is around 90%.

Examples

The following examples deal with converting an AVL tree string representation to tree data structure and vice versa. Here is the code that defines the AVL tree:

class AvlTreeBaseNode {
    left: null | AvlTreeNode;
    right: null | AvlTreeNode;
    id: number;

    constructor(_: AvlTreeBaseNode) {
        const { id, left, right } = _;
        this.id = id;
        this.left = left;
        this.right = right;
    }
}

export class AvlTreeRootNode extends AvlTreeBaseNode {
    parent: null;

    constructor(_: AvlTreeRootNode) {
        const { id, left, right } = _;
        super({
            id,
            left,
            right,
        });
        this.parent = null;
    }
}

export class AvlTreeNode extends AvlTreeBaseNode {
    parent: AvlTreeNode | AvlTreeRootNode;

    constructor(_: AvlTreeNode) {
        const { parent, right, left, id } = _;
        super({
            id,
            left,
            right,
        });
        this.parent = parent;
    }
}

export class AvlTree {
    root: AvlTreeRootNode;

    constructor(_: { root: AvlTreeRootNode }) {
        const { root } = _;
        this.root = root;
    }
}

/**
 * @description
 * The tree looks like this:
 * ```
 * 20
 * |_ 25
 * |_ 10
 *    |_ 5
 * ```
 */
export const mockAvlTree: AvlTree = (() => {
    const root: AvlTreeRootNode = new AvlTreeRootNode({
        id: 20,
        left: null, //lazy setted
        parent: null,
        right: null, //lazy setted
    });
    const tree: AvlTree = new AvlTree({
        root,
    });
    const left: AvlTreeNode = new AvlTreeNode({
        id: 10,
        left: null, //lazy setted
        parent: root,
        right: null,
    });
    const leftLeft: AvlTreeNode = new AvlTreeNode({
        id: 5,
        left: null,
        parent: left,
        right: null,
    });
    const right: AvlTreeNode = new AvlTreeNode({
        id: 25,
        left: null,
        parent: root,
        right: null,
    });

    root.left = left;
    root.right = right;

    left.left = leftLeft;

    return tree;
})();

tree to string

import { treeToStringHorizontalFactory } from "..";
import { AvlTree, AvlTreeNode, AvlTreeRootNode, mockAvlTree } from "./mockTree";

describe(treeToStringHorizontalFactory.name, () => {
    it(`
        returns a function that converts a tree to string, according to the 
        instructions the factory has been provided
    `, () => {
        const avlTreeToString = treeToStringHorizontalFactory<
            AvlTreeNode | AvlTreeRootNode,
            AvlTree
        >({
            getChildren: ({ treeNode }) => {
                const children: AvlTreeNode[] = [];
                const { left, right } = treeNode;
                if (left !== null) children.push(left);
                if (right !== null) children.push(right);
                return children.reverse();
            },
            getRoot: ({ tree }) => tree.root,
            nodeToString: ({ treeNodeMetadata }) =>
                String(treeNodeMetadata.treeNode.id),
        });

        //prettier-ignore
        expect(avlTreeToString({ tree: mockAvlTree })).toBe(
            `20\n`+
            `|_ 25\n`+
            `|_ 10\n`+
            `   |_ 5`
        );
    });
});

string to tree horizontal

import { stringToTreeHorizontalFactory } from "..";
import { AvlTree, AvlTreeNode, AvlTreeRootNode, mockAvlTree } from "./mockTree";

describe(stringToTreeHorizontalFactory.name, () => {
    it(`
        returns a function that converts a string to tree, according to the 
        instructions the factory has been provided
    `, () => {
        const stringToAVLTree = stringToTreeHorizontalFactory<
            number,
            AvlTreeNode | AvlTreeRootNode,
            AvlTree
        >({
            strategy: (parameters) => {
                const root: AvlTreeRootNode = new AvlTreeRootNode({
                    id: parameters.treeNodeMetadataInLevelOrder[0]
                        .placeholderValue,
                    left: null,
                    parent: null,
                    right: null,
                });
                const avlTree: AvlTree = new AvlTree({ root });

                /**
                 * @description
                 * The main idea here is to iterate the tree node metadata, and
                 * use the information they provide to create the tree nodes.
                 * When you create a tree node, set it as the `treeNode`
                 * property of the tree node metadata it corresponds. The sole
                 * reason for that is that maybe you will need to access that
                 * node from other nodes.
                 */
                parameters.treeNodeMetadataInLevelOrder.forEach(
                    (treeNodeMetadata, i) => {
                        if (i === 0) {
                            treeNodeMetadata.treeNode = root;
                            return;
                        }
                        if (i !== 0) {
                            const parentTreeNodeMetadata =
                                treeNodeMetadata.parent;
                            if (parentTreeNodeMetadata === null) {
                                throw Error(
                                    "only the parent tree node metadata of" +
                                        "the root node can be null"
                                );
                            }
                            const parent = parentTreeNodeMetadata.treeNode;

                            const currentNode: AvlTreeNode = new AvlTreeNode({
                                id: treeNodeMetadata.placeholderValue,
                                left: null,
                                right: null,
                                parent,
                            });

                            treeNodeMetadata.treeNode = currentNode;

                            /**
                             * @description
                             * Here I connect the parent node with its child
                             * node. The only way to understand whether the
                             * child node is a left or right child, is through
                             * the brach that connects them.
                             * You are free to use the character that you see
                             * fit for branches. In this example I choose to use
                             * `⏝` for left branch and `⏜` for right branch.
                             * The branch character that you use has to be used
                             * in the tag function that is returned by the
                             * factory. Take a look at the test assertion a few
                             * lines bellow where the tag function is used.
                             */
                            if (treeNodeMetadata.branchTop === "⏝") {
                                parent.left = currentNode;
                                return;
                            }
                            if (treeNodeMetadata.branchTop === "⏜") {
                                parent.right = currentNode;
                                return;
                            }
                            throw Error(
                                `encountered branch that is not "⏝" or "⏜"`
                            );
                        }
                        throw Error("case not accounted for");
                    }
                );

                return avlTree;
            },
        });

        expect(stringToAVLTree`
            ${20}
            |⏜ ${25}
            |⏝ ${10}
               |⏝ ${5}
        `).toEqual(mockAvlTree);
    });
});

string to tree vertical

import { stringToTreeVerticalFactory } from "..";
import { AvlTree, AvlTreeNode, AvlTreeRootNode, mockAvlTree } from "./mockTree";

describe(stringToTreeVerticalFactory.name, () => {
    it(`
        returns a function that converts a string to tree, according to the 
        instructions the factory has been provided
    `, () => {
        const stringToAvlTree = stringToTreeVerticalFactory<
            number,
            AvlTreeNode | AvlTreeRootNode,
            AvlTree
        >({
            strategy: (parameters) => {
                const root: AvlTreeRootNode = new AvlTreeRootNode({
                    id: parameters.treeNodeMetadataInLevelOrder[0]
                        .placeholderValue,
                    left: null,
                    parent: null,
                    right: null,
                });
                const avlTree: AvlTree = new AvlTree({ root });

                /**
                 * @description
                 * The main idea here is to iterate the tree node metadata, and
                 * use the information they provide to create the tree nodes.
                 * When you create a tree node, set it as the `treeNode`
                 * property of the tree node metadata it corresponds. The sole
                 * reason for that is that maybe you will need to access that
                 * node from other nodes.
                 */
                parameters.treeNodeMetadataInLevelOrder.forEach(
                    (treeNodeMetadata, i) => {
                        if (i === 0) {
                            treeNodeMetadata.treeNode = root;
                            return;
                        }
                        if (i !== 0) {
                            const parentTreeNodeMetadata =
                                treeNodeMetadata.parent;
                            if (parentTreeNodeMetadata === null) {
                                throw Error(
                                    "only the parent tree node metadata of " +
                                        "the root node can be null"
                                );
                            }
                            const parent = parentTreeNodeMetadata.treeNode;

                            const currentNode: AvlTreeNode = new AvlTreeNode({
                                id: treeNodeMetadata.placeholderValue,
                                left: null,
                                right: null,
                                parent,
                            });

                            treeNodeMetadata.treeNode = currentNode;

                            /**
                             * @description
                             * Here I connect the parent node with its child
                             * node. The only way to understand whether the
                             * child node is a left or right child, is through
                             * the brach that connects them.
                             * You are free to use the character that you see
                             * fit for branches. In this example I choose to use
                             * `(` for left branch and `)` for right branch. The
                             * branch character that you use has to be used in
                             * the tag function that is returned by the factory.
                             * Take a look at the test assertion a few lines
                             * bellow where the tag function is used.
                             */
                            if (treeNodeMetadata.branchTop === "(") {
                                parent.left = currentNode;
                                return;
                            }
                            if (treeNodeMetadata.branchTop === ")") {
                                parent.right = currentNode;
                                return;
                            }
                            throw Error(
                                `encountered branch that is not "(" or ")"`
                            );
                        }
                        throw Error("case not accounted for");
                    }
                );

                return avlTree;
            },
        });

        /**
         * @description
         * You have to add dots. They define where the children group ends. You
         * do no need to do that in the last row of the tree. For example in the
         * following tree the tree node with id `20` has `10` and `25` as
         * children group. The group ends at `25` and hence the dot in the
         * branch above `25`. The tree node with id `10` has children group that
         * consist only of the tree node with id `5` and hence the dot on its
         * branch. The tree node with id `25` has no children so it has a dot
         * with no branches.
         */
        expect(stringToAvlTree`
                   ${20}
                (        ).
              ${10}     ${25}
            (.            .
            ${5}
        `).toEqual(mockAvlTree);
    });
});

Documentation

/**
 * @description
 * Returns a tag function that converts template literals to tree data
 * structures, in accordance to the instructions you have provided to the
 * factory.
 */
export declare const stringToTreeHorizontalFactory: IStringToTreeFactory;
export declare type IStringToTreeFactory = <placeholder, treeNode, tree>(parameters: {
    /**
     * @description
     * This function is used by the tag function the factory returns, to
     * convert template literals to tree data structures.
     */
    strategy: (parameters: {
        /**
         * @description
         * The tree node metadata in level order (i.e. as encountered in breadth
         * first search).
         */
        treeNodeMetadataInLevelOrder: ITreeNodeMetadataStringToTree<placeholder, treeNode>[];
    }) => tree;
}) => IStringToTree<placeholder, tree>;
export interface ITreeNodeMetadataStringToTree<placeholderType, treeNode>
    extends ITreeNodeMetadataBase<ITreeNodeMetadataStringToTree<placeholderType, treeNode>> {
    /**
     * @description
     * Returns the branch that connects the context tree node to its parent tree
     * node.
     *
     * The following tree has the `branchTop` values next to their corresponding
     * placeholders:
     *
     * ```ts
     * `
     *          ${1} null
     *     (             ).
     *   ${2} "("       ${3} ")"
     *     .        (         ).
     *           ${4} "("   ${5} ")"
     * `
     * ```
     */
    readonly branchTop: string | null;
    /**
     * @description
     * Returns the branches that connect the context tree node to its children.
     *
     * The following tree has the `branchesBottom` values next to their
     * corresponding placeholders:
     *
     * ```ts
     * `
     *        ${1} ["(",")"]
     *     (        ).
     *   ${2} []   ${3} [")"]
     *     .          ).
     *               ${5} []
     * `
     * ```
     */
    readonly branchesBottom: string[];
    /**
     * @description
     * Returns the placeholder of the template literal that corresponds to the
     * context node.
     */
    readonly placeholderValue: placeholderType;
    /**
     * @description
     * The tree node that corresponds to the context tree node metadata. It
     * throws error when you get without having set before.
     */
    treeNode: treeNode;
}
export declare type ITreeNodeMetadataBase<IExtendedTreeNodeMetadata> = {
    /**
     * @description
     * The height of the subtree of the context tree node.
     *
     * The following tree has the `subTreeHeight` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} 4
     *        (              ).
     *    ${-2} 2           ${7} 3
     *   (       ).        (      ).
     * ${-3} 1 ${-1} 1 ${6} 1    ${10} 2
     *   .        .      .      (       ).
     *                         ${8} 1   ${11} 1
     * `
     * ```
     */
    readonly subTreeHeight: number;
    /**
     * @description
     * The number of nodes of the subtree of the context node.
     *
     * The following tree has the `subTreeLength` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} 9
     *       (                 ).
     *    ${-2} 3           ${7} 5
     *   (        ).     (         ).
     * ${-3} 1 ${-1} 1 ${6} 1    ${10} 3
     *   .       .       .        (     ).
     *                         ${8} 1   ${11} 1
     * `
     * ```
     */
    readonly subTreeLength: number;
    /**
     * @description
     * The tree node metadata of the parent tree node of the context node.
     */
    readonly parent: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the left sibling of the context. For horizontal
     * tree you can treat it as bottom sibling.
     *
     * The following tree has the `leftSibling` values next to their
     * corresponding placeholders:
     *
     * ```ts
     * `
     *              ${1} null
     *       (                   ).
     *    ${-2} null           ${7} -2
     *   (        ).         (          ).
     * ${-3} null ${-1} -3 ${6} null   ${10} 6
     *   .           .       .        (       ).
     *                              ${8} null ${11} 8
     * `
     * ```
     */
    readonly leftSibling: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the right sibling of the context. For
     * horizontal tree you can treat it as top sibling.
     *
     * The following tree has the `rightSibling` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} null
     *       (                     ).
     *    ${-2} 7                 ${7} null
     *   (       ).             (          ).
     * ${-3} -1 ${-1} null ${6} 10      ${10} null
     *   .         .         .        (           ).
     *                               ${8} 11   ${11} null
     * `
     * ```
     */
    readonly rightSibling: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the left sibling group of the context. For
     * horizontal tree you can treat it as bot sibling group.
     *
     * The following tree has the `leftSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                ${1} null
     *       (                      ).
     *    ${-2} null               ${7} null
     *   (           ).          (          ).
     * ${-3} null ${-1} null  ${6} [-3,-1]   ${10} [-3,-1]
     *   .          .           .            (       ).
     *                                     ${8} null  ${11} null
     * `
     * ```
     */
    readonly leftSiblingGroup: IExtendedTreeNodeMetadata[] | null;
    /**
     * @description
     * The tree node metadata of the right sibling group of the context. For
     * horizontal tree you can treat it as top sibling group.
     *
     * The following tree has the `rightSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                ${1} null
     *       (                      ).
     *    ${-2} null                ${7} null
     *   (           ).            (          ).
     * ${-3} [6,10] ${-1} [6,10] ${6} null     ${10} null
     *   .            .            .          (          ).
     *                                      ${8} null  ${11} null
     * `
     * ```
     */
    readonly rightSiblingGroup: IExtendedTreeNodeMetadata[] | null;
    /**
     * @description
     * The tree node metadata of the sibling group of the context.
     *
     * The following tree has the `currentSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                   ${1} [1]
     *               (                 ).
     *    ${-2} [-2,7]                  ${7} [-2,7]
     *   (           ).               (            ).
     * ${-3} [-3,-1] ${-1} [-3,-1] ${6} [6,10]      ${10} [6,10]
     *   .             .             .            (          ).
     *                                        ${8} [8,11]  ${11} [8,11]
     * `
     * ```
     */
    readonly currentSiblingGroup: IExtendedTreeNodeMetadata[];
    /**
     * @description
     * The tree node metadata of the children group of the context.
     *
     * The following tree has the `childrenGroup` values next to their
     * corresponding placeholders:
     *
     * ```ts
     * `
     *          ${1} [-2,7]
     *      (                ).
     *    ${-2} [-3,-1]     ${7} [6,10]
     *   (        ).       (      ).
     * ${-3} [] ${-1} [] ${6} []  ${10} [8,11]
     *   .         .       .      (       ).
     *                           ${8} [] ${11} []
     * `
     * ```
     */
    readonly childrenGroup: IExtendedTreeNodeMetadata[];
    /**
     * @description
     * Tree node metadata in level order.
     */
    readonly all: IExtendedTreeNodeMetadata[];
};
export declare type IStringToTree<placeholder, tree> = (strings: TemplateStringsArray, ...placeholders: placeholder[]) => tree;
/**
 * @description
 * Returns a tag function that converts template literals to tree data
 * structures, in accordance to the instructions you have provided to the
 * factory.
 */
export declare const stringToTreeVerticalFactory: IStringToTreeFactory;
export declare type IStringToTreeFactory = <placeholder, treeNode, tree>(parameters: {
    /**
     * @description
     * This function is used by the tag function the factory returns, to
     * convert template literals to tree data structures.
     */
    strategy: (parameters: {
        /**
         * @description
         * The tree node metadata in level order (i.e. as encountered in breadth
         * first search).
         */
        treeNodeMetadataInLevelOrder: ITreeNodeMetadataStringToTree<placeholder, treeNode>[];
    }) => tree;
}) => IStringToTree<placeholder, tree>;
export interface ITreeNodeMetadataStringToTree<placeholderType, treeNode>
    extends ITreeNodeMetadataBase<ITreeNodeMetadataStringToTree<placeholderType, treeNode>> {
    /**
     * @description
     * Returns the branch that connects the context tree node to its parent tree
     * node.
     *
     * The following tree has the `branchTop` values next to their corresponding
     * placeholders:
     *
     * ```ts
     * `
     *          ${1} null
     *     (             ).
     *   ${2} "("       ${3} ")"
     *     .        (         ).
     *           ${4} "("   ${5} ")"
     * `
     * ```
     */
    readonly branchTop: string | null;
    /**
     * @description
     * Returns the branches that connect the context tree node to its children.
     *
     * The following tree has the `branchesBottom` values next to their
     * corresponding placeholders:
     *
     * ```ts
     * `
     *        ${1} ["(",")"]
     *     (        ).
     *   ${2} []   ${3} [")"]
     *     .          ).
     *               ${5} []
     * `
     * ```
     */
    readonly branchesBottom: string[];
    /**
     * @description
     * Returns the placeholder of the template literal that corresponds to the
     * context node.
     */
    readonly placeholderValue: placeholderType;
    /**
     * @description
     * The tree node that corresponds to the context tree node metadata. It
     * throws error when you get without having set before.
     */
    treeNode: treeNode;
}
export declare type ITreeNodeMetadataBase<IExtendedTreeNodeMetadata> = {
    /**
     * @description
     * The height of the subtree of the context tree node.
     *
     * The following tree has the `subTreeHeight` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} 4
     *        (              ).
     *    ${-2} 2           ${7} 3
     *   (       ).        (      ).
     * ${-3} 1 ${-1} 1 ${6} 1    ${10} 2
     *   .        .      .      (       ).
     *                         ${8} 1   ${11} 1
     * `
     * ```
     */
    readonly subTreeHeight: number;
    /**
     * @description
     * The number of nodes of the subtree of the context node.
     *
     * The following tree has the `subTreeLength` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} 9
     *       (                 ).
     *    ${-2} 3           ${7} 5
     *   (        ).     (         ).
     * ${-3} 1 ${-1} 1 ${6} 1    ${10} 3
     *   .       .       .        (     ).
     *                         ${8} 1   ${11} 1
     * `
     * ```
     */
    readonly subTreeLength: number;
    /**
     * @description
     * The tree node metadata of the parent tree node of the context node.
     */
    readonly parent: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the left sibling of the context. For horizontal
     * tree you can treat it as bottom sibling.
     *
     * The following tree has the `leftSibling` values next to their
     * corresponding placeholders:
     *
     * ```ts
     * `
     *              ${1} null
     *       (                   ).
     *    ${-2} null           ${7} -2
     *   (        ).         (          ).
     * ${-3} null ${-1} -3 ${6} null   ${10} 6
     *   .           .       .        (       ).
     *                              ${8} null ${11} 8
     * `
     * ```
     */
    readonly leftSibling: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the right sibling of the context. For
     * horizontal tree you can treat it as top sibling.
     *
     * The following tree has the `rightSibling` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} null
     *       (                     ).
     *    ${-2} 7                 ${7} null
     *   (       ).             (          ).
     * ${-3} -1 ${-1} null ${6} 10      ${10} null
     *   .         .         .        (           ).
     *                               ${8} 11   ${11} null
     * `
     * ```
     */
    readonly rightSibling: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the left sibling group of the context. For
     * horizontal tree you can treat it as bot sibling group.
     *
     * The following tree has the `leftSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                ${1} null
     *       (                      ).
     *    ${-2} null               ${7} null
     *   (           ).          (          ).
     * ${-3} null ${-1} null  ${6} [-3,-1]   ${10} [-3,-1]
     *   .          .           .            (       ).
     *                                     ${8} null  ${11} null
     * `
     * ```
     */
    readonly leftSiblingGroup: IExtendedTreeNodeMetadata[] | null;
    /**
     * @description
     * The tree node metadata of the right sibling group of the context. For
     * horizontal tree you can treat it as top sibling group.
     *
     * The following tree has the `rightSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                ${1} null
     *       (                      ).
     *    ${-2} null                ${7} null
     *   (           ).            (          ).
     * ${-3} [6,10] ${-1} [6,10] ${6} null     ${10} null
     *   .            .            .          (          ).
     *                                      ${8} null  ${11} null
     * `
     * ```
     */
    readonly rightSiblingGroup: IExtendedTreeNodeMetadata[] | null;
    /**
     * @description
     * The tree node metadata of the sibling group of the context.
     *
     * The following tree has the `currentSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                   ${1} [1]
     *               (                 ).
     *    ${-2} [-2,7]                  ${7} [-2,7]
     *   (           ).               (            ).
     * ${-3} [-3,-1] ${-1} [-3,-1] ${6} [6,10]      ${10} [6,10]
     *   .             .             .            (          ).
     *                                        ${8} [8,11]  ${11} [8,11]
     * `
     * ```
     */
    readonly currentSiblingGroup: IExtendedTreeNodeMetadata[];
    /**
     * @description
     * The tree node metadata of the children group of the context.
     *
     * The following tree has the `childrenGroup` values next to their
     * corresponding placeholders:
     *
     * ```ts
     * `
     *          ${1} [-2,7]
     *      (                ).
     *    ${-2} [-3,-1]     ${7} [6,10]
     *   (        ).       (      ).
     * ${-3} [] ${-1} [] ${6} []  ${10} [8,11]
     *   .         .       .      (       ).
     *                           ${8} [] ${11} []
     * `
     * ```
     */
    readonly childrenGroup: IExtendedTreeNodeMetadata[];
    /**
     * @description
     * Tree node metadata in level order.
     */
    readonly all: IExtendedTreeNodeMetadata[];
};
export declare type IStringToTree<placeholder, tree> = (strings: TemplateStringsArray, ...placeholders: placeholder[]) => tree;
/**
 * @description
 * Returns a function that converts tree data structures to horizontal tree
 * string representations, in accordance to the instructions you have provided
 * to the factory.
 */
export declare const treeToStringHorizontalFactory: ITreeToStringHorizontalFactory;
export declare type ITreeToStringHorizontalFactory = <treeNodeOrTreeRootNode, tree>(strategy: {
    /**
     * @description
     * A function that given a node from the tree, returns its immediate
     * children.
     */
    getChildren: IGetChildren<treeNodeOrTreeRootNode>;
    /**
     * @description
     * A function that given the tree returns its root node.
     */
    getRoot: IGetRoot<tree, treeNodeOrTreeRootNode>;
    /**
     * @description
     * Function that converts a tree node to string.
     */
    nodeToString: (parameters: { treeNodeMetadata: ITreeNodeMetadataTreeToString<treeNodeOrTreeRootNode> }) => string;
}) => ITreeToString<tree>;
export declare type IGetChildren<treeNodeOrTreeRootNode> = (parameters: {
    /**
     * @description
     * The tree node from which you want to get the children.
     */
    treeNode: treeNodeOrTreeRootNode;
}) => treeNodeOrTreeRootNode[];
export declare type IGetRoot<tree, treeNode> = (parameters: {
    /**
     * @description
     * The tree from which you want to get the root.
     */
    tree: tree;
}) => treeNode;
export interface ITreeNodeMetadataTreeToString<treeNode> extends ITreeNodeMetadataBase<ITreeNodeMetadataTreeToString<treeNode>> {
    /**
     * @description
     * The user defined string representation of the context tree node.
     */
    treeNodeStringRepresentation?: string;
    /**
     * @description
     * The tree node that corresponds to the context metadata.
     */
    readonly treeNode: treeNode;
}
export declare type ITreeNodeMetadataBase<IExtendedTreeNodeMetadata> = {
    /**
     * @description
     * The height of the subtree of the context tree node.
     *
     * The following tree has the `subTreeHeight` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} 4
     *        (              ).
     *    ${-2} 2           ${7} 3
     *   (       ).        (      ).
     * ${-3} 1 ${-1} 1 ${6} 1    ${10} 2
     *   .        .      .      (       ).
     *                         ${8} 1   ${11} 1
     * `
     * ```
     */
    readonly subTreeHeight: number;
    /**
     * @description
     * The number of nodes of the subtree of the context node.
     *
     * The following tree has the `subTreeLength` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} 9
     *       (                 ).
     *    ${-2} 3           ${7} 5
     *   (        ).     (         ).
     * ${-3} 1 ${-1} 1 ${6} 1    ${10} 3
     *   .       .       .        (     ).
     *                         ${8} 1   ${11} 1
     * `
     * ```
     */
    readonly subTreeLength: number;
    /**
     * @description
     * The tree node metadata of the parent tree node of the context node.
     */
    readonly parent: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the left sibling of the context. For horizontal
     * tree you can treat it as bottom sibling.
     *
     * The following tree has the `leftSibling` values next to their
     * corresponding placeholders:
     *
     * ```ts
     * `
     *              ${1} null
     *       (                   ).
     *    ${-2} null           ${7} -2
     *   (        ).         (          ).
     * ${-3} null ${-1} -3 ${6} null   ${10} 6
     *   .           .       .        (       ).
     *                              ${8} null ${11} 8
     * `
     * ```
     */
    readonly leftSibling: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the right sibling of the context. For
     * horizontal tree you can treat it as top sibling.
     *
     * The following tree has the `rightSibling` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *              ${1} null
     *       (                     ).
     *    ${-2} 7                 ${7} null
     *   (       ).             (          ).
     * ${-3} -1 ${-1} null ${6} 10      ${10} null
     *   .         .         .        (           ).
     *                               ${8} 11   ${11} null
     * `
     * ```
     */
    readonly rightSibling: IExtendedTreeNodeMetadata | null;
    /**
     * @description
     * The tree node metadata of the left sibling group of the context. For
     * horizontal tree you can treat it as bot sibling group.
     *
     * The following tree has the `leftSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                ${1} null
     *       (                      ).
     *    ${-2} null               ${7} null
     *   (           ).          (          ).
     * ${-3} null ${-1} null  ${6} [-3,-1]   ${10} [-3,-1]
     *   .          .           .            (       ).
     *                                     ${8} null  ${11} null
     * `
     * ```
     */
    readonly leftSiblingGroup: IExtendedTreeNodeMetadata[] | null;
    /**
     * @description
     * The tree node metadata of the right sibling group of the context. For
     * horizontal tree you can treat it as top sibling group.
     *
     * The following tree has the `rightSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                ${1} null
     *       (                      ).
     *    ${-2} null                ${7} null
     *   (           ).            (          ).
     * ${-3} [6,10] ${-1} [6,10] ${6} null     ${10} null
     *   .            .            .          (          ).
     *                                      ${8} null  ${11} null
     * `
     * ```
     */
    readonly rightSiblingGroup: IExtendedTreeNodeMetadata[] | null;
    /**
     * @description
     * The tree node metadata of the sibling group of the context.
     *
     * The following tree has the `currentSiblingGroup` values next to their
     * corresponding placeholders:
     * ```ts
     * `
     *                   ${1} [1]
     *               (                 ).
     *    ${-2} [-2,7]                  ${7} [-2,7]
     *   (           ).               (            ).
     * ${-3} [-3,-1] ${-1} [-3,-1] ${6} [6,10]      ${10} [6,10]
     *   .             .             .            (          ).
     *                                        ${8} [8,11]  ${11} [8,11]
     * `
     * ```
     */
    readonly currentSiblingGroup: IExtendedTreeNodeMetadata[];
    /**
     * @description
     * The tree node metadata of the children group of the context.
     *
     * The following tree has the `childrenGroup` values next to their
     * corresponding placeholders:
     *
     * ```ts
     * `
     *          ${1} [-2,7]
     *      (                ).
     *    ${-2} [-3,-1]     ${7} [6,10]
     *   (        ).       (      ).
     * ${-3} [] ${-1} [] ${6} []  ${10} [8,11]
     *   .         .       .      (       ).
     *                           ${8} [] ${11} []
     * `
     * ```
     */
    readonly childrenGroup: IExtendedTreeNodeMetadata[];
    /**
     * @description
     * Tree node metadata in level order.
     */
    readonly all: IExtendedTreeNodeMetadata[];
};
declare type ITreeToString<tree> = (parameters: {
    /**
     * @description
     * Tree to convert to string.
     */
    tree: tree;
}) => string;

Motivation

I just wanted to create declarative tree tests for any kind of tree. No packages in npm could do that, so I decided to create my own.

Contributing

I am open to suggestions/pull request to improve this program.

You will find the following commands useful:

  • Clones the github repository of this project:

    git clone https://github.com/lillallol/declarative-tree
  • Installs the node modules (nothing will work without them):

    npm install
  • Tests the code and generates code coverage:

    npm run test

    The generated code coverage is saved in ./coverage.

  • Lints the source folder using typescript and eslint:

    npm run lint
  • Builds the typescript code from the ./src folder to javascript code in ./dist:

    npm run build-ts
  • Injects in place the generated toc and imported files to README.md:

    npm run build-md
  • Checks the project for spelling mistakes:

    npm run spell-check

    Take a look at the related configuration ./cspell.json.

  • Checks ./src for dead typescript files:

    npm run dead-files

    Take a look at the related configuration ./unimportedrc.json.

  • Logs which node modules can be updated:

    npm run check-updates
  • Updates the node modules to their latest version (even if they introduce breaking changes):

    npm run update
  • Formats all the typescript files in the ./src folder:

    npm run format

Changelog

1.0.2

Bugs fixed

  • Placeholders groups were being over flattened. For example the following template literal:

    `
        ${""}
        |_ ${"p"}
           |_ ${"a"}
              |_ ${"t"}
                 |_ ${"h"}
                    |_ ${["prop1", -1, "=>", 1]}
    `

    would wrongly have as placeholders:

    ["","p","a","t","h",...["prop1", -1, "=>", 1]]

    instead of:

    ["","p","a","t","h",["prop1", -1, "=>", 1]]

1.0.1

  • Fixed some mistakes in the examples.
  • Minor internal changes.

1.0.0

  • Published the package.

License

MIT