UNPKG

@rushstack/lookup-by-path

Version:

Strongly typed trie data structure for path and URL-like strings.

301 lines 11.2 kB
/** * Readonly view of a node in the path trie used in LookupByPath * * @remarks * This interface is used to facilitate parallel traversals for comparing two `LookupByPath` instances. * * @beta */ export interface IReadonlyPathTrieNode<TItem extends {}> { /** * The value that exactly matches the current relative path */ readonly value: TItem | undefined; /** * Child nodes by subfolder */ readonly children: ReadonlyMap<string, IReadonlyPathTrieNode<TItem>> | undefined; } /** * Object containing both the matched item and the start index of the remainder of the query. * * @beta */ export interface IPrefixMatch<TItem extends {}> { /** * The item that matched the prefix */ value: TItem; /** * The index of the first character after the matched prefix */ index: number; /** * The last match found (with a shorter prefix), if any */ lastMatch?: IPrefixMatch<TItem>; } /** * The readonly component of `LookupByPath`, to simplify unit testing. * * @beta */ export interface IReadonlyLookupByPath<TItem extends {}> extends Iterable<[string, TItem]> { /** * Searches for the item associated with `childPath`, or the nearest ancestor of that path that * has an associated item. * * @returns the found item, or `undefined` if no item was found * * @example * ```ts * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]); * trie.findChildPath('foo/baz'); // returns 1 * trie.findChildPath('foo/bar/baz'); // returns 2 * ``` */ findChildPath(childPath: string, delimiter?: string): TItem | undefined; /** * Searches for the item for which the recorded prefix is the longest matching prefix of `query`. * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be * extracted. * * @returns the found item and the length of the matched prefix, or `undefined` if no item was found * * @example * ```ts * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]); * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 } * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 } * ``` */ findLongestPrefixMatch(query: string, delimiter?: string): IPrefixMatch<TItem> | undefined; /** * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that * has an associated item. * * @returns the found item, or `undefined` if no item was found * * @example * ```ts * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]); * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1 * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2 * ``` */ findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined; /** * Determines if an entry exists exactly at the specified path. * * @returns `true` if an entry exists at the specified path, `false` otherwise */ has(query: string, delimiter?: string): boolean; /** * Retrieves the entry that exists exactly at the specified path, if any. * * @returns The entry that exists exactly at the specified path, or `undefined` if no entry exists. */ get(query: string, delimiter?: string): TItem | undefined; /** * Gets the number of entries in this trie. * * @returns The number of entries in this trie. */ get size(): number; /** * @returns The root node of the trie, corresponding to the path '' */ get tree(): IReadonlyPathTrieNode<TItem>; /** * Iterates over the entries in this trie. * * @param query - An optional query. If specified only entries that start with the query will be returned. * * @returns An iterator over the entries under the specified query (or the root if no query is specified). * @remarks * Keys in the returned iterator use the provided delimiter to join segments. * Iteration order is not specified. * @example * ```ts * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]); * [...trie.entries(undefined, ',')); // returns [['foo', 1], ['foo,bar', 2]] * ``` */ entries(query?: string, delimiter?: string): IterableIterator<[string, TItem]>; /** * Iterates over the entries in this trie. * * @param query - An optional query. If specified only entries that start with the query will be returned. * * @returns An iterator over the entries under the specified query (or the root if no query is specified). * @remarks * Keys in the returned iterator use the provided delimiter to join segments. * Iteration order is not specified. */ [Symbol.iterator](query?: string, delimiter?: string): IterableIterator<[string, TItem]>; /** * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path * is not found in the trie, the info is ignored. * * @returns The grouped info, grouped by the nearest entry in the trie that contains the path * * @param infoByPath - The info to be grouped, keyed by path */ groupByChild<TInfo>(infoByPath: Map<string, TInfo>, delimiter?: string): Map<TItem, Map<string, TInfo>>; /** * Retrieves the trie node at the specified prefix, if it exists. * * @param query - The prefix to check for * @param delimiter - The path delimiter * @returns The trie node at the specified prefix, or `undefined` if no node was found */ getNodeAtPrefix(query: string, delimiter?: string): IReadonlyPathTrieNode<TItem> | undefined; } /** * This class is used to associate path-like-strings, such as those returned by `git` commands, * with entities that correspond with ancestor folders, such as Rush Projects or npm packages. * * It is optimized for efficiently locating the nearest ancestor path with an associated value. * * It is implemented as a Trie (https://en.wikipedia.org/wiki/Trie) data structure, with each edge * being a path segment. * * @example * ```ts * const trie = new LookupByPath([['foo', 1], ['bar', 2], ['foo/bar', 3]]); * trie.findChildPath('foo'); // returns 1 * trie.findChildPath('foo/baz'); // returns 1 * trie.findChildPath('baz'); // returns undefined * trie.findChildPath('foo/bar/baz'); returns 3 * trie.findChildPath('bar/foo/bar'); returns 2 * ``` * @beta */ export declare class LookupByPath<TItem extends {}> implements IReadonlyLookupByPath<TItem> { /** * The delimiter used to split paths */ readonly delimiter: string; /** * The root node of the trie, corresponding to the path '' */ private readonly _root; /** * The number of entries in this trie. */ private _size; /** * Constructs a new `LookupByPath` * * @param entries - Initial path-value pairs to populate the trie. */ constructor(entries?: Iterable<[string, TItem]>, delimiter?: string); /** * Iterates over the segments of a serialized path. * * @example * * `LookupByPath.iteratePathSegments('foo/bar/baz')` yields 'foo', 'bar', 'baz' * * `LookupByPath.iteratePathSegments('foo\\bar\\baz', '\\')` yields 'foo', 'bar', 'baz' */ static iteratePathSegments(serializedPath: string, delimiter?: string): Iterable<string>; private static _iteratePrefixes; /** * {@inheritdoc IReadonlyLookupByPath.size} */ get size(): number; /** * {@inheritdoc IReadonlyLookupByPath.tree} */ get tree(): IReadonlyPathTrieNode<TItem>; /** * Deletes all entries from this `LookupByPath` instance. * * @returns this, for chained calls */ clear(): this; /** * Associates the value with the specified serialized path. * If a value is already associated, will overwrite. * * @returns this, for chained calls */ setItem(serializedPath: string, value: TItem, delimiter?: string): this; /** * Deletes an item if it exists. * @param query - The path to the item to delete * @param delimeter - Optional override delimeter for parsing the query * @returns `true` if the item was found and deleted, `false` otherwise * @remarks * If the node has children with values, they will be retained. */ deleteItem(query: string, delimeter?: string): boolean; /** * Deletes an item and all its children. * @param query - The path to the item to delete * @param delimeter - Optional override delimeter for parsing the query * @returns `true` if any nodes were deleted, `false` otherwise */ deleteSubtree(query: string, delimeter?: string): boolean; /** * Associates the value with the specified path. * If a value is already associated, will overwrite. * * @returns this, for chained calls */ setItemFromSegments(pathSegments: Iterable<string>, value: TItem): this; /** * {@inheritdoc IReadonlyLookupByPath} */ findChildPath(childPath: string, delimiter?: string): TItem | undefined; /** * {@inheritdoc IReadonlyLookupByPath} */ findLongestPrefixMatch(query: string, delimiter?: string): IPrefixMatch<TItem> | undefined; /** * {@inheritdoc IReadonlyLookupByPath} */ findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined; /** * {@inheritdoc IReadonlyLookupByPath} */ has(key: string, delimiter?: string): boolean; /** * {@inheritdoc IReadonlyLookupByPath} */ get(key: string, delimiter?: string): TItem | undefined; /** * {@inheritdoc IReadonlyLookupByPath} */ groupByChild<TInfo>(infoByPath: Map<string, TInfo>, delimiter?: string): Map<TItem, Map<string, TInfo>>; /** * {@inheritdoc IReadonlyLookupByPath} */ entries(query?: string, delimiter?: string): IterableIterator<[string, TItem]>; /** * {@inheritdoc IReadonlyLookupByPath} */ [Symbol.iterator](query?: string, delimiter?: string): IterableIterator<[string, TItem]>; /** * {@inheritdoc IReadonlyLookupByPath} */ getNodeAtPrefix(query: string, delimiter?: string): IReadonlyPathTrieNode<TItem> | undefined; /** * Iterates through progressively longer prefixes of a given string and returns as soon * as the number of candidate items that match the prefix are 1 or 0. * * If a match is present, returns the matched itme and the length of the matched prefix. * * @returns the found item, or `undefined` if no item was found */ private _findLongestPrefixMatch; /** * Finds the node at the specified path, or `undefined` if no node was found. * * @param query - The path to the node to search for * @returns The trie node at the specified path, or `undefined` if no node was found */ private _findNodeAtPrefix; } //# sourceMappingURL=LookupByPath.d.ts.map