@rushstack/lookup-by-path 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@rushstack/lookup-by-path",
3
+ "entries": [
4
+ {
5
+ "version": "0.1.0",
6
+ "tag": "@rushstack/lookup-by-path_v0.1.0",
7
+ "date": "Thu, 08 Aug 2024 22:08:25 GMT",
8
+ "comments": {
9
+ "minor": [
10
+ {
11
+ "comment": "Extract LookupByPath from @rushstack/rush-lib."
12
+ }
13
+ ]
14
+ }
15
+ }
16
+ ]
17
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Change Log - @rushstack/lookup-by-path
2
+
3
+ This log was last generated on Thu, 08 Aug 2024 22:08:25 GMT and should not be manually modified.
4
+
5
+ ## 0.1.0
6
+ Thu, 08 Aug 2024 22:08:25 GMT
7
+
8
+ ### Minor changes
9
+
10
+ - Extract LookupByPath from @rushstack/rush-lib.
11
+
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ @rushstack/lookup-by-path
2
+
3
+ Copyright (c) Microsoft Corporation. All rights reserved.
4
+
5
+ MIT License
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # @rushstack/lookup-by-path
2
+
3
+ This library contains a strongly-typed implementation of of a [Trie](https://en.wikipedia.org/wiki/Trie) (a.k.a. prefix tree) data structure optimized for file paths and URLs.
4
+
5
+ This package is used by Rush to associate Git hashes with their nearest ancestor Rush project, for example.
6
+
7
+ ## Links
8
+
9
+ - [CHANGELOG.md](
10
+ https://github.com/microsoft/rushstack/blob/main/libraries/lookup-by-path/CHANGELOG.md) - Find
11
+ out what's new in the latest version
12
+ - [API Reference](https://api.rushstack.io/pages/lookup-by-path/)
13
+
14
+ `@rushstack/lookup-by-path` is part of the [Rush Stack](https://rushstack.io/) family of projects.
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Strongly typed trie data structure for path and URL-like strings.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+
7
+ /**
8
+ * Object containing both the matched item and the start index of the remainder of the query.
9
+ *
10
+ * @beta
11
+ */
12
+ export declare interface IPrefixMatch<TItem> {
13
+ /**
14
+ * The item that matched the prefix
15
+ */
16
+ value: TItem;
17
+ /**
18
+ * The index of the first character after the matched prefix
19
+ */
20
+ index: number;
21
+ }
22
+
23
+ /**
24
+ * This class is used to associate path-like-strings, such as those returned by `git` commands,
25
+ * with entities that correspond with ancestor folders, such as Rush Projects or npm packages.
26
+ *
27
+ * It is optimized for efficiently locating the nearest ancestor path with an associated value.
28
+ *
29
+ * It is implemented as a Trie (https://en.wikipedia.org/wiki/Trie) data structure, with each edge
30
+ * being a path segment.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const trie = new LookupByPath([['foo', 1], ['bar', 2], ['foo/bar', 3]]);
35
+ * trie.findChildPath('foo'); // returns 1
36
+ * trie.findChildPath('foo/baz'); // returns 1
37
+ * trie.findChildPath('baz'); // returns undefined
38
+ * trie.findChildPath('foo/bar/baz'); returns 3
39
+ * trie.findChildPath('bar/foo/bar'); returns 2
40
+ * ```
41
+ * @beta
42
+ */
43
+ export declare class LookupByPath<TItem> {
44
+ /**
45
+ * The delimiter used to split paths
46
+ */
47
+ readonly delimiter: string;
48
+ /**
49
+ * The root node of the trie, corresponding to the path ''
50
+ */
51
+ private readonly _root;
52
+ /**
53
+ * Constructs a new `LookupByPath`
54
+ *
55
+ * @param entries - Initial path-value pairs to populate the trie.
56
+ */
57
+ constructor(entries?: Iterable<[string, TItem]>, delimiter?: string);
58
+ /**
59
+ * Iterates over the segments of a serialized path.
60
+ *
61
+ * @example
62
+ *
63
+ * `LookupByPath.iteratePathSegments('foo/bar/baz')` yields 'foo', 'bar', 'baz'
64
+ *
65
+ * `LookupByPath.iteratePathSegments('foo\\bar\\baz', '\\')` yields 'foo', 'bar', 'baz'
66
+ */
67
+ static iteratePathSegments(serializedPath: string, delimiter?: string): Iterable<string>;
68
+ private static _iteratePrefixes;
69
+ /**
70
+ * Associates the value with the specified serialized path.
71
+ * If a value is already associated, will overwrite.
72
+ *
73
+ * @returns this, for chained calls
74
+ */
75
+ setItem(serializedPath: string, value: TItem): this;
76
+ /**
77
+ * Associates the value with the specified path.
78
+ * If a value is already associated, will overwrite.
79
+ *
80
+ * @returns this, for chained calls
81
+ */
82
+ setItemFromSegments(pathSegments: Iterable<string>, value: TItem): this;
83
+ /**
84
+ * Searches for the item associated with `childPath`, or the nearest ancestor of that path that
85
+ * has an associated item.
86
+ *
87
+ * @returns the found item, or `undefined` if no item was found
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
92
+ * trie.findChildPath('foo/baz'); // returns 1
93
+ * trie.findChildPath('foo/bar/baz'); // returns 2
94
+ * ```
95
+ */
96
+ findChildPath(childPath: string): TItem | undefined;
97
+ /**
98
+ * Searches for the item for which the recorded prefix is the longest matching prefix of `query`.
99
+ * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be
100
+ * extracted.
101
+ *
102
+ * @returns the found item and the length of the matched prefix, or `undefined` if no item was found
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
107
+ * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 }
108
+ * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 }
109
+ * ```
110
+ */
111
+ findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined;
112
+ /**
113
+ * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that
114
+ * has an associated item.
115
+ *
116
+ * @returns the found item, or `undefined` if no item was found
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
121
+ * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1
122
+ * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2
123
+ * ```
124
+ */
125
+ findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined;
126
+ /**
127
+ * Iterates through progressively longer prefixes of a given string and returns as soon
128
+ * as the number of candidate items that match the prefix are 1 or 0.
129
+ *
130
+ * If a match is present, returns the matched itme and the length of the matched prefix.
131
+ *
132
+ * @returns the found item, or `undefined` if no item was found
133
+ */
134
+ private _findLongestPrefixMatch;
135
+ }
136
+
137
+ export { }
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.47.5"
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Object containing both the matched item and the start index of the remainder of the query.
3
+ *
4
+ * @beta
5
+ */
6
+ export interface IPrefixMatch<TItem> {
7
+ /**
8
+ * The item that matched the prefix
9
+ */
10
+ value: TItem;
11
+ /**
12
+ * The index of the first character after the matched prefix
13
+ */
14
+ index: number;
15
+ }
16
+ /**
17
+ * This class is used to associate path-like-strings, such as those returned by `git` commands,
18
+ * with entities that correspond with ancestor folders, such as Rush Projects or npm packages.
19
+ *
20
+ * It is optimized for efficiently locating the nearest ancestor path with an associated value.
21
+ *
22
+ * It is implemented as a Trie (https://en.wikipedia.org/wiki/Trie) data structure, with each edge
23
+ * being a path segment.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const trie = new LookupByPath([['foo', 1], ['bar', 2], ['foo/bar', 3]]);
28
+ * trie.findChildPath('foo'); // returns 1
29
+ * trie.findChildPath('foo/baz'); // returns 1
30
+ * trie.findChildPath('baz'); // returns undefined
31
+ * trie.findChildPath('foo/bar/baz'); returns 3
32
+ * trie.findChildPath('bar/foo/bar'); returns 2
33
+ * ```
34
+ * @beta
35
+ */
36
+ export declare class LookupByPath<TItem> {
37
+ /**
38
+ * The delimiter used to split paths
39
+ */
40
+ readonly delimiter: string;
41
+ /**
42
+ * The root node of the trie, corresponding to the path ''
43
+ */
44
+ private readonly _root;
45
+ /**
46
+ * Constructs a new `LookupByPath`
47
+ *
48
+ * @param entries - Initial path-value pairs to populate the trie.
49
+ */
50
+ constructor(entries?: Iterable<[string, TItem]>, delimiter?: string);
51
+ /**
52
+ * Iterates over the segments of a serialized path.
53
+ *
54
+ * @example
55
+ *
56
+ * `LookupByPath.iteratePathSegments('foo/bar/baz')` yields 'foo', 'bar', 'baz'
57
+ *
58
+ * `LookupByPath.iteratePathSegments('foo\\bar\\baz', '\\')` yields 'foo', 'bar', 'baz'
59
+ */
60
+ static iteratePathSegments(serializedPath: string, delimiter?: string): Iterable<string>;
61
+ private static _iteratePrefixes;
62
+ /**
63
+ * Associates the value with the specified serialized path.
64
+ * If a value is already associated, will overwrite.
65
+ *
66
+ * @returns this, for chained calls
67
+ */
68
+ setItem(serializedPath: string, value: TItem): this;
69
+ /**
70
+ * Associates the value with the specified path.
71
+ * If a value is already associated, will overwrite.
72
+ *
73
+ * @returns this, for chained calls
74
+ */
75
+ setItemFromSegments(pathSegments: Iterable<string>, value: TItem): this;
76
+ /**
77
+ * Searches for the item associated with `childPath`, or the nearest ancestor of that path that
78
+ * has an associated item.
79
+ *
80
+ * @returns the found item, or `undefined` if no item was found
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
85
+ * trie.findChildPath('foo/baz'); // returns 1
86
+ * trie.findChildPath('foo/bar/baz'); // returns 2
87
+ * ```
88
+ */
89
+ findChildPath(childPath: string): TItem | undefined;
90
+ /**
91
+ * Searches for the item for which the recorded prefix is the longest matching prefix of `query`.
92
+ * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be
93
+ * extracted.
94
+ *
95
+ * @returns the found item and the length of the matched prefix, or `undefined` if no item was found
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
100
+ * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 }
101
+ * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 }
102
+ * ```
103
+ */
104
+ findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined;
105
+ /**
106
+ * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that
107
+ * has an associated item.
108
+ *
109
+ * @returns the found item, or `undefined` if no item was found
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
114
+ * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1
115
+ * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2
116
+ * ```
117
+ */
118
+ findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined;
119
+ /**
120
+ * Iterates through progressively longer prefixes of a given string and returns as soon
121
+ * as the number of candidate items that match the prefix are 1 or 0.
122
+ *
123
+ * If a match is present, returns the matched itme and the length of the matched prefix.
124
+ *
125
+ * @returns the found item, or `undefined` if no item was found
126
+ */
127
+ private _findLongestPrefixMatch;
128
+ }
129
+ //# sourceMappingURL=LookupByPath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LookupByPath.d.ts","sourceRoot":"","sources":["../src/LookupByPath.ts"],"names":[],"mappings":"AA4BA;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,KAAK;IACjC;;OAEG;IACH,KAAK,EAAE,KAAK,CAAC;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,YAAY,CAAC,KAAK;IAC7B;;OAEG;IACH,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAE7C;;;;OAIG;gBACgB,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM;IAe1E;;;;;;;;OAQG;WACY,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,GAAE,MAAY,GAAG,QAAQ,CAAC,MAAM,CAAC;IAMrG,OAAO,CAAC,MAAM,CAAE,gBAAgB;IA2BhC;;;;;OAKG;IACI,OAAO,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAI1D;;;;;OAKG;IACI,mBAAmB,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAuB9E;;;;;;;;;;;;OAYG;IACI,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAI1D;;;;;;;;;;;;;OAaG;IACI,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,SAAS;IAI7E;;;;;;;;;;;;OAYG;IACI,yBAAyB,CAAC,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,SAAS;IAqBxF;;;;;;;OAOG;IACH,OAAO,CAAC,uBAAuB;CA8BhC"}
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.LookupByPath = void 0;
6
+ /**
7
+ * This class is used to associate path-like-strings, such as those returned by `git` commands,
8
+ * with entities that correspond with ancestor folders, such as Rush Projects or npm packages.
9
+ *
10
+ * It is optimized for efficiently locating the nearest ancestor path with an associated value.
11
+ *
12
+ * It is implemented as a Trie (https://en.wikipedia.org/wiki/Trie) data structure, with each edge
13
+ * being a path segment.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const trie = new LookupByPath([['foo', 1], ['bar', 2], ['foo/bar', 3]]);
18
+ * trie.findChildPath('foo'); // returns 1
19
+ * trie.findChildPath('foo/baz'); // returns 1
20
+ * trie.findChildPath('baz'); // returns undefined
21
+ * trie.findChildPath('foo/bar/baz'); returns 3
22
+ * trie.findChildPath('bar/foo/bar'); returns 2
23
+ * ```
24
+ * @beta
25
+ */
26
+ class LookupByPath {
27
+ /**
28
+ * Constructs a new `LookupByPath`
29
+ *
30
+ * @param entries - Initial path-value pairs to populate the trie.
31
+ */
32
+ constructor(entries, delimiter) {
33
+ this._root = {
34
+ value: undefined,
35
+ children: undefined
36
+ };
37
+ this.delimiter = delimiter !== null && delimiter !== void 0 ? delimiter : '/';
38
+ if (entries) {
39
+ for (const [path, item] of entries) {
40
+ this.setItem(path, item);
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Iterates over the segments of a serialized path.
46
+ *
47
+ * @example
48
+ *
49
+ * `LookupByPath.iteratePathSegments('foo/bar/baz')` yields 'foo', 'bar', 'baz'
50
+ *
51
+ * `LookupByPath.iteratePathSegments('foo\\bar\\baz', '\\')` yields 'foo', 'bar', 'baz'
52
+ */
53
+ static *iteratePathSegments(serializedPath, delimiter = '/') {
54
+ for (const prefixMatch of this._iteratePrefixes(serializedPath, delimiter)) {
55
+ yield prefixMatch.prefix;
56
+ }
57
+ }
58
+ static *_iteratePrefixes(input, delimiter = '/') {
59
+ if (!input) {
60
+ return;
61
+ }
62
+ let previousIndex = 0;
63
+ let nextIndex = input.indexOf(delimiter);
64
+ // Leading segments
65
+ while (nextIndex >= 0) {
66
+ yield {
67
+ prefix: input.slice(previousIndex, nextIndex),
68
+ index: nextIndex
69
+ };
70
+ previousIndex = nextIndex + 1;
71
+ nextIndex = input.indexOf(delimiter, previousIndex);
72
+ }
73
+ // Last segment
74
+ if (previousIndex < input.length) {
75
+ yield {
76
+ prefix: input.slice(previousIndex, input.length),
77
+ index: input.length
78
+ };
79
+ }
80
+ }
81
+ /**
82
+ * Associates the value with the specified serialized path.
83
+ * If a value is already associated, will overwrite.
84
+ *
85
+ * @returns this, for chained calls
86
+ */
87
+ setItem(serializedPath, value) {
88
+ return this.setItemFromSegments(LookupByPath.iteratePathSegments(serializedPath, this.delimiter), value);
89
+ }
90
+ /**
91
+ * Associates the value with the specified path.
92
+ * If a value is already associated, will overwrite.
93
+ *
94
+ * @returns this, for chained calls
95
+ */
96
+ setItemFromSegments(pathSegments, value) {
97
+ let node = this._root;
98
+ for (const segment of pathSegments) {
99
+ if (!node.children) {
100
+ node.children = new Map();
101
+ }
102
+ let child = node.children.get(segment);
103
+ if (!child) {
104
+ node.children.set(segment, (child = {
105
+ value: undefined,
106
+ children: undefined
107
+ }));
108
+ }
109
+ node = child;
110
+ }
111
+ node.value = value;
112
+ return this;
113
+ }
114
+ /**
115
+ * Searches for the item associated with `childPath`, or the nearest ancestor of that path that
116
+ * has an associated item.
117
+ *
118
+ * @returns the found item, or `undefined` if no item was found
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
123
+ * trie.findChildPath('foo/baz'); // returns 1
124
+ * trie.findChildPath('foo/bar/baz'); // returns 2
125
+ * ```
126
+ */
127
+ findChildPath(childPath) {
128
+ return this.findChildPathFromSegments(LookupByPath.iteratePathSegments(childPath, this.delimiter));
129
+ }
130
+ /**
131
+ * Searches for the item for which the recorded prefix is the longest matching prefix of `query`.
132
+ * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be
133
+ * extracted.
134
+ *
135
+ * @returns the found item and the length of the matched prefix, or `undefined` if no item was found
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
140
+ * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 }
141
+ * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 }
142
+ * ```
143
+ */
144
+ findLongestPrefixMatch(query) {
145
+ return this._findLongestPrefixMatch(LookupByPath._iteratePrefixes(query, this.delimiter));
146
+ }
147
+ /**
148
+ * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that
149
+ * has an associated item.
150
+ *
151
+ * @returns the found item, or `undefined` if no item was found
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
156
+ * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1
157
+ * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2
158
+ * ```
159
+ */
160
+ findChildPathFromSegments(childPathSegments) {
161
+ var _a;
162
+ let node = this._root;
163
+ let best = node.value;
164
+ // Trivial cases
165
+ if (node.children) {
166
+ for (const segment of childPathSegments) {
167
+ const child = node.children.get(segment);
168
+ if (!child) {
169
+ break;
170
+ }
171
+ node = child;
172
+ best = (_a = node.value) !== null && _a !== void 0 ? _a : best;
173
+ if (!node.children) {
174
+ break;
175
+ }
176
+ }
177
+ }
178
+ return best;
179
+ }
180
+ /**
181
+ * Iterates through progressively longer prefixes of a given string and returns as soon
182
+ * as the number of candidate items that match the prefix are 1 or 0.
183
+ *
184
+ * If a match is present, returns the matched itme and the length of the matched prefix.
185
+ *
186
+ * @returns the found item, or `undefined` if no item was found
187
+ */
188
+ _findLongestPrefixMatch(prefixes) {
189
+ let node = this._root;
190
+ let best = node.value
191
+ ? {
192
+ value: node.value,
193
+ index: 0
194
+ }
195
+ : undefined;
196
+ // Trivial cases
197
+ if (node.children) {
198
+ for (const { prefix: hash, index } of prefixes) {
199
+ const child = node.children.get(hash);
200
+ if (!child) {
201
+ break;
202
+ }
203
+ node = child;
204
+ if (node.value !== undefined) {
205
+ best = {
206
+ value: node.value,
207
+ index
208
+ };
209
+ }
210
+ if (!node.children) {
211
+ break;
212
+ }
213
+ }
214
+ }
215
+ return best;
216
+ }
217
+ }
218
+ exports.LookupByPath = LookupByPath;
219
+ //# sourceMappingURL=LookupByPath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LookupByPath.js","sourceRoot":"","sources":["../src/LookupByPath.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * A node in the path trie used in LookupByPath\n */\ninterface IPathTrieNode<TItem> {\n /**\n * The value that exactly matches the current relative path\n */\n value: TItem | undefined;\n /**\n * Child nodes by subfolder\n */\n children: Map<string, IPathTrieNode<TItem>> | undefined;\n}\n\ninterface IPrefixEntry {\n /**\n * The prefix that was matched\n */\n prefix: string;\n /**\n * The index of the first character after the matched prefix\n */\n index: number;\n}\n\n/**\n * Object containing both the matched item and the start index of the remainder of the query.\n *\n * @beta\n */\nexport interface IPrefixMatch<TItem> {\n /**\n * The item that matched the prefix\n */\n value: TItem;\n /**\n * The index of the first character after the matched prefix\n */\n index: number;\n}\n\n/**\n * This class is used to associate path-like-strings, such as those returned by `git` commands,\n * with entities that correspond with ancestor folders, such as Rush Projects or npm packages.\n *\n * It is optimized for efficiently locating the nearest ancestor path with an associated value.\n *\n * It is implemented as a Trie (https://en.wikipedia.org/wiki/Trie) data structure, with each edge\n * being a path segment.\n *\n * @example\n * ```ts\n * const trie = new LookupByPath([['foo', 1], ['bar', 2], ['foo/bar', 3]]);\n * trie.findChildPath('foo'); // returns 1\n * trie.findChildPath('foo/baz'); // returns 1\n * trie.findChildPath('baz'); // returns undefined\n * trie.findChildPath('foo/bar/baz'); returns 3\n * trie.findChildPath('bar/foo/bar'); returns 2\n * ```\n * @beta\n */\nexport class LookupByPath<TItem> {\n /**\n * The delimiter used to split paths\n */\n public readonly delimiter: string;\n /**\n * The root node of the trie, corresponding to the path ''\n */\n private readonly _root: IPathTrieNode<TItem>;\n\n /**\n * Constructs a new `LookupByPath`\n *\n * @param entries - Initial path-value pairs to populate the trie.\n */\n public constructor(entries?: Iterable<[string, TItem]>, delimiter?: string) {\n this._root = {\n value: undefined,\n children: undefined\n };\n\n this.delimiter = delimiter ?? '/';\n\n if (entries) {\n for (const [path, item] of entries) {\n this.setItem(path, item);\n }\n }\n }\n\n /**\n * Iterates over the segments of a serialized path.\n *\n * @example\n *\n * `LookupByPath.iteratePathSegments('foo/bar/baz')` yields 'foo', 'bar', 'baz'\n *\n * `LookupByPath.iteratePathSegments('foo\\\\bar\\\\baz', '\\\\')` yields 'foo', 'bar', 'baz'\n */\n public static *iteratePathSegments(serializedPath: string, delimiter: string = '/'): Iterable<string> {\n for (const prefixMatch of this._iteratePrefixes(serializedPath, delimiter)) {\n yield prefixMatch.prefix;\n }\n }\n\n private static *_iteratePrefixes(input: string, delimiter: string = '/'): Iterable<IPrefixEntry> {\n if (!input) {\n return;\n }\n\n let previousIndex: number = 0;\n let nextIndex: number = input.indexOf(delimiter);\n\n // Leading segments\n while (nextIndex >= 0) {\n yield {\n prefix: input.slice(previousIndex, nextIndex),\n index: nextIndex\n };\n previousIndex = nextIndex + 1;\n nextIndex = input.indexOf(delimiter, previousIndex);\n }\n\n // Last segment\n if (previousIndex < input.length) {\n yield {\n prefix: input.slice(previousIndex, input.length),\n index: input.length\n };\n }\n }\n\n /**\n * Associates the value with the specified serialized path.\n * If a value is already associated, will overwrite.\n *\n * @returns this, for chained calls\n */\n public setItem(serializedPath: string, value: TItem): this {\n return this.setItemFromSegments(LookupByPath.iteratePathSegments(serializedPath, this.delimiter), value);\n }\n\n /**\n * Associates the value with the specified path.\n * If a value is already associated, will overwrite.\n *\n * @returns this, for chained calls\n */\n public setItemFromSegments(pathSegments: Iterable<string>, value: TItem): this {\n let node: IPathTrieNode<TItem> = this._root;\n for (const segment of pathSegments) {\n if (!node.children) {\n node.children = new Map();\n }\n let child: IPathTrieNode<TItem> | undefined = node.children.get(segment);\n if (!child) {\n node.children.set(\n segment,\n (child = {\n value: undefined,\n children: undefined\n })\n );\n }\n node = child;\n }\n node.value = value;\n\n return this;\n }\n\n /**\n * Searches for the item associated with `childPath`, or the nearest ancestor of that path that\n * has an associated item.\n *\n * @returns the found item, or `undefined` if no item was found\n *\n * @example\n * ```ts\n * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);\n * trie.findChildPath('foo/baz'); // returns 1\n * trie.findChildPath('foo/bar/baz'); // returns 2\n * ```\n */\n public findChildPath(childPath: string): TItem | undefined {\n return this.findChildPathFromSegments(LookupByPath.iteratePathSegments(childPath, this.delimiter));\n }\n\n /**\n * Searches for the item for which the recorded prefix is the longest matching prefix of `query`.\n * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be\n * extracted.\n *\n * @returns the found item and the length of the matched prefix, or `undefined` if no item was found\n *\n * @example\n * ```ts\n * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);\n * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 }\n * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 }\n * ```\n */\n public findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined {\n return this._findLongestPrefixMatch(LookupByPath._iteratePrefixes(query, this.delimiter));\n }\n\n /**\n * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that\n * has an associated item.\n *\n * @returns the found item, or `undefined` if no item was found\n *\n * @example\n * ```ts\n * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);\n * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1\n * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2\n * ```\n */\n public findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined {\n let node: IPathTrieNode<TItem> = this._root;\n let best: TItem | undefined = node.value;\n // Trivial cases\n if (node.children) {\n for (const segment of childPathSegments) {\n const child: IPathTrieNode<TItem> | undefined = node.children.get(segment);\n if (!child) {\n break;\n }\n node = child;\n best = node.value ?? best;\n if (!node.children) {\n break;\n }\n }\n }\n\n return best;\n }\n\n /**\n * Iterates through progressively longer prefixes of a given string and returns as soon\n * as the number of candidate items that match the prefix are 1 or 0.\n *\n * If a match is present, returns the matched itme and the length of the matched prefix.\n *\n * @returns the found item, or `undefined` if no item was found\n */\n private _findLongestPrefixMatch(prefixes: Iterable<IPrefixEntry>): IPrefixMatch<TItem> | undefined {\n let node: IPathTrieNode<TItem> = this._root;\n let best: IPrefixMatch<TItem> | undefined = node.value\n ? {\n value: node.value,\n index: 0\n }\n : undefined;\n // Trivial cases\n if (node.children) {\n for (const { prefix: hash, index } of prefixes) {\n const child: IPathTrieNode<TItem> | undefined = node.children.get(hash);\n if (!child) {\n break;\n }\n node = child;\n if (node.value !== undefined) {\n best = {\n value: node.value,\n index\n };\n }\n if (!node.children) {\n break;\n }\n }\n }\n\n return best;\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=LookupByPath.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LookupByPath.test.d.ts","sourceRoot":"","sources":["../src/LookupByPath.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LookupByPath.test.js","sourceRoot":"","sources":["../src/LookupByPath.test.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;AAE3D,iDAA8C;AAE9C,QAAQ,CAAC,2BAAY,CAAC,mBAAmB,CAAC,IAAI,EAAE,GAAG,EAAE;IACnD,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG,CAAC,GAAG,2BAAY,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,MAAM,GAAG,CAAC,GAAG,2BAAY,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,MAAM,GAAG,CAAC,GAAG,2BAAY,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,MAAM,GAAG,CAAC,GAAG,2BAAY,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAC;QACpE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,MAAM,GAAG,CAAC,GAAG,2BAAY,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAAY,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE;IACvD,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,IAAI,2BAAY,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,IAAI,2BAAY,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAyB,IAAI,2BAAY,CAAC;YAClD,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,KAAK,EAAE,CAAC,CAAC;SACX,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,IAAI,GAAyB,IAAI,2BAAY,CAAC;YAClD,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,KAAK,EAAE,CAAC,CAAC;SACX,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,IAAI,GAAyB,IAAI,2BAAY,CAAC;YAClD,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,SAAS,EAAE,CAAC,CAAC;YACd,CAAC,aAAa,EAAE,CAAC,CAAC;YAClB,CAAC,SAAS,EAAE,CAAC,CAAC;YACd,CAAC,iBAAiB,EAAE,CAAC,CAAC;SACvB,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEtD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEjD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAErD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAErD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAErE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAElD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAEzD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,IAAI,GAAyB,IAAI,2BAAY,CACjD;YACE,CAAC,SAAS,EAAE,CAAC,CAAC;YACd,CAAC,SAAS,EAAE,CAAC,CAAC;SACf,EACD,GAAG,CACJ,CAAC;QAEF,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7D,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAAY,CAAC,SAAS,CAAC,sBAAsB,CAAC,IAAI,EAAE,GAAG,EAAE;IAChE,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,IAAI,2BAAY,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,IAAI,2BAAY,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAyB,IAAI,2BAAY,CAAC;YAClD,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,QAAQ,EAAE,CAAC,CAAC;YACb,CAAC,KAAK,EAAE,CAAC,CAAC;SACX,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3E,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9E,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3E,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,IAAI,GAAyB,IAAI,2BAAY,CAAC;YAClD,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,QAAQ,EAAE,CAAC,CAAC;YACb,CAAC,KAAK,EAAE,CAAC,CAAC;YACV,CAAC,SAAS,EAAE,CAAC,CAAC;SACf,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/E,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/E,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { LookupByPath } from './LookupByPath';\n\ndescribe(LookupByPath.iteratePathSegments.name, () => {\n it('returns empty for an empty string', () => {\n const result = [...LookupByPath.iteratePathSegments('')];\n expect(result.length).toEqual(0);\n });\n it('returns the only segment of a trival string', () => {\n const result = [...LookupByPath.iteratePathSegments('foo')];\n expect(result).toEqual(['foo']);\n });\n it('treats backslashes as ordinary characters, per POSIX', () => {\n const result = [...LookupByPath.iteratePathSegments('foo\\\\bar\\\\baz')];\n expect(result).toEqual(['foo\\\\bar\\\\baz']);\n });\n it('iterates segments', () => {\n const result = [...LookupByPath.iteratePathSegments('foo/bar/baz')];\n expect(result).toEqual(['foo', 'bar', 'baz']);\n });\n it('returns correct last single character segment', () => {\n const result = [...LookupByPath.iteratePathSegments('foo/a')];\n expect(result).toEqual(['foo', 'a']);\n });\n});\n\ndescribe(LookupByPath.prototype.findChildPath.name, () => {\n it('returns empty for an empty tree', () => {\n expect(new LookupByPath().findChildPath('foo')).toEqual(undefined);\n });\n it('returns the matching node for a trivial tree', () => {\n expect(new LookupByPath([['foo', 1]]).findChildPath('foo')).toEqual(1);\n });\n it('returns the matching node for a single-layer tree', () => {\n const tree: LookupByPath<number> = new LookupByPath([\n ['foo', 1],\n ['bar', 2],\n ['baz', 3]\n ]);\n\n expect(tree.findChildPath('foo')).toEqual(1);\n expect(tree.findChildPath('bar')).toEqual(2);\n expect(tree.findChildPath('baz')).toEqual(3);\n expect(tree.findChildPath('buzz')).toEqual(undefined);\n });\n it('returns the matching parent for multi-layer queries', () => {\n const tree: LookupByPath<number> = new LookupByPath([\n ['foo', 1],\n ['bar', 2],\n ['baz', 3]\n ]);\n\n expect(tree.findChildPath('foo/bar')).toEqual(1);\n expect(tree.findChildPath('bar/baz')).toEqual(2);\n expect(tree.findChildPath('baz/foo')).toEqual(3);\n expect(tree.findChildPath('foo/foo')).toEqual(1);\n });\n it('returns the matching parent for multi-layer queries in multi-layer trees', () => {\n const tree: LookupByPath<number> = new LookupByPath([\n ['foo', 1],\n ['bar', 2],\n ['baz', 3],\n ['foo/bar', 4],\n ['foo/bar/baz', 5],\n ['baz/foo', 6],\n ['baz/baz/baz/baz', 7]\n ]);\n\n expect(tree.findChildPath('foo/foo')).toEqual(1);\n expect(tree.findChildPath('foo/bar\\\\baz')).toEqual(1);\n\n expect(tree.findChildPath('bar/baz')).toEqual(2);\n\n expect(tree.findChildPath('baz/bar')).toEqual(3);\n expect(tree.findChildPath('baz/baz')).toEqual(3);\n expect(tree.findChildPath('baz/baz/baz')).toEqual(3);\n\n expect(tree.findChildPath('foo/bar')).toEqual(4);\n expect(tree.findChildPath('foo/bar/foo')).toEqual(4);\n\n expect(tree.findChildPath('foo/bar/baz')).toEqual(5);\n expect(tree.findChildPath('foo/bar/baz/baz/baz/baz/baz')).toEqual(5);\n\n expect(tree.findChildPath('baz/foo/')).toEqual(6);\n\n expect(tree.findChildPath('baz/baz/baz/baz')).toEqual(7);\n\n expect(tree.findChildPath('')).toEqual(undefined);\n expect(tree.findChildPath('foofoo')).toEqual(undefined);\n expect(tree.findChildPath('foo\\\\bar\\\\baz')).toEqual(undefined);\n });\n it('handles custom delimiters', () => {\n const tree: LookupByPath<number> = new LookupByPath(\n [\n ['foo,bar', 1],\n ['foo/bar', 2]\n ],\n ','\n );\n\n expect(tree.findChildPath('foo/bar,baz')).toEqual(2);\n expect(tree.findChildPath('foo,bar/baz')).toEqual(undefined);\n expect(tree.findChildPathFromSegments(['foo', 'bar', 'baz'])).toEqual(1);\n });\n});\n\ndescribe(LookupByPath.prototype.findLongestPrefixMatch.name, () => {\n it('returns empty for an empty tree', () => {\n expect(new LookupByPath().findLongestPrefixMatch('foo')).toEqual(undefined);\n });\n it('returns the matching node for a trivial tree', () => {\n expect(new LookupByPath([['foo', 1]]).findLongestPrefixMatch('foo')).toEqual({ value: 1, index: 3 });\n });\n it('returns the matching node for a single-layer tree', () => {\n const tree: LookupByPath<number> = new LookupByPath([\n ['foo', 1],\n ['barbar', 2],\n ['baz', 3]\n ]);\n\n expect(tree.findLongestPrefixMatch('foo')).toEqual({ value: 1, index: 3 });\n expect(tree.findLongestPrefixMatch('barbar')).toEqual({ value: 2, index: 6 });\n expect(tree.findLongestPrefixMatch('baz')).toEqual({ value: 3, index: 3 });\n expect(tree.findLongestPrefixMatch('buzz')).toEqual(undefined);\n });\n it('returns the matching parent for multi-layer queries', () => {\n const tree: LookupByPath<number> = new LookupByPath([\n ['foo', 1],\n ['barbar', 2],\n ['baz', 3],\n ['foo/bar', 4]\n ]);\n\n expect(tree.findLongestPrefixMatch('foo/bar')).toEqual({ value: 4, index: 7 });\n expect(tree.findLongestPrefixMatch('barbar/baz')).toEqual({ value: 2, index: 6 });\n expect(tree.findLongestPrefixMatch('baz/foo')).toEqual({ value: 3, index: 3 });\n expect(tree.findLongestPrefixMatch('foo/foo')).toEqual({ value: 1, index: 3 });\n });\n});\n"]}
package/lib/index.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Strongly typed trie data structure for path and URL-like strings.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export type { IPrefixMatch } from './LookupByPath';
7
+ export { LookupByPath } from './LookupByPath';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.LookupByPath = void 0;
6
+ var LookupByPath_1 = require("./LookupByPath");
7
+ Object.defineProperty(exports, "LookupByPath", { enumerable: true, get: function () { return LookupByPath_1.LookupByPath; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAS3D,+CAA8C;AAArC,4GAAA,YAAY,OAAA","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * Strongly typed trie data structure for path and URL-like strings.\n *\n * @packageDocumentation\n */\n\nexport type { IPrefixMatch } from './LookupByPath';\nexport { LookupByPath } from './LookupByPath';\n"]}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@rushstack/lookup-by-path",
3
+ "version": "0.1.0",
4
+ "description": "Strongly typed trie data structure for path and URL-like strings.",
5
+ "main": "lib/index.js",
6
+ "typings": "dist/lookup-by-path.d.ts",
7
+ "keywords": [
8
+ "trie",
9
+ "path",
10
+ "url",
11
+ "radix tree",
12
+ "prefix tree"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": {
16
+ "url": "https://github.com/microsoft/rushstack.git",
17
+ "type": "git",
18
+ "directory": "libraries/lookup-by-path"
19
+ },
20
+ "devDependencies": {
21
+ "@rushstack/heft": "0.66.25",
22
+ "local-node-rig": "1.0.0"
23
+ },
24
+ "peerDependencies": {
25
+ "@types/node": "*"
26
+ },
27
+ "peerDependenciesMeta": {
28
+ "@types/node": {
29
+ "optional": true
30
+ }
31
+ },
32
+ "scripts": {
33
+ "build": "heft build --clean",
34
+ "_phase:build": "heft run --only build -- --clean",
35
+ "_phase:test": "heft run --only test -- --clean"
36
+ }
37
+ }