@rushstack/lookup-by-path 0.3.2 → 0.4.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 CHANGED
@@ -1,6 +1,18 @@
1
1
  {
2
2
  "name": "@rushstack/lookup-by-path",
3
3
  "entries": [
4
+ {
5
+ "version": "0.4.0",
6
+ "tag": "@rushstack/lookup-by-path_v0.4.0",
7
+ "date": "Thu, 17 Oct 2024 20:25:42 GMT",
8
+ "comments": {
9
+ "minor": [
10
+ {
11
+ "comment": "Add `IReadonlyLookupByPath` interface to help unit tests for functions that consume `LookupByPath`."
12
+ }
13
+ ]
14
+ }
15
+ },
4
16
  {
5
17
  "version": "0.3.2",
6
18
  "tag": "@rushstack/lookup-by-path_v0.3.2",
package/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # Change Log - @rushstack/lookup-by-path
2
2
 
3
- This log was last generated on Thu, 17 Oct 2024 08:35:06 GMT and should not be manually modified.
3
+ This log was last generated on Thu, 17 Oct 2024 20:25:42 GMT and should not be manually modified.
4
+
5
+ ## 0.4.0
6
+ Thu, 17 Oct 2024 20:25:42 GMT
7
+
8
+ ### Minor changes
9
+
10
+ - Add `IReadonlyLookupByPath` interface to help unit tests for functions that consume `LookupByPath`.
4
11
 
5
12
  ## 0.3.2
6
13
  Thu, 17 Oct 2024 08:35:06 GMT
@@ -24,6 +24,66 @@ export declare interface IPrefixMatch<TItem> {
24
24
  lastMatch?: IPrefixMatch<TItem>;
25
25
  }
26
26
 
27
+ /**
28
+ * The readonly component of `LookupByPath`, to simplify unit testing.
29
+ *
30
+ * @beta
31
+ */
32
+ export declare interface IReadonlyLookupByPath<TItem> {
33
+ /**
34
+ * Searches for the item associated with `childPath`, or the nearest ancestor of that path that
35
+ * has an associated item.
36
+ *
37
+ * @returns the found item, or `undefined` if no item was found
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
42
+ * trie.findChildPath('foo/baz'); // returns 1
43
+ * trie.findChildPath('foo/bar/baz'); // returns 2
44
+ * ```
45
+ */
46
+ findChildPath(childPath: string): TItem | undefined;
47
+ /**
48
+ * Searches for the item for which the recorded prefix is the longest matching prefix of `query`.
49
+ * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be
50
+ * extracted.
51
+ *
52
+ * @returns the found item and the length of the matched prefix, or `undefined` if no item was found
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
57
+ * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 }
58
+ * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 }
59
+ * ```
60
+ */
61
+ findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined;
62
+ /**
63
+ * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that
64
+ * has an associated item.
65
+ *
66
+ * @returns the found item, or `undefined` if no item was found
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
71
+ * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1
72
+ * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2
73
+ * ```
74
+ */
75
+ findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined;
76
+ /**
77
+ * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path
78
+ * is not found in the trie, the info is ignored.
79
+ *
80
+ * @returns The grouped info, grouped by the nearest entry in the trie that contains the path
81
+ *
82
+ * @param infoByPath - The info to be grouped, keyed by path
83
+ */
84
+ groupByChild<TInfo>(infoByPath: Map<string, TInfo>): Map<TItem, Map<string, TInfo>>;
85
+ }
86
+
27
87
  /**
28
88
  * This class is used to associate path-like-strings, such as those returned by `git` commands,
29
89
  * with entities that correspond with ancestor folders, such as Rush Projects or npm packages.
@@ -44,7 +104,7 @@ export declare interface IPrefixMatch<TItem> {
44
104
  * ```
45
105
  * @beta
46
106
  */
47
- export declare class LookupByPath<TItem> {
107
+ export declare class LookupByPath<TItem> implements IReadonlyLookupByPath<TItem> {
48
108
  /**
49
109
  * The delimiter used to split paths
50
110
  */
@@ -85,55 +145,19 @@ export declare class LookupByPath<TItem> {
85
145
  */
86
146
  setItemFromSegments(pathSegments: Iterable<string>, value: TItem): this;
87
147
  /**
88
- * Searches for the item associated with `childPath`, or the nearest ancestor of that path that
89
- * has an associated item.
90
- *
91
- * @returns the found item, or `undefined` if no item was found
92
- *
93
- * @example
94
- * ```ts
95
- * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
96
- * trie.findChildPath('foo/baz'); // returns 1
97
- * trie.findChildPath('foo/bar/baz'); // returns 2
98
- * ```
148
+ * {@inheritdoc IReadonlyLookupByPath}
99
149
  */
100
150
  findChildPath(childPath: string): TItem | undefined;
101
151
  /**
102
- * Searches for the item for which the recorded prefix is the longest matching prefix of `query`.
103
- * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be
104
- * extracted.
105
- *
106
- * @returns the found item and the length of the matched prefix, or `undefined` if no item was found
107
- *
108
- * @example
109
- * ```ts
110
- * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
111
- * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 }
112
- * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 }
113
- * ```
152
+ * {@inheritdoc IReadonlyLookupByPath}
114
153
  */
115
154
  findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined;
116
155
  /**
117
- * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that
118
- * has an associated item.
119
- *
120
- * @returns the found item, or `undefined` if no item was found
121
- *
122
- * @example
123
- * ```ts
124
- * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
125
- * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1
126
- * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2
127
- * ```
156
+ * {@inheritdoc IReadonlyLookupByPath}
128
157
  */
129
158
  findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined;
130
159
  /**
131
- * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path
132
- * is not found in the trie, the info is ignored.
133
- *
134
- * @returns The grouped info, grouped by the nearest entry in the trie that contains the path
135
- *
136
- * @param infoByPath - The info to be grouped, keyed by path
160
+ * {@inheritdoc IReadonlyLookupByPath}
137
161
  */
138
162
  groupByChild<TInfo>(infoByPath: Map<string, TInfo>): Map<TItem, Map<string, TInfo>>;
139
163
  /**
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.47.10"
8
+ "packageVersion": "7.47.11"
9
9
  }
10
10
  ]
11
11
  }
@@ -17,6 +17,65 @@ export interface IPrefixMatch<TItem> {
17
17
  */
18
18
  lastMatch?: IPrefixMatch<TItem>;
19
19
  }
20
+ /**
21
+ * The readonly component of `LookupByPath`, to simplify unit testing.
22
+ *
23
+ * @beta
24
+ */
25
+ export interface IReadonlyLookupByPath<TItem> {
26
+ /**
27
+ * Searches for the item associated with `childPath`, or the nearest ancestor of that path that
28
+ * has an associated item.
29
+ *
30
+ * @returns the found item, or `undefined` if no item was found
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
35
+ * trie.findChildPath('foo/baz'); // returns 1
36
+ * trie.findChildPath('foo/bar/baz'); // returns 2
37
+ * ```
38
+ */
39
+ findChildPath(childPath: string): TItem | undefined;
40
+ /**
41
+ * Searches for the item for which the recorded prefix is the longest matching prefix of `query`.
42
+ * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be
43
+ * extracted.
44
+ *
45
+ * @returns the found item and the length of the matched prefix, or `undefined` if no item was found
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
50
+ * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 }
51
+ * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 }
52
+ * ```
53
+ */
54
+ findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined;
55
+ /**
56
+ * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that
57
+ * has an associated item.
58
+ *
59
+ * @returns the found item, or `undefined` if no item was found
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
64
+ * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1
65
+ * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2
66
+ * ```
67
+ */
68
+ findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined;
69
+ /**
70
+ * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path
71
+ * is not found in the trie, the info is ignored.
72
+ *
73
+ * @returns The grouped info, grouped by the nearest entry in the trie that contains the path
74
+ *
75
+ * @param infoByPath - The info to be grouped, keyed by path
76
+ */
77
+ groupByChild<TInfo>(infoByPath: Map<string, TInfo>): Map<TItem, Map<string, TInfo>>;
78
+ }
20
79
  /**
21
80
  * This class is used to associate path-like-strings, such as those returned by `git` commands,
22
81
  * with entities that correspond with ancestor folders, such as Rush Projects or npm packages.
@@ -37,7 +96,7 @@ export interface IPrefixMatch<TItem> {
37
96
  * ```
38
97
  * @beta
39
98
  */
40
- export declare class LookupByPath<TItem> {
99
+ export declare class LookupByPath<TItem> implements IReadonlyLookupByPath<TItem> {
41
100
  /**
42
101
  * The delimiter used to split paths
43
102
  */
@@ -78,55 +137,19 @@ export declare class LookupByPath<TItem> {
78
137
  */
79
138
  setItemFromSegments(pathSegments: Iterable<string>, value: TItem): this;
80
139
  /**
81
- * Searches for the item associated with `childPath`, or the nearest ancestor of that path that
82
- * has an associated item.
83
- *
84
- * @returns the found item, or `undefined` if no item was found
85
- *
86
- * @example
87
- * ```ts
88
- * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
89
- * trie.findChildPath('foo/baz'); // returns 1
90
- * trie.findChildPath('foo/bar/baz'); // returns 2
91
- * ```
140
+ * {@inheritdoc IReadonlyLookupByPath}
92
141
  */
93
142
  findChildPath(childPath: string): TItem | undefined;
94
143
  /**
95
- * Searches for the item for which the recorded prefix is the longest matching prefix of `query`.
96
- * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be
97
- * extracted.
98
- *
99
- * @returns the found item and the length of the matched prefix, or `undefined` if no item was found
100
- *
101
- * @example
102
- * ```ts
103
- * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
104
- * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 }
105
- * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 }
106
- * ```
144
+ * {@inheritdoc IReadonlyLookupByPath}
107
145
  */
108
146
  findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined;
109
147
  /**
110
- * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that
111
- * has an associated item.
112
- *
113
- * @returns the found item, or `undefined` if no item was found
114
- *
115
- * @example
116
- * ```ts
117
- * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]);
118
- * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1
119
- * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2
120
- * ```
148
+ * {@inheritdoc IReadonlyLookupByPath}
121
149
  */
122
150
  findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined;
123
151
  /**
124
- * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path
125
- * is not found in the trie, the info is ignored.
126
- *
127
- * @returns The grouped info, grouped by the nearest entry in the trie that contains the path
128
- *
129
- * @param infoByPath - The info to be grouped, keyed by path
152
+ * {@inheritdoc IReadonlyLookupByPath}
130
153
  */
131
154
  groupByChild<TInfo>(infoByPath: Map<string, TInfo>): Map<TItem, Map<string, TInfo>>;
132
155
  /**
@@ -1 +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;IACd;;OAEG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;CACjC;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;IACI,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAmB1F;;;;;;;OAOG;IACH,OAAO,CAAC,uBAAuB;CAgChC"}
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;IACd;;OAEG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB,CAAC,KAAK;IAC1C;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IAEpD;;;;;;;;;;;;;OAaG;IACH,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAEvE;;;;;;;;;;;;OAYG;IACH,yBAAyB,CAAC,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,SAAS,CAAC;IAElF;;;;;;;OAOG;IACH,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;CACrF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,YAAY,CAAC,KAAK,CAAE,YAAW,qBAAqB,CAAC,KAAK,CAAC;IACtE;;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;;OAEG;IACI,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAI1D;;OAEG;IACI,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,SAAS;IAI7E;;OAEG;IACI,yBAAyB,CAAC,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,SAAS;IAqBxF;;OAEG;IACI,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAmB1F;;;;;;;OAOG;IACH,OAAO,CAAC,uBAAuB;CAgChC"}
@@ -112,50 +112,19 @@ class LookupByPath {
112
112
  return this;
113
113
  }
114
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
- * ```
115
+ * {@inheritdoc IReadonlyLookupByPath}
126
116
  */
127
117
  findChildPath(childPath) {
128
118
  return this.findChildPathFromSegments(LookupByPath.iteratePathSegments(childPath, this.delimiter));
129
119
  }
130
120
  /**
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
- * ```
121
+ * {@inheritdoc IReadonlyLookupByPath}
143
122
  */
144
123
  findLongestPrefixMatch(query) {
145
124
  return this._findLongestPrefixMatch(LookupByPath._iteratePrefixes(query, this.delimiter));
146
125
  }
147
126
  /**
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
- * ```
127
+ * {@inheritdoc IReadonlyLookupByPath}
159
128
  */
160
129
  findChildPathFromSegments(childPathSegments) {
161
130
  var _a;
@@ -178,12 +147,7 @@ class LookupByPath {
178
147
  return best;
179
148
  }
180
149
  /**
181
- * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path
182
- * is not found in the trie, the info is ignored.
183
- *
184
- * @returns The grouped info, grouped by the nearest entry in the trie that contains the path
185
- *
186
- * @param infoByPath - The info to be grouped, keyed by path
150
+ * {@inheritdoc IReadonlyLookupByPath}
187
151
  */
188
152
  groupByChild(infoByPath) {
189
153
  const groupedInfoByChild = new Map();
@@ -1 +1 @@
1
- {"version":3,"file":"LookupByPath.js","sourceRoot":"","sources":["../src/LookupByPath.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AA+C3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAa,YAAY;IAUvB;;;;OAIG;IACH,YAAmB,OAAmC,EAAE,SAAkB;QACxE,IAAI,CAAC,KAAK,GAAG;YACX,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,SAAS;SACpB,CAAC;QAEF,IAAI,CAAC,SAAS,GAAG,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,GAAG,CAAC;QAElC,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC;gBACnC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACI,MAAM,CAAC,CAAC,mBAAmB,CAAC,cAAsB,EAAE,YAAoB,GAAG;QAChF,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,SAAS,CAAC,EAAE,CAAC;YAC3E,MAAM,WAAW,CAAC,MAAM,CAAC;QAC3B,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,CAAC,gBAAgB,CAAC,KAAa,EAAE,YAAoB,GAAG;QACrE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;QACT,CAAC;QAED,IAAI,aAAa,GAAW,CAAC,CAAC;QAC9B,IAAI,SAAS,GAAW,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEjD,mBAAmB;QACnB,OAAO,SAAS,IAAI,CAAC,EAAE,CAAC;YACtB,MAAM;gBACJ,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE,SAAS,CAAC;gBAC7C,KAAK,EAAE,SAAS;aACjB,CAAC;YACF,aAAa,GAAG,SAAS,GAAG,CAAC,CAAC;YAC9B,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACtD,CAAC;QAED,eAAe;QACf,IAAI,aAAa,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,MAAM;gBACJ,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC;gBAChD,KAAK,EAAE,KAAK,CAAC,MAAM;aACpB,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACI,OAAO,CAAC,cAAsB,EAAE,KAAY;QACjD,OAAO,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,mBAAmB,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3G,CAAC;IAED;;;;;OAKG;IACI,mBAAmB,CAAC,YAA8B,EAAE,KAAY;QACrE,IAAI,IAAI,GAAyB,IAAI,CAAC,KAAK,CAAC;QAC5C,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;YAC5B,CAAC;YACD,IAAI,KAAK,GAAqC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACzE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,IAAI,CAAC,QAAQ,CAAC,GAAG,CACf,OAAO,EACP,CAAC,KAAK,GAAG;oBACP,KAAK,EAAE,SAAS;oBAChB,QAAQ,EAAE,SAAS;iBACpB,CAAC,CACH,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;OAYG;IACI,aAAa,CAAC,SAAiB;QACpC,OAAO,IAAI,CAAC,yBAAyB,CAAC,YAAY,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACrG,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,sBAAsB,CAAC,KAAa;QACzC,OAAO,IAAI,CAAC,uBAAuB,CAAC,YAAY,CAAC,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED;;;;;;;;;;;;OAYG;IACI,yBAAyB,CAAC,iBAAmC;;QAClE,IAAI,IAAI,GAAyB,IAAI,CAAC,KAAK,CAAC;QAC5C,IAAI,IAAI,GAAsB,IAAI,CAAC,KAAK,CAAC;QACzC,gBAAgB;QAChB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;gBACxC,MAAM,KAAK,GAAqC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC3E,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,MAAM;gBACR,CAAC;gBACD,IAAI,GAAG,KAAK,CAAC;gBACb,IAAI,GAAG,MAAA,IAAI,CAAC,KAAK,mCAAI,IAAI,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACnB,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACI,YAAY,CAAQ,UAA8B;QACvD,MAAM,kBAAkB,GAAmC,IAAI,GAAG,EAAE,CAAC;QAErE,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;YACtC,MAAM,KAAK,GAAsB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAC1D,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,SAAS;YACX,CAAC;YACD,IAAI,WAAW,GAAmC,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAChF,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;gBACxB,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YAC7C,CAAC;YACD,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9B,CAAC;QAED,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACK,uBAAuB,CAAC,QAAgC;QAC9D,IAAI,IAAI,GAAyB,IAAI,CAAC,KAAK,CAAC;QAC5C,IAAI,IAAI,GAAoC,IAAI,CAAC,KAAK;YACpD,CAAC,CAAC;gBACE,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK,EAAE,CAAC;gBACR,SAAS,EAAE,SAAS;aACrB;YACH,CAAC,CAAC,SAAS,CAAC;QACd,gBAAgB;QAChB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,QAAQ,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAqC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACxE,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,MAAM;gBACR,CAAC;gBACD,IAAI,GAAG,KAAK,CAAC;gBACb,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBAC7B,IAAI,GAAG;wBACL,KAAK,EAAE,IAAI,CAAC,KAAK;wBACjB,KAAK;wBACL,SAAS,EAAE,IAAI;qBAChB,CAAC;gBACJ,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACnB,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAvPD,oCAuPC","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 * The last match found (with a shorter prefix), if any\n */\n lastMatch?: IPrefixMatch<TItem>;\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 * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path\n * is not found in the trie, the info is ignored.\n *\n * @returns The grouped info, grouped by the nearest entry in the trie that contains the path\n *\n * @param infoByPath - The info to be grouped, keyed by path\n */\n public groupByChild<TInfo>(infoByPath: Map<string, TInfo>): Map<TItem, Map<string, TInfo>> {\n const groupedInfoByChild: Map<TItem, Map<string, TInfo>> = new Map();\n\n for (const [path, info] of infoByPath) {\n const child: TItem | undefined = this.findChildPath(path);\n if (child === undefined) {\n continue;\n }\n let groupedInfo: Map<string, TInfo> | undefined = groupedInfoByChild.get(child);\n if (!groupedInfo) {\n groupedInfo = new Map();\n groupedInfoByChild.set(child, groupedInfo);\n }\n groupedInfo.set(path, info);\n }\n\n return groupedInfoByChild;\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 lastMatch: undefined\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 lastMatch: best\n };\n }\n if (!node.children) {\n break;\n }\n }\n }\n\n return best;\n }\n}\n"]}
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 * The last match found (with a shorter prefix), if any\n */\n lastMatch?: IPrefixMatch<TItem>;\n}\n\n/**\n * The readonly component of `LookupByPath`, to simplify unit testing.\n *\n * @beta\n */\nexport interface IReadonlyLookupByPath<TItem> {\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 findChildPath(childPath: string): TItem | undefined;\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 findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined;\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 findChildPathFromSegments(childPathSegments: Iterable<string>): TItem | undefined;\n\n /**\n * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path\n * is not found in the trie, the info is ignored.\n *\n * @returns The grouped info, grouped by the nearest entry in the trie that contains the path\n *\n * @param infoByPath - The info to be grouped, keyed by path\n */\n groupByChild<TInfo>(infoByPath: Map<string, TInfo>): Map<TItem, Map<string, TInfo>>;\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> implements IReadonlyLookupByPath<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 * {@inheritdoc IReadonlyLookupByPath}\n */\n public findChildPath(childPath: string): TItem | undefined {\n return this.findChildPathFromSegments(LookupByPath.iteratePathSegments(childPath, this.delimiter));\n }\n\n /**\n * {@inheritdoc IReadonlyLookupByPath}\n */\n public findLongestPrefixMatch(query: string): IPrefixMatch<TItem> | undefined {\n return this._findLongestPrefixMatch(LookupByPath._iteratePrefixes(query, this.delimiter));\n }\n\n /**\n * {@inheritdoc IReadonlyLookupByPath}\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 * {@inheritdoc IReadonlyLookupByPath}\n */\n public groupByChild<TInfo>(infoByPath: Map<string, TInfo>): Map<TItem, Map<string, TInfo>> {\n const groupedInfoByChild: Map<TItem, Map<string, TInfo>> = new Map();\n\n for (const [path, info] of infoByPath) {\n const child: TItem | undefined = this.findChildPath(path);\n if (child === undefined) {\n continue;\n }\n let groupedInfo: Map<string, TInfo> | undefined = groupedInfoByChild.get(child);\n if (!groupedInfo) {\n groupedInfo = new Map();\n groupedInfoByChild.set(child, groupedInfo);\n }\n groupedInfo.set(path, info);\n }\n\n return groupedInfoByChild;\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 lastMatch: undefined\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 lastMatch: best\n };\n }\n if (!node.children) {\n break;\n }\n }\n }\n\n return best;\n }\n}\n"]}
package/lib/index.d.ts CHANGED
@@ -3,6 +3,6 @@
3
3
  *
4
4
  * @packageDocumentation
5
5
  */
6
- export type { IPrefixMatch } from './LookupByPath';
6
+ export type { IPrefixMatch, IReadonlyLookupByPath } from './LookupByPath';
7
7
  export { LookupByPath } from './LookupByPath';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
package/lib/index.js.map CHANGED
@@ -1 +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"]}
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, IReadonlyLookupByPath } from './LookupByPath';\nexport { LookupByPath } from './LookupByPath';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rushstack/lookup-by-path",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Strongly typed trie data structure for path and URL-like strings.",
5
5
  "main": "lib/index.js",
6
6
  "typings": "dist/lookup-by-path.d.ts",